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

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