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