# # Copyright (c) 2010-2024 Antmicro # # This file is licensed under the MIT License. # Full license text is available in 'licenses/MIT.txt'. # from array import array import ctypes from Antmicro.Renode.Peripherals.CPU import RegisterValue try: # The additional CLR reference is required on dotnet clr.AddReference("System.Security.Cryptography.Algorithms") except: pass from System.Security.Cryptography import SHA256, SHA384, SHA512 def register_bootrom_hook(addr, func): self["sysbus.cpu"].AddHook(addr, func) # Fill the bootrom's function pointer entry with the address that the hook is registered to. # For simplicity hooks are added on function pointer locations, no the actual function addresses. # The hook address is odd for Thumb mode on ARM_V7. hook_addr = addr | 1 self.SystemBus.WriteDoubleWord(addr, hook_addr) self.InfoLog("Registering bootrom function at 0x{0:X}", hook_addr) # Based on: https://chromium.googlesource.com/chromiumos/platform/ec/+/6898a6542ed0238cc182948f56e3811534db1a38/chip/npcx/header.c#43 def register_bootloader(): class FirmwareHeader(ctypes.LittleEndianStructure): _pack_ = 1 _fields_ = [ ("anchor", ctypes.c_uint32), ("ext_anchor", ctypes.c_uint16), ("spi_max_freq", ctypes.c_uint8), ("spi_read_mode", ctypes.c_uint8), ("cfg_err_detect", ctypes.c_uint8), ("fw_load_addr", ctypes.c_uint32), ("fw_entry", ctypes.c_uint32), ("err_detect_start_addr", ctypes.c_uint32), ("err_detect_end_addr", ctypes.c_uint32), ("fw_length", ctypes.c_uint32), ("flash_size", ctypes.c_uint8), ("reserved", ctypes.c_uint8 * 26), ("sig_header", ctypes.c_uint32), ("sig_fw_image", ctypes.c_uint32), ] HEADER_SIZE = ctypes.sizeof(FirmwareHeader) flash = self["sysbus.internal_flash"] def bootloader(cpu, addr): header_data = flash.ReadBytes(0x0, HEADER_SIZE) header = FirmwareHeader.from_buffer(array("B", header_data)) firmware = flash.ReadBytes(HEADER_SIZE, header.fw_length) self.SystemBus.WriteBytes(firmware, header.fw_load_addr) cpu.PC = RegisterValue.Create(header.fw_entry, 32) self.InfoLog( "Firmware loaded at: 0x{0:X} ({1} bytes). PC = 0x{2:X}", header.fw_load_addr, header.fw_length, header.fw_entry, ) register_bootrom_hook(0x0, bootloader) # Based on: # - https://chromium.googlesource.com/chromiumos/platform/ec/+/6898a6542ed0238cc182948f56e3811534db1a38/chip/npcx/trng.c # - https://chromium.googlesource.com/chromiumos/platform/ec/+/6898a6542ed0238cc182948f56e3811534db1a38/chip/npcx/sha256_chip.c def register_ncl_functions(): DRGB_BASE_ADDRESS = 0x00000110 SHA_BASE_ADDRESS = 0x0000013C POINTER_SIZE = 0x4 DRBG_CONTEXT_SIZE = 240 SHA_CONTEXT_SIZE = 212 NCL_STATUS_OK = 0xA5A5 NCL_STATUS_FAIL = 0x5A5A NCL_STATUS_INVALID_PARAM = 0x02 NCL_SHA_TYPE_2_256 = 0 NCL_SHA_TYPE_2_384 = 1 NCL_SHA_TYPE_2_512 = 2 def create_hook(name, return_value=NCL_STATUS_OK): def hook(cpu, addr): cpu.NoisyLog( "Entering '{0}' hook that returns 0x{1:X}", name, return_value ) cpu.SetRegister(0, RegisterValue.Create(return_value, 32)) cpu.PC = cpu.LR return hook rng = Antmicro.Renode.Core.PseudorandomNumberGenerator() def trng_generate(cpu, addr): out_buff = cpu.GetRegister(3).RawValue out_buff_len = self.SystemBus.ReadDoubleWord(cpu.SP.RawValue) data = System.Array[System.Byte](range(out_buff_len)) rng.NextBytes(data) self.SystemBus.WriteBytes(data, out_buff) cpu.SetRegister(0, RegisterValue.Create(NCL_STATUS_OK, 32)) cpu.PC = cpu.LR DRGB_FUNCTIONS = [ create_hook("get_context_size", DRBG_CONTEXT_SIZE), create_hook("init_context"), create_hook("power"), create_hook("finalize_context"), create_hook("init"), create_hook("config"), create_hook("instantiate"), create_hook("uninstantiate"), create_hook("reseed"), trng_generate, create_hook("clear"), ] class SHAContext: sha_buffer = System.Collections.Generic.List[System.Byte]() sha_type = None def sha_start(cpu, addr): status = NCL_STATUS_OK sha_type = cpu.GetRegister(1).RawValue if sha_type in [ NCL_SHA_TYPE_2_256, NCL_SHA_TYPE_2_384, NCL_SHA_TYPE_2_512, ]: SHAContext.sha_type = sha_type else: status = NCL_STATUS_INVALID_PARAM cpu.SetRegister(0, RegisterValue.Create(status, 32)) cpu.PC = cpu.LR def sha_finish(cpu, addr): try: if SHAContext.sha_type == NCL_SHA_TYPE_2_256: sha_instance = SHA256.Create() elif SHAContext.sha_type == NCL_SHA_TYPE_2_384: sha_instance = SHA384.Create() elif SHAContext.sha_type == NCL_SHA_TYPE_2_512: sha_instance = SHA512.Create() else: cpu.SetRegister( 0, RegisterValue.Create(NCL_STATUS_FAIL, 32) ) cpu.PC = cpu.LR return hash = sha_instance.ComputeHash(SHAContext.sha_buffer.ToArray()) SHAContext.sha_buffer.Clear() data_addr = cpu.GetRegister(1).RawValue self.SystemBus.WriteBytes(hash, data_addr) cpu.SetRegister(0, RegisterValue.Create(NCL_STATUS_OK, 32)) cpu.PC = cpu.LR finally: if sha_instance is not None: sha_instance.Dispose() def sha_update(cpu, addr): data_addr = cpu.GetRegister(1).RawValue length = cpu.GetRegister(2).RawValue data = self.SystemBus.ReadBytes(data_addr, length) SHAContext.sha_buffer.AddRange(data) cpu.SetRegister(0, RegisterValue.Create(NCL_STATUS_OK, 32)) cpu.PC = cpu.LR SHA_FUNCTIONS = [ create_hook("get_context_size", SHA_CONTEXT_SIZE), create_hook("init_context"), create_hook("finalize_context"), create_hook("init"), sha_start, sha_update, sha_finish, create_hook("calc"), create_hook("power"), create_hook("reset"), ] for base, collection in [ (DRGB_BASE_ADDRESS, DRGB_FUNCTIONS), (SHA_BASE_ADDRESS, SHA_FUNCTIONS), ]: for i, func in enumerate(collection): register_bootrom_hook(base + i * POINTER_SIZE, func) # Based on: https://chromium.googlesource.com/chromiumos/platform/ec/+/6898a6542ed0238cc182948f56e3811534db1a38/chip/npcx/rom_chip.h def register_download_from_flash(): def download_from_flash(cpu, addr): src_offset = cpu.GetRegister(0).RawValue dest_addr = cpu.GetRegister(1).RawValue size = cpu.GetRegister(2).RawValue exe_addr = self.SystemBus.ReadDoubleWord(cpu.SP.RawValue) data = self["sysbus.internal_flash"].ReadBytes(src_offset, size) self.SystemBus.WriteBytes(data, dest_addr) cpu.PC = RegisterValue.Create(exe_addr, 32) cpu.InfoLog( "Downloading from flash offset 0x{0:X} to 0x{1:X} ({2} bytes) and jumping to 0x{3:X}", src_offset, dest_addr, size, exe_addr, ) register_bootrom_hook(0x40, download_from_flash) # Based on: # - https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform/ec-legacy/chip/npcx/rom_chip.c;l=9;drc=a3e247e7d1b81c8f4bd0746af1747f955880ab1f # - https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform/ec/common/mock/otpi_mock.c;l=10;drc=c45797708aa5a69f51dc74945bf3c7125c0734f2 def register_otpi_functions(): OTPI_BASE_ADDRESS = 0x0000004C OTP_KEY_ADDR = 0x300 FAKE_OTP_KEY = [0xDE, 0xAD, 0xBE, 0xEF] * 8 # Needs to be "non-trivial" API_RET_OTP_STATUS_OK = 0xA5A5 API_RET_OTP_STATUS_FAIL = 0x5A5A POINTER_SIZE = 4 class MockOTP: powered_on = False otp_key_buffer = System.Array[System.Byte](FAKE_OTP_KEY) def set_return_value_and_pc(cpu, value): cpu.SetRegister(0, RegisterValue.Create(value, 32)) cpu.PC = cpu.LR def otpi_power(cpu, addr): on = cpu.GetRegister(0).RawValue cpu.DebugLog("Entering 'otpi_power' hook with arg on=0x{0:X}", on) MockOTP.powered_on = bool(on) set_return_value_and_pc(cpu, API_RET_OTP_STATUS_OK) def otpi_read(cpu, addr): address = cpu.GetRegister(0).RawValue data_addr = cpu.GetRegister(1).RawValue cpu.DebugLog( "Entering 'otpi_read' hook with args address=0x{0:X}, data_addr=0x{1:X}", address, data_addr, ) if not MockOTP.powered_on: set_return_value_and_pc(cpu, API_RET_OTP_STATUS_FAIL) return result = MockOTP.otp_key_buffer[address - OTP_KEY_ADDR] bytes = System.Array[System.Byte]([result]) self.SystemBus.WriteBytes(bytes, data_addr) set_return_value_and_pc(cpu, API_RET_OTP_STATUS_OK) def otpi_write(cpu, addr): address = cpu.GetRegister(0).RawValue data = cpu.GetRegister(1).RawValue cpu.DebugLog( "Entering 'otpi_write' hook with args address=0x{0:X}, data=0x{1:X}", address, data, ) if not MockOTP.powered_on: set_return_value_and_pc(cpu, API_RET_OTP_STATUS_FAIL) return MockOTP.otp_key_buffer[address - OTP_KEY_ADDR] |= data set_return_value_and_pc(cpu, API_RET_OTP_STATUS_OK) def otpi_write_protect(cpu, addr): cpu.DebugLog("Entering 'otpi_write_protect' hook") if not MockOTP.powered_on: set_return_value_and_pc(cpu, API_RET_OTP_STATUS_FAIL) return set_return_value_and_pc(cpu, API_RET_OTP_STATUS_OK) OTPI_FUNCTIONS = [ otpi_power, otpi_read, otpi_write, otpi_write_protect, ] for i, func in enumerate(OTPI_FUNCTIONS): register_bootrom_hook(OTPI_BASE_ADDRESS + i * POINTER_SIZE, func) register_bootloader() register_ncl_functions() register_download_from_flash() register_otpi_functions()