1#!/usr/bin/env python3 2# Copyright 2024 The ChromiumOS Authors 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6"""Utility for vboot nvdata (nvram, nvstorage).""" 7 8import argparse 9 10 11NVDATA_SIZE = 16 12 13 14def get_crc8(data, size): 15 """Calculate CRC-8.""" 16 # CRC-8 ITU version, with x^8 + x^2 + x + 1 polynomial. 17 # Note that result will evaluate to zero for a buffer of all zeroes. 18 crc = 0 19 20 # Calculate CRC-8 directly. A table-based algorithm would be faster, 21 # but for only a few bytes it isn't worth the code size. 22 for i in range(size): 23 crc ^= data[i] << 8 24 for _ in range(8): 25 if crc & 0x8000: 26 crc ^= 0x1070 << 3 27 crc = (crc << 1) & 0xFFFFFFFF 28 29 return (crc >> 8) % 256 30 31 32def verify_crc8(entry): 33 """Verify CRC-8 of `entry`.""" 34 assert len(entry) == NVDATA_SIZE 35 expected_crc8 = get_crc8(entry, NVDATA_SIZE - 1) 36 crc8 = entry[NVDATA_SIZE - 1] 37 return crc8 == expected_crc8 38 39 40def process_entry(entry, offset): 41 """Process an nvdata entry.""" 42 data = " ".join(f"{x:02x}" for x in entry) 43 if all(x == 0xFF for x in entry): 44 result = "EMPTY" 45 else: 46 is_valid = verify_crc8(entry) 47 result = "VALID" if is_valid else "CRC ERROR" 48 print(f"{offset:08x} {data} {result}") 49 50 51def dump(nvdata_file): 52 """Show the content of `nvdata_file`.""" 53 with open(nvdata_file, "rb") as f: 54 nvdata = f.read() 55 assert len(nvdata) % NVDATA_SIZE == 0 56 for i in range(len(nvdata) // NVDATA_SIZE): 57 offset = i * NVDATA_SIZE 58 entry = nvdata[offset : offset + NVDATA_SIZE] 59 process_entry(entry, offset) 60 61 62def verify_hex_entry(hex_string): 63 """Verify an nvdata entry.""" 64 values = [] 65 for s in hex_string.split(): 66 s = s.removeprefix("0x") 67 for i in range(0, len(s), 2): 68 value = int(s[i : i + 2], 16) 69 values.append(value) 70 if len(values) != NVDATA_SIZE: 71 raise ValueError( 72 f"Hex string should contain {NVDATA_SIZE} bytes" 73 f", {len(values)} found" 74 ) 75 76 entry = bytes(values) 77 is_valid = verify_crc8(entry) 78 print("VALID" if is_valid else "INVALID") 79 80 81def main(): 82 parser = argparse.ArgumentParser() 83 group = parser.add_mutually_exclusive_group() 84 group.add_argument("-f", "--file", help="RW_NVRAM file to dump") 85 group.add_argument( 86 "--hex", 87 help=( 88 f"Hex string of {NVDATA_SIZE} bytes to verify (for example" 89 " '50 40 00 00 00 02 00 02 00 fe ff 00 00 ff ff 60')" 90 ), 91 ) 92 93 args = parser.parse_args() 94 if args.file: 95 dump(args.file) 96 elif args.hex: 97 verify_hex_entry(args.hex) 98 else: 99 parser.print_help() 100 101 102if __name__ == "__main__": 103 main() 104