delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp/1997/09/25/05:53:15

Date: Thu, 25 Sep 1997 11:47:19 +0200 (IST)
From: Eli Zaretskii <eliz AT is DOT elta DOT co DOT il>
To: firewind <firewind AT metroid DOT dyn DOT ml DOT org>
cc: Brett Porter <bporter AT rabble DOT uow DOT edu DOT au>, djgpp AT delorie DOT com
Subject: Re: Using "far" pointers
In-Reply-To: <Pine.LNX.3.95.970924214311.371B-100000@metroid.dyn.ml.org>
Message-ID: <Pine.SUN.3.91.970925114609.22174N-100000@is>
MIME-Version: 1.0

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.)

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019