2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2013 Free Software Foundation, Inc.
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.
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.
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/>.
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>
25 #include <grub/misc.h>
26 #include <grub/types.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>
37 #include <grub/elfload.h>
38 #include <grub/lib/cmdline.h>
40 #include <grub/xen_file.h>
41 #include <grub/linux.h>
43 GRUB_MOD_LICENSE ("GPLv3+");
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
;
56 #define PAGE_SIZE 4096
57 #define MAX_MODULES (PAGE_SIZE / sizeof (struct xen_multiboot_mod_list))
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)
66 page2offset (grub_uint64_t page
)
68 return page
<< PAGE_SHIFT
;
72 #define NUMBER_OF_LEVELS 4
73 #define INTERMEDIATE_OR 7
75 #define NUMBER_OF_LEVELS 3
76 #define INTERMEDIATE_OR 3
80 get_pgtable_size (grub_uint64_t total_pages
, grub_uint64_t virt_base
)
84 grub_uint64_t ret
= 0;
85 grub_uint64_t ll
= total_pages
;
87 for (i
= 0; i
< NUMBER_OF_LEVELS
; i
++)
89 ll
= (ll
+ POINTERS_PER_PAGE
- 1) >> LOG_POINTERS_PER_PAGE
;
90 /* PAE wants all 4 root directories present. */
97 for (i
= 1; i
< NUMBER_OF_LEVELS
; i
++)
98 if (virt_base
>> (PAGE_SHIFT
+ i
* LOG_POINTERS_PER_PAGE
))
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
)
111 grub_uint64_t lx
[NUMBER_OF_LEVELS
], lxs
[NUMBER_OF_LEVELS
];
112 grub_uint64_t nlx
, nls
, sz
= 0;
116 nls
= virt_base
>> PAGE_SHIFT
;
117 for (l
= 0; l
< NUMBER_OF_LEVELS
; l
++)
119 nlx
= (nlx
+ POINTERS_PER_PAGE
- 1) >> LOG_POINTERS_PER_PAGE
;
120 /* PAE wants all 4 root directories present. */
127 lxs
[l
] = nls
& (POINTERS_PER_PAGE
- 1);
130 nls
>>= LOG_POINTERS_PER_PAGE
;
135 grub_uint64_t
*pg
= (grub_uint64_t
*) where
;
138 grub_memset (pg
, 0, sz
* PAGE_SIZE
);
140 lp
= paging_start
+ lx
[NUMBER_OF_LEVELS
- 1];
141 for (l
= NUMBER_OF_LEVELS
- 1; l
>= 1; l
--)
144 pg
[0] = page2offset (mfn_list
[lp
++]) | INTERMEDIATE_OR
;
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
;
155 pg
[0] = page2offset (mfn_list
[total_pages
]) | 5;
157 pg
+= POINTERS_PER_PAGE
;
159 for (j
= 0; j
< total_pages
; j
++)
161 if (j
>= paging_start
&& j
< lp
)
162 pg
[j
+ lxs
[0]] = page2offset (mfn_list
[j
]) | 5;
164 pg
[j
+ lxs
[0]] = page2offset (mfn_list
[j
]) | 7;
169 set_mfns (grub_xen_mfn_t
* new_mfn_list
, grub_xen_mfn_t pfn
)
172 grub_xen_mfn_t cn_pfn
= -1, st_pfn
= -1;
173 struct mmu_update m2p_updates
[4];
176 for (i
= 0; i
< grub_xen_start_page_addr
->nr_pages
; i
++)
178 if (new_mfn_list
[i
] == grub_xen_start_page_addr
->console
.domU
.mfn
)
180 if (new_mfn_list
[i
] == grub_xen_start_page_addr
->store_mfn
)
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
;
194 m2p_updates
[0].ptr
= page2offset (new_mfn_list
[pfn
]) | MMU_MACHPHYS_UPDATE
;
195 m2p_updates
[0].val
= pfn
;
197 page2offset (new_mfn_list
[pfn
+ 1]) | MMU_MACHPHYS_UPDATE
;
198 m2p_updates
[1].val
= pfn
+ 1;
200 page2offset (new_mfn_list
[cn_pfn
]) | MMU_MACHPHYS_UPDATE
;
201 m2p_updates
[2].val
= cn_pfn
;
203 page2offset (new_mfn_list
[st_pfn
]) | MMU_MACHPHYS_UPDATE
;
204 m2p_updates
[3].val
= st_pfn
;
206 grub_xen_mmu_update (m2p_updates
, 4, NULL
, DOMID_SELF
);
208 return GRUB_ERR_NONE
;
214 struct grub_relocator_xen_state state
;
215 grub_relocator_chunk_t ch
;
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
;
225 if (grub_xen_n_allocated_shared_pages
)
226 return grub_error (GRUB_ERR_BUG
, "active grants");
228 state
.mfn_list
= max_addr
;
229 next_start
.mfn_list
= max_addr
+ xen_inf
.virt_base
;
230 next_start
.first_p2m_pfn
= max_addr
>> PAGE_SHIFT
; /* Is this right? */
231 pgtsize
= sizeof (grub_xen_mfn_t
) * grub_xen_start_page_addr
->nr_pages
;
232 err
= grub_relocator_alloc_chunk_addr (relocator
, &ch
, max_addr
, pgtsize
);
233 next_start
.nr_p2m_frames
= (pgtsize
+ PAGE_SIZE
- 1) >> PAGE_SHIFT
;
236 new_mfn_list
= get_virtual_current_address (ch
);
237 grub_memcpy (new_mfn_list
,
238 (void *) grub_xen_start_page_addr
->mfn_list
, pgtsize
);
239 max_addr
= ALIGN_UP (max_addr
+ pgtsize
, PAGE_SIZE
);
241 err
= grub_relocator_alloc_chunk_addr (relocator
, &ch
,
242 max_addr
, sizeof (next_start
));
245 state
.start_info
= max_addr
+ xen_inf
.virt_base
;
246 nst
= get_virtual_current_address (ch
);
247 max_addr
= ALIGN_UP (max_addr
+ sizeof (next_start
), PAGE_SIZE
);
249 next_start
.nr_pages
= grub_xen_start_page_addr
->nr_pages
;
250 grub_memcpy (next_start
.magic
, grub_xen_start_page_addr
->magic
,
251 sizeof (next_start
.magic
));
252 next_start
.store_mfn
= grub_xen_start_page_addr
->store_mfn
;
253 next_start
.store_evtchn
= grub_xen_start_page_addr
->store_evtchn
;
254 next_start
.console
.domU
= grub_xen_start_page_addr
->console
.domU
;
255 next_start
.shared_info
= grub_xen_start_page_addr
->shared_info
;
257 err
= set_mfns (new_mfn_list
, max_addr
>> PAGE_SHIFT
);
260 max_addr
+= 2 * PAGE_SIZE
;
262 next_start
.pt_base
= max_addr
+ xen_inf
.virt_base
;
263 state
.paging_start
= max_addr
>> PAGE_SHIFT
;
265 nr_info_pages
= max_addr
>> PAGE_SHIFT
;
266 nr_pages
= nr_info_pages
;
270 nr_pages
= ALIGN_UP (nr_pages
, (ALIGN_SIZE
>> PAGE_SHIFT
));
271 nr_pt_pages
= get_pgtable_size (nr_pages
, xen_inf
.virt_base
);
273 nr_info_pages
+ nr_pt_pages
+
274 ((ADDITIONAL_SIZE
+ STACK_SIZE
) >> PAGE_SHIFT
);
275 if (nr_pages
>= nr_need_pages
)
277 nr_pages
= nr_need_pages
;
280 grub_dprintf ("xen", "bootstrap domain %llx+%llx\n",
281 (unsigned long long) xen_inf
.virt_base
,
282 (unsigned long long) page2offset (nr_pages
));
284 err
= grub_relocator_alloc_chunk_addr (relocator
, &ch
,
285 max_addr
, page2offset (nr_pt_pages
));
289 generate_page_table (get_virtual_current_address (ch
),
290 max_addr
>> PAGE_SHIFT
, nr_pages
,
291 xen_inf
.virt_base
, new_mfn_list
);
293 max_addr
+= page2offset (nr_pt_pages
);
294 state
.stack
= max_addr
+ STACK_SIZE
+ xen_inf
.virt_base
;
295 state
.entry_point
= xen_inf
.entry_point
;
297 next_start
.nr_p2m_frames
+= nr_pt_pages
;
298 next_start
.nr_pt_frames
= nr_pt_pages
;
299 state
.paging_size
= nr_pt_pages
;
303 grub_memset (&gnttab_setver
, 0, sizeof (gnttab_setver
));
305 gnttab_setver
.version
= 1;
306 grub_xen_grant_table_op (GNTTABOP_set_version
, &gnttab_setver
, 1);
308 for (i
= 0; i
< ARRAY_SIZE (grub_xen_shared_info
->evtchn_pending
); i
++)
309 grub_xen_shared_info
->evtchn_pending
[i
] = 0;
311 return grub_relocator_xen_boot (relocator
, state
, nr_pages
,
313 PAGE_SIZE
? page2offset (nr_pages
) : 0,
315 page2offset (nr_pages
- 1) +
320 grub_xen_unload (void)
322 grub_dl_unref (my_mod
);
324 return GRUB_ERR_NONE
;
327 #define HYPERCALL_INTERFACE_SIZE 32
330 static grub_uint8_t
template[] =
332 0x51, /* push %rcx */
333 0x41, 0x53, /* push %r11 */
334 0x48, 0xc7, 0xc0, 0xbb, 0xaa, 0x00, 0x00, /* mov $0xaabb,%rax */
335 0x0f, 0x05, /* syscall */
336 0x41, 0x5b, /* pop %r11 */
341 static grub_uint8_t template_iret
[] =
343 0x51, /* push %rcx */
344 0x41, 0x53, /* push %r11 */
345 0x50, /* push %rax */
346 0x48, 0xc7, 0xc0, 0x17, 0x00, 0x00, 0x00, /* mov $0x17,%rax */
347 0x0f, 0x05 /* syscall */
349 #define CALLNO_OFFSET 6
352 static grub_uint8_t
template[] =
354 0xb8, 0xbb, 0xaa, 0x00, 0x00, /* mov imm32, %eax */
355 0xcd, 0x82, /* int $0x82 */
359 static grub_uint8_t template_iret
[] =
361 0x50, /* push %eax */
362 0xb8, 0x17, 0x00, 0x00, 0x00, /* mov $0x17,%eax */
363 0xcd, 0x82, /* int $0x82 */
365 #define CALLNO_OFFSET 1
371 set_hypercall_interface (grub_uint8_t
*tgt
, unsigned callno
)
375 grub_memcpy (tgt
, template_iret
, ARRAY_SIZE (template_iret
));
376 grub_memset (tgt
+ ARRAY_SIZE (template_iret
), 0xcc,
377 HYPERCALL_INTERFACE_SIZE
- ARRAY_SIZE (template_iret
));
380 grub_memcpy (tgt
, template, ARRAY_SIZE (template));
381 grub_memset (tgt
+ ARRAY_SIZE (template), 0xcc,
382 HYPERCALL_INTERFACE_SIZE
- ARRAY_SIZE (template));
383 tgt
[CALLNO_OFFSET
] = callno
& 0xff;
384 tgt
[CALLNO_OFFSET
+ 1] = callno
>> 8;
388 #define grub_elfXX_load grub_elf64_load
390 #define grub_elfXX_load grub_elf32_load
394 grub_cmd_xen (grub_command_t cmd
__attribute__ ((unused
)),
395 int argc
, char *argv
[])
402 return grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("filename expected"));
404 grub_loader_unset ();
406 grub_memset (&next_start
, 0, sizeof (next_start
));
408 xen_module_info_page
= NULL
;
411 grub_create_loader_cmdline (argc
- 1, argv
+ 1,
412 (char *) next_start
.cmd_line
,
413 sizeof (next_start
.cmd_line
) - 1);
415 file
= grub_file_open (argv
[0]);
419 elf
= grub_xen_file (file
);
423 err
= grub_xen_get_info (elf
, &xen_inf
);
427 if (xen_inf
.arch
!= GRUB_XEN_FILE_X86_64
)
429 if (xen_inf
.arch
!= GRUB_XEN_FILE_I386_PAE
430 && xen_inf
.arch
!= GRUB_XEN_FILE_I386_PAE_BIMODE
)
433 grub_error (GRUB_ERR_BAD_OS
, "incompatible architecture: %d",
438 if (xen_inf
.virt_base
& (PAGE_SIZE
- 1))
440 grub_error (GRUB_ERR_BAD_OS
, "unaligned virt_base");
443 grub_dprintf ("xen", "virt_base = %llx, entry = %llx\n",
444 (unsigned long long) xen_inf
.virt_base
,
445 (unsigned long long) xen_inf
.entry_point
);
447 relocator
= grub_relocator_new ();
451 grub_relocator_chunk_t ch
;
452 grub_addr_t kern_start
= xen_inf
.kern_start
- xen_inf
.paddr_offset
;
453 grub_addr_t kern_end
= xen_inf
.kern_end
- xen_inf
.paddr_offset
;
455 if (xen_inf
.has_hypercall_page
)
457 grub_dprintf ("xen", "hypercall page at 0x%llx\n",
458 (unsigned long long) xen_inf
.hypercall_page
);
459 if (xen_inf
.hypercall_page
- xen_inf
.virt_base
< kern_start
)
460 kern_start
= xen_inf
.hypercall_page
- xen_inf
.virt_base
;
462 if (xen_inf
.hypercall_page
- xen_inf
.virt_base
+ PAGE_SIZE
> kern_end
)
463 kern_end
= xen_inf
.hypercall_page
- xen_inf
.virt_base
+ PAGE_SIZE
;
466 max_addr
= ALIGN_UP (kern_end
, PAGE_SIZE
);
468 err
= grub_relocator_alloc_chunk_addr (relocator
, &ch
, kern_start
,
469 kern_end
- kern_start
);
472 kern_chunk_src
= get_virtual_current_address (ch
);
474 grub_dprintf ("xen", "paddr_offset = 0x%llx\n",
475 (unsigned long long) xen_inf
.paddr_offset
);
476 grub_dprintf ("xen", "kern_start = 0x%llx, kern_end = 0x%llx\n",
477 (unsigned long long) xen_inf
.kern_start
,
478 (unsigned long long) xen_inf
.kern_end
);
480 err
= grub_elfXX_load (elf
, argv
[0],
481 (grub_uint8_t
*) kern_chunk_src
- kern_start
482 - xen_inf
.paddr_offset
, 0, 0, 0);
484 if (xen_inf
.has_hypercall_page
)
487 for (i
= 0; i
< PAGE_SIZE
/ HYPERCALL_INTERFACE_SIZE
; i
++)
488 set_hypercall_interface ((grub_uint8_t
*) kern_chunk_src
+
489 i
* HYPERCALL_INTERFACE_SIZE
+
490 xen_inf
.hypercall_page
- xen_inf
.virt_base
-
497 grub_dl_ref (my_mod
);
500 grub_loader_set (grub_xen_boot
, grub_xen_unload
, 0);
508 grub_elf_close (elf
);
510 grub_file_close (file
);
512 if (grub_errno
!= GRUB_ERR_NONE
)
519 grub_cmd_initrd (grub_command_t cmd
__attribute__ ((unused
)),
520 int argc
, char *argv
[])
522 grub_size_t size
= 0;
524 struct grub_linux_initrd_context initrd_ctx
;
525 grub_relocator_chunk_t ch
;
529 grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("filename expected"));
535 grub_error (GRUB_ERR_BAD_ARGUMENT
,
536 N_("you need to load the kernel first"));
540 if (next_start
.mod_start
|| next_start
.mod_len
)
542 grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("initrd already loaded"));
546 if (grub_initrd_init (argc
, argv
, &initrd_ctx
))
549 size
= grub_get_initrd_size (&initrd_ctx
);
553 err
= grub_relocator_alloc_chunk_addr (relocator
, &ch
, max_addr
, size
);
557 if (grub_initrd_load (&initrd_ctx
, argv
,
558 get_virtual_current_address (ch
)))
562 next_start
.mod_start
= max_addr
+ xen_inf
.virt_base
;
563 next_start
.mod_len
= size
;
565 max_addr
= ALIGN_UP (max_addr
+ size
, PAGE_SIZE
);
567 grub_dprintf ("xen", "Initrd, addr=0x%x, size=0x%x\n",
568 (unsigned) next_start
.mod_start
, (unsigned) size
);
571 grub_initrd_close (&initrd_ctx
);
577 grub_cmd_module (grub_command_t cmd
__attribute__ ((unused
)),
578 int argc
, char *argv
[])
580 grub_size_t size
= 0;
582 grub_relocator_chunk_t ch
;
583 grub_size_t cmdline_len
;
588 return grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("filename expected"));
590 if (grub_strcmp (argv
[0], "--nounzip") == 0)
598 return grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("filename expected"));
602 return grub_error (GRUB_ERR_BAD_ARGUMENT
,
603 N_("you need to load the kernel first"));
606 if ((next_start
.mod_start
|| next_start
.mod_len
) && !xen_module_info_page
)
608 return grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("initrd already loaded"));
611 /* Leave one space for terminator. */
612 if (n_modules
>= MAX_MODULES
- 1)
614 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "too many modules");
617 if (!xen_module_info_page
)
620 max_addr
= ALIGN_UP (max_addr
, PAGE_SIZE
);
621 modules_target_start
= max_addr
;
622 next_start
.mod_start
= max_addr
+ xen_inf
.virt_base
;
623 next_start
.flags
|= SIF_MULTIBOOT_MOD
;
625 err
= grub_relocator_alloc_chunk_addr (relocator
, &ch
,
626 max_addr
, MAX_MODULES
628 sizeof (xen_module_info_page
632 xen_module_info_page
= get_virtual_current_address (ch
);
633 grub_memset (xen_module_info_page
, 0, MAX_MODULES
634 * sizeof (xen_module_info_page
[0]));
635 max_addr
+= MAX_MODULES
* sizeof (xen_module_info_page
[0]);
638 max_addr
= ALIGN_UP (max_addr
, PAGE_SIZE
);
641 grub_file_filter_disable_compression ();
642 file
= grub_file_open (argv
[0]);
645 size
= grub_file_size (file
);
647 cmdline_len
= grub_loader_cmdline_size (argc
- 1, argv
+ 1);
649 err
= grub_relocator_alloc_chunk_addr (relocator
, &ch
,
650 max_addr
, cmdline_len
);
654 grub_create_loader_cmdline (argc
- 1, argv
+ 1,
655 get_virtual_current_address (ch
), cmdline_len
);
657 xen_module_info_page
[n_modules
].cmdline
= max_addr
- modules_target_start
;
658 max_addr
= ALIGN_UP (max_addr
+ cmdline_len
, PAGE_SIZE
);
662 err
= grub_relocator_alloc_chunk_addr (relocator
, &ch
, max_addr
, size
);
665 if (grub_file_read (file
, get_virtual_current_address (ch
), size
)
666 != (grub_ssize_t
) size
)
669 grub_error (GRUB_ERR_FILE_READ_ERROR
,
670 N_("premature end of file %s"), argv
[0]);
674 next_start
.mod_len
= max_addr
+ size
- modules_target_start
;
675 xen_module_info_page
[n_modules
].mod_start
= max_addr
- modules_target_start
;
676 xen_module_info_page
[n_modules
].mod_end
=
677 max_addr
+ size
- modules_target_start
;
680 grub_dprintf ("xen", "module, addr=0x%x, size=0x%x\n",
681 (unsigned) max_addr
, (unsigned) size
);
682 max_addr
= ALIGN_UP (max_addr
+ size
, PAGE_SIZE
);
686 grub_file_close (file
);
691 static grub_command_t cmd_xen
, cmd_initrd
, cmd_module
, cmd_multiboot
;
695 cmd_xen
= grub_register_command ("linux", grub_cmd_xen
,
696 0, N_("Load Linux."));
697 cmd_multiboot
= grub_register_command ("multiboot", grub_cmd_xen
,
698 0, N_("Load Linux."));
699 cmd_initrd
= grub_register_command ("initrd", grub_cmd_initrd
,
700 0, N_("Load initrd."));
701 cmd_module
= grub_register_command ("module", grub_cmd_module
,
702 0, N_("Load module."));
708 grub_unregister_command (cmd_xen
);
709 grub_unregister_command (cmd_initrd
);
710 grub_unregister_command (cmd_multiboot
);
711 grub_unregister_command (cmd_module
);