[coreboot-gerrit] New patch to review for coreboot: cbfstool: add --xip support to add-stage for x86

Aaron Durbin (adurbin@chromium.org) gerrit at coreboot.org
Wed Sep 16 01:21:40 CET 2015


Aaron Durbin (adurbin at chromium.org) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/11669

-gerrit

commit cd00651df8229f023b65b540ce40b859aba3a4cb
Author: Aaron Durbin <adurbin at chromium.org>
Date:   Tue Sep 15 17:00:23 2015 -0500

    cbfstool: add --xip support to add-stage for x86
    
    Instead of going through the locate then add-stage
    dance while linking romstage twice allow for adding romstage
    with --xip flags to perform the relocation while adding it
    into CBFS. The -P (page-size) and -a (alignment) parameters
    were added as well so one could specify the necessary
    parameters for x86 romstage.
    
    BUG=chrome-os-partner:44827
    BRANCH=None
    TEST=Built and booted on glados.
    
    Change-Id: I585619886f257e35f00961a1574009a51c28ff2b
    Signed-off-by: Aaron Durbin <adurbin at chromium.org>
---
 util/cbfstool/Makefile       |   1 +
 util/cbfstool/Makefile.inc   |   1 +
 util/cbfstool/cbfs-mkstage.c | 169 ++++++++++++++++++++++++++++++++++++++++---
 util/cbfstool/cbfstool.c     | 136 +++++++++++++++++++++-------------
 util/cbfstool/common.h       |   3 +
 5 files changed, 251 insertions(+), 59 deletions(-)

diff --git a/util/cbfstool/Makefile b/util/cbfstool/Makefile
index 65d5710..b6fb38c 100644
--- a/util/cbfstool/Makefile
+++ b/util/cbfstool/Makefile
@@ -15,6 +15,7 @@ CBFSTOOL_BINARY:=$(obj)/cbfstool
 CBFSTOOL_COMMON:=common.o cbfs_image.o compress.o fit.o
 CBFSTOOL_COMMON+=elfheaders.o cbfs-mkstage.o cbfs-mkpayload.o xdr.o
 CBFSTOOL_COMMON+=partitioned_file.o linux_trampoline.o cbfs-payload-linux.o
+CBFSTOOL_COMMON+=rmodule.o
 # LZMA
 CBFSTOOL_COMMON+=lzma/lzma.o
 CBFSTOOL_COMMON+=lzma/C/LzFind.o  lzma/C/LzmaDec.o  lzma/C/LzmaEnc.o
diff --git a/util/cbfstool/Makefile.inc b/util/cbfstool/Makefile.inc
index 976f0c2..99df7d7 100644
--- a/util/cbfstool/Makefile.inc
+++ b/util/cbfstool/Makefile.inc
@@ -6,6 +6,7 @@ cbfsobj += cbfs_image.o
 cbfsobj += cbfs-mkstage.o
 cbfsobj += cbfs-mkpayload.o
 cbfsobj += elfheaders.o
+cbfsobj += rmodule.o
 cbfsobj += xdr.o
 cbfsobj += fit.o
 cbfsobj += partitioned_file.o
diff --git a/util/cbfstool/cbfs-mkstage.c b/util/cbfstool/cbfs-mkstage.c
index 3e62525..d2a2c46 100644
--- a/util/cbfstool/cbfs-mkstage.c
+++ b/util/cbfstool/cbfs-mkstage.c
@@ -28,6 +28,7 @@
 #include "elfparsing.h"
 #include "common.h"
 #include "cbfs.h"
+#include "rmodule.h"
 
 /* Checks if program segment contains the ignored section */
 static int is_phdr_ignored(Elf64_Phdr *phdr, Elf64_Shdr *shdr)
@@ -90,6 +91,21 @@ static Elf64_Shdr *find_ignored_section_header(struct parsed_elf *pelf,
 	return NULL;
 }
 
+static void fill_cbfs_stage(struct buffer *outheader, enum comp_algo algo,
+				uint64_t entry, uint64_t loadaddr,
+				uint32_t filesize, uint32_t memsize)
+{
+	/* N.B. The original plan was that SELF data was B.E.
+	 * but: this is all L.E.
+	 * Maybe we should just change the spec.
+	 */
+	xdr_le.put32(outheader, algo);
+	xdr_le.put64(outheader, entry);
+	xdr_le.put64(outheader, loadaddr);
+	xdr_le.put32(outheader, filesize);
+	xdr_le.put32(outheader, memsize);
+}
+
 /* returns size of result, or -1 if error.
  * Note that, with the new code, this function
  * works for all elf files, not just the restricted set.
@@ -262,18 +278,12 @@ int parse_elf_to_stage(const struct buffer *input, struct buffer *output,
 	/* Set up for output marshaling. */
 	outheader.data = output->data;
 	outheader.size = 0;
-	/* N.B. The original plan was that SELF data was B.E.
-	 * but: this is all L.E.
-	 * Maybe we should just change the spec.
-	 */
-	xdr_le.put32(&outheader, algo);
+
 	/* Coreboot expects entry point to be physical address. Thus, adjust the
 	 * entry point accordingly.
 	 */
-	xdr_le.put64(&outheader, ehdr->e_entry + virt_to_phys);
-	xdr_le.put64(&outheader, data_start);
-	xdr_le.put32(&outheader, outlen);
-	xdr_le.put32(&outheader, mem_end - data_start);
+	fill_cbfs_stage(&outheader, algo, ehdr->e_entry + virt_to_phys,
+			data_start, outlen, mem_end - data_start);
 
 	if (*location)
 		*location -= sizeof(struct cbfs_stage);
@@ -284,3 +294,144 @@ err:
 	parsed_elf_destroy(&pelf);
 	return ret;
 }
+
+struct xip_context {
+	struct rmod_context rmodctx;
+	size_t ignored_section_idx;
+	Elf64_Shdr *ignored_section;
+};
+
+static int rmod_filter(struct reloc_filter *f, const Elf64_Rela *r)
+{
+	size_t symbol_index;
+	int reloc_type;
+	struct parsed_elf *pelf;
+	Elf64_Sym *sym;
+	struct xip_context *xipctx;
+
+	xipctx = f->context;
+	pelf = &xipctx->rmodctx.pelf;
+
+	/* Allow everything through if there isn't an ignored section. */
+	if (xipctx->ignored_section == NULL)
+		return 1;
+
+	reloc_type = ELF64_R_TYPE(r->r_info);
+	symbol_index = ELF64_R_SYM(r->r_info);
+	sym = &pelf->syms[symbol_index];
+
+	/* Nothing to filter. Relocation is not being applied to the
+	 * ignored section. */
+	if (sym->st_shndx != xipctx->ignored_section_idx)
+		return 1;
+
+	/* If there is any relocation to the ignored section that isn't
+	 * absolute fail as current assumptions are that all relocations
+	 * are absolute. */
+	if (reloc_type != R_386_32) {
+		ERROR("Invalid reloc to ignored section: %x\n", reloc_type);
+		return -1;
+	}
+
+	/* Relocation referencing ignored section. Don't emit it. */
+	return 0;
+}
+
+int parse_elf_to_xip_stage(const struct buffer *input, struct buffer *output,
+				uint32_t *location, const char *ignore_section)
+{
+	struct xip_context xipctx;
+	struct rmod_context *rmodctx;
+	struct reloc_filter filter;
+	struct parsed_elf *pelf;
+	size_t output_sz;
+	uint32_t adjustment;
+	struct buffer binput;
+	struct buffer boutput;
+	Elf64_Xword i;
+	int ret = -1;
+
+	xipctx.ignored_section_idx = 0;
+	rmodctx = &xipctx.rmodctx;
+	pelf = &rmodctx->pelf;
+
+	if (rmodule_init(rmodctx, input))
+		return -1;
+
+	/* Only support x86 XIP currently. */
+	if (rmodctx->pelf.ehdr.e_machine != EM_386) {
+		ERROR("Only support XIP stages for x86\n");
+		goto out;
+	}
+
+	xipctx.ignored_section =
+		find_ignored_section_header(pelf, ignore_section);
+
+	if (xipctx.ignored_section != NULL)
+		xipctx.ignored_section_idx =
+			xipctx.ignored_section - pelf->shdr;
+
+	filter.filter = rmod_filter;
+	filter.context = &xipctx;
+
+	if (rmodule_collect_relocations(rmodctx, &filter))
+		goto out;
+
+	output_sz = sizeof(struct cbfs_stage) + pelf->phdr->p_filesz;
+	if (buffer_create(output, output_sz, input->name) != 0) {
+		ERROR("Unable to allocate memory: %m\n");
+		goto out;
+	}
+	buffer_clone(&boutput, output);
+	memset(buffer_get(&boutput), 0, output_sz);
+	buffer_set_size(&boutput, 0);
+
+	/* Single loadable segment. The entire segment moves to final
+	 * location from based on virtual address of loadable segment. */
+	adjustment = *location - pelf->phdr->p_vaddr;
+	DEBUG("Relocation adjustment: %08x\n", adjustment);
+
+	fill_cbfs_stage(&boutput, CBFS_COMPRESS_NONE,
+			(uint32_t)pelf->ehdr.e_entry + adjustment,
+			(uint32_t)pelf->phdr->p_vaddr + adjustment,
+			pelf->phdr->p_filesz, pelf->phdr->p_memsz);
+	/* Need an adjustable buffer. */
+	buffer_clone(&binput, input);
+	buffer_seek(&binput, pelf->phdr->p_offset);
+	bputs(&boutput, buffer_get(&binput), pelf->phdr->p_filesz);
+
+	buffer_clone(&boutput, output);
+	buffer_seek(&boutput, sizeof(struct cbfs_stage));
+
+	/* Make adjustments to all the relocations within the program. */
+	for (i = 0; i < rmodctx->nrelocs; i++) {
+		size_t reloc_offset;
+		uint32_t val;
+		struct buffer in, out;
+
+		/* The relocations represent in-program addresses of the
+		 * linked program. Obtain the offset into the program to do
+		 * the adjustment. */
+		reloc_offset = rmodctx->emitted_relocs[i] - pelf->phdr->p_vaddr;
+
+		buffer_clone(&out, &boutput);
+		buffer_seek(&out, reloc_offset);
+		buffer_clone(&in, &out);
+		/* Appease around xdr semantics: xdr decrements buffer
+		 * size when get()ing and appends to size when put()ing. */
+		buffer_set_size(&out, 0);
+
+		val = xdr_le.get32(&in);
+		DEBUG("reloc %zx %08x -> %08x\n", reloc_offset, val,
+			val + adjustment);
+		xdr_le.put32(&out, val + adjustment);
+	}
+
+	/* Need to back up the location to include cbfs stage metadata. */
+	*location -= sizeof(struct cbfs_stage);
+	ret = 0;
+
+out:
+	rmodule_cleanup(rmodctx);
+	return ret;
+}
diff --git a/util/cbfstool/cbfstool.c b/util/cbfstool/cbfstool.c
index 182fc8e..6f8f1f4 100644
--- a/util/cbfstool/cbfstool.c
+++ b/util/cbfstool/cbfstool.c
@@ -74,6 +74,7 @@ static struct param {
 	bool fill_partial_upward;
 	bool fill_partial_downward;
 	bool show_immutable;
+	bool stage_xip;
 	int fit_empty_entries;
 	enum comp_algo compression;
 	/* for linux payloads */
@@ -115,6 +116,53 @@ static unsigned convert_to_from_top_aligned(const struct buffer *region,
 	return image_size - region->offset - offset;
 }
 
+static int do_cbfs_locate(int32_t *cbfs_addr, size_t metadata_size)
+{
+	if (!param.filename) {
+		ERROR("You need to specify -f/--filename.\n");
+		return 1;
+	}
+
+	if (!param.name) {
+		ERROR("You need to specify -n/--name.\n");
+		return 1;
+	}
+
+	struct cbfs_image image;
+	if (cbfs_image_from_buffer(&image, param.image_region,
+							param.headeroffset))
+		return 1;
+
+	if (cbfs_get_entry(&image, param.name))
+		WARN("'%s' already in CBFS.\n", param.name);
+
+	struct buffer buffer;
+	if (buffer_from_file(&buffer, param.filename) != 0) {
+		ERROR("Cannot load %s.\n", param.filename);
+		return 1;
+	}
+
+	/* Include cbfs_file size along with space for with name. */
+	metadata_size += cbfs_calculate_file_header_size(param.name);
+
+	int32_t address = cbfs_locate_entry(&image, buffer.size, param.pagesize,
+						param.alignment, metadata_size);
+	buffer_delete(&buffer);
+
+	if (address == -1) {
+		ERROR("'%s' can't fit in CBFS for page-size %#x, align %#x.\n",
+		      param.name, param.pagesize, param.alignment);
+		return 1;
+	}
+
+	if (param.top_aligned)
+		address = -convert_to_from_top_aligned(param.image_region,
+								address);
+
+	*cbfs_addr = address;
+	return 0;
+}
+
 typedef int (*convert_buffer_t)(struct buffer *buffer, uint32_t *offset,
 	struct cbfs_file *header);
 
@@ -269,8 +317,26 @@ static int cbfstool_convert_mkstage(struct buffer *buffer, uint32_t *offset,
 {
 	struct buffer output;
 	int ret;
-	ret = parse_elf_to_stage(buffer, &output, param.compression, offset,
-							param.ignore_section);
+
+	if (param.stage_xip) {
+		int32_t address;
+
+		if (do_cbfs_locate(&address, sizeof(struct cbfs_stage)))  {
+			ERROR("Could not find location for XIP stage.\n");
+			return 1;
+		}
+
+		/* Pass in a top aligned address. */
+		address = -convert_to_from_top_aligned(param.image_region,
+								address);
+		*offset = address;
+
+		ret = parse_elf_to_xip_stage(buffer, &output, offset,
+						param.ignore_section);
+	} else
+		ret = parse_elf_to_stage(buffer, &output, param.compression,
+					 offset, param.ignore_section);
+
 	if (ret != 0)
 		return -1;
 	buffer_delete(buffer);
@@ -328,53 +394,6 @@ static int cbfstool_convert_mkflatpayload(struct buffer *buffer,
 	return 0;
 }
 
-static int do_cbfs_locate(int32_t *cbfs_addr, size_t metadata_size)
-{
-	if (!param.filename) {
-		ERROR("You need to specify -f/--filename.\n");
-		return 1;
-	}
-
-	if (!param.name) {
-		ERROR("You need to specify -n/--name.\n");
-		return 1;
-	}
-
-	struct cbfs_image image;
-	if (cbfs_image_from_buffer(&image, param.image_region,
-							param.headeroffset))
-		return 1;
-
-	if (cbfs_get_entry(&image, param.name))
-		WARN("'%s' already in CBFS.\n", param.name);
-
-	struct buffer buffer;
-	if (buffer_from_file(&buffer, param.filename) != 0) {
-		ERROR("Cannot load %s.\n", param.filename);
-		return 1;
-	}
-
-	/* Include cbfs_file size along with space for with name. */
-	metadata_size += cbfs_calculate_file_header_size(param.name);
-
-	int32_t address = cbfs_locate_entry(&image, buffer.size, param.pagesize,
-						param.alignment, metadata_size);
-	buffer_delete(&buffer);
-
-	if (address == -1) {
-		ERROR("'%s' can't fit in CBFS for page-size %#x, align %#x.\n",
-		      param.name, param.pagesize, param.alignment);
-		return 1;
-	}
-
-	if (param.top_aligned)
-		address = -convert_to_from_top_aligned(param.image_region,
-								address);
-
-	*cbfs_addr = address;
-	return 0;
-}
-
 static size_t cbfs_default_metadata_size(void)
 {
 	/* TODO Old cbfstool always assume input is a stage file (and adding
@@ -410,6 +429,18 @@ static int cbfs_add(void)
 
 static int cbfs_add_stage(void)
 {
+	if (param.stage_xip) {
+		if (param.baseaddress_assigned) {
+			ERROR("Cannot specify base address for XIP.\n");
+			return 1;
+		}
+
+		if (param.compression != CBFS_COMPRESS_NONE) {
+			ERROR("Cannot specify compression for XIP.\n");
+			return 1;
+		}
+	}
+
 	return cbfs_add_component(param.filename,
 				  param.name,
 				  CBFS_COMPONENT_STAGE,
@@ -822,7 +853,7 @@ static const struct command commands[] = {
 	{"add-flat-binary", "H:r:f:n:l:e:c:b:vh?", cbfs_add_flat_binary, true,
 									true},
 	{"add-payload", "H:r:f:n:t:c:b:C:I:vh?", cbfs_add_payload, true, true},
-	{"add-stage", "H:r:f:n:t:c:b:S:vh?", cbfs_add_stage, true, true},
+	{"add-stage", "a:H:r:f:n:t:c:b:P:S:yvh?", cbfs_add_stage, true, true},
 	{"add-int", "H:r:i:n:b:vh?", cbfs_add_integer, true, true},
 	{"copy", "H:D:s:h?", cbfs_copy, true, true},
 	{"create", "M:r:s:B:b:H:o:m:vh?", cbfs_create, true, true},
@@ -865,6 +896,7 @@ static struct option long_options[] = {
 	{"type",          required_argument, 0, 't' },
 	{"verbose",       no_argument,       0, 'v' },
 	{"with-readonly", no_argument,       0, 'w' },
+	{"xip",           no_argument,       0, 'y' },
 	{NULL,            0,                 0,  0  }
 };
 
@@ -941,6 +973,7 @@ static void usage(char *name)
 	     "        (linux specific: [-C cmdline] [-I initrd])\n"
 	     " add-stage [-r image,regions] -f FILE -n NAME \\\n"
 	     "        [-c compression] [-b base] [-S section-to-ignore]    "
+	     "        [-a alignment] [-y|--xip] [-P page-size]"
 			"Add a stage to the ROM\n"
 	     " add-flat-binary [-r image,regions] -f FILE -n NAME \\\n"
 	     "        -l load-address -e entry-point [-c compression] \\\n"
@@ -1150,6 +1183,9 @@ int main(int argc, char **argv)
 			case 'S':
 				param.ignore_section = optarg;
 				break;
+			case 'y':
+				param.stage_xip = true;
+				break;
 			case 'h':
 			case '?':
 				usage(argv[0]);
diff --git a/util/cbfstool/common.h b/util/cbfstool/common.h
index 4a97fed..8073d12 100644
--- a/util/cbfstool/common.h
+++ b/util/cbfstool/common.h
@@ -184,6 +184,9 @@ int parse_flat_binary_to_payload(const struct buffer *input,
 int parse_elf_to_stage(const struct buffer *input, struct buffer *output,
 		       enum comp_algo algo, uint32_t *location,
 		       const char *ignore_section);
+/* location is TOP aligned. */
+int parse_elf_to_xip_stage(const struct buffer *input, struct buffer *output,
+				uint32_t *location, const char *ignore_section);
 
 void print_supported_filetypes(void);
 



More information about the coreboot-gerrit mailing list