]>
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, <[email protected]> |
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 | ||
15 | #include <common.h> | |
09140113 | 16 | #include <command.h> |
cdbff9fc | 17 | #include <env.h> |
36bf446b | 18 | #include <irq_func.h> |
5d73292c | 19 | #include <malloc.h> |
776cc201 | 20 | #include <acpi/acpi_table.h> |
2262cfee WD |
21 | #include <asm/io.h> |
22 | #include <asm/ptrace.h> | |
23 | #include <asm/zimage.h> | |
2262cfee | 24 | #include <asm/byteorder.h> |
0d0ba59c | 25 | #include <asm/bootm.h> |
95ffaba3 | 26 | #include <asm/bootparam.h> |
3cdc18a8 VB |
27 | #ifdef CONFIG_SYS_COREBOOT |
28 | #include <asm/arch/timestamp.h> | |
29 | #endif | |
61e0ea90 | 30 | #include <linux/compiler.h> |
5d73292c | 31 | #include <linux/libfdt.h> |
2262cfee WD |
32 | |
33 | /* | |
34 | * Memory lay-out: | |
8bde7f77 | 35 | * |
2262cfee | 36 | * relative to setup_base (which is 0x90000 currently) |
8bde7f77 WD |
37 | * |
38 | * 0x0000-0x7FFF Real mode kernel | |
2262cfee WD |
39 | * 0x8000-0x8FFF Stack and heap |
40 | * 0x9000-0x90FF Kernel command line | |
41 | */ | |
83088afb GR |
42 | #define DEFAULT_SETUP_BASE 0x90000 |
43 | #define COMMAND_LINE_OFFSET 0x9000 | |
44 | #define HEAP_END_OFFSET 0x8e00 | |
2262cfee | 45 | |
83088afb | 46 | #define COMMAND_LINE_SIZE 2048 |
2262cfee WD |
47 | |
48 | static void build_command_line(char *command_line, int auto_boot) | |
49 | { | |
50 | char *env_command_line; | |
8bde7f77 | 51 | |
2262cfee | 52 | command_line[0] = '\0'; |
8bde7f77 | 53 | |
00caae6d | 54 | env_command_line = env_get("bootargs"); |
8bde7f77 | 55 | |
2262cfee | 56 | /* set console= argument if we use a serial console */ |
83088afb | 57 | if (!strstr(env_command_line, "console=")) { |
00caae6d | 58 | if (!strcmp(env_get("stdout"), "serial")) { |
8bde7f77 | 59 | |
2262cfee | 60 | /* We seem to use serial console */ |
8bde7f77 | 61 | sprintf(command_line, "console=ttyS0,%s ", |
00caae6d | 62 | env_get("baudrate")); |
2262cfee WD |
63 | } |
64 | } | |
8bde7f77 | 65 | |
83088afb | 66 | if (auto_boot) |
2262cfee | 67 | strcat(command_line, "auto "); |
8bde7f77 | 68 | |
83088afb | 69 | if (env_command_line) |
2262cfee | 70 | strcat(command_line, env_command_line); |
8bde7f77 | 71 | |
2262cfee WD |
72 | printf("Kernel command line: \"%s\"\n", command_line); |
73 | } | |
74 | ||
69370d14 | 75 | static int kernel_magic_ok(struct setup_header *hdr) |
2262cfee | 76 | { |
95ffaba3 | 77 | if (KERNEL_MAGIC != hdr->boot_flag) { |
d3a2bc3f GB |
78 | printf("Error: Invalid Boot Flag " |
79 | "(found 0x%04x, expected 0x%04x)\n", | |
80 | hdr->boot_flag, KERNEL_MAGIC); | |
2262cfee | 81 | return 0; |
95ffaba3 GR |
82 | } else { |
83 | printf("Valid Boot Flag\n"); | |
69370d14 | 84 | return 1; |
2262cfee | 85 | } |
69370d14 | 86 | } |
8bde7f77 | 87 | |
69370d14 GB |
88 | static int get_boot_protocol(struct setup_header *hdr) |
89 | { | |
90 | if (hdr->header == KERNEL_V2_MAGIC) { | |
95ffaba3 | 91 | printf("Magic signature found\n"); |
69370d14 | 92 | return hdr->version; |
2262cfee WD |
93 | } else { |
94 | /* Very old kernel */ | |
95ffaba3 | 95 | printf("Magic signature not found\n"); |
69370d14 | 96 | return 0x0100; |
2262cfee | 97 | } |
69370d14 GB |
98 | } |
99 | ||
5d73292c IG |
100 | static int setup_device_tree(struct setup_header *hdr, const void *fdt_blob) |
101 | { | |
102 | int bootproto = get_boot_protocol(hdr); | |
103 | struct setup_data *sd; | |
104 | int size; | |
105 | ||
106 | if (bootproto < 0x0209) | |
107 | return -ENOTSUPP; | |
108 | ||
109 | if (!fdt_blob) | |
110 | return 0; | |
111 | ||
112 | size = fdt_totalsize(fdt_blob); | |
113 | if (size < 0) | |
114 | return -EINVAL; | |
115 | ||
116 | size += sizeof(struct setup_data); | |
117 | sd = (struct setup_data *)malloc(size); | |
118 | if (!sd) { | |
119 | printf("Not enough memory for DTB setup data\n"); | |
120 | return -ENOMEM; | |
121 | } | |
122 | ||
123 | sd->next = hdr->setup_data; | |
124 | sd->type = SETUP_DTB; | |
125 | sd->len = fdt_totalsize(fdt_blob); | |
126 | memcpy(sd->data, fdt_blob, sd->len); | |
127 | hdr->setup_data = (unsigned long)sd; | |
128 | ||
129 | return 0; | |
130 | } | |
131 | ||
69370d14 | 132 | struct boot_params *load_zimage(char *image, unsigned long kernel_size, |
76539383 | 133 | ulong *load_addressp) |
69370d14 GB |
134 | { |
135 | struct boot_params *setup_base; | |
136 | int setup_size; | |
137 | int bootproto; | |
138 | int big_image; | |
139 | ||
140 | struct boot_params *params = (struct boot_params *)image; | |
141 | struct setup_header *hdr = ¶ms->hdr; | |
142 | ||
143 | /* base address for real-mode segment */ | |
144 | setup_base = (struct boot_params *)DEFAULT_SETUP_BASE; | |
145 | ||
146 | if (!kernel_magic_ok(hdr)) | |
147 | return 0; | |
8bde7f77 | 148 | |
2262cfee | 149 | /* determine size of setup */ |
95ffaba3 GR |
150 | if (0 == hdr->setup_sects) { |
151 | printf("Setup Sectors = 0 (defaulting to 4)\n"); | |
2262cfee WD |
152 | setup_size = 5 * 512; |
153 | } else { | |
95ffaba3 | 154 | setup_size = (hdr->setup_sects + 1) * 512; |
2262cfee | 155 | } |
8bde7f77 | 156 | |
95ffaba3 GR |
157 | printf("Setup Size = 0x%8.8lx\n", (ulong)setup_size); |
158 | ||
83088afb | 159 | if (setup_size > SETUP_MAX_SIZE) |
2262cfee | 160 | printf("Error: Setup is too large (%d bytes)\n", setup_size); |
8bde7f77 | 161 | |
69370d14 GB |
162 | /* determine boot protocol version */ |
163 | bootproto = get_boot_protocol(hdr); | |
164 | ||
165 | printf("Using boot protocol version %x.%02x\n", | |
166 | (bootproto & 0xff00) >> 8, bootproto & 0xff); | |
167 | ||
168 | if (bootproto >= 0x0200) { | |
169 | if (hdr->setup_sects >= 15) { | |
170 | printf("Linux kernel version %s\n", | |
171 | (char *)params + | |
172 | hdr->kernel_version + 0x200); | |
173 | } else { | |
174 | printf("Setup Sectors < 15 - " | |
175 | "Cannot print kernel version.\n"); | |
176 | } | |
177 | } | |
178 | ||
2262cfee | 179 | /* Determine image type */ |
83088afb GR |
180 | big_image = (bootproto >= 0x0200) && |
181 | (hdr->loadflags & BIG_KERNEL_FLAG); | |
8bde7f77 | 182 | |
95ffaba3 | 183 | /* Determine load address */ |
d3a2bc3f | 184 | if (big_image) |
76539383 | 185 | *load_addressp = BZIMAGE_LOAD_ADDR; |
d3a2bc3f | 186 | else |
76539383 | 187 | *load_addressp = ZIMAGE_LOAD_ADDR; |
8bde7f77 | 188 | |
233dbc11 GB |
189 | printf("Building boot_params at 0x%8.8lx\n", (ulong)setup_base); |
190 | memset(setup_base, 0, sizeof(*setup_base)); | |
191 | setup_base->hdr = params->hdr; | |
8bde7f77 | 192 | |
69370d14 GB |
193 | if (bootproto >= 0x0204) |
194 | kernel_size = hdr->syssize * 16; | |
195 | else | |
196 | kernel_size -= setup_size; | |
8bde7f77 | 197 | |
8bde7f77 | 198 | if (bootproto == 0x0100) { |
83088afb GR |
199 | /* |
200 | * A very old kernel MUST have its real-mode code | |
201 | * loaded at 0x90000 | |
202 | */ | |
42fd8c19 | 203 | if ((ulong)setup_base != 0x90000) { |
2262cfee | 204 | /* Copy the real-mode kernel */ |
83088afb GR |
205 | memmove((void *)0x90000, setup_base, setup_size); |
206 | ||
2262cfee | 207 | /* Copy the command line */ |
83088afb | 208 | memmove((void *)0x99000, |
d3a2bc3f | 209 | (u8 *)setup_base + COMMAND_LINE_OFFSET, |
83088afb | 210 | COMMAND_LINE_SIZE); |
8bde7f77 | 211 | |
83088afb | 212 | /* Relocated */ |
d3a2bc3f | 213 | setup_base = (struct boot_params *)0x90000; |
2262cfee | 214 | } |
8bde7f77 | 215 | |
2262cfee | 216 | /* It is recommended to clear memory up to the 32K mark */ |
d3a2bc3f GB |
217 | memset((u8 *)0x90000 + setup_size, 0, |
218 | SETUP_MAX_SIZE - setup_size); | |
2262cfee | 219 | } |
8bde7f77 | 220 | |
69370d14 GB |
221 | if (big_image) { |
222 | if (kernel_size > BZIMAGE_MAX_SIZE) { | |
223 | printf("Error: bzImage kernel too big! " | |
224 | "(size: %ld, max: %d)\n", | |
225 | kernel_size, BZIMAGE_MAX_SIZE); | |
226 | return 0; | |
227 | } | |
228 | } else if ((kernel_size) > ZIMAGE_MAX_SIZE) { | |
229 | printf("Error: zImage kernel too big! (size: %ld, max: %d)\n", | |
230 | kernel_size, ZIMAGE_MAX_SIZE); | |
231 | return 0; | |
232 | } | |
233 | ||
76539383 SG |
234 | printf("Loading %s at address %lx (%ld bytes)\n", |
235 | big_image ? "bzImage" : "zImage", *load_addressp, kernel_size); | |
69370d14 | 236 | |
76539383 | 237 | memmove((void *)*load_addressp, image + setup_size, kernel_size); |
69370d14 GB |
238 | |
239 | return setup_base; | |
240 | } | |
241 | ||
242 | int setup_zimage(struct boot_params *setup_base, char *cmd_line, int auto_boot, | |
243 | unsigned long initrd_addr, unsigned long initrd_size) | |
244 | { | |
245 | struct setup_header *hdr = &setup_base->hdr; | |
246 | int bootproto = get_boot_protocol(hdr); | |
247 | ||
69370d14 GB |
248 | setup_base->e820_entries = install_e820_map( |
249 | ARRAY_SIZE(setup_base->e820_map), setup_base->e820_map); | |
95ffaba3 | 250 | |
69370d14 GB |
251 | if (bootproto == 0x0100) { |
252 | setup_base->screen_info.cl_magic = COMMAND_LINE_MAGIC; | |
253 | setup_base->screen_info.cl_offset = COMMAND_LINE_OFFSET; | |
254 | } | |
2262cfee | 255 | if (bootproto >= 0x0200) { |
95ffaba3 GR |
256 | hdr->type_of_loader = 8; |
257 | ||
2262cfee | 258 | if (initrd_addr) { |
d3a2bc3f GB |
259 | printf("Initial RAM disk at linear address " |
260 | "0x%08lx, size %ld bytes\n", | |
2262cfee | 261 | initrd_addr, initrd_size); |
8bde7f77 | 262 | |
95ffaba3 GR |
263 | hdr->ramdisk_image = initrd_addr; |
264 | hdr->ramdisk_size = initrd_size; | |
2262cfee WD |
265 | } |
266 | } | |
8bde7f77 | 267 | |
2262cfee | 268 | if (bootproto >= 0x0201) { |
95ffaba3 GR |
269 | hdr->heap_end_ptr = HEAP_END_OFFSET; |
270 | hdr->loadflags |= HEAP_FLAG; | |
2262cfee | 271 | } |
8bde7f77 | 272 | |
97d1e0c8 SG |
273 | if (cmd_line) { |
274 | if (bootproto >= 0x0202) { | |
275 | hdr->cmd_line_ptr = (uintptr_t)cmd_line; | |
276 | } else if (bootproto >= 0x0200) { | |
277 | setup_base->screen_info.cl_magic = COMMAND_LINE_MAGIC; | |
278 | setup_base->screen_info.cl_offset = | |
279 | (uintptr_t)cmd_line - (uintptr_t)setup_base; | |
280 | ||
281 | hdr->setup_move_size = 0x9100; | |
282 | } | |
95ffaba3 | 283 | |
97d1e0c8 SG |
284 | /* build command line at COMMAND_LINE_OFFSET */ |
285 | build_command_line(cmd_line, auto_boot); | |
2262cfee | 286 | } |
2262cfee | 287 | |
378960d8 AS |
288 | #ifdef CONFIG_INTEL_MID |
289 | if (bootproto >= 0x0207) | |
290 | hdr->hardware_subarch = X86_SUBARCH_INTEL_MID; | |
291 | #endif | |
292 | ||
d905aa8a AS |
293 | #ifdef CONFIG_GENERATE_ACPI_TABLE |
294 | setup_base->acpi_rsdp_addr = acpi_get_rsdp_addr(); | |
295 | #endif | |
296 | ||
5d73292c | 297 | setup_device_tree(hdr, (const void *)env_get_hex("fdtaddr", 0)); |
a4520022 BM |
298 | setup_video(&setup_base->screen_info); |
299 | ||
1fdeacd3 BM |
300 | #ifdef CONFIG_EFI_STUB |
301 | setup_efi_info(&setup_base->efi_info); | |
302 | #endif | |
303 | ||
69370d14 | 304 | return 0; |
2262cfee WD |
305 | } |
306 | ||
8e18e6e1 GR |
307 | void setup_pcat_compatibility(void) |
308 | __attribute__((weak, alias("__setup_pcat_compatibility"))); | |
309 | ||
310 | void __setup_pcat_compatibility(void) | |
311 | { | |
312 | } | |
313 | ||
09140113 | 314 | int do_zboot(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) |
95ffaba3 | 315 | { |
69370d14 | 316 | struct boot_params *base_ptr; |
abe98f49 | 317 | void *bzImage_addr = NULL; |
76539383 | 318 | ulong load_address; |
abe98f49 | 319 | char *s; |
95ffaba3 | 320 | ulong bzImage_size = 0; |
6f9d9986 GB |
321 | ulong initrd_addr = 0; |
322 | ulong initrd_size = 0; | |
95ffaba3 GR |
323 | |
324 | disable_interrupts(); | |
325 | ||
326 | /* Setup board for maximum PC/AT Compatibility */ | |
327 | setup_pcat_compatibility(); | |
328 | ||
d3a2bc3f | 329 | if (argc >= 2) { |
abe98f49 GR |
330 | /* argv[1] holds the address of the bzImage */ |
331 | s = argv[1]; | |
d3a2bc3f | 332 | } else { |
00caae6d | 333 | s = env_get("fileaddr"); |
d3a2bc3f | 334 | } |
abe98f49 GR |
335 | |
336 | if (s) | |
337 | bzImage_addr = (void *)simple_strtoul(s, NULL, 16); | |
95ffaba3 | 338 | |
6f9d9986 | 339 | if (argc >= 3) { |
abe98f49 | 340 | /* argv[2] holds the size of the bzImage */ |
95ffaba3 | 341 | bzImage_size = simple_strtoul(argv[2], NULL, 16); |
6f9d9986 GB |
342 | } |
343 | ||
344 | if (argc >= 4) | |
345 | initrd_addr = simple_strtoul(argv[3], NULL, 16); | |
346 | if (argc >= 5) | |
347 | initrd_size = simple_strtoul(argv[4], NULL, 16); | |
95ffaba3 | 348 | |
d3a2bc3f | 349 | /* Lets look for */ |
69370d14 | 350 | base_ptr = load_zimage(bzImage_addr, bzImage_size, &load_address); |
95ffaba3 | 351 | |
83088afb | 352 | if (!base_ptr) { |
2c363cb0 | 353 | puts("## Kernel loading failed ...\n"); |
69370d14 | 354 | return -1; |
95ffaba3 | 355 | } |
69370d14 | 356 | if (setup_zimage(base_ptr, (char *)base_ptr + COMMAND_LINE_OFFSET, |
6f9d9986 | 357 | 0, initrd_addr, initrd_size)) { |
2c363cb0 | 358 | puts("Setting up boot parameters failed ...\n"); |
69370d14 GB |
359 | return -1; |
360 | } | |
361 | ||
69370d14 | 362 | /* we assume that the kernel is in place */ |
76539383 | 363 | return boot_linux_kernel((ulong)base_ptr, load_address, false); |
95ffaba3 GR |
364 | } |
365 | ||
366 | U_BOOT_CMD( | |
6f9d9986 | 367 | zboot, 5, 0, do_zboot, |
95ffaba3 | 368 | "Boot bzImage", |
6f9d9986 GB |
369 | "[addr] [size] [initrd addr] [initrd size]\n" |
370 | " addr - The optional starting address of the bzimage.\n" | |
371 | " If not set it defaults to the environment\n" | |
372 | " variable \"fileaddr\".\n" | |
373 | " size - The optional size of the bzimage. Defaults to\n" | |
374 | " zero.\n" | |
375 | " initrd addr - The address of the initrd image to use, if any.\n" | |
376 | " initrd size - The size of the initrd image to use, if any.\n" | |
95ffaba3 | 377 | ); |