diff options
Diffstat (limited to 'src/drivers')
-rw-r--r-- | src/drivers/elog/elog.c | 155 |
1 files changed, 97 insertions, 58 deletions
diff --git a/src/drivers/elog/elog.c b/src/drivers/elog/elog.c index d3c01c2d58..18ca3b7adf 100644 --- a/src/drivers/elog/elog.c +++ b/src/drivers/elog/elog.c @@ -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()); |