diff options
author | Luis R. Rodriguez <mcgrof@kernel.org> | 2015-11-13 16:35:12 -0800 |
---|---|---|
committer | Luis R. Rodriguez <mcgrof@kernel.org> | 2016-02-11 08:42:16 -0800 |
commit | 222b33f87da9b337141b0b2d0d02dfa7844746a8 (patch) | |
tree | d8769a6c305e45ef9853c433a4a839b5c8521fc5 | |
parent | 34589e4fec1f341474777e22862f512f8b287382 (diff) | |
download | linker-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-- | Makefile | 7 | ||||
-rw-r--r-- | README | 38 | ||||
-rw-r--r-- | parse-bzimage.c | 196 |
3 files changed, 239 insertions, 2 deletions
@@ -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 @@ -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; +} |