From: Ruslan Ruslichenko <[email protected]> The patch adds possibility to read and parse device tree node properties and set them to newly created object.
Signed-off-by: Ruslan Ruslichenko <[email protected]> --- hw/core/fdt_generic_util.c | 222 +++++++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) diff --git a/hw/core/fdt_generic_util.c b/hw/core/fdt_generic_util.c index 8131511e70..2a3862b9ac 100644 --- a/hw/core/fdt_generic_util.c +++ b/hw/core/fdt_generic_util.c @@ -40,7 +40,9 @@ #include "qemu/config-file.h" #include "hw/core/boards.h" #include "qemu/option.h" +#include "hw/core/qdev-properties.h" #include "hw/cpu/cluster.h" +#include "qobject/qlist.h" #ifndef FDT_GENERIC_UTIL_ERR_DEBUG #define FDT_GENERIC_UTIL_ERR_DEBUG 3 @@ -342,6 +344,25 @@ static Object *fdt_create_from_compat(const char *compat, char **dev_type) return ret; } +/*FIXME: roll into device tree functionality */ + +static inline uint64_t get_int_be(const void *p, int len) +{ + switch (len) { + case 1: + return *((uint8_t *)p); + case 2: + return be16_to_cpu(*((uint16_t *)p)); + case 4: + return be32_to_cpu(*((uint32_t *)p)); + case 8: + return be32_to_cpu(*((uint64_t *)p)); + default: + fprintf(stderr, "unsupported integer length\n"); + abort(); + } +} + /* * Error handler for device creation failure. * @@ -369,10 +390,185 @@ static void fdt_dev_error(FDTMachineInfo *fdti, char *node_path, char *compat) } } +static void fdt_init_qdev_link_prop(Object *obj, ObjectProperty *p, + FDTMachineInfo *fdti, + const char *node_path, + const QEMUDevtreeProp *prop) +{ + int len = prop->len; + const void *val = prop->value; + const char *propname = prop->name; + + Object *linked_dev, *proxy; + char target_node_path[DT_PATH_LENGTH]; + g_autofree char *propname_target = g_strconcat(propname, "-target", NULL); + Error *errp = NULL; + + if (qemu_devtree_get_node_by_phandle(fdti->fdt, target_node_path, + get_int_be(val, len))) { + abort(); + } + + while (!fdt_init_has_opaque(fdti, target_node_path)) { + fdt_init_yield(fdti); + } + linked_dev = fdt_init_get_opaque(fdti, target_node_path); + + proxy = linked_dev ? object_property_get_link(linked_dev, + propname_target, + &errp) : NULL; + if (!errp && proxy) { + DB_PRINT_NP(0, "detected proxy object for %s connection\n", propname); + linked_dev = proxy; + } + + if (!linked_dev) { + linked_dev = object_resolve_link(obj, propname, + target_node_path, &errp); + if (!linked_dev) { + return; + } + } + + errp = NULL; + object_property_set_link(obj, propname, linked_dev, &errp); + if (errp) { + /* Unable to set the property, maybe it is a memory alias? */ + MemoryRegion *alias_mr; + int offset = len / 2; + int region = 0; + + if (len > 4) { + region = get_int_be(val + offset, len - offset); + } + + alias_mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(linked_dev), region); + + object_property_set_link(obj, propname, OBJECT(alias_mr), &error_abort); + + } + + DB_PRINT_NP(0, "set link %s\n", propname); +} + +static void fdt_init_qdev_scalar_prop(Object *obj, ObjectProperty *p, + FDTMachineInfo *fdti, + const char *node_path, + const QEMUDevtreeProp *prop) +{ + const char *propname = trim_vendor(prop->name); + const void *val = prop->value; + int len = prop->len; + + /* FIXME: handle generically using accessors and stuff */ + if (!strncmp(p->type, "link", 4)) { + fdt_init_qdev_link_prop(obj, p, fdti, node_path, prop); + return; + } + + if (!strcmp(p->type, "uint8") || !strcmp(p->type, "uint16") || + !strcmp(p->type, "uint32") || !strcmp(p->type, "uint64") || + !strcmp(p->type, "int8") || !strcmp(p->type, "int16") || + !strcmp(p->type, "int32") || !strcmp(p->type, "int64")) { + object_property_set_int(obj, propname, + get_int_be(val, len), &error_abort); + DB_PRINT_NP(0, "set property %s to 0x%llx\n", propname, + (unsigned long long)get_int_be(val, len)); + return; + } + + if (!strcmp(p->type, "boolean") || !strcmp(p->type, "bool")) { + object_property_set_bool(obj, propname, + !!get_int_be(val, len), &error_abort); + DB_PRINT_NP(0, "set property %s to %s\n", propname, + get_int_be(val, len) ? "true" : "false"); + return; + } + + if (!strcmp(p->type, "string") || !strcmp(p->type, "str")) { + object_property_set_str(obj, propname, + (const char *)val, &error_abort); + DB_PRINT_NP(0, "set property %s to %s\n", propname, (const char *)val); + return; + } + + DB_PRINT_NP(0, "WARNING: property is of unknown type\n"); +} + +static size_t fdt_array_elem_len(FDTMachineInfo *fdti, + const char *node_path, + const char *propname) +{ + g_autofree char *elem_cells_propname = NULL; + Error *err = NULL; + uint32_t elem_cells; + + /* + * Default element size to 1 uint32_t cell, unless it is explicitly + * given in the same FDT node (not inherited). + */ + elem_cells_propname = g_strconcat("#", propname, "-cells", NULL); + elem_cells = qemu_fdt_getprop_cell(fdti->fdt, node_path, + elem_cells_propname, 0, &err); + + return (err ? 1 : elem_cells) * 4; +} + +static void fdt_init_qdev_array_prop(Object *obj, + FDTMachineInfo *fdti, + const char *node_path, + QEMUDevtreeProp *prop) +{ + const char *propname = trim_vendor(prop->name); + int nr = prop->len; + uint32_t elem_len; + QList *qlist = qlist_new(); + const char *prop_type; + const void *prop_value = prop->value; + + if (!prop->value || !nr) { + return; + } + + elem_len = fdt_array_elem_len(fdti, node_path, propname); + if (nr % elem_len) { + return; + } + + nr /= elem_len; + + prop_type = qdev_prop_get_array_elem_type(DEVICE(obj), propname); + if (!prop_type) { + DB_PRINT_NP(0, "fail to get property array elem type\n"); + return; + } + + while (nr--) { + if (!strcmp(prop_type, "uint8") || !strcmp(prop_type, "uint16") || + !strcmp(prop_type, "uint32") || !strcmp(prop_type, "uint64") || + !strcmp(prop_type, "int8") || !strcmp(prop_type, "int16") || + !strcmp(prop_type, "int32") || !strcmp(prop_type, "int64")) { + qlist_append_int(qlist, get_int_be(prop_value, elem_len)); + } else if (!strcmp(prop_type, "boolean") || !strcmp(prop_type, "bool")) { + qlist_append_bool(qlist, !!get_int_be(prop_value, elem_len)); + } else if (!strcmp(prop_type, "string") || !strcmp(prop_type, "str")) { + qlist_append_str(qlist, (const char *)prop_value); + } + + prop_value += elem_len; + + /* TBD: add link type support */ + } + + qdev_prop_set_array(DEVICE(obj), propname, qlist); + DB_PRINT_NP(0, "set property %s propname to <list>\n", propname); +} + static int fdt_init_qdev(char *node_path, FDTMachineInfo *fdti, char *compat) { Object *dev, *parent; char *dev_type = NULL; + QEMUDevtreeProp *prop, *props; char parent_node_path[DT_PATH_LENGTH]; if (!compat) { @@ -460,7 +656,33 @@ static int fdt_init_qdev(char *node_path, FDTMachineInfo *fdti, char *compat) } fdt_init_set_opaque(fdti, node_path, dev); + props = qemu_devtree_get_props(fdti->fdt, node_path); + for (prop = props; prop->name; prop++) { + const char *propname = trim_vendor(prop->name); + ObjectProperty *p = NULL; + + p = object_property_find(OBJECT(dev), propname); + if (p) { + DB_PRINT_NP(1, "matched property: %s of type %s, len %d\n", + propname, p->type, prop->len); + } + if (!p) { + continue; + } + + if (!strcmp(p->type, "list")) { + fdt_init_qdev_array_prop(dev, fdti, node_path, prop); + } + + if (!strcmp(propname, "type")) { + continue; + } + + fdt_init_qdev_scalar_prop(OBJECT(dev), p, fdti, node_path, prop); + } + g_free(dev_type); + g_free(props); return 0; } -- 2.43.0
