coreboot-kgpe-d16/src/lib/fmap.c
Julius Werner c827c9b216 fmap: Map less space in fallback path without CBFS verification
This is a fixup to CB:78914 which inadvertently broke the RK3288 SoC.
Unfortunately we can only accommodate very little PRERAM_CBFS_CACHE in
the tiny SRAM for that chip, so we would not be able to map an entire
FMAP. Solve this problem for now by mapping less space when CBFS
verification is disabled, and disallowing CBFS verification on that SoC.

Change-Id: I2e419d157dc26bb70a6dd62e44dc6607e51cf791
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/78971
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Yu-Ping Wu <yupingso@google.com>
2023-11-13 14:19:01 +00:00

332 lines
7.6 KiB
C

/* SPDX-License-Identifier: GPL-2.0-only */
#include <boot_device.h>
#include <cbmem.h>
#include <console/console.h>
#include <fmap.h>
#include <metadata_hash.h>
#include <stddef.h>
#include <string.h>
#include <symbols.h>
#include <endian.h>
#include "fmap_config.h"
/*
* See http://code.google.com/p/flashmap/ for more information on FMAP.
*/
static int fmap_print_once;
static struct region_device fmap_cache;
#define print_once(...) do { \
if (!fmap_print_once) \
printk(__VA_ARGS__); \
} while (0)
uint64_t get_fmap_flash_offset(void)
{
return FMAP_OFFSET;
}
static int verify_fmap(const struct fmap *fmap)
{
if (memcmp(fmap->signature, FMAP_SIGNATURE, sizeof(fmap->signature))) {
if (ENV_INITIAL_STAGE)
printk(BIOS_ERR, "Invalid FMAP at %#x\n", FMAP_OFFSET);
return -1;
}
static bool done = false;
if (!CONFIG(CBFS_VERIFICATION) || !ENV_INITIAL_STAGE || done)
return 0; /* Only need to check hash in first stage. */
/* On error we need to die right here, lest we risk a TOCTOU attack where the cache is
filled with a tampered FMAP but the later fallback path is fed a valid one. */
if (metadata_hash_verify_fmap(fmap, FMAP_SIZE) != VB2_SUCCESS)
die("FMAP verification failure");
done = true;
return 0;
}
static void report(const struct fmap *fmap)
{
print_once(BIOS_DEBUG, "FMAP: Found \"%s\" version %d.%d at %#x.\n",
fmap->name, fmap->ver_major, fmap->ver_minor, FMAP_OFFSET);
print_once(BIOS_DEBUG, "FMAP: base = %#llx size = %#x #areas = %d\n",
(long long)le64toh(fmap->base), le32toh(fmap->size),
le16toh(fmap->nareas));
fmap_print_once = 1;
}
static void setup_preram_cache(struct region_device *cache_rdev)
{
if (CONFIG(NO_FMAP_CACHE))
return;
/* No need to use FMAP cache in SMM */
if (ENV_SMM)
return;
if (!ENV_ROMSTAGE_OR_BEFORE) {
/* We get here if ramstage makes an FMAP access before calling
cbmem_initialize(). We should avoid letting it come to that,
so print a warning. */
print_once(BIOS_WARNING,
"WARNING: Post-RAM FMAP access too early for cache!\n");
return;
}
struct fmap *fmap = (struct fmap *)_fmap_cache;
if (!(ENV_INITIAL_STAGE)) {
/* NOTE: This assumes that the first stage will make
at least one FMAP access (usually from finding CBFS). */
if (!verify_fmap(fmap))
goto register_cache;
/* This shouldn't happen, so no point providing a fallback path here. */
die("FMAP cache corrupted?!\n");
}
/* In case we fail below, make sure the cache is invalid. */
memset(fmap->signature, 0, sizeof(fmap->signature));
boot_device_init();
const struct region_device *boot_rdev = boot_device_ro();
if (!boot_rdev)
return;
/* memlayout statically guarantees that the FMAP_CACHE is big enough. */
if (rdev_readat(boot_rdev, fmap, FMAP_OFFSET, FMAP_SIZE) != FMAP_SIZE)
return;
if (verify_fmap(fmap))
return;
report(fmap);
register_cache:
rdev_chain_mem(cache_rdev, fmap, FMAP_SIZE);
}
static int find_fmap_directory(struct region_device *fmrd)
{
const struct region_device *boot;
struct fmap *fmap;
size_t offset = FMAP_OFFSET;
/* Try FMAP cache first */
if (!region_device_sz(&fmap_cache))
setup_preram_cache(&fmap_cache);
if (region_device_sz(&fmap_cache))
return rdev_chain_full(fmrd, &fmap_cache);
/* Cache setup in pre-RAM stages can't fail, unless flash I/O in general failed. */
if (!CONFIG(NO_FMAP_CACHE) && ENV_ROMSTAGE_OR_BEFORE)
return -1;
boot_device_init();
boot = boot_device_ro();
if (boot == NULL)
return -1;
fmap = rdev_mmap(boot, offset,
CONFIG(CBFS_VERIFICATION) ? FMAP_SIZE : sizeof(struct fmap));
if (fmap == NULL)
return -1;
if (verify_fmap(fmap)) {
rdev_munmap(boot, fmap);
return -1;
}
report(fmap);
rdev_munmap(boot, fmap);
return rdev_chain(fmrd, boot, offset, FMAP_SIZE);
}
int fmap_locate_area_as_rdev(const char *name, struct region_device *area)
{
struct region ar;
if (fmap_locate_area(name, &ar))
return -1;
return boot_device_ro_subregion(&ar, area);
}
int fmap_locate_area_as_rdev_rw(const char *name, struct region_device *area)
{
struct region ar;
if (fmap_locate_area(name, &ar))
return -1;
return boot_device_rw_subregion(&ar, area);
}
int fmap_locate_area(const char *name, struct region *ar)
{
struct region_device fmrd;
size_t offset;
if (name == NULL || ar == NULL)
return -1;
if (find_fmap_directory(&fmrd))
return -1;
/* Start reading the areas just after fmap header. */
offset = sizeof(struct fmap);
while (1) {
struct fmap_area *area;
area = rdev_mmap(&fmrd, offset, sizeof(*area));
if (area == NULL)
return -1;
if (strcmp((const char *)area->name, name)) {
rdev_munmap(&fmrd, area);
offset += sizeof(struct fmap_area);
continue;
}
printk(BIOS_DEBUG, "FMAP: area %s found @ %x (%d bytes)\n",
name, le32toh(area->offset), le32toh(area->size));
ar->offset = le32toh(area->offset);
ar->size = le32toh(area->size);
rdev_munmap(&fmrd, area);
return 0;
}
printk(BIOS_DEBUG, "FMAP: area %s not found\n", name);
return -1;
}
int fmap_find_region_name(const struct region * const ar,
char name[FMAP_STRLEN])
{
struct region_device fmrd;
size_t offset;
if (name == NULL || ar == NULL)
return -1;
if (find_fmap_directory(&fmrd))
return -1;
/* Start reading the areas just after fmap header. */
offset = sizeof(struct fmap);
while (1) {
struct fmap_area *area;
area = rdev_mmap(&fmrd, offset, sizeof(*area));
if (area == NULL)
return -1;
if ((ar->offset != le32toh(area->offset)) ||
(ar->size != le32toh(area->size))) {
rdev_munmap(&fmrd, area);
offset += sizeof(struct fmap_area);
continue;
}
printk(BIOS_DEBUG, "FMAP: area (%zx, %zx) found, named %s\n",
ar->offset, ar->size, area->name);
memcpy(name, area->name, FMAP_STRLEN);
rdev_munmap(&fmrd, area);
return 0;
}
printk(BIOS_DEBUG, "FMAP: area (%zx, %zx) not found\n",
ar->offset, ar->size);
return -1;
}
ssize_t fmap_read_area(const char *name, void *buffer, size_t size)
{
struct region_device rdev;
if (fmap_locate_area_as_rdev(name, &rdev))
return -1;
return rdev_readat(&rdev, buffer, 0,
MIN(size, region_device_sz(&rdev)));
}
ssize_t fmap_overwrite_area(const char *name, const void *buffer, size_t size)
{
struct region_device rdev;
if (fmap_locate_area_as_rdev_rw(name, &rdev))
return -1;
if (size > region_device_sz(&rdev))
return -1;
if (rdev_eraseat(&rdev, 0, region_device_sz(&rdev)) < 0)
return -1;
return rdev_writeat(&rdev, buffer, 0, size);
}
static void fmap_register_cbmem_cache(void)
{
const struct cbmem_entry *e;
/* Find the FMAP cache installed by previous stage */
e = cbmem_entry_find(CBMEM_ID_FMAP);
/* Don't set fmap_cache so that find_fmap_directory will use regular path */
if (!e)
return;
rdev_chain_mem(&fmap_cache, cbmem_entry_start(e), cbmem_entry_size(e));
}
/*
* The main reason to copy the FMAP into CBMEM is to make it available to the
* OS on every architecture. As side effect use the CBMEM copy as cache.
*/
static void fmap_add_cbmem_cache(void)
{
struct region_device fmrd;
if (find_fmap_directory(&fmrd))
return;
/* Reloads the FMAP even on ACPI S3 resume */
const size_t s = region_device_sz(&fmrd);
struct fmap *fmap = cbmem_add(CBMEM_ID_FMAP, s);
if (!fmap) {
printk(BIOS_ERR, "Failed to allocate CBMEM\n");
return;
}
const ssize_t ret = rdev_readat(&fmrd, fmap, 0, s);
if (ret != s) {
printk(BIOS_ERR, "Failed to read FMAP into CBMEM\n");
cbmem_entry_remove(cbmem_entry_find(CBMEM_ID_FMAP));
return;
}
}
static void fmap_setup_cbmem_cache(int unused)
{
if (ENV_CREATES_CBMEM)
fmap_add_cbmem_cache();
/* Finally advertise the cache for the current stage */
fmap_register_cbmem_cache();
}
CBMEM_READY_HOOK(fmap_setup_cbmem_cache);