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.

mromprefix.S 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. /*
  2. * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
  3. *
  4. * This program is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU General Public License as
  6. * published by the Free Software Foundation; either version 2 of the
  7. * License, or any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful, but
  10. * WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  17. * 02110-1301, USA.
  18. *
  19. */
  20. FILE_LICENCE ( GPL2_OR_LATER )
  21. #define PCIBIOS_READ_CONFIG_WORD 0xb109
  22. #define PCIBIOS_READ_CONFIG_DWORD 0xb10a
  23. #define PCIBIOS_WRITE_CONFIG_WORD 0xb10c
  24. #define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d
  25. #define PCI_COMMAND 0x04
  26. #define PCI_COMMAND_MEM 0x02
  27. #define PCI_BAR_0 0x10
  28. #define PCI_BAR_5 0x24
  29. #define PCI_BAR_EXPROM 0x30
  30. #define PCIR_SIGNATURE ( 'P' + ( 'C' << 8 ) + ( 'I' << 16 ) + ( 'R' << 24 ) )
  31. #define ROMPREFIX_EXCLUDE_PAYLOAD 1
  32. #define ROMPREFIX_MORE_IMAGES 1
  33. #define _pcirom_start _mrom_start
  34. #include "pciromprefix.S"
  35. .text
  36. .arch i386
  37. .code16
  38. /* Obtain access to payload by exposing the expansion ROM BAR at the
  39. * address currently used by a suitably large memory BAR on the same
  40. * device. The memory BAR is temporarily disabled. Using a memory
  41. * BAR on the same device means that we don't have to worry about the
  42. * configuration of any intermediate PCI bridges.
  43. *
  44. * Parameters:
  45. * %ds:0000 : Prefix
  46. * %esi : Buffer for copy of image source (or zero if no buffer available)
  47. * %ecx : Expected offset within buffer of first payload block
  48. * Returns:
  49. * %esi : Valid image source address (buffered or unbuffered)
  50. * %ecx : Actual offset within buffer of first payload block
  51. * CF set on error
  52. */
  53. .section ".text16.early", "awx", @progbits
  54. .globl open_payload
  55. open_payload:
  56. /* Preserve registers */
  57. pushl %eax
  58. pushw %bx
  59. pushl %edx
  60. pushl %edi
  61. pushw %bp
  62. pushw %es
  63. pushw %ds
  64. /* Retrieve bus:dev.fn from .prefix */
  65. movw init_pci_busdevfn, %bx
  66. /* Set up %ds for access to .text16.early */
  67. pushw %cs
  68. popw %ds
  69. /* Set up %es for access to flat address space */
  70. xorw %ax, %ax
  71. movw %ax, %es
  72. /* Store bus:dev.fn to .text16.early */
  73. movw %bx, payload_pci_busdevfn
  74. /* Get expansion ROM BAR current value */
  75. movw $PCI_BAR_EXPROM, %di
  76. call pci_read_bar
  77. movl %eax, rom_bar_orig_value
  78. /* Get expansion ROM BAR size */
  79. call pci_size_mem_bar_low
  80. movl %ecx, rom_bar_size
  81. /* Find a suitable memory BAR to use */
  82. movw $PCI_BAR_0, %di /* %di is PCI BAR register */
  83. xorw %bp, %bp /* %bp is increment */
  84. find_mem_bar:
  85. /* Move to next BAR */
  86. addw %bp, %di
  87. cmpw $PCI_BAR_5, %di
  88. jle 1f
  89. stc
  90. movl $0xbabababa, %esi /* Report "No suitable BAR" */
  91. movl rom_bar_size, %ecx
  92. jmp 99f
  93. 1: movw $4, %bp
  94. /* Get BAR current value */
  95. call pci_read_bar
  96. /* Skip non-existent BARs */
  97. notl %eax
  98. testl %eax, %eax
  99. notl %eax
  100. jz find_mem_bar
  101. /* Skip I/O BARs */
  102. testb $0x01, %al
  103. jnz find_mem_bar
  104. /* Set increment to 8 for 64-bit BARs */
  105. testb $0x04, %al
  106. jz 1f
  107. movw $8, %bp
  108. 1:
  109. /* Skip 64-bit BARs with high dword set; we couldn't use this
  110. * address for the (32-bit) expansion ROM BAR anyway
  111. */
  112. testl %edx, %edx
  113. jnz find_mem_bar
  114. /* Get low dword of BAR size */
  115. call pci_size_mem_bar_low
  116. /* Skip BARs smaller than the expansion ROM BAR */
  117. cmpl %ecx, rom_bar_size
  118. ja find_mem_bar
  119. /* We have a memory BAR with a 32-bit address that is large
  120. * enough to use. Store BAR number and original value.
  121. */
  122. movw %di, stolen_bar_register
  123. movl %eax, stolen_bar_orig_value
  124. /* Remove flags from BAR address */
  125. xorb %al, %al
  126. /* Write zero to our stolen BAR. This doesn't technically
  127. * disable it, but it's a pretty safe bet that the PCI bridge
  128. * won't pass through accesses to this region anyway. Note
  129. * that the high dword (if any) must already be zero.
  130. */
  131. xorl %ecx, %ecx
  132. call pci_write_config_dword
  133. /* Enable expansion ROM BAR at stolen BAR's address */
  134. movl %eax, %ecx
  135. orb $0x1, %cl
  136. movw $PCI_BAR_EXPROM, %di
  137. call pci_write_config_dword
  138. /* Locate our ROM image */
  139. 1: movl $0xaa55, %ecx /* 55aa signature */
  140. addr32 es cmpw %cx, (%eax)
  141. jne 2f
  142. movl $PCIR_SIGNATURE, %ecx /* PCIR signature */
  143. addr32 es movzwl 0x18(%eax), %edx
  144. addr32 es cmpl %ecx, (%eax,%edx)
  145. jne 2f
  146. addr32 es cmpl $_build_id, build_id(%eax) /* iPXE build ID */
  147. je 3f
  148. movl $0x80, %ecx /* Last image */
  149. addr32 es testb %cl, 0x15(%eax,%edx)
  150. jnz 2f
  151. addr32 es movzwl 0x10(%eax,%edx), %ecx /* PCIR image length */
  152. shll $9, %ecx
  153. addl %ecx, %eax
  154. jmp 1b
  155. 2: /* Failure */
  156. stc
  157. movl %eax, %esi /* Report failure address */
  158. jmp 99f
  159. 3:
  160. /* Copy payload to buffer, or set buffer address to BAR address */
  161. testl %esi, %esi
  162. jz 1f
  163. /* We have a buffer; copy payload to it. Since .mrom is
  164. * designed specifically for real hardware, we assume that
  165. * flat real mode is working properly. (In the unlikely event
  166. * that this code is run inside a hypervisor that doesn't
  167. * properly support flat real mode, it will die horribly.)
  168. */
  169. pushl %esi
  170. movl %esi, %edi
  171. movl %eax, %esi
  172. addr32 es movzbl 2(%esi), %ecx
  173. shll $7, %ecx
  174. addr32 es movzwl mpciheader_image_length(%esi,%ecx,4), %edx
  175. shll $7, %edx
  176. addl %edx, %ecx
  177. addr32 es rep movsl
  178. popl %esi
  179. jmp 2f
  180. 1: /* We have no buffer; set %esi to the BAR address */
  181. movl %eax, %esi
  182. 2:
  183. /* Locate first payload block (after the dummy ROM header) */
  184. addr32 es movzbl 2(%esi), %ecx
  185. shll $9, %ecx
  186. addl $_pprefix_skip, %ecx
  187. clc
  188. /* Restore registers and return */
  189. 99: popw %ds
  190. popw %es
  191. popw %bp
  192. popl %edi
  193. popl %edx
  194. popw %bx
  195. popl %eax
  196. lret
  197. .size open_payload, . - open_payload
  198. .section ".text16.early.data", "aw", @progbits
  199. payload_pci_busdevfn:
  200. .word 0
  201. .size payload_pci_busdevfn, . - payload_pci_busdevfn
  202. .section ".text16.early.data", "aw", @progbits
  203. rom_bar_orig_value:
  204. .long 0
  205. .size rom_bar_orig_value, . - rom_bar_orig_value
  206. .section ".text16.early.data", "aw", @progbits
  207. rom_bar_size:
  208. .long 0
  209. .size rom_bar_size, . - rom_bar_size
  210. .section ".text16.early.data", "aw", @progbits
  211. stolen_bar_register:
  212. .word 0
  213. .size stolen_bar_register, . - stolen_bar_register
  214. .section ".text16.early.data", "aw", @progbits
  215. stolen_bar_orig_value:
  216. .long 0
  217. .size stolen_bar_orig_value, . - stolen_bar_orig_value
  218. /* Restore original BAR values
  219. *
  220. * Parameters:
  221. * none
  222. * Returns:
  223. * none
  224. */
  225. .section ".text16.early", "awx", @progbits
  226. .globl close_payload
  227. close_payload:
  228. /* Preserve registers */
  229. pushw %bx
  230. pushw %di
  231. pushl %ecx
  232. pushw %ds
  233. /* Set up %ds for access to .text16.early */
  234. pushw %cs
  235. popw %ds
  236. /* Retrieve stored bus:dev.fn */
  237. movw payload_pci_busdevfn, %bx
  238. /* Restore expansion ROM BAR original value */
  239. movw $PCI_BAR_EXPROM, %di
  240. movl rom_bar_orig_value, %ecx
  241. call pci_write_config_dword
  242. /* Restore stolen BAR original value */
  243. movw stolen_bar_register, %di
  244. movl stolen_bar_orig_value, %ecx
  245. call pci_write_config_dword
  246. /* Restore registers and return */
  247. popw %ds
  248. popl %ecx
  249. popw %di
  250. popw %bx
  251. lret
  252. .size close_payload, . - close_payload
  253. /* Get PCI BAR value
  254. *
  255. * Parameters:
  256. * %bx : PCI bus:dev.fn
  257. * %di : PCI BAR register number
  258. * Returns:
  259. * %edx:%eax : PCI BAR value
  260. */
  261. .section ".text16.early", "awx", @progbits
  262. pci_read_bar:
  263. /* Preserve registers */
  264. pushl %ecx
  265. pushw %di
  266. /* Read low dword value */
  267. call pci_read_config_dword
  268. movl %ecx, %eax
  269. /* Read high dword value, if applicable */
  270. xorl %edx, %edx
  271. andb $0x07, %cl
  272. cmpb $0x04, %cl
  273. jne 1f
  274. addw $4, %di
  275. call pci_read_config_dword
  276. movl %ecx, %edx
  277. 1:
  278. /* Restore registers and return */
  279. popw %di
  280. popl %ecx
  281. ret
  282. .size pci_read_bar, . - pci_read_bar
  283. /* Get low dword of PCI memory BAR size
  284. *
  285. * Parameters:
  286. * %bx : PCI bus:dev.fn
  287. * %di : PCI BAR register number
  288. * %eax : Low dword of current PCI BAR value
  289. * Returns:
  290. * %ecx : PCI BAR size
  291. */
  292. .section ".text16.early", "awx", @progbits
  293. pci_size_mem_bar_low:
  294. /* Preserve registers */
  295. pushw %dx
  296. /* Disable memory accesses */
  297. xorw %dx, %dx
  298. call pci_set_mem_access
  299. /* Write all ones to BAR */
  300. xorl %ecx, %ecx
  301. decl %ecx
  302. call pci_write_config_dword
  303. /* Read back BAR */
  304. call pci_read_config_dword
  305. /* Calculate size */
  306. notl %ecx
  307. orb $0x0f, %cl
  308. incl %ecx
  309. /* Restore original value */
  310. pushl %ecx
  311. movl %eax, %ecx
  312. call pci_write_config_dword
  313. popl %ecx
  314. /* Enable memory accesses */
  315. movw $PCI_COMMAND_MEM, %dx
  316. call pci_set_mem_access
  317. /* Restore registers and return */
  318. popw %dx
  319. ret
  320. .size pci_size_mem_bar_low, . - pci_size_mem_bar_low
  321. /* Read PCI config dword
  322. *
  323. * Parameters:
  324. * %bx : PCI bus:dev.fn
  325. * %di : PCI register number
  326. * Returns:
  327. * %ecx : Dword value
  328. */
  329. .section ".text16.early", "awx", @progbits
  330. pci_read_config_dword:
  331. /* Preserve registers */
  332. pushl %eax
  333. pushl %ebx
  334. pushl %edx
  335. /* Issue INT 0x1a,b10a */
  336. movw $PCIBIOS_READ_CONFIG_DWORD, %ax
  337. int $0x1a
  338. /* Restore registers and return */
  339. popl %edx
  340. popl %ebx
  341. popl %eax
  342. ret
  343. .size pci_read_config_dword, . - pci_read_config_dword
  344. /* Write PCI config dword
  345. *
  346. * Parameters:
  347. * %bx : PCI bus:dev.fn
  348. * %di : PCI register number
  349. * %ecx : PCI BAR value
  350. * Returns:
  351. * none
  352. */
  353. .section ".text16.early", "awx", @progbits
  354. pci_write_config_dword:
  355. /* Preserve registers */
  356. pushal
  357. /* Issue INT 0x1a,b10d */
  358. movw $PCIBIOS_WRITE_CONFIG_DWORD, %ax
  359. int $0x1a
  360. /* Restore registers and return */
  361. popal
  362. ret
  363. .size pci_write_config_dword, . - pci_write_config_dword
  364. /* Enable/disable memory access response in PCI command word
  365. *
  366. * Parameters:
  367. * %bx : PCI bus:dev.fn
  368. * %dx : PCI_COMMAND_MEM, or zero
  369. * Returns:
  370. * none
  371. */
  372. .section ".text16.early", "awx", @progbits
  373. pci_set_mem_access:
  374. /* Preserve registers */
  375. pushal
  376. /* Read current value of command register */
  377. pushw %bx
  378. pushw %dx
  379. movw $PCI_COMMAND, %di
  380. movw $PCIBIOS_READ_CONFIG_WORD, %ax
  381. int $0x1a
  382. popw %dx
  383. popw %bx
  384. /* Set memory access enable as appropriate */
  385. andw $~PCI_COMMAND_MEM, %cx
  386. orw %dx, %cx
  387. /* Write new value of command register */
  388. movw $PCIBIOS_WRITE_CONFIG_WORD, %ax
  389. int $0x1a
  390. /* Restore registers and return */
  391. popal
  392. ret
  393. .size pci_set_mem_access, . - pci_set_mem_access
  394. /* Payload prefix
  395. *
  396. * We include a dummy ROM header to cover the "hidden" portion of the
  397. * overall ROM image.
  398. */
  399. .globl _payload_align
  400. .equ _payload_align, 512
  401. .section ".pprefix", "ax", @progbits
  402. .org 0x00
  403. mromheader:
  404. .word 0xaa55 /* BIOS extension signature */
  405. .org 0x18
  406. .word mpciheader
  407. .org 0x1a
  408. .word 0
  409. .size mromheader, . - mromheader
  410. mpciheader:
  411. .ascii "PCIR" /* Signature */
  412. .word pci_vendor_id /* Vendor identification */
  413. .word pci_device_id /* Device identification */
  414. .word 0x0000 /* Device list pointer */
  415. .word mpciheader_len /* PCI data structure length */
  416. .byte 0x03 /* PCI data structure revision */
  417. .byte 0x02, 0x00, 0x00 /* Class code */
  418. mpciheader_image_length:
  419. .word 0 /* Image length */
  420. .word 0x0001 /* Revision level */
  421. .byte 0xff /* Code type */
  422. .byte 0x80 /* Last image indicator */
  423. mpciheader_runtime_length:
  424. .word 0 /* Maximum run-time image length */
  425. .word 0x0000 /* Configuration utility code header */
  426. .word 0x0000 /* DMTF CLP entry point */
  427. .equ mpciheader_len, . - mpciheader
  428. .size mpciheader, . - mpciheader
  429. .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
  430. .ascii "APPW"
  431. .long mpciheader_image_length
  432. .long 512
  433. .long 0
  434. .ascii "APPW"
  435. .long mpciheader_runtime_length
  436. .long 512
  437. .long 0
  438. .previous
  439. /* Fix up additional image source size
  440. *
  441. */
  442. .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
  443. .ascii "ADPW"
  444. .long extra_size
  445. .long 512
  446. .long 0
  447. .previous