1 //
2 // Copyright (c) 2010-2023 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System.Linq;
8 using Antmicro.Renode.Core;
9 using Antmicro.Renode.Core.Structure.Registers;
10 using Antmicro.Renode.Logging;
11 using Antmicro.Renode.Peripherals.Bus;
12 using Antmicro.Renode.Utilities;
13 
14 namespace Antmicro.Renode.Peripherals.Miscellaneous
15 {
16     [AllowedTranslations(AllowedTranslation.ByteToDoubleWord | AllowedTranslation.WordToDoubleWord)]
17     public class AmbiqApollo4_Security : BasicDoubleWordPeripheral, IKnownSize
18     {
AmbiqApollo4_Security(IMachine machine)19         public AmbiqApollo4_Security(IMachine machine) : base(machine)
20         {
21             systemBus = machine.GetSystemBus(this);
22             DefineRegisters();
23         }
24 
Reset()25         public override void Reset()
26         {
27             crcErrorOccurred = false;
28         }
29 
30         public bool LogDataRead { get; set; }
31         public long Size => 0x90;
32 
CalculateCrc32()33         private void CalculateCrc32()
34         {
35             var byteLength = (int)wordLength.Value * 4;
36             this.Log(LogLevel.Debug, "Calculating CRC32 for the <0x{0:X8},0x{1:X8}> address range; seed: 0x{2:X8}", address.Value, (long)address.Value + byteLength, crcSeedOrResult.Value);
37 
38             if(crcSeedOrResult.Value != ValidCrcSeed)
39             {
40                 this.Log(LogLevel.Warning, "The seed is invalid for CRC32 calculation: 0x{0:X8} (should be 0x{1:X8})", crcSeedOrResult.Value, ValidCrcSeed);
41                 crcErrorOccurred = true;
42                 return;
43             }
44 
45             byte[] data;
46             try
47             {
48                 data = systemBus.ReadBytes(address.Value, byteLength, onlyMemory: true);
49                 if(LogDataRead)
50                 {
51                     this.Log(LogLevel.Noisy, "Data for CRC32 calculation:\n{0}", data.Select(b => "0x" + b.ToString("X2")).Stringify(limitPerLine: 8));
52                 }
53 
54                 var result = new CRCEngine(0x04C11DB7, 32, init: (uint)crcSeedOrResult.Value).Calculate(data);
55 
56                 // The most common CRC-32 algorithm (CRC-32/BZIP2) requires inverting the output (xoring with 0xffffffff).
57                 // See the 'xorout' parameter: https://reveng.sourceforge.io/crc-catalogue/all.htm#crc.cat.crc-32-bzip2
58                 result = ~result;
59 
60                 this.Log(LogLevel.Debug, "CRC32 calculation result: 0x{0:X8}", result);
61                 crcSeedOrResult.Value = result;
62             }
63             catch(Exceptions.RecoverableException exception)
64             {
65                 this.Log(LogLevel.Debug, "Error when reading memory to calculate CRC32: {0}", exception.Message);
66                 crcErrorOccurred = true;
67             }
68 
69             calculationEnabled.Value = false;
70         }
71 
DefineRegisters()72         private void DefineRegisters()
73         {
74             Registers.Control.Define(this)
75                 .WithFlag(0, out calculationEnabled, name: "ENABLE")
76                 .WithReservedBits(1, 3)
77                 .WithEnumField(4, 4, out functionSelect, name: "FUNCTION", changeCallback: (_, newValue) =>
78                 {
79                     if(newValue != Functions.CRC32)
80                     {
81                         this.Log(LogLevel.Warning, "Unsupported function selected: {0}", newValue);
82                     }
83                 })
84                 .WithReservedBits(8, 23)
85                 .WithFlag(31, FieldMode.Read, name: "CRCERROR", valueProviderCallback: _ => crcErrorOccurred)
86                 .WithChangeCallback((_, __) =>
87                 {
88                     if(calculationEnabled.Value && functionSelect.Value == Functions.CRC32)
89                     {
90                         CalculateCrc32();
91                     }
92                 })
93                 // Every write clears the error status.
94                 .WithWriteCallback((_, __) => crcErrorOccurred = false)
95                 ;
96 
97             Registers.SourceAddress.Define(this)
98                 .WithValueField(0, 32, out address, name: "ADDR")
99                 ;
100 
101             Registers.Length.Define(this)
102                 .WithReservedBits(0, 2)
103                 .WithValueField(2, 22, out wordLength, name: "LEN")
104                 .WithReservedBits(24, 8)
105                 ;
106 
107             Registers.CRCSeedOrResult.Define(this)
108                 .WithValueField(0, 32, out crcSeedOrResult, name: "CRC")
109                 ;
110 
111             Registers.LockControl.Define(this)
112                 .WithTag("SELECT", 0, 8)
113                 .WithReservedBits(8, 24)
114                 ;
115 
116             Registers.LockStatus.Define(this)
117                 .WithTag("STATUS", 0, 32)
118                 ;
119 
120             Registers.Key0.Define(this)
121                 .WithTag("KEY0", 0, 32)
122                 ;
123 
124             Registers.Key1.Define(this)
125                 .WithTag("KEY1", 0, 32)
126                 ;
127 
128             Registers.Key2.Define(this)
129                 .WithTag("KEY2", 0, 32)
130                 ;
131 
132             Registers.Key3.Define(this)
133                 .WithTag("KEY3", 0, 32)
134                 ;
135         }
136 
137         private bool crcErrorOccurred;
138 
139         private IValueRegisterField address;
140         private IValueRegisterField crcSeedOrResult;
141         private IFlagRegisterField calculationEnabled;
142         private IEnumRegisterField<Functions> functionSelect;
143         private IValueRegisterField wordLength;
144 
145         private readonly IBusController systemBus;
146 
147         private const uint ValidCrcSeed = 0xFFFFFFFF;
148 
149         private enum Functions
150         {
151             CRC32 = 0x0,
152             DMAPseudoRandomNumberStreamFromCRC = 0x1,
153             GenerateDMAStreamFromAddress = 0x2,
154         }
155 
156         private enum Registers : long
157         {
158             Control = 0x0,
159             SourceAddress = 0x10,
160             Length = 0x20,
161             CRCSeedOrResult = 0x30,
162             LockControl = 0x78,
163             LockStatus = 0x7C,
164             Key0 = 0x80,
165             Key1 = 0x84,
166             Key2 = 0x88,
167             Key3 = 0x8C,
168         }
169     }
170 }
171