[SeaBIOS] 16 Segementation help

Kevin O'Connor kevin at koconnor.net
Wed Mar 14 01:20:59 CET 2012


On Tue, Mar 13, 2012 at 06:44:19PM +0900, Daniel Castro wrote:
> Hello,
> 
> Sorry to bring this again, but I am still struggling with 16 bit disk operation.
> 
> This time I have a simpler question.
> 
> When I do container_of(...) I get pointer to 0xd630.
> The address when I created the drive struct is 0x000fd630.
> What do I need to do to be able to use the return of container_of in 16bit code?

When in "segmented" mode (both 16bit mode and 32bit segmented mode)
the processor does not have a uniform view of memory.  This is quite
complex and painful to work with, so basically no modern systems use
it anymore.  For compatibility, seabios must still support 16bit mode
though.  This means that when it is in segmented mode it has a
different view of memory than the init code (which runs in regular
32bit flat mode).

So, the following code:

u8 myglobal VAR16VISIBLE;
...
    dprintf(1, "myglobal addr = %p\n", &myglobal);

Will show two different results - it prints 0xf1234 in 32bit flat mode
(it's a variable in the f-segment).  In 16bit mode it shows up as
0x1234 - the build arranges for this in order to keep the address
within a 16bit range.  To access the variable in 16bit mode, one must
use GET_GLOBAL(myglobal) - this will arrange for the proper segment
accessor (in this case %cs) to be used in conjunction with the
variable address (eg, 0x1234) to load the proper value.

However, the following code:

int *myptr VAR16VISIBLE;
...
   myptr = malloc_fseg(16);
...
   dprintf(1, "myptr = %p\n", GET_GLOBAL(myptr));

The dprintf will show 0xf4567 regardless of the mode it is in.  That
is because the pointer is loaded up at runtime with a 32 bit flat
address, and the build has no way of "fixing" that up.  To access the
content of the pointer, one would use the GET_GLOBALFLAT() macro.

To make things slightly more complex, a *drive_g pointer returned from
block.c:getDrive() makes the conversion from GLOBALFLAT to GLOBAL (via
the GLOBALFLAT2GLOBAL macro.  So, if you're working with drive_g
pointers, you should just use GET_GLOBAL.  This, BTW, is the reason
for the "_g" and "_gf" sufixes found in places through the code -
they're identifiers for pointers that are global vs globalflat.

Also, be aware that *every* pointer access (that isn't pointing to a
variable on the stack) must be wrapped in a macro.  Failure to do this
will result in random data being read.  As an example of this, to read
the integer pointed to by the global variable "myptr" above, one would
need to run GET_GLOBALFLAT(*GET_GLOBAL(myptr)) - one macro to extract
the global variable and the other macro to extract the data pointed to
by the global variable.

So, basically, pointers have to be accessed the correct way in order
for the system to work.  I know this is complex, but it's just the
reality of programming in segmented mode.  This complexity has nothing
to do with container_of - that macro works the same way regardless of
the mode.

-Kevin



More information about the SeaBIOS mailing list