diff options
author | Patrick Georgi <patrick@georgi-clan.de> | 2013-08-27 20:22:21 +0200 |
---|---|---|
committer | Patrick Georgi <patrick@georgi-clan.de> | 2013-08-31 08:58:34 +0200 |
commit | de36d333c27c258bc05ecc0b6649fbdafcae619f (patch) | |
tree | d80a0dea660b4f74dc8031acfa9e9fa9a597f04e | |
parent | dcccbd13966379eeaee79b08db0d58d024536ae8 (diff) | |
download | coreboot-de36d333c27c258bc05ecc0b6649fbdafcae619f.tar.xz |
Add a (b)zImage parser to cbfstool
In the great tradition of LinuxBIOS this allows adding
a kernel as payload. add-payload is extended to also
allow adding an initial ramdisk (-I filename) and a
command line (-C console=ttyS0).
Change-Id: Iaca499a98b0adf0134e78d6bf020b6531a626aaa
Signed-off-by: Patrick Georgi <patrick.georgi@secunet.com>
Signed-off-by: Patrick Georgi <patrick@georgi-clan.de>
Reviewed-on: http://review.coreboot.org/3302
Tested-by: build bot (Jenkins)
Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
-rw-r--r-- | util/cbfstool/Makefile | 2 | ||||
-rw-r--r-- | util/cbfstool/Makefile.inc | 3 | ||||
-rw-r--r-- | util/cbfstool/cbfs-mkpayload.c | 1 | ||||
-rw-r--r-- | util/cbfstool/cbfs-payload-linux.c | 238 | ||||
-rw-r--r-- | util/cbfstool/cbfstool.c | 19 | ||||
-rw-r--r-- | util/cbfstool/common.h | 3 | ||||
-rw-r--r-- | util/cbfstool/linux.h | 191 | ||||
-rw-r--r-- | util/cbfstool/linux_trampoline.c | 141 |
8 files changed, 597 insertions, 1 deletions
diff --git a/util/cbfstool/Makefile b/util/cbfstool/Makefile index a0de08d2f7..3aa5edc718 100644 --- a/util/cbfstool/Makefile +++ b/util/cbfstool/Makefile @@ -12,6 +12,8 @@ COMMON+=cbfs-mkstage.o cbfs-mkpayload.o COMMON+=lzma/lzma.o COMMON+=lzma/C/LzFind.o lzma/C/LzmaDec.o lzma/C/LzmaEnc.o +COMMON+=linux_trampoline.o cbfs-payload-linux.o + COMMON:=$(addprefix $(obj)/,$(COMMON)) all: dep $(BINARY) diff --git a/util/cbfstool/Makefile.inc b/util/cbfstool/Makefile.inc index 970620d783..fc120f3b22 100644 --- a/util/cbfstool/Makefile.inc +++ b/util/cbfstool/Makefile.inc @@ -11,6 +11,9 @@ cbfsobj += lzma.o cbfsobj += LzFind.o cbfsobj += LzmaDec.o cbfsobj += LzmaEnc.o +# linux as payload +cbfsobj += linux_trampoline.o +cbfsobj += cbfs-payload-linux.o CBFSTOOLFLAGS=-D_7ZIP_ST -g diff --git a/util/cbfstool/cbfs-mkpayload.c b/util/cbfstool/cbfs-mkpayload.c index 9f3dabf40f..78988a0182 100644 --- a/util/cbfstool/cbfs-mkpayload.c +++ b/util/cbfstool/cbfs-mkpayload.c @@ -377,3 +377,4 @@ int parse_fv_to_payload(const struct buffer *input, return 0; } + diff --git a/util/cbfstool/cbfs-payload-linux.c b/util/cbfstool/cbfs-payload-linux.c new file mode 100644 index 0000000000..149265c714 --- /dev/null +++ b/util/cbfstool/cbfs-payload-linux.c @@ -0,0 +1,238 @@ +/* + * cbfs-payload-linux + * + * Copyright (C) 2013 Patrick Georgi <patrick@georgi-clan.de> + * + * 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; version 2 of the License. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "common.h" +#include "cbfs.h" +#include "linux.h" + +/* TODO: + * handle special arguments + * mem= argument - only affects loading decisions (kernel + initrd), not e820 -> build time + * vga= argument (FILO ignores this) + * add support for more parameters to trampoline: + * alt_mem_k, ext_mem_k (not strictly necessary since e820 takes precedence) + * framebuffer/console values + * + * larger work: + * is compress() safe to use in a size constrained buffer? ie. do(es) the + * compression algorithm(s) stop once the compression result reaches input + * size (ie. incompressible data)? + */ +int parse_bzImage_to_payload(const struct buffer *input, + struct buffer *output, const char *initrd_name, + char *cmdline, comp_algo algo) +{ + int cur_len = 0; + int num_segments = 3; /* parameter block, real kernel, and trampoline */ + + comp_func_ptr compress = compression_function(algo); + if (!compress) + return -1; + + unsigned int initrd_base = 64*1024*1024; + unsigned int initrd_size = 0; + void *initrd_data = NULL; + if (initrd_name != NULL) { + /* TODO: load initrd, set initrd_size */ + num_segments++; + FILE *initrd_file = fopen(initrd_name, "rb"); + if (!initrd_file) { + ERROR("could not open initrd.\n"); + return -1; + } + fseek(initrd_file, 0, SEEK_END); + initrd_size = ftell(initrd_file); + fseek(initrd_file, 0, SEEK_SET); + initrd_data = malloc(initrd_size); + if (!initrd_data) { + ERROR("could not allocate memory for initrd.\n"); + return -1; + } + if (fread(initrd_data, initrd_size, 1, initrd_file) != 1) { + ERROR("could not load initrd.\n"); + return -1; + } + fclose(initrd_file); + } + + unsigned int cmdline_size = 0; + if (cmdline != NULL) { + num_segments++; + cmdline_size = strlen(cmdline) + 1; + } + + struct linux_header *hdr = (struct linux_header *)input->data; + unsigned int setup_size = 4 * 512; + if (hdr->setup_sects != 0) { + setup_size = (hdr->setup_sects + 1) * 512; + } + + /* Setup parameter block. Imitate FILO. */ + struct linux_params params; + params.mount_root_rdonly = hdr->root_flags; + params.orig_root_dev = hdr->root_dev; + /* Sensible video defaults. Might be overridden on runtime by coreboot tables. */ + params.orig_video_mode = 3; + params.orig_video_cols = 80; + params.orig_video_lines = 25; + params.orig_video_isVGA = 1; + params.orig_video_points = 16; + + params.loader_type = 0xff; /* Unregistered Linux loader */ + + if (cmdline != NULL) { + if (hdr->protocol_version < 0x202) { + params.cl_magic = CL_MAGIC_VALUE; + params.cl_offset = COMMAND_LINE_LOC - LINUX_PARAM_LOC; + } else { + params.cmd_line_ptr = COMMAND_LINE_LOC; + } + } + + unsigned long kernel_base = 0x100000; + if ((hdr->protocol_version >= 0x200) && (!hdr->loadflags & 1)) { + kernel_base = 0x1000; /* zImage kernel */ + } + /* kernel prefers an address, so listen */ + if ((hdr->protocol_version >= 0x20a) && (!(hdr->pref_address >> 32))) { + kernel_base = hdr->pref_address; + } + if (hdr->protocol_version >= 0x205) { + params.relocatable_kernel = hdr->relocatable_kernel; + params.kernel_alignment = hdr->kernel_alignment; + if (hdr->relocatable_kernel != 0) { + /* 16 MB should be way outside coreboot's playground, + * so if possible (relocatable kernel) use that to + * avoid a trampoline copy. */ + kernel_base = ALIGN(16*1024*1024, params.kernel_alignment); + } + } + + /* We have a trampoline and use that, but it can simply use + * this information for its jump to real Linux. */ + params.kernel_start = kernel_base; + + void *kernel_data = input->data + setup_size; + unsigned int kernel_size = input->size - setup_size; + + if (initrd_data != NULL) { + /* TODO: this is a bit of a hack. Linux recommends to store + * initrd near to end-of-mem, but that's hard to do on build + * time. It definitely fails to read the image if it's too + * close to the kernel, so give it some room. + */ + initrd_base = ALIGN(kernel_base + kernel_size, 16*1024*1024); + + params.initrd_start = initrd_base; + params.initrd_size = initrd_size; + } + + struct cbfs_payload_segment *segs; + unsigned long doffset = (num_segments + 1) * sizeof(*segs); + + /* Allocate a block of memory to store the data in */ + int isize = sizeof(params) + kernel_size + cmdline_size + initrd_size; + if (buffer_create(output, doffset + isize, input->name) != 0) + return -1; + memset(output->data, 0, output->size); + + segs = (struct cbfs_payload_segment *)output->data; + + /* parameter block */ + segs[0].type = PAYLOAD_SEGMENT_DATA; + segs[0].load_addr = htonll(LINUX_PARAM_LOC); + segs[0].mem_len = htonl(sizeof(params)); + segs[0].offset = htonl(doffset); + + compress((void*)¶ms, sizeof(params), output->data + doffset, &cur_len); + segs[0].compression = htonl(algo); + segs[0].len = htonl(cur_len); + + doffset += cur_len; + + /* code block */ + segs[1].type = PAYLOAD_SEGMENT_CODE; + segs[1].load_addr = htonll(kernel_base); + segs[1].mem_len = htonl(kernel_size); + segs[1].offset = htonl(doffset); + + compress(kernel_data, kernel_size, output->data + doffset, &cur_len); + segs[1].compression = htonl(algo); + segs[1].len = htonl(cur_len); + + doffset += cur_len; + + /* trampoline */ + extern void *trampoline_start; + extern long trampoline_size; + + unsigned int entrypoint = 0x40000; /* TODO: any better place? */ + + segs[2].type = PAYLOAD_SEGMENT_CODE; + segs[2].load_addr = htonll(entrypoint); + segs[2].mem_len = htonl(trampoline_size); + segs[2].offset = htonl(doffset); + + compress(trampoline_start, trampoline_size, output->data + doffset, &cur_len); + segs[2].compression = htonl(algo); + segs[2].len = htonl(cur_len); + + doffset += cur_len; + + if (cmdline_size > 0) { + /* command line block */ + segs[3].type = PAYLOAD_SEGMENT_DATA; + segs[3].load_addr = htonll(COMMAND_LINE_LOC); + segs[3].mem_len = htonl(cmdline_size); + segs[3].offset = htonl(doffset); + + compress(cmdline, cmdline_size, output->data + doffset, &cur_len); + segs[3].compression = htonl(algo); + segs[3].len = htonl(cur_len); + + doffset += cur_len; + } + + if (initrd_size > 0) { + /* setup block */ + segs[num_segments-1].type = PAYLOAD_SEGMENT_DATA; + segs[num_segments-1].load_addr = htonll(initrd_base); + segs[num_segments-1].mem_len = htonl(initrd_size); + segs[num_segments-1].offset = htonl(doffset); + + compress(initrd_data, initrd_size, output->data + doffset, &cur_len); + segs[num_segments-1].compression = htonl(algo); + segs[num_segments-1].len = htonl(cur_len); + + doffset += cur_len; + } + + /* prepare entry point segment */ + segs[num_segments].type = PAYLOAD_SEGMENT_ENTRY; + segs[num_segments].load_addr = htonll(entrypoint); + output->size = doffset; + + return 0; +} + + diff --git a/util/cbfstool/cbfstool.c b/util/cbfstool/cbfstool.c index fc49ed2c8f..34002a9e3d 100644 --- a/util/cbfstool/cbfstool.c +++ b/util/cbfstool/cbfstool.c @@ -57,6 +57,9 @@ static struct param { uint32_t top_aligned; int fit_empty_entries; comp_algo algo; + /* for linux payloads */ + char *initrd; + char *cmdline; } param = { /* All variables not listed are initialized as zero. */ .algo = CBFS_COMPRESS_NONE, @@ -194,6 +197,11 @@ static int cbfstool_convert_mkpayload(struct buffer *buffer, uint32_t *offset) { if (ret != 0) ret = parse_fv_to_payload(buffer, &output, param.algo); + /* If it's neither ELF nor UEFI Fv, try bzImage */ + if (ret != 0) + ret = parse_bzImage_to_payload(buffer, &output, + param.initrd, param.cmdline, param.algo); + /* Not a supported payload type */ if (ret != 0) { ERROR("Not a supported payload type (ELF / FV).\n"); @@ -502,7 +510,7 @@ static int cbfs_update_fit(void) static const struct command commands[] = { {"add", "f:n:t:b:vh?", cbfs_add}, - {"add-payload", "f:n:t:c:b:vh?", cbfs_add_payload}, + {"add-payload", "f:n:t:c:b:vh?C:I:", cbfs_add_payload}, {"add-stage", "f:n:t:c:b:vh?", cbfs_add_stage}, {"add-flat-binary", "f:n:l:e:c:b:vh?", cbfs_add_flat_binary}, {"add-int", "i:n:b:vh?", cbfs_add_integer}, @@ -531,6 +539,8 @@ static struct option long_options[] = { {"int", required_argument, 0, 'i' }, {"machine", required_argument, 0, 'm' }, {"empty-fits", required_argument, 0, 'x' }, + {"initrd", required_argument, 0, 'I' }, + {"cmdline", required_argument, 0, 'C' }, {"verbose", no_argument, 0, 'v' }, {"help", no_argument, 0, 'h' }, {NULL, 0, 0, 0 } @@ -550,6 +560,7 @@ static void usage(char *name) "Add a component\n" " add-payload -f FILE -n NAME [-c compression] [-b base] " "Add a payload to the ROM\n" + " (linux specific: [-C cmdline] [-I initrd])\n" " add-stage -f FILE -n NAME [-c compression] [-b base] " "Add a stage to the ROM\n" " add-flat-binary -f FILE -n NAME -l load-address \\\n" @@ -691,6 +702,12 @@ int main(int argc, char **argv) case 'm': arch = string_to_arch(optarg); break; + case 'I': + param.initrd = optarg; + break; + case 'C': + param.cmdline = optarg; + break; case 'h': case '?': usage(argv[0]); diff --git a/util/cbfstool/common.h b/util/cbfstool/common.h index e197143bc7..6e12fcb064 100644 --- a/util/cbfstool/common.h +++ b/util/cbfstool/common.h @@ -102,6 +102,9 @@ int parse_elf_to_payload(const struct buffer *input, struct buffer *output, comp_algo algo); int parse_fv_to_payload(const struct buffer *input, struct buffer *output, comp_algo algo); +int parse_bzImage_to_payload(const struct buffer *input, + struct buffer *output, const char *initrd, + char *cmdline, comp_algo algo); int parse_flat_binary_to_payload(const struct buffer *input, struct buffer *output, uint32_t loadaddress, diff --git a/util/cbfstool/linux.h b/util/cbfstool/linux.h new file mode 100644 index 0000000000..20837e3707 --- /dev/null +++ b/util/cbfstool/linux.h @@ -0,0 +1,191 @@ +/* + * This file is part of coreboot.. + * + * 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; version 2 of the License. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * Linux/i386 loader + * Supports bzImage, zImage and Image format. + * + * Based on work by Steve Gehlbach. + * Portions are taken from mkelfImage. + * + * 2003-09 by SONE Takeshi + */ + +#include <stdint.h> + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +#define LINUX_PARAM_LOC 0x90000 +#define COMMAND_LINE_LOC 0x91000 +#define GDT_LOC 0x92000 +#define STACK_LOC 0x93000 + +#define E820MAX 32 /* number of entries in E820MAP */ +struct e820entry { + unsigned long long addr; /* start of memory segment */ + unsigned long long size; /* size of memory segment */ + unsigned long type; /* type of memory segment */ +#define E820_RAM 1 +#define E820_RESERVED 2 +#define E820_ACPI 3 /* usable as RAM once ACPI tables have been read */ +#define E820_NVS 4 +}; + +/* The header of Linux/i386 kernel */ +struct linux_header { + u8 reserved1[0x1f1]; /* 0x000 */ + u8 setup_sects; /* 0x1f1 */ + u16 root_flags; /* 0x1f2 */ + u32 syssize; /* 0x1f4 (2.04+) */ + u8 reserved2[2]; /* 0x1f8 */ + u16 vid_mode; /* 0x1fa */ + u16 root_dev; /* 0x1fc */ + u16 boot_sector_magic; /* 0x1fe */ + /* 2.00+ */ + u8 reserved3[2]; /* 0x200 */ + u8 header_magic[4]; /* 0x202 */ + u16 protocol_version; /* 0x206 */ + u32 realmode_swtch; /* 0x208 */ + u16 start_sys; /* 0x20c */ + u16 kver_addr; /* 0x20e */ + u8 type_of_loader; /* 0x210 */ + u8 loadflags; /* 0x211 */ + u16 setup_move_size; /* 0x212 */ + u32 code32_start; /* 0x214 */ + u32 ramdisk_image; /* 0x218 */ + u32 ramdisk_size; /* 0x21c */ + u8 reserved4[4]; /* 0x220 */ + /* 2.01+ */ + u16 heap_end_ptr; /* 0x224 */ + u8 reserved5[2]; /* 0x226 */ + /* 2.02+ */ + u32 cmd_line_ptr; /* 0x228 */ + /* 2.03+ */ + u32 initrd_addr_max; /* 0x22c */ + /* 2.05+ */ + u32 kernel_alignment; /* 0x230 */ + u8 relocatable_kernel; /* 0x234 */ + u8 min_alignment; /* 0x235 (2.10+) */ + u8 reserved6[2]; /* 0x236 */ + /* 2.06+ */ + u32 cmdline_size; /* 0x238 */ + /* 2.07+ */ + u32 hardware_subarch; /* 0x23c */ + u64 hardware_subarch_data;/* 0x240 */ + /* 2.08+ */ + u32 payload_offset; /* 0x248 */ + u32 payload_length; /* 0x24c */ + /* 2.09+ */ + u64 setup_data; /* 0x250 */ + /* 2.10+ */ + u64 pref_address; /* 0x258 */ + u32 init_size; /* 0x260 */ +} __attribute__ ((packed)); + +/* Paramters passed to 32-bit part of Linux + * This is another view of the structure above.. */ +struct linux_params { + u8 orig_x; /* 0x00 */ + u8 orig_y; /* 0x01 */ + u16 ext_mem_k; /* 0x02 -- EXT_MEM_K sits here */ + u16 orig_video_page; /* 0x04 */ + u8 orig_video_mode; /* 0x06 */ + u8 orig_video_cols; /* 0x07 */ + u16 unused2; /* 0x08 */ + u16 orig_video_ega_bx; /* 0x0a */ + u16 unused3; /* 0x0c */ + u8 orig_video_lines; /* 0x0e */ + u8 orig_video_isVGA; /* 0x0f */ + u16 orig_video_points; /* 0x10 */ + + /* VESA graphic mode -- linear frame buffer */ + u16 lfb_width; /* 0x12 */ + u16 lfb_height; /* 0x14 */ + u16 lfb_depth; /* 0x16 */ + u32 lfb_base; /* 0x18 */ + u32 lfb_size; /* 0x1c */ + u16 cl_magic; /* 0x20 */ +#define CL_MAGIC_VALUE 0xA33F + u16 cl_offset; /* 0x22 */ + u16 lfb_linelength; /* 0x24 */ + u8 red_size; /* 0x26 */ + u8 red_pos; /* 0x27 */ + u8 green_size; /* 0x28 */ + u8 green_pos; /* 0x29 */ + u8 blue_size; /* 0x2a */ + u8 blue_pos; /* 0x2b */ + u8 rsvd_size; /* 0x2c */ + u8 rsvd_pos; /* 0x2d */ + u16 vesapm_seg; /* 0x2e */ + u16 vesapm_off; /* 0x30 */ + u16 pages; /* 0x32 */ + u8 reserved4[12]; /* 0x34 -- 0x3f reserved for future expansion */ + + //struct apm_bios_info apm_bios_info; /* 0x40 */ + u8 apm_bios_info[0x40]; + //struct drive_info_struct drive_info; /* 0x80 */ + u8 drive_info[0x20]; + //struct sys_desc_table sys_desc_table; /* 0xa0 */ + u8 sys_desc_table[0x140]; + u32 alt_mem_k; /* 0x1e0 */ + u8 reserved5[4]; /* 0x1e4 */ + u8 e820_map_nr; /* 0x1e8 */ + u8 reserved6[9]; /* 0x1e9 */ + u16 mount_root_rdonly; /* 0x1f2 */ + u8 reserved7[4]; /* 0x1f4 */ + u16 ramdisk_flags; /* 0x1f8 */ +#define RAMDISK_IMAGE_START_MASK 0x07FF +#define RAMDISK_PROMPT_FLAG 0x8000 +#define RAMDISK_LOAD_FLAG 0x4000 + u8 reserved8[2]; /* 0x1fa */ + u16 orig_root_dev; /* 0x1fc */ + u8 reserved9[1]; /* 0x1fe */ + u8 aux_device_info; /* 0x1ff */ + u8 reserved10[2]; /* 0x200 */ + u8 param_block_signature[4]; /* 0x202 */ + u16 param_block_version; /* 0x206 */ + u8 reserved11[8]; /* 0x208 */ + u8 loader_type; /* 0x210 */ +#define LOADER_TYPE_LOADLIN 1 +#define LOADER_TYPE_BOOTSECT_LOADER 2 +#define LOADER_TYPE_SYSLINUX 3 +#define LOADER_TYPE_ETHERBOOT 4 +#define LOADER_TYPE_KERNEL 5 + u8 loader_flags; /* 0x211 */ + u8 reserved12[2]; /* 0x212 */ + u32 kernel_start; /* 0x214 */ + u32 initrd_start; /* 0x218 */ + u32 initrd_size; /* 0x21c */ + u8 reserved12_5[8]; /* 0x220 */ + u32 cmd_line_ptr; /* 0x228 */ + u32 initrd_addr_max; /* 0x22c */ + u32 kernel_alignment; /* 0x230 */ + u8 relocatable_kernel; /* 0x234 */ + u8 reserved13[155]; /* 0x22c */ + struct e820entry e820_map[E820MAX]; /* 0x2d0 */ + u8 reserved16[688]; /* 0x550 */ +#define COMMAND_LINE_SIZE 256 + /* Command line is copied here by 32-bit i386/kernel/head.S. + * So I will follow the boot protocol, rather than putting it + * directly here. --ts1 */ + u8 command_line[COMMAND_LINE_SIZE]; /* 0x800 */ + u8 reserved17[1792]; /* 0x900 - 0x1000 */ +}; + diff --git a/util/cbfstool/linux_trampoline.c b/util/cbfstool/linux_trampoline.c new file mode 100644 index 0000000000..79d15769a2 --- /dev/null +++ b/util/cbfstool/linux_trampoline.c @@ -0,0 +1,141 @@ +/* + * linux_trampoline + * + * Copyright (C) 2013 Patrick Georgi <patrick@georgi-clan.de> + * + * 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; version 2 of the License. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA + */ + +#if 0 +/* NOTE: THIS CODE MUST REMAIN POSITION INDEPENDENT + * IT SHOULDN'T USE THE STACK + * AND IN GENERAL EXPECT NOTHING BUT RAM TO WORK + */ +.code32 +.data +#define HEADER_SIG 0x4f49424c // LBIO little endian +#define CB_TAG_FORWARD 0x11 +#define CB_TAG_MEMORY 0x1 +#define CB_TAG_FRAMEBUFFER 0x12 + +#define LINUX_PARAM_LOC 0x90000 +#define E820_NR_OFFSET 0x1e8 +#define LINUX_ENTRY_OFFSET 0x214 +#define E820_OFFSET 0x2d0 + +.trampoline_start: + +cld +xor %edx, %edx +mov $0, %ecx + +.headerSearch: +mov $0x10000, %ebx +add %ecx, %ebx +mov (%ecx), %eax +cmp $HEADER_SIG, %eax +je .headerSearchDone // found the header +add $16, %ecx +cmp %ecx, %ebx +jne .headerSearch + +.headerSearchDone: +cmp %ecx, %ebx // reached the end == not found anything? +je 2f // give up + +// we assume the checksum is okay, no test +mov 4(%ecx), %ebx +add %ecx, %ebx // ebx = cb_header + header_bytes +mov 20(%ecx), %ecx // ecx = table_entries + +.tableScan: +cmp $CB_TAG_FORWARD, (%ebx) +jne .testMemory + +/* forward tag: assume 32bit pointer */ +mov 8(%ebx), %ecx +jmp .headerSearch + +.testMemory: +cmp $CB_TAG_MEMORY, (%ebx) +jne .testFramebuffer + +/* memory tag: copy e820 map and entry count. also determine alt_mem_k */ +mov 4(%ebx), %eax +sub $8, %eax +shr $2, %eax /* eax = number of dwords of e820 data */ +cmp $(32 * 5), %eax /* linux wants at most 32 entries of 5 dwords */ +jng 1f +mov $(32 * 5), %eax /* only copy 32 entries */ +1: +mov %eax, %esi +mov $5, %edi +div %edi +mov %eax, (LINUX_PARAM_LOC + E820_NR_OFFSET) +mov %esi, %eax +xchg %eax, %ecx +lea 8(%ebx), %esi /* e820 data source */ +mov $(LINUX_PARAM_LOC + E820_OFFSET), %edi +rep movsl +xchg %eax, %ecx +jmp .endScan + +.testFramebuffer: +cmp $CB_TAG_FRAMEBUFFER, (%ebx) +jne .endScan +/* TODO: handle framebuffer tag */ + +.endScan: +add 4(%ebx), %ebx +dec %ecx +jnz .tableScan + +/* finally: jump to kernel */ +mov $LINUX_PARAM_LOC, %esi +jmp *(LINUX_PARAM_LOC + LINUX_ENTRY_OFFSET) + + +2: +hlt +jmp 2b + +.trampoline_end: + +.global trampoline_start, trampoline_size +trampoline_start: +.long .trampoline_start +trampoline_size: +.long .trampoline_end - .trampoline_start +#endif + +/* The code above is hand-crafted to fit various contraints. + * To simplify porting, the below matches the above. + * When changing any code in here, compile the above as a .S + * file, objcopy it to binary and paste the result below (minus + * the last 8 bytes which are trampoline_start and trampoline_size). + */ +const unsigned char trampoline[] = { +0xfc, 0x31, 0xd2, 0xb9, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x00, 0x00, 0x01, 0x00, 0x01, 0xcb, 0x8b, +0x01, 0x3d, 0x4c, 0x42, 0x49, 0x4f, 0x74, 0x07, 0x83, 0xc1, 0x10, 0x39, 0xcb, 0x75, 0xe9, 0x39, +0xcb, 0x74, 0x60, 0x8b, 0x59, 0x04, 0x01, 0xcb, 0x8b, 0x49, 0x14, 0x83, 0x3b, 0x11, 0x75, 0x05, +0x8b, 0x4b, 0x08, 0xeb, 0xd3, 0x83, 0x3b, 0x01, 0x75, 0x33, 0x8b, 0x43, 0x04, 0x83, 0xe8, 0x08, +0xc1, 0xe8, 0x02, 0x3d, 0xa0, 0x00, 0x00, 0x00, 0x7e, 0x05, 0xb8, 0xa0, 0x00, 0x00, 0x00, 0x89, +0xc6, 0xbf, 0x05, 0x00, 0x00, 0x00, 0xf7, 0xf7, 0xa3, 0xe8, 0x01, 0x09, 0x00, 0x89, 0xf0, 0x91, +0x8d, 0x73, 0x08, 0xbf, 0xd0, 0x02, 0x09, 0x00, 0xf3, 0xa5, 0x91, 0xeb, 0x05, 0x83, 0x3b, 0x12, +0x75, 0x00, 0x03, 0x5b, 0x04, 0x49, 0x75, 0xb3, 0xbe, 0x00, 0x00, 0x09, 0x00, 0xff, 0x25, 0x14, +0x02, 0x09, 0x00, 0xf4, 0xeb, 0xfd +}; + +void * const trampoline_start = &trampoline; +const unsigned long trampoline_size = sizeof trampoline; |