libpayload/libcbfs: add a function to return all the payload headers

For choosing payloads we need to be able to read out all the payload
headers. cbfs_payload_headers() delivers names and all the info available
about any payloads.

BUG=None
TEST=builds, tested on Nyan, works fine.
BRANCH=None

Change-Id: If98437819d53cc01d175234fc7429d6aa3383c2c
Signed-off-by: Ronald G. Minnich <rminnich@google.com>
Reviewed-on: https://chromium-review.googlesource.com/180352
Commit-Queue: Ronald Minnich <rminnich@chromium.org>
Tested-by: Ronald Minnich <rminnich@chromium.org>
Reviewed-by: Puneet Kumar <puneetster@chromium.org>
This commit is contained in:
Ronald G. Minnich 2013-12-04 11:38:53 -08:00 committed by chrome-internal-fetch
commit 8476c04022
2 changed files with 127 additions and 2 deletions

View file

@ -150,8 +150,10 @@ struct cbfs_stage {
uint32_t memlen; /** total length of object in memory */
} __attribute__((packed));
/** this is the sub-header for payload components. Payloads
are loaded by coreboot at the end of the boot process */
/** this is the sub-header for payload components. Payloads are
* loaded by coreboot or other payloads at the end of the boot
* process
*/
struct cbfs_payload_segment {
uint32_t type;
@ -166,6 +168,19 @@ struct cbfs_payload {
struct cbfs_payload_segment segments;
};
/* for returning payload info to (e.g.) bayou.
* Return the file header, payload header, and name.
* It must be done this way because we can't
* assume file headers and names are contiguous.
* Walking the headers can be expensive, so we get all
* the information we can.
*/
struct cbfs_payload_info {
struct cbfs_file file;
struct cbfs_payload payload;
const char *name;
};
#define PAYLOAD_SEGMENT_CODE 0x45444F43
#define PAYLOAD_SEGMENT_DATA 0x41544144
#define PAYLOAD_SEGMENT_BSS 0x20535342
@ -214,6 +229,14 @@ struct cbfs_media {
/* returns pointer to a file entry inside CBFS or NULL */
struct cbfs_file *cbfs_get_file(struct cbfs_media *media, const char *name);
/* Fills the pointer info with an array of payload file info structs.
* Reads at most maxentries items.
* Returns number we found.
*/
int cbfs_payload_headers(struct cbfs_media *media,
struct cbfs_payload_info *info,
int maxentries);
/* 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);

View file

@ -226,6 +226,108 @@ int cbfs_execute_stage(struct cbfs_media *media, const char *name)
return run_address((void *)(uintptr_t)ntohll(stage->entry));
}
/* Get all the payload headers.
* One might be tempted to implement cbfs operations as
* - get all headers into memory
* - scan the headers we got for a [payload, stage, file name]
*
* But: FLASH IO can be a very expensive operation on some systems.
* For that reason, we keep this operation separate from anything else.
* In other words, it is not intended to be a building block; in fact
* the only current use is for the payload choose (Bayou) which does need
* to scan all payloads. In future, we might decide we want to return all
* the headers, but realistically we only need this for payloads right now
* and it makes the bayou code less complex to just return payloads.
* Return a count of the payloads found, up to maxentries payloads.
*/
int cbfs_payload_headers(struct cbfs_media *media,
struct cbfs_payload_info *info,
int maxentries)
{
int cur;
const char *file_name;
uint32_t offset, align, romsize, name_len;
const struct cbfs_header *header;
struct cbfs_file file;
struct cbfs_media default_media;
cur = 0;
if (media == CBFS_DEFAULT_MEDIA) {
media = &default_media;
if (init_default_cbfs_media(media) != 0) {
ERROR("Failed to initialize default media.\n");
return 0;
}
}
if (CBFS_HEADER_INVALID_ADDRESS == (header = cbfs_get_header(media)))
return 0;
// 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.
DEBUG("CBFS location: 0x%x~0x%x, align: %d\n", offset, romsize, align);
media->open(media);
while (cur < maxentries && offset < romsize &&
media->read(media, &file, offset, sizeof(file))
== sizeof(file)) {
DEBUG("cur %d offset %08x\n", cur, offset);
if (memcmp(CBFS_FILE_MAGIC, file.magic,
sizeof(file.magic))) {
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;
}
// load file name (arbitrary length).
// Do it here so we can debug if we want. It's not expensive.
name_len = ntohl(file.offset) - sizeof(file);
file_name = (const char*)media->map(
media, offset + sizeof(file), name_len);
if (file_name == CBFS_MEDIA_INVALID_MAP_ADDRESS) {
DEBUG("ERROR: Failed to get filename: 0x%x.\n", offset);
continue;
}
DEBUG("Check :%s: type %x\n", file_name, ntohl(file.type));
if (ntohl(file.type) == CBFS_TYPE_PAYLOAD){
DEBUG(" - add entry 0x%x file name (%d bytes)...\n",
offset, name_len);
info[cur].file = file;
info[cur].name = file_name;
/* we need the metadata too. */
if (media->read(media, &info[cur].payload, offset,
sizeof(info[cur].payload)) !=
sizeof(info[cur].payload)){
ERROR("ERROR: Failed to get payload"
"info for %s@0x%x.\n",
file_name, offset);
continue;
}
cur++;
}
// Move to next file.
offset += ntohl(file.len) + ntohl(file.offset);
DEBUG("offset moves to %08x\n", offset);
if (offset % align)
offset += align - (offset % align);
}
media->close(media);
return cur;
}
#if !CONFIG_ALT_CBFS_LOAD_PAYLOAD && !defined(__BOOT_BLOCK__)
void *cbfs_load_payload(struct cbfs_media *media, const char *name)
{