diff mbox

[8/9] MicroBlaze: Add a config that is dynamically set up by a device tree file.

Message ID 1347236407-10465-9-git-send-email-crwulff@gmail.com
State New
Headers show

Commit Message

crwulff@gmail.com Sept. 10, 2012, 12:20 a.m. UTC
From: Chris Wulff <crwulff@gmail.com>

Signed-off-by: Chris Wulff <crwulff@gmail.com>
---
 default-configs/microblaze-softmmu.mak |    1 +
 hw/labx_microblaze_devicetree.c        |  597 ++++++++++++++++++++++++++++++++
 hw/microblaze/Makefile.objs            |    3 +-
 3 files changed, 600 insertions(+), 1 deletion(-)
 create mode 100644 hw/labx_microblaze_devicetree.c

Comments

Blue Swirl Sept. 11, 2012, 7:27 p.m. UTC | #1
On Mon, Sep 10, 2012 at 12:20 AM,  <crwulff@gmail.com> wrote:
> From: Chris Wulff <crwulff@gmail.com>
>
> Signed-off-by: Chris Wulff <crwulff@gmail.com>
> ---
>  default-configs/microblaze-softmmu.mak |    1 +
>  hw/labx_microblaze_devicetree.c        |  597 ++++++++++++++++++++++++++++++++
>  hw/microblaze/Makefile.objs            |    3 +-
>  3 files changed, 600 insertions(+), 1 deletion(-)
>  create mode 100644 hw/labx_microblaze_devicetree.c
>
> diff --git a/default-configs/microblaze-softmmu.mak b/default-configs/microblaze-softmmu.mak
> index 64c9485..7e4cab8 100644
> --- a/default-configs/microblaze-softmmu.mak
> +++ b/default-configs/microblaze-softmmu.mak
> @@ -5,3 +5,4 @@ CONFIG_PFLASH_CFI01=y
>  CONFIG_SERIAL=y
>  CONFIG_XILINX=y
>  CONFIG_XILINX_AXI=y
> +CONFIG_LABX=y
> diff --git a/hw/labx_microblaze_devicetree.c b/hw/labx_microblaze_devicetree.c
> new file mode 100644
> index 0000000..581ad14
> --- /dev/null
> +++ b/hw/labx_microblaze_devicetree.c
> @@ -0,0 +1,597 @@
> +/*
> + * Flexible model of microblaze designs that use a device-tree to determine
> + * the hardware configuration.
> + *
> + * Copyright (c) 2010 LabX Technologies, LLC
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library 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
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include "sysbus.h"
> +#include "hw.h"
> +#include "net.h"
> +#include "flash.h"
> +#include "sysemu.h"
> +#include "devices.h"
> +#include "boards.h"
> +#include "device_tree.h"
> +#include "xilinx.h"
> +#include "labx_devices.h"
> +#include "loader.h"
> +#include "elf.h"
> +#include "blockdev.h"
> +#include "exec-memory.h"
> +#include "microblaze_pic_cpu.h"
> +
> +#define LMB_BRAM_SIZE  (128 * 1024)
> +
> +static struct
> +{
> +    uint32_t bootstrap_pc;
> +    uint32_t cmdline;
> +    uint32_t initrd;
> +    uint32_t fdt;
> +} boot_info;

Please read CODING_STYLE, struct names must use CamelCase.

> +
> +/* Current ethernet device index for multiple network interfaces */
> +static int eth_dev_index = -1;

This looks fishy.

> +
> +static void main_cpu_reset(void *opaque)
> +{
> +    MicroBlazeCPU *cpu = opaque;
> +    CPUMBState *env = &cpu->env;
> +
> +    cpu_reset(CPU(cpu));
> +
> +    env->regs[5] = boot_info.cmdline;
> +    env->regs[6] = boot_info.initrd;
> +    env->regs[7] = boot_info.fdt;
> +    env->sregs[SR_PC] = boot_info.bootstrap_pc;
> +}
> +
> +#ifndef CONFIG_FDT
> +#error "Device-tree support is required for this target to function"
> +#endif
> +
> +static uint32_t fdt_get_int_from_array(void *fdt, int node,
> +                                       const char *name, int index)
> +{
> +    int array_length;
> +    const void *array = qemu_devtree_getprop_offset(fdt, node, name,
> +                                                    &array_length);
> +    if (index >= array_length) {
> +        printf("fdt_get_int_from_array: requesting %s from node %d, "
> +               "index %d out of range (%d max)\n",
> +               name, node, index, array_length);
> +        return 0;
> +    } else {
> +        return qemu_devtree_int_array_index(array, index);
> +    }
> +}
> +
> +#define BINARY_DEVICE_TREE_FILE "labx-microblaze.dtb"
> +static void *get_device_tree(int *fdt_size)
> +{
> +    char *path;
> +    void *fdt;
> +
> +    /* Try the local "mb.dtb" override.  */
> +    fdt = load_device_tree("mb.dtb", fdt_size);
> +    if (!fdt) {
> +        path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
> +        if (path) {
> +            fdt = load_device_tree(path, fdt_size);
> +            g_free(path);
> +        }
> +    }
> +
> +    return fdt;
> +}
> +
> +static int labx_microblaze_load_device_tree(target_phys_addr_t addr,
> +                                      uint32_t ramsize,
> +                                      target_phys_addr_t initrd_base,
> +                                      target_phys_addr_t initrd_size,
> +                                      const char *kernel_cmdline)
> +{
> +    int fdt_size;
> +    void *fdt;
> +    int r;
> +
> +    fdt = get_device_tree(&fdt_size);
> +
> +    if (!fdt) {
> +        return 0;
> +    }
> +
> +    if (kernel_cmdline && strlen(kernel_cmdline)) {
> +        r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs",
> +                                        kernel_cmdline);
> +        if (r < 0) {
> +            fprintf(stderr, "couldn't set /chosen/bootargs\n");
> +        }
> +    }
> +    cpu_physical_memory_write(addr, (void *)fdt, fdt_size);
> +
> +    return fdt_size;
> +}
> +
> +static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
> +{
> +    return addr - 0x30000000LL;

ULL

> +}
> +
> +static ram_addr_t get_dram_base(void *fdt)
> +{
> +    int root = qemu_devtree_node_offset(fdt, "/");
> +    int memory = qemu_devtree_subnode_offset_namelen(fdt, root, "memory", 6);
> +    if (memory > 0) {
> +        int reglen;
> +        const void *reg =
> +            qemu_devtree_getprop_offset(fdt, memory, "reg", &reglen);
> +
> +        if (reglen >= 4) {
> +            printf("DRAM base %08X, size %08X\n",
> +                   qemu_devtree_int_array_index(reg, 0),
> +                   qemu_devtree_int_array_index(reg, 1));
> +            return qemu_devtree_int_array_index(reg, 0);
> +        }
> +    }
> +
> +    printf("DRAM base not found. Defaulting to 0x88000000\n");
> +
> +    return 0x88000000; /* Default to something reasonable */
> +}
> +
> +typedef void (*device_init_func_t)(void *fdt, int node);
> +
> +typedef struct {
> +    device_init_func_t probe;
> +    int pass;
> +    const char **compat;
> +
> +} devinfo_t;

Although a common error, _t suffix is reserved.

> +
> +/*
> + * Xilinx interrupt controller device
> + */
> +MicroBlazeCPU *g_cpu;
> +static qemu_irq irq[32] = {};
> +static qemu_irq *cpu_irq;

These should form the device state structure, passed around instead of
static state.

> +
> +static void xilinx_interrupt_controller_probe(void *fdt, int node)
> +{
> +    int i;
> +    DeviceState *dev;
> +    int reglen;
> +    const void *reg = qemu_devtree_getprop_offset(fdt, node, "reg", &reglen);
> +    uint32_t irq_addr = qemu_devtree_int_array_index(reg, 0);
> +    int nrIrqLen;

CamelCase is not used for other identifiers.

> +    const void *nrIrq =
> +        qemu_devtree_getprop_offset(fdt, node, "xlnx,num-intr-inputs",
> +                                    &nrIrqLen);
> +    uint32_t nrIrqs = qemu_devtree_int_array_index(nrIrq, 0);
> +
> +    printf("  IRQ BASE %08X NIRQS %d\n", irq_addr, nrIrqs);

Leftover debugging?

> +
> +    cpu_irq = microblaze_pic_init_cpu(&g_cpu->env);
> +    dev = xilinx_intc_create(irq_addr, cpu_irq[0], 2);
> +    for (i = 0; i < nrIrqs; i++) {
> +        irq[i] = qdev_get_gpio_in(dev, i);
> +    }
> +}
> +
> +devinfo_t xilinx_interrupt_controller_device = {

static const?

> +    .probe = &xilinx_interrupt_controller_probe,
> +    .pass = 0,
> +    .compat = (const char * []) { "xlnx,xps-intc-1.00.a", NULL }
> +};
> +
> +/*
> + * Flash device
> + */
> +static void flash_probe(void *fdt, int node)
> +{
> +    int reglen;
> +    const void *reg = qemu_devtree_getprop_offset(fdt, node, "reg", &reglen);
> +    uint32_t flash_addr = qemu_devtree_int_array_index(reg, 0);
> +    uint32_t flash_size = qemu_devtree_int_array_index(reg, 1);
> +
> +    DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0);
> +    pflash_cfi01_register(flash_addr, NULL,
> +                          qemu_devtree_get_name(fdt, node, NULL), flash_size,
> +                          dinfo ? dinfo->bdrv : NULL, (64 * 1024),
> +                          flash_size >> 16,
> +                          1, 0x89, 0x18, 0x0000, 0x0, 1);
> +    printf("-- loaded %d bytes to %08X\n",

Leftover debugging?

> +           load_image_targphys(qemu_devtree_get_name(fdt, node, NULL),
> +                               flash_addr, flash_size),
> +           flash_addr);
> +}
> +
> +devinfo_t flash_device = {

static const?

> +    .probe = &flash_probe,
> +    .pass = 1,
> +    .compat = (const char * []) { "cfi-flash", NULL }
> +};
> +
> +/*
> + * Xilinx timer device
> + */
> +static void xilinx_timer_probe(void *fdt, int node)
> +{
> +    uint32_t timer_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> +    uint32_t timer_irq = fdt_get_int_from_array(fdt, node, "interrupts", 0);
> +
> +    printf("  TIMER BASE %08X IRQ %d\n", timer_addr, timer_irq);

Please fix all of these.

> +
> +    /* 2 timers at irq 2 @ 62 Mhz.  */
> +    xilinx_timer_create(timer_addr, irq[timer_irq], 0, 62 * 1000000);
> +}
> +
> +devinfo_t xilinx_timer_device = {

Likewise.

> +    .probe = &xilinx_timer_probe,
> +    .pass = 1,
> +    .compat = (const char * []) { "xlnx,xps-timer-1.00.a", NULL }
> +};
> +
> +/*
> + * Xilinx uartlite device
> + */
> +static void xilinx_uartlite_probe(void *fdt, int node)
> +{
> +    uint32_t uart_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> +    uint32_t uart_irq = fdt_get_int_from_array(fdt, node, "interrupts", 0);
> +
> +    printf("  UART BASE %08X IRQ %d\n", uart_addr, uart_irq);
> +
> +    sysbus_create_simple("xlnx.xps-uartlite", uart_addr, irq[uart_irq]);
> +}
> +
> +devinfo_t xilinx_uartlite_device = {
> +    .probe = &xilinx_uartlite_probe,
> +    .pass = 1,
> +    .compat = (const char * []) { "xlnx,xps-uartlite-1.00.a", NULL }
> +};
> +
> +/*
> + * Xilinx ethernetlite device
> + */
> +static void xilinx_ethlite_probe(void *fdt, int node)
> +{
> +    uint32_t eth_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> +    uint32_t eth_irq = fdt_get_int_from_array(fdt, node, "interrupts", 0);
> +
> +    xilinx_ethlite_create(&nd_table[++eth_dev_index], eth_addr,
> +                          irq[eth_irq], 0, 0);

The device should not access nd_table, this should be handled at board level.

> +}
> +
> +devinfo_t xilinx_ethlite_device = {
> +    .probe = &xilinx_ethlite_probe,
> +    .pass = 1,
> +    .compat = (const char * []) { "xlnx,xps-ethernetlite-2.00.b", NULL }
> +};
> +
> +/*
> + * LabX audio packetizer device
> + */
> +static void labx_audio_packetizer_probe(void *fdt, int node)
> +{
> +    uint32_t packetizer_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> +    uint32_t packetizer_irq =
> +        fdt_get_int_from_array(fdt, node, "interrupts", 0);
> +    uint32_t clock_domains =
> +        fdt_get_int_from_array(fdt, node, "xlnx,num-clock-domains", 0);
> +    uint32_t cache_words =
> +        fdt_get_int_from_array(fdt, node, "xlnx,cache-data-words", 0);
> +
> +    labx_audio_packetizer_create(packetizer_addr, irq[packetizer_irq],
> +                                 clock_domains, cache_words);
> +}
> +
> +devinfo_t labx_audio_packetizer_device = {
> +    .probe = &labx_audio_packetizer_probe,
> +    .pass = 1,
> +    .compat = (const char * []) { "xlnx,labx-audio-packetizer-1.00.a", NULL }
> +};
> +
> +/*
> + * LabX audio depacketizer device
> + */
> +static void labx_audio_depacketizer_probe(void *fdt, int node)
> +{
> +    uint32_t depacketizer_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> +    uint32_t depacketizer_irq =
> +        fdt_get_int_from_array(fdt, node, "interrupts", 0);
> +    uint32_t clock_domains =
> +        fdt_get_int_from_array(fdt, node, "xlnx,num-clock-domains", 0);
> +    uint32_t cache_words =
> +        fdt_get_int_from_array(fdt, node, "xlnx,cache-data-words", 0);
> +
> +    int ifLen;
> +    const void *ifType =
> +        qemu_devtree_getprop_offset(fdt, node, "xlnx,interface-type", &ifLen);
> +    int hasDMA = (0 != strncmp("CACHE_RAM", (const char *)ifType, ifLen));
> +
> +    labx_audio_depacketizer_create(depacketizer_addr, irq[depacketizer_irq],
> +                                   clock_domains, cache_words, hasDMA);
> +}
> +
> +devinfo_t labx_audio_depacketizer_device = {
> +    .probe = &labx_audio_depacketizer_probe,
> +    .pass = 1,
> +    .compat = (const char * []) {
> +        "xlnx,labx-audio-depacketizer-1.00.a",
> +        "xlnx,labx-audio-depacketizer-1.01.a",
> +        NULL
> +    }
> +};
> +
> +/*
> + * LabX dma device
> + */
> +static void labx_dma_probe(void *fdt, int node)
> +{
> +    uint32_t dma_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> +
> +    labx_dma_create(dma_addr, 1024);
> +}
> +
> +devinfo_t labx_dma_device = {
> +    .probe = &labx_dma_probe,
> +    .pass = 1,
> +    .compat = (const char * []) {
> +        "xlnx,labx-dma-1.00.a",
> +        "xlnx,labx-dma-1.01.a",
> +        "xlnx,labx-local-audio-1.00.a",
> +        NULL
> +    }
> +};
> +
> +/*
> + * LabX ethernet device
> + */
> +static void labx_ethernet_probe(void *fdt, int node)
> +{
> +    uint32_t ethernet_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> +    uint32_t host_irq =
> +        fdt_get_int_from_array(fdt, node, "interrupts", 0);
> +    uint32_t fifo_irq =
> +        fdt_get_int_from_array(fdt, node, "interrupts", 2);
> +    uint32_t phy_irq =
> +        fdt_get_int_from_array(fdt, node, "interrupts", 4);
> +
> +    labx_ethernet_create(&nd_table[++eth_dev_index], ethernet_addr,
> +                         irq[host_irq], irq[fifo_irq], irq[phy_irq]);
> +}
> +
> +devinfo_t labx_ethernet_device = {
> +    .probe = &labx_ethernet_probe,
> +    .pass = 1,
> +    .compat = (const char * []) { "xlnx,labx-ethernet-1.00.a", NULL }
> +};
> +
> +/*
> + * LabX ptp device
> + */
> +static void labx_ptp_probe(void *fdt, int node)
> +{
> +    uint32_t ptp_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> +
> +    labx_ptp_create(ptp_addr);
> +}
> +
> +devinfo_t labx_ptp_device = {
> +    .probe = &labx_ptp_probe,
> +    .pass = 1,
> +    .compat = (const char * []) { "xlnx,labx-ptp-1.00.a", NULL }
> +};
> +
> +/*
> + * Table of available devices
> + */
> +devinfo_t *devices[] = {
> +    &xilinx_interrupt_controller_device,
> +    &flash_device,
> +    &xilinx_timer_device,
> +    &xilinx_uartlite_device,
> +    &xilinx_ethlite_device,
> +    &labx_audio_packetizer_device,
> +    &labx_audio_depacketizer_device,
> +    &labx_dma_device,
> +    &labx_ethernet_device,
> +    &labx_ptp_device,
> +    NULL
> +};
> +
> +static int plb_device_probe(void *fdt, int node, int pass)
> +{
> +    devinfo_t **dev = &(devices[0]);

Unnecessary parentheses.

> +
> +    while (*dev) {
> +        const char **compat = &((*dev)->compat[0]);
> +        while (*compat) {
> +            if (0 == qemu_devtree_node_check_compatible(fdt, node, *compat)) {

Usually the constant is on right hand side.

> +                if (pass == (*dev)->pass) {
> +                    printf("Adding a device for node %s\n",
> +                           qemu_devtree_get_name(fdt, node, NULL));
> +
> +                    (*dev)->probe(fdt, node);
> +                    return 0;
> +                }
> +
> +                if (pass < (*dev)->pass) {
> +                    /* Probe again on the next pass */
> +                    return 1;
> +                }
> +            }
> +
> +            compat++;
> +        }
> +
> +        dev++;
> +    }
> +
> +    return 0;
> +}
> +
> +static void plb_bus_probe(void *fdt)
> +{
> +    int root = qemu_devtree_node_offset(fdt, "/");
> +    int plb = qemu_devtree_subnode_offset_namelen(fdt, root, "plb", 3);
> +    if (plb > 0) {
> +        /* Do multiple passes through the devices. Some have dependencies
> +           on others being first */
> +        int pass = 0;
> +        int again = 0;
> +        do {
> +            int child = plb;
> +            again = 0;
> +            do {
> +                child = qemu_devtree_next_child_offset(fdt, plb, child);
> +                if (child < 0) {
> +                    break;
> +                }
> +
> +                again |= plb_device_probe(fdt, child, pass);
> +
> +            } while (1);
> +
> +            pass++;
> +
> +        } while (again);
> +    }
> +}
> +
> +static void
> +labx_microblaze_init(ram_addr_t ram_size,
> +                     const char *boot_device,
> +                     const char *kernel_filename,
> +                     const char *kernel_cmdline,
> +                     const char *initrd_filename,
> +                     const char *cpu_model)
> +{
> +    MemoryRegion *address_space_mem = get_system_memory();
> +
> +    int kernel_size;
> +    int fdt_size;
> +    void *fdt = get_device_tree(&fdt_size);
> +    target_phys_addr_t ddr_base = get_dram_base(fdt);
> +    MemoryRegion *phys_lmb_bram = g_new(MemoryRegion, 1);
> +    MemoryRegion *phys_ram = g_new(MemoryRegion, 1);
> +    CPUMBState *env;
> +
> +    /* init CPUs */
> +    if (cpu_model == NULL) {
> +        cpu_model = "microblaze";
> +    }
> +    g_cpu = cpu_mb_init(cpu_model);
> +    env = &g_cpu->env;
> +
> +    env->pvr.regs[10] = 0x0c000000; /* spartan 3a dsp family.  */
> +    qemu_register_reset(main_cpu_reset, g_cpu);
> +
> +    /* Attach emulated BRAM through the LMB. LMB size is not specified in the
> +       device-tree but there must be one to hold the vector table. */
> +    memory_region_init_ram(phys_lmb_bram, "labx_microblaze.lmb_bram",
> +                           LMB_BRAM_SIZE);
> +    vmstate_register_ram_global(phys_lmb_bram);
> +    memory_region_add_subregion(address_space_mem, 0x00000000, phys_lmb_bram);
> +
> +    memory_region_init_ram(phys_ram, "labx_microblaze.ram", ram_size);
> +    vmstate_register_ram_global(phys_ram);
> +    memory_region_add_subregion(address_space_mem, ddr_base, phys_ram);
> +
> +
> +    /* Create other devices listed in the device-tree */
> +    plb_bus_probe(fdt);
> +
> +    if (kernel_filename) {
> +        uint64_t entry, low, high;
> +        uint32_t base32;
> +
> +        /* Boots a kernel elf binary.  */
> +        kernel_size = load_elf(kernel_filename, NULL, NULL,
> +                               &entry, &low, &high,
> +                               1, ELF_MACHINE, 0);
> +        base32 = entry;
> +        if (base32 == 0xc0000000) {
> +            kernel_size = load_elf(kernel_filename, translate_kernel_address,
> +                                   NULL, &entry, NULL, NULL,
> +                                   1, ELF_MACHINE, 0);
> +        }
> +        /* Always boot into physical ram.  */
> +        boot_info.bootstrap_pc = ddr_base + (entry & 0x07ffffff);
> +
> +        /* If it wasn't an ELF image, try an u-boot image.  */
> +        if (kernel_size < 0) {
> +            target_phys_addr_t uentry, loadaddr;
> +
> +            kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0);
> +            boot_info.bootstrap_pc = uentry;
> +            high = (loadaddr + kernel_size + 3) & ~3;

The constants should use ULL so that they are not truncated to 32 bits.

> +        }
> +
> +        /* Not an ELF image nor an u-boot image, try a RAW image.  */
> +        if (kernel_size < 0) {
> +            kernel_size = load_image_targphys(kernel_filename, ddr_base,
> +                                              ram_size);
> +            boot_info.bootstrap_pc = ddr_base;
> +            high = (ddr_base + kernel_size + 3) & ~3;

Also here.

> +        }
> +
> +        if (initrd_filename) {
> +            uint32_t initrd_base = 0x88c00000;
> +            uint32_t initrd_size =
> +                    load_image_targphys(initrd_filename, initrd_base,
> +                                        ram_size - initrd_base);
> +            if (initrd_size <= 0) {
> +                fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
> +                initrd_filename);
> +                exit(1);
> +            }
> +
> +            boot_info.initrd = initrd_base;
> +        } else {
> +            boot_info.initrd = 0x00000000;
> +        }
> +
> +        boot_info.cmdline = high + 4096;
> +        if (kernel_cmdline && strlen(kernel_cmdline)) {
> +            pstrcpy_targphys("cmdline", boot_info.cmdline, 256, kernel_cmdline);
> +        }
> +        /* Provide a device-tree.  */
> +        boot_info.fdt = boot_info.cmdline + 4096;
> +        labx_microblaze_load_device_tree(boot_info.fdt, ram_size,
> +                                   0, 0,
> +                                   kernel_cmdline);
> +    }
> +}
> +
> +static QEMUMachine labx_microblaze_machine = {
> +    .name = "labx-microblaze-devicetree",
> +    .desc = "Microblaze design based on the peripherals specified "
> +            "in the device-tree.",
> +    .init = labx_microblaze_init,
> +    .is_default = 1
> +};
> +
> +static void labx_microblaze_machine_init(void)
> +{
> +    qemu_register_machine(&labx_microblaze_machine);
> +}
> +
> +machine_init(labx_microblaze_machine_init);
> +

Useless newline before EOF.

> diff --git a/hw/microblaze/Makefile.objs b/hw/microblaze/Makefile.objs
> index 274d2c5..632cd60 100644
> --- a/hw/microblaze/Makefile.objs
> +++ b/hw/microblaze/Makefile.objs
> @@ -1,4 +1,5 @@
> -obj-y = petalogix_s3adsp1800_mmu.o
> +obj-y  = labx_microblaze_devicetree.o
> +obj-y += petalogix_s3adsp1800_mmu.o
>  obj-y += petalogix_ml605_mmu.o
>  obj-y += microblaze_boot.o
>
> --
> 1.7.9.5
>
>
Peter A. G. Crosthwaite Sept. 11, 2012, 11:59 p.m. UTC | #2
Hi Chris,

This needs to be reviewed along with the petalogix implementation, but a
few top level comments.

Regards,
Peter

On Sun, 2012-09-09 at 20:20 -0400, crwulff@gmail.com wrote:
> From: Chris Wulff <crwulff@gmail.com>
> 
> Signed-off-by: Chris Wulff <crwulff@gmail.com>
> ---
>  default-configs/microblaze-softmmu.mak |    1 +
>  hw/labx_microblaze_devicetree.c        |  597 ++++++++++++++++++++++++++++++++
>  hw/microblaze/Makefile.objs            |    3 +-
>  3 files changed, 600 insertions(+), 1 deletion(-)
>  create mode 100644 hw/labx_microblaze_devicetree.c
> 
> diff --git a/default-configs/microblaze-softmmu.mak b/default-configs/microblaze-softmmu.mak
> index 64c9485..7e4cab8 100644
> --- a/default-configs/microblaze-softmmu.mak
> +++ b/default-configs/microblaze-softmmu.mak
> @@ -5,3 +5,4 @@ CONFIG_PFLASH_CFI01=y
>  CONFIG_SERIAL=y
>  CONFIG_XILINX=y
>  CONFIG_XILINX_AXI=y
> +CONFIG_LABX=y
> diff --git a/hw/labx_microblaze_devicetree.c b/hw/labx_microblaze_devicetree.c
> new file mode 100644
> index 0000000..581ad14
> --- /dev/null
> +++ b/hw/labx_microblaze_devicetree.c
> @@ -0,0 +1,597 @@
> +/*
> + * Flexible model of microblaze designs that use a device-tree to determine
> + * the hardware configuration.
> + *
> + * Copyright (c) 2010 LabX Technologies, LLC
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library 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
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include "sysbus.h"
> +#include "hw.h"
> +#include "net.h"
> +#include "flash.h"
> +#include "sysemu.h"
> +#include "devices.h"
> +#include "boards.h"
> +#include "device_tree.h"
> +#include "xilinx.h"
> +#include "labx_devices.h"
> +#include "loader.h"
> +#include "elf.h"
> +#include "blockdev.h"
> +#include "exec-memory.h"
> +#include "microblaze_pic_cpu.h"
> +
> +#define LMB_BRAM_SIZE  (128 * 1024)
> +
> +static struct
> +{
> +    uint32_t bootstrap_pc;
> +    uint32_t cmdline;
> +    uint32_t initrd;
> +    uint32_t fdt;
> +} boot_info;
> +
> +/* Current ethernet device index for multiple network interfaces */
> +static int eth_dev_index = -1;
> +
> +static void main_cpu_reset(void *opaque)
> +{
> +    MicroBlazeCPU *cpu = opaque;
> +    CPUMBState *env = &cpu->env;
> +
> +    cpu_reset(CPU(cpu));
> +
> +    env->regs[5] = boot_info.cmdline;
> +    env->regs[6] = boot_info.initrd;
> +    env->regs[7] = boot_info.fdt;
> +    env->sregs[SR_PC] = boot_info.bootstrap_pc;
> +}
> +

Please use microblaze bootloader (hw/microblaze_boot.c). We need to
consolidate bootloaders. If there is functionality missing from mb_boot,
please add it there.

> +#ifndef CONFIG_FDT
> +#error "Device-tree support is required for this target to function"
> +#endif
> +

This should be handled by the make infrastructure. This machine model
should be obj-(CONIFG_FDT)+= .. so this machine model just dissappears
when QEMU is build without FDT.

> +static uint32_t fdt_get_int_from_array(void *fdt, int node,
> +                                       const char *name, int index)
> +{
> +    int array_length;
> +    const void *array = qemu_devtree_getprop_offset(fdt, node, name,
> +                                                    &array_length);
> +    if (index >= array_length) {
> +        printf("fdt_get_int_from_array: requesting %s from node %d, "
> +               "index %d out of range (%d max)\n",
> +               name, node, index, array_length);
> +        return 0;
> +    } else {
> +        return qemu_devtree_int_array_index(array, index);
> +    }
> +}
> +
> +#define BINARY_DEVICE_TREE_FILE "labx-microblaze.dtb"
> +static void *get_device_tree(int *fdt_size)
> +{
> +    char *path;
> +    void *fdt;
> +
> +    /* Try the local "mb.dtb" override.  */
> +    fdt = load_device_tree("mb.dtb", fdt_size);
> +    if (!fdt) {
> +        path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
> +        if (path) {
> +            fdt = load_device_tree(path, fdt_size);
> +            g_free(path);
> +        }
> +    }
> +
> +    return fdt;
> +}
> +
> +static int labx_microblaze_load_device_tree(target_phys_addr_t addr,
> +                                      uint32_t ramsize,
> +                                      target_phys_addr_t initrd_base,
> +                                      target_phys_addr_t initrd_size,
> +                                      const char *kernel_cmdline)
> +{
> +    int fdt_size;
> +    void *fdt;
> +    int r;
> +
> +    fdt = get_device_tree(&fdt_size);
> +
> +    if (!fdt) {
> +        return 0;
> +    }
> +
> +    if (kernel_cmdline && strlen(kernel_cmdline)) {
> +        r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs",
> +                                        kernel_cmdline);
> +        if (r < 0) {
> +            fprintf(stderr, "couldn't set /chosen/bootargs\n");
> +        }
> +    }
> +    cpu_physical_memory_write(addr, (void *)fdt, fdt_size);
> +
> +    return fdt_size;
> +}
> +
> +static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
> +{
> +    return addr - 0x30000000LL;
> +}
> +
> +static ram_addr_t get_dram_base(void *fdt)
> +{
> +    int root = qemu_devtree_node_offset(fdt, "/");
> +    int memory = qemu_devtree_subnode_offset_namelen(fdt, root, "memory", 6);
> +    if (memory > 0) {
> +        int reglen;
> +        const void *reg =
> +            qemu_devtree_getprop_offset(fdt, memory, "reg", &reglen);
> +
> +        if (reglen >= 4) {
> +            printf("DRAM base %08X, size %08X\n",
> +                   qemu_devtree_int_array_index(reg, 0),
> +                   qemu_devtree_int_array_index(reg, 1));
> +            return qemu_devtree_int_array_index(reg, 0);
> +        }
> +    }
> +
> +    printf("DRAM base not found. Defaulting to 0x88000000\n");
> +
> +    return 0x88000000; /* Default to something reasonable */
> +}
> +
> +typedef void (*device_init_func_t)(void *fdt, int node);
> +
> +typedef struct {
> +    device_init_func_t probe;
> +    int pass;
> +    const char **compat;
> +
> +} devinfo_t;
> +
> +/*
> + * Xilinx interrupt controller device
> + */
> +MicroBlazeCPU *g_cpu;
> +static qemu_irq irq[32] = {};
> +static qemu_irq *cpu_irq;
> +
> +static void xilinx_interrupt_controller_probe(void *fdt, int node)
> +{
> +    int i;
> +    DeviceState *dev;
> +    int reglen;
> +    const void *reg = qemu_devtree_getprop_offset(fdt, node, "reg", &reglen);
> +    uint32_t irq_addr = qemu_devtree_int_array_index(reg, 0);
> +    int nrIrqLen;
> +    const void *nrIrq =
> +        qemu_devtree_getprop_offset(fdt, node, "xlnx,num-intr-inputs",
> +                                    &nrIrqLen);
> +    uint32_t nrIrqs = qemu_devtree_int_array_index(nrIrq, 0);
> +
> +    printf("  IRQ BASE %08X NIRQS %d\n", irq_addr, nrIrqs);
> +
> +    cpu_irq = microblaze_pic_init_cpu(&g_cpu->env);
> +    dev = xilinx_intc_create(irq_addr, cpu_irq[0], 2);
> +    for (i = 0; i < nrIrqs; i++) {
> +        irq[i] = qdev_get_gpio_in(dev, i);
> +    }
> +}
> +
> +devinfo_t xilinx_interrupt_controller_device = {
> +    .probe = &xilinx_interrupt_controller_probe,
> +    .pass = 0,
> +    .compat = (const char * []) { "xlnx,xps-intc-1.00.a", NULL }
> +};
> +
> +/*
> + * Flash device
> + */
> +static void flash_probe(void *fdt, int node)
> +{
> +    int reglen;
> +    const void *reg = qemu_devtree_getprop_offset(fdt, node, "reg", &reglen);
> +    uint32_t flash_addr = qemu_devtree_int_array_index(reg, 0);
> +    uint32_t flash_size = qemu_devtree_int_array_index(reg, 1);
> +
> +    DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0);
> +    pflash_cfi01_register(flash_addr, NULL,
> +                          qemu_devtree_get_name(fdt, node, NULL), flash_size,
> +                          dinfo ? dinfo->bdrv : NULL, (64 * 1024),
> +                          flash_size >> 16,
> +                          1, 0x89, 0x18, 0x0000, 0x0, 1);
> +    printf("-- loaded %d bytes to %08X\n",
> +           load_image_targphys(qemu_devtree_get_name(fdt, node, NULL),
> +                               flash_addr, flash_size),
> +           flash_addr);
> +}
> +
> +devinfo_t flash_device = {
> +    .probe = &flash_probe,
> +    .pass = 1,
> +    .compat = (const char * []) { "cfi-flash", NULL }
> +};
> +
> +/*
> + * Xilinx timer device
> + */
> +static void xilinx_timer_probe(void *fdt, int node)
> +{
> +    uint32_t timer_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> +    uint32_t timer_irq = fdt_get_int_from_array(fdt, node, "interrupts", 0);
> +
> +    printf("  TIMER BASE %08X IRQ %d\n", timer_addr, timer_irq);
> +
> +    /* 2 timers at irq 2 @ 62 Mhz.  */
> +    xilinx_timer_create(timer_addr, irq[timer_irq], 0, 62 * 1000000);

No check is made for the one_timer_only property.

> +}
> +
> +devinfo_t xilinx_timer_device = {
> +    .probe = &xilinx_timer_probe,
> +    .pass = 1,
> +    .compat = (const char * []) { "xlnx,xps-timer-1.00.a", NULL }
> +};
> +
> +/*
> + * Xilinx uartlite device
> + */
> +static void xilinx_uartlite_probe(void *fdt, int node)
> +{
> +    uint32_t uart_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> +    uint32_t uart_irq = fdt_get_int_from_array(fdt, node, "interrupts", 0);
> +
> +    printf("  UART BASE %08X IRQ %d\n", uart_addr, uart_irq);
> +
> +    sysbus_create_simple("xlnx.xps-uartlite", uart_addr, irq[uart_irq]);
> +}
> +
> +devinfo_t xilinx_uartlite_device = {
> +    .probe = &xilinx_uartlite_probe,
> +    .pass = 1,
> +    .compat = (const char * []) { "xlnx,xps-uartlite-1.00.a", NULL }
> +};
> +
> +/*
> + * Xilinx ethernetlite device
> + */
> +static void xilinx_ethlite_probe(void *fdt, int node)
> +{
> +    uint32_t eth_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> +    uint32_t eth_irq = fdt_get_int_from_array(fdt, node, "interrupts", 0);
> +
> +    xilinx_ethlite_create(&nd_table[++eth_dev_index], eth_addr,
> +                          irq[eth_irq], 0, 0);
> +}
> +
> +devinfo_t xilinx_ethlite_device = {
> +    .probe = &xilinx_ethlite_probe,
> +    .pass = 1,
> +    .compat = (const char * []) { "xlnx,xps-ethernetlite-2.00.b", NULL }
> +};
> +
> +/*
> + * LabX audio packetizer device
> + */
> +static void labx_audio_packetizer_probe(void *fdt, int node)
> +{
> +    uint32_t packetizer_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> +    uint32_t packetizer_irq =
> +        fdt_get_int_from_array(fdt, node, "interrupts", 0);
> +    uint32_t clock_domains =
> +        fdt_get_int_from_array(fdt, node, "xlnx,num-clock-domains", 0);
> +    uint32_t cache_words =
> +        fdt_get_int_from_array(fdt, node, "xlnx,cache-data-words", 0);
> +
> +    labx_audio_packetizer_create(packetizer_addr, irq[packetizer_irq],
> +                                 clock_domains, cache_words);
> +}
> +
> +devinfo_t labx_audio_packetizer_device = {
> +    .probe = &labx_audio_packetizer_probe,
> +    .pass = 1,
> +    .compat = (const char * []) { "xlnx,labx-audio-packetizer-1.00.a", NULL }
> +};
> +
> +/*
> + * LabX audio depacketizer device
> + */
> +static void labx_audio_depacketizer_probe(void *fdt, int node)
> +{
> +    uint32_t depacketizer_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> +    uint32_t depacketizer_irq =
> +        fdt_get_int_from_array(fdt, node, "interrupts", 0);
> +    uint32_t clock_domains =
> +        fdt_get_int_from_array(fdt, node, "xlnx,num-clock-domains", 0);
> +    uint32_t cache_words =
> +        fdt_get_int_from_array(fdt, node, "xlnx,cache-data-words", 0);
> +
> +    int ifLen;
> +    const void *ifType =
> +        qemu_devtree_getprop_offset(fdt, node, "xlnx,interface-type", &ifLen);
> +    int hasDMA = (0 != strncmp("CACHE_RAM", (const char *)ifType, ifLen));
> +
> +    labx_audio_depacketizer_create(depacketizer_addr, irq[depacketizer_irq],
> +                                   clock_domains, cache_words, hasDMA);
> +}
> +
> +devinfo_t labx_audio_depacketizer_device = {
> +    .probe = &labx_audio_depacketizer_probe,
> +    .pass = 1,
> +    .compat = (const char * []) {
> +        "xlnx,labx-audio-depacketizer-1.00.a",
> +        "xlnx,labx-audio-depacketizer-1.01.a",
> +        NULL
> +    }
> +};
> +
> +/*
> + * LabX dma device
> + */
> +static void labx_dma_probe(void *fdt, int node)
> +{
> +    uint32_t dma_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> +
> +    labx_dma_create(dma_addr, 1024);
> +}
> +
> +devinfo_t labx_dma_device = {
> +    .probe = &labx_dma_probe,
> +    .pass = 1,
> +    .compat = (const char * []) {
> +        "xlnx,labx-dma-1.00.a",
> +        "xlnx,labx-dma-1.01.a",
> +        "xlnx,labx-local-audio-1.00.a",
> +        NULL
> +    }
> +};
> +
> +/*
> + * LabX ethernet device
> + */
> +static void labx_ethernet_probe(void *fdt, int node)
> +{
> +    uint32_t ethernet_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> +    uint32_t host_irq =
> +        fdt_get_int_from_array(fdt, node, "interrupts", 0);
> +    uint32_t fifo_irq =
> +        fdt_get_int_from_array(fdt, node, "interrupts", 2);
> +    uint32_t phy_irq =
> +        fdt_get_int_from_array(fdt, node, "interrupts", 4);
> +
> +    labx_ethernet_create(&nd_table[++eth_dev_index], ethernet_addr,
> +                         irq[host_irq], irq[fifo_irq], irq[phy_irq]);
> +}
> +
> +devinfo_t labx_ethernet_device = {
> +    .probe = &labx_ethernet_probe,
> +    .pass = 1,
> +    .compat = (const char * []) { "xlnx,labx-ethernet-1.00.a", NULL }
> +};
> +
> +/*
> + * LabX ptp device
> + */
> +static void labx_ptp_probe(void *fdt, int node)
> +{
> +    uint32_t ptp_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> +
> +    labx_ptp_create(ptp_addr);
> +}
> +
> +devinfo_t labx_ptp_device = {
> +    .probe = &labx_ptp_probe,
> +    .pass = 1,
> +    .compat = (const char * []) { "xlnx,labx-ptp-1.00.a", NULL }
> +};
> +
> +/*
> + * Table of available devices
> + */
> +devinfo_t *devices[] = {
> +    &xilinx_interrupt_controller_device,
> +    &flash_device,
> +    &xilinx_timer_device,
> +    &xilinx_uartlite_device,
> +    &xilinx_ethlite_device,
> +    &labx_audio_packetizer_device,
> +    &labx_audio_depacketizer_device,
> +    &labx_dma_device,
> +    &labx_ethernet_device,
> +    &labx_ptp_device,
> +    NULL
> +};
> +
> +static int plb_device_probe(void *fdt, int node, int pass)

QEMU abstacts away interconnect implementations. You should probably
also drive off the "simple-bus" compat rather than PLB for machine model
compatibility. s/plb/bus/g or something like it.

> +{
> +    devinfo_t **dev = &(devices[0]);
> +
> +    while (*dev) {
> +        const char **compat = &((*dev)->compat[0]);
> +        while (*compat) {
> +            if (0 == qemu_devtree_node_check_compatible(fdt, node, *compat)) {
> +                if (pass == (*dev)->pass) {
> +                    printf("Adding a device for node %s\n",
> +                           qemu_devtree_get_name(fdt, node, NULL));
> +
> +                    (*dev)->probe(fdt, node);
> +                    return 0;
> +                }
> +
> +                if (pass < (*dev)->pass) {
> +                    /* Probe again on the next pass */
> +                    return 1;
> +                }
> +            }
> +
> +            compat++;
> +        }
> +
> +        dev++;
> +    }
> +
> +    return 0;
> +}
> +
> +static void plb_bus_probe(void *fdt)
> +{
> +    int root = qemu_devtree_node_offset(fdt, "/");
> +    int plb = qemu_devtree_subnode_offset_namelen(fdt, root, "plb", 3);
> +    if (plb > 0) {
> +        /* Do multiple passes through the devices. Some have dependencies
> +           on others being first */
> +        int pass = 0;
> +        int again = 0;
> +        do {
> +            int child = plb;
> +            again = 0;
> +            do {
> +                child = qemu_devtree_next_child_offset(fdt, plb, child);
> +                if (child < 0) {
> +                    break;
> +                }
> +
> +                again |= plb_device_probe(fdt, child, pass);
> +
> +            } while (1);
> +
> +            pass++;
> +
> +        } while (again);
> +    }
> +}
> +
> +static void
> +labx_microblaze_init(ram_addr_t ram_size,
> +                     const char *boot_device,
> +                     const char *kernel_filename,
> +                     const char *kernel_cmdline,
> +                     const char *initrd_filename,
> +                     const char *cpu_model)
> +{
> +    MemoryRegion *address_space_mem = get_system_memory();
> +
> +    int kernel_size;
> +    int fdt_size;
> +    void *fdt = get_device_tree(&fdt_size);
> +    target_phys_addr_t ddr_base = get_dram_base(fdt);
> +    MemoryRegion *phys_lmb_bram = g_new(MemoryRegion, 1);
> +    MemoryRegion *phys_ram = g_new(MemoryRegion, 1);
> +    CPUMBState *env;
> +
> +    /* init CPUs */
> +    if (cpu_model == NULL) {
> +        cpu_model = "microblaze";
> +    }
> +    g_cpu = cpu_mb_init(cpu_model);
> +    env = &g_cpu->env;
> +
> +    env->pvr.regs[10] = 0x0c000000; /* spartan 3a dsp family.  */

I have dtb driven solution for this.

> +    qemu_register_reset(main_cpu_reset, g_cpu);
> +
> +    /* Attach emulated BRAM through the LMB. LMB size is not specified in the
> +       device-tree but there must be one to hold the vector table. */
> +    memory_region_init_ram(phys_lmb_bram, "labx_microblaze.lmb_bram",
> +                           LMB_BRAM_SIZE);
> +    vmstate_register_ram_global(phys_lmb_bram);
> +    memory_region_add_subregion(address_space_mem, 0x00000000, phys_lmb_bram);
> +
> +    memory_region_init_ram(phys_ram, "labx_microblaze.ram", ram_size);
> +    vmstate_register_ram_global(phys_ram);
> +    memory_region_add_subregion(address_space_mem, ddr_base, phys_ram);
> +
> +
> +    /* Create other devices listed in the device-tree */
> +    plb_bus_probe(fdt);
> +
> +    if (kernel_filename) {
> +        uint64_t entry, low, high;
> +        uint32_t base32;
> +
> +        /* Boots a kernel elf binary.  */
> +        kernel_size = load_elf(kernel_filename, NULL, NULL,
> +                               &entry, &low, &high,
> +                               1, ELF_MACHINE, 0);
> +        base32 = entry;
> +        if (base32 == 0xc0000000) {
> +            kernel_size = load_elf(kernel_filename, translate_kernel_address,
> +                                   NULL, &entry, NULL, NULL,
> +                                   1, ELF_MACHINE, 0);
> +        }
> +        /* Always boot into physical ram.  */
> +        boot_info.bootstrap_pc = ddr_base + (entry & 0x07ffffff);
> +
> +        /* If it wasn't an ELF image, try an u-boot image.  */
> +        if (kernel_size < 0) {
> +            target_phys_addr_t uentry, loadaddr;
> +
> +            kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0);
> +            boot_info.bootstrap_pc = uentry;
> +            high = (loadaddr + kernel_size + 3) & ~3;
> +        }
> +
> +        /* Not an ELF image nor an u-boot image, try a RAW image.  */
> +        if (kernel_size < 0) {
> +            kernel_size = load_image_targphys(kernel_filename, ddr_base,
> +                                              ram_size);
> +            boot_info.bootstrap_pc = ddr_base;
> +            high = (ddr_base + kernel_size + 3) & ~3;
> +        }
> +
> +        if (initrd_filename) {
> +            uint32_t initrd_base = 0x88c00000;
> +            uint32_t initrd_size =
> +                    load_image_targphys(initrd_filename, initrd_base,
> +                                        ram_size - initrd_base);
> +            if (initrd_size <= 0) {
> +                fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
> +                initrd_filename);
> +                exit(1);
> +            }
> +
> +            boot_info.initrd = initrd_base;
> +        } else {
> +            boot_info.initrd = 0x00000000;
> +        }
> +
> +        boot_info.cmdline = high + 4096;
> +        if (kernel_cmdline && strlen(kernel_cmdline)) {
> +            pstrcpy_targphys("cmdline", boot_info.cmdline, 256, kernel_cmdline);
> +        }
> +        /* Provide a device-tree.  */
> +        boot_info.fdt = boot_info.cmdline + 4096;
> +        labx_microblaze_load_device_tree(boot_info.fdt, ram_size,
> +                                   0, 0,
> +                                   kernel_cmdline);
> +    }
> +}
> +
> +static QEMUMachine labx_microblaze_machine = {

Its a generic machine, so should it be badged as labx?

> +    .name = "labx-microblaze-devicetree",
> +    .desc = "Microblaze design based on the peripherals specified "
> +            "in the device-tree.",
> +    .init = labx_microblaze_init,
> +    .is_default = 1

> +};
> +
> +static void labx_microblaze_machine_init(void)
> +{
> +    qemu_register_machine(&labx_microblaze_machine);
> +}
> +
> +machine_init(labx_microblaze_machine_init);
> +
> diff --git a/hw/microblaze/Makefile.objs b/hw/microblaze/Makefile.objs
> index 274d2c5..632cd60 100644
> --- a/hw/microblaze/Makefile.objs
> +++ b/hw/microblaze/Makefile.objs
> @@ -1,4 +1,5 @@
> -obj-y = petalogix_s3adsp1800_mmu.o
> +obj-y  = labx_microblaze_devicetree.o

obj-y += labx_microblaze_devicetree.(CONFIG-FDT)

> +obj-y += petalogix_s3adsp1800_mmu.o
>  obj-y += petalogix_ml605_mmu.o
>  obj-y += microblaze_boot.o
>
Peter A. G. Crosthwaite Sept. 12, 2012, 12:17 a.m. UTC | #3
On Tue, 2012-09-11 at 19:27 +0000, Blue Swirl wrote:
> On Mon, Sep 10, 2012 at 12:20 AM,  <crwulff@gmail.com> wrote:
> > From: Chris Wulff <crwulff@gmail.com>
> >
> > +/*
> > + * Xilinx ethernetlite device
> > + */
> > +static void xilinx_ethlite_probe(void *fdt, int node)
> > +{
> > +    uint32_t eth_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> > +    uint32_t eth_irq = fdt_get_int_from_array(fdt, node, "interrupts", 0);
> > +
> > +    xilinx_ethlite_create(&nd_table[++eth_dev_index], eth_addr,
> > +                          irq[eth_irq], 0, 0);
> 
> The device should not access nd_table, this should be handled at board level.
> 

This is board level isn't it? This function calls
xilinx_ethlite_create() which in turn does the qdev_create so this is
pre device creation. Im open to suggestions on how to do this in a
non-global hacking way however as I have the same problem in my tree.

Regards,
Peter

> > +}
> > +
> > +devinfo_t xilinx_ethlite_device = {
> > +    .probe = &xilinx_ethlite_probe,
> > +    .pass = 1,
> > +    .compat = (const char * []) { "xlnx,xps-ethernetlite-2.00.b", NULL }
> > +};
> > +
crwulff@gmail.com Sept. 14, 2012, 4:01 a.m. UTC | #4
I'm all for consolidating the different device-tree based "boards" as much
as possible. This, the petalogix variant, and the NiosII one that I created
as part of this patch set too, are all very similar. We just need some
generic way to register the set of devices that can be created and then the
walking of the device tree itself could probably be generic. I'd be happy
to work with you to move in that direction.

The only thing that I ran into that is a bit different is that the NiosII
declares the type of attached interrupt controller inside the CPU block and
I ended up enumerating that as if the cpu were itself a bus.

  -- Chris Wulff

On Tue, Sep 11, 2012 at 7:59 PM, Peter Crosthwaite <
peter.crosthwaite@petalogix.com> wrote:

> Hi Chris,
>
> This needs to be reviewed along with the petalogix implementation, but a
> few top level comments.
>
> Regards,
> Peter
>
> On Sun, 2012-09-09 at 20:20 -0400, crwulff@gmail.com wrote:
> > From: Chris Wulff <crwulff@gmail.com>
> >
> > Signed-off-by: Chris Wulff <crwulff@gmail.com>
> > ---
> >  default-configs/microblaze-softmmu.mak |    1 +
> >  hw/labx_microblaze_devicetree.c        |  597
> ++++++++++++++++++++++++++++++++
> >  hw/microblaze/Makefile.objs            |    3 +-
> >  3 files changed, 600 insertions(+), 1 deletion(-)
> >  create mode 100644 hw/labx_microblaze_devicetree.c
> >
> > diff --git a/default-configs/microblaze-softmmu.mak
> b/default-configs/microblaze-softmmu.mak
> > index 64c9485..7e4cab8 100644
> > --- a/default-configs/microblaze-softmmu.mak
> > +++ b/default-configs/microblaze-softmmu.mak
> > @@ -5,3 +5,4 @@ CONFIG_PFLASH_CFI01=y
> >  CONFIG_SERIAL=y
> >  CONFIG_XILINX=y
> >  CONFIG_XILINX_AXI=y
> > +CONFIG_LABX=y
> > diff --git a/hw/labx_microblaze_devicetree.c
> b/hw/labx_microblaze_devicetree.c
> > new file mode 100644
> > index 0000000..581ad14
> > --- /dev/null
> > +++ b/hw/labx_microblaze_devicetree.c
> > @@ -0,0 +1,597 @@
> > +/*
> > + * Flexible model of microblaze designs that use a device-tree to
> determine
> > + * the hardware configuration.
> > + *
> > + * Copyright (c) 2010 LabX Technologies, LLC
> > + *
> > + * This library is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU Lesser General Public
> > + * License as published by the Free Software Foundation; either
> > + * version 2.1 of the License, or (at your option) any later version.
> > + *
> > + * This library 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
> > + * Lesser General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU Lesser General Public
> > + * License along with this library; if not, see
> > + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> > + */
> > +
> > +#include "sysbus.h"
> > +#include "hw.h"
> > +#include "net.h"
> > +#include "flash.h"
> > +#include "sysemu.h"
> > +#include "devices.h"
> > +#include "boards.h"
> > +#include "device_tree.h"
> > +#include "xilinx.h"
> > +#include "labx_devices.h"
> > +#include "loader.h"
> > +#include "elf.h"
> > +#include "blockdev.h"
> > +#include "exec-memory.h"
> > +#include "microblaze_pic_cpu.h"
> > +
> > +#define LMB_BRAM_SIZE  (128 * 1024)
> > +
> > +static struct
> > +{
> > +    uint32_t bootstrap_pc;
> > +    uint32_t cmdline;
> > +    uint32_t initrd;
> > +    uint32_t fdt;
> > +} boot_info;
> > +
> > +/* Current ethernet device index for multiple network interfaces */
> > +static int eth_dev_index = -1;
> > +
> > +static void main_cpu_reset(void *opaque)
> > +{
> > +    MicroBlazeCPU *cpu = opaque;
> > +    CPUMBState *env = &cpu->env;
> > +
> > +    cpu_reset(CPU(cpu));
> > +
> > +    env->regs[5] = boot_info.cmdline;
> > +    env->regs[6] = boot_info.initrd;
> > +    env->regs[7] = boot_info.fdt;
> > +    env->sregs[SR_PC] = boot_info.bootstrap_pc;
> > +}
> > +
>
> Please use microblaze bootloader (hw/microblaze_boot.c). We need to
> consolidate bootloaders. If there is functionality missing from mb_boot,
> please add it there.
>
> > +#ifndef CONFIG_FDT
> > +#error "Device-tree support is required for this target to function"
> > +#endif
> > +
>
> This should be handled by the make infrastructure. This machine model
> should be obj-(CONIFG_FDT)+= .. so this machine model just dissappears
> when QEMU is build without FDT.
>
> > +static uint32_t fdt_get_int_from_array(void *fdt, int node,
> > +                                       const char *name, int index)
> > +{
> > +    int array_length;
> > +    const void *array = qemu_devtree_getprop_offset(fdt, node, name,
> > +                                                    &array_length);
> > +    if (index >= array_length) {
> > +        printf("fdt_get_int_from_array: requesting %s from node %d, "
> > +               "index %d out of range (%d max)\n",
> > +               name, node, index, array_length);
> > +        return 0;
> > +    } else {
> > +        return qemu_devtree_int_array_index(array, index);
> > +    }
> > +}
> > +
> > +#define BINARY_DEVICE_TREE_FILE "labx-microblaze.dtb"
> > +static void *get_device_tree(int *fdt_size)
> > +{
> > +    char *path;
> > +    void *fdt;
> > +
> > +    /* Try the local "mb.dtb" override.  */
> > +    fdt = load_device_tree("mb.dtb", fdt_size);
> > +    if (!fdt) {
> > +        path = qemu_find_file(QEMU_FILE_TYPE_BIOS,
> BINARY_DEVICE_TREE_FILE);
> > +        if (path) {
> > +            fdt = load_device_tree(path, fdt_size);
> > +            g_free(path);
> > +        }
> > +    }
> > +
> > +    return fdt;
> > +}
> > +
> > +static int labx_microblaze_load_device_tree(target_phys_addr_t addr,
> > +                                      uint32_t ramsize,
> > +                                      target_phys_addr_t initrd_base,
> > +                                      target_phys_addr_t initrd_size,
> > +                                      const char *kernel_cmdline)
> > +{
> > +    int fdt_size;
> > +    void *fdt;
> > +    int r;
> > +
> > +    fdt = get_device_tree(&fdt_size);
> > +
> > +    if (!fdt) {
> > +        return 0;
> > +    }
> > +
> > +    if (kernel_cmdline && strlen(kernel_cmdline)) {
> > +        r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs",
> > +                                        kernel_cmdline);
> > +        if (r < 0) {
> > +            fprintf(stderr, "couldn't set /chosen/bootargs\n");
> > +        }
> > +    }
> > +    cpu_physical_memory_write(addr, (void *)fdt, fdt_size);
> > +
> > +    return fdt_size;
> > +}
> > +
> > +static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
> > +{
> > +    return addr - 0x30000000LL;
> > +}
> > +
> > +static ram_addr_t get_dram_base(void *fdt)
> > +{
> > +    int root = qemu_devtree_node_offset(fdt, "/");
> > +    int memory = qemu_devtree_subnode_offset_namelen(fdt, root,
> "memory", 6);
> > +    if (memory > 0) {
> > +        int reglen;
> > +        const void *reg =
> > +            qemu_devtree_getprop_offset(fdt, memory, "reg", &reglen);
> > +
> > +        if (reglen >= 4) {
> > +            printf("DRAM base %08X, size %08X\n",
> > +                   qemu_devtree_int_array_index(reg, 0),
> > +                   qemu_devtree_int_array_index(reg, 1));
> > +            return qemu_devtree_int_array_index(reg, 0);
> > +        }
> > +    }
> > +
> > +    printf("DRAM base not found. Defaulting to 0x88000000\n");
> > +
> > +    return 0x88000000; /* Default to something reasonable */
> > +}
> > +
> > +typedef void (*device_init_func_t)(void *fdt, int node);
> > +
> > +typedef struct {
> > +    device_init_func_t probe;
> > +    int pass;
> > +    const char **compat;
> > +
> > +} devinfo_t;
> > +
> > +/*
> > + * Xilinx interrupt controller device
> > + */
> > +MicroBlazeCPU *g_cpu;
> > +static qemu_irq irq[32] = {};
> > +static qemu_irq *cpu_irq;
> > +
> > +static void xilinx_interrupt_controller_probe(void *fdt, int node)
> > +{
> > +    int i;
> > +    DeviceState *dev;
> > +    int reglen;
> > +    const void *reg = qemu_devtree_getprop_offset(fdt, node, "reg",
> &reglen);
> > +    uint32_t irq_addr = qemu_devtree_int_array_index(reg, 0);
> > +    int nrIrqLen;
> > +    const void *nrIrq =
> > +        qemu_devtree_getprop_offset(fdt, node, "xlnx,num-intr-inputs",
> > +                                    &nrIrqLen);
> > +    uint32_t nrIrqs = qemu_devtree_int_array_index(nrIrq, 0);
> > +
> > +    printf("  IRQ BASE %08X NIRQS %d\n", irq_addr, nrIrqs);
> > +
> > +    cpu_irq = microblaze_pic_init_cpu(&g_cpu->env);
> > +    dev = xilinx_intc_create(irq_addr, cpu_irq[0], 2);
> > +    for (i = 0; i < nrIrqs; i++) {
> > +        irq[i] = qdev_get_gpio_in(dev, i);
> > +    }
> > +}
> > +
> > +devinfo_t xilinx_interrupt_controller_device = {
> > +    .probe = &xilinx_interrupt_controller_probe,
> > +    .pass = 0,
> > +    .compat = (const char * []) { "xlnx,xps-intc-1.00.a", NULL }
> > +};
> > +
> > +/*
> > + * Flash device
> > + */
> > +static void flash_probe(void *fdt, int node)
> > +{
> > +    int reglen;
> > +    const void *reg = qemu_devtree_getprop_offset(fdt, node, "reg",
> &reglen);
> > +    uint32_t flash_addr = qemu_devtree_int_array_index(reg, 0);
> > +    uint32_t flash_size = qemu_devtree_int_array_index(reg, 1);
> > +
> > +    DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0);
> > +    pflash_cfi01_register(flash_addr, NULL,
> > +                          qemu_devtree_get_name(fdt, node, NULL),
> flash_size,
> > +                          dinfo ? dinfo->bdrv : NULL, (64 * 1024),
> > +                          flash_size >> 16,
> > +                          1, 0x89, 0x18, 0x0000, 0x0, 1);
> > +    printf("-- loaded %d bytes to %08X\n",
> > +           load_image_targphys(qemu_devtree_get_name(fdt, node, NULL),
> > +                               flash_addr, flash_size),
> > +           flash_addr);
> > +}
> > +
> > +devinfo_t flash_device = {
> > +    .probe = &flash_probe,
> > +    .pass = 1,
> > +    .compat = (const char * []) { "cfi-flash", NULL }
> > +};
> > +
> > +/*
> > + * Xilinx timer device
> > + */
> > +static void xilinx_timer_probe(void *fdt, int node)
> > +{
> > +    uint32_t timer_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> > +    uint32_t timer_irq = fdt_get_int_from_array(fdt, node,
> "interrupts", 0);
> > +
> > +    printf("  TIMER BASE %08X IRQ %d\n", timer_addr, timer_irq);
> > +
> > +    /* 2 timers at irq 2 @ 62 Mhz.  */
> > +    xilinx_timer_create(timer_addr, irq[timer_irq], 0, 62 * 1000000);
>
> No check is made for the one_timer_only property.
>
> > +}
> > +
> > +devinfo_t xilinx_timer_device = {
> > +    .probe = &xilinx_timer_probe,
> > +    .pass = 1,
> > +    .compat = (const char * []) { "xlnx,xps-timer-1.00.a", NULL }
> > +};
> > +
> > +/*
> > + * Xilinx uartlite device
> > + */
> > +static void xilinx_uartlite_probe(void *fdt, int node)
> > +{
> > +    uint32_t uart_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> > +    uint32_t uart_irq = fdt_get_int_from_array(fdt, node, "interrupts",
> 0);
> > +
> > +    printf("  UART BASE %08X IRQ %d\n", uart_addr, uart_irq);
> > +
> > +    sysbus_create_simple("xlnx.xps-uartlite", uart_addr, irq[uart_irq]);
> > +}
> > +
> > +devinfo_t xilinx_uartlite_device = {
> > +    .probe = &xilinx_uartlite_probe,
> > +    .pass = 1,
> > +    .compat = (const char * []) { "xlnx,xps-uartlite-1.00.a", NULL }
> > +};
> > +
> > +/*
> > + * Xilinx ethernetlite device
> > + */
> > +static void xilinx_ethlite_probe(void *fdt, int node)
> > +{
> > +    uint32_t eth_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> > +    uint32_t eth_irq = fdt_get_int_from_array(fdt, node, "interrupts",
> 0);
> > +
> > +    xilinx_ethlite_create(&nd_table[++eth_dev_index], eth_addr,
> > +                          irq[eth_irq], 0, 0);
> > +}
> > +
> > +devinfo_t xilinx_ethlite_device = {
> > +    .probe = &xilinx_ethlite_probe,
> > +    .pass = 1,
> > +    .compat = (const char * []) { "xlnx,xps-ethernetlite-2.00.b", NULL }
> > +};
> > +
> > +/*
> > + * LabX audio packetizer device
> > + */
> > +static void labx_audio_packetizer_probe(void *fdt, int node)
> > +{
> > +    uint32_t packetizer_addr = fdt_get_int_from_array(fdt, node, "reg",
> 0);
> > +    uint32_t packetizer_irq =
> > +        fdt_get_int_from_array(fdt, node, "interrupts", 0);
> > +    uint32_t clock_domains =
> > +        fdt_get_int_from_array(fdt, node, "xlnx,num-clock-domains", 0);
> > +    uint32_t cache_words =
> > +        fdt_get_int_from_array(fdt, node, "xlnx,cache-data-words", 0);
> > +
> > +    labx_audio_packetizer_create(packetizer_addr, irq[packetizer_irq],
> > +                                 clock_domains, cache_words);
> > +}
> > +
> > +devinfo_t labx_audio_packetizer_device = {
> > +    .probe = &labx_audio_packetizer_probe,
> > +    .pass = 1,
> > +    .compat = (const char * []) { "xlnx,labx-audio-packetizer-1.00.a",
> NULL }
> > +};
> > +
> > +/*
> > + * LabX audio depacketizer device
> > + */
> > +static void labx_audio_depacketizer_probe(void *fdt, int node)
> > +{
> > +    uint32_t depacketizer_addr = fdt_get_int_from_array(fdt, node,
> "reg", 0);
> > +    uint32_t depacketizer_irq =
> > +        fdt_get_int_from_array(fdt, node, "interrupts", 0);
> > +    uint32_t clock_domains =
> > +        fdt_get_int_from_array(fdt, node, "xlnx,num-clock-domains", 0);
> > +    uint32_t cache_words =
> > +        fdt_get_int_from_array(fdt, node, "xlnx,cache-data-words", 0);
> > +
> > +    int ifLen;
> > +    const void *ifType =
> > +        qemu_devtree_getprop_offset(fdt, node, "xlnx,interface-type",
> &ifLen);
> > +    int hasDMA = (0 != strncmp("CACHE_RAM", (const char *)ifType,
> ifLen));
> > +
> > +    labx_audio_depacketizer_create(depacketizer_addr,
> irq[depacketizer_irq],
> > +                                   clock_domains, cache_words, hasDMA);
> > +}
> > +
> > +devinfo_t labx_audio_depacketizer_device = {
> > +    .probe = &labx_audio_depacketizer_probe,
> > +    .pass = 1,
> > +    .compat = (const char * []) {
> > +        "xlnx,labx-audio-depacketizer-1.00.a",
> > +        "xlnx,labx-audio-depacketizer-1.01.a",
> > +        NULL
> > +    }
> > +};
> > +
> > +/*
> > + * LabX dma device
> > + */
> > +static void labx_dma_probe(void *fdt, int node)
> > +{
> > +    uint32_t dma_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> > +
> > +    labx_dma_create(dma_addr, 1024);
> > +}
> > +
> > +devinfo_t labx_dma_device = {
> > +    .probe = &labx_dma_probe,
> > +    .pass = 1,
> > +    .compat = (const char * []) {
> > +        "xlnx,labx-dma-1.00.a",
> > +        "xlnx,labx-dma-1.01.a",
> > +        "xlnx,labx-local-audio-1.00.a",
> > +        NULL
> > +    }
> > +};
> > +
> > +/*
> > + * LabX ethernet device
> > + */
> > +static void labx_ethernet_probe(void *fdt, int node)
> > +{
> > +    uint32_t ethernet_addr = fdt_get_int_from_array(fdt, node, "reg",
> 0);
> > +    uint32_t host_irq =
> > +        fdt_get_int_from_array(fdt, node, "interrupts", 0);
> > +    uint32_t fifo_irq =
> > +        fdt_get_int_from_array(fdt, node, "interrupts", 2);
> > +    uint32_t phy_irq =
> > +        fdt_get_int_from_array(fdt, node, "interrupts", 4);
> > +
> > +    labx_ethernet_create(&nd_table[++eth_dev_index], ethernet_addr,
> > +                         irq[host_irq], irq[fifo_irq], irq[phy_irq]);
> > +}
> > +
> > +devinfo_t labx_ethernet_device = {
> > +    .probe = &labx_ethernet_probe,
> > +    .pass = 1,
> > +    .compat = (const char * []) { "xlnx,labx-ethernet-1.00.a", NULL }
> > +};
> > +
> > +/*
> > + * LabX ptp device
> > + */
> > +static void labx_ptp_probe(void *fdt, int node)
> > +{
> > +    uint32_t ptp_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> > +
> > +    labx_ptp_create(ptp_addr);
> > +}
> > +
> > +devinfo_t labx_ptp_device = {
> > +    .probe = &labx_ptp_probe,
> > +    .pass = 1,
> > +    .compat = (const char * []) { "xlnx,labx-ptp-1.00.a", NULL }
> > +};
> > +
> > +/*
> > + * Table of available devices
> > + */
> > +devinfo_t *devices[] = {
> > +    &xilinx_interrupt_controller_device,
> > +    &flash_device,
> > +    &xilinx_timer_device,
> > +    &xilinx_uartlite_device,
> > +    &xilinx_ethlite_device,
> > +    &labx_audio_packetizer_device,
> > +    &labx_audio_depacketizer_device,
> > +    &labx_dma_device,
> > +    &labx_ethernet_device,
> > +    &labx_ptp_device,
> > +    NULL
> > +};
> > +
> > +static int plb_device_probe(void *fdt, int node, int pass)
>
> QEMU abstacts away interconnect implementations. You should probably
> also drive off the "simple-bus" compat rather than PLB for machine model
> compatibility. s/plb/bus/g or something like it.
>
> > +{
> > +    devinfo_t **dev = &(devices[0]);
> > +
> > +    while (*dev) {
> > +        const char **compat = &((*dev)->compat[0]);
> > +        while (*compat) {
> > +            if (0 == qemu_devtree_node_check_compatible(fdt, node,
> *compat)) {
> > +                if (pass == (*dev)->pass) {
> > +                    printf("Adding a device for node %s\n",
> > +                           qemu_devtree_get_name(fdt, node, NULL));
> > +
> > +                    (*dev)->probe(fdt, node);
> > +                    return 0;
> > +                }
> > +
> > +                if (pass < (*dev)->pass) {
> > +                    /* Probe again on the next pass */
> > +                    return 1;
> > +                }
> > +            }
> > +
> > +            compat++;
> > +        }
> > +
> > +        dev++;
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static void plb_bus_probe(void *fdt)
> > +{
> > +    int root = qemu_devtree_node_offset(fdt, "/");
> > +    int plb = qemu_devtree_subnode_offset_namelen(fdt, root, "plb", 3);
> > +    if (plb > 0) {
> > +        /* Do multiple passes through the devices. Some have
> dependencies
> > +           on others being first */
> > +        int pass = 0;
> > +        int again = 0;
> > +        do {
> > +            int child = plb;
> > +            again = 0;
> > +            do {
> > +                child = qemu_devtree_next_child_offset(fdt, plb, child);
> > +                if (child < 0) {
> > +                    break;
> > +                }
> > +
> > +                again |= plb_device_probe(fdt, child, pass);
> > +
> > +            } while (1);
> > +
> > +            pass++;
> > +
> > +        } while (again);
> > +    }
> > +}
> > +
> > +static void
> > +labx_microblaze_init(ram_addr_t ram_size,
> > +                     const char *boot_device,
> > +                     const char *kernel_filename,
> > +                     const char *kernel_cmdline,
> > +                     const char *initrd_filename,
> > +                     const char *cpu_model)
> > +{
> > +    MemoryRegion *address_space_mem = get_system_memory();
> > +
> > +    int kernel_size;
> > +    int fdt_size;
> > +    void *fdt = get_device_tree(&fdt_size);
> > +    target_phys_addr_t ddr_base = get_dram_base(fdt);
> > +    MemoryRegion *phys_lmb_bram = g_new(MemoryRegion, 1);
> > +    MemoryRegion *phys_ram = g_new(MemoryRegion, 1);
> > +    CPUMBState *env;
> > +
> > +    /* init CPUs */
> > +    if (cpu_model == NULL) {
> > +        cpu_model = "microblaze";
> > +    }
> > +    g_cpu = cpu_mb_init(cpu_model);
> > +    env = &g_cpu->env;
> > +
> > +    env->pvr.regs[10] = 0x0c000000; /* spartan 3a dsp family.  */
>
> I have dtb driven solution for this.
>
> > +    qemu_register_reset(main_cpu_reset, g_cpu);
> > +
> > +    /* Attach emulated BRAM through the LMB. LMB size is not specified
> in the
> > +       device-tree but there must be one to hold the vector table. */
> > +    memory_region_init_ram(phys_lmb_bram, "labx_microblaze.lmb_bram",
> > +                           LMB_BRAM_SIZE);
> > +    vmstate_register_ram_global(phys_lmb_bram);
> > +    memory_region_add_subregion(address_space_mem, 0x00000000,
> phys_lmb_bram);
> > +
> > +    memory_region_init_ram(phys_ram, "labx_microblaze.ram", ram_size);
> > +    vmstate_register_ram_global(phys_ram);
> > +    memory_region_add_subregion(address_space_mem, ddr_base, phys_ram);
> > +
> > +
> > +    /* Create other devices listed in the device-tree */
> > +    plb_bus_probe(fdt);
> > +
> > +    if (kernel_filename) {
> > +        uint64_t entry, low, high;
> > +        uint32_t base32;
> > +
> > +        /* Boots a kernel elf binary.  */
> > +        kernel_size = load_elf(kernel_filename, NULL, NULL,
> > +                               &entry, &low, &high,
> > +                               1, ELF_MACHINE, 0);
> > +        base32 = entry;
> > +        if (base32 == 0xc0000000) {
> > +            kernel_size = load_elf(kernel_filename,
> translate_kernel_address,
> > +                                   NULL, &entry, NULL, NULL,
> > +                                   1, ELF_MACHINE, 0);
> > +        }
> > +        /* Always boot into physical ram.  */
> > +        boot_info.bootstrap_pc = ddr_base + (entry & 0x07ffffff);
> > +
> > +        /* If it wasn't an ELF image, try an u-boot image.  */
> > +        if (kernel_size < 0) {
> > +            target_phys_addr_t uentry, loadaddr;
> > +
> > +            kernel_size = load_uimage(kernel_filename, &uentry,
> &loadaddr, 0);
> > +            boot_info.bootstrap_pc = uentry;
> > +            high = (loadaddr + kernel_size + 3) & ~3;
> > +        }
> > +
> > +        /* Not an ELF image nor an u-boot image, try a RAW image.  */
> > +        if (kernel_size < 0) {
> > +            kernel_size = load_image_targphys(kernel_filename, ddr_base,
> > +                                              ram_size);
> > +            boot_info.bootstrap_pc = ddr_base;
> > +            high = (ddr_base + kernel_size + 3) & ~3;
> > +        }
> > +
> > +        if (initrd_filename) {
> > +            uint32_t initrd_base = 0x88c00000;
> > +            uint32_t initrd_size =
> > +                    load_image_targphys(initrd_filename, initrd_base,
> > +                                        ram_size - initrd_base);
> > +            if (initrd_size <= 0) {
> > +                fprintf(stderr, "qemu: could not load initial ram disk
> '%s'\n",
> > +                initrd_filename);
> > +                exit(1);
> > +            }
> > +
> > +            boot_info.initrd = initrd_base;
> > +        } else {
> > +            boot_info.initrd = 0x00000000;
> > +        }
> > +
> > +        boot_info.cmdline = high + 4096;
> > +        if (kernel_cmdline && strlen(kernel_cmdline)) {
> > +            pstrcpy_targphys("cmdline", boot_info.cmdline, 256,
> kernel_cmdline);
> > +        }
> > +        /* Provide a device-tree.  */
> > +        boot_info.fdt = boot_info.cmdline + 4096;
> > +        labx_microblaze_load_device_tree(boot_info.fdt, ram_size,
> > +                                   0, 0,
> > +                                   kernel_cmdline);
> > +    }
> > +}
> > +
> > +static QEMUMachine labx_microblaze_machine = {
>
> Its a generic machine, so should it be badged as labx?
>
> > +    .name = "labx-microblaze-devicetree",
> > +    .desc = "Microblaze design based on the peripherals specified "
> > +            "in the device-tree.",
> > +    .init = labx_microblaze_init,
> > +    .is_default = 1
>
> > +};
> > +
> > +static void labx_microblaze_machine_init(void)
> > +{
> > +    qemu_register_machine(&labx_microblaze_machine);
> > +}
> > +
> > +machine_init(labx_microblaze_machine_init);
> > +
> > diff --git a/hw/microblaze/Makefile.objs b/hw/microblaze/Makefile.objs
> > index 274d2c5..632cd60 100644
> > --- a/hw/microblaze/Makefile.objs
> > +++ b/hw/microblaze/Makefile.objs
> > @@ -1,4 +1,5 @@
> > -obj-y = petalogix_s3adsp1800_mmu.o
> > +obj-y  = labx_microblaze_devicetree.o
>
> obj-y += labx_microblaze_devicetree.(CONFIG-FDT)
>
> > +obj-y += petalogix_s3adsp1800_mmu.o
> >  obj-y += petalogix_ml605_mmu.o
> >  obj-y += microblaze_boot.o
> >
>
>
>
Blue Swirl Sept. 14, 2012, 7:13 p.m. UTC | #5
On Wed, Sep 12, 2012 at 12:17 AM, Peter Crosthwaite
<peter.crosthwaite@petalogix.com> wrote:
> On Tue, 2012-09-11 at 19:27 +0000, Blue Swirl wrote:
>> On Mon, Sep 10, 2012 at 12:20 AM,  <crwulff@gmail.com> wrote:
>> > From: Chris Wulff <crwulff@gmail.com>
>> >
>> > +/*
>> > + * Xilinx ethernetlite device
>> > + */
>> > +static void xilinx_ethlite_probe(void *fdt, int node)
>> > +{
>> > +    uint32_t eth_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
>> > +    uint32_t eth_irq = fdt_get_int_from_array(fdt, node, "interrupts", 0);
>> > +
>> > +    xilinx_ethlite_create(&nd_table[++eth_dev_index], eth_addr,
>> > +                          irq[eth_irq], 0, 0);
>>
>> The device should not access nd_table, this should be handled at board level.
>>
>
> This is board level isn't it? This function calls
> xilinx_ethlite_create() which in turn does the qdev_create so this is
> pre device creation. Im open to suggestions on how to do this in a
> non-global hacking way however as I have the same problem in my tree.

OK, the device code is mixed with the board code and that was
confusing me. It could be better to move the device part to a separate
file.

>
> Regards,
> Peter
>
>> > +}
>> > +
>> > +devinfo_t xilinx_ethlite_device = {
>> > +    .probe = &xilinx_ethlite_probe,
>> > +    .pass = 1,
>> > +    .compat = (const char * []) { "xlnx,xps-ethernetlite-2.00.b", NULL }
>> > +};
>> > +
>
>
diff mbox

Patch

diff --git a/default-configs/microblaze-softmmu.mak b/default-configs/microblaze-softmmu.mak
index 64c9485..7e4cab8 100644
--- a/default-configs/microblaze-softmmu.mak
+++ b/default-configs/microblaze-softmmu.mak
@@ -5,3 +5,4 @@  CONFIG_PFLASH_CFI01=y
 CONFIG_SERIAL=y
 CONFIG_XILINX=y
 CONFIG_XILINX_AXI=y
+CONFIG_LABX=y
diff --git a/hw/labx_microblaze_devicetree.c b/hw/labx_microblaze_devicetree.c
new file mode 100644
index 0000000..581ad14
--- /dev/null
+++ b/hw/labx_microblaze_devicetree.c
@@ -0,0 +1,597 @@ 
+/*
+ * Flexible model of microblaze designs that use a device-tree to determine
+ * the hardware configuration.
+ *
+ * Copyright (c) 2010 LabX Technologies, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "sysbus.h"
+#include "hw.h"
+#include "net.h"
+#include "flash.h"
+#include "sysemu.h"
+#include "devices.h"
+#include "boards.h"
+#include "device_tree.h"
+#include "xilinx.h"
+#include "labx_devices.h"
+#include "loader.h"
+#include "elf.h"
+#include "blockdev.h"
+#include "exec-memory.h"
+#include "microblaze_pic_cpu.h"
+
+#define LMB_BRAM_SIZE  (128 * 1024)
+
+static struct
+{
+    uint32_t bootstrap_pc;
+    uint32_t cmdline;
+    uint32_t initrd;
+    uint32_t fdt;
+} boot_info;
+
+/* Current ethernet device index for multiple network interfaces */
+static int eth_dev_index = -1;
+
+static void main_cpu_reset(void *opaque)
+{
+    MicroBlazeCPU *cpu = opaque;
+    CPUMBState *env = &cpu->env;
+
+    cpu_reset(CPU(cpu));
+
+    env->regs[5] = boot_info.cmdline;
+    env->regs[6] = boot_info.initrd;
+    env->regs[7] = boot_info.fdt;
+    env->sregs[SR_PC] = boot_info.bootstrap_pc;
+}
+
+#ifndef CONFIG_FDT
+#error "Device-tree support is required for this target to function"
+#endif
+
+static uint32_t fdt_get_int_from_array(void *fdt, int node,
+                                       const char *name, int index)
+{
+    int array_length;
+    const void *array = qemu_devtree_getprop_offset(fdt, node, name,
+                                                    &array_length);
+    if (index >= array_length) {
+        printf("fdt_get_int_from_array: requesting %s from node %d, "
+               "index %d out of range (%d max)\n",
+               name, node, index, array_length);
+        return 0;
+    } else {
+        return qemu_devtree_int_array_index(array, index);
+    }
+}
+
+#define BINARY_DEVICE_TREE_FILE "labx-microblaze.dtb"
+static void *get_device_tree(int *fdt_size)
+{
+    char *path;
+    void *fdt;
+
+    /* Try the local "mb.dtb" override.  */
+    fdt = load_device_tree("mb.dtb", fdt_size);
+    if (!fdt) {
+        path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
+        if (path) {
+            fdt = load_device_tree(path, fdt_size);
+            g_free(path);
+        }
+    }
+
+    return fdt;
+}
+
+static int labx_microblaze_load_device_tree(target_phys_addr_t addr,
+                                      uint32_t ramsize,
+                                      target_phys_addr_t initrd_base,
+                                      target_phys_addr_t initrd_size,
+                                      const char *kernel_cmdline)
+{
+    int fdt_size;
+    void *fdt;
+    int r;
+
+    fdt = get_device_tree(&fdt_size);
+
+    if (!fdt) {
+        return 0;
+    }
+
+    if (kernel_cmdline && strlen(kernel_cmdline)) {
+        r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs",
+                                        kernel_cmdline);
+        if (r < 0) {
+            fprintf(stderr, "couldn't set /chosen/bootargs\n");
+        }
+    }
+    cpu_physical_memory_write(addr, (void *)fdt, fdt_size);
+
+    return fdt_size;
+}
+
+static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
+{
+    return addr - 0x30000000LL;
+}
+
+static ram_addr_t get_dram_base(void *fdt)
+{
+    int root = qemu_devtree_node_offset(fdt, "/");
+    int memory = qemu_devtree_subnode_offset_namelen(fdt, root, "memory", 6);
+    if (memory > 0) {
+        int reglen;
+        const void *reg =
+            qemu_devtree_getprop_offset(fdt, memory, "reg", &reglen);
+
+        if (reglen >= 4) {
+            printf("DRAM base %08X, size %08X\n",
+                   qemu_devtree_int_array_index(reg, 0),
+                   qemu_devtree_int_array_index(reg, 1));
+            return qemu_devtree_int_array_index(reg, 0);
+        }
+    }
+
+    printf("DRAM base not found. Defaulting to 0x88000000\n");
+
+    return 0x88000000; /* Default to something reasonable */
+}
+
+typedef void (*device_init_func_t)(void *fdt, int node);
+
+typedef struct {
+    device_init_func_t probe;
+    int pass;
+    const char **compat;
+
+} devinfo_t;
+
+/*
+ * Xilinx interrupt controller device
+ */
+MicroBlazeCPU *g_cpu;
+static qemu_irq irq[32] = {};
+static qemu_irq *cpu_irq;
+
+static void xilinx_interrupt_controller_probe(void *fdt, int node)
+{
+    int i;
+    DeviceState *dev;
+    int reglen;
+    const void *reg = qemu_devtree_getprop_offset(fdt, node, "reg", &reglen);
+    uint32_t irq_addr = qemu_devtree_int_array_index(reg, 0);
+    int nrIrqLen;
+    const void *nrIrq =
+        qemu_devtree_getprop_offset(fdt, node, "xlnx,num-intr-inputs",
+                                    &nrIrqLen);
+    uint32_t nrIrqs = qemu_devtree_int_array_index(nrIrq, 0);
+
+    printf("  IRQ BASE %08X NIRQS %d\n", irq_addr, nrIrqs);
+
+    cpu_irq = microblaze_pic_init_cpu(&g_cpu->env);
+    dev = xilinx_intc_create(irq_addr, cpu_irq[0], 2);
+    for (i = 0; i < nrIrqs; i++) {
+        irq[i] = qdev_get_gpio_in(dev, i);
+    }
+}
+
+devinfo_t xilinx_interrupt_controller_device = {
+    .probe = &xilinx_interrupt_controller_probe,
+    .pass = 0,
+    .compat = (const char * []) { "xlnx,xps-intc-1.00.a", NULL }
+};
+
+/*
+ * Flash device
+ */
+static void flash_probe(void *fdt, int node)
+{
+    int reglen;
+    const void *reg = qemu_devtree_getprop_offset(fdt, node, "reg", &reglen);
+    uint32_t flash_addr = qemu_devtree_int_array_index(reg, 0);
+    uint32_t flash_size = qemu_devtree_int_array_index(reg, 1);
+
+    DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0);
+    pflash_cfi01_register(flash_addr, NULL,
+                          qemu_devtree_get_name(fdt, node, NULL), flash_size,
+                          dinfo ? dinfo->bdrv : NULL, (64 * 1024),
+                          flash_size >> 16,
+                          1, 0x89, 0x18, 0x0000, 0x0, 1);
+    printf("-- loaded %d bytes to %08X\n",
+           load_image_targphys(qemu_devtree_get_name(fdt, node, NULL),
+                               flash_addr, flash_size),
+           flash_addr);
+}
+
+devinfo_t flash_device = {
+    .probe = &flash_probe,
+    .pass = 1,
+    .compat = (const char * []) { "cfi-flash", NULL }
+};
+
+/*
+ * Xilinx timer device
+ */
+static void xilinx_timer_probe(void *fdt, int node)
+{
+    uint32_t timer_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
+    uint32_t timer_irq = fdt_get_int_from_array(fdt, node, "interrupts", 0);
+
+    printf("  TIMER BASE %08X IRQ %d\n", timer_addr, timer_irq);
+
+    /* 2 timers at irq 2 @ 62 Mhz.  */
+    xilinx_timer_create(timer_addr, irq[timer_irq], 0, 62 * 1000000);
+}
+
+devinfo_t xilinx_timer_device = {
+    .probe = &xilinx_timer_probe,
+    .pass = 1,
+    .compat = (const char * []) { "xlnx,xps-timer-1.00.a", NULL }
+};
+
+/*
+ * Xilinx uartlite device
+ */
+static void xilinx_uartlite_probe(void *fdt, int node)
+{
+    uint32_t uart_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
+    uint32_t uart_irq = fdt_get_int_from_array(fdt, node, "interrupts", 0);
+
+    printf("  UART BASE %08X IRQ %d\n", uart_addr, uart_irq);
+
+    sysbus_create_simple("xlnx.xps-uartlite", uart_addr, irq[uart_irq]);
+}
+
+devinfo_t xilinx_uartlite_device = {
+    .probe = &xilinx_uartlite_probe,
+    .pass = 1,
+    .compat = (const char * []) { "xlnx,xps-uartlite-1.00.a", NULL }
+};
+
+/*
+ * Xilinx ethernetlite device
+ */
+static void xilinx_ethlite_probe(void *fdt, int node)
+{
+    uint32_t eth_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
+    uint32_t eth_irq = fdt_get_int_from_array(fdt, node, "interrupts", 0);
+
+    xilinx_ethlite_create(&nd_table[++eth_dev_index], eth_addr,
+                          irq[eth_irq], 0, 0);
+}
+
+devinfo_t xilinx_ethlite_device = {
+    .probe = &xilinx_ethlite_probe,
+    .pass = 1,
+    .compat = (const char * []) { "xlnx,xps-ethernetlite-2.00.b", NULL }
+};
+
+/*
+ * LabX audio packetizer device
+ */
+static void labx_audio_packetizer_probe(void *fdt, int node)
+{
+    uint32_t packetizer_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
+    uint32_t packetizer_irq =
+        fdt_get_int_from_array(fdt, node, "interrupts", 0);
+    uint32_t clock_domains =
+        fdt_get_int_from_array(fdt, node, "xlnx,num-clock-domains", 0);
+    uint32_t cache_words =
+        fdt_get_int_from_array(fdt, node, "xlnx,cache-data-words", 0);
+
+    labx_audio_packetizer_create(packetizer_addr, irq[packetizer_irq],
+                                 clock_domains, cache_words);
+}
+
+devinfo_t labx_audio_packetizer_device = {
+    .probe = &labx_audio_packetizer_probe,
+    .pass = 1,
+    .compat = (const char * []) { "xlnx,labx-audio-packetizer-1.00.a", NULL }
+};
+
+/*
+ * LabX audio depacketizer device
+ */
+static void labx_audio_depacketizer_probe(void *fdt, int node)
+{
+    uint32_t depacketizer_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
+    uint32_t depacketizer_irq =
+        fdt_get_int_from_array(fdt, node, "interrupts", 0);
+    uint32_t clock_domains =
+        fdt_get_int_from_array(fdt, node, "xlnx,num-clock-domains", 0);
+    uint32_t cache_words =
+        fdt_get_int_from_array(fdt, node, "xlnx,cache-data-words", 0);
+
+    int ifLen;
+    const void *ifType =
+        qemu_devtree_getprop_offset(fdt, node, "xlnx,interface-type", &ifLen);
+    int hasDMA = (0 != strncmp("CACHE_RAM", (const char *)ifType, ifLen));
+
+    labx_audio_depacketizer_create(depacketizer_addr, irq[depacketizer_irq],
+                                   clock_domains, cache_words, hasDMA);
+}
+
+devinfo_t labx_audio_depacketizer_device = {
+    .probe = &labx_audio_depacketizer_probe,
+    .pass = 1,
+    .compat = (const char * []) {
+        "xlnx,labx-audio-depacketizer-1.00.a",
+        "xlnx,labx-audio-depacketizer-1.01.a",
+        NULL
+    }
+};
+
+/*
+ * LabX dma device
+ */
+static void labx_dma_probe(void *fdt, int node)
+{
+    uint32_t dma_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
+
+    labx_dma_create(dma_addr, 1024);
+}
+
+devinfo_t labx_dma_device = {
+    .probe = &labx_dma_probe,
+    .pass = 1,
+    .compat = (const char * []) {
+        "xlnx,labx-dma-1.00.a",
+        "xlnx,labx-dma-1.01.a",
+        "xlnx,labx-local-audio-1.00.a",
+        NULL
+    }
+};
+
+/*
+ * LabX ethernet device
+ */
+static void labx_ethernet_probe(void *fdt, int node)
+{
+    uint32_t ethernet_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
+    uint32_t host_irq =
+        fdt_get_int_from_array(fdt, node, "interrupts", 0);
+    uint32_t fifo_irq =
+        fdt_get_int_from_array(fdt, node, "interrupts", 2);
+    uint32_t phy_irq =
+        fdt_get_int_from_array(fdt, node, "interrupts", 4);
+
+    labx_ethernet_create(&nd_table[++eth_dev_index], ethernet_addr,
+                         irq[host_irq], irq[fifo_irq], irq[phy_irq]);
+}
+
+devinfo_t labx_ethernet_device = {
+    .probe = &labx_ethernet_probe,
+    .pass = 1,
+    .compat = (const char * []) { "xlnx,labx-ethernet-1.00.a", NULL }
+};
+
+/*
+ * LabX ptp device
+ */
+static void labx_ptp_probe(void *fdt, int node)
+{
+    uint32_t ptp_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
+
+    labx_ptp_create(ptp_addr);
+}
+
+devinfo_t labx_ptp_device = {
+    .probe = &labx_ptp_probe,
+    .pass = 1,
+    .compat = (const char * []) { "xlnx,labx-ptp-1.00.a", NULL }
+};
+
+/*
+ * Table of available devices
+ */
+devinfo_t *devices[] = {
+    &xilinx_interrupt_controller_device,
+    &flash_device,
+    &xilinx_timer_device,
+    &xilinx_uartlite_device,
+    &xilinx_ethlite_device,
+    &labx_audio_packetizer_device,
+    &labx_audio_depacketizer_device,
+    &labx_dma_device,
+    &labx_ethernet_device,
+    &labx_ptp_device,
+    NULL
+};
+
+static int plb_device_probe(void *fdt, int node, int pass)
+{
+    devinfo_t **dev = &(devices[0]);
+
+    while (*dev) {
+        const char **compat = &((*dev)->compat[0]);
+        while (*compat) {
+            if (0 == qemu_devtree_node_check_compatible(fdt, node, *compat)) {
+                if (pass == (*dev)->pass) {
+                    printf("Adding a device for node %s\n",
+                           qemu_devtree_get_name(fdt, node, NULL));
+
+                    (*dev)->probe(fdt, node);
+                    return 0;
+                }
+
+                if (pass < (*dev)->pass) {
+                    /* Probe again on the next pass */
+                    return 1;
+                }
+            }
+
+            compat++;
+        }
+
+        dev++;
+    }
+
+    return 0;
+}
+
+static void plb_bus_probe(void *fdt)
+{
+    int root = qemu_devtree_node_offset(fdt, "/");
+    int plb = qemu_devtree_subnode_offset_namelen(fdt, root, "plb", 3);
+    if (plb > 0) {
+        /* Do multiple passes through the devices. Some have dependencies
+           on others being first */
+        int pass = 0;
+        int again = 0;
+        do {
+            int child = plb;
+            again = 0;
+            do {
+                child = qemu_devtree_next_child_offset(fdt, plb, child);
+                if (child < 0) {
+                    break;
+                }
+
+                again |= plb_device_probe(fdt, child, pass);
+
+            } while (1);
+
+            pass++;
+
+        } while (again);
+    }
+}
+
+static void
+labx_microblaze_init(ram_addr_t ram_size,
+                     const char *boot_device,
+                     const char *kernel_filename,
+                     const char *kernel_cmdline,
+                     const char *initrd_filename,
+                     const char *cpu_model)
+{
+    MemoryRegion *address_space_mem = get_system_memory();
+
+    int kernel_size;
+    int fdt_size;
+    void *fdt = get_device_tree(&fdt_size);
+    target_phys_addr_t ddr_base = get_dram_base(fdt);
+    MemoryRegion *phys_lmb_bram = g_new(MemoryRegion, 1);
+    MemoryRegion *phys_ram = g_new(MemoryRegion, 1);
+    CPUMBState *env;
+
+    /* init CPUs */
+    if (cpu_model == NULL) {
+        cpu_model = "microblaze";
+    }
+    g_cpu = cpu_mb_init(cpu_model);
+    env = &g_cpu->env;
+
+    env->pvr.regs[10] = 0x0c000000; /* spartan 3a dsp family.  */
+    qemu_register_reset(main_cpu_reset, g_cpu);
+
+    /* Attach emulated BRAM through the LMB. LMB size is not specified in the
+       device-tree but there must be one to hold the vector table. */
+    memory_region_init_ram(phys_lmb_bram, "labx_microblaze.lmb_bram",
+                           LMB_BRAM_SIZE);
+    vmstate_register_ram_global(phys_lmb_bram);
+    memory_region_add_subregion(address_space_mem, 0x00000000, phys_lmb_bram);
+
+    memory_region_init_ram(phys_ram, "labx_microblaze.ram", ram_size);
+    vmstate_register_ram_global(phys_ram);
+    memory_region_add_subregion(address_space_mem, ddr_base, phys_ram);
+
+
+    /* Create other devices listed in the device-tree */
+    plb_bus_probe(fdt);
+
+    if (kernel_filename) {
+        uint64_t entry, low, high;
+        uint32_t base32;
+
+        /* Boots a kernel elf binary.  */
+        kernel_size = load_elf(kernel_filename, NULL, NULL,
+                               &entry, &low, &high,
+                               1, ELF_MACHINE, 0);
+        base32 = entry;
+        if (base32 == 0xc0000000) {
+            kernel_size = load_elf(kernel_filename, translate_kernel_address,
+                                   NULL, &entry, NULL, NULL,
+                                   1, ELF_MACHINE, 0);
+        }
+        /* Always boot into physical ram.  */
+        boot_info.bootstrap_pc = ddr_base + (entry & 0x07ffffff);
+
+        /* If it wasn't an ELF image, try an u-boot image.  */
+        if (kernel_size < 0) {
+            target_phys_addr_t uentry, loadaddr;
+
+            kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0);
+            boot_info.bootstrap_pc = uentry;
+            high = (loadaddr + kernel_size + 3) & ~3;
+        }
+
+        /* Not an ELF image nor an u-boot image, try a RAW image.  */
+        if (kernel_size < 0) {
+            kernel_size = load_image_targphys(kernel_filename, ddr_base,
+                                              ram_size);
+            boot_info.bootstrap_pc = ddr_base;
+            high = (ddr_base + kernel_size + 3) & ~3;
+        }
+
+        if (initrd_filename) {
+            uint32_t initrd_base = 0x88c00000;
+            uint32_t initrd_size =
+                    load_image_targphys(initrd_filename, initrd_base,
+                                        ram_size - initrd_base);
+            if (initrd_size <= 0) {
+                fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
+                initrd_filename);
+                exit(1);
+            }
+
+            boot_info.initrd = initrd_base;
+        } else {
+            boot_info.initrd = 0x00000000;
+        }
+
+        boot_info.cmdline = high + 4096;
+        if (kernel_cmdline && strlen(kernel_cmdline)) {
+            pstrcpy_targphys("cmdline", boot_info.cmdline, 256, kernel_cmdline);
+        }
+        /* Provide a device-tree.  */
+        boot_info.fdt = boot_info.cmdline + 4096;
+        labx_microblaze_load_device_tree(boot_info.fdt, ram_size,
+                                   0, 0,
+                                   kernel_cmdline);
+    }
+}
+
+static QEMUMachine labx_microblaze_machine = {
+    .name = "labx-microblaze-devicetree",
+    .desc = "Microblaze design based on the peripherals specified "
+            "in the device-tree.",
+    .init = labx_microblaze_init,
+    .is_default = 1
+};
+
+static void labx_microblaze_machine_init(void)
+{
+    qemu_register_machine(&labx_microblaze_machine);
+}
+
+machine_init(labx_microblaze_machine_init);
+
diff --git a/hw/microblaze/Makefile.objs b/hw/microblaze/Makefile.objs
index 274d2c5..632cd60 100644
--- a/hw/microblaze/Makefile.objs
+++ b/hw/microblaze/Makefile.objs
@@ -1,4 +1,5 @@ 
-obj-y = petalogix_s3adsp1800_mmu.o
+obj-y  = labx_microblaze_devicetree.o
+obj-y += petalogix_s3adsp1800_mmu.o
 obj-y += petalogix_ml605_mmu.o
 obj-y += microblaze_boot.o