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.Linq;
12 using System.IO;
13 using Antmicro.Renode.Core;
14 using Antmicro.Renode.Core.Structure.Registers;
15 using Antmicro.Renode.Exceptions;
16 using Antmicro.Renode.Logging;
17 using Antmicro.Renode.Peripherals.Bus;
18 using Antmicro.Renode.Peripherals.Timers;
19 using Antmicro.Renode.Time;
20 using Antmicro.Renode.Utilities;
21 using Antmicro.Renode.Utilities.Packets;
22 using Org.BouncyCastle.Crypto;
23 using Org.BouncyCastle.Crypto.Engines;
24 using Org.BouncyCastle.Crypto.Modes;
25 using Org.BouncyCastle.Crypto.Macs;
26 using Org.BouncyCastle.Crypto.Parameters;
27 using Org.BouncyCastle.Crypto.Digests;
28 
29 namespace Antmicro.Renode.Peripherals.Miscellaneous.SiLabs
30 {
31     public class EFR32xG2_SEMAILBOX_1 : IDoubleWordPeripheral, IKnownSize
32     {
EFR32xG2_SEMAILBOX_1(Machine machine)33         public EFR32xG2_SEMAILBOX_1(Machine machine)
34         {
35             this.machine = machine;
36 
37             txFifo = new Queue<uint>();
38             rxFifo = new Queue<uint>();
39 
40             RxIRQ = new GPIO();
41             TxIRQ = new GPIO();
42 
43             Silabs_SecureElement = new Silabs_SecureElement(machine, this, txFifo, rxFifo, false);
44 
45             registersCollection = BuildRegistersCollection();
46         }
47 
Reset()48         public void Reset()
49         {
50             txFifo.Clear();
51             rxFifo.Clear();
52             Silabs_SecureElement.Reset();
53             rxHeaderAvailable = false;
54         }
55 
ReadDoubleWord(long offset)56         public uint ReadDoubleWord(long offset)
57         {
58             var result = 0U;
59 
60             if(!registersCollection.TryRead(offset, out result))
61             {
62                 this.Log(LogLevel.Noisy, "Unhandled read at offset 0x{0:X} ({1}).", offset, (Registers)offset);
63             }
64             else
65             {
66                 this.Log(LogLevel.Noisy, "Read at offset 0x{0:X} ({1}), returned 0x{2:X}.", offset, (Registers)offset, result);
67             }
68 
69             return result;
70         }
71 
WriteDoubleWord(long offset, uint value)72         public void WriteDoubleWord(long offset, uint value)
73         {
74             this.Log(LogLevel.Noisy, "Write at offset 0x{0:X} ({1}), value 0x{2:X}.", offset, (Registers)offset, value);
75             if(!registersCollection.TryWrite(offset, value))
76             {
77                 this.Log(LogLevel.Noisy, "Unhandled write at offset 0x{0:X} ({1}), value 0x{2:X}.", offset, (Registers)offset, value);
78                 return;
79             }
80         }
81 
BuildRegistersCollection()82         private DoubleWordRegisterCollection BuildRegistersCollection()
83         {
84             var registerDictionary = new Dictionary<long, DoubleWordRegister>
85             {
86                 {(long)Registers.TxStatus, new DoubleWordRegister(this)
87                     .WithValueField(0, 16, FieldMode.Read, valueProviderCallback: _ => (uint)TxFifoWordsCount, name: "REMBYTES")
88                     .WithValueField(16, 4, FieldMode.Read, valueProviderCallback: _ => 0 /* TODO */, name: "MSGINFO")
89                     .WithFlag(20, FieldMode.Read, valueProviderCallback: _ => !TxFifoIsAlmostFull, name: "TXINT")
90                     .WithFlag(21, FieldMode.Read, valueProviderCallback: _ => TxFifoIsFull, name: "TXFULL")
91                     .WithReservedBits(22, 1)
92                     .WithFlag(23, FieldMode.Read, valueProviderCallback: _ => false /* TODO */, name: "TXERROR")
93                     .WithReservedBits(24, 8)
94                 },
95                 {(long)Registers.RxStatus, new DoubleWordRegister(this)
96                     .WithValueField(0, 16, FieldMode.Read, valueProviderCallback: _ => (uint)RxFifoWordsCount, name: "REMBYTES")
97                     .WithValueField(16, 4, FieldMode.Read, valueProviderCallback: _ => 0 /* TODO */, name: "MSGINFO")
98                     .WithFlag(20, out rxInterrupt, FieldMode.Read, name: "RXINT")
99                     .WithFlag(21, FieldMode.Read, valueProviderCallback: _ => RxFifoIsEmpty, name: "RXEMPTY")
100                     .WithFlag(22, FieldMode.Read, valueProviderCallback: _ => RxHeaderAvailable, name: "RXHDR")
101                     .WithFlag(23, FieldMode.Read, valueProviderCallback: _ => false /* TODO */, name: "RXERROR")
102                     .WithReservedBits(24, 8)
103                 },
104                 {(long)Registers.TxProtection, new DoubleWordRegister(this)
105                     .WithReservedBits(0, 21)
106                     .WithTaggedFlag("UNPROTECTED", 21)
107                     .WithTaggedFlag("PRIVILGED", 22)
108                     .WithTaggedFlag("NONSECURE", 23)
109                     .WithTag("USER", 24, 8)
110                 },
111                 {(long)Registers.RxProtection, new DoubleWordRegister(this)
112                     .WithReservedBits(0, 21)
113                     .WithTaggedFlag("UNPROTECTED", 21)
114                     .WithTaggedFlag("PRIVILGED", 22)
115                     .WithTaggedFlag("NONSECURE", 23)
116                     .WithTag("USER", 24, 8)
117                 },
118                 {(long)Registers.TxHeader, new DoubleWordRegister(this)
119                     .WithValueField(0, 32, FieldMode.Write, writeCallback: (_, value) => { TxHeader = (uint)value; }, name: "TXHEADER")
120                 },
121                 {(long)Registers.RxHeader, new DoubleWordRegister(this)
122                     .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => (uint)RxHeader, name: "RXHEADER")
123                 },
124                 {(long)Registers.Config, new DoubleWordRegister(this)
125                     .WithFlag(0, out txInterruptEnable, name: "TXINTEN")
126                     .WithFlag(1, out rxInterruptEnable, name: "RXINTEN")
127                     .WithReservedBits(2, 30)
128                     .WithChangeCallback((_, __) => UpdateInterrupts())
129                 },
130             };
131 
132             var startOffset = (long)Registers.Fifo0;
133             var blockSize = (long)Registers.Fifo1 - (long)Registers.Fifo0;
134 
135             for(var index = 0; index < FifoWordSize; index++)
136             {
137                 var i = index;
138 
139                 registerDictionary.Add(startOffset + blockSize*i,
140                     new DoubleWordRegister(this)
141                         .WithValueField(0, 32, valueProviderCallback: _ => RxFifoDequeue(), writeCallback: (_, value) => { TxFifoEnqueue((uint)value); }, name: $"FIFO{i}")
142                 );
143             }
144             return new DoubleWordRegisterCollection(this, registerDictionary);
145         }
146 
147         public long Size => 0x4000;
148         public GPIO RxIRQ { get; }
149         public GPIO TxIRQ { get; }
150 
151 #region fields
152         protected readonly Machine machine;
153         protected readonly DoubleWordRegisterCollection registersCollection;
154         private readonly Silabs_SecureElement Silabs_SecureElement;
155         private const uint FifoWordSize = 16;
156         // TODO: according to the design book, TXSTATUS.TXINT field: "Interrupt status (same value as interrupt signal).
157         // High when TX FIFO is not almost-full (enough available space to start sending a message)."
158         // As of now I don't know what "enough available space to send a message" means, so for now I assume a message
159         // needs the whole FIFO.
160         private const uint TxFifoAlmostFullThreshold = 1;
161         private Queue<uint> txFifo;
162         private Queue<uint> rxFifo;
163         private int TxFifoWordsCount => txFifo.Count;
164         private int RxFifoWordsCount => rxFifo.Count;
165         private bool TxFifoIsAlmostFull => (TxFifoWordsCount >= TxFifoAlmostFullThreshold);
166         private bool TxFifoIsFull => (TxFifoWordsCount == FifoWordSize);
167         private bool RxFifoIsFull => (RxFifoWordsCount == FifoWordSize);
168         private bool TxFifoIsEmpty => (TxFifoWordsCount == 0);
169         private bool RxFifoIsEmpty => (RxFifoWordsCount == 0);
170         private bool rxHeaderAvailable = false;
171         private IFlagRegisterField txInterruptEnable;
172         private IFlagRegisterField rxInterruptEnable;
173         private IFlagRegisterField rxInterrupt;
174 
175         private uint TxHeader
176         {
177             set
178             {
179                 Silabs_SecureElement.TxHeaderSetCallback(value);
180                 TxFifoEnqueue(value);
181             }
182         }
183 
184         private uint RxHeader
185         {
186             get
187             {
188                 uint retValue;
189                 if (RxHeaderAvailable)
190                 {
191                     retValue = RxFifoDequeue();
192                     RxHeaderAvailable = false;
193                 }
194                 else
195                 {
196                     // Return an error response code in case the RXHEADER is not available.
197                     retValue = Silabs_SecureElement.GetDefaultErrorStatus();
198                 }
199                 rxInterrupt.Value = false;
200                 UpdateInterrupts();
201                 return retValue;
202             }
203         }
204 
205         private bool RxHeaderAvailable
206         {
207             get
208             {
209                 return rxHeaderAvailable;
210             }
211 
212             set
213             {
214                 rxHeaderAvailable = value;
215             }
216         }
217 #endregion
218 
219 #region system methods
UpdateInterrupts()220         private void UpdateInterrupts()
221         {
222             machine.ClockSource.ExecuteInLock(delegate {
223                 // TXINT: Interrupt status (same value as interrupt signal).
224                 // High when TX FIFO is not almost-full (enough available space to start sending a message).
225                 var irq = txInterruptEnable.Value && !TxFifoIsAlmostFull;
226                 if (irq)
227                 {
228                     this.Log(LogLevel.Noisy, "IRQ TX set");
229                 }
230                 TxIRQ.Set(irq);
231 
232                 // RXINT: Interrupt status (same value as interrupt signal). High when RX FIFO is not almost-empty
233                 // or when the end of the message is ready in the FIFO (enough data available to start reading).
234                 irq = rxInterruptEnable.Value && rxInterrupt.Value;
235                 if (irq)
236                 {
237                     this.Log(LogLevel.Noisy, "IRQ RX set");
238                 }
239                 RxIRQ.Set(irq);
240             });
241         }
242 
TxFifoEnqueue(uint value)243         private void TxFifoEnqueue(uint value)
244         {
245             if (!TxFifoIsFull)
246             {
247                 txFifo.Enqueue(value);
248 
249                 // If true, a command was processed and a response was added to the RX queue.
250                 if (Silabs_SecureElement.TxFifoEnqueueCallback(value))
251                 {
252                     rxInterrupt.Value = true;
253                     RxHeaderAvailable = true;
254                 }
255 
256                 UpdateInterrupts();
257             }
258             else
259             {
260                 this.Log(LogLevel.Error, "TxFifoEnqueue(): queue is FULL!");
261             }
262         }
263 
TxFifoDequeue()264         private uint TxFifoDequeue()
265         {
266             uint ret = 0;
267 
268             if (!TxFifoIsEmpty)
269             {
270                 ret = txFifo.Dequeue();
271                 this.Log(LogLevel.Info, "TxFifo Dequeued: {0:X}", ret);
272                 UpdateInterrupts();
273             }
274             else
275             {
276                 this.Log(LogLevel.Error, "TxFifoDequeue(): queue is EMPTY!");
277             }
278 
279             return ret;
280         }
281 
RxFifoEnqueue(uint value)282         private void RxFifoEnqueue(uint value)
283         {
284             if (!RxFifoIsFull)
285             {
286                 rxFifo.Enqueue(value);
287             }
288             else
289             {
290                 this.Log(LogLevel.Error, "RxFifoEnqueue(): queue is FULL!");
291             }
292         }
293 
RxFifoDequeue()294         private uint RxFifoDequeue()
295         {
296             uint ret = 0;
297 
298             if (!RxFifoIsEmpty)
299             {
300                 ret = rxFifo.Dequeue();
301                 this.Log(LogLevel.Info, "RxFifo Dequeued: {0:X}", ret);
302             }
303             else
304             {
305                 this.Log(LogLevel.Error, "RxFifoDequeue(): queue is EMPTY!");
306             }
307 
308             return ret;
309         }
310 #endregion
311 
312 #region enums
313         private enum Registers
314         {
315             Fifo0           = 0x00,
316             Fifo1           = 0x04,
317             Fifo2           = 0x08,
318             Fifo3           = 0x0C,
319             Fifo4           = 0x10,
320             Fifo5           = 0x14,
321             Fifo6           = 0x18,
322             Fifo7           = 0x1C,
323             Fifo8           = 0x20,
324             Fifo9           = 0x24,
325             Fifo10          = 0x28,
326             Fifo11          = 0x2C,
327             Fifo12          = 0x30,
328             Fifo13          = 0x34,
329             Fifo14          = 0x38,
330             Fifo15          = 0x3C,
331             TxStatus        = 0x40,
332             RxStatus        = 0x44,
333             TxProtection    = 0x48,
334             RxProtection    = 0x4C,
335             TxHeader        = 0x50,
336             RxHeader        = 0x54,
337             Config          = 0x58,
338         }
339 #endregion
340     }
341 }