blob: 6bf36d046fdc313d92ac936766adec2d7048fcbc [file] [log] [blame]
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +10001/*
2 * Microblaze kernel loader
3 *
4 * Copyright (c) 2012 Peter Crosthwaite <peter.crosthwaite@petalogix.com>
5 * Copyright (c) 2012 PetaLogix
6 * Copyright (c) 2009 Edgar E. Iglesias.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 * THE SOFTWARE.
25 */
26
Paolo Bonzini1de7afc2012-12-17 18:20:00 +010027#include "qemu/option.h"
28#include "qemu/config-file.h"
Edgar E. Iglesiasec426ff2013-05-05 11:06:37 +020029#include "qemu/error-report.h"
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100030#include "qemu-common.h"
Paolo Bonzini9c17d612012-12-17 18:20:04 +010031#include "sysemu/device_tree.h"
Markus Armbruster7bccd942013-07-04 15:09:21 +020032#include "sysemu/sysemu.h"
Paolo Bonzini83c9f4c2013-02-04 15:40:22 +010033#include "hw/loader.h"
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100034#include "elf.h"
35
Paolo Bonzini47b43a12013-03-18 17:36:02 +010036#include "boot.h"
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100037
38static struct
39{
Andreas Färberbf494362012-05-05 12:30:53 +020040 void (*machine_cpu_reset)(MicroBlazeCPU *);
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100041 uint32_t bootstrap_pc;
42 uint32_t cmdline;
Edgar E. Iglesiasec426ff2013-05-05 11:06:37 +020043 uint32_t initrd_start;
44 uint32_t initrd_end;
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100045 uint32_t fdt;
46} boot_info;
47
48static void main_cpu_reset(void *opaque)
49{
Andreas Färberbf494362012-05-05 12:30:53 +020050 MicroBlazeCPU *cpu = opaque;
51 CPUMBState *env = &cpu->env;
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100052
Andreas Färberbf494362012-05-05 12:30:53 +020053 cpu_reset(CPU(cpu));
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100054 env->regs[5] = boot_info.cmdline;
Edgar E. Iglesiasec426ff2013-05-05 11:06:37 +020055 env->regs[6] = boot_info.initrd_start;
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100056 env->regs[7] = boot_info.fdt;
57 env->sregs[SR_PC] = boot_info.bootstrap_pc;
58 if (boot_info.machine_cpu_reset) {
Andreas Färberbf494362012-05-05 12:30:53 +020059 boot_info.machine_cpu_reset(cpu);
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100060 }
61}
62
Avi Kivitya8170e52012-10-23 12:30:10 +020063static int microblaze_load_dtb(hwaddr addr,
Edgar E. Iglesiasd0b022a2013-05-05 10:52:41 +020064 uint32_t ramsize,
Edgar E. Iglesiasec426ff2013-05-05 11:06:37 +020065 uint32_t initrd_start,
66 uint32_t initrd_end,
Edgar E. Iglesiasd0b022a2013-05-05 10:52:41 +020067 const char *kernel_cmdline,
68 const char *dtb_filename)
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100069{
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100070 int fdt_size;
Peter A. G. Crosthwaiteda71ebd2012-03-04 21:03:55 +100071 void *fdt = NULL;
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100072 int r;
73
Peter A. G. Crosthwaiteda71ebd2012-03-04 21:03:55 +100074 if (dtb_filename) {
75 fdt = load_device_tree(dtb_filename, &fdt_size);
76 }
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100077 if (!fdt) {
Peter A. G. Crosthwaiteda71ebd2012-03-04 21:03:55 +100078 return 0;
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100079 }
80
81 if (kernel_cmdline) {
Peter Crosthwaite5a4348d2013-11-11 18:14:41 +100082 r = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs",
83 kernel_cmdline);
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100084 if (r < 0) {
85 fprintf(stderr, "couldn't set /chosen/bootargs\n");
86 }
87 }
88
Edgar E. Iglesiasec426ff2013-05-05 11:06:37 +020089 if (initrd_start) {
Peter Crosthwaite5a4348d2013-11-11 18:14:41 +100090 qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start",
91 initrd_start);
Edgar E. Iglesiasec426ff2013-05-05 11:06:37 +020092
Peter Crosthwaite5a4348d2013-11-11 18:14:41 +100093 qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end",
94 initrd_end);
Edgar E. Iglesiasec426ff2013-05-05 11:06:37 +020095 }
96
Stefan Weile1fe50d2013-04-12 20:53:58 +020097 cpu_physical_memory_write(addr, fdt, fdt_size);
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100098 return fdt_size;
99}
100
101static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
102{
103 return addr - 0x30000000LL;
104}
105
Avi Kivitya8170e52012-10-23 12:30:10 +0200106void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base,
Edgar E. Iglesiasec426ff2013-05-05 11:06:37 +0200107 uint32_t ramsize,
108 const char *initrd_filename,
109 const char *dtb_filename,
Andreas Färberbf494362012-05-05 12:30:53 +0200110 void (*machine_cpu_reset)(MicroBlazeCPU *))
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +1000111{
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +1000112 QemuOpts *machine_opts;
Markus Armbruster7bccd942013-07-04 15:09:21 +0200113 const char *kernel_filename;
114 const char *kernel_cmdline;
115 const char *dtb_arg;
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +1000116
Markus Armbruster7bccd942013-07-04 15:09:21 +0200117 machine_opts = qemu_get_machine_opts();
118 kernel_filename = qemu_opt_get(machine_opts, "kernel");
119 kernel_cmdline = qemu_opt_get(machine_opts, "append");
120 dtb_arg = qemu_opt_get(machine_opts, "dtb");
121 if (dtb_arg) { /* Preference a -dtb argument */
122 dtb_filename = dtb_arg;
123 } else { /* default to pcbios dtb as passed by machine_init */
124 dtb_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, dtb_filename);
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +1000125 }
126
127 boot_info.machine_cpu_reset = machine_cpu_reset;
Andreas Färberbf494362012-05-05 12:30:53 +0200128 qemu_register_reset(main_cpu_reset, cpu);
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +1000129
130 if (kernel_filename) {
131 int kernel_size;
132 uint64_t entry, low, high;
133 uint32_t base32;
134 int big_endian = 0;
135
136#ifdef TARGET_WORDS_BIGENDIAN
137 big_endian = 1;
138#endif
139
140 /* Boots a kernel elf binary. */
141 kernel_size = load_elf(kernel_filename, NULL, NULL,
142 &entry, &low, &high,
143 big_endian, ELF_MACHINE, 0);
144 base32 = entry;
145 if (base32 == 0xc0000000) {
146 kernel_size = load_elf(kernel_filename, translate_kernel_address,
147 NULL, &entry, NULL, NULL,
148 big_endian, ELF_MACHINE, 0);
149 }
150 /* Always boot into physical ram. */
Peter Crosthwaitee5bfd642014-04-28 17:12:58 -0700151 boot_info.bootstrap_pc = (uint32_t)entry;
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +1000152
153 /* If it wasn't an ELF image, try an u-boot image. */
154 if (kernel_size < 0) {
Avi Kivitya8170e52012-10-23 12:30:10 +0200155 hwaddr uentry, loadaddr;
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +1000156
157 kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0);
158 boot_info.bootstrap_pc = uentry;
159 high = (loadaddr + kernel_size + 3) & ~3;
160 }
161
162 /* Not an ELF image nor an u-boot image, try a RAW image. */
163 if (kernel_size < 0) {
164 kernel_size = load_image_targphys(kernel_filename, ddr_base,
165 ram_size);
166 boot_info.bootstrap_pc = ddr_base;
167 high = (ddr_base + kernel_size + 3) & ~3;
168 }
169
Edgar E. Iglesiasec426ff2013-05-05 11:06:37 +0200170 if (initrd_filename) {
171 int initrd_size;
172 uint32_t initrd_offset;
173
174 high = ROUND_UP(high + kernel_size, 4);
175 boot_info.initrd_start = high;
176 initrd_offset = boot_info.initrd_start - ddr_base;
Edgar E. Iglesias1b939d92013-05-03 15:19:45 +0200177
178 initrd_size = load_ramdisk(initrd_filename,
179 boot_info.initrd_start,
180 ram_size - initrd_offset);
181 if (initrd_size < 0) {
182 initrd_size = load_image_targphys(initrd_filename,
183 boot_info.initrd_start,
184 ram_size - initrd_offset);
185 }
Edgar E. Iglesiasec426ff2013-05-05 11:06:37 +0200186 if (initrd_size < 0) {
187 error_report("qemu: could not load initrd '%s'\n",
188 initrd_filename);
189 exit(EXIT_FAILURE);
190 }
191 boot_info.initrd_end = boot_info.initrd_start + initrd_size;
192 high = ROUND_UP(high + initrd_size, 4);
193 }
194
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +1000195 boot_info.cmdline = high + 4096;
196 if (kernel_cmdline && strlen(kernel_cmdline)) {
197 pstrcpy_targphys("cmdline", boot_info.cmdline, 256, kernel_cmdline);
198 }
199 /* Provide a device-tree. */
200 boot_info.fdt = boot_info.cmdline + 4096;
Edgar E. Iglesiasd0b022a2013-05-05 10:52:41 +0200201 microblaze_load_dtb(boot_info.fdt, ram_size,
Edgar E. Iglesiasec426ff2013-05-05 11:06:37 +0200202 boot_info.initrd_start,
203 boot_info.initrd_end,
Edgar E. Iglesiasd0b022a2013-05-05 10:52:41 +0200204 kernel_cmdline,
205 dtb_filename);
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +1000206 }
207
208}