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.

pxeprefix.S 15KB

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