summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/commonlib/bsd/include/commonlib/bsd/cbfs_serialized.h24
-rw-r--r--src/lib/cbfs.c56
-rw-r--r--src/lib/rmodule.c80
-rw-r--r--src/soc/amd/common/block/pi/refcode_loader.c6
-rw-r--r--util/cbfstool/cbfs-mkstage.c147
-rw-r--r--util/cbfstool/cbfs_image.c142
-rw-r--r--util/cbfstool/cbfstool.c74
-rw-r--r--util/cbfstool/common.h6
-rw-r--r--util/cbfstool/rmodule.c3
9 files changed, 206 insertions, 332 deletions
diff --git a/src/commonlib/bsd/include/commonlib/bsd/cbfs_serialized.h b/src/commonlib/bsd/include/commonlib/bsd/cbfs_serialized.h
index dc14cd5f49..dc80ed0d3d 100644
--- a/src/commonlib/bsd/include/commonlib/bsd/cbfs_serialized.h
+++ b/src/commonlib/bsd/include/commonlib/bsd/cbfs_serialized.h
@@ -20,7 +20,8 @@ enum cbfs_type {
CBFS_TYPE_NULL = 0xffffffff,
CBFS_TYPE_BOOTBLOCK = 0x01,
CBFS_TYPE_CBFSHEADER = 0x02,
- CBFS_TYPE_STAGE = 0x10,
+ CBFS_TYPE_LEGACY_STAGE = 0x10,
+ CBFS_TYPE_STAGE = 0x11,
CBFS_TYPE_SELF = 0x20,
CBFS_TYPE_FIT = 0x21,
CBFS_TYPE_OPTIONROM = 0x30,
@@ -131,6 +132,7 @@ enum cbfs_file_attr_tag {
CBFS_FILE_ATTR_TAG_ALIGNMENT = 0x42434c41, /* BE: 'BCLA' */
CBFS_FILE_ATTR_TAG_IBB = 0x32494242, /* BE: '2IBB' */
CBFS_FILE_ATTR_TAG_PADDING = 0x47444150, /* BE: 'GNDP' */
+ CBFS_FILE_ATTR_TAG_STAGEHEADER = 0x53746748, /* BE: 'StgH' */
};
struct cbfs_file_attr_compression {
@@ -160,22 +162,20 @@ struct cbfs_file_attr_align {
uint32_t alignment;
} __packed;
+struct cbfs_file_attr_stageheader {
+ uint32_t tag;
+ uint32_t len;
+ uint64_t loadaddr; /* Memory address to load the code to. */
+ uint32_t entry_offset; /* Offset of entry point from loadaddr. */
+ uint32_t memlen; /* Total length (including BSS) in memory. */
+} __packed;
+
+
/*** Component sub-headers ***/
/* Following are component sub-headers for the "standard"
component types */
-/** This is the sub-header for stage components. Stages are
- loaded by coreboot during the normal boot process */
-
-struct cbfs_stage {
- uint32_t compression; /** Compression type */
- uint64_t entry; /** entry point */
- uint64_t load; /** Where to load in memory */
- uint32_t len; /** length of data to load */
- uint32_t memlen; /** total length of object in memory */
-} __packed;
-
/** this is the sub-header for payload components. Payloads
are loaded by coreboot at the end of the boot process */
diff --git a/src/lib/cbfs.c b/src/lib/cbfs.c
index a274551c33..fbf4531862 100644
--- a/src/lib/cbfs.c
+++ b/src/lib/cbfs.c
@@ -386,11 +386,6 @@ cb_err_t cbfs_prog_stage_load(struct prog *pstage)
{
union cbfs_mdata mdata;
struct region_device rdev;
- struct cbfs_stage stage;
- uint8_t *load;
- void *entry;
- size_t fsize;
- size_t foffset;
cb_err_t err;
prog_locate_hook(pstage);
@@ -401,50 +396,41 @@ cb_err_t cbfs_prog_stage_load(struct prog *pstage)
assert(be32toh(mdata.h.type) == CBFS_TYPE_STAGE);
pstage->cbfs_type = CBFS_TYPE_STAGE;
- if (rdev_readat(&rdev, &stage, 0, sizeof(stage)) != sizeof(stage))
- return CB_CBFS_IO;
-
- fsize = region_device_sz(&rdev);
- fsize -= sizeof(stage);
- foffset = 0;
- foffset += sizeof(stage);
-
- /* cbfs_stage fields are written in little endian despite the other
- cbfs data types being encoded in big endian. */
- stage.compression = read_le32(&stage.compression);
- stage.entry = read_le64(&stage.entry);
- stage.load = read_le64(&stage.load);
- stage.len = read_le32(&stage.len);
- stage.memlen = read_le32(&stage.memlen);
-
- assert(fsize == stage.len);
+ enum cbfs_compression compression = CBFS_COMPRESS_NONE;
+ const struct cbfs_file_attr_compression *cattr = cbfs_find_attr(&mdata,
+ CBFS_FILE_ATTR_TAG_COMPRESSION, sizeof(*cattr));
+ if (cattr)
+ compression = be32toh(cattr->compression);
- load = (void *)(uintptr_t)stage.load;
- entry = (void *)(uintptr_t)stage.entry;
+ const struct cbfs_file_attr_stageheader *sattr = cbfs_find_attr(&mdata,
+ CBFS_FILE_ATTR_TAG_STAGEHEADER, sizeof(*sattr));
+ if (!sattr)
+ return CB_ERR;
+ prog_set_area(pstage, (void *)(uintptr_t)be64toh(sattr->loadaddr),
+ be32toh(sattr->memlen));
+ prog_set_entry(pstage, prog_start(pstage) +
+ be32toh(sattr->entry_offset), NULL);
/* Hacky way to not load programs over read only media. The stages
* that would hit this path initialize themselves. */
if ((ENV_BOOTBLOCK || ENV_SEPARATE_VERSTAGE) &&
!CONFIG(NO_XIP_EARLY_STAGES) && CONFIG(BOOT_DEVICE_MEMORY_MAPPED)) {
- void *mapping = rdev_mmap(&rdev, foffset, fsize);
+ void *mapping = rdev_mmap_full(&rdev);
rdev_munmap(&rdev, mapping);
- if (mapping == load)
- goto out;
+ if (mapping == prog_start(pstage))
+ return CB_SUCCESS;
}
- fsize = cbfs_stage_load_and_decompress(&rdev, foffset, fsize, load,
- stage.memlen, stage.compression);
+ size_t fsize = cbfs_stage_load_and_decompress(&rdev, 0, region_device_sz(&rdev),
+ prog_start(pstage), prog_size(pstage), compression);
if (!fsize)
return CB_ERR;
/* Clear area not covered by file. */
- memset(&load[fsize], 0, stage.memlen - fsize);
-
- prog_segment_loaded((uintptr_t)load, stage.memlen, SEG_FINAL);
+ memset(prog_start(pstage) + fsize, 0, prog_size(pstage) - fsize);
-out:
- prog_set_area(pstage, load, stage.memlen);
- prog_set_entry(pstage, entry, NULL);
+ prog_segment_loaded((uintptr_t)prog_start(pstage), prog_size(pstage),
+ SEG_FINAL);
return CB_SUCCESS;
}
diff --git a/src/lib/rmodule.c b/src/lib/rmodule.c
index 6ea9db724b..ac9eb0b306 100644
--- a/src/lib/rmodule.c
+++ b/src/lib/rmodule.c
@@ -2,7 +2,6 @@
#include <assert.h>
#include <cbmem.h>
#include <cbfs.h>
-#include <cbfs_private.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@@ -13,6 +12,8 @@
/* Change this define to get more verbose debugging for module loading. */
#define PK_ADJ_LEVEL BIOS_NEVER
+const size_t region_alignment = MIN_UNSAFE(DYN_CBMEM_ALIGN_SIZE, 4096);
+
static inline int rmodule_is_loaded(const struct rmodule *module)
{
return module->location != NULL;
@@ -190,20 +191,26 @@ int rmodule_load(void *base, struct rmodule *module)
return 0;
}
-int rmodule_calc_region(unsigned int region_alignment, size_t rmodule_size,
- size_t *region_size, int *load_offset)
+static void *rmodule_cbfs_allocator(void *rsl_arg, size_t unused,
+ union cbfs_mdata *mdata)
{
- /* region_alignment must be a power of 2. */
- if (region_alignment & (region_alignment - 1))
- BUG();
-
- if (region_alignment < 4096)
- region_alignment = 4096;
+ struct rmod_stage_load *rsl = rsl_arg;
+
+ assert(IS_POWER_OF_2(region_alignment) &&
+ region_alignment >= sizeof(struct rmodule_header));
+
+ /* The CBFS core just passes us the decompressed size of the file, but
+ we need to know the memlen of the binary image. We need to find and
+ parse the stage header explicitly. */
+ const struct cbfs_file_attr_stageheader *sattr = cbfs_find_attr(mdata,
+ CBFS_FILE_ATTR_TAG_STAGEHEADER, sizeof(*sattr));
+ if (!sattr) {
+ printk(BIOS_ERR, "rmodule '%s' has no stage header!\n",
+ rsl->prog->name);
+ return NULL;
+ }
- /* Sanity check rmodule_header size. The code below assumes it is less
- * than the minimum alignment required. */
- if (region_alignment < sizeof(struct rmodule_header))
- BUG();
+ const size_t memlen = be32toh(sattr->memlen);
/* Place the rmodule according to alignment. The rmodule files
* themselves are packed as a header and a payload, however the rmodule
@@ -215,7 +222,7 @@ int rmodule_calc_region(unsigned int region_alignment, size_t rmodule_size,
* to place the rmodule so that the program falls on the aligned
* address with the header just before it. Therefore, we need at least
* a page to account for the size of the header. */
- *region_size = ALIGN(rmodule_size + region_alignment, 4096);
+ size_t region_size = ALIGN(memlen + region_alignment, 4096);
/* The program starts immediately after the header. However,
* it needs to be aligned to a 4KiB boundary. Therefore, adjust the
* program location so that the program lands on a page boundary. The
@@ -231,22 +238,17 @@ int rmodule_calc_region(unsigned int region_alignment, size_t rmodule_size,
* | >= 0 bytes from alignment |
* +--------------------------------+ region_alignment
*/
- *load_offset = region_alignment;
- return region_alignment - sizeof(struct rmodule_header);
+ uint8_t *stage_region = cbmem_add(rsl->cbmem_id, region_size);
+ if (stage_region == NULL)
+ return NULL;
+
+ return stage_region + region_alignment - sizeof(struct rmodule_header);
}
int rmodule_stage_load(struct rmod_stage_load *rsl)
{
struct rmodule rmod_stage;
- size_t region_size;
- char *stage_region;
- int rmodule_offset;
- int load_offset;
- struct cbfs_stage stage;
- void *rmod_loc;
- struct region_device rdev;
- union cbfs_mdata mdata;
if (rsl->prog == NULL || prog_name(rsl->prog) == NULL)
return -1;
@@ -254,37 +256,15 @@ int rmodule_stage_load(struct rmod_stage_load *rsl)
if (prog_locate_hook(rsl->prog))
return -1;
- if (cbfs_boot_lookup(prog_name(rsl->prog), false, &mdata, &rdev) != CB_SUCCESS)
- return -1;
-
- assert(be32toh(mdata.h.type) == CBFS_TYPE_STAGE);
- rsl->prog->cbfs_type = CBFS_TYPE_STAGE;
-
- if (rdev_readat(&rdev, &stage, 0, sizeof(stage)) != sizeof(stage))
- return -1;
-
- rmodule_offset =
- rmodule_calc_region(DYN_CBMEM_ALIGN_SIZE,
- stage.memlen, &region_size, &load_offset);
-
- stage_region = cbmem_add(rsl->cbmem_id, region_size);
-
- if (stage_region == NULL)
- return -1;
-
- rmod_loc = &stage_region[rmodule_offset];
-
- printk(BIOS_INFO, "Decompressing stage %s @ %p (%d bytes)\n",
- prog_name(rsl->prog), rmod_loc, stage.memlen);
-
- if (!cbfs_load_and_decompress(&rdev, sizeof(stage), stage.len, rmod_loc,
- stage.memlen, stage.compression))
+ void *rmod_loc = cbfs_alloc(prog_name(rsl->prog),
+ rmodule_cbfs_allocator, rsl, NULL);
+ if (!rmod_loc)
return -1;
if (rmodule_parse(rmod_loc, &rmod_stage))
return -1;
- if (rmodule_load(&stage_region[load_offset], &rmod_stage))
+ if (rmodule_load(rmod_loc + sizeof(struct rmodule_header), &rmod_stage))
return -1;
prog_set_area(rsl->prog, rmod_stage.location,
diff --git a/src/soc/amd/common/block/pi/refcode_loader.c b/src/soc/amd/common/block/pi/refcode_loader.c
index fe2df5b972..70cedb3c1c 100644
--- a/src/soc/amd/common/block/pi/refcode_loader.c
+++ b/src/soc/amd/common/block/pi/refcode_loader.c
@@ -29,14 +29,8 @@ static int agesa_locate_raw_file(const char *name, struct region_device *rdev)
static int agesa_locate_stage_file_early(const char *name,
struct region_device *rdev)
{
- const size_t metadata_sz = sizeof(struct cbfs_stage);
-
if (agesa_locate_file(name, rdev, CBFS_TYPE_STAGE))
return -1;
-
- /* Peel off the cbfs stage metadata. */
- return rdev_chain(rdev, rdev, metadata_sz,
- region_device_sz(rdev) - metadata_sz);
}
static int agesa_locate_stage_file_ramstage(const char *name,
diff --git a/util/cbfstool/cbfs-mkstage.c b/util/cbfstool/cbfs-mkstage.c
index bd1cf54b6b..68eb641bfc 100644
--- a/util/cbfstool/cbfs-mkstage.c
+++ b/util/cbfstool/cbfs-mkstage.c
@@ -10,8 +10,6 @@
#include "cbfs.h"
#include "rmodule.h"
-#include <commonlib/bsd/compression.h>
-
/* Checks if program segment contains the ignored section */
static int is_phdr_ignored(Elf64_Phdr *phdr, Elf64_Shdr *shdr)
{
@@ -73,19 +71,20 @@ static Elf64_Shdr *find_ignored_section_header(struct parsed_elf *pelf,
return NULL;
}
-static void fill_cbfs_stage(struct buffer *outheader, enum cbfs_compression algo,
- uint64_t entry, uint64_t loadaddr,
- uint32_t filesize, uint32_t memsize)
+static int fill_cbfs_stageheader(struct cbfs_file_attr_stageheader *stageheader,
+ uint64_t entry, uint64_t loadaddr,
+ 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);
+ if (entry - loadaddr >= memsize) {
+ ERROR("stage entry point out of bounds!\n");
+ return -1;
+ }
+
+ stageheader->loadaddr = htonll(loadaddr);
+ stageheader->memlen = htonl(memsize);
+ stageheader->entry_offset = htonl(entry - loadaddr);
+
+ return 0;
}
/* returns size of result, or -1 if error.
@@ -93,25 +92,20 @@ static void fill_cbfs_stage(struct buffer *outheader, enum cbfs_compression algo
* works for all elf files, not just the restricted set.
*/
int parse_elf_to_stage(const struct buffer *input, struct buffer *output,
- enum cbfs_compression algo, const char *ignore_section)
+ const char *ignore_section,
+ struct cbfs_file_attr_stageheader *stageheader)
{
struct parsed_elf pelf;
Elf64_Phdr *phdr;
Elf64_Ehdr *ehdr;
Elf64_Shdr *shdr_ignored;
Elf64_Addr virt_to_phys;
- char *buffer;
- struct buffer outheader;
int ret = -1;
int headers;
- int i, outlen;
+ int i;
uint64_t data_start, data_end, mem_end;
- comp_func_ptr compress = compression_function(algo);
- if (!compress)
- return -1;
-
int flags = ELF_PARSE_PHDR | ELF_PARSE_SHDR | ELF_PARSE_STRTAB;
if (parse_elf(input, &pelf, flags)) {
@@ -178,15 +172,13 @@ int parse_elf_to_stage(const struct buffer *input, struct buffer *output,
exit(1);
}
- /* allocate an intermediate buffer for the data */
- buffer = calloc(data_end - data_start, 1);
-
- if (buffer == NULL) {
+ if (buffer_create(output, data_end - data_start, input->name) != 0) {
ERROR("Unable to allocate memory: %m\n");
goto err;
}
+ memset(output->data, 0, output->size);
- /* Copy the file data into the buffer */
+ /* Copy the file data into the output buffer */
for (i = 0; i < headers; i++) {
if (phdr[i].p_type != PT_LOAD)
@@ -207,90 +199,17 @@ int parse_elf_to_stage(const struct buffer *input, struct buffer *output,
ERROR("Underflow copying out the segment."
"File has %zu bytes left, segment end is %zu\n",
input->size, (size_t)(phdr[i].p_offset + phdr[i].p_filesz));
- free(buffer);
goto err;
}
- memcpy(buffer + (phdr[i].p_paddr - data_start),
+ memcpy(&output->data[phdr[i].p_paddr - data_start],
&input->data[phdr[i].p_offset],
phdr[i].p_filesz);
}
- /* Now make the output buffer */
- if (buffer_create(output, sizeof(struct cbfs_stage) + data_end - data_start,
- input->name) != 0) {
- ERROR("Unable to allocate memory: %m\n");
- free(buffer);
- goto err;
- }
- memset(output->data, 0, output->size);
-
- /* Compress the data, at which point we'll know information
- * to fill out the header. This seems backward but it works because
- * - the output header is a known size (not always true in many xdr's)
- * - we do need to know the compressed output size first
- * If compression fails or makes the data bigger, we'll warn about it
- * and use the original data.
- */
- if (compress(buffer, data_end - data_start,
- (output->data + sizeof(struct cbfs_stage)),
- &outlen) < 0 || (unsigned)outlen > data_end - data_start) {
- WARN("Compression failed or would make the data bigger "
- "- disabled.\n");
- memcpy(output->data + sizeof(struct cbfs_stage),
- buffer, data_end - data_start);
- outlen = data_end - data_start;
- algo = CBFS_COMPRESS_NONE;
- }
-
- /* Check for enough BSS scratch space to decompress LZ4 in-place. */
- if (algo == CBFS_COMPRESS_LZ4) {
- size_t result;
- size_t memlen = mem_end - data_start;
- size_t compressed_size = outlen;
- char *compare_buffer = malloc(memlen);
- char *start = compare_buffer + memlen - compressed_size;
-
- if (compare_buffer == NULL) {
- ERROR("Can't allocate memory!\n");
- free(buffer);
- goto err;
- }
-
- memcpy(start, output->data + sizeof(struct cbfs_stage),
- compressed_size);
- result = ulz4fn(start, compressed_size, compare_buffer, memlen);
-
- if (result == 0) {
- ERROR("Not enough scratch space to decompress LZ4 in-place -- increase BSS size or disable compression!\n");
- free(compare_buffer);
- free(buffer);
- goto err;
- }
- if (result != data_end - data_start ||
- memcmp(compare_buffer, buffer, data_end - data_start)) {
- ERROR("LZ4 compression BUG! Report to mailing list.\n");
- free(compare_buffer);
- free(buffer);
- goto err;
- }
- free(compare_buffer);
- }
-
- free(buffer);
-
- /* Set up for output marshaling. */
- outheader.data = output->data;
- outheader.size = 0;
-
/* coreboot expects entry point to be physical address. Thus, adjust the
- * entry point accordingly.
- */
- fill_cbfs_stage(&outheader, algo, ehdr->e_entry + virt_to_phys,
- data_start, outlen, mem_end - data_start);
-
- output->size = sizeof(struct cbfs_stage) + outlen;
- ret = 0;
-
+ entry point accordingly. */
+ ret = fill_cbfs_stageheader(stageheader, ehdr->e_entry + virt_to_phys,
+ data_start, mem_end - data_start);
err:
parsed_elf_destroy(&pelf);
return ret;
@@ -341,13 +260,13 @@ static int rmod_filter(struct reloc_filter *f, const Elf64_Rela *r)
}
int parse_elf_to_xip_stage(const struct buffer *input, struct buffer *output,
- uint32_t *location, const char *ignore_section)
+ uint32_t *location, const char *ignore_section,
+ struct cbfs_file_attr_stageheader *stageheader)
{
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;
@@ -381,13 +300,12 @@ int parse_elf_to_xip_stage(const struct buffer *input, struct buffer *output,
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) {
+ if (buffer_create(output, pelf->phdr->p_filesz, input->name) != 0) {
ERROR("Unable to allocate memory: %m\n");
goto out;
}
buffer_clone(&boutput, output);
- memset(buffer_get(&boutput), 0, output_sz);
+ memset(buffer_get(&boutput), 0, pelf->phdr->p_filesz);
buffer_set_size(&boutput, 0);
/* Single loadable segment. The entire segment moves to final
@@ -395,17 +313,16 @@ int parse_elf_to_xip_stage(const struct buffer *input, struct buffer *output,
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);
+ fill_cbfs_stageheader(stageheader,
+ (uint32_t)pelf->ehdr.e_entry + adjustment,
+ (uint32_t)pelf->phdr->p_vaddr + adjustment,
+ 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++) {
@@ -431,8 +348,6 @@ int parse_elf_to_xip_stage(const struct buffer *input, struct buffer *output,
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:
diff --git a/util/cbfstool/cbfs_image.c b/util/cbfstool/cbfs_image.c
index d3c6c94d48..1fb19bacd6 100644
--- a/util/cbfstool/cbfs_image.c
+++ b/util/cbfstool/cbfs_image.c
@@ -824,69 +824,6 @@ struct cbfs_file *cbfs_get_entry(struct cbfs_image *image, const char *name)
return NULL;
}
-static int cbfs_stage_decompress(struct cbfs_stage *stage, struct buffer *buff)
-{
- struct buffer reader;
- char *orig_buffer;
- char *new_buffer;
- size_t new_buff_sz;
- decomp_func_ptr decompress;
-
- buffer_clone(&reader, buff);
-
- /* The stage metadata is in little endian. */
- stage->compression = xdr_le.get32(&reader);
- stage->entry = xdr_le.get64(&reader);
- stage->load = xdr_le.get64(&reader);
- stage->len = xdr_le.get32(&reader);
- stage->memlen = xdr_le.get32(&reader);
-
- /* Create a buffer just with the uncompressed program now that the
- * struct cbfs_stage has been peeled off. */
- if (stage->compression == CBFS_COMPRESS_NONE) {
- new_buff_sz = buffer_size(buff) - sizeof(struct cbfs_stage);
-
- orig_buffer = buffer_get(buff);
- new_buffer = calloc(1, new_buff_sz);
- memcpy(new_buffer, orig_buffer + sizeof(struct cbfs_stage),
- new_buff_sz);
- buffer_init(buff, buff->name, new_buffer, new_buff_sz);
- free(orig_buffer);
- return 0;
- }
-
- decompress = decompression_function(stage->compression);
- if (decompress == NULL)
- return -1;
-
- orig_buffer = buffer_get(buff);
-
- /* This can be too big of a buffer needed, but there's no current
- * field indicating decompressed size of data. */
- new_buff_sz = stage->memlen;
- new_buffer = calloc(1, new_buff_sz);
-
- if (decompress(orig_buffer + sizeof(struct cbfs_stage),
- (int)(buffer_size(buff) - sizeof(struct cbfs_stage)),
- new_buffer, (int)new_buff_sz, &new_buff_sz)) {
- ERROR("Couldn't decompress stage.\n");
- free(new_buffer);
- return -1;
- }
-
- /* Include correct size for full stage info. */
- buffer_init(buff, buff->name, new_buffer, new_buff_sz);
-
- /* True decompressed size is just the data size -- no metadata. */
- stage->len = new_buff_sz;
- /* Stage is not compressed. */
- stage->compression = CBFS_COMPRESS_NONE;
-
- free(orig_buffer);
-
- return 0;
-}
-
static int cbfs_payload_decompress(struct cbfs_payload_segment *segments,
struct buffer *buff, int num_seg)
{
@@ -1020,11 +957,11 @@ static int init_elf_from_arch(Elf64_Ehdr *ehdr, uint32_t cbfs_arch)
return 0;
}
-static int cbfs_stage_make_elf(struct buffer *buff, uint32_t arch)
+static int cbfs_stage_make_elf(struct buffer *buff, uint32_t arch,
+ struct cbfs_file *entry)
{
Elf64_Ehdr ehdr;
Elf64_Shdr shdr;
- struct cbfs_stage stage;
struct elf_writer *ew;
struct buffer elf_out;
size_t empty_sz;
@@ -1035,16 +972,23 @@ static int cbfs_stage_make_elf(struct buffer *buff, uint32_t arch)
return -1;
}
- if (cbfs_stage_decompress(&stage, buff)) {
- ERROR("Failed to decompress stage.\n");
+ struct cbfs_file_attr_stageheader *stage = NULL;
+ for (struct cbfs_file_attribute *attr = cbfs_file_first_attr(entry);
+ attr != NULL; attr = cbfs_file_next_attr(entry, attr)) {
+ if (ntohl(attr->tag) == CBFS_FILE_ATTR_TAG_STAGEHEADER) {
+ stage = (struct cbfs_file_attr_stageheader *)attr;
+ break;
+ }
+ }
+
+ if (stage == NULL) {
+ ERROR("Stage header not found for %s\n", entry->filename);
return -1;
}
if (init_elf_from_arch(&ehdr, arch))
return -1;
- ehdr.e_entry = stage.entry;
-
/* Attempt rmodule translation first. */
rmod_ret = rmodule_stage_to_elf(&ehdr, buff);
@@ -1056,6 +1000,8 @@ static int cbfs_stage_make_elf(struct buffer *buff, uint32_t arch)
/* Rmodule couldn't do anything with the data. Continue on with SELF. */
+ ehdr.e_entry = ntohll(stage->loadaddr) + ntohl(stage->entry_offset);
+
ew = elf_writer_init(&ehdr);
if (ew == NULL) {
ERROR("Unable to init ELF writer.\n");
@@ -1065,9 +1011,9 @@ static int cbfs_stage_make_elf(struct buffer *buff, uint32_t arch)
memset(&shdr, 0, sizeof(shdr));
shdr.sh_type = SHT_PROGBITS;
shdr.sh_flags = SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR;
- shdr.sh_addr = stage.load;
- shdr.sh_size = stage.len;
- empty_sz = stage.memlen - stage.len;
+ shdr.sh_addr = ntohll(stage->loadaddr);
+ shdr.sh_size = buffer_size(buff);
+ empty_sz = ntohl(stage->memlen) - buffer_size(buff);
if (elf_writer_add_section(ew, &shdr, buff, ".program")) {
ERROR("Unable to add ELF section: .program\n");
@@ -1082,7 +1028,7 @@ static int cbfs_stage_make_elf(struct buffer *buff, uint32_t arch)
memset(&shdr, 0, sizeof(shdr));
shdr.sh_type = SHT_NOBITS;
shdr.sh_flags = SHF_WRITE | SHF_ALLOC;
- shdr.sh_addr = stage.load + stage.len;
+ shdr.sh_addr = ntohl(stage->loadaddr) + buffer_size(buff);
shdr.sh_size = empty_sz;
if (elf_writer_add_section(ew, &shdr, &b, ".empty")) {
ERROR("Unable to add ELF section: .empty\n");
@@ -1106,7 +1052,8 @@ static int cbfs_stage_make_elf(struct buffer *buff, uint32_t arch)
return 0;
}
-static int cbfs_payload_make_elf(struct buffer *buff, uint32_t arch)
+static int cbfs_payload_make_elf(struct buffer *buff, uint32_t arch,
+ unused struct cbfs_file *entry)
{
Elf64_Ehdr ehdr;
Elf64_Shdr shdr;
@@ -1258,7 +1205,7 @@ static int cbfs_payload_make_elf(struct buffer *buff, uint32_t arch)
}
if (elf_writer_serialize(ew, &elf_out)) {
- ERROR("Unable to create ELF file from stage.\n");
+ ERROR("Unable to create ELF file from payload.\n");
goto out;
}
@@ -1320,13 +1267,13 @@ int cbfs_export_entry(struct cbfs_image *image, const char *entry_name,
}
/*
- * The stage metadata is never compressed proper for cbfs_stage
- * files. The contents of the stage data can be though. Therefore
- * one has to do a second pass for stages to potentially decompress
- * the stage data to make it more meaningful.
+ * We want to export stages and payloads as ELFs, not with coreboot's
+ * custom stage/SELF binary formats, so we need to do extra processing
+ * to turn them back into an ELF.
*/
if (do_processing) {
- int (*make_elf)(struct buffer *, uint32_t) = NULL;
+ int (*make_elf)(struct buffer *, uint32_t,
+ struct cbfs_file *) = NULL;
switch (ntohl(entry->type)) {
case CBFS_TYPE_STAGE:
make_elf = cbfs_stage_make_elf;
@@ -1335,7 +1282,7 @@ int cbfs_export_entry(struct cbfs_image *image, const char *entry_name,
make_elf = cbfs_payload_make_elf;
break;
}
- if (make_elf && make_elf(&buffer, arch)) {
+ if (make_elf && make_elf(&buffer, arch, entry)) {
ERROR("Failed to write %s into %s.\n",
entry_name, filename);
buffer_delete(&buffer);
@@ -1387,17 +1334,29 @@ int cbfs_print_header_info(struct cbfs_image *image)
return 0;
}
-static int cbfs_print_stage_info(struct cbfs_stage *stage, FILE* fp)
+static int cbfs_print_stage_info(struct cbfs_file *entry, FILE* fp)
{
+
+ struct cbfs_file_attr_stageheader *stage = NULL;
+ for (struct cbfs_file_attribute *attr = cbfs_file_first_attr(entry);
+ attr != NULL; attr = cbfs_file_next_attr(entry, attr)) {
+ if (ntohl(attr->tag) == CBFS_FILE_ATTR_TAG_STAGEHEADER) {
+ stage = (struct cbfs_file_attr_stageheader *)attr;
+ break;
+ }
+ }
+
+ if (stage == NULL) {
+ fprintf(fp, " ERROR: stage header not found!\n");
+ return -1;
+ }
+
fprintf(fp,
- " %s compression, entry: 0x%" PRIx64 ", load: 0x%" PRIx64 ", "
- "length: %d/%d\n",
- lookup_name_by_type(types_cbfs_compression,
- stage->compression, "(unknown)"),
- stage->entry,
- stage->load,
- stage->len,
- stage->memlen);
+ " entry: 0x%" PRIx64 ", load: 0x%" PRIx64 ", "
+ "memlen: %d\n",
+ ntohll(stage->loadaddr) + ntohl(stage->entry_offset),
+ ntohll(stage->loadaddr),
+ ntohl(stage->memlen));
return 0;
}
@@ -1519,8 +1478,7 @@ int cbfs_print_entry_info(struct cbfs_image *image, struct cbfs_file *entry,
/* note the components of the subheader may be in host order ... */
switch (ntohl(entry->type)) {
case CBFS_TYPE_STAGE:
- cbfs_print_stage_info((struct cbfs_stage *)
- CBFS_SUBHEADER(entry), fp);
+ cbfs_print_stage_info(entry, fp);
break;
case CBFS_TYPE_SELF:
diff --git a/util/cbfstool/cbfstool.c b/util/cbfstool/cbfstool.c
index 3e80712ca2..6133536b4b 100644
--- a/util/cbfstool/cbfstool.c
+++ b/util/cbfstool/cbfstool.c
@@ -14,7 +14,9 @@
#include "cbfs_sections.h"
#include "elfparsing.h"
#include "partitioned_file.h"
+#include "lz4/lib/xxhash.h"
#include <commonlib/bsd/cbfs_private.h>
+#include <commonlib/bsd/compression.h>
#include <commonlib/bsd/metadata_hash.h>
#include <commonlib/fsp.h>
#include <commonlib/endian.h>
@@ -911,16 +913,7 @@ static int cbfs_add_component(const char *filename,
sizeof(struct cbfs_file_attr_position));
if (attrs == NULL)
goto error;
- /* If we add a stage or a payload, we need to take */
- /* care about the additional metadata that is added */
- /* to the cbfs file and therefore set the position */
- /* the real beginning of the data. */
- if (type == CBFS_TYPE_STAGE)
- attrs->position = htonl(offset - sizeof(struct cbfs_stage));
- else if (type == CBFS_TYPE_SELF)
- attrs->position = htonl(offset - sizeof(struct cbfs_payload));
- else
- attrs->position = htonl(offset);
+ attrs->position = htonl(offset);
}
/* Add alignment attribute if used */
if (param.alignment) {
@@ -1118,11 +1111,18 @@ static int cbfstool_convert_mkstage(struct buffer *buffer, uint32_t *offset,
* stages that would actually fit once compressed.
*/
if ((param.alignment || param.stage_xip) &&
- do_cbfs_locate(offset, sizeof(struct cbfs_stage), data_size)) {
+ do_cbfs_locate(offset, sizeof(struct cbfs_file_attr_stageheader),
+ data_size)) {
ERROR("Could not find location for stage.\n");
return 1;
}
+ struct cbfs_file_attr_stageheader *stageheader = (void *)
+ cbfs_add_file_attr(header, CBFS_FILE_ATTR_TAG_STAGEHEADER,
+ sizeof(struct cbfs_file_attr_stageheader));
+ if (!stageheader)
+ return -1;
+
if (param.stage_xip) {
/*
* Ensure the address is a memory mapped one. This assumes
@@ -1132,19 +1132,57 @@ static int cbfstool_convert_mkstage(struct buffer *buffer, uint32_t *offset,
*offset = convert_addr_space(param.image_region, *offset);
ret = parse_elf_to_xip_stage(buffer, &output, offset,
- param.ignore_section);
+ param.ignore_section,
+ stageheader);
} else {
- ret = parse_elf_to_stage(buffer, &output, param.compression,
- param.ignore_section);
+ ret = parse_elf_to_stage(buffer, &output, param.ignore_section,
+ stageheader);
}
-
if (ret != 0)
return -1;
+
+ /* Store a hash of original uncompressed stage to compare later. */
+ size_t decmp_size = buffer_size(&output);
+ uint32_t decmp_hash = XXH32(buffer_get(&output), decmp_size, 0);
+
+ /* Chain to base conversion routine to handle compression. */
+ ret = cbfstool_convert_raw(&output, offset, header);
+ if (ret != 0)
+ goto fail;
+
+ /* Special care must be taken for LZ4-compressed stages that the BSS is
+ large enough to provide scratch space for in-place decompression. */
+ if (!param.precompression && param.compression == CBFS_COMPRESS_LZ4) {
+ size_t memlen = ntohl(stageheader->memlen);
+ size_t compressed_size = buffer_size(&output);
+ uint8_t *compare_buffer = malloc(memlen);
+ uint8_t *start = compare_buffer + memlen - compressed_size;
+ if (!compare_buffer) {
+ ERROR("Out of memory\n");
+ goto fail;
+ }
+ memcpy(start, buffer_get(&output), compressed_size);
+ ret = ulz4fn(start, compressed_size, compare_buffer, memlen);
+ if (ret == 0) {
+ ERROR("Not enough scratch space to decompress LZ4 in-place -- increase BSS size or disable compression!\n");
+ free(compare_buffer);
+ goto fail;
+ } else if (ret != (int)decmp_size ||
+ decmp_hash != XXH32(compare_buffer, decmp_size, 0)) {
+ ERROR("LZ4 compression BUG! Report to mailing list.\n");
+ free(compare_buffer);
+ goto fail;
+ }
+ free(compare_buffer);
+ }
+
buffer_delete(buffer);
- // Direct assign, no dupe.
- memcpy(buffer, &output, sizeof(*buffer));
- header->len = htonl(output.size);
+ buffer_clone(buffer, &output);
return 0;
+
+fail:
+ buffer_delete(&output);
+ return -1;
}
static int cbfstool_convert_mkpayload(struct buffer *buffer,
diff --git a/util/cbfstool/common.h b/util/cbfstool/common.h
index db9c7e7297..479fd713dd 100644
--- a/util/cbfstool/common.h
+++ b/util/cbfstool/common.h
@@ -174,10 +174,12 @@ int parse_flat_binary_to_payload(const struct buffer *input,
enum cbfs_compression algo);
/* cbfs-mkstage.c */
int parse_elf_to_stage(const struct buffer *input, struct buffer *output,
- enum cbfs_compression algo, const char *ignore_section);
+ const char *ignore_section,
+ struct cbfs_file_attr_stageheader *stageheader);
/* location is TOP aligned. */
int parse_elf_to_xip_stage(const struct buffer *input, struct buffer *output,
- uint32_t *location, const char *ignore_section);
+ uint32_t *location, const char *ignore_section,
+ struct cbfs_file_attr_stageheader *stageheader);
void print_supported_architectures(void);
void print_supported_filetypes(void);
diff --git a/util/cbfstool/rmodule.c b/util/cbfstool/rmodule.c
index 258a4d8803..4ac2951f72 100644
--- a/util/cbfstool/rmodule.c
+++ b/util/cbfstool/rmodule.c
@@ -498,8 +498,9 @@ write_elf(const struct rmod_context *ctx, const struct buffer *in,
/* Program contents. */
buffer_splice(&program, in, ctx->phdr->p_offset, ctx->phdr->p_filesz);
- /* Create ELF writer with modified entry point. */
+ /* Create ELF writer. Set entry point to 0 to match section offsets. */
memcpy(&ehdr, &ctx->pelf.ehdr, sizeof(ehdr));
+ ehdr.e_entry = 0;
ew = elf_writer_init(&ehdr);
if (ew == NULL) {