]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - arch/x86/lib/zimage.c
x86: Add support for booting Linux using the 32 bit boot protocol
[karo-tx-uboot.git] / arch / x86 / lib / zimage.c
1 /*
2  * Copyright (c) 2011 The Chromium OS Authors.
3  * (C) Copyright 2002
4  * Daniel Engström, Omicron Ceti AB, <daniel@omicron.se>
5  *
6  * See file CREDITS for list of people who contributed to this
7  * project.
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation; either version 2 of
12  * the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
22  * MA 02111-1307 USA
23  */
24
25 /*
26  * Linux x86 zImage and bzImage loading
27  *
28  * based on the procdure described in
29  * linux/Documentation/i386/boot.txt
30  */
31
32 #include <common.h>
33 #include <asm/io.h>
34 #include <asm/ptrace.h>
35 #include <asm/zimage.h>
36 #include <asm/realmode.h>
37 #include <asm/byteorder.h>
38 #include <asm/bootparam.h>
39
40 /*
41  * Memory lay-out:
42  *
43  * relative to setup_base (which is 0x90000 currently)
44  *
45  *      0x0000-0x7FFF   Real mode kernel
46  *      0x8000-0x8FFF   Stack and heap
47  *      0x9000-0x90FF   Kernel command line
48  */
49 #define DEFAULT_SETUP_BASE      0x90000
50 #define COMMAND_LINE_OFFSET     0x9000
51 #define HEAP_END_OFFSET         0x8e00
52
53 #define COMMAND_LINE_SIZE       2048
54
55 unsigned generic_install_e820_map(unsigned max_entries,
56                                   struct e820entry *entries)
57 {
58         return 0;
59 }
60
61 unsigned install_e820_map(unsigned max_entries,
62                           struct e820entry *entries)
63         __attribute__((weak, alias("generic_install_e820_map")));
64
65 static void build_command_line(char *command_line, int auto_boot)
66 {
67         char *env_command_line;
68
69         command_line[0] = '\0';
70
71         env_command_line =  getenv("bootargs");
72
73         /* set console= argument if we use a serial console */
74         if (!strstr(env_command_line, "console=")) {
75                 if (!strcmp(getenv("stdout"), "serial")) {
76
77                         /* We seem to use serial console */
78                         sprintf(command_line, "console=ttyS0,%s ",
79                                 getenv("baudrate"));
80                 }
81         }
82
83         if (auto_boot)
84                 strcat(command_line, "auto ");
85
86         if (env_command_line)
87                 strcat(command_line, env_command_line);
88
89         printf("Kernel command line: \"%s\"\n", command_line);
90 }
91
92 void *load_zimage(char *image, unsigned long kernel_size,
93                   unsigned long initrd_addr, unsigned long initrd_size,
94                   int auto_boot, void **load_address)
95 {
96         struct boot_params *setup_base;
97         int setup_size;
98         int bootproto;
99         int big_image;
100
101         struct boot_params *params = (struct boot_params *)image;
102         struct setup_header *hdr = &params->hdr;
103
104         /* base address for real-mode segment */
105         setup_base = (struct boot_params *)DEFAULT_SETUP_BASE;
106
107         if (KERNEL_MAGIC != hdr->boot_flag) {
108                 printf("Error: Invalid Boot Flag "
109                         "(found 0x%04x, expected 0x%04x)\n",
110                         hdr->boot_flag, KERNEL_MAGIC);
111                 return 0;
112         } else {
113                 printf("Valid Boot Flag\n");
114         }
115
116         /* determine boot protocol version */
117         if (KERNEL_V2_MAGIC == hdr->header) {
118                 printf("Magic signature found\n");
119
120                 bootproto = hdr->version;
121         } else {
122                 /* Very old kernel */
123                 printf("Magic signature not found\n");
124                 bootproto = 0x0100;
125         }
126
127         /* determine size of setup */
128         if (0 == hdr->setup_sects) {
129                 printf("Setup Sectors = 0 (defaulting to 4)\n");
130                 setup_size = 5 * 512;
131         } else {
132                 setup_size = (hdr->setup_sects + 1) * 512;
133         }
134
135         printf("Setup Size = 0x%8.8lx\n", (ulong)setup_size);
136
137         if (setup_size > SETUP_MAX_SIZE)
138                 printf("Error: Setup is too large (%d bytes)\n", setup_size);
139
140         /* Determine image type */
141         big_image = (bootproto >= 0x0200) &&
142                     (hdr->loadflags & BIG_KERNEL_FLAG);
143
144         /* Determine load address */
145         if (big_image)
146                 *load_address = (void *)BZIMAGE_LOAD_ADDR;
147         else
148                 *load_address = (void *)ZIMAGE_LOAD_ADDR;
149
150 #if defined CONFIG_ZBOOT_32
151         printf("Building boot_params at 0x%8.8lx\n", (ulong)setup_base);
152         memset(setup_base, 0, sizeof(*setup_base));
153         setup_base->hdr = params->hdr;
154
155         setup_base->e820_entries = install_e820_map(
156                 ARRAY_SIZE(setup_base->e820_map), setup_base->e820_map);
157 #else
158         /* load setup */
159         printf("Moving Real-Mode Code to 0x%8.8lx (%d bytes)\n",
160                (ulong)setup_base, setup_size);
161         memmove(setup_base, image, setup_size);
162 #endif
163
164         printf("Using boot protocol version %x.%02x\n",
165                (bootproto & 0xff00) >> 8, bootproto & 0xff);
166
167         if (bootproto == 0x0100) {
168                 setup_base->screen_info.cl_magic = COMMAND_LINE_MAGIC;
169                 setup_base->screen_info.cl_offset = COMMAND_LINE_OFFSET;
170
171                 /*
172                  * A very old kernel MUST have its real-mode code
173                  * loaded at 0x90000
174                  */
175                 if ((u32)setup_base != 0x90000) {
176                         /* Copy the real-mode kernel */
177                         memmove((void *)0x90000, setup_base, setup_size);
178
179                         /* Copy the command line */
180                         memmove((void *)0x99000,
181                                 (u8 *)setup_base + COMMAND_LINE_OFFSET,
182                                 COMMAND_LINE_SIZE);
183
184                          /* Relocated */
185                         setup_base = (struct boot_params *)0x90000;
186                 }
187
188                 /* It is recommended to clear memory up to the 32K mark */
189                 memset((u8 *)0x90000 + setup_size, 0,
190                        SETUP_MAX_SIZE - setup_size);
191         }
192
193         /* We are now setting up the real-mode version of the header */
194         hdr = &setup_base->hdr;
195
196         if (bootproto >= 0x0200) {
197                 hdr->type_of_loader = 8;
198
199                 if (hdr->setup_sects >= 15) {
200                         printf("Linux kernel version %s\n",
201                                (char *)params +
202                                hdr->kernel_version + 0x200);
203                 } else {
204                         printf("Setup Sectors < 15 - "
205                                "Cannot print kernel version.\n");
206                 }
207
208                 if (initrd_addr) {
209                         printf("Initial RAM disk at linear address "
210                                "0x%08lx, size %ld bytes\n",
211                                initrd_addr, initrd_size);
212
213                         hdr->ramdisk_image = initrd_addr;
214                         hdr->ramdisk_size = initrd_size;
215                 }
216         }
217
218         if (bootproto >= 0x0201) {
219                 hdr->heap_end_ptr = HEAP_END_OFFSET;
220                 hdr->loadflags |= HEAP_FLAG;
221         }
222
223         if (bootproto >= 0x0202) {
224                 hdr->cmd_line_ptr =
225                         (uintptr_t)setup_base + COMMAND_LINE_OFFSET;
226         } else if (bootproto >= 0x0200) {
227                 setup_base->screen_info.cl_magic = COMMAND_LINE_MAGIC;
228                 setup_base->screen_info.cl_offset = COMMAND_LINE_OFFSET;
229
230                 hdr->setup_move_size = 0x9100;
231         }
232
233         if (bootproto >= 0x0204)
234                 kernel_size = hdr->syssize * 16;
235         else
236                 kernel_size -= setup_size;
237
238
239         if (big_image) {
240                 if ((kernel_size) > BZIMAGE_MAX_SIZE) {
241                         printf("Error: bzImage kernel too big! "
242                                 "(size: %ld, max: %d)\n",
243                                 kernel_size, BZIMAGE_MAX_SIZE);
244                         return 0;
245                 }
246         } else if ((kernel_size) > ZIMAGE_MAX_SIZE) {
247                 printf("Error: zImage kernel too big! (size: %ld, max: %d)\n",
248                        kernel_size, ZIMAGE_MAX_SIZE);
249                 return 0;
250         }
251
252         /* build command line at COMMAND_LINE_OFFSET */
253         build_command_line((char *)setup_base + COMMAND_LINE_OFFSET, auto_boot);
254
255         printf("Loading %czImage at address 0x%08x (%ld bytes)\n",
256                big_image ? 'b' : ' ', (u32)*load_address, kernel_size);
257
258         memmove(*load_address, image + setup_size, kernel_size);
259
260         /* ready for booting */
261         return setup_base;
262 }
263
264 void boot_zimage(void *setup_base, void *load_address)
265 {
266         printf("\nStarting kernel ...\n\n");
267
268 #if defined CONFIG_ZBOOT_32
269         /*
270          * Set %ebx, %ebp, and %edi to 0, %esi to point to the boot_params
271          * structure, and then jump to the kernel. We assume that %cs is
272          * 0x10, 4GB flat, and read/execute, and the data segments are 0x18,
273          * 4GB flat, and read/write. U-boot is setting them up that way for
274          * itself in arch/i386/cpu/cpu.c.
275          */
276         __asm__ __volatile__ (
277         "movl $0, %%ebp         \n"
278         "cli                    \n"
279         "jmp %[kernel_entry]    \n"
280         :: [kernel_entry]"a"(load_address),
281            [boot_params] "S"(setup_base),
282            "b"(0), "D"(0)
283         :  "%ebp"
284         );
285 #else
286         struct pt_regs regs;
287
288         memset(&regs, 0, sizeof(struct pt_regs));
289         regs.xds = (u32)setup_base >> 4;
290         regs.xes = regs.xds;
291         regs.xss = regs.xds;
292         regs.esp = 0x9000;
293         regs.eflags = 0;
294         enter_realmode(((u32)setup_base + SETUP_START_OFFSET) >> 4, 0,
295                        &regs, &regs);
296 #endif
297 }
298
299 int do_zboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
300 {
301         void *base_ptr;
302         void *bzImage_addr = NULL;
303         void *load_address;
304         char *s;
305         ulong bzImage_size = 0;
306
307         disable_interrupts();
308
309         /* Setup board for maximum PC/AT Compatibility */
310         setup_pcat_compatibility();
311
312         if (argc >= 2) {
313                 /* argv[1] holds the address of the bzImage */
314                 s = argv[1];
315         } else {
316                 s = getenv("fileaddr");
317         }
318
319         if (s)
320                 bzImage_addr = (void *)simple_strtoul(s, NULL, 16);
321
322         if (argc >= 3)
323                 /* argv[2] holds the size of the bzImage */
324                 bzImage_size = simple_strtoul(argv[2], NULL, 16);
325
326         /* Lets look for */
327         base_ptr = load_zimage(bzImage_addr, bzImage_size, 0, 0, 0,
328                 &load_address);
329
330         if (!base_ptr) {
331                 printf("## Kernel loading failed ...\n");
332         } else {
333                 printf("## Transferring control to Linux "
334                        "(at address %08x) ...\n",
335                        (u32)base_ptr);
336
337                 /* we assume that the kernel is in place */
338                 boot_zimage(base_ptr, load_address);
339                 /* does not return */
340         }
341
342         return -1;
343 }
344
345 U_BOOT_CMD(
346         zboot, 2, 0,    do_zboot,
347         "Boot bzImage",
348         ""
349 );