forked from espressif/esp-idf
feat(nvs): NVS tool add key consistency check
This commit is contained in:
@@ -18,7 +18,7 @@ def integrity_check(nvs_partition: NVS_Partition, nvs_log: NVS_Logger) -> None:
|
|||||||
if len(nvs_partition.pages) < 3:
|
if len(nvs_partition.pages) < 3:
|
||||||
nvs_log.info(
|
nvs_log.info(
|
||||||
nvs_log.yellow(
|
nvs_log.yellow(
|
||||||
'Partition has to have at least 3 pages to function properly!'
|
'NVS Partition must contain 3 pages (sectors) at least to function properly!'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -26,10 +26,11 @@ def integrity_check(nvs_partition: NVS_Partition, nvs_log: NVS_Logger) -> None:
|
|||||||
if not any(page.header['status'] == 'Empty' for page in nvs_partition.pages):
|
if not any(page.header['status'] == 'Empty' for page in nvs_partition.pages):
|
||||||
nvs_log.info(
|
nvs_log.info(
|
||||||
nvs_log.red(
|
nvs_log.red(
|
||||||
'There are no free (empty) pages in the partition, there needs to be at least one free page!'
|
'''No free (empty) page found in the NVS partition,
|
||||||
|
at least one free page is required for proper function!'''
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
nvs_log.info(nvs_log.red('Has the NVS partition been truncated?\n'))
|
nvs_log.info(nvs_log.red('NVS partition possibly truncated?\n'))
|
||||||
|
|
||||||
for page in nvs_partition.pages:
|
for page in nvs_partition.pages:
|
||||||
# page: NVS_Page
|
# page: NVS_Page
|
||||||
@@ -42,12 +43,12 @@ def integrity_check(nvs_partition: NVS_Partition, nvs_log: NVS_Logger) -> None:
|
|||||||
if page.raw_entry_state_bitmap != bytearray({0xFF}) * nvs_const.entry_size:
|
if page.raw_entry_state_bitmap != bytearray({0xFF}) * nvs_const.entry_size:
|
||||||
nvs_log.info(
|
nvs_log.info(
|
||||||
nvs_log.red(
|
nvs_log.red(
|
||||||
'Page is reported as empty but entry state bitmap is not empty!'
|
'The page is reported as Empty but its entry state bitmap is not empty!'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if any([not e.is_empty for e in page.entries]):
|
if any([not e.is_empty for e in page.entries]):
|
||||||
nvs_log.info(
|
nvs_log.info(
|
||||||
nvs_log.red('Page is reported as empty but there are data written!')
|
nvs_log.red('The page is reported as Empty but there are data written!')
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# Check page header CRC32
|
# Check page header CRC32
|
||||||
@@ -65,22 +66,35 @@ def integrity_check(nvs_partition: NVS_Partition, nvs_log: NVS_Logger) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Check all entries
|
# Check all entries
|
||||||
|
seen_written_entires: Dict[str, list[NVS_Entry]] = {}
|
||||||
for entry in page.entries:
|
for entry in page.entries:
|
||||||
# entry: NVS_Entry
|
# entry: NVS_Entry
|
||||||
|
|
||||||
# Entry state check
|
# Entries stored in 'page.entries' are primitive data types, blob indexes or string/blob data
|
||||||
|
|
||||||
|
# Variable length values themselves occupy whole 32 bytes (therefore metadata values are meaningless)
|
||||||
|
# and are stored in as entries inside string/blob data entry 'entry.children' list
|
||||||
|
|
||||||
|
# Duplicate entry check (1) - same key, different index - find duplicates
|
||||||
|
if entry.state == 'Written':
|
||||||
|
if entry.key in seen_written_entires:
|
||||||
|
seen_written_entires[entry.key].append(entry)
|
||||||
|
else:
|
||||||
|
seen_written_entires[entry.key] = [entry]
|
||||||
|
|
||||||
|
# Entry state check - doesn't check variable length values (metadata such as state are meaningless as all 32 bytes are pure data)
|
||||||
if entry.is_empty:
|
if entry.is_empty:
|
||||||
if entry.state == 'Written':
|
if entry.state == 'Written':
|
||||||
nvs_log.info(
|
nvs_log.info(
|
||||||
nvs_log.red(
|
nvs_log.red(
|
||||||
f' Entry #{entry.index:03d} is reported as written but is empty!'
|
f' Entry #{entry.index:03d} is reported as Written but it is empty!'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
elif entry.state == 'Erased':
|
elif entry.state == 'Erased':
|
||||||
nvs_log.info(
|
nvs_log.info(
|
||||||
nvs_log.yellow(
|
nvs_log.yellow(
|
||||||
f' Entry #{entry.index:03d} is reported as erased but is empty!'
|
f' Entry #{entry.index:03d} is reported as Erased but it is empty! (Only entries reported as Empty should be empty)'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -103,17 +117,16 @@ def integrity_check(nvs_partition: NVS_Partition, nvs_log: NVS_Logger) -> None:
|
|||||||
# Entry children CRC32 check
|
# Entry children CRC32 check
|
||||||
if (
|
if (
|
||||||
entry.metadata['span'] > 1
|
entry.metadata['span'] > 1
|
||||||
and entry.metadata['crc']['data_original']
|
and (entry.metadata['crc']['data_original'] != entry.metadata['crc']['data_computed'])
|
||||||
!= entry.metadata['crc']['data_computed']
|
|
||||||
):
|
):
|
||||||
nvs_log.info(
|
nvs_log.info(
|
||||||
nvs_log.red(
|
nvs_log.red(
|
||||||
f' Entry #{entry.index:03d} {entry.key} data has wrong CRC32!'
|
f' Entry #{entry.index:03d} {entry.key} data (string, blob) has wrong CRC32!'
|
||||||
),
|
),
|
||||||
f'Written:',
|
f'Written:',
|
||||||
nvs_log.red(f'{entry.metadata["crc"]["original"]:x}'),
|
nvs_log.red(f'{entry.metadata["crc"]["data_original"]:x}'),
|
||||||
f'Generated:',
|
f'Generated:',
|
||||||
nvs_log.green(f'{entry.metadata["crc"]["computed"]:x}'),
|
nvs_log.green(f'{entry.metadata["crc"]["data_computed"]:x}'),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Entry type check
|
# Entry type check
|
||||||
@@ -161,6 +174,23 @@ def integrity_check(nvs_partition: NVS_Partition, nvs_log: NVS_Logger) -> None:
|
|||||||
else:
|
else:
|
||||||
used_namespaces[entry.metadata['namespace']] = None
|
used_namespaces[entry.metadata['namespace']] = None
|
||||||
|
|
||||||
|
# Duplicate entry check (2) - same key, different index - print duplicates
|
||||||
|
duplicate_entries_list = [seen_written_entires[key] for key in seen_written_entires if len(seen_written_entires[key]) > 1]
|
||||||
|
for duplicate_entries in duplicate_entries_list:
|
||||||
|
# duplicate_entries: list[NVS_Entry]
|
||||||
|
nvs_log.info(
|
||||||
|
nvs_log.red(
|
||||||
|
f'''Entry key {duplicate_entries[0].key} on page no. {page.header["page_index"]}
|
||||||
|
with status {page.header["status"]} is used by the following entries:'''
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for entry in duplicate_entries:
|
||||||
|
nvs_log.info(
|
||||||
|
nvs_log.red(
|
||||||
|
f'Entry #{entry.index:03d} {entry.key} is a duplicate!'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
nvs_log.info()
|
nvs_log.info()
|
||||||
|
|
||||||
# Blob checks
|
# Blob checks
|
||||||
|
@@ -110,7 +110,7 @@ def main() -> None:
|
|||||||
|
|
||||||
if args.integrity_check:
|
if args.integrity_check:
|
||||||
nvs_log.info()
|
nvs_log.info()
|
||||||
nvs_check.integrity_check(nvs, nvs_logger)
|
nvs_check.integrity_check(nvs, nvs_log)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
Reference in New Issue
Block a user