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 PMM_ALLOCATE 0x0000
18 #define PMM_FIND 0x0001
19 #define PMM_HANDLE_BASE ( ( ( 'F' - 'A' + 1 ) << 26 ) + \
20 ( ( 'E' - 'A' + 1 ) << 21 ) + \
21 ( ( 'N' - 'A' + 1 ) << 16 ) )
22 #define PMM_HANDLE_BASE_IMAGE_SOURCE \
23 ( PMM_HANDLE_BASE | 0x00001000 )
24 #define PMM_HANDLE_BASE_DECOMPRESS_TO \
25 ( PMM_HANDLE_BASE | 0x00002000 )
27 /* ROM banner timeout. Based on the configurable BANNER_TIMEOUT in
28 * config.h, but converted to a number of (18Hz) timer ticks, and
29 * doubled to allow for BIOSes that switch video modes immediately
30 * beforehand, so rendering the message almost invisible to the user.
32 #define ROM_BANNER_TIMEOUT ( 2 * ( 18 * BANNER_TIMEOUT ) / 10 )
34 /* Allow payload to be excluded from ROM size
36 #if ROMPREFIX_EXCLUDE_PAYLOAD
37 #define ZINFO_TYPE_ADxB "ADHB"
38 #define ZINFO_TYPE_ADxW "ADHW"
40 #define ZINFO_TYPE_ADxB "ADDB"
41 #define ZINFO_TYPE_ADxW "ADDW"
47 .section ".prefix", "ax", @progbits
51 .word 0xAA55 /* BIOS extension signature */
52 romheader_size: .byte 0 /* Size in 512-byte blocks */
53 jmp init /* Initialisation vector */
62 .size romheader, . - romheader
64 .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
65 .ascii ZINFO_TYPE_ADxB
72 .long _build_id /* Randomly-generated build ID */
75 .ascii "PCIR" /* Signature */
76 .word pci_vendor_id /* Vendor identification */
77 .word pci_device_id /* Device identification */
78 .word 0x0000 /* Device list pointer */
79 .word pciheader_len /* PCI data structure length */
80 .byte 0x03 /* PCI data structure revision */
81 .byte 0x02, 0x00, 0x00 /* Class code */
82 pciheader_image_length:
83 .word 0 /* Image length */
84 .word 0x0001 /* Revision level */
85 .byte 0x00 /* Code type */
86 .byte 0x80 /* Last image indicator */
87 pciheader_runtime_length:
88 .word 0 /* Maximum run-time image length */
89 .word 0x0000 /* Configuration utility code header */
90 .word 0x0000 /* DMTF CLP entry point */
91 .equ pciheader_len, . - pciheader
92 .size pciheader, . - pciheader
94 .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
95 .ascii ZINFO_TYPE_ADxW
96 .long pciheader_image_length
99 .ascii ZINFO_TYPE_ADxW
100 .long pciheader_runtime_length
106 .ascii "$PnP" /* Signature */
107 .byte 0x01 /* Structure revision */
108 .byte ( pnpheader_len / 16 ) /* Length (in 16 byte increments) */
109 .word 0x0000 /* Offset of next header */
110 .byte 0x00 /* Reserved */
111 .byte 0x00 /* Checksum */
112 .long 0x00000000 /* Device identifier */
113 .word mfgstr /* Manufacturer string */
114 .word prodstr /* Product name */
115 .byte 0x02 /* Device base type code */
116 .byte 0x00 /* Device sub-type code */
117 .byte 0x00 /* Device interface type code */
118 .byte 0xf4 /* Device indicator */
119 .word 0x0000 /* Boot connection vector */
120 .word 0x0000 /* Disconnect vector */
121 .word bev_entry /* Boot execution vector */
122 .word 0x0000 /* Reserved */
123 .word 0x0000 /* Static resource information vector*/
124 .equ pnpheader_len, . - pnpheader
125 .size pnpheader, . - pnpheader
127 /* Manufacturer string */
129 .asciz "http://etherboot.org"
130 .size mfgstr, . - mfgstr
134 * Defaults to PRODUCT_SHORT_NAME. If the ROM image is writable at
135 * initialisation time, it will be filled in to include the PCI
136 * bus:dev.fn number of the card as well.
139 .ascii PRODUCT_SHORT_NAME
144 .asciz "xx:xx.x)" /* Filled in by init code */
145 .size prodstr, . - prodstr
150 .ascii "UNDI" /* Signature */
151 .byte undiheader_len /* Length of structure */
152 .byte 0 /* Checksum */
153 .byte 0 /* Structure revision */
154 .byte 0,1,2 /* PXE version: 2.1.0 */
155 .word undiloader /* Offset to loader routine */
156 .word _data16_memsz /* Stack segment size */
157 .word _data16_memsz /* Data segment size */
158 .word _text16_memsz /* Code segment size */
159 .ascii "PCIR" /* Bus type */
160 .equ undiheader_len, . - undiheader
161 .size undiheader, . - undiheader
163 /* Initialisation (called once during POST)
165 * Determine whether or not this is a PnP system via a signature
166 * check. If it is PnP, return to the PnP BIOS indicating that we are
167 * a boot-capable device; the BIOS will call our boot execution vector
168 * if it wants to boot us. If it is not PnP, hook INT 19.
171 /* Preserve registers, clear direction flag, set %ds=%cs */
181 /* Shuffle some registers around. We need %di available for
182 * the print_xxx functions, and in a register that's
183 * addressable from %es, so shuffle as follows:
185 * %di (pointer to PnP structure) => %bx
186 * %bx (runtime segment address, for PCI 3.0) => %gs
191 /* Store PCI bus:dev.fn address */
192 movw %ax, init_pci_busdevfn
194 /* Print message as early as possible */
195 movw $init_message, %si
198 call print_pci_busdevfn
200 /* Fill in product name string, if possible */
201 movw $prodstr_pci_id, %di
202 call print_pci_busdevfn
203 movb $( ' ' ), prodstr_separator
205 /* Print segment address */
212 /* Check for PCI BIOS version */
220 cmpl $PCI_SIGNATURE, %edx
224 movw $init_message_pci, %si
228 call print_hex_nibble
235 /* PCI >=3.0: leave %gs as-is if sane */
237 cmpw $0xa000, %ax /* Insane if %gs < 0xa000 */
239 movw %cs, %bx /* Sane if %cs == %gs */
242 movzbw romheader_size, %cx /* Sane if %cs+len <= %gs */
247 movw %cs, %bx /* Sane if %gs+len <= %cs */
251 pci3_insane: /* PCI 3.0 with insane %gs value: print error and ignore %gs */
257 /* PCI <3.0: set %gs (runtime segment) = %cs (init-time segment) */
264 /* Check for PnP BIOS. Although %es:di should point to the
265 * PnP BIOS signature on entry, some BIOSes fail to do this.
267 movw $( 0xf000 - 1 ), %bx
272 cmpl $PNP_SIGNATURE, %es:0
281 /* Is PnP: print PnP message */
282 movw $init_message_pnp, %si
286 no_pnp: /* Not PnP-compliant - hook INT 19 */
287 movw $init_message_int19, %si
292 pushl %es:( 0x19 * 4 )
294 pushw %gs /* %gs contains runtime %cs */
296 popl %es:( 0x19 * 4 )
300 movw $( 0xe000 - 1 ), %bx
305 cmpl $PMM_SIGNATURE, %es:0
314 /* PMM found: print PMM message */
315 movw $init_message_pmm, %si
318 /* We have PMM and so a 1kB stack: preserve whole registers */
320 /* Allocate image source PMM block */
321 movzwl image_source_len_pgh, %ecx
322 movl $PMM_HANDLE_BASE_IMAGE_SOURCE, %ebx
323 movw $get_pmm_image_source, %bp
325 movl %esi, image_source
327 /* Copy ROM to image source PMM block */
333 movzbl romheader_size, %ecx
335 addr32 rep movsb /* PMM presence implies flat real mode */
338 movb shrunk_rom_size, %al
339 movb %al, romheader_size
340 1: /* Allocate decompression PMM block. Round up the size to the
341 * nearest 128kB and use the size within the PMM handle; this
342 * allows the same decompression area to be shared between
343 * multiple gPXE ROMs even with differing build IDs
345 movl $_textdata_memsz_pgh, %ecx
346 addl $0x00001fff, %ecx
347 andl $0xffffe000, %ecx
350 orl $PMM_HANDLE_BASE_DECOMPRESS_TO, %ebx
351 movw $get_pmm_decompress_to, %bp
353 movl %esi, decompress_to
354 /* Restore registers */
358 /* Update checksum */
361 movzbw romheader_size, %cx
368 /* Copy self to option ROM space. Required for PCI3.0, which
369 * loads us to a temporary location in low memory. Will be a
370 * no-op for lower PCI versions.
377 movzbw romheader_size, %cx
384 /* Prompt for POST-time shell */
385 movw $init_message_prompt, %si
390 movw $init_message_dots, %si
392 /* Wait for Ctrl-B */
399 movw $init_message_done, %si
403 /* Ctrl-B was pressed: invoke gPXE. The keypress will be
404 * picked up by the initial shell prompt, and we will drop
407 stc /* Inhibit relocation */
411 /* Restore registers */
418 /* Indicate boot capability to PnP BIOS, if present */
423 /* Attempt to find or allocate PMM block
426 * %ecx : size of block to allocate, in paragraphs
427 * %ebx : PMM handle base
428 * %bp : routine to check acceptability of found blocks
429 * %es:0000 : PMM structure
432 * %esi : allocated block address, or zero (with CF set) if allocation failed
435 /* Preserve registers */
440 /* Try to find existing block */
441 pushl %ebx /* PMM handle */
450 /* Block found - check acceptability */
453 /* Block not acceptable - increment handle and retry */
457 /* Block not found - try to allocate new block */
458 pushw $0x0002 /* Extended memory */
459 pushl %ebx /* PMM handle */
460 pushl %ecx /* Length */
467 movw $'+', %di /* Indicate allocation attempt */
472 /* Print block address */
480 /* Restore registers and return */
484 .size get_pmm, . - get_pmm
486 /* Check acceptability of image source block */
487 get_pmm_image_source:
492 cmpl %es:build_id(%esi), %eax
497 .size get_pmm_image_source, . - get_pmm_image_source
499 /* Check acceptability of decompression block */
500 get_pmm_decompress_to:
503 .size get_pmm_decompress_to, . - get_pmm_decompress_to
506 * Note to hardware vendors:
508 * If you wish to brand this boot ROM, please do so by defining the
509 * strings PRODUCT_NAME and PRODUCT_SHORT_NAME in config/general.h.
511 * While nothing in the GPL prevents you from removing all references
512 * to gPXE or http://etherboot.org, we prefer you not to do so.
514 * If you have an OEM-mandated branding requirement that cannot be
515 * satisfied simply by defining PRODUCT_NAME and PRODUCT_SHORT_NAME,
518 * [ Including an ASCII NUL in PRODUCT_NAME is considered to be
519 * bypassing the spirit of this request! ]
525 .asciz "gPXE (http://etherboot.org) - "
526 .size init_message, . - init_message
529 .size init_message_pci, . - init_message_pci
532 .size init_message_pnp, . - init_message_pnp
535 .size init_message_pmm, . - init_message_pmm
538 .size init_message_int19, . - init_message_int19
540 .asciz "\nPress Ctrl-B to configure "
541 .size init_message_prompt, . - init_message_prompt
544 .size init_message_dots, . - init_message_dots
547 .size init_message_done, . - init_message_done
554 .size init_pci_busdevfn, . - init_pci_busdevfn
558 * May be either zero (indicating to use option ROM space as source),
559 * or within a PMM-allocated block.
564 .size image_source, . - image_source
566 /* Image source area length (in paragraphs)
569 image_source_len_pgh:
571 .size image_source_len_pgh, . - image_source_len_pgh
572 .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
574 .long image_source_len_pgh
579 /* Shrunk ROM size (in 512-byte sectors)
584 .size shrunk_rom_size, . - shrunk_rom_size
585 .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
587 .long shrunk_rom_size
592 /* Temporary decompression area
594 * May be either zero (indicating to use default decompression area in
595 * high memory), or within a PMM-allocated block.
600 .size decompress_to, . - decompress_to
602 /* Boot Execution Vector entry point
604 * Called by the PnP BIOS when it wants to boot us.
607 clc /* Allow relocation */
611 .size bev_entry, . - bev_entry
615 * Called via the hooked INT 19 if we detected a non-PnP BIOS. We
616 * attempt to return via the original INT 19 vector (if we were able
622 /* Prompt user to press B to boot */
623 movw $int19_message_prompt, %si
628 movw $int19_message_dots, %si
635 movw $int19_message_done, %si
639 /* Leave keypress in buffer and start gPXE. The keypress will
640 * cause the usual initial Ctrl-B prompt to be skipped.
642 clc /* Allow relocation */
645 1: /* Try to call original INT 19 vector */
646 movl %cs:orig_int19, %eax
650 2: /* No chained vector: issue INT 18 as a last resort */
652 .size int19_entry, . - int19_entry
655 .size orig_int19, . - orig_int19
657 int19_message_prompt:
658 .asciz "Press N to skip booting from "
659 .size int19_message_prompt, . - int19_message_prompt
662 .size int19_message_dots, . - int19_message_dots
665 .size int19_message_done, . - int19_message_done
667 /* Execute as a boot device
670 exec: /* Set %ds = %cs */
674 /* Preserve state of CF */
677 /* Print message as soon as possible */
681 movw $exec_message_pre_install, %si
684 /* Store magic word on BIOS stack and remember BIOS %ss:sp */
689 /* Obtain a reasonably-sized temporary stack */
699 movl image_source, %esi
700 movl decompress_to, %edi
701 call install_prealloc
703 /* Print message indicating successful installation */
704 movw $exec_message_post_install, %si
708 /* Set up real-mode stack */
712 /* Jump to .text16 segment */
716 .section ".text16", "awx", @progbits
721 popl %ecx /* discard */
726 /* Restore BIOS stack */
730 /* Check magic word on BIOS stack */
732 cmpl $STACK_MAGIC, %eax
734 /* BIOS stack OK: return to caller */
736 1: /* BIOS stack corrupt: use INT 18 */
740 exec_message_pre_install:
741 .asciz " starting execution..."
742 .size exec_message_pre_install, . - exec_message_pre_install
743 exec_message_post_install:
745 .size exec_message_post_install, . - exec_message_post_install
747 /* Wait for key press specified by %bl (masked by %bh)
749 * Used by init and INT19 code when prompting user. If the specified
750 * key is pressed, it is left in the keyboard buffer.
752 * Returns with ZF set iff specified key is pressed.
755 /* Preserve registers */
758 1: /* Empty the keyboard buffer before waiting for input */
765 2: /* Wait for a key press */
766 movw $ROM_BANNER_TIMEOUT, %cx
768 js 99f /* Exit with ZF clear */
769 /* Wait for timer tick to be updated */
771 /* Check to see if a key was pressed */
775 /* Check to see if key was the specified key */
778 je 99f /* Exit with ZF set */
779 /* Not the specified key: remove from buffer and stop waiting */
783 popfw /* Exit with ZF clear */
784 99: /* Restore registers and return */
788 .size wait_for_key, . - wait_for_key
790 /* Wait for timer tick
792 * Used by wait_for_key
799 movl %fs:(0x6c), %eax
804 cmpl %fs:(0x6c), %eax
809 .size wait_for_tick, . - wait_for_tick