drivers/elog: Group event log state information
Group event log state information together to manage them better during different stages of coreboot. BUG=b:117884485 BRANCH=None TEST=Add an event log from romstage, boot to ChromeOS Change-Id: I62792c0f5063c89ad11b512f1777c7ab8a2c13e5 Signed-off-by: Karthikeyan Ramasubramanian <kramasub@google.com> Reviewed-on: https://review.coreboot.org/29541 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Furquan Shaikh <furquan@google.com> Reviewed-by: Aaron Durbin <adurbin@chromium.org>
This commit is contained in:
parent
25b387a50b
commit
07bc08c299
|
@ -16,6 +16,7 @@
|
|||
#if IS_ENABLED(CONFIG_HAVE_ACPI_RESUME)
|
||||
#include <arch/acpi.h>
|
||||
#endif
|
||||
#include <arch/early_variables.h>
|
||||
#include <bootstate.h>
|
||||
#include <cbmem.h>
|
||||
#include <console/console.h>
|
||||
|
@ -42,33 +43,38 @@
|
|||
#endif
|
||||
|
||||
#define NV_NEEDS_ERASE (~(size_t)0)
|
||||
/*
|
||||
* Static variables for ELOG state
|
||||
*/
|
||||
static u16 full_threshold;
|
||||
static u16 shrink_size;
|
||||
|
||||
/*
|
||||
* The non-volatile storage chases the mirrored copy. When nv_last_write
|
||||
* is less than the mirrored last write the non-volatile storage needs
|
||||
* to be updated.
|
||||
*/
|
||||
static size_t mirror_last_write;
|
||||
static size_t nv_last_write;
|
||||
|
||||
static struct region_device nv_dev;
|
||||
/* Device that mirrors the eventlog in memory. */
|
||||
static struct mem_region_device mirror_dev;
|
||||
|
||||
static enum {
|
||||
enum elog_init_state {
|
||||
ELOG_UNINITIALIZED = 0,
|
||||
ELOG_INITIALIZED,
|
||||
ELOG_BROKEN,
|
||||
} elog_initialized = ELOG_UNINITIALIZED;
|
||||
};
|
||||
|
||||
struct elog_state {
|
||||
u16 full_threshold;
|
||||
u16 shrink_size;
|
||||
|
||||
/*
|
||||
* The non-volatile storage chases the mirrored copy. When nv_last_write
|
||||
* is less than the mirrored last write the non-volatile storage needs
|
||||
* to be updated.
|
||||
*/
|
||||
size_t mirror_last_write;
|
||||
size_t nv_last_write;
|
||||
|
||||
struct region_device nv_dev;
|
||||
/* Device that mirrors the eventlog in memory. */
|
||||
struct mem_region_device mirror_dev;
|
||||
|
||||
enum elog_init_state elog_initialized;
|
||||
};
|
||||
|
||||
static struct elog_state g_elog_state CAR_GLOBAL;
|
||||
|
||||
static inline struct region_device *mirror_dev_get(void)
|
||||
{
|
||||
return &mirror_dev.rdev;
|
||||
struct elog_state *es = car_get_var_ptr(&g_elog_state);
|
||||
|
||||
return &es->mirror_dev.rdev;
|
||||
}
|
||||
|
||||
static size_t elog_events_start(void)
|
||||
|
@ -79,7 +85,9 @@ static size_t elog_events_start(void)
|
|||
|
||||
static size_t elog_events_total_space(void)
|
||||
{
|
||||
return region_device_sz(&nv_dev) - elog_events_start();
|
||||
struct elog_state *es = car_get_var_ptr(&g_elog_state);
|
||||
|
||||
return region_device_sz(&es->nv_dev) - elog_events_start();
|
||||
}
|
||||
|
||||
static struct event_header *elog_get_event_buffer(size_t offset, size_t size)
|
||||
|
@ -89,8 +97,10 @@ static struct event_header *elog_get_event_buffer(size_t offset, size_t size)
|
|||
|
||||
static struct event_header *elog_get_next_event_buffer(size_t size)
|
||||
{
|
||||
elog_debug("ELOG: new event at offset 0x%zx\n", mirror_last_write);
|
||||
return elog_get_event_buffer(mirror_last_write, size);
|
||||
struct elog_state *es = car_get_var_ptr(&g_elog_state);
|
||||
|
||||
elog_debug("ELOG: new event at offset 0x%zx\n", es->mirror_last_write);
|
||||
return elog_get_event_buffer(es->mirror_last_write, size);
|
||||
}
|
||||
|
||||
static void elog_put_event_buffer(struct event_header *event)
|
||||
|
@ -100,53 +110,71 @@ static void elog_put_event_buffer(struct event_header *event)
|
|||
|
||||
static size_t elog_mirror_reset_last_write(void)
|
||||
{
|
||||
struct elog_state *es = car_get_var_ptr(&g_elog_state);
|
||||
/* Return previous write value. */
|
||||
size_t prev = mirror_last_write;
|
||||
mirror_last_write = 0;
|
||||
size_t prev = es->mirror_last_write;
|
||||
|
||||
es->mirror_last_write = 0;
|
||||
return prev;
|
||||
}
|
||||
|
||||
static void elog_mirror_increment_last_write(size_t size)
|
||||
{
|
||||
mirror_last_write += size;
|
||||
struct elog_state *es = car_get_var_ptr(&g_elog_state);
|
||||
|
||||
es->mirror_last_write += size;
|
||||
}
|
||||
|
||||
static void elog_nv_reset_last_write(void)
|
||||
{
|
||||
nv_last_write = 0;
|
||||
struct elog_state *es = car_get_var_ptr(&g_elog_state);
|
||||
|
||||
es->nv_last_write = 0;
|
||||
}
|
||||
|
||||
static void elog_nv_increment_last_write(size_t size)
|
||||
{
|
||||
nv_last_write += size;
|
||||
struct elog_state *es = car_get_var_ptr(&g_elog_state);
|
||||
|
||||
es->nv_last_write += size;
|
||||
}
|
||||
|
||||
static void elog_nv_needs_possible_erase(void)
|
||||
{
|
||||
struct elog_state *es = car_get_var_ptr(&g_elog_state);
|
||||
|
||||
/* If last write is 0 it means it is already erased. */
|
||||
if (nv_last_write != 0)
|
||||
nv_last_write = NV_NEEDS_ERASE;
|
||||
if (es->nv_last_write != 0)
|
||||
es->nv_last_write = NV_NEEDS_ERASE;
|
||||
}
|
||||
|
||||
static bool elog_should_shrink(void)
|
||||
{
|
||||
return mirror_last_write >= full_threshold;
|
||||
struct elog_state *es = car_get_var_ptr(&g_elog_state);
|
||||
|
||||
return es->mirror_last_write >= es->full_threshold;
|
||||
}
|
||||
|
||||
static bool elog_nv_needs_erase(void)
|
||||
{
|
||||
return nv_last_write == NV_NEEDS_ERASE;
|
||||
struct elog_state *es = car_get_var_ptr(&g_elog_state);
|
||||
|
||||
return es->nv_last_write == NV_NEEDS_ERASE;
|
||||
}
|
||||
|
||||
static bool elog_nv_needs_update(void)
|
||||
{
|
||||
return nv_last_write != mirror_last_write;
|
||||
struct elog_state *es = car_get_var_ptr(&g_elog_state);
|
||||
|
||||
return es->nv_last_write != es->mirror_last_write;
|
||||
}
|
||||
|
||||
static size_t elog_nv_region_to_update(size_t *offset)
|
||||
{
|
||||
*offset = nv_last_write;
|
||||
return mirror_last_write - nv_last_write;
|
||||
struct elog_state *es = car_get_var_ptr(&g_elog_state);
|
||||
|
||||
*offset = es->nv_last_write;
|
||||
return es->mirror_last_write - es->nv_last_write;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -318,6 +346,7 @@ static void elog_nv_write(size_t offset, size_t size)
|
|||
{
|
||||
void *address;
|
||||
const struct region_device *rdev = mirror_dev_get();
|
||||
struct elog_state *es = car_get_var_ptr(&g_elog_state);
|
||||
|
||||
if (!size)
|
||||
return;
|
||||
|
@ -331,7 +360,7 @@ static void elog_nv_write(size_t offset, size_t size)
|
|||
return;
|
||||
|
||||
/* Write the data to flash */
|
||||
if (rdev_writeat(&nv_dev, address, offset, size) != size)
|
||||
if (rdev_writeat(&es->nv_dev, address, offset, size) != size)
|
||||
printk(BIOS_ERR, "ELOG: NV Write failed at 0x%zx, size 0x%zx\n",
|
||||
offset, size);
|
||||
|
||||
|
@ -344,11 +373,12 @@ static void elog_nv_write(size_t offset, size_t size)
|
|||
*/
|
||||
static void elog_nv_erase(void)
|
||||
{
|
||||
size_t size = region_device_sz(&nv_dev);
|
||||
struct elog_state *es = car_get_var_ptr(&g_elog_state);
|
||||
size_t size = region_device_sz(&es->nv_dev);
|
||||
elog_debug("%s()\n", __func__);
|
||||
|
||||
/* Erase the sectors in this region */
|
||||
if (rdev_eraseat(&nv_dev, 0, size) != size)
|
||||
if (rdev_eraseat(&es->nv_dev, 0, size) != size)
|
||||
printk(BIOS_ERR, "ELOG: erase failure.\n");
|
||||
}
|
||||
|
||||
|
@ -406,11 +436,12 @@ static int elog_scan_flash(void)
|
|||
elog_debug("elog_scan_flash()\n");
|
||||
void *mirror_buffer;
|
||||
const struct region_device *rdev = mirror_dev_get();
|
||||
size_t size = region_device_sz(&nv_dev);
|
||||
struct elog_state *es = car_get_var_ptr(&g_elog_state);
|
||||
size_t size = region_device_sz(&es->nv_dev);
|
||||
|
||||
/* Fill memory buffer by reading from SPI */
|
||||
mirror_buffer = rdev_mmap_full(rdev);
|
||||
if (rdev_readat(&nv_dev, mirror_buffer, 0, size) != size) {
|
||||
if (rdev_readat(&es->nv_dev, mirror_buffer, 0, size) != size) {
|
||||
rdev_munmap(rdev, mirror_buffer);
|
||||
printk(BIOS_ERR, "ELOG: NV read failure.\n");
|
||||
return -1;
|
||||
|
@ -574,8 +605,10 @@ static int elog_prepare_empty(void)
|
|||
|
||||
static int elog_shrink(void)
|
||||
{
|
||||
struct elog_state *es = car_get_var_ptr(&g_elog_state);
|
||||
|
||||
if (elog_should_shrink())
|
||||
return elog_shrink_by_size(shrink_size);
|
||||
return elog_shrink_by_size(es->shrink_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -584,16 +617,18 @@ static int elog_shrink(void)
|
|||
*/
|
||||
static inline u8 *elog_flash_offset_to_address(void)
|
||||
{
|
||||
struct elog_state *es = car_get_var_ptr(&g_elog_state);
|
||||
|
||||
/* Only support memory-mapped devices. */
|
||||
if (!IS_ENABLED(CONFIG_BOOT_DEVICE_MEMORY_MAPPED))
|
||||
return NULL;
|
||||
|
||||
if (!region_device_sz(&nv_dev))
|
||||
if (!region_device_sz(&es->nv_dev))
|
||||
return NULL;
|
||||
|
||||
/* Get a view into the read-only boot device. */
|
||||
return rdev_mmap(boot_device_ro(), region_device_offset(&nv_dev),
|
||||
region_device_sz(&nv_dev));
|
||||
return rdev_mmap(boot_device_ro(), region_device_offset(&es->nv_dev),
|
||||
region_device_sz(&es->nv_dev));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -605,7 +640,8 @@ int elog_smbios_write_type15(unsigned long *current, int handle)
|
|||
struct smbios_type15 *t = (struct smbios_type15 *)*current;
|
||||
int len = sizeof(struct smbios_type15);
|
||||
uintptr_t log_address;
|
||||
size_t elog_size = region_device_sz(&nv_dev);
|
||||
struct elog_state *es = car_get_var_ptr(&g_elog_state);
|
||||
size_t elog_size = region_device_sz(&es->nv_dev);
|
||||
|
||||
if (IS_ENABLED(CONFIG_ELOG_CBMEM)) {
|
||||
/* Save event log buffer into CBMEM for the OS to read */
|
||||
|
@ -659,7 +695,8 @@ static int elog_find_flash(void)
|
|||
{
|
||||
size_t total_size;
|
||||
size_t reserved_space = ELOG_MIN_AVAILABLE_ENTRIES * MAX_EVENT_SIZE;
|
||||
struct region_device *rdev = &nv_dev;
|
||||
struct elog_state *es = car_get_var_ptr(&g_elog_state);
|
||||
struct region_device *rdev = &es->nv_dev;
|
||||
|
||||
elog_debug("%s()\n", __func__);
|
||||
|
||||
|
@ -682,10 +719,10 @@ static int elog_find_flash(void)
|
|||
total_size = MIN(4*KiB, region_device_sz(rdev));
|
||||
rdev_chain(rdev, rdev, 0, total_size);
|
||||
|
||||
full_threshold = total_size - reserved_space;
|
||||
shrink_size = total_size * ELOG_SHRINK_PERCENTAGE / 100;
|
||||
es->full_threshold = total_size - reserved_space;
|
||||
es->shrink_size = total_size * ELOG_SHRINK_PERCENTAGE / 100;
|
||||
|
||||
if (reserved_space > shrink_size) {
|
||||
if (reserved_space > es->shrink_size) {
|
||||
printk(BIOS_ERR, "ELOG: SHRINK_PERCENTAGE too small\n");
|
||||
return -1;
|
||||
}
|
||||
|
@ -698,6 +735,7 @@ static int elog_sync_to_nv(void)
|
|||
size_t offset;
|
||||
size_t size;
|
||||
bool erase_needed;
|
||||
struct elog_state *es = car_get_var_ptr(&g_elog_state);
|
||||
|
||||
/* Determine if any updates are required. */
|
||||
if (!elog_nv_needs_update())
|
||||
|
@ -729,7 +767,7 @@ static int elog_sync_to_nv(void)
|
|||
if (elog_scan_flash() < 0) {
|
||||
printk(BIOS_ERR, "ELOG: Sync back from NV storage failed.\n");
|
||||
elog_debug_dump_buffer("ELOG: Buffer from NV:\n");
|
||||
elog_initialized = ELOG_BROKEN;
|
||||
es->elog_initialized = ELOG_BROKEN;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -758,8 +796,9 @@ int elog_init(void)
|
|||
{
|
||||
void *mirror_buffer;
|
||||
size_t elog_size;
|
||||
struct elog_state *es = car_get_var_ptr(&g_elog_state);
|
||||
|
||||
switch (elog_initialized) {
|
||||
switch (es->elog_initialized) {
|
||||
case ELOG_UNINITIALIZED:
|
||||
break;
|
||||
case ELOG_INITIALIZED:
|
||||
|
@ -767,7 +806,7 @@ int elog_init(void)
|
|||
case ELOG_BROKEN:
|
||||
return -1;
|
||||
}
|
||||
elog_initialized = ELOG_BROKEN;
|
||||
es->elog_initialized = ELOG_BROKEN;
|
||||
|
||||
elog_debug("elog_init()\n");
|
||||
|
||||
|
@ -775,19 +814,19 @@ int elog_init(void)
|
|||
if (elog_find_flash() < 0)
|
||||
return -1;
|
||||
|
||||
elog_size = region_device_sz(&nv_dev);
|
||||
elog_size = region_device_sz(&es->nv_dev);
|
||||
mirror_buffer = malloc(elog_size);
|
||||
if (!mirror_buffer) {
|
||||
printk(BIOS_ERR, "ELOG: Unable to allocate backing store\n");
|
||||
return -1;
|
||||
}
|
||||
mem_region_device_rw_init(&mirror_dev, mirror_buffer, elog_size);
|
||||
mem_region_device_rw_init(&es->mirror_dev, mirror_buffer, elog_size);
|
||||
|
||||
/*
|
||||
* Mark as initialized to allow elog_init() to be called and deemed
|
||||
* successful in the prepare/shrink path which adds events.
|
||||
*/
|
||||
elog_initialized = ELOG_INITIALIZED;
|
||||
es->elog_initialized = ELOG_INITIALIZED;
|
||||
|
||||
/* Load the log from flash and prepare the flash if necessary. */
|
||||
if (elog_scan_flash() < 0 && elog_prepare_empty() < 0) {
|
||||
|
@ -796,8 +835,8 @@ int elog_init(void)
|
|||
}
|
||||
|
||||
printk(BIOS_INFO, "ELOG: area is %zu bytes, full threshold %d,"
|
||||
" shrink size %d\n", region_device_sz(&nv_dev),
|
||||
full_threshold, shrink_size);
|
||||
" shrink size %d\n", region_device_sz(&es->nv_dev),
|
||||
es->full_threshold, es->shrink_size);
|
||||
|
||||
if (elog_do_add_boot_count()) {
|
||||
elog_add_event_dword(ELOG_TYPE_BOOT, boot_count_read());
|
||||
|
|
Loading…
Reference in New Issue