]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
2262cfee | 2 | /* |
d3a2bc3f | 3 | * Copyright (c) 2011 The Chromium OS Authors. |
2262cfee | 4 | * (C) Copyright 2002 |
fa82f871 | 5 | * Daniel Engström, Omicron Ceti AB, <daniel@omicron.se> |
2262cfee WD |
6 | */ |
7 | ||
8bde7f77 | 8 | /* |
dbf7115a | 9 | * Linux x86 zImage and bzImage loading |
8bde7f77 WD |
10 | * |
11 | * based on the procdure described in | |
2262cfee WD |
12 | * linux/Documentation/i386/boot.txt |
13 | */ | |
14 | ||
b73d61a5 SG |
15 | #define LOG_CATEGORY LOGC_BOOT |
16 | ||
d678a59d | 17 | #include <common.h> |
488d89af | 18 | #include <bootm.h> |
09140113 | 19 | #include <command.h> |
cdbff9fc | 20 | #include <env.h> |
e7bae828 | 21 | #include <init.h> |
36bf446b | 22 | #include <irq_func.h> |
b73d61a5 | 23 | #include <log.h> |
5d73292c | 24 | #include <malloc.h> |
c886557c | 25 | #include <mapmem.h> |
776cc201 | 26 | #include <acpi/acpi_table.h> |
2262cfee WD |
27 | #include <asm/io.h> |
28 | #include <asm/ptrace.h> | |
29 | #include <asm/zimage.h> | |
2262cfee | 30 | #include <asm/byteorder.h> |
0d0ba59c | 31 | #include <asm/bootm.h> |
95ffaba3 | 32 | #include <asm/bootparam.h> |
e70be676 | 33 | #include <asm/efi.h> |
e7bae828 | 34 | #include <asm/global_data.h> |
3cdc18a8 VB |
35 | #ifdef CONFIG_SYS_COREBOOT |
36 | #include <asm/arch/timestamp.h> | |
37 | #endif | |
61e0ea90 | 38 | #include <linux/compiler.h> |
7c79eddb | 39 | #include <linux/ctype.h> |
5d73292c | 40 | #include <linux/libfdt.h> |
2262cfee | 41 | |
e7bae828 SG |
42 | DECLARE_GLOBAL_DATA_PTR; |
43 | ||
2262cfee WD |
44 | /* |
45 | * Memory lay-out: | |
8bde7f77 | 46 | * |
2262cfee | 47 | * relative to setup_base (which is 0x90000 currently) |
8bde7f77 WD |
48 | * |
49 | * 0x0000-0x7FFF Real mode kernel | |
2262cfee WD |
50 | * 0x8000-0x8FFF Stack and heap |
51 | * 0x9000-0x90FF Kernel command line | |
52 | */ | |
83088afb GR |
53 | #define DEFAULT_SETUP_BASE 0x90000 |
54 | #define COMMAND_LINE_OFFSET 0x9000 | |
55 | #define HEAP_END_OFFSET 0x8e00 | |
2262cfee | 56 | |
83088afb | 57 | #define COMMAND_LINE_SIZE 2048 |
2262cfee | 58 | |
bade0ac0 | 59 | /* Current state of the boot */ |
e4255f45 | 60 | struct zboot_state state; |
5588e776 | 61 | |
2262cfee WD |
62 | static void build_command_line(char *command_line, int auto_boot) |
63 | { | |
64 | char *env_command_line; | |
8bde7f77 | 65 | |
2262cfee | 66 | command_line[0] = '\0'; |
8bde7f77 | 67 | |
00caae6d | 68 | env_command_line = env_get("bootargs"); |
8bde7f77 | 69 | |
2262cfee | 70 | /* set console= argument if we use a serial console */ |
83088afb | 71 | if (!strstr(env_command_line, "console=")) { |
00caae6d | 72 | if (!strcmp(env_get("stdout"), "serial")) { |
8bde7f77 | 73 | |
2262cfee | 74 | /* We seem to use serial console */ |
8bde7f77 | 75 | sprintf(command_line, "console=ttyS0,%s ", |
00caae6d | 76 | env_get("baudrate")); |
2262cfee WD |
77 | } |
78 | } | |
8bde7f77 | 79 | |
83088afb | 80 | if (auto_boot) |
2262cfee | 81 | strcat(command_line, "auto "); |
8bde7f77 | 82 | |
83088afb | 83 | if (env_command_line) |
2262cfee | 84 | strcat(command_line, env_command_line); |
06c085c0 SG |
85 | #ifdef DEBUG |
86 | printf("Kernel command line:"); | |
87 | puts(command_line); | |
88 | printf("\n"); | |
89 | #endif | |
2262cfee WD |
90 | } |
91 | ||
69370d14 | 92 | static int kernel_magic_ok(struct setup_header *hdr) |
2262cfee | 93 | { |
95ffaba3 | 94 | if (KERNEL_MAGIC != hdr->boot_flag) { |
d3a2bc3f GB |
95 | printf("Error: Invalid Boot Flag " |
96 | "(found 0x%04x, expected 0x%04x)\n", | |
97 | hdr->boot_flag, KERNEL_MAGIC); | |
2262cfee | 98 | return 0; |
95ffaba3 GR |
99 | } else { |
100 | printf("Valid Boot Flag\n"); | |
69370d14 | 101 | return 1; |
2262cfee | 102 | } |
69370d14 | 103 | } |
8bde7f77 | 104 | |
c038f3be | 105 | static int get_boot_protocol(struct setup_header *hdr, bool verbose) |
69370d14 GB |
106 | { |
107 | if (hdr->header == KERNEL_V2_MAGIC) { | |
c038f3be SG |
108 | if (verbose) |
109 | printf("Magic signature found\n"); | |
69370d14 | 110 | return hdr->version; |
2262cfee WD |
111 | } else { |
112 | /* Very old kernel */ | |
c038f3be SG |
113 | if (verbose) |
114 | printf("Magic signature not found\n"); | |
69370d14 | 115 | return 0x0100; |
2262cfee | 116 | } |
69370d14 GB |
117 | } |
118 | ||
5d73292c IG |
119 | static int setup_device_tree(struct setup_header *hdr, const void *fdt_blob) |
120 | { | |
c038f3be | 121 | int bootproto = get_boot_protocol(hdr, false); |
5d73292c IG |
122 | struct setup_data *sd; |
123 | int size; | |
124 | ||
125 | if (bootproto < 0x0209) | |
126 | return -ENOTSUPP; | |
127 | ||
128 | if (!fdt_blob) | |
129 | return 0; | |
130 | ||
131 | size = fdt_totalsize(fdt_blob); | |
132 | if (size < 0) | |
133 | return -EINVAL; | |
134 | ||
135 | size += sizeof(struct setup_data); | |
136 | sd = (struct setup_data *)malloc(size); | |
137 | if (!sd) { | |
138 | printf("Not enough memory for DTB setup data\n"); | |
139 | return -ENOMEM; | |
140 | } | |
141 | ||
142 | sd->next = hdr->setup_data; | |
143 | sd->type = SETUP_DTB; | |
144 | sd->len = fdt_totalsize(fdt_blob); | |
145 | memcpy(sd->data, fdt_blob, sd->len); | |
146 | hdr->setup_data = (unsigned long)sd; | |
147 | ||
148 | return 0; | |
149 | } | |
150 | ||
d0dfbf54 | 151 | const char *zimage_get_kernel_version(struct boot_params *params, |
c038f3be SG |
152 | void *kernel_base) |
153 | { | |
154 | struct setup_header *hdr = ¶ms->hdr; | |
155 | int bootproto; | |
7c79eddb | 156 | const char *s, *end; |
c038f3be SG |
157 | |
158 | bootproto = get_boot_protocol(hdr, false); | |
d0dfbf54 SG |
159 | log_debug("bootproto %x, hdr->setup_sects %x\n", bootproto, |
160 | hdr->setup_sects); | |
c038f3be SG |
161 | if (bootproto < 0x0200 || hdr->setup_sects < 15) |
162 | return NULL; | |
163 | ||
7c79eddb | 164 | /* sanity-check the kernel version in case it is missing */ |
d0dfbf54 SG |
165 | log_debug("hdr->kernel_version %x, str at %p\n", hdr->kernel_version, |
166 | kernel_base + hdr->kernel_version + 0x200); | |
7c79eddb SG |
167 | for (s = kernel_base + hdr->kernel_version + 0x200, end = s + 0x100; *s; |
168 | s++) { | |
169 | if (!isprint(*s)) | |
170 | return NULL; | |
171 | } | |
172 | ||
c038f3be SG |
173 | return kernel_base + hdr->kernel_version + 0x200; |
174 | } | |
175 | ||
69370d14 | 176 | struct boot_params *load_zimage(char *image, unsigned long kernel_size, |
76539383 | 177 | ulong *load_addressp) |
69370d14 GB |
178 | { |
179 | struct boot_params *setup_base; | |
c038f3be | 180 | const char *version; |
69370d14 GB |
181 | int setup_size; |
182 | int bootproto; | |
183 | int big_image; | |
184 | ||
185 | struct boot_params *params = (struct boot_params *)image; | |
186 | struct setup_header *hdr = ¶ms->hdr; | |
187 | ||
188 | /* base address for real-mode segment */ | |
189 | setup_base = (struct boot_params *)DEFAULT_SETUP_BASE; | |
190 | ||
191 | if (!kernel_magic_ok(hdr)) | |
192 | return 0; | |
8bde7f77 | 193 | |
2262cfee | 194 | /* determine size of setup */ |
95ffaba3 | 195 | if (0 == hdr->setup_sects) { |
a40f890b | 196 | log_warning("Setup Sectors = 0 (defaulting to 4)\n"); |
2262cfee WD |
197 | setup_size = 5 * 512; |
198 | } else { | |
95ffaba3 | 199 | setup_size = (hdr->setup_sects + 1) * 512; |
2262cfee | 200 | } |
8bde7f77 | 201 | |
a40f890b | 202 | log_debug("Setup Size = 0x%8.8lx\n", (ulong)setup_size); |
95ffaba3 | 203 | |
83088afb | 204 | if (setup_size > SETUP_MAX_SIZE) |
2262cfee | 205 | printf("Error: Setup is too large (%d bytes)\n", setup_size); |
8bde7f77 | 206 | |
69370d14 | 207 | /* determine boot protocol version */ |
c038f3be | 208 | bootproto = get_boot_protocol(hdr, true); |
69370d14 | 209 | |
a40f890b SG |
210 | log_debug("Using boot protocol version %x.%02x\n", |
211 | (bootproto & 0xff00) >> 8, bootproto & 0xff); | |
69370d14 | 212 | |
d0dfbf54 | 213 | version = zimage_get_kernel_version(params, image); |
c038f3be SG |
214 | if (version) |
215 | printf("Linux kernel version %s\n", version); | |
216 | else | |
217 | printf("Setup Sectors < 15 - Cannot print kernel version\n"); | |
69370d14 | 218 | |
2262cfee | 219 | /* Determine image type */ |
83088afb GR |
220 | big_image = (bootproto >= 0x0200) && |
221 | (hdr->loadflags & BIG_KERNEL_FLAG); | |
8bde7f77 | 222 | |
95ffaba3 | 223 | /* Determine load address */ |
d3a2bc3f | 224 | if (big_image) |
76539383 | 225 | *load_addressp = BZIMAGE_LOAD_ADDR; |
d3a2bc3f | 226 | else |
76539383 | 227 | *load_addressp = ZIMAGE_LOAD_ADDR; |
8bde7f77 | 228 | |
233dbc11 GB |
229 | printf("Building boot_params at 0x%8.8lx\n", (ulong)setup_base); |
230 | memset(setup_base, 0, sizeof(*setup_base)); | |
231 | setup_base->hdr = params->hdr; | |
8bde7f77 | 232 | |
69370d14 GB |
233 | if (bootproto >= 0x0204) |
234 | kernel_size = hdr->syssize * 16; | |
235 | else | |
236 | kernel_size -= setup_size; | |
8bde7f77 | 237 | |
8bde7f77 | 238 | if (bootproto == 0x0100) { |
83088afb GR |
239 | /* |
240 | * A very old kernel MUST have its real-mode code | |
241 | * loaded at 0x90000 | |
242 | */ | |
42fd8c19 | 243 | if ((ulong)setup_base != 0x90000) { |
2262cfee | 244 | /* Copy the real-mode kernel */ |
83088afb GR |
245 | memmove((void *)0x90000, setup_base, setup_size); |
246 | ||
2262cfee | 247 | /* Copy the command line */ |
83088afb | 248 | memmove((void *)0x99000, |
d3a2bc3f | 249 | (u8 *)setup_base + COMMAND_LINE_OFFSET, |
83088afb | 250 | COMMAND_LINE_SIZE); |
8bde7f77 | 251 | |
83088afb | 252 | /* Relocated */ |
d3a2bc3f | 253 | setup_base = (struct boot_params *)0x90000; |
2262cfee | 254 | } |
8bde7f77 | 255 | |
2262cfee | 256 | /* It is recommended to clear memory up to the 32K mark */ |
d3a2bc3f GB |
257 | memset((u8 *)0x90000 + setup_size, 0, |
258 | SETUP_MAX_SIZE - setup_size); | |
2262cfee | 259 | } |
8bde7f77 | 260 | |
69370d14 GB |
261 | if (big_image) { |
262 | if (kernel_size > BZIMAGE_MAX_SIZE) { | |
263 | printf("Error: bzImage kernel too big! " | |
264 | "(size: %ld, max: %d)\n", | |
265 | kernel_size, BZIMAGE_MAX_SIZE); | |
266 | return 0; | |
267 | } | |
268 | } else if ((kernel_size) > ZIMAGE_MAX_SIZE) { | |
269 | printf("Error: zImage kernel too big! (size: %ld, max: %d)\n", | |
270 | kernel_size, ZIMAGE_MAX_SIZE); | |
271 | return 0; | |
272 | } | |
273 | ||
76539383 SG |
274 | printf("Loading %s at address %lx (%ld bytes)\n", |
275 | big_image ? "bzImage" : "zImage", *load_addressp, kernel_size); | |
69370d14 | 276 | |
76539383 | 277 | memmove((void *)*load_addressp, image + setup_size, kernel_size); |
69370d14 GB |
278 | |
279 | return setup_base; | |
280 | } | |
281 | ||
282 | int setup_zimage(struct boot_params *setup_base, char *cmd_line, int auto_boot, | |
4f96023a | 283 | ulong initrd_addr, ulong initrd_size, ulong cmdline_force) |
69370d14 GB |
284 | { |
285 | struct setup_header *hdr = &setup_base->hdr; | |
c038f3be | 286 | int bootproto = get_boot_protocol(hdr, false); |
69370d14 | 287 | |
b73d61a5 | 288 | log_debug("Setup E820 entries\n"); |
2f91fc40 | 289 | if (IS_ENABLED(CONFIG_COREBOOT_SYSINFO)) { |
e7bae828 SG |
290 | setup_base->e820_entries = cb_install_e820_map( |
291 | ARRAY_SIZE(setup_base->e820_map), setup_base->e820_map); | |
2f91fc40 SG |
292 | } else { |
293 | setup_base->e820_entries = install_e820_map( | |
294 | ARRAY_SIZE(setup_base->e820_map), setup_base->e820_map); | |
e7bae828 | 295 | } |
95ffaba3 | 296 | |
69370d14 GB |
297 | if (bootproto == 0x0100) { |
298 | setup_base->screen_info.cl_magic = COMMAND_LINE_MAGIC; | |
299 | setup_base->screen_info.cl_offset = COMMAND_LINE_OFFSET; | |
300 | } | |
2262cfee | 301 | if (bootproto >= 0x0200) { |
00630f63 | 302 | hdr->type_of_loader = 0x80; /* U-Boot version 0 */ |
2262cfee | 303 | if (initrd_addr) { |
d3a2bc3f GB |
304 | printf("Initial RAM disk at linear address " |
305 | "0x%08lx, size %ld bytes\n", | |
2262cfee | 306 | initrd_addr, initrd_size); |
8bde7f77 | 307 | |
95ffaba3 GR |
308 | hdr->ramdisk_image = initrd_addr; |
309 | hdr->ramdisk_size = initrd_size; | |
2262cfee WD |
310 | } |
311 | } | |
8bde7f77 | 312 | |
2262cfee | 313 | if (bootproto >= 0x0201) { |
95ffaba3 GR |
314 | hdr->heap_end_ptr = HEAP_END_OFFSET; |
315 | hdr->loadflags |= HEAP_FLAG; | |
2262cfee | 316 | } |
8bde7f77 | 317 | |
97d1e0c8 | 318 | if (cmd_line) { |
488d89af SG |
319 | int max_size = 0xff; |
320 | int ret; | |
321 | ||
b73d61a5 | 322 | log_debug("Setup cmdline\n"); |
488d89af SG |
323 | if (bootproto >= 0x0206) |
324 | max_size = hdr->cmdline_size; | |
97d1e0c8 SG |
325 | if (bootproto >= 0x0202) { |
326 | hdr->cmd_line_ptr = (uintptr_t)cmd_line; | |
327 | } else if (bootproto >= 0x0200) { | |
328 | setup_base->screen_info.cl_magic = COMMAND_LINE_MAGIC; | |
329 | setup_base->screen_info.cl_offset = | |
330 | (uintptr_t)cmd_line - (uintptr_t)setup_base; | |
331 | ||
332 | hdr->setup_move_size = 0x9100; | |
333 | } | |
95ffaba3 | 334 | |
97d1e0c8 | 335 | /* build command line at COMMAND_LINE_OFFSET */ |
4f96023a SG |
336 | if (cmdline_force) |
337 | strcpy(cmd_line, (char *)cmdline_force); | |
338 | else | |
339 | build_command_line(cmd_line, auto_boot); | |
440c6645 SG |
340 | if (IS_ENABLED(CONFIG_CMD_BOOTM)) { |
341 | ret = bootm_process_cmdline(cmd_line, max_size, | |
342 | BOOTM_CL_ALL); | |
343 | if (ret) { | |
344 | printf("Cmdline setup failed (max_size=%x, bootproto=%x, err=%d)\n", | |
345 | max_size, bootproto, ret); | |
346 | return ret; | |
347 | } | |
488d89af SG |
348 | } |
349 | printf("Kernel command line: \""); | |
350 | puts(cmd_line); | |
351 | printf("\"\n"); | |
2262cfee | 352 | } |
2262cfee | 353 | |
30b372d4 | 354 | if (IS_ENABLED(CONFIG_INTEL_MID) && bootproto >= 0x0207) |
378960d8 | 355 | hdr->hardware_subarch = X86_SUBARCH_INTEL_MID; |
378960d8 | 356 | |
30b372d4 SG |
357 | if (IS_ENABLED(CONFIG_GENERATE_ACPI_TABLE)) |
358 | setup_base->acpi_rsdp_addr = acpi_get_rsdp_addr(); | |
d905aa8a | 359 | |
b73d61a5 | 360 | log_debug("Setup devicetree\n"); |
5d73292c | 361 | setup_device_tree(hdr, (const void *)env_get_hex("fdtaddr", 0)); |
a4520022 BM |
362 | setup_video(&setup_base->screen_info); |
363 | ||
30b372d4 SG |
364 | if (IS_ENABLED(CONFIG_EFI_STUB)) |
365 | setup_efi_info(&setup_base->efi_info); | |
1fdeacd3 | 366 | |
69370d14 | 367 | return 0; |
2262cfee WD |
368 | } |
369 | ||
bade0ac0 | 370 | int zboot_load(void) |
1d9e4bb7 SG |
371 | { |
372 | struct boot_params *base_ptr; | |
db0c6f47 | 373 | int ret; |
1d9e4bb7 | 374 | |
f82cd7b7 SG |
375 | if (state.base_ptr) { |
376 | struct boot_params *from = (struct boot_params *)state.base_ptr; | |
377 | ||
378 | base_ptr = (struct boot_params *)DEFAULT_SETUP_BASE; | |
a40f890b SG |
379 | log_debug("Building boot_params at 0x%8.8lx\n", |
380 | (ulong)base_ptr); | |
f82cd7b7 SG |
381 | memset(base_ptr, '\0', sizeof(*base_ptr)); |
382 | base_ptr->hdr = from->hdr; | |
383 | } else { | |
384 | base_ptr = load_zimage((void *)state.bzimage_addr, state.bzimage_size, | |
385 | &state.load_address); | |
386 | if (!base_ptr) { | |
387 | puts("## Kernel loading failed ...\n"); | |
c886557c | 388 | return -EINVAL; |
f82cd7b7 | 389 | } |
95ffaba3 | 390 | } |
88f1cd6c | 391 | state.base_ptr = base_ptr; |
c886557c | 392 | |
db0c6f47 SG |
393 | ret = env_set_hex("zbootbase", map_to_sysmem(state.base_ptr)); |
394 | if (!ret) | |
395 | ret = env_set_hex("zbootaddr", state.load_address); | |
396 | if (ret) | |
397 | return ret; | |
398 | ||
c886557c SG |
399 | return 0; |
400 | } | |
401 | ||
bade0ac0 | 402 | int zboot_setup(void) |
c886557c SG |
403 | { |
404 | struct boot_params *base_ptr = state.base_ptr; | |
405 | int ret; | |
406 | ||
407 | ret = setup_zimage(base_ptr, (char *)base_ptr + COMMAND_LINE_OFFSET, | |
408 | 0, state.initrd_addr, state.initrd_size, | |
409 | (ulong)state.cmdline); | |
410 | if (ret) | |
411 | return -EINVAL; | |
412 | ||
413 | return 0; | |
414 | } | |
415 | ||
bade0ac0 | 416 | int zboot_go(void) |
88f1cd6c | 417 | { |
37c9f9cc SG |
418 | struct boot_params *params = state.base_ptr; |
419 | struct setup_header *hdr = ¶ms->hdr; | |
420 | bool image_64bit; | |
421 | ulong entry; | |
88f1cd6c SG |
422 | int ret; |
423 | ||
e9d31b30 | 424 | disable_interrupts(); |
88f1cd6c | 425 | |
37c9f9cc SG |
426 | entry = state.load_address; |
427 | image_64bit = false; | |
428 | if (IS_ENABLED(CONFIG_X86_RUN_64BIT) && | |
429 | (hdr->xloadflags & XLF_KERNEL_64)) { | |
430 | entry += 0x200; | |
431 | image_64bit = true; | |
432 | } | |
433 | ||
69370d14 | 434 | /* we assume that the kernel is in place */ |
37c9f9cc | 435 | ret = boot_linux_kernel((ulong)state.base_ptr, entry, image_64bit); |
c886557c SG |
436 | |
437 | return ret; | |
438 | } | |
439 | ||
d2c485a0 SG |
440 | int zboot_run(ulong addr, ulong size, ulong initrd, ulong initrd_size, |
441 | ulong base, char *cmdline) | |
c886557c SG |
442 | { |
443 | int ret; | |
444 | ||
39f70452 | 445 | zboot_start(addr, size, initrd, initrd_size, base, cmdline); |
c886557c SG |
446 | ret = zboot_load(); |
447 | if (ret) | |
448 | return log_msg_ret("ld", ret); | |
449 | ret = zboot_setup(); | |
450 | if (ret) | |
451 | return log_msg_ret("set", ret); | |
452 | ret = zboot_go(); | |
453 | if (ret) | |
d2c485a0 | 454 | return log_msg_ret("go", ret); |
c886557c SG |
455 | |
456 | return -EFAULT; | |
457 | } | |
458 | ||
631c2b9f SG |
459 | static void print_num(const char *name, ulong value) |
460 | { | |
461 | printf("%-20s: %lx\n", name, value); | |
462 | } | |
463 | ||
464 | static void print_num64(const char *name, u64 value) | |
465 | { | |
466 | printf("%-20s: %llx\n", name, value); | |
467 | } | |
468 | ||
469 | static const char *const e820_type_name[E820_COUNT] = { | |
470 | [E820_RAM] = "RAM", | |
471 | [E820_RESERVED] = "Reserved", | |
472 | [E820_ACPI] = "ACPI", | |
473 | [E820_NVS] = "ACPI NVS", | |
474 | [E820_UNUSABLE] = "Unusable", | |
475 | }; | |
476 | ||
477 | static const char *const bootloader_id[] = { | |
478 | "LILO", | |
479 | "Loadlin", | |
480 | "bootsect-loader", | |
481 | "Syslinux", | |
482 | "Etherboot/gPXE/iPXE", | |
483 | "ELILO", | |
484 | "undefined", | |
485 | "GRUB", | |
486 | "U-Boot", | |
487 | "Xen", | |
488 | "Gujin", | |
489 | "Qemu", | |
490 | "Arcturus Networks uCbootloader", | |
491 | "kexec-tools", | |
492 | "Extended", | |
493 | "Special", | |
494 | "Reserved", | |
495 | "Minimal Linux Bootloader", | |
496 | "OVMF UEFI virtualization stack", | |
497 | }; | |
498 | ||
499 | struct flag_info { | |
500 | uint bit; | |
501 | const char *name; | |
502 | }; | |
503 | ||
504 | static struct flag_info load_flags[] = { | |
505 | { LOADED_HIGH, "loaded-high" }, | |
506 | { QUIET_FLAG, "quiet" }, | |
507 | { KEEP_SEGMENTS, "keep-segments" }, | |
508 | { CAN_USE_HEAP, "can-use-heap" }, | |
509 | }; | |
510 | ||
511 | static struct flag_info xload_flags[] = { | |
512 | { XLF_KERNEL_64, "64-bit-entry" }, | |
513 | { XLF_CAN_BE_LOADED_ABOVE_4G, "can-load-above-4gb" }, | |
514 | { XLF_EFI_HANDOVER_32, "32-efi-handoff" }, | |
515 | { XLF_EFI_HANDOVER_64, "64-efi-handoff" }, | |
516 | { XLF_EFI_KEXEC, "kexec-efi-runtime" }, | |
517 | }; | |
518 | ||
519 | static void print_flags(struct flag_info *flags, int count, uint value) | |
520 | { | |
521 | int i; | |
522 | ||
523 | printf("%-20s:", ""); | |
524 | for (i = 0; i < count; i++) { | |
525 | uint mask = flags[i].bit; | |
526 | ||
527 | if (value & mask) | |
528 | printf(" %s", flags[i].name); | |
529 | } | |
530 | printf("\n"); | |
531 | } | |
532 | ||
533 | static void show_loader(struct setup_header *hdr) | |
534 | { | |
535 | bool version_valid = false; | |
536 | int type, version; | |
537 | const char *name; | |
538 | ||
539 | type = hdr->type_of_loader >> 4; | |
540 | version = hdr->type_of_loader & 0xf; | |
541 | if (type == 0xe) | |
542 | type = 0x10 + hdr->ext_loader_type; | |
543 | version |= hdr->ext_loader_ver << 4; | |
544 | if (!hdr->type_of_loader) { | |
545 | name = "pre-2.00 bootloader"; | |
546 | } else if (hdr->type_of_loader == 0xff) { | |
547 | name = "unknown"; | |
548 | } else if (type < ARRAY_SIZE(bootloader_id)) { | |
549 | name = bootloader_id[type]; | |
550 | version_valid = true; | |
551 | } else { | |
552 | name = "undefined"; | |
553 | } | |
554 | printf("%20s %s", "", name); | |
555 | if (version_valid) | |
556 | printf(", version %x", version); | |
557 | printf("\n"); | |
558 | } | |
559 | ||
cbb607d2 | 560 | void zimage_dump(struct boot_params *base_ptr, bool show_cmdline) |
631c2b9f | 561 | { |
631c2b9f SG |
562 | struct setup_header *hdr; |
563 | const char *version; | |
564 | int i; | |
565 | ||
631c2b9f SG |
566 | printf("Setup located at %p:\n\n", base_ptr); |
567 | print_num64("ACPI RSDP addr", base_ptr->acpi_rsdp_addr); | |
568 | ||
569 | printf("E820: %d entries\n", base_ptr->e820_entries); | |
570 | if (base_ptr->e820_entries) { | |
cbb607d2 | 571 | printf("%12s %10s %s\n", "Addr", "Size", "Type"); |
631c2b9f SG |
572 | for (i = 0; i < base_ptr->e820_entries; i++) { |
573 | struct e820_entry *entry = &base_ptr->e820_map[i]; | |
574 | ||
575 | printf("%12llx %10llx %s\n", entry->addr, entry->size, | |
576 | entry->type < E820_COUNT ? | |
577 | e820_type_name[entry->type] : | |
578 | simple_itoa(entry->type)); | |
579 | } | |
580 | } | |
581 | ||
582 | hdr = &base_ptr->hdr; | |
583 | print_num("Setup sectors", hdr->setup_sects); | |
584 | print_num("Root flags", hdr->root_flags); | |
585 | print_num("Sys size", hdr->syssize); | |
586 | print_num("RAM size", hdr->ram_size); | |
587 | print_num("Video mode", hdr->vid_mode); | |
588 | print_num("Root dev", hdr->root_dev); | |
589 | print_num("Boot flag", hdr->boot_flag); | |
590 | print_num("Jump", hdr->jump); | |
591 | print_num("Header", hdr->header); | |
592 | if (hdr->header == KERNEL_V2_MAGIC) | |
593 | printf("%-20s %s\n", "", "Kernel V2"); | |
594 | else | |
595 | printf("%-20s %s\n", "", "Ancient kernel, using version 100"); | |
596 | print_num("Version", hdr->version); | |
597 | print_num("Real mode switch", hdr->realmode_swtch); | |
90f2b5ab | 598 | print_num("Start sys seg", hdr->start_sys_seg); |
631c2b9f | 599 | print_num("Kernel version", hdr->kernel_version); |
d0dfbf54 SG |
600 | version = zimage_get_kernel_version(base_ptr, |
601 | (void *)state.bzimage_addr); | |
631c2b9f SG |
602 | if (version) |
603 | printf(" @%p: %s\n", version, version); | |
604 | print_num("Type of loader", hdr->type_of_loader); | |
605 | show_loader(hdr); | |
606 | print_num("Load flags", hdr->loadflags); | |
607 | print_flags(load_flags, ARRAY_SIZE(load_flags), hdr->loadflags); | |
608 | print_num("Setup move size", hdr->setup_move_size); | |
609 | print_num("Code32 start", hdr->code32_start); | |
610 | print_num("Ramdisk image", hdr->ramdisk_image); | |
611 | print_num("Ramdisk size", hdr->ramdisk_size); | |
612 | print_num("Bootsect kludge", hdr->bootsect_kludge); | |
613 | print_num("Heap end ptr", hdr->heap_end_ptr); | |
614 | print_num("Ext loader ver", hdr->ext_loader_ver); | |
615 | print_num("Ext loader type", hdr->ext_loader_type); | |
616 | print_num("Command line ptr", hdr->cmd_line_ptr); | |
cbb607d2 | 617 | if (show_cmdline && hdr->cmd_line_ptr) { |
631c2b9f SG |
618 | printf(" "); |
619 | /* Use puts() to avoid limits from CONFIG_SYS_PBSIZE */ | |
4f96023a | 620 | puts((char *)(ulong)hdr->cmd_line_ptr); |
631c2b9f SG |
621 | printf("\n"); |
622 | } | |
623 | print_num("Initrd addr max", hdr->initrd_addr_max); | |
624 | print_num("Kernel alignment", hdr->kernel_alignment); | |
625 | print_num("Relocatable kernel", hdr->relocatable_kernel); | |
626 | print_num("Min alignment", hdr->min_alignment); | |
627 | if (hdr->min_alignment) | |
628 | printf("%-20s: %x\n", "", 1 << hdr->min_alignment); | |
629 | print_num("Xload flags", hdr->xloadflags); | |
630 | print_flags(xload_flags, ARRAY_SIZE(xload_flags), hdr->xloadflags); | |
631 | print_num("Cmdline size", hdr->cmdline_size); | |
632 | print_num("Hardware subarch", hdr->hardware_subarch); | |
633 | print_num64("HW subarch data", hdr->hardware_subarch_data); | |
634 | print_num("Payload offset", hdr->payload_offset); | |
635 | print_num("Payload length", hdr->payload_length); | |
636 | print_num64("Setup data", hdr->setup_data); | |
637 | print_num64("Pref address", hdr->pref_address); | |
638 | print_num("Init size", hdr->init_size); | |
639 | print_num("Handover offset", hdr->handover_offset); | |
640 | if (get_boot_protocol(hdr, false) >= 0x215) | |
641 | print_num("Kernel info offset", hdr->kernel_info_offset); | |
577c4ad0 | 642 | } |
9ad5fdf1 SG |
643 | |
644 | void zboot_start(ulong bzimage_addr, ulong bzimage_size, ulong initrd_addr, | |
645 | ulong initrd_size, ulong base_addr, const char *cmdline) | |
646 | { | |
647 | memset(&state, '\0', sizeof(state)); | |
648 | ||
649 | state.bzimage_size = bzimage_size; | |
650 | state.initrd_addr = initrd_addr; | |
651 | state.initrd_size = initrd_size; | |
652 | if (base_addr) { | |
653 | state.base_ptr = map_sysmem(base_addr, 0); | |
654 | state.load_address = bzimage_addr; | |
655 | } else { | |
656 | state.bzimage_addr = bzimage_addr; | |
657 | } | |
658 | state.cmdline = cmdline; | |
659 | } | |
660 | ||
661 | void zboot_info(void) | |
662 | { | |
663 | printf("Kernel loaded at %08lx, setup_base=%p\n", | |
664 | state.load_address, state.base_ptr); | |
665 | } |