]> git.proxmox.com Git - grub2.git/blob - grub-core/loader/efi/chainloader.c
* grub-core/loader/efi/chainloader.c (copy_file_path): Handle non-ASCII
[grub2.git] / grub-core / loader / efi / chainloader.c
1 /* chainloader.c - boot another boot loader */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2002,2004,2006,2007,2008 Free Software Foundation, Inc.
5 *
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.
10 *
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.
15 *
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/>.
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>
28 #include <grub/charset.h>
29 #include <grub/mm.h>
30 #include <grub/types.h>
31 #include <grub/dl.h>
32 #include <grub/efi/api.h>
33 #include <grub/efi/efi.h>
34 #include <grub/efi/disk.h>
35 #include <grub/command.h>
36 #include <grub/i18n.h>
37 #include <grub/net.h>
38
39 GRUB_MOD_LICENSE ("GPLv3+");
40
41 static grub_dl_t my_mod;
42
43 static grub_efi_physical_address_t address;
44 static grub_efi_uintn_t pages;
45 static grub_efi_device_path_t *file_path;
46 static grub_efi_handle_t image_handle;
47 static grub_efi_char16_t *cmdline;
48
49 static grub_err_t
50 grub_chainloader_unload (void)
51 {
52 grub_efi_boot_services_t *b;
53
54 b = grub_efi_system_table->boot_services;
55 efi_call_1 (b->unload_image, image_handle);
56 efi_call_2 (b->free_pages, address, pages);
57
58 grub_free (file_path);
59 grub_free (cmdline);
60 cmdline = 0;
61 file_path = 0;
62
63 grub_dl_unref (my_mod);
64 return GRUB_ERR_NONE;
65 }
66
67 static grub_err_t
68 grub_chainloader_boot (void)
69 {
70 grub_efi_boot_services_t *b;
71 grub_efi_status_t status;
72 grub_efi_uintn_t exit_data_size;
73 grub_efi_char16_t *exit_data = NULL;
74
75 b = grub_efi_system_table->boot_services;
76 status = efi_call_3 (b->start_image, image_handle, &exit_data_size, &exit_data);
77 if (status != GRUB_EFI_SUCCESS)
78 {
79 if (exit_data)
80 {
81 char *buf;
82
83 buf = grub_malloc (exit_data_size * 4 + 1);
84 if (buf)
85 {
86 *grub_utf16_to_utf8 ((grub_uint8_t *) buf,
87 exit_data, exit_data_size) = 0;
88
89 grub_error (GRUB_ERR_BAD_OS, buf);
90 grub_free (buf);
91 }
92 }
93 else
94 grub_error (GRUB_ERR_BAD_OS, "unknown error");
95 }
96
97 if (exit_data)
98 efi_call_1 (b->free_pool, exit_data);
99
100 grub_loader_unset ();
101
102 return grub_errno;
103 }
104
105 static void
106 copy_file_path (grub_efi_file_path_device_path_t *fp,
107 const char *str, grub_efi_uint16_t len)
108 {
109 grub_efi_char16_t *p;
110 grub_efi_uint16_t size;
111
112 fp->header.type = GRUB_EFI_MEDIA_DEVICE_PATH_TYPE;
113 fp->header.subtype = GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE;
114
115 size = grub_utf8_to_utf16 (fp->path_name, len * GRUB_MAX_UTF16_PER_UTF8,
116 (const grub_uint8_t *) str, len, 0);
117 for (p = fp->path_name; p < fp->path_name + size; p++)
118 if (*p == '/')
119 *p = '\\';
120
121 size = size * sizeof (grub_efi_char16_t) + sizeof (*fp);
122 fp->header.length[0] = (grub_efi_uint8_t) (size & 0xff);
123 fp->header.length[1] = (grub_efi_uint8_t) (size >> 8);
124 }
125
126 static grub_efi_device_path_t *
127 make_file_path (grub_efi_device_path_t *dp, const char *filename)
128 {
129 char *dir_start;
130 char *dir_end;
131 grub_size_t size;
132 grub_efi_device_path_t *d;
133
134 dir_start = grub_strchr (filename, ')');
135 if (! dir_start)
136 dir_start = (char *) filename;
137 else
138 dir_start++;
139
140 dir_end = grub_strrchr (dir_start, '/');
141 if (! dir_end)
142 {
143 grub_error (GRUB_ERR_BAD_FILENAME, "invalid EFI file path");
144 return 0;
145 }
146
147 size = 0;
148 d = dp;
149 while (1)
150 {
151 size += GRUB_EFI_DEVICE_PATH_LENGTH (d);
152 if ((GRUB_EFI_END_ENTIRE_DEVICE_PATH (d)))
153 break;
154 d = GRUB_EFI_NEXT_DEVICE_PATH (d);
155 }
156
157 file_path = grub_malloc (size
158 + ((grub_strlen (dir_start) + 1)
159 * GRUB_MAX_UTF16_PER_UTF8
160 * sizeof (grub_efi_char16_t))
161 + sizeof (grub_efi_file_path_device_path_t) * 2);
162 if (! file_path)
163 return 0;
164
165 grub_memcpy (file_path, dp, size);
166
167 /* Fill the file path for the directory. */
168 d = (grub_efi_device_path_t *) ((char *) file_path
169 + ((char *) d - (char *) dp));
170 grub_efi_print_device_path (d);
171 copy_file_path ((grub_efi_file_path_device_path_t *) d,
172 dir_start, dir_end - dir_start);
173
174 /* Fill the file path for the file. */
175 d = GRUB_EFI_NEXT_DEVICE_PATH (d);
176 copy_file_path ((grub_efi_file_path_device_path_t *) d,
177 dir_end + 1, grub_strlen (dir_end + 1));
178
179 /* Fill the end of device path nodes. */
180 d = GRUB_EFI_NEXT_DEVICE_PATH (d);
181 d->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
182 d->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
183 d->length[0] = sizeof (*d);
184 d->length[1] = 0;
185
186 return file_path;
187 }
188
189 static grub_err_t
190 grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
191 int argc, char *argv[])
192 {
193 grub_file_t file = 0;
194 grub_ssize_t size;
195 grub_efi_status_t status;
196 grub_efi_boot_services_t *b;
197 grub_device_t dev = 0;
198 grub_efi_device_path_t *dp = 0;
199 grub_efi_loaded_image_t *loaded_image;
200 char *filename;
201 grub_efi_handle_t dev_handle = 0;
202
203 if (argc == 0)
204 return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
205 filename = argv[0];
206
207 grub_dl_ref (my_mod);
208
209 /* Initialize some global variables. */
210 address = 0;
211 image_handle = 0;
212 file_path = 0;
213
214 b = grub_efi_system_table->boot_services;
215
216 file = grub_file_open (filename);
217 if (! file)
218 goto fail;
219
220 /* Get the root device's device path. */
221 dev = grub_device_open (0);
222 if (! dev)
223 goto fail;
224
225 if (dev->disk)
226 dev_handle = grub_efidisk_get_device_handle (dev->disk);
227 else if (dev->net && dev->net->server)
228 {
229 grub_net_network_level_address_t addr;
230 struct grub_net_network_level_interface *inf;
231 grub_net_network_level_address_t gateway;
232 grub_err_t err;
233
234 err = grub_net_resolve_address (dev->net->server, &addr);
235 if (err)
236 goto fail;
237
238 err = grub_net_route_address (addr, &gateway, &inf);
239 if (err)
240 goto fail;
241
242 dev_handle = grub_efinet_get_device_handle (inf->card);
243 }
244
245 if (dev_handle)
246 dp = grub_efi_get_device_path (dev_handle);
247
248 if (! dp)
249 {
250 grub_error (GRUB_ERR_BAD_DEVICE, "not a valid root device");
251 goto fail;
252 }
253
254 file_path = make_file_path (dp, filename);
255 if (! file_path)
256 goto fail;
257
258 grub_printf ("file path: ");
259 grub_efi_print_device_path (file_path);
260
261 size = grub_file_size (file);
262 if (!size)
263 {
264 grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
265 filename);
266 goto fail;
267 }
268 pages = (((grub_efi_uintn_t) size + ((1 << 12) - 1)) >> 12);
269
270 status = efi_call_4 (b->allocate_pages, GRUB_EFI_ALLOCATE_ANY_PAGES,
271 GRUB_EFI_LOADER_CODE,
272 pages, &address);
273 if (status != GRUB_EFI_SUCCESS)
274 {
275 grub_dprintf ("chain", "Failed to allocate %u pages\n",
276 (unsigned int) pages);
277 grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
278 goto fail;
279 }
280
281 if (grub_file_read (file, (void *) ((grub_addr_t) address), size) != size)
282 {
283 if (grub_errno == GRUB_ERR_NONE)
284 grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
285 filename);
286
287 goto fail;
288 }
289
290 status = efi_call_6 (b->load_image, 0, grub_efi_image_handle, file_path,
291 (void *) ((grub_addr_t) address), size,
292 &image_handle);
293 if (status != GRUB_EFI_SUCCESS)
294 {
295 if (status == GRUB_EFI_OUT_OF_RESOURCES)
296 grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of resources");
297 else
298 grub_error (GRUB_ERR_BAD_OS, "cannot load image");
299
300 goto fail;
301 }
302
303 /* LoadImage does not set a device handler when the image is
304 loaded from memory, so it is necessary to set it explicitly here.
305 This is a mess. */
306 loaded_image = grub_efi_get_loaded_image (image_handle);
307 if (! loaded_image)
308 {
309 grub_error (GRUB_ERR_BAD_OS, "no loaded image available");
310 goto fail;
311 }
312 loaded_image->device_handle = dev_handle;
313
314 grub_file_close (file);
315
316 if (argc > 1)
317 {
318 int i, len;
319 grub_efi_char16_t *p16;
320
321 for (i = 1, len = 0; i < argc; i++)
322 len += grub_strlen (argv[i]) + 1;
323
324 len *= sizeof (grub_efi_char16_t);
325 cmdline = p16 = grub_malloc (len);
326 if (! cmdline)
327 goto fail;
328
329 for (i = 1; i < argc; i++)
330 {
331 char *p8;
332
333 p8 = argv[i];
334 while (*p8)
335 *(p16++) = *(p8++);
336
337 *(p16++) = ' ';
338 }
339 *(--p16) = 0;
340
341 loaded_image->load_options = cmdline;
342 loaded_image->load_options_size = len;
343 }
344
345 grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 0);
346 return 0;
347
348 fail:
349
350 if (dev)
351 grub_device_close (dev);
352
353 if (file)
354 grub_file_close (file);
355
356 grub_free (file_path);
357
358 if (address)
359 efi_call_2 (b->free_pages, address, pages);
360
361 grub_dl_unref (my_mod);
362
363 return grub_errno;
364 }
365
366 static grub_command_t cmd;
367
368 GRUB_MOD_INIT(chainloader)
369 {
370 cmd = grub_register_command ("chainloader", grub_cmd_chainloader,
371 0, N_("Load another boot loader."));
372 my_mod = mod;
373 }
374
375 GRUB_MOD_FINI(chainloader)
376 {
377 grub_unregister_command (cmd);
378 }