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