]> git.proxmox.com Git - grub2.git/blame - grub-core/loader/efi/chainloader.c
verifiers: File type for fine-grained signature-verification controlling
[grub2.git] / grub-core / loader / efi / chainloader.c
CommitLineData
7f362539 1/* chainloader.c - boot another boot loader */
2/*
3 * GRUB -- GRand Unified Bootloader
f36cc108 4 * Copyright (C) 2002,2004,2006,2007,2008 Free Software Foundation, Inc.
7f362539 5 *
5a79f472 6 * GRUB is free software: you can redistribute it and/or modify
7f362539 7 * it under the terms of the GNU General Public License as published by
5a79f472 8 * the Free Software Foundation, either version 3 of the License, or
7f362539 9 * (at your option) any later version.
10 *
5a79f472 11 * GRUB is distributed in the hope that it will be useful,
7f362539 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.
15 *
16 * You should have received a copy of the GNU General Public License
5a79f472 17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
7f362539 18 */
19
20/* TODO: support load options. */
21
22#include <grub/loader.h>
23#include <grub/file.h>
24#include <grub/err.h>
25#include <grub/device.h>
26#include <grub/disk.h>
27#include <grub/misc.h>
1d3c6f1d 28#include <grub/charset.h>
7f362539 29#include <grub/mm.h>
30#include <grub/types.h>
7f362539 31#include <grub/dl.h>
32#include <grub/efi/api.h>
33#include <grub/efi/efi.h>
34#include <grub/efi/disk.h>
b1b797cb 35#include <grub/command.h>
809bbfeb 36#include <grub/i18n.h>
1ecd61a4 37#include <grub/net.h>
3e4f3566
VS
38#if defined (__i386__) || defined (__x86_64__)
39#include <grub/macho.h>
40#include <grub/i386/macho.h>
41#endif
7f362539 42
e745cf0c
VS
43GRUB_MOD_LICENSE ("GPLv3+");
44
7f362539 45static grub_dl_t my_mod;
46
47static grub_efi_physical_address_t address;
48static grub_efi_uintn_t pages;
49static grub_efi_device_path_t *file_path;
50static grub_efi_handle_t image_handle;
20011694 51static grub_efi_char16_t *cmdline;
7f362539 52
53static grub_err_t
54grub_chainloader_unload (void)
55{
56 grub_efi_boot_services_t *b;
b39f9d20 57
7f362539 58 b = grub_efi_system_table->boot_services;
20011694 59 efi_call_1 (b->unload_image, image_handle);
60 efi_call_2 (b->free_pages, address, pages);
61
7f362539 62 grub_free (file_path);
20011694 63 grub_free (cmdline);
64 cmdline = 0;
52856af2 65 file_path = 0;
b39f9d20 66
7f362539 67 grub_dl_unref (my_mod);
68 return GRUB_ERR_NONE;
69}
70
71static grub_err_t
72grub_chainloader_boot (void)
73{
74 grub_efi_boot_services_t *b;
75 grub_efi_status_t status;
76 grub_efi_uintn_t exit_data_size;
52856af2 77 grub_efi_char16_t *exit_data = NULL;
b39f9d20 78
7f362539 79 b = grub_efi_system_table->boot_services;
20011694 80 status = efi_call_3 (b->start_image, image_handle, &exit_data_size, &exit_data);
7f362539 81 if (status != GRUB_EFI_SUCCESS)
82 {
83 if (exit_data)
84 {
85 char *buf;
b39f9d20 86
7f362539 87 buf = grub_malloc (exit_data_size * 4 + 1);
88 if (buf)
89 {
90 *grub_utf16_to_utf8 ((grub_uint8_t *) buf,
91 exit_data, exit_data_size) = 0;
b39f9d20 92
7f362539 93 grub_error (GRUB_ERR_BAD_OS, buf);
94 grub_free (buf);
95 }
7f362539 96 }
9c4b5c13
VS
97 else
98 grub_error (GRUB_ERR_BAD_OS, "unknown error");
7f362539 99 }
100
101 if (exit_data)
20011694 102 efi_call_1 (b->free_pool, exit_data);
7f362539 103
52856af2 104 grub_loader_unset ();
b39f9d20 105
7f362539 106 return grub_errno;
107}
108
109static void
110copy_file_path (grub_efi_file_path_device_path_t *fp,
111 const char *str, grub_efi_uint16_t len)
112{
113 grub_efi_char16_t *p;
114 grub_efi_uint16_t size;
b39f9d20 115
7f362539 116 fp->header.type = GRUB_EFI_MEDIA_DEVICE_PATH_TYPE;
117 fp->header.subtype = GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE;
c21b17e6
VS
118
119 size = grub_utf8_to_utf16 (fp->path_name, len * GRUB_MAX_UTF16_PER_UTF8,
120 (const grub_uint8_t *) str, len, 0);
121 for (p = fp->path_name; p < fp->path_name + size; p++)
122 if (*p == '/')
123 *p = '\\';
124
ce95549c
AB
125 /* File Path is NULL terminated */
126 fp->path_name[size++] = '\0';
219401b8 127 fp->header.length = size * sizeof (grub_efi_char16_t) + sizeof (*fp);
7f362539 128}
129
130static grub_efi_device_path_t *
131make_file_path (grub_efi_device_path_t *dp, const char *filename)
132{
133 char *dir_start;
134 char *dir_end;
135 grub_size_t size;
136 grub_efi_device_path_t *d;
137
138 dir_start = grub_strchr (filename, ')');
139 if (! dir_start)
140 dir_start = (char *) filename;
141 else
142 dir_start++;
143
144 dir_end = grub_strrchr (dir_start, '/');
145 if (! dir_end)
146 {
147 grub_error (GRUB_ERR_BAD_FILENAME, "invalid EFI file path");
148 return 0;
149 }
b39f9d20 150
7f362539 151 size = 0;
152 d = dp;
153 while (1)
154 {
155 size += GRUB_EFI_DEVICE_PATH_LENGTH (d);
156 if ((GRUB_EFI_END_ENTIRE_DEVICE_PATH (d)))
157 break;
158 d = GRUB_EFI_NEXT_DEVICE_PATH (d);
159 }
b39f9d20 160
ce95549c
AB
161 /* File Path is NULL terminated. Allocate space for 2 extra characters */
162 /* FIXME why we split path in two components? */
7f362539 163 file_path = grub_malloc (size
ce95549c 164 + ((grub_strlen (dir_start) + 2)
c21b17e6 165 * GRUB_MAX_UTF16_PER_UTF8
7f362539 166 * sizeof (grub_efi_char16_t))
167 + sizeof (grub_efi_file_path_device_path_t) * 2);
168 if (! file_path)
169 return 0;
170
171 grub_memcpy (file_path, dp, size);
b39f9d20 172
7f362539 173 /* Fill the file path for the directory. */
174 d = (grub_efi_device_path_t *) ((char *) file_path
175 + ((char *) d - (char *) dp));
176 grub_efi_print_device_path (d);
177 copy_file_path ((grub_efi_file_path_device_path_t *) d,
178 dir_start, dir_end - dir_start);
179
180 /* Fill the file path for the file. */
181 d = GRUB_EFI_NEXT_DEVICE_PATH (d);
182 copy_file_path ((grub_efi_file_path_device_path_t *) d,
183 dir_end + 1, grub_strlen (dir_end + 1));
184
185 /* Fill the end of device path nodes. */
186 d = GRUB_EFI_NEXT_DEVICE_PATH (d);
187 d->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
188 d->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
219401b8 189 d->length = sizeof (*d);
7f362539 190
191 return file_path;
192}
193
b1b797cb 194static grub_err_t
195grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
196 int argc, char *argv[])
7f362539 197{
198 grub_file_t file = 0;
199 grub_ssize_t size;
200 grub_efi_status_t status;
201 grub_efi_boot_services_t *b;
7f362539 202 grub_device_t dev = 0;
203 grub_efi_device_path_t *dp = 0;
204 grub_efi_loaded_image_t *loaded_image;
20011694 205 char *filename;
3e4f3566 206 void *boot_image = 0;
1ecd61a4 207 grub_efi_handle_t dev_handle = 0;
20011694 208
209 if (argc == 0)
9c4b5c13 210 return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
20011694 211 filename = argv[0];
b39f9d20 212
7f362539 213 grub_dl_ref (my_mod);
214
215 /* Initialize some global variables. */
216 address = 0;
217 image_handle = 0;
218 file_path = 0;
b39f9d20 219
7f362539 220 b = grub_efi_system_table->boot_services;
221
ca0a4f68 222 file = grub_file_open (filename, GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE);
7f362539 223 if (! file)
224 goto fail;
225
226 /* Get the root device's device path. */
227 dev = grub_device_open (0);
228 if (! dev)
229 goto fail;
230
231 if (dev->disk)
1ecd61a4
VS
232 dev_handle = grub_efidisk_get_device_handle (dev->disk);
233 else if (dev->net && dev->net->server)
7f362539 234 {
1ecd61a4
VS
235 grub_net_network_level_address_t addr;
236 struct grub_net_network_level_interface *inf;
237 grub_net_network_level_address_t gateway;
238 grub_err_t err;
239
240 err = grub_net_resolve_address (dev->net->server, &addr);
241 if (err)
242 goto fail;
243
244 err = grub_net_route_address (addr, &gateway, &inf);
245 if (err)
246 goto fail;
247
248 dev_handle = grub_efinet_get_device_handle (inf->card);
7f362539 249 }
b39f9d20 250
1ecd61a4
VS
251 if (dev_handle)
252 dp = grub_efi_get_device_path (dev_handle);
253
254 if (! dp)
7f362539 255 {
256 grub_error (GRUB_ERR_BAD_DEVICE, "not a valid root device");
257 goto fail;
258 }
259
260 file_path = make_file_path (dp, filename);
261 if (! file_path)
262 goto fail;
263
264 grub_printf ("file path: ");
265 grub_efi_print_device_path (file_path);
b39f9d20 266
7f362539 267 size = grub_file_size (file);
52856af2
VS
268 if (!size)
269 {
d61386e2
VS
270 grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
271 filename);
52856af2
VS
272 goto fail;
273 }
7f362539 274 pages = (((grub_efi_uintn_t) size + ((1 << 12) - 1)) >> 12);
b39f9d20 275
20011694 276 status = efi_call_4 (b->allocate_pages, GRUB_EFI_ALLOCATE_ANY_PAGES,
7f362539 277 GRUB_EFI_LOADER_CODE,
278 pages, &address);
279 if (status != GRUB_EFI_SUCCESS)
280 {
922aabf3
VS
281 grub_dprintf ("chain", "Failed to allocate %u pages\n",
282 (unsigned int) pages);
c9eb96b5 283 grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
7f362539 284 goto fail;
285 }
286
3e4f3566
VS
287 boot_image = (void *) ((grub_addr_t) address);
288 if (grub_file_read (file, boot_image, size) != size)
7f362539 289 {
290 if (grub_errno == GRUB_ERR_NONE)
9c4b5c13
VS
291 grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
292 filename);
7f362539 293
294 goto fail;
295 }
296
3e4f3566
VS
297#if defined (__i386__) || defined (__x86_64__)
298 if (size >= (grub_ssize_t) sizeof (struct grub_macho_fat_header))
299 {
300 struct grub_macho_fat_header *head = boot_image;
301 if (head->magic
302 == grub_cpu_to_le32_compile_time (GRUB_MACHO_FAT_EFI_MAGIC))
303 {
304 grub_uint32_t i;
305 struct grub_macho_fat_arch *archs
306 = (struct grub_macho_fat_arch *) (head + 1);
307 for (i = 0; i < grub_cpu_to_le32 (head->nfat_arch); i++)
308 {
309 if (GRUB_MACHO_CPUTYPE_IS_HOST_CURRENT (archs[i].cputype))
310 break;
311 }
312 if (i == grub_cpu_to_le32 (head->nfat_arch))
313 {
314 grub_error (GRUB_ERR_BAD_OS, "no compatible arch found");
315 goto fail;
316 }
317 if (grub_cpu_to_le32 (archs[i].offset)
318 > ~grub_cpu_to_le32 (archs[i].size)
319 || grub_cpu_to_le32 (archs[i].offset)
320 + grub_cpu_to_le32 (archs[i].size)
321 > (grub_size_t) size)
322 {
323 grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
324 filename);
325 goto fail;
326 }
327 boot_image = (char *) boot_image + grub_cpu_to_le32 (archs[i].offset);
328 size = grub_cpu_to_le32 (archs[i].size);
329 }
330 }
331#endif
332
20011694 333 status = efi_call_6 (b->load_image, 0, grub_efi_image_handle, file_path,
3e4f3566
VS
334 boot_image, size,
335 &image_handle);
7f362539 336 if (status != GRUB_EFI_SUCCESS)
337 {
338 if (status == GRUB_EFI_OUT_OF_RESOURCES)
339 grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of resources");
340 else
341 grub_error (GRUB_ERR_BAD_OS, "cannot load image");
b39f9d20 342
7f362539 343 goto fail;
344 }
345
346 /* LoadImage does not set a device handler when the image is
347 loaded from memory, so it is necessary to set it explicitly here.
348 This is a mess. */
349 loaded_image = grub_efi_get_loaded_image (image_handle);
350 if (! loaded_image)
351 {
352 grub_error (GRUB_ERR_BAD_OS, "no loaded image available");
353 goto fail;
354 }
355 loaded_image->device_handle = dev_handle;
b39f9d20 356
20011694 357 if (argc > 1)
358 {
359 int i, len;
360 grub_efi_char16_t *p16;
361
362 for (i = 1, len = 0; i < argc; i++)
363 len += grub_strlen (argv[i]) + 1;
364
365 len *= sizeof (grub_efi_char16_t);
366 cmdline = p16 = grub_malloc (len);
367 if (! cmdline)
368 goto fail;
369
370 for (i = 1; i < argc; i++)
371 {
372 char *p8;
373
374 p8 = argv[i];
375 while (*p8)
376 *(p16++) = *(p8++);
377
378 *(p16++) = ' ';
379 }
380 *(--p16) = 0;
381
382 loaded_image->load_options = cmdline;
383 loaded_image->load_options_size = len;
384 }
385
c058e856
AB
386 grub_file_close (file);
387 grub_device_close (dev);
388
7f362539 389 grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 0);
b1b797cb 390 return 0;
b39f9d20 391
7f362539 392 fail:
393
394 if (dev)
395 grub_device_close (dev);
b39f9d20 396
7f362539 397 if (file)
398 grub_file_close (file);
399
cbf597af 400 grub_free (file_path);
b39f9d20 401
7f362539 402 if (address)
20011694 403 efi_call_2 (b->free_pages, address, pages);
b39f9d20 404
7f362539 405 grub_dl_unref (my_mod);
b1b797cb 406
407 return grub_errno;
7f362539 408}
409
b1b797cb 410static grub_command_t cmd;
7f362539 411
412GRUB_MOD_INIT(chainloader)
413{
b1b797cb 414 cmd = grub_register_command ("chainloader", grub_cmd_chainloader,
809bbfeb 415 0, N_("Load another boot loader."));
7f362539 416 my_mod = mod;
417}
418
419GRUB_MOD_FINI(chainloader)
420{
b1b797cb 421 grub_unregister_command (cmd);
7f362539 422}