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_MACHINE40, 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 | } |