1 // 2 // Copyright (c) 2010-2025 Antmicro 3 // Copyright (c) 2022-2025 Silicon Labs 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 9 using System; 10 using System.Collections.Generic; 11 using System.IO; 12 using Antmicro.Renode.Core; 13 using Antmicro.Renode.Core.Structure.Registers; 14 using Antmicro.Renode.Exceptions; 15 using Antmicro.Renode.Logging; 16 using Antmicro.Renode.Peripherals.Bus; 17 using Antmicro.Renode.Peripherals.Timers; 18 using Antmicro.Renode.Peripherals.Memory; 19 using Antmicro.Renode.Time; 20 using Org.BouncyCastle.Crypto.Engines; 21 using Org.BouncyCastle.Crypto.Macs; 22 using Org.BouncyCastle.Crypto.Parameters; 23 24 namespace Antmicro.Renode.Peripherals.Miscellaneous.SiLabs 25 { 26 [AllowedTranslations(AllowedTranslation.ByteToDoubleWord | AllowedTranslation.WordToDoubleWord)] 27 public class EFR32xG2_RNGCTRL : IBusPeripheral 28 { EFR32xG2_RNGCTRL(Machine machine)29 public EFR32xG2_RNGCTRL(Machine machine) 30 { 31 this.machine = machine; 32 33 fifo = new Queue<uint>(); 34 cbcMacCipher = new CbcBlockCipherMac(new AesEngine(), 128); 35 36 IRQ = new GPIO(); 37 registersCollection = BuildRegistersCollection(); 38 } 39 Reset()40 public void Reset() 41 { 42 SoftwareReset = false; 43 SoftwareReset = true; 44 SoftwareReset = false; 45 } 46 47 [ConnectionRegionAttribute("rngctrl_s")] ReadDoubleWordRegisterSecure(long offset)48 public uint ReadDoubleWordRegisterSecure(long offset) 49 { 50 return ReadRegister(offset); 51 } 52 53 [ConnectionRegionAttribute("rngctrl_ns")] ReadDoubleWordRegisterNonSecure(long offset)54 public uint ReadDoubleWordRegisterNonSecure(long offset) 55 { 56 return ReadRegister(offset); 57 } 58 59 [ConnectionRegionAttribute("rngctrl_s")] WriteDoubleWordRegisterSecure(long offset, uint value)60 public void WriteDoubleWordRegisterSecure(long offset, uint value) 61 { 62 WriteRegister(offset, value); 63 } 64 65 [ConnectionRegionAttribute("rngctrl_ns")] WriteDoubleWordRegisterNonSecure(long offset, uint value)66 public void WriteDoubleWordRegisterNonSecure(long offset, uint value) 67 { 68 WriteRegister(offset, value); 69 } 70 71 [ConnectionRegionAttribute("rngfifo_s")] ReadDoubleWordFifoSecure(long offset)72 public uint ReadDoubleWordFifoSecure(long offset) 73 { 74 return ReadFifo(offset); 75 } 76 77 [ConnectionRegionAttribute("rngfifo_ns")] ReadDoubleWordFifoNonSecure(long offset)78 public uint ReadDoubleWordFifoNonSecure(long offset) 79 { 80 return ReadFifo(offset); 81 } 82 83 [ConnectionRegionAttribute("rngfifo_s")] WriteDoubleWordFifoSecure(long offset, uint value)84 public void WriteDoubleWordFifoSecure(long offset, uint value) 85 { 86 // Writing not supported 87 } 88 89 [ConnectionRegionAttribute("rngfifo_ns")] WriteDoubleWordFifoNonSecure(long offset, uint value)90 public void WriteDoubleWordFifoNonSecure(long offset, uint value) 91 { 92 // Writing not supported 93 } 94 ReadRegister(long offset)95 public uint ReadRegister(long offset) 96 { 97 var result = 0U; 98 99 if(!registersCollection.TryRead(offset, out result)) 100 { 101 this.Log(LogLevel.Noisy, "Unhandled read at offset 0x{0:X} ({1}).", offset, (Registers)offset); 102 } 103 else 104 { 105 this.Log(LogLevel.Noisy, "Read at offset 0x{0:X} ({1}), returned 0x{2:X}.", offset, (Registers)offset, result); 106 } 107 108 return result; 109 } 110 WriteRegister(long offset, uint value)111 public void WriteRegister(long offset, uint value) 112 { 113 this.Log(LogLevel.Noisy, "Write at offset 0x{0:X} ({1}), value 0x{2:X}.", offset, (Registers)offset, value); 114 if(!registersCollection.TryWrite(offset, value)) 115 { 116 this.Log(LogLevel.Noisy, "Unhandled write at offset 0x{0:X} ({1}), value 0x{2:X}.", offset, (Registers)offset, value); 117 } 118 } 119 ReadFifo(long offset)120 public uint ReadFifo(long offset) 121 { 122 return FifoDequeue(); 123 } 124 BuildRegistersCollection()125 private DoubleWordRegisterCollection BuildRegistersCollection() 126 { 127 var registerDictionary = new Dictionary<long, DoubleWordRegister> 128 { 129 {(long)Registers.RngControl, new DoubleWordRegister(this, 0x00040000) 130 .WithFlag(0, valueProviderCallback: _ => Enable, writeCallback: (_, value) => Enable = value, name: "ENABLE") 131 .WithTaggedFlag("CONTROL", 1) 132 .WithFlag(2, out testEnable, name: "TESTEN") 133 .WithTaggedFlag("CONDBYPASS", 3) 134 .WithTaggedFlag("REPCOUNTIEN", 4) 135 .WithTaggedFlag("APT64IEN", 5) 136 .WithTaggedFlag("APT4096IEN", 6) 137 .WithFlag(7, out fifoFullInterruptEnable, name: "FULLIEN") 138 .WithFlag(8, valueProviderCallback: _ => SoftwareReset, writeCallback: (_, value) => SoftwareReset = value, name: "SOFTRESET") 139 .WithTaggedFlag("PREIEN", 9) 140 .WithTaggedFlag("ALMIEN", 10) 141 .WithTaggedFlag("FORCERUN", 11) 142 .WithTaggedFlag("BYPNIST", 12) 143 .WithTaggedFlag("BYPAIS31", 13) 144 .WithTaggedFlag("HEALTHTESTSEL", 14) 145 .WithTaggedFlag("AIS31TESTSEL", 15) 146 .WithValueField(16, 4, valueProviderCallback: _ => CbcMacBlockNumber, writeCallback: (_, value) => CbcMacBlockNumber = (uint)value, name: "NB128BITBLOCKS") 147 .WithTaggedFlag("FIFOWRSTARTUP", 20) 148 .WithReservedBits(21, 11) 149 .WithChangeCallback((_, __) => UpdateInterrupts()) 150 }, 151 {(long)Registers.FifoLevel, new DoubleWordRegister(this) 152 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => FifoLevel, name: "FIFOLEVEL") 153 }, 154 {(long)Registers.FifoDepth, new DoubleWordRegister(this, 0x40) 155 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => FifoDepth, name: "FIFODEPTH") 156 }, 157 {(long)Registers.RngStatus, new DoubleWordRegister(this) 158 .WithTaggedFlag("TESTDATABUSY", 0) 159 .WithEnumField<DoubleWordRegister, RngState>(1, 3, FieldMode.Read, valueProviderCallback: _ => State, name: "RXSETEVENT1") 160 .WithTaggedFlag("REPCOUNTIF", 4) 161 .WithTaggedFlag("APT64IF", 5) 162 .WithTaggedFlag("APT4096IF", 6) 163 .WithFlag(7, out fifoFullInterrupt, name: "FULLIF") 164 .WithTaggedFlag("PREIF", 8) 165 .WithTaggedFlag("ALMIF", 9) 166 .WithTaggedFlag("STARTUPPASS", 10) 167 .WithReservedBits(11, 21) 168 .WithWriteCallback((_, __) => UpdateInterrupts()) 169 }, 170 {(long)Registers.Key0, new DoubleWordRegister(this) 171 .WithValueField(0, 32, valueProviderCallback: _ => GetKeyDoubleWord(0), writeCallback: (_, value) => SetKeyDoubleWord(0, (uint)value), name: "KEY") 172 }, 173 {(long)Registers.Key1, new DoubleWordRegister(this) 174 .WithValueField(0, 32, valueProviderCallback: _ => GetKeyDoubleWord(1), writeCallback: (_, value) => SetKeyDoubleWord(1, (uint)value), name: "KEY") 175 }, 176 {(long)Registers.Key2, new DoubleWordRegister(this) 177 .WithValueField(0, 32, valueProviderCallback: _ => GetKeyDoubleWord(2), writeCallback: (_, value) => SetKeyDoubleWord(2, (uint)value), name: "KEY") 178 }, 179 {(long)Registers.Key3, new DoubleWordRegister(this) 180 .WithValueField(0, 32, valueProviderCallback: _ => GetKeyDoubleWord(3), writeCallback: (_, value) => SetKeyDoubleWord(3, (uint)value), name: "KEY") 181 }, 182 {(long)Registers.TestData, new DoubleWordRegister(this) 183 .WithValueField(0, 32, FieldMode.Write, writeCallback: (_, value) => TestData = (uint)value, name: "VALUE") 184 }, 185 }; 186 return new DoubleWordRegisterCollection(this, registerDictionary); 187 } 188 189 public GPIO IRQ { get; } 190 private readonly Machine machine; 191 private static PseudorandomNumberGenerator random = EmulationManager.Instance.CurrentEmulation.RandomGenerator; 192 private readonly DoubleWordRegisterCollection registersCollection; 193 private const uint FifoDepth = 64; 194 private RngState state; 195 private bool enabled = false; 196 private bool softwareReset = false; 197 private Queue<uint> fifo; 198 private byte[] key = new byte[16]; 199 private byte[] cbcMacBlock = new byte[16]; 200 private uint cbcMacBlockIndex; 201 private uint cbcMacBlockNumber; 202 private uint conditioningTestDataCurrentSize; 203 private bool conditioningTestOngoing = false; 204 private CbcBlockCipherMac cbcMacCipher; 205 #region register fields 206 private IFlagRegisterField fifoFullInterruptEnable; 207 private IFlagRegisterField fifoFullInterrupt; 208 private IFlagRegisterField testEnable; 209 #endregion 210 211 #region methods GetTime()212 private TimeInterval GetTime() => machine.LocalTimeSource.ElapsedVirtualTime; 213 UpdateInterrupts()214 private void UpdateInterrupts() 215 { 216 machine.ClockSource.ExecuteInLock(delegate { 217 var irq = (fifoFullInterruptEnable.Value && fifoFullInterrupt.Value); 218 IRQ.Set(irq); 219 }); 220 } 221 GetKeyDoubleWord(uint index)222 private uint GetKeyDoubleWord(uint index) 223 { 224 if (index > 3) 225 { 226 this.Log(LogLevel.Error, "GetKeyDoubleWord(): index invalid"); 227 return 0; 228 } 229 230 return ((uint)key[index * 4] | (uint)(key[(index * 4) + 1] << 8) | (uint)(key[(index * 4) + 2] << 16) | (uint)(key[(index * 4) + 3] << 24)); 231 } 232 SetKeyDoubleWord(uint index, uint value)233 private void SetKeyDoubleWord(uint index, uint value) 234 { 235 if (index > 3) 236 { 237 this.Log(LogLevel.Error, "SetKeyDoubleWord(): index invalid"); 238 return; 239 } 240 241 key[index * 4] = (byte)(value & 0xFF); 242 key[(index * 4) + 1] = (byte)((value >> 8) & 0xFF); 243 key[(index * 4) + 2] = (byte)((value >> 16) & 0xFF); 244 key[(index * 4) + 3] = (byte)((value >> 24) & 0xFF); 245 } 246 FifoEnqueue(uint value)247 private void FifoEnqueue(uint value) 248 { 249 if (fifo.Count == FifoDepth) 250 { 251 // Ignore 252 return; 253 } 254 255 fifo.Enqueue(value); 256 257 if (fifo.Count == FifoDepth) 258 { 259 fifoFullInterrupt.Value = true; 260 UpdateInterrupts(); 261 } 262 } 263 FifoDequeue()264 private uint FifoDequeue() 265 { 266 if (fifo.Count == 0) 267 { 268 this.Log(LogLevel.Warning, "FifoDequeue(): queue is empty!"); 269 return 0; 270 } 271 272 uint ret = fifo.Dequeue(); 273 FillQueue(); 274 return ret; 275 } 276 FillQueue()277 private void FillQueue() 278 { 279 while(fifo.Count < FifoDepth) 280 { 281 FifoEnqueue((uint)random.Next()); 282 } 283 } 284 285 private bool Enable 286 { 287 get 288 { 289 return enabled; 290 } 291 set 292 { 293 if (!enabled && value) 294 { 295 enabled = true; 296 if (fifo.Count > 0) 297 { 298 this.Log(LogLevel.Warning, "Fifo not empty upon enable!"); 299 } 300 State = RngState.Running; 301 FillQueue(); 302 State = RngState.FifoFullOff; 303 } 304 305 if (!value) 306 { 307 enabled = false; 308 State = RngState.Reset; 309 } 310 } 311 } 312 313 private bool SoftwareReset 314 { 315 get 316 { 317 return softwareReset; 318 } 319 set 320 { 321 if (!softwareReset) 322 { 323 softwareReset = true; 324 State = RngState.Reset; 325 enabled = false; 326 conditioningTestOngoing = false; 327 fifo.Clear(); 328 } 329 } 330 } 331 332 private uint FifoLevel 333 { 334 get 335 { 336 return (uint)fifo.Count; 337 } 338 } 339 340 private RngState State 341 { 342 set 343 { 344 state = value; 345 } 346 get 347 { 348 return state; 349 } 350 } 351 352 private uint CbcMacBlockNumber 353 { 354 get 355 { 356 return cbcMacBlockNumber; 357 } 358 set 359 { 360 if (value != 0) 361 { 362 cbcMacBlockNumber = value; 363 } 364 } 365 } 366 367 private uint TestData 368 { 369 set 370 { 371 if (!testEnable.Value) 372 { 373 return; 374 } 375 376 if (!conditioningTestOngoing) 377 { 378 conditioningTestOngoing = true; 379 conditioningTestDataCurrentSize = 0; 380 cbcMacBlockIndex = 0; 381 KeyParameter keyParameter = new KeyParameter(key); 382 byte[] iv = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; 383 ParametersWithIV parametersWithIV = new ParametersWithIV(keyParameter, iv); 384 cbcMacCipher.Init(parametersWithIV); 385 this.Log(LogLevel.Noisy, "Creating CBCMAC object, key=[{0}]", BitConverter.ToString(key)); 386 } 387 388 if (conditioningTestDataCurrentSize <= CbcMacBlockNumber*16 - 4) 389 { 390 conditioningTestDataCurrentSize += 4; 391 cbcMacBlock[cbcMacBlockIndex + 3] = (byte)((value >> 24) & 0xFF); 392 cbcMacBlock[cbcMacBlockIndex + 2] = (byte)((value >> 16) & 0xFF); 393 cbcMacBlock[cbcMacBlockIndex + 1] = (byte)((value >> 8) & 0xFF); 394 cbcMacBlock[cbcMacBlockIndex] = (byte)(value & 0xFF); 395 cbcMacBlockIndex += 4; 396 397 if (cbcMacBlockIndex == 16) 398 { 399 this.Log(LogLevel.Noisy, "Adding block=[{0}]", BitConverter.ToString(cbcMacBlock)); 400 cbcMacCipher.BlockUpdate(cbcMacBlock, 0, 16); 401 cbcMacBlockIndex = 0; 402 } 403 } 404 405 if (conditioningTestDataCurrentSize == CbcMacBlockNumber*16) 406 { 407 conditioningTestOngoing = false; 408 byte[] output = new byte[16]; 409 cbcMacCipher.DoFinal(output, 0); 410 this.Log(LogLevel.Noisy, "CBC output=[{0}]", BitConverter.ToString(output)); 411 for(uint i = 0; i < 4; i++) 412 { 413 uint val = (uint)output[i*4] | ((uint)output[i*4 + 1] << 8) | ((uint)output[i*4 + 2] << 16) | ((uint)output[i*4 + 3] << 24); 414 FifoEnqueue(val); 415 } 416 } 417 } 418 } 419 #endregion 420 421 #region enums 422 private enum RngState 423 { 424 Reset = 0, 425 Startup = 1, 426 FifoFullOn = 2, 427 FifoFullOff = 3, 428 Running = 4, 429 Error = 5, 430 Unused_6 = 6, 431 Unused = 7, 432 } 433 434 private enum Registers 435 { 436 RngControl = 0x000, 437 FifoLevel = 0x004, 438 FifoThreshold = 0x008, 439 FifoDepth = 0x00C, 440 Key0 = 0x010, 441 Key1 = 0x014, 442 Key2 = 0x018, 443 Key3 = 0x01C, 444 TestData = 0x020, 445 RepThreshold = 0x024, 446 PropThreshold = 0x028, 447 RngStatus = 0x030, 448 InitWaitCounter = 0x034, 449 DisableOscillatorRings0 = 0x038, 450 DisableOscillatorRings1 = 0x03C, 451 SwitchOffTimer = 0x040, 452 ClockDivider = 0x044, 453 AIS31Configuration0 = 0x048, 454 AIS31Configuration1 = 0x04C, 455 AIS31Configuration2 = 0x050, 456 AIS31Status = 0x054, 457 } 458 #endregion 459 } 460 }