H. Peter Anvin
2009-May-31 18:18 UTC
[syslinux] Calling between real mode and protected mode on the core32 branch
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.
H. Peter Anvin
2009-May-31 18:41 UTC
[syslinux] Calling between real mode and protected mode on the core32 branch
H. Peter Anvin wrote:> > *** 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); >I forgot to cover core_cfarcall(): it's used to call real-mode functions that mimic the 16-bit C compiler calling convention. In that case, the first argument is (CS << 16)+IP, the second argument is a copy of the stack frame parameters (it's a structure with the parameters in order from left to right), and the third one is the size of the stack frame structure. The return value is passed in from EAX; depending on what the called function is it may be necessary to cast it to (int16_t) or (uint16_t) if it is to be used at all. In the com32 world, this is provided by a wrapper function called __cfarcall(); the only difference there is that CS and IP are two separate arguments. -hpa -- H. Peter Anvin, Intel Open Source Technology Center I work for Intel. I don't speak on their behalf.
Ferenc Wagner
2009-May-31 20:06 UTC
[syslinux] Calling between real mode and protected mode on the core32 branch
"H. Peter Anvin" <hpa at zytor.com> writes:> The first argument is [...] (CS << 6)+IP in the case of core_farcall().Is (CS << 6) an unfortunate crossing of CS*16 and CS<<4?> 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);How is the possible segment ambiguity resolved? -- Thanks, Feri.