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 8 using System; 9 using System.Numerics; 10 using System.Linq; 11 using System.Reflection; 12 using System.Collections.Generic; 13 using Antmicro.Renode.Core; 14 using Antmicro.Renode.Exceptions; 15 using Antmicro.Renode.Logging; 16 using Antmicro.Renode.Utilities; 17 using Antmicro.Renode.Peripherals.Memory; 18 using Antmicro.Renode.Peripherals.CPU; 19 using Antmicro.Renode.Core.Structure.Registers; 20 21 namespace Antmicro.Renode.Peripherals.Miscellaneous 22 { 23 public interface IOpenTitan_BigNumberAcceleratorCore 24 { ExecuteInstructions(int instructionCount, out ulong executedInstructions)25 ExecutionResult ExecuteInstructions(int instructionCount, out ulong executedInstructions); Reset()26 void Reset(); GetRegister(int id)27 RegisterValue GetRegister(int id); GetWideRegister(int number, bool special)28 BigInteger GetWideRegister(int number, bool special); 29 string FixedRandomPattern { get; set; } 30 string KeyShare0 { get; set; } 31 string KeyShare1 { get; set; } 32 CoreError LastError { get; } 33 } 34 35 public static class BigIntegerHelpers 36 { ToByteArray(this BigInteger bi, int width)37 public static byte[] ToByteArray(this BigInteger bi, int width) 38 { 39 var array = bi.ToByteArray(); 40 if(array.Length > width) 41 { 42 array = array.Take(width).ToArray(); 43 } 44 else if(array.Length < width) 45 { 46 var fill = width - array.Length; 47 var lsb = array[array.Length - 1]; 48 array = array.Concat(Enumerable.Repeat(lsb >= 0x80 ? (byte)0xff : (byte)0, fill)).ToArray(); 49 } 50 return array; 51 } 52 ToLongString(this BigInteger bi, int width)53 public static string ToLongString(this BigInteger bi, int width) 54 { 55 var s = string.Join("", bi.ToByteArray(width).Reverse().Select(x => x.ToString("x2"))); 56 return $"0x{s}"; 57 } 58 } 59 60 public class OpenTitan_BigNumberAccelerator : BasicDoubleWordPeripheral, IKnownSize 61 { OpenTitan_BigNumberAccelerator(IMachine machine)62 public OpenTitan_BigNumberAccelerator(IMachine machine) : base(machine) 63 { 64 ulong instructionMemorySize = sizeof(uint) * DataMemoryWindowsCount; 65 ulong dataMemorySize = sizeof(uint) * InstructionsMemoryWindowsCount; 66 67 dataMemory = new OpenTitan_ScrambledMemory(machine, (long)dataMemorySize); 68 instructionsMemory = new OpenTitan_ScrambledMemory(machine, (long)instructionMemorySize); 69 70 DoneIRQ = new GPIO(); 71 FatalAlert = new GPIO(); 72 RecoverableAlert = new GPIO(); 73 core = InitOtbnCore(instructionsMemory, dataMemory); 74 75 DefineRegisters(); 76 Reset(); 77 } 78 LoadELF(string filePath)79 public void LoadELF(string filePath) 80 { 81 using(var elf = ELFUtils.LoadELF(filePath)) 82 { 83 if(elf.TryGetSection(".text", out var iMemSection)) 84 { 85 instructionsMemory.WriteBytes(0, iMemSection.GetContents()); 86 } 87 else 88 { 89 var availableSections = String.Join(", ", elf.Sections.Select(x => x.Name).ToArray()); 90 throw new RecoverableException($".text section not found. Available sections: {availableSections}"); 91 } 92 93 if(elf.TryGetSection(".data", out var dMemSection)) 94 { 95 dataMemory.WriteBytes(0, dMemSection.GetContents()); 96 } 97 else 98 { 99 var availableSections = String.Join(", ", elf.Sections.Select(x => x.Name).ToArray()); 100 throw new RecoverableException($".data section not found. Available sections: {availableSections}"); 101 } 102 } 103 } 104 Reset()105 public override void Reset() 106 { 107 dataMemory.ZeroAll(); 108 instructionsMemory.ZeroAll(); 109 core.Reset(); 110 111 // DoneIRQ is unset via the registers callbacks 112 // Alerts need to be unset manually 113 FatalAlert.Unset(); 114 RecoverableAlert.Unset(); 115 executedInstructionsTotal = 0; 116 117 base.Reset(); 118 } 119 GetCoreRegister(int number)120 public ulong GetCoreRegister(int number) 121 { 122 return this.core.GetRegister(number); 123 } 124 GetWideRegister(int number, bool special)125 public string GetWideRegister(int number, bool special) 126 { 127 return core.GetWideRegister(number, special).ToLongString(32); 128 } 129 130 public long Size => 0x10000; 131 132 public GPIO DoneIRQ { get; } 133 public GPIO FatalAlert { get; } 134 public GPIO RecoverableAlert { get; } 135 136 public bool IsIdle => (status.Value == Status.Idle); 137 138 public string FixedRandomPattern 139 { 140 get => core.FixedRandomPattern; 141 set 142 { 143 core.FixedRandomPattern = value; 144 } 145 } 146 147 public string KeyShare0 148 { 149 get => core.KeyShare0; 150 set 151 { 152 core.KeyShare0 = value; 153 } 154 } 155 156 public string KeyShare1 157 { 158 get => core.KeyShare1; 159 set 160 { 161 core.KeyShare1 = value; 162 } 163 } 164 DefineRegisters()165 private void DefineRegisters() 166 { 167 Registers.InterruptState.Define(this) 168 .WithFlag(0, out doneInterruptState, FieldMode.Read | FieldMode.WriteOneToClear, name: "done") 169 .WithReservedBits(1, 31) 170 .WithWriteCallback((_, __) => UpdateInterrupts()); 171 172 Registers.InterruptEnable.Define(this) 173 .WithFlag(0, out doneInterruptEnable, name: "done") 174 .WithReservedBits(1, 31) 175 .WithWriteCallback((_, __) => UpdateInterrupts()); 176 177 Registers.InterruptTest.Define(this) 178 .WithFlag(0, FieldMode.Write, writeCallback: (_, val) => { if(val) doneInterruptState.Value = true; }, name: "done") 179 .WithReservedBits(1, 31) 180 .WithWriteCallback((_, val) => { if(val != 0) UpdateInterrupts(); }); 181 182 Registers.AlertTest.Define(this) 183 .WithFlag(0, FieldMode.Write, writeCallback: (_, val) => { if(val) FatalAlert.Blink(); }, name: "fatal") 184 .WithFlag(1, FieldMode.Write, writeCallback: (_, val) => { if(val) RecoverableAlert.Blink(); }, name: "recov") 185 .WithReservedBits(2, 30); 186 187 Registers.Command.Define(this) 188 .WithEnumField<DoubleWordRegister, Command>(0, 8, FieldMode.Write, writeCallback: (_, command) => 189 { 190 // enter the busy state now, but execute the command (and clear the busy state later) 191 // this is required by some tests 192 EnterState(command); 193 machine.LocalTimeSource.ExecuteInNearestSyncedState(__ => 194 { 195 HandleCommand(command); 196 }); 197 }, name: "cmd") 198 .WithReservedBits(8, 24); 199 200 IFlagRegisterField softwareErrorsFatal = null; 201 Registers.Control.Define(this) 202 .WithFlag(0, out softwareErrorsFatal, name: "software_errs_fatal", 203 writeCallback: (prevVal, newVal) => 204 { 205 var s = status.Value; 206 if(s != Status.Idle) 207 { 208 // according to the documentation: "Writes are ignored if OTBN is not idle." 209 softwareErrorsFatal.Value = prevVal; 210 211 this.Log(LogLevel.Warning, "Ignoring software_errs_fatal write, as the device is in {0} mode.", s); 212 } 213 }) 214 .WithReservedBits(1, 31); 215 216 Registers.Status.Define(this) 217 .WithEnumField<DoubleWordRegister, Status>(0, 8, out status, FieldMode.Read, name: "status") 218 .WithReservedBits(8, 24); 219 220 Registers.OperationResult.Define(this) 221 .WithFlag(0, out badDataAddressError, name: "bad_data_addr") 222 .WithFlag(1, out badInstructionAddressError, name: "bad_insn_addr") 223 .WithFlag(2, out callStackError, name: "call_stack") 224 .WithFlag(3, out illegalInstructionError, name: "illegal_insn") 225 .WithFlag(4, out loopError, name: "loop") 226 .WithTaggedFlag("key_invalid", 5) 227 .WithTaggedFlag("rnd_rep_chk_fail", 6) 228 .WithTaggedFlag("rnd_fips_chk_fail", 7) 229 .WithReservedBits(8, 8) 230 .WithTaggedFlag("imem_intg_violation", 16) 231 .WithTaggedFlag("dmem_intg_violation", 17) 232 .WithTaggedFlag("reg_intg_violation", 18) 233 .WithTaggedFlag("bus_intg_violation", 19) 234 .WithTaggedFlag("bad_internal_state", 20) 235 .WithTaggedFlag("illegal_bus_access", 21) 236 .WithTaggedFlag("lifecycle_escalation", 22) 237 .WithTaggedFlag("fatal_software", 23) 238 .WithReservedBits(24, 8) 239 .WithWriteCallback((_, __) => 240 { 241 // The host CPU can clear this register when OTBN is not running, by writing any value. Write attempts while OTBN is running are ignored. 242 if(!IsIdle) 243 { 244 badDataAddressError.Value = false; 245 badInstructionAddressError.Value = false; 246 callStackError.Value = false; 247 illegalInstructionError.Value = false; 248 loopError.Value = false; 249 } 250 }); 251 252 Registers.FatalAlertCause.Define(this) 253 .WithTaggedFlag("imem_intg_violation", 0) 254 .WithTaggedFlag("dmem_intg_violation", 1) 255 .WithTaggedFlag("reg_intg_violation", 2) 256 .WithTaggedFlag("bus_intg_violation", 3) 257 .WithTaggedFlag("bad_internal_state", 4) 258 .WithTaggedFlag("illegal_bus_access", 5) 259 .WithTaggedFlag("lifecycle_escalation", 6) 260 .WithTaggedFlag("fatal_software", 7) 261 .WithReservedBits(8, 24); 262 263 Registers.InstructionCount.Define(this) 264 .WithValueField(0,32, FieldMode.Read, valueProviderCallback: (_) => (uint)executedInstructionsTotal, name: "insn_cnt"); 265 266 Registers.A32bitCRCchecksumofdatawrittentomemory.Define(this) 267 .WithTag("checksum", 0, 32); 268 269 Registers.InstructionMemoryAccess.DefineMany(this, InstructionsMemoryWindowsCount, (register, regId) => 270 { 271 register 272 .WithValueField(0, 32, 273 valueProviderCallback: _ => instructionsMemory.ReadDoubleWord(regId * sizeof(uint)), 274 writeCallback: (_, val) => instructionsMemory.WriteDoubleWord(regId * sizeof(uint), (uint)val), 275 name: $"InstructionMemoryAccess{regId}"); 276 }); 277 278 Registers.DataMemoryAccess.DefineMany(this, DataMemoryWindowsCount, (register, regId) => 279 { 280 register 281 .WithValueField(0, 32, 282 valueProviderCallback: _ => dataMemory.ReadDoubleWord(regId * sizeof(uint)), 283 writeCallback: (_, val) => dataMemory.WriteDoubleWord(regId * sizeof(uint), (uint)val), 284 name: $"DataMemoryAccess{regId}"); 285 }); 286 } 287 UpdateInterrupts()288 private void UpdateInterrupts() 289 { 290 var done = doneInterruptEnable.Value && doneInterruptState.Value; 291 DoneIRQ.Set(done); 292 } 293 EnterState(Command command)294 private void EnterState(Command command) 295 { 296 switch(command) 297 { 298 case Command.Execute: 299 status.Value = Status.BusyExecute; 300 break; 301 case Command.WipeDMem: 302 status.Value = Status.BusySecureWipeDMem; 303 break; 304 case Command.WipeIMem: 305 status.Value = Status.BusySecureWipeIMem; 306 break; 307 default: 308 this.Log(LogLevel.Error, "Unrecognized command : 0x{0:X}", command); 309 return; 310 } 311 } 312 HandleCommand(Command command)313 private void HandleCommand(Command command) 314 { 315 switch(command) 316 { 317 case Command.Execute: 318 Execute(); 319 break; 320 case Command.WipeDMem: 321 dataMemory.ZeroAll(); 322 break; 323 case Command.WipeIMem: 324 instructionsMemory.ZeroAll(); 325 break; 326 default: 327 this.Log(LogLevel.Error, "Unrecognized command : 0x{0:X}", command); 328 return; 329 } 330 331 doneInterruptState.Value = true; 332 status.Value = Status.Idle; 333 UpdateInterrupts(); 334 } 335 Execute()336 private void Execute() 337 { 338 this.Log(LogLevel.Debug, "Starting execution"); 339 var result = ExecutionResult.Ok; 340 executedInstructionsTotal = 0; 341 342 core.Reset(); 343 while(result == ExecutionResult.Ok) 344 { 345 result = core.ExecuteInstructions(1, out var executedInstructions); 346 this.Log(LogLevel.Debug, "Core executed {0} instructions and returned {1}", executedInstructions, result); 347 348 if(result == ExecutionResult.Aborted) 349 { 350 switch((CoreError)core.LastError) 351 { 352 case CoreError.BadDataAddress: 353 badDataAddressError.Value = true; 354 break; 355 356 case CoreError.BadInstructionAddress: 357 badInstructionAddressError.Value = true; 358 break; 359 360 case CoreError.CallStack: 361 callStackError.Value = true; 362 break; 363 364 case CoreError.IllegalInstruction: 365 illegalInstructionError.Value = true; 366 break; 367 368 case CoreError.Loop: 369 loopError.Value = true; 370 break; 371 } 372 break; 373 } 374 375 executedInstructionsTotal += executedInstructions; 376 } 377 378 this.Log(LogLevel.Debug, "Execution finished"); 379 } 380 InitOtbnCore(OpenTitan_ScrambledMemory iMem, OpenTitan_ScrambledMemory dMem)381 private IOpenTitan_BigNumberAcceleratorCore InitOtbnCore(OpenTitan_ScrambledMemory iMem, OpenTitan_ScrambledMemory dMem) 382 { 383 // As the binary with needed type is included during the runtime we must use reflection 384 Type coreType = null; 385 foreach(Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) 386 { 387 if(asm.FullName.StartsWith("Infrastructure")) 388 { 389 coreType = asm.GetType("Antmicro.Renode.Peripherals.CPU.OpenTitan_BigNumberAcceleratorCore", false); 390 if(coreType != null) 391 { 392 break; 393 } 394 } 395 } 396 397 if(coreType == null) 398 { 399 throw new ConstructionException($"Couldn't find the OpenTitan_BigNumberAcceleratorCore class. Check your Renode installation"); 400 } 401 402 var constructor = coreType.GetConstructor(new Type[] { typeof(OpenTitan_BigNumberAccelerator), typeof(OpenTitan_ScrambledMemory), typeof(OpenTitan_ScrambledMemory) }); 403 return (IOpenTitan_BigNumberAcceleratorCore)constructor.Invoke(new object [] { this, iMem, dMem }); 404 } 405 406 private IFlagRegisterField badDataAddressError; 407 private IFlagRegisterField badInstructionAddressError; 408 private IFlagRegisterField illegalInstructionError; 409 private IFlagRegisterField callStackError; 410 private IFlagRegisterField loopError; 411 412 private IFlagRegisterField doneInterruptEnable; 413 private IFlagRegisterField doneInterruptState; 414 private IEnumRegisterField<Status> status; 415 416 private ulong executedInstructionsTotal; 417 private IOpenTitan_BigNumberAcceleratorCore core; 418 419 private readonly OpenTitan_ScrambledMemory dataMemory; 420 private readonly OpenTitan_ScrambledMemory instructionsMemory; 421 422 private const int DataMemoryWindowsCount = 1024; 423 private const int InstructionsMemoryWindowsCount = 1024; 424 425 private enum Command 426 { 427 Execute = 0xd8, 428 WipeDMem = 0xc3, 429 WipeIMem = 0x1e, 430 } 431 432 private enum Status 433 { 434 Idle = 0x0, 435 BusyExecute = 0x1, 436 BusySecureWipeDMem = 0x2, 437 BusySecureWipeIMem = 0x3, 438 BusySecureWipeInternal = 0x4, 439 Locked = 0xFF, 440 } 441 442 private enum Registers 443 { 444 InterruptState = 0x0, 445 InterruptEnable = 0x4, 446 InterruptTest = 0x8, 447 AlertTest = 0xc, 448 Command = 0x10, 449 Control = 0x14, 450 Status = 0x18, 451 OperationResult = 0x1c, 452 FatalAlertCause = 0x20, 453 InstructionCount = 0x24, 454 A32bitCRCchecksumofdatawrittentomemory = 0x28, 455 InstructionMemoryAccess = 0x4000, 456 DataMemoryAccess = 0x8000, 457 } 458 } 459 460 public enum CoreError 461 { 462 None = 0b00000, 463 BadDataAddress = 0b00001, 464 BadInstructionAddress = 0b00010, 465 CallStack = 0b00100, 466 IllegalInstruction = 0b01000, 467 Loop = 0b10000, 468 // Other errors not implemented yet 469 } 470 } 471