virtex_ml507.c 8.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/*
 * Model of Xilinx Virtex5 ML507 PPC-440 refdesign.
 *
 * Copyright (c) 2010 Edgar E. Iglesias.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

25 26
#include "hw/sysbus.h"
#include "hw/hw.h"
P
Paolo Bonzini 已提交
27 28
#include "hw/char/serial.h"
#include "hw/block/flash.h"
29
#include "sysemu/sysemu.h"
30
#include "hw/devices.h"
31
#include "hw/boards.h"
32
#include "sysemu/device_tree.h"
33
#include "hw/loader.h"
34
#include "elf.h"
35
#include "qemu/log.h"
36
#include "exec/address-spaces.h"
37

P
Paolo Bonzini 已提交
38 39
#include "hw/ppc/ppc.h"
#include "hw/ppc/ppc4xx.h"
40
#include "ppc405.h"
41

42
#include "sysemu/blockdev.h"
43
#include "hw/xilinx.h"
44 45 46 47 48 49 50 51 52 53 54 55 56 57

#define EPAPR_MAGIC    (0x45504150)
#define FLASH_SIZE     (16 * 1024 * 1024)

static struct boot_info
{
    uint32_t bootstrap_pc;
    uint32_t cmdline;
    uint32_t fdt;
    uint32_t ima_size;
    void *vfdt;
} boot_info;

/* Create reset TLB entries for BookE, spanning the 32bit addr space.  */
A
Andreas Färber 已提交
58
static void mmubooke_create_initial_mapping(CPUPPCState *env,
59
                                     target_ulong va,
A
Avi Kivity 已提交
60
                                     hwaddr pa)
61
{
62
    ppcemb_tlb_t *tlb = &env->tlb.tlbe[0];
63 64 65 66 67 68 69 70

    tlb->attr = 0;
    tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4);
    tlb->size = 1 << 31; /* up to 0x80000000  */
    tlb->EPN = va & TARGET_PAGE_MASK;
    tlb->RPN = pa & TARGET_PAGE_MASK;
    tlb->PID = 0;

71
    tlb = &env->tlb.tlbe[1];
72 73 74 75 76 77 78 79
    tlb->attr = 0;
    tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4);
    tlb->size = 1 << 31; /* up to 0xffffffff  */
    tlb->EPN = 0x80000000 & TARGET_PAGE_MASK;
    tlb->RPN = 0x80000000 & TARGET_PAGE_MASK;
    tlb->PID = 0;
}

80 81 82 83
static PowerPCCPU *ppc440_init_xilinx(ram_addr_t *ram_size,
                                      int do_init,
                                      const char *cpu_model,
                                      uint32_t sysclk)
84
{
85
    PowerPCCPU *cpu;
A
Andreas Färber 已提交
86
    CPUPPCState *env;
87 88
    qemu_irq *irqs;

89 90
    cpu = cpu_ppc_init(cpu_model);
    if (cpu == NULL) {
91 92 93
        fprintf(stderr, "Unable to initialize CPU!\n");
        exit(1);
    }
94
    env = &cpu->env;
95

96
    ppc_booke_timers_init(cpu, sysclk, 0/* no flags */);
97 98 99 100

    ppc_dcr_init(env, NULL, NULL);

    /* interrupt controller */
101
    irqs = g_malloc0(sizeof(qemu_irq) * PPCUIC_OUTPUT_NB);
102 103
    irqs[PPCUIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT];
    irqs[PPCUIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT];
B
Blue Swirl 已提交
104
    ppcuic_init(env, irqs, 0x0C0, 0, 1);
105
    return cpu;
106 107 108 109
}

static void main_cpu_reset(void *opaque)
{
110 111
    PowerPCCPU *cpu = opaque;
    CPUPPCState *env = &cpu->env;
112 113
    struct boot_info *bi = env->load_info;

114
    cpu_reset(CPU(cpu));
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
    /* Linux Kernel Parameters (passing device tree):
       *   r3: pointer to the fdt
       *   r4: 0
       *   r5: 0
       *   r6: epapr magic
       *   r7: size of IMA in bytes
       *   r8: 0
       *   r9: 0
    */
    env->gpr[1] = (16<<20) - 8;
    /* Provide a device-tree.  */
    env->gpr[3] = bi->fdt;
    env->nip = bi->bootstrap_pc;

    /* Create a mapping for the kernel.  */
    mmubooke_create_initial_mapping(env, 0, 0);
    env->gpr[6] = tswap32(EPAPR_MAGIC);
    env->gpr[7] = bi->ima_size;
}

#define BINARY_DEVICE_TREE_FILE "virtex-ml507.dtb"
A
Avi Kivity 已提交
136
static int xilinx_load_device_tree(hwaddr addr,
137
                                      uint32_t ramsize,
A
Avi Kivity 已提交
138 139
                                      hwaddr initrd_base,
                                      hwaddr initrd_size,
140 141 142 143
                                      const char *kernel_cmdline)
{
    char *path;
    int fdt_size;
144
    void *fdt = NULL;
145
    int r;
146
    const char *dtb_filename;
147

148 149 150 151 152 153
    dtb_filename = qemu_opt_get(qemu_get_machine_opts(), "dtb");
    if (dtb_filename) {
        fdt = load_device_tree(dtb_filename, &fdt_size);
        if (!fdt) {
            error_report("Error while loading device tree file '%s'",
                dtb_filename);
154
        }
155 156 157
    } else {
        /* Try the local "ppc.dtb" override.  */
        fdt = load_device_tree("ppc.dtb", &fdt_size);
E
Edgar E. Iglesias 已提交
158
        if (!fdt) {
159 160 161 162 163
            path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
            if (path) {
                fdt = load_device_tree(path, &fdt_size);
                g_free(path);
            }
E
Edgar E. Iglesias 已提交
164
        }
165
    }
166 167 168
    if (!fdt) {
        return 0;
    }
169 170 171
    r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline);
    if (r < 0)
        fprintf(stderr, "couldn't set /chosen/bootargs\n");
S
Stefan Weil 已提交
172
    cpu_physical_memory_write(addr, fdt, fdt_size);
173 174 175
    return fdt_size;
}

176
static void virtex_init(QEMUMachineInitArgs *args)
177
{
178 179 180 181
    ram_addr_t ram_size = args->ram_size;
    const char *cpu_model = args->cpu_model;
    const char *kernel_filename = args->kernel_filename;
    const char *kernel_cmdline = args->kernel_cmdline;
182
    MemoryRegion *address_space_mem = get_system_memory();
183
    DeviceState *dev;
184
    PowerPCCPU *cpu;
A
Andreas Färber 已提交
185
    CPUPPCState *env;
A
Avi Kivity 已提交
186
    hwaddr ram_base = 0;
187
    DriveInfo *dinfo;
A
Avi Kivity 已提交
188
    MemoryRegion *phys_ram = g_new(MemoryRegion, 1);
189 190 191 192 193 194 195 196 197
    qemu_irq irq[32], *cpu_irq;
    int kernel_size;
    int i;

    /* init CPUs */
    if (cpu_model == NULL) {
        cpu_model = "440-Xilinx";
    }

198 199
    cpu = ppc440_init_xilinx(&ram_size, 1, cpu_model, 400000000);
    env = &cpu->env;
200
    qemu_register_reset(main_cpu_reset, cpu);
201

202
    memory_region_init_ram(phys_ram, NULL, "ram", ram_size);
203
    vmstate_register_ram_global(phys_ram);
A
Avi Kivity 已提交
204
    memory_region_add_subregion(address_space_mem, ram_base, phys_ram);
205 206

    dinfo = drive_get(IF_PFLASH, 0, 0);
207
    pflash_cfi01_register(0xfc000000, NULL, "virtex.flash", FLASH_SIZE,
208 209
                          dinfo ? dinfo->bdrv : NULL, (64 * 1024),
                          FLASH_SIZE >> 16,
210
                          1, 0x89, 0x18, 0x0000, 0x0, 1);
211 212 213 214 215 216 217

    cpu_irq = (qemu_irq *) &env->irq_inputs[PPC40x_INPUT_INT];
    dev = xilinx_intc_create(0x81800000, cpu_irq[0], 0);
    for (i = 0; i < 32; i++) {
        irq[i] = qdev_get_gpio_in(dev, i);
    }

218 219
    serial_mm_init(address_space_mem, 0x83e01003ULL, 2, irq[9], 115200,
                   serial_hds[0], DEVICE_LITTLE_ENDIAN);
220 221

    /* 2 timers at irq 2 @ 62 Mhz.  */
222
    xilinx_timer_create(0x83c00000, irq[3], 0, 62 * 1000000);
223 224 225

    if (kernel_filename) {
        uint64_t entry, low, high;
A
Avi Kivity 已提交
226
        hwaddr boot_offset;
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264

        /* Boots a kernel elf binary.  */
        kernel_size = load_elf(kernel_filename, NULL, NULL,
                               &entry, &low, &high, 1, ELF_MACHINE, 0);
        boot_info.bootstrap_pc = entry & 0x00ffffff;

        if (kernel_size < 0) {
            boot_offset = 0x1200000;
            /* If we failed loading ELF's try a raw image.  */
            kernel_size = load_image_targphys(kernel_filename,
                                              boot_offset,
                                              ram_size);
            boot_info.bootstrap_pc = boot_offset;
            high = boot_info.bootstrap_pc + kernel_size + 8192;
        }

        boot_info.ima_size = kernel_size;

        /* Provide a device-tree.  */
        boot_info.fdt = high + (8192 * 2);
        boot_info.fdt &= ~8191;
        xilinx_load_device_tree(boot_info.fdt, ram_size, 0, 0, kernel_cmdline);
    }
    env->load_info = &boot_info;
}

static QEMUMachine virtex_machine = {
    .name = "virtex-ml507",
    .desc = "Xilinx Virtex ML507 reference design",
    .init = virtex_init,
};

static void virtex_machine_init(void)
{
    qemu_register_machine(&virtex_machine);
}

machine_init(virtex_machine_init);