[fdt] Allow paths and aliases to be terminated with separator characters

Non-permitted name characters such as a colon are sometimes used to
separate alias names or paths from additional metadata, such as the
baud rate for a UART in the "/chosen/stdout-path" property.

Support the use of such alias names and paths by allowing any
character not permitted in a property name to terminate a property or
node name match.  (This is a very relaxed matching rule that will
produce false positive matches on invalid input, but this is unlikely
to cause problems in practice.)

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown
2025-06-11 16:08:42 +01:00
parent 1de3aef78c
commit 7e96e5f2ef
2 changed files with 37 additions and 9 deletions

View File

@@ -24,6 +24,7 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <assert.h>
#include <byteswap.h>
@@ -50,6 +51,33 @@ struct image_tag fdt_image __image_tag = {
/** Amount of free space to add whenever we have to reallocate a tree */
#define FDT_INSERT_PAD 1024
/**
* Check if character is permitted in a name
*
* @v ch Character
* @ret is_permitted Character is permitted in a name
*/
static int fdt_permitted ( char ch ) {
static const char permitted[] = ",._+?#-";
return ( isalnum ( ch ) || strchr ( permitted, ch ) );
}
/**
* Compare node name
*
* @v desc Token descriptor
* @v name Name (terminated by NUL or any non-permitted character)
* @ret is_match Name matches token descriptor
*/
static int fdt_match ( const struct fdt_descriptor *desc, const char *name ) {
size_t len = strlen ( desc->name );
/* Check name and terminator */
return ( ( memcmp ( desc->name, name, len ) == 0 ) &&
( ! ( name[len] && fdt_permitted ( name[len] ) ) ) );
}
/**
* Describe device tree token
*
@@ -318,15 +346,9 @@ int fdt_parent ( struct fdt *fdt, unsigned int offset, unsigned int *parent ) {
static int fdt_child ( struct fdt *fdt, unsigned int offset, const char *name,
unsigned int *child ) {
struct fdt_descriptor desc;
const char *sep;
size_t name_len;
int depth;
int rc;
/* Determine length of name (may be terminated with NUL or '/') */
sep = strchr ( name, '/' );
name_len = ( sep ? ( ( size_t ) ( sep - name ) ) : strlen ( name ) );
/* Enter node */
if ( ( rc = fdt_enter ( fdt, offset, &desc ) ) != 0 )
return rc;
@@ -346,8 +368,7 @@ static int fdt_child ( struct fdt *fdt, unsigned int offset, const char *name,
DBGC2 ( fdt, "FDT +%#04x has child node \"%s\" at "
"+%#04x\n", offset, desc.name, desc.offset );
assert ( desc.depth > 0 );
if ( ( strlen ( desc.name ) == name_len ) &&
( memcmp ( name, desc.name, name_len ) == 0 ) ) {
if ( fdt_match ( &desc, name ) ) {
*child = desc.offset;
return 0;
}
@@ -495,7 +516,7 @@ static int fdt_property ( struct fdt *fdt, unsigned int offset,
"+%#04x len %#zx\n", offset, desc->name,
desc->offset, desc->len );
assert ( desc->depth == 0 );
if ( strcmp ( name, desc->name ) == 0 ) {
if ( fdt_match ( desc, name ) ) {
DBGC2_HDA ( fdt, 0, desc->data, desc->len );
return 0;
}

View File

@@ -213,6 +213,10 @@ static void fdt_test_exec ( void ) {
ok ( strcmp ( string, "sifive,uart0" ) == 0 );
ok ( fdt_path ( &fdt, "/nonexistent", &offset ) != 0 );
ok ( fdt_path ( &fdt, "/cpus/nonexistent", &offset ) != 0 );
ok ( fdt_path ( &fdt, "/soc/serial@10010000:115200n8",
&offset ) == 0 );
ok ( ( string = fdt_string ( &fdt, offset, "compatible" ) ) != NULL );
ok ( strcmp ( string, "sifive,uart0" ) == 0 );
/* Verify 64-bit integer properties */
ok ( fdt_u64 ( &fdt, 0, "#address-cells", &u64 ) == 0 );
@@ -249,6 +253,9 @@ static void fdt_test_exec ( void ) {
ok ( ( string = fdt_string ( &fdt, offset, "compatible" ) ) != NULL );
ok ( strcmp ( string, "sifive,uart0" ) == 0 );
ok ( fdt_alias ( &fdt, "nonexistent0", &offset ) != 0 );
ok ( fdt_alias ( &fdt, "ethernet0:params", &offset ) == 0 );
ok ( ( string = fdt_string ( &fdt, offset, "phy-mode" ) ) != NULL );
ok ( strcmp ( string, "gmii" ) == 0 );
/* Verify node description */
ok ( fdt_path ( &fdt, "/memory@80000000", &offset ) == 0 );