Aaron Durbin (adurbin@google.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/5394
-gerrit
commit da9a3e01f298f829fc5cbeefa1944d698e5b25bf Author: Aaron Durbin adurbin@chromium.org Date: Thu Mar 13 08:50:00 2014 -0500
regions support
Needs some cleanup work. Compile tested as of now.
Change-Id: I2a78278090ae928468e197ac73d1fb18596bf8cb Signed-off-by: Aaron Durbin adurbin@chromium.org --- src/Kconfig | 7 + src/arch/x86/boot/smbios.c | 8 +- src/arch/x86/lib/rom_media.c | 114 +++--- src/cpu/intel/microcode/microcode.c | 38 +- src/cpu/x86/mirror_payload.c | 38 +- src/device/pci_device.c | 7 +- src/device/pci_rom.c | 232 +++++++----- src/drivers/elog/elog.c | 142 +++----- src/include/cbfs.h | 120 ++++--- src/include/cbfs_core.h | 44 --- src/include/device/pci_rom.h | 4 +- src/include/payload_loader.h | 3 + src/include/region.h | 138 ++++++++ src/include/rmodule.h | 7 +- src/lib/Makefile.inc | 10 + src/lib/bootrw_spi_flash_region.c | 107 ++++++ src/lib/cbfs.c | 489 ++++++++++++++++++-------- src/lib/cbfs_boot_region.c | 133 +++++++ src/lib/cbfs_core.c | 224 ------------ src/lib/loaders/cbfs_payload_loader.c | 18 +- src/lib/loaders/cbfs_ramstage_loader.c | 12 +- src/lib/loaders/load_and_run_payload.c | 5 +- src/lib/region.c | 293 +++++++++++++++ src/lib/rmodule.c | 46 ++- src/lib/selfboot.c | 113 +++--- src/soc/intel/baytrail/Kconfig | 1 + src/soc/intel/baytrail/mrc_cache.c | 20 +- src/soc/intel/baytrail/nvm.c | 27 +- src/soc/intel/baytrail/romstage/raminit.c | 9 +- src/soc/intel/baytrail/romstage/romstage.c | 4 + src/vendorcode/google/chromeos/Makefile.inc | 1 + src/vendorcode/google/chromeos/chromeos.c | 4 + src/vendorcode/google/chromeos/fmap.c | 174 +++++---- src/vendorcode/google/chromeos/fmap.h | 25 +- src/vendorcode/google/chromeos/vboot_loader.c | 47 ++- 35 files changed, 1725 insertions(+), 939 deletions(-)
diff --git a/src/Kconfig b/src/Kconfig index 531a8c8..b0b8d26 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -1094,3 +1094,10 @@ config REG_SCRIPT default n help Internal option that controls whether we compile in register scripts. + +config BOOT_REGION_SPI_FLASH + def_bool n + depends on ARCH_X86 + help + Use generic SPI flash boot region support. This will provide a + function to initialize the read-write boot region. diff --git a/src/arch/x86/boot/smbios.c b/src/arch/x86/boot/smbios.c index 0e84acc..0434715 100644 --- a/src/arch/x86/boot/smbios.c +++ b/src/arch/x86/boot/smbios.c @@ -27,7 +27,6 @@ #include <device/device.h> #include <arch/cpu.h> #include <cpu/x86/name.h> -#include <cbfs_core.h> #include <arch/byteorder.h> #include <elog.h> #if CONFIG_CHROMEOS @@ -147,12 +146,7 @@ static int smbios_write_type0(unsigned long *current, int handle) #endif
{ - const struct cbfs_header *header; - u32 romsize = CONFIG_ROM_SIZE; - header = cbfs_get_header(CBFS_DEFAULT_MEDIA); - if (header != CBFS_HEADER_INVALID_ADDRESS) - romsize = ntohl(header->romsize); - t->bios_rom_size = (romsize / 65535) - 1; + t->bios_rom_size = (CONFIG_ROM_SIZE / 65535) - 1; }
t->system_bios_major_release = 4; diff --git a/src/arch/x86/lib/rom_media.c b/src/arch/x86/lib/rom_media.c index ed2122c..aa17d64 100644 --- a/src/arch/x86/lib/rom_media.c +++ b/src/arch/x86/lib/rom_media.c @@ -18,84 +18,64 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301 USA */ -#include <cbfs.h> +#include <stdint.h> #include <string.h> +#include <console/console.h> +#include <region.h>
-#ifdef LIBPAYLOAD -# define printk(x...) -# define init_default_cbfs_media libpayload_init_default_cbfs_media - extern int libpayload_init_default_cbfs_media(struct cbfs_media *media); -#else -# include <console/console.h> -#endif - -// Implementation of memory-mapped ROM media source on X86. - -static int x86_rom_open(struct cbfs_media *media) { - return 0; +static inline void *get_pointer(offset_t off) +{ + const uintptr_t base_address = (uintptr_t)-CONFIG_ROM_SIZE; + return (void *)(base_address + off); }
-static void *x86_rom_map(struct cbfs_media *media, size_t offset, size_t count) { - void *ptr; - // Some address (ex, pointer to master header) may be given in memory - // mapped location. To workaround that, we handle >0xf0000000 as real - // memory pointer. - - if ((uint32_t)offset > (uint32_t)0xf0000000) - ptr = (void*)offset; - else - ptr = (void*)(0 - (uint32_t)media->context + offset); - return ptr; +static void * +boot_mmap(const struct region_device *rdev, offset_t off, size_t size) +{ + return get_pointer(off); }
-static void *x86_rom_unmap(struct cbfs_media *media, const void *address) { - return NULL; +static void boot_munmap(const struct region_device *rdev, void *addr) +{ }
-static size_t x86_rom_read(struct cbfs_media *media, void *dest, size_t offset, - size_t count) { - void *ptr = x86_rom_map(media, offset, count); - memcpy(dest, ptr, count); - x86_rom_unmap(media, ptr); - return count; +static ssize_t +boot_readat(const struct region_device *rdev, void *buf, + offset_t off, size_t size) +{ + memcpy(buf, get_pointer(off), size); + return size; }
-static int x86_rom_close(struct cbfs_media *media) { - return 0; -} +static const struct region_device_ops boot_dev_ops = { + .mmap = boot_mmap, + .munmap = boot_munmap, + .readat = boot_readat, +}; + +static const struct region_device boot_dev = { + .ops = &boot_dev_ops, + .region = { + .offset = 0, + .size = CONFIG_ROM_SIZE, + }, +};
-int init_x86rom_cbfs_media(struct cbfs_media *media); -int init_x86rom_cbfs_media(struct cbfs_media *media) { - // On X86, we always keep a reference of pointer to CBFS header in - // 0xfffffffc, and the pointer is still a memory-mapped address. - // Since the CBFS core always use ROM offset, we need to figure out - // header->romsize even before media is initialized. - struct cbfs_header *header = (struct cbfs_header*) - *(uint32_t*)(0xfffffffc); - if (CBFS_HEADER_MAGIC != ntohl(header->magic)) { -#if defined(CONFIG_ROM_SIZE) - printk(BIOS_ERR, "Invalid CBFS master header at %p\n", header); - media->context = (void*)CONFIG_ROM_SIZE; -#else - return -1; -#endif - } else { - uint32_t romsize = ntohl(header->romsize); - media->context = (void*)romsize; -#if defined(CONFIG_ROM_SIZE) - if (CONFIG_ROM_SIZE != romsize) - printk(BIOS_INFO, "Warning: rom size unmatch (%d/%d)\n", - CONFIG_ROM_SIZE, romsize); -#endif +void boot_media_initialize(void) +{ + rhandle_t rh; + const char *bootro_name = BOOTRO_REGION_NAME; + + rh = region_locate(bootro_name); + + /* Already registered. */ + if (rhandle_valid(rh)) { + printk(BIOS_DEBUG, "Already Registered: '%s'\n", bootro_name); + return; } - media->open = x86_rom_open; - media->close = x86_rom_close; - media->map = x86_rom_map; - media->unmap = x86_rom_unmap; - media->read = x86_rom_read; - return 0; -}
-int init_default_cbfs_media(struct cbfs_media *media) { - return init_x86rom_cbfs_media(media); + rh = region_register(bootro_name, &boot_dev); + + if (!rhandle_valid(rh)) + printk(BIOS_ERR, "Failed to register '%s'.\n", bootro_name); } diff --git a/src/cpu/intel/microcode/microcode.c b/src/cpu/intel/microcode/microcode.c index 947dfd7..d6753c1 100644 --- a/src/cpu/intel/microcode/microcode.c +++ b/src/cpu/intel/microcode/microcode.c @@ -107,28 +107,48 @@ void intel_microcode_load_unlocked(const void *microcode_patch) #endif }
+#if !defined(__ROMCC__) +static void *microcode_locate(u32 *len) +{ + const struct cbfs_file_descriptor *fd; + void *buf; + + fd = cbfs_open_by_name(cbfs_default_descriptor(), MICROCODE_CBFS_FILE); + if (fd == NULL) + return NULL; + + /* + * The following sequence relies on x86 having a persistent + * memory-mapped view of the SPI rom. + */ + *len = cbfs_file_content_size(fd); + buf = cbfs_file_map_content(fd, 0, *len); + cbfs_file_close(fd); + + return buf; +} +#endif + const void *intel_microcode_find(void) { - struct cbfs_file *microcode_file; const struct microcode *ucode_updates; u32 eax, microcode_len; u32 pf, rev, sig, update_size; unsigned int x86_model, x86_family; msr_t msr; +#if defined(__ROMCC__) + struct cbfs_file *file;
-#ifdef __PRE_RAM__ - microcode_file = walkcbfs_head((char *) MICROCODE_CBFS_FILE); + file = walkcbfs_head((char *) MICROCODE_CBFS_FILE); + microcode_len = ntohl(file->len); + ucode_updates = CBFS_SUBHEADER(file); #else - microcode_file = cbfs_get_file(CBFS_DEFAULT_MEDIA, - MICROCODE_CBFS_FILE); + ucode_updates = microcode_locate(µcode_len); #endif
- if (!microcode_file) + if (!ucode_updates) return NULL;
- ucode_updates = CBFS_SUBHEADER(microcode_file); - microcode_len = ntohl(microcode_file->len); - /* CPUID sets MSR 0x8B iff a microcode update has been loaded. */ msr.lo = 0; msr.hi = 0; diff --git a/src/cpu/x86/mirror_payload.c b/src/cpu/x86/mirror_payload.c index edd2641..a6975e1 100644 --- a/src/cpu/x86/mirror_payload.c +++ b/src/cpu/x86/mirror_payload.c @@ -30,26 +30,12 @@ void mirror_payload(struct payload *payload) size_t size; char *src; uintptr_t alignment_diff; - const unsigned long cacheline_size = 64; - const uintptr_t intra_cacheline_mask = cacheline_size - 1; - const uintptr_t cacheline_mask = ~intra_cacheline_mask; + const unsigned long dword_size = sizeof(uint32_t); + const uintptr_t dword_mask = dword_size - 1;
src = payload->backing_store.data; size = payload->backing_store.size;
- /* - * Adjust size so that the start and end points are aligned to a - * cacheline. The SPI hardware controllers on Intel machines should - * cache full length cachelines as well as prefetch data. Once the - * data is mirrored in memory all accesses should hit the CPU's cache. - */ - alignment_diff = (intra_cacheline_mask & (uintptr_t)src); - size += alignment_diff; - - size = ALIGN(size, cacheline_size); - - printk(BIOS_DEBUG, "Payload aligned size: 0x%zx\n", size); - buffer = bootmem_allocate_buffer(size);
if (buffer == NULL) { @@ -57,7 +43,18 @@ void mirror_payload(struct payload *payload) return; }
- src = (void *)(cacheline_mask & (uintptr_t)src); + /* + * Do copies in 32-bit aligned reads from backing source. + * 1. Do a copy to align the source address. + * 2. Do remaining copy. + */ + alignment_diff = dword_mask & (uintptr_t)src; + if (alignment_diff != 0) { + mempcy(buffer, src, alignment_diff); + src += alignment_diff; + buffer += alignment_diff; + size -= alignment_diff; + }
/* * Note that if mempcy is not using 32-bit moves the performance will @@ -66,6 +63,9 @@ void mirror_payload(struct payload *payload) */ memcpy(buffer, src, size);
- /* Update the payload's backing store. */ - payload->backing_store.data = &buffer[alignment_diff]; + /* + * Update the payload's backing store. May leak resources depending + * cbfs stroage source. + */ + payload->backing_store.data = buffer; } diff --git a/src/device/pci_device.c b/src/device/pci_device.c index aa0d954..bcdbbe4 100644 --- a/src/device/pci_device.c +++ b/src/device/pci_device.c @@ -667,7 +667,7 @@ int oprom_is_loaded = 0; void pci_dev_init(struct device *dev) { #if CONFIG_VGA_ROM_RUN - struct rom_header *rom, *ram; + struct rom_header *ram;
/* Only execute VGA ROMs. */ if (((dev->class >> 8) != PCI_CLASS_DISPLAY_VGA)) @@ -694,11 +694,8 @@ void pci_dev_init(struct device *dev) return; #endif
- rom = pci_rom_probe(dev); - if (rom == NULL) - return; + ram = pci_load_vga_rom(dev);
- ram = pci_rom_load(dev, rom); if (ram == NULL) return;
diff --git a/src/device/pci_rom.c b/src/device/pci_rom.c index 1bdccf0..bb17931 100644 --- a/src/device/pci_rom.c +++ b/src/device/pci_rom.c @@ -6,6 +6,7 @@ * (Written by Yinghai Lu yhlu@tyan.com for Tyan) * Copyright (C) 2005 Ronald G. Minnich rminnich@gmail.com * Copyright (C) 2005-2007 Stefan Reinauer stepan@openbios.org + * Copyright (C) 2014 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 @@ -28,62 +29,15 @@ #include <device/pci_ops.h> #include <string.h> #include <cbfs.h> +#include <cbfs_core.h>
-struct rom_header *pci_rom_probe(struct device *dev) +static struct rom_header * +pci_rom_verify_and_load(struct device *dev, struct rom_header *rom_header, + void *load_addr, int check_didvid) { - struct rom_header *rom_header; struct pci_data *rom_data; - - /* If it's in FLASH, then don't check device for ROM. */ - rom_header = cbfs_load_optionrom(CBFS_DEFAULT_MEDIA, dev->vendor, - dev->device, NULL); - - u32 vendev = (dev->vendor << 16) | dev->device; - u32 mapped_vendev = vendev; - - if (map_oprom_vendev) - mapped_vendev = map_oprom_vendev(vendev); - - if (!rom_header) { - if (vendev != mapped_vendev) { - rom_header = cbfs_load_optionrom( - CBFS_DEFAULT_MEDIA, - mapped_vendev >> 16, - mapped_vendev & 0xffff, NULL); - } - } - - if (rom_header) { - printk(BIOS_DEBUG, "In CBFS, ROM address for %s = %p\n", - dev_path(dev), rom_header); - } else { - u32 rom_address; - - rom_address = pci_read_config32(dev, PCI_ROM_ADDRESS); - - if (rom_address == 0x00000000 || rom_address == 0xffffffff) { -#if CONFIG_BOARD_EMULATION_QEMU_X86 - if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) - rom_address = 0xc0000; - else -#endif - return NULL; - } else { - /* Enable expansion ROM address decoding. */ - pci_write_config32(dev, PCI_ROM_ADDRESS, - rom_address|PCI_ROM_ADDRESS_ENABLE); - } - -#if CONFIG_ON_DEVICE_ROM_RUN - printk(BIOS_DEBUG, "Option ROM address for %s = %lx\n", - dev_path(dev), (unsigned long)rom_address); - rom_header = (struct rom_header *)rom_address; -#else - printk(BIOS_DEBUG, "Option ROM execution disabled " - "for %s\n", dev_path(dev)); - return NULL; -#endif - } + unsigned int rom_size; + unsigned int image_size;
printk(BIOS_SPEW, "PCI expansion ROM, signature 0x%04x, " "INIT size 0x%04x, data ptr 0x%04x\n", @@ -100,10 +54,11 @@ struct rom_header *pci_rom_probe(struct device *dev)
printk(BIOS_SPEW, "PCI ROM image, vendor ID %04x, device ID %04x,\n", rom_data->vendor, rom_data->device); + /* If the device id is mapped, a mismatch is expected */ - if ((dev->vendor != rom_data->vendor - || dev->device != rom_data->device) - && (vendev == mapped_vendev)) { + if (check_didvid != 0 && + (dev->vendor != rom_data->vendor || + dev->device != rom_data->device)) { printk(BIOS_ERR, "ID mismatch: vendor ID %04x, " "device ID %04x\n", rom_data->vendor, rom_data->device); return NULL; @@ -117,29 +72,16 @@ struct rom_header *pci_rom_probe(struct device *dev) printk(BIOS_DEBUG, "Class Code mismatch ROM %08x, dev %08x\n", (rom_data->class_hi << 8) | rom_data->class_lo, dev->class); - // return NULL; }
- return rom_header; -} - -static void *pci_ram_image_start = (void *)PCI_RAM_IMAGE_START; - -struct rom_header *pci_rom_load(struct device *dev, - struct rom_header *rom_header) -{ - struct pci_data * rom_data; - unsigned int rom_size; - unsigned int image_size=0; - + image_size = 0; do { /* Get next image. */ - rom_header = (struct rom_header *)((void *) rom_header - + image_size); - - rom_data = (struct pci_data *)((void *) rom_header - + le32_to_cpu(rom_header->data)); + int data_offset;
+ rom_header = (void *)((char *)rom_header + image_size); + data_offset = le32_to_cpu(rom_header->data); + rom_data = (void *)((char *)rom_header + data_offset); image_size = le32_to_cpu(rom_data->ilen) * 512; } while ((rom_data->type != 0) && (rom_data->indicator != 0)); // make sure we got x86 version
@@ -148,30 +90,128 @@ struct rom_header *pci_rom_load(struct device *dev,
rom_size = rom_header->size * 512;
- /* - * We check to see if the device thinks it is a VGA device not - * whether the ROM image is for a VGA device because some - * devices have a mismatch between the hardware and the ROM. - */ - if (PCI_CLASS_DISPLAY_VGA == (dev->class >> 8)) { + if (load_addr != (void *)rom_header) { + printk(BIOS_DEBUG, "Copying ROM from %p to %p, 0x%x bytes.\n", + rom_header, load_addr, rom_size); + memcpy(load_addr, rom_header, rom_size); + } + + return load_addr; +} + +static inline int tohex4(unsigned int c) +{ + return (c <= 9) ? (c + '0') : (c - 10 + 'a'); +} + +static void tohex16(uint16_t 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); +} + +static const struct cbfs_file_descriptor * +cbfs_open_optionrom(uint16_t vendor, uint16_t device) +{ + char name[17] = "pciXXXX,XXXX.rom"; + + tohex16(vendor, name+3); + tohex16(device, name+8); + + return cbfs_open_by_name_type(cbfs_default_descriptor(), name, + CBFS_TYPE_OPTIONROM); +} + +static void *cbfs_probe_and_load(struct device *dev, void *dest) +{ + const struct cbfs_file_descriptor *file; + uint32_t vendev; + int remapped; + struct rom_header *hdr; + void *load_addr; + ssize_t filesz; + + load_addr = NULL; + remapped = 0; + file = cbfs_open_optionrom(dev->vendor, dev->device); + + if (file == NULL && map_oprom_vendev != NULL) { + vendev = map_oprom_vendev((dev->vendor << 16) | dev->device); + file = cbfs_open_optionrom(vendev >> 16, vendev & 0xffff); + remapped = 1; + } + + if (file == NULL) + return NULL; + + filesz = cbfs_file_content_size(file); + hdr = cbfs_file_map_content(file, 0, filesz); + if (hdr == NULL) + goto out; + + printk(BIOS_DEBUG, "In CBFS, ROM address for %s = %p\n", + dev_path(dev), hdr); + + load_addr = pci_rom_verify_and_load(dev, hdr, dest, !remapped); + cbfs_file_unmap(file, hdr); + +out: + cbfs_file_close(file); + return load_addr; +} + +#if CONFIG_ON_DEVICE_ROM_RUN +static void *dev_probe_and_load(struct device *dev, void *dest) +{ + uint32_t rom_address; + struct rom_header *rom_header; + + rom_address = pci_read_config32(dev, PCI_ROM_ADDRESS); + + if (rom_address == 0x00000000 || rom_address == 0xffffffff) { + if (IS_ENABLED(CONFIG_BOARD_EMULATION_QEMU_X86)) + rom_address = 0xc0000; + else + return NULL; + } else { + /* Enable expansion ROM address decoding. */ + pci_write_config32(dev, PCI_ROM_ADDRESS, + rom_address|PCI_ROM_ADDRESS_ENABLE); + } + printk(BIOS_DEBUG, "Option ROM address for %s = %lx\n", + dev_path(dev), (unsigned long)rom_address); + + rom_header = (struct rom_header *)rom_address; + + return pci_rom_verify_and_load(dev, rom_header, dest, 1); +} +#endif + +struct rom_header *pci_load_vga_rom(struct device *dev) +{ + void *vga_load_addr = (void *)PCI_VGA_RAM_IMAGE_START; + void *load_addr; + + if (PCI_CLASS_DISPLAY_VGA != (dev->class >> 8)) + return NULL; + #if !CONFIG_MULTIPLE_VGA_ADAPTERS - extern device_t vga_pri; /* Primary VGA device (device.c). */ - if (dev != vga_pri) return NULL; /* Only one VGA supported. */ + extern device_t vga_pri; /* Primary VGA device (device.c). */ + if (dev != vga_pri) return NULL; /* Only one VGA supported. */ #endif - if ((void *)PCI_VGA_RAM_IMAGE_START != rom_header) { - printk(BIOS_DEBUG, "Copying VGA ROM Image from %p to " - "0x%x, 0x%x bytes\n", rom_header, - PCI_VGA_RAM_IMAGE_START, rom_size); - memcpy((void *)PCI_VGA_RAM_IMAGE_START, rom_header, - rom_size); - } - return (struct rom_header *) (PCI_VGA_RAM_IMAGE_START); - }
- printk(BIOS_DEBUG, "Copying non-VGA ROM image from %p to %p, 0x%x " - "bytes\n", rom_header, pci_ram_image_start, rom_size); + load_addr = cbfs_probe_and_load(dev, vga_load_addr); + + if (load_addr == NULL) { +#if CONFIG_ON_DEVICE_ROM_RUN + load_addr = dev_probe_and_load(dev, vga_load_addr); +#else + printk(BIOS_DEBUG, "Option ROM execution disabled for %s\n", + dev_path(dev)); +#endif + }
- memcpy(pci_ram_image_start, rom_header, rom_size); - pci_ram_image_start += rom_size; - return (struct rom_header *) (pci_ram_image_start-rom_size); + return load_addr; } diff --git a/src/drivers/elog/elog.c b/src/drivers/elog/elog.c index de34928..597c32e 100644 --- a/src/drivers/elog/elog.c +++ b/src/drivers/elog/elog.c @@ -22,10 +22,9 @@ #include <console/console.h> #include <pc80/mc146818rtc.h> #include <smbios.h> -#include <spi-generic.h> -#include <spi_flash.h> #include <stdint.h> #include <string.h> +#include <region.h> #include <elog.h> #include "elog_internal.h"
@@ -54,13 +53,15 @@ #define elog_debug(STR...) #endif
+ +#define ELOG_REGION_NAME "elog" + /* * Static variables for ELOG state */ static struct elog_area *elog_area; static u16 total_size; static u16 log_size; -static u32 flash_base;
static elog_area_state area_state; static elog_header_state header_state; @@ -70,51 +71,10 @@ static u16 next_event_offset; static u16 event_count;
static int elog_initialized; -static struct spi_flash *elog_spi; - - -static inline u32 get_rom_size(void) -{ - u32 rom_size; - - /* Assume the used space of the ROM image starts from 0. The - * physical size of the device may not be completely used. */ - rom_size = elog_spi->size; - if (rom_size > CONFIG_ROM_SIZE) - rom_size = CONFIG_ROM_SIZE; - - return rom_size; -} - -/* - * Convert a memory mapped flash address into a flash offset - */ -static inline u32 elog_flash_address_to_offset(u8 *address) -{ - u32 rom_size; - - if (!elog_spi) - return 0;
- rom_size = get_rom_size(); - - return (u32)address - ((u32)~0UL - rom_size + 1); -} - -/* - * Convert a flash offset into a memory mapped flash address - */ -static inline u8* elog_flash_offset_to_address(u32 offset) -{ - u32 rom_size; - - if (!elog_spi) - return NULL; - - rom_size = get_rom_size(); - - return (u8*)((u32)~0UL - rom_size + 1 + offset); -} +/* Region device for accessing the ELOG area. */ +static struct region_device elog_dev; +static rhandle_t elog_rh = INVALID_RHANDLE;
/* * Pointer to an event log header in the event data area @@ -259,17 +219,16 @@ static void elog_flash_write(void *address, u32 size) { u32 offset;
- if (!address || !size || !elog_spi) + if (!address || !size) return;
- offset = flash_base; - offset += (u8 *)address - (u8 *)elog_area; + offset = (u8 *)address - (u8 *)elog_area;
elog_debug("elog_flash_write(address=0x%p offset=0x%08x size=%u)\n", address, offset, size);
/* Write the data to flash */ - elog_spi->write(elog_spi, offset, size, address); + region_writeat(elog_rh, address, offset, size); }
/* @@ -280,17 +239,16 @@ static void elog_flash_erase(void *address, u32 size) { u32 offset;
- if (!address || !size || !elog_spi) + if (!address || !size) return;
- offset = flash_base; - offset += (u8 *)address - (u8*)elog_area; + offset = (u8 *)address - (u8*)elog_area;
elog_debug("elog_flash_erase(address=0x%p offset=0x%08x size=%u)\n", address, offset, size);
/* Erase the sectors in this region */ - elog_spi->erase(elog_spi, offset, size); + region_eraseat(elog_rh, offset, size); }
/* @@ -348,7 +306,7 @@ static void elog_scan_flash(void) event_buffer_state = ELOG_EVENT_BUFFER_OK;
/* Fill memory buffer by reading from SPI */ - elog_spi->read(elog_spi, flash_base, total_size, elog_area); + region_readat(elog_rh, elog_area, 0, total_size);
next_event_offset = 0; event_count = 0; @@ -449,13 +407,17 @@ int elog_smbios_write_type15(unsigned long *current, int handle) { struct smbios_type15 *t = (struct smbios_type15 *)*current; int len = sizeof(struct smbios_type15); + void *backing_store;
#if CONFIG_ELOG_CBMEM /* Save event log buffer into CBMEM for the OS to read */ - void *cbmem = cbmem_add(CBMEM_ID_ELOG, total_size); - if (!cbmem) + backing_store = cbmem_add(CBMEM_ID_ELOG, total_size); + if (!backing_store) return 0; - memcpy(cbmem, elog_area, total_size); + memcpy(backing_store, elog_area, total_size); +#else + /* This assumes x86 memory-mapped capabilities of flash. */ + backing_store = region_mmap(elog_rh, 0, total_size); #endif
memset(t, 0, len); @@ -468,11 +430,7 @@ int elog_smbios_write_type15(unsigned long *current, int handle) t->access_method = SMBIOS_EVENTLOG_ACCESS_METHOD_MMIO32; t->log_status = SMBIOS_EVENTLOG_STATUS_VALID; t->change_token = 0; -#if CONFIG_ELOG_CBMEM - t->address = (u32)cbmem; -#else - t->address = (u32)elog_flash_offset_to_address(flash_base); -#endif + t->address = (u32)backing_store; t->header_format = ELOG_HEADER_TYPE_OEM; t->log_type_descriptors = 0; t->log_type_descriptor_length = 2; @@ -506,33 +464,46 @@ int elog_clear(void) return 0; }
-static void elog_find_flash(void) +static int elog_find_flash(void) { -#if CONFIG_CHROMEOS - u8 *flash_base_ptr; -#endif + rhandle_t rh; + + if (rhandle_valid(elog_rh)) + return 0;
elog_debug("elog_find_flash()\n");
#if CONFIG_CHROMEOS /* Find the ELOG base and size in FMAP */ - total_size = find_fmap_entry("RW_ELOG", (void **)&flash_base_ptr); - if (total_size < 0) { + if (fmap_locate("RW_ELOG", &elog_dev.region.offset, + &elog_dev.region.size)) { printk(BIOS_WARNING, "ELOG: Unable to find RW_ELOG in FMAP, " "using CONFIG_ELOG_FLASH_BASE instead\n"); - total_size = CONFIG_ELOG_AREA_SIZE; } else { - flash_base = elog_flash_address_to_offset(flash_base_ptr); - /* Use configured size if smaller than FMAP size */ - if (total_size > CONFIG_ELOG_AREA_SIZE) - total_size = CONFIG_ELOG_AREA_SIZE; + if (elog_dev.region.size > CONFIG_ELOG_AREA_SIZE) + elog_dev.region.size = CONFIG_ELOG_AREA_SIZE; } #else - flash_base = CONFIG_ELOG_FLASH_BASE; - total_size = CONFIG_ELOG_AREA_SIZE; + elog_dev.region.offset = CONFIG_ELOG_FLASH_BASE; + elog_dev.region.size = CONFIG_ELOG_AREA_SIZE; #endif + total_size = elog_dev.region.size; log_size = total_size - sizeof(struct elog_header); + + rh = region_locate(BOOTRW_REGION_NAME); + if (!rhandle_valid(rh)) { + printk(BIOS_WARNING, "Unable to find writable boot region.\n"); + return -1; + } + rh = region_register_subregion("elog", &elog_dev, rh); + if (!rhandle_valid(rh)) { + printk(BIOS_WARNING, "Unable to register ELOG region.\n"); + return -1; + } + + elog_rh = rh; + return 0; }
/* @@ -545,18 +516,9 @@ int elog_init(void)
elog_debug("elog_init()\n");
- /* Prepare SPI */ - spi_init(); - elog_spi = spi_flash_probe(0, 0, 0, 0); - if (!elog_spi) { - printk(BIOS_ERR, "ELOG: Unable to find SPI flash\n"); - return -1; - } - /* Set up the backing store */ - elog_find_flash(); - if (flash_base == 0) { - printk(BIOS_ERR, "ELOG: Invalid flash base\n"); + if (elog_find_flash()) { + printk(BIOS_ERR, "ELOG: region not set up.\n"); return -1; }
@@ -589,8 +551,8 @@ int elog_init(void)
elog_initialized = 1;
- printk(BIOS_INFO, "ELOG: FLASH @0x%p [SPI 0x%08x]\n", - elog_area, flash_base); + printk(BIOS_INFO, "ELOG: FLASH @0x%p [SPI 0x%08zx]\n", + elog_area, (size_t)elog_dev.region.offset);
printk(BIOS_INFO, "ELOG: area is %d bytes, full threshold %d," " shrink size %d\n", total_size, diff --git a/src/include/cbfs.h b/src/include/cbfs.h index ebdbf43..bd000c9 100644 --- a/src/include/cbfs.h +++ b/src/include/cbfs.h @@ -1,14 +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) 2014 Google Inc. * - * This file is dual-licensed. You can choose between: - * - The GNU GPL, version 2, as published by the Free Software Foundation - * - The revised BSD license (without advertising clause) - * - * --------------------------------------------------------------------------- * 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 * the Free Software Foundation; version 2 of the License. @@ -20,62 +14,84 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA - * --------------------------------------------------------------------------- - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * --------------------------------------------------------------------------- + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - #ifndef _CBFS_H_ #define _CBFS_H_
-#include <cbfs_core.h> +#include <stdint.h> +#include <region.h>
-void *cbfs_load_optionrom(struct cbfs_media *media, uint16_t vendor, - uint16_t device, void * dest); -void *cbfs_load_stage(struct cbfs_media *media, const char *name); - -/* Simple buffer for streaming media. */ -struct cbfs_simple_buffer { - char *buffer; - size_t allocated; +/* Describe an instance of cbfs. */ +struct cbfs_descriptor { + rhandle_t rh; + size_t align; size_t size; - size_t last_allocate; };
-void *cbfs_simple_buffer_map(struct cbfs_simple_buffer *buffer, - struct cbfs_media *media, - size_t offset, size_t count); +struct cbfs_file_descriptor; + +/* Obtain the default cbfs_descriptor. */ +const struct cbfs_descriptor *cbfs_default_descriptor(void); + +/* Open a file by name. Return NULL on error. */ +const struct cbfs_file_descriptor * +cbfs_open_by_name(const struct cbfs_descriptor *cbfs, const char *name); + +/* Open a file by name and type. */ +const struct cbfs_file_descriptor * +cbfs_open_by_name_type(const struct cbfs_descriptor *cbfs, + const char *name, uint32_t type); + +/* Close a previously opened file. */ +void cbfs_file_close(const struct cbfs_file_descriptor *fd); + +/* Return the size of the file's contents. < 0 on error. */ +ssize_t cbfs_file_content_size(const struct cbfs_file_descriptor *fd); + +/* Return the size of the file's metadata contents. < 0 on error. */ +ssize_t cbfs_file_metadata_size(const struct cbfs_file_descriptor *fd); + +/* + * Read size bytes file contents from offset of into buffer. Returns < 0 + * on error or bytes read. + */ +ssize_t cbfs_file_read_content(const struct cbfs_file_descriptor *fd, + void *buf, offset_t offset, size_t size); + +/* Attempt to map the file content. Returns NULL on error. */ +void *cbfs_file_map_content(const struct cbfs_file_descriptor *fd, + offset_t offset, size_t size); + +/* + * Read size bytes file metadata from offset of into buffer. Returns < 0 + * on error or bytes read. + */ +ssize_t cbfs_file_read_metadata(const struct cbfs_file_descriptor *fd, + void *buf, offset_t offset, size_t size); +/* + * Attempt to map all of the file's metadata. Depending on the file type there + * may be no metadata in which case NULL will be returned. File types of + * CBFS_TYPE_STAGE and CBFS_TYPE_PAYLOAD are currently the only files that + * contain metadata. + * + * Returns NULL on error. + */ +void *cbfs_file_map_metadata(const struct cbfs_file_descriptor *fd); + +/* Unmap the mapping associated with addr. No partial unmappings allowed. */ +void cbfs_file_unmap(const struct cbfs_file_descriptor *fd, void *addr); +
-void *cbfs_simple_buffer_unmap(struct cbfs_simple_buffer *buffer, - const void *address); +void *cbfs_load_optionrom(const struct cbfs_descriptor *cbfs, + uint16_t vendor, uint16_t device, void * dest);
-// Utility functions -int run_address(void *f); +/* Return entry point to stage, or NULL on failure. */ +void *cbfs_load_stage(const struct cbfs_descriptor *cbfs, const char *name);
-/* Defined in individual arch / board implementation. */ -int init_default_cbfs_media(struct cbfs_media *media); +/* Decompress range of file to destination. Returns bytes decompressed. */ +size_t cbfs_file_inflate(const struct cbfs_file_descriptor *fd, + offset_t offset, size_t src_sz, void *dest);
#if defined(__PRE_RAM__) struct romstage_handoff; diff --git a/src/include/cbfs_core.h b/src/include/cbfs_core.h index a1d8127..6a91454 100644 --- a/src/include/cbfs_core.h +++ b/src/include/cbfs_core.h @@ -186,50 +186,6 @@ struct cbfs_optionrom { uint32_t len; } __attribute__((packed));
-#define CBFS_MEDIA_INVALID_MAP_ADDRESS ((void*)(0xffffffff)) -#define CBFS_DEFAULT_MEDIA ((void*)(0x0)) - -/* Media for CBFS to load files. */ -struct cbfs_media { - - /* implementation dependent context, to hold resource references */ - void *context; - - /* opens media and returns 0 on success, -1 on failure */ - int (*open)(struct cbfs_media *media); - - /* returns number of bytes read from media into dest, starting from - * offset for count of bytes */ - size_t (*read)(struct cbfs_media *media, void *dest, size_t offset, - size_t count); - - /* returns a pointer to memory with count of bytes from media source - * starting from offset, or CBFS_MEDIA_INVALID_MAP_ADDRESS on failure. - * Note: mapped data can't be free unless unmap is called, even if you - * do close first. */ - void * (*map)(struct cbfs_media *media, size_t offset, size_t count); - - /* returns NULL and releases the memory by address, which was allocated - * by map */ - void * (*unmap)(struct cbfs_media *media, const void *address); - - /* closes media and returns 0 on success, -1 on failure. */ - int (*close)(struct cbfs_media *media); -}; - -/* returns pointer to a file entry inside CBFS or NULL */ -struct cbfs_file *cbfs_get_file(struct cbfs_media *media, const char *name); - -/* returns pointer to file content inside CBFS after if type is correct */ -void *cbfs_get_file_content(struct cbfs_media *media, const char *name, - int type, size_t *sz); - -/* returns decompressed size on success, 0 on failure */ -int cbfs_decompress(int algo, void *src, void *dst, int len); - -/* returns a pointer to CBFS master header, or CBFS_HEADER_INVALID_ADDRESS - * on failure */ -const struct cbfs_header *cbfs_get_header(struct cbfs_media *media);
#endif /* __ROMCC__ */
diff --git a/src/include/device/pci_rom.h b/src/include/device/pci_rom.h index fe77276..4bd0bfd 100644 --- a/src/include/device/pci_rom.h +++ b/src/include/device/pci_rom.h @@ -6,7 +6,6 @@ #define PCI_ROM_HDR 0xAA55 #define PCI_DATA_HDR (uint32_t) ( ('R' << 24) | ('I' << 16) | ('C' << 8) | 'P' )
-#define PCI_RAM_IMAGE_START 0xD0000 #define PCI_VGA_RAM_IMAGE_START 0xC0000
struct rom_header { @@ -33,8 +32,7 @@ struct pci_data { uint16_t reserved_2; };
-struct rom_header *pci_rom_probe(struct device *dev); -struct rom_header *pci_rom_load(struct device *dev, struct rom_header *rom_header); +struct rom_header *pci_load_vga_rom(struct device *dev); u32 __attribute__((weak)) map_oprom_vendev(u32 vendev);
#endif diff --git a/src/include/payload_loader.h b/src/include/payload_loader.h index 7a3f045..7b6fc1c 100644 --- a/src/include/payload_loader.h +++ b/src/include/payload_loader.h @@ -22,6 +22,8 @@ #include <stdint.h> #include <stddef.h>
+struct cbfs_file_descriptor; + struct buffer_area { void *data; size_t size; @@ -29,6 +31,7 @@ struct buffer_area {
struct payload { const char *name; + const struct cbfs_file_descriptor *fd; struct buffer_area backing_store; /* Used when payload wants memory coreboot ramstage is running at. */ struct buffer_area bounce; diff --git a/src/include/region.h b/src/include/region.h new file mode 100644 index 0000000..f1b99df --- /dev/null +++ b/src/include/region.h @@ -0,0 +1,138 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 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 + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef REGION_H +#define REGION_H + +#include <stdint.h> +#include <stddef.h> + +/* + * Region support. + * + * Regions are intended to abstract away the access mechanisms for blocks of + * data. This could be SPI, eMMC, or a memory region as the backing store. + * Subregions can be made by registering a region_device that is associated + * with another region. Regions can be addressed by name, but there is no + * hierarchy in the namespace. i.e. the operations through subregions are + * hierarchical but the namespace is not. + * + * There are two views to the boot media: read-only and read-write. On certain + * architectures the read-write availability is not available until late in + * ramstage. Therefore, users of each region need to be cognizant of the fact + * that different implementations may be backing a particular region. e.g. on + * x86 the read-only boot region doesn't have write and erase capabilities, but + * the read-write boot region would allow writes and erases but no mmap. + */ + +/* + * Initialize the boot region. Called directly in pre-ramstage or + * automatically in ramstage. + */ +void boot_region_init(void); +/* Initialize the boot media. Board/Arch specific code implements this. */ +void boot_media_initialize(void); +/* + * A potential call for chipsets that can't bring their RW area online til + * after the device init in ramstage. + */ +void bootrw_media_initialize(void); +/* Initialize the default CBFS region. */ +void cbfs_default_media_initialize(void); + +typedef uintptr_t offset_t; +typedef long rhandle_t; +#define INVALID_RHANDLE ((rhandle_t)~((uintptr_t)0)) + +#define BOOTRO_REGION_NAME "bootro" +#define BOOTRW_REGION_NAME "bootrw" +#define DEFAULT_CBFS_REGION_NAME "cbfs" + +struct region_device; + +struct region_device_ops { + void *(*mmap)(const struct region_device *, offset_t, size_t); + void (*munmap)(const struct region_device *, void *); + ssize_t (*readat)(const struct region_device *, void *, + offset_t, size_t); + ssize_t (*writeat)(const struct region_device *, const void *, + offset_t, size_t); + ssize_t (*eraseat)(const struct region_device *, offset_t, size_t); +}; + +struct region { + offset_t offset; + size_t size; +}; + +struct region_device { + const struct region_device_ops *ops; + struct region region; +}; + +/* Return 1 if child is contained within parent else 0. */ +int region_contained(const struct region *parent, const struct region *child); + +static inline int rhandle_valid(rhandle_t rh) +{ + return !!(rh >= 0); +} + +/* Register a region_device by name. */ +rhandle_t region_register(const char *name, const struct region_device *rdev); + +/* Register a subregion by name under parent. rdev->ops must be NULL. */ +rhandle_t region_register_subregion(const char *name, + const struct region_device *rdev, + rhandle_t parent); + +/* Locate a region of provided name. */ +rhandle_t region_locate(const char *name); + +/* + * Read subregion within provided region. + * Returns bytes read, < 0 on error. + */ +ssize_t region_readat(rhandle_t rh, void *buf, offset_t offset, size_t count); + +/* + * Write to subregion within provided region. + * Returns bytes written, < 0 on error. + */ +ssize_t region_writeat(rhandle_t rh, void *buf, offset_t offset, size_t count); + +/* + * Erase subregion within provide region. + * Returns bytes erased, < 0 on error. + */ +ssize_t region_eraseat(rhandle_t rh, offset_t offset, size_t count); + +/* + * Map subregion described by offset and size within the region provided. + * Returns pointer to mapped subregion or NULL on error. + */ +void *region_mmap(rhandle_t rh, offset_t offset, size_t count); + +/* Unmap previously mapped region. */ +void region_munmap(rhandle_t rh, void *addr); + +/* Get size of region represented by rh. < 0 on error. */ +ssize_t region_size(rhandle_t rh); + +#endif /* REGION_H */ diff --git a/src/include/rmodule.h b/src/include/rmodule.h index 2147ab0..19f32e4 100644 --- a/src/include/rmodule.h +++ b/src/include/rmodule.h @@ -61,6 +61,7 @@ int rmodule_calc_region(unsigned int region_alignment, size_t rmodule_size, #if CONFIG_DYNAMIC_CBMEM struct cbfs_stage; struct cbmem_entry; +struct cbfs_descriptor;
struct rmod_stage_load { /* Inputs */ @@ -72,8 +73,10 @@ struct rmod_stage_load { };
/* Both of the following functions return 0 on success, -1 on error. */ -int rmodule_stage_load(struct rmod_stage_load *rsl, struct cbfs_stage *stage); -int rmodule_stage_load_from_cbfs(struct rmod_stage_load *rsl); +int rmodule_stage_load(struct rmod_stage_load *rsl, struct cbfs_stage *stage, + void *data); +int rmodule_stage_load_from_cbfs(const struct cbfs_descriptor *cbfs, + struct rmod_stage_load *rsl); #endif
struct rmodule { diff --git a/src/lib/Makefile.inc b/src/lib/Makefile.inc index acd334e..052dc9f 100644 --- a/src/lib/Makefile.inc +++ b/src/lib/Makefile.inc @@ -18,7 +18,9 @@ # subdirs-y += loaders
+ifneq ($(CONFIG_ARCH_X86),y) bootblock-y += cbfs.c +endif ifneq ($(CONFIG_HAVE_ARCH_MEMSET),y) bootblock-y += memset.c endif @@ -109,6 +111,13 @@ ramstage-y += cbmem_info.c
ramstage-y += hexdump.c romstage-y += hexdump.c +ramstage-y += region.c +romstage-y += region.c +ramstage-y += cbfs_boot_region.c +romstage-y += cbfs_boot_region.c +smm-y += cbfs_boot_region.c +ramstage-$(CONFIG_BOOT_REGION_SPI_FLASH) += bootrw_spi_flash_region.c +smm-$(CONFIG_BOOT_REGION_SPI_FLASH) += bootrw_spi_flash_region.c
ramstage-$(CONFIG_REG_SCRIPT) += reg_script.c
@@ -125,6 +134,7 @@ smm-y += memmove.c endif smm-y += cbfs.c memcmp.c smm-y += gcc.c +smm-y += region.c
$(obj)/lib/version.ramstage.o : $(obj)/build.h
diff --git a/src/lib/bootrw_spi_flash_region.c b/src/lib/bootrw_spi_flash_region.c new file mode 100644 index 0000000..80b1cd1 --- /dev/null +++ b/src/lib/bootrw_spi_flash_region.c @@ -0,0 +1,107 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 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 + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include <stddef.h> +#include <region.h> +#include <spi-generic.h> +#include <spi_flash.h> + +struct bootrw_flash_device { + struct spi_flash *spi; + struct region_device dev; +}; + + +static ssize_t +bootrw_readat(const struct region_device *rdev, void *buf, + offset_t offset, size_t size) +{ + struct bootrw_flash_device *dev; + + dev = container_of(rdev, struct bootrw_flash_device, dev); + if (dev->spi->read(dev->spi, offset, size, buf)) + return -1; + return size; +} + +static ssize_t +bootrw_writeat(const struct region_device *rdev, const void *buf, + offset_t offset, size_t size) +{ + struct bootrw_flash_device *dev; + + dev = container_of(rdev, struct bootrw_flash_device, dev); + if (dev->spi->write(dev->spi, offset, size, buf)) + return -1; + return size; +} + +static ssize_t +bootrw_eraseat(const struct region_device *rdev, offset_t offset, size_t size) +{ + struct bootrw_flash_device *dev; + + dev = container_of(rdev, struct bootrw_flash_device, dev); + if (dev->spi->erase(dev->spi, offset, size)) + return -1; + return size; +} + +static const struct region_device_ops bootrw_ops = { + .readat = bootrw_readat, + .writeat = bootrw_writeat, + .eraseat = bootrw_eraseat, +}; + +static struct bootrw_flash_device flashdev = { + .dev = { + .ops = &bootrw_ops, + .region = { + .offset = 0, + .size = CONFIG_ROM_SIZE, + }, + } +}; + +void bootrw_media_initialize(void) +{ + /* Reinitialize spi regs in case. */ + spi_init(); + + if (flashdev.spi != NULL) + return; + + flashdev.spi = spi_flash_probe(0, 0, 0, 0); + if (flashdev.spi == NULL) { + printk(BIOS_WARNING, "SPI probing failed for boot region.\n"); + return; + } + + if (flashdev.spi->size != CONFIG_ROM_SIZE) { + printk(BIOS_WARNING, + "SPI mismatch with CONFIG_ROM_SIZE: %zx vs %zx\n", + (size_t)CONFIG_ROM_SIZE, (size_t)flashdev.spi->size); + return; + } + + if (!rhandle_valid(region_register(BOOTRW_REGION_NAME, &flashdev.dev))) + printk(BIOS_WARNING, "Couldn't register '%s' region.\n", + BOOTRW_REGION_NAME); +} diff --git a/src/lib/cbfs.c b/src/lib/cbfs.c index dc08937..04f4034 100644 --- a/src/lib/cbfs.c +++ b/src/lib/cbfs.c @@ -1,8 +1,7 @@ /* * 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) 2014-2014 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 +17,390 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA */
-#ifdef LIBPAYLOAD -# include <libpayload-config.h> -# ifdef CONFIG_LZMA -# include <lzma.h> -# define CBFS_CORE_WITH_LZMA -# endif -# define CBFS_MINI_BUILD -#elif defined(__SMM__) -# define CBFS_MINI_BUILD -#elif defined(__BOOT_BLOCK__) - /* No LZMA in boot block. */ -#elif defined(__PRE_RAM__) && !CONFIG_COMPRESS_RAMSTAGE - /* No LZMA in romstage if ramstage is not compressed. */ -#else -# define CBFS_CORE_WITH_LZMA -# include <lib.h> +#if !defined(__BOOT_BLOCK__) && !(defined(__PRE_RAM__) && !IS_ENABLED(CONFIG_COMPRESS_RAMSTAGE)) +#define CBFS_WITH_LZMA #endif
-#include <cbfs.h> #include <string.h> +#include <arch/early_variables.h> +#include <cbfs.h> +#include <cbfs_core.h> #include <cbmem.h> +#include <console/console.h> +#include <lib.h>
-#ifdef LIBPAYLOAD -# include <stdio.h> -# define DEBUG(x...) -# define LOG(x...) printf(x) -# define ERROR(x...) printf(x) -#else -# include <console/console.h> -# define ERROR(x...) printk(BIOS_ERR, "CBFS: " x) -# define LOG(x...) printk(BIOS_INFO, "CBFS: " x) -# if CONFIG_DEBUG_CBFS -# define DEBUG(x...) printk(BIOS_SPEW, "CBFS: " x) -# else -# define DEBUG(x...) -# endif -#endif +struct cbfs_file_descriptor { + rhandle_t rh; + struct region metadata; + struct region content; +};
-#if defined(CONFIG_CBFS_HEADER_ROM_OFFSET) && (CONFIG_CBFS_HEADER_ROM_OFFSET) -# define CBFS_HEADER_ROM_ADDRESS (CONFIG_CBFS_HEADER_ROM_OFFSET) -#else -// Indirect address: only works on 32bit top-aligned systems. -# define CBFS_HEADER_ROM_ADDRESS (*(uint32_t *)0xfffffffc) -#endif +#define MAX_CBFS_OPEN_FILES 4 +struct cbfs_file_cache { + struct cbfs_file_descriptor files[MAX_CBFS_OPEN_FILES]; +};
-#include "cbfs_core.c" +static struct cbfs_file_cache g_cbfs_files CAR_GLOBAL;
-#ifndef __SMM__ -static inline int tohex4(unsigned int c) +static struct cbfs_file_cache *get_cache(void) { - return (c <= 9) ? (c + '0') : (c - 10 + 'a'); + return car_get_var_ptr(&g_cbfs_files); }
-static void tohex16(unsigned int val, char* dest) +static int cbfs_fd_available(const struct cbfs_file_descriptor *fd) { - dest[0] = tohex4(val>>12); - dest[1] = tohex4((val>>8) & 0xf); - dest[2] = tohex4((val>>4) & 0xf); - dest[3] = tohex4(val & 0xf); + return !!(fd->content.size == 0 && fd->metadata.size == 0); }
-void *cbfs_load_optionrom(struct cbfs_media *media, uint16_t vendor, - uint16_t device, void *dest) +static void cbfs_fd_mark_free(const struct cbfs_file_descriptor *cfd) { - char name[17] = "pciXXXX,XXXX.rom"; - struct cbfs_optionrom *orom; - uint8_t *src; + struct cbfs_file_descriptor *fd = (struct cbfs_file_descriptor *)cfd; + memset(fd, 0, sizeof(*fd)); + fd->rh = INVALID_RHANDLE; +}
- tohex16(vendor, name+3); - tohex16(device, name+8); +static struct cbfs_file_descriptor *get_available_file(void) +{ + int i; + struct cbfs_file_cache *cache = get_cache();
- orom = (struct cbfs_optionrom *) - cbfs_get_file_content(media, name, CBFS_TYPE_OPTIONROM, NULL); + for (i = 0; i < MAX_CBFS_OPEN_FILES; i++) { + if (cbfs_fd_available(&cache->files[i])) + return &cache->files[i]; + } + + return NULL; +}
- if (orom == NULL) +static void cbfs_fd_add_metadata(struct cbfs_file_descriptor *fd, size_t sz) +{ + fd->metadata.size += sz; + fd->content.offset += sz; + fd->content.size -= sz; +} + +static const struct cbfs_file_descriptor * +allocate_file(const struct cbfs_descriptor *cbfs, const struct cbfs_file *file, + offset_t offset) +{ + struct cbfs_file_descriptor *fd; + + fd = get_available_file(); + + if (fd == NULL) return NULL;
- /* 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. - */ + fd->rh = cbfs->rh; + fd->metadata.offset = file->offset + offset; + fd->metadata.size = 0; + fd->content.offset = file->offset + offset; + fd->content.size = file->len;
- /* BUG: the cbfstool is (not yet) including a cbfs_optionrom header */ - src = (uint8_t *)orom; // + sizeof(struct cbfs_optionrom); + if (file->type != CBFS_TYPE_STAGE || file->type != CBFS_TYPE_PAYLOAD) + return fd;
- if (! dest) - return src; + if (file->type == CBFS_TYPE_STAGE) { + cbfs_fd_add_metadata(fd, sizeof(struct cbfs_stage)); + return fd; + }
- if (!cbfs_decompress(ntohl(orom->compression), - src, - dest, - ntohl(orom->len))) + /* Parse payload segments. */ + while (offset + sizeof(struct cbfs_payload_segment) <= file->len) { + struct cbfs_payload_segment segment; + ssize_t read_sz; + + read_sz = region_readat(cbfs->rh, &segment, offset, + sizeof(segment)); + if (read_sz != sizeof(segment)) { + cbfs_fd_mark_free(fd); + return NULL; + } + + switch (segment.type) { + case PAYLOAD_SEGMENT_CODE: + case PAYLOAD_SEGMENT_DATA: + case PAYLOAD_SEGMENT_BSS: + case PAYLOAD_SEGMENT_PARAMS: + cbfs_fd_add_metadata(fd, sizeof(segment)); + break; + case PAYLOAD_SEGMENT_ENTRY: + cbfs_fd_add_metadata(fd, sizeof(segment)); + return fd; + default: + cbfs_fd_mark_free(fd); + return NULL; + } + } + + cbfs_fd_mark_free(fd); + return NULL; +} + +static offset_t +cbfs_next_offset(offset_t offset, size_t align, size_t increment) +{ + offset += increment; + if (offset % align) + offset += align - (offset % align); + return offset; +} + +static const struct cbfs_file_descriptor * +__cbfs_open(const struct cbfs_descriptor *cbfs,const char *name, + uint32_t *type) +{ + const struct cbfs_file_descriptor *fd; + struct cbfs_file file; + ssize_t read_sz; + offset_t offset; + size_t cbfs_sz; + size_t align; + rhandle_t rh; + + if (cbfs == NULL || name == NULL) return NULL;
- return dest; + offset = 0; + rh = cbfs->rh; + align = cbfs->align; + cbfs_sz = cbfs->size; + fd = NULL; + + while (offset + sizeof(file) < cbfs_sz) { + char *file_name; + size_t name_sz; + size_t entry_sz; + int found; + + read_sz = region_readat(rh, &file, offset, sizeof(file)); + if (read_sz != sizeof(file)) + return NULL; + + if (memcmp(CBFS_FILE_MAGIC, file.magic, sizeof(file.magic))) { + offset = cbfs_next_offset(offset, align, align); + continue; + } + + file.len = ntohl(file.len); + file.type = ntohl(file.type); + file.offset = ntohl(file.offset); + entry_sz = file.len + file.offset; + + /* File type doesn't match. */ + if (type != NULL && *type != file.type) { + offset = cbfs_next_offset(offset, align, entry_sz); + continue; + } + name_sz = file.offset - sizeof(file); + file_name = region_mmap(rh, offset + sizeof(file), name_sz); + if (file_name == NULL) { + offset = cbfs_next_offset(offset, align, entry_sz); + continue; + } + + found = !strcmp(file_name, name); + region_munmap(rh, file_name); + + if (found) { + fd = allocate_file(cbfs, &file, offset); + break; + } + offset = cbfs_next_offset(offset, align, entry_sz); + } + + return fd; }
-void * cbfs_load_stage(struct cbfs_media *media, const char *name) +const struct cbfs_file_descriptor * +cbfs_open_by_name(const struct cbfs_descriptor *cbfs, const char *name) { - struct cbfs_stage *stage = (struct cbfs_stage *) - cbfs_get_file_content(media, name, CBFS_TYPE_STAGE, NULL); - /* this is a mess. There is no ntohll. */ - /* for now, assume compatible byte order until we solve this. */ - uint32_t entry; - uint32_t final_size; + return __cbfs_open(cbfs, name, NULL); +}
- if (stage == NULL) - return (void *) -1; +const struct cbfs_file_descriptor * +cbfs_open_by_name_type(const struct cbfs_descriptor *cbfs, + const char *name, uint32_t type) +{ + return __cbfs_open(cbfs, name, &type); +}
- LOG("loading stage %s @ 0x%x (%d bytes), entry @ 0x%llx\n", - name, - (uint32_t) stage->load, stage->memlen, - stage->entry); +void cbfs_file_close(const struct cbfs_file_descriptor *file) +{ + if (file != NULL) + cbfs_fd_mark_free(file); +}
- final_size = cbfs_decompress(stage->compression, - ((unsigned char *) stage) + - sizeof(struct cbfs_stage), - (void *) (uint32_t) stage->load, - stage->len); - if (!final_size) - return (void *) -1; +ssize_t cbfs_file_content_size(const struct cbfs_file_descriptor *fd) +{ + if (fd == NULL) + return -1;
- /* Stages rely the below clearing so that the bss is initialized. */ - memset((void *)((uintptr_t)stage->load + final_size), 0, - stage->memlen - final_size); + return fd->content.size; +} + +ssize_t cbfs_file_metadata_size(const struct cbfs_file_descriptor *fd) +{ + if (fd == NULL) + return -1; + + return fd->metadata.size; +} + +ssize_t cbfs_file_read_content(const struct cbfs_file_descriptor *fd, + void *buf, offset_t offset, size_t size) +{ + struct region region = { + .offset = offset, + .size = size, + };
- DEBUG("stage loaded.\n"); + if (fd == NULL) + return -1;
- entry = stage->entry; - // entry = ntohll(stage->entry); + if (!region_contained(&fd->content, ®ion)) + return -1;
- return (void *) entry; + return region_readat(fd->rh, buf, fd->content.offset + offset, size); }
-/* Simple buffer */ +void *cbfs_file_map_content(const struct cbfs_file_descriptor *fd, + offset_t offset, size_t size) +{ + struct region region = { + .offset = offset, + .size = size, + };
-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) - 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; -} - -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; + if (fd == NULL) + return NULL; + + if (!region_contained(&fd->content, ®ion)) + return NULL; + + return region_mmap(fd->rh, fd->content.offset + offset, size); }
-/** - * run_address is passed the address of a function taking no parameters and - * jumps to it, returning the result. - * @param f the address to call as a function. - * @return value returned by the function. - */ +ssize_t cbfs_file_read_metadata(const struct cbfs_file_descriptor *fd, + void *buf, offset_t offset, size_t size) +{ + struct region region = { + .offset = offset, + .size = size, + }; + + if (fd == NULL) + return -1;
-int run_address(void *f) + if (!region_contained(&fd->metadata, ®ion)) + return -1; + + return region_readat(fd->rh, buf, fd->metadata.offset + offset, size); +} + +void *cbfs_file_map_metadata(const struct cbfs_file_descriptor *fd) { - int (*v) (void); - v = f; - return v(); + if (fd == NULL) + return NULL; + + return region_mmap(fd->rh, fd->metadata.offset, fd->metadata.size); }
+void cbfs_file_unmap(const struct cbfs_file_descriptor *fd, void *addr) +{ + if (fd == NULL) + return; + + region_munmap(fd->rh, addr); +} + +#ifndef __SMM__ +size_t cbfs_file_inflate(const struct cbfs_file_descriptor *fd, + offset_t offset, size_t src_sz, void *dest) +{ +#if defined(CBFS_WITH_LZMA) + void *src; + size_t dest_sz; + + src = cbfs_file_map_content(fd, offset, src_sz); + + if (src == NULL) + return 0; + + dest_sz = ulzma(src, dest); + + cbfs_file_unmap(fd, src); + + return dest_sz; +#else + return 0; +#endif +} + +void *cbfs_load_stage(const struct cbfs_descriptor *cbfs, const char *name) +{ + struct cbfs_stage stage; + const struct cbfs_file_descriptor *file; + size_t inflated_sz; + ssize_t metadata_sz; + ssize_t file_sz; + uint8_t *dest; + void *entry; + + entry = NULL; + file = cbfs_open_by_name_type(cbfs, name, CBFS_TYPE_STAGE); + + if (file == NULL) + return NULL; + + metadata_sz = cbfs_file_read_metadata(file, &stage, 0, sizeof(stage)); + if (metadata_sz != sizeof(stage)) + goto out; + + file_sz = cbfs_file_content_size(file); + if (file_sz < 0) + goto out; + + printk(BIOS_INFO, + "Loading stage '%s' 0x%llx (%d bytes), entry point 0x%llx\n", + name, (long long) stage.load, stage.memlen, + (long long)stage.entry); + + dest = (void *)(uintptr_t)stage.load; + inflated_sz = stage.memlen; + + switch (stage.compression) { + case CBFS_COMPRESS_NONE: + if (cbfs_file_read_content(file, dest, 0, file_sz) != file_sz) + goto out; + break; +#if defined(CBFS_WITH_LZMA) + case CBFS_COMPRESS_LZMA: + inflated_sz = cbfs_file_inflate(file, 0, file_sz, dest); + if (inflated_sz == 0) + goto out; + break; +#endif + default: + printk(BIOS_ERR,"Invalid compression scheme: %d\n", + stage.compression); + goto out; + } + + if (inflated_sz > stage.memlen) { + printk(BIOS_ERR,"Inflated size %zu > memory size %zu.\n", + inflated_sz, (size_t)stage.memlen); + goto out; + } + + /* Stages rely the below clearing so that the bss is initialized. */ + memset(dest + inflated_sz, 0, stage.memlen - inflated_sz); + + printk(BIOS_DEBUG,"stage loaded.\n"); + + entry = (void *)(uintptr_t)stage.entry; + +out: + cbfs_file_close(file); + return entry; +} #endif diff --git a/src/lib/cbfs_boot_region.c b/src/lib/cbfs_boot_region.c new file mode 100644 index 0000000..6ab12f7 --- /dev/null +++ b/src/lib/cbfs_boot_region.c @@ -0,0 +1,133 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 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 + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdint.h> +#include <arch/early_variables.h> +#include <arch/byteorder.h> +#include <console/console.h> +#include <cbfs.h> +#include <cbfs_core.h> +#include <region.h> + +static struct cbfs_descriptor g_cbfs_descriptor CAR_GLOBAL; + +const struct cbfs_descriptor *cbfs_default_descriptor(void) +{ + return car_get_var_ptr(&g_cbfs_descriptor); +} + +static const struct region_device cbfs_dev = { + .region = { + .offset = CONFIG_ROM_SIZE - CONFIG_CBFS_SIZE, + .size = CONFIG_CBFS_SIZE, + }, +}; + +#if IS_ENABLED(CONFIG_ARCH_X86) +static offset_t get_header_offset(rhandle_t rh) +{ + int32_t header_offset; + offset_t off; + ssize_t rsize; + + rsize = region_size(rh); + off = rsize - sizeof(header_offset); + region_readat(rh, &header_offset, off, sizeof(header_offset)); + return rsize + header_offset; +} +#else +static inline offset_t get_header_offset(rhandle_t rh) +{ + return CONFIG_CBFS_HEADER_ROM_OFFSET; +} +#endif + +void cbfs_default_media_initialize(void) +{ + rhandle_t rh; + offset_t header_offset; + struct cbfs_header *hdr; + size_t cbfs_size; + struct cbfs_descriptor *cbfs_desc; + + /* Already initialized. */ + rh = region_locate(DEFAULT_CBFS_REGION_NAME); + if (rhandle_valid(rh)) + return; + + /* Mark region as invalid. */ + cbfs_desc = (struct cbfs_descriptor *)cbfs_default_descriptor(); + cbfs_desc->rh = INVALID_RHANDLE; + + /* CBFS provides a readonly view of the rom. */ + rh = region_locate(BOOTRO_REGION_NAME); + + if (!rhandle_valid(rh)) { + printk(BIOS_ERR, "Boot region not found to register cbfs.\n"); + return; + } + + header_offset = get_header_offset(rh); + hdr = region_mmap(rh, header_offset, sizeof(*hdr)); + + if (hdr == NULL) { + printk(BIOS_ERR, "Couldn't mmap() cbfs header.\n"); + return; + } + + if (CBFS_HEADER_MAGIC != ntohl(hdr->magic)) { + printk(BIOS_ERR, "Invalid CBFS magic @ 0x%zx: %x vs %x\n", + (size_t)header_offset, CBFS_HEADER_MAGIC, + ntohl(hdr->magic)); + goto out; + } + + if (cbfs_dev.region.offset != ntohl(hdr->offset)) { + printk(BIOS_ERR, "CBFS offset doesn't match: 0x%zx vs 0x%zx\n", + (size_t)cbfs_dev.region.offset, + (size_t)ntohl(hdr->offset)); + goto out; + } + + cbfs_size = ntohl(hdr->romsize) - ntohl(hdr->offset); + + if (cbfs_dev.region.size != cbfs_size) { + printk(BIOS_ERR, "CBFS size doesn't match: 0x%zx vs 0x%zx\n", + cbfs_dev.region.size, cbfs_size); + goto out; + } + +#if IS_ENABLED(CONFIG_ARCH_X86) + cbfs_size -= htonl(hdr->bootblocksize); +#endif + + cbfs_desc->align = ntohl(hdr->align); + cbfs_desc->size = cbfs_size; + + printk(BIOS_DEBUG, "%s: CBFS align 0x%zx size 0x%zx\n", __func__, + cbfs_desc->align, cbfs_desc->size); + + cbfs_desc->rh = region_register_subregion(DEFAULT_CBFS_REGION_NAME, + &cbfs_dev, rh); + if (!rhandle_valid(cbfs_desc->rh)) + printk(BIOS_ERR, "Unable to register default CBFS region.\n"); + +out: + region_munmap(rh, hdr); +} diff --git a/src/lib/cbfs_core.c b/src/lib/cbfs_core.c deleted file mode 100644 index 50c037e..0000000 --- a/src/lib/cbfs_core.c +++ /dev/null @@ -1,224 +0,0 @@ -/* - * This file is part of the coreboot project. - * - * Copyright (C) 2011 secunet Security Networks AG - * Copyright (C) 2013 The Chromium OS Authors. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -/* The CBFS core requires a couple of #defines or functions to adapt it to the - * target environment: - * - * CBFS_CORE_WITH_LZMA (must be #define) - * if defined, ulzma() must exist for decompression of data streams - * - * CBFS_HEADER_ROM_ADDRESS - * ROM address (offset) of CBFS header. Underlying CBFS media may interpret - * it in other way so we call this "address". - * - * ERROR(x...) - * print an error message x (in printf format) - * - * LOG(x...) - * print a message x (in printf format) - * - * DEBUG(x...) - * print a debug message x (in printf format) - * - */ - -#include <cbfs.h> -#include <string.h> - -/* returns a pointer to CBFS master header, or CBFS_HEADER_INVALID_ADDRESS - * on failure */ -const struct cbfs_header *cbfs_get_header(struct cbfs_media *media) -{ - const struct cbfs_header *header; - struct cbfs_media default_media; - - if (media == CBFS_DEFAULT_MEDIA) { - media = &default_media; - if (init_default_cbfs_media(media) != 0) { - ERROR("Failed to initialize default media.\n"); - return CBFS_HEADER_INVALID_ADDRESS; - } - } - - media->open(media); - DEBUG("CBFS_HEADER_ROM_ADDRESS: 0x%x/0x%x\n", CBFS_HEADER_ROM_ADDRESS, - CONFIG_ROM_SIZE); - header = media->map(media, CBFS_HEADER_ROM_ADDRESS, sizeof(*header)); - media->close(media); - - if (header == CBFS_MEDIA_INVALID_MAP_ADDRESS) { - ERROR("Failed to load CBFS header from 0x%x\n", - CBFS_HEADER_ROM_ADDRESS); - return CBFS_HEADER_INVALID_ADDRESS; - } - - if (CBFS_HEADER_MAGIC != ntohl(header->magic)) { - ERROR("Could not find valid CBFS master header at %x: " - "%x vs %x.\n", CBFS_HEADER_ROM_ADDRESS, CBFS_HEADER_MAGIC, - ntohl(header->magic)); - if (header->magic == 0xffffffff) { - ERROR("Maybe ROM is not mapped properly?\n"); - } - return CBFS_HEADER_INVALID_ADDRESS; - } - return header; -} - -/* public API starts here*/ -struct cbfs_file *cbfs_get_file(struct cbfs_media *media, const char *name) -{ - const char *file_name; - uint32_t offset, align, romsize, name_len; - const struct cbfs_header *header; - struct cbfs_file file, *file_ptr; - struct cbfs_media default_media; - - if (media == CBFS_DEFAULT_MEDIA) { - media = &default_media; - if (init_default_cbfs_media(media) != 0) { - ERROR("Failed to initialize default media.\n"); - return NULL; - } - } - - if (CBFS_HEADER_INVALID_ADDRESS == (header = cbfs_get_header(media))) - return NULL; - - // Logical offset (for source media) of first file. - offset = ntohl(header->offset); - align = ntohl(header->align); - romsize = ntohl(header->romsize); - - // TODO Add a "size" in CBFS header for a platform independent way to - // determine the end of CBFS data. -#if defined(CONFIG_ARCH_X86) && CONFIG_ARCH_X86 - romsize -= htonl(header->bootblocksize); -#endif - DEBUG("CBFS location: 0x%x~0x%x, align: %d\n", offset, romsize, align); - - DEBUG("Looking for '%s' starting from 0x%x.\n", name, offset); - media->open(media); - while (offset < romsize && - media->read(media, &file, offset, sizeof(file)) == sizeof(file)) { - if (memcmp(CBFS_FILE_MAGIC, file.magic, - sizeof(file.magic)) != 0) { - uint32_t new_align = align; - if (offset % align) - new_align += align - (offset % align); - ERROR("ERROR: No file header found at 0x%x - " - "try next aligned address: 0x%x.\n", offset, - offset + new_align); - offset += new_align; - continue; - } - name_len = ntohl(file.offset) - sizeof(file); - DEBUG(" - load entry 0x%x file name (%d bytes)...\n", offset, - name_len); - - // load file name (arbitrary length). - file_name = (const char *)media->map( - media, offset + sizeof(file), name_len); - if (file_name == CBFS_MEDIA_INVALID_MAP_ADDRESS) { - ERROR("ERROR: Failed to get filename: 0x%x.\n", offset); - } else if (strcmp(file_name, name) == 0) { - int file_offset = ntohl(file.offset), - file_len = ntohl(file.len); - DEBUG("Found file (offset=0x%x, len=%d).\n", - offset + file_offset, file_len); - media->unmap(media, file_name); - file_ptr = media->map(media, offset, - file_offset + file_len); - media->close(media); - return file_ptr; - } else { - DEBUG(" (unmatched file @0x%x: %s)\n", offset, - file_name); - media->unmap(media, file_name); - } - - // Move to next file. - offset += ntohl(file.len) + ntohl(file.offset); - if (offset % align) - offset += align - (offset % align); - } - media->close(media); - LOG("WARNING: '%s' not found.\n", name); - return NULL; -} - -void *cbfs_get_file_content(struct cbfs_media *media, const char *name, - int type, size_t *sz) -{ - struct cbfs_file *file = cbfs_get_file(media, name); - - if (sz) - *sz = 0; - - if (file == NULL) { - ERROR("Could not find file '%s'.\n", name); - return NULL; - } - - if (ntohl(file->type) != type) { - ERROR("File '%s' is of type %x, but we requested %x.\n", name, - ntohl(file->type), type); - return NULL; - } - - if (sz) - *sz = ntohl(file->len); - - return (void *)CBFS_SUBHEADER(file); -} - -int cbfs_decompress(int algo, void *src, void *dst, int len) -{ - switch (algo) { - case CBFS_COMPRESS_NONE: - /* Reads need to be aligned at 4 bytes to avoid - poor flash performance. */ - while (len && ((u32)src & 3)) { - *(u8*)dst++ = *(u8*)src++; - len--; - } - memmove(dst, src, len); - return len; -#ifdef CBFS_CORE_WITH_LZMA - case CBFS_COMPRESS_LZMA: - return ulzma(src, dst); -#endif - default: - ERROR("tried to decompress %d bytes with algorithm #%x," - "but that algorithm id is unsupported.\n", len, - algo); - return 0; - } -} - diff --git a/src/lib/loaders/cbfs_payload_loader.c b/src/lib/loaders/cbfs_payload_loader.c index 2c1d179..31ad7e6 100644 --- a/src/lib/loaders/cbfs_payload_loader.c +++ b/src/lib/loaders/cbfs_payload_loader.c @@ -18,20 +18,30 @@ */
#include <cbfs.h> +#include <cbfs_core.h> #include <payload_loader.h>
static int cbfs_locate_payload(struct payload *payload) { void *buffer; size_t size; - const int type = CBFS_TYPE_PAYLOAD; + const struct cbfs_file_descriptor *fd;
- buffer = cbfs_get_file_content(CBFS_DEFAULT_MEDIA, payload->name, - type, &size); + fd = cbfs_open_by_name_type(cbfs_default_descriptor(), payload->name, + CBFS_TYPE_PAYLOAD);
- if (buffer == NULL) + if (fd == NULL) return -1;
+ size = cbfs_file_content_size(fd); + buffer = cbfs_file_map_content(fd, 0, size); + + if (buffer == NULL) { + cbfs_file_close(fd); + return -1; + } + + payload->fd = fd; payload->backing_store.data = buffer; payload->backing_store.size = size;
diff --git a/src/lib/loaders/cbfs_ramstage_loader.c b/src/lib/loaders/cbfs_ramstage_loader.c index 5d5cc0b..5d5edd8 100644 --- a/src/lib/loaders/cbfs_ramstage_loader.c +++ b/src/lib/loaders/cbfs_ramstage_loader.c @@ -34,7 +34,8 @@ static void *cbfs_load_ramstage(uint32_t cbmem_id, const char *name, .name = name, };
- if (rmodule_stage_load_from_cbfs(&rmod_ram)) { + if (rmodule_stage_load_from_cbfs(cbfs_default_descriptor(), + &rmod_ram)) { printk(BIOS_DEBUG, "Could not load ramstage.\n"); return NULL; } @@ -49,14 +50,7 @@ static void *cbfs_load_ramstage(uint32_t cbmem_id, const char *name, static void *cbfs_load_ramstage(uint32_t cbmem_id, const char *name, const struct cbmem_entry **cbmem_entry) { - void *entry; - - entry = cbfs_load_stage(CBFS_DEFAULT_MEDIA, name); - - if ((void *)entry == (void *) -1) - entry = NULL; - - return entry; + return cbfs_load_stage(cbfs_default_descriptor(), name); }
#endif /* CONFIG_RELOCATABLE_RAMSTAGE */ diff --git a/src/lib/loaders/load_and_run_payload.c b/src/lib/loaders/load_and_run_payload.c index 2204090..ac4e7bf 100644 --- a/src/lib/loaders/load_and_run_payload.c +++ b/src/lib/loaders/load_and_run_payload.c @@ -58,9 +58,8 @@ struct payload *payload_load(void) ops->name); continue; } - printk(BIOS_DEBUG, "%s: located payload @ %p, %zu bytes.\n", - ops->name, payload->backing_store.data, - payload->backing_store.size); + printk(BIOS_DEBUG, "%s: located payload, %zu bytes.\n", + ops->name, payload->backing_store.size); break; }
diff --git a/src/lib/region.c b/src/lib/region.c new file mode 100644 index 0000000..4dadb12 --- /dev/null +++ b/src/lib/region.c @@ -0,0 +1,293 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 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 + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <string.h> +#include <console/console.h> +#include <arch/early_variables.h> +#include <region.h> +#include <vendorcode/google/chromeos/fmap.h> + +/* MAX_REGIONS could be configurable. */ +#define MAX_REGIONS 6 + +struct region_registry_entry { + rhandle_t parent_slot; + const char *name; + const struct region_device *rdev; +}; + +struct region_registry { + struct region_registry_entry entries[MAX_REGIONS]; +}; + +static struct region_registry g_regions CAR_GLOBAL; + +static inline struct region_registry *get_registry(void) +{ + return car_get_var_ptr(&g_regions); +} + +static const struct region_registry_entry * +registry_entry_lookup(const struct region_registry *rr, rhandle_t rh) +{ + if (rh < 0 || rh >= MAX_REGIONS) + return NULL; + + return &rr->entries[rh]; +} + +int region_contained(const struct region *parent, const struct region *child) +{ + if (child->size > parent->size) + return 0; + + if (child->offset >= parent->size) + return 0; + + if ((child->offset + child->size) > parent->size) + return 0; + + return 1; +} + +static rhandle_t __region_register(struct region_registry *rr, const char *name, + const struct region_device *rdev, + rhandle_t parent) +{ + rhandle_t slot; + + for (slot = 0; slot < MAX_REGIONS; slot++) { + struct region_registry_entry *entry = &rr->entries[slot]; + + /* Find an empty slot. */ + if (entry->rdev != NULL) + continue; + + entry->parent_slot = parent; + entry->name = name; + entry->rdev = rdev; + printk(BIOS_DEBUG, + "Region '%s' registered: slot %u, %zu @ 0x%zx.\n", + name, (unsigned int)slot, rdev->region.size, + (size_t)rdev->region.offset); + return slot; + } + + return INVALID_RHANDLE; +} + +rhandle_t region_register(const char *name, const struct region_device *rdev) +{ + if (name == NULL || rdev == NULL || rdev->ops == NULL) + return INVALID_RHANDLE; + + return __region_register(get_registry(), name, rdev, INVALID_RHANDLE); +} + +rhandle_t region_register_subregion(const char *name, + const struct region_device *rdev, + rhandle_t parent) +{ + struct region_registry *rr; + const struct region_registry_entry *pentry; + + /* Subregions require NULL ops. */ + if (name == NULL || rdev == NULL || rdev->ops != NULL) + return INVALID_RHANDLE; + + rr = get_registry(); + + /* Parent is invalid. */ + pentry = registry_entry_lookup(rr, parent); + if (pentry == NULL) + return INVALID_RHANDLE; + + /* Ensure subregion is within bounds. */ + if (!region_contained(&pentry->rdev->region, &rdev->region)) + return INVALID_RHANDLE; + + return __region_register(rr, name, rdev, parent); +} + +/* Locate a region of provided name. */ +rhandle_t region_locate(const char *name) +{ + rhandle_t slot; + struct region_registry *rr; + + if (name == NULL) + return INVALID_RHANDLE; + + rr = get_registry(); + + for (slot = 0; slot < MAX_REGIONS; slot++) { + const struct region_registry_entry *entry = &rr->entries[slot]; + + /* No more valid entries. */ + if (entry->rdev == NULL) + break; + + if (!strcmp(entry->name, name)) + return slot; + } + return INVALID_RHANDLE; +} + +/* + * Obtain the appropriate region_device while optionally translating the + * region into the parent's domain. + */ +static const struct region_device * +find_parent(rhandle_t rh, struct region *region) +{ + const struct region_registry_entry *entry; + struct region_registry *rr; + + rr = get_registry(); + entry = registry_entry_lookup(rr, rh); + + if (entry == NULL) + return NULL; + + if (region != NULL && !region_contained(&entry->rdev->region, region)) + return NULL; + + while (entry->rdev->ops == NULL) { + /* Translate the region into the parent's view. */ + if (region != NULL) + region->offset += entry->rdev->region.offset; + entry = &rr->entries[entry->parent_slot]; + } + + return entry->rdev; +} + +ssize_t region_readat(rhandle_t rh, void *buf, offset_t offset, size_t count) +{ + const struct region_device *rdev; + struct region region = { + .offset = offset, + .size = count, + }; + + rdev = find_parent(rh, ®ion); + + if (rdev == NULL || rdev->ops->readat == NULL) + return -1; + + return rdev->ops->readat(rdev, buf, region.offset, region.size); +} + +ssize_t region_writeat(rhandle_t rh, void *buf, offset_t offset, size_t count) +{ + const struct region_device *rdev; + struct region region = { + .offset = offset, + .size = count, + }; + + rdev = find_parent(rh, ®ion); + + if (rdev == NULL || rdev->ops->writeat == NULL) + return -1; + + return rdev->ops->writeat(rdev, buf, region.offset, region.size); +} + +ssize_t region_eraseat(rhandle_t rh, offset_t offset, size_t count) +{ + const struct region_device *rdev; + struct region region = { + .offset = offset, + .size = count, + }; + + rdev = find_parent(rh, ®ion); + + if (rdev == NULL || rdev->ops->eraseat == NULL) + return -1; + + return rdev->ops->eraseat(rdev, region.offset, region.size); +} + +void *region_mmap(rhandle_t rh, offset_t offset, size_t count) +{ + const struct region_device *rdev; + struct region region = { + .offset = offset, + .size = count, + }; + + if (count == 0) + return NULL; + + rdev = find_parent(rh, ®ion); + + if (rdev == NULL || rdev->ops->mmap == NULL) + return NULL; + + return rdev->ops->mmap(rdev, region.offset, region.size); +} + +void region_munmap(rhandle_t rh, void *addr) +{ + const struct region_device *rdev; + + rdev = find_parent(rh, NULL); + + if (rdev == NULL || rdev->ops->munmap == NULL) + return; + + return rdev->ops->munmap(rdev, addr); +} + +ssize_t region_size(rhandle_t rh) +{ + const struct region_registry_entry *entry; + + entry = registry_entry_lookup(get_registry(), rh); + + if (entry == NULL) + return -1; + + return entry->rdev->region.size; +} + +void boot_region_init(void) +{ + boot_media_initialize(); + cbfs_default_media_initialize(); + fmap_initialize(); +} + +/* Provide weak default. */ +void __attribute__((weak)) bootrw_media_initialize(void) {} + +#if !defined(__PRE_RAM__) +#include <bootstate.h> +static void regions_bscb(void *unused) +{ + boot_region_init(); +} + +BOOT_STATE_INIT_ENTRIES(regions_init) = { + BOOT_STATE_INIT_ENTRY(BS_DEV_INIT_CHIPS, BS_ON_ENTRY, + regions_bscb, NULL), +}; +#endif diff --git a/src/lib/rmodule.c b/src/lib/rmodule.c index 2cb70b8..33481b2 100644 --- a/src/lib/rmodule.c +++ b/src/lib/rmodule.c @@ -21,6 +21,9 @@ #include <stdlib.h> #include <string.h> #include <console/console.h> +#include <cbfs.h> +#include <cbfs_core.h> +#include <lib.h> #include <rmodule.h>
/* Change this define to get more verbose debugging for module loading. */ @@ -305,11 +308,12 @@ int rmodule_calc_region(unsigned int region_alignment, size_t rmodule_size, #include <cbmem.h> #include <cbfs_core.h>
-int rmodule_stage_load(struct rmod_stage_load *rsl, struct cbfs_stage *stage) +int rmodule_stage_load(struct rmod_stage_load *rsl, struct cbfs_stage *stage, + void *data) { struct rmodule rmod_stage; size_t region_size; - char *stage_region; + unsigned char *stage_region; int rmodule_offset; int load_offset; const struct cbmem_entry *cbmem_entry; @@ -331,8 +335,12 @@ int rmodule_stage_load(struct rmod_stage_load *rsl, struct cbfs_stage *stage) printk(BIOS_INFO, "Decompressing stage %s @ 0x%p (%d bytes)\n", rsl->name, &stage_region[rmodule_offset], stage->memlen);
- if (!cbfs_decompress(stage->compression, &stage[1], - &stage_region[rmodule_offset], stage->len)) + if (stage->compression == CBFS_COMPRESS_NONE) { + memcpy(data, &stage_region[rmodule_offset], stage->len); + } else if (stage->compression == CBFS_COMPRESS_LZMA) { + if (!ulzma(data, &stage_region[rmodule_offset])) + return -1; + } else return -1;
if (rmodule_parse(&stage_region[rmodule_offset], &rmod_stage)) @@ -347,17 +355,35 @@ int rmodule_stage_load(struct rmod_stage_load *rsl, struct cbfs_stage *stage) return 0; }
-int rmodule_stage_load_from_cbfs(struct rmod_stage_load *rsl) +int rmodule_stage_load_from_cbfs(const struct cbfs_descriptor *cbfs, + struct rmod_stage_load *rsl) { - struct cbfs_stage *stage; + const struct cbfs_file_descriptor *fd; + struct cbfs_stage stage; + size_t rdsz = sizeof(stage); + int ret; + void *buf;
- stage = cbfs_get_file_content(CBFS_DEFAULT_MEDIA, - rsl->name, CBFS_TYPE_STAGE, NULL); + fd = cbfs_open_by_name_type(cbfs, rsl->name, CBFS_TYPE_STAGE);
- if (stage == NULL) + if (fd == NULL) return -1;
- return rmodule_stage_load(rsl, stage); + ret = -1; + if (cbfs_file_read_metadata(fd, &stage, 0, rdsz) != rdsz) + goto out; + + buf = cbfs_file_map_content(fd, 0, cbfs_file_content_size(fd)); + if (buf == NULL) + goto out; + + ret = rmodule_stage_load(rsl, &stage, buf); + + cbfs_file_unmap(fd, buf); + +out: + cbfs_file_close(fd); + return ret; }
#endif /* DYNAMIC_CBMEM */ diff --git a/src/lib/selfboot.c b/src/lib/selfboot.c index feff03e..b6c8797 100644 --- a/src/lib/selfboot.c +++ b/src/lib/selfboot.c @@ -25,6 +25,7 @@ #include <stdlib.h> #include <string.h> #include <cbfs.h> +#include <cbfs_core.h> #include <lib.h> #include <bootmem.h> #include <payload_loader.h> @@ -40,7 +41,7 @@ struct segment { struct segment *next; struct segment *prev; unsigned long s_dstaddr; - unsigned long s_srcaddr; + unsigned long s_srcoffset; unsigned long s_memsz; unsigned long s_filesz; int compression; @@ -140,7 +141,7 @@ static int relocate_segment(unsigned long buffer, struct segment *seg) new->s_memsz = len; seg->s_memsz -= len; seg->s_dstaddr += len; - seg->s_srcaddr += len; + seg->s_srcoffset += len; if (seg->s_filesz > len) { new->s_filesz = len; seg->s_filesz -= len; @@ -176,7 +177,7 @@ static int relocate_segment(unsigned long buffer, struct segment *seg) seg->s_memsz = len; new->s_memsz -= len; new->s_dstaddr += len; - new->s_srcaddr += len; + new->s_srcoffset += len; if (seg->s_filesz > len) { seg->s_filesz = len; new->s_filesz -= len; @@ -213,17 +214,12 @@ static int relocate_segment(unsigned long buffer, struct segment *seg)
static int build_self_segment_list( - struct segment *head, + struct segment *head, struct cbfs_payload_segment *segment, struct payload *payload, uintptr_t *entry) { struct segment *new; - struct segment *ptr; - struct cbfs_payload_segment *segment, *first_segment; - struct cbfs_payload *cbfs_payload; - cbfs_payload = payload->backing_store.data; memset(head, 0, sizeof(*head)); head->next = head->prev = head; - first_segment = segment = &cbfs_payload->segments;
while(1) { printk(BIOS_DEBUG, "Loading segment from rom address 0x%p\n", segment); @@ -237,43 +233,39 @@ static int build_self_segment_list( case PAYLOAD_SEGMENT_DATA: printk(BIOS_DEBUG, " %s (compression=%x)\n", segment->type == PAYLOAD_SEGMENT_CODE ? "code" : "data", - ntohl(segment->compression)); + segment->compression); new = malloc(sizeof(*new)); - new->s_dstaddr = ntohll(segment->load_addr); - new->s_memsz = ntohl(segment->mem_len); - new->compression = ntohl(segment->compression); - - new->s_srcaddr = (uintptr_t) - ((unsigned char *)first_segment) - + ntohl(segment->offset); - new->s_filesz = ntohl(segment->len); - printk(BIOS_DEBUG, " New segment dstaddr 0x%lx memsize 0x%lx srcaddr 0x%lx filesize 0x%lx\n", - new->s_dstaddr, new->s_memsz, new->s_srcaddr, new->s_filesz); + new->s_dstaddr = segment->load_addr; + new->s_memsz = segment->mem_len; + new->compression = segment->compression; + + new->s_srcoffset = segment->offset; + new->s_filesz = segment->len; + printk(BIOS_DEBUG, " New segment dstaddr 0x%lx memsize 0x%lx srcoffset 0x%lx filesize 0x%lx\n", + new->s_dstaddr, new->s_memsz, new->s_srcoffset, new->s_filesz); /* Clean up the values */ if (new->s_filesz > new->s_memsz) { new->s_filesz = new->s_memsz; } printk(BIOS_DEBUG, " (cleaned up) New segment addr 0x%lx size 0x%lx offset 0x%lx filesize 0x%lx\n", - new->s_dstaddr, new->s_memsz, new->s_srcaddr, new->s_filesz); + new->s_dstaddr, new->s_memsz, new->s_srcoffset, new->s_filesz); break;
case PAYLOAD_SEGMENT_BSS: - printk(BIOS_DEBUG, " BSS 0x%p (%d byte)\n", (void *) - (intptr_t)ntohll(segment->load_addr), - ntohl(segment->mem_len)); + printk(BIOS_DEBUG, " BSS 0x%p (%d byte)\n", + (void *)(intptr_t)segment->load_addr, + segment->mem_len); new = malloc(sizeof(*new)); new->s_filesz = 0; - new->s_srcaddr = (uintptr_t) - ((unsigned char *)first_segment) - + ntohl(segment->offset); - new->s_dstaddr = ntohll(segment->load_addr); - new->s_memsz = ntohl(segment->mem_len); + new->s_srcoffset = segment->offset; + new->s_dstaddr = segment->load_addr; + new->s_memsz = segment->mem_len; break;
case PAYLOAD_SEGMENT_ENTRY: printk(BIOS_DEBUG, " Entry Point 0x%p\n", - (void *)(intptr_t)ntohll(segment->load_addr)); - *entry = ntohll(segment->load_addr); + (void *)(intptr_t)segment->load_addr); + *entry = segment->load_addr; /* Per definition, a payload always has the entry point * as last segment. Thus, we use the occurrence of the * entry point as break condition for the loop. @@ -292,17 +284,11 @@ static int build_self_segment_list( /* We have found another CODE, DATA or BSS segment */ segment++;
- /* Find place where to insert our segment */ - for(ptr = head->next; ptr != head; ptr = ptr->next) { - if (new->s_srcaddr < ntohll(segment->load_addr)) - break; - } - - /* Order by stream offset */ - new->next = ptr; - new->prev = ptr->prev; - ptr->prev->next = new; - ptr->prev = new; + /* Add to end of list. */ + new->next = head; + new->prev = head->prev; + head->prev->next = new; + head->prev = new; }
return 1; @@ -315,6 +301,7 @@ static int load_self_segments( struct segment *ptr; const unsigned long one_meg = (1UL << 20); unsigned long bounce_high = lb_end; + unsigned char *srcaddr = payload->backing_store.data;
for(ptr = head->next; ptr != head; ptr = ptr->next) { if (bootmem_region_targets_usable_ram(ptr->s_dstaddr, @@ -378,7 +365,7 @@ static int load_self_segments(
/* Compute the boundaries of the segment */ dest = (unsigned char *)(ptr->s_dstaddr); - src = (unsigned char *)(ptr->s_srcaddr); + src = srcaddr + ptr->s_srcoffset;
/* Copy data from the initial buffer */ if (ptr->s_filesz) { @@ -441,13 +428,51 @@ static int load_self_segments( return 1; }
+static void +parse_cbfs_segments(struct cbfs_payload_segment *segments, size_t size) +{ + int i; + int num; + + num = size / sizeof(struct cbfs_payload_segment); + + for (i = 0; i < num; i++) { + /* Note: the type field is in host order. */ + segments[i].compression = ntohl(segments[i].compression); + segments[i].offset = ntohl(segments[i].offset); + /* Fix up the offsets to be relative to the data stream. */ + segments[i].offset -= size; + segments[i].load_addr = ntohll(segments[i].load_addr); + segments[i].len = ntohl(segments[i].len); + segments[i].mem_len = ntohl(segments[i].mem_len); + } +} + void *selfload(struct payload *payload) { uintptr_t entry = 0; struct segment head; + ssize_t metadata_sz; + struct cbfs_payload_segment *segments; + + metadata_sz = cbfs_file_metadata_size(payload->fd); + + if (metadata_sz <= 0) + return NULL; + + segments = malloc(sizeof(struct cbfs_payload_segment)); + + if (segments == NULL) + return NULL; + + if (cbfs_file_read_metadata(payload->fd, segments, 0, metadata_sz) != + metadata_sz) + return NULL; + + parse_cbfs_segments(segments, metadata_sz);
/* Preprocess the self segments */ - if (!build_self_segment_list(&head, payload, &entry)) + if (!build_self_segment_list(&head, segments, payload, &entry)) goto out;
/* Load the segments */ diff --git a/src/soc/intel/baytrail/Kconfig b/src/soc/intel/baytrail/Kconfig index 2360b36..63b5c89 100644 --- a/src/soc/intel/baytrail/Kconfig +++ b/src/soc/intel/baytrail/Kconfig @@ -8,6 +8,7 @@ if SOC_INTEL_BAYTRAIL
config CPU_SPECIFIC_OPTIONS def_bool y + select BOOT_REGION_SPI_FLASH select CACHE_MRC_SETTINGS select CAR_MIGRATION select COLLECT_TIMESTAMPS diff --git a/src/soc/intel/baytrail/mrc_cache.c b/src/soc/intel/baytrail/mrc_cache.c index 5613761..784952d 100644 --- a/src/soc/intel/baytrail/mrc_cache.c +++ b/src/soc/intel/baytrail/mrc_cache.c @@ -20,6 +20,7 @@ #include <string.h> #include <console/console.h> #include <cbmem.h> +#include <region.h> #include <ip_checksum.h> #if CONFIG_CHROMEOS #include <vendorcode/google/chromeos/fmap.h> @@ -40,11 +41,20 @@ struct mrc_data_region { static int mrc_cache_get_region(struct mrc_data_region *region) { #if CONFIG_CHROMEOS - int ret; - ret = find_fmap_entry("RW_MRC_CACHE", ®ion->base); - if (ret >= 0) { - region->size = ret; - return 0; + offset_t offset; + size_t size; + if (!fmap_locate("RW_MRC_CACHE", &offset, &size)) { + void *mapping; + rhandle_t rh; + + rh = region_locate(BOOTRO_REGION_NAME); + mapping = region_mmap(rh, offset, size); + + if (mapping != NULL) { + region->base = mapping; + region->size = size; + return 0; + } } #endif region->base = (void *)CONFIG_MRC_SETTINGS_CACHE_BASE; diff --git a/src/soc/intel/baytrail/nvm.c b/src/soc/intel/baytrail/nvm.c index dccc801..44d67ae 100644 --- a/src/soc/intel/baytrail/nvm.c +++ b/src/soc/intel/baytrail/nvm.c @@ -24,26 +24,25 @@ #include <spi-generic.h> #include <spi_flash.h> #include <baytrail/nvm.h> +#include <region.h>
/* This module assumes the flash is memory mapped just below 4GiB in the * address space for reading. Also this module assumes an area it erased * when all bytes read as all 0xff's. */
-static struct spi_flash *flash; +static rhandle_t rwrh = INVALID_RHANDLE;
static int nvm_init(void) { - if (flash != NULL) + if (rhandle_valid(rwrh)) return 0;
- spi_init(); - flash = spi_flash_probe(0, 0, 1000000, SPI_MODE_3); - if (!flash) { - printk(BIOS_DEBUG, "Could not find SPI device\n"); - return -1; - } + rwrh = region_locate(BOOTRW_REGION_NAME);
- return 0; + if (rhandle_valid(rwrh)) + return 0; + + return -1; }
/* Convert memory mapped pointer to flash offset. */ @@ -69,7 +68,10 @@ int nvm_erase(void *start, size_t size) { if (nvm_init() < 0) return -1; - flash->erase(flash, to_flash_offset(start), size); + + if (region_eraseat(rwrh, to_flash_offset(start), size) != size) + return -1; + return 0; }
@@ -78,6 +80,9 @@ int nvm_write(void *start, const void *data, size_t size) { if (nvm_init() < 0) return -1; - flash->write(flash, to_flash_offset(start), size, data); + + if (region_eraseat(rwrh, to_flash_offset(start), size) != size) + return -1; + return 0; } diff --git a/src/soc/intel/baytrail/romstage/raminit.c b/src/soc/intel/baytrail/romstage/raminit.c index 7bcd54f..496e755 100644 --- a/src/soc/intel/baytrail/romstage/raminit.c +++ b/src/soc/intel/baytrail/romstage/raminit.c @@ -105,6 +105,7 @@ void raminit(struct mrc_params *mp, int prev_sleep_state) int ret; mrc_wrapper_entry_t mrc_entry; const struct mrc_saved_data *cache; + const struct cbfs_file_descriptor *fd;
/* Fill in default entries. */ mp->version = MRC_PARAMS_VER; @@ -118,10 +119,11 @@ void raminit(struct mrc_params *mp, int prev_sleep_state) printk(BIOS_DEBUG, "No MRC cache found.\n"); }
- mrc_entry = cbfs_get_file_content(CBFS_DEFAULT_MEDIA, "mrc.bin", 0xab, - NULL); + fd = cbfs_open_by_name_type(cbfs_default_descriptor(), "mrc.bin", 0xab); + mrc_entry = cbfs_file_map_content(fd, 0, cbfs_file_content_size(fd));
if (mrc_entry == NULL) { + cbfs_file_close(fd); printk(BIOS_DEBUG, "Couldn't find mrc.bin\n"); return; } @@ -130,6 +132,9 @@ void raminit(struct mrc_params *mp, int prev_sleep_state)
ret = mrc_entry(mp);
+ cbfs_file_unmap(fd, mrc_entry); + cbfs_file_close(fd); + print_dram_info();
cbmem_initialize_empty(); diff --git a/src/soc/intel/baytrail/romstage/romstage.c b/src/soc/intel/baytrail/romstage/romstage.c index 5af9bf4..37aae10 100644 --- a/src/soc/intel/baytrail/romstage/romstage.c +++ b/src/soc/intel/baytrail/romstage/romstage.c @@ -29,6 +29,7 @@ #include <ec/google/chromeec/ec.h> #endif #include <elog.h> +#include <region.h> #include <ramstage_cache.h> #include <romstage_handoff.h> #include <timestamp.h> @@ -133,6 +134,9 @@ void * asmlinkage romstage_main(unsigned long bist, google_chromeec_early_init(); #endif
+ /* Initialize the boot media. */ + boot_region_init(); + /* Call into mainboard. */ mainboard_romstage_entry(&rp);
diff --git a/src/vendorcode/google/chromeos/Makefile.inc b/src/vendorcode/google/chromeos/Makefile.inc index 007bd57..24afb2c 100644 --- a/src/vendorcode/google/chromeos/Makefile.inc +++ b/src/vendorcode/google/chromeos/Makefile.inc @@ -27,6 +27,7 @@ romstage-y += fmap.c ramstage-y += fmap.c ramstage-$(CONFIG_CHROMEOS_RAMOOPS) += ramoops.c smm-y += fmap.c +smm-y += ../../../lib/region.c ifneq ($(wildcard src/mainboard/$(MAINBOARDDIR)/chromeos.c),) ramstage-srcs += src/mainboard/$(MAINBOARDDIR)/chromeos.c romstage-srcs += src/mainboard/$(MAINBOARDDIR)/chromeos.c diff --git a/src/vendorcode/google/chromeos/chromeos.c b/src/vendorcode/google/chromeos/chromeos.c index e917ba1..6f20109 100644 --- a/src/vendorcode/google/chromeos/chromeos.c +++ b/src/vendorcode/google/chromeos/chromeos.c @@ -121,10 +121,14 @@ static int vboot_locate_payload(struct payload *payload) if (buffer == NULL) return -1;
+#if 0 payload->backing_store.data = buffer; payload->backing_store.size = size;
return 0; +#endif + /* TODO: fake a cbfs. Also need this in rmodule loading. */ + return -1; }
const struct payload_loader_ops vboot_payload_loader = { diff --git a/src/vendorcode/google/chromeos/fmap.c b/src/vendorcode/google/chromeos/fmap.c index 538b8c3..e0ad7c0 100644 --- a/src/vendorcode/google/chromeos/fmap.c +++ b/src/vendorcode/google/chromeos/fmap.c @@ -20,97 +20,131 @@ #include <stdint.h> #include <stddef.h> #include <string.h> +#include <arch/early_variables.h> #include <console/console.h> +#include <region.h> #include "fmap.h"
-/* Find FMAP data structure in ROM. +/* * See http://code.google.com/p/flashmap/ for more information on FMAP. */ -const struct fmap *fmap_find(void) -{ - /* FIXME: Get rid of the hard codes. The "easy" way would be to - * do a binary search, but since ROM accesses are slow, we don't - * want to spend a lot of time looking for the FMAP. An elegant - * solution would be to store a pointer to the FMAP in the CBFS - * master header; that would require some more changes to cbfstool - * and possibly cros_bundle_firmware. - */ - - /* wrapping around 0x100000000 */ - const struct fmap *fmap = (void *) - (CONFIG_FLASHMAP_OFFSET - CONFIG_ROM_SIZE);
- if (memcmp(fmap, FMAP_SIGNATURE, sizeof(FMAP_SIGNATURE)-1)) { - printk(BIOS_DEBUG, "No FMAP found at %p.\n", fmap); - return NULL; - } +/* + * The reason for the global state is that FMAP needs to be available in + * romstage on x86 machines. This necessitates keeping some global variables + * around because there is no malloc or global modifiable data without being + * placed in BSS. This code relies on CAR globals being migrated in romstage. + */
- printk(BIOS_DEBUG, "FMAP: Found "%s" version %d.%d at %p.\n", - fmap->name, fmap->ver_major, fmap->ver_minor, fmap); - printk(BIOS_DEBUG, "FMAP: base = %llx size = %x #areas = %d\n", - (unsigned long long)fmap->base, fmap->size, fmap->nareas); +#define FMAP_REGION_NAME "fmap" + +struct fmap_state { + int registered; + int num_entries; +}; + +static struct fmap_state g_fmap_state CAR_GLOBAL; +static const offset_t fmap_offset = CONFIG_FLASHMAP_OFFSET;
- return fmap; +static inline struct fmap_state *get_state(void) +{ + return car_get_var_ptr(&g_fmap_state); }
-const struct fmap_area *find_fmap_area(const struct fmap *fmap, - const char name[]) +static const struct region_device fmap_rdev = { + .region = { + .offset = 0, + .size = CONFIG_ROM_SIZE, + }, +}; + +/* Return < 0 on failure, 0 on success */ +static int fmap_init(void) { - const struct fmap_area *area = NULL; - - if (fmap) { - int i; - for (i = 0; i < fmap->nareas; i++) { - if (!strcmp((const char *)fmap->areas[i].name, name)) { - area = &fmap->areas[i]; - break; - } - } - } + struct fmap_state *gs; + rhandle_t rh; + struct fmap *fmap;
- if (area) { - printk(BIOS_DEBUG, "FMAP: area %s found\n", name); - printk(BIOS_DEBUG, "FMAP: offset: %x\n", area->offset); - printk(BIOS_DEBUG, "FMAP: size: %d bytes\n", area->size); - } else { - printk(BIOS_DEBUG, "FMAP: area %s not found\n", name); + gs = get_state(); + + if (gs->registered) + return 1; + + /* Execute this function only once. Default to invalid. */ + gs->registered = 1; + rh = region_locate(BOOTRO_REGION_NAME); + + if (!rhandle_valid(rh)) + return -1; + + fmap = region_mmap(rh, fmap_offset, sizeof(*fmap)); + + if (fmap == NULL) + return -1; + + if (memcmp(fmap, FMAP_SIGNATURE, sizeof(FMAP_SIGNATURE)-1)) { + printk(BIOS_DEBUG, "No FMAP found at %zx.\n", fmap_offset); + region_munmap(rh, fmap); + return -1; } + printk(BIOS_DEBUG, "FMAP: Found "%s" version %d.%d at %zx.\n", + fmap->name, fmap->ver_major, fmap->ver_minor, fmap_offset); + printk(BIOS_DEBUG, "FMAP: base = %llx size = %x #areas = %d\n", + (unsigned long long)fmap->base, fmap->size, fmap->nareas); + + gs->num_entries = fmap->nareas; + region_munmap(rh, fmap); + + /* Register fmap device under the boot region. */ + if (!rhandle_valid(region_register_subregion(FMAP_REGION_NAME, + &fmap_rdev, rh))) + return -1;
- return area; + return 0; }
-int find_fmap_entry(const char name[], void **pointer) +int fmap_locate(const char *name, offset_t *offset, size_t *size) { -#ifndef __PRE_RAM__ - static -#endif - const struct fmap *fmap = NULL; - const struct fmap_area *area; - void *base = NULL; + rhandle_t rh; + struct fmap_area *areas; + struct fmap_state * gs; + int i; + int ret = -1;
- if (!fmap) - fmap = fmap_find(); + rh = region_locate(FMAP_REGION_NAME); + gs = get_state();
- area = find_fmap_area(fmap, name); + if (!rhandle_valid(rh) || gs == NULL) + return ret;
- if (!area) - return -1; + areas = region_mmap(rh, sizeof(struct fmap) + fmap_offset, + gs->num_entries * sizeof(*areas)); + + if (areas == NULL) { + printk(BIOS_DEBUG, "FMAP: could not map areas.\n"); + return ret; + } + + for (i = 0; i < gs->num_entries; i++) { + if (strncmp((char *)&areas[i].name[0], name, FMAP_STRLEN)) + continue; + printk(BIOS_DEBUG, "FMAP: area %s found\n", name); + printk(BIOS_DEBUG, "FMAP: offset: %x\n", areas[i].offset); + printk(BIOS_DEBUG, "FMAP: size: %d bytes\n", areas[i].size);
- /* Right now cros_bundle_firmware does not write a valid - * base address into the FMAP. Hence, if base is 0, assume - * 4GB-8MB as base address. - */ - if (fmap->base) { - base = (void *)(unsigned long)fmap->base; - printk(BIOS_DEBUG, "FMAP: %s base at %p\n", name, base); - } else { - base = (void *)(0 - CONFIG_ROM_SIZE); - printk(BIOS_WARNING, "FMAP: No valid base address, using" - " 0x%p\n", base); + *offset = areas[i].offset; + *size = areas[i].size; + ret = 0; + break; }
- *pointer = (void*) ((u32)base + area->offset); - printk(BIOS_DEBUG, "FMAP: %s at %p (offset %x)\n", - name, *pointer, area->offset); - return area->size; + region_munmap(rh, areas); + + return ret; +} + +void fmap_initialize(void) +{ + if (fmap_init() < 0) + printk(BIOS_DEBUG, "FMAP region wasn't registered.\n"); } diff --git a/src/vendorcode/google/chromeos/fmap.h b/src/vendorcode/google/chromeos/fmap.h index a3d2abd..de935e7 100644 --- a/src/vendorcode/google/chromeos/fmap.h +++ b/src/vendorcode/google/chromeos/fmap.h @@ -37,6 +37,7 @@ #define FLASHMAP_LIB_FMAP_H__
#include <stdint.h> +#include <region.h>
#define FMAP_SIGNATURE "__FMAP__" #define FMAP_VER_MAJOR 1 /* this header's FMAP minor version */ @@ -70,10 +71,24 @@ struct fmap { struct fmap_area areas[]; } __attribute__((packed));
+#if IS_ENABLED(CONFIG_CHROMEOS) +/* + * Initialize the fmap state within coreboot. + */ +void fmap_initialize(void); + +/* + * Locate a region by name within the fmap. offset and size are filled in + * relative to the boot media the global fmap is located. Returns 0 on + * success, < 0 when unable to find the region. + */ +int fmap_locate(const char *name, offset_t *offset, size_t *size); +#else /* IS_ENABLED(CONFIG_CHROMEOS) */ +static inline void fmap_initialize(void) {} +static inline int fmap_locate(const char *name, offset_t *offset, size_t *size) +{ + return -1; +} +#endif /* IS_ENABLED(CONFIG_CHROMEOS) */
-/* coreboot specific function prototypes */ -const struct fmap *fmap_find(void); -const struct fmap_area *find_fmap_area(const struct fmap *fmap, - const char name[]); -int find_fmap_entry(const char name[], void **pointer); #endif /* FLASHMAP_LIB_FMAP_H__*/ diff --git a/src/vendorcode/google/chromeos/vboot_loader.c b/src/vendorcode/google/chromeos/vboot_loader.c index 0c5220a..98bc1a5 100644 --- a/src/vendorcode/google/chromeos/vboot_loader.c +++ b/src/vendorcode/google/chromeos/vboot_loader.c @@ -21,6 +21,7 @@ #include <stdint.h> #include <stddef.h> #include <cbfs.h> +#include <cbfs_core.h> #include <cbmem.h> #include <console/console.h> #include <console/vtxprintf.h> @@ -45,7 +46,8 @@ static void vboot_run_stub(struct vboot_context *context) }; void (*entry)(struct vboot_context *context);
- if (rmodule_stage_load_from_cbfs(&rmod_stage)) { + if (rmodule_stage_load_from_cbfs(cbfs_default_descriptor(), + &rmod_stage)) { printk(BIOS_DEBUG, "Could not load vboot stub.\n"); goto out; } @@ -74,6 +76,30 @@ static void fatal_error(void) hard_reset(); }
+static void locate_region(const char *name, void **parea, uint32_t *psize) +{ + offset_t offset; + size_t size; + rhandle_t rh; + + *psize = 0; + *parea = NULL; + + if (fmap_locate(name, &offset, &size)) + return; + + rh = region_locate(BOOTRO_REGION_NAME); + if (!rhandle_valid(rh)) + return; + + /* + * This leaks the mappings on anything but x86 systems which have + * CBFS memory-mapped. + */ + *psize = size; + *parea = region_mmap(rh, offset, size); +} + static void vboot_invoke_wrapper(struct vboot_handoff *vboot_handoff) { VbCommonParams cparams; @@ -105,20 +131,17 @@ static void vboot_invoke_wrapper(struct vboot_handoff *vboot_handoff) context.cparams = &cparams; context.fparams = &fparams;
- cparams.gbb_size = find_fmap_entry("GBB", &cparams.gbb_data); + locate_region("GBB", &cparams.gbb_data, &cparams.gbb_size); cparams.shared_data_blob = &vboot_handoff->shared_data[0]; cparams.shared_data_size = VB_SHARED_DATA_MIN_SIZE; cparams.caller_context = &context;
- fparams.verification_size_A = - find_fmap_entry("VBLOCK_A", &fparams.verification_block_A); - fparams.verification_size_B = - find_fmap_entry("VBLOCK_B", &fparams.verification_block_B); - - context.fw_a_size = - find_fmap_entry("FW_MAIN_A", (void **)&context.fw_a); - context.fw_b_size = - find_fmap_entry("FW_MAIN_B", (void **)&context.fw_b); + locate_region("VBLOCK_A", &fparams.verification_block_A, + &fparams.verification_size_A); + locate_region("VBLOCK_B", &fparams.verification_block_B, + &fparams.verification_size_B); + locate_region("FW_MAIN_A", (void **)&context.fw_a, &context.fw_a_size); + locate_region("FW_MAIN_B", (void **)&context.fw_b, &context.fw_b_size);
/* Check all fmap entries. */ if (context.fw_a == NULL || context.fw_b == NULL || @@ -195,7 +218,7 @@ static void *vboot_load_ramstage(uint32_t cbmem_id, const char *name,
stage = (void *)fwc->address;
- if (rmodule_stage_load(&rmod_load, stage)) { + if (rmodule_stage_load(&rmod_load, stage, &stage[1])) { vboot_handoff->selected_firmware = VB_SELECT_FIRMWARE_READONLY; printk(BIOS_DEBUG, "Could not load ramstage region.\n"); return NULL;