[ucode] Remove userptr_t from microcode image parsing

Simplify microcode image parsing by assuming that all image content is
directly accessible via pointer dereferences.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown
2025-04-24 14:25:00 +01:00
parent 605cff4c84
commit 8923a216b0

View File

@@ -357,24 +357,22 @@ static void ucode_describe ( struct image *image, size_t start,
* @ret rc Return status code * @ret rc Return status code
*/ */
static int ucode_verify ( struct image *image, size_t start, size_t len ) { static int ucode_verify ( struct image *image, size_t start, size_t len ) {
uint32_t checksum = 0; const uint32_t *dword;
uint32_t dword; uint32_t checksum;
size_t offset; unsigned int count;
/* Check length is a multiple of dwords */ /* Check length is a multiple of dwords */
if ( ( len % sizeof ( dword ) ) != 0 ) { if ( ( len % sizeof ( *dword ) ) != 0 ) {
DBGC ( image, "UCODE %s+%#04zx invalid length %#zx\n", DBGC ( image, "UCODE %s+%#04zx invalid length %#zx\n",
image->name, start, len ); image->name, start, len );
return -EINVAL; return -EINVAL;
} }
dword = ( image->data + start );
/* Calculate checksum */ /* Calculate checksum */
for ( offset = start ; len ; count = ( len / sizeof ( *dword ) );
offset += sizeof ( dword ), len -= sizeof ( dword ) ) { for ( checksum = 0 ; count ; count-- )
copy_from_user ( &dword, image->data, offset, checksum += *(dword++);
sizeof ( dword ) );
checksum += dword;
}
if ( checksum != 0 ) { if ( checksum != 0 ) {
DBGC ( image, "UCODE %s+%#04zx bad checksum %#08x\n", DBGC ( image, "UCODE %s+%#04zx bad checksum %#08x\n",
image->name, start, checksum ); image->name, start, checksum );
@@ -394,9 +392,9 @@ static int ucode_verify ( struct image *image, size_t start, size_t len ) {
*/ */
static int ucode_parse_intel ( struct image *image, size_t start, static int ucode_parse_intel ( struct image *image, size_t start,
struct ucode_update *update ) { struct ucode_update *update ) {
struct intel_ucode_header hdr; const struct intel_ucode_header *hdr;
struct intel_ucode_ext_header exthdr; const struct intel_ucode_ext_header *exthdr;
struct intel_ucode_ext ext; const struct intel_ucode_ext *ext;
struct ucode_descriptor desc; struct ucode_descriptor desc;
size_t remaining; size_t remaining;
size_t offset; size_t offset;
@@ -407,27 +405,27 @@ static int ucode_parse_intel ( struct image *image, size_t start,
/* Read header */ /* Read header */
remaining = ( image->len - start ); remaining = ( image->len - start );
if ( remaining < sizeof ( hdr ) ) { if ( remaining < sizeof ( *hdr ) ) {
DBGC ( image, "UCODE %s+%#04zx too small for Intel header\n", DBGC ( image, "UCODE %s+%#04zx too small for Intel header\n",
image->name, start ); image->name, start );
return -ENOEXEC; return -ENOEXEC;
} }
copy_from_user ( &hdr, image->data, start, sizeof ( hdr ) ); hdr = ( image->data + start );
/* Determine lengths */ /* Determine lengths */
data_len = hdr.data_len; data_len = hdr->data_len;
if ( ! data_len ) if ( ! data_len )
data_len = INTEL_UCODE_DATA_LEN; data_len = INTEL_UCODE_DATA_LEN;
len = hdr.len; len = hdr->len;
if ( ! len ) if ( ! len )
len = ( sizeof ( hdr ) + data_len ); len = ( sizeof ( *hdr ) + data_len );
/* Verify a selection of fields */ /* Verify a selection of fields */
if ( ( hdr.hver != INTEL_UCODE_HVER ) || if ( ( hdr->hver != INTEL_UCODE_HVER ) ||
( hdr.lver != INTEL_UCODE_LVER ) || ( hdr->lver != INTEL_UCODE_LVER ) ||
( len < sizeof ( hdr ) ) || ( len < sizeof ( *hdr ) ) ||
( len > remaining ) || ( len > remaining ) ||
( data_len > ( len - sizeof ( hdr ) ) ) || ( data_len > ( len - sizeof ( *hdr ) ) ) ||
( ( data_len % sizeof ( uint32_t ) ) != 0 ) || ( ( data_len % sizeof ( uint32_t ) ) != 0 ) ||
( ( len % INTEL_UCODE_ALIGN ) != 0 ) ) { ( ( len % INTEL_UCODE_ALIGN ) != 0 ) ) {
DBGC2 ( image, "UCODE %s+%#04zx is not an Intel update\n", DBGC2 ( image, "UCODE %s+%#04zx is not an Intel update\n",
@@ -442,48 +440,46 @@ static int ucode_parse_intel ( struct image *image, size_t start,
return rc; return rc;
/* Populate descriptor */ /* Populate descriptor */
desc.signature = hdr.signature; desc.signature = hdr->signature;
desc.version = hdr.version; desc.version = hdr->version;
desc.address = ( virt_to_phys ( image->data ) + desc.address = ( virt_to_phys ( image->data ) + start +
start + sizeof ( hdr ) ); sizeof ( *hdr ) );
/* Add non-extended descriptor, if applicable */ /* Add non-extended descriptor, if applicable */
ucode_describe ( image, start, &ucode_intel, &desc, hdr.platforms, ucode_describe ( image, start, &ucode_intel, &desc, hdr->platforms,
update ); update );
/* Construct extended descriptors, if applicable */ /* Construct extended descriptors, if applicable */
offset = ( sizeof ( hdr ) + data_len ); offset = ( sizeof ( *hdr ) + data_len );
if ( offset <= ( len - sizeof ( exthdr ) ) ) { if ( offset <= ( len - sizeof ( *exthdr ) ) ) {
/* Read extended header */ /* Read extended header */
copy_from_user ( &exthdr, image->data, ( start + offset ), exthdr = ( image->data + start + offset );
sizeof ( exthdr ) ); offset += sizeof ( *exthdr );
offset += sizeof ( exthdr );
/* Read extended signatures */ /* Read extended signatures */
for ( i = 0 ; i < exthdr.count ; i++ ) { for ( i = 0 ; i < exthdr->count ; i++ ) {
/* Read extended signature */ /* Read extended signature */
if ( offset > ( len - sizeof ( ext ) ) ) { if ( offset > ( len - sizeof ( *ext ) ) ) {
DBGC ( image, "UCODE %s+%#04zx extended " DBGC ( image, "UCODE %s+%#04zx extended "
"signature overrun\n", "signature overrun\n",
image->name, start ); image->name, start );
return -EINVAL; return -EINVAL;
} }
copy_from_user ( &ext, image->data, ( start + offset ), ext = ( image->data + start + offset );
sizeof ( ext ) ); offset += sizeof ( *ext );
offset += sizeof ( ext );
/* Avoid duplicating non-extended descriptor */ /* Avoid duplicating non-extended descriptor */
if ( ( ext.signature == hdr.signature ) && if ( ( ext->signature == hdr->signature ) &&
( ext.platforms == hdr.platforms ) ) { ( ext->platforms == hdr->platforms ) ) {
continue; continue;
} }
/* Construct descriptor, if applicable */ /* Construct descriptor, if applicable */
desc.signature = ext.signature; desc.signature = ext->signature;
ucode_describe ( image, start, &ucode_intel, &desc, ucode_describe ( image, start, &ucode_intel, &desc,
ext.platforms, update ); ext->platforms, update );
} }
} }
@@ -500,10 +496,10 @@ static int ucode_parse_intel ( struct image *image, size_t start,
*/ */
static int ucode_parse_amd ( struct image *image, size_t start, static int ucode_parse_amd ( struct image *image, size_t start,
struct ucode_update *update ) { struct ucode_update *update ) {
struct amd_ucode_header hdr; const struct amd_ucode_header *hdr;
struct amd_ucode_equivalence equiv; const struct amd_ucode_equivalence *equiv;
struct amd_ucode_patch_header phdr; const struct amd_ucode_patch_header *phdr;
struct amd_ucode_patch patch; const struct amd_ucode_patch *patch;
struct ucode_descriptor desc; struct ucode_descriptor desc;
size_t remaining; size_t remaining;
size_t offset; size_t offset;
@@ -513,92 +509,85 @@ static int ucode_parse_amd ( struct image *image, size_t start,
/* Read header */ /* Read header */
remaining = ( image->len - start ); remaining = ( image->len - start );
if ( remaining < sizeof ( hdr ) ) { if ( remaining < sizeof ( *hdr ) ) {
DBGC ( image, "UCODE %s+%#04zx too small for AMD header\n", DBGC ( image, "UCODE %s+%#04zx too small for AMD header\n",
image->name, start ); image->name, start );
return -ENOEXEC; return -ENOEXEC;
} }
copy_from_user ( &hdr, image->data, start, sizeof ( hdr ) ); hdr = ( image->data + start );
/* Check header */ /* Check header */
if ( hdr.magic != AMD_UCODE_MAGIC ) { if ( hdr->magic != AMD_UCODE_MAGIC ) {
DBGC2 ( image, "UCODE %s+%#04zx is not an AMD update\n", DBGC2 ( image, "UCODE %s+%#04zx is not an AMD update\n",
image->name, start ); image->name, start );
return -ENOEXEC; return -ENOEXEC;
} }
DBGC2 ( image, "UCODE %s+%#04zx is an AMD update\n", DBGC2 ( image, "UCODE %s+%#04zx is an AMD update\n",
image->name, start ); image->name, start );
if ( hdr.type != AMD_UCODE_EQUIV_TYPE ) { if ( hdr->type != AMD_UCODE_EQUIV_TYPE ) {
DBGC ( image, "UCODE %s+%#04zx unsupported equivalence table " DBGC ( image, "UCODE %s+%#04zx unsupported equivalence table "
"type %d\n", image->name, start, hdr.type ); "type %d\n", image->name, start, hdr->type );
return -ENOTSUP; return -ENOTSUP;
} }
if ( hdr.len > ( remaining - sizeof ( hdr ) ) ) { if ( hdr->len > ( remaining - sizeof ( *hdr ) ) ) {
DBGC ( image, "UCODE %s+%#04zx truncated equivalence table\n", DBGC ( image, "UCODE %s+%#04zx truncated equivalence table\n",
image->name, start ); image->name, start );
return -EINVAL; return -EINVAL;
} }
/* Count number of equivalence table entries */ /* Count number of equivalence table entries */
offset = sizeof ( hdr ); offset = sizeof ( *hdr );
for ( count = 0 ; offset < ( sizeof ( hdr ) + hdr.len ) ; equiv = ( image->data + start + offset );
count++, offset += sizeof ( equiv ) ) { for ( count = 0 ; offset < ( sizeof ( *hdr ) + hdr->len ) ;
copy_from_user ( &equiv, image->data, ( start + offset ), count++, offset += sizeof ( *equiv ) ) {
sizeof ( equiv ) ); if ( ! equiv[count].signature )
if ( ! equiv.signature )
break; break;
} }
DBGC2 ( image, "UCODE %s+%#04zx has %d equivalence table entries\n", DBGC2 ( image, "UCODE %s+%#04zx has %d equivalence table entries\n",
image->name, start, count ); image->name, start, count );
/* Parse available updates */ /* Parse available updates */
offset = ( sizeof ( hdr ) + hdr.len ); offset = ( sizeof ( *hdr ) + hdr->len );
used = 0; used = 0;
while ( used < count ) { while ( used < count ) {
/* Read patch header */ /* Read patch header */
if ( ( offset + sizeof ( phdr ) ) > remaining ) { if ( ( offset + sizeof ( *phdr ) ) > remaining ) {
DBGC ( image, "UCODE %s+%#04zx truncated patch " DBGC ( image, "UCODE %s+%#04zx truncated patch "
"header\n", image->name, start ); "header\n", image->name, start );
return -EINVAL; return -EINVAL;
} }
copy_from_user ( &phdr, image->data, ( start + offset ), phdr = ( image->data + start + offset );
sizeof ( phdr ) ); offset += sizeof ( *phdr );
offset += sizeof ( phdr );
/* Validate patch header */ /* Validate patch header */
if ( phdr.type != AMD_UCODE_PATCH_TYPE ) { if ( phdr->type != AMD_UCODE_PATCH_TYPE ) {
DBGC ( image, "UCODE %s+%#04zx unsupported patch type " DBGC ( image, "UCODE %s+%#04zx unsupported patch type "
"%d\n", image->name, start, phdr.type ); "%d\n", image->name, start, phdr->type );
return -ENOTSUP; return -ENOTSUP;
} }
if ( phdr.len < sizeof ( patch ) ) { if ( phdr->len < sizeof ( *patch ) ) {
DBGC ( image, "UCODE %s+%#04zx underlength patch\n", DBGC ( image, "UCODE %s+%#04zx underlength patch\n",
image->name, start ); image->name, start );
return -EINVAL; return -EINVAL;
} }
if ( phdr.len > ( remaining - offset ) ) { if ( phdr->len > ( remaining - offset ) ) {
DBGC ( image, "UCODE %s+%#04zx truncated patch\n", DBGC ( image, "UCODE %s+%#04zx truncated patch\n",
image->name, start ); image->name, start );
return -EINVAL; return -EINVAL;
} }
/* Read patch and construct descriptor */ /* Read patch and construct descriptor */
copy_from_user ( &patch, image->data, ( start + offset ), patch = ( image->data + start + offset );
sizeof ( patch ) ); desc.version = patch->version;
desc.version = patch.version;
desc.address = ( virt_to_phys ( image->data ) + desc.address = ( virt_to_phys ( image->data ) +
start + offset ); start + offset );
offset += phdr.len; offset += phdr->len;
/* Parse equivalence table to find matching signatures */ /* Parse equivalence table to find matching signatures */
for ( i = 0 ; i < count ; i++ ) { for ( i = 0 ; i < count ; i++ ) {
copy_from_user ( &equiv, image->data, if ( patch->id == equiv[i].id ) {
( start + sizeof ( hdr ) + desc.signature = equiv[i].signature;
( i * ( sizeof ( equiv ) ) ) ),
sizeof ( equiv ) );
if ( patch.id == equiv.id ) {
desc.signature = equiv.signature;
ucode_describe ( image, start, &ucode_amd, ucode_describe ( image, start, &ucode_amd,
&desc, 0, update ); &desc, 0, update );
used++; used++;
@@ -743,19 +732,19 @@ static int ucode_exec ( struct image *image ) {
* @ret rc Return status code * @ret rc Return status code
*/ */
static int ucode_probe ( struct image *image ) { static int ucode_probe ( struct image *image ) {
union { const union {
struct intel_ucode_header intel; struct intel_ucode_header intel;
struct amd_ucode_header amd; struct amd_ucode_header amd;
} header; } *header;
/* Sanity check */ /* Sanity check */
if ( image->len < sizeof ( header ) ) { if ( image->len < sizeof ( *header ) ) {
DBGC ( image, "UCODE %s too short\n", image->name ); DBGC ( image, "UCODE %s too short\n", image->name );
return -ENOEXEC; return -ENOEXEC;
} }
/* Read first microcode image header */ /* Read first microcode image header */
copy_from_user ( &header, image->data, 0, sizeof ( header ) ); header = image->data;
/* Check for something that looks like an Intel update /* Check for something that looks like an Intel update
* *
@@ -768,19 +757,19 @@ static int ucode_probe ( struct image *image ) {
* the image, and do not want to have a microcode image * the image, and do not want to have a microcode image
* erroneously treated as a PXE boot executable. * erroneously treated as a PXE boot executable.
*/ */
if ( ( header.intel.hver == INTEL_UCODE_HVER ) && if ( ( header->intel.hver == INTEL_UCODE_HVER ) &&
( header.intel.lver == INTEL_UCODE_LVER ) && ( header->intel.lver == INTEL_UCODE_LVER ) &&
( ( header.intel.date.century == 0x19 ) || ( ( header->intel.date.century == 0x19 ) ||
( ( header.intel.date.century >= 0x20 ) && ( ( header->intel.date.century >= 0x20 ) &&
( header.intel.date.century <= 0x29 ) ) ) ) { ( header->intel.date.century <= 0x29 ) ) ) ) {
DBGC ( image, "UCODE %s+%#04zx looks like an Intel update\n", DBGC ( image, "UCODE %s+%#04zx looks like an Intel update\n",
image->name, ( ( size_t ) 0 ) ); image->name, ( ( size_t ) 0 ) );
return 0; return 0;
} }
/* Check for AMD update signature */ /* Check for AMD update signature */
if ( ( header.amd.magic == AMD_UCODE_MAGIC ) && if ( ( header->amd.magic == AMD_UCODE_MAGIC ) &&
( header.amd.type == AMD_UCODE_EQUIV_TYPE ) ) { ( header->amd.type == AMD_UCODE_EQUIV_TYPE ) ) {
DBGC ( image, "UCODE %s+%#04zx looks like an AMD update\n", DBGC ( image, "UCODE %s+%#04zx looks like an AMD update\n",
image->name, ( ( size_t ) 0 ) ); image->name, ( ( size_t ) 0 ) );
return 0; return 0;