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.Time;
19 using Org.BouncyCastle.Crypto;
20 using Org.BouncyCastle.Crypto.Engines;
21 using Org.BouncyCastle.Crypto.Modes;
22 using Org.BouncyCastle.Crypto.Parameters;
23 
24 namespace Antmicro.Renode.Peripherals.Miscellaneous.SiLabs
25 {
26     public class EFR32xG2_AES_1 : IDoubleWordPeripheral, IKnownSize
27     {
EFR32xG2_AES_1(Machine machine)28         public EFR32xG2_AES_1(Machine machine)
29         {
30             this.machine = machine;
31 
32             IRQ = new GPIO();
33             registersCollection = BuildRegistersCollection();
34         }
35 
Reset()36         public void Reset()
37         {
38         }
39 
ReadDoubleWord(long offset)40         public uint ReadDoubleWord(long offset)
41         {
42             var result = 0U;
43             if(!registersCollection.TryRead(offset, out result))
44             {
45                 this.Log(LogLevel.Noisy, "Unhandled read at offset 0x{0:X} ({1}).", offset, (Registers)offset);
46             }
47             else
48             {
49                 this.Log(LogLevel.Noisy, "Read at offset 0x{0:X} ({1}), returned 0x{2:X}.", offset, (Registers)offset, result);
50             }
51             return result;
52         }
53 
WriteDoubleWord(long offset, uint value)54         public void WriteDoubleWord(long offset, uint value)
55         {
56             this.Log(LogLevel.Noisy, "Write at offset 0x{0:X} ({1}), value 0x{2:X}.", offset, (Registers)offset, value);
57             if(!registersCollection.TryWrite(offset, value))
58             {
59                 this.Log(LogLevel.Noisy, "Unhandled write at offset 0x{0:X} ({1}), value 0x{2:X}.", offset, (Registers)offset, value);
60                 return;
61             }
62         }
63 
BuildRegistersCollection()64         private DoubleWordRegisterCollection BuildRegistersCollection()
65         {
66             var registerDictionary = new Dictionary<long, DoubleWordRegister>
67             {
68                 {(long)Registers.FetcherAddress, new DoubleWordRegister(this)
69                     .WithValueField(0, 32, out fetcherAddress, name: "ADDR")
70                 },
71                 {(long)Registers.FetcherLength, new DoubleWordRegister(this)
72                     .WithValueField(0, 28, out fetcherLength, name: "LENGTH")
73                     .WithFlag(28, out fetcherConstantAddress, name: "CONSTADDR")
74                     .WithFlag(29, out fetcherRealignLength, name: "REALIGN")
75                     .WithReservedBits(30, 2)
76                 },
77                 {(long)Registers.FetcherTag, new DoubleWordRegister(this)
78                     .WithValueField(0, 32, out fetcherTag, name: "TAG")
79                 },
80                 {(long)Registers.PusherAddress, new DoubleWordRegister(this)
81                     .WithValueField(0, 32, out pusherAddress, name: "ADDR")
82                 },
83                 {(long)Registers.PusherLength, new DoubleWordRegister(this)
84                     .WithValueField(0, 28, out pusherLength, name: "LENGTH")
85                     .WithFlag(28, out pusherConstantAddress, name: "CONSTADDR")
86                     .WithFlag(29, out pusherRealignLength, name: "REALIGN")
87                     .WithFlag(30, out pusherDiscardData, name: "DISCARD")
88                     .WithReservedBits(31, 1)
89                 },
90                 {(long)Registers.InterruptEnable, new DoubleWordRegister(this)
91                     .WithFlag(0, out fetcherEndOfBlockInterruptEnable, name: "FETCHERENDOFBLOCKIEN")
92                     .WithFlag(1, out fetcherStoppedInterruptEnable, name: "FETCHERSTOPPEDIEN")
93                     .WithFlag(2, out fetcherErrorInterruptEnable, name: "FETCHERERRORIEN")
94                     .WithFlag(3, out pusherEndOfBlockInterruptEnable, name: "PUSHERENDOFBLOCKIEN")
95                     .WithFlag(4, out pusherStoppedInterruptEnable, name: "PUSHERSTOPPEDIEN")
96                     .WithFlag(5, out pusherErrorInterruptEnable, name: "PUSHERERRORIEN")
97                     .WithReservedBits(6, 26)
98                     .WithWriteCallback((_, __) => UpdateInterrupts())
99                 },
100                 {(long)Registers.InterruptEnableSet, new DoubleWordRegister(this)
101                     .WithFlag(0, FieldMode.Write, writeCallback: (_, value) => { if (value) fetcherEndOfBlockInterruptEnable.Value = true; }, name: "FETCHERENDOFBLOCKIENSET")
102                     .WithFlag(1, FieldMode.Write, writeCallback: (_, value) => { if (value) fetcherStoppedInterruptEnable.Value = true; }, name: "FETCHERSTOPPEDIENSET")
103                     .WithFlag(2, FieldMode.Write, writeCallback: (_, value) => { if (value) fetcherErrorInterruptEnable.Value = true; }, name: "FETCHERERRORIENSET")
104                     .WithFlag(3, FieldMode.Write, writeCallback: (_, value) => { if (value) pusherEndOfBlockInterruptEnable.Value = true; }, name: "PUSHERENDOFBLOCKIENSET")
105                     .WithFlag(4, FieldMode.Write, writeCallback: (_, value) => { if (value) pusherStoppedInterruptEnable.Value = true; }, name: "PUSHERSTOPPEDIENSET")
106                     .WithFlag(5, FieldMode.Write, writeCallback: (_, value) => { if (value) pusherErrorInterruptEnable.Value = true; }, name: "PUSHERERRORIENSET")
107                     .WithReservedBits(6, 26)
108                     .WithWriteCallback((_, __) => UpdateInterrupts())
109                 },
110                 {(long)Registers.InterruptEnableClear, new DoubleWordRegister(this)
111                     .WithFlag(0, FieldMode.Write, writeCallback: (_, value) => { if (value) fetcherEndOfBlockInterruptEnable.Value = false; }, name: "FETCHERENDOFBLOCKIENCLR")
112                     .WithFlag(1, FieldMode.Write, writeCallback: (_, value) => { if (value) fetcherStoppedInterruptEnable.Value = false; }, name: "FETCHERSTOPPEDIENCLR")
113                     .WithFlag(2, FieldMode.Write, writeCallback: (_, value) => { if (value) fetcherErrorInterruptEnable.Value = false; }, name: "FETCHERERRORIENCLR")
114                     .WithFlag(3, FieldMode.Write, writeCallback: (_, value) => { if (value) pusherEndOfBlockInterruptEnable.Value = false; }, name: "PUSHERENDOFBLOCKIENCLR")
115                     .WithFlag(4, FieldMode.Write, writeCallback: (_, value) => { if (value) pusherStoppedInterruptEnable.Value = false; }, name: "PUSHERSTOPPEDIENCLR")
116                     .WithFlag(5, FieldMode.Write, writeCallback: (_, value) => { if (value) pusherErrorInterruptEnable.Value = false; }, name: "PUSHERERRORIENCLR")
117                     .WithReservedBits(6, 26)
118                     .WithWriteCallback((_, __) => UpdateInterrupts())
119                 },
120                 {(long)Registers.InterruptFlag, new DoubleWordRegister(this)
121                     .WithFlag(0, out fetcherEndOfBlockInterrupt, FieldMode.Read, name: "FETCHERENDOFBLOCKIF")
122                     .WithFlag(1, out fetcherStoppedInterrupt, FieldMode.Read, name: "FETCHERSTOPPEDIF")
123                     .WithFlag(2, out fetcherErrorInterrupt, FieldMode.Read, name: "FETCHERERRORIF")
124                     .WithFlag(3, out pusherEndOfBlockInterrupt, FieldMode.Read, name: "PUSHERENDOFBLOCKIF")
125                     .WithFlag(4, out pusherStoppedInterrupt, FieldMode.Read, name: "PUSHERSTOPPEDIF")
126                     .WithFlag(5, out pusherErrorInterrupt, FieldMode.Read, name: "PUSHERERRORIF")
127                     .WithReservedBits(6, 26)
128                     .WithWriteCallback((_, __) => UpdateInterrupts())
129                 },
130                 {(long)Registers.InterruptFlagMasked, new DoubleWordRegister(this)
131                     .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => (fetcherEndOfBlockInterrupt.Value && fetcherEndOfBlockInterruptEnable.Value), name: "FETCHERENDOFBLOCKIF")
132                     .WithFlag(1, FieldMode.Read, valueProviderCallback: _ => (fetcherStoppedInterrupt.Value && fetcherStoppedInterruptEnable.Value), name: "FETCHERSTOPPEDIF")
133                     .WithFlag(2, FieldMode.Read, valueProviderCallback: _ => (fetcherErrorInterrupt.Value && fetcherErrorInterruptEnable.Value), name: "FETCHERERRORIF")
134                     .WithFlag(3, FieldMode.Read, valueProviderCallback: _ => (pusherEndOfBlockInterrupt.Value && pusherEndOfBlockInterruptEnable.Value), name: "PUSHERENDOFBLOCKIF")
135                     .WithFlag(4, FieldMode.Read, valueProviderCallback: _ => (pusherStoppedInterrupt.Value && pusherStoppedInterruptEnable.Value), name: "PUSHERSTOPPEDIF")
136                     .WithFlag(5, FieldMode.Read, valueProviderCallback: _ => (pusherErrorInterrupt.Value && pusherErrorInterruptEnable.Value), name: "PUSHERERRORIF")
137                     .WithReservedBits(6, 26)
138                     .WithWriteCallback((_, __) => UpdateInterrupts())
139                 },
140                 {(long)Registers.InterruptFlagClear, new DoubleWordRegister(this)
141                     .WithFlag(0, FieldMode.Write, writeCallback: (_, value) => { if (value) fetcherEndOfBlockInterrupt.Value = false; }, name: "FETCHERENDOFBLOCKIFCLR")
142                     .WithFlag(1, FieldMode.Write, writeCallback: (_, value) => { if (value) fetcherStoppedInterrupt.Value = false; }, name: "FETCHERSTOPPEDIFCLR")
143                     .WithFlag(2, FieldMode.Write, writeCallback: (_, value) => { if (value) fetcherErrorInterrupt.Value = false; }, name: "FETCHERERRORIFCLR")
144                     .WithFlag(3, FieldMode.Write, writeCallback: (_, value) => { if (value) pusherEndOfBlockInterrupt.Value = false; }, name: "PUSHERENDOFBLOCKIFCLR")
145                     .WithFlag(4, FieldMode.Write, writeCallback: (_, value) => { if (value) pusherStoppedInterrupt.Value = false; }, name: "PUSHERSTOPPEDIFCLR")
146                     .WithFlag(5, FieldMode.Write, writeCallback: (_, value) => { if (value) pusherErrorInterrupt.Value = false; }, name: "PUSHERERRORIFCLR")
147                     .WithReservedBits(6, 26)
148                     .WithWriteCallback((_, __) => UpdateInterrupts())
149                 },
150                 {(long)Registers.Control, new DoubleWordRegister(this)
151                     .WithFlag(0, out fetcherScatterGather, name: "FETCHERSCATTERGATHER")
152                     .WithFlag(1, out pusherScatterGather, name: "PUSHERSCATTERGATHER")
153                     .WithFlag(2, out fetcherStopAtEndOfBlock, name: "STOPFETCHER")
154                     .WithFlag(3, out pusherStopAtEndOfBlock, name: "STOPPUSHER")
155                     .WithTaggedFlag("SWRESET", 4)
156                     .WithReservedBits(5, 27)
157                 },
158                 {(long)Registers.Command, new DoubleWordRegister(this)
159                     .WithFlag(0, FieldMode.Write, writeCallback: (_, value) => { if (value) { StartFetcher(); } }, name: "STARTFETCHER")
160                     .WithFlag(1, FieldMode.Write, writeCallback: (_, value) => { if (value) { StartPusher(); } }, name: "STARTPUSHER")
161                     .WithReservedBits(2, 30)
162                 },
163                 {(long)Registers.Status, new DoubleWordRegister(this)
164                     .WithFlag(0, out fetcherBusy, FieldMode.Read, name: "FETCHERBSY")
165                     .WithFlag(1, out pusherBusy, FieldMode.Read, name: "PUSHERBSY")
166                     .WithReservedBits(2, 2)
167                     .WithTaggedFlag("NOTEMPTY", 4)
168                     .WithTaggedFlag("WAITING", 5)
169                     .WithTaggedFlag("SOFTRSTBSY", 6)
170                     .WithReservedBits(7, 9)
171                     .WithTag("FIFODATANUM", 16, 16)
172                     //.WithValueField(16, 16, FieldMode.Read, valueProviderCallback: _ => /* TODO */ 0, name: "FIFODATANUM")
173                 },
174             };
175             return new DoubleWordRegisterCollection(this, registerDictionary);
176         }
177 
178         public long Size => 0x4000;
179         public GPIO IRQ { get; }
180         private readonly Machine machine;
181         private readonly DoubleWordRegisterCollection registersCollection;
182 #region register fields
183         // Direct mode: written by SW (address of the first data)
184         // Scatter/gather mode: Written by SW (address of first descriptor). Afterwards, it is updated
185         //                      by the hardware after each processed descriptor.
186         private IValueRegisterField fetcherAddress;
187         // Direct mode: written by SW
188         // Scatter/gather mode: not used
189         private IValueRegisterField fetcherLength;
190         // Direct mode: written by SW
191         // Scatter/gather mode: not used
192         private IFlagRegisterField  fetcherConstantAddress;
193         // Direct mode: written by SW
194         // Scatter/gather mode: not used
195         private IFlagRegisterField  fetcherRealignLength;
196         // Direct mode: written by SW
197         // Scatter/gather mode: not used
198         private IValueRegisterField fetcherTag;
199         // When this bit is high, the fetcher will stop at the end of the current block
200         // (even if the STOP bit in the descriptor is low).
201         private IFlagRegisterField  fetcherStopAtEndOfBlock;
202         // When this bit is zero, the fetcher runs in direct mode.
203         // When this bit is one, the fetcher runs in scatter-gather mode.
204         private IFlagRegisterField  fetcherScatterGather;
205         // This bit is high as long as the fetcher is busy
206         private IFlagRegisterField fetcherBusy;
207         // Direct mode: written by SW (address of the first data)
208         // Scatter/gather mode: Written by SW (address of first descriptor). Afterwards, it is updated
209         //                      by the hardware after each processed descriptor.
210         private IValueRegisterField pusherAddress;
211         // Direct mode: written by SW
212         // Scatter/gather mode: not used
213         private IValueRegisterField pusherLength;
214         // Direct mode: written by SW
215         // Scatter/gather mode: not used
216         private IFlagRegisterField  pusherConstantAddress;
217         // Direct mode: written by SW
218         // Scatter/gather mode: not used
219         private IFlagRegisterField  pusherRealignLength;
220         // Direct mode: written by SW
221         // Scatter/gather mode: not used
222         private IFlagRegisterField  pusherDiscardData;
223         // When this bit is high, the pusher will stop at the end of the current block
224         // (even if the STOP bit in the descriptor is low).
225         private IFlagRegisterField  pusherStopAtEndOfBlock;
226         // When this bit is zero, the pusher runs in direct mode.
227         // When this bit is one, the pusher runs in scatter-gather mode.
228         private IFlagRegisterField  pusherScatterGather;
229         // This bit is high as long as the pusher is busy
230         private IFlagRegisterField pusherBusy;
231 
232         // Interrupt flags
233         private IFlagRegisterField fetcherEndOfBlockInterruptEnable;
234         private IFlagRegisterField fetcherStoppedInterruptEnable;
235         private IFlagRegisterField fetcherErrorInterruptEnable;
236         private IFlagRegisterField pusherEndOfBlockInterruptEnable;
237         private IFlagRegisterField pusherStoppedInterruptEnable;
238         private IFlagRegisterField pusherErrorInterruptEnable;
239         // Triggered at the end of each block (if enabled in the descriptor - scatter-gather only)
240         private IFlagRegisterField fetcherEndOfBlockInterrupt;
241         // Triggered when reaching a block with Stop=1 (or end of direct transfer)
242         private IFlagRegisterField fetcherStoppedInterrupt;
243         // Triggered when an error response is received from AXI
244         private IFlagRegisterField fetcherErrorInterrupt;
245         // Triggered at the end of each block (if enabled in the descriptor - scatter-gather only)
246         private IFlagRegisterField pusherEndOfBlockInterrupt;
247         // Triggered when reaching a block with Stop=1 (or end of direct transfer)
248         private IFlagRegisterField pusherStoppedInterrupt;
249         // Triggered when an error response is received from AXI
250         private IFlagRegisterField pusherErrorInterrupt;
251 #endregion
252 
253 #region methods
UpdateInterrupts()254         private void UpdateInterrupts()
255         {
256             machine.ClockSource.ExecuteInLock(delegate {
257                 var irq = ((fetcherEndOfBlockInterruptEnable.Value && fetcherEndOfBlockInterrupt.Value)
258                            || (fetcherStoppedInterruptEnable.Value || fetcherStoppedInterrupt.Value)
259                            || (fetcherErrorInterruptEnable.Value || fetcherErrorInterrupt.Value)
260                            || (pusherEndOfBlockInterruptEnable.Value && pusherEndOfBlockInterrupt.Value)
261                            || (pusherStoppedInterruptEnable.Value || pusherStoppedInterrupt.Value)
262                            || (pusherErrorInterruptEnable.Value || pusherErrorInterrupt.Value));
263                 IRQ.Set(irq);
264             });
265         }
266 
267         // Commmands
StartFetcher()268         private void StartFetcher()
269         {
270             // TODO: for now we support only Scatter/Gather mode for both fetcher and pusher
271             if (!fetcherScatterGather.Value || !pusherScatterGather.Value)
272             {
273                 this.Log(LogLevel.Error, "START_FETCHER: direct mode not supported");
274                 return;
275             }
276 
277             fetcherBusy.Value = true;
278             pusherBusy.Value = true;
279 
280             this.Log(LogLevel.Noisy, "FETCHER Descriptor(s):");
281             List<DmaDescriptor> fetcherDescriptorList = ParseDescriptors(true);
282             this.Log(LogLevel.Noisy, "PUSHER Descriptor(s):");
283             List<DmaDescriptor> pusherDescriptorList = ParseDescriptors(false);
284 
285             switch(fetcherDescriptorList[0].EngineSelect)
286             {
287                 case CryptoEngine.Aes:
288                     RunAesEngine(fetcherDescriptorList, pusherDescriptorList);
289                 break;
290                 default:
291                     this.Log(LogLevel.Error, "START_FETCHER: crypto engine not supported");
292                     return;
293             }
294 
295             fetcherBusy.Value = false;
296             pusherBusy.Value = false;
297 
298             // TODO: we might need to set some interrupt flags here. However, sl_radioaes driver does not make use of them.
299 
300             UpdateInterrupts();
301         }
302 
StartPusher()303         private void StartPusher()
304         {
305             // Nothing to do here, the StartFetcher command already grabs the pusher descriptors and writes them.
306         }
307 
ParseDescriptors(bool parseFetcher)308         private List<DmaDescriptor> ParseDescriptors(bool parseFetcher)
309         {
310             List<DmaDescriptor> list = new List<DmaDescriptor>();
311             uint currentDescriptorAddress = (uint)((parseFetcher) ? fetcherAddress.Value : pusherAddress.Value);
312             bool moreDescriptors = true;
313             uint descriptorIndex = 0;
314 
315             while(moreDescriptors)
316             {
317                 DmaDescriptor descriptor = new DmaDescriptor();
318                 descriptor.FirstDataAddress = machine.SystemBus.ReadDoubleWord(currentDescriptorAddress);
319                 descriptor.NextDescriptorAddress = machine.SystemBus.ReadDoubleWord(currentDescriptorAddress + 0x4);
320                 descriptor.LastDescriptor = ((descriptor.NextDescriptorAddress & 0x1) > 0);
321                 descriptor.NextDescriptorAddress &= ~0x3U;
322                 descriptor.Length = machine.SystemBus.ReadDoubleWord(currentDescriptorAddress + 0x8);
323                 descriptor.ConstantAddress = ((descriptor.Length & 0x10000000) > 0);
324                 descriptor.Realign = ((descriptor.Length & 0x20000000) > 0);
325                 if (parseFetcher)
326                 {
327                     descriptor.ZeroPadding = ((descriptor.Length & 0x40000000) > 0);
328                 }
329                 else
330                 {
331                     descriptor.Discard = ((descriptor.Length & 0x40000000) > 0);
332                 }
333                 descriptor.InterruptEnable = ((descriptor.Length & 0x80000000) > 0);
334                 descriptor.Length &= 0x0FFFFFFF;
335                 descriptor.Tag = (parseFetcher) ? machine.SystemBus.ReadDoubleWord(currentDescriptorAddress + 0xC) : 0;
336                 descriptor.Data = new byte[descriptor.Length];
337                 for(uint i=0; i<descriptor.Length; i++)
338                 {
339                     // When the zero-padding bit is set, the fetcher generates zeroes instead of reading data from memory.
340                     // For pusher, we always zero the data.
341                     descriptor.Data[i] = (byte)((descriptor.ZeroPadding || !parseFetcher) ? 0 : machine.SystemBus.ReadByte(descriptor.FirstDataAddress + i));
342                 }
343                 list.Add(descriptor);
344 
345                 this.Log(LogLevel.Noisy, "DESCRIPTOR {0}: Tag:{1:X} Length:{2} Last:{3} Const:{4} Realign:{5} ZeroPad:{6} IE:{7} Data:[{8}]",
346                          descriptorIndex, descriptor.Tag, descriptor.Length, descriptor.LastDescriptor, descriptor.ConstantAddress,
347                          descriptor.Realign, descriptor.ZeroPadding, descriptor.InterruptEnable, BitConverter.ToString(descriptor.Data));
348                 this.Log(LogLevel.Noisy, "COMMON: Engine:{0}, Last:{1}", descriptor.EngineSelect, descriptor.IsLastDataOrConfig);
349                 if (parseFetcher)
350                 {
351                     if (descriptor.IsData)
352                     {
353                         this.Log(LogLevel.Noisy, "DATA: Type:{0}, Invalid Bytes/Bits:{1}", descriptor.DataType, descriptor.InvalidBytesOrBits);
354                     }
355                     else
356                     {
357                         this.Log(LogLevel.Noisy, "CONFIG: Offset:{0}", descriptor.OffsetStartAddress);
358                     }
359                 }
360 
361                 moreDescriptors = !descriptor.LastDescriptor;
362                 // TODO: handle the realign bit set here
363                 currentDescriptorAddress = descriptor.NextDescriptorAddress;
364                 descriptorIndex++;
365 
366                 // In scatter/gather node, the hardware updates the fetchAddress after each processed descriptor
367                 // unless the constant address flag is set.
368                 if (parseFetcher && fetcherScatterGather.Value && moreDescriptors && !descriptor.ConstantAddress)
369                 {
370                     fetcherAddress.Value = currentDescriptorAddress;
371                 }
372             }
373             return list;
374         }
375 
RunAesEngine(List<DmaDescriptor> fetcherDescriptorList, List<DmaDescriptor> pusherDescriptorList)376         void RunAesEngine(List<DmaDescriptor> fetcherDescriptorList, List<DmaDescriptor> pusherDescriptorList)
377         {
378             int configDescriptorIndex = -1;
379             int keyDescriptorIndex = -1;
380             int key2DescriptorIndex = -1;
381             int inputTextDescriptorIndex = -1;
382             int ivDescriptorIndex = -1;
383             int iv2DescriptorIndex = -1;
384             for(int i=0; i<fetcherDescriptorList.Count; i++)
385             {
386                 if (fetcherDescriptorList[i].IsData && fetcherDescriptorList[i].DataType == CryptoDataType.Payload)
387                 {
388                     inputTextDescriptorIndex = i;
389                 }
390                 else if (fetcherDescriptorList[i].IsConfig && fetcherDescriptorList[i].OffsetStartAddress == 0x8)
391                 {
392                     keyDescriptorIndex = i;
393                 }
394                 else if (fetcherDescriptorList[i].IsConfig && fetcherDescriptorList[i].OffsetStartAddress == 0x0)
395                 {
396                     configDescriptorIndex = i;
397                 }
398                 else if (fetcherDescriptorList[i].IsConfig && fetcherDescriptorList[i].OffsetStartAddress == 0x28)
399                 {
400                     ivDescriptorIndex = i;
401                 }
402                 else if (fetcherDescriptorList[i].IsConfig && fetcherDescriptorList[i].OffsetStartAddress == 0x38)
403                 {
404                     iv2DescriptorIndex = i;
405                 }
406                 else if (fetcherDescriptorList[i].IsConfig && fetcherDescriptorList[i].OffsetStartAddress == 0x48)
407                 {
408                     key2DescriptorIndex = i;
409                 }
410             }
411 
412             this.Log(LogLevel.Noisy, "RunAesEngine(): config_index={0} key_index={1} key2_index={2} iv_index={3} iv2_index={4} plaintextIndex={5}",
413                      configDescriptorIndex, keyDescriptorIndex, key2DescriptorIndex, ivDescriptorIndex, iv2DescriptorIndex, inputTextDescriptorIndex);
414 
415             if (keyDescriptorIndex < 0 || configDescriptorIndex < 0 || inputTextDescriptorIndex < 0)
416             {
417                 this.Log(LogLevel.Error, "RunAesEngine(): one or more expected descriptors is missing");
418                 return;
419             }
420 
421             this.Log(LogLevel.Noisy, "RunAesEngine(): Config:[{0}]", BitConverter.ToString(fetcherDescriptorList[configDescriptorIndex].Data));
422 
423             IBlockCipher cipher = null;
424             KeyParameter keyParameter = new KeyParameter(fetcherDescriptorList[keyDescriptorIndex].Data);
425             this.Log(LogLevel.Noisy, "RunAesEngine(): key:[{0}]", BitConverter.ToString(fetcherDescriptorList[keyDescriptorIndex].Data));
426 
427             ParametersWithIV parametersWithIV = null;
428             if (ivDescriptorIndex >= 0)
429             {
430                 parametersWithIV = new ParametersWithIV(keyParameter, fetcherDescriptorList[ivDescriptorIndex].Data);
431                 this.Log(LogLevel.Noisy, "RunAesEngine(): IV:[{0}]", BitConverter.ToString(fetcherDescriptorList[ivDescriptorIndex].Data));
432             }
433 
434             this.Log(LogLevel.Noisy, "RunAesEngine(): Input Data:[{0}]", BitConverter.ToString(fetcherDescriptorList[inputTextDescriptorIndex].Data));
435 
436             // Bit 0: encryption or decryption operation
437             // 0: encryption operation
438             // 1: decryption operation
439             bool encrypt = ((fetcherDescriptorList[configDescriptorIndex].Data[0] & 0x1) == 0);
440             // Bit 4: Cx_Load:
441             // 0: AES operation is initial; no contet is given as input
442             // 1: AES operation is not initial; the context must be provided as input
443             bool cxLoad = ((fetcherDescriptorList[configDescriptorIndex].Data[0] & (0x1 << 4)) > 0);
444             // Bit 5: Cx_Save:
445             // 0: AES operation is final; the engine will not return the context
446             // 1: AES operation is not final; the engine will return the context
447             bool cxSave = ((fetcherDescriptorList[configDescriptorIndex].Data[0] & (0x1 << 4)) > 0);
448             // 16:8 Mode of Operation
449             AesMode mode = (AesMode)(fetcherDescriptorList[configDescriptorIndex].Data[1] | ((fetcherDescriptorList[configDescriptorIndex].Data[2] & 0x1) << 8));
450             this.Log(LogLevel.Noisy, "RunAesEngine(): encrypt={0} cxLoad={1} cxSave={2} AES mode: {3}", encrypt, cxLoad, cxSave, mode);
451 
452             int outputTextDescriptorIndex = -1;
453             int outputIvDescriptorIndex = -1;
454 
455             switch(mode)
456             {
457                 case AesMode.Ecb:
458                     cipher = new AesEngine();
459                     // The output IV length is always 0 when using ECB
460                     cipher.Init(encrypt, keyParameter);
461                     // In ECB mode we expect a single pusher descriptor which stores the ouput text
462                     outputTextDescriptorIndex = 0;
463                 break;
464                 case AesMode.Ctr:
465                     cipher = new SicBlockCipher(new AesEngine());
466                     cipher.Init(encrypt, parametersWithIV);
467                     outputTextDescriptorIndex = 0;
468                     outputIvDescriptorIndex = 1;
469                 break;
470                 default:
471                     this.Log(LogLevel.Error, "RunAesEngine(): AES mode not supported");
472                     return;
473             }
474 
475             for(int i = 0; i < fetcherDescriptorList[inputTextDescriptorIndex].Length; i += 16)
476             {
477                 cipher.ProcessBlock(fetcherDescriptorList[inputTextDescriptorIndex].Data, i, pusherDescriptorList[outputTextDescriptorIndex].Data, i);
478             }
479 
480             for (uint i=0; i<pusherDescriptorList[outputTextDescriptorIndex].Length; i++)
481             {
482                 machine.SystemBus.WriteByte(pusherDescriptorList[outputTextDescriptorIndex].FirstDataAddress + i, pusherDescriptorList[outputTextDescriptorIndex].Data[i]);
483             }
484 
485             this.Log(LogLevel.Noisy, "RunAesEngine(): Output Data:[{0}]", BitConverter.ToString(pusherDescriptorList[outputTextDescriptorIndex].Data));
486 
487             // TODO: RENODE-51: The crypto mode classes do not expose the IV (for example the SicBlockCipher class).
488             // We should write the output IV here, for now we just do a +1 on each byte of the input IV.
489             if (cxSave)
490             {
491                 for (uint i=0; i<pusherDescriptorList[outputIvDescriptorIndex].Length; i++)
492                 {
493                     pusherDescriptorList[outputIvDescriptorIndex].Data[i] = (byte)(fetcherDescriptorList[ivDescriptorIndex].Data[i] + 1);
494                     machine.SystemBus.WriteByte(pusherDescriptorList[outputIvDescriptorIndex].FirstDataAddress + i, pusherDescriptorList[outputIvDescriptorIndex].Data[i]);
495                 }
496                 this.Log(LogLevel.Noisy, "RunAesEngine(): Output IV:[{0}]", BitConverter.ToString(pusherDescriptorList[outputIvDescriptorIndex].Data));
497             }
498         }
499 #endregion
500 
501 #region enums
502         private enum AesMode
503         {
504             Ecb           = 0x001,
505             Ccb           = 0x002,
506             Ctr           = 0x004,
507             Cfb           = 0x008,
508             Ofb           = 0x010,
509             Ccm           = 0x020,
510             GcmGmac       = 0x040,
511             Xts           = 0x080,
512             Cmac          = 0x100,
513         }
514         private enum Registers
515         {
516             FetcherAddress              = 0x000,
517             FetcherLength               = 0x008,
518             FetcherTag                  = 0x00C,
519             PusherAddress               = 0x010,
520             PusherLength                = 0x018,
521             InterruptEnable             = 0x01C,
522             InterruptEnableSet          = 0x020,
523             InterruptEnableClear        = 0x024,
524             InterruptFlag               = 0x028,
525             InterruptFlagMasked         = 0x02C,
526             InterruptFlagClear          = 0x030,
527             Control                     = 0x034,
528             Command                     = 0x038,
529             Status                      = 0x03C,
530         }
531 
532         private enum CryptoEngine
533         {
534             Bypass       = 0x0,
535             Aes          = 0x1,
536             Des          = 0x2,
537             Hash         = 0x3,
538             ChaChaPoly   = 0x4,
539             Sha3         = 0x5,
540             AesGcm       = 0x6,
541             AesXts       = 0x7,
542             HashPlusAes  = 0x8,
543             AesPlusHash  = 0x9,
544             Zuc          = 0xA,
545             Sm4          = 0xB,
546             HpChaChaPoly = 0xC,
547             Snow3g       = 0xD,
548             Kasumi       = 0xE,
549             Aria         = 0xF,
550         }
551 
552         private enum CryptoDataType
553         {
554             Unused,
555             Payload,
556             Header,
557             Message,
558             InitializationData,
559             HMAC_Key,
560             ReferenceHash,
561             ReferenceTag,
562             ReferenceDigest,
563         }
564 #endregion
565 
566         private class DmaDescriptor
567         {
DmaDescriptor()568             public DmaDescriptor()
569             {
570             }
571 
572             public CryptoEngine EngineSelect
573             {
574                 get => (CryptoEngine)(this.Tag & 0xF);
575             }
576 
577             public bool IsData
578             {
579                 get => ((this.Tag & 0x10) == 0);
580             }
581 
582             public bool IsConfig
583             {
584                 get => !this.IsData;
585             }
586 
587             public bool IsLastDataOrConfig
588             {
589                 get => ((this.Tag & 0x20) > 0);
590             }
591             public uint InvalidBytesOrBits
592             {
593                 // Bits 13:8
594                 get => IsData ? ((this.Tag & 0xCF00) >> 8) : 0;
595             }
596 
597             public uint OffsetStartAddress
598             {
599                 // Bits 15:8
600                 get => IsData ? 0 : ((this.Tag & 0xFF00) >> 8);
601             }
602 
603             public CryptoDataType DataType
604             {
605                 get
606                 {
607                     if (!IsData)
608                     {
609                         return CryptoDataType.Unused;
610                     }
611 
612                     // Bits 7:6
613                     uint dataType = ((this.Tag & 0xC0) >> 6);
614                     switch(this.EngineSelect)
615                     {
616                         case CryptoEngine.Aes:
617                         case CryptoEngine.Sm4:
618                         case CryptoEngine.Aria:
619                             switch(dataType)
620                             {
621                                 case 0:
622                                     return CryptoDataType.Payload;
623                                 case 1:
624                                     return CryptoDataType.Header;
625                             }
626                         break;
627                         case CryptoEngine.Hash:
628                         case CryptoEngine.Sha3:
629                             switch(dataType)
630                             {
631                                 case 0:
632                                     return CryptoDataType.Message;
633                                 case 1:
634                                     return CryptoDataType.InitializationData;
635                                 case 2:
636                                     return CryptoDataType.HMAC_Key;
637                                 case 3:
638                                     return CryptoDataType.ReferenceHash;
639                             }
640                         break;
641                         case CryptoEngine.AesGcm:
642                             switch(dataType)
643                             {
644                                 case 0:
645                                     return CryptoDataType.Payload;
646                                 case 1:
647                                     return CryptoDataType.Header;
648                                 case 3:
649                                     return CryptoDataType.ReferenceTag;
650                             }
651                         break;
652                         case CryptoEngine.ChaChaPoly:
653                         case CryptoEngine.HpChaChaPoly:
654                             switch(dataType)
655                             {
656                                 case 0:
657                                     return CryptoDataType.Payload;
658                                 case 1:
659                                     return CryptoDataType.Header;
660                                 case 3:
661                                     return CryptoDataType.ReferenceDigest;
662                             }
663                         break;
664                     }
665                     return CryptoDataType.Unused;
666                 }
667             }
668 
669             public uint FirstDataAddress;
670             public bool LastDescriptor;
671             public uint NextDescriptorAddress;
672             public uint Length;
673             public bool ConstantAddress;
674             public bool Realign;
675             public bool Discard;
676             public bool ZeroPadding;
677             public bool InterruptEnable;
678             public uint Tag;
679             public byte[] Data;
680         }
681     }
682 }