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.

pcivpd.c 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  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. * You can also choose to distribute this program under the terms of
  20. * the Unmodified Binary Distribution Licence (as given in the file
  21. * COPYING.UBDL), provided that you have satisfied its requirements.
  22. */
  23. FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
  24. #include <stdint.h>
  25. #include <stdlib.h>
  26. #include <unistd.h>
  27. #include <errno.h>
  28. #include <byteswap.h>
  29. #include <ipxe/pci.h>
  30. #include <ipxe/isapnp.h>
  31. #include <ipxe/pcivpd.h>
  32. /** @file
  33. *
  34. * PCI Vital Product Data
  35. *
  36. */
  37. /**
  38. * Initialise PCI Vital Product Data
  39. *
  40. * @v vpd PCI VPD
  41. * @v pci PCI device
  42. * @ret rc Return status code
  43. */
  44. int pci_vpd_init ( struct pci_vpd *vpd, struct pci_device *pci ) {
  45. /* Initialise structure */
  46. vpd->pci = pci;
  47. pci_vpd_invalidate_cache ( vpd );
  48. /* Locate VPD capability */
  49. vpd->cap = pci_find_capability ( pci, PCI_CAP_ID_VPD );
  50. if ( ! vpd->cap ) {
  51. DBGC ( vpd, PCI_FMT " does not support VPD\n",
  52. PCI_ARGS ( pci ) );
  53. return -ENOTTY;
  54. }
  55. DBGC ( vpd, PCI_FMT " VPD is at offset %02x\n",
  56. PCI_ARGS ( pci ), vpd->cap );
  57. return 0;
  58. }
  59. /**
  60. * Read one dword of PCI Vital Product Data
  61. *
  62. * @v vpd PCI VPD
  63. * @v address Address to read
  64. * @ret data Read data
  65. * @ret rc Return status code
  66. */
  67. static int pci_vpd_read_dword ( struct pci_vpd *vpd, int address,
  68. uint32_t *data ) {
  69. struct pci_device *pci = vpd->pci;
  70. unsigned int cap = vpd->cap;
  71. unsigned int retries;
  72. uint16_t flag;
  73. /* Fail if no VPD present */
  74. if ( ! cap )
  75. return -ENOTTY;
  76. /* Return cached value, if present */
  77. if ( pci_vpd_cache_is_valid ( vpd ) &&
  78. ( vpd->cache.address == address ) ) {
  79. *data = vpd->cache.data;
  80. return 0;
  81. }
  82. /* Initiate read */
  83. pci_write_config_word ( pci, ( cap + PCI_VPD_ADDRESS ), address );
  84. /* Wait for read to complete */
  85. for ( retries = 0 ; retries < PCI_VPD_MAX_WAIT_MS ; retries++ ) {
  86. /* Check if data is ready */
  87. pci_read_config_word ( pci, ( cap + PCI_VPD_ADDRESS ), &flag );
  88. if ( flag & PCI_VPD_FLAG ) {
  89. /* Read data */
  90. pci_read_config_dword ( pci, ( cap + PCI_VPD_DATA ),
  91. data );
  92. DBGC2 ( vpd, PCI_FMT " VPD %04x => %08x\n",
  93. PCI_ARGS ( pci ), address, htonl ( *data ) );
  94. /* Populate cache */
  95. vpd->cache.address = address;
  96. vpd->cache.data = *data;
  97. return 0;
  98. }
  99. /* Wait 1ms before retrying */
  100. mdelay ( 1 );
  101. }
  102. DBGC ( vpd, PCI_FMT " VPD %04x read via %02x timed out\n",
  103. PCI_ARGS ( pci ), address, cap );
  104. return -ETIMEDOUT;
  105. }
  106. /**
  107. * Write one dword of PCI Vital Product Data
  108. *
  109. * @v vpd PCI VPD
  110. * @v address Address to write
  111. * @v data Data to write
  112. * @ret rc Return status code
  113. */
  114. static int pci_vpd_write_dword ( struct pci_vpd *vpd, int address,
  115. uint32_t data ) {
  116. struct pci_device *pci = vpd->pci;
  117. unsigned int cap = vpd->cap;
  118. unsigned int retries;
  119. uint16_t flag;
  120. /* Fail if no VPD present */
  121. if ( ! cap )
  122. return -ENOTTY;
  123. /* Invalidate cache */
  124. pci_vpd_invalidate_cache ( vpd );
  125. DBGC2 ( vpd, PCI_FMT " VPD %04x <= %08x\n",
  126. PCI_ARGS ( pci ), address, htonl ( data ) );
  127. /* Write data */
  128. pci_write_config_dword ( pci, ( cap + PCI_VPD_DATA ), data );
  129. /* Initiate write */
  130. pci_write_config_word ( pci, ( cap + PCI_VPD_ADDRESS ),
  131. ( address | PCI_VPD_FLAG ) );
  132. /* Wait for write to complete */
  133. for ( retries = 0 ; retries < PCI_VPD_MAX_WAIT_MS ; retries++ ) {
  134. /* Check if write has completed */
  135. pci_read_config_word ( pci, ( cap + PCI_VPD_ADDRESS ), &flag );
  136. if ( ! ( flag & PCI_VPD_FLAG ) )
  137. return 0;
  138. /* Wait 1ms before retrying */
  139. mdelay ( 1 );
  140. }
  141. DBGC ( vpd, PCI_FMT " VPD %04x write via %02x timed out\n",
  142. PCI_ARGS ( pci ), address, cap );
  143. return -ETIMEDOUT;
  144. }
  145. /**
  146. * Read PCI VPD
  147. *
  148. * @v vpd PCI VPD
  149. * @v address Starting address
  150. * @v buf Data buffer
  151. * @v len Length of data buffer
  152. * @ret rc Return status code
  153. */
  154. int pci_vpd_read ( struct pci_vpd *vpd, unsigned int address, void *buf,
  155. size_t len ) {
  156. uint8_t *bytes = buf;
  157. uint32_t data;
  158. size_t skip_len;
  159. unsigned int i;
  160. int rc;
  161. /* Calculate length to skip at start of data */
  162. skip_len = ( address & 0x03 );
  163. /* Read data, a dword at a time */
  164. for ( address &= ~0x03 ; len ; address += 4 ) {
  165. /* Read whole dword */
  166. if ( ( rc = pci_vpd_read_dword ( vpd, address, &data ) ) != 0 )
  167. return rc;
  168. /* Copy data to buffer */
  169. for ( i = 4 ; i ; i-- ) {
  170. if ( skip_len ) {
  171. skip_len--;
  172. } else if ( len ) {
  173. *(bytes++) = data;
  174. len--;
  175. }
  176. data = ( ( data << 24 ) | ( data >> 8 ) );
  177. }
  178. }
  179. return 0;
  180. }
  181. /**
  182. * Write PCI VPD
  183. *
  184. * @v vpd PCI VPD
  185. * @v address Starting address
  186. * @v buf Data buffer
  187. * @v len Length of data buffer
  188. * @ret rc Return status code
  189. */
  190. int pci_vpd_write ( struct pci_vpd *vpd, unsigned int address, const void *buf,
  191. size_t len ) {
  192. const uint8_t *bytes = buf;
  193. uint32_t data;
  194. size_t skip_len;
  195. unsigned int i;
  196. int rc;
  197. /* Calculate length to skip at start of data */
  198. skip_len = ( address & 0x03 );
  199. /* Write data, a dword at a time */
  200. for ( address &= ~0x03 ; len ; address += 4 ) {
  201. /* Read existing dword, if necessary */
  202. if ( skip_len || ( len <= 0x03 ) ) {
  203. if ( ( rc = pci_vpd_read_dword ( vpd, address,
  204. &data ) ) != 0 )
  205. return rc;
  206. }
  207. /* Copy data from buffer */
  208. for ( i = 4 ; i ; i-- ) {
  209. if ( skip_len ) {
  210. skip_len--;
  211. } else if ( len ) {
  212. data = ( ( data & ~0xff ) | *(bytes++) );
  213. len--;
  214. }
  215. data = ( ( data << 24 ) | ( data >> 8 ) );
  216. }
  217. /* Write whole dword */
  218. if ( ( rc = pci_vpd_write_dword ( vpd, address, data ) ) != 0 )
  219. return rc;
  220. }
  221. return 0;
  222. }
  223. /**
  224. * Dump PCI VPD region (for debugging)
  225. *
  226. * @v vpd PCI VPD
  227. * @v address Starting address
  228. * @v len Length of data
  229. */
  230. static void pci_vpd_dump ( struct pci_vpd *vpd, unsigned int address,
  231. size_t len ) {
  232. int rc;
  233. /* Do nothing in non-debug builds */
  234. if ( ! DBG_LOG )
  235. return;
  236. /* Read data */
  237. {
  238. char buf[len];
  239. if ( ( rc = pci_vpd_read ( vpd, address, buf,
  240. sizeof ( buf ) ) ) != 0 )
  241. return;
  242. DBGC_HDA ( vpd, address, buf, sizeof ( buf ) );
  243. }
  244. }
  245. /**
  246. * Locate PCI VPD tag
  247. *
  248. * @v vpd PCI VPD
  249. * @v tag ISAPnP tag
  250. * @ret address Address of tag body
  251. * @ret len Length of tag body
  252. * @ret rc Return status code
  253. */
  254. static int pci_vpd_find_tag ( struct pci_vpd *vpd, unsigned int tag,
  255. unsigned int *address, size_t *len ) {
  256. uint8_t read_tag;
  257. uint16_t read_len;
  258. int rc;
  259. /* Scan through tags looking for a match */
  260. *address = 0;
  261. do {
  262. /* Read tag byte */
  263. if ( ( rc = pci_vpd_read ( vpd, (*address)++, &read_tag,
  264. sizeof ( read_tag ) ) ) != 0 )
  265. return rc;
  266. /* Extract tag and length */
  267. if ( ISAPNP_IS_LARGE_TAG ( read_tag ) ) {
  268. if ( ( rc = pci_vpd_read ( vpd, *address, &read_len,
  269. sizeof ( read_len ) ) ) != 0)
  270. return rc;
  271. *address += sizeof ( read_len );
  272. read_len = le16_to_cpu ( read_len );
  273. read_tag = ISAPNP_LARGE_TAG_NAME ( read_tag );
  274. } else {
  275. read_len = ISAPNP_SMALL_TAG_LEN ( read_tag );
  276. read_tag = ISAPNP_SMALL_TAG_NAME ( read_tag );
  277. }
  278. /* Check for tag match */
  279. if ( tag == read_tag ) {
  280. *len = read_len;
  281. DBGC ( vpd, PCI_FMT " VPD tag %02x is at "
  282. "[%04x,%04zx)\n", PCI_ARGS ( vpd->pci ), tag,
  283. *address, ( *address + *len ) );
  284. return 0;
  285. }
  286. /* Move to next tag */
  287. *address += read_len;
  288. } while ( read_tag != ISAPNP_TAG_END );
  289. DBGC ( vpd, PCI_FMT " VPD tag %02x not found\n",
  290. PCI_ARGS ( vpd->pci ), tag );
  291. return -ENOENT;
  292. }
  293. /**
  294. * Locate PCI VPD field
  295. *
  296. * @v vpd PCI VPD
  297. * @v field VPD field descriptor
  298. * @ret address Address of field body
  299. * @ret len Length of field body
  300. * @ret rc Return status code
  301. */
  302. int pci_vpd_find ( struct pci_vpd *vpd, unsigned int field,
  303. unsigned int *address, size_t *len ) {
  304. struct pci_vpd_field read_field;
  305. int rc;
  306. /* Locate containing tag */
  307. if ( ( rc = pci_vpd_find_tag ( vpd, PCI_VPD_TAG ( field ),
  308. address, len ) ) != 0 )
  309. return rc;
  310. /* Return immediately if we are searching for a whole-tag field */
  311. if ( ! PCI_VPD_KEYWORD ( field ) ) {
  312. pci_vpd_dump ( vpd, *address, *len );
  313. return 0;
  314. }
  315. /* Scan through fields looking for a match */
  316. while ( *len >= sizeof ( read_field ) ) {
  317. /* Read field header */
  318. if ( ( rc = pci_vpd_read ( vpd, *address, &read_field,
  319. sizeof ( read_field ) ) ) != 0 )
  320. return rc;
  321. *address += sizeof ( read_field );
  322. *len -= sizeof ( read_field );
  323. /* Check for keyword match */
  324. if ( read_field.keyword == PCI_VPD_KEYWORD ( field ) ) {
  325. *len = read_field.len;
  326. DBGC ( vpd, PCI_FMT " VPD field " PCI_VPD_FIELD_FMT
  327. " is at [%04x,%04zx)\n", PCI_ARGS ( vpd->pci ),
  328. PCI_VPD_FIELD_ARGS ( field ),
  329. *address, ( *address + *len ) );
  330. pci_vpd_dump ( vpd, *address, *len );
  331. return 0;
  332. }
  333. /* Move to next field */
  334. if ( read_field.len > *len )
  335. break;
  336. *address += read_field.len;
  337. *len -= read_field.len;
  338. }
  339. DBGC ( vpd, PCI_FMT " VPD field " PCI_VPD_FIELD_FMT " not found\n",
  340. PCI_ARGS ( vpd->pci ), PCI_VPD_FIELD_ARGS ( field ) );
  341. return -ENOENT;
  342. }
  343. /**
  344. * Resize VPD field
  345. *
  346. * @v vpd PCI VPD
  347. * @v field VPD field descriptor
  348. * @v len New length of field body
  349. * @ret address Address of field body
  350. * @ret rc Return status code
  351. */
  352. int pci_vpd_resize ( struct pci_vpd *vpd, unsigned int field, size_t len,
  353. unsigned int *address ) {
  354. struct pci_vpd_field rw_field;
  355. struct pci_vpd_field old_field;
  356. struct pci_vpd_field new_field;
  357. unsigned int rw_address;
  358. unsigned int old_address;
  359. unsigned int copy_address;
  360. unsigned int dst_address;
  361. unsigned int dump_address;
  362. size_t rw_len;
  363. size_t old_len;
  364. size_t available_len;
  365. size_t copy_len;
  366. size_t dump_len;
  367. void *copy;
  368. int rc;
  369. /* Sanity checks */
  370. assert ( PCI_VPD_TAG ( field ) == PCI_VPD_TAG_RW );
  371. assert ( PCI_VPD_KEYWORD ( field ) != 0 );
  372. assert ( field != PCI_VPD_FIELD_RW );
  373. /* Locate 'RW' field */
  374. if ( ( rc = pci_vpd_find ( vpd, PCI_VPD_FIELD_RW, &rw_address,
  375. &rw_len ) ) != 0 )
  376. goto err_no_rw;
  377. /* Locate old field, if any */
  378. if ( ( rc = pci_vpd_find ( vpd, field, &old_address,
  379. &old_len ) ) == 0 ) {
  380. /* Field already exists */
  381. if ( old_address > rw_address ) {
  382. DBGC ( vpd, PCI_FMT " VPD field " PCI_VPD_FIELD_FMT
  383. " at [%04x,%04zx) is after field "
  384. PCI_VPD_FIELD_FMT " at [%04x,%04zx)\n",
  385. PCI_ARGS ( vpd->pci ),
  386. PCI_VPD_FIELD_ARGS ( field ),
  387. old_address, ( old_address + old_len ),
  388. PCI_VPD_FIELD_ARGS ( PCI_VPD_FIELD_RW ),
  389. rw_address, ( rw_address + rw_len ) );
  390. rc = -ENXIO;
  391. goto err_after_rw;
  392. }
  393. dst_address = ( old_address - sizeof ( old_field ) );
  394. copy_address = ( old_address + old_len );
  395. copy_len = ( rw_address - sizeof ( rw_field ) - copy_address );
  396. /* Calculate available length */
  397. available_len = ( rw_len + old_len );
  398. } else {
  399. /* Field does not yet exist */
  400. dst_address = ( rw_address - sizeof ( rw_field ) );
  401. copy_address = dst_address;
  402. copy_len = 0;
  403. /* Calculate available length */
  404. available_len = ( ( rw_len > sizeof ( new_field ) ) ?
  405. ( rw_len - sizeof ( new_field ) ) : 0 );
  406. }
  407. /* Dump region before changes */
  408. dump_address = dst_address;
  409. dump_len = ( rw_address + rw_len - dump_address );
  410. DBGC ( vpd, PCI_FMT " VPD before resizing field " PCI_VPD_FIELD_FMT
  411. " to %zd bytes:\n", PCI_ARGS ( vpd->pci ),
  412. PCI_VPD_FIELD_ARGS ( field ), len );
  413. pci_vpd_dump ( vpd, dump_address, dump_len );
  414. /* Check available length */
  415. if ( available_len > PCI_VPD_MAX_LEN )
  416. available_len = PCI_VPD_MAX_LEN;
  417. if ( len > available_len ) {
  418. DBGC ( vpd, PCI_FMT " VPD no space for field "
  419. PCI_VPD_FIELD_FMT " (need %02zx, have %02zx)\n",
  420. PCI_ARGS ( vpd->pci ), PCI_VPD_FIELD_ARGS ( field ),
  421. len, available_len );
  422. rc = -ENOSPC;
  423. goto err_no_space;
  424. }
  425. /* Preserve intermediate fields, if any */
  426. copy = malloc ( copy_len );
  427. if ( ! copy ) {
  428. rc = -ENOMEM;
  429. goto err_copy_alloc;
  430. }
  431. if ( ( rc = pci_vpd_read ( vpd, copy_address, copy, copy_len ) ) != 0 )
  432. goto err_copy_read;
  433. /* Create new field, if applicable */
  434. if ( len ) {
  435. new_field.keyword = PCI_VPD_KEYWORD ( field );
  436. new_field.len = len;
  437. if ( ( rc = pci_vpd_write ( vpd, dst_address, &new_field,
  438. sizeof ( new_field ) ) ) != 0 )
  439. goto err_new_write;
  440. dst_address += sizeof ( new_field );
  441. *address = dst_address;
  442. DBGC ( vpd, PCI_FMT " VPD field " PCI_VPD_FIELD_FMT " is now "
  443. "at [%04x,%04x)\n", PCI_ARGS ( vpd->pci ),
  444. PCI_VPD_FIELD_ARGS ( field ), dst_address,
  445. ( dst_address + new_field.len ) );
  446. dst_address += len;
  447. } else {
  448. DBGC ( vpd, PCI_FMT " VPD field " PCI_VPD_FIELD_FMT
  449. " no longer exists\n", PCI_ARGS ( vpd->pci ),
  450. PCI_VPD_FIELD_ARGS ( field ) );
  451. }
  452. /* Restore intermediate fields, if any */
  453. if ( ( rc = pci_vpd_write ( vpd, dst_address, copy, copy_len ) ) != 0 )
  454. goto err_copy_write;
  455. dst_address += copy_len;
  456. /* Create 'RW' field */
  457. rw_field.keyword = PCI_VPD_KEYWORD ( PCI_VPD_FIELD_RW );
  458. rw_field.len = ( rw_len +
  459. ( rw_address - sizeof ( rw_field ) ) - dst_address );
  460. if ( ( rc = pci_vpd_write ( vpd, dst_address, &rw_field,
  461. sizeof ( rw_field ) ) ) != 0 )
  462. goto err_rw_write;
  463. dst_address += sizeof ( rw_field );
  464. DBGC ( vpd, PCI_FMT " VPD field " PCI_VPD_FIELD_FMT " is now "
  465. "at [%04x,%04x)\n", PCI_ARGS ( vpd->pci ),
  466. PCI_VPD_FIELD_ARGS ( PCI_VPD_FIELD_RW ), dst_address,
  467. ( dst_address + rw_field.len ) );
  468. /* Dump region after changes */
  469. DBGC ( vpd, PCI_FMT " VPD after resizing field " PCI_VPD_FIELD_FMT
  470. " to %zd bytes:\n", PCI_ARGS ( vpd->pci ),
  471. PCI_VPD_FIELD_ARGS ( field ), len );
  472. pci_vpd_dump ( vpd, dump_address, dump_len );
  473. rc = 0;
  474. err_rw_write:
  475. err_new_write:
  476. err_copy_write:
  477. err_copy_read:
  478. free ( copy );
  479. err_copy_alloc:
  480. err_no_space:
  481. err_after_rw:
  482. err_no_rw:
  483. return rc;
  484. }