]>
Commit | Line | Data |
---|---|---|
9612ebc0 VS |
1 | /* |
2 | * GRUB -- GRand Unified Bootloader | |
3 | * Copyright (C) 2013 Free Software Foundation, Inc. | |
4 | * | |
5 | * GRUB is free software: you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation, either version 3 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * GRUB is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with GRUB. If not, see <http://www.gnu.org/licenses/>. | |
17 | */ | |
18 | ||
19 | #include <grub/loader.h> | |
20 | #include <grub/memory.h> | |
21 | #include <grub/normal.h> | |
22 | #include <grub/file.h> | |
23 | #include <grub/disk.h> | |
24 | #include <grub/err.h> | |
25 | #include <grub/misc.h> | |
26 | #include <grub/types.h> | |
27 | #include <grub/dl.h> | |
28 | #include <grub/mm.h> | |
29 | #include <grub/term.h> | |
30 | #include <grub/cpu/linux.h> | |
31 | #include <grub/video.h> | |
32 | #include <grub/video_fb.h> | |
33 | #include <grub/command.h> | |
34 | #include <grub/xen/relocator.h> | |
35 | #include <grub/i18n.h> | |
36 | #include <grub/elf.h> | |
37 | #include <grub/elfload.h> | |
38 | #include <grub/lib/cmdline.h> | |
39 | #include <grub/xen.h> | |
40 | #include <grub/xen_file.h> | |
41 | #include <grub/linux.h> | |
42 | ||
43 | GRUB_MOD_LICENSE ("GPLv3+"); | |
44 | ||
45 | static struct grub_relocator *relocator = NULL; | |
46 | static grub_uint64_t max_addr; | |
47 | static grub_dl_t my_mod; | |
48 | static int loaded = 0; | |
49 | static struct start_info next_start; | |
50 | static void *kern_chunk_src; | |
51 | static struct grub_xen_file_info xen_inf; | |
52 | static struct xen_multiboot_mod_list *xen_module_info_page; | |
53 | static grub_uint64_t modules_target_start; | |
54 | static grub_size_t n_modules; | |
55 | ||
56 | #define PAGE_SIZE 4096 | |
57 | #define MAX_MODULES (PAGE_SIZE / sizeof (struct xen_multiboot_mod_list)) | |
58 | #define PAGE_SHIFT 12 | |
59 | #define STACK_SIZE 1048576 | |
60 | #define ADDITIONAL_SIZE (1 << 19) | |
61 | #define ALIGN_SIZE (1 << 22) | |
62 | #define LOG_POINTERS_PER_PAGE 9 | |
63 | #define POINTERS_PER_PAGE (1 << LOG_POINTERS_PER_PAGE) | |
64 | ||
65 | static grub_uint64_t | |
66 | page2offset (grub_uint64_t page) | |
67 | { | |
68 | return page << PAGE_SHIFT; | |
69 | } | |
70 | ||
71 | #ifdef __x86_64__ | |
72 | #define NUMBER_OF_LEVELS 4 | |
73 | #define INTERMEDIATE_OR 7 | |
74 | #else | |
75 | #define NUMBER_OF_LEVELS 3 | |
76 | #define INTERMEDIATE_OR 3 | |
77 | #endif | |
78 | ||
79 | static grub_uint64_t | |
80 | get_pgtable_size (grub_uint64_t total_pages, grub_uint64_t virt_base) | |
81 | { | |
82 | if (!virt_base) | |
83 | total_pages++; | |
84 | grub_uint64_t ret = 0; | |
85 | grub_uint64_t ll = total_pages; | |
86 | int i; | |
87 | for (i = 0; i < NUMBER_OF_LEVELS; i++) | |
88 | { | |
89 | ll = (ll + POINTERS_PER_PAGE - 1) >> LOG_POINTERS_PER_PAGE; | |
90 | /* PAE wants all 4 root directories present. */ | |
91 | #ifdef __i386__ | |
92 | if (i == 1) | |
93 | ll = 4; | |
94 | #endif | |
95 | ret += ll; | |
96 | } | |
97 | for (i = 1; i < NUMBER_OF_LEVELS; i++) | |
98 | if (virt_base >> (PAGE_SHIFT + i * LOG_POINTERS_PER_PAGE)) | |
99 | ret++; | |
100 | return ret; | |
101 | } | |
102 | ||
103 | static void | |
104 | generate_page_table (grub_uint64_t *where, grub_uint64_t paging_start, | |
105 | grub_uint64_t total_pages, grub_uint64_t virt_base, | |
106 | grub_xen_mfn_t *mfn_list) | |
107 | { | |
108 | if (!virt_base) | |
109 | total_pages++; | |
110 | ||
111 | grub_uint64_t lx[NUMBER_OF_LEVELS], lxs[NUMBER_OF_LEVELS]; | |
112 | grub_uint64_t nlx, nls, sz = 0; | |
113 | int l; | |
114 | ||
115 | nlx = total_pages; | |
116 | nls = virt_base >> PAGE_SHIFT; | |
117 | for (l = 0; l < NUMBER_OF_LEVELS; l++) | |
118 | { | |
119 | nlx = (nlx + POINTERS_PER_PAGE - 1) >> LOG_POINTERS_PER_PAGE; | |
120 | /* PAE wants all 4 root directories present. */ | |
121 | #ifdef __i386__ | |
122 | if (l == 1) | |
123 | nlx = 4; | |
124 | #endif | |
125 | lx[l] = nlx; | |
126 | sz += lx[l]; | |
127 | lxs[l] = nls & (POINTERS_PER_PAGE - 1); | |
128 | if (nls && l != 0) | |
129 | sz++; | |
130 | nls >>= LOG_POINTERS_PER_PAGE; | |
131 | } | |
132 | ||
133 | grub_uint64_t lp; | |
134 | grub_uint64_t j; | |
135 | grub_uint64_t *pg = (grub_uint64_t *) where; | |
136 | int pr = 0; | |
137 | ||
138 | grub_memset (pg, 0, sz * PAGE_SIZE); | |
139 | ||
140 | lp = paging_start + lx[NUMBER_OF_LEVELS - 1]; | |
141 | for (l = NUMBER_OF_LEVELS - 1; l >= 1; l--) | |
142 | { | |
143 | if (lxs[l] || pr) | |
144 | pg[0] = page2offset (mfn_list[lp++]) | INTERMEDIATE_OR; | |
145 | if (pr) | |
146 | pg += POINTERS_PER_PAGE; | |
147 | for (j = 0; j < lx[l - 1]; j++) | |
148 | pg[j + lxs[l]] = page2offset (mfn_list[lp++]) | INTERMEDIATE_OR; | |
149 | pg += lx[l] * POINTERS_PER_PAGE; | |
150 | if (lxs[l]) | |
151 | pr = 1; | |
152 | } | |
153 | ||
154 | if (lxs[0] || pr) | |
155 | pg[0] = page2offset (mfn_list[total_pages]) | 5; | |
156 | if (pr) | |
157 | pg += POINTERS_PER_PAGE; | |
158 | ||
159 | for (j = 0; j < total_pages; j++) | |
160 | { | |
161 | if (j >= paging_start && j < lp) | |
162 | pg[j + lxs[0]] = page2offset (mfn_list[j]) | 5; | |
163 | else | |
164 | pg[j + lxs[0]] = page2offset (mfn_list[j]) | 7; | |
165 | } | |
166 | } | |
167 | ||
168 | static grub_err_t | |
169 | set_mfns (grub_xen_mfn_t * new_mfn_list, grub_xen_mfn_t pfn) | |
170 | { | |
171 | grub_xen_mfn_t i, t; | |
172 | grub_xen_mfn_t cn_pfn = -1, st_pfn = -1; | |
173 | struct mmu_update m2p_updates[4]; | |
174 | ||
175 | ||
176 | for (i = 0; i < grub_xen_start_page_addr->nr_pages; i++) | |
177 | { | |
178 | if (new_mfn_list[i] == grub_xen_start_page_addr->console.domU.mfn) | |
179 | cn_pfn = i; | |
180 | if (new_mfn_list[i] == grub_xen_start_page_addr->store_mfn) | |
181 | st_pfn = i; | |
182 | } | |
183 | if (cn_pfn == (grub_xen_mfn_t)-1) | |
184 | return grub_error (GRUB_ERR_BUG, "no console"); | |
185 | if (st_pfn == (grub_xen_mfn_t)-1) | |
186 | return grub_error (GRUB_ERR_BUG, "no store"); | |
187 | t = new_mfn_list[pfn]; | |
188 | new_mfn_list[pfn] = new_mfn_list[cn_pfn]; | |
189 | new_mfn_list[cn_pfn] = t; | |
190 | t = new_mfn_list[pfn + 1]; | |
191 | new_mfn_list[pfn + 1] = new_mfn_list[st_pfn]; | |
192 | new_mfn_list[st_pfn] = t; | |
193 | ||
194 | m2p_updates[0].ptr = page2offset (new_mfn_list[pfn]) | MMU_MACHPHYS_UPDATE; | |
195 | m2p_updates[0].val = pfn; | |
196 | m2p_updates[1].ptr = | |
197 | page2offset (new_mfn_list[pfn + 1]) | MMU_MACHPHYS_UPDATE; | |
198 | m2p_updates[1].val = pfn + 1; | |
199 | m2p_updates[2].ptr = | |
200 | page2offset (new_mfn_list[cn_pfn]) | MMU_MACHPHYS_UPDATE; | |
201 | m2p_updates[2].val = cn_pfn; | |
202 | m2p_updates[3].ptr = | |
203 | page2offset (new_mfn_list[st_pfn]) | MMU_MACHPHYS_UPDATE; | |
204 | m2p_updates[3].val = st_pfn; | |
205 | ||
206 | grub_xen_mmu_update (m2p_updates, 4, NULL, DOMID_SELF); | |
207 | ||
208 | return GRUB_ERR_NONE; | |
209 | } | |
210 | ||
211 | static grub_err_t | |
212 | grub_xen_boot (void) | |
213 | { | |
214 | struct grub_relocator_xen_state state; | |
215 | grub_relocator_chunk_t ch; | |
216 | grub_err_t err; | |
217 | grub_size_t pgtsize; | |
218 | struct start_info *nst; | |
219 | grub_uint64_t nr_info_pages; | |
220 | grub_uint64_t nr_pages, nr_pt_pages, nr_need_pages; | |
221 | struct gnttab_set_version gnttab_setver; | |
222 | grub_xen_mfn_t *new_mfn_list; | |
223 | grub_size_t i; | |
224 | ||
71669c3b VS |
225 | grub_video_restore (); |
226 | ||
9612ebc0 VS |
227 | if (grub_xen_n_allocated_shared_pages) |
228 | return grub_error (GRUB_ERR_BUG, "active grants"); | |
229 | ||
230 | state.mfn_list = max_addr; | |
231 | next_start.mfn_list = max_addr + xen_inf.virt_base; | |
232 | next_start.first_p2m_pfn = max_addr >> PAGE_SHIFT; /* Is this right? */ | |
233 | pgtsize = sizeof (grub_xen_mfn_t) * grub_xen_start_page_addr->nr_pages; | |
234 | err = grub_relocator_alloc_chunk_addr (relocator, &ch, max_addr, pgtsize); | |
235 | next_start.nr_p2m_frames = (pgtsize + PAGE_SIZE - 1) >> PAGE_SHIFT; | |
236 | if (err) | |
237 | return err; | |
238 | new_mfn_list = get_virtual_current_address (ch); | |
239 | grub_memcpy (new_mfn_list, | |
240 | (void *) grub_xen_start_page_addr->mfn_list, pgtsize); | |
241 | max_addr = ALIGN_UP (max_addr + pgtsize, PAGE_SIZE); | |
242 | ||
243 | err = grub_relocator_alloc_chunk_addr (relocator, &ch, | |
244 | max_addr, sizeof (next_start)); | |
245 | if (err) | |
246 | return err; | |
247 | state.start_info = max_addr + xen_inf.virt_base; | |
248 | nst = get_virtual_current_address (ch); | |
249 | max_addr = ALIGN_UP (max_addr + sizeof (next_start), PAGE_SIZE); | |
250 | ||
251 | next_start.nr_pages = grub_xen_start_page_addr->nr_pages; | |
252 | grub_memcpy (next_start.magic, grub_xen_start_page_addr->magic, | |
253 | sizeof (next_start.magic)); | |
254 | next_start.store_mfn = grub_xen_start_page_addr->store_mfn; | |
255 | next_start.store_evtchn = grub_xen_start_page_addr->store_evtchn; | |
256 | next_start.console.domU = grub_xen_start_page_addr->console.domU; | |
257 | next_start.shared_info = grub_xen_start_page_addr->shared_info; | |
258 | ||
259 | err = set_mfns (new_mfn_list, max_addr >> PAGE_SHIFT); | |
260 | if (err) | |
261 | return err; | |
262 | max_addr += 2 * PAGE_SIZE; | |
263 | ||
264 | next_start.pt_base = max_addr + xen_inf.virt_base; | |
265 | state.paging_start = max_addr >> PAGE_SHIFT; | |
266 | ||
267 | nr_info_pages = max_addr >> PAGE_SHIFT; | |
268 | nr_pages = nr_info_pages; | |
269 | ||
270 | while (1) | |
271 | { | |
272 | nr_pages = ALIGN_UP (nr_pages, (ALIGN_SIZE >> PAGE_SHIFT)); | |
273 | nr_pt_pages = get_pgtable_size (nr_pages, xen_inf.virt_base); | |
274 | nr_need_pages = | |
275 | nr_info_pages + nr_pt_pages + | |
276 | ((ADDITIONAL_SIZE + STACK_SIZE) >> PAGE_SHIFT); | |
277 | if (nr_pages >= nr_need_pages) | |
278 | break; | |
279 | nr_pages = nr_need_pages; | |
280 | } | |
281 | ||
282 | grub_dprintf ("xen", "bootstrap domain %llx+%llx\n", | |
283 | (unsigned long long) xen_inf.virt_base, | |
284 | (unsigned long long) page2offset (nr_pages)); | |
285 | ||
286 | err = grub_relocator_alloc_chunk_addr (relocator, &ch, | |
287 | max_addr, page2offset (nr_pt_pages)); | |
288 | if (err) | |
289 | return err; | |
290 | ||
291 | generate_page_table (get_virtual_current_address (ch), | |
292 | max_addr >> PAGE_SHIFT, nr_pages, | |
293 | xen_inf.virt_base, new_mfn_list); | |
294 | ||
295 | max_addr += page2offset (nr_pt_pages); | |
296 | state.stack = max_addr + STACK_SIZE + xen_inf.virt_base; | |
297 | state.entry_point = xen_inf.entry_point; | |
298 | ||
299 | next_start.nr_p2m_frames += nr_pt_pages; | |
300 | next_start.nr_pt_frames = nr_pt_pages; | |
301 | state.paging_size = nr_pt_pages; | |
302 | ||
303 | *nst = next_start; | |
304 | ||
305 | grub_memset (&gnttab_setver, 0, sizeof (gnttab_setver)); | |
306 | ||
307 | gnttab_setver.version = 1; | |
308 | grub_xen_grant_table_op (GNTTABOP_set_version, &gnttab_setver, 1); | |
309 | ||
310 | for (i = 0; i < ARRAY_SIZE (grub_xen_shared_info->evtchn_pending); i++) | |
311 | grub_xen_shared_info->evtchn_pending[i] = 0; | |
312 | ||
313 | return grub_relocator_xen_boot (relocator, state, nr_pages, | |
314 | xen_inf.virt_base < | |
315 | PAGE_SIZE ? page2offset (nr_pages) : 0, | |
316 | nr_pages - 1, | |
317 | page2offset (nr_pages - 1) + | |
318 | xen_inf.virt_base); | |
319 | } | |
320 | ||
321 | static grub_err_t | |
322 | grub_xen_unload (void) | |
323 | { | |
324 | grub_dl_unref (my_mod); | |
325 | loaded = 0; | |
326 | return GRUB_ERR_NONE; | |
327 | } | |
328 | ||
329 | #define HYPERCALL_INTERFACE_SIZE 32 | |
330 | ||
331 | #ifdef __x86_64__ | |
332 | static grub_uint8_t template[] = | |
333 | { | |
334 | 0x51, /* push %rcx */ | |
335 | 0x41, 0x53, /* push %r11 */ | |
336 | 0x48, 0xc7, 0xc0, 0xbb, 0xaa, 0x00, 0x00, /* mov $0xaabb,%rax */ | |
337 | 0x0f, 0x05, /* syscall */ | |
338 | 0x41, 0x5b, /* pop %r11 */ | |
339 | 0x59, /* pop %rcx */ | |
340 | 0xc3 /* ret */ | |
341 | }; | |
342 | ||
343 | static grub_uint8_t template_iret[] = | |
344 | { | |
345 | 0x51, /* push %rcx */ | |
346 | 0x41, 0x53, /* push %r11 */ | |
347 | 0x50, /* push %rax */ | |
348 | 0x48, 0xc7, 0xc0, 0x17, 0x00, 0x00, 0x00, /* mov $0x17,%rax */ | |
349 | 0x0f, 0x05 /* syscall */ | |
350 | }; | |
351 | #define CALLNO_OFFSET 6 | |
352 | #else | |
353 | ||
354 | static grub_uint8_t template[] = | |
355 | { | |
356 | 0xb8, 0xbb, 0xaa, 0x00, 0x00, /* mov imm32, %eax */ | |
357 | 0xcd, 0x82, /* int $0x82 */ | |
358 | 0xc3 /* ret */ | |
359 | }; | |
360 | ||
361 | static grub_uint8_t template_iret[] = | |
362 | { | |
363 | 0x50, /* push %eax */ | |
364 | 0xb8, 0x17, 0x00, 0x00, 0x00, /* mov $0x17,%eax */ | |
365 | 0xcd, 0x82, /* int $0x82 */ | |
366 | }; | |
367 | #define CALLNO_OFFSET 1 | |
368 | ||
369 | #endif | |
370 | ||
371 | ||
372 | static void | |
373 | set_hypercall_interface (grub_uint8_t *tgt, unsigned callno) | |
374 | { | |
375 | if (callno == 0x17) | |
376 | { | |
377 | grub_memcpy (tgt, template_iret, ARRAY_SIZE (template_iret)); | |
378 | grub_memset (tgt + ARRAY_SIZE (template_iret), 0xcc, | |
379 | HYPERCALL_INTERFACE_SIZE - ARRAY_SIZE (template_iret)); | |
380 | return; | |
381 | } | |
382 | grub_memcpy (tgt, template, ARRAY_SIZE (template)); | |
383 | grub_memset (tgt + ARRAY_SIZE (template), 0xcc, | |
384 | HYPERCALL_INTERFACE_SIZE - ARRAY_SIZE (template)); | |
385 | tgt[CALLNO_OFFSET] = callno & 0xff; | |
386 | tgt[CALLNO_OFFSET + 1] = callno >> 8; | |
387 | } | |
388 | ||
389 | #ifdef __x86_64__ | |
390 | #define grub_elfXX_load grub_elf64_load | |
391 | #else | |
392 | #define grub_elfXX_load grub_elf32_load | |
393 | #endif | |
394 | ||
395 | static grub_err_t | |
396 | grub_cmd_xen (grub_command_t cmd __attribute__ ((unused)), | |
397 | int argc, char *argv[]) | |
398 | { | |
399 | grub_file_t file; | |
400 | grub_elf_t elf; | |
401 | grub_err_t err; | |
402 | ||
403 | if (argc == 0) | |
404 | return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); | |
405 | ||
406 | grub_loader_unset (); | |
407 | ||
408 | grub_memset (&next_start, 0, sizeof (next_start)); | |
409 | ||
410 | xen_module_info_page = NULL; | |
411 | n_modules = 0; | |
412 | ||
413 | grub_create_loader_cmdline (argc - 1, argv + 1, | |
414 | (char *) next_start.cmd_line, | |
415 | sizeof (next_start.cmd_line) - 1); | |
416 | ||
417 | file = grub_file_open (argv[0]); | |
418 | if (!file) | |
419 | return grub_errno; | |
420 | ||
421 | elf = grub_xen_file (file); | |
422 | if (!elf) | |
423 | goto fail; | |
424 | ||
425 | err = grub_xen_get_info (elf, &xen_inf); | |
426 | if (err) | |
427 | goto fail; | |
428 | #ifdef __x86_64__ | |
429 | if (xen_inf.arch != GRUB_XEN_FILE_X86_64) | |
430 | #else | |
431 | if (xen_inf.arch != GRUB_XEN_FILE_I386_PAE | |
432 | && xen_inf.arch != GRUB_XEN_FILE_I386_PAE_BIMODE) | |
433 | #endif | |
434 | { | |
435 | grub_error (GRUB_ERR_BAD_OS, "incompatible architecture: %d", | |
436 | xen_inf.arch); | |
437 | goto fail; | |
438 | } | |
439 | ||
440 | if (xen_inf.virt_base & (PAGE_SIZE - 1)) | |
441 | { | |
442 | grub_error (GRUB_ERR_BAD_OS, "unaligned virt_base"); | |
443 | goto fail; | |
444 | } | |
445 | grub_dprintf ("xen", "virt_base = %llx, entry = %llx\n", | |
446 | (unsigned long long) xen_inf.virt_base, | |
447 | (unsigned long long) xen_inf.entry_point); | |
448 | ||
449 | relocator = grub_relocator_new (); | |
450 | if (!relocator) | |
451 | goto fail; | |
452 | ||
453 | grub_relocator_chunk_t ch; | |
454 | grub_addr_t kern_start = xen_inf.kern_start - xen_inf.paddr_offset; | |
455 | grub_addr_t kern_end = xen_inf.kern_end - xen_inf.paddr_offset; | |
456 | ||
457 | if (xen_inf.has_hypercall_page) | |
458 | { | |
459 | grub_dprintf ("xen", "hypercall page at 0x%llx\n", | |
460 | (unsigned long long) xen_inf.hypercall_page); | |
461 | if (xen_inf.hypercall_page - xen_inf.virt_base < kern_start) | |
462 | kern_start = xen_inf.hypercall_page - xen_inf.virt_base; | |
463 | ||
464 | if (xen_inf.hypercall_page - xen_inf.virt_base + PAGE_SIZE > kern_end) | |
465 | kern_end = xen_inf.hypercall_page - xen_inf.virt_base + PAGE_SIZE; | |
466 | } | |
467 | ||
468 | max_addr = ALIGN_UP (kern_end, PAGE_SIZE); | |
469 | ||
470 | err = grub_relocator_alloc_chunk_addr (relocator, &ch, kern_start, | |
471 | kern_end - kern_start); | |
472 | if (err) | |
473 | goto fail; | |
474 | kern_chunk_src = get_virtual_current_address (ch); | |
475 | ||
476 | grub_dprintf ("xen", "paddr_offset = 0x%llx\n", | |
477 | (unsigned long long) xen_inf.paddr_offset); | |
478 | grub_dprintf ("xen", "kern_start = 0x%llx, kern_end = 0x%llx\n", | |
479 | (unsigned long long) xen_inf.kern_start, | |
480 | (unsigned long long) xen_inf.kern_end); | |
481 | ||
482 | err = grub_elfXX_load (elf, argv[0], | |
483 | (grub_uint8_t *) kern_chunk_src - kern_start | |
484 | - xen_inf.paddr_offset, 0, 0, 0); | |
485 | ||
486 | if (xen_inf.has_hypercall_page) | |
487 | { | |
488 | unsigned i; | |
489 | for (i = 0; i < PAGE_SIZE / HYPERCALL_INTERFACE_SIZE; i++) | |
490 | set_hypercall_interface ((grub_uint8_t *) kern_chunk_src + | |
491 | i * HYPERCALL_INTERFACE_SIZE + | |
492 | xen_inf.hypercall_page - xen_inf.virt_base - | |
493 | kern_start, i); | |
494 | } | |
495 | ||
496 | if (err) | |
497 | goto fail; | |
498 | ||
499 | grub_dl_ref (my_mod); | |
500 | loaded = 1; | |
501 | ||
502 | grub_loader_set (grub_xen_boot, grub_xen_unload, 0); | |
503 | loaded = 1; | |
504 | ||
505 | goto fail; | |
506 | ||
507 | fail: | |
508 | ||
509 | if (elf) | |
510 | grub_elf_close (elf); | |
511 | else if (file) | |
512 | grub_file_close (file); | |
513 | ||
514 | if (grub_errno != GRUB_ERR_NONE) | |
515 | loaded = 0; | |
516 | ||
517 | return grub_errno; | |
518 | } | |
519 | ||
520 | static grub_err_t | |
521 | grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), | |
522 | int argc, char *argv[]) | |
523 | { | |
524 | grub_size_t size = 0; | |
525 | grub_err_t err; | |
526 | struct grub_linux_initrd_context initrd_ctx; | |
527 | grub_relocator_chunk_t ch; | |
528 | ||
529 | if (argc == 0) | |
530 | { | |
531 | grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); | |
532 | goto fail; | |
533 | } | |
534 | ||
535 | if (!loaded) | |
536 | { | |
537 | grub_error (GRUB_ERR_BAD_ARGUMENT, | |
538 | N_("you need to load the kernel first")); | |
539 | goto fail; | |
540 | } | |
541 | ||
542 | if (next_start.mod_start || next_start.mod_len) | |
543 | { | |
544 | grub_error (GRUB_ERR_BAD_ARGUMENT, N_("initrd already loaded")); | |
545 | goto fail; | |
546 | } | |
547 | ||
548 | if (grub_initrd_init (argc, argv, &initrd_ctx)) | |
549 | goto fail; | |
550 | ||
551 | size = grub_get_initrd_size (&initrd_ctx); | |
552 | ||
553 | if (size) | |
554 | { | |
555 | err = grub_relocator_alloc_chunk_addr (relocator, &ch, max_addr, size); | |
556 | if (err) | |
557 | return err; | |
558 | ||
559 | if (grub_initrd_load (&initrd_ctx, argv, | |
560 | get_virtual_current_address (ch))) | |
561 | goto fail; | |
562 | } | |
563 | ||
564 | next_start.mod_start = max_addr + xen_inf.virt_base; | |
565 | next_start.mod_len = size; | |
566 | ||
567 | max_addr = ALIGN_UP (max_addr + size, PAGE_SIZE); | |
568 | ||
569 | grub_dprintf ("xen", "Initrd, addr=0x%x, size=0x%x\n", | |
570 | (unsigned) next_start.mod_start, (unsigned) size); | |
571 | ||
572 | fail: | |
573 | grub_initrd_close (&initrd_ctx); | |
574 | ||
575 | return grub_errno; | |
576 | } | |
577 | ||
578 | static grub_err_t | |
579 | grub_cmd_module (grub_command_t cmd __attribute__ ((unused)), | |
580 | int argc, char *argv[]) | |
581 | { | |
582 | grub_size_t size = 0; | |
583 | grub_err_t err; | |
584 | grub_relocator_chunk_t ch; | |
585 | grub_size_t cmdline_len; | |
586 | int nounzip = 0; | |
587 | grub_file_t file; | |
588 | ||
589 | if (argc == 0) | |
590 | return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); | |
591 | ||
592 | if (grub_strcmp (argv[0], "--nounzip") == 0) | |
593 | { | |
594 | argv++; | |
595 | argc--; | |
596 | nounzip = 1; | |
597 | } | |
598 | ||
599 | if (argc == 0) | |
600 | return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); | |
601 | ||
602 | if (!loaded) | |
603 | { | |
604 | return grub_error (GRUB_ERR_BAD_ARGUMENT, | |
605 | N_("you need to load the kernel first")); | |
606 | } | |
607 | ||
608 | if ((next_start.mod_start || next_start.mod_len) && !xen_module_info_page) | |
609 | { | |
610 | return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("initrd already loaded")); | |
611 | } | |
612 | ||
613 | /* Leave one space for terminator. */ | |
614 | if (n_modules >= MAX_MODULES - 1) | |
615 | { | |
616 | return grub_error (GRUB_ERR_BAD_ARGUMENT, "too many modules"); | |
617 | } | |
618 | ||
619 | if (!xen_module_info_page) | |
620 | { | |
621 | n_modules = 0; | |
622 | max_addr = ALIGN_UP (max_addr, PAGE_SIZE); | |
623 | modules_target_start = max_addr; | |
624 | next_start.mod_start = max_addr + xen_inf.virt_base; | |
625 | next_start.flags |= SIF_MULTIBOOT_MOD; | |
626 | ||
627 | err = grub_relocator_alloc_chunk_addr (relocator, &ch, | |
628 | max_addr, MAX_MODULES | |
629 | * | |
630 | sizeof (xen_module_info_page | |
631 | [0])); | |
632 | if (err) | |
633 | return err; | |
634 | xen_module_info_page = get_virtual_current_address (ch); | |
635 | grub_memset (xen_module_info_page, 0, MAX_MODULES | |
636 | * sizeof (xen_module_info_page[0])); | |
637 | max_addr += MAX_MODULES * sizeof (xen_module_info_page[0]); | |
638 | } | |
639 | ||
640 | max_addr = ALIGN_UP (max_addr, PAGE_SIZE); | |
641 | ||
642 | if (nounzip) | |
643 | grub_file_filter_disable_compression (); | |
644 | file = grub_file_open (argv[0]); | |
645 | if (!file) | |
646 | return grub_errno; | |
647 | size = grub_file_size (file); | |
648 | ||
649 | cmdline_len = grub_loader_cmdline_size (argc - 1, argv + 1); | |
650 | ||
651 | err = grub_relocator_alloc_chunk_addr (relocator, &ch, | |
652 | max_addr, cmdline_len); | |
653 | if (err) | |
654 | goto fail; | |
655 | ||
656 | grub_create_loader_cmdline (argc - 1, argv + 1, | |
657 | get_virtual_current_address (ch), cmdline_len); | |
658 | ||
659 | xen_module_info_page[n_modules].cmdline = max_addr - modules_target_start; | |
660 | max_addr = ALIGN_UP (max_addr + cmdline_len, PAGE_SIZE); | |
661 | ||
662 | if (size) | |
663 | { | |
664 | err = grub_relocator_alloc_chunk_addr (relocator, &ch, max_addr, size); | |
665 | if (err) | |
666 | goto fail; | |
667 | if (grub_file_read (file, get_virtual_current_address (ch), size) | |
668 | != (grub_ssize_t) size) | |
669 | { | |
670 | if (!grub_errno) | |
671 | grub_error (GRUB_ERR_FILE_READ_ERROR, | |
672 | N_("premature end of file %s"), argv[0]); | |
673 | goto fail; | |
674 | } | |
675 | } | |
676 | next_start.mod_len = max_addr + size - modules_target_start; | |
677 | xen_module_info_page[n_modules].mod_start = max_addr - modules_target_start; | |
678 | xen_module_info_page[n_modules].mod_end = | |
679 | max_addr + size - modules_target_start; | |
680 | ||
681 | n_modules++; | |
682 | grub_dprintf ("xen", "module, addr=0x%x, size=0x%x\n", | |
683 | (unsigned) max_addr, (unsigned) size); | |
684 | max_addr = ALIGN_UP (max_addr + size, PAGE_SIZE); | |
685 | ||
686 | ||
687 | fail: | |
688 | grub_file_close (file); | |
689 | ||
690 | return grub_errno; | |
691 | } | |
692 | ||
693 | static grub_command_t cmd_xen, cmd_initrd, cmd_module, cmd_multiboot; | |
694 | ||
695 | GRUB_MOD_INIT (xen) | |
696 | { | |
697 | cmd_xen = grub_register_command ("linux", grub_cmd_xen, | |
57a691b7 | 698 | 0, N_("Load Linux.")); |
9612ebc0 | 699 | cmd_multiboot = grub_register_command ("multiboot", grub_cmd_xen, |
57a691b7 | 700 | 0, N_("Load Linux.")); |
9612ebc0 VS |
701 | cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd, |
702 | 0, N_("Load initrd.")); | |
703 | cmd_module = grub_register_command ("module", grub_cmd_module, | |
704 | 0, N_("Load module.")); | |
705 | my_mod = mod; | |
706 | } | |
707 | ||
708 | GRUB_MOD_FINI (xen) | |
709 | { | |
710 | grub_unregister_command (cmd_xen); | |
711 | grub_unregister_command (cmd_initrd); | |
712 | grub_unregister_command (cmd_multiboot); | |
713 | grub_unregister_command (cmd_module); | |
714 | } |