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