| #!/usr/bin/env python3 |
| # Copyright 2024 The ChromiumOS Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Utility for vboot nvdata (nvram, nvstorage).""" |
| |
| import argparse |
| |
| |
| NVDATA_SIZE = 16 |
| |
| |
| def get_crc8(data, size): |
| """Calculate CRC-8.""" |
| # CRC-8 ITU version, with x^8 + x^2 + x + 1 polynomial. |
| # Note that result will evaluate to zero for a buffer of all zeroes. |
| crc = 0 |
| |
| # Calculate CRC-8 directly. A table-based algorithm would be faster, |
| # but for only a few bytes it isn't worth the code size. |
| for i in range(size): |
| crc ^= data[i] << 8 |
| for _ in range(8): |
| if crc & 0x8000: |
| crc ^= 0x1070 << 3 |
| crc = (crc << 1) & 0xFFFFFFFF |
| |
| return (crc >> 8) % 256 |
| |
| |
| def verify_crc8(entry): |
| """Verify CRC-8 of `entry`.""" |
| assert len(entry) == NVDATA_SIZE |
| expected_crc8 = get_crc8(entry, NVDATA_SIZE - 1) |
| crc8 = entry[NVDATA_SIZE - 1] |
| return crc8 == expected_crc8 |
| |
| |
| def process_entry(entry, offset): |
| """Process an nvdata entry.""" |
| data = " ".join(f"{x:02x}" for x in entry) |
| if all(x == 0xFF for x in entry): |
| result = "EMPTY" |
| else: |
| is_valid = verify_crc8(entry) |
| result = "VALID" if is_valid else "CRC ERROR" |
| print(f"{offset:08x} {data} {result}") |
| |
| |
| def dump(nvdata_file): |
| """Show the content of `nvdata_file`.""" |
| with open(nvdata_file, "rb") as f: |
| nvdata = f.read() |
| assert len(nvdata) % NVDATA_SIZE == 0 |
| for i in range(len(nvdata) // NVDATA_SIZE): |
| offset = i * NVDATA_SIZE |
| entry = nvdata[offset : offset + NVDATA_SIZE] |
| process_entry(entry, offset) |
| |
| |
| def verify_hex_entry(hex_string): |
| """Verify an nvdata entry.""" |
| values = [] |
| for s in hex_string.split(): |
| s = s.removeprefix("0x") |
| for i in range(0, len(s), 2): |
| value = int(s[i : i + 2], 16) |
| values.append(value) |
| if len(values) != NVDATA_SIZE: |
| raise ValueError( |
| f"Hex string should contain {NVDATA_SIZE} bytes" |
| f", {len(values)} found" |
| ) |
| |
| entry = bytes(values) |
| is_valid = verify_crc8(entry) |
| print("VALID" if is_valid else "INVALID") |
| |
| |
| def main(): |
| parser = argparse.ArgumentParser() |
| group = parser.add_mutually_exclusive_group() |
| group.add_argument("-f", "--file", help="RW_NVRAM file to dump") |
| group.add_argument( |
| "--hex", |
| help=( |
| f"Hex string of {NVDATA_SIZE} bytes to verify (for example" |
| " '50 40 00 00 00 02 00 02 00 fe ff 00 00 ff ff 60')" |
| ), |
| ) |
| |
| args = parser.parse_args() |
| if args.file: |
| dump(args.file) |
| elif args.hex: |
| verify_hex_entry(args.hex) |
| else: |
| parser.print_help() |
| |
| |
| if __name__ == "__main__": |
| main() |