mirror of
https://git.proxmox.com/git/fwupd
synced 2025-07-29 17:48:57 +00:00
Improve FDT parsing compatibility
Newer versions of dtc agressively pack the string table, using *any* matching suffix rather than whole strings. This is specification compliant, but breaks if you use the offset as a hash key as a [premature] optimization. Just use the string table as a flat block of memory rather than pre-parsing it. This fixes reading the system FDT on new OpenBMC builds.
This commit is contained in:
parent
2b0f92506b
commit
eb30233b35
@ -169,7 +169,7 @@ fu_fdt_firmware_get_image_by_path(FuFdtFirmware *self, const gchar *path, GError
|
|||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
fu_fdt_firmware_parse_dt_struct(FuFdtFirmware *self, GBytes *fw, GHashTable *strtab, GError **error)
|
fu_fdt_firmware_parse_dt_struct(FuFdtFirmware *self, GBytes *fw, GBytes *strtab, GError **error)
|
||||||
{
|
{
|
||||||
gsize bufsz = 0;
|
gsize bufsz = 0;
|
||||||
gsize offset = 0;
|
gsize offset = 0;
|
||||||
@ -258,8 +258,8 @@ fu_fdt_firmware_parse_dt_struct(FuFdtFirmware *self, GBytes *fw, GHashTable *str
|
|||||||
if (token == FDT_PROP) {
|
if (token == FDT_PROP) {
|
||||||
guint32 prop_len = 0;
|
guint32 prop_len = 0;
|
||||||
guint32 prop_nameoff = 0;
|
guint32 prop_nameoff = 0;
|
||||||
gpointer value = NULL;
|
|
||||||
g_autoptr(GBytes) blob = NULL;
|
g_autoptr(GBytes) blob = NULL;
|
||||||
|
g_autoptr(GString) str = NULL;
|
||||||
|
|
||||||
/* sanity check */
|
/* sanity check */
|
||||||
if (firmware_current == FU_FIRMWARE(self)) {
|
if (firmware_current == FU_FIRMWARE(self)) {
|
||||||
@ -288,23 +288,18 @@ fu_fdt_firmware_parse_dt_struct(FuFdtFirmware *self, GBytes *fw, GHashTable *str
|
|||||||
offset += sizeof(FuFdtProp);
|
offset += sizeof(FuFdtProp);
|
||||||
|
|
||||||
/* add property */
|
/* add property */
|
||||||
if (!g_hash_table_lookup_extended(strtab,
|
str = fu_string_new_safe(g_bytes_get_data(strtab, NULL),
|
||||||
GINT_TO_POINTER(prop_nameoff),
|
g_bytes_get_size(strtab),
|
||||||
NULL,
|
prop_nameoff,
|
||||||
&value)) {
|
error);
|
||||||
g_set_error(error,
|
if (str == NULL) {
|
||||||
G_IO_ERROR,
|
g_prefix_error(error, "invalid strtab offset 0x%x: ", prop_nameoff);
|
||||||
G_IO_ERROR_INVALID_DATA,
|
|
||||||
"invalid strtab offset 0x%x",
|
|
||||||
prop_nameoff);
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
blob = fu_bytes_new_offset(fw, offset, prop_len, error);
|
blob = fu_bytes_new_offset(fw, offset, prop_len, error);
|
||||||
if (blob == NULL)
|
if (blob == NULL)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
fu_fdt_image_set_attr(FU_FDT_IMAGE(firmware_current),
|
fu_fdt_image_set_attr(FU_FDT_IMAGE(firmware_current), str->str, blob);
|
||||||
(const gchar *)value,
|
|
||||||
blob);
|
|
||||||
offset += prop_len;
|
offset += prop_len;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -332,43 +327,6 @@ fu_fdt_firmware_parse_dt_struct(FuFdtFirmware *self, GBytes *fw, GHashTable *str
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
|
||||||
fu_fdt_firmware_parse_dt_strings(FuFdtFirmware *self,
|
|
||||||
GBytes *fw,
|
|
||||||
GHashTable *strtab,
|
|
||||||
GError **error)
|
|
||||||
{
|
|
||||||
gsize bufsz = 0;
|
|
||||||
const guint8 *buf = g_bytes_get_data(fw, &bufsz);
|
|
||||||
|
|
||||||
/* debug */
|
|
||||||
if (g_getenv("FU_FDT_FIRMWARE_VERBOSE") != NULL)
|
|
||||||
fu_dump_bytes(G_LOG_DOMAIN, "dt_strings", fw);
|
|
||||||
|
|
||||||
/* parse */
|
|
||||||
for (gsize offset = 0; offset < bufsz; offset++) {
|
|
||||||
g_autoptr(GString) str = NULL;
|
|
||||||
str = fu_string_new_safe(buf, bufsz, offset, error);
|
|
||||||
if (str == NULL)
|
|
||||||
return FALSE;
|
|
||||||
if (str->len == 0) {
|
|
||||||
g_set_error(error,
|
|
||||||
G_IO_ERROR,
|
|
||||||
G_IO_ERROR_INVALID_DATA,
|
|
||||||
"strtab invalid @0x%x",
|
|
||||||
(guint)offset);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
if (g_getenv("FU_FDT_FIRMWARE_VERBOSE") != NULL)
|
|
||||||
g_debug("strtab: %s", str->str);
|
|
||||||
g_hash_table_insert(strtab, GINT_TO_POINTER(offset), g_strdup(str->str));
|
|
||||||
offset += str->len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* success */
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
fu_fdt_firmware_parse_mem_rsvmap(FuFdtFirmware *self,
|
fu_fdt_firmware_parse_mem_rsvmap(FuFdtFirmware *self,
|
||||||
const guint8 *buf,
|
const guint8 *buf,
|
||||||
@ -451,7 +409,6 @@ fu_fdt_firmware_parse(FuFirmware *firmware,
|
|||||||
guint32 size_dt_strings = 0;
|
guint32 size_dt_strings = 0;
|
||||||
guint32 size_dt_struct = 0;
|
guint32 size_dt_struct = 0;
|
||||||
const guint8 *buf = g_bytes_get_data(fw, &bufsz);
|
const guint8 *buf = g_bytes_get_data(fw, &bufsz);
|
||||||
g_autoptr(GHashTable) strtab = NULL; /* uint:str */
|
|
||||||
|
|
||||||
/* sanity check */
|
/* sanity check */
|
||||||
if (!fu_memread_uint32_safe(buf,
|
if (!fu_memread_uint32_safe(buf,
|
||||||
@ -472,8 +429,7 @@ fu_fdt_firmware_parse(FuFirmware *firmware,
|
|||||||
}
|
}
|
||||||
fu_firmware_set_size(firmware, totalsize);
|
fu_firmware_set_size(firmware, totalsize);
|
||||||
|
|
||||||
/* read string table */
|
/* read header */
|
||||||
strtab = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
|
|
||||||
if (!fu_memread_uint32_safe(buf,
|
if (!fu_memread_uint32_safe(buf,
|
||||||
bufsz,
|
bufsz,
|
||||||
offset + G_STRUCT_OFFSET(FuFdtHeader, off_dt_strings),
|
offset + G_STRUCT_OFFSET(FuFdtHeader, off_dt_strings),
|
||||||
@ -488,17 +444,6 @@ fu_fdt_firmware_parse(FuFirmware *firmware,
|
|||||||
G_BIG_ENDIAN,
|
G_BIG_ENDIAN,
|
||||||
error))
|
error))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
if (size_dt_strings != 0x0) {
|
|
||||||
g_autoptr(GBytes) dt_strings = NULL;
|
|
||||||
dt_strings =
|
|
||||||
fu_bytes_new_offset(fw, offset + off_dt_strings, size_dt_strings, error);
|
|
||||||
if (dt_strings == NULL)
|
|
||||||
return FALSE;
|
|
||||||
if (!fu_fdt_firmware_parse_dt_strings(self, dt_strings, strtab, error))
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read out DT struct */
|
|
||||||
if (!fu_memread_uint32_safe(buf,
|
if (!fu_memread_uint32_safe(buf,
|
||||||
bufsz,
|
bufsz,
|
||||||
offset + G_STRUCT_OFFSET(FuFdtHeader, off_dt_struct),
|
offset + G_STRUCT_OFFSET(FuFdtHeader, off_dt_struct),
|
||||||
@ -513,16 +458,6 @@ fu_fdt_firmware_parse(FuFirmware *firmware,
|
|||||||
G_BIG_ENDIAN,
|
G_BIG_ENDIAN,
|
||||||
error))
|
error))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
if (size_dt_struct != 0x0) {
|
|
||||||
g_autoptr(GBytes) dt_struct = NULL;
|
|
||||||
dt_struct = fu_bytes_new_offset(fw, offset + off_dt_struct, size_dt_struct, error);
|
|
||||||
if (dt_struct == NULL)
|
|
||||||
return FALSE;
|
|
||||||
if (!fu_fdt_firmware_parse_dt_struct(self, dt_struct, strtab, error))
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read out reserved map */
|
|
||||||
if (!fu_memread_uint32_safe(buf,
|
if (!fu_memread_uint32_safe(buf,
|
||||||
bufsz,
|
bufsz,
|
||||||
offset + G_STRUCT_OFFSET(FuFdtHeader, off_mem_rsvmap),
|
offset + G_STRUCT_OFFSET(FuFdtHeader, off_mem_rsvmap),
|
||||||
@ -538,8 +473,6 @@ fu_fdt_firmware_parse(FuFirmware *firmware,
|
|||||||
error))
|
error))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* read in CPUID */
|
|
||||||
if (!fu_memread_uint32_safe(buf,
|
if (!fu_memread_uint32_safe(buf,
|
||||||
bufsz,
|
bufsz,
|
||||||
offset + G_STRUCT_OFFSET(FuFdtHeader, boot_cpuid_phys),
|
offset + G_STRUCT_OFFSET(FuFdtHeader, boot_cpuid_phys),
|
||||||
@ -547,8 +480,6 @@ fu_fdt_firmware_parse(FuFirmware *firmware,
|
|||||||
G_BIG_ENDIAN,
|
G_BIG_ENDIAN,
|
||||||
error))
|
error))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
/* header version */
|
|
||||||
if (!fu_memread_uint32_safe(buf,
|
if (!fu_memread_uint32_safe(buf,
|
||||||
bufsz,
|
bufsz,
|
||||||
offset + G_STRUCT_OFFSET(FuFdtHeader, last_comp_version),
|
offset + G_STRUCT_OFFSET(FuFdtHeader, last_comp_version),
|
||||||
@ -573,6 +504,23 @@ fu_fdt_firmware_parse(FuFirmware *firmware,
|
|||||||
error))
|
error))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
fu_firmware_set_version_raw(firmware, version);
|
fu_firmware_set_version_raw(firmware, version);
|
||||||
|
|
||||||
|
/* parse device tree struct */
|
||||||
|
if (size_dt_struct != 0x0 && size_dt_strings != 0x0) {
|
||||||
|
g_autoptr(GBytes) dt_strings = NULL;
|
||||||
|
g_autoptr(GBytes) dt_struct = NULL;
|
||||||
|
dt_strings =
|
||||||
|
fu_bytes_new_offset(fw, offset + off_dt_strings, size_dt_strings, error);
|
||||||
|
if (dt_strings == NULL)
|
||||||
|
return FALSE;
|
||||||
|
dt_struct = fu_bytes_new_offset(fw, offset + off_dt_struct, size_dt_struct, error);
|
||||||
|
if (dt_struct == NULL)
|
||||||
|
return FALSE;
|
||||||
|
if (!fu_fdt_firmware_parse_dt_struct(self, dt_struct, dt_strings, error))
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* success */
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user