1 /* At entry, the processor is in 16 bit real mode and the code is being
2 * executed from an address it was not linked to. Code must be pic and
3 * 32 bit sensitive until things are fixed up.
5 * Also be very careful as the stack is at the rear end of the interrupt
6 * table so using a noticeable amount of stack space is a no-no.
9 FILE_LICENCE ( GPL2_OR_LATER )
11 #include <config/general.h>
13 #define PNP_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'n' << 16 ) + ( 'P' << 24 ) )
14 #define PMM_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'M' << 16 ) + ( 'M' << 24 ) )
15 #define PCI_SIGNATURE ( 'P' + ( 'C' << 8 ) + ( 'I' << 16 ) + ( ' ' << 24 ) )
16 #define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) )
17 #define PNP_GET_BBS_VERSION 0x60
18 #define PMM_ALLOCATE 0x0000
19 #define PMM_DEALLOCATE 0x0002
21 /* ROM banner timeout. Based on the configurable BANNER_TIMEOUT in
22 * config.h, but converted to a number of (18Hz) timer ticks, and
23 * doubled to allow for BIOSes that switch video modes immediately
24 * beforehand, so rendering the message almost invisible to the user.
26 #define ROM_BANNER_TIMEOUT ( 2 * ( 18 * BANNER_TIMEOUT ) / 10 )
28 /* We can load a ROM in two ways: have the BIOS load all of it (.rom prefix)
29 * or have the BIOS load a stub that loads the rest using PCI (.xrom prefix).
30 * The latter is not as widely supported, but allows the use of large ROMs
31 * on some systems with crowded option ROM space.
34 #ifdef LOAD_ROM_FROM_PCI
35 #define ROM_SIZE_VALUE _prefix_filesz_sect /* Amount to load in BIOS */
37 #define ROM_SIZE_VALUE 0 /* Load amount (before compr. fixup) */
44 .section ".prefix", "ax", @progbits
48 .word 0xAA55 /* BIOS extension signature */
49 romheader_size: .byte ROM_SIZE_VALUE /* Size in 512-byte blocks */
50 jmp init /* Initialisation vector */
61 .size romheader, . - romheader
63 .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
64 #ifndef LOAD_ROM_FROM_PCI
77 .ascii "PCIR" /* Signature */
78 .word pci_vendor_id /* Vendor identification */
79 .word pci_device_id /* Device identification */
80 .word 0x0000 /* Device list pointer */
81 .word pciheader_len /* PCI data structure length */
82 .byte 0x03 /* PCI data structure revision */
83 .byte 0x02, 0x00, 0x00 /* Class code */
84 pciheader_image_length:
85 .word ROM_SIZE_VALUE /* Image length */
86 .word 0x0001 /* Revision level */
87 .byte 0x00 /* Code type */
88 .byte 0x80 /* Last image indicator */
89 pciheader_runtime_length:
90 .word ROM_SIZE_VALUE /* Maximum run-time image length */
91 .word 0x0000 /* Configuration utility code header */
92 .word 0x0000 /* DMTF CLP entry point */
93 .equ pciheader_len, . - pciheader
94 .size pciheader, . - pciheader
96 #ifndef LOAD_ROM_FROM_PCI
97 .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
99 .long pciheader_image_length
103 .long pciheader_runtime_length
110 .ascii "$PnP" /* Signature */
111 .byte 0x01 /* Structure revision */
112 .byte ( pnpheader_len / 16 ) /* Length (in 16 byte increments) */
113 .word 0x0000 /* Offset of next header */
114 .byte 0x00 /* Reserved */
115 .byte 0x00 /* Checksum */
116 .long 0x00000000 /* Device identifier */
117 .word mfgstr /* Manufacturer string */
118 .word prodstr /* Product name */
119 .byte 0x02 /* Device base type code */
120 .byte 0x00 /* Device sub-type code */
121 .byte 0x00 /* Device interface type code */
122 .byte 0xf4 /* Device indicator */
123 .word 0x0000 /* Boot connection vector */
124 .word 0x0000 /* Disconnect vector */
125 .word bev_entry /* Boot execution vector */
126 .word 0x0000 /* Reserved */
127 .word 0x0000 /* Static resource information vector*/
128 .equ pnpheader_len, . - pnpheader
129 .size pnpheader, . - pnpheader
131 /* Manufacturer string */
133 .asciz "http://etherboot.org"
134 .size mfgstr, . - mfgstr
138 * Defaults to PRODUCT_SHORT_NAME. If the ROM image is writable at
139 * initialisation time, it will be filled in to include the PCI
140 * bus:dev.fn number of the card as well.
143 .ascii PRODUCT_SHORT_NAME
148 .asciz "xx:xx.x)" /* Filled in by init code */
149 .size prodstr, . - prodstr
154 .ascii "UNDI" /* Signature */
155 .byte undiheader_len /* Length of structure */
156 .byte 0 /* Checksum */
157 .byte 0 /* Structure revision */
158 .byte 0,1,2 /* PXE version: 2.1.0 */
159 .word undiloader /* Offset to loader routine */
160 .word _data16_memsz /* Stack segment size */
161 .word _data16_memsz /* Data segment size */
162 .word _text16_memsz /* Code segment size */
163 .ascii "PCIR" /* Bus type */
164 .equ undiheader_len, . - undiheader
165 .size undiheader, . - undiheader
167 /* Initialisation (called once during POST)
169 * Determine whether or not this is a PnP system via a signature
170 * check. If it is PnP, return to the PnP BIOS indicating that we are
171 * a boot-capable device; the BIOS will call our boot execution vector
172 * if it wants to boot us. If it is not PnP, hook INT 19.
175 /* Preserve registers, clear direction flag, set %ds=%cs */
185 /* Shuffle some registers around. We need %di available for
186 * the print_xxx functions, and in a register that's
187 * addressable from %es, so shuffle as follows:
189 * %di (pointer to PnP structure) => %bx
190 * %bx (runtime segment address, for PCI 3.0) => %gs
195 /* Print message as early as possible */
196 movw $init_message, %si
199 call print_pci_busdevfn
201 #ifdef LOAD_ROM_FROM_PCI
202 /* Save PCI bus:dev.fn for later use */
203 movw %ax, pci_busdevfn
206 /* Fill in product name string, if possible */
207 movw $prodstr_pci_id, %di
208 call print_pci_busdevfn
209 movb $( ' ' ), prodstr_separator
211 /* Print segment address */
218 /* Check for PCI BIOS version */
226 cmpl $PCI_SIGNATURE, %edx
230 #ifdef LOAD_ROM_FROM_PCI
233 movw $init_message_pci, %si
237 call print_hex_nibble
244 /* PCI >=3.0: leave %gs as-is if sane */
246 cmpw $0xa000, %ax /* Insane if %gs < 0xa000 */
248 movw %cs, %bx /* Sane if %cs == %gs */
251 movzbw romheader_size, %cx /* Sane if %cs+len <= %gs */
256 movw %cs, %bx /* Sane if %gs+len <= %cs */
260 pci3_insane: /* PCI 3.0 with insane %gs value: print error and ignore %gs */
266 /* PCI <3.0: set %gs (runtime segment) = %cs (init-time segment) */
273 /* Check for PnP BIOS. Although %es:di should point to the
274 * PnP BIOS signature on entry, some BIOSes fail to do this.
276 movw $( 0xf000 - 1 ), %bx
281 cmpl $PNP_SIGNATURE, %es:0
290 /* Is PnP: print PnP message */
291 movw $init_message_pnp, %si
295 pushw %es:0x1b /* Real-mode data segment */
296 pushw %ds /* &(bbs_version) */
298 pushw $PNP_GET_BBS_VERSION
303 no_pnp: /* Not PnP-compliant - therefore cannot be BBS-compliant */
304 no_bbs: /* Not BBS-compliant - must hook INT 19 */
305 movw $init_message_int19, %si
310 pushl %es:( 0x19 * 4 )
312 pushw %gs /* %gs contains runtime %cs */
314 popl %es:( 0x19 * 4 )
316 got_bbs: /* BBS compliant - no need to hook INT 19 */
317 movw $init_message_bbs, %si
323 movw $( 0xe000 - 1 ), %bx
328 cmpl $PMM_SIGNATURE, %es:0
337 /* PMM found: print PMM message */
338 movw $init_message_pmm, %si
341 /* We have PMM and so a 1kB stack: preserve upper register halves */
343 /* Calculate required allocation size in %esi */
344 movzwl real_size, %eax
346 addl $_textdata_memsz, %eax
347 orw $0xffff, %ax /* Ensure allocation size is at least 64kB */
349 subw $15, %cx /* Round up and convert to 64kB count */
353 /* Try to allocate block via PMM */
354 pushw $0x0006 /* Aligned, extended memory */
355 pushl $0xffffffff /* No handle */
358 pushl %eax /* Allocation size in paragraphs */
362 /* Abort if allocation fails */
363 testw %dx, %dx /* %ax==0 even on success, since align>=64kB */
365 /* If block has A20==1, free block and try again with twice
366 * the allocation size (and hence alignment).
372 pushw $PMM_DEALLOCATE
377 got_pmm: /* PMM allocation succeeded */
378 movw %dx, ( image_source + 2 )
387 /* Copy ROM to PMM block */
390 movl image_source, %edi
392 movzbl romheader_size, %ecx
394 addr32 rep movsb /* PMM presence implies flat real mode */
395 movl %edi, decompress_to
397 movb $_prefix_memsz_sect, romheader_size
398 #if defined(SHRINK_WITHOUT_PMM) || defined(LOAD_ROM_FROM_PCI)
401 /* Print marker and copy ourselves to high memory */
402 movl $HIGHMEM_LOADPOINT, image_source
411 /* Restore upper register halves */
413 #if defined(LOAD_ROM_FROM_PCI)
418 /* Cannot continue without PMM - print error message */
420 movw $init_message_no_pmm, %si
423 /* Wait for five seconds to let user see message */
425 1: call wait_for_tick
427 /* Mark environment as invalid and return */
428 movl $0, decompress_to
435 /* Update checksum */
438 movzbw romheader_size, %cx
445 /* Copy self to option ROM space. Required for PCI3.0, which
446 * loads us to a temporary location in low memory. Will be a
447 * no-op for lower PCI versions.
454 movzbw romheader_size, %cx
461 /* Prompt for POST-time shell */
462 movw $init_message_prompt, %si
467 movw $init_message_dots, %si
469 /* Wait for Ctrl-B */
476 movw $init_message_done, %si
480 /* Ctrl-B was pressed: invoke gPXE. The keypress will be
481 * picked up by the initial shell prompt, and we will drop
487 /* Restore registers */
494 /* Indicate boot capability to PnP BIOS, if present */
500 * Note to hardware vendors:
502 * If you wish to brand this boot ROM, please do so by defining the
503 * strings PRODUCT_NAME and PRODUCT_SHORT_NAME in config/general.h.
505 * While nothing in the GPL prevents you from removing all references
506 * to gPXE or http://etherboot.org, we prefer you not to do so.
508 * If you have an OEM-mandated branding requirement that cannot be
509 * satisfied simply by defining PRODUCT_NAME and PRODUCT_SHORT_NAME,
512 * [ Including an ASCII NUL in PRODUCT_NAME is considered to be
513 * bypassing the spirit of this request! ]
519 .asciz "gPXE (http://etherboot.org) - "
520 .size init_message, . - init_message
523 .size init_message_pci, . - init_message_pci
526 .size init_message_pnp, . - init_message_pnp
529 .size init_message_bbs, . - init_message_bbs
532 .size init_message_pmm, . - init_message_pmm
533 #ifdef LOAD_ROM_FROM_PCI
535 .asciz "\nPMM required but not present!\n"
536 .size init_message_no_pmm, . - init_message_no_pmm
540 .size init_message_int19, . - init_message_int19
542 .asciz "\nPress Ctrl-B to configure "
543 .size init_message_prompt, . - init_message_prompt
546 .size init_message_dots, . - init_message_dots
549 .size init_message_done, . - init_message_done
551 /* ROM image location
553 * May be either within option ROM space, or within PMM-allocated block.
558 .size image_source, . - image_source
560 /* Temporary decompression area
562 * May be either at HIGHMEM_LOADPOINT, or within PMM-allocated block.
563 * If a PCI ROM load fails, this will be set to zero.
567 .long HIGHMEM_LOADPOINT
568 .size decompress_to, . - decompress_to
570 #ifdef LOAD_ROM_FROM_PCI
572 /* Set if the PCI BIOS is present, even <3.0 */
575 .byte 0 /* for alignment */
576 .size pcibios_present, . - pcibios_present
578 /* PCI bus:device.function word
580 * Filled in by init in the .xrom case, so the remainder of the ROM
585 .size pci_busdevfn, . - pci_busdevfn
591 * Filled in by BBS BIOS. We ignore the value.
595 .size bbs_version, . - bbs_version
597 /* Boot Execution Vector entry point
599 * Called by the PnP BIOS when it wants to boot us.
605 .size bev_entry, . - bev_entry
608 #ifdef LOAD_ROM_FROM_PCI
610 #define PCI_ROM_ADDRESS 0x30 /* Bits 31:11 address, 10:1 reserved */
611 #define PCI_ROM_ADDRESS_ENABLE 0x00000001
612 #define PCI_ROM_ADDRESS_MASK 0xfffff800
614 #define PCIBIOS_READ_WORD 0xb109
615 #define PCIBIOS_READ_DWORD 0xb10a
616 #define PCIBIOS_WRITE_WORD 0xb10c
617 #define PCIBIOS_WRITE_DWORD 0xb10d
619 /* Determine size of PCI BAR
621 * %bx : PCI bus:dev.fn to probe
622 * %di : Address of BAR to find size of
623 * %edx : Mask of address bits within BAR
625 * %ecx : Size for a memory resource,
626 * 1 for an I/O resource (bit 0 set).
627 * CF : Set on error or nonexistent device (all-ones read)
629 * All other registers saved.
637 /* Read current BAR value */
638 movw $PCIBIOS_READ_DWORD, %ax
641 /* Check for device existence and save it */
642 testb $1, %cl /* I/O bit? */
644 andl $1, %ecx /* If so, exit with %ecx = 1 */
647 testl %ecx, %ecx /* Set ZF iff %ecx was all-ones */
650 stc /* All ones - exit with CF set */
652 1: movl %ecx, %esi /* Save in %esi */
654 /* Write all ones to BAR */
656 movw $PCIBIOS_WRITE_DWORD, %ax
660 movw $PCIBIOS_READ_DWORD, %ax
663 /* Find decode size from least set bit in mask BAR */
664 bsfl %ecx, %ecx /* Find least set bit, log2(decode size) */
665 jz 1f /* Mask BAR should not be zero */
668 shll %cl, %edx /* %edx = decode size */
670 1: xorl %edx, %edx /* Return zero size for mask BAR zero */
672 /* Restore old BAR value */
674 movw $PCIBIOS_WRITE_DWORD, %ax
677 movl %edx, %ecx /* Return size in %ecx */
679 /* Restore registers and return */
685 .size pci_bar_size, . - pci_bar_size
689 * Called from init in the .xrom case to load the non-prefix code
690 * using the PCI ROM BAR.
692 * Returns with carry flag set on error. All registers saved.
696 * Use PCI BIOS access to config space. The calls take
698 * %ah : 0xb1 %al : function
700 * %di : config space address
701 * %ecx : value to write (for writes)
703 * %ecx : value read (for reads)
705 * CF : error indication
707 * All registers not used for return are preserved.
710 /* Save registers and set up %es for big real mode */
716 /* Check PCI BIOS presence */
717 cmpb $0, pcibios_present
720 /* Load existing PCI ROM BAR */
721 movw $PCIBIOS_READ_DWORD, %ax
722 movw pci_busdevfn, %bx
723 movw $PCI_ROM_ADDRESS, %di
726 /* Maybe it's already enabled? */
727 testb $PCI_ROM_ADDRESS_ENABLE, %cl
729 movb $1, %dl /* Flag indicating no deinit required */
733 /* Determine PCI BAR decode size */
734 1: movl $PCI_ROM_ADDRESS_MASK, %edx
735 call pci_bar_size /* Returns decode size in %ecx */
736 jc err_size_insane /* CF => no ROM BAR, %ecx == ffffffff */
738 /* Check sanity of decode size */
741 shll $9, %eax /* %eax = ROM size */
743 ja err_size_insane /* Insane if decode size < ROM size */
745 jae err_size_insane /* Insane if decode size >= 1MB */
747 /* Find a place to map the BAR
748 * In theory we should examine e820 and all PCI BARs to find a
749 * free region. However, we run at POST when e820 may not be
750 * available, and memory reads of an unmapped location are
751 * de facto standardized to return all-ones. Thus, we can get
752 * away with searching high memory (0xf0000000 and up) on
753 * multiples of the ROM BAR decode size for a sufficiently
754 * large all-ones region.
756 movl %ecx, %edx /* Save ROM BAR size in %edx */
757 movl $0xf0000000, %ebp
759 notl %eax /* %eax = all ones */
764 addr32 repe scasl /* Scan %es:edi for anything not all-ones */
767 testl $0x80000000, %ebp
773 /* Save current BAR value on stack to restore later */
774 movw $PCIBIOS_READ_DWORD, %ax
775 movw $PCI_ROM_ADDRESS, %di
780 movw $PCIBIOS_WRITE_DWORD, %ax
782 orb $PCI_ROM_ADDRESS_ENABLE, %cl
785 xorb %dl, %dl /* %dl = 0 : ROM was not already mapped */
787 /* Check and copy ROM - enter with %dl set to skip unmapping,
788 * %ebp set to mapped ROM BAR address.
789 * We check up to prodstr_separator for equality, since anything past
790 * that may have been modified. Since our check includes the checksum
791 * byte over the whole ROM stub, that should be sufficient.
793 xorb %dh, %dh /* %dh = 0 : ROM did not fail integrity check */
795 /* Verify ROM integrity */
798 movl $prodstr_separator, %ecx
801 incb %dh /* ROM failed integrity check */
802 movl %ecx, %ebp /* Save number of bytes left */
806 /* Print BAR address and indicate whether we mapped it ourselves */
812 movb $( '-' ), %al /* '-' for self-mapped */
814 subb %dl, %al /* '+' = '-' - 2 for BIOS-mapped */
817 /* Copy ROM at %ebp to PMM or highmem block */
819 movl image_source, %edi
820 movzwl real_size, %ecx
823 movl %edi, decompress_to
825 testb %dl, %dl /* Was ROM already mapped? */
828 /* Unmap the ROM by restoring old ROM BAR */
829 movw $PCIBIOS_WRITE_DWORD, %ax
830 movw $PCI_ROM_ADDRESS, %di
841 err_pcibios: /* No PCI BIOS available */
842 movw $load_message_no_pcibios, %si
843 xorl %eax, %eax /* "error code" is zero */
845 err_size_insane: /* BAR has size (%ecx) that is insane */
846 movw $load_message_size_insane, %si
849 err_no_bar: /* No space of sufficient size (%edx) found */
850 movw $load_message_no_bar, %si
853 err_rom_invalid: /* Loaded ROM does not match (%ebp bytes left) */
854 movw $load_message_rom_invalid, %si
855 movzbl romheader_size, %eax
858 decl %eax /* %eax is now byte index of failure */
860 1: /* Error handler - print message at %si and dword in %eax */
869 .size load_from_pci, . - load_from_pci
871 load_message_no_pcibios:
872 .asciz "\nNo PCI BIOS found! "
873 .size load_message_no_pcibios, . - load_message_no_pcibios
875 load_message_size_insane:
876 .asciz "\nROM resource has invalid size "
877 .size load_message_size_insane, . - load_message_size_insane
880 .asciz "\nNo memory hole of sufficient size "
881 .size load_message_no_bar, . - load_message_no_bar
883 load_message_rom_invalid:
884 .asciz "\nLoaded ROM is invalid at "
885 .size load_message_rom_invalid, . - load_message_rom_invalid
887 #endif /* LOAD_ROM_FROM_PCI */
892 * Called via the hooked INT 19 if we detected a non-PnP BIOS. We
893 * attempt to return via the original INT 19 vector (if we were able
899 /* Prompt user to press B to boot */
900 movw $int19_message_prompt, %si
905 movw $int19_message_dots, %si
912 movw $int19_message_done, %si
916 /* Leave keypress in buffer and start gPXE. The keypress will
917 * cause the usual initial Ctrl-B prompt to be skipped.
921 1: /* Try to call original INT 19 vector */
922 movl %cs:orig_int19, %eax
926 2: /* No chained vector: issue INT 18 as a last resort */
928 .size int19_entry, . - int19_entry
931 .size orig_int19, . - orig_int19
933 int19_message_prompt:
934 .asciz "Press N to skip booting from "
935 .size int19_message_prompt, . - int19_message_prompt
938 .size int19_message_dots, . - int19_message_dots
941 .size int19_message_done, . - int19_message_done
943 /* Execute as a boot device
946 exec: /* Set %ds = %cs */
950 #ifdef LOAD_ROM_FROM_PCI
951 /* Don't execute if load was invalid */
952 cmpl $0, decompress_to
958 /* Print message as soon as possible */
962 movw $exec_message, %si
965 /* Store magic word on BIOS stack and remember BIOS %ss:sp */
970 /* Obtain a reasonably-sized temporary stack */
976 movl image_source, %esi
977 movl decompress_to, %edi
979 call install_prealloc
981 /* Set up real-mode stack */
985 /* Jump to .text16 segment */
989 .section ".text16", "awx", @progbits
994 popl %ecx /* discard */
999 /* Restore BIOS stack */
1003 /* Check magic word on BIOS stack */
1005 cmpl $STACK_MAGIC, %eax
1007 /* BIOS stack OK: return to caller */
1009 1: /* BIOS stack corrupt: use INT 18 */
1014 .asciz " starting execution\n"
1015 .size exec_message, . - exec_message
1017 /* Wait for key press specified by %bl (masked by %bh)
1019 * Used by init and INT19 code when prompting user. If the specified
1020 * key is pressed, it is left in the keyboard buffer.
1022 * Returns with ZF set iff specified key is pressed.
1025 /* Preserve registers */
1028 1: /* Empty the keyboard buffer before waiting for input */
1035 2: /* Wait for a key press */
1036 movw $ROM_BANNER_TIMEOUT, %cx
1038 js 99f /* Exit with ZF clear */
1039 /* Wait for timer tick to be updated */
1041 /* Check to see if a key was pressed */
1045 /* Check to see if key was the specified key */
1048 je 99f /* Exit with ZF set */
1049 /* Not the specified key: remove from buffer and stop waiting */
1053 popfw /* Exit with ZF clear */
1054 99: /* Restore registers and return */
1058 .size wait_for_key, . - wait_for_key
1060 /* Wait for timer tick
1062 * Used by wait_for_key
1069 movl %fs:(0x6c), %eax
1074 cmpl %fs:(0x6c), %eax
1079 .size wait_for_tick, . - wait_for_tick