ec3a462d03
For reasons explained in a previous CL, it might be necessary to "load" a file from CBFS in place. The loading code in CBFS was, however, zeroing the area of memory the stage was about to be loaded into. When the CBFS data is located elsewhere this works fine, but when it isn't you end up clobbering the data you're trying to load. Also, there's no reason to zero memory we're about to load something into or have just loaded something into. This change makes it so that we only zero out the portion of the memory between what was loaded/decompressed and the final size of the stage in memory. Change-Id: If34df16bd74b2969583e11ef6a26eb4065842f57 Signed-off-by: Gabe Black <gabeblack@chromium.org> Reviewed-on: http://review.coreboot.org/3579 Tested-by: build bot (Jenkins) Reviewed-by: Patrick Georgi <patrick@georgi-clan.de>
257 lines
7.4 KiB
C
257 lines
7.4 KiB
C
/*
|
|
* This file is part of the libpayload project.
|
|
*
|
|
* Copyright (C) 2011 secunet Security Networks AG
|
|
* 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
|
|
|
|
#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
|
|
#else
|
|
# define CBFS_CORE_WITH_LZMA
|
|
# include <lib.h>
|
|
#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)
|
|
#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
|
|
|
|
#if defined(CONFIG_CBFS_HEADER_ROM_OFFSET) && (CONFIG_CBFS_HEADER_ROM_OFFSET)
|
|
# define CBFS_HEADER_ROM_ADDRESS (CONFIG_CBFS_HEADER_ROM_OFFSET)
|
|
#else
|
|
/* ugly hack: this assumes that "media" exists
|
|
in the scope where the macro is used. */
|
|
static uint32_t fetch_x86_header(struct cbfs_media *media)
|
|
{
|
|
uint32_t *header_ptr = media->map(media, 0xfffffffc, 4);
|
|
return *header_ptr;
|
|
}
|
|
# define CBFS_HEADER_ROM_ADDRESS fetch_x86_header(media)
|
|
#endif
|
|
|
|
#include "cbfs_core.c"
|
|
|
|
#ifndef __SMM__
|
|
static inline int tohex4(unsigned int c)
|
|
{
|
|
return (c <= 9) ? (c + '0') : (c - 10 + 'a');
|
|
}
|
|
|
|
static void tohex16(unsigned int val, char* dest)
|
|
{
|
|
dest[0] = tohex4(val>>12);
|
|
dest[1] = tohex4((val>>8) & 0xf);
|
|
dest[2] = tohex4((val>>4) & 0xf);
|
|
dest[3] = tohex4(val & 0xf);
|
|
}
|
|
|
|
void *cbfs_load_optionrom(struct cbfs_media *media, uint16_t vendor,
|
|
uint16_t device, void *dest)
|
|
{
|
|
char name[17] = "pciXXXX,XXXX.rom";
|
|
struct cbfs_optionrom *orom;
|
|
uint8_t *src;
|
|
|
|
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 * cbfs_load_stage(struct cbfs_media *media, const char *name)
|
|
{
|
|
struct cbfs_stage *stage = (struct cbfs_stage *)
|
|
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;
|
|
uint32_t final_size;
|
|
|
|
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);
|
|
|
|
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;
|
|
|
|
memset((void *)((uintptr_t)stage->load + final_size), 0,
|
|
stage->memlen - final_size);
|
|
|
|
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
|