Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
nvstore.cpp
00001 /* 00002 * Copyright (c) 2018 ARM Limited. All rights reserved. 00003 * SPDX-License-Identifier: Apache-2.0 00004 * Licensed under the Apache License, Version 2.0 (the License); you may 00005 * not use this file except in compliance with the License. 00006 * You may obtain a copy of the License at 00007 * 00008 * http://www.apache.org/licenses/LICENSE-2.0 00009 * 00010 * Unless required by applicable law or agreed to in writing, software 00011 * distributed under the License is distributed on an AS IS BASIS, WITHOUT 00012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00013 * See the License for the specific language governing permissions and 00014 * limitations under the License. 00015 */ 00016 00017 // ----------------------------------------------------------- Includes ----------------------------------------------------------- 00018 00019 #include "nvstore.h" 00020 00021 #if NVSTORE_ENABLED 00022 00023 #include "FlashIAP.h" 00024 #include "mbed_critical.h" 00025 #include "mbed_assert.h" 00026 #include "Thread.h" 00027 #include "mbed_wait_api.h" 00028 #include <algorithm> 00029 #include <string.h> 00030 #include <stdio.h> 00031 00032 // --------------------------------------------------------- Definitions ---------------------------------------------------------- 00033 00034 static const uint16_t delete_item_flag = 0x8000; 00035 static const uint16_t set_once_flag = 0x4000; 00036 static const uint16_t header_flag_mask = 0xF000; 00037 00038 static const uint16_t master_record_key = 0xFFE; 00039 static const uint16_t no_key = 0xFFF; 00040 static const uint16_t last_reserved_key = master_record_key; 00041 00042 typedef struct 00043 { 00044 uint16_t key_and_flags; 00045 uint16_t size; 00046 uint32_t crc; 00047 } nvstore_record_header_t; 00048 00049 static const uint32_t offs_by_key_area_mask = 0x80000000UL; 00050 static const uint32_t offs_by_key_set_once_mask = 0x40000000UL; 00051 static const uint32_t offs_by_key_flag_mask = 0xC0000000UL; 00052 static const unsigned int offs_by_key_area_bit_pos = 31; 00053 static const unsigned int offs_by_key_set_once_bit_pos = 30; 00054 00055 typedef struct { 00056 uint16_t version; 00057 uint16_t reserved1; 00058 uint32_t reserved2; 00059 } master_record_data_t; 00060 00061 static const uint32_t min_area_size = 4096; 00062 00063 static const int num_write_retries = 16; 00064 00065 static const uint8_t blank_flash_val = 0xFF; 00066 00067 // See whether any of these defines are given (by config files) 00068 // If so, this means that that area configuration is given by the user 00069 #if defined(NVSTORE_AREA_1_ADDRESS) || defined(NVSTORE_AREA_1_SIZE) ||\ 00070 defined(NVSTORE_AREA_2_ADDRESS) || defined(NVSTORE_AREA_2_SIZE) 00071 00072 // Require all area configuration parameters if any one of them is present 00073 #if !defined(NVSTORE_AREA_1_ADDRESS) || !defined(NVSTORE_AREA_1_SIZE) ||\ 00074 !defined(NVSTORE_AREA_2_ADDRESS) || !defined(NVSTORE_AREA_2_SIZE) 00075 #error Incomplete NVStore area configuration 00076 #endif 00077 #if (NVSTORE_AREA_1_SIZE == 0) || (NVSTORE_AREA_2_SIZE == 0) 00078 #error NVStore area size cannot be 0 00079 #endif 00080 00081 NVStore::nvstore_area_data_t NVStore::initial_area_params[] = {{NVSTORE_AREA_1_ADDRESS, NVSTORE_AREA_1_SIZE}, 00082 {NVSTORE_AREA_2_ADDRESS, NVSTORE_AREA_2_SIZE}}; 00083 #else 00084 NVStore::nvstore_area_data_t NVStore::initial_area_params[] = {{0, 0}, 00085 {0, 0}}; 00086 #endif 00087 00088 typedef enum { 00089 NVSTORE_AREA_STATE_NONE = 0, 00090 NVSTORE_AREA_STATE_EMPTY, 00091 NVSTORE_AREA_STATE_VALID, 00092 } area_state_e; 00093 00094 static const uint32_t initial_crc = 0xFFFFFFFF; 00095 00096 00097 // -------------------------------------------------- Local Functions Declaration ---------------------------------------------------- 00098 00099 // -------------------------------------------------- Functions Implementation ---------------------------------------------------- 00100 00101 // Align a value to a specified size. 00102 // Parameters : 00103 // val - [IN] Value. 00104 // size - [IN] Size. 00105 // Return : Aligned value. 00106 static inline uint32_t align_up(uint32_t val, uint32_t size) 00107 { 00108 return (((val - 1) / size) + 1) * size; 00109 } 00110 00111 // CRC32 calculation. Supports "rolling" calculation (using the initial value). 00112 // Parameters : 00113 // init_crc - [IN] Initial CRC. 00114 // data_size - [IN] Buffer's data size. 00115 // data_buf - [IN] Data buffer. 00116 // Return : CRC. 00117 static uint32_t crc32(uint32_t init_crc, uint32_t data_size, uint8_t *data_buf) 00118 { 00119 uint32_t i, j; 00120 uint32_t crc, mask; 00121 00122 crc = init_crc; 00123 for (i = 0; i < data_size; i++) { 00124 crc = crc ^ (uint32_t) (data_buf[i]); 00125 for (j = 0; j < 8; j++) { 00126 mask = -(crc & 1); 00127 crc = (crc >> 1) ^ (0xEDB88320 & mask); 00128 } 00129 } 00130 return crc; 00131 } 00132 00133 NVStore::NVStore() : _init_done(0), _init_attempts(0), _active_area(0), _max_keys(NVSTORE_MAX_KEYS), 00134 _active_area_version(0), _free_space_offset(0), _size(0), _mutex(0), _offset_by_key(0), _flash(0), 00135 _min_prog_size(0), _page_buf(0) 00136 { 00137 } 00138 00139 NVStore::~NVStore() 00140 { 00141 if (_init_done) { 00142 deinit(); 00143 } 00144 } 00145 00146 uint16_t NVStore::get_max_keys() const 00147 { 00148 return _max_keys; 00149 } 00150 00151 uint16_t NVStore::get_max_possible_keys() 00152 { 00153 if (!_init_done) { 00154 init(); 00155 } 00156 00157 size_t max_possible_keys = _size / align_up(sizeof(nvstore_record_header_t) * 2, _min_prog_size) - 1; 00158 00159 return (uint16_t)std::min(max_possible_keys, (size_t) last_reserved_key); 00160 } 00161 00162 void NVStore::set_max_keys(uint16_t num_keys) 00163 { 00164 MBED_ASSERT(num_keys < get_max_possible_keys()); 00165 _max_keys = num_keys; 00166 // User is allowed to change number of keys. As this affects init, need to deinitialize now. 00167 // Don't call init right away - it is lazily called by get/set functions if needed. 00168 deinit(); 00169 } 00170 00171 int NVStore::flash_read_area(uint8_t area, uint32_t offset, uint32_t size, void *buf) 00172 { 00173 return _flash->read(buf, _flash_area_params[area].address + offset, size); 00174 } 00175 00176 int NVStore::flash_write_area(uint8_t area, uint32_t offset, uint32_t size, const void *buf) 00177 { 00178 int ret; 00179 // On some boards, write action can fail due to HW limitations (like critical drivers 00180 // that disable all other actions). Just retry a few times until success. 00181 for (int i = 0; i < num_write_retries; i++) { 00182 ret = _flash->program(buf, _flash_area_params[area].address + offset, size); 00183 if (!ret) { 00184 return ret; 00185 } 00186 wait_ms(1); 00187 } 00188 return ret; 00189 } 00190 00191 int NVStore::flash_erase_area(uint8_t area) 00192 { 00193 int ret; 00194 // On some boards, write action can fail due to HW limitations (like critical drivers 00195 // that disable all other actions). Just retry a few times until success. 00196 for (int i = 0; i < num_write_retries; i++) { 00197 ret = _flash->erase(_flash_area_params[area].address, _flash_area_params[area].size); 00198 if (!ret) { 00199 return ret; 00200 } 00201 wait_ms(1); 00202 } 00203 return ret; 00204 } 00205 00206 void NVStore::calc_validate_area_params() 00207 { 00208 int num_sectors = 0; 00209 00210 size_t flash_addr = _flash->get_flash_start(); 00211 size_t flash_size = _flash->get_flash_size(); 00212 size_t sector_size; 00213 int max_sectors = flash_size / _flash->get_sector_size(flash_addr) + 1; 00214 size_t *sector_map = new size_t[max_sectors]; 00215 00216 int area = 0; 00217 size_t left_size = flash_size; 00218 00219 memcpy(_flash_area_params, initial_area_params, sizeof(_flash_area_params)); 00220 int user_config = (_flash_area_params[0].size != 0); 00221 int in_area = 0; 00222 size_t area_size = 0; 00223 00224 while (left_size) { 00225 sector_size = _flash->get_sector_size(flash_addr); 00226 sector_map[num_sectors++] = flash_addr; 00227 00228 if (user_config) { 00229 // User configuration - here we validate it 00230 // Check that address is on a sector boundary, that size covers complete sector sizes, 00231 // and that areas don't overlap. 00232 if (_flash_area_params[area].address == flash_addr) { 00233 in_area = 1; 00234 } 00235 if (in_area) { 00236 area_size += sector_size; 00237 if (area_size == _flash_area_params[area].size) { 00238 area++; 00239 if (area == NVSTORE_NUM_AREAS) { 00240 break; 00241 } 00242 in_area = 0; 00243 area_size = 0; 00244 } 00245 } 00246 } 00247 00248 flash_addr += sector_size; 00249 left_size -= sector_size; 00250 } 00251 sector_map[num_sectors] = flash_addr; 00252 00253 if (user_config) { 00254 // Valid areas were counted. Assert if not the expected number. 00255 MBED_ASSERT(area == NVSTORE_NUM_AREAS); 00256 } else { 00257 // Not user configuration - calculate area parameters. 00258 // Take last two sectors by default. If their sizes aren't big enough, take 00259 // a few consecutive ones. 00260 area = 1; 00261 _flash_area_params[area].size = 0; 00262 int i; 00263 for (i = num_sectors - 1; i >= 0; i--) { 00264 sector_size = sector_map[i+1] - sector_map[i]; 00265 _flash_area_params[area].size += sector_size; 00266 if (_flash_area_params[area].size >= min_area_size) { 00267 _flash_area_params[area].address = sector_map[i]; 00268 area--; 00269 if (area < 0) { 00270 break; 00271 } 00272 _flash_area_params[area].size = 0; 00273 } 00274 } 00275 } 00276 00277 delete[] sector_map; 00278 } 00279 00280 00281 int NVStore::calc_empty_space(uint8_t area, uint32_t &offset) 00282 { 00283 uint32_t buf[32]; 00284 uint8_t *chbuf; 00285 uint32_t i, j; 00286 int ret; 00287 00288 offset = _size; 00289 for (i = 0; i < _size / sizeof(buf); i++) { 00290 offset -= sizeof(buf); 00291 ret = flash_read_area(area, offset, sizeof(buf), buf); 00292 if (ret) { 00293 return ret; 00294 } 00295 chbuf = (uint8_t *) buf; 00296 for (j = sizeof(buf); j > 0; j--) { 00297 if (chbuf[j - 1] != blank_flash_val) { 00298 offset += j; 00299 return 0; 00300 } 00301 } 00302 } 00303 return 0; 00304 } 00305 00306 int NVStore::read_record(uint8_t area, uint32_t offset, uint16_t buf_size, void *buf, 00307 uint16_t &actual_size, int validate_only, int &valid, 00308 uint16_t &key, uint16_t &flags, uint32_t &next_offset) 00309 { 00310 uint8_t int_buf[128]; 00311 void *buf_ptr; 00312 uint16_t data_size, chunk_size; 00313 int os_ret; 00314 nvstore_record_header_t header; 00315 uint32_t crc = initial_crc; 00316 00317 valid = 1; 00318 00319 os_ret = flash_read_area(area, offset, sizeof(header), &header); 00320 if (os_ret) { 00321 return NVSTORE_READ_ERROR; 00322 } 00323 00324 crc = crc32(crc, sizeof(header) - sizeof(header.crc), (uint8_t *) &header); 00325 00326 actual_size = 0; 00327 key = header.key_and_flags & ~header_flag_mask; 00328 flags = header.key_and_flags & header_flag_mask; 00329 00330 if ((key >= _max_keys) && (key != master_record_key)) { 00331 valid = 0; 00332 return NVSTORE_SUCCESS; 00333 } 00334 00335 data_size = header.size; 00336 offset += sizeof(header); 00337 00338 // In case of validate only enabled, we use our internal buffer for data reading, 00339 // instead of the user one. This allows us to use a smaller buffer, on which CRC 00340 // is continuously calculated. 00341 if (validate_only) { 00342 buf_ptr = int_buf; 00343 buf_size = sizeof(int_buf); 00344 } else { 00345 if (data_size > buf_size) { 00346 offset += data_size; 00347 actual_size = data_size; 00348 next_offset = align_up(offset, _min_prog_size); 00349 return NVSTORE_BUFF_TOO_SMALL; 00350 } 00351 buf_ptr = buf; 00352 } 00353 00354 while (data_size) { 00355 chunk_size = std::min(data_size, buf_size); 00356 os_ret = flash_read_area(area, offset, chunk_size, buf_ptr); 00357 if (os_ret) { 00358 return NVSTORE_READ_ERROR; 00359 } 00360 crc = crc32(crc, chunk_size, (uint8_t *) buf_ptr); 00361 data_size -= chunk_size; 00362 offset += chunk_size; 00363 } 00364 00365 if (header.crc != crc) { 00366 valid = 0; 00367 return NVSTORE_SUCCESS; 00368 } 00369 00370 actual_size = header.size; 00371 next_offset = align_up(offset, _min_prog_size); 00372 00373 return NVSTORE_SUCCESS; 00374 } 00375 00376 int NVStore::write_record(uint8_t area, uint32_t offset, uint16_t key, uint16_t flags, 00377 uint32_t data_size, const void *data_buf, uint32_t &next_offset) 00378 { 00379 nvstore_record_header_t header; 00380 uint32_t crc = initial_crc; 00381 int os_ret; 00382 uint8_t *prog_buf; 00383 00384 header.key_and_flags = key | flags; 00385 header.size = data_size; 00386 header.crc = 0; // Satisfy compiler 00387 crc = crc32(crc, sizeof(header) - sizeof(header.crc), (uint8_t *) &header); 00388 if (data_size) { 00389 crc = crc32(crc, data_size, (uint8_t *) data_buf); 00390 } 00391 header.crc = crc; 00392 00393 // In case page size is greater than header size, we can't write header and data 00394 // separately. Instead, we need to copy header and start of data to our page buffer 00395 // and write them together. Otherwise, simply write header and data separately. 00396 uint32_t prog_size = sizeof(header); 00397 uint32_t copy_size = 0; 00398 if (_min_prog_size > sizeof(header)) { 00399 prog_buf = _page_buf; 00400 memcpy(prog_buf, &header, sizeof(header)); 00401 if (data_size) { 00402 memcpy(prog_buf, &header, sizeof(header)); 00403 copy_size = std::min(data_size, _min_prog_size - sizeof(header)); 00404 memcpy(prog_buf + sizeof(header), data_buf, copy_size); 00405 data_size -= copy_size; 00406 prog_size += copy_size; 00407 } 00408 } else { 00409 prog_buf = (uint8_t *) &header; 00410 } 00411 00412 os_ret = flash_write_area(area, offset, prog_size, prog_buf); 00413 if (os_ret) { 00414 return NVSTORE_WRITE_ERROR; 00415 } 00416 offset += prog_size; 00417 00418 if (data_size) { 00419 prog_buf = (uint8_t *) data_buf + copy_size; 00420 os_ret = flash_write_area(area, offset, data_size, prog_buf); 00421 if (os_ret) { 00422 return NVSTORE_WRITE_ERROR; 00423 } 00424 offset += data_size; 00425 } 00426 00427 next_offset = align_up(offset, _min_prog_size); 00428 return NVSTORE_SUCCESS; 00429 } 00430 00431 int NVStore::write_master_record(uint8_t area, uint16_t version, uint32_t &next_offset) 00432 { 00433 master_record_data_t master_rec; 00434 00435 master_rec.version = version; 00436 master_rec.reserved1 = 0; 00437 master_rec.reserved2 = 0; 00438 return write_record(area, 0, master_record_key, 0, sizeof(master_rec), 00439 &master_rec, next_offset); 00440 } 00441 00442 int NVStore::copy_record(uint8_t from_area, uint32_t from_offset, uint32_t to_offset, 00443 uint32_t &next_offset) 00444 { 00445 uint8_t local_buf[128]; 00446 uint16_t record_size, chunk_size, prog_buf_size; 00447 int os_ret; 00448 nvstore_record_header_t *header; 00449 uint8_t *read_buf, *prog_buf; 00450 00451 // This function assumes that the source record is valid, so no need to recalculate CRC. 00452 00453 if (_min_prog_size > sizeof(nvstore_record_header_t)) { 00454 prog_buf = _page_buf; 00455 prog_buf_size = _min_prog_size; 00456 } else { 00457 prog_buf = local_buf; 00458 prog_buf_size = sizeof(local_buf); 00459 } 00460 read_buf = prog_buf; 00461 00462 os_ret = flash_read_area(from_area, from_offset, sizeof(nvstore_record_header_t), read_buf); 00463 if (os_ret) { 00464 return NVSTORE_READ_ERROR; 00465 } 00466 00467 header = (nvstore_record_header_t *) read_buf; 00468 record_size = sizeof(nvstore_record_header_t) + header->size; 00469 00470 // No need to copy records whose flags indicate deletion 00471 if (header->key_and_flags & delete_item_flag) { 00472 next_offset = align_up(to_offset, _min_prog_size); 00473 return NVSTORE_SUCCESS; 00474 } 00475 00476 // no need to align record size here, as it won't change the outcome of this condition 00477 if (to_offset + record_size >= _size) { 00478 return NVSTORE_FLASH_AREA_TOO_SMALL; 00479 } 00480 00481 uint16_t start_size = sizeof(nvstore_record_header_t); 00482 from_offset += start_size; 00483 read_buf += start_size; 00484 record_size -= start_size; 00485 00486 do { 00487 chunk_size = std::min(record_size, (uint16_t)(prog_buf_size - start_size)); 00488 if (chunk_size) { 00489 os_ret = flash_read_area(from_area, from_offset, chunk_size, read_buf); 00490 if (os_ret) { 00491 return NVSTORE_READ_ERROR; 00492 } 00493 } 00494 os_ret = flash_write_area(1 - from_area, to_offset, chunk_size + start_size, prog_buf); 00495 if (os_ret) { 00496 return NVSTORE_WRITE_ERROR; 00497 } 00498 00499 read_buf = prog_buf; 00500 record_size -= chunk_size; 00501 from_offset += chunk_size; 00502 to_offset += chunk_size + start_size; 00503 start_size = 0; 00504 } while (record_size); 00505 00506 next_offset = align_up(to_offset, _min_prog_size); 00507 return NVSTORE_SUCCESS; 00508 } 00509 00510 int NVStore::garbage_collection(uint16_t key, uint16_t flags, uint16_t buf_size, const void *buf) 00511 { 00512 uint32_t curr_offset, new_area_offset, next_offset; 00513 int ret; 00514 uint8_t curr_area; 00515 00516 new_area_offset = align_up(sizeof(nvstore_record_header_t) + sizeof(master_record_data_t), _min_prog_size); 00517 00518 // If GC is triggered by a set item request, we need to first write that item in the new location, 00519 // otherwise we may either write it twice (if already included), or lose it in case we decide 00520 // to skip it at garbage collection phase (and the system crashes). 00521 if ((key != no_key) && !(flags & delete_item_flag)) { 00522 ret = write_record(1 - _active_area, new_area_offset, key, 0, buf_size, buf, next_offset); 00523 if (ret != NVSTORE_SUCCESS) { 00524 return ret; 00525 } 00526 _offset_by_key[key] = new_area_offset | (1 - _active_area) << offs_by_key_area_bit_pos | 00527 (((flags & set_once_flag) != 0) << offs_by_key_set_once_bit_pos); 00528 new_area_offset = next_offset; 00529 } 00530 00531 // Now iterate on all types, and copy the ones who have valid offsets (meaning that they exist) 00532 // to the other area. 00533 for (key = 0; key < _max_keys; key++) { 00534 curr_offset = _offset_by_key[key]; 00535 uint16_t save_flags = curr_offset & offs_by_key_area_mask; 00536 curr_area = (uint8_t)(curr_offset >> offs_by_key_area_bit_pos) & 1; 00537 curr_offset &= ~offs_by_key_flag_mask; 00538 if ((!curr_offset) || (curr_area != _active_area)) { 00539 continue; 00540 } 00541 ret = copy_record(curr_area, curr_offset, new_area_offset, next_offset); 00542 if (ret != NVSTORE_SUCCESS) { 00543 return ret; 00544 } 00545 _offset_by_key[key] = new_area_offset | (1 - curr_area) << offs_by_key_area_bit_pos | save_flags; 00546 new_area_offset = next_offset; 00547 } 00548 00549 // Now write master record, with version incremented by 1. 00550 _active_area_version++; 00551 ret = write_master_record(1 - _active_area, _active_area_version, next_offset); 00552 if (ret != NVSTORE_SUCCESS) { 00553 return ret; 00554 } 00555 00556 _free_space_offset = new_area_offset; 00557 00558 // Only now we can switch to the new active area 00559 _active_area = 1 - _active_area; 00560 00561 // The older area doesn't concern us now. Erase it now. 00562 if (flash_erase_area(1 - _active_area)) { 00563 return NVSTORE_WRITE_ERROR; 00564 } 00565 00566 return ret; 00567 } 00568 00569 00570 int NVStore::do_get(uint16_t key, uint16_t buf_size, void *buf, uint16_t &actual_size, 00571 int validate_only) 00572 { 00573 int ret = NVSTORE_SUCCESS; 00574 int valid; 00575 uint32_t record_offset, next_offset; 00576 uint16_t read_type, flags; 00577 uint8_t area; 00578 00579 if (!_init_done) { 00580 ret = init(); 00581 if (ret != NVSTORE_SUCCESS) { 00582 return ret; 00583 } 00584 } 00585 00586 if (key >= _max_keys) { 00587 return NVSTORE_BAD_VALUE; 00588 } 00589 00590 if (!buf) { 00591 buf_size = 0; 00592 } 00593 00594 _mutex->lock(); 00595 record_offset = _offset_by_key[key]; 00596 00597 if (!record_offset) { 00598 _mutex->unlock(); 00599 return NVSTORE_NOT_FOUND; 00600 } 00601 00602 area = (uint8_t)(record_offset >> offs_by_key_area_bit_pos) & 1; 00603 record_offset &= ~offs_by_key_flag_mask; 00604 00605 ret = read_record(area, record_offset, buf_size, buf, 00606 actual_size, validate_only, valid, 00607 read_type, flags, next_offset); 00608 if ((ret == NVSTORE_SUCCESS) && !valid) { 00609 ret = NVSTORE_DATA_CORRUPT; 00610 } 00611 00612 _mutex->unlock(); 00613 return ret; 00614 } 00615 00616 int NVStore::get(uint16_t key, uint16_t buf_size, void *buf, uint16_t &actual_size) 00617 { 00618 return do_get(key, buf_size, buf, actual_size, 0); 00619 } 00620 00621 int NVStore::get_item_size(uint16_t key, uint16_t &actual_size) 00622 { 00623 return do_get(key, 0, NULL, actual_size, 1); 00624 } 00625 00626 int NVStore::do_set(uint16_t key, uint16_t buf_size, const void *buf, uint16_t flags) 00627 { 00628 int ret = NVSTORE_SUCCESS; 00629 uint32_t record_offset, record_size, new_free_space; 00630 uint32_t next_offset; 00631 00632 if (!_init_done) { 00633 ret = init(); 00634 if (ret != NVSTORE_SUCCESS) { 00635 return ret; 00636 } 00637 } 00638 00639 if (key >= _max_keys) { 00640 return NVSTORE_BAD_VALUE; 00641 } 00642 00643 if (!buf) { 00644 buf_size = 0; 00645 } 00646 00647 if ((flags & delete_item_flag) && !_offset_by_key[key]) { 00648 return NVSTORE_NOT_FOUND; 00649 } 00650 00651 if (_offset_by_key[key] & offs_by_key_set_once_mask) { 00652 return NVSTORE_ALREADY_EXISTS; 00653 } 00654 00655 record_size = align_up(sizeof(nvstore_record_header_t) + buf_size, _min_prog_size); 00656 00657 _mutex->lock(); 00658 00659 new_free_space = core_util_atomic_incr_u32(&_free_space_offset, record_size); 00660 record_offset = new_free_space - record_size; 00661 00662 // If we cross the area limit, we need to invoke GC. 00663 if (new_free_space >= _size) { 00664 ret = garbage_collection(key, flags, buf_size, buf); 00665 _mutex->unlock(); 00666 return ret; 00667 } 00668 00669 // Now write the record 00670 ret = write_record(_active_area, record_offset, key, flags, buf_size, buf, next_offset); 00671 if (ret != NVSTORE_SUCCESS) { 00672 _mutex->unlock(); 00673 return ret; 00674 } 00675 00676 // Update _offset_by_key. High bit indicates area. 00677 if (flags & delete_item_flag) { 00678 _offset_by_key[key] = 0; 00679 } else { 00680 _offset_by_key[key] = record_offset | (_active_area << offs_by_key_area_bit_pos) | 00681 (((flags & set_once_flag) != 0) << offs_by_key_set_once_bit_pos); 00682 } 00683 00684 _mutex->unlock(); 00685 00686 return NVSTORE_SUCCESS; 00687 } 00688 00689 int NVStore::set(uint16_t key, uint16_t buf_size, const void *buf) 00690 { 00691 return do_set(key, buf_size, buf, 0); 00692 } 00693 00694 int NVStore::set_once(uint16_t key, uint16_t buf_size, const void *buf) 00695 { 00696 return do_set(key, buf_size, buf, set_once_flag); 00697 } 00698 00699 int NVStore::remove(uint16_t key) 00700 { 00701 return do_set(key, 0, NULL, delete_item_flag); 00702 } 00703 00704 int NVStore::init() 00705 { 00706 area_state_e area_state[NVSTORE_NUM_AREAS]; 00707 uint32_t free_space_offset_of_area[NVSTORE_NUM_AREAS]; 00708 uint32_t init_attempts_val; 00709 uint32_t next_offset; 00710 int os_ret; 00711 int ret = NVSTORE_SUCCESS; 00712 int valid; 00713 uint16_t key; 00714 uint16_t flags; 00715 uint16_t versions[NVSTORE_NUM_AREAS]; 00716 uint16_t actual_size; 00717 00718 if (_init_done) { 00719 return NVSTORE_SUCCESS; 00720 } 00721 00722 // This handles the case that init function is called by more than one thread concurrently. 00723 // Only the one who gets the value of 1 in _init_attempts_val will proceed, while others will 00724 // wait until init is finished. 00725 init_attempts_val = core_util_atomic_incr_u32(&_init_attempts, 1); 00726 if (init_attempts_val != 1) { 00727 while (!_init_done) { 00728 wait_ms(1); 00729 } 00730 return NVSTORE_SUCCESS; 00731 } 00732 00733 _offset_by_key = new uint32_t[_max_keys]; 00734 MBED_ASSERT(_offset_by_key); 00735 00736 for (key = 0; key < _max_keys; key++) { 00737 _offset_by_key[key] = 0; 00738 } 00739 00740 _mutex = new PlatformMutex; 00741 MBED_ASSERT(_mutex); 00742 00743 _size = (uint32_t) -1; 00744 _flash = new mbed::FlashIAP; 00745 MBED_ASSERT(_flash); 00746 _flash->init(); 00747 00748 _min_prog_size = std::max(_flash->get_page_size(), (uint32_t)sizeof(nvstore_record_header_t)); 00749 if (_min_prog_size > sizeof(nvstore_record_header_t)) { 00750 _page_buf = new uint8_t[_min_prog_size]; 00751 MBED_ASSERT(_page_buf); 00752 } 00753 00754 calc_validate_area_params(); 00755 00756 for (uint8_t area = 0; area < NVSTORE_NUM_AREAS; area++) { 00757 area_state[area] = NVSTORE_AREA_STATE_NONE; 00758 free_space_offset_of_area[area] = 0; 00759 versions[area] = 0; 00760 00761 _size = std::min(_size, _flash_area_params[area].size); 00762 00763 // Find start of empty space at the end of the area. This serves for both 00764 // knowing whether the area is empty and for the record traversal at the end. 00765 os_ret = calc_empty_space(area, free_space_offset_of_area[area]); 00766 MBED_ASSERT(!os_ret); 00767 00768 if (!free_space_offset_of_area[area]) { 00769 area_state[area] = NVSTORE_AREA_STATE_EMPTY; 00770 continue; 00771 } 00772 00773 // Check validity of master record 00774 master_record_data_t master_rec; 00775 ret = read_record(area, 0, sizeof(master_rec), &master_rec, 00776 actual_size, 0, valid, 00777 key, flags, next_offset); 00778 MBED_ASSERT((ret == NVSTORE_SUCCESS) || (ret == NVSTORE_BUFF_TOO_SMALL)); 00779 if (ret == NVSTORE_BUFF_TOO_SMALL) { 00780 // Buf too small error means that we have a corrupt master record - 00781 // treat it as such 00782 valid = 0; 00783 } 00784 00785 // We have a non valid master record, in a non-empty area. Just erase the area. 00786 if ((!valid) || (key != master_record_key)) { 00787 os_ret = flash_erase_area(area); 00788 MBED_ASSERT(!os_ret); 00789 area_state[area] = NVSTORE_AREA_STATE_EMPTY; 00790 continue; 00791 } 00792 versions[area] = master_rec.version; 00793 00794 // Place _free_space_offset after the master record (for the traversal, 00795 // which takes place after this loop). 00796 _free_space_offset = next_offset; 00797 area_state[area] = NVSTORE_AREA_STATE_VALID; 00798 00799 // Unless both areas are valid (a case handled later), getting here means 00800 // that we found our active area. 00801 _active_area = area; 00802 _active_area_version = versions[area]; 00803 } 00804 00805 // In case we have two empty areas, arbitrarily assign 0 to the active one. 00806 if ((area_state[0] == NVSTORE_AREA_STATE_EMPTY) && (area_state[1] == NVSTORE_AREA_STATE_EMPTY)) { 00807 _active_area = 0; 00808 ret = write_master_record(_active_area, 1, _free_space_offset); 00809 MBED_ASSERT(ret == NVSTORE_SUCCESS); 00810 _init_done = 1; 00811 return NVSTORE_SUCCESS; 00812 } 00813 00814 // In case we have two valid areas, choose the one having the higher version (or 0 00815 // in case of wrap around). Erase the other one. 00816 if ((area_state[0] == NVSTORE_AREA_STATE_VALID) && (area_state[1] == NVSTORE_AREA_STATE_VALID)) { 00817 if ((versions[0] > versions[1]) || (!versions[0])) { 00818 _active_area = 0; 00819 } else { 00820 _active_area = 1; 00821 } 00822 _active_area_version = versions[_active_area]; 00823 os_ret = flash_erase_area(1 - _active_area); 00824 MBED_ASSERT(!os_ret); 00825 } 00826 00827 // Traverse area until reaching the empty space at the end or until reaching a faulty record 00828 while (_free_space_offset < free_space_offset_of_area[_active_area]) { 00829 ret = read_record(_active_area, _free_space_offset, 0, NULL, 00830 actual_size, 1, valid, 00831 key, flags, next_offset); 00832 MBED_ASSERT(ret == NVSTORE_SUCCESS); 00833 00834 // In case we have a faulty record, this probably means that the system crashed when written. 00835 // Perform a garbage collection, to make the the other area valid. 00836 if (!valid) { 00837 ret = garbage_collection(no_key, 0, 0, NULL); 00838 break; 00839 } 00840 if (flags & delete_item_flag) { 00841 _offset_by_key[key] = 0; 00842 } else { 00843 _offset_by_key[key] = _free_space_offset | (_active_area << offs_by_key_area_bit_pos) | 00844 (((flags & set_once_flag) != 0) << offs_by_key_set_once_bit_pos); 00845 } 00846 _free_space_offset = next_offset; 00847 } 00848 00849 _init_done = 1; 00850 return NVSTORE_SUCCESS; 00851 } 00852 00853 int NVStore::deinit() 00854 { 00855 if (_init_done) { 00856 _flash->deinit(); 00857 delete _flash; 00858 delete _mutex; 00859 delete[] _offset_by_key; 00860 if (_page_buf) { 00861 delete[] _page_buf; 00862 _page_buf = 0; 00863 } 00864 } 00865 00866 _init_attempts = 0; 00867 _init_done = 0; 00868 00869 return NVSTORE_SUCCESS; 00870 } 00871 00872 int NVStore::reset() 00873 { 00874 uint8_t area; 00875 int os_ret; 00876 00877 if (!_init_done) { 00878 init(); 00879 } 00880 00881 // Erase both areas, and reinitialize the module. This is totally not thread safe, 00882 // as init doesn't take the case of re-initialization into account. It's OK, as this function 00883 // should only be called in pre-production cases. 00884 for (area = 0; area < NVSTORE_NUM_AREAS; area++) { 00885 os_ret = flash_erase_area(area); 00886 if (os_ret) { 00887 return NVSTORE_WRITE_ERROR; 00888 } 00889 } 00890 00891 deinit(); 00892 return init(); 00893 } 00894 00895 int NVStore::get_area_params(uint8_t area, uint32_t &address, size_t &size) 00896 { 00897 if (area >= NVSTORE_NUM_AREAS) { 00898 return NVSTORE_BAD_VALUE; 00899 } 00900 00901 if (!_init_done) { 00902 init(); 00903 } 00904 00905 address = _flash_area_params[area].address; 00906 size = _flash_area_params[area].size; 00907 00908 return NVSTORE_SUCCESS; 00909 } 00910 00911 size_t NVStore::size() 00912 { 00913 if (!_init_done) { 00914 init(); 00915 } 00916 00917 return _size; 00918 } 00919 00920 #endif // NVSTORE_ENABLED
Generated on Tue Jul 12 2022 13:31:10 by
