[syslinux] Calling between real mode and protected mode on the core32 branch

H. Peter Anvin hpa at zytor.com
Sun May 31 11:18:27 PDT 2009


liu asked me for some clarification on how intermode calls work on the
core32 branch, so I decided that that was probably something that really
should be published more widely.  This is the *current state* of the
core32 branch; all this stuff is subject to change as development
progresses:


*** Call from real mode (16-bit) to protected mode (32-bit):

These calls are done with the pm_call macro:

	extern pm_func
	pm_call pm_func

the "extern" just tells NASM this is a symbol from outside the file; it
has the effect of being a prototype.  I have started collecting the
external symbols into the header file extern.inc just to avoid
cluttering things up too badly, although there are still quite a few of
them just out in the code still.

The pm_func routine gets called as such:

void pm_func(com32sys_t *regs)
{
	/* ... */
}

... where *regs is the real-mode register state.  This is an
input/output structure: it both reflects the incoming registers and can
be changed to affect the registers on return to real mode.

16-bit pointers in a seg:off pair can be converted to a 32-bit pointer via:

	ptr = MK_PTR(seg, off);

for example:

	ptr = MK_PTR(regs->es, regs->ebx.w[0]); /* ES:BX */


*** Calls from protected mode (32-bit) to real mode (16-bit):

This is done via the core_intcall(), core_farcall() and core_cfarcall()
functions, which are the direct versions of the analogous instructions
in the com32 world.  They are defined in <core.h>:

void __cdecl core_intcall(uint8_t, const com32sys_t *, com32sys_t *);
void __cdecl core_farcall(uint32_t, const com32sys_t *, com32sys_t *);
int __cdecl core_cfarcall(uint32_t, const void *, uint32_t);

core_intcall() and core_farcall() both are used to call real-mode code
using register passing.  The first argument is the int number in case of
core_intcall(), or a (CS << 6)+IP in the case of core_farcall().  In the
care of the Syslinux core itself, CS is zero, so one can simply pass in
the IP, the address of the function.

The second argument is the register image passed into the function, and
the third argument is the register image coming back from the function.
 The latter can be NULL.

To break a 32-bit pointer apart into a seg:off pair, use the SEG() and
OFFS() macros:

	regs.es       = SEG(ptr);
	regs.ebx.w[0] = OFFS(ptr);

BUT, this is where things get tricky: this can only be done for a
pointer that is below the 1 MB real-mode limit.  The way one usually
deals with this is to pass data through the 64K core_xfer_buf, which is
located below the megabyte limit (for com32 modules, this is known as
__com32.cs_bounce).


	-hpa

-- 
H. Peter Anvin, Intel Open Source Technology Center
I work for Intel.  I don't speak on their behalf.




More information about the Syslinux mailing list