| File: | hw/arm/boot.c |
| Location: | line 137, column 18 |
| Description: | Assigned value is garbage or undefined |
| 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/hw.h" | |||
| 12 | #include "hw/arm/arm.h" | |||
| 13 | #include "sysemu/sysemu.h" | |||
| 14 | #include "hw/boards.h" | |||
| 15 | #include "hw/loader.h" | |||
| 16 | #include "elf.h" | |||
| 17 | #include "sysemu/device_tree.h" | |||
| 18 | #include "qemu/config-file.h" | |||
| 19 | ||||
| 20 | /* Kernel boot protocol is specified in the kernel docs | |||
| 21 | * Documentation/arm/Booting and Documentation/arm64/booting.txt | |||
| 22 | * They have different preferred image load offsets from system RAM base. | |||
| 23 | */ | |||
| 24 | #define KERNEL_ARGS_ADDR0x100 0x100 | |||
| 25 | #define KERNEL_LOAD_ADDR0x00010000 0x00010000 | |||
| 26 | #define KERNEL64_LOAD_ADDR0x00080000 0x00080000 | |||
| 27 | ||||
| 28 | typedef enum { | |||
| 29 | FIXUP_NONE = 0, /* do nothing */ | |||
| 30 | FIXUP_TERMINATOR, /* end of insns */ | |||
| 31 | FIXUP_BOARDID, /* overwrite with board ID number */ | |||
| 32 | FIXUP_ARGPTR, /* overwrite with pointer to kernel args */ | |||
| 33 | FIXUP_ENTRYPOINT, /* overwrite with kernel entry point */ | |||
| 34 | FIXUP_GIC_CPU_IF, /* overwrite with GIC CPU interface address */ | |||
| 35 | FIXUP_BOOTREG, /* overwrite with boot register address */ | |||
| 36 | FIXUP_DSB, /* overwrite with correct DSB insn for cpu */ | |||
| 37 | FIXUP_MAX, | |||
| 38 | } FixupType; | |||
| 39 | ||||
| 40 | typedef struct ARMInsnFixup { | |||
| 41 | uint32_t insn; | |||
| 42 | FixupType fixup; | |||
| 43 | } ARMInsnFixup; | |||
| 44 | ||||
| 45 | static const ARMInsnFixup bootloader_aarch64[] = { | |||
| 46 | { 0x580000c0 }, /* ldr x0, arg ; Load the lower 32-bits of DTB */ | |||
| 47 | { 0xaa1f03e1 }, /* mov x1, xzr */ | |||
| 48 | { 0xaa1f03e2 }, /* mov x2, xzr */ | |||
| 49 | { 0xaa1f03e3 }, /* mov x3, xzr */ | |||
| 50 | { 0x58000084 }, /* ldr x4, entry ; Load the lower 32-bits of kernel entry */ | |||
| 51 | { 0xd61f0080 }, /* br x4 ; Jump to the kernel entry point */ | |||
| 52 | { 0, FIXUP_ARGPTR }, /* arg: .word @DTB Lower 32-bits */ | |||
| 53 | { 0 }, /* .word @DTB Higher 32-bits */ | |||
| 54 | { 0, FIXUP_ENTRYPOINT }, /* entry: .word @Kernel Entry Lower 32-bits */ | |||
| 55 | { 0 }, /* .word @Kernel Entry Higher 32-bits */ | |||
| 56 | { 0, FIXUP_TERMINATOR } | |||
| 57 | }; | |||
| 58 | ||||
| 59 | /* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */ | |||
| 60 | static const ARMInsnFixup bootloader[] = { | |||
| 61 | { 0xe3a00000 }, /* mov r0, #0 */ | |||
| 62 | { 0xe59f1004 }, /* ldr r1, [pc, #4] */ | |||
| 63 | { 0xe59f2004 }, /* ldr r2, [pc, #4] */ | |||
| 64 | { 0xe59ff004 }, /* ldr pc, [pc, #4] */ | |||
| 65 | { 0, FIXUP_BOARDID }, | |||
| 66 | { 0, FIXUP_ARGPTR }, | |||
| 67 | { 0, FIXUP_ENTRYPOINT }, | |||
| 68 | { 0, FIXUP_TERMINATOR } | |||
| 69 | }; | |||
| 70 | ||||
| 71 | /* Handling for secondary CPU boot in a multicore system. | |||
| 72 | * Unlike the uniprocessor/primary CPU boot, this is platform | |||
| 73 | * dependent. The default code here is based on the secondary | |||
| 74 | * CPU boot protocol used on realview/vexpress boards, with | |||
| 75 | * some parameterisation to increase its flexibility. | |||
| 76 | * QEMU platform models for which this code is not appropriate | |||
| 77 | * should override write_secondary_boot and secondary_cpu_reset_hook | |||
| 78 | * instead. | |||
| 79 | * | |||
| 80 | * This code enables the interrupt controllers for the secondary | |||
| 81 | * CPUs and then puts all the secondary CPUs into a loop waiting | |||
| 82 | * for an interprocessor interrupt and polling a configurable | |||
| 83 | * location for the kernel secondary CPU entry point. | |||
| 84 | */ | |||
| 85 | #define DSB_INSN0xf57ff04f 0xf57ff04f | |||
| 86 | #define CP15_DSB_INSN0xee070f9a 0xee070f9a /* mcr cp15, 0, r0, c7, c10, 4 */ | |||
| 87 | ||||
| 88 | static const ARMInsnFixup smpboot[] = { | |||
| 89 | { 0xe59f2028 }, /* ldr r2, gic_cpu_if */ | |||
| 90 | { 0xe59f0028 }, /* ldr r0, bootreg_addr */ | |||
| 91 | { 0xe3a01001 }, /* mov r1, #1 */ | |||
| 92 | { 0xe5821000 }, /* str r1, [r2] - set GICC_CTLR.Enable */ | |||
| 93 | { 0xe3a010ff }, /* mov r1, #0xff */ | |||
| 94 | { 0xe5821004 }, /* str r1, [r2, 4] - set GIC_PMR.Priority to 0xff */ | |||
| 95 | { 0, FIXUP_DSB }, /* dsb */ | |||
| 96 | { 0xe320f003 }, /* wfi */ | |||
| 97 | { 0xe5901000 }, /* ldr r1, [r0] */ | |||
| 98 | { 0xe1110001 }, /* tst r1, r1 */ | |||
| 99 | { 0x0afffffb }, /* beq <wfi> */ | |||
| 100 | { 0xe12fff11 }, /* bx r1 */ | |||
| 101 | { 0, FIXUP_GIC_CPU_IF }, /* gic_cpu_if: .word 0x.... */ | |||
| 102 | { 0, FIXUP_BOOTREG }, /* bootreg_addr: .word 0x.... */ | |||
| 103 | { 0, FIXUP_TERMINATOR } | |||
| 104 | }; | |||
| 105 | ||||
| 106 | static void write_bootloader(const char *name, hwaddr addr, | |||
| 107 | const ARMInsnFixup *insns, uint32_t *fixupcontext) | |||
| 108 | { | |||
| 109 | /* Fix up the specified bootloader fragment and write it into | |||
| 110 | * guest memory using rom_add_blob_fixed(). fixupcontext is | |||
| 111 | * an array giving the values to write in for the fixup types | |||
| 112 | * which write a value into the code array. | |||
| 113 | */ | |||
| 114 | int i, len; | |||
| 115 | uint32_t *code; | |||
| 116 | ||||
| 117 | len = 0; | |||
| 118 | while (insns[len].fixup != FIXUP_TERMINATOR) { | |||
| 119 | len++; | |||
| 120 | } | |||
| 121 | ||||
| 122 | code = g_new0(uint32_t, len)((uint32_t *) g_malloc0_n ((len), sizeof (uint32_t))); | |||
| 123 | ||||
| 124 | for (i = 0; i < len; i++) { | |||
| 125 | uint32_t insn = insns[i].insn; | |||
| 126 | FixupType fixup = insns[i].fixup; | |||
| 127 | ||||
| 128 | switch (fixup) { | |||
| 129 | case FIXUP_NONE: | |||
| 130 | break; | |||
| 131 | case FIXUP_BOARDID: | |||
| 132 | case FIXUP_ARGPTR: | |||
| 133 | case FIXUP_ENTRYPOINT: | |||
| 134 | case FIXUP_GIC_CPU_IF: | |||
| 135 | case FIXUP_BOOTREG: | |||
| 136 | case FIXUP_DSB: | |||
| 137 | insn = fixupcontext[fixup]; | |||
| ||||
| 138 | break; | |||
| 139 | default: | |||
| 140 | abort(); | |||
| 141 | } | |||
| 142 | code[i] = tswap32(insn); | |||
| 143 | } | |||
| 144 | ||||
| 145 | rom_add_blob_fixed(name, code, len * sizeof(uint32_t), addr)rom_add_blob(name, code, len * sizeof(uint32_t), addr, ((void *)0), ((void*)0), ((void*)0)); | |||
| 146 | ||||
| 147 | g_free(code); | |||
| 148 | } | |||
| 149 | ||||
| 150 | static void default_write_secondary(ARMCPU *cpu, | |||
| 151 | const struct arm_boot_info *info) | |||
| 152 | { | |||
| 153 | uint32_t fixupcontext[FIXUP_MAX]; | |||
| 154 | ||||
| 155 | fixupcontext[FIXUP_GIC_CPU_IF] = info->gic_cpu_if_addr; | |||
| 156 | fixupcontext[FIXUP_BOOTREG] = info->smp_bootreg_addr; | |||
| 157 | if (arm_feature(&cpu->env, ARM_FEATURE_V7)) { | |||
| 158 | fixupcontext[FIXUP_DSB] = DSB_INSN0xf57ff04f; | |||
| 159 | } else { | |||
| 160 | fixupcontext[FIXUP_DSB] = CP15_DSB_INSN0xee070f9a; | |||
| 161 | } | |||
| 162 | ||||
| 163 | write_bootloader("smpboot", info->smp_loader_start, | |||
| 164 | smpboot, fixupcontext); | |||
| 165 | } | |||
| 166 | ||||
| 167 | static void default_reset_secondary(ARMCPU *cpu, | |||
| 168 | const struct arm_boot_info *info) | |||
| 169 | { | |||
| 170 | CPUARMState *env = &cpu->env; | |||
| 171 | ||||
| 172 | stl_phys_notdirty(info->smp_bootreg_addr, 0); | |||
| 173 | env->regs[15] = info->smp_loader_start; | |||
| 174 | } | |||
| 175 | ||||
| 176 | #define WRITE_WORD(p, value)do { stl_phys_notdirty(p, value); p += 4; } while (0) do { \ | |||
| 177 | stl_phys_notdirty(p, value); \ | |||
| 178 | p += 4; \ | |||
| 179 | } while (0) | |||
| 180 | ||||
| 181 | static void set_kernel_args(const struct arm_boot_info *info) | |||
| 182 | { | |||
| 183 | int initrd_size = info->initrd_size; | |||
| 184 | hwaddr base = info->loader_start; | |||
| 185 | hwaddr p; | |||
| 186 | ||||
| 187 | p = base + KERNEL_ARGS_ADDR0x100; | |||
| 188 | /* ATAG_CORE */ | |||
| 189 | WRITE_WORD(p, 5)do { stl_phys_notdirty(p, 5); p += 4; } while (0); | |||
| 190 | WRITE_WORD(p, 0x54410001)do { stl_phys_notdirty(p, 0x54410001); p += 4; } while (0); | |||
| 191 | WRITE_WORD(p, 1)do { stl_phys_notdirty(p, 1); p += 4; } while (0); | |||
| 192 | WRITE_WORD(p, 0x1000)do { stl_phys_notdirty(p, 0x1000); p += 4; } while (0); | |||
| 193 | WRITE_WORD(p, 0)do { stl_phys_notdirty(p, 0); p += 4; } while (0); | |||
| 194 | /* ATAG_MEM */ | |||
| 195 | /* TODO: handle multiple chips on one ATAG list */ | |||
| 196 | WRITE_WORD(p, 4)do { stl_phys_notdirty(p, 4); p += 4; } while (0); | |||
| 197 | WRITE_WORD(p, 0x54410002)do { stl_phys_notdirty(p, 0x54410002); p += 4; } while (0); | |||
| 198 | WRITE_WORD(p, info->ram_size)do { stl_phys_notdirty(p, info->ram_size); p += 4; } while (0); | |||
| 199 | WRITE_WORD(p, info->loader_start)do { stl_phys_notdirty(p, info->loader_start); p += 4; } while (0); | |||
| 200 | if (initrd_size) { | |||
| 201 | /* ATAG_INITRD2 */ | |||
| 202 | WRITE_WORD(p, 4)do { stl_phys_notdirty(p, 4); p += 4; } while (0); | |||
| 203 | WRITE_WORD(p, 0x54420005)do { stl_phys_notdirty(p, 0x54420005); p += 4; } while (0); | |||
| 204 | WRITE_WORD(p, info->initrd_start)do { stl_phys_notdirty(p, info->initrd_start); p += 4; } while (0); | |||
| 205 | WRITE_WORD(p, initrd_size)do { stl_phys_notdirty(p, initrd_size); p += 4; } while (0); | |||
| 206 | } | |||
| 207 | if (info->kernel_cmdline && *info->kernel_cmdline) { | |||
| 208 | /* ATAG_CMDLINE */ | |||
| 209 | int cmdline_size; | |||
| 210 | ||||
| 211 | cmdline_size = strlen(info->kernel_cmdline); | |||
| 212 | cpu_physical_memory_write(p + 8, info->kernel_cmdline, | |||
| 213 | cmdline_size + 1); | |||
| 214 | cmdline_size = (cmdline_size >> 2) + 1; | |||
| 215 | WRITE_WORD(p, cmdline_size + 2)do { stl_phys_notdirty(p, cmdline_size + 2); p += 4; } while ( 0); | |||
| 216 | WRITE_WORD(p, 0x54410009)do { stl_phys_notdirty(p, 0x54410009); p += 4; } while (0); | |||
| 217 | p += cmdline_size * 4; | |||
| 218 | } | |||
| 219 | if (info->atag_board) { | |||
| 220 | /* ATAG_BOARD */ | |||
| 221 | int atag_board_len; | |||
| 222 | uint8_t atag_board_buf[0x1000]; | |||
| 223 | ||||
| 224 | atag_board_len = (info->atag_board(info, atag_board_buf) + 3) & ~3; | |||
| 225 | WRITE_WORD(p, (atag_board_len + 8) >> 2)do { stl_phys_notdirty(p, (atag_board_len + 8) >> 2); p += 4; } while (0); | |||
| 226 | WRITE_WORD(p, 0x414f4d50)do { stl_phys_notdirty(p, 0x414f4d50); p += 4; } while (0); | |||
| 227 | cpu_physical_memory_write(p, atag_board_buf, atag_board_len); | |||
| 228 | p += atag_board_len; | |||
| 229 | } | |||
| 230 | /* ATAG_END */ | |||
| 231 | WRITE_WORD(p, 0)do { stl_phys_notdirty(p, 0); p += 4; } while (0); | |||
| 232 | WRITE_WORD(p, 0)do { stl_phys_notdirty(p, 0); p += 4; } while (0); | |||
| 233 | } | |||
| 234 | ||||
| 235 | static void set_kernel_args_old(const struct arm_boot_info *info) | |||
| 236 | { | |||
| 237 | hwaddr p; | |||
| 238 | const char *s; | |||
| 239 | int initrd_size = info->initrd_size; | |||
| 240 | hwaddr base = info->loader_start; | |||
| 241 | ||||
| 242 | /* see linux/include/asm-arm/setup.h */ | |||
| 243 | p = base + KERNEL_ARGS_ADDR0x100; | |||
| 244 | /* page_size */ | |||
| 245 | WRITE_WORD(p, 4096)do { stl_phys_notdirty(p, 4096); p += 4; } while (0); | |||
| 246 | /* nr_pages */ | |||
| 247 | WRITE_WORD(p, info->ram_size / 4096)do { stl_phys_notdirty(p, info->ram_size / 4096); p += 4; } while (0); | |||
| 248 | /* ramdisk_size */ | |||
| 249 | WRITE_WORD(p, 0)do { stl_phys_notdirty(p, 0); p += 4; } while (0); | |||
| 250 | #define FLAG_READONLY1 1 | |||
| 251 | #define FLAG_RDLOAD4 4 | |||
| 252 | #define FLAG_RDPROMPT8 8 | |||
| 253 | /* flags */ | |||
| 254 | WRITE_WORD(p, FLAG_READONLY | FLAG_RDLOAD | FLAG_RDPROMPT)do { stl_phys_notdirty(p, 1 | 4 | 8); p += 4; } while (0); | |||
| 255 | /* rootdev */ | |||
| 256 | WRITE_WORD(p, (31 << 8) | 0)do { stl_phys_notdirty(p, (31 << 8) | 0); p += 4; } while (0); /* /dev/mtdblock0 */ | |||
| 257 | /* video_num_cols */ | |||
| 258 | WRITE_WORD(p, 0)do { stl_phys_notdirty(p, 0); p += 4; } while (0); | |||
| 259 | /* video_num_rows */ | |||
| 260 | WRITE_WORD(p, 0)do { stl_phys_notdirty(p, 0); p += 4; } while (0); | |||
| 261 | /* video_x */ | |||
| 262 | WRITE_WORD(p, 0)do { stl_phys_notdirty(p, 0); p += 4; } while (0); | |||
| 263 | /* video_y */ | |||
| 264 | WRITE_WORD(p, 0)do { stl_phys_notdirty(p, 0); p += 4; } while (0); | |||
| 265 | /* memc_control_reg */ | |||
| 266 | WRITE_WORD(p, 0)do { stl_phys_notdirty(p, 0); p += 4; } while (0); | |||
| 267 | /* unsigned char sounddefault */ | |||
| 268 | /* unsigned char adfsdrives */ | |||
| 269 | /* unsigned char bytes_per_char_h */ | |||
| 270 | /* unsigned char bytes_per_char_v */ | |||
| 271 | WRITE_WORD(p, 0)do { stl_phys_notdirty(p, 0); p += 4; } while (0); | |||
| 272 | /* pages_in_bank[4] */ | |||
| 273 | WRITE_WORD(p, 0)do { stl_phys_notdirty(p, 0); p += 4; } while (0); | |||
| 274 | WRITE_WORD(p, 0)do { stl_phys_notdirty(p, 0); p += 4; } while (0); | |||
| 275 | WRITE_WORD(p, 0)do { stl_phys_notdirty(p, 0); p += 4; } while (0); | |||
| 276 | WRITE_WORD(p, 0)do { stl_phys_notdirty(p, 0); p += 4; } while (0); | |||
| 277 | /* pages_in_vram */ | |||
| 278 | WRITE_WORD(p, 0)do { stl_phys_notdirty(p, 0); p += 4; } while (0); | |||
| 279 | /* initrd_start */ | |||
| 280 | if (initrd_size) { | |||
| 281 | WRITE_WORD(p, info->initrd_start)do { stl_phys_notdirty(p, info->initrd_start); p += 4; } while (0); | |||
| 282 | } else { | |||
| 283 | WRITE_WORD(p, 0)do { stl_phys_notdirty(p, 0); p += 4; } while (0); | |||
| 284 | } | |||
| 285 | /* initrd_size */ | |||
| 286 | WRITE_WORD(p, initrd_size)do { stl_phys_notdirty(p, initrd_size); p += 4; } while (0); | |||
| 287 | /* rd_start */ | |||
| 288 | WRITE_WORD(p, 0)do { stl_phys_notdirty(p, 0); p += 4; } while (0); | |||
| 289 | /* system_rev */ | |||
| 290 | WRITE_WORD(p, 0)do { stl_phys_notdirty(p, 0); p += 4; } while (0); | |||
| 291 | /* system_serial_low */ | |||
| 292 | WRITE_WORD(p, 0)do { stl_phys_notdirty(p, 0); p += 4; } while (0); | |||
| 293 | /* system_serial_high */ | |||
| 294 | WRITE_WORD(p, 0)do { stl_phys_notdirty(p, 0); p += 4; } while (0); | |||
| 295 | /* mem_fclk_21285 */ | |||
| 296 | WRITE_WORD(p, 0)do { stl_phys_notdirty(p, 0); p += 4; } while (0); | |||
| 297 | /* zero unused fields */ | |||
| 298 | while (p < base + KERNEL_ARGS_ADDR0x100 + 256 + 1024) { | |||
| 299 | WRITE_WORD(p, 0)do { stl_phys_notdirty(p, 0); p += 4; } while (0); | |||
| 300 | } | |||
| 301 | s = info->kernel_cmdline; | |||
| 302 | if (s) { | |||
| 303 | cpu_physical_memory_write(p, s, strlen(s) + 1); | |||
| 304 | } else { | |||
| 305 | WRITE_WORD(p, 0)do { stl_phys_notdirty(p, 0); p += 4; } while (0); | |||
| 306 | } | |||
| 307 | } | |||
| 308 | ||||
| 309 | static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo) | |||
| 310 | { | |||
| 311 | void *fdt = NULL((void*)0); | |||
| 312 | int size, rc; | |||
| 313 | uint32_t acells, scells; | |||
| 314 | ||||
| 315 | if (binfo->dtb_filename) { | |||
| 316 | char *filename; | |||
| 317 | filename = qemu_find_file(QEMU_FILE_TYPE_BIOS0, binfo->dtb_filename); | |||
| 318 | if (!filename) { | |||
| 319 | fprintf(stderrstderr, "Couldn't open dtb file %s\n", binfo->dtb_filename); | |||
| 320 | goto fail; | |||
| 321 | } | |||
| 322 | ||||
| 323 | fdt = load_device_tree(filename, &size); | |||
| 324 | if (!fdt) { | |||
| 325 | fprintf(stderrstderr, "Couldn't open dtb file %s\n", filename); | |||
| 326 | g_free(filename); | |||
| 327 | goto fail; | |||
| 328 | } | |||
| 329 | g_free(filename); | |||
| 330 | } else if (binfo->get_dtb) { | |||
| 331 | fdt = binfo->get_dtb(binfo, &size); | |||
| 332 | if (!fdt) { | |||
| 333 | fprintf(stderrstderr, "Board was unable to create a dtb blob\n"); | |||
| 334 | goto fail; | |||
| 335 | } | |||
| 336 | } | |||
| 337 | ||||
| 338 | acells = qemu_fdt_getprop_cell(fdt, "/", "#address-cells"); | |||
| 339 | scells = qemu_fdt_getprop_cell(fdt, "/", "#size-cells"); | |||
| 340 | if (acells == 0 || scells == 0) { | |||
| 341 | fprintf(stderrstderr, "dtb file invalid (#address-cells or #size-cells 0)\n"); | |||
| 342 | goto fail; | |||
| 343 | } | |||
| 344 | ||||
| 345 | if (scells < 2 && binfo->ram_size >= (1ULL << 32)) { | |||
| 346 | /* This is user error so deserves a friendlier error message | |||
| 347 | * than the failure of setprop_sized_cells would provide | |||
| 348 | */ | |||
| 349 | fprintf(stderrstderr, "qemu: dtb file not compatible with " | |||
| 350 | "RAM size > 4GB\n"); | |||
| 351 | goto fail; | |||
| 352 | } | |||
| 353 | ||||
| 354 | rc = qemu_fdt_setprop_sized_cells(fdt, "/memory", "reg",({ uint64_t qdt_tmp[] = { acells, binfo->loader_start, scells , binfo->ram_size }; qemu_fdt_setprop_sized_cells_from_array (fdt, "/memory", "reg", (sizeof(qdt_tmp) / sizeof((qdt_tmp)[0 ])) / 2, qdt_tmp); }) | |||
| 355 | acells, binfo->loader_start,({ uint64_t qdt_tmp[] = { acells, binfo->loader_start, scells , binfo->ram_size }; qemu_fdt_setprop_sized_cells_from_array (fdt, "/memory", "reg", (sizeof(qdt_tmp) / sizeof((qdt_tmp)[0 ])) / 2, qdt_tmp); }) | |||
| 356 | scells, binfo->ram_size)({ uint64_t qdt_tmp[] = { acells, binfo->loader_start, scells , binfo->ram_size }; qemu_fdt_setprop_sized_cells_from_array (fdt, "/memory", "reg", (sizeof(qdt_tmp) / sizeof((qdt_tmp)[0 ])) / 2, qdt_tmp); }); | |||
| 357 | if (rc < 0) { | |||
| 358 | fprintf(stderrstderr, "couldn't set /memory/reg\n"); | |||
| 359 | goto fail; | |||
| 360 | } | |||
| 361 | ||||
| 362 | if (binfo->kernel_cmdline && *binfo->kernel_cmdline) { | |||
| 363 | rc = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", | |||
| 364 | binfo->kernel_cmdline); | |||
| 365 | if (rc < 0) { | |||
| 366 | fprintf(stderrstderr, "couldn't set /chosen/bootargs\n"); | |||
| 367 | goto fail; | |||
| 368 | } | |||
| 369 | } | |||
| 370 | ||||
| 371 | if (binfo->initrd_size) { | |||
| 372 | rc = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", | |||
| 373 | binfo->initrd_start); | |||
| 374 | if (rc < 0) { | |||
| 375 | fprintf(stderrstderr, "couldn't set /chosen/linux,initrd-start\n"); | |||
| 376 | goto fail; | |||
| 377 | } | |||
| 378 | ||||
| 379 | rc = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", | |||
| 380 | binfo->initrd_start + binfo->initrd_size); | |||
| 381 | if (rc < 0) { | |||
| 382 | fprintf(stderrstderr, "couldn't set /chosen/linux,initrd-end\n"); | |||
| 383 | goto fail; | |||
| 384 | } | |||
| 385 | } | |||
| 386 | ||||
| 387 | if (binfo->modify_dtb) { | |||
| 388 | binfo->modify_dtb(binfo, fdt); | |||
| 389 | } | |||
| 390 | ||||
| 391 | qemu_fdt_dumpdtb(fdt, size); | |||
| 392 | ||||
| 393 | cpu_physical_memory_write(addr, fdt, size); | |||
| 394 | ||||
| 395 | g_free(fdt); | |||
| 396 | ||||
| 397 | return 0; | |||
| 398 | ||||
| 399 | fail: | |||
| 400 | g_free(fdt); | |||
| 401 | return -1; | |||
| 402 | } | |||
| 403 | ||||
| 404 | static void do_cpu_reset(void *opaque) | |||
| 405 | { | |||
| 406 | ARMCPU *cpu = opaque; | |||
| 407 | CPUARMState *env = &cpu->env; | |||
| 408 | const struct arm_boot_info *info = env->boot_info; | |||
| 409 | ||||
| 410 | cpu_reset(CPU(cpu)((CPUState *)object_dynamic_cast_assert(((Object *)((cpu))), ( "cpu"), "/home/stefan/src/qemu/qemu.org/qemu/hw/arm/boot.c", 410 , __func__))); | |||
| 411 | if (info) { | |||
| 412 | if (!info->is_linux) { | |||
| 413 | /* Jump to the entry point. */ | |||
| 414 | env->regs[15] = info->entry & 0xfffffffe; | |||
| 415 | env->thumb = info->entry & 1; | |||
| 416 | } else { | |||
| 417 | if (CPU(cpu)((CPUState *)object_dynamic_cast_assert(((Object *)((cpu))), ( "cpu"), "/home/stefan/src/qemu/qemu.org/qemu/hw/arm/boot.c", 417 , __func__)) == first_cpu((&cpus)->tqh_first)) { | |||
| 418 | if (env->aarch64) { | |||
| 419 | env->pc = info->loader_start; | |||
| 420 | } else { | |||
| 421 | env->regs[15] = info->loader_start; | |||
| 422 | } | |||
| 423 | ||||
| 424 | if (!info->dtb_filename) { | |||
| 425 | if (old_param) { | |||
| 426 | set_kernel_args_old(info); | |||
| 427 | } else { | |||
| 428 | set_kernel_args(info); | |||
| 429 | } | |||
| 430 | } | |||
| 431 | } else { | |||
| 432 | info->secondary_cpu_reset_hook(cpu, info); | |||
| 433 | } | |||
| 434 | } | |||
| 435 | } | |||
| 436 | } | |||
| 437 | ||||
| 438 | void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) | |||
| 439 | { | |||
| 440 | CPUState *cs = CPU(cpu)((CPUState *)object_dynamic_cast_assert(((Object *)((cpu))), ( "cpu"), "/home/stefan/src/qemu/qemu.org/qemu/hw/arm/boot.c", 440 , __func__)); | |||
| 441 | int kernel_size; | |||
| 442 | int initrd_size; | |||
| 443 | int is_linux = 0; | |||
| 444 | uint64_t elf_entry; | |||
| 445 | hwaddr entry, kernel_load_offset; | |||
| 446 | int big_endian; | |||
| 447 | static const ARMInsnFixup *primary_loader; | |||
| 448 | ||||
| 449 | /* Load the kernel. */ | |||
| 450 | if (!info->kernel_filename) { | |||
| ||||
| 451 | /* If no kernel specified, do nothing; we will start from address 0 | |||
| 452 | * (typically a boot ROM image) in the same way as hardware. | |||
| 453 | */ | |||
| 454 | return; | |||
| 455 | } | |||
| 456 | ||||
| 457 | if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { | |||
| 458 | primary_loader = bootloader_aarch64; | |||
| 459 | kernel_load_offset = KERNEL64_LOAD_ADDR0x00080000; | |||
| 460 | } else { | |||
| 461 | primary_loader = bootloader; | |||
| 462 | kernel_load_offset = KERNEL_LOAD_ADDR0x00010000; | |||
| 463 | } | |||
| 464 | ||||
| 465 | info->dtb_filename = qemu_opt_get(qemu_get_machine_opts(), "dtb"); | |||
| 466 | ||||
| 467 | if (!info->secondary_cpu_reset_hook) { | |||
| 468 | info->secondary_cpu_reset_hook = default_reset_secondary; | |||
| 469 | } | |||
| 470 | if (!info->write_secondary_boot) { | |||
| 471 | info->write_secondary_boot = default_write_secondary; | |||
| 472 | } | |||
| 473 | ||||
| 474 | if (info->nb_cpus == 0) | |||
| 475 | info->nb_cpus = 1; | |||
| 476 | ||||
| 477 | #ifdef TARGET_WORDS_BIGENDIAN | |||
| 478 | big_endian = 1; | |||
| 479 | #else | |||
| 480 | big_endian = 0; | |||
| 481 | #endif | |||
| 482 | ||||
| 483 | /* We want to put the initrd far enough into RAM that when the | |||
| 484 | * kernel is uncompressed it will not clobber the initrd. However | |||
| 485 | * on boards without much RAM we must ensure that we still leave | |||
| 486 | * enough room for a decent sized initrd, and on boards with large | |||
| 487 | * amounts of RAM we must avoid the initrd being so far up in RAM | |||
| 488 | * that it is outside lowmem and inaccessible to the kernel. | |||
| 489 | * So for boards with less than 256MB of RAM we put the initrd | |||
| 490 | * halfway into RAM, and for boards with 256MB of RAM or more we put | |||
| 491 | * the initrd at 128MB. | |||
| 492 | */ | |||
| 493 | info->initrd_start = info->loader_start + | |||
| 494 | MIN(info->ram_size / 2, 128 * 1024 * 1024)(((info->ram_size / 2) < (128 * 1024 * 1024)) ? (info-> ram_size / 2) : (128 * 1024 * 1024)); | |||
| 495 | ||||
| 496 | /* Assume that raw images are linux kernels, and ELF images are not. */ | |||
| 497 | kernel_size = load_elf(info->kernel_filename, NULL((void*)0), NULL((void*)0), &elf_entry, | |||
| 498 | NULL((void*)0), NULL((void*)0), big_endian, ELF_MACHINE183, 1); | |||
| 499 | entry = elf_entry; | |||
| 500 | if (kernel_size < 0) { | |||
| 501 | kernel_size = load_uimage(info->kernel_filename, &entry, NULL((void*)0), | |||
| 502 | &is_linux); | |||
| 503 | } | |||
| 504 | if (kernel_size < 0) { | |||
| 505 | entry = info->loader_start + kernel_load_offset; | |||
| 506 | kernel_size = load_image_targphys(info->kernel_filename, entry, | |||
| 507 | info->ram_size - kernel_load_offset); | |||
| 508 | is_linux = 1; | |||
| 509 | } | |||
| 510 | if (kernel_size < 0) { | |||
| 511 | fprintf(stderrstderr, "qemu: could not load kernel '%s'\n", | |||
| 512 | info->kernel_filename); | |||
| 513 | exit(1); | |||
| 514 | } | |||
| 515 | info->entry = entry; | |||
| 516 | if (is_linux) { | |||
| 517 | uint32_t fixupcontext[FIXUP_MAX]; | |||
| 518 | ||||
| 519 | if (info->initrd_filename) { | |||
| 520 | initrd_size = load_ramdisk(info->initrd_filename, | |||
| 521 | info->initrd_start, | |||
| 522 | info->ram_size - | |||
| 523 | info->initrd_start); | |||
| 524 | if (initrd_size < 0) { | |||
| 525 | initrd_size = load_image_targphys(info->initrd_filename, | |||
| 526 | info->initrd_start, | |||
| 527 | info->ram_size - | |||
| 528 | info->initrd_start); | |||
| 529 | } | |||
| 530 | if (initrd_size < 0) { | |||
| 531 | fprintf(stderrstderr, "qemu: could not load initrd '%s'\n", | |||
| 532 | info->initrd_filename); | |||
| 533 | exit(1); | |||
| 534 | } | |||
| 535 | } else { | |||
| 536 | initrd_size = 0; | |||
| 537 | } | |||
| 538 | info->initrd_size = initrd_size; | |||
| 539 | ||||
| 540 | fixupcontext[FIXUP_BOARDID] = info->board_id; | |||
| 541 | ||||
| 542 | /* for device tree boot, we pass the DTB directly in r2. Otherwise | |||
| 543 | * we point to the kernel args. | |||
| 544 | */ | |||
| 545 | if (info->dtb_filename || info->get_dtb) { | |||
| 546 | /* Place the DTB after the initrd in memory. Note that some | |||
| 547 | * kernels will trash anything in the 4K page the initrd | |||
| 548 | * ends in, so make sure the DTB isn't caught up in that. | |||
| 549 | */ | |||
| 550 | hwaddr dtb_start = QEMU_ALIGN_UP(info->initrd_start + initrd_size,(((info->initrd_start + initrd_size) + (4096) - 1) / ((4096 )) * ((4096))) | |||
| 551 | 4096)(((info->initrd_start + initrd_size) + (4096) - 1) / ((4096 )) * ((4096))); | |||
| 552 | if (load_dtb(dtb_start, info)) { | |||
| 553 | exit(1); | |||
| 554 | } | |||
| 555 | fixupcontext[FIXUP_ARGPTR] = dtb_start; | |||
| 556 | } else { | |||
| 557 | fixupcontext[FIXUP_ARGPTR] = info->loader_start + KERNEL_ARGS_ADDR0x100; | |||
| 558 | if (info->ram_size >= (1ULL << 32)) { | |||
| 559 | fprintf(stderrstderr, "qemu: RAM size must be less than 4GB to boot" | |||
| 560 | " Linux kernel using ATAGS (try passing a device tree" | |||
| 561 | " using -dtb)\n"); | |||
| 562 | exit(1); | |||
| 563 | } | |||
| 564 | } | |||
| 565 | fixupcontext[FIXUP_ENTRYPOINT] = entry; | |||
| 566 | ||||
| 567 | write_bootloader("bootloader", info->loader_start, | |||
| 568 | primary_loader, fixupcontext); | |||
| 569 | ||||
| 570 | if (info->nb_cpus > 1) { | |||
| 571 | info->write_secondary_boot(cpu, info); | |||
| 572 | } | |||
| 573 | } | |||
| 574 | info->is_linux = is_linux; | |||
| 575 | ||||
| 576 | for (; cs; cs = CPU_NEXT(cs)((cs)->node.tqe_next)) { | |||
| 577 | cpu = ARM_CPU(cs)((ARMCPU *)object_dynamic_cast_assert(((Object *)((cs))), ("arm-cpu" ), "/home/stefan/src/qemu/qemu.org/qemu/hw/arm/boot.c", 577, __func__ )); | |||
| 578 | cpu->env.boot_info = info; | |||
| 579 | qemu_register_reset(do_cpu_reset, cpu); | |||
| 580 | } | |||
| 581 | } |