blob: 6cda8f5a665a041d8143923f973b8850122d1b50 [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
2 * Copyright (c) 2013 Heather Lee Wilson
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23#include <dev/flash_nor.h>
24#include <lib/norfs.h>
25#include <lib/norfs_inode.h>
26#include <lib/norfs_config.h>
27#include <iovec.h>
28#include <stdlib.h>
29#include <err.h>
30#include <string.h>
31#include <platform.h>
32#include <lib/cksum.h>
33#include <platform/flash_nor_config.h>
34#include <list.h>
35#include <debug.h>
36
37/* FRIEND_TEST non-static if unit testing, in order to
38 * allow functions to be exposed by a test header file.
39 */
40#ifdef WITH_LIB_UNITTEST
41#define FRIEND_TEST
42#else
43#define FRIEND_TEST static
44#endif
45
46#define member_size(type, member) sizeof(((type *)0)->member)
47
48
49/* Handle rolling over of version field beyond max(uint16_t) by assuming that
50 * the difference between the smallest and largest versions are no more than
51 * half the range of uint16_t. By subtracting one unsigned uint16_t from
52 * another, then casting the delta to int16_t, if the delta is greater than
53 * 1/2 * max(uint16_t), then the most significant bit will be a 1, causing the
54 * signed integer to be negative and VERSION_GREATER_THAN to return false.
55 */
56#define VERSION_GREATER_THAN(a, b) ((int16_t)((a) - (b)) > 0)
57
58struct norfs_header {
59 uint32_t key;
60 uint16_t version;
61 uint16_t len;
62 uint8_t flags;
63 uint16_t crc;
64};
65
66/* Block header written after successful erase. */
67FRIEND_TEST const unsigned char NORFS_BLOCK_HEADER[4] = {'T', 'O', 'F', 'U'};
68/* Block header to indicate garbage collection has started. */
69FRIEND_TEST const unsigned char NORFS_BLOCK_GC_STARTED_HEADER[2] = {'S', 'O'};
70/* Block header to indicate block was not interrupted during garbage
71 * collection.
72 */
73FRIEND_TEST const unsigned char NORFS_BLOCK_GC_FINISHED_HEADER[2] = {'U', 'P'};
74
75FRIEND_TEST uint32_t write_pointer = 0;
76FRIEND_TEST uint32_t total_remaining_space = NORFS_AVAILABLE_SPACE;
77FRIEND_TEST uint8_t num_free_blocks = 0;
78static bool fs_mounted = false;
79FRIEND_TEST uint32_t norfs_nvram_offset;
80static struct list_node inode_list;
81
82static bool block_free[NORFS_NUM_BLOCKS];
83
84static status_t collect_garbage(void);
85static status_t load_and_verify_obj(uint32_t *ptr, struct norfs_header *header);
86
87FRIEND_TEST uint8_t block_num(uint32_t flash_pointer)
88{
89 return flash_pointer/FLASH_PAGE_SIZE;
90}
91
92/* Update pointer to a free block. If no free blocks, return error. */
93FRIEND_TEST status_t find_free_block(uint32_t *ptr)
94{
95 uint8_t i = block_num(*ptr) + 1;
96 uint8_t imod;
97 for (uint8_t j = 0; j < NORFS_NUM_BLOCKS; i++, j++) {
98 imod = i % NORFS_NUM_BLOCKS;
99 /* If is a free block, return. */
100 if (block_free[imod]) {
101 *ptr = imod * FLASH_PAGE_SIZE + sizeof(NORFS_BLOCK_HEADER);
102 return NO_ERROR;
103 }
104 }
105 /* A free block could not be found. */
106 return ERR_NO_MEMORY;
107}
108
109static uint32_t curr_block_free_space(uint32_t pointer)
110{
111 return (block_num(pointer) + 1) * FLASH_PAGE_SIZE - pointer;
112}
113
114static bool block_full(uint8_t block, uint32_t ptr)
115{
116 if (block != block_num(ptr)) {
117 return true;
118 }
119 return curr_block_free_space(ptr) < NORFS_OBJ_OFFSET;
120}
121
122static uint8_t select_garbage_block(uint32_t ptr)
123{
124 return (block_num(ptr) + 1) % 8;
125}
126
127static ssize_t nvram_read(size_t offset, size_t length, void *ptr)
128{
129 return flash_nor_read(NORFS_BANK, offset + norfs_nvram_offset, length, ptr);
130}
131
132static ssize_t nvram_write(size_t offset, size_t length, const void *ptr)
133{
134 return flash_nor_write(NORFS_BANK, offset + norfs_nvram_offset, length,
135 ptr);
136}
137
138static ssize_t nvram_erase_pages(size_t offset, size_t length)
139{
140 return flash_nor_erase_pages(NORFS_BANK, offset + norfs_nvram_offset,
141 length);
142}
143
144unsigned char* nvram_flash_pointer(uint32_t loc)
145{
146 return FLASH_PTR(flash_nor_get_bank(NORFS_BANK), loc + norfs_nvram_offset);
147}
148
149FRIEND_TEST bool get_inode(uint32_t key, struct norfs_inode **inode)
150{
151 struct list_node *curr_lnode;
152 struct norfs_inode *curr_inode;
153 uint32_t curr_key;
154
155 if (!inode)
156 return false;
157
158 *inode = NULL;
159 list_for_every(&inode_list, curr_lnode) {
160 curr_inode = containerof(curr_lnode, struct norfs_inode, lnode);
161 nvram_read(curr_inode->location + NORFS_KEY_OFFSET,
162 sizeof(curr_key), &curr_key);
163 if (curr_key == key) {
164 *inode = curr_inode;
165 return true;
166 }
167 }
168 return false;
169}
170
171static uint16_t calculate_header_crc(uint32_t key, uint16_t version,
172 uint16_t len, uint8_t flags)
173{
174 uint16_t crc = crc16((unsigned char *) &key, sizeof(key));
175 crc = update_crc16(crc, (unsigned char *) &version, sizeof(version));
176 crc = update_crc16(crc, (unsigned char *) &len, sizeof(len));
177 crc = update_crc16(crc, (unsigned char *) &flags, sizeof(flags));
178 return crc;
179}
180
181/* Read header into parameter buffers. Return bytes written. */
182static ssize_t read_header(uint32_t ptr, struct norfs_header *header)
183{
184 ssize_t total_bytes_read = 0;
185 ssize_t bytes_read = 0;
186 bytes_read = nvram_read(ptr + NORFS_KEY_OFFSET,
187 sizeof(header->key), &header->key);
188 if (bytes_read < 0) {
189 return bytes_read;
190 }
191 total_bytes_read += bytes_read;
192 bytes_read = nvram_read(ptr + NORFS_VERSION_OFFSET,
193 sizeof(header->version), &header->version);
194 if (bytes_read < 0) {
195 return bytes_read;
196 }
197 total_bytes_read += bytes_read;
198 bytes_read = nvram_read(ptr + NORFS_LENGTH_OFFSET,
199 sizeof(header->len), &header->len);
200 if (bytes_read < 0) {
201 return bytes_read;
202 }
203 total_bytes_read += bytes_read;
204 bytes_read = nvram_read(ptr + NORFS_FLAGS_OFFSET,
205 sizeof(header->flags), &header->flags);
206 if (bytes_read < 0) {
207 return bytes_read;
208 }
209 total_bytes_read += bytes_read;
210
211 /* Increment pointer by one byte to account for unused padding. */
212 total_bytes_read += 1;
213 bytes_read = nvram_read(ptr + NORFS_CHECKSUM_OFFSET,
214 sizeof(header->crc), &header->crc);
215 if (bytes_read < 0) {
216 return bytes_read;
217 }
218 total_bytes_read += bytes_read;
219
220 return total_bytes_read;
221}
222
223status_t norfs_read_obj_iovec(uint32_t key, iovec_t *obj_iov,
224 uint32_t iov_count, size_t *bytes_read, uint8_t flags)
225{
226 if (!fs_mounted)
227 return ERR_NOT_MOUNTED;
228
229 uint32_t read_ptr;
230 uint16_t to_read, total_to_read;
231 struct norfs_inode *inode;
232 struct norfs_header stored_header;
233 int16_t bytes = 0;
234 uint8_t i = 0;
235 ssize_t iov_size = iovec_size(obj_iov, iov_count);
236
237 if (bytes_read) {
238 *bytes_read = 0;
239 }
240
241 if (!get_inode(key, &inode)) {
242 return ERR_NOT_FOUND;
243 }
244 read_ptr = inode->location;
245 bytes = read_header(read_ptr, &stored_header);
246
247 total_to_read = MIN(stored_header.len, iov_size);
248 if (bytes < 0) {
249 TRACEF("Error reading header. Status: %d\n", bytes);
250 return bytes;
251 }
252 read_ptr += bytes;
253
254 if (stored_header.flags & NORFS_DELETED_MASK) {
255 TRACEF("Object is already deleted.\n");
256 return ERR_NOT_FOUND;
257 }
258
259 while (*bytes_read < total_to_read) {
260 to_read = MIN(total_to_read - *bytes_read, obj_iov[i].iov_len);
261 bytes = nvram_read(read_ptr + *bytes_read,
262 to_read, obj_iov[i].iov_base);
263 if (bytes < 0) {
264 TRACEF("Read failed with error: %d\n", bytes);
265 return bytes;
266 }
267 if (bytes_read) {
268 *bytes_read += bytes;
269 }
270 i++;
271 }
272
273 return NO_ERROR;
274}
275
276static status_t write_obj_header(uint32_t *ptr, uint32_t key, uint16_t version,
277 uint16_t len, uint8_t flags, uint16_t crc)
278{
279 unsigned char buff[WORD_SIZE];
280 int bytes_written;
281
282 bytes_written = nvram_write(*ptr, sizeof(key), &key);
283 if (bytes_written < 0) {
284 return bytes_written;
285 }
286 *ptr += bytes_written;
287
288 memcpy(buff, &version, sizeof(version));
289 memcpy(buff + sizeof(version), &len, sizeof(len));
290 bytes_written = nvram_write(*ptr, sizeof(buff), &buff);
291 if (bytes_written < 0) {
292 return bytes_written;
293 }
294 *ptr += bytes_written;
295
296 memcpy(buff, &flags, sizeof(flags));
297 memset(buff + 1, 1, sizeof(flags));
298 memcpy(buff + 2, &crc, sizeof(crc));
299 bytes_written = nvram_write(*ptr, sizeof(buff), &buff);
300 if (bytes_written < 0) {
301 return bytes_written;
302 }
303 *ptr += bytes_written;
304 return NO_ERROR;
305}
306
307static status_t copy_iovec_to_disk(const struct iovec *iov, uint16_t iov_count,
308 uint32_t *location, uint16_t *crc)
309{
310 unsigned char word[4] = {0};
311 uint16_t iov_ptr = 0;
312 uint16_t word_ptr = 0;
313 uint8_t word_size = sizeof(word);
314 int bytes_written;
315 for (uint16_t i = 0; i < iov_count; i++) {
316 while ((uint16_t)iov[i].iov_len - iov_ptr > word_size - word_ptr) {
317 memcpy(word + word_ptr,
318 (unsigned char *)iov[i].iov_base + iov_ptr, word_size -
319 word_ptr);
320 iov_ptr += word_size - word_ptr;
321
322 bytes_written = nvram_write(*location, sizeof(word),
323 &word);
324 if (bytes_written < 0) {
325 TRACEF("Error while writing: %d\n", bytes_written);
326 return bytes_written;
327 }
328
329 *location += bytes_written;
330 *crc = update_crc16(*crc, word, sizeof(word));
331 memset(word, 0, sizeof(word));
332 word_ptr = 0;
333 }
334
335 memcpy((unsigned char *)word + word_ptr,
336 (unsigned char *)iov[i].iov_base + iov_ptr,
337 iov[i].iov_len - iov_ptr);
338 word_ptr += iov[i].iov_len - iov_ptr;
339
340 iov_ptr = 0;
341 }
342
343 if (word_ptr > 0) {
344 bytes_written = nvram_write(*location, sizeof(word),
345 &word);
346
347 if (bytes_written < 0) {
348 TRACEF("Error writing: %d\n", bytes_written);
349 return bytes_written;
350 }
351
352 *location += bytes_written;
353 *crc = update_crc16(*crc, word, word_ptr);
354 }
355 return NO_ERROR;
356}
357
358static status_t initialize_next_block(uint32_t *ptr)
359{
360 uint32_t header_pointer;
361 ssize_t bytes_written;
362 status_t status;
363
364 /* Update write pointer. */
365 status = find_free_block(ptr);
366 if (status) {
367 TRACEF("Error finding free block. Status: %d\n", status);
368 return status;
369 }
370
371 num_free_blocks--;
372 block_free[block_num(*ptr)] = false;
373 bytes_written = nvram_write(*ptr,
374 sizeof(NORFS_BLOCK_GC_STARTED_HEADER), &NORFS_BLOCK_GC_STARTED_HEADER);
375
376 if (bytes_written < 0) {
377 TRACEF("Error writing header.\n");
378 return bytes_written;
379 }
380
381 header_pointer = *ptr + bytes_written;
382 *ptr += sizeof(NORFS_BLOCK_GC_STARTED_HEADER) +
383 sizeof(NORFS_BLOCK_GC_FINISHED_HEADER);
384
385 if (num_free_blocks < NORFS_MIN_FREE_BLOCKS) {
386 status = collect_garbage();
387 if (status) {
388 TRACEF("Failed to collection garbage. Error: %d\n.",
389 status);
390 return status;
391 }
392 }
393
394 status = nvram_write(header_pointer,
395 sizeof(NORFS_BLOCK_GC_FINISHED_HEADER),
396 &NORFS_BLOCK_GC_FINISHED_HEADER);
397 if (status < 0) {
398 TRACEF("Failed to write header.\n");
399 return status;
400 }
401
402 return NO_ERROR;
403}
404
405status_t write_obj_iovec(const iovec_t *iov, uint iov_count, uint *location,
406 uint32_t key, uint16_t version, uint8_t flags)
407{
408 uint16_t crc = 0;
409 uint16_t len = iovec_size(iov, iov_count);
410 status_t status;
411
412 uint32_t header_loc = *location;
413 *location += NORFS_OBJ_OFFSET;
414 crc = calculate_header_crc(key, version, len, flags);
415
416 if (!(flags & NORFS_DELETED_MASK)) {
417 /* If object is not a deletion header, write object bytes to disk. */
418 status = copy_iovec_to_disk(iov, iov_count, location, &crc);
419 if (status) {
420 TRACEF("Error writing. Status: %d\n", status);
421 return status;
422 }
423 }
424
425 status = write_obj_header(&header_loc, key, version, len, flags, crc);
426 if (status) {
427 TRACEF("Error writing. Status: %d\n", status);
428 return status;
429 }
430
431 return NO_ERROR;
432}
433
434status_t norfs_read_obj(uint32_t key, unsigned char *buffer,
435 uint16_t buffer_len, size_t *bytes_read,
436 uint8_t flags)
437{
438 if (!fs_mounted)
439 return ERR_NOT_MOUNTED;
440
441 struct iovec vec[1];
442 vec->iov_base = buffer;
443 vec->iov_len = buffer_len;
444 status_t status = norfs_read_obj_iovec(key, vec, 1, bytes_read, flags);
445
446 return status;
447}
448
449status_t norfs_put_obj(uint32_t key, unsigned char *obj, uint16_t len,
450 uint8_t flags)
451{
452 if (!fs_mounted)
453 return ERR_NOT_MOUNTED;
454
455 if (key == 0xFFFF) {
456 return ERR_INVALID_ARGS;
457 }
458 status_t status;
459 struct iovec const vec[1] = {{obj, len}};
460 status = norfs_put_obj_iovec(key, vec, 1, flags);
461 return status;
462}
463
464bool is_deleted(uint32_t loc)
465{
466 uint8_t flags;
467 nvram_read(loc + NORFS_FLAGS_OFFSET, sizeof(flags), &flags);
468 return NORFS_DELETED_MASK & flags;
469}
470
471status_t norfs_remove_obj(uint32_t key)
472{
473 if (!fs_mounted)
474 return ERR_NOT_MOUNTED;
475
476 struct norfs_inode *inode;
477 uint16_t prior_len;
478 struct iovec iov[1];
479 status_t status;
480 bool success = get_inode(key, &inode);
481 if (!success || is_deleted(inode->location))
482 return ERR_NOT_FOUND;
483
484 status = nvram_read(inode->location + NORFS_LENGTH_OFFSET,
485 sizeof(uint16_t), &prior_len);
486 if (status < 0) {
487 TRACEF("Failed to read during norfs_remove_obj. Status: %d\n", status);
488 return status;
489 }
490
491 iov->iov_base = NULL;
492 iov->iov_len = 0;
493
494 /*
495 * Write a deleted object by passing a null iovec pointer. Only header
496 * will be written.
497 */
498 status = norfs_put_obj_iovec(key, iov, 0, NORFS_DELETED_MASK);
499 if (status)
500 TRACEF("Error putting object. %d\n", status);
501
502 return status;
503}
504
505static status_t find_space_for_object(uint16_t obj_len, uint32_t *ptr)
506{
507 status_t status;
508 uint8_t initial_block_num = block_num(*ptr);
509 while (curr_block_free_space(*ptr) < (uint16_t) NORFS_FLASH_SIZE(obj_len)) {
510 status = initialize_next_block(ptr);
511 if (status)
512 return status;
513 /* If we have already tried and failed to write to this block, exit. */
514 if (block_num(*ptr) == initial_block_num)
515 return ERR_NO_MEMORY;
516 }
517 return NO_ERROR;
518}
519
520/*
521 * Store an object in flash. Moves write_pointer to new block and garbage
522 * collects if needed. If write fails, will reattempt.
523 * How to handle write failures is not fully defined - at the moment I stop once
524 * find_free_block is attempting to rewrite to a block it has already failed to
525 * write to - after a full loop in a round-robin style garbage selection of
526 * blocks. Which is a lot of write attempts.
527 */
528status_t norfs_put_obj_iovec(uint32_t key, const iovec_t *iov,
529 uint32_t iov_count, uint8_t flags)
530{
531 if (!fs_mounted)
532 return ERR_NOT_MOUNTED;
533
534 if (key == 0xFFFF) {
535 return ERR_INVALID_ARGS;
536 }
537
538 uint8_t block_num_to_write;
539 struct norfs_inode *inode;
540 uint16_t len = iovec_size(iov, iov_count);
541 status_t status;
542 uint8_t stored_flags;
543 uint16_t version = 0;
544 uint32_t header_loc;
545 bool deletion = flags & NORFS_DELETED_MASK;
546 bool obj_preexists = get_inode(key, &inode);
547 if (obj_preexists) {
548 nvram_read(inode->location + NORFS_FLAGS_OFFSET,
549 sizeof(stored_flags), &stored_flags);
550 if (stored_flags & flags & NORFS_DELETED_MASK) {
551 /* Attempting to delete an object no longer in filesystem. */
552 TRACEF("The object attempting to be removed has already been \
553 deleted. stored_flags: 0x%x\n", stored_flags);
554 return ERR_NOT_FOUND;
555 }
556
557 nvram_read(inode->location + NORFS_VERSION_OFFSET,
558 sizeof(version), &version);
559 version++;
560 } else if (deletion) {
561 /* Attempting to delete a non-existent object. */
562 TRACEF("Attempting to remove an object not in filesystem.\n");
563 return ERR_NOT_FOUND;
564 } else {
565 inode = malloc(sizeof(struct norfs_inode));
566 inode->reference_count = 1;
567 }
568
569 flash_nor_begin(NORFS_BANK);
570
571 if (NORFS_FLASH_SIZE(len) > NORFS_MAX_OBJ_LEN) {
572 TRACEF("Object too big. Not adding.\n");
573 flash_nor_end(NORFS_BANK);
574 return ERR_TOO_BIG;
575 }
576
577 if (!deletion && (NORFS_FLASH_SIZE(len) > total_remaining_space)) {
578 TRACEF("Not enough remaining space. Remaining space: %d\tObject len: \
579 %d\n", total_remaining_space, len);
580 flash_nor_end(NORFS_BANK);
581 return ERR_NO_MEMORY;
582 }
583
584 status = find_space_for_object(len, &write_pointer);
585 if (status) {
586 TRACEF("Error finding space for object. Status: %d\n", status);
587 flash_nor_end(NORFS_BANK);
588 return ERR_IO;
589 }
590
591 block_num_to_write = block_num(write_pointer);
592 header_loc = write_pointer;
593 status = write_obj_iovec(iov, iov_count, &write_pointer, key,
594 version, flags);
595 if (!status) {
596 if (!obj_preexists) {
597 list_add_tail(&inode_list, &inode->lnode);
598 } else {
599 /* If object preexists, remove outdated version from remaining space. */
600 uint16_t prior_len;
601 nvram_read(inode->location + NORFS_LENGTH_OFFSET,
602 sizeof(uint16_t), &prior_len);
603 total_remaining_space += NORFS_FLASH_SIZE(prior_len);
604 inode->reference_count++;
605 }
606 inode->location = header_loc;
607 total_remaining_space -= NORFS_FLASH_SIZE(len);
608 } else {
609 TRACEF("Error writing object. Status: %d\n", status);
610 }
611
612 /* If write error, or if fell off block, find new block. */
613 if (status < 0 || block_num(write_pointer) != block_num_to_write) {
614 initialize_next_block(&write_pointer);
615 }
616
617 flash_nor_end(NORFS_BANK);
618 return status;
619}
620
621static void remove_inode(struct norfs_inode *inode)
622{
623 if (!inode)
624 return;
625 list_delete(&inode->lnode);
626 free(inode);
627 inode = NULL;
628}
629
630/* Verifies objects, and copies to new block if it is the latest version. */
631static status_t collect_garbage_object(uint32_t *garbage_read_pointer,
632 uint32_t *garbage_write_pointer)
633{
634 struct norfs_inode *inode;
635 struct norfs_header header;
636 bool inode_found;
637 status_t status;
638 struct iovec iov[1];
639 uint32_t new_obj_loc;
640 uint32_t garb_obj_loc = *garbage_read_pointer;
641 status = load_and_verify_obj(garbage_read_pointer, &header);
642 if (status) {
643 TRACEF("Failed to load garbage_obj at %d\n", *garbage_read_pointer);
644 return status;
645 }
646 inode_found = get_inode(header.key, &inode);
647 if (inode_found) {
648 if (garb_obj_loc == inode->location) {
649 /* Object in garbage block is latest version. */
650 if (header.flags & NORFS_DELETED_MASK && (inode->reference_count == 1)) {
651 /* If last version of object, remove. */
652 remove_inode(inode);
653 total_remaining_space += NORFS_OBJ_OFFSET;
654 return NO_ERROR;
655 }
656 iov->iov_base = nvram_flash_pointer(garb_obj_loc + NORFS_OBJ_OFFSET);
657 iov->iov_len = header.len;
658 new_obj_loc = *garbage_write_pointer;
659 status = write_obj_iovec(iov, 1, garbage_write_pointer, header.key,
660 header.version + 1, header.flags);
661 if (status) {
662 TRACEF("Failed to copy garbage object. Status: %d\n", status);
663 return status;
664 }
665 inode->location = new_obj_loc;
666 return NO_ERROR;
667 } else {
668 inode->reference_count--;
669 return NO_ERROR;
670 }
671 }
672
673 if (is_deleted(*garbage_read_pointer)) {
674 return NO_ERROR;
675 }
676 return ERR_NOT_FOUND;
677}
678
679static status_t erase_block(uint8_t block)
680{
681 ssize_t bytes_erased;
682 ssize_t bytes_written;
683 status_t status;
684 uint32_t loc = block * FLASH_PAGE_SIZE;
685
686 /* Block must fall within range of actual number of NVRAM blocks. */
687 if (block > NORFS_NUM_BLOCKS) {
688 TRACEF("Invalid block number: %d.\n", block);
689 return ERR_INVALID_ARGS;
690 }
691 status = flash_nor_begin(NORFS_BANK);
692 if (status) {
693 flash_nor_end(NORFS_BANK);
694 return status;
695 }
696
697 bytes_erased = nvram_erase_pages(loc, FLASH_PAGE_SIZE);
698 if (bytes_erased != FLASH_PAGE_SIZE) {
699 flash_nor_end(NORFS_BANK);
700 TRACEF("Did not erase exactly one flash page. Something went \
701 wrong.\n");
702 return ERR_IO;
703 }
704
705 bytes_written = nvram_write(loc, sizeof(NORFS_BLOCK_HEADER),
706 &NORFS_BLOCK_HEADER);
707
708 flash_nor_end(NORFS_BANK);
709 if (bytes_written < 0) {
710 TRACEF("Error during nvram_write. Status: %d\n", bytes_written);
711 return bytes_written;
712 }
713 block_free[block] = true;
714 num_free_blocks++;
715
716 return NO_ERROR;
717}
718
719FRIEND_TEST status_t collect_block(uint32_t garbage_block,
720 uint32_t *garbage_write_ptr)
721{
722 status_t status;
723 uint32_t garbage_read_ptr = garbage_block * FLASH_PAGE_SIZE +
724 NORFS_BLOCK_HEADER_SIZE;
725
726 while (!(block_full(garbage_block, garbage_read_ptr))) {
727 status = collect_garbage_object(&garbage_read_ptr, garbage_write_ptr);
728 if (status) {
729 break;
730 }
731 }
732 return erase_block(garbage_block);
733}
734
735static status_t collect_garbage(void)
736{
737 status_t status;
738 uint8_t garbage_read_block = select_garbage_block(write_pointer);
739 status = collect_block(garbage_read_block, &write_pointer);
740
741 return status;
742}
743
744/*
745 * Load object into buffer and verify object's integrity via crc. ptr parameter
746 * is updated upon successful verification.
747 */
748static status_t load_and_verify_obj(uint32_t *ptr, struct norfs_header *header)
749{
750 uint16_t calculated_crc;
751 ssize_t bytes;
752 ssize_t total_bytes_read = 0;
753 bytes = read_header(*ptr, header);
754 unsigned char *obj_ptr;
755
756 if (bytes < 0) {
757 TRACEF("Reading header failed at location: %d. Err: %d.\n", *ptr, bytes);
758 return bytes;
759 }
760
761 total_bytes_read += bytes;
762
763 /* If object is longer than the remaining space in current block, fail.
764 * When attempting to verify unwritten file space, will fail here. */
765 if (block_num(*ptr + header->len - 1) != block_num(*ptr)) {
766 return ERR_IO;
767 }
768
769 calculated_crc = calculate_header_crc(header->key, header->version,
770 header->len, header->flags);
771
772
773 obj_ptr = nvram_flash_pointer(*ptr + total_bytes_read);
774 calculated_crc = update_crc16(calculated_crc, obj_ptr, header->len);
775
776 total_bytes_read += header->len;
777 if (calculated_crc != header->crc) {
778 TRACEF("CRC check failed. Calculated: 0x%x\tActual: 0x%x\n",
779 calculated_crc, header->crc);
780 return ERR_CRC_FAIL;
781 }
782 *ptr += total_bytes_read;
783 *ptr = ROUNDUP(*ptr, WORD_SIZE);
784 return NO_ERROR;
785}
786
787status_t read_block_verification(uint32_t *ptr)
788{
789 unsigned char block_header[sizeof(NORFS_BLOCK_HEADER) +
790 sizeof(NORFS_BLOCK_GC_STARTED_HEADER) +
791 sizeof(NORFS_BLOCK_GC_FINISHED_HEADER)];
792 int bytes_read = nvram_read(*ptr, sizeof(block_header),
793 block_header);
794
795 if (bytes_read < 0) {
796 TRACEF("Error reading while verifying block. Location: %d\n", *ptr);
797 return ERR_BAD_STATE;
798 }
799
800 for (uint8_t i = 0; i < sizeof(NORFS_BLOCK_HEADER); i++) {
801 if (block_header[i] != *(NORFS_BLOCK_HEADER + i)) {
802 return ERR_BAD_STATE;
803 }
804 }
805
806 bool valid_free_block = true;
807 for (uint8_t i = sizeof(NORFS_BLOCK_HEADER);
808 i < sizeof(NORFS_BLOCK_HEADER) +
809 sizeof(NORFS_BLOCK_GC_STARTED_HEADER) +
810 sizeof(NORFS_BLOCK_GC_FINISHED_HEADER);
811 i++) {
812 valid_free_block = valid_free_block && (block_header[i] == 0xFF);
813 }
814
815 /* This block has been successfully erased but has not been written to. */
816 if (valid_free_block) {
817 return ERR_NOT_CONFIGURED;
818 }
819
820 if (block_header[4] != NORFS_BLOCK_GC_STARTED_HEADER[0] ||
821 block_header[5] != NORFS_BLOCK_GC_STARTED_HEADER[1] ||
822 block_header[6] != NORFS_BLOCK_GC_FINISHED_HEADER[0] ||
823 block_header[7] != NORFS_BLOCK_GC_FINISHED_HEADER[1]) {
824 TRACEF("Garbage collection header invalid.\n");
825 return ERR_BAD_STATE;
826 }
827
828 *ptr += bytes_read;
829 return NO_ERROR;
830}
831
832static status_t mount_next_obj(void)
833{
834 uint16_t curr_obj_loc;
835 uint16_t inode_version, inode_len;
836 curr_obj_loc = write_pointer;
837 struct norfs_inode *inode;
838 struct norfs_header header;
839 status_t status;
840
841 status = load_and_verify_obj(&write_pointer, &header);
842 if (status) {
843 return status;
844 }
845 if (get_inode(header.key, &inode)) {
846 nvram_read(inode->location + NORFS_VERSION_OFFSET,
847 sizeof(inode_version), &inode_version);
848 if (VERSION_GREATER_THAN(header.version, inode_version)) {
849 /* This is a newer version of object than the version
850 currently being linked to in the inode. */
851 nvram_read(inode->location + NORFS_LENGTH_OFFSET,
852 sizeof(inode_len), &inode_len);
853 total_remaining_space += inode_len;
854 total_remaining_space -= header.len;
855 inode->location = curr_obj_loc;
856 }
857 inode->reference_count += 1;
858 } else {
859 /* Object not yet held in memory. Create new inode. */
860 inode = malloc(sizeof(struct norfs_inode));
861 inode->location = curr_obj_loc;
862
863 inode->reference_count = 1;
864
865 list_add_tail(&inode_list, &inode->lnode);
866 total_remaining_space -= NORFS_FLASH_SIZE(header.len);
867 }
868
869 return NO_ERROR;
870}
871
872/*
873 * Inodes for deleted objects need to be maintained during mounting, in case
874 * references show up in later blocks. However, these references need to be
875 * pruned prior to usage.
876 */
877static void purge_unreferenced_inodes(void)
878{
879 struct list_node *curr_lnode, *temp_node;
880 struct norfs_inode *curr_inode;
881 list_for_every_safe(&inode_list, curr_lnode, temp_node) {
882 curr_inode = containerof(curr_lnode, struct norfs_inode, lnode);
883 if (curr_inode->reference_count == 0) {
884 remove_inode(curr_inode);
885 }
886 }
887}
888
889status_t norfs_mount_fs(uint32_t offset)
890{
891 if (fs_mounted) {
892 TRACEF("Filesystem already mounted.\n");
893 return ERR_ALREADY_MOUNTED;
894 }
895 status_t status = 0;
896 norfs_nvram_offset = offset;
897
898 list_initialize(&inode_list);
899 flash_nor_begin(NORFS_BANK);
900 srand(current_time());
901
902 total_remaining_space = NORFS_AVAILABLE_SPACE;
903 num_free_blocks = 0;
904 TRACEF("Mounting NOR file system.\n");
905 for (uint8_t i = 0; i < NORFS_NUM_BLOCKS; i++) {
906 write_pointer = i * FLASH_PAGE_SIZE;
907 status = read_block_verification(&write_pointer);
908 if (status == ERR_BAD_STATE) {
909 erase_block(i);
910 continue;
911 } else if (status == ERR_NOT_CONFIGURED) {
912 /* Valid empty block. */
913 block_free[i] = true;
914 num_free_blocks++;
915 continue;
916 } else if (status != NO_ERROR) {
917 TRACEF("Unexpected status: %d. Exiting.\n", status);
918 flash_nor_end(NORFS_BANK);
919 return status;
920 }
921 block_free[i] = false;
922 while (!block_full(i, write_pointer)) {
923 status = mount_next_obj();
924 if (status)
925 break;
926 }
927 }
928
929 purge_unreferenced_inodes();
930
931 write_pointer = rand() % NORFS_NVRAM_SIZE;
932 status = initialize_next_block(&write_pointer);
933 if (status) {
934 TRACEF("Failed to find free block after mount.\n");
935 flash_nor_end(NORFS_BANK);
936 return status;
937 }
938
939 TRACEF("NOR filesystem successfully mounted.\n");
940 flash_nor_end(NORFS_BANK);
941 fs_mounted = true;
942 return NO_ERROR;
943}
944
945void norfs_unmount_fs(void)
946{
947 TRACEF("Unmounting NOR file system\n");
948 struct list_node *curr_lnode;
949 struct norfs_inode *curr_inode;
950 struct list_node *temp_node;
951
952 if (!fs_mounted) {
953 TRACEF("Filesystem not mounted.\n");
954 return;
955 }
956 if (!list_is_empty(&inode_list)) {
957 list_for_every_safe(&inode_list, curr_lnode, temp_node) {
958 if (curr_lnode) {
959 curr_inode = containerof(curr_lnode, struct norfs_inode, lnode);
960 remove_inode(curr_inode);
961 }
962 }
963 }
964 write_pointer = rand() % NORFS_NVRAM_SIZE;
965 total_remaining_space = NORFS_AVAILABLE_SPACE;
966 num_free_blocks = 0;
967 for (uint8_t i = 0; i < NORFS_NUM_BLOCKS; i++) {
968 block_free[i] = false;
969 }
970 fs_mounted = false;
971}
972
973void norfs_wipe_fs(void)
974{
975 norfs_unmount_fs();
976 flash_nor_begin(0);
977 nvram_erase_pages(0, 8 * FLASH_PAGE_SIZE);
978 flash_nor_end(0);
979 norfs_mount_fs(norfs_nvram_offset);
980}