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

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079
  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. FILE_LICENCE ( GPL2_OR_LATER )
  9. #include <config/general.h>
  10. #define PNP_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'n' << 16 ) + ( 'P' << 24 ) )
  11. #define PMM_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'M' << 16 ) + ( 'M' << 24 ) )
  12. #define PCI_SIGNATURE ( 'P' + ( 'C' << 8 ) + ( 'I' << 16 ) + ( ' ' << 24 ) )
  13. #define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) )
  14. #define PNP_GET_BBS_VERSION 0x60
  15. #define PMM_ALLOCATE 0x0000
  16. #define PMM_DEALLOCATE 0x0002
  17. /* ROM banner timeout. Based on the configurable BANNER_TIMEOUT in
  18. * config.h, but converted to a number of (18Hz) timer ticks, and
  19. * doubled to allow for BIOSes that switch video modes immediately
  20. * beforehand, so rendering the message almost invisible to the user.
  21. */
  22. #define ROM_BANNER_TIMEOUT ( 2 * ( 18 * BANNER_TIMEOUT ) / 10 )
  23. /* We can load a ROM in two ways: have the BIOS load all of it (.rom prefix)
  24. * or have the BIOS load a stub that loads the rest using PCI (.xrom prefix).
  25. * The latter is not as widely supported, but allows the use of large ROMs
  26. * on some systems with crowded option ROM space.
  27. */
  28. #ifdef LOAD_ROM_FROM_PCI
  29. #define ROM_SIZE_VALUE _prefix_filesz_sect /* Amount to load in BIOS */
  30. #else
  31. #define ROM_SIZE_VALUE 0 /* Load amount (before compr. fixup) */
  32. #endif
  33. .text
  34. .code16
  35. .arch i386
  36. .section ".prefix", "ax", @progbits
  37. .org 0x00
  38. romheader:
  39. .word 0xAA55 /* BIOS extension signature */
  40. romheader_size: .byte ROM_SIZE_VALUE /* Size in 512-byte blocks */
  41. jmp init /* Initialisation vector */
  42. checksum:
  43. .byte 0, 0
  44. real_size:
  45. .word 0
  46. .org 0x16
  47. .word undiheader
  48. .org 0x18
  49. .word pciheader
  50. .org 0x1a
  51. .word pnpheader
  52. .size romheader, . - romheader
  53. .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
  54. #ifndef LOAD_ROM_FROM_PCI
  55. .ascii "ADDB"
  56. .long romheader_size
  57. .long 512
  58. .long 0
  59. #endif
  60. .ascii "ADDB"
  61. .long real_size
  62. .long 512
  63. .long 0
  64. .previous
  65. pciheader:
  66. .ascii "PCIR" /* Signature */
  67. .word pci_vendor_id /* Vendor identification */
  68. .word pci_device_id /* Device identification */
  69. .word 0x0000 /* Device list pointer */
  70. .word pciheader_len /* PCI data structure length */
  71. .byte 0x03 /* PCI data structure revision */
  72. .byte 0x02, 0x00, 0x00 /* Class code */
  73. pciheader_image_length:
  74. .word ROM_SIZE_VALUE /* Image length */
  75. .word 0x0001 /* Revision level */
  76. .byte 0x00 /* Code type */
  77. .byte 0x80 /* Last image indicator */
  78. pciheader_runtime_length:
  79. .word ROM_SIZE_VALUE /* Maximum run-time image length */
  80. .word 0x0000 /* Configuration utility code header */
  81. .word 0x0000 /* DMTF CLP entry point */
  82. .equ pciheader_len, . - pciheader
  83. .size pciheader, . - pciheader
  84. #ifndef LOAD_ROM_FROM_PCI
  85. .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
  86. .ascii "ADDW"
  87. .long pciheader_image_length
  88. .long 512
  89. .long 0
  90. .ascii "ADDW"
  91. .long pciheader_runtime_length
  92. .long 512
  93. .long 0
  94. .previous
  95. #endif
  96. pnpheader:
  97. .ascii "$PnP" /* Signature */
  98. .byte 0x01 /* Structure revision */
  99. .byte ( pnpheader_len / 16 ) /* Length (in 16 byte increments) */
  100. .word 0x0000 /* Offset of next header */
  101. .byte 0x00 /* Reserved */
  102. .byte 0x00 /* Checksum */
  103. .long 0x00000000 /* Device identifier */
  104. .word mfgstr /* Manufacturer string */
  105. .word prodstr /* Product name */
  106. .byte 0x02 /* Device base type code */
  107. .byte 0x00 /* Device sub-type code */
  108. .byte 0x00 /* Device interface type code */
  109. .byte 0xf4 /* Device indicator */
  110. .word 0x0000 /* Boot connection vector */
  111. .word 0x0000 /* Disconnect vector */
  112. .word bev_entry /* Boot execution vector */
  113. .word 0x0000 /* Reserved */
  114. .word 0x0000 /* Static resource information vector*/
  115. .equ pnpheader_len, . - pnpheader
  116. .size pnpheader, . - pnpheader
  117. /* Manufacturer string */
  118. mfgstr:
  119. .asciz "http://ipxe.org"
  120. .size mfgstr, . - mfgstr
  121. /* Product string
  122. *
  123. * Defaults to PRODUCT_SHORT_NAME. If the ROM image is writable at
  124. * initialisation time, it will be filled in to include the PCI
  125. * bus:dev.fn number of the card as well.
  126. */
  127. prodstr:
  128. .ascii PRODUCT_SHORT_NAME
  129. prodstr_separator:
  130. .byte 0
  131. .ascii "(PCI "
  132. prodstr_pci_id:
  133. .asciz "xx:xx.x)" /* Filled in by init code */
  134. .size prodstr, . - prodstr
  135. .globl undiheader
  136. .weak undiloader
  137. undiheader:
  138. .ascii "UNDI" /* Signature */
  139. .byte undiheader_len /* Length of structure */
  140. .byte 0 /* Checksum */
  141. .byte 0 /* Structure revision */
  142. .byte 0,1,2 /* PXE version: 2.1.0 */
  143. .word undiloader /* Offset to loader routine */
  144. .word _data16_memsz /* Stack segment size */
  145. .word _data16_memsz /* Data segment size */
  146. .word _text16_memsz /* Code segment size */
  147. .ascii "PCIR" /* Bus type */
  148. .equ undiheader_len, . - undiheader
  149. .size undiheader, . - undiheader
  150. /* Initialisation (called once during POST)
  151. *
  152. * Determine whether or not this is a PnP system via a signature
  153. * check. If it is PnP, return to the PnP BIOS indicating that we are
  154. * a boot-capable device; the BIOS will call our boot execution vector
  155. * if it wants to boot us. If it is not PnP, hook INT 19.
  156. */
  157. init:
  158. /* Preserve registers, clear direction flag, set %ds=%cs */
  159. pushaw
  160. pushw %ds
  161. pushw %es
  162. pushw %fs
  163. pushw %gs
  164. cld
  165. pushw %cs
  166. popw %ds
  167. /* Shuffle some registers around. We need %di available for
  168. * the print_xxx functions, and in a register that's
  169. * addressable from %es, so shuffle as follows:
  170. *
  171. * %di (pointer to PnP structure) => %bx
  172. * %bx (runtime segment address, for PCI 3.0) => %gs
  173. */
  174. movw %bx, %gs
  175. movw %di, %bx
  176. /* Print message as early as possible */
  177. movw $init_message, %si
  178. xorw %di, %di
  179. call print_message
  180. call print_pci_busdevfn
  181. #ifdef LOAD_ROM_FROM_PCI
  182. /* Save PCI bus:dev.fn for later use */
  183. movw %ax, pci_busdevfn
  184. #endif
  185. /* Fill in product name string, if possible */
  186. movw $prodstr_pci_id, %di
  187. call print_pci_busdevfn
  188. movb $( ' ' ), prodstr_separator
  189. /* Print segment address */
  190. movb $( ' ' ), %al
  191. xorw %di, %di
  192. call print_character
  193. movw %cs, %ax
  194. call print_hex_word
  195. /* Check for PCI BIOS version */
  196. pushl %ebx
  197. pushl %edx
  198. pushl %edi
  199. stc
  200. movw $0xb101, %ax
  201. int $0x1a
  202. jc no_pci3
  203. cmpl $PCI_SIGNATURE, %edx
  204. jne no_pci3
  205. testb %ah, %ah
  206. jnz no_pci3
  207. #ifdef LOAD_ROM_FROM_PCI
  208. incb pcibios_present
  209. #endif
  210. movw $init_message_pci, %si
  211. xorw %di, %di
  212. call print_message
  213. movb %bh, %al
  214. call print_hex_nibble
  215. movb $( '.' ), %al
  216. call print_character
  217. movb %bl, %al
  218. call print_hex_byte
  219. cmpb $3, %bh
  220. jb no_pci3
  221. /* PCI >=3.0: leave %gs as-is if sane */
  222. movw %gs, %ax
  223. cmpw $0xa000, %ax /* Insane if %gs < 0xa000 */
  224. jb pci3_insane
  225. movw %cs, %bx /* Sane if %cs == %gs */
  226. cmpw %bx, %ax
  227. je 1f
  228. movzbw romheader_size, %cx /* Sane if %cs+len <= %gs */
  229. shlw $5, %cx
  230. addw %cx, %bx
  231. cmpw %bx, %ax
  232. jae 1f
  233. movw %cs, %bx /* Sane if %gs+len <= %cs */
  234. addw %cx, %ax
  235. cmpw %bx, %ax
  236. jbe 1f
  237. pci3_insane: /* PCI 3.0 with insane %gs value: print error and ignore %gs */
  238. movb $( '!' ), %al
  239. call print_character
  240. movw %gs, %ax
  241. call print_hex_word
  242. no_pci3:
  243. /* PCI <3.0: set %gs (runtime segment) = %cs (init-time segment) */
  244. pushw %cs
  245. popw %gs
  246. 1: popl %edi
  247. popl %edx
  248. popl %ebx
  249. /* Check for PnP BIOS. Although %es:di should point to the
  250. * PnP BIOS signature on entry, some BIOSes fail to do this.
  251. */
  252. movw $( 0xf000 - 1 ), %bx
  253. pnp_scan:
  254. incw %bx
  255. jz no_pnp
  256. movw %bx, %es
  257. cmpl $PNP_SIGNATURE, %es:0
  258. jne pnp_scan
  259. xorw %dx, %dx
  260. xorw %si, %si
  261. movzbw %es:5, %cx
  262. 1: es lodsb
  263. addb %al, %dl
  264. loop 1b
  265. jnz pnp_scan
  266. /* Is PnP: print PnP message */
  267. movw $init_message_pnp, %si
  268. xorw %di, %di
  269. call print_message
  270. /* Check for BBS */
  271. pushw %es:0x1b /* Real-mode data segment */
  272. pushw %ds /* &(bbs_version) */
  273. pushw $bbs_version
  274. pushw $PNP_GET_BBS_VERSION
  275. lcall *%es:0xd
  276. addw $8, %sp
  277. testw %ax, %ax
  278. je got_bbs
  279. no_pnp: /* Not PnP-compliant - therefore cannot be BBS-compliant */
  280. no_bbs: /* Not BBS-compliant - must hook INT 19 */
  281. movw $init_message_int19, %si
  282. xorw %di, %di
  283. call print_message
  284. xorw %ax, %ax
  285. movw %ax, %es
  286. pushl %es:( 0x19 * 4 )
  287. popl orig_int19
  288. pushw %gs /* %gs contains runtime %cs */
  289. pushw $int19_entry
  290. popl %es:( 0x19 * 4 )
  291. jmp bbs_done
  292. got_bbs: /* BBS compliant - no need to hook INT 19 */
  293. movw $init_message_bbs, %si
  294. xorw %di, %di
  295. call print_message
  296. bbs_done:
  297. /* Check for PMM */
  298. movw $( 0xe000 - 1 ), %bx
  299. pmm_scan:
  300. incw %bx
  301. jz no_pmm
  302. movw %bx, %es
  303. cmpl $PMM_SIGNATURE, %es:0
  304. jne pmm_scan
  305. xorw %dx, %dx
  306. xorw %si, %si
  307. movzbw %es:5, %cx
  308. 1: es lodsb
  309. addb %al, %dl
  310. loop 1b
  311. jnz pmm_scan
  312. /* PMM found: print PMM message */
  313. movw $init_message_pmm, %si
  314. xorw %di, %di
  315. call print_message
  316. /* We have PMM and so a 1kB stack: preserve upper register halves */
  317. pushal
  318. /* Calculate required allocation size in %esi */
  319. movzwl real_size, %eax
  320. shll $9, %eax
  321. addl $_textdata_memsz, %eax
  322. orw $0xffff, %ax /* Ensure allocation size is at least 64kB */
  323. bsrl %eax, %ecx
  324. subw $15, %cx /* Round up and convert to 64kB count */
  325. movw $1, %si
  326. shlw %cl, %si
  327. pmm_loop:
  328. /* Try to allocate block via PMM */
  329. pushw $0x0006 /* Aligned, extended memory */
  330. pushl $0xffffffff /* No handle */
  331. movzwl %si, %eax
  332. shll $12, %eax
  333. pushl %eax /* Allocation size in paragraphs */
  334. pushw $PMM_ALLOCATE
  335. lcall *%es:7
  336. addw $12, %sp
  337. /* Abort if allocation fails */
  338. testw %dx, %dx /* %ax==0 even on success, since align>=64kB */
  339. jz pmm_fail
  340. /* If block has A20==1, free block and try again with twice
  341. * the allocation size (and hence alignment).
  342. */
  343. testw $0x0010, %dx
  344. jz got_pmm
  345. pushw %dx
  346. pushw $0
  347. pushw $PMM_DEALLOCATE
  348. lcall *%es:7
  349. addw $6, %sp
  350. addw %si, %si
  351. jmp pmm_loop
  352. got_pmm: /* PMM allocation succeeded */
  353. movw %dx, ( image_source + 2 )
  354. movw %dx, %ax
  355. xorw %di, %di
  356. call print_hex_word
  357. movb $( '@' ), %al
  358. call print_character
  359. movw %si, %ax
  360. call print_hex_byte
  361. pmm_copy:
  362. /* Copy ROM to PMM block */
  363. xorw %ax, %ax
  364. movw %ax, %es
  365. movl image_source, %edi
  366. xorl %esi, %esi
  367. movzbl romheader_size, %ecx
  368. shll $9, %ecx
  369. addr32 rep movsb /* PMM presence implies flat real mode */
  370. movl %edi, decompress_to
  371. /* Shrink ROM */
  372. movb $_prefix_memsz_sect, romheader_size
  373. #if defined(SHRINK_WITHOUT_PMM) || defined(LOAD_ROM_FROM_PCI)
  374. jmp pmm_done
  375. pmm_fail:
  376. /* Print marker and copy ourselves to high memory */
  377. movl $HIGHMEM_LOADPOINT, image_source
  378. xorw %di, %di
  379. movb $( '!' ), %al
  380. call print_character
  381. jmp pmm_copy
  382. pmm_done:
  383. #else
  384. pmm_fail:
  385. #endif
  386. /* Restore upper register halves */
  387. popal
  388. #if defined(LOAD_ROM_FROM_PCI)
  389. call load_from_pci
  390. jc load_err
  391. jmp load_ok
  392. no_pmm:
  393. /* Cannot continue without PMM - print error message */
  394. xorw %di, %di
  395. movw $init_message_no_pmm, %si
  396. call print_message
  397. load_err:
  398. /* Wait for five seconds to let user see message */
  399. movw $90, %cx
  400. 1: call wait_for_tick
  401. loop 1b
  402. /* Mark environment as invalid and return */
  403. movl $0, decompress_to
  404. jmp out
  405. load_ok:
  406. #else
  407. no_pmm:
  408. #endif
  409. /* Update checksum */
  410. xorw %bx, %bx
  411. xorw %si, %si
  412. movzbw romheader_size, %cx
  413. shlw $9, %cx
  414. 1: lodsb
  415. addb %al, %bl
  416. loop 1b
  417. subb %bl, checksum
  418. /* Copy self to option ROM space. Required for PCI3.0, which
  419. * loads us to a temporary location in low memory. Will be a
  420. * no-op for lower PCI versions.
  421. */
  422. movb $( ' ' ), %al
  423. xorw %di, %di
  424. call print_character
  425. movw %gs, %ax
  426. call print_hex_word
  427. movzbw romheader_size, %cx
  428. shlw $9, %cx
  429. movw %ax, %es
  430. xorw %si, %si
  431. xorw %di, %di
  432. cs rep movsb
  433. /* Prompt for POST-time shell */
  434. movw $init_message_prompt, %si
  435. xorw %di, %di
  436. call print_message
  437. movw $prodstr, %si
  438. call print_message
  439. movw $init_message_dots, %si
  440. call print_message
  441. /* Wait for Ctrl-B */
  442. movw $0xff02, %bx
  443. call wait_for_key
  444. /* Clear prompt */
  445. pushf
  446. xorw %di, %di
  447. call print_kill_line
  448. movw $init_message_done, %si
  449. call print_message
  450. popf
  451. jnz out
  452. /* Ctrl-B was pressed: invoke iPXE. The keypress will be
  453. * picked up by the initial shell prompt, and we will drop
  454. * into a shell.
  455. */
  456. pushw %cs
  457. call exec
  458. out:
  459. /* Restore registers */
  460. popw %gs
  461. popw %fs
  462. popw %es
  463. popw %ds
  464. popaw
  465. /* Indicate boot capability to PnP BIOS, if present */
  466. movw $0x20, %ax
  467. lret
  468. .size init, . - init
  469. /*
  470. * Note to hardware vendors:
  471. *
  472. * If you wish to brand this boot ROM, please do so by defining the
  473. * strings PRODUCT_NAME and PRODUCT_SHORT_NAME in config/general.h.
  474. *
  475. * While nothing in the GPL prevents you from removing all references
  476. * to iPXE or http://ipxe.org, we prefer you not to do so.
  477. *
  478. * If you have an OEM-mandated branding requirement that cannot be
  479. * satisfied simply by defining PRODUCT_NAME and PRODUCT_SHORT_NAME,
  480. * please contact us.
  481. *
  482. * [ Including an ASCII NUL in PRODUCT_NAME is considered to be
  483. * bypassing the spirit of this request! ]
  484. */
  485. init_message:
  486. .ascii "\n"
  487. .ascii PRODUCT_NAME
  488. .ascii "\n"
  489. .asciz "iPXE (http://ipxe.org) - "
  490. .size init_message, . - init_message
  491. init_message_pci:
  492. .asciz " PCI"
  493. .size init_message_pci, . - init_message_pci
  494. init_message_pnp:
  495. .asciz " PnP"
  496. .size init_message_pnp, . - init_message_pnp
  497. init_message_bbs:
  498. .asciz " BBS"
  499. .size init_message_bbs, . - init_message_bbs
  500. init_message_pmm:
  501. .asciz " PMM"
  502. .size init_message_pmm, . - init_message_pmm
  503. #ifdef LOAD_ROM_FROM_PCI
  504. init_message_no_pmm:
  505. .asciz "\nPMM required but not present!\n"
  506. .size init_message_no_pmm, . - init_message_no_pmm
  507. #endif
  508. init_message_int19:
  509. .asciz " INT19"
  510. .size init_message_int19, . - init_message_int19
  511. init_message_prompt:
  512. .asciz "\nPress Ctrl-B to configure "
  513. .size init_message_prompt, . - init_message_prompt
  514. init_message_dots:
  515. .asciz "..."
  516. .size init_message_dots, . - init_message_dots
  517. init_message_done:
  518. .asciz "\n\n"
  519. .size init_message_done, . - init_message_done
  520. /* ROM image location
  521. *
  522. * May be either within option ROM space, or within PMM-allocated block.
  523. */
  524. .globl image_source
  525. image_source:
  526. .long 0
  527. .size image_source, . - image_source
  528. /* Temporary decompression area
  529. *
  530. * May be either at HIGHMEM_LOADPOINT, or within PMM-allocated block.
  531. * If a PCI ROM load fails, this will be set to zero.
  532. */
  533. .globl decompress_to
  534. decompress_to:
  535. .long HIGHMEM_LOADPOINT
  536. .size decompress_to, . - decompress_to
  537. #ifdef LOAD_ROM_FROM_PCI
  538. /* Set if the PCI BIOS is present, even <3.0 */
  539. pcibios_present:
  540. .byte 0
  541. .byte 0 /* for alignment */
  542. .size pcibios_present, . - pcibios_present
  543. /* PCI bus:device.function word
  544. *
  545. * Filled in by init in the .xrom case, so the remainder of the ROM
  546. * can be located.
  547. */
  548. pci_busdevfn:
  549. .word 0
  550. .size pci_busdevfn, . - pci_busdevfn
  551. #endif
  552. /* BBS version
  553. *
  554. * Filled in by BBS BIOS. We ignore the value.
  555. */
  556. bbs_version:
  557. .word 0
  558. .size bbs_version, . - bbs_version
  559. /* Boot Execution Vector entry point
  560. *
  561. * Called by the PnP BIOS when it wants to boot us.
  562. */
  563. bev_entry:
  564. pushw %cs
  565. call exec
  566. lret
  567. .size bev_entry, . - bev_entry
  568. #ifdef LOAD_ROM_FROM_PCI
  569. #define PCI_ROM_ADDRESS 0x30 /* Bits 31:11 address, 10:1 reserved */
  570. #define PCI_ROM_ADDRESS_ENABLE 0x00000001
  571. #define PCI_ROM_ADDRESS_MASK 0xfffff800
  572. #define PCIBIOS_READ_WORD 0xb109
  573. #define PCIBIOS_READ_DWORD 0xb10a
  574. #define PCIBIOS_WRITE_WORD 0xb10c
  575. #define PCIBIOS_WRITE_DWORD 0xb10d
  576. /* Determine size of PCI BAR
  577. *
  578. * %bx : PCI bus:dev.fn to probe
  579. * %di : Address of BAR to find size of
  580. * %edx : Mask of address bits within BAR
  581. *
  582. * %ecx : Size for a memory resource,
  583. * 1 for an I/O resource (bit 0 set).
  584. * CF : Set on error or nonexistent device (all-ones read)
  585. *
  586. * All other registers saved.
  587. */
  588. pci_bar_size:
  589. /* Save registers */
  590. pushw %ax
  591. pushl %esi
  592. pushl %edx
  593. /* Read current BAR value */
  594. movw $PCIBIOS_READ_DWORD, %ax
  595. int $0x1a
  596. /* Check for device existence and save it */
  597. testb $1, %cl /* I/O bit? */
  598. jz 1f
  599. andl $1, %ecx /* If so, exit with %ecx = 1 */
  600. jmp 99f
  601. 1: notl %ecx
  602. testl %ecx, %ecx /* Set ZF iff %ecx was all-ones */
  603. notl %ecx
  604. jnz 1f
  605. stc /* All ones - exit with CF set */
  606. jmp 99f
  607. 1: movl %ecx, %esi /* Save in %esi */
  608. /* Write all ones to BAR */
  609. movl %edx, %ecx
  610. movw $PCIBIOS_WRITE_DWORD, %ax
  611. int $0x1a
  612. /* Read back BAR */
  613. movw $PCIBIOS_READ_DWORD, %ax
  614. int $0x1a
  615. /* Find decode size from least set bit in mask BAR */
  616. bsfl %ecx, %ecx /* Find least set bit, log2(decode size) */
  617. jz 1f /* Mask BAR should not be zero */
  618. xorl %edx, %edx
  619. incl %edx
  620. shll %cl, %edx /* %edx = decode size */
  621. jmp 2f
  622. 1: xorl %edx, %edx /* Return zero size for mask BAR zero */
  623. /* Restore old BAR value */
  624. 2: movl %esi, %ecx
  625. movw $PCIBIOS_WRITE_DWORD, %ax
  626. int $0x1a
  627. movl %edx, %ecx /* Return size in %ecx */
  628. /* Restore registers and return */
  629. 99: popl %edx
  630. popl %esi
  631. popw %ax
  632. ret
  633. .size pci_bar_size, . - pci_bar_size
  634. /* PCI ROM loader
  635. *
  636. * Called from init in the .xrom case to load the non-prefix code
  637. * using the PCI ROM BAR.
  638. *
  639. * Returns with carry flag set on error. All registers saved.
  640. */
  641. load_from_pci:
  642. /*
  643. * Use PCI BIOS access to config space. The calls take
  644. *
  645. * %ah : 0xb1 %al : function
  646. * %bx : bus/dev/fn
  647. * %di : config space address
  648. * %ecx : value to write (for writes)
  649. *
  650. * %ecx : value read (for reads)
  651. * %ah : return code
  652. * CF : error indication
  653. *
  654. * All registers not used for return are preserved.
  655. */
  656. /* Save registers and set up %es for big real mode */
  657. pushal
  658. pushw %es
  659. xorw %ax, %ax
  660. movw %ax, %es
  661. /* Check PCI BIOS presence */
  662. cmpb $0, pcibios_present
  663. jz err_pcibios
  664. /* Load existing PCI ROM BAR */
  665. movw $PCIBIOS_READ_DWORD, %ax
  666. movw pci_busdevfn, %bx
  667. movw $PCI_ROM_ADDRESS, %di
  668. int $0x1a
  669. /* Maybe it's already enabled? */
  670. testb $PCI_ROM_ADDRESS_ENABLE, %cl
  671. jz 1f
  672. movb $1, %dl /* Flag indicating no deinit required */
  673. movl %ecx, %ebp
  674. jmp check_rom
  675. /* Determine PCI BAR decode size */
  676. 1: movl $PCI_ROM_ADDRESS_MASK, %edx
  677. call pci_bar_size /* Returns decode size in %ecx */
  678. jc err_size_insane /* CF => no ROM BAR, %ecx == ffffffff */
  679. /* Check sanity of decode size */
  680. xorl %eax, %eax
  681. movw real_size, %ax
  682. shll $9, %eax /* %eax = ROM size */
  683. cmpl %ecx, %eax
  684. ja err_size_insane /* Insane if decode size < ROM size */
  685. cmpl $0x100000, %ecx
  686. jae err_size_insane /* Insane if decode size >= 1MB */
  687. /* Find a place to map the BAR
  688. * In theory we should examine e820 and all PCI BARs to find a
  689. * free region. However, we run at POST when e820 may not be
  690. * available, and memory reads of an unmapped location are
  691. * de facto standardized to return all-ones. Thus, we can get
  692. * away with searching high memory (0xf0000000 and up) on
  693. * multiples of the ROM BAR decode size for a sufficiently
  694. * large all-ones region.
  695. */
  696. movl %ecx, %edx /* Save ROM BAR size in %edx */
  697. movl $0xf0000000, %ebp
  698. xorl %eax, %eax
  699. notl %eax /* %eax = all ones */
  700. bar_search:
  701. movl %ebp, %edi
  702. movl %edx, %ecx
  703. shrl $2, %ecx
  704. addr32 repe scasl /* Scan %es:edi for anything not all-ones */
  705. jz bar_found
  706. addl %edx, %ebp
  707. testl $0x80000000, %ebp
  708. jz err_no_bar
  709. jmp bar_search
  710. bar_found:
  711. movl %edi, %ebp
  712. /* Save current BAR value on stack to restore later */
  713. movw $PCIBIOS_READ_DWORD, %ax
  714. movw $PCI_ROM_ADDRESS, %di
  715. int $0x1a
  716. pushl %ecx
  717. /* Map the ROM */
  718. movw $PCIBIOS_WRITE_DWORD, %ax
  719. movl %ebp, %ecx
  720. orb $PCI_ROM_ADDRESS_ENABLE, %cl
  721. int $0x1a
  722. xorb %dl, %dl /* %dl = 0 : ROM was not already mapped */
  723. check_rom:
  724. /* Check and copy ROM - enter with %dl set to skip unmapping,
  725. * %ebp set to mapped ROM BAR address.
  726. * We check up to prodstr_separator for equality, since anything past
  727. * that may have been modified. Since our check includes the checksum
  728. * byte over the whole ROM stub, that should be sufficient.
  729. */
  730. xorb %dh, %dh /* %dh = 0 : ROM did not fail integrity check */
  731. /* Verify ROM integrity */
  732. xorl %esi, %esi
  733. movl %ebp, %edi
  734. movl $prodstr_separator, %ecx
  735. addr32 repe cmpsb
  736. jz copy_rom
  737. incb %dh /* ROM failed integrity check */
  738. movl %ecx, %ebp /* Save number of bytes left */
  739. jmp skip_load
  740. copy_rom:
  741. /* Print BAR address and indicate whether we mapped it ourselves */
  742. movb $( ' ' ), %al
  743. xorw %di, %di
  744. call print_character
  745. movl %ebp, %eax
  746. call print_hex_dword
  747. movb $( '-' ), %al /* '-' for self-mapped */
  748. subb %dl, %al
  749. subb %dl, %al /* '+' = '-' - 2 for BIOS-mapped */
  750. call print_character
  751. /* Copy ROM at %ebp to PMM or highmem block */
  752. movl %ebp, %esi
  753. movl image_source, %edi
  754. movzwl real_size, %ecx
  755. shll $9, %ecx
  756. addr32 es rep movsb
  757. movl %edi, decompress_to
  758. skip_load:
  759. testb %dl, %dl /* Was ROM already mapped? */
  760. jnz skip_unmap
  761. /* Unmap the ROM by restoring old ROM BAR */
  762. movw $PCIBIOS_WRITE_DWORD, %ax
  763. movw $PCI_ROM_ADDRESS, %di
  764. popl %ecx
  765. int $0x1a
  766. skip_unmap:
  767. /* Error handling */
  768. testb %dh, %dh
  769. jnz err_rom_invalid
  770. clc
  771. jmp 99f
  772. err_pcibios: /* No PCI BIOS available */
  773. movw $load_message_no_pcibios, %si
  774. xorl %eax, %eax /* "error code" is zero */
  775. jmp 1f
  776. err_size_insane: /* BAR has size (%ecx) that is insane */
  777. movw $load_message_size_insane, %si
  778. movl %ecx, %eax
  779. jmp 1f
  780. err_no_bar: /* No space of sufficient size (%edx) found */
  781. movw $load_message_no_bar, %si
  782. movl %edx, %eax
  783. jmp 1f
  784. err_rom_invalid: /* Loaded ROM does not match (%ebp bytes left) */
  785. movw $load_message_rom_invalid, %si
  786. movzbl romheader_size, %eax
  787. shll $9, %eax
  788. subl %ebp, %eax
  789. decl %eax /* %eax is now byte index of failure */
  790. 1: /* Error handler - print message at %si and dword in %eax */
  791. xorw %di, %di
  792. call print_message
  793. call print_hex_dword
  794. stc
  795. 99: popw %es
  796. popal
  797. ret
  798. .size load_from_pci, . - load_from_pci
  799. load_message_no_pcibios:
  800. .asciz "\nNo PCI BIOS found! "
  801. .size load_message_no_pcibios, . - load_message_no_pcibios
  802. load_message_size_insane:
  803. .asciz "\nROM resource has invalid size "
  804. .size load_message_size_insane, . - load_message_size_insane
  805. load_message_no_bar:
  806. .asciz "\nNo memory hole of sufficient size "
  807. .size load_message_no_bar, . - load_message_no_bar
  808. load_message_rom_invalid:
  809. .asciz "\nLoaded ROM is invalid at "
  810. .size load_message_rom_invalid, . - load_message_rom_invalid
  811. #endif /* LOAD_ROM_FROM_PCI */
  812. /* INT19 entry point
  813. *
  814. * Called via the hooked INT 19 if we detected a non-PnP BIOS. We
  815. * attempt to return via the original INT 19 vector (if we were able
  816. * to store it).
  817. */
  818. int19_entry:
  819. pushw %cs
  820. popw %ds
  821. /* Prompt user to press B to boot */
  822. movw $int19_message_prompt, %si
  823. xorw %di, %di
  824. call print_message
  825. movw $prodstr, %si
  826. call print_message
  827. movw $int19_message_dots, %si
  828. call print_message
  829. movw $0xdf4e, %bx
  830. call wait_for_key
  831. pushf
  832. xorw %di, %di
  833. call print_kill_line
  834. movw $int19_message_done, %si
  835. call print_message
  836. popf
  837. jz 1f
  838. /* Leave keypress in buffer and start iPXE. The keypress will
  839. * cause the usual initial Ctrl-B prompt to be skipped.
  840. */
  841. pushw %cs
  842. call exec
  843. 1: /* Try to call original INT 19 vector */
  844. movl %cs:orig_int19, %eax
  845. testl %eax, %eax
  846. je 2f
  847. ljmp *%cs:orig_int19
  848. 2: /* No chained vector: issue INT 18 as a last resort */
  849. int $0x18
  850. .size int19_entry, . - int19_entry
  851. orig_int19:
  852. .long 0
  853. .size orig_int19, . - orig_int19
  854. int19_message_prompt:
  855. .asciz "Press N to skip booting from "
  856. .size int19_message_prompt, . - int19_message_prompt
  857. int19_message_dots:
  858. .asciz "..."
  859. .size int19_message_dots, . - int19_message_dots
  860. int19_message_done:
  861. .asciz "\n\n"
  862. .size int19_message_done, . - int19_message_done
  863. /* Execute as a boot device
  864. *
  865. */
  866. exec: /* Set %ds = %cs */
  867. pushw %cs
  868. popw %ds
  869. #ifdef LOAD_ROM_FROM_PCI
  870. /* Don't execute if load was invalid */
  871. cmpl $0, decompress_to
  872. jne 1f
  873. lret
  874. 1:
  875. #endif
  876. /* Print message as soon as possible */
  877. movw $prodstr, %si
  878. xorw %di, %di
  879. call print_message
  880. movw $exec_message, %si
  881. call print_message
  882. /* Store magic word on BIOS stack and remember BIOS %ss:sp */
  883. pushl $STACK_MAGIC
  884. movw %ss, %dx
  885. movw %sp, %bp
  886. /* Obtain a reasonably-sized temporary stack */
  887. xorw %ax, %ax
  888. movw %ax, %ss
  889. movw $0x7c00, %sp
  890. /* Install iPXE */
  891. movl image_source, %esi
  892. movl decompress_to, %edi
  893. call alloc_basemem
  894. call install_prealloc
  895. /* Set up real-mode stack */
  896. movw %bx, %ss
  897. movw $_estack16, %sp
  898. /* Jump to .text16 segment */
  899. pushw %ax
  900. pushw $1f
  901. lret
  902. .section ".text16", "awx", @progbits
  903. 1: /* Call main() */
  904. pushl $main
  905. pushw %cs
  906. call prot_call
  907. popl %ecx /* discard */
  908. /* Uninstall iPXE */
  909. call uninstall
  910. /* Restore BIOS stack */
  911. movw %dx, %ss
  912. movw %bp, %sp
  913. /* Check magic word on BIOS stack */
  914. popl %eax
  915. cmpl $STACK_MAGIC, %eax
  916. jne 1f
  917. /* BIOS stack OK: return to caller */
  918. lret
  919. 1: /* BIOS stack corrupt: use INT 18 */
  920. int $0x18
  921. .previous
  922. exec_message:
  923. .asciz " starting execution\n"
  924. .size exec_message, . - exec_message
  925. /* Wait for key press specified by %bl (masked by %bh)
  926. *
  927. * Used by init and INT19 code when prompting user. If the specified
  928. * key is pressed, it is left in the keyboard buffer.
  929. *
  930. * Returns with ZF set iff specified key is pressed.
  931. */
  932. wait_for_key:
  933. /* Preserve registers */
  934. pushw %cx
  935. pushw %ax
  936. 1: /* Empty the keyboard buffer before waiting for input */
  937. movb $0x01, %ah
  938. int $0x16
  939. jz 2f
  940. xorw %ax, %ax
  941. int $0x16
  942. jmp 1b
  943. 2: /* Wait for a key press */
  944. movw $ROM_BANNER_TIMEOUT, %cx
  945. 3: decw %cx
  946. js 99f /* Exit with ZF clear */
  947. /* Wait for timer tick to be updated */
  948. call wait_for_tick
  949. /* Check to see if a key was pressed */
  950. movb $0x01, %ah
  951. int $0x16
  952. jz 3b
  953. /* Check to see if key was the specified key */
  954. andb %bh, %al
  955. cmpb %al, %bl
  956. je 99f /* Exit with ZF set */
  957. /* Not the specified key: remove from buffer and stop waiting */
  958. pushfw
  959. xorw %ax, %ax
  960. int $0x16
  961. popfw /* Exit with ZF clear */
  962. 99: /* Restore registers and return */
  963. popw %ax
  964. popw %cx
  965. ret
  966. .size wait_for_key, . - wait_for_key
  967. /* Wait for timer tick
  968. *
  969. * Used by wait_for_key
  970. */
  971. wait_for_tick:
  972. pushl %eax
  973. pushw %fs
  974. movw $0x40, %ax
  975. movw %ax, %fs
  976. movl %fs:(0x6c), %eax
  977. 1: pushf
  978. sti
  979. hlt
  980. popf
  981. cmpl %fs:(0x6c), %eax
  982. je 1b
  983. popw %fs
  984. popl %eax
  985. ret
  986. .size wait_for_tick, . - wait_for_tick