From patchwork Thu Aug 25 06:41:09 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Peter A. G. Crosthwaite" X-Patchwork-Id: 111470 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [140.186.70.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 07351B6F6F for ; Thu, 25 Aug 2011 16:43:45 +1000 (EST) Received: from localhost ([::1]:40189 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QwTf6-00089u-75 for incoming@patchwork.ozlabs.org; Thu, 25 Aug 2011 02:43:36 -0400 Received: from eggs.gnu.org ([140.186.70.92]:46843) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QwTeo-0007ej-3I for qemu-devel@nongnu.org; Thu, 25 Aug 2011 02:43:20 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1QwTel-0003wq-TF for qemu-devel@nongnu.org; Thu, 25 Aug 2011 02:43:17 -0400 Received: from mail-gw0-f45.google.com ([74.125.83.45]:48703) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QwTel-0003uJ-PG for qemu-devel@nongnu.org; Thu, 25 Aug 2011 02:43:15 -0400 Received: by mail-gw0-f45.google.com with SMTP id 19so1694034gwb.4 for ; Wed, 24 Aug 2011 23:43:15 -0700 (PDT) Received: by 10.236.177.66 with SMTP id c42mr16724844yhm.14.1314254595488; Wed, 24 Aug 2011 23:43:15 -0700 (PDT) Received: from localhost ([124.148.20.9]) by mx.google.com with ESMTPS id c63sm477412yhe.18.2011.08.24.23.43.11 (version=TLSv1/SSLv3 cipher=OTHER); Wed, 24 Aug 2011 23:43:14 -0700 (PDT) From: "Peter A. G. Crosthwaite" To: qemu-devel@nongnu.org, stefanha@linux.vnet.ibm.com, edgar.iglesias@gmail.com, john.williams@petalogix.com, michal.simek@petalogix.com Date: Thu, 25 Aug 2011 16:41:09 +1000 Message-Id: <1314254480-22438-4-git-send-email-peter.crosthwaite@petalogix.com> X-Mailer: git-send-email 1.7.3.2 In-Reply-To: <1314254480-22438-1-git-send-email-peter.crosthwaite@petalogix.com> References: <1314254480-22438-1-git-send-email-peter.crosthwaite@petalogix.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 2) X-Received-From: 74.125.83.45 Cc: "Peter A. G. Crosthwaite" Subject: [Qemu-devel] [RFC PATCH V1 03/14] fdt_generic: First revision X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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 --- 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 diff --git a/Makefile.objs b/Makefile.objs index d1f3e5d..9fd18ff 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -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 diff --git a/hw/fdt_generic.c b/hw/fdt_generic.c new file mode 100644 index 0000000..cfcaaf6 --- /dev/null +++ b/hw/fdt_generic.c @@ -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 . + * + * 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); +} diff --git a/hw/fdt_generic.h b/hw/fdt_generic.h new file mode 100644 index 0000000..977b627 --- /dev/null +++ b/hw/fdt_generic.h @@ -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 */ diff --git a/hw/fdt_generic_util.c b/hw/fdt_generic_util.c new file mode 100644 index 0000000..bd78fc4 --- /dev/null +++ b/hw/fdt_generic_util.c @@ -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 . + * + * 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"); diff --git a/hw/fdt_generic_util.h b/hw/fdt_generic_util.h new file mode 100644 index 0000000..161cf7f --- /dev/null +++ b/hw/fdt_generic_util.h @@ -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 */