src/boot.c | 142 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 126 insertions(+), 16 deletions(-)
Introduce a robust semantic matcher for Open Firmware (OFW) device paths to
replace the previous prefix-based string matching logic. This change enables
SeaBIOS to accurately correlate bootorder entries with concrete hardware
devices by understanding the topological structure and addressing scheme of
IEEE 1275-compliant paths.
Key changes:
- Implement parse_hex to handle standard and relaxed unit addresses (e.g.,
treating "@1,0" and "@1" as equivalent for function 0).
- Implement fw_path_match to perform node-by-node comparisons, supporting
wildcards ('*') for both node names and unit addresses.
- Incorporate a generic name fallback mechanism where nodes match based solely
on unit addresses if names differ, essential for Broadcom/standard PCI
addressing compatibility.
- Update find_prio and boot_lchs_find to utilize the new semantic matcher,
ensuring consistent behavior across boot priority and disk geometry selection.
These improvements ensure that boot ordering remains predictable and flexible
even when host-side tools use generic labels or when controller driver names
vary within the guest.
Signed-off-by: Glenn Griffin <glenng@google.com>
---
src/boot.c | 142 +++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 126 insertions(+), 16 deletions(-)
diff --git a/src/boot.c b/src/boot.c
index 5c37dafd..d60950c1 100644
--- a/src/boot.c
+++ b/src/boot.c
@@ -29,24 +29,134 @@
// See if 'str' starts with 'glob' - if glob contains an '*' character
// it will match any number of characters in str that aren't a '/' or
// the next glob character.
-static char *
-glob_prefix(const char *glob, const char *str)
+// Parse a hex number from a string.
+static int
+parse_hex(const char **ptr)
{
- for (;;) {
- if (!*glob && (!*str || *str == '/'))
- return (char*)str;
- if (*glob == '*') {
- if (!*str || *str == '/' || *str == glob[1])
- glob++;
- else
- str++;
+ const char *p = *ptr;
+ int val = 0;
+ while (1) {
+ char c = *p;
+ if (c >= '0' && c <= '9')
+ val = (val << 4) | (c - '0');
+ else if (c >= 'a' && c <= 'f')
+ val = (val << 4) | (c - 'a' + 10);
+ else if (c >= 'A' && c <= 'F')
+ val = (val << 4) | (c - 'A' + 10);
+ else
+ break;
+ p++;
+ }
+ *ptr = p;
+ return val;
+}
+
+// Perform semantic matching of an Open Firmware path glob against a
concrete path.
+// Supports:
+// - '*' wildcard for node names (e.g., /pci@* matches /pci@i0cf8)
+// - Generic fallback: if addresses match, node name is ignored (e.g.
/disk@3,0 matches /nvme@3)
+// - Relaxed unit address matching (e.g., @3,0 matches @3)
+static int
+fw_path_match(const char *glob, const char *path)
+{
+ while (*glob && *path) {
+ // match separators
+ if (*glob == '/') {
+ if (*path != '/')
+ return 0;
+ glob++;
+ path++;
continue;
}
- if (*glob != *str)
- return NULL;
- glob++;
- str++;
+
+ // parse node name
+ const char *glob_end = strchr(glob, '@');
+ const char *path_end = strchr(path, '@');
+ // Handle cases where @ is missing (malformed or root?) or
next char is /
+ const char *glob_slash = strchr(glob, '/');
+ const char *path_slash = strchr(path, '/');
+
+ if (!glob_end || (glob_slash && glob_slash < glob_end))
+ glob_end = glob_slash ? glob_slash : glob + strlen(glob);
+ if (!path_end || (path_slash && path_slash < path_end))
+ path_end = path_slash ? path_slash : path + strlen(path);
+
+ int glob_len = glob_end - glob;
+ int path_len = path_end - path;
+
+ // Check Name Match
+ int name_match = 0;
+ if (glob[0] == '*')
+ name_match = 1; // Wildcard matches anything
+ else if (glob_len == path_len && memcmp(glob, path, glob_len) == 0)
+ name_match = 1; // Exact match
+
+ glob = glob_end;
+ path = path_end;
+
+ // Check Address Match
+ int glob_has_addr = (*glob == '@');
+ int path_has_addr = (*path == '@');
+
+ if (glob_has_addr != path_has_addr)
+ return 0; // Structure mismatch
+
+ if (glob_has_addr) {
+ glob++;
+ path++;
+
+ // Check for wildcard in address
+ if (glob[0] == '*') {
+ // Wildcard address matches any path address
+ // But we still require structure match (both had @)
+ // We advance pointers past address
+ // Consume glob address until / or end
+ while (*glob && *glob != '/') glob++;
+ // Consume path address until / or end
+ while (*path && *path != '/') path++;
+
+ // If name matched (or was *) AND address is *, then match.
+ if (!name_match) return 0;
+ continue;
+ }
+
+ // Parse Address
+ // Format: N[,M]
+ int glob_val1 = parse_hex(&glob);
+ int path_val1 = parse_hex(&path);
+
+ if (glob_val1 != path_val1)
+ return 0;
+
+ int glob_val2 = 0;
+ int path_val2 = 0;
+
+ if (*glob == ',') {
+ glob++;
+ glob_val2 = parse_hex(&glob);
+ }
+ if (*path == ',') {
+ path++;
+ path_val2 = parse_hex(&path);
+ }
+
+ // Semantic Address Match: Treat missing secondary address as 0
+ // If addresses match, we consider the node a match even
if names differed!
+ // (Generic PCI fallback logic: /ethernet@3 == /scsi@3 if
address is 3)
+ if (glob_val2 != path_val2)
+ return 0;
+
+ // If we are here, addresses matched.
+ // We ignore 'name_match' result (allow mismatch/alias)
+ } else {
+ // No addresses. Strict name match required (or wildcard)
+ if (!name_match)
+ return 0;
+ }
}
+
+ // Both must end or have matched fully
+ return (*glob == '\0' && *path == '\0');
}
#define FW_PCI_DOMAIN "/pci@i0cf8"
@@ -177,7 +287,7 @@ boot_lchs_find(const char *glob)
dprintf(1, "Searching bios-geometry for: %s\n", glob);
int i;
for (i = 0; i < BiosGeometryCount; i++)
- if (glob_prefix(glob, BiosGeometry[i].name))
+ if (fw_path_match(glob, BiosGeometry[i].name))
return &BiosGeometry[i];
return NULL;
}
@@ -290,7 +400,7 @@ find_prio(const char *glob)
dprintf(1, "Searching bootorder for: %s\n", glob);
int i;
for (i = 0; i < BootorderCount; i++)
- if (glob_prefix(glob, Bootorder[i]))
+ if (fw_path_match(glob, Bootorder[i]))
return i+1;
return -1;
}
--
2.52.0.457.g6b5491de43-goog
_______________________________________________
SeaBIOS mailing list -- seabios@seabios.org
To unsubscribe send an email to seabios-leave@seabios.org
On Tue, Jan 13, 2026 at 12:00:43PM -0800, Glenn Griffin via SeaBIOS wrote:
> Introduce a robust semantic matcher for Open Firmware (OFW) device paths to
> replace the previous prefix-based string matching logic. This change enables
> SeaBIOS to accurately correlate bootorder entries with concrete hardware
> devices by understanding the topological structure and addressing scheme of
> IEEE 1275-compliant paths.
>
> Key changes:
> - Implement parse_hex to handle standard and relaxed unit addresses (e.g.,
> treating "@1,0" and "@1" as equivalent for function 0).
> - Implement fw_path_match to perform node-by-node comparisons, supporting
> wildcards ('*') for both node names and unit addresses.
> - Incorporate a generic name fallback mechanism where nodes match based solely
> on unit addresses if names differ, essential for Broadcom/standard PCI
> addressing compatibility.
> - Update find_prio and boot_lchs_find to utilize the new semantic matcher,
> ensuring consistent behavior across boot priority and disk geometry selection.
>
> These improvements ensure that boot ordering remains predictable and flexible
> even when host-side tools use generic labels or when controller driver names
> vary within the guest.
What is the use case? I assume some hypervisor which is not qemu?
The current code works solid with qemu, but we do indeed take some
shortcuts because we know how the paths generated by qemu look like.
take care,
Gerd
_______________________________________________
SeaBIOS mailing list -- seabios@seabios.org
To unsubscribe send an email to seabios-leave@seabios.org
© 2016 - 2026 Red Hat, Inc.