blob: 49b5d2350d990e5ec9055adce058a233d1cc858e [file] [log] [blame]
#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;
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, "-------------------------------------------------\n");
if (hdr->setup_sects > 15) {
char kver_str[128];
fseek(f, hdr->kernel_version + 0x200, SEEK_SET);
fread(kver_str, 128, 1, f);
kver_str[127] = '\0';
fprintf(stdout, "Kernel version: %s\n", kver_str);
} else
fprintf(stdout, "No kernel version information available\n");
fprintf(stdout, "-------------------------------------------------\n");
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;
}