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