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 }