Commit 84cd9327 authored by Gabe Black's avatar Gabe Black Committed by Tom Rini

fs: Add a Coreboot Filesystem (CBFS) driver and commands

This change adds CBFS support and some commands to use it to u-boot. These
commands are:

cbfsinit - Initialize CBFS support and pull all metadata into RAM. The end of
the ROM is an optional parameter which defaults to the standard 0xffffffff and
can be used to support multiple CBFSes in a system. The last one set up with
cbfsinit is the one that will be used.

cbfsinfo - Print information from the CBFS header.

cbfsls - Print out the size, type, and name of all the files in the current
CBFS. Recognized types are translated into symbolic names.

cbfsload - Load a file from CBFS into memory. Like the similar command for fat
filesystems, you can optionally provide a maximum size.

Support for CBFS is compiled in when the CONFIG_CMD_CBFS option is specified.

The CBFS driver can also be used programmatically from within u-boot.

If u-boot needs something out of CBFS very early before the heap is
configured, it won't be able to use the normal CBFS support which caches some
information in memory it allocates from the heap. The
cbfs_file_find_uncached function searches a CBFS instance without touching
the heap.
Signed-off-by: default avatarGabe Black <gabeblack@google.com>
Signed-off-by: default avatarStefan Reinauer <reinauer@chromium.org>
Signed-off-by: default avatarSimon Glass <sjg@chromium.org>
parent 9936be31
......@@ -260,7 +260,8 @@ LIBS-y += drivers/net/npe/libnpe.o
endif
LIBS-$(CONFIG_OF_EMBED) += dts/libdts.o
LIBS-y += arch/$(ARCH)/lib/lib$(ARCH).o
LIBS-y += fs/cramfs/libcramfs.o \
LIBS-y += fs/cbfs/libcbfs.o \
fs/cramfs/libcramfs.o \
fs/ext4/libext4fs.o \
fs/fat/libfat.o \
fs/fdos/libfdos.o \
......
......@@ -1316,6 +1316,13 @@ The following options need to be configured:
This will also enable the command "fatwrite" enabling the
user to write files to FAT.
CBFS (Coreboot Filesystem) support
CONFIG_CMD_CBFS
Define this to enable support for reading from a Coreboot
filesystem. Available commands are cbfsinit, cbfsinfo, cbfsls
and cbfsload.
- Keyboard Support:
CONFIG_ISA_KEYBOARD
......
......@@ -70,6 +70,7 @@ COBJS-$(CONFIG_CMD_BMP) += cmd_bmp.o
COBJS-$(CONFIG_CMD_BOOTLDR) += cmd_bootldr.o
COBJS-$(CONFIG_CMD_BOOTSTAGE) += cmd_bootstage.o
COBJS-$(CONFIG_CMD_CACHE) += cmd_cache.o
COBJS-$(CONFIG_CMD_CBFS) += cmd_cbfs.o
COBJS-$(CONFIG_CMD_CONSOLE) += cmd_console.o
COBJS-$(CONFIG_CMD_CPLBINFO) += cmd_cplbinfo.o
COBJS-$(CONFIG_DATAFLASH_MMC_SELECT) += cmd_dataflash_mmc_mux.o
......
/*
* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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; either version 2 of
* the License, or (at your option) any later version.
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
/*
* CBFS commands
*/
#include <common.h>
#include <command.h>
#include <cbfs.h>
int do_cbfs_init(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
{
uintptr_t end_of_rom = 0xffffffff;
char *ep;
if (argc > 2) {
printf("usage: cbfsls [end of rom]>\n");
return 0;
}
if (argc == 2) {
end_of_rom = (int)simple_strtoul(argv[1], &ep, 16);
if (*ep) {
puts("\n** Invalid end of ROM **\n");
return 1;
}
}
file_cbfs_init(end_of_rom);
if (file_cbfs_result != CBFS_SUCCESS) {
printf("%s.\n", file_cbfs_error());
return 1;
}
return 0;
}
U_BOOT_CMD(
cbfsinit, 2, 0, do_cbfs_init,
"initialize the cbfs driver",
"[end of rom]\n"
" - Initialize the cbfs driver. The optional 'end of rom'\n"
" parameter specifies where the end of the ROM is that the\n"
" CBFS is in. It defaults to 0xFFFFFFFF\n"
);
int do_cbfs_fsload(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
{
const struct cbfs_cachenode *file;
unsigned long offset;
unsigned long count;
char buf[12];
long size;
if (argc < 3) {
printf("usage: cbfsload <addr> <filename> [bytes]\n");
return 1;
}
/* parse offset and count */
offset = simple_strtoul(argv[1], NULL, 16);
if (argc == 4)
count = simple_strtoul(argv[3], NULL, 16);
else
count = 0;
file = file_cbfs_find(argv[2]);
if (!file) {
if (file_cbfs_result == CBFS_FILE_NOT_FOUND)
printf("%s: %s\n", file_cbfs_error(), argv[2]);
else
printf("%s.\n", file_cbfs_error());
return 1;
}
printf("reading %s\n", file_cbfs_name(file));
size = file_cbfs_read(file, (void *)offset, count);
printf("\n%ld bytes read\n", size);
sprintf(buf, "%lX", size);
setenv("filesize", buf);
return 0;
}
U_BOOT_CMD(
cbfsload, 4, 0, do_cbfs_fsload,
"load binary file from a cbfs filesystem",
"<addr> <filename> [bytes]\n"
" - load binary file 'filename' from the cbfs to address 'addr'\n"
);
int do_cbfs_ls(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
{
const struct cbfs_cachenode *file = file_cbfs_get_first();
int files = 0;
if (!file) {
printf("%s.\n", file_cbfs_error());
return 1;
}
printf(" size type name\n");
printf("------------------------------------------\n");
while (file) {
u32 type = file_cbfs_type(file);
char *type_name = NULL;
const char *filename = file_cbfs_name(file);
printf(" %8d", file_cbfs_size(file));
switch (type) {
case CBFS_TYPE_STAGE:
type_name = "stage";
break;
case CBFS_TYPE_PAYLOAD:
type_name = "payload";
break;
case CBFS_TYPE_OPTIONROM:
type_name = "option rom";
break;
case CBFS_TYPE_BOOTSPLASH:
type_name = "boot splash";
break;
case CBFS_TYPE_RAW:
type_name = "raw";
break;
case CBFS_TYPE_VSA:
type_name = "vsa";
break;
case CBFS_TYPE_MBI:
type_name = "mbi";
break;
case CBFS_TYPE_MICROCODE:
type_name = "microcode";
break;
case CBFS_COMPONENT_CMOS_DEFAULT:
type_name = "cmos default";
break;
case CBFS_COMPONENT_CMOS_LAYOUT:
type_name = "cmos layout";
break;
case -1UL:
type_name = "null";
break;
}
if (type_name)
printf(" %16s", type_name);
else
printf(" %16d", type);
if (filename[0])
printf(" %s\n", filename);
else
printf(" %s\n", "(empty)");
file_cbfs_get_next(&file);
files++;
}
printf("\n%d file(s)\n\n", files);
return 0;
}
U_BOOT_CMD(
cbfsls, 1, 1, do_cbfs_ls,
"list files",
" - list the files in the cbfs\n"
);
int do_cbfs_fsinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
{
const struct cbfs_header *header = file_cbfs_get_header();
if (!header) {
printf("%s.\n", file_cbfs_error());
return 1;
}
printf("\n");
printf("CBFS version: %#x\n", header->version);
printf("ROM size: %#x\n", header->rom_size);
printf("Boot block size: %#x\n", header->boot_block_size);
printf("CBFS size: %#x\n",
header->rom_size - header->boot_block_size - header->offset);
printf("Alignment: %d\n", header->align);
printf("Offset: %#x\n", header->offset);
printf("\n");
return 0;
}
U_BOOT_CMD(
cbfsinfo, 1, 1, do_cbfs_fsinfo,
"print information about filesystem",
" - print information about the cbfs filesystem\n"
);
......@@ -22,6 +22,7 @@
#
#
subdirs-$(CONFIG_CMD_CBFS) += cbfs
subdirs-$(CONFIG_CMD_CRAMFS) := cramfs
subdirs-$(CONFIG_CMD_EXT4) += ext4
ifndef CONFIG_CMD_EXT4
......
#
# See file CREDITS for list of people who contributed to this
# project.
#
# 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; either version 2 of
# the License, or (at your option) any later version.
#
# 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., 59 Temple Place, Suite 330, Boston,
# MA 02111-1307 USA
#
include $(TOPDIR)/config.mk
LIB = $(obj)libcbfs.o
COBJS-$(CONFIG_CMD_CBFS) := cbfs.o
SRCS := $(COBJS-y:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS-y))
all: $(LIB)
$(LIB): $(obj).depend $(OBJS)
$(call cmd_link_o_target, $(OBJS))
#########################################################################
# defines $(obj).depend target
include $(SRCTREE)/rules.mk
sinclude $(obj).depend
#########################################################################
/*
* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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; either version 2 of
* the License, or (at your option) any later version.
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <cbfs.h>
#include <malloc.h>
#include <asm/byteorder.h>
enum cbfs_result file_cbfs_result;
const char *file_cbfs_error(void)
{
switch (file_cbfs_result) {
case CBFS_SUCCESS:
return "Success";
case CBFS_NOT_INITIALIZED:
return "CBFS not initialized";
case CBFS_BAD_HEADER:
return "Bad CBFS header";
case CBFS_BAD_FILE:
return "Bad CBFS file";
case CBFS_FILE_NOT_FOUND:
return "File not found";
default:
return "Unknown";
}
}
static const u32 good_magic = 0x4f524243;
static const u8 good_file_magic[] = "LARCHIVE";
static int initialized;
static struct cbfs_header cbfs_header;
static struct cbfs_cachenode *file_cache;
/* Do endian conversion on the CBFS header structure. */
static void swap_header(struct cbfs_header *dest, struct cbfs_header *src)
{
dest->magic = be32_to_cpu(src->magic);
dest->version = be32_to_cpu(src->version);
dest->rom_size = be32_to_cpu(src->rom_size);
dest->boot_block_size = be32_to_cpu(src->boot_block_size);
dest->align = be32_to_cpu(src->align);
dest->offset = be32_to_cpu(src->offset);
}
/* Do endian conversion on a CBFS file header. */
static void swap_file_header(struct cbfs_fileheader *dest,
const struct cbfs_fileheader *src)
{
memcpy(&dest->magic, &src->magic, sizeof(dest->magic));
dest->len = be32_to_cpu(src->len);
dest->type = be32_to_cpu(src->type);
dest->checksum = be32_to_cpu(src->checksum);
dest->offset = be32_to_cpu(src->offset);
}
/*
* Given a starting position in memory, scan forward, bounded by a size, and
* find the next valid CBFS file. No memory is allocated by this function. The
* caller is responsible for allocating space for the new file structure.
*
* @param start The location in memory to start from.
* @param size The size of the memory region to search.
* @param align The alignment boundaries to check on.
* @param newNode A pointer to the file structure to load.
* @param used A pointer to the count of of bytes scanned through,
* including the file if one is found.
*
* @return 1 if a file is found, 0 if one isn't.
*/
static int file_cbfs_next_file(u8 *start, u32 size, u32 align,
struct cbfs_cachenode *newNode, u32 *used)
{
struct cbfs_fileheader header;
*used = 0;
while (size >= align) {
const struct cbfs_fileheader *fileHeader =
(const struct cbfs_fileheader *)start;
u32 name_len;
u32 step;
/* Check if there's a file here. */
if (memcmp(good_file_magic, &(fileHeader->magic),
sizeof(fileHeader->magic))) {
*used += align;
size -= align;
start += align;
continue;
}
swap_file_header(&header, fileHeader);
if (header.offset < sizeof(const struct cbfs_cachenode *) ||
header.offset > header.len) {
file_cbfs_result = CBFS_BAD_FILE;
return -1;
}
newNode->next = NULL;
newNode->type = header.type;
newNode->data = start + header.offset;
newNode->data_length = header.len;
name_len = header.offset - sizeof(struct cbfs_cachenode *);
newNode->name = (char *)fileHeader +
sizeof(struct cbfs_cachenode *);
newNode->name_length = name_len;
newNode->checksum = header.checksum;
step = header.len;
if (step % align)
step = step + align - step % align;
*used += step;
return 1;
}
return 0;
}
/* Look through a CBFS instance and copy file metadata into regular memory. */
static void file_cbfs_fill_cache(u8 *start, u32 size, u32 align)
{
struct cbfs_cachenode *cache_node;
struct cbfs_cachenode *newNode;
struct cbfs_cachenode **cache_tail = &file_cache;
/* Clear out old information. */
cache_node = file_cache;
while (cache_node) {
struct cbfs_cachenode *oldNode = cache_node;
cache_node = cache_node->next;
free(oldNode);
}
file_cache = NULL;
while (size >= align) {
int result;
u32 used;
newNode = (struct cbfs_cachenode *)
malloc(sizeof(struct cbfs_cachenode));
result = file_cbfs_next_file(start, size, align,
newNode, &used);
if (result < 0) {
free(newNode);
return;
} else if (result == 0) {
free(newNode);
break;
}
*cache_tail = newNode;
cache_tail = &newNode->next;
size -= used;
start += used;
}
file_cbfs_result = CBFS_SUCCESS;
}
/* Get the CBFS header out of the ROM and do endian conversion. */
static int file_cbfs_load_header(uintptr_t end_of_rom,
struct cbfs_header *header)
{
struct cbfs_header *header_in_rom;
header_in_rom = (struct cbfs_header *)(uintptr_t)
*(u32 *)(end_of_rom - 3);
swap_header(header, header_in_rom);
if (header->magic != good_magic || header->offset >
header->rom_size - header->boot_block_size) {
file_cbfs_result = CBFS_BAD_HEADER;
return 1;
}
return 0;
}
void file_cbfs_init(uintptr_t end_of_rom)
{
u8 *start_of_rom;
initialized = 0;
if (file_cbfs_load_header(end_of_rom, &cbfs_header))
return;
start_of_rom = (u8 *)(end_of_rom + 1 - cbfs_header.rom_size);
file_cbfs_fill_cache(start_of_rom + cbfs_header.offset,
cbfs_header.rom_size, cbfs_header.align);
if (file_cbfs_result == CBFS_SUCCESS)
initialized = 1;
}
const struct cbfs_header *file_cbfs_get_header(void)
{
if (initialized) {
file_cbfs_result = CBFS_SUCCESS;
return &cbfs_header;
} else {
file_cbfs_result = CBFS_NOT_INITIALIZED;
return NULL;
}
}
const struct cbfs_cachenode *file_cbfs_get_first(void)
{
if (!initialized) {
file_cbfs_result = CBFS_NOT_INITIALIZED;
return NULL;
} else {
file_cbfs_result = CBFS_SUCCESS;
return file_cache;
}
}
void file_cbfs_get_next(const struct cbfs_cachenode **file)
{
if (!initialized) {
file_cbfs_result = CBFS_NOT_INITIALIZED;
file = NULL;
return;
}
if (*file)
*file = (*file)->next;
file_cbfs_result = CBFS_SUCCESS;
}
const struct cbfs_cachenode *file_cbfs_find(const char *name)
{
struct cbfs_cachenode *cache_node = file_cache;
if (!initialized) {
file_cbfs_result = CBFS_NOT_INITIALIZED;
return NULL;
}
while (cache_node) {
if (!strcmp(name, cache_node->name))
break;
cache_node = cache_node->next;
}
if (!cache_node)
file_cbfs_result = CBFS_FILE_NOT_FOUND;
else
file_cbfs_result = CBFS_SUCCESS;
return cache_node;
}
const struct cbfs_cachenode *file_cbfs_find_uncached(uintptr_t end_of_rom,
const char *name)
{
u8 *start;
u32 size;
u32 align;
static struct cbfs_cachenode node;
if (file_cbfs_load_header(end_of_rom, &cbfs_header))
return NULL;
start = (u8 *)(end_of_rom + 1 - cbfs_header.rom_size);
size = cbfs_header.rom_size;
align = cbfs_header.align;
while (size >= align) {
int result;
u32 used;
result = file_cbfs_next_file(start, size, align, &node, &used);
if (result < 0)
return NULL;
else if (result == 0)
break;
if (!strcmp(name, node.name))
return &node;
size -= used;
start += used;
}
file_cbfs_result = CBFS_FILE_NOT_FOUND;
return NULL;
}
const char *file_cbfs_name(const struct cbfs_cachenode *file)
{
file_cbfs_result = CBFS_SUCCESS;
return file->name;
}
u32 file_cbfs_size(const struct cbfs_cachenode *file)
{
file_cbfs_result = CBFS_SUCCESS;
return file->data_length;
}
u32 file_cbfs_type(const struct cbfs_cachenode *file)
{
file_cbfs_result = CBFS_SUCCESS;
return file->type;
}
long file_cbfs_read(const struct cbfs_cachenode *file, void *buffer,
unsigned long maxsize)
{
u32 size;
size = file->data_length;
if (maxsize && size > maxsize)
size = maxsize;
memcpy(buffer, file->data, size);
file_cbfs_result = CBFS_SUCCESS;
return size;
}
/*
* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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; either version 2 of
* the License, or (at your option) any later version.
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#ifndef __CBFS_H
#define __CBFS_H
#include <compiler.h>
#include <linux/compiler.h>
enum cbfs_result {
CBFS_SUCCESS = 0,
CBFS_NOT_INITIALIZED,
CBFS_BAD_HEADER,
CBFS_BAD_FILE,
CBFS_FILE_NOT_FOUND
};
enum cbfs_filetype {
CBFS_TYPE_STAGE = 0x10,
CBFS_TYPE_PAYLOAD = 0x20,
CBFS_TYPE_OPTIONROM = 0x30,
CBFS_TYPE_BOOTSPLASH = 0x40,
CBFS_TYPE_RAW = 0x50,
CBFS_TYPE_VSA = 0x51,
CBFS_TYPE_MBI = 0x52,
CBFS_TYPE_MICROCODE = 0x53,
CBFS_COMPONENT_CMOS_DEFAULT = 0xaa,
CBFS_COMPONENT_CMOS_LAYOUT = 0x01aa
};
struct cbfs_header {
u32 magic;
u32 version;
u32 rom_size;
u32 boot_block_size;
u32 align;
u32 offset;
u32 pad[2];
} __packed;
struct cbfs_fileheader {
u8 magic[8];
u32 len;
u32 type;
u32 checksum;
u32 offset;
} __packed;
struct cbfs_cachenode {
struct cbfs_cachenode *next;
u32 type;
void *data;
u32 data_length;
char *name;
u32 name_length;
u32 checksum;
} __packed;
extern enum cbfs_result file_cbfs_result;
/*
* Return a string describing the most recent error condition.
*
* @return A pointer to the constant string.
*/
const char *file_cbfs_error(void);
/*
* Initialize the CBFS driver and load metadata into RAM.
*
* @param end_of_rom Points to the end of the ROM the CBFS should be read
* from.
*/
void file_cbfs_init(uintptr_t end_of_rom);
/*
* Get the header structure for the current CBFS.
*
* @return A pointer to the constant structure, or NULL if there is none.
*/
const struct cbfs_header *file_cbfs_get_header(void);
/*
* Get a handle for the first file in CBFS.
*
* @return A handle for the first file in CBFS, NULL on error.
*/
const struct cbfs_cachenode *file_cbfs_get_first(void);
/*
* Get a handle to the file after this one in CBFS.
*
* @param file A pointer to the handle to advance.
*/
void file_cbfs_get_next(const struct cbfs_cachenode **file);
/*
* Find a file with a particular name in CBFS.
*