From 4be1674d32f7027a69a253704081599463188462 Mon Sep 17 00:00:00 2001 From: Aaron Durbin Date: Tue, 15 Sep 2015 17:00:23 -0500 Subject: 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 Reviewed-on: http://review.coreboot.org/11669 Tested-by: build bot (Jenkins) Reviewed-by: Patrick Georgi --- 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(-) (limited to 'util') diff --git a/util/cbfstool/Makefile b/util/cbfstool/Makefile index 65d5710605..b6fb38cebb 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 976f0c294b..99df7d7858 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 3e62525d48..d2a2c4631e 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 182fc8ebe5..6f8f1f4cc4 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 4a97fed6cb..8073d12222 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); -- cgit v1.2.3