diff options
author | Aaron Durbin <adurbin@chromium.org> | 2016-12-03 22:08:20 -0600 |
---|---|---|
committer | Aaron Durbin <adurbin@chromium.org> | 2016-12-15 07:51:35 +0100 |
commit | 31be2c969eed74510c3546bad0dbb9a7334f5843 (patch) | |
tree | a7b5d682bfe421a34454d320ec78d04e6911f71b /src | |
parent | f1f322b1a883e3d50a1907e29b5aa333a0f795a8 (diff) | |
download | coreboot-31be2c969eed74510c3546bad0dbb9a7334f5843.tar.xz |
soc/intel/common: remove mrc cache assumptions
Update the mrc cache implementation to use region_file. Instead
of relying on memory-mapped access and pointer arithmetic
use the region_devices and region_file to obtain the latest
data associated with the region. This removes the need for the
nvm wrapper as the region_devices can be used directly. Thus,
the library is more generic and can be extended to work on
different boot mediums.
BUG=chrome-os-partner:56151
Change-Id: Ic14e2d2f7339e50256b4a3a297fc33991861ca44
Signed-off-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-on: https://review.coreboot.org/17717
Tested-by: build bot (Jenkins)
Reviewed-by: Duncan Laurie <dlaurie@chromium.org>
Diffstat (limited to 'src')
-rw-r--r-- | src/drivers/intel/fsp1_1/romstage.c | 29 | ||||
-rw-r--r-- | src/drivers/intel/fsp2_0/memory_init.c | 58 | ||||
-rw-r--r-- | src/soc/intel/apollolake/mmap_boot.c | 17 | ||||
-rw-r--r-- | src/soc/intel/apollolake/romstage.c | 15 | ||||
-rw-r--r-- | src/soc/intel/baytrail/romstage/raminit.c | 15 | ||||
-rw-r--r-- | src/soc/intel/broadwell/romstage/raminit.c | 16 | ||||
-rw-r--r-- | src/soc/intel/common/mrc_cache.c | 619 | ||||
-rw-r--r-- | src/soc/intel/common/mrc_cache.h | 45 | ||||
-rw-r--r-- | src/soc/intel/common/nvm.c | 122 | ||||
-rw-r--r-- | src/soc/intel/common/nvm.h | 16 |
10 files changed, 433 insertions, 519 deletions
diff --git a/src/drivers/intel/fsp1_1/romstage.c b/src/drivers/intel/fsp1_1/romstage.c index bb3e96c051..73fb66dee6 100644 --- a/src/drivers/intel/fsp1_1/romstage.c +++ b/src/drivers/intel/fsp1_1/romstage.c @@ -19,6 +19,7 @@ #include <arch/io.h> #include <arch/cbfs.h> #include <arch/early_variables.h> +#include <assert.h> #include <boardid.h> #include <console/console.h> #include <cbmem.h> @@ -99,7 +100,7 @@ void *cache_as_ram_stage_main(FSP_INFO_HEADER *fih) /* Entry from the mainboard. */ void romstage_common(struct romstage_params *params) { - const struct mrc_saved_data *cache; + struct region_device rdev; struct pei_data *pei_data; post_code(0x32); @@ -127,11 +128,15 @@ void romstage_common(struct romstage_params *params) printk(BIOS_DEBUG, "Recovery mode: not using MRC cache.\n"); } else if (IS_ENABLED(CONFIG_CACHE_MRC_SETTINGS) - && (!mrc_cache_get_current_with_version(&cache, - params->fsp_version))) { + && (!mrc_cache_get_current(MRC_TRAINING_DATA, + params->fsp_version, + &rdev))) { /* MRC cache found */ - params->pei_data->saved_data_size = cache->size; - params->pei_data->saved_data = &cache->data[0]; + params->pei_data->saved_data_size = + region_device_sz(&rdev); + params->pei_data->saved_data = rdev_mmap_full(&rdev); + /* Assum boot device is memory mapped. */ + assert(IS_ENABLED(CONFIG_BOOT_DEVICE_MEMORY_MAPPED)); } else if (params->pei_data->boot_mode == ACPI_S3) { /* Waking from S3 and no cache. */ printk(BIOS_DEBUG, @@ -155,10 +160,10 @@ void romstage_common(struct romstage_params *params) if ((params->pei_data->boot_mode != ACPI_S3) && (params->pei_data->data_to_save_size != 0) && (params->pei_data->data_to_save != NULL)) - mrc_cache_stash_data_with_version( + mrc_cache_stash_data(MRC_TRAINING_DATA, + params->fsp_version, params->pei_data->data_to_save, - params->pei_data->data_to_save_size, - params->fsp_version); + params->pei_data->data_to_save_size); } /* Save DIMM information */ @@ -355,15 +360,15 @@ __attribute__((weak)) void mainboard_add_dimm_info( } /* Get the memory configuration data */ -__attribute__((weak)) int mrc_cache_get_current_with_version( - const struct mrc_saved_data **cache, uint32_t version) +__attribute__((weak)) int mrc_cache_get_current(int type, uint32_t version, + struct region_device *rdev) { return -1; } /* Save the memory configuration data */ -__attribute__((weak)) int mrc_cache_stash_data_with_version(const void *data, - size_t size, uint32_t version) +__attribute__((weak)) int mrc_cache_stash_data(int type, uint32_t version, + const void *data, size_t size) { return -1; } diff --git a/src/drivers/intel/fsp2_0/memory_init.c b/src/drivers/intel/fsp2_0/memory_init.c index 63a5733a6b..e9386d6f8d 100644 --- a/src/drivers/intel/fsp2_0/memory_init.c +++ b/src/drivers/intel/fsp2_0/memory_init.c @@ -15,6 +15,7 @@ #include <arch/io.h> #include <arch/cpu.h> #include <arch/symbols.h> +#include <assert.h> #include <cbfs.h> #include <cbmem.h> #include <console/console.h> @@ -107,9 +108,9 @@ static void save_memory_training_data(bool s3wake, uint32_t fsp_version) * code which saves the data to flash doesn't write if the latest * training data matches this one. */ - if (mrc_cache_stash_data_with_version(mrc_data, mrc_data_size, - fsp_version) < 0) - printk(BIOS_ERR, "Failed to stash MRC data\n"); + if (mrc_cache_stash_data(MRC_TRAINING_DATA, fsp_version, mrc_data, + mrc_data_size) < 0) + printk(BIOS_ERR, "Failed to stash MRC data\n"); mrc_cache_update_tpm_hash(mrc_data, mrc_data_size); } @@ -146,24 +147,6 @@ static void do_fsp_post_memory_init(bool s3wake, uint32_t fsp_version) romstage_handoff_init(s3wake); } -static const char *mrc_cache_get_region_name(void) -{ - /* In normal mode, always use DEFAULT_MRC_CACHE */ - if (!vboot_recovery_mode_enabled()) - return DEFAULT_MRC_CACHE; - - /* - * In recovery mode, force retraining by returning NULL if: - * 1. Recovery cache is not supported, or - * 2. Memory retrain switch is set. - */ - if (!IS_ENABLED(CONFIG_HAS_RECOVERY_MRC_CACHE) || - vboot_recovery_mode_memory_retrain()) - return NULL; - - return RECOVERY_MRC_CACHE; -} - static int mrc_cache_verify_tpm_hash(const uint8_t *data, size_t size) { uint8_t data_hash[VB2_SHA256_DIGEST_SIZE]; @@ -209,29 +192,46 @@ static int mrc_cache_verify_tpm_hash(const uint8_t *data, size_t size) static void fsp_fill_mrc_cache(FSPM_ARCH_UPD *arch_upd, bool s3wake, uint32_t fsp_version) { - const struct mrc_saved_data *mrc_cache; - const char *name; + struct region_device rdev; + void *data; arch_upd->NvsBufferPtr = NULL; if (!IS_ENABLED(CONFIG_CACHE_MRC_SETTINGS)) return; - name = mrc_cache_get_region_name(); + /* + * In recovery mode, force retraining: + * 1. Recovery cache is not supported, or + * 2. Memory retrain switch is set. + */ + if (vboot_recovery_mode_enabled()) { + if (!IS_ENABLED(CONFIG_HAS_RECOVERY_MRC_CACHE)) + return; + if (vboot_recovery_mode_memory_retrain()) + return; + } + + if (mrc_cache_get_current(MRC_TRAINING_DATA, fsp_version, &rdev) < 0) + return; + + /* Assume boot device is memory mapped. */ + assert(IS_ENABLED(CONFIG_BOOT_DEVICE_MEMORY_MAPPED)); + data = rdev_mmap_full(&rdev); - if (mrc_cache_get_current_from_region(&mrc_cache, fsp_version, name)) + if (data == NULL) return; - if (!mrc_cache_verify_tpm_hash(mrc_cache->data, mrc_cache->size)) + if (!mrc_cache_verify_tpm_hash(data, region_device_sz(&rdev))) return; /* MRC cache found */ - arch_upd->NvsBufferPtr = (void *)mrc_cache->data; + arch_upd->NvsBufferPtr = data; arch_upd->BootMode = s3wake ? FSP_BOOT_ON_S3_RESUME: FSP_BOOT_ASSUMING_NO_CONFIGURATION_CHANGES; - printk(BIOS_SPEW, "MRC cache found, size %x bootmode:%d\n", - mrc_cache->size, arch_upd->BootMode); + printk(BIOS_SPEW, "MRC cache found, size %zx bootmode:%d\n", + region_device_sz(&rdev), arch_upd->BootMode); } static enum cb_err check_region_overlap(const struct memranges *ranges, diff --git a/src/soc/intel/apollolake/mmap_boot.c b/src/soc/intel/apollolake/mmap_boot.c index 7159c24f52..3213b09b23 100644 --- a/src/soc/intel/apollolake/mmap_boot.c +++ b/src/soc/intel/apollolake/mmap_boot.c @@ -23,7 +23,6 @@ #include <console/console.h> #include <fmap.h> #include <soc/flash_ctrlr.h> -#include <soc/intel/common/nvm.h> #include <soc/mmap_boot.h> /* @@ -148,22 +147,6 @@ const struct cbfs_locator cbfs_master_header_locator = { .locate = iafw_boot_region_properties, }; -uint32_t nvm_mmio_to_flash_offset(void *p) -{ - bios_mmap_init(); - - size_t start, size; - start = car_get_var(bios_start); - size = car_get_var(bios_size); - - /* - * Returns : - * addr - base of mmaped region in addr space + offset of mmaped region - * start on flash - */ - return (uintptr_t)p - (4ULL * GiB - size) + start; -} - size_t get_bios_size(void) { bios_mmap_init(); diff --git a/src/soc/intel/apollolake/romstage.c b/src/soc/intel/apollolake/romstage.c index 77562c4699..8c2a1a330a 100644 --- a/src/soc/intel/apollolake/romstage.c +++ b/src/soc/intel/apollolake/romstage.c @@ -202,8 +202,9 @@ asmlinkage void car_stage_entry(void) new_var_data = fsp_find_extension_hob_by_guid(hob_variable_guid, &var_size); if (new_var_data) - mrc_cache_stash_vardata(new_var_data, var_size, - car_get_var(fsp_version)); + mrc_cache_stash_data(MRC_VARIABLE_DATA, + car_get_var(fsp_version), new_var_data, + var_size); else printk(BIOS_ERR, "Failed to determine variable data\n"); @@ -258,7 +259,7 @@ static void fill_console_params(FSPM_UPD *mupd) void platform_fsp_memory_init_params_cb(FSPM_UPD *mupd, uint32_t version) { - const struct mrc_saved_data *msd; + struct region_device rdev; fill_console_params(mupd); mainboard_memory_init_params(mupd); @@ -293,10 +294,10 @@ void platform_fsp_memory_init_params_cb(FSPM_UPD *mupd, uint32_t version) * wrong/missing key renders DRAM contents useless. */ - if (mrc_cache_get_vardata(&msd, version) < 0) { - printk(BIOS_DEBUG, "MRC variable data missing/invalid\n"); - } else { - mupd->FspmConfig.VariableNvsBufferPtr = (void*) msd->data; + if (mrc_cache_get_current(MRC_VARIABLE_DATA, version, &rdev) == 0) { + /* Assume leaking is ok. */ + assert(IS_ENABLED(CONFIG_BOOT_DEVICE_MEMORY_MAPPED)); + mupd->FspmConfig.VariableNvsBufferPtr = rdev_mmap_full(&rdev); } car_set_var(fsp_version, version); diff --git a/src/soc/intel/baytrail/romstage/raminit.c b/src/soc/intel/baytrail/romstage/raminit.c index a79fe9533b..190231d7c3 100644 --- a/src/soc/intel/baytrail/romstage/raminit.c +++ b/src/soc/intel/baytrail/romstage/raminit.c @@ -15,7 +15,7 @@ #include <stddef.h> #include <arch/acpi.h> -#include <arch/io.h> +#include <assert.h> #include <cbfs.h> #include <cbmem.h> #include <console/console.h> @@ -111,7 +111,7 @@ void raminit(struct mrc_params *mp, int prev_sleep_state) { int ret; mrc_wrapper_entry_t mrc_entry; - const struct mrc_saved_data *cache; + struct region_device rdev; /* Fill in default entries. */ mp->version = MRC_PARAMS_VER; @@ -125,9 +125,11 @@ void raminit(struct mrc_params *mp, int prev_sleep_state) if (vboot_recovery_mode_enabled()) { printk(BIOS_DEBUG, "Recovery mode: not using MRC cache.\n"); - } else if (!mrc_cache_get_current(&cache)) { - mp->saved_data_size = cache->size; - mp->saved_data = &cache->data[0]; + } else if (!mrc_cache_get_current(MRC_TRAINING_DATA, 0, &rdev)) { + mp->saved_data_size = region_device_sz(&rdev); + mp->saved_data = rdev_mmap_full(&rdev); + /* Assume boot device is memory mapped. */ + assert(IS_ENABLED(CONFIG_BOOT_DEVICE_MEMORY_MAPPED)); } else if (prev_sleep_state == ACPI_S3) { /* If waking from S3 and no cache then. */ printk(BIOS_DEBUG, "No MRC cache found in S3 resume path.\n"); @@ -178,5 +180,6 @@ void raminit(struct mrc_params *mp, int prev_sleep_state) mp->data_to_save_size); if (mp->data_to_save != NULL && mp->data_to_save_size > 0) - mrc_cache_stash_data(mp->data_to_save, mp->data_to_save_size); + mrc_cache_stash_data(MRC_TRAINING_DATA, 0, mp->data_to_save, + mp->data_to_save_size); } diff --git a/src/soc/intel/broadwell/romstage/raminit.c b/src/soc/intel/broadwell/romstage/raminit.c index 61b1bc9cb9..1185788ea7 100644 --- a/src/soc/intel/broadwell/romstage/raminit.c +++ b/src/soc/intel/broadwell/romstage/raminit.c @@ -15,6 +15,7 @@ #include <arch/cbfs.h> #include <arch/io.h> +#include <assert.h> #include <cbfs.h> #include <cbmem.h> #include <console/console.h> @@ -41,7 +42,7 @@ */ void raminit(struct pei_data *pei_data) { - const struct mrc_saved_data *cache; + struct region_device rdev; struct memory_info* mem_info; pei_wrapper_entry_t entry; int ret; @@ -51,10 +52,12 @@ void raminit(struct pei_data *pei_data) if (vboot_recovery_mode_enabled()) { /* Recovery mode does not use MRC cache */ printk(BIOS_DEBUG, "Recovery mode: not using MRC cache.\n"); - } else if (!mrc_cache_get_current(&cache)) { + } else if (!mrc_cache_get_current(MRC_TRAINING_DATA, 0, &rdev)) { /* MRC cache found */ - pei_data->saved_data_size = cache->size; - pei_data->saved_data = &cache->data[0]; + pei_data->saved_data_size = region_device_sz(&rdev); + pei_data->saved_data = rdev_mmap_full(&rdev); + /* Assume boot device is memory mapped. */ + assert(IS_ENABLED(CONFIG_BOOT_DEVICE_MEMORY_MAPPED)); } else if (pei_data->boot_mode == ACPI_S3) { /* Waking from S3 and no cache. */ printk(BIOS_DEBUG, "No MRC cache found in S3 resume path.\n"); @@ -118,8 +121,9 @@ void raminit(struct pei_data *pei_data) pei_data->data_to_save_size); if (pei_data->data_to_save != NULL && pei_data->data_to_save_size > 0) - mrc_cache_stash_data(pei_data->data_to_save, - pei_data->data_to_save_size); + mrc_cache_stash_data(MRC_TRAINING_DATA, 0, + pei_data->data_to_save, + pei_data->data_to_save_size); printk(BIOS_DEBUG, "create cbmem for dimm information\n"); mem_info = cbmem_add(CBMEM_ID_MEMINFO, sizeof(struct memory_info)); diff --git a/src/soc/intel/common/mrc_cache.c b/src/soc/intel/common/mrc_cache.c index e9d670ec40..69e5b00de1 100644 --- a/src/soc/intel/common/mrc_cache.c +++ b/src/soc/intel/common/mrc_cache.c @@ -14,344 +14,343 @@ */ #include <string.h> +#include <boot_device.h> #include <bootstate.h> #include <console/console.h> #include <cbmem.h> #include <elog.h> #include <fmap.h> #include <ip_checksum.h> +#include <region_file.h> #include <vboot/vboot_common.h> #include "mrc_cache.h" #include "nvm.h" -#define MRC_DATA_ALIGN 0x1000 +#define DEFAULT_MRC_CACHE "RW_MRC_CACHE" +#define VARIABLE_MRC_CACHE "RW_VAR_MRC_CACHE" +#define RECOVERY_MRC_CACHE "RECOVERY_MRC_CACHE" +#define UNIFIED_MRC_CACHE "UNIFIED_MRC_CACHE" + #define MRC_DATA_SIGNATURE (('M'<<0)|('R'<<8)|('C'<<16)|('D'<<24)) -/* The mrc_data_region describes the larger non-volatile area to store - * mrc_saved_data objects.*/ -struct mrc_data_region { - void *base; - uint32_t size; -}; +struct mrc_metadata { + uint32_t signature; + uint32_t data_size; + uint16_t data_checksum; + uint16_t header_checksum; + uint32_t version; +} __attribute__((packed)); enum result { - WRITE_FAILURE = -1, - ERASE_FAILURE = -2, - OTHER_FAILURE = -3, + UPDATE_FAILURE = -1, UPDATE_SUCCESS = 0, ALREADY_UPTODATE = 1 }; -/* common code */ -static int mrc_cache_get_region(const char *name, - struct mrc_data_region *region) -{ - bool located_by_fmap = true; - struct region_device rdev; +#define NORMAL_FLAG (1 << 0) +#define RECOVERY_FLAG (1 << 1) - region->base = NULL; - region->size = 0; - - if (fmap_locate_area_as_rdev(name, &rdev)) - located_by_fmap = false; +struct cache_region { + const char *name; + uint32_t cbmem_id; + int type; + int elog_slot; + int flags; +}; - /* CHROMEOS builds must get their MRC cache from FMAP. */ - if (IS_ENABLED(CONFIG_CHROMEOS) && !located_by_fmap) - return -1; +static const struct cache_region recovery_training = { + .name = RECOVERY_MRC_CACHE, + .cbmem_id = CBMEM_ID_MRCDATA, + .type = MRC_TRAINING_DATA, + .elog_slot = ELOG_MEM_CACHE_UPDATE_SLOT_RECOVERY, +#if IS_ENABLED(CONFIG_HAS_RECOVERY_MRC_CACHE) + .flags = RECOVERY_FLAG, +#else + .flags = 0, +#endif +}; - if (located_by_fmap) { - region->size = region_device_sz(&rdev); - region->base = rdev_mmap_full(&rdev); +static const struct cache_region normal_training = { + .name = DEFAULT_MRC_CACHE, + .cbmem_id = CBMEM_ID_MRCDATA, + .type = MRC_TRAINING_DATA, + .elog_slot = ELOG_MEM_CACHE_UPDATE_SLOT_NORMAL, + .flags = NORMAL_FLAG | RECOVERY_FLAG, +}; - if (region->base == NULL) - return -1; - } else { - region->base = (void *)CONFIG_MRC_SETTINGS_CACHE_BASE; - region->size = CONFIG_MRC_SETTINGS_CACHE_SIZE; - } +static const struct cache_region variable_data = { + .name = VARIABLE_MRC_CACHE, + .cbmem_id = CBMEM_ID_VAR_MRCDATA, + .type = MRC_VARIABLE_DATA, + .elog_slot = ELOG_MEM_CACHE_UPDATE_SLOT_VARIABLE, + .flags = NORMAL_FLAG | RECOVERY_FLAG, +}; - return 0; -} +/* Order matters here for priority in matching. */ +static const struct cache_region *cache_regions[] = { + &recovery_training, + &normal_training, + &variable_data, +}; -/* Protect mrc region with a Protected Range Register */ -static int __protect_mrc_cache(const struct mrc_data_region *region, - const char *name) +static int lookup_region_by_name(const char *name, struct region *r) { - if (!IS_ENABLED(CONFIG_MRC_SETTINGS_PROTECT)) + /* This assumes memory mapped boot media just under 4GiB. */ + const uint32_t pointer_base_32bit = -CONFIG_ROM_SIZE; + + if (fmap_locate_area(name, r) == 0) return 0; - if (nvm_is_write_protected() <= 0) { - printk(BIOS_INFO, "MRC: NOT enabling PRR for %s.\n", name); - return 1; + /* CHROMEOS builds must get their MRC cache from FMAP. */ + if (IS_ENABLED(CONFIG_CHROMEOS)) { + printk(BIOS_ERR, "MRC: Chrome OS lookup failure.\n"); + return -1; } - if (nvm_protect(region->base, region->size) < 0) { - printk(BIOS_ERR, "MRC: ERROR setting PRR for %s.\n", name); + if (!IS_ENABLED(CONFIG_BOOT_DEVICE_MEMORY_MAPPED)) return -1; - } - printk(BIOS_INFO, "MRC: Enabled Protected Range on %s.\n", name); + /* Base is in the form of a pointer. Make it an offset. */ + r->offset = CONFIG_MRC_SETTINGS_CACHE_BASE - pointer_base_32bit; + r->size = CONFIG_MRC_SETTINGS_CACHE_SIZE; + return 0; } -static int protect_mrc_cache(const char *name) +static const struct cache_region *lookup_region_type(int type) { - struct mrc_data_region region; - if (mrc_cache_get_region(name, ®ion) < 0) { - printk(BIOS_ERR, "MRC: Could not find region %s\n", name); - return -1; + int i; + int flags; + + if (vboot_recovery_mode_enabled()) + flags = RECOVERY_FLAG; + else + flags = NORMAL_FLAG; + + for (i = 0; i < ARRAY_SIZE(cache_regions); i++) { + if (cache_regions[i]->type != type) + continue; + if ((cache_regions[i]->flags & flags) == flags) + return cache_regions[i]; } - return __protect_mrc_cache(®ion, name); + return NULL; } -static int mrc_cache_in_region(const struct mrc_data_region *region, - const struct mrc_saved_data *cache) +int mrc_cache_stash_data(int type, uint32_t version, const void *data, + size_t size) { - uintptr_t region_end; - uintptr_t cache_end; - - if ((uintptr_t)cache < (uintptr_t)region->base) - return 0; + const struct cache_region *cr; + size_t cbmem_size; + struct mrc_metadata *md; + + cr = lookup_region_type(type); + if (cr == NULL) { + printk(BIOS_ERR, "MRC: failed to add to cbmem for type %d.\n", + type); + return -1; + } - region_end = (uintptr_t)region->base; - region_end += region->size; + cbmem_size = sizeof(*md) + size; - if ((uintptr_t)cache >= region_end) - return 0; + md = cbmem_add(cr->cbmem_id, cbmem_size); - if ((sizeof(*cache) + (uintptr_t)cache) >= region_end) - return 0; + if (md == NULL) { + printk(BIOS_ERR, "MRC: failed to add '%s' to cbmem.\n", + cr->name); + return -1; + } - cache_end = (uintptr_t)cache; - cache_end += cache->size + sizeof(*cache); + memset(md, 0, sizeof(*md)); + md->signature = MRC_DATA_SIGNATURE; + md->data_size = size; + md->version = version; + md->data_checksum = compute_ip_checksum(data, size); + md->header_checksum = compute_ip_checksum(md, sizeof(*md)); + memcpy(&md[1], data, size); - if (cache_end > region_end) - return 0; - - return 1; + return 0; } -static int mrc_cache_valid(const struct mrc_data_region *region, - const struct mrc_saved_data *cache) +static const struct cache_region *lookup_region(struct region *r, int type) { - uint32_t checksum; - - if (cache->signature != MRC_DATA_SIGNATURE) - return 0; - - if (cache->size > region->size) - return 0; - - checksum = compute_ip_checksum((void *)&cache->data[0], cache->size); - - if (cache->checksum != checksum) - return 0; + const struct cache_region *cr; - return 1; -} + cr = lookup_region_type(type); -static const struct mrc_saved_data * -next_cache_block(const struct mrc_saved_data *cache) -{ - uintptr_t next = (uintptr_t)cache; + if (cr == NULL) { + printk(BIOS_ERR, "MRC: failed to locate region type %d.\n", + type); + return NULL; + } - next += ALIGN(cache->size + sizeof(*cache), MRC_DATA_ALIGN); + if (lookup_region_by_name(cr->name, r) < 0) + return NULL; - return (const struct mrc_saved_data *)next; + return cr; } -/* Locate the most recently saved MRC data. */ -static int __mrc_cache_get_current(const struct mrc_data_region *region, - const struct mrc_saved_data **cache, - uint32_t version) +static int mrc_header_valid(struct region_device *rdev, struct mrc_metadata *md) { - const struct mrc_saved_data *msd; - const struct mrc_saved_data *verified_cache; - int slot = 0; + uint16_t checksum; + uint16_t checksum_result; + size_t size; - msd = region->base; - - verified_cache = NULL; + if (rdev_readat(rdev, md, 0, sizeof(*md)) < 0) { + printk(BIOS_ERR, "MRC: couldn't read metadata\n"); + return -1; + } - while (mrc_cache_in_region(region, msd) && - mrc_cache_valid(region, msd)) { - verified_cache = msd; - msd = next_cache_block(msd); - slot++; + if (md->signature != MRC_DATA_SIGNATURE) { + printk(BIOS_ERR, "MRC: invalid header signature\n"); + return -1; } - /* - * Update pointer to the most recently saved MRC data before returning - * any error. This ensures that the caller can use next available slot - * if required. - */ - *cache = verified_cache; + /* Compute checksum over header with 0 as the value. */ + checksum = md->header_checksum; + md->header_checksum = 0; + checksum_result = compute_ip_checksum(md, sizeof(*md)); - if (verified_cache == NULL) + if (checksum != checksum_result) { + printk(BIOS_ERR, "MRC: header checksum mismatch: %x vs %x\n", + checksum, checksum_result); return -1; + } + + /* Put back original. */ + md->header_checksum = checksum; - if (verified_cache->version != version) { - printk(BIOS_DEBUG, "MRC: cache version mismatch: %x vs %x\n", - verified_cache->version, version); + /* Re-size the region device according to the metadata as a region_file + * does block allocation. */ + size = sizeof(*md) + md->data_size; + if (rdev_chain(rdev, rdev, 0, size) < 0) { + printk(BIOS_ERR, "MRC: size exceeds rdev size: %zx vs %zx\n", + size, region_device_sz(rdev)); return -1; } - printk(BIOS_DEBUG, "MRC: cache slot %d @ %p\n", slot-1, verified_cache); - return 0; } -int mrc_cache_get_current_from_region(const struct mrc_saved_data **cache, - uint32_t version, - const char *region_name) +static int mrc_data_valid(const struct region_device *rdev, + const struct mrc_metadata *md) { - struct mrc_data_region region; - - if (!region_name) { - printk(BIOS_ERR, "MRC: Requires memory retraining.\n"); + void *data; + uint16_t checksum; + const size_t md_size = sizeof(*md); + const size_t data_size = md->data_size; + + data = rdev_mmap(rdev, md_size, data_size); + if (data == NULL) { + printk(BIOS_ERR, "MRC: mmap failure on data verification.\n"); return -1; } - printk(BIOS_ERR, "MRC: Using data from %s\n", region_name); - - if (mrc_cache_get_region(region_name, ®ion) < 0) { - printk(BIOS_ERR, "MRC: Region %s not found. " - "Requires memory retraining.\n", region_name); - return -1; - } + checksum = compute_ip_checksum(data, data_size); - if (__mrc_cache_get_current(®ion, cache, version) < 0) { - printk(BIOS_ERR, "MRC: Valid slot not found in %s." - "Requires memory retraining.\n", region_name); + rdev_munmap(rdev, data); + if (md->data_checksum != checksum) { + printk(BIOS_ERR, "MRC: data checksum mismatch: %x vs %x\n", + md->data_checksum, checksum); return -1; } return 0; } -int mrc_cache_get_current_with_version(const struct mrc_saved_data **cache, - uint32_t version) -{ - return mrc_cache_get_current_from_region(cache, version, - DEFAULT_MRC_CACHE); -} - -int mrc_cache_get_current(const struct mrc_saved_data **cache) +static int mrc_cache_latest(const char *name, + const struct region_device *backing_rdev, + struct mrc_metadata *md, + struct region_file *cache_file, + struct region_device *rdev, + bool fail_bad_data) { - return mrc_cache_get_current_with_version(cache, 0); -} - -int mrc_cache_get_vardata(const struct mrc_saved_data **cache, uint32_t version) -{ - struct mrc_data_region region; - - if (mrc_cache_get_region(VARIABLE_MRC_CACHE, ®ion) < 0) + /* Init and obtain a handle to the file data. */ + if (region_file_init(cache_file, backing_rdev) < 0) { + printk(BIOS_ERR, "MRC: region file invalid in '%s'\n", name); return -1; + } - return __mrc_cache_get_current(®ion, cache, version); -} - -/* Fill in mrc_saved_data structure with payload. */ -static void mrc_cache_fill(struct mrc_saved_data *cache, const void *data, - size_t size, uint32_t version) -{ - cache->signature = MRC_DATA_SIGNATURE; - cache->size = size; - cache->version = version; - memcpy(&cache->data[0], data, size); - cache->checksum = compute_ip_checksum((void *)&cache->data[0], - cache->size); -} - -static int _mrc_stash_data(const void *data, size_t size, uint32_t version, - uint32_t cbmem_id) -{ - int cbmem_size; - struct mrc_saved_data *cache; - - cbmem_size = sizeof(*cache) + ALIGN(size, 16); - - cache = cbmem_add(cbmem_id, cbmem_size); + /* Provide a 0 sized region_device from here on out so the caller + * has a valid yet unusable region_device. */ + rdev_chain(rdev, backing_rdev, 0, 0); - if (cache == NULL) { - printk(BIOS_ERR, "MRC: No space in cbmem for training data.\n"); - return -1; + /* No data to return. */ + if (region_file_data(cache_file, rdev) < 0) { + printk(BIOS_ERR, "MRC: no data in '%s'\n", name); + return fail_bad_data ? -1 : 0; } - /* Clear alignment padding bytes at end of data. */ - memset(&cache->data[size], 0, cbmem_size - size - sizeof(*cache)); - - printk(BIOS_DEBUG, "MRC: Relocate data from %p to %p (%zu bytes)\n", - data, cache, size); + /* Validate header and resize region to reflect actual usage on the + * saved medium (including metadata and data). */ + if (mrc_header_valid(rdev, md) < 0) { + printk(BIOS_ERR, "MRC: invalid header in '%s'\n", name); + return fail_bad_data ? -1 : 0; + } - mrc_cache_fill(cache, data, size, version); + /* Validate Data */ + if (mrc_data_valid(rdev, md) < 0) { + printk(BIOS_ERR, "MRC: invalid data in '%s'\n", name); + return fail_bad_data ? -1 : 0; + } return 0; } -int mrc_cache_stash_data_with_version(const void *data, size_t size, - uint32_t version) +int mrc_cache_get_current(int type, uint32_t version, + struct region_device *rdev) { - return _mrc_stash_data(data, size, version, CBMEM_ID_MRCDATA); -} + const struct cache_region *cr; + struct region region; + struct region_device read_rdev; + struct region_file cache_file; + struct mrc_metadata md; + size_t data_size; + const size_t md_size = sizeof(md); + const bool fail_bad_data = true; + + cr = lookup_region(®ion, type); + + if (cr == NULL) + return -1; -int mrc_cache_stash_vardata(const void *data, size_t size, uint32_t version) -{ - return _mrc_stash_data(data, size, version, CBMEM_ID_VAR_MRCDATA); -} + if (boot_device_ro_subregion(®ion, &read_rdev) < 0) + return -1; + if (mrc_cache_latest(cr->name, &read_rdev, &md, &cache_file, rdev, + fail_bad_data) < 0) + return -1; -int mrc_cache_stash_data(const void *data, size_t size) -{ - return mrc_cache_stash_data_with_version(data, size, 0); + if (version != md.version) { + printk(BIOS_INFO, "MRC: version mismatch: %x vs %x\n", + md.version, version); + return -1; + } + + /* Re-size rdev to only contain the data. i.e. remove metadata. */ + data_size = md.data_size; + return rdev_chain(rdev, rdev, md_size, data_size); } -static int mrc_slot_valid(const struct mrc_data_region *region, - const struct mrc_saved_data *slot, - const struct mrc_saved_data *to_save) +static bool mrc_cache_needs_update(const struct region_device *rdev, + const struct cbmem_entry *to_be_updated) { - uintptr_t region_begin; - uintptr_t region_end; - uintptr_t slot_end; - uintptr_t slot_begin; - uint32_t size; - - region_begin = (uintptr_t)region->base; - region_end = region_begin + region->size; - slot_begin = (uintptr_t)slot; - size = to_save->size + sizeof(*to_save); - slot_end = slot_begin + size; - - if (slot_begin < region_begin || slot_begin >= region_end) - return 0; + void *mapping; + size_t size = region_device_sz(rdev); + bool need_update = false; - if (size > region->size) - return 0; - - if (slot_end > region_end || slot_end < region_begin) - return 0; - - if (!nvm_is_erased(slot, size)) - return 0; + if (cbmem_entry_size(to_be_updated) != size) + return true; - return 1; -} + mapping = rdev_mmap_full(rdev); -static const struct mrc_saved_data * -mrc_cache_next_slot(const struct mrc_data_region *region, - const struct mrc_saved_data *current_slot) -{ - const struct mrc_saved_data *next_slot; + if (memcmp(cbmem_entry_start(to_be_updated), mapping, size)) + need_update = true; - if (current_slot == NULL) { - next_slot = region->base; - } else { - next_slot = next_cache_block(current_slot); - } + rdev_munmap(rdev, mapping); - return next_slot; + return need_update; } static void log_event_cache_update(uint8_t slot, enum result res) @@ -363,9 +362,7 @@ static void log_event_cache_update(uint8_t slot, enum result res) /* Filter through interesting events only */ switch (res) { - case WRITE_FAILURE: /* fall-through */ - case ERASE_FAILURE: /* fall-through */ - case OTHER_FAILURE: /* fall-through */ + case UPDATE_FAILURE: event.status = ELOG_MEM_CACHE_UPDATE_STATUS_FAIL; break; case UPDATE_SUCCESS: @@ -379,63 +376,95 @@ static void log_event_cache_update(uint8_t slot, enum result res) printk(BIOS_ERR, "Failed to log mem cache update event.\n"); } -static int update_mrc_cache_type(uint32_t cbmem_id, const char *region_name) +/* During ramstage this code purposefully uses incoherent transactions between + * read and write. The read assumes a memory-mapped boot device that can be used + * to quickly locate and compare the up-to-date data. However, when an update + * is required it uses the writeable region access to perform the update. */ +static void update_mrc_cache_by_type(int type) { - const struct mrc_saved_data *current_boot; - const struct mrc_saved_data *current_saved; - const struct mrc_saved_data *next_slot; - struct mrc_data_region region; - int res; + const struct cache_region *cr; + struct region region; + struct region_device read_rdev; + struct region_device write_rdev; + struct region_file cache_file; + struct mrc_metadata md; + const struct cbmem_entry *to_be_updated; + struct incoherent_rdev backing_irdev; + const struct region_device *backing_rdev; + struct region_device latest_rdev; + const bool fail_bad_data = false; + + cr = lookup_region(®ion, type); + + if (cr == NULL) + return; - printk(BIOS_DEBUG, "MRC: Updating cache data.\n"); + to_be_updated = cbmem_entry_find(cr->cbmem_id); + if (to_be_updated == NULL) { + printk(BIOS_ERR, "MRC: No data in cbmem for '%s'.\n", + cr->name); + return; + } - printk(BIOS_ERR, "MRC: Cache region selected - %s\n", region_name); + printk(BIOS_DEBUG, "MRC: Checking cached data update for '%s'.\n", + cr->name); - if (mrc_cache_get_region(region_name, ®ion)) { - printk(BIOS_ERR, "MRC: Could not obtain cache region.\n"); - return OTHER_FAILURE; - } + if (boot_device_ro_subregion(®ion, &read_rdev) < 0) + return; - current_boot = cbmem_find(cbmem_id); - if (!current_boot) { - printk(BIOS_ERR, "MRC: No cache in cbmem.\n"); - return OTHER_FAILURE; - } + if (boot_device_rw_subregion(®ion, &write_rdev) < 0) + return; + + backing_rdev = incoherent_rdev_init(&backing_irdev, ®ion, &read_rdev, + &write_rdev); - if (!mrc_cache_valid(®ion, current_boot)) { - printk(BIOS_ERR, "MRC: Cache data in cbmem invalid.\n"); - return OTHER_FAILURE; + if (backing_rdev == NULL) + return; + + if (mrc_cache_latest(cr->name, backing_rdev, &md, &cache_file, + &latest_rdev, fail_bad_data) < 0) + return; + + if (!mrc_cache_needs_update(&latest_rdev, to_be_updated)) { + log_event_cache_update(cr->elog_slot, ALREADY_UPTODATE); + return; } - current_saved = NULL; + printk(BIOS_DEBUG, "MRC: cache data '%s' needs update.\n", cr->name); + + if (region_file_update_data(&cache_file, + cbmem_entry_start(to_be_updated), + cbmem_entry_size(to_be_updated)) < 0) + log_event_cache_update(cr->elog_slot, UPDATE_FAILURE); + else + log_event_cache_update(cr->elog_slot, UPDATE_SUCCESS); +} + +/* Protect mrc region with a Protected Range Register */ +static int protect_mrc_cache(const char *name) +{ + struct region region; + + if (!IS_ENABLED(CONFIG_MRC_SETTINGS_PROTECT)) + return 0; + + if (lookup_region_by_name(name, ®ion) < 0) { + printk(BIOS_ERR, "MRC: Could not find region '%s'\n", name); + return -1; + } - if (!__mrc_cache_get_current(®ion, ¤t_saved, - current_boot->version)) { - if (current_saved->size == current_boot->size && - !memcmp(¤t_saved->data[0], ¤t_boot->data[0], - current_saved->size)) { - printk(BIOS_DEBUG, "MRC: Cache up to date.\n"); - return ALREADY_UPTODATE; - } + if (nvm_is_write_protected() <= 0) { + printk(BIOS_INFO, "MRC: NOT enabling PRR for '%s'.\n", name); + return 0; } - next_slot = mrc_cache_next_slot(®ion, current_saved); - - if (!mrc_slot_valid(®ion, next_slot, current_boot)) { - printk(BIOS_DEBUG, "MRC: Slot @ %p is invalid.\n", next_slot); - if (!nvm_is_erased(region.base, region.size)) { - if (nvm_erase(region.base, region.size) < 0) { - printk(BIOS_DEBUG, "MRC: Failure erasing " - "region %s.\n", region_name); - return ERASE_FAILURE; - } - } - next_slot = region.base; + if (nvm_protect(®ion) < 0) { + printk(BIOS_ERR, "MRC: ERROR setting PRR for '%s'.\n", name); + return -1; } - res = nvm_write((void *)next_slot, current_boot, current_boot->size - + sizeof(*current_boot)); - return res < 0 ? WRITE_FAILURE : UPDATE_SUCCESS; + printk(BIOS_INFO, "MRC: Enabled Protected Range on '%s'.\n", name); + return 0; } static void protect_mrc_region(void) @@ -459,30 +488,10 @@ static void protect_mrc_region(void) static void update_mrc_cache(void *unused) { - uint8_t slot; - const char *region_name; - enum result res; - - /* First update either recovery or default cache */ - if (vboot_recovery_mode_enabled() && - IS_ENABLED(CONFIG_HAS_RECOVERY_MRC_CACHE)) { - region_name = RECOVERY_MRC_CACHE; - slot = ELOG_MEM_CACHE_UPDATE_SLOT_RECOVERY; - } else { - region_name = DEFAULT_MRC_CACHE; - slot = ELOG_MEM_CACHE_UPDATE_SLOT_NORMAL; - } + update_mrc_cache_by_type(MRC_TRAINING_DATA); - res = update_mrc_cache_type(CBMEM_ID_MRCDATA, region_name); - log_event_cache_update(slot, res); - - /* Next update variable cache if in use */ - if (IS_ENABLED(CONFIG_MRC_SETTINGS_VARIABLE_DATA)) { - res = update_mrc_cache_type(CBMEM_ID_VAR_MRCDATA, - VARIABLE_MRC_CACHE); - log_event_cache_update(ELOG_MEM_CACHE_UPDATE_SLOT_VARIABLE, - res); - } + if (IS_ENABLED(CONFIG_MRC_SETTINGS_VARIABLE_DATA)) + update_mrc_cache_by_type(MRC_VARIABLE_DATA); protect_mrc_region(); } diff --git a/src/soc/intel/common/mrc_cache.h b/src/soc/intel/common/mrc_cache.h index 6414accfcf..4511fc3016 100644 --- a/src/soc/intel/common/mrc_cache.h +++ b/src/soc/intel/common/mrc_cache.h @@ -19,34 +19,25 @@ #include <stddef.h> #include <stdint.h> -#define DEFAULT_MRC_CACHE "RW_MRC_CACHE" -#define VARIABLE_MRC_CACHE "RW_VAR_MRC_CACHE" -#define RECOVERY_MRC_CACHE "RECOVERY_MRC_CACHE" -#define UNIFIED_MRC_CACHE "UNIFIED_MRC_CACHE" +enum { + MRC_TRAINING_DATA, + MRC_VARIABLE_DATA, +}; -/* Wrapper object to save MRC information. */ -struct mrc_saved_data { - uint32_t signature; - uint32_t size; - uint32_t checksum; - uint32_t version; - uint8_t data[0]; -} __attribute__((packed)); - -/* Locate the most recently saved MRC data. */ -int mrc_cache_get_current(const struct mrc_saved_data **cache); -int mrc_cache_get_current_with_version(const struct mrc_saved_data **cache, - uint32_t version); -int mrc_cache_get_vardata(const struct mrc_saved_data **cache, - uint32_t version); -int mrc_cache_get_current_from_region(const struct mrc_saved_data **cache, - uint32_t version, - const char *region_name); +/* + * It's up to the caller to decide when to retrieve and stash data. There is + * differentiation on recovery mode CONFIG_HAS_RECOVERY_MRC_CACHE, but that's + * only for locating where to retrieve and save the data. If a platform doesn't + * want to update the data then it shouldn't stash the data for saving. + * Similarly, if the platform doesn't need the data for booting because of a + * policy don't request the data. + */ -/* Stash the resulting MRC data to be saved in non-volatile storage later. */ -int mrc_cache_stash_data(const void *data, size_t size); -int mrc_cache_stash_data_with_version(const void *data, size_t size, - uint32_t version); -int mrc_cache_stash_vardata(const void *data, size_t size, uint32_t version); +/* Get and stash data for saving provided the type passed in. The functions + * return < 0 on error, 0 on success. */ +int mrc_cache_get_current(int type, uint32_t version, + struct region_device *rdev); +int mrc_cache_stash_data(int type, uint32_t version, const void *data, + size_t size); #endif /* _COMMON_MRC_CACHE_H_ */ diff --git a/src/soc/intel/common/nvm.c b/src/soc/intel/common/nvm.c index 99a1fbfb15..b7e0f4e7ac 100644 --- a/src/soc/intel/common/nvm.c +++ b/src/soc/intel/common/nvm.c @@ -24,113 +24,43 @@ #include "nvm.h" #include "spi_flash.h" -/* This module assumes the flash is memory mapped just below 4GiB in the - * address space for reading. Also this module assumes an area it erased - * when all bytes read as all 0xff's. */ - -static struct spi_flash *flash; - -static int nvm_init(void) +/* Read flash status register to determine if write protect is active */ +int nvm_is_write_protected(void) { - if (flash != NULL) - return 0; + u8 sr1; + u8 wp_gpio; + u8 wp_spi; - spi_init(); - flash = spi_flash_probe(0, 0); - if (!flash) { - printk(BIOS_DEBUG, "Could not find SPI device\n"); - return -1; - } - - return 0; -} + if (!IS_ENABLED(CONFIG_CHROMEOS)) + return 0; -/* - * Convert memory mapped pointer to flash offset. - * - * This is weak because not every platforms memory-maps the NVM media in the - * same manner. This is a stop-gap solution. - * - * The root of the problem is that users of this API work in memory space for - * both reads and writes, but erase and write must be done in flash space. This - * also only works when the media is memory-mapped, which is no longer - * universally true. The long-term approach should be to rewrite this and its - * users to work in flash space, while using rdev_read() instead of rdev_mmap(). - */ -__attribute__((weak)) -uint32_t nvm_mmio_to_flash_offset(void *p) -{ - return CONFIG_ROM_SIZE + (uintptr_t)p; -} + if (!IS_ENABLED(CONFIG_BOOT_DEVICE_SPI_FLASH)) + return 0; -int nvm_is_erased(const void *start, size_t size) -{ - const uint8_t *cur = start; - const uint8_t erased_value = 0xff; + /* Read Write Protect GPIO if available */ + wp_gpio = get_write_protect_state(); - while (size > 0) { - if (*cur != erased_value) - return 0; - cur++; - size--; + /* Read Status Register 1 */ + if (spi_flash_status(boot_device_spi_flash(), &sr1) < 0) { + printk(BIOS_ERR, "Failed to read SPI status register 1\n"); + return -1; } - return 1; -} + wp_spi = !!(sr1 & 0x80); -int nvm_erase(void *start, size_t size) -{ - if (nvm_init() < 0) - return -1; - return spi_flash_erase(flash, nvm_mmio_to_flash_offset(start), size); -} + printk(BIOS_DEBUG, "SPI flash protection: WPSW=%d SRP0=%d\n", + wp_gpio, wp_spi); -/* Write data to NVM. Returns 0 on success < 0 on error. */ -int nvm_write(void *start, const void *data, size_t size) -{ - if (nvm_init() < 0) - return -1; - return spi_flash_write(flash, nvm_mmio_to_flash_offset(start), size, - data); + return wp_gpio && wp_spi; } -/* Read flash status register to determine if write protect is active */ -int nvm_is_write_protected(void) +/* Apply protection to a range of flash */ +int nvm_protect(const struct region *r) { - if (nvm_init() < 0) - return -1; - - if (IS_ENABLED(CONFIG_CHROMEOS)) { - u8 sr1; - u8 wp_gpio; - u8 wp_spi; - - /* Read Write Protect GPIO if available */ - wp_gpio = get_write_protect_state(); - - /* Read Status Register 1 */ - if (spi_flash_status(flash, &sr1) < 0) { - printk(BIOS_ERR, - "Failed to read SPI status register 1\n"); - return -1; - } - wp_spi = !!(sr1 & 0x80); - - printk(BIOS_DEBUG, "SPI flash protection: WPSW=%d SRP0=%d\n", - wp_gpio, wp_spi); + if (!IS_ENABLED(CONFIG_MRC_SETTINGS_PROTECT)) + return 0; - return wp_gpio && wp_spi; - } - return 0; -} + if (!IS_ENABLED(CONFIG_BOOT_DEVICE_SPI_FLASH)) + return 0; -/* Apply protection to a range of flash */ -int nvm_protect(void *start, size_t size) -{ -#if IS_ENABLED(CONFIG_MRC_SETTINGS_PROTECT) - if (nvm_init() < 0) - return -1; - return spi_flash_protect(nvm_mmio_to_flash_offset(start), size); -#else - return -1; -#endif + return spi_flash_protect(region_offset(r), region_sz(r)); } diff --git a/src/soc/intel/common/nvm.h b/src/soc/intel/common/nvm.h index 074a2ce406..1e32ed8637 100644 --- a/src/soc/intel/common/nvm.h +++ b/src/soc/intel/common/nvm.h @@ -16,24 +16,12 @@ #ifndef _COMMON_NVM_H_ #define _COMMON_NVM_H_ -#include <stddef.h> - -/* Determine if area is erased. returns 1 if erased. 0 otherwise. */ -int nvm_is_erased(const void *start, size_t size); - -/* Erase region according to start and size. Returns < 0 on error else 0. */ -int nvm_erase(void *start, size_t size); - -/* Write data to NVM. Returns 0 on success < 0 on error. */ -int nvm_write(void *start, const void *data, size_t size); +#include <commonlib/region.h> /* Determine if flash device is write protected */ int nvm_is_write_protected(void); /* Apply protection to a range of flash */ -int nvm_protect(void *start, size_t size); - -/* Map MMIO address to actual address in flash */ -uint32_t nvm_mmio_to_flash_offset(void *p); +int nvm_protect(const struct region *region); #endif /* _COMMON_NVM_H_ */ |