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:
		
				
					committed by
					
						 Aaron Durbin
						Aaron Durbin
					
				
			
			
				
	
			
			
			
						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()); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user