xref: /aosp_15_r20/external/vboot_reference/utility/vbnv_util.py (revision 8617a60d3594060b7ecbd21bc622a7c14f3cf2bc)
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