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 )
31 .section ".prefix", "ax", @progbits
35 .word 0xAA55 /* BIOS extension signature */
36 romheader_size: .byte 0 /* Size in 512-byte blocks */
37 jmp init /* Initialisation vector */
46 .size romheader, . - romheader
48 .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
56 .ascii "PCIR" /* Signature */
57 .word pci_vendor_id /* Vendor identification */
58 .word pci_device_id /* Device identification */
59 .word 0x0000 /* Device list pointer */
60 .word pciheader_len /* PCI data structure length */
61 .byte 0x03 /* PCI data structure revision */
62 .byte 0x02, 0x00, 0x00 /* Class code */
63 pciheader_image_length:
64 .word 0 /* Image length */
65 .word 0x0001 /* Revision level */
66 .byte 0x00 /* Code type */
67 .byte 0x80 /* Last image indicator */
68 pciheader_runtime_length:
69 .word 0 /* Maximum run-time image length */
70 .word 0x0000 /* Configuration utility code header */
71 .word 0x0000 /* DMTF CLP entry point */
72 .equ pciheader_len, . - pciheader
73 .size pciheader, . - pciheader
75 .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
77 .long pciheader_image_length
81 .long pciheader_runtime_length
87 .ascii "$PnP" /* Signature */
88 .byte 0x01 /* Structure revision */
89 .byte ( pnpheader_len / 16 ) /* Length (in 16 byte increments) */
90 .word 0x0000 /* Offset of next header */
91 .byte 0x00 /* Reserved */
92 .byte 0x00 /* Checksum */
93 .long 0x00000000 /* Device identifier */
94 .word mfgstr /* Manufacturer string */
95 .word prodstr /* Product name */
96 .byte 0x02 /* Device base type code */
97 .byte 0x00 /* Device sub-type code */
98 .byte 0x00 /* Device interface type code */
99 .byte 0xf4 /* Device indicator */
100 .word 0x0000 /* Boot connection vector */
101 .word 0x0000 /* Disconnect vector */
102 .word bev_entry /* Boot execution vector */
103 .word 0x0000 /* Reserved */
104 .word 0x0000 /* Static resource information vector*/
105 .equ pnpheader_len, . - pnpheader
106 .size pnpheader, . - pnpheader
108 /* Manufacturer string */
110 .asciz "http://etherboot.org"
111 .size mfgstr, . - mfgstr
115 * Defaults to PRODUCT_SHORT_NAME. If the ROM image is writable at
116 * initialisation time, it will be filled in to include the PCI
117 * bus:dev.fn number of the card as well.
120 .ascii PRODUCT_SHORT_NAME
125 .asciz "xx:xx.x)" /* Filled in by init code */
126 .size prodstr, . - prodstr
131 .ascii "UNDI" /* Signature */
132 .byte undiheader_len /* Length of structure */
133 .byte 0 /* Checksum */
134 .byte 0 /* Structure revision */
135 .byte 0,1,2 /* PXE version: 2.1.0 */
136 .word undiloader /* Offset to loader routine */
137 .word _data16_memsz /* Stack segment size */
138 .word _data16_memsz /* Data segment size */
139 .word _text16_memsz /* Code segment size */
140 .ascii "PCIR" /* Bus type */
141 .equ undiheader_len, . - undiheader
142 .size undiheader, . - undiheader
144 /* Initialisation (called once during POST)
146 * Determine whether or not this is a PnP system via a signature
147 * check. If it is PnP, return to the PnP BIOS indicating that we are
148 * a boot-capable device; the BIOS will call our boot execution vector
149 * if it wants to boot us. If it is not PnP, hook INT 19.
152 /* Preserve registers, clear direction flag, set %ds=%cs */
162 /* Shuffle some registers around. We need %di available for
163 * the print_xxx functions, and in a register that's
164 * addressable from %es, so shuffle as follows:
166 * %di (pointer to PnP structure) => %bx
167 * %bx (runtime segment address, for PCI 3.0) => %gs
172 /* Print message as early as possible */
173 movw $init_message, %si
176 call print_pci_busdevfn
178 /* Fill in product name string, if possible */
179 movw $prodstr_pci_id, %di
180 call print_pci_busdevfn
181 movb $( ' ' ), prodstr_separator
183 /* Print segment address */
190 /* Check for PCI BIOS version */
198 cmpl $PCI_SIGNATURE, %edx
202 movw $init_message_pci, %si
206 call print_hex_nibble
213 /* PCI >=3.0: leave %gs as-is if sane */
215 cmpw $0xa000, %ax /* Insane if %gs < 0xa000 */
217 movw %cs, %bx /* Sane if %cs == %gs */
220 movzbw romheader_size, %cx /* Sane if %cs+len <= %gs */
225 movw %cs, %bx /* Sane if %gs+len <= %cs */
229 pci3_insane: /* PCI 3.0 with insane %gs value: print error and ignore %gs */
235 /* PCI <3.0: set %gs (runtime segment) = %cs (init-time segment) */
242 /* Check for PnP BIOS. Although %es:di should point to the
243 * PnP BIOS signature on entry, some BIOSes fail to do this.
245 movw $( 0xf000 - 1 ), %bx
250 cmpl $PNP_SIGNATURE, %es:0
259 /* Is PnP: print PnP message */
260 movw $init_message_pnp, %si
264 pushw %es:0x1b /* Real-mode data segment */
265 pushw %ds /* &(bbs_version) */
267 pushw $PNP_GET_BBS_VERSION
272 no_pnp: /* Not PnP-compliant - therefore cannot be BBS-compliant */
273 no_bbs: /* Not BBS-compliant - must hook INT 19 */
274 movw $init_message_int19, %si
279 pushl %es:( 0x19 * 4 )
281 pushw %gs /* %gs contains runtime %cs */
283 popl %es:( 0x19 * 4 )
285 got_bbs: /* BBS compliant - no need to hook INT 19 */
286 movw $init_message_bbs, %si
292 movw $( 0xe000 - 1 ), %bx
297 cmpl $PMM_SIGNATURE, %es:0
306 /* PMM found: print PMM message */
307 movw $init_message_pmm, %si
310 /* We have PMM and so a 1kB stack: preserve upper register halves */
312 /* Calculate required allocation size in %esi */
313 movzbl romheader_size, %eax
315 addl $_textdata_memsz, %eax
316 orw $0xffff, %ax /* Ensure allocation size is at least 64kB */
318 subw $15, %cx /* Round up and convert to 64kB count */
322 /* Try to allocate block via PMM */
323 pushw $0x0006 /* Aligned, extended memory */
324 pushl $0xffffffff /* No handle */
327 pushl %eax /* Allocation size in paragraphs */
331 /* Abort if allocation fails */
332 testw %dx, %dx /* %ax==0 even on success, since align>=64kB */
334 /* If block has A20==1, free block and try again with twice
335 * the allocation size (and hence alignment).
341 pushw $PMM_DEALLOCATE
346 got_pmm: /* PMM allocation succeeded */
347 movw %dx, ( image_source + 2 )
355 /* Copy ROM to PMM block */
358 movl image_source, %edi
360 movzbl romheader_size, %ecx
362 addr32 rep movsb /* PMM presence implies flat real mode */
363 movl %edi, decompress_to
365 movb $_prefix_memsz_sect, romheader_size
367 /* Restore upper register halves */
371 /* Update checksum */
374 movzbw romheader_size, %cx
381 /* Copy self to option ROM space. Required for PCI3.0, which
382 * loads us to a temporary location in low memory. Will be a
383 * no-op for lower PCI versions.
390 movzbw romheader_size, %cx
397 /* Prompt for POST-time shell */
398 movw $init_message_prompt, %si
403 movw $init_message_dots, %si
405 /* Wait for Ctrl-B */
412 movw $init_message_done, %si
416 /* Ctrl-B was pressed: invoke gPXE. The keypress will be
417 * picked up by the initial shell prompt, and we will drop
423 /* Restore registers */
430 /* Indicate boot capability to PnP BIOS, if present */
436 * Note to hardware vendors:
438 * If you wish to brand this boot ROM, please do so by defining the
439 * strings PRODUCT_NAME and PRODUCT_SHORT_NAME in config/general.h.
441 * While nothing in the GPL prevents you from removing all references
442 * to gPXE or http://etherboot.org, we prefer you not to do so.
444 * If you have an OEM-mandated branding requirement that cannot be
445 * satisfied simply by defining PRODUCT_NAME and PRODUCT_SHORT_NAME,
448 * [ Including an ASCII NUL in PRODUCT_NAME is considered to be
449 * bypassing the spirit of this request! ]
455 .asciz "gPXE (http://etherboot.org) - "
456 .size init_message, . - init_message
459 .size init_message_pci, . - init_message_pci
462 .size init_message_pnp, . - init_message_pnp
465 .size init_message_bbs, . - init_message_bbs
468 .size init_message_pmm, . - init_message_pmm
471 .size init_message_int19, . - init_message_int19
473 .asciz "\nPress Ctrl-B to configure "
474 .size init_message_prompt, . - init_message_prompt
477 .size init_message_dots, . - init_message_dots
480 .size init_message_done, . - init_message_done
482 /* ROM image location
484 * May be either within option ROM space, or within PMM-allocated block.
489 .size image_source, . - image_source
491 /* Temporary decompression area
493 * May be either at HIGHMEM_LOADPOINT, or within PMM-allocated block.
497 .long HIGHMEM_LOADPOINT
498 .size decompress_to, . - decompress_to
502 * Filled in by BBS BIOS. We ignore the value.
506 .size bbs_version, . - bbs_version
508 /* Boot Execution Vector entry point
510 * Called by the PnP BIOS when it wants to boot us.
516 .size bev_entry, . - bev_entry
520 * Called via the hooked INT 19 if we detected a non-PnP BIOS. We
521 * attempt to return via the original INT 19 vector (if we were able
527 /* Prompt user to press B to boot */
528 movw $int19_message_prompt, %si
533 movw $int19_message_dots, %si
540 movw $int19_message_done, %si
544 /* Leave keypress in buffer and start gPXE. The keypress will
545 * cause the usual initial Ctrl-B prompt to be skipped.
549 1: /* Try to call original INT 19 vector */
550 movl %cs:orig_int19, %eax
554 2: /* No chained vector: issue INT 18 as a last resort */
556 .size int19_entry, . - int19_entry
559 .size orig_int19, . - orig_int19
561 int19_message_prompt:
562 .asciz "Press N to skip booting from "
563 .size int19_message_prompt, . - int19_message_prompt
566 .size int19_message_dots, . - int19_message_dots
569 .size int19_message_done, . - int19_message_done
571 /* Execute as a boot device
574 exec: /* Set %ds = %cs */
578 /* Print message as soon as possible */
582 movw $exec_message, %si
585 /* Store magic word on BIOS stack and remember BIOS %ss:sp */
590 /* Obtain a reasonably-sized temporary stack */
596 movl image_source, %esi
597 movl decompress_to, %edi
599 call install_prealloc
601 /* Set up real-mode stack */
605 /* Jump to .text16 segment */
609 .section ".text16", "awx", @progbits
614 popl %ecx /* discard */
619 /* Restore BIOS stack */
623 /* Check magic word on BIOS stack */
625 cmpl $STACK_MAGIC, %eax
627 /* BIOS stack OK: return to caller */
629 1: /* BIOS stack corrupt: use INT 18 */
634 .asciz " starting execution\n"
635 .size exec_message, . - exec_message
637 /* Wait for key press specified by %bl (masked by %bh)
639 * Used by init and INT19 code when prompting user. If the specified
640 * key is pressed, it is left in the keyboard buffer.
642 * Returns with ZF set iff specified key is pressed.
645 /* Preserve registers */
648 1: /* Empty the keyboard buffer before waiting for input */
655 2: /* Wait for a key press */
656 movw $ROM_BANNER_TIMEOUT, %cx
658 js 99f /* Exit with ZF clear */
659 /* Wait for timer tick to be updated */
661 /* Check to see if a key was pressed */
665 /* Check to see if key was the specified key */
668 je 99f /* Exit with ZF set */
669 /* Not the specified key: remove from buffer and stop waiting */
673 popfw /* Exit with ZF clear */
674 99: /* Restore registers and return */
678 .size wait_for_key, . - wait_for_key
680 /* Wait for timer tick
682 * Used by wait_for_key
689 movl %fs:(0x6c), %eax
694 cmpl %fs:(0x6c), %eax
699 .size wait_for_tick, . - wait_for_tick