1#
2# Copyright (c) 2010-2024 Antmicro
3#
4# This file is licensed under the MIT License.
5# Full license text is available in 'licenses/MIT.txt'.
6#
7
8from array import array
9import ctypes
10
11from Antmicro.Renode.Peripherals.CPU import RegisterValue
12
13try:
14    # The additional CLR reference is required on dotnet
15    clr.AddReference("System.Security.Cryptography.Algorithms")
16except:
17    pass
18
19from System.Security.Cryptography import SHA256, SHA384, SHA512
20
21
22def register_bootrom_hook(addr, func):
23    self["sysbus.cpu"].AddHook(addr, func)
24    # Fill the bootrom's function pointer entry with the address that the hook is registered to.
25    # For simplicity hooks are added on function pointer locations, no the actual function addresses.
26    self.SystemBus.WriteDoubleWord(addr, addr)
27    self.InfoLog("Registering bootrom function at 0x{0:X}", addr)
28
29
30# Based on: https://chromium.googlesource.com/chromiumos/platform/ec/+/6898a6542ed0238cc182948f56e3811534db1a38/chip/npcx/header.c#43
31def register_bootloader():
32    class FirmwareHeader(ctypes.LittleEndianStructure):
33        _pack_ = 1
34        _fields_ = [
35            ("anchor", ctypes.c_uint32),
36            ("ext_anchor", ctypes.c_uint16),
37            ("spi_max_freq", ctypes.c_uint8),
38            ("spi_read_mode", ctypes.c_uint8),
39            ("cfg_err_detect", ctypes.c_uint8),
40            ("fw_load_addr", ctypes.c_uint32),
41            ("fw_entry", ctypes.c_uint32),
42            ("err_detect_start_addr", ctypes.c_uint32),
43            ("err_detect_end_addr", ctypes.c_uint32),
44            ("fw_length", ctypes.c_uint32),
45            ("flash_size", ctypes.c_uint8),
46            ("reserved", ctypes.c_uint8 * 26),
47            ("sig_header", ctypes.c_uint32),
48            ("sig_fw_image", ctypes.c_uint32),
49        ]
50
51    HEADER_SIZE = ctypes.sizeof(FirmwareHeader)
52    flash = self["sysbus.internal_flash"]
53
54    def bootloader(cpu, addr):
55        header_data = flash.ReadBytes(0x0, HEADER_SIZE)
56        header = FirmwareHeader.from_buffer(array("B", header_data))
57
58        firmware = flash.ReadBytes(HEADER_SIZE, header.fw_length)
59        self.SystemBus.WriteBytes(firmware, header.fw_load_addr)
60
61        cpu.PC = RegisterValue.Create(header.fw_entry, 32)
62
63        self.InfoLog(
64            "Firmware loaded at: 0x{0:X} ({1} bytes). PC = 0x{2:X}",
65            header.fw_load_addr,
66            header.fw_length,
67            header.fw_entry,
68        )
69
70    register_bootrom_hook(0x0, bootloader)
71
72
73# Based on:
74# - https://chromium.googlesource.com/chromiumos/platform/ec/+/6898a6542ed0238cc182948f56e3811534db1a38/chip/npcx/trng.c
75# - https://chromium.googlesource.com/chromiumos/platform/ec/+/6898a6542ed0238cc182948f56e3811534db1a38/chip/npcx/sha256_chip.c
76def register_ncl_functions():
77    DRGB_BASE_ADDRESS = 0x00000110
78    SHA_BASE_ADDRESS = 0x0000013C
79
80    POINTER_SIZE = 0x4
81
82    DRBG_CONTEXT_SIZE = 240
83    SHA_CONTEXT_SIZE = 212
84
85    NCL_STATUS_OK = 0xA5A5
86    NCL_STATUS_FAIL = 0x5A5A
87    NCL_STATUS_INVALID_PARAM = 0x02
88
89    NCL_SHA_TYPE_2_256 = 0
90    NCL_SHA_TYPE_2_384 = 1
91    NCL_SHA_TYPE_2_512 = 2
92
93    def create_hook(name, return_value=NCL_STATUS_OK):
94        def hook(cpu, addr):
95            cpu.NoisyLog(
96                "Entering '{0}' hook that returns 0x{1:X}", name, return_value
97            )
98            cpu.SetRegister(0, RegisterValue.Create(return_value, 32))
99            cpu.PC = cpu.LR
100
101        return hook
102
103    rng = Antmicro.Renode.Core.PseudorandomNumberGenerator()
104
105    def trng_generate(cpu, addr):
106        out_buff = cpu.GetRegister(3).RawValue
107        out_buff_len = self.SystemBus.ReadDoubleWord(cpu.SP.RawValue)
108
109        data = System.Array[System.Byte](range(out_buff_len))
110        rng.NextBytes(data)
111        self.SystemBus.WriteBytes(data, out_buff)
112
113        cpu.SetRegister(0, RegisterValue.Create(NCL_STATUS_OK, 32))
114        cpu.PC = cpu.LR
115
116    DRGB_FUNCTIONS = [
117        create_hook("get_context_size", DRBG_CONTEXT_SIZE),
118        create_hook("init_context"),
119        create_hook("power"),
120        create_hook("finalize_context"),
121        create_hook("init"),
122        create_hook("config"),
123        create_hook("instantiate"),
124        create_hook("uninstantiate"),
125        create_hook("reseed"),
126        trng_generate,
127        create_hook("clear"),
128    ]
129
130    class SHAContext:
131        sha_buffer = System.Collections.Generic.List[System.Byte]()
132        sha_type = None
133
134    def sha_start(cpu, addr):
135        status = NCL_STATUS_OK
136        sha_type = cpu.GetRegister(1).RawValue
137        if sha_type in [
138            NCL_SHA_TYPE_2_256,
139            NCL_SHA_TYPE_2_384,
140            NCL_SHA_TYPE_2_512,
141        ]:
142            SHAContext.sha_type = sha_type
143        else:
144            status = NCL_STATUS_INVALID_PARAM
145        cpu.SetRegister(0, RegisterValue.Create(status, 32))
146        cpu.PC = cpu.LR
147
148    def sha_finish(cpu, addr):
149        try:
150            if SHAContext.sha_type == NCL_SHA_TYPE_2_256:
151                sha_instance = SHA256.Create()
152            elif SHAContext.sha_type == NCL_SHA_TYPE_2_384:
153                sha_instance = SHA384.Create()
154            elif SHAContext.sha_type == NCL_SHA_TYPE_2_512:
155                sha_instance = SHA512.Create()
156            else:
157                cpu.SetRegister(
158                    0, RegisterValue.Create(NCL_STATUS_FAIL, 32)
159                )
160                cpu.PC = cpu.LR
161                return
162
163            hash = sha_instance.ComputeHash(SHAContext.sha_buffer.ToArray())
164            SHAContext.sha_buffer.Clear()
165
166            data_addr = cpu.GetRegister(1).RawValue
167            self.SystemBus.WriteBytes(hash, data_addr)
168
169            cpu.SetRegister(0, RegisterValue.Create(NCL_STATUS_OK, 32))
170            cpu.PC = cpu.LR
171        finally:
172            if sha_instance is not None:
173                sha_instance.Dispose()
174
175    def sha_update(cpu, addr):
176        data_addr = cpu.GetRegister(1).RawValue
177        length = cpu.GetRegister(2).RawValue
178        data = self.SystemBus.ReadBytes(data_addr, length)
179
180        SHAContext.sha_buffer.AddRange(data)
181
182        cpu.SetRegister(0, RegisterValue.Create(NCL_STATUS_OK, 32))
183        cpu.PC = cpu.LR
184
185    SHA_FUNCTIONS = [
186        create_hook("get_context_size", SHA_CONTEXT_SIZE),
187        create_hook("init_context"),
188        create_hook("finalize_context"),
189        create_hook("init"),
190        sha_start,
191        sha_update,
192        sha_finish,
193        create_hook("calc"),
194        create_hook("power"),
195        create_hook("reset"),
196    ]
197
198    for base, collection in [
199        (DRGB_BASE_ADDRESS, DRGB_FUNCTIONS),
200        (SHA_BASE_ADDRESS, SHA_FUNCTIONS),
201    ]:
202        for i, func in enumerate(collection):
203            register_bootrom_hook(base + i * POINTER_SIZE, func)
204
205
206# Based on: https://chromium.googlesource.com/chromiumos/platform/ec/+/6898a6542ed0238cc182948f56e3811534db1a38/chip/npcx/rom_chip.h
207def register_download_from_flash():
208    def download_from_flash(cpu, addr):
209        src_offset = cpu.GetRegister(0).RawValue
210        dest_addr = cpu.GetRegister(1).RawValue
211        size = cpu.GetRegister(2).RawValue
212        exe_addr = self.SystemBus.ReadDoubleWord(cpu.SP.RawValue)
213
214        data = self["sysbus.internal_flash"].ReadBytes(src_offset, size)
215        self.SystemBus.WriteBytes(data, dest_addr)
216
217        cpu.PC = RegisterValue.Create(exe_addr, 32)
218
219        cpu.InfoLog(
220            "Downloading from flash offset 0x{0:X} to 0x{1:X} ({2} bytes) and jumping to 0x{3:X}",
221            src_offset,
222            dest_addr,
223            size,
224            exe_addr,
225        )
226
227    register_bootrom_hook(0x40, download_from_flash)
228
229
230register_bootloader()
231register_ncl_functions()
232register_download_from_flash()
233