From 2c3b0672faceb449abf048b1450173238e103c5f Mon Sep 17 00:00:00 2001 From: Li Hua Qian Date: Fri, 15 Dec 2023 10:58:00 +0800 Subject: [PATCH] iot2050-eio-manager: Add module firmware update Signed-off-by: Li Hua Qian --- .../files/gRPC/EIOManager/iot2050-eio.proto | 3 +- .../files/gRPC/EIOManager/iot2050_eio_pb2.py | 28 +++---- .../files/gRPC/EIOManager/iot2050_eio_pb2.pyi | 8 +- .../files/iot2050-eio-cli.py | 74 ++++++++++++++----- .../files/iot2050-eio-service.py | 2 +- .../files/iot2050_eio_fwu.py | 51 +++++++++---- .../files/iot2050_eio_global.py | 4 + 7 files changed, 116 insertions(+), 54 deletions(-) diff --git a/recipes-app/iot2050-eio-manager/files/gRPC/EIOManager/iot2050-eio.proto b/recipes-app/iot2050-eio-manager/files/gRPC/EIOManager/iot2050-eio.proto index d79d5e4c8..ff5a8ecc7 100644 --- a/recipes-app/iot2050-eio-manager/files/gRPC/EIOManager/iot2050-eio.proto +++ b/recipes-app/iot2050-eio-manager/files/gRPC/EIOManager/iot2050-eio.proto @@ -80,7 +80,8 @@ message SyncTimeReply { */ message UpdateFirmwareRequest { int32 entity = 1; - string firmware = 2; + bytes firmware = 2; + string firmware_type = 3; } /* UpdateFirmwareReply diff --git a/recipes-app/iot2050-eio-manager/files/gRPC/EIOManager/iot2050_eio_pb2.py b/recipes-app/iot2050-eio-manager/files/gRPC/EIOManager/iot2050_eio_pb2.py index 399af47c2..dfb339f48 100644 --- a/recipes-app/iot2050-eio-manager/files/gRPC/EIOManager/iot2050_eio_pb2.py +++ b/recipes-app/iot2050-eio-manager/files/gRPC/EIOManager/iot2050_eio_pb2.py @@ -13,7 +13,7 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!gRPC/EIOManager/iot2050-eio.proto\x12\neiomanager\"\"\n\rDeployRequest\x12\x11\n\tyaml_data\x18\x01 \x01(\t\".\n\x0b\x44\x65ployReply\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t\"\x1f\n\x0fRetrieveRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\"C\n\rRetrieveReply\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x11\n\tyaml_data\x18\x03 \x01(\t\"-\n\x0fSyncTimeRequest\x12\x11\n\x04time\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x07\n\x05_time\"0\n\rSyncTimeReply\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t\"9\n\x15UpdateFirmwareRequest\x12\x0e\n\x06\x65ntity\x18\x01 \x01(\x05\x12\x10\n\x08\x66irmware\x18\x02 \x01(\t\"6\n\x13UpdateFirmwareReply\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t\"!\n\x0f\x43heckFWURequest\x12\x0e\n\x06\x65ntity\x18\x01 \x01(\x05\"0\n\rCheckFWUReply\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t\"\x15\n\x13ReadEIOEventRequest\"C\n\x11ReadEIOEventReply\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\r\n\x05\x65vent\x18\x03 \x01(\t2\xc8\x03\n\nEIOManager\x12>\n\x06\x44\x65ploy\x12\x19.eiomanager.DeployRequest\x1a\x17.eiomanager.DeployReply\"\x00\x12\x44\n\x08Retrieve\x12\x1b.eiomanager.RetrieveRequest\x1a\x19.eiomanager.RetrieveReply\"\x00\x12\x44\n\x08SyncTime\x12\x1b.eiomanager.SyncTimeRequest\x1a\x19.eiomanager.SyncTimeReply\"\x00\x12V\n\x0eUpdateFirmware\x12!.eiomanager.UpdateFirmwareRequest\x1a\x1f.eiomanager.UpdateFirmwareReply\"\x00\x12\x44\n\x08\x43heckFWU\x12\x1b.eiomanager.CheckFWURequest\x1a\x19.eiomanager.CheckFWUReply\"\x00\x12P\n\x0cReadEIOEvent\x12\x1f.eiomanager.ReadEIOEventRequest\x1a\x1d.eiomanager.ReadEIOEventReply\"\x00\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!gRPC/EIOManager/iot2050-eio.proto\x12\neiomanager\"\"\n\rDeployRequest\x12\x11\n\tyaml_data\x18\x01 \x01(\t\".\n\x0b\x44\x65ployReply\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t\"\x1f\n\x0fRetrieveRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\"C\n\rRetrieveReply\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x11\n\tyaml_data\x18\x03 \x01(\t\"-\n\x0fSyncTimeRequest\x12\x11\n\x04time\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x07\n\x05_time\"0\n\rSyncTimeReply\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t\"P\n\x15UpdateFirmwareRequest\x12\x0e\n\x06\x65ntity\x18\x01 \x01(\x05\x12\x10\n\x08\x66irmware\x18\x02 \x01(\x0c\x12\x15\n\rfirmware_type\x18\x03 \x01(\t\"6\n\x13UpdateFirmwareReply\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t\"!\n\x0f\x43heckFWURequest\x12\x0e\n\x06\x65ntity\x18\x01 \x01(\x05\"0\n\rCheckFWUReply\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t\"\x15\n\x13ReadEIOEventRequest\"C\n\x11ReadEIOEventReply\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\r\n\x05\x65vent\x18\x03 \x01(\t2\xc8\x03\n\nEIOManager\x12>\n\x06\x44\x65ploy\x12\x19.eiomanager.DeployRequest\x1a\x17.eiomanager.DeployReply\"\x00\x12\x44\n\x08Retrieve\x12\x1b.eiomanager.RetrieveRequest\x1a\x19.eiomanager.RetrieveReply\"\x00\x12\x44\n\x08SyncTime\x12\x1b.eiomanager.SyncTimeRequest\x1a\x19.eiomanager.SyncTimeReply\"\x00\x12V\n\x0eUpdateFirmware\x12!.eiomanager.UpdateFirmwareRequest\x1a\x1f.eiomanager.UpdateFirmwareReply\"\x00\x12\x44\n\x08\x43heckFWU\x12\x1b.eiomanager.CheckFWURequest\x1a\x19.eiomanager.CheckFWUReply\"\x00\x12P\n\x0cReadEIOEvent\x12\x1f.eiomanager.ReadEIOEventRequest\x1a\x1d.eiomanager.ReadEIOEventReply\"\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -33,17 +33,17 @@ _globals['_SYNCTIMEREPLY']._serialized_start=282 _globals['_SYNCTIMEREPLY']._serialized_end=330 _globals['_UPDATEFIRMWAREREQUEST']._serialized_start=332 - _globals['_UPDATEFIRMWAREREQUEST']._serialized_end=389 - _globals['_UPDATEFIRMWAREREPLY']._serialized_start=391 - _globals['_UPDATEFIRMWAREREPLY']._serialized_end=445 - _globals['_CHECKFWUREQUEST']._serialized_start=447 - _globals['_CHECKFWUREQUEST']._serialized_end=480 - _globals['_CHECKFWUREPLY']._serialized_start=482 - _globals['_CHECKFWUREPLY']._serialized_end=530 - _globals['_READEIOEVENTREQUEST']._serialized_start=532 - _globals['_READEIOEVENTREQUEST']._serialized_end=553 - _globals['_READEIOEVENTREPLY']._serialized_start=555 - _globals['_READEIOEVENTREPLY']._serialized_end=622 - _globals['_EIOMANAGER']._serialized_start=625 - _globals['_EIOMANAGER']._serialized_end=1081 + _globals['_UPDATEFIRMWAREREQUEST']._serialized_end=412 + _globals['_UPDATEFIRMWAREREPLY']._serialized_start=414 + _globals['_UPDATEFIRMWAREREPLY']._serialized_end=468 + _globals['_CHECKFWUREQUEST']._serialized_start=470 + _globals['_CHECKFWUREQUEST']._serialized_end=503 + _globals['_CHECKFWUREPLY']._serialized_start=505 + _globals['_CHECKFWUREPLY']._serialized_end=553 + _globals['_READEIOEVENTREQUEST']._serialized_start=555 + _globals['_READEIOEVENTREQUEST']._serialized_end=576 + _globals['_READEIOEVENTREPLY']._serialized_start=578 + _globals['_READEIOEVENTREPLY']._serialized_end=645 + _globals['_EIOMANAGER']._serialized_start=648 + _globals['_EIOMANAGER']._serialized_end=1104 # @@protoc_insertion_point(module_scope) diff --git a/recipes-app/iot2050-eio-manager/files/gRPC/EIOManager/iot2050_eio_pb2.pyi b/recipes-app/iot2050-eio-manager/files/gRPC/EIOManager/iot2050_eio_pb2.pyi index 08338e3a0..a8b6cc763 100644 --- a/recipes-app/iot2050-eio-manager/files/gRPC/EIOManager/iot2050_eio_pb2.pyi +++ b/recipes-app/iot2050-eio-manager/files/gRPC/EIOManager/iot2050_eio_pb2.pyi @@ -49,12 +49,14 @@ class SyncTimeReply(_message.Message): def __init__(self, status: _Optional[int] = ..., message: _Optional[str] = ...) -> None: ... class UpdateFirmwareRequest(_message.Message): - __slots__ = ["entity", "firmware"] + __slots__ = ["entity", "firmware", "firmware_type"] ENTITY_FIELD_NUMBER: _ClassVar[int] FIRMWARE_FIELD_NUMBER: _ClassVar[int] + FIRMWARE_TYPE_FIELD_NUMBER: _ClassVar[int] entity: int - firmware: str - def __init__(self, entity: _Optional[int] = ..., firmware: _Optional[str] = ...) -> None: ... + firmware: bytes + firmware_type: str + def __init__(self, entity: _Optional[int] = ..., firmware: _Optional[bytes] = ..., firmware_type: _Optional[str] = ...) -> None: ... class UpdateFirmwareReply(_message.Message): __slots__ = ["status", "message"] diff --git a/recipes-app/iot2050-eio-manager/files/iot2050-eio-cli.py b/recipes-app/iot2050-eio-manager/files/iot2050-eio-cli.py index ffb9340bc..a9839dcd3 100755 --- a/recipes-app/iot2050-eio-manager/files/iot2050-eio-cli.py +++ b/recipes-app/iot2050-eio-manager/files/iot2050-eio-cli.py @@ -56,7 +56,7 @@ def show_progress_bar(task, interval): time.sleep(interval) -def do_update_firmware(firmware): +def do_update_firmware(firmware, firmware_type): with grpc.insecure_channel(iot2050_eio_api_server) as channel: stub = EIOManagerStub(channel) print("===================================================") @@ -65,7 +65,8 @@ def do_update_firmware(firmware): with ThreadPoolExecutor(max_workers=1) as pool: future = pool.submit( stub.UpdateFirmware, - UpdateFirmwareRequest(firmware=firmware) + UpdateFirmwareRequest(firmware=firmware, + firmware_type=firmware_type) ) show_progress_bar(future, 0.3) print() @@ -86,8 +87,10 @@ def do_update_firmware(firmware): 2. %(prog)s config retrieve config.yaml Retrieve the config from Extended IO Controller and store into config.yaml 3. %(prog)s fwu controller [firmware.bin] - update firmware for Extended IO Controller, using firmware.bin if provided, + Update firmware for Extended IO Controller, using firmware.bin if provided, otherwise using the stock firmware file. + 3. %(prog)s fwu module -s 1 -fwa fwa.bin + Update firmware a of the module in slot 1 Example Configuration File: @@ -112,11 +115,27 @@ def do_update_firmware(firmware): help='Config file in yaml format') fwu_parser = subparsers.add_parser("fwu", help='firmware update help') - fwu_parser.add_argument('action', metavar='ACTION', - choices=['controller', 'module'], - help='Specify the firmware update type') - fwu_parser.add_argument('firmware', nargs='?', metavar='FIRMWARE', type=str, - help='Firmware file') + fwu_subparsers = fwu_parser.add_subparsers(help='sub-command help', + title='fwu-commands', + dest="fwu_command") + controller_parser = fwu_subparsers.add_parser("controller", help='controller help') + controller_parser.add_argument('firmware', nargs='?', metavar='FIRMWARE', + type=argparse.FileType('rb'), + help='Firmware file') + module_parser = fwu_subparsers.add_parser("module", help='module help') + module_parser.add_argument('firmware', nargs='?', metavar='FIRMWARE', + type=argparse.FileType('rb'), + help='Firmware file') + module_parser.add_argument('-s', '--slot', + help='Specify the slot number', + nargs=1) + group = module_parser.add_mutually_exclusive_group() + group.add_argument('-fwa', '--firmware-a', + help='Update firmware a of module', + action='store_true') + group.add_argument('-fwb', '--firmware-b', + help='Update firmware b of module', + action='store_true') args = parser.parse_args() @@ -142,23 +161,38 @@ def do_update_firmware(firmware): with open(args.config, 'w', encoding='ascii') as f_config: f_config.write(config_returned) elif args.command == 'fwu': - if args.firmware: - firmware = args.firmware - else: - firmware = EIO_FWU_MAP3_FW_BIN - status, message = do_fwu_check() - if status == 0: - # no need to update! - print(message) - sys.exit(0) + if args.fwu_command == 'controller': + if args.firmware: + firmware = args.firmware.read() + else: + firmware = EIO_FWU_MAP3_FW_BIN + status, message = do_fwu_check() + if status == 0: + # no need to update! + print(message) + sys.exit(0) + + response = do_update_firmware(firmware, "map3") + elif args.fwu_command == 'module': + if not args.slot or \ + (not args.firmware_a and not args.firmware_b): + module_parser.print_help(sys.stderr) + sys.exit(1) + if args.firmware_a: + firmware_type = f'slot{args.slot[0]}/fwa' + elif args.firmware_b: + firmware_type = f'slot{args.slot[0]}/fwb' + firmware = args.firmware.read() + response = do_update_firmware(firmware, firmware_type) - response = do_update_firmware(firmware) if response.status: print(f"ERROR: {response.message}") - print("EIO firmware update failed, please try again!") + print(f"EIO {args.fwu_command} firmware update failed, please try again!") sys.exit(1) - print("EIO firmware update completed. Please reboot the device.") + print(f"EIO {args.fwu_command} firmware update completed." + f" Please reboot the {args.fwu_command}!") else: parser.print_help(sys.stderr) sys.exit(1) + diff --git a/recipes-app/iot2050-eio-manager/files/iot2050-eio-service.py b/recipes-app/iot2050-eio-manager/files/iot2050-eio-service.py index 5fdbd9dac..6aa00fa4b 100644 --- a/recipes-app/iot2050-eio-manager/files/iot2050-eio-service.py +++ b/recipes-app/iot2050-eio-manager/files/iot2050-eio-service.py @@ -71,7 +71,7 @@ def SyncTime(self, request: SyncTimeRequest, context): return SyncTimeReply(status=0, message=f'{time}') def UpdateFirmware(self, request: UpdateFirmwareRequest, context): - status, message = update_firmware(request.firmware) + status, message = update_firmware(request.firmware, request.firmware_type) return UpdateFirmwareReply(status=status, message=f'{message}') def CheckFWU(self, request: CheckFWURequest, context): diff --git a/recipes-app/iot2050-eio-manager/files/iot2050_eio_fwu.py b/recipes-app/iot2050-eio-manager/files/iot2050_eio_fwu.py index 9de44fbf1..88968b905 100755 --- a/recipes-app/iot2050-eio-manager/files/iot2050_eio_fwu.py +++ b/recipes-app/iot2050-eio-manager/files/iot2050_eio_fwu.py @@ -15,6 +15,7 @@ from iot2050_eio_global import ( EIO_FS_FW_VER, EIO_FWU_META, + EIO_MODULE_PATH, EIO_FWU_MAP3_FW_BIN ) @@ -74,7 +75,7 @@ class EIOFirmware(): MAP3_CERTIFICATE_OFFSET = MAP3_FLASH_SIZE_1_MB - 32 * 1024 def __init__(self, firmware): - self.firmware = open(firmware, "rb") + self.firmware = firmware self.chip = gpiod.Chip("/dev/gpiochip2") self.spi_mux_pin = self.chip.get_line(86) @@ -95,8 +96,7 @@ def __get_write_firmware(self): read_buffer = ctypes.create_string_buffer(self.MAP3_FLASH_SIZE_1_MB) self.flash_prog.read(read_buffer, self.MAP3_FLASH_SIZE_1_MB) - self.firmware.seek(0) - write_firmware = self.firmware.read() + write_firmware = self.firmware # Firmware and certificate partitions are required, other # partitions are reserved. write_firmware = write_firmware[:self.MAP3_FIRMWARE_SIZE] \ @@ -128,7 +128,19 @@ def __del__(self): self.spi_mux_pin.release() if hasattr(self, "flash_prog") and self.flash_prog: self.flash_prog.release() - self.firmware.close() + + +class ModuleFirmware(): + def __init__(self, firmware, firmware_type): + self.firmware = firmware + self.firmware_target = EIO_MODULE_PATH + firmware_type + + def write(self): + with open(self.firmware_target, "wb") as f: + try: + f.write(self.firmware) + except OSError as e: + raise UpgradeError(f"ModuleFirmware writes failed: {e}") class FirmwareUpdate(): @@ -136,9 +148,17 @@ class FirmwareUpdate(): The FirmwareUpdate models the firmware updating behavior for all firmware update. """ - def __init__(self, firmware): + def __init__(self, firmware, firmware_type): self.firmwares = {} - self.firmwares["map3"] = EIOFirmware(firmware) + self.to_verify = True + if "map3" == firmware_type: + self.firmwares[firmware_type] = EIOFirmware(firmware) + if "slot" in firmware_type: + self.firmwares[firmware_type] = ModuleFirmware(firmware, firmware_type) + self.to_verify = False + + if not self.firmwares: + raise UpgradeError("No valid firmware!") def update(self): """Update the firmware to the specified flash""" @@ -146,13 +166,14 @@ def update(self): for firmware_type in self.firmwares: self.firmwares[firmware_type].write() - content = self.firmwares[firmware_type].write_firmware - firmware_md5 = self.__get_md5_digest(content) - content = self.firmwares[firmware_type].read() - read_out_md5 = self.__get_md5_digest(content) + if self.to_verify: + content = self.firmwares[firmware_type].write_firmware + firmware_md5 = self.__get_md5_digest(content) + content = self.firmwares[firmware_type].read() + read_out_md5 = self.__get_md5_digest(content) - if firmware_md5 != read_out_md5: - raise UpgradeError("Firmware digest verification failed") + if firmware_md5 != read_out_md5: + raise UpgradeError("Firmware digest verification failed") def __get_md5_digest(self, content): """Verify the update integrity""" @@ -180,7 +201,7 @@ def collect_fwu_info(self) -> tuple[int, str]: Returns: tuple[int, str]: The 1st int element indicates the firmware status: - + - 0: means no need to update - 1: means firmware need update. - 2: means firmware need update, however, the firmware @@ -208,9 +229,9 @@ def collect_fwu_info(self) -> tuple[int, str]: return status, message -def update_firmware(firmware): +def update_firmware(firmware, firmware_type): try: - FirmwareUpdate(firmware).update() + FirmwareUpdate(firmware, firmware_type).update() return 0, "Firmware upgrade successfully!" except UpgradeError as e: return 1, e diff --git a/recipes-app/iot2050-eio-manager/files/iot2050_eio_global.py b/recipes-app/iot2050-eio-manager/files/iot2050_eio_global.py index 2c402d8f1..c37bed3ef 100644 --- a/recipes-app/iot2050-eio-manager/files/iot2050_eio_global.py +++ b/recipes-app/iot2050-eio-manager/files/iot2050_eio_global.py @@ -29,6 +29,9 @@ # Extended IO FUSE filesystem path for eio events 'EIO_FS_EVENT': '/eiofs/log/event', + # Extended IO FUSE filesystem path for modules + 'EIO_MODULE_PATH': '/eiofs/controller/', + # EIO Firmware Update: Meta data 'EIO_FWU_META': '/usr/lib/iot2050/eio/firmware-version', @@ -57,6 +60,7 @@ EIO_FS_CONFIG = effective_conf['EIO_FS_CONFIG'] EIO_FS_FW_VER = effective_conf['EIO_FS_FW_VER'] EIO_FS_EVENT = effective_conf['EIO_FS_EVENT'] +EIO_MODULE_PATH = effective_conf['EIO_MODULE_PATH'] EIO_FWU_META = effective_conf['EIO_FWU_META'] EIO_FWU_MAP3_FW_BIN = effective_conf['EIO_FWU_MAP3_FW_BIN'] EIO_SCHEMA_ROOT = effective_conf['EIO_SCHEMA_ROOT']