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 memcpy(_flash_area_params, 0, sizeof(_flash_area_params)); 00138 } 00139 00140 NVStore::~NVStore() 00141 { 00142 if (_init_done) { 00143 deinit(); 00144 } 00145 } 00146 00147 uint16_t NVStore::get_max_keys() const 00148 { 00149 return _max_keys; 00150 } 00151 00152 uint16_t NVStore::get_max_possible_keys() 00153 { 00154 if (!_init_done) { 00155 init(); 00156 } 00157 00158 size_t max_possible_keys = _size / align_up(sizeof(nvstore_record_header_t) * 2, _min_prog_size) - 1; 00159 00160 return (uint16_t)std::min(max_possible_keys, (size_t) last_reserved_key); 00161 } 00162 00163 void NVStore::set_max_keys(uint16_t num_keys) 00164 { 00165 MBED_ASSERT(num_keys < get_max_possible_keys()); 00166 _max_keys = num_keys; 00167 // User is allowed to change number of keys. As this affects init, need to deinitialize now. 00168 // Don't call init right away - it is lazily called by get/set functions if needed. 00169 deinit(); 00170 } 00171 00172 int NVStore::flash_read_area(uint8_t area, uint32_t offset, uint32_t size, void *buf) 00173 { 00174 return _flash->read(buf, _flash_area_params[area].address + offset, size); 00175 } 00176 00177 int NVStore::flash_write_area(uint8_t area, uint32_t offset, uint32_t size, const void *buf) 00178 { 00179 int ret; 00180 // On some boards, write action can fail due to HW limitations (like critical drivers 00181 // that disable all other actions). Just retry a few times until success. 00182 for (int i = 0; i < num_write_retries; i++) { 00183 ret = _flash->program(buf, _flash_area_params[area].address + offset, size); 00184 if (!ret) { 00185 return ret; 00186 } 00187 wait_ms(1); 00188 } 00189 return ret; 00190 } 00191 00192 int NVStore::flash_erase_area(uint8_t area) 00193 { 00194 int ret; 00195 // On some boards, write action can fail due to HW limitations (like critical drivers 00196 // that disable all other actions). Just retry a few times until success. 00197 for (int i = 0; i < num_write_retries; i++) { 00198 ret = _flash->erase(_flash_area_params[area].address, _flash_area_params[area].size); 00199 if (!ret) { 00200 return ret; 00201 } 00202 wait_ms(1); 00203 } 00204 return ret; 00205 } 00206 00207 void NVStore::calc_validate_area_params() 00208 { 00209 int num_sectors = 0; 00210 00211 size_t flash_addr = _flash->get_flash_start(); 00212 size_t flash_size = _flash->get_flash_size(); 00213 size_t sector_size; 00214 int max_sectors = flash_size / _flash->get_sector_size(flash_addr) + 1; 00215 size_t *sector_map = new size_t[max_sectors]; 00216 00217 int area = 0; 00218 size_t left_size = flash_size; 00219 00220 memcpy(_flash_area_params, initial_area_params, sizeof(_flash_area_params)); 00221 int user_config = (_flash_area_params[0].size != 0); 00222 int in_area = 0; 00223 size_t area_size = 0; 00224 00225 while (left_size) { 00226 sector_size = _flash->get_sector_size(flash_addr); 00227 sector_map[num_sectors++] = flash_addr; 00228 00229 if (user_config) { 00230 // User configuration - here we validate it 00231 // Check that address is on a sector boundary, that size covers complete sector sizes, 00232 // and that areas don't overlap. 00233 if (_flash_area_params[area].address == flash_addr) { 00234 in_area = 1; 00235 } 00236 if (in_area) { 00237 area_size += sector_size; 00238 if (area_size == _flash_area_params[area].size) { 00239 area++; 00240 if (area == NVSTORE_NUM_AREAS) { 00241 break; 00242 } 00243 in_area = 0; 00244 area_size = 0; 00245 } 00246 } 00247 } 00248 00249 flash_addr += sector_size; 00250 left_size -= sector_size; 00251 } 00252 sector_map[num_sectors] = flash_addr; 00253 00254 if (user_config) { 00255 // Valid areas were counted. Assert if not the expected number. 00256 MBED_ASSERT(area == NVSTORE_NUM_AREAS); 00257 } else { 00258 // Not user configuration - calculate area parameters. 00259 // Take last two sectors by default. If their sizes aren't big enough, take 00260 // a few consecutive ones. 00261 area = 1; 00262 _flash_area_params[area].size = 0; 00263 int i; 00264 for (i = num_sectors - 1; i >= 0; i--) { 00265 sector_size = sector_map[i+1] - sector_map[i]; 00266 _flash_area_params[area].size += sector_size; 00267 if (_flash_area_params[area].size >= min_area_size) { 00268 _flash_area_params[area].address = sector_map[i]; 00269 area--; 00270 if (area < 0) { 00271 break; 00272 } 00273 _flash_area_params[area].size = 0; 00274 } 00275 } 00276 } 00277 00278 delete[] sector_map; 00279 } 00280 00281 00282 int NVStore::calc_empty_space(uint8_t area, uint32_t &offset) 00283 { 00284 uint32_t buf[32]; 00285 uint8_t *chbuf; 00286 uint32_t i, j; 00287 int ret; 00288 00289 offset = _size; 00290 for (i = 0; i < _size / sizeof(buf); i++) { 00291 offset -= sizeof(buf); 00292 ret = flash_read_area(area, offset, sizeof(buf), buf); 00293 if (ret) { 00294 return ret; 00295 } 00296 chbuf = (uint8_t *) buf; 00297 for (j = sizeof(buf); j > 0; j--) { 00298 if (chbuf[j - 1] != blank_flash_val) { 00299 offset += j; 00300 return 0; 00301 } 00302 } 00303 } 00304 return 0; 00305 } 00306 00307 int NVStore::read_record(uint8_t area, uint32_t offset, uint16_t buf_size, void *buf, 00308 uint16_t &actual_size, int validate_only, int &valid, 00309 uint16_t &key, uint16_t &flags, uint32_t &next_offset) 00310 { 00311 uint8_t int_buf[128]; 00312 void *buf_ptr; 00313 uint16_t data_size, chunk_size; 00314 int os_ret; 00315 nvstore_record_header_t header; 00316 uint32_t crc = initial_crc; 00317 00318 valid = 1; 00319 00320 os_ret = flash_read_area(area, offset, sizeof(header), &header); 00321 if (os_ret) { 00322 return NVSTORE_READ_ERROR; 00323 } 00324 00325 crc = crc32(crc, sizeof(header) - sizeof(header.crc), (uint8_t *) &header); 00326 00327 actual_size = 0; 00328 key = header.key_and_flags & ~header_flag_mask; 00329 flags = header.key_and_flags & header_flag_mask; 00330 00331 if ((key >= _max_keys) && (key != master_record_key)) { 00332 valid = 0; 00333 return NVSTORE_SUCCESS; 00334 } 00335 00336 data_size = header.size; 00337 offset += sizeof(header); 00338 00339 // In case of validate only enabled, we use our internal buffer for data reading, 00340 // instead of the user one. This allows us to use a smaller buffer, on which CRC 00341 // is continuously calculated. 00342 if (validate_only) { 00343 buf_ptr = int_buf; 00344 buf_size = sizeof(int_buf); 00345 } else { 00346 if (data_size > buf_size) { 00347 offset += data_size; 00348 actual_size = data_size; 00349 next_offset = align_up(offset, _min_prog_size); 00350 return NVSTORE_BUFF_TOO_SMALL; 00351 } 00352 buf_ptr = buf; 00353 } 00354 00355 while (data_size) { 00356 chunk_size = std::min(data_size, buf_size); 00357 os_ret = flash_read_area(area, offset, chunk_size, buf_ptr); 00358 if (os_ret) { 00359 return NVSTORE_READ_ERROR; 00360 } 00361 crc = crc32(crc, chunk_size, (uint8_t *) buf_ptr); 00362 data_size -= chunk_size; 00363 offset += chunk_size; 00364 } 00365 00366 if (header.crc != crc) { 00367 valid = 0; 00368 return NVSTORE_SUCCESS; 00369 } 00370 00371 actual_size = header.size; 00372 next_offset = align_up(offset, _min_prog_size); 00373 00374 return NVSTORE_SUCCESS; 00375 } 00376 00377 int NVStore::write_record(uint8_t area, uint32_t offset, uint16_t key, uint16_t flags, 00378 uint32_t data_size, const void *data_buf, uint32_t &next_offset) 00379 { 00380 nvstore_record_header_t header; 00381 uint32_t crc = initial_crc; 00382 int os_ret; 00383 uint8_t *prog_buf; 00384 00385 header.key_and_flags = key | flags; 00386 header.size = data_size; 00387 header.crc = 0; // Satisfy compiler 00388 crc = crc32(crc, sizeof(header) - sizeof(header.crc), (uint8_t *) &header); 00389 if (data_size) { 00390 crc = crc32(crc, data_size, (uint8_t *) data_buf); 00391 } 00392 header.crc = crc; 00393 00394 // In case page size is greater than header size, we can't write header and data 00395 // separately. Instead, we need to copy header and start of data to our page buffer 00396 // and write them together. Otherwise, simply write header and data separately. 00397 uint32_t prog_size = sizeof(header); 00398 uint32_t copy_size = 0; 00399 if (_min_prog_size > sizeof(header)) { 00400 prog_buf = _page_buf; 00401 memcpy(prog_buf, &header, sizeof(header)); 00402 if (data_size) { 00403 memcpy(prog_buf, &header, sizeof(header)); 00404 copy_size = std::min(data_size, _min_prog_size - sizeof(header)); 00405 memcpy(prog_buf + sizeof(header), data_buf, copy_size); 00406 data_size -= copy_size; 00407 prog_size += copy_size; 00408 } 00409 } else { 00410 prog_buf = (uint8_t *) &header; 00411 } 00412 00413 os_ret = flash_write_area(area, offset, prog_size, prog_buf); 00414 if (os_ret) { 00415 return NVSTORE_WRITE_ERROR; 00416 } 00417 offset += prog_size; 00418 00419 if (data_size) { 00420 prog_buf = (uint8_t *) data_buf + copy_size; 00421 os_ret = flash_write_area(area, offset, data_size, prog_buf); 00422 if (os_ret) { 00423 return NVSTORE_WRITE_ERROR; 00424 } 00425 offset += data_size; 00426 } 00427 00428 next_offset = align_up(offset, _min_prog_size); 00429 return NVSTORE_SUCCESS; 00430 } 00431 00432 int NVStore::write_master_record(uint8_t area, uint16_t version, uint32_t &next_offset) 00433 { 00434 master_record_data_t master_rec; 00435 00436 master_rec.version = version; 00437 master_rec.reserved1 = 0; 00438 master_rec.reserved2 = 0; 00439 return write_record(area, 0, master_record_key, 0, sizeof(master_rec), 00440 &master_rec, next_offset); 00441 } 00442 00443 int NVStore::copy_record(uint8_t from_area, uint32_t from_offset, uint32_t to_offset, 00444 uint32_t &next_offset) 00445 { 00446 uint8_t local_buf[128]; 00447 uint16_t record_size, chunk_size, prog_buf_size; 00448 int os_ret; 00449 nvstore_record_header_t *header; 00450 uint8_t *read_buf, *prog_buf; 00451 00452 // This function assumes that the source record is valid, so no need to recalculate CRC. 00453 00454 if (_min_prog_size > sizeof(nvstore_record_header_t)) { 00455 prog_buf = _page_buf; 00456 prog_buf_size = _min_prog_size; 00457 } else { 00458 prog_buf = local_buf; 00459 prog_buf_size = sizeof(local_buf); 00460 } 00461 read_buf = prog_buf; 00462 00463 os_ret = flash_read_area(from_area, from_offset, sizeof(nvstore_record_header_t), read_buf); 00464 if (os_ret) { 00465 return NVSTORE_READ_ERROR; 00466 } 00467 00468 header = (nvstore_record_header_t *) read_buf; 00469 record_size = sizeof(nvstore_record_header_t) + header->size; 00470 00471 // No need to copy records whose flags indicate deletion 00472 if (header->key_and_flags & delete_item_flag) { 00473 next_offset = align_up(to_offset, _min_prog_size); 00474 return NVSTORE_SUCCESS; 00475 } 00476 00477 // no need to align record size here, as it won't change the outcome of this condition 00478 if (to_offset + record_size >= _size) { 00479 return NVSTORE_FLASH_AREA_TOO_SMALL; 00480 } 00481 00482 uint16_t start_size = sizeof(nvstore_record_header_t); 00483 from_offset += start_size; 00484 read_buf += start_size; 00485 record_size -= start_size; 00486 00487 do { 00488 chunk_size = std::min(record_size, (uint16_t)(prog_buf_size - start_size)); 00489 if (chunk_size) { 00490 os_ret = flash_read_area(from_area, from_offset, chunk_size, read_buf); 00491 if (os_ret) { 00492 return NVSTORE_READ_ERROR; 00493 } 00494 } 00495 os_ret = flash_write_area(1 - from_area, to_offset, chunk_size + start_size, prog_buf); 00496 if (os_ret) { 00497 return NVSTORE_WRITE_ERROR; 00498 } 00499 00500 read_buf = prog_buf; 00501 record_size -= chunk_size; 00502 from_offset += chunk_size; 00503 to_offset += chunk_size + start_size; 00504 start_size = 0; 00505 } while (record_size); 00506 00507 next_offset = align_up(to_offset, _min_prog_size); 00508 return NVSTORE_SUCCESS; 00509 } 00510 00511 int NVStore::garbage_collection(uint16_t key, uint16_t flags, uint16_t buf_size, const void *buf) 00512 { 00513 uint32_t curr_offset, new_area_offset, next_offset; 00514 int ret; 00515 uint8_t curr_area; 00516 00517 new_area_offset = align_up(sizeof(nvstore_record_header_t) + sizeof(master_record_data_t), _min_prog_size); 00518 00519 // If GC is triggered by a set item request, we need to first write that item in the new location, 00520 // otherwise we may either write it twice (if already included), or lose it in case we decide 00521 // to skip it at garbage collection phase (and the system crashes). 00522 if ((key != no_key) && !(flags & delete_item_flag)) { 00523 ret = write_record(1 - _active_area, new_area_offset, key, 0, buf_size, buf, next_offset); 00524 if (ret != NVSTORE_SUCCESS) { 00525 return ret; 00526 } 00527 _offset_by_key[key] = new_area_offset | (1 - _active_area) << offs_by_key_area_bit_pos | 00528 (((flags & set_once_flag) != 0) << offs_by_key_set_once_bit_pos); 00529 new_area_offset = next_offset; 00530 } 00531 00532 // Now iterate on all types, and copy the ones who have valid offsets (meaning that they exist) 00533 // to the other area. 00534 for (key = 0; key < _max_keys; key++) { 00535 curr_offset = _offset_by_key[key]; 00536 uint16_t save_flags = curr_offset & offs_by_key_area_mask; 00537 curr_area = (uint8_t)(curr_offset >> offs_by_key_area_bit_pos) & 1; 00538 curr_offset &= ~offs_by_key_flag_mask; 00539 if ((!curr_offset) || (curr_area != _active_area)) { 00540 continue; 00541 } 00542 ret = copy_record(curr_area, curr_offset, new_area_offset, next_offset); 00543 if (ret != NVSTORE_SUCCESS) { 00544 return ret; 00545 } 00546 _offset_by_key[key] = new_area_offset | (1 - curr_area) << offs_by_key_area_bit_pos | save_flags; 00547 new_area_offset = next_offset; 00548 } 00549 00550 // Now write master record, with version incremented by 1. 00551 _active_area_version++; 00552 ret = write_master_record(1 - _active_area, _active_area_version, next_offset); 00553 if (ret != NVSTORE_SUCCESS) { 00554 return ret; 00555 } 00556 00557 _free_space_offset = new_area_offset; 00558 00559 // Only now we can switch to the new active area 00560 _active_area = 1 - _active_area; 00561 00562 // The older area doesn't concern us now. Erase it now. 00563 if (flash_erase_area(1 - _active_area)) { 00564 return NVSTORE_WRITE_ERROR; 00565 } 00566 00567 return ret; 00568 } 00569 00570 00571 int NVStore::do_get(uint16_t key, uint16_t buf_size, void *buf, uint16_t &actual_size, 00572 int validate_only) 00573 { 00574 int ret = NVSTORE_SUCCESS; 00575 int valid; 00576 uint32_t record_offset, next_offset; 00577 uint16_t read_type, flags; 00578 uint8_t area; 00579 00580 if (!_init_done) { 00581 ret = init(); 00582 if (ret != NVSTORE_SUCCESS) { 00583 return ret; 00584 } 00585 } 00586 00587 if (key >= _max_keys) { 00588 return NVSTORE_BAD_VALUE; 00589 } 00590 00591 if (!buf) { 00592 buf_size = 0; 00593 // This is only required in order to satisfy static code analysis tools, fearing 00594 // that a null buff is dereferenced inside read_record function. However, this won't happen 00595 // when buf_size is 0, so just have buf point to a dummy location. 00596 buf = &flags; 00597 } 00598 00599 _mutex->lock(); 00600 record_offset = _offset_by_key[key]; 00601 00602 if (!record_offset) { 00603 _mutex->unlock(); 00604 return NVSTORE_NOT_FOUND; 00605 } 00606 00607 area = (uint8_t)(record_offset >> offs_by_key_area_bit_pos) & 1; 00608 record_offset &= ~offs_by_key_flag_mask; 00609 00610 ret = read_record(area, record_offset, buf_size, buf, 00611 actual_size, validate_only, valid, 00612 read_type, flags, next_offset); 00613 if ((ret == NVSTORE_SUCCESS) && !valid) { 00614 ret = NVSTORE_DATA_CORRUPT; 00615 } 00616 00617 _mutex->unlock(); 00618 return ret; 00619 } 00620 00621 int NVStore::get(uint16_t key, uint16_t buf_size, void *buf, uint16_t &actual_size) 00622 { 00623 return do_get(key, buf_size, buf, actual_size, 0); 00624 } 00625 00626 int NVStore::get_item_size(uint16_t key, uint16_t &actual_size) 00627 { 00628 return do_get(key, 0, NULL, actual_size, 1); 00629 } 00630 00631 int NVStore::do_set(uint16_t &key, uint16_t buf_size, const void *buf, uint16_t flags) 00632 { 00633 int ret = NVSTORE_SUCCESS; 00634 uint32_t record_offset, record_size, new_free_space; 00635 uint32_t next_offset; 00636 00637 if (!_init_done) { 00638 ret = init(); 00639 if (ret != NVSTORE_SUCCESS) { 00640 return ret; 00641 } 00642 } 00643 00644 if ((key != no_key) && (key >= _max_keys)) { 00645 return NVSTORE_BAD_VALUE; 00646 } 00647 00648 if ((key == no_key) && (flags & delete_item_flag)) { 00649 return NVSTORE_BAD_VALUE; 00650 } 00651 00652 if (!buf) { 00653 buf_size = 0; 00654 } 00655 00656 if ((flags & delete_item_flag) && !_offset_by_key[key]) { 00657 return NVSTORE_NOT_FOUND; 00658 } 00659 00660 if ((key != no_key) && (_offset_by_key[key] & offs_by_key_set_once_mask)) { 00661 return NVSTORE_ALREADY_EXISTS; 00662 } 00663 00664 record_size = align_up(sizeof(nvstore_record_header_t) + buf_size, _min_prog_size); 00665 00666 _mutex->lock(); 00667 00668 if (key == no_key) { 00669 for (key = NVSTORE_NUM_PREDEFINED_KEYS; key < _max_keys; key++) { 00670 if (!_offset_by_key[key]) { 00671 break; 00672 } 00673 } 00674 if (key == _max_keys) { 00675 return NVSTORE_NO_FREE_KEY; 00676 } 00677 } 00678 00679 new_free_space = core_util_atomic_incr_u32(&_free_space_offset, record_size); 00680 record_offset = new_free_space - record_size; 00681 00682 // If we cross the area limit, we need to invoke GC. 00683 if (new_free_space >= _size) { 00684 ret = garbage_collection(key, flags, buf_size, buf); 00685 _mutex->unlock(); 00686 return ret; 00687 } 00688 00689 // Now write the record 00690 ret = write_record(_active_area, record_offset, key, flags, buf_size, buf, next_offset); 00691 if (ret != NVSTORE_SUCCESS) { 00692 _mutex->unlock(); 00693 return ret; 00694 } 00695 00696 // Update _offset_by_key. High bit indicates area. 00697 if (flags & delete_item_flag) { 00698 _offset_by_key[key] = 0; 00699 } else { 00700 _offset_by_key[key] = record_offset | (_active_area << offs_by_key_area_bit_pos) | 00701 (((flags & set_once_flag) != 0) << offs_by_key_set_once_bit_pos); 00702 } 00703 00704 _mutex->unlock(); 00705 00706 return NVSTORE_SUCCESS; 00707 } 00708 00709 int NVStore::set(uint16_t key, uint16_t buf_size, const void *buf) 00710 { 00711 return do_set(key, buf_size, buf, 0); 00712 } 00713 00714 int NVStore::set_once(uint16_t key, uint16_t buf_size, const void *buf) 00715 { 00716 return do_set(key, buf_size, buf, set_once_flag); 00717 } 00718 00719 int NVStore::set_alloc_key(uint16_t &key, uint16_t buf_size, const void *buf) 00720 { 00721 key = no_key; 00722 return do_set(key, buf_size, buf, 0); 00723 } 00724 00725 int NVStore::remove(uint16_t key) 00726 { 00727 return do_set(key, 0, NULL, delete_item_flag); 00728 } 00729 00730 int NVStore::init() 00731 { 00732 area_state_e area_state[NVSTORE_NUM_AREAS]; 00733 uint32_t free_space_offset_of_area[NVSTORE_NUM_AREAS]; 00734 uint32_t init_attempts_val; 00735 uint32_t next_offset; 00736 int os_ret; 00737 int ret = NVSTORE_SUCCESS; 00738 int valid; 00739 uint16_t key; 00740 uint16_t flags; 00741 uint16_t versions[NVSTORE_NUM_AREAS]; 00742 uint16_t actual_size; 00743 00744 if (_init_done) { 00745 return NVSTORE_SUCCESS; 00746 } 00747 00748 // This handles the case that init function is called by more than one thread concurrently. 00749 // Only the one who gets the value of 1 in _init_attempts_val will proceed, while others will 00750 // wait until init is finished. 00751 init_attempts_val = core_util_atomic_incr_u32(&_init_attempts, 1); 00752 if (init_attempts_val != 1) { 00753 while (!_init_done) { 00754 wait_ms(1); 00755 } 00756 return NVSTORE_SUCCESS; 00757 } 00758 00759 _offset_by_key = new uint32_t[_max_keys]; 00760 MBED_ASSERT(_offset_by_key); 00761 00762 for (key = 0; key < _max_keys; key++) { 00763 _offset_by_key[key] = 0; 00764 } 00765 00766 _mutex = new PlatformMutex; 00767 MBED_ASSERT(_mutex); 00768 00769 _size = (uint32_t) -1; 00770 _flash = new mbed::FlashIAP; 00771 MBED_ASSERT(_flash); 00772 _flash->init(); 00773 00774 _min_prog_size = std::max(_flash->get_page_size(), (uint32_t)sizeof(nvstore_record_header_t)); 00775 if (_min_prog_size > sizeof(nvstore_record_header_t)) { 00776 _page_buf = new uint8_t[_min_prog_size]; 00777 MBED_ASSERT(_page_buf); 00778 } 00779 00780 calc_validate_area_params(); 00781 00782 for (uint8_t area = 0; area < NVSTORE_NUM_AREAS; area++) { 00783 area_state[area] = NVSTORE_AREA_STATE_NONE; 00784 free_space_offset_of_area[area] = 0; 00785 versions[area] = 0; 00786 00787 _size = std::min(_size, _flash_area_params[area].size); 00788 00789 // Find start of empty space at the end of the area. This serves for both 00790 // knowing whether the area is empty and for the record traversal at the end. 00791 os_ret = calc_empty_space(area, free_space_offset_of_area[area]); 00792 MBED_ASSERT(!os_ret); 00793 00794 if (!free_space_offset_of_area[area]) { 00795 area_state[area] = NVSTORE_AREA_STATE_EMPTY; 00796 continue; 00797 } 00798 00799 // Check validity of master record 00800 master_record_data_t master_rec; 00801 ret = read_record(area, 0, sizeof(master_rec), &master_rec, 00802 actual_size, 0, valid, 00803 key, flags, next_offset); 00804 MBED_ASSERT((ret == NVSTORE_SUCCESS) || (ret == NVSTORE_BUFF_TOO_SMALL)); 00805 if (ret == NVSTORE_BUFF_TOO_SMALL) { 00806 // Buf too small error means that we have a corrupt master record - 00807 // treat it as such 00808 valid = 0; 00809 } 00810 00811 // We have a non valid master record, in a non-empty area. Just erase the area. 00812 if ((!valid) || (key != master_record_key)) { 00813 os_ret = flash_erase_area(area); 00814 MBED_ASSERT(!os_ret); 00815 area_state[area] = NVSTORE_AREA_STATE_EMPTY; 00816 continue; 00817 } 00818 versions[area] = master_rec.version; 00819 00820 // Place _free_space_offset after the master record (for the traversal, 00821 // which takes place after this loop). 00822 _free_space_offset = next_offset; 00823 area_state[area] = NVSTORE_AREA_STATE_VALID; 00824 00825 // Unless both areas are valid (a case handled later), getting here means 00826 // that we found our active area. 00827 _active_area = area; 00828 _active_area_version = versions[area]; 00829 } 00830 00831 // In case we have two empty areas, arbitrarily assign 0 to the active one. 00832 if ((area_state[0] == NVSTORE_AREA_STATE_EMPTY) && (area_state[1] == NVSTORE_AREA_STATE_EMPTY)) { 00833 _active_area = 0; 00834 ret = write_master_record(_active_area, 1, _free_space_offset); 00835 MBED_ASSERT(ret == NVSTORE_SUCCESS); 00836 _init_done = 1; 00837 return NVSTORE_SUCCESS; 00838 } 00839 00840 // In case we have two valid areas, choose the one having the higher version (or 0 00841 // in case of wrap around). Erase the other one. 00842 if ((area_state[0] == NVSTORE_AREA_STATE_VALID) && (area_state[1] == NVSTORE_AREA_STATE_VALID)) { 00843 if ((versions[0] > versions[1]) || (!versions[0])) { 00844 _active_area = 0; 00845 } else { 00846 _active_area = 1; 00847 } 00848 _active_area_version = versions[_active_area]; 00849 os_ret = flash_erase_area(1 - _active_area); 00850 MBED_ASSERT(!os_ret); 00851 } 00852 00853 // Traverse area until reaching the empty space at the end or until reaching a faulty record 00854 while (_free_space_offset < free_space_offset_of_area[_active_area]) { 00855 ret = read_record(_active_area, _free_space_offset, 0, NULL, 00856 actual_size, 1, valid, 00857 key, flags, next_offset); 00858 MBED_ASSERT(ret == NVSTORE_SUCCESS); 00859 00860 // In case we have a faulty record, this probably means that the system crashed when written. 00861 // Perform a garbage collection, to make the the other area valid. 00862 if (!valid) { 00863 ret = garbage_collection(no_key, 0, 0, NULL); 00864 break; 00865 } 00866 if (flags & delete_item_flag) { 00867 _offset_by_key[key] = 0; 00868 } else { 00869 _offset_by_key[key] = _free_space_offset | (_active_area << offs_by_key_area_bit_pos) | 00870 (((flags & set_once_flag) != 0) << offs_by_key_set_once_bit_pos); 00871 } 00872 _free_space_offset = next_offset; 00873 } 00874 00875 _init_done = 1; 00876 return NVSTORE_SUCCESS; 00877 } 00878 00879 int NVStore::deinit() 00880 { 00881 if (_init_done) { 00882 _flash->deinit(); 00883 delete _flash; 00884 delete _mutex; 00885 delete[] _offset_by_key; 00886 if (_page_buf) { 00887 delete[] _page_buf; 00888 _page_buf = 0; 00889 } 00890 } 00891 00892 _init_attempts = 0; 00893 _init_done = 0; 00894 00895 return NVSTORE_SUCCESS; 00896 } 00897 00898 int NVStore::reset() 00899 { 00900 uint8_t area; 00901 int os_ret; 00902 00903 if (!_init_done) { 00904 init(); 00905 } 00906 00907 // Erase both areas, and reinitialize the module. This is totally not thread safe, 00908 // as init doesn't take the case of re-initialization into account. It's OK, as this function 00909 // should only be called in pre-production cases. 00910 for (area = 0; area < NVSTORE_NUM_AREAS; area++) { 00911 os_ret = flash_erase_area(area); 00912 if (os_ret) { 00913 return NVSTORE_WRITE_ERROR; 00914 } 00915 } 00916 00917 deinit(); 00918 return init(); 00919 } 00920 00921 int NVStore::get_area_params(uint8_t area, uint32_t &address, size_t &size) 00922 { 00923 if (area >= NVSTORE_NUM_AREAS) { 00924 return NVSTORE_BAD_VALUE; 00925 } 00926 00927 if (!_init_done) { 00928 init(); 00929 } 00930 00931 address = _flash_area_params[area].address; 00932 size = _flash_area_params[area].size; 00933 00934 return NVSTORE_SUCCESS; 00935 } 00936 00937 size_t NVStore::size() 00938 { 00939 if (!_init_done) { 00940 init(); 00941 } 00942 00943 return _size; 00944 } 00945 00946 #endif // NVSTORE_ENABLED
Generated on Tue Jul 12 2022 12:22:16 by
