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