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