blob: ed61e483ee8b204913c9d4d605ebb3490bc2b520 [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
Peter Maydell8fd9dec2016-01-26 18:05:31 +000027#include "qemu/osdep.h"
Paolo Bonzini2c65db52020-10-28 07:36:57 -040028#include "qemu/datadir.h"
Paolo Bonzini4771d752016-01-19 21:51:44 +010029#include "cpu.h"
Paolo Bonzini1de7afc2012-12-17 18:20:00 +010030#include "qemu/option.h"
31#include "qemu/config-file.h"
Edgar E. Iglesiasec426ff2013-05-05 11:06:37 +020032#include "qemu/error-report.h"
Jason A. Donenfeldb91b6b52022-09-08 11:40:30 +020033#include "qemu/guest-random.h"
Paolo Bonzini9c17d612012-12-17 18:20:04 +010034#include "sysemu/device_tree.h"
Markus Armbruster71e8a912019-08-12 07:23:38 +020035#include "sysemu/reset.h"
Paolo Bonzinif2ce39b2020-11-02 09:44:36 -050036#include "hw/boards.h"
Paolo Bonzini83c9f4c2013-02-04 15:40:22 +010037#include "hw/loader.h"
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100038#include "elf.h"
Veronia Bahaaf348b6d2016-03-20 19:16:19 +020039#include "qemu/cutils.h"
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100040
Paolo Bonzini47b43a12013-03-18 17:36:02 +010041#include "boot.h"
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100042
43static struct
44{
Andreas Färberbf494362012-05-05 12:30:53 +020045 void (*machine_cpu_reset)(MicroBlazeCPU *);
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100046 uint32_t bootstrap_pc;
47 uint32_t cmdline;
Edgar E. Iglesiasec426ff2013-05-05 11:06:37 +020048 uint32_t initrd_start;
49 uint32_t initrd_end;
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100050 uint32_t fdt;
51} boot_info;
52
53static void main_cpu_reset(void *opaque)
54{
Andreas Färberbf494362012-05-05 12:30:53 +020055 MicroBlazeCPU *cpu = opaque;
Peter Crosthwaite691b9572015-06-23 20:19:23 -070056 CPUState *cs = CPU(cpu);
Andreas Färberbf494362012-05-05 12:30:53 +020057 CPUMBState *env = &cpu->env;
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100058
Peter Crosthwaite691b9572015-06-23 20:19:23 -070059 cpu_reset(cs);
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100060 env->regs[5] = boot_info.cmdline;
Edgar E. Iglesiasec426ff2013-05-05 11:06:37 +020061 env->regs[6] = boot_info.initrd_start;
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100062 env->regs[7] = boot_info.fdt;
Peter Crosthwaite691b9572015-06-23 20:19:23 -070063 cpu_set_pc(cs, boot_info.bootstrap_pc);
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100064 if (boot_info.machine_cpu_reset) {
Andreas Färberbf494362012-05-05 12:30:53 +020065 boot_info.machine_cpu_reset(cpu);
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100066 }
67}
68
Avi Kivitya8170e52012-10-23 12:30:10 +020069static int microblaze_load_dtb(hwaddr addr,
Edgar E. Iglesiasd0b022a2013-05-05 10:52:41 +020070 uint32_t ramsize,
Edgar E. Iglesiasec426ff2013-05-05 11:06:37 +020071 uint32_t initrd_start,
72 uint32_t initrd_end,
Edgar E. Iglesiasd0b022a2013-05-05 10:52:41 +020073 const char *kernel_cmdline,
74 const char *dtb_filename)
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100075{
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100076 int fdt_size;
Peter A. G. Crosthwaiteda71ebd2012-03-04 21:03:55 +100077 void *fdt = NULL;
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100078 int r;
Jason A. Donenfeldb91b6b52022-09-08 11:40:30 +020079 uint8_t rng_seed[32];
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100080
Peter A. G. Crosthwaiteda71ebd2012-03-04 21:03:55 +100081 if (dtb_filename) {
82 fdt = load_device_tree(dtb_filename, &fdt_size);
83 }
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100084 if (!fdt) {
Peter A. G. Crosthwaiteda71ebd2012-03-04 21:03:55 +100085 return 0;
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100086 }
87
Jason A. Donenfeldb91b6b52022-09-08 11:40:30 +020088 qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed));
89 qemu_fdt_setprop(fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed));
90
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100091 if (kernel_cmdline) {
Peter Crosthwaite5a4348d2013-11-11 18:14:41 +100092 r = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs",
93 kernel_cmdline);
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +100094 if (r < 0) {
95 fprintf(stderr, "couldn't set /chosen/bootargs\n");
96 }
97 }
98
Edgar E. Iglesiasec426ff2013-05-05 11:06:37 +020099 if (initrd_start) {
Peter Crosthwaite5a4348d2013-11-11 18:14:41 +1000100 qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start",
101 initrd_start);
Edgar E. Iglesiasec426ff2013-05-05 11:06:37 +0200102
Peter Crosthwaite5a4348d2013-11-11 18:14:41 +1000103 qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end",
104 initrd_end);
Edgar E. Iglesiasec426ff2013-05-05 11:06:37 +0200105 }
106
Stefan Weile1fe50d2013-04-12 20:53:58 +0200107 cpu_physical_memory_write(addr, fdt, fdt_size);
Paolo Bonzini100781a2019-10-01 15:36:22 +0200108 g_free(fdt);
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +1000109 return fdt_size;
110}
111
112static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
113{
114 return addr - 0x30000000LL;
115}
116
Avi Kivitya8170e52012-10-23 12:30:10 +0200117void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base,
Edgar E. Iglesiasec426ff2013-05-05 11:06:37 +0200118 uint32_t ramsize,
119 const char *initrd_filename,
120 const char *dtb_filename,
Andreas Färberbf494362012-05-05 12:30:53 +0200121 void (*machine_cpu_reset)(MicroBlazeCPU *))
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +1000122{
Markus Armbruster7bccd942013-07-04 15:09:21 +0200123 const char *kernel_filename;
124 const char *kernel_cmdline;
125 const char *dtb_arg;
Gonglei4d850402015-03-05 11:05:20 +0800126 char *filename = NULL;
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +1000127
Paolo Bonzinif2ce39b2020-11-02 09:44:36 -0500128 kernel_filename = current_machine->kernel_filename;
129 kernel_cmdline = current_machine->kernel_cmdline;
130 dtb_arg = current_machine->dtb;
Gonglei4d850402015-03-05 11:05:20 +0800131 /* default to pcbios dtb as passed by machine_init */
Alistair Francisd4c6d362018-01-22 11:43:16 -0800132 if (!dtb_arg && dtb_filename) {
Gonglei4d850402015-03-05 11:05:20 +0800133 filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, dtb_filename);
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +1000134 }
135
136 boot_info.machine_cpu_reset = machine_cpu_reset;
Andreas Färberbf494362012-05-05 12:30:53 +0200137 qemu_register_reset(main_cpu_reset, cpu);
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +1000138
139 if (kernel_filename) {
140 int kernel_size;
BALATON Zoltan617160c2020-07-05 19:22:11 +0200141 uint64_t entry, high;
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +1000142 uint32_t base32;
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +1000143
144 /* Boots a kernel elf binary. */
Liam Merwick4366e1d2019-01-15 12:18:03 +0000145 kernel_size = load_elf(kernel_filename, NULL, NULL, NULL,
BALATON Zoltan617160c2020-07-05 19:22:11 +0200146 &entry, NULL, &high, NULL,
Thomas Huthded625e2023-09-07 13:35:00 +0200147 TARGET_BIG_ENDIAN, EM_MICROBLAZE, 0, 0);
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +1000148 base32 = entry;
149 if (base32 == 0xc0000000) {
Liam Merwick4366e1d2019-01-15 12:18:03 +0000150 kernel_size = load_elf(kernel_filename, NULL,
151 translate_kernel_address, NULL,
Aleksandar Markovic6cdda0f2020-01-26 23:55:04 +0100152 &entry, NULL, NULL, NULL,
Thomas Huthded625e2023-09-07 13:35:00 +0200153 TARGET_BIG_ENDIAN, EM_MICROBLAZE, 0, 0);
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +1000154 }
155 /* Always boot into physical ram. */
Peter Crosthwaitee5bfd642014-04-28 17:12:58 -0700156 boot_info.bootstrap_pc = (uint32_t)entry;
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +1000157
158 /* If it wasn't an ELF image, try an u-boot image. */
159 if (kernel_size < 0) {
Nick Hudsonf831f952019-01-07 08:31:50 +0000160 hwaddr uentry, loadaddr = LOAD_UIMAGE_LOADADDR_INVALID;
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +1000161
Max Filippov25bda502014-10-19 07:42:22 +0400162 kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0,
163 NULL, NULL);
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +1000164 boot_info.bootstrap_pc = uentry;
165 high = (loadaddr + kernel_size + 3) & ~3;
166 }
167
168 /* Not an ELF image nor an u-boot image, try a RAW image. */
169 if (kernel_size < 0) {
170 kernel_size = load_image_targphys(kernel_filename, ddr_base,
Paolo Bonzini89ee4a02020-10-28 06:20:01 -0400171 ramsize);
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +1000172 boot_info.bootstrap_pc = ddr_base;
173 high = (ddr_base + kernel_size + 3) & ~3;
174 }
175
Edgar E. Iglesiasec426ff2013-05-05 11:06:37 +0200176 if (initrd_filename) {
177 int initrd_size;
178 uint32_t initrd_offset;
179
180 high = ROUND_UP(high + kernel_size, 4);
181 boot_info.initrd_start = high;
182 initrd_offset = boot_info.initrd_start - ddr_base;
Edgar E. Iglesias1b939d92013-05-03 15:19:45 +0200183
184 initrd_size = load_ramdisk(initrd_filename,
185 boot_info.initrd_start,
Paolo Bonzini89ee4a02020-10-28 06:20:01 -0400186 ramsize - initrd_offset);
Edgar E. Iglesias1b939d92013-05-03 15:19:45 +0200187 if (initrd_size < 0) {
188 initrd_size = load_image_targphys(initrd_filename,
189 boot_info.initrd_start,
Paolo Bonzini89ee4a02020-10-28 06:20:01 -0400190 ramsize - initrd_offset);
Edgar E. Iglesias1b939d92013-05-03 15:19:45 +0200191 }
Edgar E. Iglesiasec426ff2013-05-05 11:06:37 +0200192 if (initrd_size < 0) {
Ishani Chughd0e31a12017-04-13 21:44:39 +0530193 error_report("could not load initrd '%s'",
Edgar E. Iglesiasec426ff2013-05-05 11:06:37 +0200194 initrd_filename);
195 exit(EXIT_FAILURE);
196 }
197 boot_info.initrd_end = boot_info.initrd_start + initrd_size;
198 high = ROUND_UP(high + initrd_size, 4);
199 }
200
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +1000201 boot_info.cmdline = high + 4096;
202 if (kernel_cmdline && strlen(kernel_cmdline)) {
203 pstrcpy_targphys("cmdline", boot_info.cmdline, 256, kernel_cmdline);
204 }
205 /* Provide a device-tree. */
206 boot_info.fdt = boot_info.cmdline + 4096;
Paolo Bonzini89ee4a02020-10-28 06:20:01 -0400207 microblaze_load_dtb(boot_info.fdt, ramsize,
Edgar E. Iglesiasec426ff2013-05-05 11:06:37 +0200208 boot_info.initrd_start,
209 boot_info.initrd_end,
Edgar E. Iglesiasd0b022a2013-05-05 10:52:41 +0200210 kernel_cmdline,
Gonglei4d850402015-03-05 11:05:20 +0800211 /* Preference a -dtb argument */
212 dtb_arg ? dtb_arg : filename);
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +1000213 }
Gonglei4d850402015-03-05 11:05:20 +0800214 g_free(filename);
Peter A. G. Crosthwaited94e7432012-03-04 21:03:51 +1000215}