qemu/hw/arm_boot.c
<<
>>
Prefs
   1/*
   2 * ARM kernel loader.
   3 *
   4 * Copyright (c) 2006-2007 CodeSourcery.
   5 * Written by Paul Brook
   6 *
   7 * This code is licensed under the GPL.
   8 */
   9
  10#include "config.h"
  11#include "hw.h"
  12#include "arm-misc.h"
  13#include "sysemu.h"
  14#include "boards.h"
  15#include "loader.h"
  16#include "elf.h"
  17#include "device_tree.h"
  18
  19#define KERNEL_ARGS_ADDR 0x100
  20#define KERNEL_LOAD_ADDR 0x00010000
  21#define INITRD_LOAD_ADDR 0x00d00000
  22
  23/* The worlds second smallest bootloader.  Set r0-r2, then jump to kernel.  */
  24static uint32_t bootloader[] = {
  25  0xe3a00000, /* mov     r0, #0 */
  26  0xe59f1004, /* ldr     r1, [pc, #4] */
  27  0xe59f2004, /* ldr     r2, [pc, #4] */
  28  0xe59ff004, /* ldr     pc, [pc, #4] */
  29  0, /* Board ID */
  30  0, /* Address of kernel args.  Set by integratorcp_init.  */
  31  0  /* Kernel entry point.  Set by integratorcp_init.  */
  32};
  33
  34/* Handling for secondary CPU boot in a multicore system.
  35 * Unlike the uniprocessor/primary CPU boot, this is platform
  36 * dependent. The default code here is based on the secondary
  37 * CPU boot protocol used on realview/vexpress boards, with
  38 * some parameterisation to increase its flexibility.
  39 * QEMU platform models for which this code is not appropriate
  40 * should override write_secondary_boot and secondary_cpu_reset_hook
  41 * instead.
  42 *
  43 * This code enables the interrupt controllers for the secondary
  44 * CPUs and then puts all the secondary CPUs into a loop waiting
  45 * for an interprocessor interrupt and polling a configurable
  46 * location for the kernel secondary CPU entry point.
  47 */
  48static uint32_t smpboot[] = {
  49  0xe59f201c, /* ldr r2, gic_cpu_if */
  50  0xe59f001c, /* ldr r0, startaddr */
  51  0xe3a01001, /* mov r1, #1 */
  52  0xe5821000, /* str r1, [r2] */
  53  0xe320f003, /* wfi */
  54  0xe5901000, /* ldr     r1, [r0] */
  55  0xe1110001, /* tst     r1, r1 */
  56  0x0afffffb, /* beq     <wfi> */
  57  0xe12fff11, /* bx      r1 */
  58  0,          /* gic_cpu_if: base address of GIC CPU interface */
  59  0           /* bootreg: Boot register address is held here */
  60};
  61
  62static void default_write_secondary(CPUARMState *env,
  63                                    const struct arm_boot_info *info)
  64{
  65    int n;
  66    smpboot[ARRAY_SIZE(smpboot) - 1] = info->smp_bootreg_addr;
  67    smpboot[ARRAY_SIZE(smpboot) - 2] = info->gic_cpu_if_addr;
  68    for (n = 0; n < ARRAY_SIZE(smpboot); n++) {
  69        smpboot[n] = tswap32(smpboot[n]);
  70    }
  71    rom_add_blob_fixed("smpboot", smpboot, sizeof(smpboot),
  72                       info->smp_loader_start);
  73}
  74
  75static void default_reset_secondary(CPUARMState *env,
  76                                    const struct arm_boot_info *info)
  77{
  78    stl_phys_notdirty(info->smp_bootreg_addr, 0);
  79    env->regs[15] = info->smp_loader_start;
  80}
  81
  82#define WRITE_WORD(p, value) do { \
  83    stl_phys_notdirty(p, value);  \
  84    p += 4;                       \
  85} while (0)
  86
  87static void set_kernel_args(const struct arm_boot_info *info)
  88{
  89    int initrd_size = info->initrd_size;
  90    target_phys_addr_t base = info->loader_start;
  91    target_phys_addr_t p;
  92
  93    p = base + KERNEL_ARGS_ADDR;
  94    /* ATAG_CORE */
  95    WRITE_WORD(p, 5);
  96    WRITE_WORD(p, 0x54410001);
  97    WRITE_WORD(p, 1);
  98    WRITE_WORD(p, 0x1000);
  99    WRITE_WORD(p, 0);
 100    /* ATAG_MEM */
 101    /* TODO: handle multiple chips on one ATAG list */
 102    WRITE_WORD(p, 4);
 103    WRITE_WORD(p, 0x54410002);
 104    WRITE_WORD(p, info->ram_size);
 105    WRITE_WORD(p, info->loader_start);
 106    if (initrd_size) {
 107        /* ATAG_INITRD2 */
 108        WRITE_WORD(p, 4);
 109        WRITE_WORD(p, 0x54420005);
 110        WRITE_WORD(p, info->loader_start + INITRD_LOAD_ADDR);
 111        WRITE_WORD(p, initrd_size);
 112    }
 113    if (info->kernel_cmdline && *info->kernel_cmdline) {
 114        /* ATAG_CMDLINE */
 115        int cmdline_size;
 116
 117        cmdline_size = strlen(info->kernel_cmdline);
 118        cpu_physical_memory_write(p + 8, (void *)info->kernel_cmdline,
 119                                  cmdline_size + 1);
 120        cmdline_size = (cmdline_size >> 2) + 1;
 121        WRITE_WORD(p, cmdline_size + 2);
 122        WRITE_WORD(p, 0x54410009);
 123        p += cmdline_size * 4;
 124    }
 125    if (info->atag_board) {
 126        /* ATAG_BOARD */
 127        int atag_board_len;
 128        uint8_t atag_board_buf[0x1000];
 129
 130        atag_board_len = (info->atag_board(info, atag_board_buf) + 3) & ~3;
 131        WRITE_WORD(p, (atag_board_len + 8) >> 2);
 132        WRITE_WORD(p, 0x414f4d50);
 133        cpu_physical_memory_write(p, atag_board_buf, atag_board_len);
 134        p += atag_board_len;
 135    }
 136    /* ATAG_END */
 137    WRITE_WORD(p, 0);
 138    WRITE_WORD(p, 0);
 139}
 140
 141static void set_kernel_args_old(const struct arm_boot_info *info)
 142{
 143    target_phys_addr_t p;
 144    const char *s;
 145    int initrd_size = info->initrd_size;
 146    target_phys_addr_t base = info->loader_start;
 147
 148    /* see linux/include/asm-arm/setup.h */
 149    p = base + KERNEL_ARGS_ADDR;
 150    /* page_size */
 151    WRITE_WORD(p, 4096);
 152    /* nr_pages */
 153    WRITE_WORD(p, info->ram_size / 4096);
 154    /* ramdisk_size */
 155    WRITE_WORD(p, 0);
 156#define FLAG_READONLY   1
 157#define FLAG_RDLOAD     4
 158#define FLAG_RDPROMPT   8
 159    /* flags */
 160    WRITE_WORD(p, FLAG_READONLY | FLAG_RDLOAD | FLAG_RDPROMPT);
 161    /* rootdev */
 162    WRITE_WORD(p, (31 << 8) | 0);       /* /dev/mtdblock0 */
 163    /* video_num_cols */
 164    WRITE_WORD(p, 0);
 165    /* video_num_rows */
 166    WRITE_WORD(p, 0);
 167    /* video_x */
 168    WRITE_WORD(p, 0);
 169    /* video_y */
 170    WRITE_WORD(p, 0);
 171    /* memc_control_reg */
 172    WRITE_WORD(p, 0);
 173    /* unsigned char sounddefault */
 174    /* unsigned char adfsdrives */
 175    /* unsigned char bytes_per_char_h */
 176    /* unsigned char bytes_per_char_v */
 177    WRITE_WORD(p, 0);
 178    /* pages_in_bank[4] */
 179    WRITE_WORD(p, 0);
 180    WRITE_WORD(p, 0);
 181    WRITE_WORD(p, 0);
 182    WRITE_WORD(p, 0);
 183    /* pages_in_vram */
 184    WRITE_WORD(p, 0);
 185    /* initrd_start */
 186    if (initrd_size)
 187        WRITE_WORD(p, info->loader_start + INITRD_LOAD_ADDR);
 188    else
 189        WRITE_WORD(p, 0);
 190    /* initrd_size */
 191    WRITE_WORD(p, initrd_size);
 192    /* rd_start */
 193    WRITE_WORD(p, 0);
 194    /* system_rev */
 195    WRITE_WORD(p, 0);
 196    /* system_serial_low */
 197    WRITE_WORD(p, 0);
 198    /* system_serial_high */
 199    WRITE_WORD(p, 0);
 200    /* mem_fclk_21285 */
 201    WRITE_WORD(p, 0);
 202    /* zero unused fields */
 203    while (p < base + KERNEL_ARGS_ADDR + 256 + 1024) {
 204        WRITE_WORD(p, 0);
 205    }
 206    s = info->kernel_cmdline;
 207    if (s) {
 208        cpu_physical_memory_write(p, (void *)s, strlen(s) + 1);
 209    } else {
 210        WRITE_WORD(p, 0);
 211    }
 212}
 213
 214static int load_dtb(target_phys_addr_t addr, const struct arm_boot_info *binfo)
 215{
 216#ifdef CONFIG_FDT
 217    uint32_t mem_reg_property[] = { cpu_to_be32(binfo->loader_start),
 218                                    cpu_to_be32(binfo->ram_size) };
 219    void *fdt = NULL;
 220    char *filename;
 221    int size, rc;
 222
 223    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, binfo->dtb_filename);
 224    if (!filename) {
 225        fprintf(stderr, "Couldn't open dtb file %s\n", binfo->dtb_filename);
 226        return -1;
 227    }
 228
 229    fdt = load_device_tree(filename, &size);
 230    if (!fdt) {
 231        fprintf(stderr, "Couldn't open dtb file %s\n", filename);
 232        g_free(filename);
 233        return -1;
 234    }
 235    g_free(filename);
 236
 237    rc = qemu_devtree_setprop(fdt, "/memory", "reg", mem_reg_property,
 238                               sizeof(mem_reg_property));
 239    if (rc < 0) {
 240        fprintf(stderr, "couldn't set /memory/reg\n");
 241    }
 242
 243    rc = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs",
 244                                      binfo->kernel_cmdline);
 245    if (rc < 0) {
 246        fprintf(stderr, "couldn't set /chosen/bootargs\n");
 247    }
 248
 249    if (binfo->initrd_size) {
 250        rc = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start",
 251                binfo->loader_start + INITRD_LOAD_ADDR);
 252        if (rc < 0) {
 253            fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n");
 254        }
 255
 256        rc = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-end",
 257                    binfo->loader_start + INITRD_LOAD_ADDR +
 258                    binfo->initrd_size);
 259        if (rc < 0) {
 260            fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n");
 261        }
 262    }
 263
 264    cpu_physical_memory_write(addr, fdt, size);
 265
 266    return 0;
 267
 268#else
 269    fprintf(stderr, "Device tree requested, "
 270                "but qemu was compiled without fdt support\n");
 271    return -1;
 272#endif
 273}
 274
 275static void do_cpu_reset(void *opaque)
 276{
 277    CPUARMState *env = opaque;
 278    const struct arm_boot_info *info = env->boot_info;
 279
 280    cpu_state_reset(env);
 281    if (info) {
 282        if (!info->is_linux) {
 283            /* Jump to the entry point.  */
 284            env->regs[15] = info->entry & 0xfffffffe;
 285            env->thumb = info->entry & 1;
 286        } else {
 287            if (env == first_cpu) {
 288                env->regs[15] = info->loader_start;
 289                if (!info->dtb_filename) {
 290                    if (old_param) {
 291                        set_kernel_args_old(info);
 292                    } else {
 293                        set_kernel_args(info);
 294                    }
 295                }
 296            } else {
 297                info->secondary_cpu_reset_hook(env, info);
 298            }
 299        }
 300    }
 301}
 302
 303void arm_load_kernel(CPUARMState *env, struct arm_boot_info *info)
 304{
 305    int kernel_size;
 306    int initrd_size;
 307    int n;
 308    int is_linux = 0;
 309    uint64_t elf_entry;
 310    target_phys_addr_t entry;
 311    int big_endian;
 312    QemuOpts *machine_opts;
 313
 314    /* Load the kernel.  */
 315    if (!info->kernel_filename) {
 316        fprintf(stderr, "Kernel image must be specified\n");
 317        exit(1);
 318    }
 319
 320    machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0);
 321    if (machine_opts) {
 322        info->dtb_filename = qemu_opt_get(machine_opts, "dtb");
 323    } else {
 324        info->dtb_filename = NULL;
 325    }
 326
 327    if (!info->secondary_cpu_reset_hook) {
 328        info->secondary_cpu_reset_hook = default_reset_secondary;
 329    }
 330    if (!info->write_secondary_boot) {
 331        info->write_secondary_boot = default_write_secondary;
 332    }
 333
 334    if (info->nb_cpus == 0)
 335        info->nb_cpus = 1;
 336
 337#ifdef TARGET_WORDS_BIGENDIAN
 338    big_endian = 1;
 339#else
 340    big_endian = 0;
 341#endif
 342
 343    /* Assume that raw images are linux kernels, and ELF images are not.  */
 344    kernel_size = load_elf(info->kernel_filename, NULL, NULL, &elf_entry,
 345                           NULL, NULL, big_endian, ELF_MACHINE, 1);
 346    entry = elf_entry;
 347    if (kernel_size < 0) {
 348        kernel_size = load_uimage(info->kernel_filename, &entry, NULL,
 349                                  &is_linux);
 350    }
 351    if (kernel_size < 0) {
 352        entry = info->loader_start + KERNEL_LOAD_ADDR;
 353        kernel_size = load_image_targphys(info->kernel_filename, entry,
 354                                          ram_size - KERNEL_LOAD_ADDR);
 355        is_linux = 1;
 356    }
 357    if (kernel_size < 0) {
 358        fprintf(stderr, "qemu: could not load kernel '%s'\n",
 359                info->kernel_filename);
 360        exit(1);
 361    }
 362    info->entry = entry;
 363    if (is_linux) {
 364        if (info->initrd_filename) {
 365            initrd_size = load_image_targphys(info->initrd_filename,
 366                                              info->loader_start
 367                                              + INITRD_LOAD_ADDR,
 368                                              ram_size - INITRD_LOAD_ADDR);
 369            if (initrd_size < 0) {
 370                fprintf(stderr, "qemu: could not load initrd '%s'\n",
 371                        info->initrd_filename);
 372                exit(1);
 373            }
 374        } else {
 375            initrd_size = 0;
 376        }
 377        info->initrd_size = initrd_size;
 378
 379        bootloader[4] = info->board_id;
 380
 381        /* for device tree boot, we pass the DTB directly in r2. Otherwise
 382         * we point to the kernel args.
 383         */
 384        if (info->dtb_filename) {
 385            /* Place the DTB after the initrd in memory */
 386            target_phys_addr_t dtb_start = TARGET_PAGE_ALIGN(info->loader_start
 387                                                             + INITRD_LOAD_ADDR
 388                                                             + initrd_size);
 389            if (load_dtb(dtb_start, info)) {
 390                exit(1);
 391            }
 392            bootloader[5] = dtb_start;
 393        } else {
 394            bootloader[5] = info->loader_start + KERNEL_ARGS_ADDR;
 395        }
 396        bootloader[6] = entry;
 397        for (n = 0; n < sizeof(bootloader) / 4; n++) {
 398            bootloader[n] = tswap32(bootloader[n]);
 399        }
 400        rom_add_blob_fixed("bootloader", bootloader, sizeof(bootloader),
 401                           info->loader_start);
 402        if (info->nb_cpus > 1) {
 403            info->write_secondary_boot(env, info);
 404        }
 405    }
 406    info->is_linux = is_linux;
 407
 408    for (; env; env = env->next_cpu) {
 409        env->boot_info = info;
 410        qemu_register_reset(do_cpu_reset, env);
 411    }
 412}
 413