You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

romprefix.S 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  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.
  4. *
  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.
  7. */
  8. #include <config/general.h>
  9. #define PNP_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'n' << 16 ) + ( 'P' << 24 ) )
  10. #define PMM_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'M' << 16 ) + ( 'M' << 24 ) )
  11. #define PCI_SIGNATURE ( 'P' + ( 'C' << 8 ) + ( 'I' << 16 ) + ( ' ' << 24 ) )
  12. #define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) )
  13. #define PNP_GET_BBS_VERSION 0x60
  14. #define PMM_ALLOCATE 0x0000
  15. /* ROM banner timeout. Based on the configurable BANNER_TIMEOUT in
  16. * config.h, but converted to a number of (18Hz) timer ticks, and
  17. * doubled to allow for BIOSes that switch video modes immediately
  18. * beforehand, so rendering the message almost invisible to the user.
  19. */
  20. #define ROM_BANNER_TIMEOUT ( 2 * ( 18 * BANNER_TIMEOUT ) / 10 )
  21. .text
  22. .code16
  23. .arch i386
  24. .section ".prefix", "ax", @progbits
  25. .org 0x00
  26. romheader:
  27. .word 0xAA55 /* BIOS extension signature */
  28. romheader_size: .byte _filesz_sect /* Size in 512-byte blocks */
  29. jmp init /* Initialisation vector */
  30. checksum:
  31. .byte 0
  32. .org 0x16
  33. .word undiheader
  34. .org 0x18
  35. .word pciheader
  36. .org 0x1a
  37. .word pnpheader
  38. .size romheader, . - romheader
  39. .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
  40. .ascii "SUBB"
  41. .long romheader_size
  42. .long 512
  43. .long 0
  44. .previous
  45. pciheader:
  46. .ascii "PCIR" /* Signature */
  47. .word pci_vendor_id /* Vendor identification */
  48. .word pci_device_id /* Device identification */
  49. .word 0x0000 /* Device list pointer */
  50. .word pciheader_len /* PCI data structure length */
  51. .byte 0x03 /* PCI data structure revision */
  52. .byte 0x02, 0x00, 0x00 /* Class code */
  53. pciheader_image_length:
  54. .word _filesz_sect /* Image length */
  55. .word 0x0001 /* Revision level */
  56. .byte 0x00 /* Code type */
  57. .byte 0x80 /* Last image indicator */
  58. pciheader_runtime_length:
  59. .word _filesz_sect /* Maximum run-time image length */
  60. .word 0x0000 /* Configuration utility code header */
  61. .word 0x0000 /* DMTF CLP entry point */
  62. .equ pciheader_len, . - pciheader
  63. .size pciheader, . - pciheader
  64. .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
  65. .ascii "SUBW"
  66. .long pciheader_image_length
  67. .long 512
  68. .long 0
  69. .ascii "SUBW"
  70. .long pciheader_runtime_length
  71. .long 512
  72. .long 0
  73. .previous
  74. pnpheader:
  75. .ascii "$PnP" /* Signature */
  76. .byte 0x01 /* Structure revision */
  77. .byte ( pnpheader_len / 16 ) /* Length (in 16 byte increments) */
  78. .word 0x0000 /* Offset of next header */
  79. .byte 0x00 /* Reserved */
  80. .byte 0x00 /* Checksum */
  81. .long 0x00000000 /* Device identifier */
  82. .word mfgstr /* Manufacturer string */
  83. .word prodstr /* Product name */
  84. .byte 0x02 /* Device base type code */
  85. .byte 0x00 /* Device sub-type code */
  86. .byte 0x00 /* Device interface type code */
  87. .byte 0xf4 /* Device indicator */
  88. .word 0x0000 /* Boot connection vector */
  89. .word 0x0000 /* Disconnect vector */
  90. .word bev_entry /* Boot execution vector */
  91. .word 0x0000 /* Reserved */
  92. .word 0x0000 /* Static resource information vector*/
  93. .equ pnpheader_len, . - pnpheader
  94. .size pnpheader, . - pnpheader
  95. /* Manufacturer string */
  96. mfgstr:
  97. .asciz "http://etherboot.org"
  98. .size mfgstr, . - mfgstr
  99. /* Product string
  100. *
  101. * Defaults to "gPXE". If the ROM image is writable at initialisation
  102. * time, it will be filled in to include the PCI bus:dev.fn number of
  103. * the card as well.
  104. */
  105. prodstr:
  106. .ascii "gPXE"
  107. prodstr_separator:
  108. .byte 0
  109. .ascii "(PCI "
  110. prodstr_pci_id:
  111. .asciz "xx:xx.x)" /* Filled in by init code */
  112. .size prodstr, . - prodstr
  113. .globl undiheader
  114. undiheader:
  115. .ascii "UNDI" /* Signature */
  116. .byte undiheader_len /* Length of structure */
  117. .byte 0 /* Checksum */
  118. .byte 0 /* Structure revision */
  119. .byte 0,1,2 /* PXE version: 2.1.0 */
  120. .word undiloader /* Offset to loader routine */
  121. .word _data16_memsz /* Stack segment size */
  122. .word _data16_memsz /* Data segment size */
  123. .word _text16_memsz /* Code segment size */
  124. .ascii "PCIR" /* Bus type */
  125. .equ undiheader_len, . - undiheader
  126. .size undiheader, . - undiheader
  127. /* Initialisation (called once during POST)
  128. *
  129. * Determine whether or not this is a PnP system via a signature
  130. * check. If it is PnP, return to the PnP BIOS indicating that we are
  131. * a boot-capable device; the BIOS will call our boot execution vector
  132. * if it wants to boot us. If it is not PnP, hook INT 19.
  133. */
  134. init:
  135. /* Preserve registers, clear direction flag, set %ds=%cs */
  136. pushaw
  137. pushw %ds
  138. pushw %es
  139. pushw %fs
  140. pushw %gs
  141. cld
  142. pushw %cs
  143. popw %ds
  144. /* Shuffle some registers around. We need %di available for
  145. * the print_xxx functions, and in a register that's
  146. * addressable from %es, so shuffle as follows:
  147. *
  148. * %di (pointer to PnP structure) => %bx
  149. * %bx (runtime segment address, for PCI 3.0) => %gs
  150. */
  151. movw %bx, %gs
  152. movw %di, %bx
  153. /* Print message as early as possible */
  154. movw $init_message, %si
  155. xorw %di, %di
  156. call print_message
  157. call print_pci_busdevfn
  158. /* Fill in product name string, if possible */
  159. movw $prodstr_pci_id, %di
  160. call print_pci_busdevfn
  161. movb $' ', prodstr_separator
  162. /* Print segment address */
  163. movb $' ', %al
  164. xorw %di, %di
  165. call print_character
  166. movw %cs, %ax
  167. call print_hex_word
  168. /* Check for PCI BIOS version */
  169. pushl %ebx
  170. pushl %edx
  171. pushl %edi
  172. stc
  173. movw $0xb101, %ax
  174. int $0x1a
  175. jc 1f
  176. cmpl $PCI_SIGNATURE, %edx
  177. jne 1f
  178. testb %ah, %ah
  179. jnz 1f
  180. movw $init_message_pci, %si
  181. xorw %di, %di
  182. call print_message
  183. movb %bh, %al
  184. call print_hex_nibble
  185. movb $'.', %al
  186. call print_character
  187. movb %bl, %al
  188. call print_hex_byte
  189. cmpb $3, %bh
  190. jb 1f
  191. /* PCI >=3.0: leave %gs as-is if sane */
  192. movw %gs, %ax
  193. cmpw $0xc000, %ax
  194. jae 2f
  195. /* PCI 3.0 with insane %gs value: print error and ignore %gs */
  196. movb $'!', %al
  197. call print_character
  198. movw %gs, %ax
  199. call print_hex_word
  200. 1: /* PCI <3.0: set %gs (runtime segment) = %cs (init-time segment) */
  201. pushw %cs
  202. popw %gs
  203. 2: popl %edi
  204. popl %edx
  205. popl %ebx
  206. /* Check for PnP BIOS */
  207. testw $0x0f, %bx /* PnP signature must be aligned - bochs */
  208. jnz no_bbs /* uses unalignment to indicate 'fake' PnP. */
  209. cmpl $PNP_SIGNATURE, %es:0(%bx)
  210. jne no_bbs
  211. /* Is PnP: print PnP message */
  212. movw $init_message_pnp, %si
  213. xorw %di, %di
  214. call print_message
  215. /* Check for BBS */
  216. pushw %es:0x1b(%bx) /* Real-mode data segment */
  217. pushw %ds /* &(bbs_version) */
  218. pushw $bbs_version
  219. pushw $PNP_GET_BBS_VERSION
  220. lcall *%es:0xd(%bx)
  221. addw $8, %sp
  222. testw %ax, %ax
  223. je got_bbs
  224. no_bbs: /* Not BBS-compliant - must hook INT 19 */
  225. movw $init_message_int19, %si
  226. xorw %di, %di
  227. call print_message
  228. xorw %ax, %ax
  229. movw %ax, %es
  230. pushl %es:( 0x19 * 4 )
  231. popl orig_int19
  232. pushw %gs /* %gs contains runtime %cs */
  233. pushw $int19_entry
  234. popl %es:( 0x19 * 4 )
  235. jmp bbs_done
  236. got_bbs: /* BBS compliant - no need to hook INT 19 */
  237. movw $init_message_bbs, %si
  238. xorw %di, %di
  239. call print_message
  240. bbs_done:
  241. /* Check for PMM */
  242. movw $( 0xe000 - 1 ), %bx
  243. pmm_scan:
  244. incw %bx
  245. jz no_pmm
  246. movw %bx, %es
  247. cmpl $PMM_SIGNATURE, %es:0
  248. jne pmm_scan
  249. xorw %dx, %dx
  250. xorw %si, %si
  251. movzbw %es:5, %cx
  252. 1: es lodsb
  253. addb %al, %dl
  254. loop 1b
  255. jnz pmm_scan
  256. /* PMM found: print PMM message */
  257. movw $init_message_pmm, %si
  258. xorw %di, %di
  259. call print_message
  260. /* Try to allocate 2MB block via PMM */
  261. pushw $0x0006 /* Aligned, extended memory */
  262. pushl $0xffffffff /* No handle */
  263. pushl $( 0x00200000 / 16 ) /* 2MB in paragraphs */
  264. pushw $PMM_ALLOCATE
  265. lcall *%es:7
  266. addw $12, %sp
  267. movw %dx, %ax
  268. xorw %di, %di
  269. call print_hex_word
  270. movw %dx, ( image_source + 2 )
  271. testw %dx, %dx /* %ax==0 even on success, since align=2MB */
  272. jz no_pmm
  273. /* PMM allocation succeeded: copy ROM to PMM block */
  274. pushal /* PMM presence implies 1kB stack */
  275. xorw %ax, %ax
  276. movw %ax, %es
  277. movl image_source, %edi
  278. xorl %esi, %esi
  279. movzbl romheader_size, %ecx
  280. shll $9, %ecx
  281. addr32 rep movsb /* PMM presence implies flat real mode */
  282. movl %edi, decompress_to
  283. /* Shrink ROM and update checksum */
  284. xorw %bx, %bx
  285. xorw %si, %si
  286. movw $_prefix_memsz_sect, %cx
  287. movb %cl, romheader_size
  288. shlw $9, %cx
  289. 1: lodsb
  290. addb %al, %bl
  291. loop 1b
  292. subb %bl, checksum
  293. popal
  294. no_pmm:
  295. /* Copy self to option ROM space. Required for PCI3.0, which
  296. * loads us to a temporary location in low memory. Will be a
  297. * no-op for lower PCI versions.
  298. */
  299. movb $' ', %al
  300. xorw %di, %di
  301. call print_character
  302. movw %gs, %ax
  303. call print_hex_word
  304. movzbw romheader_size, %cx
  305. shlw $9, %cx
  306. movw %ax, %es
  307. xorw %si, %si
  308. xorw %di, %di
  309. cs rep movsb
  310. /* Prompt for POST-time shell */
  311. movw $init_message_prompt, %si
  312. xorw %di, %di
  313. call print_message
  314. /* Wait for Ctrl-B */
  315. movw $0xff02, %bx
  316. call wait_for_key
  317. /* Clear prompt */
  318. pushf
  319. movw $clear_message, %si
  320. xorw %di, %di
  321. call print_message
  322. popf
  323. jnz 1f
  324. /* Ctrl-B was pressed: invoke gPXE. The keypress will be
  325. * picked up by the initial shell prompt, and we will drop
  326. * into a shell.
  327. */
  328. pushw %cs
  329. call exec
  330. 1:
  331. /* Restore registers */
  332. popw %gs
  333. popw %fs
  334. popw %es
  335. popw %ds
  336. popaw
  337. /* Indicate boot capability to PnP BIOS, if present */
  338. movw $0x20, %ax
  339. lret
  340. .size init, . - init
  341. init_message:
  342. .asciz "gPXE (http://etherboot.org) - "
  343. .size init_message, . - init_message
  344. init_message_pci:
  345. .asciz " PCI"
  346. .size init_message_pci, . - init_message_pci
  347. init_message_pnp:
  348. .asciz " PnP"
  349. .size init_message_pnp, . - init_message_pnp
  350. init_message_bbs:
  351. .asciz " BBS"
  352. .size init_message_bbs, . - init_message_bbs
  353. init_message_pmm:
  354. .asciz " PMM"
  355. .size init_message_pmm, . - init_message_pmm
  356. init_message_int19:
  357. .asciz " INT19"
  358. .size init_message_int19, . - init_message_int19
  359. init_message_prompt:
  360. .asciz "\nPress Ctrl-B to configure gPXE..."
  361. .size init_message_prompt, . - init_message_prompt
  362. clear_message:
  363. .asciz "\r \n\n"
  364. .size clear_message, . - clear_message
  365. /* ROM image location
  366. *
  367. * May be either within option ROM space, or within PMM-allocated block.
  368. */
  369. image_source:
  370. .long 0
  371. .size image_source, . - image_source
  372. /* Temporary decompression area
  373. *
  374. * May be either at HIGHMEM_LOADPOINT, or within PMM-allocated block.
  375. */
  376. decompress_to:
  377. .long HIGHMEM_LOADPOINT
  378. .size decompress_to, . - decompress_to
  379. /* BBS version
  380. *
  381. * Filled in by BBS BIOS. We ignore the value.
  382. */
  383. bbs_version:
  384. .word 0
  385. .size bbs_version, . - bbs_version
  386. /* Boot Execution Vector entry point
  387. *
  388. * Called by the PnP BIOS when it wants to boot us.
  389. */
  390. bev_entry:
  391. pushw %cs
  392. call exec
  393. lret
  394. .size bev_entry, . - bev_entry
  395. /* INT19 entry point
  396. *
  397. * Called via the hooked INT 19 if we detected a non-PnP BIOS. We
  398. * attempt to return via the original INT 19 vector (if we were able
  399. * to store it).
  400. */
  401. int19_entry:
  402. pushw %cs
  403. popw %ds
  404. /* Prompt user to press B to boot */
  405. movw $int19_message_prompt, %si
  406. xorw %di, %di
  407. call print_message
  408. movw $prodstr, %si
  409. call print_message
  410. movw $int19_message_dots, %si
  411. call print_message
  412. movw $0xdf42, %bx
  413. call wait_for_key
  414. pushf
  415. movw $clear_message, %si
  416. xorw %di, %di
  417. call print_message
  418. popf
  419. jnz 1f
  420. /* Leave keypress in buffer and start gPXE. The keypress will
  421. * cause the usual initial Ctrl-B prompt to be skipped.
  422. */
  423. pushw %cs
  424. call exec
  425. 1: /* Try to call original INT 19 vector */
  426. movl %cs:orig_int19, %eax
  427. testl %eax, %eax
  428. je 2f
  429. ljmp *%cs:orig_int19
  430. 2: /* No chained vector: issue INT 18 as a last resort */
  431. int $0x18
  432. .size int19_entry, . - int19_entry
  433. orig_int19:
  434. .long 0
  435. .size orig_int19, . - orig_int19
  436. int19_message_prompt:
  437. .asciz "Press B to boot from "
  438. .size int19_message_prompt, . - int19_message_prompt
  439. int19_message_dots:
  440. .asciz "..."
  441. .size int19_message_dots, . - int19_message_dots
  442. /* Execute as a boot device
  443. *
  444. */
  445. exec: /* Set %ds = %cs */
  446. pushw %cs
  447. popw %ds
  448. /* Print message as soon as possible */
  449. movw $prodstr, %si
  450. xorw %di, %di
  451. call print_message
  452. movw $exec_message, %si
  453. call print_message
  454. /* Store magic word on BIOS stack and remember BIOS %ss:sp */
  455. pushl $STACK_MAGIC
  456. movw %ss, %dx
  457. movw %sp, %bp
  458. /* Obtain a reasonably-sized temporary stack */
  459. xorw %ax, %ax
  460. movw %ax, %ss
  461. movw $0x7c00, %sp
  462. /* Install gPXE */
  463. movl image_source, %esi
  464. movl decompress_to, %edi
  465. call alloc_basemem
  466. call install_prealloc
  467. /* Set up real-mode stack */
  468. movw %bx, %ss
  469. movw $_estack16, %sp
  470. /* Jump to .text16 segment */
  471. pushw %ax
  472. pushw $1f
  473. lret
  474. .section ".text16", "awx", @progbits
  475. 1: /* Call main() */
  476. pushl $main
  477. pushw %cs
  478. call prot_call
  479. /* No need to clean up stack; we are about to reload %ss:sp */
  480. /* Restore BIOS stack */
  481. movw %dx, %ss
  482. movw %bp, %sp
  483. /* Check magic word on BIOS stack */
  484. popl %eax
  485. cmpl $STACK_MAGIC, %eax
  486. jne 1f
  487. /* BIOS stack OK: return to caller */
  488. lret
  489. 1: /* BIOS stack corrupt: use INT 18 */
  490. int $0x18
  491. .previous
  492. exec_message:
  493. .asciz " starting execution\n"
  494. .size exec_message, . - exec_message
  495. /* UNDI loader
  496. *
  497. * Called by an external program to load our PXE stack.
  498. */
  499. undiloader:
  500. /* Save registers */
  501. pushl %esi
  502. pushl %edi
  503. pushw %ds
  504. pushw %es
  505. pushw %bx
  506. /* ROM segment address to %ds */
  507. pushw %cs
  508. popw %ds
  509. /* UNDI loader parameter structure address into %es:%di */
  510. movw %sp, %bx
  511. movw %ss:18(%bx), %di
  512. movw %ss:20(%bx), %es
  513. /* Install to specified real-mode addresses */
  514. pushw %di
  515. movw %es:12(%di), %bx
  516. movw %es:14(%di), %ax
  517. movl image_source, %esi
  518. movl decompress_to, %edi
  519. call install_prealloc
  520. popw %di
  521. /* Call UNDI loader C code */
  522. pushl $pxe_loader_call
  523. pushw %cs
  524. pushw $1f
  525. pushw %ax
  526. pushw $prot_call
  527. lret
  528. 1: popw %bx /* discard */
  529. popw %bx /* discard */
  530. /* Restore registers and return */
  531. popw %bx
  532. popw %es
  533. popw %ds
  534. popl %edi
  535. popl %esi
  536. lret
  537. .size undiloader, . - undiloader
  538. /* Wait for key press specified by %bl (masked by %bh)
  539. *
  540. * Used by init and INT19 code when prompting user. If the specified
  541. * key is pressed, it is left in the keyboard buffer.
  542. *
  543. * Returns with ZF set iff specified key is pressed.
  544. */
  545. wait_for_key:
  546. /* Preserve registers */
  547. pushw %cx
  548. pushw %ax
  549. 1: /* Empty the keyboard buffer before waiting for input */
  550. movb $0x01, %ah
  551. int $0x16
  552. jz 2f
  553. xorw %ax, %ax
  554. int $0x16
  555. jmp 1b
  556. 2: /* Wait for a key press */
  557. movw $ROM_BANNER_TIMEOUT, %cx
  558. 3: decw %cx
  559. js 99f /* Exit with ZF clear */
  560. /* Wait for timer tick to be updated */
  561. call wait_for_tick
  562. /* Check to see if a key was pressed */
  563. movb $0x01, %ah
  564. int $0x16
  565. jz 3b
  566. /* Check to see if key was the specified key */
  567. andb %bh, %al
  568. cmpb %al, %bl
  569. je 99f /* Exit with ZF set */
  570. /* Not the specified key: remove from buffer and stop waiting */
  571. pushfw
  572. xorw %ax, %ax
  573. int $0x16
  574. popfw /* Exit with ZF clear */
  575. 99: /* Restore registers and return */
  576. popw %ax
  577. popw %cx
  578. ret
  579. .size wait_for_key, . - wait_for_key
  580. /* Wait for timer tick
  581. *
  582. * Used by wait_for_key
  583. */
  584. wait_for_tick:
  585. pushl %eax
  586. pushw %fs
  587. movw $0x40, %ax
  588. movw %ax, %fs
  589. movl %fs:(0x6c), %eax
  590. 1: pushf
  591. sti
  592. hlt
  593. popf
  594. cmpl %fs:(0x6c), %eax
  595. je 1b
  596. popw %fs
  597. popl %eax
  598. ret
  599. .size wait_for_tick, . - wait_for_tick