qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] Re: [RFC] Machine description as data


From: Markus Armbruster
Subject: [Qemu-devel] Re: [RFC] Machine description as data
Date: Mon, 23 Mar 2009 16:50:57 +0100
User-agent: Gnus/5.11 (Gnus v5.11) Emacs/22.3 (gnu/linux)

Seventh iteration of the prototype.  Work in progress, not quite ready for
merging.

New:

* New machine creation interface.  Machine types implementing the new
  interface provide QEMUMachine member drvtab[] instead of init().  When
  main() detects that, it builds machine configuration and passes it to
  (device-independent) dt_create_machine() instead of calling
  (device-dependent) init().

* Virtio block, balloon & console.

* Rebased to git-svn-id: svn://svn.savannah.nongnu.org/qemu/address@hidden 
c046a42c-6fe2-441c-8c8c-71466251a162

Not in, but not forgotten either:

* A few more renames suggested by reviewers.

* Reduce unnecessary differences to IEEE 1275 trees.

Shortcuts:

* No support for systems without PCI bus.

* I didn't implement all the devices of the "pc" original.  Missing:
  - Option ROMs
  - Audio

* Command line options -usb and -no-acpi have no effect; both USB and
  ACPI are always enabled.

* The configuration tree is simplistic.  I expect it to evolve, and I
  wouldn't exclude the possibility of wholesale replacement.

* The initial configuration tree is hardcoded in dt_read_config().  It
  should be read from a configuration file.

* A bus is identified by its kind and number.  The bus number depends on
  its position in the tree.  Means for position-independent addressing
  would be nice.

* The interface to the shared code in hw/pc.c (hw/pcint.h) is rather
  crude.

* The memory driver is PC-specific.  It should be generic and
  data-driven, but getting there isn't quite as easy as it sounds.
  Memory (and sometimes even holes) need to be allocated in just the
  right order to ensure guest physical address equals host offset for
  certain memory ranges.  I feel the proper way to address this is a
  better guest memory allocation interface.

* The pc-misc driver should most probably be split up some.

Bugs:

* hw/ppce500_mpc8544ds.c doesn't compile when I configure with fdt
  support.

* If I configure both a virtio block device and a virtio console, the
  Linux guest kernel hangs.  The same happens when I move virtio code in
  pc.c in an otherwise unmodified QEMU so that balloon and console are
  initialized earlier.


 Makefile              |    1 
 Makefile.target       |    4 
 dt.c                  |  779 ++++++++++++++++++++++++++++++++++++++++++++++++++
 dt.h                  |  115 +++++++
 hw/boards.h           |    6 
 hw/pc.c               |   47 +--
 hw/pcdt.c             |  677 +++++++++++++++++++++++++++++++++++++++++++
 hw/pci.h              |    2 
 hw/pcint.h            |   46 ++
 hw/vmware_vga.c       |    4 
 net.c                 |    2 
 net.h                 |    1 
 qemu-common.h         |    1 
 target-i386/machine.c |    1 
 tree.c                |  285 ++++++++++++++++++
 tree.h                |   41 ++
 vl.c                  |   15 
 17 files changed, 1992 insertions(+), 35 deletions(-)


/*
 * QEMU PC System Emulator
 *
 * Copyright (C) 2009 Red Hat, Inc., Markus Armbruster <address@hidden>
 * Copyright (c) 2003-2004 Fabrice Bellard
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

/*
 * Configure and build a machine from configuration data
 *
 * This is generic, device-independent code driven by device-dependent
 * configuration data, talking to devices through an abstract device
 * interface.
 *
 * Machine types using it implement QEMUMachine member drvtab[]
 * instead of member init().  See hw/pcdt.c for an example.
 */

#include <assert.h>
#include "block.h"
#include "cpu.h"
#include "dt.h"
#include "net.h"
#include "tree.h"
#include "sysemu.h"

#ifdef HAVE_FDT
#include <libfdt.h>
#endif

/* Forward declarations */
static void dt_parse_prop(dt_device *dev, tree_prop *prop);
static void dt_add_dyn_devs(tree *conf, dt_host *host,
                            const dt_driver drvtab[], int vga_ram_size);
static void dt_fdt_test(tree *conf);


/* Host Configuration */

struct dt_host {
    /* connection NIC <-> VLAN */
    int nics;
    tree *nic[MAX_NICS];
    VLANState *nic_vlan[MAX_NICS];
    /* connection drive <-> block driver state */
    int drives;
    int virtio_drives;
    tree *drive[MAX_DRIVES];
    BlockDriverState *drive_state[MAX_DRIVES];
};

static void dt_attach_nic(dt_host *host, tree *nic, VLANState *vlan)
{
    assert(host->nics < MAX_NICS);
    host->nic[host->nics] = nic;
    host->nic_vlan[host->nics] = vlan;
    host->nics++;
}

VLANState *dt_find_vlan(tree *conf, dt_host *host)
{
    int i;

    for (i = 0; i < host->nics; i++) {
        if (host->nic[i] == conf)
            return host->nic_vlan[i];
    }
    return NULL;
}

static void dt_attach_drive(dt_host *host, tree *node, BlockDriverState *state)
{
    assert(host->drives < MAX_DRIVES);
    host->drive[host->drives] = node;
    host->drive_state[host->drives] = state;
    host->drives++;
}

void dt_find_drives(tree *conf, dt_host *host,
                    BlockDriverState *drive[], int n)
{
    int i, unit;

    memset(drive, 0, n * sizeof(drive[0]));

    for (i = 0; i < host->drives; i++) {
        if (tree_parent(host->drive[i]) != conf)
            continue;
        unit = dt_get_unit(dt_device_of(host->drive[i]));
        assert(unit < n && !drive[unit]);
        drive[unit] = host->drive_state[i];
    }
}

static void dt_print_host_config(dt_host *host)
{
    char buf[1024];
    int i;

    for (i = 0; i < host->nics; i++) {
        if (!host->nic[i])
            continue;
        tree_path(host->nic[i], buf, sizeof(buf));
        printf("nic#%d\tvlan %-4d\t%s\n",
               i, host->nic_vlan[i]->id, buf);
    }

    for (i = 0; i < host->drives; i++) {
        tree_path(host->drive[i], buf, sizeof(buf));
        printf("drive#%d\t%-15s %s\n",
               i, bdrv_get_device_name(host->drive_state[i]), buf);
    }
}


/* Device Interface */

static const dt_driver *dt_driver_by_name(const char *name,
                                          const dt_driver drvtab[])
{
    int i;

    for (i = 0; drvtab[i].name; i++) {
        if (!strcmp(name, drvtab[i].name))
            return &drvtab[i];
    }
    return NULL;
}

dt_device *dt_device_of(tree *conf)
{
    return tree_get_user(conf);
}

dt_device *dt_parent_device(dt_device *dev)
{
    tree *p = tree_parent(dev->conf);

    return p ? dt_device_of(p) : NULL;
}

static dt_device *dt_do_find_bus(tree *conf, dt_bus_type bus_type, int *skip)
{
    dt_device *dev;
    tree *child;

    dev = dt_device_of(conf);
    if (dev->drv->bus_type == bus_type && (*skip)-- == 0)
        return dev;

    TREE_FOREACH_CHILD(child, conf) {
        dev = dt_do_find_bus(child, bus_type, skip);
        if (dev)
            return dev;
    }

    return NULL;
}

static dt_device *dt_find_bus(tree *conf, dt_bus_type bus_type, int busno)
{
    return dt_do_find_bus(conf, bus_type, &busno);
}

PCIBus *dt_get_pcibus(dt_device *dev)
{
    dt_device *bus = dt_parent_device(dev);

    return bus->drv->get_pcibus(bus);
}

int dt_get_unit(dt_device *dev)
{
    return dev->drv->get_unit(dev);
}

static dt_device *dt_new_device(tree *conf, const dt_driver *drv)
{
    dt_device *dev;
    tree_prop *prop;

    dev = qemu_malloc(sizeof(*dev));
    dev->conf = conf;
    dev->drv = drv;
    LIST_INIT(&dev->reqs);
    dev->visit = 0;
    dev->priv = qemu_malloc(drv->privsz);
    tree_put_user(conf, dev);

    TREE_FOREACH_PROP(prop, conf)
        dt_parse_prop(dev, prop);

    return dev;
}

static dt_device *dt_create(tree *conf, const dt_driver drvtab[])
{
    const dt_driver *drv;
    dt_device *dev;
    tree *child;

    drv = dt_driver_by_name(tree_node_name(conf), drvtab);
    if (!drv) {
        fprintf(stderr, "No driver for device %s\n",
                tree_node_name(conf));
        exit(1);
    }

    assert((drv->bus_type == DT_BUS_PCI) == (drv->get_pcibus != NULL));

    dev = dt_new_device(conf, drv);

    TREE_FOREACH_CHILD(child, conf)
        dt_create(child, drvtab);

    return dev;
}

static void dt_config(tree *conf, dt_host *host)
{
    dt_device *dev = dt_device_of(conf);
    dt_device *bus = dt_parent_device(dev);
    tree *child;

    if (dev->drv->parent_bus_type == DT_BUS_NONE
        ? bus != NULL
        : bus == NULL || bus->drv->bus_type != dev->drv->parent_bus_type) {
        fprintf(stderr, "Device %s is not on a suitable bus\n",
                dev->drv->name);
        exit(1);
    }

    if (dev->drv->config)
        dev->drv->config(dev, host);

    TREE_FOREACH_CHILD(child, conf)
        dt_config(child, host);
}

tree *dt_require_named(dt_device *dev, const char *reqname)
{
    dt_tree_list *l = qemu_malloc(sizeof(*l));

    l->conf = tree_node_by_name(dev->conf, reqname);
    LIST_INSERT_HEAD(&dev->reqs, l, link);
    return l->conf;
}

static void dt_do_visit(dt_device *dev,
                        void (*fun)(dt_device *, void *arg),
                        void *arg, int visit)
{
    dt_device *parent, *req, *child;
    dt_tree_list *l;
    tree *k;

    assert(dev->visit < visit - 1);
    dev->visit = visit - 1;
    parent = dt_parent_device(dev);
    if (parent && parent->visit < visit)
        dt_do_visit(parent, fun, arg, visit);
    LIST_FOREACH(l, &dev->reqs, link) {
        req = dt_device_of(l->conf);
        if (req->visit < visit)
            dt_do_visit(req, fun, arg, visit);
    }
    dev->visit = visit;
    fun(dev, arg);
    TREE_FOREACH_CHILD(k, dev->conf) {
        child = dt_device_of(k);
        if (child->visit < visit - 1)
            dt_do_visit(child, fun, arg, visit);
    }
}

static void dt_visit(tree *node,
                     void (*fun)(dt_device *, void *arg),
                     void *arg)
{
    static int visit;

    visit += 2;
    dt_do_visit(dt_device_of(node), fun, arg, visit);
}

static void dt_init_visitor(dt_device *dev, void *arg)
{
    if (dev->drv->init)
        dev->drv->init(dev);
}

static void dt_init(tree *conf)
{
    dt_visit(conf, dt_init_visitor, NULL);
}

static void dt_start(tree *conf)
{
    dt_device *dev = dt_device_of(conf);
    tree *child;

    if (dev && dev->drv->start)
        dev->drv->start(dev);

    TREE_FOREACH_CHILD(child, conf)
        dt_start(child);
}

void dt_create_machine(tree *conf)
{
    dt_fdt_test(conf);
    dt_init(conf);
    dt_start(conf);
}


/* Device properties */

static const dt_prop_spec *dt_prop_spec_by_name(const dt_driver *drv,
                                                const char *name)
{
    const dt_prop_spec *spec;

    for (spec = drv->prop_spec; spec && spec->name; spec++) {
        if (!strcmp(spec->name, name))
            return spec;
    }
    return NULL;
}

static void dt_parse_prop(dt_device *dev, tree_prop *prop)
{
    const char *name = tree_prop_name(prop);
    size_t size;
    const char *val = tree_prop_value(prop, &size);
    const dt_prop_spec *spec = dt_prop_spec_by_name(dev->drv, name);

    if (!spec) {
        fprintf(stderr, "A %s device has no property %s\n",
                dev->drv->name, name);
        exit(1);
    }

    if (memchr(val, 0, size) != val + size - 1
        || spec->parse((char *)dev->priv + spec->offs, val, spec) < 0) {
        fprintf(stderr, "Bad value %.*s for property %s of device %s\n",
                size, val, name, dev->drv->name);
        exit(1);
    }
}

int dt_parse_string(void *dst, const char *src, const dt_prop_spec *spec)
{
    assert(spec->size == sizeof(char *));
    *(const char **)dst = src;
    return 0;
}

int dt_parse_int(void *dst, const char *src, const dt_prop_spec *spec)
{
    char *ep;
    long val;

    assert(spec->size == sizeof(int));
    errno = 0;
    val = strtol(src, &ep, 0);
    if (*ep || ep == src || errno || (int)val != val)
        return -1;
    *(int *)dst = val;
    return 0;
}

int dt_parse_ram_addr_t(void *dst, const char *src, const dt_prop_spec *spec)
{
    char *ep;
    unsigned long val;

    assert(spec->size == sizeof(ram_addr_t));
    errno = 0;
    val = strtoul(src, &ep, 0);
    if (*ep || ep == src || errno || (ram_addr_t)val != val)
        return -1;
    *(ram_addr_t *)dst = val;
    return 0;
}

int dt_parse_macaddr(void *dst, const char *src, const dt_prop_spec *spec)
{
    assert(spec->size == 6);
    if (parse_macaddr(dst, src) < 0)
        return -1;
    return 0;
}


/* Dynamic Devices */

static void dt_add_dyn_dev(tree *conf, tree *node, const dt_driver drvtab[],
                           int busno)
{
    dt_device *dev = dt_create(node, drvtab);
    dt_device *bus = dt_find_bus(conf, dev->drv->parent_bus_type, busno);

    if (!bus) {
        fprintf(stderr, "No suitable bus for device %s\n", dev->drv->name);
        exit(1);
    }

    tree_insert(bus->conf, node);
}

static void dt_add_vga(tree *conf, const dt_driver drvtab[],
                       const char *model, int vga_ram_size)
{
    tree *node = tree_new_child(NULL, "vga", NULL);

    tree_put_propf(node, "model", "%s", model);
    tree_put_propf(node, "ram", "%#x", vga_ram_size);
    dt_add_dyn_dev(conf, node, drvtab, 0);
}

static void dt_add_virtio_console(tree *conf, const dt_driver drvtab[],
                                  int index)
{
    tree *node = tree_new_child(NULL, "virtio-console", NULL);

    tree_put_propf(node, "index", "%d", index);
    dt_add_dyn_dev(conf, node, drvtab, 0);
}

static void dt_add_nic(tree *conf, dt_host *host, const dt_driver drvtab[],
                       NICInfo *n)
{
    tree *node = node = tree_new_child(NULL, "nic", NULL);

    tree_put_propf(node, "mac", "%02x:%02x:%02x:%02x:%02x:%02x",
                   n->macaddr[0], n->macaddr[1], n->macaddr[2],
                   n->macaddr[3], n->macaddr[4], n->macaddr[5]);
    tree_put_propf(node, "model", "%s",
                   n->model ? n->model : "ne2k_pci");
    if (n->name)
        tree_put_propf(node, "name", "%s", n->name);
    dt_add_dyn_dev(conf, node, drvtab, 0);
    dt_attach_nic(host, node, n->vlan);
}

static void dt_add_scsi(tree *conf, const dt_driver drvtab[], int busno)
{
    tree *node = tree_new_child(NULL, "scsi", NULL);

    dt_add_dyn_dev(conf, node, drvtab, 0);
    assert(dt_find_bus(conf, DT_BUS_SCSI, busno)->conf == node);
}

static void dt_add_virtio_block(tree *conf, const dt_driver drvtab[],
                                int busno)
{
    tree *node = tree_new_child(NULL, "virtio-block", NULL);

    dt_add_dyn_dev(conf, node, drvtab, 0);
    assert(dt_find_bus(conf, DT_BUS_VIRTIO, busno)->conf == node);
}

static const char *block_if_name[] = {
    [IF_IDE] = "ide",
    [IF_SCSI] = "scsi",
    [IF_FLOPPY] = "floppy",
    [IF_PFLASH] = "pflash",
    [IF_MTD] = "mtd",
    [IF_SD] = "sd",
    [IF_VIRTIO] = "virtio",
};

static void dt_do_add_drive(tree *conf, dt_host *host,
                            const dt_driver drvtab[],
                            int bus_type, int busno, int unit,
                            BlockDriverState *bdrv)
{
    char buf[32];
    tree *node;

    snprintf(buf, sizeof(buf), "%s-drive", block_if_name[bus_type]);
    node = tree_new_child(NULL, strdup(buf), NULL);
    tree_put_propf(node, "unit", "%d", unit);
    dt_add_dyn_dev(conf, node, drvtab, busno);
    dt_attach_drive(host, node, bdrv);
}

static void dt_add_drive(tree *conf, dt_host *host, const dt_driver drvtab[],
                         DriveInfo *d)
{
    switch (d->type) {
    case IF_IDE:
        /* hack to hang all IDE drives off the same node for now */
        dt_do_add_drive(conf, host, drvtab,
                        d->type, 0, d->bus * MAX_IDE_DEVS + d->unit, d->bdrv);
        break;
    case IF_SCSI:
    case IF_FLOPPY:
        dt_do_add_drive(conf, host, drvtab,
                        d->type, d->bus, d->unit, d->bdrv);
        break;
    case IF_VIRTIO:
        /* See comment in on virtio block in dt_add_dyn_devs() */
        dt_do_add_drive(conf, host, drvtab,
                        d->type, host->virtio_drives++, 0, d->bdrv);
        break;
    case IF_PFLASH:
    case IF_MTD:
    case IF_SD:
        /* TODO implement */
        fprintf(stderr, "Ignoring unimplemented drive %s\n",
                drives_opt[d->drive_opt_idx].opt);
        break;
    }
}

static void dt_add_dyn_devs(tree *conf, dt_host *host,
                            const dt_driver drvtab[], int vga_ram_size)
{
    int i, max_bus, busno;

    /* VGA */
    if (cirrus_vga_enabled || vmsvga_enabled || std_vga_enabled) {
        dt_add_vga(conf, drvtab,
                   cirrus_vga_enabled ? "cirrus"
                   : vmsvga_enabled ? "vms" : "std",
                   vga_ram_size);
    }

    /* Virtio consoles */
    for (i = 0; i < MAX_VIRTIO_CONSOLES; i++) {
        if (virtcon_hds[i])
            dt_add_virtio_console(conf, drvtab, i);
    }

    /* NICs */
    for(i = 0; i < nb_nics; i++)
        dt_add_nic(conf, host, drvtab, &nd_table[i]);

    /*
     * SCSI controllers
     *
     * This creates all controllers 0..max_bus, whether they have
     * drives or not.  Matches pc.c behavior.
     */
    max_bus = drive_get_max_bus(IF_SCSI);
    for (i = 0; i <= max_bus; i++)
        dt_add_scsi(conf, drvtab, i);

    /*
     * Virtio block controllers
     *
     * Each virtio drive is its own PCI device.  Since the device tree
     * should reflect that, we give each device on its own virtio
     * block controller node.
     *
     * DriveInfo's bus and unit are a mess.  The user can specify any
     * bus or unit number.  An unspecified bus number defaults to
     * zero, and an unspecified unit number defaults to the first
     * unused one (see drive_init()).  pc.c silently ignores all
     * virtio drives with non-zero bus number, and all drives on bus
     * zero after the first unused unit number.  Instead of
     * replicating that questionable behavior, simply ignore bus and
     * unit for these drives.
     */
    busno = 0;
    for (i = 0; i < nb_drives; i++) {
        if (drives_table[i].type == IF_VIRTIO)
            dt_add_virtio_block(conf, drvtab, busno++);
    }

    /* Drives */
    for (i = 0; i < nb_drives; i++)
        dt_add_drive(conf, host, drvtab, &drives_table[i]);
}


/* Create a configuration */

tree *dt_read_config(const char *name)
{
#ifdef TARGET_X86_64
#define CPU_MODEL_DEFAULT "qemu64"
#else
#define CPU_MODEL_DEFAULT "qemu32"
#endif
    tree *root, *pci, *leaf;

    /*
     * TODO Read from config file.
     *
     * TODO Pretty far from a comprehensive machine configuration, but
     * we need to start somewhere.
     */
    if (strcmp(name, "pcdt")) {
        fprintf(stderr, "qemu: machine %s not implemented", name);
        exit(1);
    }
    root = tree_new_child(NULL, "", NULL);
    leaf = tree_new_child(root, "cpus", NULL);
    tree_put_propf(leaf, "model", "%s", CPU_MODEL_DEFAULT);
    leaf = tree_new_child(root, "memory", NULL);
    leaf = tree_new_child(root, "pc-misc", NULL);
    pci = tree_new_child(root, "pci", NULL);
    leaf = tree_new_child(pci, "piix3", NULL);
    leaf = tree_new_child(pci, "virtio-balloon", NULL);
    return root;
#undef CPU_MODEL_DEFAULT
}

/*
 * Extract configuration from arguments and various global variables
 * and put it into our machine configuration.
 */
void dt_modify_config(tree *conf,
                      const dt_driver drvtab[],
                      ram_addr_t ram_size, int vga_ram_size,
                      const char *boot_device,
                      const char *kernel_filename,
                      const char *kernel_cmdline,
                      const char *initrd_filename,
                      const char *cpu_model)
{
    /*
     * TODO This is still pretty cheesy: we insert stuff into the tree
     * at hardcoded places.  Replacing placeholders instead would be
     * more flexible.  Another idea is to mark certain parts of the
     * initial tree optional, and remove them here.
     */
    tree *node;
    dt_host host;

    tree_print(conf);

    node = tree_node_by_name(conf, "/cpus");
    tree_put_propf(node, "num", "%d", smp_cpus);
    if (cpu_model)
        tree_put_propf(node, "model", "%s", cpu_model);

    node = tree_node_by_name(conf, "/memory");
    tree_put_propf(node, "ram", "%#lx", (unsigned long)ram_size);

    node = tree_node_by_name(conf, "/pc-misc");
    tree_put_propf(node, "boot-device", "%s", boot_device);

    /* Unimplemented stuff */
    if (kernel_filename)
        abort();                /* TODO */

    dt_create(conf, drvtab);
    memset(&host, 0, sizeof(host));
    dt_add_dyn_devs(conf, &host, drvtab, vga_ram_size);
    dt_config(conf, &host);

    dt_print_host_config(&host);
    tree_print(conf);
}


/* Interfacing with FDT */

/*
 * Note: translation to FDT loses the association between
 * configuration tree nodes and devices.
 */

#ifdef HAVE_FDT

static int dt_fdt_chk(int res);
static void dt_subtree_to_fdt(const tree *conf, void *fdt);

static void *dt_tree_to_fdt(const tree *conf)
{
    int sz = 1024 * 1024;       /* FIXME arbitrary limit */
    void *fdt = qemu_malloc(sz);

    dt_fdt_chk(fdt_create(fdt, sz));
    dt_subtree_to_fdt(conf, fdt);
    dt_fdt_chk(fdt_finish(fdt));
    return fdt;
}

static void dt_subtree_to_fdt(const tree *conf, void *fdt)
{
    tree_prop *prop;
    tree *child;
    const void *pv;
    size_t sz;

    dt_fdt_chk(fdt_begin_node(fdt, tree_node_name(conf)));
    TREE_FOREACH_PROP(prop, conf) {
        pv = tree_prop_value(prop, &sz);
        dt_fdt_chk(fdt_property(fdt, tree_prop_name(prop), pv, sz));
    }
    TREE_FOREACH_CHILD(child, conf)
        dt_subtree_to_fdt(child, fdt);
    dt_fdt_chk(fdt_end_node(fdt));
}

static tree *dt_fdt_to_tree(const void *fdt)
{
    int offs, next, depth;
    uint32_t tag;
    struct fdt_property *prop;
    tree *stack[32];            /* FIXME arbitrary limit */

    stack[0] = NULL;            /* "parent" of root */
    next = depth = 0;

    for (;;) {
        offs = next;
        tag = fdt_next_tag(fdt, offs, &next);
        switch (tag) {
        case FDT_PROP:
            /*
             * libfdt apparently doesn't provide a way to get property
             * by offset, do it by hand
             */
            assert(0 < depth && depth < ARRAY_SIZE(stack));
            prop = (void *)(const char *)fdt + fdt_off_dt_struct(fdt) + offs;
            tree_put_prop(stack[depth],
                          fdt_string(fdt, fdt32_to_cpu(prop->nameoff)),
                          prop->data,
                          fdt32_to_cpu(prop->len));
        case FDT_NOP:
            break;
        case FDT_BEGIN_NODE:
            depth++;
            assert(0 < depth && depth < ARRAY_SIZE(stack));
            stack[depth] = tree_new_child(stack[depth-1],
                                          fdt_get_name(fdt, offs, NULL),
                                          NULL);
            break;
        case FDT_END_NODE:
            depth--;
            break;
        case FDT_END:
            dt_fdt_chk(next);
            return stack[1];
        }
    }
}

static int dt_fdt_chk(int res)
{
    if (res < 0) {
        fprintf(stderr, "%s\n", fdt_strerror(res)); /* FIXME cryptic */
        exit(1);
    }
    return res;
}

static void dt_fdt_test(tree *conf)
{
    void *fdt;

    fdt = dt_tree_to_fdt(conf);
    conf = dt_fdt_to_tree(fdt);
    tree_print(conf);
    free(fdt);
}
#else
static void dt_fdt_test(tree *conf) { }
#endif




reply via email to

[Prev in Thread] Current Thread [Next in Thread]