@@ -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-
352387static 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
12101283err :
12111284 return rc ;
0 commit comments