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 14KB

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