Annotation of qemu/roms/ipxe/src/drivers/bus/pcivpd.c, revision 1.1.1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.