From 99322fd3b37abd88867ccc3e9f60df0195730036 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 15 Apr 2025 20:14:03 +0100 Subject: [PATCH] [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 --- src/core/fdt.c | 92 +++++++++++++++++++++++++++++++++-------- src/include/ipxe/fdt.h | 5 +++ src/tests/fdt_test.c | 94 +++++++++++++++++++++++++++--------------- 3 files changed, 140 insertions(+), 51 deletions(-) diff --git a/src/core/fdt.c b/src/core/fdt.c index 85394a8e0..54f930286 100644 --- a/src/core/fdt.c +++ b/src/core/fdt.c @@ -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; } diff --git a/src/include/ipxe/fdt.h b/src/include/ipxe/fdt.h index a41dcaa30..fb0ae7752 100644 --- a/src/include/ipxe/fdt.h +++ b/src/include/ipxe/fdt.h @@ -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, diff --git a/src/tests/fdt_test.c b/src/tests/fdt_test.c index 738966df8..0a143ec62 100644 --- a/src/tests/fdt_test.c +++ b/src/tests/fdt_test.c @@ -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 );