Updated PCI IDs to latest snapshot.
[tangerine.git] / arch / common / boot / grub2 / loader / powerpc / ieee1275 / linux.c
blob3b85341358ac14dbf997ea6753730091a2ccd4d6
1 /* linux.c - boot Linux */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
20 #include <grub/elf.h>
21 #include <grub/elfload.h>
22 #include <grub/loader.h>
23 #include <grub/dl.h>
24 #include <grub/mm.h>
25 #include <grub/rescue.h>
26 #include <grub/misc.h>
27 #include <grub/ieee1275/ieee1275.h>
28 #include <grub/machine/loader.h>
30 #define ELF32_LOADMASK (0xc0000000UL)
31 #define ELF64_LOADMASK (0xc000000000000000ULL)
33 static grub_dl_t my_mod;
35 static int loaded;
37 static grub_addr_t initrd_addr;
38 static grub_size_t initrd_size;
40 static grub_addr_t linux_addr;
41 static grub_size_t linux_size;
43 static char *linux_args;
45 typedef void (*kernel_entry_t) (void *, unsigned long, int (void *),
46 unsigned long, unsigned long);
48 static grub_err_t
49 grub_linux_boot (void)
51 kernel_entry_t linuxmain;
52 grub_ssize_t actual;
54 /* Set the command line arguments. */
55 grub_ieee1275_set_property (grub_ieee1275_chosen, "bootargs", linux_args,
56 grub_strlen (linux_args) + 1, &actual);
58 grub_dprintf ("loader", "Entry point: 0x%x\n", linux_addr);
59 grub_dprintf ("loader", "Initrd at: 0x%x, size 0x%x\n", initrd_addr,
60 initrd_size);
61 grub_dprintf ("loader", "Boot arguments: %s\n", linux_args);
62 grub_dprintf ("loader", "Jumping to Linux...\n");
64 /* Boot the kernel. */
65 linuxmain = (kernel_entry_t) linux_addr;
66 linuxmain ((void *) initrd_addr, initrd_size, grub_ieee1275_entry_fn, 0, 0);
68 return GRUB_ERR_NONE;
71 static grub_err_t
72 grub_linux_release_mem (void)
74 grub_free (linux_args);
75 linux_args = 0;
77 if (linux_addr && grub_ieee1275_release (linux_addr, linux_size))
78 return grub_error (GRUB_ERR_OUT_OF_MEMORY, "Can not release memory");
80 if (initrd_addr && grub_ieee1275_release (initrd_addr, initrd_size))
81 return grub_error (GRUB_ERR_OUT_OF_MEMORY, "Can not release memory");
83 linux_addr = 0;
84 initrd_addr = 0;
86 return GRUB_ERR_NONE;
89 static grub_err_t
90 grub_linux_unload (void)
92 grub_err_t err;
94 err = grub_linux_release_mem ();
95 grub_dl_unref (my_mod);
97 loaded = 0;
99 return err;
102 static grub_err_t
103 grub_linux_load32 (grub_elf_t elf)
105 Elf32_Addr entry;
106 int found_addr = 0;
108 /* Linux's entry point incorrectly contains a virtual address. */
109 entry = elf->ehdr.ehdr32.e_entry & ~ELF32_LOADMASK;
110 if (entry == 0)
111 entry = 0x01400000;
113 linux_size = grub_elf32_size (elf);
114 if (linux_size == 0)
115 return grub_errno;
116 /* Pad it; the kernel scribbles over memory beyond its load address. */
117 linux_size += 0x100000;
119 /* On some systems, firmware occupies the memory we're trying to use.
120 * Happily, Linux can be loaded anywhere (it relocates itself). Iterate
121 * until we find an open area. */
122 for (linux_addr = entry; linux_addr < entry + 200 * 0x100000; linux_addr += 0x100000)
124 grub_dprintf ("loader", "Attempting to claim at 0x%x, size 0x%x.\n",
125 linux_addr, linux_size);
126 found_addr = grub_claimmap (linux_addr, linux_size);
127 if (found_addr != -1)
128 break;
130 if (found_addr == -1)
131 return grub_error (GRUB_ERR_OUT_OF_MEMORY, "Could not claim memory.");
133 /* Now load the segments into the area we claimed. */
134 auto grub_err_t offset_phdr (Elf32_Phdr *phdr, grub_addr_t *addr);
135 grub_err_t offset_phdr (Elf32_Phdr *phdr, grub_addr_t *addr)
137 /* Linux's program headers incorrectly contain virtual addresses.
138 * Translate those to physical, and offset to the area we claimed. */
139 *addr = (phdr->p_paddr & ~ELF32_LOADMASK) + linux_addr;
140 return 0;
142 return grub_elf32_load (elf, offset_phdr, 0, 0);
145 static grub_err_t
146 grub_linux_load64 (grub_elf_t elf)
148 Elf64_Addr entry;
149 int found_addr = 0;
151 /* Linux's entry point incorrectly contains a virtual address. */
152 entry = elf->ehdr.ehdr64.e_entry & ~ELF64_LOADMASK;
153 if (entry == 0)
154 entry = 0x01400000;
156 linux_size = grub_elf64_size (elf);
157 if (linux_size == 0)
158 return grub_errno;
159 /* Pad it; the kernel scribbles over memory beyond its load address. */
160 linux_size += 0x100000;
162 /* On some systems, firmware occupies the memory we're trying to use.
163 * Happily, Linux can be loaded anywhere (it relocates itself). Iterate
164 * until we find an open area. */
165 for (linux_addr = entry; linux_addr < entry + 200 * 0x100000; linux_addr += 0x100000)
167 grub_dprintf ("loader", "Attempting to claim at 0x%x, size 0x%x.\n",
168 linux_addr, linux_size);
169 found_addr = grub_claimmap (linux_addr, linux_size);
170 if (found_addr != -1)
171 break;
173 if (found_addr == -1)
174 return grub_error (GRUB_ERR_OUT_OF_MEMORY, "Could not claim memory.");
176 /* Now load the segments into the area we claimed. */
177 auto grub_err_t offset_phdr (Elf64_Phdr *phdr, grub_addr_t *addr);
178 grub_err_t offset_phdr (Elf64_Phdr *phdr, grub_addr_t *addr)
180 /* Linux's program headers incorrectly contain virtual addresses.
181 * Translate those to physical, and offset to the area we claimed. */
182 *addr = (phdr->p_paddr & ~ELF64_LOADMASK) + linux_addr;
183 return 0;
185 return grub_elf64_load (elf, offset_phdr, 0, 0);
188 void
189 grub_rescue_cmd_linux (int argc, char *argv[])
191 grub_elf_t elf = 0;
192 int i;
193 int size;
194 char *dest;
196 grub_dl_ref (my_mod);
198 if (argc == 0)
200 grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified");
201 goto out;
204 elf = grub_elf_open (argv[0]);
205 if (! elf)
206 goto out;
208 if (elf->ehdr.ehdr32.e_type != ET_EXEC)
210 grub_error (GRUB_ERR_UNKNOWN_OS,
211 "This ELF file is not of the right type\n");
212 goto out;
215 /* Release the previously used memory. */
216 grub_loader_unset ();
218 if (grub_elf_is_elf32 (elf))
219 grub_linux_load32 (elf);
220 else
221 if (grub_elf_is_elf64 (elf))
222 grub_linux_load64 (elf);
223 else
225 grub_error (GRUB_ERR_BAD_FILE_TYPE, "Unknown ELF class");
226 goto out;
229 size = sizeof ("BOOT_IMAGE=") + grub_strlen (argv[0]);
230 for (i = 0; i < argc; i++)
231 size += grub_strlen (argv[i]) + 1;
233 linux_args = grub_malloc (size);
234 if (! linux_args)
235 goto out;
237 /* Specify the boot file. */
238 dest = grub_stpcpy (linux_args, "BOOT_IMAGE=");
239 dest = grub_stpcpy (dest, argv[0]);
241 for (i = 1; i < argc; i++)
243 *dest++ = ' ';
244 dest = grub_stpcpy (dest, argv[i]);
247 out:
249 if (elf)
250 grub_elf_close (elf);
252 if (grub_errno != GRUB_ERR_NONE)
254 grub_linux_release_mem ();
255 grub_dl_unref (my_mod);
256 loaded = 0;
258 else
260 grub_loader_set (grub_linux_boot, grub_linux_unload, 1);
261 initrd_addr = 0;
262 loaded = 1;
266 void
267 grub_rescue_cmd_initrd (int argc, char *argv[])
269 grub_file_t file = 0;
270 grub_ssize_t size;
271 grub_addr_t first_addr;
272 grub_addr_t addr;
273 int found_addr = 0;
275 if (argc == 0)
277 grub_error (GRUB_ERR_BAD_ARGUMENT, "no initrd specified");
278 goto fail;
281 if (!loaded)
283 grub_error (GRUB_ERR_BAD_ARGUMENT, "You need to load the kernel first.");
284 goto fail;
287 file = grub_file_open (argv[0]);
288 if (! file)
289 goto fail;
291 first_addr = linux_addr + linux_size;
292 size = grub_file_size (file);
294 /* Attempt to claim at a series of addresses until successful in
295 the same way that grub_rescue_cmd_linux does. */
296 for (addr = first_addr; addr < first_addr + 200 * 0x100000; addr += 0x100000)
298 grub_dprintf ("loader", "Attempting to claim at 0x%x, size 0x%x.\n",
299 addr, size);
300 found_addr = grub_claimmap (addr, size);
301 if (found_addr != -1)
302 break;
305 if (found_addr == -1)
307 grub_error (GRUB_ERR_OUT_OF_MEMORY, "Can not claim memory");
308 goto fail;
311 grub_dprintf ("loader", "Loading initrd at 0x%x, size 0x%x\n", addr, size);
313 if (grub_file_read (file, (void *) addr, size) != size)
315 grub_ieee1275_release (addr, size);
316 grub_error (GRUB_ERR_FILE_READ_ERROR, "Couldn't read file");
317 goto fail;
320 initrd_addr = addr;
321 initrd_size = size;
323 fail:
324 if (file)
325 grub_file_close (file);
330 GRUB_MOD_INIT(linux)
332 grub_rescue_register_command ("linux", grub_rescue_cmd_linux,
333 "load a linux kernel");
334 grub_rescue_register_command ("initrd", grub_rescue_cmd_initrd,
335 "load an initrd");
336 my_mod = mod;
339 GRUB_MOD_FINI(linux)
341 grub_rescue_unregister_command ("linux");
342 grub_rescue_unregister_command ("initrd");