[fdt] Add fdt_cells() to read cell-based properties such as "reg"

Add fdt_cells() to read scalar values encoded within a cell array,
reimplement fdt_u64() as a wrapper around this, and add fdt_u32() for
completeness.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown
2025-04-15 20:14:03 +01:00
parent 2c406ec0b1
commit 99322fd3b3
3 changed files with 140 additions and 51 deletions

View File

@@ -468,7 +468,55 @@ const char * fdt_string ( struct fdt *fdt, unsigned int offset,
}
/**
* Find integer property
* Get integer property
*
* @v fdt Device tree
* @v offset Starting node offset
* @v name Property name
* @v index Starting cell index
* @v count Number of cells (or 0 to read all remaining cells)
* @v value Integer value to fill in
* @ret rc Return status code
*/
int fdt_cells ( struct fdt *fdt, unsigned int offset, const char *name,
unsigned int index, unsigned int count, uint64_t *value ) {
struct fdt_descriptor desc;
const uint32_t *cell;
unsigned int total;
int rc;
/* Clear value */
*value = 0;
/* Find property */
if ( ( rc = fdt_property ( fdt, offset, name, &desc ) ) != 0 )
return rc;
cell = desc.data;
/* Determine number of cells */
total = ( desc.len / sizeof ( *cell ) );
if ( ( index > total ) || ( count > ( total - index ) ) ) {
DBGC ( fdt, "FDT truncated integer \"%s\"\n", name );
return -ERANGE;
}
if ( ! count )
count = ( total - index );
if ( count > ( sizeof ( *value ) / sizeof ( *cell ) ) ) {
DBGC ( fdt, "FDT overlength integer \"%s\"\n", name );
return -ERANGE;
}
/* Read value */
for ( cell += index ; count ; cell++, count-- ) {
*value <<= 32;
*value |= be32_to_cpu ( *cell );
}
return 0;
}
/**
* Get 64-bit integer property
*
* @v fdt Device tree
* @v offset Starting node offset
@@ -478,32 +526,40 @@ const char * fdt_string ( struct fdt *fdt, unsigned int offset,
*/
int fdt_u64 ( struct fdt *fdt, unsigned int offset, const char *name,
uint64_t *value ) {
struct fdt_descriptor desc;
const uint8_t *data;
size_t remaining;
int rc;
/* Clear value */
*value = 0;
/* Read value */
if ( ( rc = fdt_cells ( fdt, offset, name, 0, 0, value ) ) != 0 )
return rc;
/* Find property */
if ( ( rc = fdt_property ( fdt, offset, name, &desc ) ) != 0 )
return 0;
}
/**
* Get 32-bit integer property
*
* @v fdt Device tree
* @v offset Starting node offset
* @v name Property name
* @v value Integer value to fill in
* @ret rc Return status code
*/
int fdt_u32 ( struct fdt *fdt, unsigned int offset, const char *name,
uint32_t *value ) {
uint64_t value64;
int rc;
/* Read value */
if ( ( rc = fdt_u64 ( fdt, offset, name, &value64 ) ) != 0 )
return rc;
/* Check range */
if ( desc.len > sizeof ( *value ) ) {
DBGC ( fdt, "FDT oversized integer property \"%s\"\n", name );
*value = value64;
if ( *value != value64 ) {
DBGC ( fdt, "FDT overlength 32-bit integer \"%s\"\n", name );
return -ERANGE;
}
/* Parse value */
data = desc.data;
remaining = desc.len;
while ( remaining-- ) {
*value <<= 8;
*value |= *(data++);
}
return 0;
}

View File

@@ -137,8 +137,13 @@ extern const char * fdt_strings ( struct fdt *fdt, unsigned int offset,
const char *name, unsigned int *count );
extern const char * fdt_string ( struct fdt *fdt, unsigned int offset,
const char *name );
extern int fdt_cells ( struct fdt *fdt, unsigned int offset, const char *name,
unsigned int index, unsigned int count,
uint64_t *value );
extern int fdt_u64 ( struct fdt *fdt, unsigned int offset, const char *name,
uint64_t *value );
extern int fdt_u32 ( struct fdt *fdt, unsigned int offset, const char *name,
uint32_t *value );
extern int fdt_mac ( struct fdt *fdt, unsigned int offset,
struct net_device *netdev );
extern int fdt_parse ( struct fdt *fdt, struct fdt_header *hdr,

View File

@@ -38,10 +38,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** Simplified QEMU sifive_u device tree blob */
static const uint8_t sifive_u[] = {
0xd0, 0x0d, 0xfe, 0xed, 0x00, 0x00, 0x05, 0x43, 0x00, 0x00, 0x00, 0x38,
0x00, 0x00, 0x04, 0x68, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x11,
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdb,
0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xd0, 0x0d, 0xfe, 0xed, 0x00, 0x00, 0x05, 0x61, 0x00, 0x00, 0x00, 0x38,
0x00, 0x00, 0x04, 0x7c, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x11,
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5,
0x00, 0x00, 0x04, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03,
@@ -127,30 +127,32 @@ static const uint8_t sifive_u[] = {
0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x16,
0x00, 0x00, 0x00, 0x1b, 0x73, 0x69, 0x66, 0x69, 0x76, 0x65, 0x2c, 0x66,
0x75, 0x35, 0x34, 0x30, 0x2d, 0x63, 0x30, 0x30, 0x30, 0x2d, 0x67, 0x65,
0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x65, 0x74, 0x68, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x2d, 0x70, 0x68, 0x79, 0x40, 0x30, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09,
0x23, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2d, 0x63, 0x65, 0x6c,
0x6c, 0x73, 0x00, 0x23, 0x73, 0x69, 0x7a, 0x65, 0x2d, 0x63, 0x65, 0x6c,
0x6c, 0x73, 0x00, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x69, 0x62, 0x6c,
0x65, 0x00, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x00, 0x73, 0x74, 0x64, 0x6f,
0x75, 0x74, 0x2d, 0x70, 0x61, 0x74, 0x68, 0x00, 0x73, 0x65, 0x72, 0x69,
0x61, 0x6c, 0x30, 0x00, 0x65, 0x74, 0x68, 0x65, 0x72, 0x6e, 0x65, 0x74,
0x30, 0x00, 0x74, 0x69, 0x6d, 0x65, 0x62, 0x61, 0x73, 0x65, 0x2d, 0x66,
0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x79, 0x00, 0x64, 0x65, 0x76,
0x69, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x00, 0x72, 0x65, 0x67,
0x00, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x00, 0x72, 0x69, 0x73, 0x63,
0x76, 0x2c, 0x69, 0x73, 0x61, 0x00, 0x23, 0x69, 0x6e, 0x74, 0x65, 0x72,
0x72, 0x75, 0x70, 0x74, 0x2d, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x00, 0x69,
0x6e, 0x74, 0x65, 0x72, 0x72, 0x75, 0x70, 0x74, 0x2d, 0x63, 0x6f, 0x6e,
0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x00, 0x72, 0x61, 0x6e, 0x67,
0x65, 0x73, 0x00, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x2d, 0x6d, 0x61, 0x63,
0x2d, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x00, 0x70, 0x68, 0x79,
0x2d, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x00, 0x70, 0x68, 0x79, 0x2d,
0x6d, 0x6f, 0x64, 0x65, 0x00, 0x72, 0x65, 0x67, 0x2d, 0x6e, 0x61, 0x6d,
0x65, 0x73, 0x00
0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08,
0x00, 0x00, 0x00, 0xdb, 0x00, 0x00, 0x00, 0x02, 0x54, 0x0b, 0xe4, 0x00,
0x00, 0x00, 0x00, 0x01, 0x65, 0x74, 0x68, 0x65, 0x72, 0x6e, 0x65, 0x74,
0x2d, 0x70, 0x68, 0x79, 0x40, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, 0x23, 0x61, 0x64, 0x64,
0x72, 0x65, 0x73, 0x73, 0x2d, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x00, 0x23,
0x73, 0x69, 0x7a, 0x65, 0x2d, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x00, 0x63,
0x6f, 0x6d, 0x70, 0x61, 0x74, 0x69, 0x62, 0x6c, 0x65, 0x00, 0x6d, 0x6f,
0x64, 0x65, 0x6c, 0x00, 0x73, 0x74, 0x64, 0x6f, 0x75, 0x74, 0x2d, 0x70,
0x61, 0x74, 0x68, 0x00, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x30, 0x00,
0x65, 0x74, 0x68, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x30, 0x00, 0x74, 0x69,
0x6d, 0x65, 0x62, 0x61, 0x73, 0x65, 0x2d, 0x66, 0x72, 0x65, 0x71, 0x75,
0x65, 0x6e, 0x63, 0x79, 0x00, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f,
0x74, 0x79, 0x70, 0x65, 0x00, 0x72, 0x65, 0x67, 0x00, 0x73, 0x74, 0x61,
0x74, 0x75, 0x73, 0x00, 0x72, 0x69, 0x73, 0x63, 0x76, 0x2c, 0x69, 0x73,
0x61, 0x00, 0x23, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x72, 0x75, 0x70, 0x74,
0x2d, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x00, 0x69, 0x6e, 0x74, 0x65, 0x72,
0x72, 0x75, 0x70, 0x74, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
0x6c, 0x65, 0x72, 0x00, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x00, 0x6c,
0x6f, 0x63, 0x61, 0x6c, 0x2d, 0x6d, 0x61, 0x63, 0x2d, 0x61, 0x64, 0x64,
0x72, 0x65, 0x73, 0x73, 0x00, 0x70, 0x68, 0x79, 0x2d, 0x68, 0x61, 0x6e,
0x64, 0x6c, 0x65, 0x00, 0x70, 0x68, 0x79, 0x2d, 0x6d, 0x6f, 0x64, 0x65,
0x00, 0x72, 0x65, 0x67, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x00, 0x6d,
0x61, 0x78, 0x2d, 0x73, 0x70, 0x65, 0x65, 0x64, 0x00
};
/**
@@ -162,6 +164,7 @@ static void fdt_test_exec ( void ) {
struct fdt_header *hdr;
struct fdt fdt;
const char *string;
uint32_t u32;
uint64_t u64;
unsigned int count;
unsigned int offset;
@@ -194,11 +197,6 @@ static void fdt_test_exec ( void ) {
&count ) ) == NULL );
ok ( count == 0 );
/* Verify integer properties */
ok ( fdt_u64 ( &fdt, 0, "#address-cells", &u64 ) == 0 );
ok ( u64 == 2 );
ok ( fdt_u64 ( &fdt, 0, "#nonexistent", &u64 ) != 0 );
/* Verify path lookup */
ok ( fdt_path ( &fdt, "", &offset ) == 0 );
ok ( offset == 0 );
@@ -214,6 +212,36 @@ static void fdt_test_exec ( void ) {
ok ( fdt_path ( &fdt, "/nonexistent", &offset ) != 0 );
ok ( fdt_path ( &fdt, "/cpus/nonexistent", &offset ) != 0 );
/* Verify 64-bit integer properties */
ok ( fdt_u64 ( &fdt, 0, "#address-cells", &u64 ) == 0 );
ok ( u64 == 2 );
ok ( fdt_path ( &fdt, "/soc/ethernet@10090000", &offset ) == 0 );
ok ( fdt_u64 ( &fdt, offset, "max-speed", &u64 ) == 0 );
ok ( u64 == 10000000000ULL );
ok ( fdt_u64 ( &fdt, offset, "#nonexistent", &u64 ) != 0 );
/* Verify 32-bit integer properties */
ok ( fdt_u32 ( &fdt, 0, "#address-cells", &u32 ) == 0 );
ok ( u32 == 2 );
ok ( fdt_u32 ( &fdt, 0, "#nonexistent", &u32 ) != 0 );
ok ( fdt_path ( &fdt, "/soc/ethernet@10090000", &offset ) == 0 );
ok ( fdt_u32 ( &fdt, offset, "max-speed", &u32 ) != 0 );
/* Verify cell properties */
ok ( fdt_path ( &fdt, "/soc/ethernet@10090000", &offset ) == 0 );
ok ( fdt_cells ( &fdt, offset, "reg", 4, 2, &u64 ) == 0 );
ok ( u64 == 0x100a0000 );
ok ( fdt_cells ( &fdt, offset, "reg", 6, 2, &u64 ) == 0 );
ok ( u64 == 0x1000 );
ok ( fdt_cells ( &fdt, offset, "reg", 0, 2, &u64 ) == 0 );
ok ( u64 == 0x10090000 );
ok ( fdt_cells ( &fdt, offset, "reg", 6, 0, &u64 ) == 0 );
ok ( u64 == 0x1000 );
ok ( fdt_cells ( &fdt, offset, "reg", 8, 0, &u64 ) == 0 );
ok ( u64 == 0 );
ok ( fdt_cells ( &fdt, offset, "reg", 7, 2, &u64 ) != 0 );
ok ( fdt_cells ( &fdt, offset, "notareg", 0, 1, &u64 ) != 0 );
/* Verify alias lookup */
ok ( fdt_alias ( &fdt, "serial0", &offset ) == 0 );
ok ( ( string = fdt_string ( &fdt, offset, "compatible" ) ) != NULL );