summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVarad Gautam <vrd@amazon.de>2019-06-27 12:12:43 +0200
committerSimon Horman <horms@verge.net.au>2019-07-03 09:56:22 +0200
commit22a2ed55132e2a3afc639034484a48120ef2a464 (patch)
tree982731b3390ece2e7f91525d00e47f53beae386c
parentd6bc88c06962b74c7d0d6af8f2193d2483caae97 (diff)
downloadkexec-tools-22a2ed55132e2a3afc639034484a48120ef2a464.tar.gz
x86: Support multiboot2 images
Add a new type `multiboot2-x86` that allows loading multiboot2 [1] images within the relocation range specified in the image header. The image is always placed at the lowest available address, regardless of the preference information. [1] https://www.gnu.org/software/grub/manual/multiboot2/multiboot.html Signed-off-by: Varad Gautam <vrd@amazon.de> Signed-off-by: Simon Horman <horms@verge.net.au>
-rw-r--r--include/x86/multiboot2.h416
-rw-r--r--kexec/arch/i386/kexec-mb2-x86.c543
-rw-r--r--kexec/arch/i386/kexec-x86.c2
-rw-r--r--kexec/arch/x86_64/Makefile1
-rw-r--r--kexec/arch/x86_64/kexec-x86_64.c2
-rw-r--r--kexec/arch/x86_64/kexec-x86_64.h5
-rw-r--r--kexec/kexec.824
7 files changed, 993 insertions, 0 deletions
diff --git a/include/x86/multiboot2.h b/include/x86/multiboot2.h
new file mode 100644
index 00000000..5693923c
--- /dev/null
+++ b/include/x86/multiboot2.h
@@ -0,0 +1,416 @@
+/* multiboot2.h - Multiboot 2 header file. */
+/* Copyright (C) 1999,2003,2007,2008,2009,2010 Free Software Foundation, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY
+ * DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+ * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef MULTIBOOT_HEADER
+#define MULTIBOOT_HEADER 1
+
+/* How many bytes from the start of the file we search for the header. */
+#define MULTIBOOT_SEARCH 32768
+#define MULTIBOOT_HEADER_ALIGN 8
+
+/* The magic field should contain this. */
+#define MULTIBOOT2_HEADER_MAGIC 0xe85250d6
+
+/* This should be in %eax. */
+#define MULTIBOOT2_BOOTLOADER_MAGIC 0x36d76289
+
+/* Alignment of multiboot modules. */
+#define MULTIBOOT_MOD_ALIGN 0x00001000
+
+/* Alignment of the multiboot info structure. */
+#define MULTIBOOT_INFO_ALIGN 0x00000008
+
+/* Flags set in the 'flags' member of the multiboot header. */
+
+#define MULTIBOOT_TAG_ALIGN 8
+#define MULTIBOOT_TAG_TYPE_END 0
+#define MULTIBOOT_TAG_TYPE_CMDLINE 1
+#define MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME 2
+#define MULTIBOOT_TAG_TYPE_MODULE 3
+#define MULTIBOOT_TAG_TYPE_BASIC_MEMINFO 4
+#define MULTIBOOT_TAG_TYPE_BOOTDEV 5
+#define MULTIBOOT_TAG_TYPE_MMAP 6
+#define MULTIBOOT_TAG_TYPE_VBE 7
+#define MULTIBOOT_TAG_TYPE_FRAMEBUFFER 8
+#define MULTIBOOT_TAG_TYPE_ELF_SECTIONS 9
+#define MULTIBOOT_TAG_TYPE_APM 10
+#define MULTIBOOT_TAG_TYPE_EFI32 11
+#define MULTIBOOT_TAG_TYPE_EFI64 12
+#define MULTIBOOT_TAG_TYPE_SMBIOS 13
+#define MULTIBOOT_TAG_TYPE_ACPI_OLD 14
+#define MULTIBOOT_TAG_TYPE_ACPI_NEW 15
+#define MULTIBOOT_TAG_TYPE_NETWORK 16
+#define MULTIBOOT_TAG_TYPE_EFI_MMAP 17
+#define MULTIBOOT_TAG_TYPE_EFI_BS 18
+#define MULTIBOOT_TAG_TYPE_EFI32_IH 19
+#define MULTIBOOT_TAG_TYPE_EFI64_IH 20
+#define MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR 21
+
+#define MULTIBOOT_HEADER_TAG_END 0
+#define MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST 1
+#define MULTIBOOT_HEADER_TAG_ADDRESS 2
+#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS 3
+#define MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS 4
+#define MULTIBOOT_HEADER_TAG_FRAMEBUFFER 5
+#define MULTIBOOT_HEADER_TAG_MODULE_ALIGN 6
+#define MULTIBOOT_HEADER_TAG_EFI_BS 7
+#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64 9
+#define MULTIBOOT_HEADER_TAG_RELOCATABLE 10
+
+#define MULTIBOOT2_ARCHITECTURE_I386 0
+#define MULTIBOOT2_ARCHITECTURE_MIPS32 4
+#define MULTIBOOT_HEADER_TAG_OPTIONAL 1
+
+#define MULTIBOOT_LOAD_PREFERENCE_NONE 0
+#define MULTIBOOT_LOAD_PREFERENCE_LOW 1
+#define MULTIBOOT_LOAD_PREFERENCE_HIGH 2
+
+#define MULTIBOOT_CONSOLE_FLAGS_CONSOLE_REQUIRED 1
+#define MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED 2
+
+#ifndef ASM_FILE
+
+typedef unsigned char multiboot_uint8_t;
+typedef unsigned short multiboot_uint16_t;
+typedef unsigned int multiboot_uint32_t;
+typedef unsigned long long multiboot_uint64_t;
+
+struct multiboot_header
+{
+ /* Must be MULTIBOOT_MAGIC - see above. */
+ multiboot_uint32_t magic;
+
+ /* ISA */
+ multiboot_uint32_t architecture;
+
+ /* Total header length. */
+ multiboot_uint32_t header_length;
+
+ /* The above fields plus this one must equal 0 mod 2^32. */
+ multiboot_uint32_t checksum;
+};
+
+struct multiboot_header_tag
+{
+ multiboot_uint16_t type;
+ multiboot_uint16_t flags;
+ multiboot_uint32_t size;
+};
+
+struct multiboot_header_tag_information_request
+{
+ multiboot_uint16_t type;
+ multiboot_uint16_t flags;
+ multiboot_uint32_t size;
+ multiboot_uint32_t requests[0];
+};
+
+struct multiboot_header_tag_address
+{
+ multiboot_uint16_t type;
+ multiboot_uint16_t flags;
+ multiboot_uint32_t size;
+ multiboot_uint32_t header_addr;
+ multiboot_uint32_t load_addr;
+ multiboot_uint32_t load_end_addr;
+ multiboot_uint32_t bss_end_addr;
+};
+
+struct multiboot_header_tag_entry_address
+{
+ multiboot_uint16_t type;
+ multiboot_uint16_t flags;
+ multiboot_uint32_t size;
+ multiboot_uint32_t entry_addr;
+};
+
+struct multiboot_header_tag_console_flags
+{
+ multiboot_uint16_t type;
+ multiboot_uint16_t flags;
+ multiboot_uint32_t size;
+ multiboot_uint32_t console_flags;
+};
+
+struct multiboot_header_tag_framebuffer
+{
+ multiboot_uint16_t type;
+ multiboot_uint16_t flags;
+ multiboot_uint32_t size;
+ multiboot_uint32_t width;
+ multiboot_uint32_t height;
+ multiboot_uint32_t depth;
+};
+
+struct multiboot_header_tag_module_align
+{
+ multiboot_uint16_t type;
+ multiboot_uint16_t flags;
+ multiboot_uint32_t size;
+};
+
+struct multiboot_header_tag_relocatable
+{
+ multiboot_uint16_t type;
+ multiboot_uint16_t flags;
+ multiboot_uint32_t size;
+ multiboot_uint32_t min_addr;
+ multiboot_uint32_t max_addr;
+ multiboot_uint32_t align;
+ multiboot_uint32_t preference;
+};
+
+struct multiboot_color
+{
+ multiboot_uint8_t red;
+ multiboot_uint8_t green;
+ multiboot_uint8_t blue;
+};
+
+struct multiboot_mmap_entry
+{
+ multiboot_uint64_t addr;
+ multiboot_uint64_t len;
+#define MULTIBOOT_MEMORY_AVAILABLE 1
+#define MULTIBOOT_MEMORY_RESERVED 2
+#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3
+#define MULTIBOOT_MEMORY_NVS 4
+#define MULTIBOOT_MEMORY_BADRAM 5
+ multiboot_uint32_t type;
+ multiboot_uint32_t zero;
+};
+typedef struct multiboot_mmap_entry multiboot_memory_map_t;
+
+struct multiboot_tag
+{
+ multiboot_uint32_t type;
+ multiboot_uint32_t size;
+};
+
+struct multiboot_tag_string
+{
+ multiboot_uint32_t type;
+ multiboot_uint32_t size;
+ char string[0];
+};
+
+struct multiboot_tag_module
+{
+ multiboot_uint32_t type;
+ multiboot_uint32_t size;
+ multiboot_uint32_t mod_start;
+ multiboot_uint32_t mod_end;
+ char cmdline[0];
+};
+
+struct multiboot_tag_basic_meminfo
+{
+ multiboot_uint32_t type;
+ multiboot_uint32_t size;
+ multiboot_uint32_t mem_lower;
+ multiboot_uint32_t mem_upper;
+};
+
+struct multiboot_tag_bootdev
+{
+ multiboot_uint32_t type;
+ multiboot_uint32_t size;
+ multiboot_uint32_t biosdev;
+ multiboot_uint32_t slice;
+ multiboot_uint32_t part;
+};
+
+struct multiboot_tag_mmap
+{
+ multiboot_uint32_t type;
+ multiboot_uint32_t size;
+ multiboot_uint32_t entry_size;
+ multiboot_uint32_t entry_version;
+ struct multiboot_mmap_entry entries[0];
+};
+
+struct multiboot_vbe_info_block
+{
+ multiboot_uint8_t external_specification[512];
+};
+
+struct multiboot_vbe_mode_info_block
+{
+ multiboot_uint8_t external_specification[256];
+};
+
+struct multiboot_tag_vbe
+{
+ multiboot_uint32_t type;
+ multiboot_uint32_t size;
+
+ multiboot_uint16_t vbe_mode;
+ multiboot_uint16_t vbe_interface_seg;
+ multiboot_uint16_t vbe_interface_off;
+ multiboot_uint16_t vbe_interface_len;
+
+ struct multiboot_vbe_info_block vbe_control_info;
+ struct multiboot_vbe_mode_info_block vbe_mode_info;
+};
+
+struct multiboot_tag_framebuffer_common
+{
+ multiboot_uint32_t type;
+ multiboot_uint32_t size;
+
+ multiboot_uint64_t framebuffer_addr;
+ multiboot_uint32_t framebuffer_pitch;
+ multiboot_uint32_t framebuffer_width;
+ multiboot_uint32_t framebuffer_height;
+ multiboot_uint8_t framebuffer_bpp;
+#define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0
+#define MULTIBOOT_FRAMEBUFFER_TYPE_RGB 1
+#define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT 2
+ multiboot_uint8_t framebuffer_type;
+ multiboot_uint16_t reserved;
+};
+
+struct multiboot_tag_framebuffer
+{
+ struct multiboot_tag_framebuffer_common common;
+
+ union
+ {
+ struct
+ {
+ multiboot_uint16_t framebuffer_palette_num_colors;
+ struct multiboot_color framebuffer_palette[0];
+ };
+ struct
+ {
+ multiboot_uint8_t framebuffer_red_field_position;
+ multiboot_uint8_t framebuffer_red_mask_size;
+ multiboot_uint8_t framebuffer_green_field_position;
+ multiboot_uint8_t framebuffer_green_mask_size;
+ multiboot_uint8_t framebuffer_blue_field_position;
+ multiboot_uint8_t framebuffer_blue_mask_size;
+ };
+ };
+};
+
+struct multiboot_tag_elf_sections
+{
+ multiboot_uint32_t type;
+ multiboot_uint32_t size;
+ multiboot_uint32_t num;
+ multiboot_uint32_t entsize;
+ multiboot_uint32_t shndx;
+ char sections[0];
+};
+
+struct multiboot_tag_apm
+{
+ multiboot_uint32_t type;
+ multiboot_uint32_t size;
+ multiboot_uint16_t version;
+ multiboot_uint16_t cseg;
+ multiboot_uint32_t offset;
+ multiboot_uint16_t cseg_16;
+ multiboot_uint16_t dseg;
+ multiboot_uint16_t flags;
+ multiboot_uint16_t cseg_len;
+ multiboot_uint16_t cseg_16_len;
+ multiboot_uint16_t dseg_len;
+};
+
+struct multiboot_tag_efi32
+{
+ multiboot_uint32_t type;
+ multiboot_uint32_t size;
+ multiboot_uint32_t pointer;
+};
+
+struct multiboot_tag_efi64
+{
+ multiboot_uint32_t type;
+ multiboot_uint32_t size;
+ multiboot_uint64_t pointer;
+};
+
+struct multiboot_tag_smbios
+{
+ multiboot_uint32_t type;
+ multiboot_uint32_t size;
+ multiboot_uint8_t major;
+ multiboot_uint8_t minor;
+ multiboot_uint8_t reserved[6];
+ multiboot_uint8_t tables[0];
+};
+
+struct multiboot_tag_old_acpi
+{
+ multiboot_uint32_t type;
+ multiboot_uint32_t size;
+ multiboot_uint8_t rsdp[0];
+};
+
+struct multiboot_tag_new_acpi
+{
+ multiboot_uint32_t type;
+ multiboot_uint32_t size;
+ multiboot_uint8_t rsdp[0];
+};
+
+struct multiboot_tag_network
+{
+ multiboot_uint32_t type;
+ multiboot_uint32_t size;
+ multiboot_uint8_t dhcpack[0];
+};
+
+struct multiboot_tag_efi_mmap
+{
+ multiboot_uint32_t type;
+ multiboot_uint32_t size;
+ multiboot_uint32_t descr_size;
+ multiboot_uint32_t descr_vers;
+ multiboot_uint8_t efi_mmap[0];
+};
+
+struct multiboot_tag_efi32_ih
+{
+ multiboot_uint32_t type;
+ multiboot_uint32_t size;
+ multiboot_uint32_t pointer;
+};
+
+struct multiboot_tag_efi64_ih
+{
+ multiboot_uint32_t type;
+ multiboot_uint32_t size;
+ multiboot_uint64_t pointer;
+};
+
+struct multiboot_tag_load_base_addr
+{
+ multiboot_uint32_t type;
+ multiboot_uint32_t size;
+ multiboot_uint32_t load_base_addr;
+};
+
+#endif /* ! ASM_FILE */
+
+#endif /* ! MULTIBOOT_HEADER */
diff --git a/kexec/arch/i386/kexec-mb2-x86.c b/kexec/arch/i386/kexec-mb2-x86.c
new file mode 100644
index 00000000..7eaab0c0
--- /dev/null
+++ b/kexec/arch/i386/kexec-mb2-x86.c
@@ -0,0 +1,543 @@
+/*
+ * kexec-mb2-x86.c
+ *
+ * multiboot2 support for kexec to boot xen.
+ *
+ * Copyright (C) 2019 Varad Gautam (vrd at amazon.de), Amazon.com, Inc. or its affiliates.
+ *
+ * Parts based on GNU GRUB, Copyright (C) 2000 Free Software Foundation, Inc
+ * Parts taken from kexec-multiboot-x86.c, Eric Biederman (ebiederm@xmission.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <elf.h>
+#include <boot/elf_boot.h>
+#include <ip_checksum.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+#include "kexec-x86.h"
+#include <arch/options.h>
+
+/* From GNU GRUB */
+#include <x86/multiboot2.h>
+#include <x86/mb_info.h>
+
+/* Framebuffer */
+#include <sys/ioctl.h>
+#include <linux/fb.h>
+
+extern struct arch_options_t arch_options;
+
+/* Static storage */
+static char headerbuf[MULTIBOOT_SEARCH];
+static struct multiboot_header *mbh = NULL;
+struct multiboot2_header_info {
+ struct multiboot_header_tag_information_request *request_tag;
+ struct multiboot_header_tag_address *addr_tag;
+ struct multiboot_header_tag_entry_address *entry_addr_tag;
+ struct multiboot_header_tag_console_flags *console_tag;
+ struct multiboot_header_tag_framebuffer *fb_tag;
+ struct multiboot_header_tag_module_align *mod_align_tag;
+ struct multiboot_header_tag_relocatable *rel_tag;
+} mhi;
+
+#define ALIGN_UP(addr, align) \
+ ((addr + (typeof (addr)) align - 1) & ~((typeof (addr)) align - 1))
+
+int multiboot2_x86_probe(const char *buf, off_t buf_len)
+/* Is it a good idea to try booting this file? */
+{
+ int i, len;
+ /* First of all, check that this is an ELF file */
+ if ((i=elf_x86_probe(buf, buf_len)) < 0)
+ return i;
+
+ /* Now look for a multiboot header. */
+ len = MULTIBOOT_SEARCH;
+ if (len > buf_len)
+ len = buf_len;
+
+ memcpy(headerbuf, buf, len);
+ if (len < sizeof(struct multiboot_header)) {
+ /* Short file */
+ return -1;
+ }
+ for (mbh = (struct multiboot_header *) headerbuf;
+ ((char *) mbh <= (char *) headerbuf + len - sizeof(struct multiboot_header));
+ mbh = (struct multiboot_header *) ((char *) mbh + MULTIBOOT_HEADER_ALIGN)) {
+ if (mbh->magic == MULTIBOOT2_HEADER_MAGIC
+ && !((mbh->magic+mbh->architecture+mbh->header_length+mbh->checksum) & 0xffffffff)) {
+ /* Found multiboot header. */
+ return 0;
+ }
+ }
+ /* Not multiboot */
+ return -1;
+}
+
+void multiboot2_x86_usage(void)
+/* Multiboot-specific options */
+{
+ printf(" --command-line=STRING Set the kernel command line to STRING.\n");
+ printf(" --reuse-cmdline Use kernel command line from running system.\n");
+ printf(" --module=\"MOD arg1 arg2...\" Load module MOD with command-line \"arg1...\"\n");
+ printf(" (can be used multiple times).\n");
+}
+
+static size_t
+multiboot2_get_mbi_size(int ranges, int cmdline_size, int modcount, int modcmd_size)
+{
+ return (2 * sizeof (uint32_t) + sizeof (struct multiboot_tag)
+ + sizeof (struct multiboot_tag)
+ + ALIGN_UP (sizeof (struct multiboot_tag_basic_meminfo), MULTIBOOT_TAG_ALIGN)
+ + ALIGN_UP ((sizeof (struct multiboot_tag_mmap)
+ + ranges * sizeof (struct multiboot_mmap_entry)), MULTIBOOT_TAG_ALIGN)
+ + ALIGN_UP (sizeof (struct multiboot_tag_load_base_addr), MULTIBOOT_TAG_ALIGN)
+ + (sizeof (struct multiboot_tag_string)
+ + ALIGN_UP (cmdline_size, MULTIBOOT_TAG_ALIGN))
+ + (sizeof (struct multiboot_tag_string)
+ + ALIGN_UP (strlen(BOOTLOADER " " BOOTLOADER_VERSION) + 1, MULTIBOOT_TAG_ALIGN))
+ + (modcount * sizeof (struct multiboot_tag_module) + modcmd_size));
+}
+
+static void multiboot2_read_header_tags(void)
+{
+ struct multiboot_header_tag *tag;
+
+ for (tag = (struct multiboot_header_tag *) (mbh + 1);
+ tag->type != MULTIBOOT_TAG_TYPE_END;
+ tag = (struct multiboot_header_tag *) ((char *) tag + ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN)))
+ {
+ switch (tag->type)
+ {
+ case MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST:
+ {
+ mhi.request_tag = (struct multiboot_header_tag_information_request *) tag;
+ break;
+ }
+ case MULTIBOOT_HEADER_TAG_RELOCATABLE:
+ {
+ mhi.rel_tag = (struct multiboot_header_tag_relocatable *) tag;
+ break;
+ }
+ case MULTIBOOT_HEADER_TAG_ADDRESS:
+ {
+ mhi.addr_tag = (struct multiboot_header_tag_address *) tag;
+ break;
+ }
+ case MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS:
+ {
+ mhi.entry_addr_tag = (struct multiboot_header_tag_entry_address *) tag;
+ break;
+ }
+ case MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS:
+ {
+ mhi.console_tag = (struct multiboot_header_tag_console_flags *) tag;
+ break;
+ }
+ case MULTIBOOT_HEADER_TAG_FRAMEBUFFER:
+ {
+ mhi.fb_tag = (struct multiboot_header_tag_framebuffer *) tag;
+ break;
+ }
+ case MULTIBOOT_HEADER_TAG_MODULE_ALIGN:
+ {
+ mhi.mod_align_tag = (struct multiboot_header_tag_module_align *) tag;
+ break;
+ }
+ case MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64:
+ case MULTIBOOT_HEADER_TAG_EFI_BS:
+ /* Ignoring EFI. */
+ break;
+ default:
+ {
+ if (!(tag->flags & MULTIBOOT_HEADER_TAG_OPTIONAL))
+ fprintf(stderr, "unsupported tag: 0x%x", tag->type);
+ break;
+ }
+ }
+ }
+}
+
+struct multiboot_mmap_entry *multiboot_construct_memory_map(struct memory_range *range,
+ int ranges,
+ unsigned long long *mem_lower,
+ unsigned long long *mem_upper)
+{
+ struct multiboot_mmap_entry *entries;
+ int i;
+
+ *mem_lower = *mem_upper = 0;
+ entries = xmalloc(ranges * sizeof(*entries));
+ for (i = 0; i < ranges; i++) {
+ entries[i].addr = range[i].start;
+ entries[i].len = range[i].end - range[i].start + 1;
+
+ if (range[i].type == RANGE_RAM) {
+ entries[i].type = MULTIBOOT_MEMORY_AVAILABLE;
+ /*
+ * Is this the "low" memory? Can't just test
+ * against zero, because Linux protects (and
+ * hides) the first few pages of physical
+ * memory.
+ */
+
+ if ((range[i].start <= 64*1024)
+ && (range[i].end > *mem_lower)) {
+ range[i].start = 0;
+ *mem_lower = range[i].end;
+ }
+ /* Is this the "high" memory? */
+ if ((range[i].start <= 0x100000)
+ && (range[i].end > *mem_upper + 0x100000))
+ *mem_upper = range[i].end - 0x100000;
+ } else if (range[i].type == RANGE_ACPI)
+ entries[i].type = MULTIBOOT_MEMORY_ACPI_RECLAIMABLE;
+ else if (range[i].type == RANGE_ACPI_NVS)
+ entries[i].type = MULTIBOOT_MEMORY_NVS;
+ else if (range[i].type == RANGE_RESERVED)
+ entries[i].type = MULTIBOOT_MEMORY_RESERVED;
+ }
+ return entries;
+}
+
+static uint64_t multiboot2_make_mbi(struct kexec_info *info, char *cmdline, int cmdline_len,
+ unsigned long load_base_addr, void *mbi_buf, size_t mbi_bytes)
+{
+ uint64_t *ptrorig = mbi_buf;
+ struct multiboot_mmap_entry *mmap_entries;
+ unsigned long long mem_lower = 0, mem_upper = 0;
+
+ *ptrorig = mbi_bytes; /* u32 total_size, u32 reserved */
+ ptrorig++;
+
+ mmap_entries = multiboot_construct_memory_map(info->memory_range, info->memory_ranges, &mem_lower, &mem_upper);
+ {
+ struct multiboot_tag_basic_meminfo *tag = (struct multiboot_tag_basic_meminfo *) ptrorig;
+
+ tag->type = MULTIBOOT_TAG_TYPE_BASIC_MEMINFO;
+ tag->size = sizeof (struct multiboot_tag_basic_meminfo);
+ tag->mem_lower = mem_lower;
+ tag->mem_upper = mem_upper;
+ ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (*ptrorig);
+ }
+
+ {
+ struct multiboot_tag_mmap *tag = (struct multiboot_tag_mmap *) ptrorig;
+
+ tag->type = MULTIBOOT_TAG_TYPE_MMAP;
+ tag->size = sizeof(struct multiboot_tag_mmap) + sizeof(struct multiboot_mmap_entry) * info->memory_ranges;
+ tag->entry_size = sizeof(struct multiboot_mmap_entry);
+ tag->entry_version = 0;
+ memcpy(tag->entries, mmap_entries, tag->entry_size * info->memory_ranges);
+ ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (*ptrorig);
+ }
+
+ if (mhi.rel_tag) {
+ struct multiboot_tag_load_base_addr *tag = (struct multiboot_tag_load_base_addr *) ptrorig;
+
+ tag->type = MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR;
+ tag->size = sizeof (struct multiboot_tag_load_base_addr);
+ tag->load_base_addr = load_base_addr;
+ ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (*ptrorig);
+ }
+
+ {
+ struct multiboot_tag_string *tag = (struct multiboot_tag_string *) ptrorig;
+
+ tag->type = MULTIBOOT_TAG_TYPE_CMDLINE;
+ tag->size = sizeof (struct multiboot_tag_string) + cmdline_len;
+ memcpy(tag->string, cmdline, cmdline_len);
+ ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (*ptrorig);
+ }
+
+ {
+ struct multiboot_tag_string *tag = (struct multiboot_tag_string *) ptrorig;
+
+ tag->type = MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME;
+ tag->size = sizeof(struct multiboot_tag_string) + strlen(BOOTLOADER " " BOOTLOADER_VERSION) + 1;
+ sprintf(tag->string, "%s", BOOTLOADER " " BOOTLOADER_VERSION);
+ ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (*ptrorig);
+ }
+
+ if (mhi.fb_tag) {
+ struct multiboot_tag_framebuffer *tag = (struct multiboot_tag_framebuffer *) ptrorig;
+ struct fb_fix_screeninfo info;
+ struct fb_var_screeninfo mode;
+ int fd;
+
+ tag->common.type = MULTIBOOT_TAG_TYPE_FRAMEBUFFER;
+ tag->common.size = sizeof(struct multiboot_tag_framebuffer);
+ /* check if purgatory will reset to standard ega text mode */
+ if (arch_options.reset_vga || arch_options.console_vga) {
+ tag->common.framebuffer_type = MB_FRAMEBUFFER_TYPE_EGA_TEXT;
+ tag->common.framebuffer_addr = 0xb8000;
+ tag->common.framebuffer_pitch = 80*2;
+ tag->common.framebuffer_width = 80;
+ tag->common.framebuffer_height = 25;
+ tag->common.framebuffer_bpp = 16;
+
+ ptrorig += ALIGN_UP (tag->common.size, MULTIBOOT_TAG_ALIGN) / sizeof (*ptrorig);
+ goto out;
+ }
+
+ /* use current graphics framebuffer settings */
+ fd = open("/dev/fb0", O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "can't open /dev/fb0: %s\n", strerror(errno));
+ goto out;
+ }
+ if (ioctl(fd, FBIOGET_FSCREENINFO, &info) < 0){
+ fprintf(stderr, "can't get screeninfo: %s\n", strerror(errno));
+ close(fd);
+ goto out;
+ }
+ if (ioctl(fd, FBIOGET_VSCREENINFO, &mode) < 0){
+ fprintf(stderr, "can't get modeinfo: %s\n", strerror(errno));
+ close(fd);
+ goto out;
+ }
+ close(fd);
+
+ if (info.smem_start == 0 || info.smem_len == 0) {
+ fprintf(stderr, "can't get linerar framebuffer address\n");
+ goto out;
+ }
+
+ if (info.type != FB_TYPE_PACKED_PIXELS) {
+ fprintf(stderr, "unsupported framebuffer type\n");
+ goto out;
+ }
+
+ if (info.visual != FB_VISUAL_TRUECOLOR) {
+ fprintf(stderr, "unsupported framebuffer visual\n");
+ goto out;
+ }
+
+ tag->common.framebuffer_type = MB_FRAMEBUFFER_TYPE_RGB;
+ tag->common.framebuffer_addr = info.smem_start;
+ tag->common.framebuffer_pitch = info.line_length;
+ tag->common.framebuffer_width = mode.xres;
+ tag->common.framebuffer_height = mode.yres;
+ tag->common.framebuffer_bpp = mode.bits_per_pixel;
+
+ tag->framebuffer_red_field_position = mode.red.offset;
+ tag->framebuffer_red_mask_size = mode.red.length;
+ tag->framebuffer_green_field_position = mode.green.offset;
+ tag->framebuffer_green_mask_size = mode.green.length;
+ tag->framebuffer_blue_field_position = mode.blue.offset;
+ tag->framebuffer_blue_mask_size = mode.blue.length;
+
+ ptrorig += ALIGN_UP (tag->common.size, MULTIBOOT_TAG_ALIGN) / sizeof (*ptrorig);
+ }
+
+out:
+ return (uint64_t) ptrorig;
+}
+
+static uint64_t multiboot2_mbi_add_module(void *mbi_buf, uint64_t mbi_ptr, uint32_t mod_start,
+ uint32_t mod_end, char *mod_clp)
+{
+ struct multiboot_tag_module *tag = (struct multiboot_tag_module *) mbi_ptr;
+
+ tag->type = MULTIBOOT_TAG_TYPE_MODULE;
+ tag->size = sizeof(struct multiboot_tag_module) + strlen((char *)(long) mod_clp) + 1;
+ tag->mod_start = mod_start;
+ tag->mod_end = mod_end;
+
+ memcpy(tag->cmdline, (char *)(long) mod_clp, strlen((char *)(long) mod_clp) + 1);
+ mbi_ptr += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN);
+
+ return mbi_ptr;
+}
+
+static uint64_t multiboot2_mbi_end(void *mbi_buf, uint64_t mbi_ptr)
+{
+ struct multiboot_tag *tag = (struct multiboot_tag *) mbi_ptr;
+
+ tag->type = MULTIBOOT_TAG_TYPE_END;
+ tag->size = sizeof (struct multiboot_tag);
+ mbi_ptr += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN);
+
+ return mbi_ptr;
+}
+
+int multiboot2_x86_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info)
+{
+ struct mem_ehdr ehdr;
+ void *mbi_buf;
+ size_t mbi_bytes;
+ unsigned long addr;
+ struct entry32_regs regs;
+ char *command_line = NULL, *tmp_cmdline = NULL;
+ int command_line_len;
+ char *imagename, *cp, *append = NULL;;
+ int result;
+ int opt;
+ int modules, mod_command_line_space;
+ uint64_t mbi_ptr;
+ char *mod_clp_base;
+ /* See options.h -- add any more there, too. */
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ { "command-line", 1, 0, OPT_CL },
+ { "append", 1, 0, OPT_CL },
+ { "reuse-cmdline", 0, 0, OPT_REUSE_CMDLINE },
+ { "module", 1, 0, OPT_MOD },
+ { 0, 0, 0, 0 },
+ };
+ static const char short_options[] = KEXEC_ARCH_OPT_STR "";
+
+ /* Probe for the MB header if it's not already found */
+ if (mbh == NULL && multiboot_x86_probe(buf, len) != 1)
+ {
+ fprintf(stderr, "Cannot find a loadable multiboot2 header.\n");
+ return -1;
+ }
+
+ /* Parse the header tags. */
+ multiboot2_read_header_tags();
+
+ /* Parse the command line */
+ command_line_len = 0;
+ modules = 0;
+ mod_command_line_space = 0;
+ result = 0;
+ while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1)
+ {
+ switch(opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_ARCH_MAX) {
+ break;
+ }
+ case OPT_CL:
+ append = optarg;
+ break;
+ case OPT_REUSE_CMDLINE:
+ tmp_cmdline = get_command_line();
+ break;
+ case OPT_MOD:
+ modules++;
+ mod_command_line_space += strlen(optarg) + 1;
+ break;
+ }
+ }
+ imagename = argv[optind];
+
+ /* Final command line = imagename + <OPT_REUSE_CMDLINE> + <OPT_CL> */
+ tmp_cmdline = concat_cmdline(command_line, append);
+ if (command_line) {
+ free(command_line);
+ }
+ command_line = concat_cmdline(imagename, tmp_cmdline);
+ if (tmp_cmdline) {
+ free(tmp_cmdline);
+ }
+ command_line_len = strlen(command_line) + 1;
+
+ /* Load the ELF executable */
+ if (mhi.rel_tag)
+ elf_exec_build_load_relocatable(info, &ehdr, buf, len, 0,
+ mhi.rel_tag->min_addr, mhi.rel_tag->max_addr,
+ mhi.rel_tag->align);
+ else
+ elf_exec_build_load(info, &ehdr, buf, len, 0);
+
+ /* Load the setup code */
+ elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size,
+ 0, ULONG_MAX, 1, 0);
+
+ /* Construct information tags. */
+ mbi_bytes = multiboot2_get_mbi_size(info->memory_ranges, command_line_len, modules, mod_command_line_space);
+ mbi_buf = xmalloc(mbi_bytes);
+
+ mbi_ptr = multiboot2_make_mbi(info, command_line, command_line_len, info->rhdr.rel_addr, mbi_buf, mbi_bytes);
+ free(command_line);
+
+ /* Load modules */
+ if (modules) {
+ char *mod_filename, *mod_command_line, *mod_clp, *buf;
+ off_t mod_size;
+ int i = 0;
+
+ mod_clp_base = xmalloc(mod_command_line_space);
+
+ /* Go back and parse the module command lines */
+ mod_clp = mod_clp_base;
+ optind = opterr = 1;
+ while((opt = getopt_long(argc, argv,
+ short_options, options, 0)) != -1) {
+ if (opt != OPT_MOD) continue;
+
+ /* Split module filename from command line */
+ mod_command_line = mod_filename = optarg;
+ if ((cp = strchr(mod_filename, ' ')) != NULL) {
+ /* See as I discard the 'const' modifier */
+ *cp = '\0';
+ }
+
+ /* Load the module */
+ buf = slurp_decompress_file(mod_filename, &mod_size);
+
+ if (cp != NULL) *cp = ' ';
+
+ /* Pick the next aligned spot to load it in. Always page align. */
+ addr = add_buffer(info, buf, mod_size, mod_size, getpagesize(),
+ mhi.rel_tag->min_addr, mhi.rel_tag->max_addr, 1);
+
+ /* Add the module command line */
+ sprintf(mod_clp, "%s", mod_command_line);
+
+ mbi_ptr = multiboot2_mbi_add_module(mbi_buf, mbi_ptr, addr, addr + mod_size, mod_clp);
+
+ mod_clp += strlen(mod_clp) + 1;
+ i++;
+ }
+
+ free(mod_clp_base);
+ }
+
+ mbi_ptr = multiboot2_mbi_end(mbi_buf, mbi_ptr);
+
+ if (sort_segments(info) < 0)
+ return -1;
+
+ addr = add_buffer(info, mbi_buf, mbi_bytes, mbi_bytes, 4,
+ mhi.rel_tag->min_addr, mhi.rel_tag->max_addr, 1);
+
+ elf_rel_get_symbol(&info->rhdr, "entry32_regs", &regs, sizeof(regs));
+ regs.eax = MULTIBOOT2_BOOTLOADER_MAGIC;
+ regs.ebx = addr;
+ regs.eip = ehdr.e_entry;
+ elf_rel_set_symbol(&info->rhdr, "entry32_regs", &regs, sizeof(regs));
+
+ return 0;
+}
diff --git a/kexec/arch/i386/kexec-x86.c b/kexec/arch/i386/kexec-x86.c
index fb0e6f9c..444cb690 100644
--- a/kexec/arch/i386/kexec-x86.c
+++ b/kexec/arch/i386/kexec-x86.c
@@ -36,6 +36,8 @@
struct file_type file_type[] = {
{ "multiboot-x86", multiboot_x86_probe, multiboot_x86_load,
multiboot_x86_usage },
+ { "multiboot2-x86", multiboot2_x86_probe, multiboot2_x86_load,
+ multiboot2_x86_usage },
{ "elf-x86", elf_x86_probe, elf_x86_load, elf_x86_usage },
{ "bzImage", bzImage_probe, bzImage_load, bzImage_usage },
{ "beoboot-x86", beoboot_probe, beoboot_load, beoboot_usage },
diff --git a/kexec/arch/x86_64/Makefile b/kexec/arch/x86_64/Makefile
index 1cf10f98..275add5d 100644
--- a/kexec/arch/x86_64/Makefile
+++ b/kexec/arch/x86_64/Makefile
@@ -4,6 +4,7 @@
x86_64_KEXEC_SRCS = kexec/arch/i386/kexec-elf-x86.c
x86_64_KEXEC_SRCS += kexec/arch/i386/kexec-bzImage.c
x86_64_KEXEC_SRCS += kexec/arch/i386/kexec-multiboot-x86.c
+x86_64_KEXEC_SRCS += kexec/arch/i386/kexec-mb2-x86.c
x86_64_KEXEC_SRCS += kexec/arch/i386/kexec-beoboot-x86.c
x86_64_KEXEC_SRCS += kexec/arch/i386/kexec-nbi.c
x86_64_KEXEC_SRCS += kexec/arch/i386/x86-linux-setup.c
diff --git a/kexec/arch/x86_64/kexec-x86_64.c b/kexec/arch/x86_64/kexec-x86_64.c
index ccdc9806..394cfca6 100644
--- a/kexec/arch/x86_64/kexec-x86_64.c
+++ b/kexec/arch/x86_64/kexec-x86_64.c
@@ -36,6 +36,8 @@ struct file_type file_type[] = {
{ "elf-x86_64", elf_x86_64_probe, elf_x86_64_load, elf_x86_64_usage },
{ "multiboot-x86", multiboot_x86_probe, multiboot_x86_load,
multiboot_x86_usage },
+ { "multiboot2-x86", multiboot2_x86_probe, multiboot2_x86_load,
+ multiboot2_x86_usage },
{ "elf-x86", elf_x86_probe, elf_x86_load, elf_x86_usage },
{ "bzImage64", bzImage64_probe, bzImage64_load, bzImage64_usage },
{ "bzImage", bzImage_probe, bzImage_load, bzImage_usage },
diff --git a/kexec/arch/x86_64/kexec-x86_64.h b/kexec/arch/x86_64/kexec-x86_64.h
index 4cdeffb2..21c3a738 100644
--- a/kexec/arch/x86_64/kexec-x86_64.h
+++ b/kexec/arch/x86_64/kexec-x86_64.h
@@ -33,4 +33,9 @@ int bzImage64_load(int argc, char **argv, const char *buf, off_t len,
struct kexec_info *info);
void bzImage64_usage(void);
+int multiboot2_x86_load(int argc, char **argv, const char *buf, off_t len,
+ struct kexec_info *info);
+void multiboot2_x86_usage(void);
+int multiboot2_x86_probe(const char *buf, off_t buf_len);
+
#endif /* KEXEC_X86_64_H */
diff --git a/kexec/kexec.8 b/kexec/kexec.8
index 2fafaaae..25807258 100644
--- a/kexec/kexec.8
+++ b/kexec/kexec.8
@@ -311,6 +311,30 @@ with command-line arguments
.I "arg1 arg2 ..."
This parameter can be specified multiple times.
.RE
+.PP
+.B multiboot2-x86
+.RS
+.TP
+.BI \-\-command\-line= string
+Set the kernel command line to
+.IR string .
+.TP
+.BI \-\-reuse-cmdline
+Use the command line from the running system. When a panic kernel is loaded, it
+strips the
+.I
+crashkernel
+parameter automatically. The
+.I BOOT_IMAGE
+parameter is also stripped.
+.TP
+.BI \-\-module= "mod arg1 arg2 ..."
+Load module
+.I mod
+with command-line arguments
+.I "arg1 arg2 ..."
+This parameter can be specified multiple times.
+.RE
.SH ARCHITECTURE OPTIONS
.TP