ROM images with a FMAP may have multiple CBFS. Scan all available CBFS so that, say, a SeaBIOS bootable image doesn't have to be in the main CBFS.
Coreboot puts the FMAP location in the BOOT_MEDIA_PARAMS entry in the coreboot table. We can grab that and can all regions for CBFS files.
Signed-off-by: Ben Gardner gardner.ben@gmail.com --- src/fw/coreboot.c | 148 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 124 insertions(+), 24 deletions(-)
diff --git a/src/fw/coreboot.c b/src/fw/coreboot.c index 4957b80..0309832 100644 --- a/src/fw/coreboot.c +++ b/src/fw/coreboot.c @@ -86,6 +86,41 @@ struct cbmem_console { } PACKED; static struct cbmem_console *cbcon = NULL;
+#define CB_TAG_BOOT_MEDIA_PARAMS 0x0030 +struct cb_boot_media_params { + u32 tag; + u32 size; + /* offsets are relative to start of boot media */ + u64 fmap_offset; + u64 cbfs_offset; + u64 cbfs_size; + u64 boot_media_size; +}; + +#define FMAP_SIGNATURE "__FMAP__" +#define FMAP_VER_MAJOR 1 /* this header's FMAP minor version */ +#define FMAP_VER_MINOR 1 /* this header's FMAP minor version */ +#define FMAP_STRLEN 32 /* maximum length for strings, */ + +/* Mapping of volatile and static regions in firmware binary */ +struct fmap_area { + u32 offset; /* offset relative to base */ + u32 size; /* size in bytes */ + u8 name[FMAP_STRLEN]; /* descriptive name */ + u16 flags; /* flags for this area */ +} PACKED; + +struct fmap { + u8 signature[8]; /* "__FMAP__" (0x5F5F464D41505F5F) */ + u8 ver_major; /* major version */ + u8 ver_minor; /* minor version */ + u64 base; /* address of the firmware binary */ + u32 size; /* size of firmware binary in bytes */ + u8 name[FMAP_STRLEN]; /* name of this firmware binary */ + u16 nareas; /* number of areas described by fmap_areas[] below */ + struct fmap_area areas[]; +} PACKED; + static u16 ipchksum(char *buf, int count) { @@ -159,6 +194,39 @@ find_cb_table(void)
static struct cb_memory *CBMemTable; const char *CBvendor = "", *CBpart = ""; +struct fmap *CBfmap; + +static void +fmap_init(struct fmap *fm) +{ + if (memcmp(fm->signature, FMAP_SIGNATURE, sizeof(fm->signature)) != 0) { + dprintf(1, "FMAP: bad signature\n"); + return; + } + if ((fm->ver_major != FMAP_VER_MAJOR) || + (fm->ver_minor != FMAP_VER_MINOR)) { + dprintf(1, "FMAP: bad version %d.%d\n", fm->ver_major, fm->ver_minor); + return; + } + + /* print out the FMAP contents */ + void *base = (void *)(uintptr_t)fm->base; + dprintf(1, "FMAP: %p 0x%08x 0x%04x %s\n", + base, fm->size, fm->nareas, fm->name); + + int idx; + for (idx = 0; idx < fm->nareas; idx++) { + struct fmap_area *fa = &fm->areas[idx]; + const u8 *ptr = base + fa->offset; + int is_cbfs = (fa->size > 4096) && (memcmp(ptr, "LARCHIVE", 8) == 0); + dprintf(is_cbfs ? 1 : 3, + " [%d] %p 0x%08x 0x%04x %s%s\n", + idx, ptr, fa->size, fa->flags, fa->name, + is_cbfs ? "(CBFS)" : ""); + } + /* looks good -- save the FMAP pointer for later */ + CBfmap = fm; +}
// Populate max ram and e820 map info by scanning for a coreboot table. void @@ -205,6 +273,17 @@ coreboot_preinit(void) dprintf(1, "Found mainboard %s %s\n", CBvendor, CBpart); }
+ struct cb_boot_media_params *cbbmb = find_cb_subtable(cbh, CB_TAG_BOOT_MEDIA_PARAMS); + if (cbbmb) { + /* ~0 is used to indicate 'not present' */ + if ((cbbmb->fmap_offset + 1) != 0) { + /* Assuming the ROM map ends at 4 GB */ + u8 *base = (u8 *)(u32)~(cbbmb->boot_media_size - 1); + struct fmap *fm = (void *)(base + cbbmb->fmap_offset); + fmap_init(fm); + } + } + return;
fail: @@ -414,30 +493,11 @@ process_links_file(void) free(links); }
-void -coreboot_cbfs_init(void) +static void +coreboot_cbfs_scan(struct cbfs_file *fhdr, u32 romsize, u32 align) { - if (!CONFIG_COREBOOT_FLASH) - return; - - struct cbfs_header *hdr = *(void **)(CONFIG_CBFS_LOCATION - 4); - if ((u32)hdr & 0x03) { - dprintf(1, "Invalid CBFS pointer %p\n", hdr); - return; - } - if (CONFIG_CBFS_LOCATION && (u32)hdr > CONFIG_CBFS_LOCATION) - // Looks like the pointer is relative to CONFIG_CBFS_LOCATION - hdr = (void*)hdr + CONFIG_CBFS_LOCATION; - if (hdr->magic != cpu_to_be32(CBFS_HEADER_MAGIC)) { - dprintf(1, "Unable to find CBFS (ptr=%p; got %x not %x)\n" - , hdr, hdr->magic, cpu_to_be32(CBFS_HEADER_MAGIC)); - return; - } - dprintf(1, "Found CBFS header at %p\n", hdr); + u32 romstart = (u32)fhdr;
- u32 romsize = be32_to_cpu(hdr->romsize); - u32 romstart = CONFIG_CBFS_LOCATION - romsize; - struct cbfs_file *fhdr = (void*)romstart + be32_to_cpu(hdr->offset); for (;;) { if ((u32)fhdr - romstart > romsize) break; @@ -464,8 +524,48 @@ coreboot_cbfs_init(void) } romfile_add(&cfile->file);
- fhdr = (void*)ALIGN((u32)cfile->data + cfile->rawsize - , be32_to_cpu(hdr->align)); + fhdr = (void*)ALIGN((u32)cfile->data + cfile->rawsize, align); + } +} + +void +coreboot_cbfs_init(void) +{ + if (!CONFIG_COREBOOT_FLASH) + return; + + if (CBfmap) { + void *base = (void *)(uintptr_t)CBfmap->base; + int idx; + + for (idx = 0; idx < CBfmap->nareas; idx++) { + struct fmap_area *fa = &CBfmap->areas[idx]; + struct cbfs_file *cf = (struct cbfs_file *)(base + fa->offset); + if (fa->size > 4096) { + coreboot_cbfs_scan(cf, fa->size, 64); + } + } + } else { + /* no FMAP - read CBFS header location from last 4 bytes of ROM */ + struct cbfs_header *hdr = *(void **)(CONFIG_CBFS_LOCATION - 4); + if ((u32)hdr & 0x03) { + dprintf(1, "Invalid CBFS pointer %p\n", hdr); + return; + } + if (CONFIG_CBFS_LOCATION && (u32)hdr > CONFIG_CBFS_LOCATION) + // Looks like the pointer is relative to CONFIG_CBFS_LOCATION + hdr = (void*)hdr + CONFIG_CBFS_LOCATION; + if (hdr->magic != cpu_to_be32(CBFS_HEADER_MAGIC)) { + dprintf(1, "Unable to find CBFS (ptr=%p; got %x not %x)\n", + hdr, hdr->magic, cpu_to_be32(CBFS_HEADER_MAGIC)); + return; + } + dprintf(1, "Found CBFS header at %p\n", hdr); + + u32 romsize = be32_to_cpu(hdr->romsize); + u32 romstart = CONFIG_CBFS_LOCATION - romsize; + struct cbfs_file *fhdr = (void*)romstart + be32_to_cpu(hdr->offset); + coreboot_cbfs_scan(fhdr, romsize, be32_to_cpu(hdr->align)); }
process_links_file();
Hi,
- if ((fm->ver_major != FMAP_VER_MAJOR) ||
(fm->ver_minor != FMAP_VER_MINOR)) {
dprintf(1, "FMAP: bad version %d.%d\n", fm->ver_major, fm->ver_minor);
return;
- }
What is the policy on major/minor versioning?
If minor version bumps are supposed to be backward compatible we can make the minor version test a bit less strict.
-void -coreboot_cbfs_init(void) +static void +coreboot_cbfs_scan(struct cbfs_file *fhdr, u32 romsize, u32 align)
Factoring out the fbfs scan code into the new coreboot_cbfs_scan function should be a separate patch.
cheers, Gerd
On Wed, Mar 09, 2016 at 12:19:43PM -0600, Ben Gardner wrote:
ROM images with a FMAP may have multiple CBFS. Scan all available CBFS so that, say, a SeaBIOS bootable image doesn't have to be in the main CBFS.
Coreboot puts the FMAP location in the BOOT_MEDIA_PARAMS entry in the coreboot table. We can grab that and can all regions for CBFS files.
Thanks. Can you provide some additional background information on how this is intended to work. If multiple CBFS locations are found, is there a chance of file name conflicts? Is the fmap stuff used for primary/backup rom images and if so how will seabios account for that if it scans both images?
Also, can you describe the high level use case you had in mind.
-Kevin
Hi Kevin,
I plan to have the default CBFS contain only coreboot and SeabIOS and a few payloads (coreinfo, memtest86, PXE) that I don't intend to change often. That would be protected with a hash and would be kept small for performance reasons. ChromeOS appears to do something similar and take it a step further and write-protect that "read only" region of flash.
The other CBFS would contain larger images that I'm likely to change, such as Linux.
There is a chance of name conflicts and that may cause problems. In my use case, the answer is "don't do that".
From a random sampling of FMD files in the coreboot tree
(google/glados/chromeos.fmd), I see that ChromeOS uses three: FW_MAIN_A, FW_MAIN_B, and the default COREBOOT. Assuming FW_MAIN_A and FW_MAIN_B contain files with similar names, that would cause confusion if they use SeaBIOS. But I think that ChromeOS is switching to use the Depthcharge bootloader.
So, maybe this wouldn't work well for everyone.
Ben
On Thu, Mar 10, 2016 at 9:10 AM, Kevin O'Connor kevin@koconnor.net wrote:
On Wed, Mar 09, 2016 at 12:19:43PM -0600, Ben Gardner wrote:
ROM images with a FMAP may have multiple CBFS. Scan all available CBFS so that, say, a SeaBIOS bootable image doesn't have to be in the main CBFS.
Coreboot puts the FMAP location in the BOOT_MEDIA_PARAMS entry in the coreboot table. We can grab that and can all regions for CBFS files.
Thanks. Can you provide some additional background information on how this is intended to work. If multiple CBFS locations are found, is there a chance of file name conflicts? Is the fmap stuff used for primary/backup rom images and if so how will seabios account for that if it scans both images?
Also, can you describe the high level use case you had in mind.
-Kevin