iPXE
Data Structures | Defines | Functions
pcivpd.h File Reference

PCI Vital Product Data. More...

#include <stdint.h>
#include <byteswap.h>
#include <ipxe/isapnp.h>
#include <ipxe/pci.h>

Go to the source code of this file.

Data Structures

struct  pci_vpd_field
 A PCI VPD field. More...
struct  pci_vpd_cache
 PCI VPD cache. More...
struct  pci_vpd
 PCI VPD. More...

Defines

#define PCI_VPD_ADDRESS   0x02
 PCI VPD address register.
#define PCI_VPD_FLAG   0x8000
 PCI VPD write flag.
#define PCI_VPD_DATA   0x04
 PCI VPD data register.
#define PCI_VPD_MAX_LEN   0xff
 Maximum PCI VPD field length.
#define PCI_VPD_FIELD(tag, keyword1, keyword2)   ( ( (tag) << 16 ) | ( (keyword2) << 8 ) | ( (keyword1) << 0 ) )
 Construct PCI VPD field descriptor.
#define PCI_VPD_WHOLE_TAG_FIELD(tag)   PCI_VPD_FIELD ( (tag), '\0', '\0' )
 Construct PCI VPD whole-tag field descriptor.
#define PCI_VPD_TAG(field)   ( (field) >> 16 )
 Extract PCI VPD ISAPnP tag.
#define PCI_VPD_KEYWORD(field)   ( cpu_to_le16 ( (field) & 0xffff ) )
 Extract PCI VPD keyword.
#define PCI_VPD_FIELD_FMT   "%c%c"
 PCI VPD field debug message format.
#define PCI_VPD_FIELD_ARGS(field)   ( (field) >> 0 ), ( (field) >> 8 )
 PCI VPD field debug message arguments.
#define PCI_VPD_TAG_RO   0x90
 PCI VPD Read-Only field tag.
#define PCI_VPD_TAG_RW   0x91
 PCI VPD Read-Write field tag.
#define PCI_VPD_FIELD_NAME   PCI_VPD_WHOLE_TAG_FIELD ( ISAPNP_TAG_ANSISTR )
 PCI VPD Card Name field descriptor.
#define PCI_VPD_FIELD_PN   PCI_VPD_FIELD ( PCI_VPD_TAG_RO, 'P', 'N' )
 PCI VPD Part Number field descriptor.
#define PCI_VPD_FIELD_EC   PCI_VPD_FIELD ( PCI_VPD_TAG_RO, 'E', 'C' )
 PCI VPD Engineering Change Level field descriptor.
#define PCI_VPD_FIELD_FG   PCI_VPD_FIELD ( PCI_VPD_TAG_RO, 'F', 'G' )
 PCI VPD Fabric Geography field descriptor.
#define PCI_VPD_FIELD_LC   PCI_VPD_FIELD ( PCI_VPD_TAG_RO, 'L', 'C' )
 PCI VPD Location field descriptor.
#define PCI_VPD_FIELD_MN   PCI_VPD_FIELD ( PCI_VPD_TAG_RO, 'M', 'N' )
 PCI VPD Manufacturer ID field descriptor.
#define PCI_VPD_FIELD_PG   PCI_VPD_FIELD ( PCI_VPD_TAG_RO, 'P', 'G' )
 PCI VPD PCI Geography field descriptor.
#define PCI_VPD_FIELD_SN   PCI_VPD_FIELD ( PCI_VPD_TAG_RO, 'S', 'N' )
 PCI VPD Serial Number field descriptor.
#define PCI_VPD_FIELD_CP   PCI_VPD_FIELD ( PCI_VPD_TAG_RO, 'C', 'P' )
 PCI VPD Extended Capability field descriptor.
#define PCI_VPD_FIELD_RV   PCI_VPD_FIELD ( PCI_VPD_TAG_RO, 'R', 'V' )
 PCI VPD Checksum and Reserved field descriptor.
#define PCI_VPD_FIELD_YA   PCI_VPD_FIELD ( PCI_VPD_TAG_RW, 'Y', 'A' )
 PCI VPD Asset Tag field descriptor.
#define PCI_VPD_FIELD_RW   PCI_VPD_FIELD ( PCI_VPD_TAG_RW, 'R', 'W' )
 PCI VPD Remaining Read/Write Area field descriptor.
#define PCI_VPD_MAX_WAIT_MS   100
 Maximum wait for PCI VPD (in ms)

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
static int pci_vpd_is_present (struct pci_vpd *vpd)
 Check for presence of PCI VPD.
static int pci_vpd_cache_is_valid (struct pci_vpd *vpd)
 Check if PCI VPD read cache is valid.
static void pci_vpd_invalidate_cache (struct pci_vpd *vpd)
 Invalidate PCI VPD read cache.
int pci_vpd_init (struct pci_vpd *vpd, struct pci_device *pci)
 Initialise PCI Vital Product Data.
int pci_vpd_read (struct pci_vpd *vpd, unsigned int address, void *buf, size_t len)
 Read PCI VPD.
int pci_vpd_write (struct pci_vpd *vpd, unsigned int address, const void *buf, size_t len)
 Write PCI VPD.
int pci_vpd_find (struct pci_vpd *vpd, unsigned int field, unsigned int *address, size_t *len)
 Locate PCI VPD field.
int pci_vpd_resize (struct pci_vpd *vpd, unsigned int field, size_t len, unsigned int *address)
 Resize VPD field.

Detailed Description

PCI Vital Product Data.

Definition in file pcivpd.h.


Define Documentation

#define PCI_VPD_ADDRESS   0x02

PCI VPD address register.

Definition at line 19 of file pcivpd.h.

Referenced by pci_vpd_read_dword(), and pci_vpd_write_dword().

#define PCI_VPD_FLAG   0x8000

PCI VPD write flag.

Definition at line 22 of file pcivpd.h.

Referenced by pci_vpd_read_dword(), and pci_vpd_write_dword().

#define PCI_VPD_DATA   0x04

PCI VPD data register.

Definition at line 25 of file pcivpd.h.

Referenced by pci_vpd_read_dword(), and pci_vpd_write_dword().

#define PCI_VPD_MAX_LEN   0xff

Maximum PCI VPD field length.

Definition at line 36 of file pcivpd.h.

Referenced by pci_vpd_resize().

#define PCI_VPD_FIELD (   tag,
  keyword1,
  keyword2 
)    ( ( (tag) << 16 ) | ( (keyword2) << 8 ) | ( (keyword1) << 0 ) )

Construct PCI VPD field descriptor.

Parameters:
tagISAPnP tag
keyword1First character of keyword
keyword2Second character of keyword
Return values:
fieldVPD field descriptor

Definition at line 45 of file pcivpd.h.

#define PCI_VPD_WHOLE_TAG_FIELD (   tag)    PCI_VPD_FIELD ( (tag), '\0', '\0' )

Construct PCI VPD whole-tag field descriptor.

Parameters:
tagISAPnP tag
Return values:
fieldVPD field descriptor

Definition at line 53 of file pcivpd.h.

#define PCI_VPD_TAG (   field)    ( (field) >> 16 )

Extract PCI VPD ISAPnP tag.

Parameters:
fieldVPD field descriptor
Return values:
tagISAPnP tag

Definition at line 60 of file pcivpd.h.

Referenced by pci_vpd_find(), and pci_vpd_resize().

#define PCI_VPD_KEYWORD (   field)    ( cpu_to_le16 ( (field) & 0xffff ) )

Extract PCI VPD keyword.

Parameters:
fieldVPD field descriptor
Return values:
keywordKeyword

Definition at line 67 of file pcivpd.h.

Referenced by pci_vpd_find(), and pci_vpd_resize().

#define PCI_VPD_FIELD_FMT   "%c%c"

PCI VPD field debug message format.

Definition at line 70 of file pcivpd.h.

Referenced by nvs_vpd_nvo_init(), nvs_vpd_read(), nvs_vpd_resize(), nvs_vpd_write(), pci_vpd_find(), and pci_vpd_resize().

#define PCI_VPD_FIELD_ARGS (   field)    ( (field) >> 0 ), ( (field) >> 8 )

PCI VPD field debug message arguments.

Definition at line 73 of file pcivpd.h.

Referenced by nvs_vpd_nvo_init(), nvs_vpd_read(), nvs_vpd_resize(), nvs_vpd_write(), pci_vpd_find(), and pci_vpd_resize().

#define PCI_VPD_TAG_RO   0x90

PCI VPD Read-Only field tag.

Definition at line 77 of file pcivpd.h.

#define PCI_VPD_TAG_RW   0x91

PCI VPD Read-Write field tag.

Definition at line 80 of file pcivpd.h.

Referenced by pci_vpd_resize().

#define PCI_VPD_FIELD_NAME   PCI_VPD_WHOLE_TAG_FIELD ( ISAPNP_TAG_ANSISTR )

PCI VPD Card Name field descriptor.

Definition at line 83 of file pcivpd.h.

#define PCI_VPD_FIELD_PN   PCI_VPD_FIELD ( PCI_VPD_TAG_RO, 'P', 'N' )

PCI VPD Part Number field descriptor.

Definition at line 86 of file pcivpd.h.

#define PCI_VPD_FIELD_EC   PCI_VPD_FIELD ( PCI_VPD_TAG_RO, 'E', 'C' )

PCI VPD Engineering Change Level field descriptor.

Definition at line 89 of file pcivpd.h.

#define PCI_VPD_FIELD_FG   PCI_VPD_FIELD ( PCI_VPD_TAG_RO, 'F', 'G' )

PCI VPD Fabric Geography field descriptor.

Definition at line 92 of file pcivpd.h.

#define PCI_VPD_FIELD_LC   PCI_VPD_FIELD ( PCI_VPD_TAG_RO, 'L', 'C' )

PCI VPD Location field descriptor.

Definition at line 95 of file pcivpd.h.

#define PCI_VPD_FIELD_MN   PCI_VPD_FIELD ( PCI_VPD_TAG_RO, 'M', 'N' )

PCI VPD Manufacturer ID field descriptor.

Definition at line 98 of file pcivpd.h.

#define PCI_VPD_FIELD_PG   PCI_VPD_FIELD ( PCI_VPD_TAG_RO, 'P', 'G' )

PCI VPD PCI Geography field descriptor.

Definition at line 101 of file pcivpd.h.

#define PCI_VPD_FIELD_SN   PCI_VPD_FIELD ( PCI_VPD_TAG_RO, 'S', 'N' )

PCI VPD Serial Number field descriptor.

Definition at line 104 of file pcivpd.h.

#define PCI_VPD_FIELD_CP   PCI_VPD_FIELD ( PCI_VPD_TAG_RO, 'C', 'P' )

PCI VPD Extended Capability field descriptor.

Definition at line 107 of file pcivpd.h.

#define PCI_VPD_FIELD_RV   PCI_VPD_FIELD ( PCI_VPD_TAG_RO, 'R', 'V' )

PCI VPD Checksum and Reserved field descriptor.

Definition at line 110 of file pcivpd.h.

#define PCI_VPD_FIELD_YA   PCI_VPD_FIELD ( PCI_VPD_TAG_RW, 'Y', 'A' )

PCI VPD Asset Tag field descriptor.

Definition at line 113 of file pcivpd.h.

#define PCI_VPD_FIELD_RW   PCI_VPD_FIELD ( PCI_VPD_TAG_RW, 'R', 'W' )

PCI VPD Remaining Read/Write Area field descriptor.

Definition at line 116 of file pcivpd.h.

Referenced by pci_vpd_resize().

#define PCI_VPD_MAX_WAIT_MS   100

Maximum wait for PCI VPD (in ms)

Definition at line 119 of file pcivpd.h.

Referenced by pci_vpd_read_dword(), and pci_vpd_write_dword().


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL  )
static int pci_vpd_is_present ( struct pci_vpd vpd) [inline, static]

Check for presence of PCI VPD.

Parameters:
vpdPCI VPD
Return values:
is_presentVPD is present

Definition at line 146 of file pcivpd.h.

                                           {
        return ( vpd->cap != 0 );
}
static int pci_vpd_cache_is_valid ( struct pci_vpd vpd) [inline, static]

Check if PCI VPD read cache is valid.

Parameters:
vpdPCI VPD
Return values:
is_validRead cache is valid

Definition at line 157 of file pcivpd.h.

Referenced by pci_vpd_read_dword().

                                               {
        return ( vpd->cache.address >= 0 );
}
static void pci_vpd_invalidate_cache ( struct pci_vpd vpd) [inline, static]

Invalidate PCI VPD read cache.

Parameters:
vpdPCI VPD

Definition at line 167 of file pcivpd.h.

Referenced by pci_vpd_init(), and pci_vpd_write_dword().

                                                 {
        vpd->cache.address = -1;
}
int pci_vpd_init ( struct pci_vpd vpd,
struct pci_device pci 
)

Initialise PCI Vital Product Data.

Parameters:
vpdPCI VPD
pciPCI device
Return values:
rcReturn status code

Definition at line 48 of file pcivpd.c.

References pci_vpd::cap, DBGC, ENOTTY, pci_vpd::pci, PCI_ARGS, PCI_CAP_ID_VPD, pci_find_capability(), PCI_FMT, and pci_vpd_invalidate_cache().

Referenced by nvs_vpd_init().

                                                                 {

        /* Initialise structure */
        vpd->pci = pci;
        pci_vpd_invalidate_cache ( vpd );

        /* Locate VPD capability */
        vpd->cap = pci_find_capability ( pci, PCI_CAP_ID_VPD );
        if ( ! vpd->cap ) {
                DBGC ( vpd, PCI_FMT " does not support VPD\n",
                       PCI_ARGS ( pci ) );
                return -ENOTTY;
        }

        DBGC ( vpd, PCI_FMT " VPD is at offset %02x\n",
               PCI_ARGS ( pci ), vpd->cap );
        return 0;
}
int pci_vpd_read ( struct pci_vpd vpd,
unsigned int  address,
void *  buf,
size_t  len 
)

Read PCI VPD.

Parameters:
vpdPCI VPD
addressStarting address
bufData buffer
lenLength of data buffer
Return values:
rcReturn status code

Definition at line 183 of file pcivpd.c.

References bytes, data, len, pci_vpd_read_dword(), and rc.

Referenced by nvs_vpd_read(), pci_vpd_dump(), pci_vpd_find(), pci_vpd_find_tag(), and pci_vpd_resize().

                                {
        uint8_t *bytes = buf;
        uint32_t data;
        size_t skip_len;
        unsigned int i;
        int rc;

        /* Calculate length to skip at start of data */
        skip_len = ( address & 0x03 );

        /* Read data, a dword at a time */
        for ( address &= ~0x03 ; len ; address += 4 ) {

                /* Read whole dword */
                if ( ( rc = pci_vpd_read_dword ( vpd, address, &data ) ) != 0 )
                        return rc;

                /* Copy data to buffer */
                for ( i = 4 ; i ; i-- ) {
                        if ( skip_len ) {
                                skip_len--;
                        } else if ( len ) {
                                *(bytes++) = data;
                                len--;
                        }
                        data = ( ( data << 24 ) | ( data >> 8 ) );
                }
        }

        return 0;
}
int pci_vpd_write ( struct pci_vpd vpd,
unsigned int  address,
const void *  buf,
size_t  len 
)

Write PCI VPD.

Parameters:
vpdPCI VPD
addressStarting address
bufData buffer
lenLength of data buffer
Return values:
rcReturn status code

Definition at line 225 of file pcivpd.c.

References bytes, data, len, pci_vpd_read_dword(), pci_vpd_write_dword(), and rc.

Referenced by nvs_vpd_write(), and pci_vpd_resize().

                                 {
        const uint8_t *bytes = buf;
        uint32_t data;
        size_t skip_len;
        unsigned int i;
        int rc;

        /* Calculate length to skip at start of data */
        skip_len = ( address & 0x03 );

        /* Write data, a dword at a time */
        for ( address &= ~0x03 ; len ; address += 4 ) {

                /* Read existing dword, if necessary */
                if ( skip_len || ( len <= 0x03 ) ) {
                        if ( ( rc = pci_vpd_read_dword ( vpd, address,
                                                         &data ) ) != 0 )
                                return rc;
                }

                /* Copy data from buffer */
                for ( i = 4 ; i ; i-- ) {
                        if ( skip_len ) {
                                skip_len--;
                        } else if ( len ) {
                                data = ( ( data & ~0xff ) | *(bytes++) );
                                len--;
                        }
                        data = ( ( data << 24 ) | ( data >> 8 ) );
                }

                /* Write whole dword */
                if ( ( rc = pci_vpd_write_dword ( vpd, address, data ) ) != 0 )
                        return rc;
        }
        return 0;
}
int pci_vpd_find ( struct pci_vpd vpd,
unsigned int  field,
unsigned int *  address,
size_t len 
)

Locate PCI VPD field.

Parameters:
vpdPCI VPD
fieldVPD field descriptor
Return values:
addressAddress of field body
lenLength of field body
rcReturn status code

Definition at line 353 of file pcivpd.c.

References DBGC, ENOENT, pci_vpd_field::keyword, pci_vpd_field::len, pci_vpd::pci, PCI_ARGS, PCI_FMT, pci_vpd_dump(), PCI_VPD_FIELD_ARGS, PCI_VPD_FIELD_FMT, pci_vpd_find_tag(), PCI_VPD_KEYWORD, pci_vpd_read(), PCI_VPD_TAG, and rc.

Referenced by nvs_vpd_nvo_init(), nvs_vpd_read(), nvs_vpd_write(), and pci_vpd_resize().

                                                        {
        struct pci_vpd_field read_field;
        int rc;

        /* Locate containing tag */
        if ( ( rc = pci_vpd_find_tag ( vpd, PCI_VPD_TAG ( field ),
                                       address, len ) ) != 0 )
                return rc;

        /* Return immediately if we are searching for a whole-tag field */
        if ( ! PCI_VPD_KEYWORD ( field ) ) {
                pci_vpd_dump ( vpd, *address, *len );
                return 0;
        }

        /* Scan through fields looking for a match */
        while ( *len >= sizeof ( read_field ) ) {

                /* Read field header */
                if ( ( rc = pci_vpd_read ( vpd, *address, &read_field,
                                           sizeof ( read_field ) ) ) != 0 )
                        return rc;
                *address += sizeof ( read_field );
                *len -= sizeof ( read_field );

                /* Check for keyword match */
                if ( read_field.keyword == PCI_VPD_KEYWORD ( field ) ) {
                        *len = read_field.len;
                        DBGC ( vpd, PCI_FMT " VPD field " PCI_VPD_FIELD_FMT
                               " is at [%04x,%04zx)\n", PCI_ARGS ( vpd->pci ),
                               PCI_VPD_FIELD_ARGS ( field ),
                               *address, ( *address + *len ) );
                        pci_vpd_dump ( vpd, *address, *len );
                        return 0;
                }

                /* Move to next field */
                if ( read_field.len > *len )
                        break;
                *address += read_field.len;
                *len -= read_field.len;
        }

        DBGC ( vpd, PCI_FMT " VPD field " PCI_VPD_FIELD_FMT " not found\n",
               PCI_ARGS ( vpd->pci ), PCI_VPD_FIELD_ARGS ( field ) );
        return -ENOENT;
}
int pci_vpd_resize ( struct pci_vpd vpd,
unsigned int  field,
size_t  len,
unsigned int *  address 
)

Resize VPD field.

Parameters:
vpdPCI VPD
fieldVPD field descriptor
lenNew length of field body
Return values:
addressAddress of field body
rcReturn status code

Definition at line 411 of file pcivpd.c.

References assert, DBGC, ENOMEM, ENOSPC, ENXIO, free, pci_vpd_field::keyword, pci_vpd_field::len, len, malloc(), pci_vpd::pci, PCI_ARGS, PCI_FMT, pci_vpd_dump(), PCI_VPD_FIELD_ARGS, PCI_VPD_FIELD_FMT, PCI_VPD_FIELD_RW, pci_vpd_find(), PCI_VPD_KEYWORD, PCI_VPD_MAX_LEN, pci_vpd_read(), PCI_VPD_TAG, PCI_VPD_TAG_RW, pci_vpd_write(), and rc.

Referenced by nvs_vpd_resize().

                                             {
        struct pci_vpd_field rw_field;
        struct pci_vpd_field old_field;
        struct pci_vpd_field new_field;
        unsigned int rw_address;
        unsigned int old_address;
        unsigned int copy_address;
        unsigned int dst_address;
        unsigned int dump_address;
        size_t rw_len;
        size_t old_len;
        size_t available_len;
        size_t copy_len;
        size_t dump_len;
        void *copy;
        int rc;

        /* Sanity checks */
        assert ( PCI_VPD_TAG ( field ) == PCI_VPD_TAG_RW );
        assert ( PCI_VPD_KEYWORD ( field ) != 0 );
        assert ( field != PCI_VPD_FIELD_RW );

        /* Locate 'RW' field */
        if ( ( rc = pci_vpd_find ( vpd, PCI_VPD_FIELD_RW, &rw_address,
                                   &rw_len ) ) != 0 )
                goto err_no_rw;

        /* Locate old field, if any */
        if ( ( rc = pci_vpd_find ( vpd, field, &old_address,
                                   &old_len ) ) == 0 ) {

                /* Field already exists */
                if ( old_address > rw_address ) {
                        DBGC ( vpd, PCI_FMT " VPD field " PCI_VPD_FIELD_FMT
                               " at [%04x,%04zx) is after field "
                               PCI_VPD_FIELD_FMT " at [%04x,%04zx)\n",
                               PCI_ARGS ( vpd->pci ),
                               PCI_VPD_FIELD_ARGS ( field ),
                               old_address, ( old_address + old_len ),
                               PCI_VPD_FIELD_ARGS ( PCI_VPD_FIELD_RW ),
                               rw_address, ( rw_address + rw_len ) );
                        rc = -ENXIO;
                        goto err_after_rw;
                }
                dst_address = ( old_address - sizeof ( old_field ) );
                copy_address = ( old_address + old_len );
                copy_len = ( rw_address - sizeof ( rw_field ) - copy_address );

                /* Calculate available length */
                available_len = ( rw_len + old_len );

        } else {

                /* Field does not yet exist */
                dst_address = ( rw_address - sizeof ( rw_field ) );
                copy_address = dst_address;
                copy_len = 0;

                /* Calculate available length */
                available_len = ( ( rw_len > sizeof ( new_field ) ) ?
                                  ( rw_len - sizeof ( new_field ) ) : 0 );
        }

        /* Dump region before changes */
        dump_address = dst_address;
        dump_len = ( rw_address + rw_len - dump_address );
        DBGC ( vpd, PCI_FMT " VPD before resizing field " PCI_VPD_FIELD_FMT
               " to %zd bytes:\n", PCI_ARGS ( vpd->pci ),
               PCI_VPD_FIELD_ARGS ( field ), len );
        pci_vpd_dump ( vpd, dump_address, dump_len );

        /* Check available length */
        if ( available_len > PCI_VPD_MAX_LEN )
                available_len = PCI_VPD_MAX_LEN;
        if ( len > available_len ) {
                DBGC ( vpd, PCI_FMT " VPD no space for field "
                       PCI_VPD_FIELD_FMT " (need %02zx, have %02zx)\n",
                       PCI_ARGS ( vpd->pci ), PCI_VPD_FIELD_ARGS ( field ),
                       len, available_len );
                rc = -ENOSPC;
                goto err_no_space;
        }

        /* Preserve intermediate fields, if any */
        copy = malloc ( copy_len );
        if ( ! copy ) {
                rc = -ENOMEM;
                goto err_copy_alloc;
        }
        if ( ( rc = pci_vpd_read ( vpd, copy_address, copy, copy_len ) ) != 0 )
                goto err_copy_read;

        /* Create new field, if applicable */
        if ( len ) {
                new_field.keyword = PCI_VPD_KEYWORD ( field );
                new_field.len = len;
                if ( ( rc = pci_vpd_write ( vpd, dst_address, &new_field,
                                            sizeof ( new_field ) ) ) != 0 )
                        goto err_new_write;
                dst_address += sizeof ( new_field );
                *address = dst_address;
                DBGC ( vpd, PCI_FMT " VPD field " PCI_VPD_FIELD_FMT " is now "
                       "at [%04x,%04x)\n", PCI_ARGS ( vpd->pci ),
                       PCI_VPD_FIELD_ARGS ( field ), dst_address,
                       ( dst_address + new_field.len ) );
                dst_address += len;
        } else {
                DBGC ( vpd, PCI_FMT " VPD field " PCI_VPD_FIELD_FMT
                       " no longer exists\n", PCI_ARGS ( vpd->pci ),
                       PCI_VPD_FIELD_ARGS ( field ) );
        }

        /* Restore intermediate fields, if any */
        if ( ( rc = pci_vpd_write ( vpd, dst_address, copy, copy_len ) ) != 0 )
                goto err_copy_write;
        dst_address += copy_len;

        /* Create 'RW' field */
        rw_field.keyword = PCI_VPD_KEYWORD ( PCI_VPD_FIELD_RW );
        rw_field.len = ( rw_len +
                         ( rw_address - sizeof ( rw_field ) ) - dst_address );
        if ( ( rc = pci_vpd_write ( vpd, dst_address, &rw_field,
                                    sizeof ( rw_field ) ) ) != 0 )
                goto err_rw_write;
        dst_address += sizeof ( rw_field );
        DBGC ( vpd, PCI_FMT " VPD field " PCI_VPD_FIELD_FMT " is now "
               "at [%04x,%04x)\n", PCI_ARGS ( vpd->pci ),
               PCI_VPD_FIELD_ARGS ( PCI_VPD_FIELD_RW ), dst_address,
               ( dst_address + rw_field.len ) );

        /* Dump region after changes */
        DBGC ( vpd, PCI_FMT " VPD after resizing field " PCI_VPD_FIELD_FMT
               " to %zd bytes:\n", PCI_ARGS ( vpd->pci ),
               PCI_VPD_FIELD_ARGS ( field ), len );
        pci_vpd_dump ( vpd, dump_address, dump_len );

        rc = 0;

 err_rw_write:
 err_new_write:
 err_copy_write:
 err_copy_read:
        free ( copy );
 err_copy_alloc:
 err_no_space:
 err_after_rw:
 err_no_rw:
        return rc;
}