libpayload: New CBFS to support multiple firmware media sources.

Upgrade CBFS in libpayload to use new media-based implementation from coreboot
( http://review.coreboot.org/#/c/2182/ ).

Old CBFS functions (cbfs_find, cbfs_find_file, get_cbfs_header) are still
supported, although the recommended way is to use new CBFS API.

To migrate your existing x86 payload source:
	- Change cbfs_find to cbfs_get_file
	- Change cbfs_find_file to cbfs_get_file_content
	- Prefix every CBFS call with a CBFS_DEFAULT_MEDIA argument.

Ex, char *jpeg_data = cbfs_find_file("splash.jpg", CBFS_TYPE_BOOTSPLASH);
 => char *jpeg_data = cbfs_get_file_content(
		CBFS_DEFAULT_MEDIA, "splash.jpg", CBFS_TYPE_BOOTSPLASH);

The legacy setup_cbfs_from_{ram,flash} is also supported, although the better
equivalent is to make a new media instance:
	struct cbfs_media ram_media;
	init_cbfs_ram_media(&ram_media, start, size);
	char *data = cbfs_get_file_content(&ram_media, "myfile", my_type);

Verified by being successfully linked with filo.

Change-Id: If797bc7e3ba975d7e3be905c59424f7a93b8ce11
Signed-off-by: Hung-Te Lin <hungte@chromium.org>
Reviewed-on: http://review.coreboot.org/2191
Tested-by: build bot (Jenkins)
Reviewed-by: Paul Menzel <paulepanter@users.sourceforge.net>
Reviewed-by: Anton Kochkov <anton.kochkov@gmail.com>
This commit is contained in:
Hung-Te Lin 2013-01-25 12:42:40 +08:00 committed by Anton Kochkov
parent 02ae0bf8fe
commit d01d0368f4
9 changed files with 688 additions and 172 deletions

View File

@ -33,5 +33,7 @@ libc-y += timer.c coreboot.c util.S
libc-y += exec.S virtual.c libc-y += exec.S virtual.c
libc-y += string.c libc-y += string.c
libcbfs-$(CONFIG_CBFS) += rom_media.c
# Multiboot support is configurable # Multiboot support is configurable
libc-$(CONFIG_MULTIBOOT) += multiboot.c libc-$(CONFIG_MULTIBOOT) += multiboot.c

View File

@ -0,0 +1,111 @@
/*
* This file is part of the libpayload project.
*
* Copyright (C) 2013 Google, Inc.
*
* 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.
*/
#define LIBPAYLOAD
#include <cbfs.h>
#include <string.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 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 *x86_rom_unmap(struct cbfs_media *media, const void *address) {
return NULL;
}
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 int x86_rom_close(struct cbfs_media *media) {
return 0;
}
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
}
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);
}

View File

@ -1,16 +1,92 @@
#include <arch/types.h> /*
* This file is part of the libpayload project.
*
* Copyright (C) 2008 Jordan Crouse <jordan@cosmicpenguin.net>
* Copyright (C) 2013 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.
*
* 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
* ---------------------------------------------------------------------------
* 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.
* ---------------------------------------------------------------------------
*/
#ifndef _CBFS_H_
#define _CBFS_H_
#include <cbfs_core.h>
/* legacy APIs */
const struct cbfs_header *get_cbfs_header(void);
struct cbfs_file *cbfs_find(const char *name);
void *cbfs_find_file(const char *name, int type);
int cbfs_execute_stage(struct cbfs_media *media, const char *name);
void *cbfs_load_optionrom(struct cbfs_media *media, uint16_t vendor,
uint16_t device, void * dest);
void *cbfs_load_payload(struct cbfs_media *media, const char *name);
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;
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);
void *cbfs_simple_buffer_unmap(struct cbfs_simple_buffer *buffer,
const void *address);
// Utility functions
int run_address(void *f);
/* Defined in src/lib/selfboot.c */
struct lb_memory;
int selfboot(struct lb_memory *mem, struct cbfs_payload *payload);
/* Defined in individual arch / board implementation. */
int init_default_cbfs_media(struct cbfs_media *media);
/* FIXME: workaround for coreboot/libpayload Kconfig differences */
#ifndef CONFIG_ARCH_ARMV7
#define CONFIG_ARCH_ARMV7 0
#endif
#ifndef CONFIG_ARCH_POWERPC
#define CONFIG_ARCH_POWERPC 0
#endif
#ifndef CONFIG_ARCH_X86
#define CONFIG_ARCH_X86 0
#endif #endif
#include "cbfs_core.h"
void setup_cbfs_from_ram(void* start, uint32_t size);
void setup_cbfs_from_flash(void);

View File

@ -1,5 +1,5 @@
/* /*
* This file is part of the coreboot project. * This file is part of the libpayload project.
* *
* Copyright (C) 2008 Jordan Crouse <jordan@cosmicpenguin.net> * Copyright (C) 2008 Jordan Crouse <jordan@cosmicpenguin.net>
* Copyright (C) 2012 Google, Inc. * Copyright (C) 2012 Google, Inc.
@ -50,6 +50,10 @@
#ifndef _CBFS_CORE_H_ #ifndef _CBFS_CORE_H_
#define _CBFS_CORE_H_ #define _CBFS_CORE_H_
#include <endian.h>
#include <stddef.h>
#include <stdint.h>
/** These are standard values for the known compression /** These are standard values for the known compression
alogrithms that coreboot knows about for stages and alogrithms that coreboot knows about for stages and
payloads. Of course, other CBFS users can use whatever payloads. Of course, other CBFS users can use whatever
@ -74,24 +78,18 @@
#define CBFS_COMPONENT_CMOS_DEFAULT 0xaa #define CBFS_COMPONENT_CMOS_DEFAULT 0xaa
#define CBFS_COMPONENT_CMOS_LAYOUT 0x01aa #define CBFS_COMPONENT_CMOS_LAYOUT 0x01aa
/** this is the master cbfs header - it need to be
located somewhere in the bootblock. Where it
actually lives is up to coreboot. On x86, a
pointer to this header will live at 0xFFFFFFFC,
so we can easily find it. */
#define CBFS_HEADER_MAGIC 0x4F524243 #define CBFS_HEADER_MAGIC 0x4F524243
#if CONFIG_ARCH_X86
#define CBFS_HEADPTR_ADDR 0xFFFFFFFC
#elif CONFIG_ARCH_ARMV7
/* FIXME: This could also be 0xFFFF0000 with HIVECS enabled */
#define CBFS_HEADPTR_ADDR 0x0000000C
#endif
#define CBFS_HEADER_VERSION1 0x31313131 #define CBFS_HEADER_VERSION1 0x31313131
#define CBFS_HEADER_VERSION2 0x31313132 #define CBFS_HEADER_VERSION2 0x31313132
#define CBFS_HEADER_VERSION CBFS_HEADER_VERSION2 #define CBFS_HEADER_VERSION CBFS_HEADER_VERSION2
#define CBFS_HEADER_INVALID_ADDRESS ((void*)(0xffffffff))
/** this is the master cbfs header - it need to be located somewhere available
to bootblock (to load romstage). Where it actually lives is up to coreboot.
On x86, a pointer to this header will live at 0xFFFFFFFC.
For other platforms, you need to define CONFIG_CBFS_HEADER_ROM_OFFSET */
struct cbfs_header { struct cbfs_header {
uint32_t magic; uint32_t magic;
uint32_t version; uint32_t version;
@ -181,16 +179,49 @@ struct cbfs_optionrom {
#define CBFS_NAME(_c) (((char *) (_c)) + sizeof(struct cbfs_file)) #define CBFS_NAME(_c) (((char *) (_c)) + sizeof(struct cbfs_file))
#define CBFS_SUBHEADER(_p) ( (void *) ((((uint8_t *) (_p)) + ntohl((_p)->offset))) ) #define CBFS_SUBHEADER(_p) ( (void *) ((((uint8_t *) (_p)) + ntohl((_p)->offset))) )
/* returns pointer to file inside CBFS or NULL */ #define CBFS_MEDIA_INVALID_MAP_ADDRESS ((void*)(0xffffffff))
struct cbfs_file *cbfs_find(const char *name); #define CBFS_DEFAULT_MEDIA ((void*)(0x0))
/* returns pointer to file data inside CBFS */ /* Media for CBFS to load files. */
void *cbfs_get_file(const char *name); struct cbfs_media {
/* returns pointer to file data inside CBFS after if type is correct */ /* implementation dependent context, to hold resource references */
void *cbfs_find_file(const char *name, int type); 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);
/* returns 0 on success, -1 on failure */ /* returns 0 on success, -1 on failure */
int cbfs_decompress(int algo, void *src, void *dst, int len); int cbfs_decompress(int algo, void *src, void *dst, int len);
struct cbfs_header *get_cbfs_header(void);
/* 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 #endif

View File

@ -32,4 +32,5 @@
#include <arch/types.h> #include <arch/types.h>
typedef unsigned long uintptr_t; typedef unsigned long uintptr_t;
typedef long intptr_t;
#endif #endif

View File

@ -28,4 +28,5 @@
## ##
libcbfs-$(CONFIG_CBFS) += cbfs.c libcbfs-$(CONFIG_CBFS) += cbfs.c
libcbfs-$(CONFIG_CBFS) += ram_media.c

View File

@ -2,6 +2,7 @@
* This file is part of the libpayload project. * This file is part of the libpayload project.
* *
* Copyright (C) 2011 secunet Security Networks AG * Copyright (C) 2011 secunet Security Networks AG
* Copyright (C) 2013 Google, Inc.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions * modification, are permitted provided that the following conditions
@ -26,80 +27,221 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE. * SUCH DAMAGE.
*/ */
#define LIBPAYLOAD
#include <endian.h> #ifdef LIBPAYLOAD
#include <stdio.h> # include <libpayload-config.h>
#include <string.h>
#include <cbfs.h>
# ifdef CONFIG_LZMA # ifdef CONFIG_LZMA
#define CBFS_CORE_WITH_LZMA
# include <lzma.h> # include <lzma.h>
# define CBFS_CORE_WITH_LZMA
# endif
# define CBFS_MINI_BUILD
#elif defined(__SMM__)
# define CBFS_MINI_BUILD
#else
# define CBFS_CORE_WITH_LZMA
# include <lib.h>
#endif #endif
#include <cbfs.h>
#include <string.h>
#ifdef LIBPAYLOAD
# include <stdio.h>
# define DEBUG(x...)
# define LOG(x...) printf(x)
# define ERROR(x...) printf(x) # define ERROR(x...) printf(x)
#define LOG(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
static uint32_t host_virt_to_phys(void *addr); #if defined(CONFIG_CBFS_HEADER_ROM_OFFSET) && (CONFIG_CBFS_HEADER_ROM_OFFSET)
static void *host_phys_to_virt(uint32_t addr); # define CBFS_HEADER_ROM_ADDRESS (CONFIG_CBFS_HEADER_ROM_OFFSET)
#else
uint32_t romstart(void); // Indirect address: only works on 32bit top-aligned systems.
uint32_t romend(void); # define CBFS_HEADER_ROM_ADDRESS (*(uint32_t*)0xfffffffc)
#endif
#include <arch/virtual.h>
static uint32_t host_virt_to_phys(void *addr)
{
return virt_to_phys(addr);
}
static void *host_phys_to_virt(uint32_t addr)
{
return phys_to_virt(addr);
}
#undef virt_to_phys
#undef phys_to_virt
uint32_t (*virt_to_phys)(void *) = host_virt_to_phys;
void* (*phys_to_virt)(uint32_t) = host_phys_to_virt;
uint32_t _romstart = 0xffffffff;
uint32_t _romend = 0;
uint32_t romstart(void)
{
return _romstart;
}
uint32_t romend(void)
{
return _romend;
}
#include "cbfs_core.c" #include "cbfs_core.c"
static uint32_t ram_cbfs_offset; #ifndef __SMM__
static inline int tohex4(unsigned int c)
static uint32_t ram_virt_to_phys(void *addr)
{ {
return (uint32_t)addr - ram_cbfs_offset; return (c <= 9) ? (c + '0') : (c - 10 + 'a');
} }
static void *ram_phys_to_virt(uint32_t addr) static void tohex16(unsigned int val, char* dest)
{ {
return (void*)addr + ram_cbfs_offset; dest[0] = tohex4(val>>12);
dest[1] = tohex4((val>>8) & 0xf);
dest[2] = tohex4((val>>4) & 0xf);
dest[3] = tohex4(val & 0xf);
} }
void setup_cbfs_from_ram(void* start, uint32_t size) void *cbfs_load_optionrom(struct cbfs_media *media, uint16_t vendor,
uint16_t device, void *dest)
{ {
/* assumes rollover */ char name[17] = "pciXXXX,XXXX.rom";
ram_cbfs_offset = (uint32_t)start + size; struct cbfs_optionrom *orom;
virt_to_phys = ram_virt_to_phys; uint8_t *src;
phys_to_virt = ram_phys_to_virt;
tohex16(vendor, name+3);
tohex16(device, name+8);
orom = (struct cbfs_optionrom *)
cbfs_get_file_content(media, name, CBFS_TYPE_OPTIONROM);
if (orom == 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.
*/
/* BUG: the cbfstool is (not yet) including a cbfs_optionrom header */
src = (uint8_t*)orom; // + sizeof(struct cbfs_optionrom);
if (! dest)
return src;
if (cbfs_decompress(ntohl(orom->compression),
src,
dest,
ntohl(orom->len)))
return NULL;
return dest;
} }
void setup_cbfs_from_flash(void) void * cbfs_load_stage(struct cbfs_media *media, const char *name)
{ {
virt_to_phys = host_virt_to_phys; struct cbfs_stage *stage = (struct cbfs_stage *)
phys_to_virt = host_phys_to_virt; cbfs_get_file_content(media, name, CBFS_TYPE_STAGE);
/* this is a mess. There is no ntohll. */
/* for now, assume compatible byte order until we solve this. */
uint32_t entry;
if (stage == NULL)
return (void *) -1;
LOG("loading stage %s @ 0x%x (%d bytes), entry @ 0x%llx\n",
name,
(uint32_t) stage->load, stage->memlen,
stage->entry);
memset((void *) (uint32_t) stage->load, 0, stage->memlen);
if (cbfs_decompress(stage->compression,
((unsigned char *) stage) +
sizeof(struct cbfs_stage),
(void *) (uint32_t) stage->load,
stage->len))
return (void *) -1;
DEBUG("stage loaded.\n");
entry = stage->entry;
// entry = ntohll(stage->entry);
return (void *) entry;
} }
int cbfs_execute_stage(struct cbfs_media *media, const char *name)
{
struct cbfs_stage *stage = (struct cbfs_stage *)
cbfs_get_file_content(media, name, CBFS_TYPE_STAGE);
if (stage == NULL)
return 1;
if (ntohl(stage->compression) != CBFS_COMPRESS_NONE) {
LOG("Unable to run %s: Compressed file"
"Not supported for in-place execution\n", name);
return 1;
}
/* FIXME: This isn't right */
LOG("run @ %p\n", (void *) ntohl((uint32_t) stage->entry));
return run_address((void *)(uintptr_t)ntohll(stage->entry));
}
void *cbfs_load_payload(struct cbfs_media *media, const char *name)
{
return (struct cbfs_payload *)cbfs_get_file_content(
media, name, CBFS_TYPE_PAYLOAD);
}
struct cbfs_file *cbfs_find(const char *name) {
return cbfs_get_file(CBFS_DEFAULT_MEDIA, name);
}
void *cbfs_find_file(const char *name, int type) {
return cbfs_get_file_content(CBFS_DEFAULT_MEDIA, name, type);
}
const struct cbfs_header *get_cbfs_header(void) {
return cbfs_get_header(CBFS_DEFAULT_MEDIA);
}
/* Simple buffer */
void *cbfs_simple_buffer_map(struct cbfs_simple_buffer *buffer,
struct cbfs_media *media,
size_t offset, size_t count) {
void *address = buffer->buffer + buffer->allocated;;
DEBUG("simple_buffer_map(offset=%d, count=%d): "
"allocated=%d, size=%d, last_allocate=%d\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=%d, size=%d, last_allocate=%d\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;
}
/**
* 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.
*/
int run_address(void *f)
{
int (*v) (void);
v = f;
return v();
}
#endif

View File

@ -2,6 +2,7 @@
* This file is part of the libpayload project. * This file is part of the libpayload project.
* *
* Copyright (C) 2011 secunet Security Networks AG * Copyright (C) 2011 secunet Security Networks AG
* Copyright (C) 2013 Google, Inc.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions * modification, are permitted provided that the following conditions
@ -27,119 +28,153 @@
* SUCH DAMAGE. * SUCH DAMAGE.
*/ */
/* The CBFS core requires a couple of #defines or functions to adapt it to the target environment: /* The CBFS core requires a couple of #defines or functions to adapt it to the
* target environment:
* *
* CBFS_CORE_WITH_LZMA (must be #define) * CBFS_CORE_WITH_LZMA (must be #define)
* if defined, ulzma() must exist for decompression of data streams * if defined, ulzma() must exist for decompression of data streams
* *
* phys_to_virt(x), virt_to_phys(x) * CBFS_HEADER_ROM_ADDRESS
* translate physical addresses to virtual and vice versa * ROM address (offset) of CBFS header. Underlying CBFS media may interpret
* can be idempotent if no mapping is necessary. * it in other way so we call this "address".
* *
* ERROR(x...) * ERROR(x...)
* print an error message x (in printf format) * print an error message x (in printf format)
* *
* LOG(x...) * LOG(x...)
* print a message x (in printf format)
*
* DEBUG(x...)
* print a debug message x (in printf format) * print a debug message x (in printf format)
* *
* romstart()
* returns the start address of the ROM image, or 0xffffffff if ROM is
* top-aligned. This is a physical address.
*
* romend()
* returns the highest address of the ROM image + 1, for use if
* romstart() == 0xffffffff. This is a physical address.
*/ */
#include <cbfs_core.h> #include <cbfs.h>
#include <string.h>
/* returns a pointer to CBFS master header, or CBFS_HEADER_INVALID_ADDRESS
/* returns pointer to master header or 0xffffffff if not found */ * on failure */
struct cbfs_header *get_cbfs_header(void) const struct cbfs_header *cbfs_get_header(struct cbfs_media *media)
{ {
struct cbfs_header *header; const struct cbfs_header *header;
struct cbfs_media default_media;
/* find header */ if (media == CBFS_DEFAULT_MEDIA) {
if (romstart() == 0xffffffff) { media = &default_media;
header = (struct cbfs_header*)phys_to_virt(*(uint32_t*)phys_to_virt(romend() + CBFS_HEADPTR_ADDR)); if (init_default_cbfs_media(media) != 0) {
} else { ERROR("Failed to initializee default media.\n");
// FIXME: where's the master header on ARM (our current bottom-aligned platform)? return NULL;
header = NULL;
} }
}
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)) { if (CBFS_HEADER_MAGIC != ntohl(header->magic)) {
ERROR("Could not find valid CBFS master header at %p: %x vs %x.\n", header, 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) { if (header->magic == 0xffffffff) {
ERROR("Maybe ROM is not mapped properly?\n"); ERROR("Maybe ROM is not mapped properly?\n");
} }
return (void*)0xffffffff; return CBFS_HEADER_INVALID_ADDRESS;
} }
return header; return header;
} }
// by must be power-of-two
#define CBFS_ALIGN(val, by) (typeof(val))((uint32_t)(val + by - 1) & (uint32_t)~(by - 1))
#define CBFS_ALIGN_UP(val, by) CBFS_ALIGN(val + 1, by)
/* public API starts here*/ /* public API starts here*/
struct cbfs_file *cbfs_find(const char *name) struct cbfs_file *cbfs_get_file(struct cbfs_media *media, const char *name)
{ {
struct cbfs_header *header = get_cbfs_header(); const char *file_name;
if (header == (void*)0xffffffff) return NULL; uint32_t offset, align, romsize, name_len;
const struct cbfs_header *header;
struct cbfs_file file, *file_ptr;
struct cbfs_media default_media;
LOG("Searching for %s\n", name); if (media == CBFS_DEFAULT_MEDIA) {
media = &default_media;
void *data, *dataend, *origdata; if (init_default_cbfs_media(media) != 0) {
/* find first entry */ ERROR("Failed to initializee default media.\n");
if (romstart() == 0xffffffff) { return NULL;
data = (void*)phys_to_virt(romend()) - ntohl(header->romsize) + ntohl(header->offset); }
dataend = (void*)phys_to_virt(romend());
} else {
data = (void*)phys_to_virt(romstart()) + ntohl(header->offset);
dataend = (void*)phys_to_virt(romstart()) + ntohl(header->romsize);
} }
dataend -= ntohl(header->bootblocksize);
int align = ntohl(header->align); if (CBFS_HEADER_INVALID_ADDRESS == (header = cbfs_get_header(media)))
return NULL;
origdata = data; // Logical offset (for source media) of first file.
while ((data < (dataend - 1)) && (data >= origdata)) { offset = ntohl(header->offset);
struct cbfs_file *file = data; align = ntohl(header->align);
if (memcmp(CBFS_FILE_MAGIC, file->magic, strlen(CBFS_FILE_MAGIC)) != 0) { romsize = ntohl(header->romsize);
// no file header found. corruption?
// proceed in aligned steps to resynchronize // TODO header->romsize seems broken now on ARM. Remove this when it's
LOG("ERROR: No file header found at %p, attempting to recover by searching for header\n", data); // fixed.
data = phys_to_virt(CBFS_ALIGN_UP(virt_to_phys(data), align)); #if defined(CONFIG_ARCH_ARMV7) && CONFIG_ARCH_ARMV7
romsize = CONFIG_ROM_SIZE;
#endif
DEBUG("offset: 0x%x, align: %d, romsize: %d\n", offset, align, romsize);
LOG("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%xx - "
"try next aligned address: 0x%x.\n", offset,
offset + new_align);
offset += new_align;
continue; continue;
} }
LOG("Check %s\n", CBFS_NAME(file)); name_len = ntohl(file.offset) - sizeof(file);
if (strcmp(CBFS_NAME(file), name) == 0) { DEBUG(" - load entry 0x%x file name (%d bytes)...\n", offset,
return file; name_len);
}
void *olddata = data; // load file name (arbitrary length).
data = phys_to_virt(CBFS_ALIGN(virt_to_phys(data) + ntohl(file->len) + ntohl(file->offset), align)); file_name = (const char*)media->map(
if (olddata > data) { media, offset + sizeof(file), name_len);
LOG("Something is wrong here. File chain moved from %p to %p\n", olddata, data); if (file_name == CBFS_MEDIA_INVALID_MAP_ADDRESS) {
return NULL; 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);
LOG("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 {
LOG(" (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);
ERROR("ERROR: Not found.\n");
return NULL; return NULL;
} }
void *cbfs_get_file(const char *name) void *cbfs_get_file_content(struct cbfs_media *media, const char *name, int type)
{ {
struct cbfs_file *file = cbfs_find(name); struct cbfs_file *file = cbfs_get_file(media, name);
if (file == NULL) {
ERROR("Could not find file '%s'.\n", name);
return NULL;
}
return (void*)CBFS_SUBHEADER(file);
}
void *cbfs_find_file(const char *name, int type)
{
struct cbfs_file *file = cbfs_find(name);
if (file == NULL) { if (file == NULL) {
ERROR("Could not find file '%s'.\n", name); ERROR("Could not find file '%s'.\n", name);
@ -147,7 +182,8 @@ void *cbfs_find_file(const char *name, int type)
} }
if (ntohl(file->type) != type) { if (ntohl(file->type) != type) {
ERROR("File '%s' is of type %x, but we requested %x.\n", name, ntohl(file->type), type); ERROR("File '%s' is of type %x, but we requested %x.\n", name,
ntohl(file->type), type);
return NULL; return NULL;
} }
@ -168,7 +204,9 @@ int cbfs_decompress(int algo, void *src, void *dst, int len)
return -1; return -1;
#endif #endif
default: default:
ERROR("tried to decompress %d bytes with algorithm #%x, but that algorithm id is unsupported.\n", len, algo); ERROR("tried to decompress %d bytes with algorithm #%x,"
"but that algorithm id is unsupported.\n", len,
algo);
return -1; return -1;
} }
} }

View File

@ -0,0 +1,114 @@
/*
* This file is part of the libpayload project.
*
* Copyright (C) 2013 Google, Inc.
*
* 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.
*/
#include <cbfs.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
// Implementation of a media source based on given memory buffer.
struct ram_media {
char *start;
size_t size;
};
static int ram_open(struct cbfs_media *media) {
return 0;
}
static void *ram_map(struct cbfs_media *media, size_t offset, size_t count) {
struct ram_media *m = (struct ram_media*)media->context;
if (offset + count >= m->size) {
printf("ERROR: ram_map: request out of range (0x%x+0x%x)\n",
offset, count);
return NULL;
}
return (void*)(m->start + offset);
}
static void *ram_unmap(struct cbfs_media *media, const void *address) {
return NULL;
}
static size_t ram_read(struct cbfs_media *media, void *dest, size_t offset,
size_t count) {
void *ptr = ram_map(media, offset, count);
memcpy(dest, ptr, count);
ram_unmap(media, ptr);
return count;
}
static int ram_close(struct cbfs_media *media) {
return 0;
}
int init_cbfs_ram_media(struct cbfs_media *media, void *start, size_t size);
int init_cbfs_ram_media(struct cbfs_media *media, void *start, size_t size) {
// TODO Find a way to release unused media. Maybe adding media->destroy.
struct ram_media *m = (struct ram_media*)malloc(sizeof(*m));
m->start = start;
m->size = size;
media->context = (void*)m;
media->open = ram_open;
media->close = ram_close;
media->map = ram_map;
media->unmap = ram_unmap;
media->read = ram_read;
return 0;
}
// Legacy setup_cbfs_from_*.
static int is_default_cbfs_media_initialized;
static struct cbfs_media default_cbfs_media;
int setup_cbfs_from_ram(void *start, uint32_t size);
int setup_cbfs_from_ram(void *start, uint32_t size) {
int result = init_cbfs_ram_media(&default_cbfs_media, start, size);
if (result == 0)
is_default_cbfs_media_initialized = 1;
return result;
}
extern int libpayload_init_default_cbfs_media(struct cbfs_media *media);
int setup_cbfs_from_flash(void);
int setup_cbfs_from_flash(void) {
int result = libpayload_init_default_cbfs_media(&default_cbfs_media);
if (result == 0)
is_default_cbfs_media_initialized = 1;
return result;
}
int init_default_cbfs_media(struct cbfs_media *media) {
int result = 0;
if (is_default_cbfs_media_initialized != 1) {
result = setup_cbfs_from_flash();
}
if (result == 0)
memcpy(media, &default_cbfs_media, sizeof(*media));
return result;
}