Date: Thu, 25 Sep 1997 11:47:19 +0200 (IST) From: Eli Zaretskii To: firewind cc: Brett Porter , djgpp AT delorie DOT com Subject: Re: Using "far" pointers In-Reply-To: Message-ID: MIME-Version: 1.0 Content-Type: TEXT/PLAIN; charset=US-ASCII Precedence: bulk On Wed, 24 Sep 1997, firewind wrote: > > I know you can't actually load ES with an arbitary value: it has to be a > > selector or you get a GPF. The above is correct, but only when the processor is in protected mode. In that case, any segment registers can only de loaded with a valid selector. A valid selector points into one of the descriptor tables (either the global GDT or local LDT) to a descriptor whose present bit is set. However, in the context of calling real-mode interrupts, the ES value is not a protected-mode selector, but a real-mode segment, which will be loaded into the ES register only *after* the processor switches to real mode. So in this case, ES can have any value, as no protection is available in real mode. > indos = _farpeekb(_dos_ds, es*16+bx); > > 'es' (the real mode selector, AFAIK) No, it's the real-mode segment of the DOS internal data. In other words, it is the value loaded into DS register when DOS executes its code on behalf of some program that requested any of DOS services. > must be multiplied by 16 to be valid > (I don't know why, but the info page for _far* says so, so I do it :), and > then the offset ('bx') is added to that, resulting in a valid address in > the _dos_ds segment. The reason for the seg*16+off rule is that the `_dos_ds' segment has zero starting address. Therefore, the linear addresses in that segment correspond 1-to-1 to the 20-bit real-mode addresses. Since real-mode 20-bit addresses are computed by the processor by shifting the segment 4 bits to the left, then adding the offset, you need to do the same to provide functions like `_farpeekl' and `dosmemget' with a linear address which will access the same byte as in real mode. > My question was, if the _far* functions perform some sort of memory > protection, what is the result when 'es' and 'bx' are garbage? Based > on what you said, I now believe it would be a SIGSEGV. Get ready for a shock: _far* functions do NOT provide any protection whatsoever. The protection is not something implemented by a library function, it's intrinsic in the protected-mode operation of the CPU. It is important to understand that (at least as far as data access is concerned) this protection is solely based on the segment descriptor that is loaded when a particular segment is accessed. In particular, the processor checks whether the segment limit is exceeded, whether you are trying to write into a read-only segment, etc. Any ``garbage'' that passes these tests will not be prevented from doing its harm. So the correct question would be: does `_dos_ds' define a segment that guards against garbled addresses? Sadly, in DJGPP v2.01, the answer is NO! `_dos_ds' is defined as both readable and writeable (you need a way to pass data to DOS and BIOS, not only read it), and its limit is 4GB. The reason for the latter is that people wanted to use `_dos_ds' to access devices which are mapped to memory above 1MB mark, so the limit for `_dos_ds' was enlarged in v2.01. It turns out that enlarging the limit doesn't help accessing memory-mapped devices outside 1MB, because many DPMI servers require further DPMI calls before they allow such accesses. But this fact became apparent too late into v2.01 beta-testing, and so the limit was left at 4GB. I hope that for v2.02 I will be able to convince The Powers That Be to set the limit back to 1MB (+64KB, to enable accesses to the HMA). But even then, any combination of ES and BX that yields ES*16+BX which doesn't exceed this limit will never trigger any GPF; you just get garbled data when you read that address, and risk crashing the system if you write it. If you want more protection, you need to create your own selectors, with the limit that suits the application. For example, the FAQ shows in section 18.4 how to create a selector that only spans 64KB of the video memory, which is appropriate if you want to move data to/from the screen. > While this flag is nonzero, you -CANNOT- call any DOS > function, since DOS in non-reentrant; doing so will cause Bad Things(tm). > Therefore, whenever a properly-written, DOS-calling ISR enters, it checks > the InDOS flag, and if it is nonzero, does not do anything. I won't go into the intricacies of this (it could fill a book), but this is mostly, but not entirely true. First, some DOS functions in the low numbers can be called while InDOS is set, since they are executed on a different stack and therefore don't trigger the reentrancy problems if you need to do file I/O. And second, the most important exception to the don't-call-DOS-while-InDOS-flag-is-set rule is when COMMAND.COM is waiting for user input: it is then parked inside a DOS call that reads stdin. (The solution to this dilemma is to intercept the Idle interrupt--Int 28h--which is repeatedly called by COMMAND.COM while it waits for input, and which means that you can safely call DOS functions above 0Ah.)