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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731
  1. #define PXENV_UNDI_SHUTDOWN 0x0005
  2. #define PXENV_UNDI_GET_NIC_TYPE 0x0012
  3. #define PXENV_STOP_UNDI 0x0015
  4. #define PXENV_UNLOAD_STACK 0x0070
  5. .text
  6. .arch i386
  7. .org 0
  8. .section ".prefix", "ax", @progbits
  9. .section ".prefix.data", "aw", @progbits
  10. .code16
  11. #include <undi.h>
  12. /*****************************************************************************
  13. * Entry point: set operating context, print welcome message
  14. *****************************************************************************
  15. */
  16. .section ".prefix"
  17. /* Set up our non-stack segment registers */
  18. jmp $0x7c0, $1f
  19. 1: movw %cs, %ax
  20. movw %ax, %ds
  21. movw $0x40, %ax /* BIOS data segment access */
  22. movw %ax, %fs
  23. /* Record PXENV+ and !PXE nominal addresses */
  24. movw %es, %ax /* PXENV+ address */
  25. movw %ax, pxenv_segment
  26. movw %bx, pxenv_offset
  27. popl %eax /* Discard return address */
  28. popl ppxe_segoff /* !PXE address */
  29. /* Set up stack just below 0x7c00 */
  30. xorw %ax, %ax
  31. movw %ax, %ss
  32. movw $0x7c00, %sp
  33. /* Clear direction flag, for the sake of sanity */
  34. cld
  35. /* Print welcome message */
  36. movw $10f, %si
  37. call print_message
  38. .section ".prefix.data"
  39. 10: .asciz "PXE->EB:"
  40. .previous
  41. /*****************************************************************************
  42. * Verify PXENV+ structure and record parameters of interest
  43. *****************************************************************************
  44. */
  45. detect_pxenv:
  46. /* Signature check */
  47. les pxenv_segoff, %di
  48. cmpl $0x4e455850, %es:(%di) /* 'PXEN' signature */
  49. jne no_pxenv
  50. cmpw $0x2b56, %es:4(%di) /* 'V+' signature */
  51. jne no_pxenv
  52. /* Record entry point and UNDI segments */
  53. pushl %es:0x0a(%di) /* Entry point */
  54. popl entry_segoff
  55. pushw %es:0x24(%di) /* UNDI code segment */
  56. pushw %es:0x26(%di) /* UNDI code size */
  57. popl undi_code_segoff
  58. pushw %es:0x20(%di) /* UNDI data segment */
  59. pushw %es:0x22(%di) /* UNDI data size */
  60. popl undi_data_segoff
  61. /* Print "PXENV+ at <address>" */
  62. movw $10f, %si
  63. call print_message
  64. movw %bx, %di
  65. call print_segoff
  66. movb $',', %al
  67. call print_character
  68. jmp 99f
  69. .section ".prefix.data"
  70. 10: .asciz " PXENV+ at "
  71. .previous
  72. no_pxenv:
  73. xorl %eax, %eax
  74. movl %eax, pxenv_segoff
  75. 99:
  76. /*****************************************************************************
  77. * Verify !PXE structure and record parameters of interest
  78. *****************************************************************************
  79. */
  80. detect_ppxe:
  81. /* Signature check */
  82. les ppxe_segoff, %di
  83. cmpl $0x45585021, %es:(%di) /* '!PXE' signature */
  84. jne no_ppxe
  85. /* Record structure address, entry point, and UNDI segments */
  86. pushw %es
  87. popw ppxe_segment
  88. movw %di, ppxe_offset
  89. pushl %es:0x10(%di) /* Entry point */
  90. popl entry_segoff
  91. pushw %es:0x30(%di) /* UNDI code segment */
  92. pushw %es:0x36(%di) /* UNDI code size */
  93. popl undi_code_segoff
  94. pushw %es:0x28(%di) /* UNDI data segment */
  95. pushw %es:0x2e(%di) /* UNDI data size */
  96. popl undi_data_segoff
  97. /* Print "!PXE at <address>" */
  98. movw $10f, %si
  99. call print_message
  100. call print_segoff
  101. movb $',', %al
  102. call print_character
  103. jmp 99f
  104. .section ".prefix.data"
  105. 10: .asciz " !PXE at "
  106. .previous
  107. no_ppxe:
  108. xorl %eax, %eax
  109. movl %eax, ppxe_segoff
  110. 99:
  111. /*****************************************************************************
  112. * Sanity check: we must have an entry point
  113. *****************************************************************************
  114. */
  115. check_have_stack:
  116. /* Check for entry point */
  117. movl entry_segoff, %eax
  118. testl %eax, %eax
  119. jnz 99f
  120. /* No entry point: print message and skip everything else */
  121. movw $10f, %si
  122. call print_message
  123. jmp finished
  124. .section ".prefix.data"
  125. 10: .asciz " No PXE stack found!\n"
  126. .previous
  127. 99:
  128. /*****************************************************************************
  129. * Calculate base memory usage by UNDI
  130. *****************************************************************************
  131. */
  132. find_undi_basemem_usage:
  133. movw undi_code_segment, %ax
  134. movw undi_code_size, %bx
  135. movw undi_data_segment, %cx
  136. movw undi_data_size, %dx
  137. cmpw %ax, %cx
  138. ja 1f
  139. xchgw %ax, %cx
  140. xchgw %bx, %dx
  141. 1: /* %ax:%bx now describes the lower region, %cx:%dx the higher */
  142. shrw $6, %ax /* Round down to nearest kB */
  143. movw %ax, undi_fbms_start
  144. addw $0x0f, %dx /* Round up to next segment */
  145. shrw $4, %dx
  146. addw %dx, %cx
  147. addw $((1024 / 16) - 1), %cx /* Round up to next kB */
  148. shrw $6, %cx
  149. movw %cx, undi_fbms_end
  150. /*****************************************************************************
  151. * Print information about detected PXE stack
  152. *****************************************************************************
  153. */
  154. print_structure_information:
  155. /* Print entry point */
  156. movw $10f, %si
  157. call print_message
  158. les entry_segoff, %di
  159. call print_segoff
  160. .section ".prefix.data"
  161. 10: .asciz " entry point at "
  162. .previous
  163. /* Print UNDI code segment */
  164. movw $10f, %si
  165. call print_message
  166. les undi_code_segoff, %di
  167. call print_segoff
  168. .section ".prefix.data"
  169. 10: .asciz "\n UNDI code segment "
  170. .previous
  171. /* Print UNDI data segment */
  172. movw $10f, %si
  173. call print_message
  174. les undi_data_segoff, %di
  175. call print_segoff
  176. .section ".prefix.data"
  177. 10: .asciz ", data segment "
  178. .previous
  179. /* Print UNDI memory usage */
  180. movw $10f, %si
  181. call print_message
  182. movw undi_fbms_start, %ax
  183. call print_word
  184. movb $'-', %al
  185. call print_character
  186. movw undi_fbms_end, %ax
  187. call print_word
  188. movw $20f, %si
  189. call print_message
  190. .section ".prefix.data"
  191. 10: .asciz " ("
  192. 20: .asciz "kB)\n"
  193. .previous
  194. /*****************************************************************************
  195. * Determine physical device
  196. *****************************************************************************
  197. */
  198. get_physical_device:
  199. /* Issue PXENV_UNDI_GET_NIC_TYPE */
  200. movw $PXENV_UNDI_GET_NIC_TYPE, %bx
  201. call pxe_call
  202. jnc 1f
  203. call print_pxe_error
  204. jmp no_physical_device
  205. 1: /* Determine physical device type */
  206. movb ( pxe_parameter_structure + 0x02 ), %al
  207. cmpb $2, %al
  208. je pci_physical_device
  209. jmp no_physical_device
  210. pci_physical_device:
  211. /* Record PCI bus:dev.fn and vendor/device IDs */
  212. movl ( pxe_parameter_structure + 0x03 ), %eax
  213. movl %eax, pci_vendor
  214. movw ( pxe_parameter_structure + 0x0b ), %ax
  215. movw %ax, pci_busdevfn
  216. movw $10f, %si
  217. call print_message
  218. call print_pci_busdevfn
  219. movb $0x0a, %al
  220. call print_character
  221. jmp 99f
  222. .section ".prefix.data"
  223. 10: .asciz " UNDI device is PCI "
  224. .previous
  225. no_physical_device:
  226. /* No device found, or device type not understood */
  227. movw $10f, %si
  228. call print_message
  229. .section ".prefix.data"
  230. 10: .asciz " Unable to determine UNDI physical device\n"
  231. .previous
  232. 99:
  233. /*****************************************************************************
  234. * Leave NIC in a safe state
  235. *****************************************************************************
  236. */
  237. shutdown_nic:
  238. /* Issue PXENV_UNDI_SHUTDOWN */
  239. movw $PXENV_UNDI_SHUTDOWN, %bx
  240. call pxe_call
  241. jnc 1f
  242. call print_pxe_error
  243. 1:
  244. /*****************************************************************************
  245. * Unload PXE base code
  246. *****************************************************************************
  247. */
  248. unload_base_code:
  249. /* Issue PXENV_UNLOAD_STACK */
  250. movw $PXENV_UNLOAD_STACK, %bx
  251. call pxe_call
  252. jnc 1f
  253. call print_pxe_error
  254. jmp 99f
  255. 1: /* Free base memory used by PXE base code */
  256. movw %fs:(0x13), %si
  257. movw undi_fbms_start, %di
  258. call free_basemem
  259. 99:
  260. /*****************************************************************************
  261. * Unload UNDI driver
  262. *****************************************************************************
  263. */
  264. #ifndef PXELOADER_KEEP_UNDI
  265. unload_undi:
  266. /* Issue PXENV_STOP_UNDI */
  267. movw $PXENV_STOP_UNDI, %bx
  268. call pxe_call
  269. jnc 1f
  270. call print_pxe_error
  271. jmp 99f
  272. 1: /* Free base memory used by UNDI */
  273. movw undi_fbms_start, %si
  274. movw undi_fbms_end, %di
  275. call free_basemem
  276. /* Clear UNDI_FL_STARTED */
  277. andw $~UNDI_FL_STARTED, flags
  278. 99:
  279. #endif /* PXELOADER_KEEP_UNDI */
  280. /*****************************************************************************
  281. * Print remaining free base memory
  282. *****************************************************************************
  283. */
  284. print_free_basemem:
  285. movw $10f, %si
  286. call print_message
  287. movw %fs:(0x13), %ax
  288. call print_word
  289. movw $20f, %si
  290. call print_message
  291. .section ".prefix.data"
  292. 10: .asciz " "
  293. 20: .asciz "kB free base memory after PXE unload\n"
  294. .previous
  295. /*****************************************************************************
  296. * Exit point
  297. *****************************************************************************
  298. */
  299. finished:
  300. jmp run_etherboot
  301. /*****************************************************************************
  302. * Subroutine: print character (with LF -> LF,CR translation)
  303. *
  304. * Parameters:
  305. * %al : character to print
  306. * Returns:
  307. * Nothing
  308. *****************************************************************************
  309. */
  310. print_character:
  311. /* Preserve registers */
  312. pushw %ax
  313. pushw %bx
  314. pushw %bp
  315. /* Print character */
  316. movw $0x0007, %bx /* page 0, attribute 7 (normal) */
  317. movb $0x0e, %ah /* write char, tty mode */
  318. cmpb $0x0a, %al /* '\n'? */
  319. jne 1f
  320. int $0x10
  321. movb $0x0d, %al
  322. 1: int $0x10
  323. /* Restore registers and return */
  324. popw %bp
  325. popw %bx
  326. popw %ax
  327. ret
  328. /*****************************************************************************
  329. * Subroutine: print a NUL-terminated string
  330. *
  331. * Parameters:
  332. * %ds:%si : string to print
  333. * Returns:
  334. * Nothing
  335. *****************************************************************************
  336. */
  337. print_message:
  338. /* Preserve registers */
  339. pushw %ax
  340. pushw %si
  341. /* Print string */
  342. 1: lodsb
  343. testb %al, %al
  344. je 2f
  345. call print_character
  346. jmp 1b
  347. 2: /* Restore registers and return */
  348. popw %si
  349. popw %ax
  350. ret
  351. /*****************************************************************************
  352. * Subroutine: print hex digit
  353. *
  354. * Parameters:
  355. * %al (low nibble) : digit to print
  356. * Returns:
  357. * Nothing
  358. *****************************************************************************
  359. */
  360. print_hex_nibble:
  361. /* Preserve registers */
  362. pushw %ax
  363. /* Print digit (technique by Norbert Juffa <norbert.juffa@amd.com> */
  364. andb $0x0f, %al
  365. cmpb $10, %al
  366. sbbb $0x69, %al
  367. das
  368. call print_character
  369. /* Restore registers and return */
  370. popw %ax
  371. ret
  372. /*****************************************************************************
  373. * Subroutine: print hex byte
  374. *
  375. * Parameters:
  376. * %al : byte to print
  377. * Returns:
  378. * Nothing
  379. *****************************************************************************
  380. */
  381. print_hex_byte:
  382. rorb $4, %al
  383. call print_hex_nibble
  384. rorb $4, %al
  385. call print_hex_nibble
  386. ret
  387. /*****************************************************************************
  388. * Subroutine: print hex word
  389. *
  390. * Parameters:
  391. * %ax : word to print
  392. * Returns:
  393. * Nothing
  394. *****************************************************************************
  395. */
  396. print_hex_word:
  397. xchgb %al, %ah
  398. call print_hex_byte
  399. xchgb %al, %ah
  400. call print_hex_byte
  401. ret
  402. /*****************************************************************************
  403. * Subroutine: print segment:offset address
  404. *
  405. * Parameters:
  406. * %es:%di : segment:offset address to print
  407. * Returns:
  408. * Nothing
  409. *****************************************************************************
  410. */
  411. print_segoff:
  412. /* Preserve registers */
  413. pushw %ax
  414. /* Print "<segment>:offset" */
  415. movw %es, %ax
  416. call print_hex_word
  417. movb $':', %al
  418. call print_character
  419. movw %di, %ax
  420. call print_hex_word
  421. /* Restore registers and return */
  422. popw %ax
  423. ret
  424. /*****************************************************************************
  425. * Subroutine: print decimal word
  426. *
  427. * Parameters:
  428. * %ax : word to print
  429. * Returns:
  430. * Nothing
  431. *****************************************************************************
  432. */
  433. print_word:
  434. /* Preserve registers */
  435. pushw %ax
  436. pushw %bx
  437. pushw %cx
  438. pushw %dx
  439. /* Build up digit sequence on stack */
  440. movw $10, %bx
  441. xorw %cx, %cx
  442. 1: xorw %dx, %dx
  443. divw %bx, %ax
  444. pushw %dx
  445. incw %cx
  446. testw %ax, %ax
  447. jnz 1b
  448. /* Print digit sequence */
  449. 1: popw %ax
  450. call print_hex_nibble
  451. loop 1b
  452. /* Restore registers and return */
  453. popw %dx
  454. popw %cx
  455. popw %bx
  456. popw %ax
  457. ret
  458. /*****************************************************************************
  459. * Subroutine: print PCI bus:dev.fn
  460. *
  461. * Parameters:
  462. * %ax : PCI bus:dev.fn to print
  463. * Returns:
  464. * Nothing
  465. *****************************************************************************
  466. */
  467. print_pci_busdevfn:
  468. /* Preserve registers */
  469. pushw %ax
  470. /* Print bus */
  471. xchgb %al, %ah
  472. call print_hex_byte
  473. /* Print ":" */
  474. movb $':', %al
  475. call print_character
  476. /* Print device */
  477. movb %ah, %al
  478. shrb $3, %al
  479. call print_hex_byte
  480. /* Print "." */
  481. movb $'.', %al
  482. call print_character
  483. /* Print function */
  484. movb %ah, %al
  485. andb $0x07, %al
  486. call print_hex_nibble
  487. /* Restore registers and return */
  488. popw %ax
  489. ret
  490. /*****************************************************************************
  491. * Subroutine: zero 1kB block of base memory
  492. *
  493. * Parameters:
  494. * %si : block to zero (in kB)
  495. * Returns:
  496. * Nothing
  497. *****************************************************************************
  498. */
  499. zero_kb:
  500. /* Preserve registers */
  501. pushw %ax
  502. pushw %cx
  503. pushw %di
  504. pushw %es
  505. /* Zero block */
  506. movw %si, %ax
  507. shlw $6, %ax
  508. movw %ax, %es
  509. movw $0x400, %cx
  510. xorw %di, %di
  511. xorw %ax, %ax
  512. rep stosb
  513. /* Restore registers and return */
  514. popw %es
  515. popw %di
  516. popw %cx
  517. popw %ax
  518. ret
  519. /*****************************************************************************
  520. * Subroutine: free and zero base memory
  521. *
  522. * Parameters:
  523. * %si : Expected current free base memory counter (in kB)
  524. * %di : Desired new free base memory counter (in kB)
  525. * %fs : BIOS data segment (0x40)
  526. * Returns:
  527. * %ax : Actual new free base memory counter (in kB)
  528. *
  529. * The base memory from %si kB to %di kB is unconditionally zeroed.
  530. * It will be freed if and only if the expected current free base
  531. * memory counter (%si) matches the actual current free base memory
  532. * counter in 0x40:0x13; if this does not match then the memory will
  533. * be leaked.
  534. *****************************************************************************
  535. */
  536. free_basemem:
  537. /* Zero base memory */
  538. pushw %si
  539. 1: cmpw %si, %di
  540. je 2f
  541. call zero_kb
  542. incw %si
  543. jmp 1b
  544. 2: popw %si
  545. /* Free base memory */
  546. movw %fs:(0x13), %ax /* Current FBMS to %ax */
  547. cmpw %ax, %si /* Update FBMS only if "old" value */
  548. jne 1f /* is correct */
  549. movw %di, %ax
  550. 1: movw %ax, %fs:(0x13)
  551. ret
  552. /*****************************************************************************
  553. * Subroutine: make a PXE API call. Works with either !PXE or PXENV+ API.
  554. *
  555. * Parameters:
  556. * %bx : PXE API call number
  557. * %ds:pxe_parameter_structure : Parameters for PXE API call
  558. * Returns:
  559. * %ax : PXE status code (not exit code)
  560. * CF set if %ax is non-zero
  561. *****************************************************************************
  562. */
  563. pxe_call:
  564. /* Preserve registers */
  565. pushw %di
  566. pushw %es
  567. /* Set up registers for PXENV+ API. %bx already set up */
  568. pushw %ds
  569. popw %es
  570. movw $pxe_parameter_structure, %di
  571. /* Set up stack for !PXE API */
  572. pushw %es
  573. pushw %di
  574. pushw %bx
  575. /* Make the API call */
  576. lcall *entry_segoff
  577. /* Reset the stack */
  578. addw $6, %sp
  579. movw pxe_parameter_structure, %ax
  580. clc
  581. testw %ax, %ax
  582. jz 1f
  583. stc
  584. 1: /* Restore registers and return */
  585. popw %es
  586. popw %di
  587. ret
  588. /*****************************************************************************
  589. * Subroutine: print PXE API call error message
  590. *
  591. * Parameters:
  592. * %ax : PXE status code
  593. * %bx : PXE API call number
  594. * Returns:
  595. * Nothing
  596. *****************************************************************************
  597. */
  598. print_pxe_error:
  599. pushw %si
  600. movw $10f, %si
  601. call print_message
  602. xchgw %ax, %bx
  603. call print_hex_word
  604. movw $20f, %si
  605. call print_message
  606. xchgw %ax, %bx
  607. call print_hex_word
  608. movw $30f, %si
  609. call print_message
  610. popw %si
  611. ret
  612. .section ".prefix.data"
  613. 10: .asciz " UNDI API call "
  614. 20: .asciz " failed: status code "
  615. 30: .asciz "\n"
  616. .previous
  617. /*****************************************************************************
  618. * PXE data structures
  619. *****************************************************************************
  620. */
  621. pxe_parameter_structure: .fill 20
  622. undi_code_segoff:
  623. undi_code_size: .word 0
  624. undi_code_segment: .word 0
  625. undi_data_segoff:
  626. undi_data_size: .word 0
  627. undi_data_segment: .word 0
  628. /* The following fields are part of a struct undi_device */
  629. undi_device:
  630. pxenv_segoff:
  631. pxenv_offset: .word 0
  632. pxenv_segment: .word 0
  633. ppxe_segoff:
  634. ppxe_offset: .word 0
  635. ppxe_segment: .word 0
  636. entry_segoff:
  637. entry_offset: .word 0
  638. entry_segment: .word 0
  639. undi_fbms_start: .word 0
  640. undi_fbms_end: .word 0
  641. pci_busdevfn: .word UNDI_NO_PCI_BUSDEVFN
  642. isapnp_csn: .word UNDI_NO_ISAPNP_CSN
  643. isapnp_read_port: .word UNDI_NO_ISAPNP_READ_PORT
  644. pci_vendor: .word 0
  645. pci_device: .word 0
  646. flags: .word UNDI_FL_STARTED
  647. .equ undi_device_size, ( . - undi_device )
  648. /*****************************************************************************
  649. * Run Etherboot main code
  650. *****************************************************************************
  651. */
  652. run_etherboot:
  653. /* Install Etherboot */
  654. call install
  655. /* Set up real-mode stack */
  656. movw %bx, %ss
  657. movw $_estack16, %sp
  658. #ifdef PXELOADER_KEEP_UNDI
  659. /* Copy our undi_device structure to the preloaded_undi variable */
  660. movw %bx, %es
  661. movw $preloaded_undi, %di
  662. movw $undi_device, %si
  663. movw $undi_device_size, %cx
  664. rep movsb
  665. #endif
  666. /* Jump to .text16 segment with %ds pointing to .data16 */
  667. movw %bx, %ds
  668. pushw %ax
  669. pushw $1f
  670. lret
  671. .section ".text16", "ax", @progbits
  672. 1:
  673. /* Run main program */
  674. pushl $main
  675. pushw %cs
  676. call prot_call
  677. popl %eax /* discard */
  678. /* Boot next device */
  679. int $0x18
  680. .previous