[PATCH 08/27] hw/core/fdt_generic_util: implement fdt_init_qdev

Ruslan Ruslichenko posted 27 patches 1 week, 4 days ago
Maintainers: Peter Maydell <peter.maydell@linaro.org>, Alistair Francis <alistair@alistair23.me>, "Edgar E. Iglesias" <edgar.iglesias@gmail.com>, Eduardo Habkost <eduardo@habkost.net>, Marcel Apfelbaum <marcel.apfelbaum@gmail.com>, "Philippe Mathieu-Daudé" <philmd@linaro.org>, Yanan Wang <wangyanan55@huawei.com>, Zhao Liu <zhao1.liu@intel.com>, Paolo Bonzini <pbonzini@redhat.com>, "Daniel P. Berrangé" <berrange@redhat.com>, David Gibson <david@gibson.dropbear.id.au>, Peter Xu <peterx@redhat.com>
[PATCH 08/27] hw/core/fdt_generic_util: implement fdt_init_qdev
Posted by Ruslan Ruslichenko 1 week, 4 days ago
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>

Implement main routine to create device object from
compat.

The following steps implemented:

- parse node compatible and device types
- try to create object from compat
- set correct parent based on device tree.

Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
 hw/core/fdt_generic_util.c | 224 +++++++++++++++++++++++++++++++++++++
 1 file changed, 224 insertions(+)

diff --git a/hw/core/fdt_generic_util.c b/hw/core/fdt_generic_util.c
index 4338dc956b..74310e9eb3 100644
--- a/hw/core/fdt_generic_util.c
+++ b/hw/core/fdt_generic_util.c
@@ -40,6 +40,7 @@
 #include "qemu/config-file.h"
 #include "hw/core/boards.h"
 #include "qemu/option.h"
+#include "hw/cpu/cluster.h"
 
 #ifndef FDT_GENERIC_UTIL_ERR_DEBUG
 #define FDT_GENERIC_UTIL_ERR_DEBUG 3
@@ -235,8 +236,231 @@ static int simple_bus_fdt_init(char *node_path, FDTMachineInfo *fdti)
     return 0;
 }
 
+/* FIXME: figure out a real solution to this */
+
+#define DIGIT(a) ((a) >= '0' && (a) <= '9')
+#define LOWER_CASE(a) ((a) >= 'a' && (a) <= 'z')
+
+static void trim_version(char *x)
+{
+    long result;
+
+    for (;;) {
+        x = strchr(x, '-');
+        if (!x) {
+            return;
+        }
+        if (DIGIT(x[1])) {
+            /* Try to trim Xilinx version suffix */
+            const char *p;
+
+            qemu_strtol(x + 1, &p, 0, &result);
+
+            if (*p == '.') {
+                *x = 0;
+                return;
+            } else if (*p == 0) {
+                return;
+            }
+        } else if (x[1] == 'r' && x[3] == 'p') {
+            /* Try to trim ARM version suffix */
+            if (DIGIT(x[2]) && DIGIT(x[4])) {
+                *x = 0;
+                return;
+            }
+        }
+        x++;
+    }
+}
+
+static void substitute_char(char *s, char a, char b)
+{
+    for (;;) {
+        s = strchr(s, a);
+        if (!s) {
+            return;
+        }
+        *s = b;
+        s++;
+    }
+}
+
+static inline const char *trim_vendor(const char *s)
+{
+    /* FIXME: be more intelligent */
+    const char *ret = memchr(s, ',', strlen(s));
+    return ret ? ret + 1 : s;
+}
+
+static Object *fdt_create_from_compat(const char *compat, char **dev_type)
+{
+    Object *ret = NULL;
+    char *c = g_strdup(compat);
+
+    /* Try to create the object */
+    ret = object_new(c);
+
+    if (!ret) {
+        /* Trim the version off the end and try again */
+        trim_version(c);
+        ret = object_new(c);
+
+        if (!ret) {
+            /* Replace commas with full stops */
+            substitute_char(c, ',', '.');
+            ret = object_new(c);
+        }
+    }
+
+    if (!ret) {
+        /*
+         * Restart with the orginal string and now replace commas with full
+         * stops and try again. This means that versions are still included.
+         */
+        g_free(c);
+        c = g_strdup(compat);
+        substitute_char(c, ',', '.');
+        ret = object_new(c);
+    }
+
+    if (dev_type) {
+        *dev_type = c;
+    } else {
+        g_free(c);
+    }
+
+    if (!ret) {
+        const char *no_vendor = trim_vendor(compat);
+
+        if (no_vendor != compat) {
+            return fdt_create_from_compat(no_vendor, dev_type);
+        }
+    }
+    return ret;
+}
+
+/*
+ * Error handler for device creation failure.
+ *
+ * We look for qemu-fdt-abort-on-error properties up the tree.
+ * If we find one, we abort with the provided error message.
+ */
+static void fdt_dev_error(FDTMachineInfo *fdti, char *node_path, char *compat)
+{
+    const char *abort_on_error;
+    const char *warn_on_error;
+
+    warn_on_error = qemu_fdt_getprop(fdti->fdt, node_path,
+                                   "qemu-fdt-warn-on-error", 0,
+                                   true, NULL);
+    abort_on_error = qemu_fdt_getprop(fdti->fdt, node_path,
+                                   "qemu-fdt-abort-on-error", 0,
+                                   true, NULL);
+    if (warn_on_error) {
+        if (strncmp("device_type", compat, strlen("device_type"))) {
+            warn_report("%s: %s", compat, warn_on_error);
+        }
+    }
+
+    if (abort_on_error) {
+        error_report("Failed to create %s", compat);
+        error_setg(&error_fatal, "%s", abort_on_error);
+    }
+}
+
 static int fdt_init_qdev(char *node_path, FDTMachineInfo *fdti, char *compat)
 {
+    Object *dev, *parent;
+    char *dev_type = NULL;
+    char parent_node_path[DT_PATH_LENGTH];
+
+    if (!compat) {
+        return 1;
+    }
+    dev = fdt_create_from_compat(compat, &dev_type);
+    if (!dev) {
+        DB_PRINT_NP(1, "no match found for %s\n", compat);
+        fdt_dev_error(fdti, node_path, compat);
+        return 1;
+    }
+    DB_PRINT_NP(1, "matched compat %s\n", compat);
+
+    /* Do this super early so fdt_generic_num_cpus is correct ASAP */
+    if (object_dynamic_cast(dev, TYPE_CPU)) {
+        fdt_generic_num_cpus++;
+        DB_PRINT_NP(0, "is a CPU - total so far %d\n", fdt_generic_num_cpus);
+    }
+
+    if (qemu_devtree_getparent(fdti->fdt, parent_node_path, node_path)) {
+        abort();
+    }
+    while (!fdt_init_has_opaque(fdti, parent_node_path) &&
+           !object_dynamic_cast(dev, TYPE_CPU)) {
+        fdt_init_yield(fdti);
+    }
+
+    parent = fdt_init_get_opaque(fdti, parent_node_path);
+
+    if (object_dynamic_cast(dev, TYPE_CPU)) {
+        parent = fdt_init_get_cpu_cluster(fdti, parent, compat);
+    }
+
+    if (dev->parent) {
+        DB_PRINT_NP(0, "Node already parented - skipping node\n");
+    } else if (parent) {
+        DB_PRINT_NP(1, "parenting node\n");
+        object_property_add_child(OBJECT(parent),
+                              qemu_devtree_get_node_name(fdti->fdt, node_path),
+                              OBJECT(dev));
+        if (object_dynamic_cast(dev, TYPE_DEVICE)) {
+            Object *parent_bus = parent;
+            unsigned int depth = 0;
+
+            DB_PRINT_NP(1, "bus parenting node\n");
+            /* Look for an FDT ancestor that is a Bus.  */
+            while (parent_bus && !object_dynamic_cast(parent_bus, TYPE_BUS)) {
+                /*
+                 * Assert against insanely deep hierarchies which are an
+                 * indication of loops.
+                 */
+                assert(depth < 4096);
+
+                parent_bus = parent_bus->parent;
+                depth++;
+            }
+
+            if (!parent_bus
+                && object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE)) {
+                /*
+                 * Didn't find any bus. Use the default sysbus one.
+                 * This allows ad-hoc busses belonging to sysbus devices to be
+                 * visible to -device bus=x.
+                 */
+                parent_bus = OBJECT(sysbus_get_default());
+            }
+
+            if (parent_bus) {
+                qdev_set_parent_bus(DEVICE(dev), BUS(parent_bus),
+                                    &error_abort);
+            }
+        }
+    } else {
+        DB_PRINT_NP(1, "orphaning node\n");
+        if (object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE)) {
+            qdev_set_parent_bus(DEVICE(dev), BUS(sysbus_get_default()),
+                                &error_abort);
+        }
+
+        /* FIXME: Make this go away (centrally) */
+        object_property_add_child(
+                              object_get_root(),
+                              qemu_devtree_get_node_name(fdti->fdt, node_path),
+                              OBJECT(dev));
+    }
+    fdt_init_set_opaque(fdti, node_path, dev);
+
+    g_free(dev_type);
+
     return 0;
 }
 
-- 
2.43.0