|
1 /* |
|
2 * ARM kernel loader. |
|
3 * |
|
4 * Copyright (c) 2006-2007 CodeSourcery. |
|
5 * Written by Paul Brook |
|
6 * |
|
7 * This code is licenced under the GPL. |
|
8 */ |
|
9 |
|
10 #include "hw.h" |
|
11 #include "arm-misc.h" |
|
12 #include "sysemu.h" |
|
13 #include "devtree.h" |
|
14 |
|
15 #define KERNEL_ARGS_ADDR 0x100 |
|
16 #define KERNEL_LOAD_ADDR 0x00010000 |
|
17 #define INITRD_LOAD_ADDR 0x00800000 |
|
18 |
|
19 /* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */ |
|
20 static uint32_t bootloader[] = { |
|
21 0xe3a00000, /* mov r0, #0 */ |
|
22 0xe3a01000, /* mov r1, #0x?? */ |
|
23 0xe3811c00, /* orr r1, r1, #0x??00 */ |
|
24 0xe59f2000, /* ldr r2, [pc, #0] */ |
|
25 0xe59ff000, /* ldr pc, [pc, #0] */ |
|
26 0, /* Address of kernel args. Set by integratorcp_init. */ |
|
27 0 /* Kernel entry point. Set by integratorcp_init. */ |
|
28 }; |
|
29 |
|
30 /* Entry point for secondary CPUs. Enable interrupt controller and |
|
31 Issue WFI until start address is written to system controller. */ |
|
32 static uint32_t smpboot[] = { |
|
33 0xe3a00201, /* mov r0, #0x10000000 */ |
|
34 0xe3800601, /* orr r0, r0, #0x001000000 */ |
|
35 0xe3a01001, /* mov r1, #1 */ |
|
36 0xe5801100, /* str r1, [r0, #0x100] */ |
|
37 0xe3a00201, /* mov r0, #0x10000000 */ |
|
38 0xe3800030, /* orr r0, #0x30 */ |
|
39 0xe320f003, /* wfi */ |
|
40 0xe5901000, /* ldr r1, [r0] */ |
|
41 0xe3110003, /* tst r1, #3 */ |
|
42 0x1afffffb, /* bne <wfi> */ |
|
43 0xe12fff11 /* bx r1 */ |
|
44 }; |
|
45 |
|
46 static void main_cpu_reset(void *opaque) |
|
47 { |
|
48 CPUState *env = opaque; |
|
49 |
|
50 cpu_reset(env); |
|
51 if (env->boot_info) |
|
52 arm_load_kernel(env, env->boot_info); |
|
53 |
|
54 /* TODO: Reset secondary CPUs. */ |
|
55 } |
|
56 |
|
57 static void set_kernel_args(struct arm_boot_info *info, |
|
58 int initrd_size, void *base) |
|
59 { |
|
60 uint32_t *p; |
|
61 |
|
62 p = (uint32_t *)(base + KERNEL_ARGS_ADDR); |
|
63 /* ATAG_CORE */ |
|
64 stl_raw(p++, 5); |
|
65 stl_raw(p++, 0x54410001); |
|
66 stl_raw(p++, 1); |
|
67 stl_raw(p++, 0x1000); |
|
68 stl_raw(p++, 0); |
|
69 /* ATAG_MEM */ |
|
70 /* TODO: handle multiple chips on one ATAG list */ |
|
71 stl_raw(p++, 4); |
|
72 stl_raw(p++, 0x54410002); |
|
73 stl_raw(p++, info->ram_size); |
|
74 stl_raw(p++, info->loader_start); |
|
75 if (initrd_size) { |
|
76 /* ATAG_INITRD2 */ |
|
77 stl_raw(p++, 4); |
|
78 stl_raw(p++, 0x54420005); |
|
79 stl_raw(p++, info->loader_start + INITRD_LOAD_ADDR); |
|
80 stl_raw(p++, initrd_size); |
|
81 } |
|
82 if (info->kernel_cmdline && *info->kernel_cmdline) { |
|
83 /* ATAG_CMDLINE */ |
|
84 int cmdline_size; |
|
85 |
|
86 cmdline_size = strlen(info->kernel_cmdline); |
|
87 memcpy(p + 2, info->kernel_cmdline, cmdline_size + 1); |
|
88 cmdline_size = (cmdline_size >> 2) + 1; |
|
89 stl_raw(p++, cmdline_size + 2); |
|
90 stl_raw(p++, 0x54410009); |
|
91 p += cmdline_size; |
|
92 } |
|
93 if (info->atag_board) { |
|
94 /* ATAG_BOARD */ |
|
95 int atag_board_len; |
|
96 |
|
97 atag_board_len = (info->atag_board(info, p + 2) + 3) >> 2; |
|
98 stl_raw(p++, 2 + atag_board_len); |
|
99 stl_raw(p++, 0x414f4d50); |
|
100 p += atag_board_len; |
|
101 } |
|
102 /* ATAG_END */ |
|
103 stl_raw(p++, 0); |
|
104 stl_raw(p++, 0); |
|
105 } |
|
106 |
|
107 static void set_kernel_args_old(struct arm_boot_info *info, |
|
108 int initrd_size, void *base) |
|
109 { |
|
110 uint32_t *p; |
|
111 unsigned char *s; |
|
112 |
|
113 /* see linux/include/asm-arm/setup.h */ |
|
114 p = (uint32_t *)(base + KERNEL_ARGS_ADDR); |
|
115 /* page_size */ |
|
116 stl_raw(p++, 4096); |
|
117 /* nr_pages */ |
|
118 stl_raw(p++, info->ram_size / 4096); |
|
119 /* ramdisk_size */ |
|
120 stl_raw(p++, 0); |
|
121 #define FLAG_READONLY 1 |
|
122 #define FLAG_RDLOAD 4 |
|
123 #define FLAG_RDPROMPT 8 |
|
124 /* flags */ |
|
125 stl_raw(p++, FLAG_READONLY | FLAG_RDLOAD | FLAG_RDPROMPT); |
|
126 /* rootdev */ |
|
127 stl_raw(p++, (31 << 8) | 0); /* /dev/mtdblock0 */ |
|
128 /* video_num_cols */ |
|
129 stl_raw(p++, 0); |
|
130 /* video_num_rows */ |
|
131 stl_raw(p++, 0); |
|
132 /* video_x */ |
|
133 stl_raw(p++, 0); |
|
134 /* video_y */ |
|
135 stl_raw(p++, 0); |
|
136 /* memc_control_reg */ |
|
137 stl_raw(p++, 0); |
|
138 /* unsigned char sounddefault */ |
|
139 /* unsigned char adfsdrives */ |
|
140 /* unsigned char bytes_per_char_h */ |
|
141 /* unsigned char bytes_per_char_v */ |
|
142 stl_raw(p++, 0); |
|
143 /* pages_in_bank[4] */ |
|
144 stl_raw(p++, 0); |
|
145 stl_raw(p++, 0); |
|
146 stl_raw(p++, 0); |
|
147 stl_raw(p++, 0); |
|
148 /* pages_in_vram */ |
|
149 stl_raw(p++, 0); |
|
150 /* initrd_start */ |
|
151 if (initrd_size) |
|
152 stl_raw(p++, info->loader_start + INITRD_LOAD_ADDR); |
|
153 else |
|
154 stl_raw(p++, 0); |
|
155 /* initrd_size */ |
|
156 stl_raw(p++, initrd_size); |
|
157 /* rd_start */ |
|
158 stl_raw(p++, 0); |
|
159 /* system_rev */ |
|
160 stl_raw(p++, 0); |
|
161 /* system_serial_low */ |
|
162 stl_raw(p++, 0); |
|
163 /* system_serial_high */ |
|
164 stl_raw(p++, 0); |
|
165 /* mem_fclk_21285 */ |
|
166 stl_raw(p++, 0); |
|
167 /* zero unused fields */ |
|
168 memset(p, 0, 256 + 1024 - |
|
169 (p - ((uint32_t *)(base + KERNEL_ARGS_ADDR)))); |
|
170 s = base + KERNEL_ARGS_ADDR + 256 + 1024; |
|
171 if (info->kernel_cmdline) |
|
172 strcpy (s, info->kernel_cmdline); |
|
173 else |
|
174 stb_raw(s, 0); |
|
175 } |
|
176 |
|
177 void arm_load_kernel(CPUState *env, struct arm_boot_info *info) |
|
178 { |
|
179 int kernel_size; |
|
180 int initrd_size; |
|
181 int n; |
|
182 int is_linux = 0; |
|
183 uint64_t elf_entry; |
|
184 target_ulong entry; |
|
185 uint32_t pd; |
|
186 void *loader_phys; |
|
187 |
|
188 /* Load the kernel. */ |
|
189 if (!info->kernel_filename) { |
|
190 fprintf(stderr, "Kernel image must be specified\n"); |
|
191 exit(1); |
|
192 } |
|
193 |
|
194 if (!env->boot_info) { |
|
195 if (info->nb_cpus == 0) |
|
196 info->nb_cpus = 1; |
|
197 env->boot_info = info; |
|
198 qemu_register_reset(main_cpu_reset, env); |
|
199 } |
|
200 |
|
201 pd = cpu_get_physical_page_desc(info->loader_start); |
|
202 /* FIXME: This is broken if it overlaps multiple regions. */ |
|
203 loader_phys = host_ram_addr((pd & TARGET_PAGE_MASK) + |
|
204 (info->loader_start & ~TARGET_PAGE_MASK)); |
|
205 |
|
206 /* Assume that raw images are linux kernels, and ELF images are not. */ |
|
207 kernel_size = load_elf(info->kernel_filename, 0, &elf_entry, NULL, NULL); |
|
208 entry = elf_entry; |
|
209 if (kernel_size < 0) { |
|
210 kernel_size = load_uimage(info->kernel_filename, &entry, NULL, |
|
211 &is_linux); |
|
212 } |
|
213 if (kernel_size < 0) { |
|
214 kernel_size = load_image(info->kernel_filename, |
|
215 loader_phys + KERNEL_LOAD_ADDR); |
|
216 entry = info->loader_start + KERNEL_LOAD_ADDR; |
|
217 is_linux = 1; |
|
218 } |
|
219 if (kernel_size < 0) { |
|
220 fprintf(stderr, "qemu: could not load kernel '%s'\n", |
|
221 info->kernel_filename); |
|
222 exit(1); |
|
223 } |
|
224 if (!is_linux) { |
|
225 /* Jump to the entry point. */ |
|
226 env->regs[15] = entry & 0xfffffffe; |
|
227 env->thumb = entry & 1; |
|
228 } else { |
|
229 if (info->initrd_filename) { |
|
230 initrd_size = load_image(info->initrd_filename, |
|
231 loader_phys + INITRD_LOAD_ADDR); |
|
232 if (initrd_size < 0) { |
|
233 fprintf(stderr, "qemu: could not load initrd '%s'\n", |
|
234 info->initrd_filename); |
|
235 exit(1); |
|
236 } |
|
237 } else { |
|
238 initrd_size = 0; |
|
239 } |
|
240 bootloader[1] |= info->board_id & 0xff; |
|
241 bootloader[2] |= (info->board_id >> 8) & 0xff; |
|
242 bootloader[5] = info->loader_start + KERNEL_ARGS_ADDR; |
|
243 bootloader[6] = entry; |
|
244 for (n = 0; n < sizeof(bootloader) / 4; n++) |
|
245 stl_raw(loader_phys + (n * 4), bootloader[n]); |
|
246 if (info->nb_cpus > 1) |
|
247 for (n = 0; n < sizeof(smpboot) / 4; n++) |
|
248 stl_raw(loader_phys + info->ram_size + (n * 4), smpboot[n]); |
|
249 if (old_param) |
|
250 set_kernel_args_old(info, initrd_size, loader_phys); |
|
251 else |
|
252 set_kernel_args(info, initrd_size, loader_phys); |
|
253 } |
|
254 } |
|
255 |
|
256 void cpu_bootstrap(const char *kernel_filename, const char *kernel_cmdline, |
|
257 const char *initrd_filename) |
|
258 { |
|
259 static struct arm_boot_info binfo; |
|
260 memset(&binfo, 0, sizeof(binfo)); |
|
261 /* FIXME: Handle multiple ram regions. */ |
|
262 binfo.loader_start = devtree_ram_map[0].base; |
|
263 binfo.ram_size = devtree_ram_map[0].size; |
|
264 binfo.board_id = devtree_get_config_int("board-id", 0); |
|
265 binfo.kernel_filename = kernel_filename; |
|
266 binfo.kernel_cmdline = kernel_cmdline; |
|
267 binfo.initrd_filename = initrd_filename; |
|
268 arm_load_kernel(first_cpu, &binfo); |
|
269 } |