aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuis R. Rodriguez <mcgrof@kernel.org>2015-11-13 16:35:12 -0800
committerLuis R. Rodriguez <mcgrof@kernel.org>2016-02-11 08:42:16 -0800
commit222b33f87da9b337141b0b2d0d02dfa7844746a8 (patch)
treed8769a6c305e45ef9853c433a4a839b5c8521fc5
parent34589e4fec1f341474777e22862f512f8b287382 (diff)
downloadlinker-tables-222b33f87da9b337141b0b2d0d02dfa7844746a8.tar.gz
parse-bzimage: add bzimage parser
Prior to booting Linux qemu / xen / grub2 needs to parse the bzimage, they also then re-use some of the same to set the 0 page (on 32-bit, on 64-bit this can be anywhere) for Xen HVM and KVM. This code is added to aid evaluaton of making changes on qemu / xen / grub / native kvm tool, etc. I at least have patches ready now for: * qemu - supports kvm and Xen HVM * native kvm tool I'm still evaluating Xen's requirements for PV and PVH, this is a bit complex due to the requirements of having a custom boot loader use when one doesn't want to specify the kernel and initrd manually. For that see: http://wiki.xenproject.org/wiki/PvGrub2 http://xenbits.xen.org/docs/unstable/misc/x86-xenpv-bootloader.html Signed-off-by: Luis R. Rodriguez <mcgrof@kernel.org>
-rw-r--r--Makefile7
-rw-r--r--README38
-rw-r--r--parse-bzimage.c196
3 files changed, 239 insertions, 2 deletions
diff --git a/Makefile b/Makefile
index ed5cfda..dbb3442 100644
--- a/Makefile
+++ b/Makefile
@@ -11,12 +11,15 @@ else
NQ=@echo
endif
-all: main
+all: main parse-bzimage
%.o: %.c *.h
$(NQ) ' CC ' $@
$(Q)$(CC) -c $(CPPFLAGS) $(CFLAGS) -o $@ $<
+parse-bzimage: parse-bzimage.c
+ $(NQ) ' CC ' $@
+ $(Q)$(CC) -c -o $@ $<
OBJS = sort-init.o \
start_kernel.o \
@@ -35,4 +38,4 @@ main: $(OBJS)
$(Q)$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $^
clean:
- rm -f main *.o
+ rm -f main parse-bzimage *.o
diff --git a/README b/README
index 100ed57..37d3c1d 100644
--- a/README
+++ b/README
@@ -20,6 +20,43 @@ Emulate xen boot:
./main -x
+x86 bzimage parser
+================
+
+An x86 image parser has been added to aid the integration requirements
+of using futher boot_params for dead code concerns. Example usage:
+
+./parse-bzimage ~/linux/arch/x86/boot/bzImage
+kernel: /home/mcgrof/linux/arch/x86/boot/bzImage
+kernel size: 5668000 bytes
+Going to parse kernel...
+
+Xen Expects: 0x53726448
+Qemu Expects: 0x53726448
+On image: 0x53726448
+
+
+bzImage protocol Version: v2.13
+Xen hdr->version: 525
+Qemu protocol: 525
+Qemu VERSION(2,8): 520
+
+-------------------------------------------------
+Boot protocol 2.07: 0x0207 (supports hardware_subarch)
+Boot protocol 2.08: 0x0208
+Boot protocol 2.09: 0x0209
+Boot protocol 2.10: 0x020a
+Boot protocol 2.11: 0x020b
+Boot protocol 2.12: 0x020c
+Boot protocol 2.13: 0x020d
+
+
+Member Offset Expected Match
+-------------------------------------------------------------------------
+setup_header->loadflags 0x0211 0x0211 YES
+setup_header->hardware_subarch 0x023c
+setup_header->hardware_subarch_data 0x0240
+
TODO
====
@@ -35,3 +72,4 @@ setup code. Right now we'd have code split up in two different
places, or checks with things like pv_enabled() on Linux -- in
this code booting_xen() is used. We can replace these conditionals
as well later with a proper init structure annotation.
+
diff --git a/parse-bzimage.c b/parse-bzimage.c
new file mode 100644
index 0000000..fa2aecd
--- /dev/null
+++ b/parse-bzimage.c
@@ -0,0 +1,196 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
+
+#define max(a,b) \
+ ({ __typeof__ (a) _a = (a); \
+ __typeof__ (b) _b = (b); \
+ _a > _b ? _a : _b; })
+
+#define min(a,b) \
+ ({ __typeof__ (a) _a = (a); \
+ __typeof__ (b) _b = (b); \
+ _a < _b ? _a : _b; })
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+/* struct boot_params has struct setup_header at 0ffset 0x1f1 */
+struct __attribute__((__packed__)) setup_header {
+ uint8_t _pad0[0x1f1];
+ uint8_t setup_sects;
+ uint16_t root_flags;
+ uint32_t syssize;
+ uint16_t ram_size;
+ uint16_t vid_mode;
+ uint16_t root_dev;
+ uint16_t boot_flag;
+ uint16_t jump;
+ uint32_t header;
+#define HDR_MAGIC "HdrS"
+#define HDR_MAGIC_SZ 4
+ uint16_t version;
+#define VERSION(h,l) (((h)<<8) | (l))
+ uint32_t realmode_swtch;
+ uint16_t start_sys;
+ uint16_t kernel_version;
+ uint8_t type_of_loader;
+ uint8_t loadflags;
+ uint16_t setup_move_size;
+ uint32_t code32_start;
+ uint32_t ramdisk_image;
+ uint32_t ramdisk_size;
+ uint32_t bootsect_kludge;
+ uint16_t heap_end_ptr;
+ uint16_t _pad1;
+ uint32_t cmd_line_ptr;
+ uint32_t initrd_addr_max;
+ uint32_t kernel_alignment;
+ uint8_t relocatable_kernel;
+ uint8_t _pad2[3];
+ uint32_t cmdline_size;
+ /* XXX: point of interest */
+ uint32_t hardware_subarch;
+ uint64_t hardware_subarch_data;
+ uint32_t payload_offset;
+ uint32_t payload_length;
+};
+
+void usage(char *argv[])
+{
+ fprintf(stderr, "Usage: %s <kernel-bzimage>\n", argv[0]);
+ exit(EXIT_FAILURE);
+}
+
+int main(int argc, char *argv[])
+{
+ FILE *f;
+ struct stat sb;
+ int ret = -EINVAL;;
+ const char *kernel_filename;
+ uint8_t header[8192], *setup, *kernel, *initrd_data;
+ int setup_size, initrd_size = 0, cmdline_size;
+ size_t kernel_size, read_size;
+ /* The xen way */
+ struct setup_header *hdr;
+ uint32_t hdr_int;
+ uint16_t protocol;
+
+ if (argc != 2)
+ goto err_out;
+
+ kernel_filename = argv[1];
+ ret = stat(kernel_filename, &sb);
+ if (ret == -1) {
+ fprintf(stderr, "Could not stat() file %s\n", kernel_filename);
+ goto err_out;
+ }
+
+ kernel_size = sb.st_size;
+
+ printf("kernel:\t%s\n", kernel_filename);
+ printf("kernel size:\t%lld bytes\n", kernel_size);
+
+ f = fopen(kernel_filename, "rb");
+ if (!f) {
+ fprintf(stderr, "unable to load kernel %s: %s\n",
+ kernel_filename, strerror(errno));
+ }
+
+ read_size = fread(header, 1, min(ARRAY_SIZE(header), kernel_size), f);
+ if (read_size != min(ARRAY_SIZE(header), kernel_size)) {
+ fprintf(stderr, "Invalid size read for %s: %d (%s)\n",
+ kernel_filename, read_size, strerror(errno));
+ goto err_out;
+ }
+
+ fprintf(stdout, "Going to parse kernel...\n\n");
+
+ memcpy(&hdr_int, HDR_MAGIC, sizeof(uint32_t));
+
+ hdr = (struct setup_header *) header;
+
+ /* Xen's check is by far the cleanest and easiest to read */
+ if (memcmp(&hdr->header, HDR_MAGIC, HDR_MAGIC_SZ) != 0 ) {
+ fprintf(stderr, "Bad image magic\n");
+ fprintf(stdout, "Xen Expects:\t0x%08x\n", hdr_int);
+ fprintf(stderr, "On image:\t%0x\n", hdr->header);
+ goto err_out;
+ }
+
+ fprintf(stdout, "Xen Expects:\t0x%08x\n", hdr_int);
+ fprintf(stdout, "Qemu Expects:\t0x%08x\n", 0x53726448);
+ fprintf(stdout, "On image:\t0x%08x\n", hdr->header);
+
+ fprintf(stdout, "\n\n");
+
+ /*
+ * Qemu calls this protocol, on Xen and Linux this is the
+ * boot protocol version. Xen requires at least 2.08.
+ */
+ memcpy(&protocol, header+0x206, sizeof(uint16_t));
+ fprintf(stdout, "bzImage protocol Version: v%d.%02d\n",
+ hdr->version >> 8, hdr->version & 0xff);
+ fprintf(stdout, "Xen hdr->version:\t%d\n", hdr->version);
+ fprintf(stdout, "Qemu protocol:\t\t%d\n", protocol);
+ fprintf(stdout, "Qemu VERSION(2,8):\t%d\n", VERSION(2,8));
+
+ fprintf(stdout, "\n-------------------------------------------------\n");
+ fprintf(stdout, "Boot protocol 2.07:\t0x%04x\t(supports hardware_subarch)\n", VERSION(2,7));
+ fprintf(stdout, "Boot protocol 2.08:\t0x%04x\n", VERSION(2,8));
+ fprintf(stdout, "Boot protocol 2.09:\t0x%04x\n", VERSION(2,9));
+ fprintf(stdout, "Boot protocol 2.10:\t0x%04x\n", VERSION(2,10));
+ fprintf(stdout, "Boot protocol 2.11:\t0x%04x\n", VERSION(2,11));
+ fprintf(stdout, "Boot protocol 2.12:\t0x%04x\n", VERSION(2,12));
+ fprintf(stdout, "Boot protocol 2.13:\t0x%04x\n", VERSION(2,13));
+
+ /*
+ * Refer to:
+ *
+ * Documentation/x86/zero-page.txt
+ * arch/x86/include/uapi/asm/bootparam.h
+ *
+ * Upon boot we also use the sruct setup_header on the
+ * struct boot_params. On x86 32-bit this is on the first
+ * page, aka "zero page", on 64-bit this can be anywhere.
+ * Either way we know the sruct setup_header offset within
+ * struct boot_parmams resides between [0x1f1 - 0x290]. Qemu
+ * uses direct offsets from the struct boot_parmams as with:
+ *
+ * header[0x211] |= 0x80; // CAN_USE_HEAP
+ *
+ * If we want to modify qemu to add other fields we need to
+ * know the offset. This program skips struct boot_params and
+ * by placing setup_header at 0x1f1 with a pad. To get the
+ * offset of fields we can simply use offsetof. To test
+ * correctness we know qemu's code relies on an offset of
+ * 0x211 for setup_header->loadflags. Test for that and
+ * then compute the offset for hardware_subarch.
+ */
+
+ /* Boot protocol >= 2.07 supports hardware_subarch */
+
+ fprintf(stdout, "\n\n");
+ fprintf(stdout, "Member\t\t\t\t\tOffset\tExpected\tMatch\n");
+ fprintf(stdout, "-------------------------------------------------------------------------\n");
+ fprintf(stdout, "setup_header->loadflags\t\t\t0x%04x\t0x0211\t\t%s\n",
+ offsetof(struct setup_header, loadflags),
+ offsetof(struct setup_header, loadflags) == 0x0211 ?
+ "YES" : "NO!");
+ fprintf(stdout, "setup_header->hardware_subarch\t\t0x%04x\n",
+ offsetof(struct setup_header, hardware_subarch));
+ fprintf(stdout, "setup_header->hardware_subarch_data\t0x%04x\n",
+ offsetof(struct setup_header, hardware_subarch_data));
+
+ exit(EXIT_SUCCESS);
+err_out:
+ usage(argv);
+ return ret;
+}