summaryrefslogtreecommitdiff
path: root/src/lib/cbfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/cbfs.c')
-rw-r--r--src/lib/cbfs.c334
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;
+}