When I changed mkpayload, I did not realize we had a duplicate block of code in the linux payload code. Have it use the same header generator as the standard payload code does. Change-Id: Ie39540089ce89b704290c89127da4c7b051ecb0e Signed-off-by: Ronald G. Minnich <rminnich@google.com> Reviewed-on: http://review.coreboot.org/5115 Tested-by: build bot (Jenkins) Reviewed-by: Alexandru Gagniuc <mr.nuke.me@gmail.com> Reviewed-by: Paul Menzel <paulepanter@users.sourceforge.net>
		
			
				
	
	
		
			239 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			239 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * 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 = calloc(num_segments, sizeof(*segs));
 | 
						|
 | 
						|
	/* parameter block */
 | 
						|
	segs[0].type = PAYLOAD_SEGMENT_DATA;
 | 
						|
	segs[0].load_addr = LINUX_PARAM_LOC;
 | 
						|
	segs[0].mem_len = sizeof(params);
 | 
						|
	segs[0].offset = doffset;
 | 
						|
 | 
						|
	compress((void*)¶ms, sizeof(params), output->data + doffset, &cur_len);
 | 
						|
	segs[0].compression = algo;
 | 
						|
	segs[0].len = cur_len;
 | 
						|
 | 
						|
	doffset += cur_len;
 | 
						|
 | 
						|
	/* code block */
 | 
						|
	segs[1].type = PAYLOAD_SEGMENT_CODE;
 | 
						|
	segs[1].load_addr = kernel_base;
 | 
						|
	segs[1].mem_len = kernel_size;
 | 
						|
	segs[1].offset = doffset;
 | 
						|
 | 
						|
	compress(kernel_data, kernel_size, output->data + doffset, &cur_len);
 | 
						|
	segs[1].compression = algo;
 | 
						|
	segs[1].len = 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 = entrypoint;
 | 
						|
	segs[2].mem_len = trampoline_size;
 | 
						|
	segs[2].offset = doffset;
 | 
						|
 | 
						|
	compress(trampoline_start, trampoline_size, output->data + doffset, &cur_len);
 | 
						|
	segs[2].compression = algo;
 | 
						|
	segs[2].len = cur_len;
 | 
						|
 | 
						|
	doffset += cur_len;
 | 
						|
 | 
						|
	if (cmdline_size > 0) {
 | 
						|
		/* command line block */
 | 
						|
		segs[3].type = PAYLOAD_SEGMENT_DATA;
 | 
						|
		segs[3].load_addr = COMMAND_LINE_LOC;
 | 
						|
		segs[3].mem_len = cmdline_size;
 | 
						|
		segs[3].offset = doffset;
 | 
						|
 | 
						|
		compress(cmdline, cmdline_size, output->data + doffset, &cur_len);
 | 
						|
		segs[3].compression = algo;
 | 
						|
		segs[3].len = 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 = initrd_base;
 | 
						|
		segs[num_segments-1].mem_len = initrd_size;
 | 
						|
		segs[num_segments-1].offset = doffset;
 | 
						|
 | 
						|
		compress(initrd_data, initrd_size, output->data + doffset, &cur_len);
 | 
						|
		segs[num_segments-1].compression = algo;
 | 
						|
		segs[num_segments-1].len = cur_len;
 | 
						|
 | 
						|
		doffset += cur_len;
 | 
						|
	}
 | 
						|
 | 
						|
	/* prepare entry point segment */
 | 
						|
	segs[num_segments].type = PAYLOAD_SEGMENT_ENTRY;
 | 
						|
	segs[num_segments].load_addr = entrypoint;
 | 
						|
	output->size = doffset;
 | 
						|
	xdr_segs(output, segs, num_segments);
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
 |