| # |
| # Copyright (c) 2010-2023 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. |
| self.SystemBus.WriteDoubleWord(addr, addr) |
| self.InfoLog("Registering bootrom function at 0x{0:X}", 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 |
| |
| 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.SetRegisterUnsafe(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.GetRegisterUnsafe(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.SetRegisterUnsafe(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.GetRegisterUnsafe(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.SetRegisterUnsafe(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.SetRegisterUnsafe( |
| 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.GetRegisterUnsafe(1).RawValue |
| self.SystemBus.WriteBytes(hash, data_addr) |
| |
| cpu.SetRegisterUnsafe(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.GetRegisterUnsafe(1).RawValue |
| length = cpu.GetRegisterUnsafe(2).RawValue |
| data = self.SystemBus.ReadBytes(data_addr, length) |
| |
| SHAContext.sha_buffer.AddRange(data) |
| |
| cpu.SetRegisterUnsafe(0, RegisterValue.Create(NCL_STATUS_OK, 32)) |
| cpu.PC = cpu.LR |
| |
| SHA_FUNCTIONS = [ |
| create_hook("get_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.GetRegisterUnsafe(0).RawValue |
| dest_addr = cpu.GetRegisterUnsafe(1).RawValue |
| size = cpu.GetRegisterUnsafe(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) |
| |
| |
| register_bootloader() |
| register_ncl_functions() |
| register_download_from_flash() |