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, curr_owner; 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 (owner << offs_by_key_owner_bit_pos); 00539 new_area_offset = next_offset; 00540 } 00541 00542 // Now iterate on all types, and copy the ones who have valid offsets (meaning that they exist) 00543 // to the other area. 00544 for (key = 0; key < _max_keys; key++) { 00545 curr_offset = _offset_by_key[key]; 00546 uint16_t save_flags = curr_offset & offs_by_key_flag_mask & ~offs_by_key_area_mask; 00547 curr_area = (uint8_t)(curr_offset >> offs_by_key_area_bit_pos) & 1; 00548 curr_owner = _offset_by_key[key] & offs_by_key_owner_mask; 00549 curr_offset &= offs_by_key_offset_mask; 00550 if ((!curr_offset) || (curr_area != _active_area)) { 00551 continue; 00552 } 00553 ret = copy_record(curr_area, curr_offset, new_area_offset, next_offset); 00554 if (ret != NVSTORE_SUCCESS) { 00555 return ret; 00556 } 00557 _offset_by_key[key] = new_area_offset | (1 - curr_area) << offs_by_key_area_bit_pos | save_flags | curr_owner; 00558 new_area_offset = next_offset; 00559 } 00560 00561 // Now write master record, with version incremented by 1. 00562 _active_area_version++; 00563 ret = write_master_record(1 - _active_area, _active_area_version, next_offset); 00564 if (ret != NVSTORE_SUCCESS) { 00565 return ret; 00566 } 00567 00568 _free_space_offset = new_area_offset; 00569 00570 // Only now we can switch to the new active area 00571 _active_area = 1 - _active_area; 00572 00573 // The older area doesn't concern us now. Erase it now. 00574 if (flash_erase_area(1 - _active_area)) { 00575 return NVSTORE_WRITE_ERROR; 00576 } 00577 00578 return ret; 00579 } 00580 00581 00582 int NVStore::do_get(uint16_t key, uint16_t buf_size, void *buf, uint16_t &actual_size, 00583 int validate_only) 00584 { 00585 int ret = NVSTORE_SUCCESS; 00586 int valid; 00587 uint32_t record_offset, next_offset; 00588 uint16_t read_type, flags; 00589 uint8_t area, owner; 00590 00591 if (!_init_done) { 00592 ret = init(); 00593 if (ret != NVSTORE_SUCCESS) { 00594 return ret; 00595 } 00596 } 00597 00598 if (key >= _max_keys) { 00599 return NVSTORE_BAD_VALUE; 00600 } 00601 00602 if (!buf) { 00603 buf_size = 0; 00604 // This is only required in order to satisfy static code analysis tools, fearing 00605 // that a null buff is dereferenced inside read_record function. However, this won't happen 00606 // when buf_size is 0, so just have buf point to a dummy location. 00607 buf = &flags; 00608 } 00609 00610 _mutex->lock(); 00611 00612 record_offset = _offset_by_key[key]; 00613 area = (uint8_t)(record_offset >> offs_by_key_area_bit_pos) & 1; 00614 record_offset &= offs_by_key_offset_mask; 00615 00616 if (!record_offset) { 00617 _mutex->unlock(); 00618 return NVSTORE_NOT_FOUND; 00619 } 00620 00621 ret = read_record(area, record_offset, buf_size, buf, 00622 actual_size, validate_only, valid, 00623 read_type, flags, owner, next_offset); 00624 if ((ret == NVSTORE_SUCCESS) && !valid) { 00625 ret = NVSTORE_DATA_CORRUPT; 00626 } 00627 00628 _mutex->unlock(); 00629 return ret; 00630 } 00631 00632 int NVStore::get(uint16_t key, uint16_t buf_size, void *buf, uint16_t &actual_size) 00633 { 00634 return do_get(key, buf_size, buf, actual_size, 0); 00635 } 00636 00637 int NVStore::get_item_size(uint16_t key, uint16_t &actual_size) 00638 { 00639 return do_get(key, 0, NULL, actual_size, 1); 00640 } 00641 00642 int NVStore::do_set(uint16_t key, uint16_t buf_size, const void *buf, uint16_t flags) 00643 { 00644 int ret = NVSTORE_SUCCESS; 00645 uint32_t record_offset, record_size, new_free_space; 00646 uint32_t next_offset; 00647 uint8_t owner; 00648 00649 if (!_init_done) { 00650 ret = init(); 00651 if (ret != NVSTORE_SUCCESS) { 00652 return ret; 00653 } 00654 } 00655 00656 if (key >= _max_keys) { 00657 return NVSTORE_BAD_VALUE; 00658 } 00659 00660 if (buf_size >= max_data_size) { 00661 return NVSTORE_BAD_VALUE; 00662 } 00663 00664 if (!buf) { 00665 buf_size = 0; 00666 } 00667 00668 if ((flags & delete_item_flag) && !(_offset_by_key[key] & offs_by_key_offset_mask)) { 00669 return NVSTORE_NOT_FOUND; 00670 } 00671 00672 if (_offset_by_key[key] & offs_by_key_set_once_mask) { 00673 return NVSTORE_ALREADY_EXISTS; 00674 } 00675 00676 record_size = align_up(sizeof(nvstore_record_header_t) + buf_size, _min_prog_size); 00677 00678 _mutex->lock(); 00679 00680 owner = (_offset_by_key[key] & offs_by_key_owner_mask) >> offs_by_key_owner_bit_pos; 00681 new_free_space = core_util_atomic_incr_u32(&_free_space_offset, record_size); 00682 record_offset = new_free_space - record_size; 00683 00684 // If we cross the area limit, we need to invoke GC. 00685 if (new_free_space >= _size) { 00686 ret = garbage_collection(key, flags, owner, buf_size, buf); 00687 _mutex->unlock(); 00688 return ret; 00689 } 00690 00691 // Now write the record 00692 ret = write_record(_active_area, record_offset, key, flags, owner, buf_size, buf, next_offset); 00693 if (ret != NVSTORE_SUCCESS) { 00694 _mutex->unlock(); 00695 return ret; 00696 } 00697 00698 // Update _offset_by_key 00699 if (flags & delete_item_flag) { 00700 _offset_by_key[key] = 0; 00701 } else { 00702 _offset_by_key[key] = record_offset | (_active_area << offs_by_key_area_bit_pos) | 00703 (((flags & set_once_flag) != 0) << offs_by_key_set_once_bit_pos) | 00704 (owner << offs_by_key_owner_bit_pos); 00705 } 00706 00707 _mutex->unlock(); 00708 00709 return NVSTORE_SUCCESS; 00710 } 00711 00712 int NVStore::set(uint16_t key, uint16_t buf_size, const void *buf) 00713 { 00714 return do_set(key, buf_size, buf, 0); 00715 } 00716 00717 int NVStore::set_once(uint16_t key, uint16_t buf_size, const void *buf) 00718 { 00719 return do_set(key, buf_size, buf, set_once_flag); 00720 } 00721 00722 int NVStore::allocate_key(uint16_t &key, uint8_t owner) 00723 { 00724 int ret = NVSTORE_SUCCESS; 00725 00726 if ((owner == NVSTORE_UNSPECIFIED_OWNER) || (owner >= NVSTORE_MAX_OWNERS)) { 00727 return NVSTORE_BAD_VALUE; 00728 } 00729 00730 if (!_init_done) { 00731 ret = init(); 00732 if (ret != NVSTORE_SUCCESS) { 00733 return ret; 00734 } 00735 } 00736 00737 _mutex->lock(); 00738 00739 for (key = NVSTORE_NUM_PREDEFINED_KEYS; key < _max_keys; key++) { 00740 if (!_offset_by_key[key]) { 00741 break; 00742 } 00743 } 00744 if (key == _max_keys) { 00745 ret = NVSTORE_NO_FREE_KEY; 00746 } else { 00747 _offset_by_key[key] |= offs_by_key_allocated_mask | (owner << offs_by_key_owner_bit_pos); 00748 } 00749 _mutex->unlock(); 00750 return ret; 00751 } 00752 00753 int NVStore::free_all_keys_by_owner(uint8_t owner) 00754 { 00755 int ret = NVSTORE_SUCCESS; 00756 00757 if ((owner == NVSTORE_UNSPECIFIED_OWNER) || (owner >= NVSTORE_MAX_OWNERS)) { 00758 return NVSTORE_BAD_VALUE; 00759 } 00760 00761 if (!_init_done) { 00762 ret = init(); 00763 if (ret != NVSTORE_SUCCESS) { 00764 return ret; 00765 } 00766 } 00767 00768 _mutex->lock(); 00769 00770 for (uint16_t key = 0; key < _max_keys; key++) { 00771 uint8_t curr_owner = (_offset_by_key[key] & offs_by_key_owner_mask) >> offs_by_key_owner_bit_pos; 00772 if (curr_owner != owner) { 00773 continue; 00774 } 00775 ret = remove(key); 00776 if (ret) { 00777 break; 00778 } 00779 } 00780 00781 _mutex->unlock(); 00782 return ret; 00783 } 00784 00785 int NVStore::remove(uint16_t key) 00786 { 00787 return do_set(key, 0, NULL, delete_item_flag); 00788 } 00789 00790 int NVStore::init() 00791 { 00792 area_state_e area_state[NVSTORE_NUM_AREAS]; 00793 uint32_t free_space_offset_of_area[NVSTORE_NUM_AREAS]; 00794 uint32_t init_attempts_val; 00795 uint32_t next_offset; 00796 int os_ret; 00797 int ret = NVSTORE_SUCCESS; 00798 int valid; 00799 uint16_t key; 00800 uint16_t flags; 00801 uint16_t versions[NVSTORE_NUM_AREAS]; 00802 uint16_t actual_size; 00803 uint8_t owner; 00804 00805 if (_init_done) { 00806 return NVSTORE_SUCCESS; 00807 } 00808 00809 // This handles the case that init function is called by more than one thread concurrently. 00810 // Only the one who gets the value of 1 in _init_attempts_val will proceed, while others will 00811 // wait until init is finished. 00812 init_attempts_val = core_util_atomic_incr_u32(&_init_attempts, 1); 00813 if (init_attempts_val != 1) { 00814 while (!_init_done) { 00815 wait_ms(1); 00816 } 00817 return NVSTORE_SUCCESS; 00818 } 00819 00820 _offset_by_key = new uint32_t[_max_keys]; 00821 MBED_ASSERT(_offset_by_key); 00822 00823 for (key = 0; key < _max_keys; key++) { 00824 _offset_by_key[key] = 0; 00825 } 00826 00827 _mutex = new PlatformMutex; 00828 MBED_ASSERT(_mutex); 00829 00830 _size = (uint32_t) -1; 00831 _flash = new mbed::FlashIAP; 00832 MBED_ASSERT(_flash); 00833 _flash->init(); 00834 00835 _min_prog_size = std::max(_flash->get_page_size(), (uint32_t)sizeof(nvstore_record_header_t)); 00836 if (_min_prog_size > sizeof(nvstore_record_header_t)) { 00837 _page_buf = new uint8_t[_min_prog_size]; 00838 MBED_ASSERT(_page_buf); 00839 } 00840 00841 calc_validate_area_params(); 00842 00843 for (uint8_t area = 0; area < NVSTORE_NUM_AREAS; area++) { 00844 area_state[area] = NVSTORE_AREA_STATE_NONE; 00845 free_space_offset_of_area[area] = 0; 00846 versions[area] = 0; 00847 00848 _size = std::min(_size, _flash_area_params[area].size); 00849 00850 // Find start of empty space at the end of the area. This serves for both 00851 // knowing whether the area is empty and for the record traversal at the end. 00852 os_ret = calc_empty_space(area, free_space_offset_of_area[area]); 00853 MBED_ASSERT(!os_ret); 00854 00855 if (!free_space_offset_of_area[area]) { 00856 area_state[area] = NVSTORE_AREA_STATE_EMPTY; 00857 continue; 00858 } 00859 00860 // Check validity of master record 00861 master_record_data_t master_rec; 00862 ret = read_record(area, 0, sizeof(master_rec), &master_rec, 00863 actual_size, 0, valid, 00864 key, flags, owner, next_offset); 00865 MBED_ASSERT((ret == NVSTORE_SUCCESS) || (ret == NVSTORE_BUFF_TOO_SMALL)); 00866 if (ret == NVSTORE_BUFF_TOO_SMALL) { 00867 // Buf too small error means that we have a corrupt master record - 00868 // treat it as such 00869 valid = 0; 00870 } 00871 00872 // We have a non valid master record, in a non-empty area. Just erase the area. 00873 if ((!valid) || (key != master_record_key)) { 00874 os_ret = flash_erase_area(area); 00875 MBED_ASSERT(!os_ret); 00876 area_state[area] = NVSTORE_AREA_STATE_EMPTY; 00877 continue; 00878 } 00879 versions[area] = master_rec.version; 00880 00881 // Place _free_space_offset after the master record (for the traversal, 00882 // which takes place after this loop). 00883 _free_space_offset = next_offset; 00884 area_state[area] = NVSTORE_AREA_STATE_VALID; 00885 00886 // Unless both areas are valid (a case handled later), getting here means 00887 // that we found our active area. 00888 _active_area = area; 00889 _active_area_version = versions[area]; 00890 } 00891 00892 // In case we have two empty areas, arbitrarily assign 0 to the active one. 00893 if ((area_state[0] == NVSTORE_AREA_STATE_EMPTY) && (area_state[1] == NVSTORE_AREA_STATE_EMPTY)) { 00894 _active_area = 0; 00895 ret = write_master_record(_active_area, 1, _free_space_offset); 00896 MBED_ASSERT(ret == NVSTORE_SUCCESS); 00897 _init_done = 1; 00898 return NVSTORE_SUCCESS; 00899 } 00900 00901 // In case we have two valid areas, choose the one having the higher version (or 0 00902 // in case of wrap around). Erase the other one. 00903 if ((area_state[0] == NVSTORE_AREA_STATE_VALID) && (area_state[1] == NVSTORE_AREA_STATE_VALID)) { 00904 if ((versions[0] > versions[1]) || (!versions[0])) { 00905 _active_area = 0; 00906 } else { 00907 _active_area = 1; 00908 } 00909 _active_area_version = versions[_active_area]; 00910 os_ret = flash_erase_area(1 - _active_area); 00911 MBED_ASSERT(!os_ret); 00912 } 00913 00914 // Traverse area until reaching the empty space at the end or until reaching a faulty record 00915 while (_free_space_offset < free_space_offset_of_area[_active_area]) { 00916 ret = read_record(_active_area, _free_space_offset, 0, NULL, 00917 actual_size, 1, valid, 00918 key, flags, owner, next_offset); 00919 MBED_ASSERT(ret == NVSTORE_SUCCESS); 00920 00921 // In case we have a faulty record, this probably means that the system crashed when written. 00922 // Perform a garbage collection, to make the the other area valid. 00923 if (!valid) { 00924 ret = garbage_collection(no_key, 0, 0, 0, NULL); 00925 break; 00926 } 00927 if (flags & delete_item_flag) { 00928 _offset_by_key[key] = 0; 00929 } else { 00930 _offset_by_key[key] = _free_space_offset | (_active_area << offs_by_key_area_bit_pos) | 00931 (((flags & set_once_flag) != 0) << offs_by_key_set_once_bit_pos) | 00932 (owner << offs_by_key_owner_bit_pos); 00933 } 00934 _free_space_offset = next_offset; 00935 } 00936 00937 _init_done = 1; 00938 return NVSTORE_SUCCESS; 00939 } 00940 00941 int NVStore::deinit() 00942 { 00943 if (_init_done) { 00944 _flash->deinit(); 00945 delete _flash; 00946 delete _mutex; 00947 delete[] _offset_by_key; 00948 if (_page_buf) { 00949 delete[] _page_buf; 00950 _page_buf = 0; 00951 } 00952 } 00953 00954 _init_attempts = 0; 00955 _init_done = 0; 00956 00957 return NVSTORE_SUCCESS; 00958 } 00959 00960 int NVStore::reset() 00961 { 00962 uint8_t area; 00963 int os_ret; 00964 00965 if (!_init_done) { 00966 init(); 00967 } 00968 00969 // Erase both areas, and reinitialize the module. This is totally not thread safe, 00970 // as init doesn't take the case of re-initialization into account. It's OK, as this function 00971 // should only be called in pre-production cases. 00972 for (area = 0; area < NVSTORE_NUM_AREAS; area++) { 00973 os_ret = flash_erase_area(area); 00974 if (os_ret) { 00975 return NVSTORE_WRITE_ERROR; 00976 } 00977 } 00978 00979 deinit(); 00980 return init(); 00981 } 00982 00983 int NVStore::get_area_params(uint8_t area, uint32_t &address, size_t &size) 00984 { 00985 if (area >= NVSTORE_NUM_AREAS) { 00986 return NVSTORE_BAD_VALUE; 00987 } 00988 00989 if (!_init_done) { 00990 init(); 00991 } 00992 00993 address = _flash_area_params[area].address; 00994 size = _flash_area_params[area].size; 00995 00996 return NVSTORE_SUCCESS; 00997 } 00998 00999 size_t NVStore::size() 01000 { 01001 if (!_init_done) { 01002 init(); 01003 } 01004 01005 return _size; 01006 } 01007 01008 #endif // NVSTORE_ENABLED
Generated on Tue Aug 9 2022 00:37:17 by
