Skip to content

Commit c1add2c

Browse files
RICCIARDI-Adrienanangl
authored andcommitted
[nrf fromtree] fs: nvs: Add CRC-32 to protect data
Allow to protect the data part of each NVS item with a 32-bit CRC. This uses 4 more bytes per NVS item. Signed-off-by: Adrien Ricciardi <[email protected]> (cherry picked from commit b76d630) Signed-off-by: Andrzej Głąbek <[email protected]>
1 parent c3b2f48 commit c1add2c

File tree

3 files changed

+103
-13
lines changed

3 files changed

+103
-13
lines changed

subsys/fs/nvs/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ config NVS_LOOKUP_CACHE_SIZE
2929
Number of entries in Non-volatile Storage lookup cache.
3030
It is recommended that it be a power of 2.
3131

32+
config NVS_DATA_CRC
33+
bool "Non-volatile Storage CRC protection on the data"
34+
help
35+
Enable a CRC-32 on the data part of each NVS element.
36+
The ATE CRC is not impacted by this feature and stays the same.
37+
The CRC-32 is transparently stored at the end of the data field,
38+
in the NVS data section, so 4 more bytes are needed per NVS element.
39+
3240
module = NVS
3341
module-str = nvs
3442
source "subsys/logging/Kconfig.template.log_config"

subsys/fs/nvs/nvs.c

Lines changed: 86 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -175,11 +175,44 @@ static int nvs_flash_ate_wrt(struct nvs_fs *fs, const struct nvs_ate *entry)
175175
}
176176

177177
/* data write */
178-
static int nvs_flash_data_wrt(struct nvs_fs *fs, const void *data, size_t len)
178+
static int nvs_flash_data_wrt(struct nvs_fs *fs, const void *data, size_t len, bool compute_crc)
179179
{
180180
int rc;
181181

182-
rc = nvs_flash_al_wrt(fs, fs->data_wra, data, len);
182+
/* Only add the CRC if required (ignore deletion requests, i.e. when len is 0) */
183+
if (IS_ENABLED(CONFIG_NVS_DATA_CRC) && compute_crc && (len > 0)) {
184+
size_t aligned_len, data_len = len;
185+
uint8_t *data8 = (uint8_t *)data, buf[NVS_BLOCK_SIZE + NVS_DATA_CRC_SIZE], *pbuf;
186+
uint32_t data_crc;
187+
188+
/* Write as much aligned data as possible, so the CRC can be concatenated at
189+
* the end of the unaligned data later
190+
*/
191+
aligned_len = len & ~(fs->flash_parameters->write_block_size - 1U);
192+
rc = nvs_flash_al_wrt(fs, fs->data_wra, data8, aligned_len);
193+
fs->data_wra += aligned_len;
194+
if (rc) {
195+
return rc;
196+
}
197+
data8 += aligned_len;
198+
len -= aligned_len;
199+
200+
/* Create a buffer with the unaligned data if any */
201+
pbuf = buf;
202+
if (len) {
203+
memcpy(pbuf, data8, len);
204+
pbuf += len;
205+
}
206+
207+
/* Append the CRC */
208+
data_crc = crc32_ieee(data, data_len);
209+
memcpy(pbuf, &data_crc, sizeof(data_crc));
210+
len += sizeof(data_crc);
211+
212+
rc = nvs_flash_al_wrt(fs, fs->data_wra, buf, len);
213+
} else {
214+
rc = nvs_flash_al_wrt(fs, fs->data_wra, data, len);
215+
}
183216
fs->data_wra += nvs_al_size(fs, len);
184217

185218
return rc;
@@ -279,7 +312,10 @@ static int nvs_flash_block_move(struct nvs_fs *fs, uint32_t addr, size_t len)
279312
if (rc) {
280313
return rc;
281314
}
282-
rc = nvs_flash_data_wrt(fs, buf, bytes_to_copy);
315+
/* Just rewrite the whole record, no need to recompute the CRC as the data
316+
* did not change
317+
*/
318+
rc = nvs_flash_data_wrt(fs, buf, bytes_to_copy, false);
283319
if (rc) {
284320
return rc;
285321
}
@@ -348,7 +384,6 @@ static int nvs_ate_crc8_check(const struct nvs_ate *entry)
348384
/* nvs_ate_cmp_const compares an ATE to a constant value. returns 0 if
349385
* the whole ATE is equal to value, 1 if not equal.
350386
*/
351-
352387
static int nvs_ate_cmp_const(const struct nvs_ate *entry, uint8_t value)
353388
{
354389
const uint8_t *data8 = (const uint8_t *)entry;
@@ -416,12 +451,19 @@ static int nvs_flash_wrt_entry(struct nvs_fs *fs, uint16_t id, const void *data,
416451
entry.len = (uint16_t)len;
417452
entry.part = 0xff;
418453

419-
nvs_ate_crc8_update(&entry);
420-
421-
rc = nvs_flash_data_wrt(fs, data, len);
454+
rc = nvs_flash_data_wrt(fs, data, len, true);
422455
if (rc) {
423456
return rc;
424457
}
458+
459+
#ifdef CONFIG_NVS_DATA_CRC
460+
/* No CRC has been added if this is a deletion write request */
461+
if (len > 0) {
462+
entry.len += NVS_DATA_CRC_SIZE;
463+
}
464+
#endif
465+
nvs_ate_crc8_update(&entry);
466+
425467
rc = nvs_flash_ate_wrt(fs, &entry);
426468
if (rc) {
427469
return rc;
@@ -1030,8 +1072,10 @@ ssize_t nvs_write(struct nvs_fs *fs, uint16_t id, const void *data, size_t len)
10301072
/* The maximum data size is sector size - 4 ate
10311073
* where: 1 ate for data, 1 ate for sector close, 1 ate for gc done,
10321074
* and 1 ate to always allow a delete.
1075+
* Also take into account the data CRC that is appended at the end of the data field,
1076+
* if any.
10331077
*/
1034-
if ((len > (fs->sector_size - 4 * ate_size)) ||
1078+
if ((len > (fs->sector_size - 4 * ate_size - NVS_DATA_CRC_SIZE)) ||
10351079
((len > 0) && (data == NULL))) {
10361080
return -EINVAL;
10371081
}
@@ -1080,10 +1124,10 @@ ssize_t nvs_write(struct nvs_fs *fs, uint16_t id, const void *data, size_t len)
10801124
*/
10811125
return 0;
10821126
}
1083-
} else if (len == wlk_ate.len) {
1127+
} else if (len + NVS_DATA_CRC_SIZE == wlk_ate.len) {
10841128
/* do not try to compare if lengths are not equal */
10851129
/* compare the data and if equal return 0 */
1086-
rc = nvs_flash_block_cmp(fs, rd_addr, data, len);
1130+
rc = nvs_flash_block_cmp(fs, rd_addr, data, len + NVS_DATA_CRC_SIZE);
10871131
if (rc <= 0) {
10881132
return rc;
10891133
}
@@ -1098,7 +1142,7 @@ ssize_t nvs_write(struct nvs_fs *fs, uint16_t id, const void *data, size_t len)
10981142
/* calculate required space if the entry contains data */
10991143
if (data_size) {
11001144
/* Leave space for delete ate */
1101-
required_space = data_size + ate_size;
1145+
required_space = data_size + ate_size + NVS_DATA_CRC_SIZE;
11021146
}
11031147

11041148
k_mutex_lock(&fs->nvs_lock, K_FOREVER);
@@ -1153,6 +1197,9 @@ ssize_t nvs_read_hist(struct nvs_fs *fs, uint16_t id, void *data, size_t len,
11531197
uint16_t cnt_his;
11541198
struct nvs_ate wlk_ate;
11551199
size_t ate_size;
1200+
#ifdef CONFIG_NVS_DATA_CRC
1201+
uint32_t read_data_crc, computed_data_crc;
1202+
#endif
11561203

11571204
if (!fs->ready) {
11581205
LOG_ERR("NVS not initialized");
@@ -1198,14 +1245,40 @@ ssize_t nvs_read_hist(struct nvs_fs *fs, uint16_t id, void *data, size_t len,
11981245
return -ENOENT;
11991246
}
12001247

1248+
#ifdef CONFIG_NVS_DATA_CRC
1249+
/* When data CRC is enabled, there should be at least the CRC stored in the data field */
1250+
if (wlk_ate.len < NVS_DATA_CRC_SIZE) {
1251+
return -ENOENT;
1252+
}
1253+
#endif
1254+
12011255
rd_addr &= ADDR_SECT_MASK;
12021256
rd_addr += wlk_ate.offset;
1203-
rc = nvs_flash_rd(fs, rd_addr, data, MIN(len, wlk_ate.len));
1257+
rc = nvs_flash_rd(fs, rd_addr, data, MIN(len, wlk_ate.len - NVS_DATA_CRC_SIZE));
12041258
if (rc) {
12051259
goto err;
12061260
}
12071261

1208-
return wlk_ate.len;
1262+
/* Check data CRC (only if the whole element data has been read) */
1263+
#ifdef CONFIG_NVS_DATA_CRC
1264+
if (len >= (wlk_ate.len - NVS_DATA_CRC_SIZE)) {
1265+
rd_addr += wlk_ate.len - NVS_DATA_CRC_SIZE;
1266+
rc = nvs_flash_rd(fs, rd_addr, &read_data_crc, sizeof(read_data_crc));
1267+
if (rc) {
1268+
goto err;
1269+
}
1270+
1271+
computed_data_crc = crc32_ieee(data, wlk_ate.len - NVS_DATA_CRC_SIZE);
1272+
if (read_data_crc != computed_data_crc) {
1273+
LOG_ERR("Invalid data CRC: read_data_crc=0x%08X, computed_data_crc=0x%08X",
1274+
read_data_crc, computed_data_crc);
1275+
rc = -EIO;
1276+
goto err;
1277+
}
1278+
}
1279+
#endif
1280+
1281+
return wlk_ate.len - NVS_DATA_CRC_SIZE;
12091282

12101283
err:
12111284
return rc;

subsys/fs/nvs/nvs_priv.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,15 @@ extern "C" {
3030

3131
#define NVS_LOOKUP_CACHE_NO_ADDR 0xFFFFFFFF
3232

33+
/*
34+
* Allow to use the NVS_DATA_CRC_SIZE macro in computations whether data CRC is enabled or not
35+
*/
36+
#ifdef CONFIG_NVS_DATA_CRC
37+
#define NVS_DATA_CRC_SIZE 4 /* CRC-32 size in bytes */
38+
#else
39+
#define NVS_DATA_CRC_SIZE 0
40+
#endif
41+
3342
/* Allocation Table Entry */
3443
struct nvs_ate {
3544
uint16_t id; /* data id */

0 commit comments

Comments
 (0)