]>
Commit | Line | Data |
---|---|---|
b7862564 MV |
1 | /* |
2 | * Nios2 kernel loader | |
3 | * | |
4 | * Copyright (c) 2016 Marek Vasut <marek.vasut@gmail.com> | |
5 | * | |
6 | * Based on microblaze kernel loader | |
7 | * | |
8 | * Copyright (c) 2012 Peter Crosthwaite <peter.crosthwaite@petalogix.com> | |
9 | * Copyright (c) 2012 PetaLogix | |
10 | * Copyright (c) 2009 Edgar E. Iglesias. | |
11 | * | |
12 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
13 | * of this software and associated documentation files (the "Software"), to deal | |
14 | * in the Software without restriction, including without limitation the rights | |
15 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
16 | * copies of the Software, and to permit persons to whom the Software is | |
17 | * furnished to do so, subject to the following conditions: | |
18 | * | |
19 | * The above copyright notice and this permission notice shall be included in | |
20 | * all copies or substantial portions of the Software. | |
21 | * | |
22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
25 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
27 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
28 | * THE SOFTWARE. | |
29 | */ | |
30 | ||
31 | #include "qemu/osdep.h" | |
a3c81ef9 | 32 | #include "qemu/units.h" |
2c65db5e | 33 | #include "qemu/datadir.h" |
b7862564 MV |
34 | #include "qemu/option.h" |
35 | #include "qemu/config-file.h" | |
36 | #include "qemu/error-report.h" | |
b7862564 | 37 | #include "sysemu/device_tree.h" |
71e8a915 | 38 | #include "sysemu/reset.h" |
f2ce39b4 | 39 | #include "hw/boards.h" |
b7862564 MV |
40 | #include "hw/loader.h" |
41 | #include "elf.h" | |
b7862564 MV |
42 | |
43 | #include "boot.h" | |
44 | ||
45 | #define NIOS2_MAGIC 0x534f494e | |
46 | ||
47 | static struct nios2_boot_info { | |
48 | void (*machine_cpu_reset)(Nios2CPU *); | |
49 | uint32_t bootstrap_pc; | |
50 | uint32_t cmdline; | |
51 | uint32_t initrd_start; | |
52 | uint32_t initrd_end; | |
53 | uint32_t fdt; | |
54 | } boot_info; | |
55 | ||
56 | static void main_cpu_reset(void *opaque) | |
57 | { | |
58 | Nios2CPU *cpu = opaque; | |
59 | CPUState *cs = CPU(cpu); | |
60 | CPUNios2State *env = &cpu->env; | |
61 | ||
62 | cpu_reset(CPU(cpu)); | |
63 | ||
64 | env->regs[R_ARG0] = NIOS2_MAGIC; | |
65 | env->regs[R_ARG1] = boot_info.initrd_start; | |
66 | env->regs[R_ARG2] = boot_info.fdt; | |
67 | env->regs[R_ARG3] = boot_info.cmdline; | |
68 | ||
69 | cpu_set_pc(cs, boot_info.bootstrap_pc); | |
70 | if (boot_info.machine_cpu_reset) { | |
71 | boot_info.machine_cpu_reset(cpu); | |
72 | } | |
73 | } | |
74 | ||
75 | static uint64_t translate_kernel_address(void *opaque, uint64_t addr) | |
76 | { | |
77 | return addr - 0xc0000000LL; | |
78 | } | |
79 | ||
80 | static int nios2_load_dtb(struct nios2_boot_info bi, const uint32_t ramsize, | |
81 | const char *kernel_cmdline, const char *dtb_filename) | |
82 | { | |
83 | int fdt_size; | |
84 | void *fdt = NULL; | |
85 | int r; | |
86 | ||
87 | if (dtb_filename) { | |
88 | fdt = load_device_tree(dtb_filename, &fdt_size); | |
89 | } | |
90 | if (!fdt) { | |
91 | return 0; | |
92 | } | |
93 | ||
94 | if (kernel_cmdline) { | |
95 | r = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", | |
96 | kernel_cmdline); | |
97 | if (r < 0) { | |
98 | fprintf(stderr, "couldn't set /chosen/bootargs\n"); | |
99 | } | |
100 | } | |
101 | ||
102 | if (bi.initrd_start) { | |
103 | qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", | |
104 | translate_kernel_address(NULL, bi.initrd_start)); | |
105 | ||
106 | qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", | |
107 | translate_kernel_address(NULL, bi.initrd_end)); | |
108 | } | |
109 | ||
110 | cpu_physical_memory_write(bi.fdt, fdt, fdt_size); | |
757ca9b8 | 111 | g_free(fdt); |
b7862564 MV |
112 | return fdt_size; |
113 | } | |
114 | ||
115 | void nios2_load_kernel(Nios2CPU *cpu, hwaddr ddr_base, | |
116 | uint32_t ramsize, | |
117 | const char *initrd_filename, | |
118 | const char *dtb_filename, | |
119 | void (*machine_cpu_reset)(Nios2CPU *)) | |
120 | { | |
b7862564 MV |
121 | const char *kernel_filename; |
122 | const char *kernel_cmdline; | |
123 | const char *dtb_arg; | |
124 | char *filename = NULL; | |
125 | ||
f2ce39b4 PB |
126 | kernel_filename = current_machine->kernel_filename; |
127 | kernel_cmdline = current_machine->kernel_cmdline; | |
128 | dtb_arg = current_machine->dtb; | |
b7862564 MV |
129 | /* default to pcbios dtb as passed by machine_init */ |
130 | if (!dtb_arg) { | |
131 | filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, dtb_filename); | |
132 | } | |
133 | ||
134 | boot_info.machine_cpu_reset = machine_cpu_reset; | |
135 | qemu_register_reset(main_cpu_reset, cpu); | |
136 | ||
137 | if (kernel_filename) { | |
138 | int kernel_size, fdt_size; | |
617160c9 | 139 | uint64_t entry, high; |
b7862564 MV |
140 | int big_endian = 0; |
141 | ||
ee3eb3a7 | 142 | #if TARGET_BIG_ENDIAN |
b7862564 MV |
143 | big_endian = 1; |
144 | #endif | |
145 | ||
146 | /* Boots a kernel elf binary. */ | |
4366e1db | 147 | kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, |
617160c9 | 148 | &entry, NULL, &high, NULL, |
b7862564 | 149 | big_endian, EM_ALTERA_NIOS2, 0, 0); |
ed960ba9 SL |
150 | if ((uint32_t)entry == 0xc0000000) { |
151 | /* | |
152 | * The Nios II processor reference guide documents that the | |
153 | * kernel is placed at virtual memory address 0xc0000000, | |
154 | * and we've got something that points there. Reload it | |
155 | * and adjust the entry to get the address in physical RAM. | |
156 | */ | |
4366e1db LM |
157 | kernel_size = load_elf(kernel_filename, NULL, |
158 | translate_kernel_address, NULL, | |
6cdda0ff | 159 | &entry, NULL, NULL, NULL, |
b7862564 | 160 | big_endian, EM_ALTERA_NIOS2, 0, 0); |
ed960ba9 SL |
161 | boot_info.bootstrap_pc = ddr_base + 0xc0000000 + |
162 | (entry & 0x07ffffff); | |
163 | } else { | |
164 | /* Use the entry point in the ELF image. */ | |
165 | boot_info.bootstrap_pc = (uint32_t)entry; | |
b7862564 MV |
166 | } |
167 | ||
b7862564 MV |
168 | /* If it wasn't an ELF image, try an u-boot image. */ |
169 | if (kernel_size < 0) { | |
f831f955 | 170 | hwaddr uentry, loadaddr = LOAD_UIMAGE_LOADADDR_INVALID; |
b7862564 MV |
171 | |
172 | kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0, | |
173 | NULL, NULL); | |
174 | boot_info.bootstrap_pc = uentry; | |
175 | high = loadaddr + kernel_size; | |
176 | } | |
177 | ||
178 | /* Not an ELF image nor an u-boot image, try a RAW image. */ | |
179 | if (kernel_size < 0) { | |
180 | kernel_size = load_image_targphys(kernel_filename, ddr_base, | |
541693a4 | 181 | ramsize); |
b7862564 MV |
182 | boot_info.bootstrap_pc = ddr_base; |
183 | high = ddr_base + kernel_size; | |
184 | } | |
185 | ||
a3c81ef9 | 186 | high = ROUND_UP(high, 1 * MiB); |
b7862564 MV |
187 | |
188 | /* If initrd is available, it goes after the kernel, aligned to 1M. */ | |
189 | if (initrd_filename) { | |
190 | int initrd_size; | |
191 | uint32_t initrd_offset; | |
192 | ||
193 | boot_info.initrd_start = high; | |
194 | initrd_offset = boot_info.initrd_start - ddr_base; | |
195 | ||
196 | initrd_size = load_ramdisk(initrd_filename, | |
197 | boot_info.initrd_start, | |
541693a4 | 198 | ramsize - initrd_offset); |
b7862564 MV |
199 | if (initrd_size < 0) { |
200 | initrd_size = load_image_targphys(initrd_filename, | |
201 | boot_info.initrd_start, | |
541693a4 | 202 | ramsize - initrd_offset); |
b7862564 MV |
203 | } |
204 | if (initrd_size < 0) { | |
d0e31a10 | 205 | error_report("could not load initrd '%s'", |
b7862564 MV |
206 | initrd_filename); |
207 | exit(EXIT_FAILURE); | |
208 | } | |
209 | high += initrd_size; | |
210 | } | |
211 | high = ROUND_UP(high, 4); | |
212 | boot_info.initrd_end = high; | |
213 | ||
214 | /* Device tree must be placed right after initrd (if available) */ | |
215 | boot_info.fdt = high; | |
541693a4 | 216 | fdt_size = nios2_load_dtb(boot_info, ramsize, kernel_cmdline, |
b7862564 MV |
217 | /* Preference a -dtb argument */ |
218 | dtb_arg ? dtb_arg : filename); | |
219 | high += fdt_size; | |
220 | ||
221 | /* Kernel command is at the end, 4k aligned. */ | |
a3c81ef9 | 222 | boot_info.cmdline = ROUND_UP(high, 4 * KiB); |
b7862564 MV |
223 | if (kernel_cmdline && strlen(kernel_cmdline)) { |
224 | pstrcpy_targphys("cmdline", boot_info.cmdline, 256, kernel_cmdline); | |
225 | } | |
226 | } | |
227 | g_free(filename); | |
228 | } |