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