The amd64/i386 boot(8) code runs in protected mode, however switches back to real mode for BIOS calls. The real mode code uses a scratch area located in the BSS section to load/store registers across BIOS calls. However, once the BSS moves beyond an offset of 0xffff (a logical address of 0x50000 since the base is 0x40000) it is inaccessible from the real mode code and results in access failures.
The following diff removes this restriction by loading the necessary registers (ES, BX) from the scratch area while still in protected mode, then patching them into the real mode instructions. The same is done when returning from real mode to protected mode, allowing the registers to be preserved and then stored into the scratch area from protected mode. This has been tested on amd64 and i386, however it needs further testing to ensure that it does not introduce any unexpected regressions. Please report successes or failures directly to me. ok? Index: arch/amd64/stand/libsa/gidt.S =================================================================== RCS file: /cvs/src/sys/arch/amd64/stand/libsa/gidt.S,v retrieving revision 1.6 diff -u -p -r1.6 gidt.S --- arch/amd64/stand/libsa/gidt.S 29 Dec 2006 11:44:01 -0000 1.6 +++ arch/amd64/stand/libsa/gidt.S 29 Sep 2012 16:15:11 -0000 @@ -312,6 +312,10 @@ IEMUENT(44); IEMUENT(45); IEMUENT(46); I * entry point for BIOS real-mode interface * all the magic for real-prot mode switching is here * + * Note: Once in real mode access to .data or .bss should be avoided since it + * may not be reachable within the current segment. The code also assumes that + * .text is writeable. + * * Call: %eax, %ecx, %edx, %ebx, %ebp, %esi, %edi, %es, %ds * Return: %eax, %edx, %ecx, %eflags (as returned from BIOS) * @@ -320,7 +324,7 @@ IEMUENT(44); IEMUENT(45); IEMUENT(46); I .align 8, 0x90 EMUh: /* save %eax */ - mov %eax, 3f + mov %eax, 5f pop %eax pusha @@ -332,18 +336,29 @@ EMUh: /* save BIOS int vector */ mov %al, intno + /* Load BIOS registers prior to switching to real mode. */ + movl _C_LABEL(BIOS_regs)+BIOSR_ES, %eax + mov %eax, 7f + movl _C_LABEL(BIOS_regs)+BIOSR_DS, %eax + mov %eax, 6f + prot2real push %ds - addr32 movw (_C_LABEL(BIOS_regs)+(BIOSR_ES) - LINKADDR), %ax - movw %ax, %es - addr32 movw (_C_LABEL(BIOS_regs)+(BIOSR_DS) - LINKADDR), %ax - movw %ax, %ds + # data32 movl $Leax, %eax + .byte 0x66, 0xb8 +7: .long 0x90909090 + mov %ax, %es + + # data32 movl $Leax, %eax + .byte 0x66, 0xb8 +6: .long 0x90909090 + mov %ax, %ds # data32 movl $Leax, %eax .byte 0x66, 0xb8 -3: .long 0x90909090 +5: .long 0x90909090 ;sti int $0 @@ -352,19 +367,35 @@ intno = . - 1 pop %ds - addr32 movl %ebx, (_C_LABEL(BIOS_regs)+(BIOSR_BX) - LINKADDR) - movw %es, %bx - addr32 movw %bx, (_C_LABEL(BIOS_regs)+(BIOSR_ES) - LINKADDR) + /* Preserve BX and ES for protected mode. */ + addr32 movl %eax, (2f - LINKADDR) + movl %ebx, %eax + addr32 movl %eax, (4f - LINKADDR) + movl %es, %eax + addr32 movl %eax, (3f - LINKADDR) + addr32 movl (2f - LINKADDR), %eax + movb %ah, %bh lahf xchgb %ah, %bh + /* Preserve AX for protected mode. */ addr32 movl %eax, (2f - LINKADDR) real2prot # movl $Leax, %eax .byte 0xb8 +4: .long 0x90909090 + movl %eax, _C_LABEL(BIOS_regs)+BIOSR_BX + + # movl $Leax, %eax + .byte 0xb8 +3: .long 0x90909090 + movl %eax, _C_LABEL(BIOS_regs)+BIOSR_ES + + # movl $Leax, %eax + .byte 0xb8 2: .long 0x90909090 /* pass BIOS return values back to caller */ Index: arch/i386/stand/libsa/gidt.S =================================================================== RCS file: /cvs/src/sys/arch/i386/stand/libsa/gidt.S,v retrieving revision 1.32 diff -u -p -r1.32 gidt.S --- arch/i386/stand/libsa/gidt.S 26 Dec 2006 19:30:44 -0000 1.32 +++ arch/i386/stand/libsa/gidt.S 29 Sep 2012 16:15:11 -0000 @@ -314,6 +314,10 @@ IEMUENT(44); IEMUENT(45); IEMUENT(46); I * entry point for BIOS real-mode interface * all the magic for real-prot mode switching is here * + * Note: Once in real mode access to .data or .bss should be avoided since it + * may not be reachable within the current segment. The code also assumes that + * .text is writeable. + * * Call: %eax, %ecx, %edx, %ebx, %ebp, %esi, %edi, %es, %ds * Return: %eax, %edx, %ecx, %eflags (as returned from BIOS) * @@ -322,7 +326,7 @@ IEMUENT(44); IEMUENT(45); IEMUENT(46); I .align 8, 0x90 EMUh: /* save %eax */ - mov %eax, 3f + mov %eax, 5f pop %eax pusha @@ -334,18 +338,29 @@ EMUh: /* save BIOS int vector */ mov %al, intno + /* Load BIOS registers prior to switching to real mode. */ + movl _C_LABEL(BIOS_regs)+BIOSR_ES, %eax + mov %eax, 7f + movl _C_LABEL(BIOS_regs)+BIOSR_DS, %eax + mov %eax, 6f + prot2real push %ds - addr32 movw (_C_LABEL(BIOS_regs)+(BIOSR_ES) - LINKADDR), %ax - movw %ax, %es - addr32 movw (_C_LABEL(BIOS_regs)+(BIOSR_DS) - LINKADDR), %ax - movw %ax, %ds + # data32 movl $Leax, %eax + .byte 0x66, 0xb8 +7: .long 0x90909090 + mov %ax, %es + + # data32 movl $Leax, %eax + .byte 0x66, 0xb8 +6: .long 0x90909090 + mov %ax, %ds # data32 movl $Leax, %eax .byte 0x66, 0xb8 -3: .long 0x90909090 +5: .long 0x90909090 ;sti int $0 @@ -354,19 +369,35 @@ intno = . - 1 pop %ds - addr32 movl %ebx, (_C_LABEL(BIOS_regs)+(BIOSR_BX) - LINKADDR) - movw %es, %bx - addr32 movw %bx, (_C_LABEL(BIOS_regs)+(BIOSR_ES) - LINKADDR) + /* Preserve BX and ES for protected mode. */ + addr32 movl %eax, (2f - LINKADDR) + movl %ebx, %eax + addr32 movl %eax, (4f - LINKADDR) + movl %es, %eax + addr32 movl %eax, (3f - LINKADDR) + addr32 movl (2f - LINKADDR), %eax + movb %ah, %bh lahf xchgb %ah, %bh + /* Preserve AX for protected mode. */ addr32 movl %eax, (2f - LINKADDR) real2prot # movl $Leax, %eax .byte 0xb8 +4: .long 0x90909090 + movl %eax, _C_LABEL(BIOS_regs)+BIOSR_BX + + # movl $Leax, %eax + .byte 0xb8 +3: .long 0x90909090 + movl %eax, _C_LABEL(BIOS_regs)+BIOSR_ES + + # movl $Leax, %eax + .byte 0xb8 2: .long 0x90909090 /* pass BIOS return values back to caller */ -- "Reason is not automatic. Those who deny it cannot be conquered by it. Do not count on them. Leave them alone." -- Ayn Rand