From 55a5645ad9b24f0f633b3f7005e8a0c41bf5fcfe Mon Sep 17 00:00:00 2001 From: Wiktor Grzywacz Date: Thu, 23 Jan 2025 18:09:46 +0100 Subject: [PATCH 1/2] osfv_cli/src/osfv/libs/localflash.py: added --- osfv_cli/src/osfv/libs/localflash.py | 134 +++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 osfv_cli/src/osfv/libs/localflash.py diff --git a/osfv_cli/src/osfv/libs/localflash.py b/osfv_cli/src/osfv/libs/localflash.py new file mode 100644 index 0000000..e4c4968 --- /dev/null +++ b/osfv_cli/src/osfv/libs/localflash.py @@ -0,0 +1,134 @@ +import os +import time +import subprocess +import sys + + +class LocalFlashError(Exception): + pass + + +class LocalFlasher: + """ + A local flasher: + - toggles GPIO under /sys/class/gpio/gpioX/value + - calls flashrom with local programmer + """ + + # same constants as in flash.sh + GPIO_SPI_VOLTAGE = 517 # 0 => 1.8V, 1 => 3.3V + GPIO_SPI_LINES = 516 # 1 => lines on + GPIO_SPI_VCC = 518 # 1 => VCC on + + FLASHROM_PROGRAMMER = "linux_spi:dev=/dev/spidev1.0,spispeed=16000" + + def __init__(self, voltage="1.8V", flashrom_params=""): + """ + :param voltage: "1.8V" or "3.3V" + :param flashrom_params: extra flashrom args (optional) + """ + self.voltage = voltage + self.flashrom_params = flashrom_params + + def flashrom_cmd(self, extra_args): + """ + Runs flashrom with the chosen programmer + any extra args + """ + cmd = [ + "flashrom", + "-p", + f"{self.FLASHROM_PROGRAMMER}", + ] + if self.flashrom_params: + cmd.extend(self.flashrom_params.split()) + cmd.extend(extra_args.split()) + + print(f"[localflash] Running: {' '.join(cmd)}") + try: + subprocess.check_call(cmd) + except subprocess.CalledProcessError as e: + raise LocalFlashError(f"flashrom failed with code {e.returncode}") + + def set_gpio_value(self, gpio_no, value_str): + """ + Writes '0' or '1' to /sys/class/gpio/gpioX/value + """ + path = f"/sys/class/gpio/gpio{gpio_no}/value" + if not os.path.exists(path): + raise LocalFlashError(f"GPIO {gpio_no} not exported or not found at {path}") + + # Convert '0'/'1' from the given 'value_str' if needed + if value_str == "0" or value_str == "1": + val = value_str + else: + raise LocalFlashError(f"Bad gpio value: {value_str}") + + print(f"[localflash] echo {val} > {path}") + with open(path, "w") as f: + f.write(val) + + def spi_on(self): + """ + Equivalent to flash.sh's `spiON()`: + - sets voltage (gpio 517) to 0 => 1.8V or 1 => 3.3V + - sets gpio 516 => 1 (SPI lines on) + - sets gpio 518 => 1 (VCC on) + """ + if self.voltage == "3.3V": + print("[localflash] Setting SPI VCC to 3.3V") + self.set_gpio_value(self.GPIO_SPI_VOLTAGE, "1") + else: + print("[localflash] Setting SPI VCC to 1.8V") + self.set_gpio_value(self.GPIO_SPI_VOLTAGE, "0") + + time.sleep(1) + print("[localflash] SPI lines on") + self.set_gpio_value(self.GPIO_SPI_LINES, "1") + + time.sleep(1) + print("[localflash] SPI Vcc on") + self.set_gpio_value(self.GPIO_SPI_VCC, "1") + + time.sleep(2) + + def spi_off(self): + """ + Equivalent to flash.sh's `spiOFF()`. + """ + print("[localflash] SPI Vcc off") + self.set_gpio_value(self.GPIO_SPI_VCC, "0") + print("[localflash] SPI lines off") + self.set_gpio_value(self.GPIO_SPI_LINES, "0") + # set voltage to 0 => 1.8 again, or no + self.set_gpio_value(self.GPIO_SPI_VOLTAGE, "0") + + def cmd_probe(self): + # flashrom -p ... + self.spi_on() + print("[localflash] Probing flash with flashrom") + self.flashrom_cmd("") + self.spi_off() + + def cmd_read(self, out_file): + # flashrom -p -r out_file + self.spi_on() + print(f"[localflash] Reading flash -> {out_file}") + self.flashrom_cmd(f"-r {out_file} -V") + self.spi_off() + + def cmd_write(self, in_file): + # flashrom -p -w in_file + if not os.path.isfile(in_file): + raise LocalFlashError(f"Input file {in_file} does not exist!") + self.spi_on() + print(f"[localflash] Writing {in_file} to flash...") + self.flashrom_cmd(f"-w {in_file}") + self.spi_off() + + def cmd_erase(self): + # flashrom -p -E + self.spi_on() + print("[localflash] Erasing entire flash...") + self.flashrom_cmd("-E") + self.spi_off() + From be8e99ce66c2ef1136778cf3779e92eac0e8d525 Mon Sep 17 00:00:00 2001 From: Wiktor Grzywacz Date: Thu, 23 Jan 2025 18:11:49 +0100 Subject: [PATCH 2/2] osfv_cli/src/osfv/cli/cli.py: modify to include localflash --- osfv_cli/src/osfv/cli/cli.py | 65 ++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/osfv_cli/src/osfv/cli/cli.py b/osfv_cli/src/osfv/cli/cli.py index 7a61e9e..66d46cf 100755 --- a/osfv_cli/src/osfv/cli/cli.py +++ b/osfv_cli/src/osfv/cli/cli.py @@ -8,6 +8,7 @@ import osfv.libs.utils as utils import pexpect import requests +import sys from osfv.libs.rte import RTE from osfv.libs.snipeit_api import SnipeIT from osfv.libs.sonoff_api import SonoffDevice @@ -857,6 +858,31 @@ def main(): "erase", help="Erase DUT flash with flashrom" ) + local_parser = flash_subparsers.add_parser( + "local", help="Locally flash the DUT (like flash.sh) using local GPIO toggling & flashrom" + ) + local_subparsers = local_parser.add_subparsers(dest="local_cmd", help="Local flash commands") + + local_probe_parser = local_subparsers.add_parser("probe", help="Probe flash locally") + + local_read_parser = local_subparsers.add_parser("read", help="Read flash locally") + local_read_parser.add_argument( + "--rom", + type=str, + default="read.rom", + help="Output file for reading flash content (default: read.rom)", + ) + + local_write_parser = local_subparsers.add_parser("write", help="Write flash locally") + local_write_parser.add_argument( + "--rom", + type=str, + default="write.rom", + help="Firmware file to write to flash (default: write.rom)", + ) + + local_erase_parser = local_subparsers.add_parser("erase", help="Erase flash locally") + args = parser.parse_args() snipeit_api = SnipeIT() @@ -981,6 +1007,45 @@ def main(): flash_write(rte, args) elif args.flash_cmd == "erase": flash_erase(rte, args) + elif args.flash_cmd == "local": + # We have to see which subcommand was chosen: probe/read/write/erase + from osfv.libs.localflash import LocalFlasher, LocalFlashError + + # User sets voltage, flashrom params, etc. + local_flasher = LocalFlasher(voltage="1.8V", flashrom_params="") + + # check which local_cmd subcommand was used + if args.local_cmd == "probe": + try: + local_flasher.cmd_probe() + except LocalFlashError as e: + print(f"[ERROR] {e}") + sys.exit(1) + + elif args.local_cmd == "read": + try: + local_flasher.cmd_read(args.rom) + except LocalFlashError as e: + print(f"[ERROR] {e}") + sys.exit(1) + + elif args.local_cmd == "write": + try: + local_flasher.cmd_write(args.rom) + except LocalFlashError as e: + print(f"[ERROR] {e}") + sys.exit(1) + + elif args.local_cmd == "erase": + try: + local_flasher.cmd_erase() + except LocalFlashError as e: + print(f"[ERROR] {e}") + sys.exit(1) + + else: + print("No local flash subcommand chosen. See --help.") + sys.exit(1) if not args.skip_snipeit: if already_checked_out: