diff options
author | Aaron Durbin <adurbin@chromium.org> | 2015-05-15 23:39:23 -0500 |
---|---|---|
committer | Patrick Georgi <pgeorgi@google.com> | 2015-06-02 14:09:31 +0200 |
commit | 899d13d0dff9e7495eb17950f19431e9bd344b2f (patch) | |
tree | 8901845fc299126a7125ff19fe1f7bde17e3f305 /src/lib/cbfs.c | |
parent | 68bdd00799c7b2b25f265fa9e31beb709c877eb6 (diff) |
cbfs: new API and better program loading
A new CBFS API is introduced to allow making CBFS access
easier for providing multiple CBFS sources. That is achieved
by decoupling the cbfs source from a CBFS file. A CBFS
source is described by a descriptor. It contains the necessary
properties for walking a CBFS to locate a file. The CBFS
file is then decoupled from the CBFS descriptor in that it's
no longer needed to access the contents of the file.
All of this is accomplished using the regions infrastructure
by repsenting CBFS sources and files as region_devices. Because
region_devices can be chained together forming subregions this
allows one to decouple a CBFS source from a file. This also allows
one to provide CBFS files that came from other sources for
payload and/or stage loading.
The program loading takes advantage of those very properties
by allowing multiple sources for locating a program. Because of
this we can reduce the overhead of loading programs because
it's all done in the common code paths. Only locating the
program is per source.
Change-Id: I339b84fce95f03d1dbb63a0f54a26be5eb07f7c8
Signed-off-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-on: http://review.coreboot.org/9134
Tested-by: build bot (Jenkins)
Tested-by: Raptor Engineering Automated Test Stand <noreply@raptorengineeringinc.com>
Reviewed-by: Patrick Georgi <pgeorgi@google.com>
Diffstat (limited to 'src/lib/cbfs.c')
-rw-r--r-- | src/lib/cbfs.c | 334 |
1 files changed, 190 insertions, 144 deletions
diff --git a/src/lib/cbfs.c b/src/lib/cbfs.c index 00071f8934..23dfee70c9 100644 --- a/src/lib/cbfs.c +++ b/src/lib/cbfs.c @@ -1,8 +1,8 @@ /* * This file is part of the coreboot project. * - * Copyright (C) 2008, Jordan Crouse <jordan@cosmicpenguin.net> - * Copyright (C) 2013 The Chromium OS Authors. All rights reserved. + * Copyright (C) 2011 secunet Security Networks AG + * Copyright 2015 Google Inc. * * 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 @@ -18,192 +18,238 @@ * Foundation, Inc. */ +#include <assert.h> +#include <string.h> +#include <stdlib.h> +#include <boot_device.h> +#include <cbfs.h> +#include <endian.h> +#include <lib.h> +#include <symbols.h> + +#define ERROR(x...) printk(BIOS_ERR, "CBFS: " x) +#define LOG(x...) printk(BIOS_INFO, "CBFS: " x) +#if IS_ENABLED(CONFIG_DEBUG_CBFS) +#define DEBUG(x...) printk(BIOS_SPEW, "CBFS: " x) +#else +#define DEBUG(x...) +#endif -#include <program_loading.h> -#include "cbfs_core.h" - -#ifndef __SMM__ -static inline int tohex4(unsigned int c) +int cbfs_boot_locate(struct region_device *fh, const char *name, uint32_t *type) { - return (c <= 9) ? (c + '0') : (c - 10 + 'a'); -} + struct cbfsd cbfs; + struct region_device rdev; + const struct region_device *boot_dev; + struct cbfs_props props; -static void tohex16(unsigned int val, char* dest) -{ - dest[0] = tohex4(val>>12); - dest[1] = tohex4((val>>8) & 0xf); - dest[2] = tohex4((val>>4) & 0xf); - dest[3] = tohex4(val & 0xf); -} + boot_device_init(); -void *cbfs_load_optionrom(struct cbfs_media *media, uint16_t vendor, - uint16_t device, void *dest) -{ - char name[17] = "pciXXXX,XXXX.rom"; - struct cbfs_optionrom *orom; - uint8_t *src; + if (cbfs_boot_region_properties(&props)) + return -1; - tohex16(vendor, name+3); - tohex16(device, name+8); + /* All boot CBFS operations are performed using the RO devie. */ + boot_dev = boot_device_ro(); - orom = (struct cbfs_optionrom *) - cbfs_get_file_content(media, name, CBFS_TYPE_OPTIONROM, NULL); + if (boot_dev == NULL) + return -1; - if (orom == NULL) - return NULL; + if (rdev_chain(&rdev, boot_dev, props.offset, props.size)) + return -1; - /* They might have specified a dest address. If so, we can decompress. - * If not, there's not much hope of decompressing or relocating the rom. - * in the common case, the expansion rom is uncompressed, we - * pass 0 in for the dest, and all we have to do is find the rom and - * return a pointer to it. - */ + cbfs.rdev = &rdev; + cbfs.align = props.align; - /* BUG: the cbfstool is (not yet) including a cbfs_optionrom header */ - src = (uint8_t *)orom; // + sizeof(struct cbfs_optionrom); + return cbfs_locate(fh, &cbfs, name, type); +} - if (! dest) - return src; +void *cbfs_boot_map_with_leak(const char *name, uint32_t type, size_t *size) +{ + struct region_device fh; + size_t fsize; - if (!cbfs_decompress(ntohl(orom->compression), - src, - dest, - ntohl(orom->len))) + if (cbfs_boot_locate(&fh, name, &type)) return NULL; - return dest; + fsize = region_device_sz(&fh); + + if (size != NULL) + *size = fsize; + + return rdev_mmap(&fh, 0, fsize); } -int cbfs_load_prog_stage_by_offset(struct cbfs_media *media, - struct prog *prog, ssize_t offset) +int cbfs_locate(struct region_device *fh, const struct cbfsd *cbfs, + const char *name, uint32_t *type) { - struct cbfs_stage stage; - struct cbfs_media backing_store; + size_t offset; + const struct region_device *rd; + size_t align; + + offset = 0; + rd = cbfs->rdev; + align = cbfs->align; + + LOG("Locating '%s'\n", name); + + /* Try to scan the entire cbfs region looking for file name. */ + while (1) { + struct cbfs_file file; + const size_t fsz = sizeof(file); + char *fname; + int name_match; + size_t datasz; + + DEBUG("Checking offset %zx\n", offset); + + /* Can't read file. Nothing else to do but bail out. */ + if (rdev_readat(rd, &file, offset, fsz) != fsz) + break; + + if (memcmp(file.magic, CBFS_FILE_MAGIC, sizeof(file.magic))) { + offset++; + offset = ALIGN_UP(offset, align); + continue; + } - if (init_backing_media(&media, &backing_store)) - return -1; + file.len = ntohl(file.len); + file.type = ntohl(file.type); + file.offset = ntohl(file.offset); - if (cbfs_read(media, &stage, offset, sizeof(stage)) != sizeof(stage)) { - ERROR("ERROR: failed to read stage header\n"); - return -1; - } + /* See if names match. */ + fname = rdev_mmap(rd, offset + fsz, file.offset - fsz); - LOG("loading stage from %#zx @ 0x%llx (%d bytes), entry @ 0x%llx\n", - offset, stage.load, stage.memlen, stage.entry); + if (fname == NULL) + break; - /* Stages rely the below clearing so that the bss is initialized. */ - memset((void *)(uintptr_t)stage.load, 0, stage.memlen); + name_match = !strcmp(fname, name); + rdev_munmap(rd, fname); - if (stage.compression == CBFS_COMPRESS_NONE) { - if (cbfs_read(media, (void *)(uintptr_t)stage.load, - offset + sizeof(stage), stage.len) != stage.len) { - ERROR("ERROR: Reading stage failed.\n"); - return -1; + if (!name_match) { + DEBUG(" Unmatched '%s' at %zx\n", fname, offset); + offset += file.offset + file.len; + offset = ALIGN_UP(offset, align); + continue; } - } else { - void *data = media->map(media, offset + sizeof(stage), - stage.len); - if (data == CBFS_MEDIA_INVALID_MAP_ADDRESS) { - ERROR("ERROR: Mapping stage failed.\n"); - return -1; + + if (type != NULL && *type != file.type) { + DEBUG(" Unmatched type %x at %zx\n", file.type, offset); + offset += file.offset + file.len; + offset = ALIGN_UP(offset, align); + continue; } - if (!cbfs_decompress(stage.compression, data, - (void *)(uintptr_t)stage.load, stage.len)) - return -1; - media->unmap(media, data); - } - arch_segment_loaded(stage.load, stage.memlen, SEG_FINAL); - DEBUG("stage loaded\n"); + LOG("Found @ offset %zx size %x\n", offset, file.len); + /* File and type match. Create a chained region_device to + * represent the cbfs file. */ + offset += file.offset; + datasz = file.len; + if (rdev_chain(fh, rd, offset, datasz)) + break; - prog_set_area(prog, (void *)(uintptr_t)stage.load, stage.memlen); - prog_set_entry(prog, (void *)(uintptr_t)stage.entry, NULL); + /* Success. */ + return 0; + } - return 0; + LOG("'%s' not found.\n", name); + return -1; } -int cbfs_load_prog_stage(struct cbfs_media *media, struct prog *prog) +static size_t inflate(void *src, void *dst) { - struct cbfs_file file; - ssize_t offset; - struct cbfs_media backing_store; - - if (init_backing_media(&media, &backing_store)) - return -1; - - offset = cbfs_locate_file(media, &file, prog->name); - if (offset < 0 || file.type != CBFS_TYPE_STAGE) - return -1; + if (ENV_BOOTBLOCK || ENV_VERSTAGE) + return 0; + if (ENV_ROMSTAGE && !IS_ENABLED(CONFIG_COMPRESS_RAMSTAGE)) + return 0; + return ulzma(src, dst); +} - if (cbfs_load_prog_stage_by_offset(media, prog, offset) < 0) - return -1; +static inline int tohex4(unsigned int c) +{ + return (c <= 9) ? (c + '0') : (c - 10 + 'a'); +} - return 0; +static void tohex16(unsigned int val, char* dest) +{ + dest[0] = tohex4(val>>12); + dest[1] = tohex4((val>>8) & 0xf); + dest[2] = tohex4((val>>4) & 0xf); + dest[3] = tohex4(val & 0xf); } -void *cbfs_load_stage_by_offset(struct cbfs_media *media, ssize_t offset) +void *cbfs_boot_map_optionrom(uint16_t vendor, uint16_t device) { - struct prog prog = { - .name = NULL, - }; + char name[17] = "pciXXXX,XXXX.rom"; - if (cbfs_load_prog_stage_by_offset(media, &prog, offset) < 0) - return (void *)-1; + tohex16(vendor, name+3); + tohex16(device, name+8); - return prog_entry(&prog); + return cbfs_boot_map_with_leak(name, CBFS_TYPE_OPTIONROM, NULL); } -void *cbfs_load_stage(struct cbfs_media *media, const char *name) +void *cbfs_boot_load_stage_by_name(const char *name) { - struct prog prog = { + struct prog stage = { .name = name, }; + uint32_t type = CBFS_TYPE_STAGE; - if (cbfs_load_prog_stage(media, &prog) < 0) - return (void *)-1; + if (cbfs_boot_locate(&stage.rdev, name, &type)) + return NULL; - return prog_entry(&prog); -} + if (cbfs_prog_stage_load(&stage)) + return NULL; -/* Simple buffer */ - -void *cbfs_simple_buffer_map(struct cbfs_simple_buffer *buffer, - struct cbfs_media *media, - size_t offset, size_t count) { - void *address = buffer->buffer + buffer->allocated; - DEBUG("simple_buffer_map(offset=%zd, count=%zd): " - "allocated=%zd, size=%zd, last_allocate=%zd\n", - offset, count, buffer->allocated, buffer->size, - buffer->last_allocate); - if (buffer->allocated + count > buffer->size) { - ERROR("simple_buffer: no room to map %zd bytes from %#zx\n", - count, offset); - return CBFS_MEDIA_INVALID_MAP_ADDRESS; - } - if (media->read(media, address, offset, count) != count) { - ERROR("simple_buffer: fail to read %zd bytes from 0x%zx\n", - count, offset); - return CBFS_MEDIA_INVALID_MAP_ADDRESS; - } - buffer->allocated += count; - buffer->last_allocate = count; - return address; + return prog_entry(&stage); } -void *cbfs_simple_buffer_unmap(struct cbfs_simple_buffer *buffer, - const void *address) { - // TODO Add simple buffer management so we can free more than last - // allocated one. - DEBUG("simple_buffer_unmap(address=0x%p): " - "allocated=%zd, size=%zd, last_allocate=%zd\n", - address, buffer->allocated, buffer->size, - buffer->last_allocate); - if ((buffer->buffer + buffer->allocated - buffer->last_allocate) == - address) { - buffer->allocated -= buffer->last_allocate; - buffer->last_allocate = 0; - } - return NULL; -} +int cbfs_prog_stage_load(struct prog *pstage) +{ + struct cbfs_stage stage; + uint8_t *load; + void *entry; + size_t fsize; + size_t foffset; + const struct region_device *fh = &pstage->rdev; -#endif + if (rdev_readat(fh, &stage, 0, sizeof(stage)) != sizeof(stage)) + return 0; + + fsize = region_device_sz(fh); + fsize -= sizeof(stage); + foffset = 0; + foffset += sizeof(stage); + + assert(fsize == stage.len); + + /* Note: cbfs_stage fields are currently in the endianness of the + * running processor. */ + load = (void *)(uintptr_t)stage.load; + entry = (void *)(uintptr_t)stage.entry; + + if (stage.compression == CBFS_COMPRESS_NONE) { + if (rdev_readat(fh, load, foffset, fsize) != fsize) + return -1; + } else if (stage.compression == CBFS_COMPRESS_LZMA) { + void *map = rdev_mmap(fh, foffset, fsize); + + if (map == NULL) + return -1; + + fsize = inflate(map, load); + + rdev_munmap(fh, map); + + if (!fsize) + return -1; + } else + return -1; + + /* Clear area not covered by file. */ + memset(&load[fsize], 0, stage.memlen - fsize); + + arch_segment_loaded((uintptr_t)load, stage.memlen, SEG_FINAL); + prog_set_area(pstage, load, stage.memlen); + prog_set_entry(pstage, entry, NULL); + + return 0; +} |