1 /*
2 * Copyright (c) 2020 HiSilicon (Shanghai) Technologies CO., LIMITED.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 * Description: KV Storage Library store access module implementation
15 */
16
17 #include "nv_store.h"
18 #include "nv_key.h"
19 #include "nv_page.h"
20 #include "nv_nvregion.h"
21 #include "securec.h"
22 #include "nv_porting.h"
23 #include "common_def.h"
24 #include "nv.h"
25
26 #define kv_page_base(a) ((uint8_t *)(((uint32_t)(a)) & ~(KV_PAGE_SIZE - 1)))
27 #define kv_is_pointer_in_page(x, p) ((((uint32_t)(x)) >= ((uint32_t) (p))) && \
28 (((uint32_t)(x)) < (((uint32_t) (p)) + KV_PAGE_SIZE)))
29 #define kv_is_pointer_word_aligned(x) (((uint32_t)(x) & 0x3) == 0)
30 #define kv_bytes_to_words(x) (((x)+3) >> 2) /* minimal number of words needed to store that many bytes */
31 #define kv_is_mem_in_page(x, y, p) ((kv_is_pointer_in_page((x), (p))) && \
32 (kv_is_pointer_in_page(((uint32_t)(x) + ((uint32_t)(y) - 1)), (p))))
33
34 /**
35 * Array holding the id for each KV store
36 */
37 static const uint16_t g_kv_store_ids[KV_STORE_MAX_NUM] = {
38 KV_STORE_ID_ACPU,
39 };
40
41 /**
42 * Array holding the number of pages used by each KV store
43 */
44 /* Consider using NV region contents to determine actual number of pages and stores */
45 static const uint8_t g_kv_store_num_pages[KV_STORE_MAX_NUM] = {
46 KV_STORE_PAGES_ACPU,
47 };
48
kv_store_get_id(kv_store_t store)49 uint16_t kv_store_get_id(kv_store_t store)
50 {
51 if (store < KV_STORE_MAX_NUM) {
52 return g_kv_store_ids[store];
53 }
54 return 0;
55 }
56
kv_store_get_page_count(kv_store_t store)57 uint8_t kv_store_get_page_count(kv_store_t store)
58 {
59 if (store < KV_STORE_MAX_NUM) {
60 return g_kv_store_num_pages[store];
61 }
62 return 0;
63 }
64
kv_store_get_page_handle(kv_store_t store,uint32_t page_index,kv_page_handle_t * page)65 errcode_t kv_store_get_page_handle(kv_store_t store, uint32_t page_index, kv_page_handle_t *page)
66 {
67 errcode_t res;
68 uint16_t store_id;
69 kv_page_location page_location;
70
71 (void)memset_s(page, sizeof(kv_page_handle_t), 0, sizeof(*page));
72
73 store_id = kv_store_get_id(store);
74 res = kv_nvregion_find_page(store_id, (uint8_t)page_index, &page_location, &page->page_header);
75 if (res != ERRCODE_SUCC) {
76 return res;
77 }
78 page->page_location = page_location;
79 return ERRCODE_SUCC;
80 }
81
kv_store_find_valid_key(kv_store_t store,kv_key_id key_id,kv_key_handle_t * key)82 errcode_t kv_store_find_valid_key(kv_store_t store, kv_key_id key_id, kv_key_handle_t *key)
83 {
84 uint32_t page_index;
85 uint32_t pages_in_store;
86 kv_key_filter_t search_filter;
87
88 /* We are looking for the first (and only) valid key in a store */
89 search_filter.location = 0;
90 search_filter.mask = 0xFFFF;
91 search_filter.pattern = key_id;
92 search_filter.state = KV_KEY_FILTER_STATE_VALID;
93 search_filter.type = KV_KEY_FILTER_TYPE_ANY;
94 pages_in_store = kv_store_get_page_count(store);
95 for (page_index = 0; page_index < pages_in_store; page_index++) {
96 kv_page_handle_t page;
97 if ((kv_store_get_page_handle(store, page_index, &page) == ERRCODE_SUCC) &&
98 (kv_page_find_first_key(&page, &search_filter, key) == ERRCODE_SUCC)) {
99 return ERRCODE_SUCC;
100 }
101 }
102 return ERRCODE_NV_KEY_NOT_FOUND;
103 }
104
kv_store_find_backup_key(kv_key_id key_id,kv_key_handle_t * key,kv_page_location page_location)105 static errcode_t kv_store_find_backup_key(kv_key_id key_id, kv_key_handle_t *key, kv_page_location page_location)
106 {
107 kv_page_handle_t page;
108 kv_key_filter_t search_filter;
109 page.page_location = page_location;
110 /* We are looking for the first (and only) valid key in a store */
111 search_filter.location = 0;
112 search_filter.mask = 0xFFFF;
113 search_filter.pattern = key_id;
114 search_filter.state = KV_KEY_FILTER_STATE_VALID;
115 search_filter.type = KV_KEY_FILTER_TYPE_ANY;
116
117 if (kv_page_find_first_key(&page, &search_filter, key) == ERRCODE_SUCC) {
118 return ERRCODE_SUCC;
119 }
120 return ERRCODE_NV_KEY_NOT_FOUND;
121 }
122
kv_store_get_key_attr(kv_store_t store,kv_key_id key_id,uint16_t * len,kv_attributes_t * attributes)123 errcode_t kv_store_get_key_attr(kv_store_t store, kv_key_id key_id, uint16_t *len, kv_attributes_t *attributes)
124 {
125 errcode_t res = ERRCODE_FAIL;
126 kv_key_handle_t key;
127
128 res = kv_store_find_valid_key(store, key_id, &key);
129 if (res != ERRCODE_SUCC) {
130 return res;
131 }
132
133 /* Extract stored attributes from the key */
134 *attributes = kv_key_attributes(&key);
135 *len = key.header.length;
136 return res;
137 }
138
kv_store_get_backup_key_attr(kv_key_id key_id,uint16_t * len,kv_attributes_t * attributes,kv_key_handle_t * backup_key)139 errcode_t kv_store_get_backup_key_attr(kv_key_id key_id, uint16_t *len, kv_attributes_t *attributes,
140 kv_key_handle_t *backup_key)
141 {
142 #if (CONFIG_NV_SUPPORT_BACKUP_RESTORE == NV_YES)
143 errcode_t res = ERRCODE_FAIL;
144 kv_page_location page_location;
145 kv_nvregion_area_t* nvregion_area = nv_get_region_area();
146 if (nvregion_area == NULL) {
147 return ERRCODE_FAIL;
148 }
149
150 for (uint32_t page_num = 0; page_num < KV_BACKUP_PAGE_NUM; page_num++) {
151 page_location = (kv_page_location)(uintptr_t)(nvregion_area->nv_backup_addr + page_num * KV_PAGE_SIZE);
152 res = kv_store_find_backup_key(key_id, backup_key, page_location);
153 if (res != ERRCODE_SUCC && page_num == KV_BACKUP_PAGE_NUM - 1) {
154 return res;
155 } else if (res == ERRCODE_SUCC) {
156 break;
157 }
158 }
159
160 if (attributes != NULL && len != NULL) {
161 /* Extract stored attributes from the backup_key */
162 *attributes = kv_key_attributes(backup_key);
163 *len = backup_key->header.length;
164 }
165 return res;
166 #else
167 unused(key_id);
168 unused(len);
169 unused(attributes);
170 unused(backup_key);
171 return ERRCODE_SUCC;
172 #endif
173 }
174
kv_store_read_backup_key(kv_key_id key_id,kv_store_key_data_t * key_data,kv_attributes_t * attributes)175 errcode_t kv_store_read_backup_key(kv_key_id key_id, kv_store_key_data_t *key_data,
176 kv_attributes_t *attributes)
177 {
178 #if (CONFIG_NV_SUPPORT_BACKUP_RESTORE == NV_YES)
179 errcode_t res;
180 kv_page_location page_location;
181 kv_nvregion_area_t* nvregion_area = nv_get_region_area();
182 if (nvregion_area == NULL) {
183 return ERRCODE_FAIL;
184 }
185
186 for (uint32_t page_num = 0; page_num < KV_BACKUP_PAGE_NUM; page_num++) {
187 page_location = (kv_page_location)(uintptr_t)(nvregion_area->nv_backup_addr + page_num * KV_PAGE_SIZE);
188 res = kv_store_get_backup_key(key_id, key_data, attributes, page_location);
189 if (res == ERRCODE_SUCC) {
190 return res;
191 }
192 }
193 return ERRCODE_NV_KEY_NOT_FOUND;
194 #else
195 unused(key_id);
196 unused(key_data);
197 unused(attributes);
198 return ERRCODE_NV_KEY_NOT_FOUND;
199 #endif
200 }
201
202
kv_store_get_key(kv_store_t store,kv_key_id key_id,kv_store_key_data_t * key_data,kv_attributes_t * attributes)203 errcode_t kv_store_get_key(kv_store_t store, kv_key_id key_id, kv_store_key_data_t *key_data,
204 kv_attributes_t *attributes)
205 {
206 errcode_t res;
207 kv_key_handle_t key;
208
209 res = kv_store_find_valid_key(store, key_id, &key);
210 if (res != ERRCODE_SUCC) {
211 return res;
212 }
213
214 if (attributes != NULL) {
215 *attributes = kv_key_attributes(&key);
216 }
217
218 /* Attempt to obtain key data from store */
219 key_data->kvalue_actual_length = key.header.length;
220 if (key_data->kvalue_max_length < key_data->kvalue_actual_length) {
221 return ERRCODE_NV_GET_BUFFER_TOO_SMALL;
222 }
223
224 res = kv_key_read_data(&key, key_data->kvalue);
225 return res;
226 }
227
kv_store_get_backup_key(kv_key_id key_id,kv_store_key_data_t * key_data,kv_attributes_t * attributes,kv_page_location page_location)228 errcode_t kv_store_get_backup_key(kv_key_id key_id, kv_store_key_data_t *key_data, kv_attributes_t *attributes,
229 kv_page_location page_location)
230 {
231 errcode_t res_b;
232 kv_key_handle_t key;
233
234 res_b = kv_store_find_backup_key(key_id, &key, page_location);
235 if (res_b != ERRCODE_SUCC) {
236 return res_b;
237 }
238
239 if (attributes != NULL) {
240 *attributes = kv_key_attributes(&key);
241 }
242
243 /* Attempt to obtain key data from store */
244 key_data->kvalue_actual_length = key.header.length;
245 if (key_data->kvalue_max_length < key_data->kvalue_actual_length) {
246 return ERRCODE_NV_GET_BUFFER_TOO_SMALL;
247 }
248
249 res_b = kv_key_read_data(&key, key_data->kvalue);
250 return res_b;
251 }
252
kv_store_get_status(kv_store_t store,nv_store_status_t * store_status)253 errcode_t kv_store_get_status(kv_store_t store, nv_store_status_t *store_status)
254 {
255 uint32_t pages_in_store;
256 uint32_t page_index;
257
258 if ((store >= KV_STORE_MAX_NUM) || (store_status == NULL)) {
259 return ERRCODE_NV_INVALID_PARAMS;
260 }
261
262 (void)memset_s(store_status, sizeof(nv_store_status_t), 0, sizeof(nv_store_status_t));
263
264 pages_in_store = kv_store_get_page_count(store);
265 for (page_index = 0; page_index < pages_in_store; page_index++) {
266 kv_page_handle_t page;
267 kv_page_status_t page_status;
268 if (kv_store_get_page_handle(store, page_index, &page) == ERRCODE_SUCC) {
269 kv_page_get_status(&page, &page_status);
270 store_status->total_space += page_status.total_space;
271 store_status->used_space += page_status.used_space;
272 store_status->reclaimable_space += page_status.reclaimable_space;
273 store_status->corrupted_space += page_status.corrupted_space;
274 if (page_status.max_key_space > store_status->max_key_space) {
275 store_status->max_key_space = page_status.max_key_space;
276 }
277 }
278 }
279 return ERRCODE_SUCC;
280 }
281
kv_store_find_write_page(kv_store_t store,uint32_t required_space,kv_page_handle_t * page,kv_page_status_t * page_status)282 errcode_t kv_store_find_write_page(kv_store_t store, uint32_t required_space, kv_page_handle_t *page,
283 kv_page_status_t *page_status)
284 {
285 uint32_t pages_in_store;
286 uint32_t page_index;
287 kv_page_handle_t page_tmp;
288 kv_page_status_t page_status_tmp;
289 uint32_t mininal_used_times = 0;
290 uint32_t need_defrag_page = (uint32_t)-1;
291
292 pages_in_store = kv_store_get_page_count(store);
293
294 for (page_index = 0; page_index < pages_in_store; page_index++) {
295 errcode_t res = kv_store_get_page_handle(store, page_index, page);
296 if (res != ERRCODE_SUCC) {
297 return res;
298 }
299 kv_page_get_status_from_map(page, page_status);
300
301 /* 如果有未使用的空间,直接返回该页,空间计算使用加法判断更保险 */
302 if (page_status->total_space >= (page_status->used_space + required_space)) {
303 return ERRCODE_SUCC;
304 }
305
306 /* 如果未找到有未使用空间够用的页,考虑到flash擦写平衡,在可换页的页中找到一个擦写次数最少的 */
307 if (page_status->max_key_space >= required_space) {
308 uint32_t used_times = kv_nvregion_get_use_times(page->page_location);
309 if ((need_defrag_page == (uint32_t)-1) || (used_times < mininal_used_times)) {
310 need_defrag_page = page_index;
311 page_tmp = *page;
312 page_status_tmp = *page_status;
313 mininal_used_times = used_times;
314 }
315 }
316 }
317
318 if (need_defrag_page != (uint32_t)-1) {
319 *page = page_tmp;
320 *page_status = page_status_tmp;
321 return ERRCODE_NV_DEFRAGMENTATION_NEEDED;
322 }
323
324 return ERRCODE_NV_NO_ENOUGH_SPACE;
325 }
326