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

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