diff --git a/src/arch/i386/prefix/libprefix.S b/src/arch/i386/prefix/libprefix.S index 12cf9184..406dac39 100644 --- a/src/arch/i386/prefix/libprefix.S +++ b/src/arch/i386/prefix/libprefix.S @@ -17,6 +17,10 @@ * */ + .arch i386 + .section ".prefix.lib", "awx", @progbits + .section ".data16", "aw", @progbits + /** * High memory temporary load address * @@ -27,25 +31,18 @@ * We use the start of an even megabyte so that we don't have to worry * about the current state of the A20 line. * - * We use 4MB rather than 2MB because there is at least one commercial - * PXE ROM ("Broadcom UNDI, PXE-2.1 (build 082) v2.0.4") which stores - * data required by the UNDI ROM loader (yes, the ROM loader; that's - * the component which should be impossible to damage short of - * screwing with the MMU) around the 2MB mark. Sadly, this is not a - * joke. - * + * We use 4MB rather than 2MB because some PXE stack / PMM BIOS + * combinations are known to place data required by other UNDI ROMs + * loader around the 2MB mark. */ -#define HIGHMEM_LOADPOINT ( 4 << 20 ) + .globl HIGHMEM_LOADPOINT + .equ HIGHMEM_LOADPOINT, ( 4 << 20 ) /* Image compression enabled */ #define COMPRESS 1 #define CR0_PE 1 - .arch i386 - .section ".prefix.lib", "awx", @progbits - .section ".data16", "aw", @progbits - /**************************************************************************** * pm_call (real-mode near call) * @@ -223,12 +220,15 @@ copy_bytes: * Returns: * %ds:esi : next source address (will be a multiple of 16) * Corrupts: - * %edi, %ecx, %edx + * %ecx, %edx **************************************************************************** */ .section ".prefix.lib" .code16 install_block: + /* Preserve registers */ + pushl %edi + #if COMPRESS /* Decompress source to destination */ call decompress16 @@ -249,6 +249,8 @@ install_block: addl $0xf, %esi andl $~0xf, %esi + /* Restore registers and return */ + popl %edi ret .size install_block, . - install_block @@ -270,6 +272,7 @@ install_block: */ .section ".prefix.lib" .code16 + .globl alloc_basemem alloc_basemem: /* FBMS => %ax as segment address */ movw $0x40, %ax @@ -308,13 +311,14 @@ alloc_basemem: * Returns: * %esi : next source physical address (will be a multiple of 16) * Corrupts: - * %edi, %ecx, %edx + * %ecx, %edx **************************************************************************** */ .section ".prefix.lib" .code16 install_basemem: /* Preserve registers */ + pushl %edi pushw %ds /* Preserve original %esi */ @@ -335,6 +339,7 @@ install_basemem: /* Restore registers */ popw %ds + popl %edi ret .size install_basemem, . - install_basemem @@ -351,7 +356,7 @@ install_basemem: * Returns: * %esi : next source physical address (will be a multiple of 16) * Corrupts: - * %edi, %ecx, %edx + * %ecx, %edx **************************************************************************** */ @@ -376,17 +381,14 @@ install_highmem: /**************************************************************************** * install (real-mode near call) - * install_prealloc (real-mode near call) * * Install all text and data segments. * * Parameters: - * %ax : .text16 segment address (install_prealloc only) - * %bx : .data16 segment address (install_prealloc only) + * none * Returns: - * %ax : .text16 segment address - * %bx : .data16 segment address - * %edi : .text physical address (if applicable) + * %ax : .text16 segment address + * %bx : .data16 segment address * Corrupts: * none **************************************************************************** @@ -395,26 +397,55 @@ install_highmem: .code16 .globl install install: + /* Preserve registers */ + pushl %esi + pushl %edi /* Allocate space for .text16 and .data16 */ call alloc_basemem + /* Image source = %cs:0000 */ + xorl %esi, %esi + /* Image destination = HIGHMEM_LOADPOINT */ + movl $HIGHMEM_LOADPOINT, %edi + /* Install text and data segments */ + call install_prealloc + /* Restore registers and return */ + popl %edi + popl %esi + ret .size install, . - install + +/**************************************************************************** + * install_prealloc (real-mode near call) + * + * Install all text and data segments. + * + * Parameters: + * %ax : .text16 segment address + * %bx : .data16 segment address + * %esi : Image source physical address (or zero for %cs:0000) + * %edi : Decompression temporary area physical address + * Corrupts: + * none + **************************************************************************** + */ + .section ".prefix.lib" + .code16 .globl install_prealloc install_prealloc: /* Save registers */ + pushal pushw %ds pushw %es - pushl %esi - pushl %ecx - pushl %edx /* Sanity: clear the direction flag asap */ cld /* Calculate physical address of payload (i.e. first source) */ - xorl %esi, %esi + testl %esi, %esi + jnz 1f movw %cs, %si shll $4, %esi - addl $_payload_offset, %esi +1: addl $_payload_offset, %esi /* Install .text16 */ movw %ax, %es @@ -440,7 +471,6 @@ install_prealloc: * prior to reading the E820 memory map and relocating * properly. */ - movl $HIGHMEM_LOADPOINT, %edi movl $_textdata_progbits_size, %ecx movl $_textdata_size, %edx pushl %edi @@ -473,11 +503,9 @@ install_prealloc: #endif /* Restore registers */ - popl %edx - popl %ecx - popl %esi popw %es popw %ds + popal ret .size install_prealloc, . - install_prealloc diff --git a/src/arch/i386/prefix/romprefix.S b/src/arch/i386/prefix/romprefix.S index 167641c6..86d76a5d 100644 --- a/src/arch/i386/prefix/romprefix.S +++ b/src/arch/i386/prefix/romprefix.S @@ -6,6 +6,10 @@ * table so using a noticeable amount of stack space is a no-no. */ +#define PNP_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'n' << 16 ) + ( 'P' << 24 ) ) +#define PMM_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'M' << 16 ) + ( 'M' << 24 ) ) +#define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) ) + .text .code16 .arch i386 @@ -15,7 +19,9 @@ romheader: .word 0xAA55 /* BIOS extension signature */ romheader_size: .byte _load_size_sect /* Size in 512-byte blocks */ - jmp init_vector /* Initialisation vector */ + jmp init /* Initialisation vector */ +checksum: + .byte 0 .org 0x16 .word undiheader .org 0x18 @@ -72,7 +78,7 @@ pnpheader: .byte 0x54 /* Device indicator */ .word 0x0000 /* Boot connection vector */ .word 0x0000 /* Disconnect vector */ - .word exec_vector /* Boot execution vector */ + .word bev_entry /* Boot execution vector */ .word 0x0000 /* Reserved */ .word 0x0000 /* Static resource information vector*/ .equ pnpheader_len, . - pnpheader @@ -98,60 +104,180 @@ undiheader: .equ undiheader_len, . - undiheader .size undiheader, . - undiheader -/* Initialisation vector +/* Initialisation (called once during POST) * * Determine whether or not this is a PnP system via a signature * check. If it is PnP, return to the PnP BIOS indicating that we are * a boot-capable device; the BIOS will call our boot execution vector * if it wants to boot us. If it is not PnP, hook INT 19. */ -init_vector: - pushw %si - cmpw $'$'+'P'*256, %es:0(%di) - jne notpnp - cmpw $'n'+'P'*256, %es:2(%di) - jne notpnp -ispnp: - movw $ispnp_message, %si - jmp 99f -notpnp: +init: + /* Preserve registers, clear direction flag, set %ds=%cs */ + pushaw pushw %ds - pushw $0 - popw %ds + pushw %es + cld pushw %cs - pushw $exec_vector - popl ( 0x19 * 4 ) popw %ds - movw $notpnp_message, %si -99: + /* Print message as early as possible */ + movw $init_message, %si call print_message - movw $0x20, %ax - popw %si - lret - .size init_vector, . - init_vector - -ispnp_message: - .asciz "gPXE detected PnP BIOS\r\n" - .size ispnp_message, . - ispnp_message -notpnp_message: - .asciz "gPXE detected non-PnP BIOS\r\n" - .size notpnp_message, . - notpnp_message - -/* Boot execution vector - *pciheader_size - * Called by the PnP BIOS when it wants to boot us, or via the hooked - * INT 19 if we detected a non-PnP BIOS. - */ -exec_vector: - /* Obtain a reasonably-sized stack */ + /* Check for PnP BIOS */ + cmpl $PNP_SIGNATURE, %es:0(%di) + je ispnp +notpnp: /* Not PnP: hook INT19 */ xorw %ax, %ax - movw %ax, %ss - movw $0x7c00, %sp - + movw %ax, %es + pushw %cs + pushw $int19_entry + popl %es:( 0x19 * 4 ) + jmp 99f +ispnp: /* Is PnP: print PnP message */ + movw $init_message_pnp, %si + call print_message + /* Check for PMM */ + movw $( 0xe000 - 1 ), %di +pmm_scan: + incw %di + jz 99f + movw %di, %es + cmpl $PMM_SIGNATURE, %es:0 + jne pmm_scan + xorw %bx, %bx + xorw %si, %si + movzbw %es:5, %cx +1: es lodsb + addb %al, %bl + loop 1b + jnz pmm_scan + /* PMM found: print PMM message */ + movw $init_message_pmm, %si + call print_message + /* Try to allocate 2MB block via PMM */ + pushw $0x0006 /* Aligned, extended memory */ + pushl $0xffffffff /* No handle */ + pushl $( 0x00200000 / 16 ) /* 2MB in paragraphs */ + pushw $0x0000 /* pmmAllocate */ + lcall %es:*(7) + addw $12, %sp + testw %dx, %dx /* %ax==0 even on success, since align=2MB */ + jnz gotpmm + movw $init_message_pmm_failed, %si + call print_message + jmp 99f +gotpmm: /* PMM allocation succeeded: copy ROM to PMM block */ + pushal /* PMM presence implies 1kB stack */ + movw %ax, %es /* %ax=0 already - see above */ + pushw %dx + pushw %ax + popl %edi + movl %edi, image_source + xorl %esi, %esi + movzbl romheader_size, %ecx + shll $9, %ecx + addr32 rep movsb /* PMM presence implies flat real mode */ + movl %edi, decompress_to + /* Shrink ROM and update checksum */ + xorw %bx, %bx + xorw %si, %si + movb $_prefix_size_sect, romheader_size + shlw $9, %cx +1: lodsb + addb %al, %bl + loop 1b + subb %bl, checksum + popal +99: + /* Print CRLF to terminate messages */ + movw $init_message_crlf, %si + call print_message + /* Restore registers */ + popw %es + popw %ds + popaw + /* Indicate boot capability to PnP BIOS, if present */ + movw $0x20, %ax + lret + .size init, . - init + +init_message: + .asciz "gPXE (http://etherboot.org)" + .size init_message, . - init_message +init_message_pnp: + .asciz " - PnP BIOS detected" + .size init_message_pnp, . - init_message_pnp +init_message_pmm: + .asciz ", using PMM" + .size init_message_pmm, . - init_message_pmm +init_message_pmm_failed: + .asciz " (failed)" + .size init_message_pmm_failed, . - init_message_pmm_failed +init_message_crlf: + .asciz "\r\n" + .size init_message_crlf, . - init_message_crlf + +/* ROM image location + * + * May be either within option ROM space, or within PMM-allocated block. + */ +image_source: + .long 0 + .size image_source, . - image_source +/* Temporary decompression area + * + * May be either at HIGHMEM_LOADPOINT, or within PMM-allocated block. + */ +decompress_to: + .long HIGHMEM_LOADPOINT + .size decompress_to, . - decompress_to + +/* Boot Execution Vector entry point + * + * Called by the PnP BIOS when it wants to boot us. + */ +bev_entry: + pushw %cs + call exec + lret + .size bev_entry, . - bev_entry + +/* INT19 entry point + * + * Called via the hooked INT 19 if we detected a non-PnP BIOS. + */ +int19_entry: + pushw %cs + call exec + /* No real way to return from INT19 */ + int $0x18 + .size int19_entry, . - int19_entry + +/* Execute as a boot device + * + */ +exec: /* Set %ds = %cs */ + pushw %cs + popw %ds + + /* Print message as soon as possible */ movw $exec_message, %si call print_message - call install + /* Store magic word on BIOS stack and remember BIOS %ss:sp */ + pushl $STACK_MAGIC + movw %ss, %dx + movw %sp, %bp + + /* Obtain a reasonably-sized temporary stack */ + xorw %ax, %ax + movw %ax, %ss + movw $0x7c00, %sp + + /* Install gPXE */ + movl image_source, %esi + movl decompress_to, %edi + call alloc_basemem + call install_prealloc /* Set up real-mode stack */ movw %bx, %ss @@ -162,13 +288,23 @@ exec_vector: pushw $1f lret .section ".text16", "awx", @progbits -1: +1: /* Call main() */ pushl $main pushw %cs call prot_call - popl %eax /* discard */ + /* No need to clean up stack; we are about to reload %ss:sp */ + + /* Restore BIOS stack */ + movw %dx, %ss + movw %bp, %sp - /* Boot next device */ + /* Check magic word on BIOS stack */ + popl %eax + cmpl $STACK_MAGIC, %eax + jne 1f + /* BIOS stack OK: return to caller */ + lret +1: /* BIOS stack corrupt: use INT 18 */ int $0x18 .previous @@ -182,6 +318,7 @@ exec_message: */ undiloader: /* Save registers */ + pushl %esi pushl %edi pushw %es pushw %bx @@ -193,6 +330,8 @@ undiloader: pushw %di movw %es:12(%di), %bx movw %es:14(%di), %ax + movl %cs:image_source, %esi + movl %cs:decompress_to, %edi call install_prealloc popw %di /* Call UNDI loader C code */ @@ -208,6 +347,7 @@ undiloader: popw %bx popw %es popl %edi + popl %esi lret .size undiloader, . - undiloader @@ -218,7 +358,7 @@ print_message: pushw %bx pushw %bp movw $0x0007, %bx -1: cs lodsb +1: lodsb testb %al, %al je 2f movb $0x0e, %ah /* write char, tty mode */ diff --git a/src/arch/i386/scripts/i386.lds b/src/arch/i386/scripts/i386.lds index d481db0f..a5a01056 100644 --- a/src/arch/i386/scripts/i386.lds +++ b/src/arch/i386/scripts/i386.lds @@ -257,6 +257,8 @@ SECTIONS { /* * Values calculated to save code from doing it */ + _prefix_size_pgh = ( ( _prefix_size + 15 ) / 16 ); + _prefix_size_sect = ( ( _prefix_size + 511 ) / 512 ); _text16_size_pgh = ( ( _text16_size + 15 ) / 16 ); _data16_size_pgh = ( ( _data16_size + 15 ) / 16 );