@@ -220,6 +220,9 @@ hw-obj-$(CONFIG_SMARTCARD) += usb-ccid.o ccid-card-passthru.o
hw-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o
hw-obj-$(CONFIG_USB_REDIR) += usb-redir.o
+hw-obj-$(CONFIG_FDT) += fdt_generic.o
+hw-obj-$(CONFIG_FDT) += fdt_generic_util.o
+
# PPC devices
hw-obj-$(CONFIG_OPENPIC) += openpic.o
hw-obj-$(CONFIG_PREP_PCI) += prep_pci.o
new file mode 100644
@@ -0,0 +1,199 @@
+/*
+ * Tables of FDT device models and their init functions. Keyed by compatibility
+ * strings, device instance names.
+ *
+ * Copyright (c) 2010 PetaLogix Qld Pty Ltd.
+ * Copyright (c) 2010 Peter A. G. Crosthwaite <peter.crosthwaite@petalogix.com>.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "fdt_generic.h"
+
+#define FDT_GENERIC_MAX_PATTERN_LEN 1024
+
+typedef struct TableListNode {
+ void *next;
+ char key[FDT_GENERIC_MAX_PATTERN_LEN];
+ FDTInitFn fdt_init;
+ void *opaque;
+} TableListNode;
+
+/* add a node to the table specified by *head_p */
+
+static void add_to_table(
+ FDTInitFn fdt_init,
+ const char *key,
+ void *opaque,
+ TableListNode **head_p)
+{
+ TableListNode *nn = malloc(sizeof(*nn));
+ nn->next = (void*)(*head_p);
+ strcpy(nn->key, key);
+ nn->fdt_init = fdt_init;
+ nn->opaque = opaque;
+ *head_p = nn;
+}
+
+/* FIXME: add return codes that differentiate between not found and error */
+
+/* search a table for a key string and call the fdt init function if found.
+ * Returns 0 if a match if found, 1 otherwise
+ */
+
+static int fdt_init_search_table(
+ char *node_path,
+ FDTMachineInfo *fdti,
+ const char *key, /* string to match */
+ TableListNode **head) /* head of the list to search */
+{
+ TableListNode *c = *head;
+ if (c == NULL) {
+ return 1;
+ } else if (!strcmp(key, c->key)) {
+ return c->fdt_init(node_path, fdti, c->opaque);
+ }
+ return fdt_init_search_table(node_path, fdti, key,
+ (TableListNode**)(&(*head)->next));
+}
+
+TableListNode *compat_list_head = NULL;
+
+void add_to_compat_table(FDTInitFn fdt_init, const char *compat, void *opaque)
+{
+ add_to_table(fdt_init, compat, opaque, &compat_list_head);
+}
+
+int fdt_init_compat(char *node_path, FDTMachineInfo *fdti, const char *compat)
+{
+ return fdt_init_search_table(node_path, fdti, compat, &compat_list_head);
+}
+
+TableListNode *inst_bind_list_head = NULL;
+
+void add_to_inst_bind_table(FDTInitFn fdt_init, const char *name, void *opaque)
+{
+ add_to_table(fdt_init, name, opaque, &inst_bind_list_head);
+}
+
+int fdt_init_inst_bind(char *node_path, FDTMachineInfo *fdti,
+ const char *name)
+{
+ return fdt_init_search_table(node_path, fdti, name, &inst_bind_list_head);
+}
+
+TableListNode *force_list_head = NULL;
+
+void add_to_force_table(FDTInitFn fdt_init, const char *name, void *opaque)
+{
+ add_to_table(fdt_init, name, opaque, &force_list_head);
+}
+
+int fdt_force_bind_all(FDTMachineInfo *fdti)
+{
+ int ret = 0;
+ while (force_list_head != NULL) {
+ TableListNode *to_delete = force_list_head;
+ ret |= force_list_head->fdt_init(NULL, fdti, force_list_head->opaque);
+ force_list_head = force_list_head->next;
+ free(to_delete);
+ }
+ return ret;
+}
+
+static void dump_table(TableListNode *head)
+{
+ if (head == NULL) {
+ return;
+ }
+ printf("key : %s, opaque data %p\n", head->key, head->opaque);
+ dump_table(head->next);
+}
+
+void dump_compat_table(void)
+{
+ printf("FDT COMPATIBILITY TABLE:\n");
+ dump_table(compat_list_head);
+}
+
+void dump_inst_bind_table(void)
+{
+ printf("FDT INSTANCE BINDING TABLE:\n");
+ dump_table(inst_bind_list_head);
+}
+
+void fdt_init_yield(FDTMachineInfo *fdti)
+{
+ qemu_co_queue_wait(fdti->cq);
+}
+
+void fdt_init_set_opaque(FDTMachineInfo *fdti, char *node_path, void *opaque)
+{
+ FDTDevOpaque *dp;
+ for (dp = fdti->dev_opaques;
+ dp->node_path && strcmp(dp->node_path, node_path);
+ dp++);
+ if (!dp->node_path) {
+ dp->node_path = strdup(node_path);
+ }
+ dp->opaque = opaque;
+}
+
+int fdt_init_has_opaque(FDTMachineInfo *fdti, char *node_path)
+{
+ FDTDevOpaque *dp;
+ for (dp = fdti->dev_opaques; dp->node_path; dp++) {
+ if (!strcmp(dp->node_path, node_path)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void *fdt_init_get_opaque(FDTMachineInfo *fdti, char *node_path)
+{
+ FDTDevOpaque *dp;
+ for (dp = fdti->dev_opaques; dp->node_path; dp++) {
+ if (!strcmp(dp->node_path, node_path)) {
+ return dp->opaque;
+ }
+ }
+ return NULL;
+}
+
+FDTMachineInfo *fdt_init_new_fdti(void *fdt)
+{
+ FDTMachineInfo *fdti = g_malloc0(sizeof(*fdti));
+ fdti->fdt = fdt;
+ fdti->cq = g_malloc0(sizeof(*(fdti->cq)));
+ qemu_co_queue_init(fdti->cq);
+ fdti->dev_opaques = g_malloc0( sizeof(*(fdti->dev_opaques)) *
+ (devtree_get_num_nodes(fdt) + 1) );
+ return fdti;
+}
+
+void fdt_init_destroy_fdti(FDTMachineInfo *fdti)
+{
+ FDTDevOpaque *dp;
+ for (dp = fdti->dev_opaques; dp->node_path; dp++) {
+ g_free(dp->node_path);
+ }
+ g_free(fdti->dev_opaques);
+ g_free(fdti);
+}
new file mode 100644
@@ -0,0 +1,92 @@
+/*
+ * Tables of FDT device models and their init functions. Keyed by compatibility
+ * strings, device instance names.
+ */
+
+#ifndef FDT_GENERIC_H
+#define FDT_GENERIC_H
+
+#include "qemu-common.h"
+#include "sysbus.h"
+#include "device_tree.h"
+#include "qemu-coroutine.h"
+
+typedef struct FDTDevOpaque {
+ char *node_path;
+ void *opaque;
+} FDTDevOpaque;
+
+typedef struct FDTMachineInfo {
+ /* the fdt blob */
+ void *fdt;
+ /* irq descriptors for top level int controller */
+ qemu_irq *irq_base;
+ /* per-device specific opaques */
+ FDTDevOpaque *dev_opaques;
+ /* recheck coroutine queue */
+ CoQueue *cq;
+} FDTMachineInfo;
+
+/* create a new FDTMachineInfo. The client is responsible for setting irq_base.
+ * the mutex fdt_mutex is locked on return. Client must call
+ * fdt_init_destroy_fdti to cleanup
+ */
+
+FDTMachineInfo *fdt_init_new_fdti(void *fdt);
+void fdt_init_destroy_fdti(FDTMachineInfo *fdti);
+
+typedef int (*FDTInitFn)(char *, FDTMachineInfo *, void *);
+
+/* associate a FDTInitFn with a FDT compatibility */
+
+void add_to_compat_table(FDTInitFn, const char *, void *);
+
+/* try and find a device model for a particular compatibility. If found,
+ * the FDTInitFn associated with the compat will be called and 0 will
+ * be returned. Returns non-zero on not found or error
+ */
+
+int fdt_init_compat(char *, FDTMachineInfo *, const char *);
+
+/* same as above, but associates with a FDT node name (rather than compat) */
+
+void add_to_inst_bind_table(FDTInitFn, const char *, void *);
+int fdt_init_inst_bind(char *, FDTMachineInfo *, const char *);
+
+/* Register an FDTInitFn that should always be called upon machine creation */
+
+void add_to_force_table(FDTInitFn, const char *, void *);
+int fdt_force_bind_all(FDTMachineInfo *);
+
+void dump_compat_table(void);
+void dump_inst_bind_table(void);
+
+/* Called from FDTInitFn's to inform the framework that a dependency is unresolved
+ * and the calling context needs to wait for another device to instantiate
+ * first. The calling thread will suspend until a change in state in the
+ * argument fdt machine is detected
+ */
+
+void fdt_init_yield(FDTMachineInfo *);
+
+/* set, check and get per device opaques. Keyed by fdt node_paths */
+
+void fdt_init_set_opaque(FDTMachineInfo *fdti, char *node_path, void *opaque);
+int fdt_init_has_opaque(FDTMachineInfo *fdti, char *node_path);
+void *fdt_init_get_opaque(FDTMachineInfo *fdti, char *node_path);
+
+/* statically register a FDTInitFn as being associate with a compatibility */
+
+#define fdt_register_compatibility_opaque(function, compat, n, opaque) \
+static void __attribute__((constructor)) \
+function ## n ## _register_imp( void ) { \
+ add_to_compat_table(function, compat, opaque); \
+}
+
+#define fdt_register_compatibility_n(function, compat, n) \
+fdt_register_compatibility_opaque(function, compat, n, NULL)
+
+#define fdt_register_compatibility(function, compat) \
+fdt_register_compatibility_n(function, compat, 0)
+
+#endif /* FDT_GENERIC_H */
new file mode 100644
@@ -0,0 +1,196 @@
+/*
+ * Utility functions for fdt generic framework
+ *
+ * Copyright (c) 2009 Edgar E. Iglesias.
+ * Copyright (c) 2009 Michal Simek.
+ * Copyright (c) 2011 PetaLogix Qld Pty Ltd.
+ * Copyright (c) 2011 Peter A. G. Crosthwaite <peter.crosthwaite@petalogix.com>.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "fdt_generic_util.h"
+#include "net.h"
+
+FDTMachineInfo *fdt_generic_create_machine(void *fdt, qemu_irq *cpu_irq)
+{
+ char node_path[DT_PATH_LENGTH];
+
+ FDTMachineInfo *fdti = fdt_init_new_fdti(fdt);
+
+ fdti->irq_base = cpu_irq;
+
+ /* bind any force bound instances */
+ fdt_force_bind_all(fdti);
+
+ /* parse the device tree branch under the simple-bus node */
+ if (!qemu_devtree_node_by_compatible(fdt, node_path, "simple-bus")) {
+ simple_bus_fdt_init(node_path, fdti, NULL);
+ while (qemu_co_queue_enter_next(fdti->cq));
+ }
+ else
+ fprintf(stderr, "FDT: ERROR: no system bus found in device tree\n");
+
+ printf("FDT: Device tree scan complete\n");
+ FDTMachineInfo *ret = g_malloc0(sizeof(*ret));
+ return fdti;
+}
+
+const char *fdt_generic_chosen_kcmdline(void *fdt, const char *append)
+{
+ char *p;
+ char chosen [DT_PATH_LENGTH];
+ if (!(
+ qemu_devtree_get_node_by_name(fdt, chosen, "chosen") &&
+ qemu_devtree_get_node_by_name(fdt, chosen, "chosen@0") )) {
+ int len = strlen(append), plen;
+
+ p = qemu_devtree_getprop_string(fdt, chosen,"bootargs", 0, NULL, 0);
+ if (p) {
+ plen = strlen(p);
+ len += plen;
+
+ p = memcpy(malloc(len + 2), p, plen);
+ p[plen] = ' ';
+ memcpy(p + plen + 1, append, len - plen + 1);
+ return p;
+ } else
+ return strdup(append);
+ }
+ return NULL;
+}
+
+struct FDTInitNodeArgs {
+ char *node_path;
+ FDTMachineInfo *fdti;
+};
+
+static void fdt_init_node(void *args) {
+
+ struct FDTInitNodeArgs *a = args;
+ char *node_path = a->node_path;
+ FDTMachineInfo *fdti = a->fdti;
+ g_free(a);
+
+ char *all_compats, *compat, *node_name, *next_compat;
+ int compat_len;
+
+ /* try instance binding first */
+ if (!(node_name = qemu_devtree_get_node_name(fdti->fdt, node_path))) {
+ printf("FDT: ERROR: nameless node: %s\n", node_path);
+ }
+ if (!fdt_init_inst_bind(node_path, fdti, node_name)) {
+ goto exit;
+ }
+
+ /* fallback to compatibility binding */
+ all_compats = qemu_devtree_getprop_string(fdti->fdt, node_path,
+ "compatible", 0, &compat_len, 0);
+ if (!all_compats) {
+ printf("FDT: ERROR: no compatibility found for node %s%s\n",node_path,
+ node_name);
+ return;
+ }
+ compat = all_compats;
+
+try_next_compat:
+ if (compat_len == 0) goto invalidate;
+ if (!fdt_init_compat(node_path, fdti, compat)) {
+ goto exit;
+ }
+ next_compat = rawmemchr(compat, '\0');
+ compat_len -= (next_compat + 1 - compat);
+ if (compat_len > 0) {
+ *next_compat = ' ';
+ }
+ compat = next_compat+1;
+ goto try_next_compat;
+invalidate:
+ printf("FDT: Unsupported peripheral invalidated %s compatibilities %s\n",
+ node_name, all_compats);
+ qemu_devtree_setprop_string(fdti->fdt, node_path, "compatible",
+ "invalidated");
+exit:
+ if (!fdt_init_has_opaque(fdti, node_path))
+ fdt_init_set_opaque(fdti, node_path, NULL);
+ g_free(node_path);
+ return;
+}
+
+int simple_bus_fdt_init(char *bus_node_path, FDTMachineInfo *fdti, void *unused)
+{
+ int i;
+ int num_children = qemu_devtree_get_num_children(fdti->fdt, bus_node_path, 1);
+ char **children = qemu_devtree_get_children(fdti->fdt, bus_node_path, 1);
+
+ for (i = 0; i < num_children;i++) {
+ struct FDTInitNodeArgs *init_args = g_malloc0(sizeof(*init_args));
+ init_args->node_path = children[i];
+ init_args->fdti = fdti;
+ qemu_coroutine_enter(qemu_coroutine_create(fdt_init_node), init_args);
+ }
+
+ g_free(children);
+ return 0;
+}
+
+qemu_irq fdt_get_irq_info(FDTMachineInfo *fdti, char *node_path, int irq_idx,
+ int *err, char *info) {
+ void *fdt = fdti->fdt;
+ int errl;
+ if (!err)
+ err = &errl;
+ int intc_phandle = qemu_devtree_getprop(fdt, err, node_path,
+ "interrupt-parent", 0, 1);
+ if (*err)
+ goto fail;
+ char intc_node_path[DT_PATH_LENGTH];
+ if (qemu_devtree_get_node_by_phandle(fdt, intc_node_path, intc_phandle))
+ goto fail;
+ int intc_cells = qemu_devtree_getprop(fdt, err, intc_node_path,
+ "#interrupt-cells", 0, 0);
+ if (*err)
+ goto fail;
+ int intc_idx = qemu_devtree_getprop(fdt, err, node_path, "interrupts",
+ intc_cells *irq_idx, 0);
+ if (*err)
+ goto fail;
+ while (!fdt_init_has_opaque(fdti, intc_node_path))
+ fdt_init_yield(fdti);
+ qemu_irq *irqs = fdt_init_get_opaque(fdti, intc_node_path);
+ if (!irqs)
+ goto fail;
+ if (info) {
+ char *node_name = qemu_devtree_get_node_name(fdt, intc_node_path);
+ sprintf(info, "%d (%s)", intc_idx, node_name);
+ g_free((void*)node_name);
+ }
+ return irqs[intc_idx];
+fail:
+ *err = 1;
+ if (info)
+ sprintf(info, "(none)");
+ return NULL;
+}
+
+qemu_irq fdt_get_irq(FDTMachineInfo *fdti, char *node_path, int irq_idx) {
+ return fdt_get_irq_info(fdti, node_path, irq_idx, NULL, NULL);
+}
+
+fdt_register_compatibility(simple_bus_fdt_init, "simple-bus");
new file mode 100644
@@ -0,0 +1,43 @@
+#ifndef FDT_GENERIC_UTIL_H
+#define FDT_GENERIC_UTIL_H
+
+#include "qemu-common.h"
+#include "fdt_generic.h"
+
+/* create a fdt_generic machine. the top level cpu irqs are required for
+ * systems instantiating interrupt devices. The client is responsible for
+ * destroying the returned FDTMachineInfo (using fdt_init_destroy_fdti)
+ */
+
+FDTMachineInfo *fdt_generic_create_machine(void *fdt, qemu_irq *cpu_irq);
+
+/* search a device tree for the 'chosen' node and return the boot args. An
+ * optional argument append can specify a string to cat onto the end of the
+ * returned bootargs
+ */
+
+const char *fdt_generic_chosen_kcmdline(void *fdt, const char *append);
+
+/* fdt init a simple bus. Search the bus for child nodes and instantiate or
+ * invalidate devices as appropriate. Conformant to FDTInitFn prototype, i.e.
+ * a bus may fdt_register_compatibilty this as its instantiator.
+ */
+
+int simple_bus_fdt_init(char *node_path, FDTMachineInfo *fdti, void *priv);
+
+/* get an irq for a device. The interrupt parent of a device is idenitified
+ * and the specified irq (by the interrupts device-tree property) is retrieved
+ */
+
+qemu_irq fdt_get_irq(FDTMachineInfo *fdti, char *node_path, int irq_idx);
+
+/* same as above, but poulates err with non-zero if something goes wrong, and
+ * populates info with a human readable string giving some basic information
+ * about the interrupt connection found (or not found). Both arguments are
+ * optional (i.e. can be NULL)
+ */
+
+qemu_irq fdt_get_irq_info(FDTMachineInfo *fdti, char *node_path, int irq_idx,
+ int * err, char * info);
+
+#endif /* FDT_GENERIC_UTIL_H */
First revision of fdt generic infrastructure. These modules allow for fdt generic machine models, which create machines to match a device tree specification. Signed-off-by: Peter A. G. Crosthwaite <peter.crosthwaite@petalogix.com> --- Makefile.objs | 3 + hw/fdt_generic.c | 199 +++++++++++++++++++++++++++++++++++++++++++++++++ hw/fdt_generic.h | 92 +++++++++++++++++++++++ hw/fdt_generic_util.c | 196 ++++++++++++++++++++++++++++++++++++++++++++++++ hw/fdt_generic_util.h | 43 +++++++++++ 5 files changed, 533 insertions(+), 0 deletions(-) create mode 100644 hw/fdt_generic.c create mode 100644 hw/fdt_generic.h create mode 100644 hw/fdt_generic_util.c create mode 100644 hw/fdt_generic_util.h