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 
8 using System;
9 using System.Collections.Generic;
10 using System.Linq;
11 using System.Reflection;
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.Memory;
17 using Antmicro.Renode.Utilities;
18 
19 namespace Antmicro.Renode.Peripherals.Miscellaneous
20 {
21     public class OpenTitan_OneTimeProgrammableMemoryController: BasicDoubleWordPeripheral, IKnownSize
22     {
OpenTitan_OneTimeProgrammableMemoryController(IMachine machine)23         public OpenTitan_OneTimeProgrammableMemoryController(IMachine machine) : base(machine)
24         {
25             memoryLock = new Object();
26             transitionCountLock = new Object();
27             DefineRegisters();
28 
29             FatalMacroAlert = new GPIO();
30             FatalCheckErrorAlert = new GPIO();
31             FatalBusAlert = new GPIO();
32             FatalPrimitiveOtpAlert = new GPIO();
33             RecoverablePrimitiveOtpAlert = new GPIO();
34 
35             aValues = new ushort[ABValuesWordsCount];
36             bValues = new ushort[ABValuesWordsCount];
37             cValues = new ushort[CDValuesWordsCount];
38             dValues = new ushort[CDValuesWordsCount];
39 
40             InitPositionConsumedToLifeCycleMapping();
41             underlyingMemory = new ArrayMemory(0x1000);
42 
43             Reset();
44         }
45 
Reset()46         public override void Reset()
47         {
48             base.Reset();
49             FatalMacroAlert.Unset();
50             FatalCheckErrorAlert.Unset();
51             FatalBusAlert.Unset();
52             FatalPrimitiveOtpAlert.Unset();
53             RecoverablePrimitiveOtpAlert.Unset();
54 
55             daiIdleFlag.Value = true;
56             cachedLifeCycleState = null;
57             cachedTransitionCount = null;
58         }
59 
LoadVmem(ReadFilePath filename)60         public void LoadVmem(ReadFilePath filename)
61         {
62             try
63             {
64                 var reader = new VmemReader(filename);
65                 lock(memoryLock)
66                 {
67                     foreach(var pair in reader.GetIndexDataPairs())
68                     {
69                         // VmemReader returns 4 byte values, but the two first contains only the ECC which we don't need
70                         var offset = pair.Item1 * 2;
71                         underlyingMemory.WriteWord(offset, (ushort)pair.Item2);
72                     }
73                 }
74             }
75             catch(Exception e)
76             {
77                 throw new RecoverableException($"Exception while loading file {filename}: {e.Message}");
78             }
79         }
80 
GetOtpItem(OtpItem item)81         public byte[] GetOtpItem(OtpItem item)
82         {
83             var itemLength = GetItemSizeAttribute(item);
84             lock(memoryLock)
85             {
86                 return underlyingMemory.ReadBytes((uint)item, itemLength.ByteLength);
87             }
88         }
89 
IncrementTransitionCount()90         public int IncrementTransitionCount()
91         {
92             lock(transitionCountLock)
93             {
94                 var currentCount = LifeCycleTransitionCount;
95                 if(currentCount == MaximumTransitionsCount)
96                 {
97                     this.Log(LogLevel.Warning, "Transitions count already reached its limit of {0} transitions. Trying to increment it now is illegal.", MaximumTransitionsCount);
98                     return -1;
99 
100                 }
101                 LifeCycleTransitionCount = (ushort)(currentCount + 1);
102             }
103             return LifeCycleTransitionCount;
104         }
105 
106         public long Size => 0x1800;
107 
108         public GPIO FatalMacroAlert { get; }
109         public GPIO FatalCheckErrorAlert { get; }
110         public GPIO FatalBusAlert { get; }
111         public GPIO FatalPrimitiveOtpAlert { get; }
112         public GPIO RecoverablePrimitiveOtpAlert { get; }
113 
114         public string AValuesChain
115         {
116             set
117             {
118                 aValues = SplitValueChainIntoWordsArray(value);
119             }
120         }
121 
122         public string BValuesChain
123         {
124             set
125             {
126                 bValues = SplitValueChainIntoWordsArray(value);
127             }
128         }
129 
130         public string CValuesChain
131         {
132             set
133             {
134                 cValues = SplitValueChainIntoWordsArray(value);
135             }
136         }
137 
138         public string DValuesChain
139         {
140             set
141             {
142                 dValues = SplitValueChainIntoWordsArray(value);
143             }
144         }
145 
146         public OpenTitan_LifeCycleState LifeCycleState
147         {
148             set
149             {
150                 EncodeLifeCycleState(value);
151                 cachedLifeCycleState = value;
152             }
153             get
154             {
155                 if(!cachedLifeCycleState.HasValue)
156                 {
157                     OpenTitan_LifeCycleState state;
158                     cachedLifeCycleState = TryDecodeLifeCycleState(out state) ? state : OpenTitan_LifeCycleState.Invalid;
159                 }
160                 return cachedLifeCycleState.Value;
161             }
162         }
163 
164         public ushort LifeCycleTransitionCount
165         {
166             private set
167             {
168                 cachedTransitionCount = value;
169                 EncodeLifeCycleTransitionCount(value);
170             }
171             get
172             {
173                 if(!cachedTransitionCount.HasValue)
174                 {
175                     cachedTransitionCount = DecodeLifeCycleTransitionCount();
176                 }
177                 return cachedTransitionCount.Value;
178             }
179         }
180 
DefineRegisters()181         private void DefineRegisters()
182         {
183             Registers.InterruptState.Define(this)
184                 .WithTaggedFlag("otp_operation_done", 0)
185                 .WithTaggedFlag("otp_error", 1)
186                 .WithIgnoredBits(2, 30);
187             Registers.InterruptEnable.Define(this)
188                 .WithTaggedFlag("otp_operation_done", 0)
189                 .WithTaggedFlag("otp_error", 1)
190                 .WithIgnoredBits(2, 30);
191             Registers.InterruptTest.Define(this)
192                 .WithTaggedFlag("otp_operation_done", 0)
193                 .WithTaggedFlag("otp_error", 1)
194                 .WithIgnoredBits(2, 30);
195             Registers.AlertTest.Define(this)
196                 .WithFlag(0, FieldMode.Write, writeCallback: (_, val) => { if(val) FatalMacroAlert.Blink(); }, name: "fatal_macro_error")
197                 .WithFlag(1, FieldMode.Write, writeCallback: (_, val) => { if(val) FatalCheckErrorAlert.Blink(); }, name: "fatal_check_error")
198                 .WithFlag(2, FieldMode.Write, writeCallback: (_, val) => { if(val) FatalBusAlert.Blink(); }, name: "fatal_bus_integ_error")
199                 .WithFlag(3, FieldMode.Write, writeCallback: (_, val) => { if(val) FatalPrimitiveOtpAlert.Blink(); }, name: "fatal_prim_otp_alert")
200                 .WithFlag(4, FieldMode.Write, writeCallback: (_, val) => { if(val) RecoverablePrimitiveOtpAlert.Blink(); }, name: "recov_prim_otp_alert")
201                 .WithIgnoredBits(5, 27);
202             Registers.Status.Define(this)
203                 .WithFlag(0, out vendorPartitionErrorFlag, FieldMode.Read, name: "VENDOR_TEST_ERROR")
204                 .WithFlag(1, out creatorPartitionErrorFlag, FieldMode.Read, name: "CREATOR_SW_CFG_ERROR")
205                 .WithFlag(2, out ownerPartitionErrorFlag, FieldMode.Read, name: "OWNER_SW_CFG_ERROR")
206                 .WithFlag(3, out hardwarePartitionErrorFlag, FieldMode.Read, name: "HW_CFG_ERROR")
207                 .WithFlag(4, out secret0PartitionErrorFlag, FieldMode.Read, name: "SECRET0_ERROR")
208                 .WithFlag(5, out secret1PartitionErrorFlag, FieldMode.Read, name: "SECRET1_ERROR")
209                 .WithFlag(6, out secret2PartitionErrorFlag, FieldMode.Read, name: "SECRET2_ERROR")
210                 .WithFlag(7, out lifeCyclePartitionErrorFlag, FieldMode.Read, name: "LIFE_CYCLE_ERROR")
211                 .WithFlag(8, out daiErrorFlag, FieldMode.Read, name: "DAI_ERROR")
212                 .WithTaggedFlag("LCI_ERROR", 9)
213                 .WithTaggedFlag("TIMEOUT_ERROR", 10)
214                 .WithTaggedFlag("LFSR_FSM_ERROR", 11)
215                 .WithTaggedFlag("SCRAMBLING_FSM_ERROR", 12)
216                 .WithTaggedFlag("KEY_DERIV_FSM_ERROR", 13)
217                 .WithTaggedFlag("KEY_DERIV_FSM_ERROR", 14)
218                 .WithFlag(15, out daiIdleFlag, FieldMode.Read, name: "DAI_IDLE")
219                 .WithTaggedFlag("CHECK_PENDING", 16)
220                 .WithReservedBits(17, 32 - 17);
221             Registers.ErrorCode.Define(this)
222                 .WithEnumField<DoubleWordRegister, Error>(0, 3, out vendorPartitionError, FieldMode.Read, name: "ERR_CODE_0")
223                 .WithEnumField<DoubleWordRegister, Error>(3, 3, out creatorPartitionError, FieldMode.Read, name: "ERR_CODE_1")
224                 .WithEnumField<DoubleWordRegister, Error>(6, 3, out ownerPartitionError, FieldMode.Read, name: "ERR_CODE_2")
225                 .WithEnumField<DoubleWordRegister, Error>(9, 3, out hardwarePartitionError, FieldMode.Read, name: "ERR_CODE_3")
226                 .WithEnumField<DoubleWordRegister, Error>(12, 3, out secret0PartitionError, FieldMode.Read, name: "ERR_CODE_4")
227                 .WithEnumField<DoubleWordRegister, Error>(15, 3, out secret1PartitionError, FieldMode.Read, name: "ERR_CODE_5")
228                 .WithEnumField<DoubleWordRegister, Error>(18, 3, out secret2PartitionError, FieldMode.Read, name: "ERR_CODE_6")
229                 .WithEnumField<DoubleWordRegister, Error>(21, 3, out lifeCyclePartitionError, FieldMode.Read, name: "ERR_CODE_7")
230                 .WithEnumField<DoubleWordRegister, Error>(24, 3, out daiError, FieldMode.Read, name: "ERR_CODE_8")
231                 .WithTag("ERR_CODE_9", 27, 3)
232                 .WithReservedBits(30, 2);
233             Registers.DirectAccesssRegisterEnable.Define(this, 0x1)
234                 .WithFlag(0, FieldMode.Read, name: "DIRECT_ACCESS_REGWEN")
235                 .WithReservedBits(1, 31);
236             Registers.DirectAccessCommand.Define(this)
237                 .WithFlag(0, FieldMode.WriteOneToClear, writeCallback: (_, val) => { if(val) ExecuteDirectRead(); }, name: "RD")
238                 .WithTaggedFlag("WR", 1)
239                 .WithTaggedFlag("DIGEST", 2)
240                 .WithReservedBits(3, 32 - 3);
241             Registers.DirectAccessAddress.Define(this)
242                 .WithValueField(0, 11, out accessAddress, writeCallback: (_, val) =>
243                     {
244                         this.NoisyLog("Direct access address set to: 0x{0:X} [{1}]", val, (OtpItem)val);
245                     }, name: "DIRECT_ACCESSS_ADDRESS")
246                 .WithReservedBits(11, 32 - 11);
247             Registers.DirectAccessWriteData_0.Define(this)
248                 .WithTag("DIRECT_ACCESS_WDATA_0", 0, 32);
249             Registers.DirectAccessWriteData_1.Define(this)
250                 .WithTag("DIRECT_ACCESS_WDATA_1", 0, 32);
251             Registers.DirectAccessReadData_0.Define(this, 0)
252                 .WithValueField(0, 32, out readData0, FieldMode.Read, name: "DIRECT_ACCESS_RDATA_0");
253             Registers.DirectAccessReadData_1.Define(this, 0)
254                 .WithValueField(0, 32, out readData1, FieldMode.Read, name: "DIRECT_ACCESS_RDATA_1");
255             Registers.CheckTriggerRegisterWriteEnable.Define(this)
256                 .WithTaggedFlag("CHECK_TRIGGER_REGWEN", 0)
257                 .WithIgnoredBits(1, 31);
258             Registers.CheckTrigger.Define(this)
259                 .WithTaggedFlag("INREGRITY", 0)
260                 .WithTaggedFlag("CONSISTENCY", 1)
261                 .WithIgnoredBits(2, 30);
262             Registers.CheckRegistersWriteEnable.Define(this, 0x1)
263                 .WithFlag(0, FieldMode.Read | FieldMode.WriteZeroToClear, name: "CHECK_REGWEN")
264                 .WithReservedBits(1, 31);
265             Registers.CheckTimeout.Define(this)
266                 .WithTag("CHECK_TIMEOUT", 0, 32);
267             Registers.IntegrityCheckPeriod.Define(this)
268                 .WithTag("INTEGRITY_CHECK_PERIOD", 0, 32);
269             Registers.ConsistencyCheckPeriod.Define(this)
270                 .WithTag("CONSISTENCY_CHECK_PERIOD", 0, 32);
271             Registers.VendorTestReadLock.Define(this, 0x1)
272                 .WithFlag(0, out vendorPartitionUnlockedFlag, FieldMode.WriteZeroToClear, name: "VENDOR_TEST_READ_LOCK")
273                 .WithIgnoredBits(1, 31);
274             Registers.CreatorSoftwareConfigReadLock.Define(this, 0x1)
275                 .WithFlag(0, out creatorPartitionUnlockedFlag, FieldMode.WriteZeroToClear, name: "VENDOR_TEST_READ_LOCK")
276                 .WithIgnoredBits(1, 31);
277             Registers.OwnerSoftwareConfigReadLock.Define(this, 0x1)
278                 .WithFlag(0, out ownerPartitionUnlockedFlag, FieldMode.WriteZeroToClear, name: "VENDOR_TEST_READ_LOCK")
279                 .WithIgnoredBits(1, 31);
280             Registers.VendorTestDigest0.Define(this)
281                 .WithTag("VENDOR_TEST_DIGEST_0", 0, 32);
282             Registers.VendorTestDigest1.Define(this)
283                 .WithTag("VENDOR_TEST_DIGEST_1", 0, 32);
284             Registers.CreatorSoftwareConfigDigest0.Define(this)
285                 .WithTag("CREATOR_SW_CFG_DIGEST_0", 0, 32);
286             Registers.CreatorSoftwareConfigDigest1.Define(this)
287                 .WithTag("CREATOR_SW_CFG_DIGEST_1", 0, 32);
288             Registers.OwnerSoftwareConfigDigest0.Define(this)
289                 .WithTag("OWNER_SW_CFG_DIGEST_0", 0, 32);
290             Registers.OwnerSoftwareConfigDigest1.Define(this)
291                 .WithTag("OWNER_SW_CFG_DIGEST_1", 0, 32);
292             Registers.HardwareConfigDigest0.Define(this)
293                 .WithTag("HW_CFG_DIGEST_0", 0, 32);
294             Registers.HardwareConfigDigest1.Define(this)
295                 .WithTag("HW_CFG_DIGEST_1", 0, 32);
296             Registers.Secret0Digest0.Define(this)
297                 .WithTag("SECRET0_DIGEST0", 0, 32);
298             Registers.Secret0Digest1.Define(this)
299                 .WithTag("SECRET0_DIGEST1", 0, 32);
300             Registers.Secret1Digest0.Define(this)
301                 .WithTag("SECRET1_DIGEST0", 0, 32);
302             Registers.Secret1Digest1.Define(this)
303                 .WithTag("SECRET1_DIGEST1", 0, 32);
304             Registers.Secret2Digest0.Define(this)
305                 .WithTag("SECRET2_DIGEST0", 0, 32);
306             Registers.Secret2Digest1.Define(this)
307                 .WithTag("SECRET2_DIGEST1", 0, 32);
308             Registers.SoftwareConfiguredWindowStart.Define32Many(this, 512, (register, index) =>
309                 {
310                     register
311                         .WithFlag(0, valueProviderCallback: _ => true)
312                         .WithReservedBits(1, 31)
313                         ;
314                 });
315         }
316 
ExecuteDirectRead()317         private void ExecuteDirectRead()
318         {
319             this.NoisyLog("Starting the DAI read");
320 
321             OtpItem item;
322             OtpPartition partition;
323             int itemOffset, partitionOffset;
324             var readAddress = (uint)accessAddress.Value;
325 
326             if(!TryGetOtpPartitionAndItem(readAddress, out item, out partition, out itemOffset, out partitionOffset))
327             {
328                 this.Log(LogLevel.Error, "Failed to find an OTP Partition or OTP Item at the address 0x{0:X}", readAddress);
329                 return;
330             }
331 
332             if(!IsPartitionReadable(partition))
333             {
334                 this.Log(LogLevel.Warning, "The DAI read failed due to the parition being locked");
335                 RaisePartitionError(Error.Access, partition);
336                 daiError.Value = Error.Access;
337                 daiErrorFlag.Value = true;
338                 return;
339             }
340 
341             this.NoisyLog("Executing read from the {0}+0x{1:X} on the partition {2}+0x{3:X}", item, itemOffset, partition, partitionOffset);
342 
343             var itemAttribute = GetItemSizeAttribute(item);
344             DirectReadInner(readAddress, itemAttribute.Is64Bit);
345             daiIdleFlag.Value = true;
346         }
347 
DirectReadInner(uint readAddress, bool is64BitItem)348         private void DirectReadInner(uint readAddress, bool is64BitItem)
349         {
350             lock(memoryLock)
351             {
352                 readData0.Value = underlyingMemory.ReadDoubleWord(readAddress);
353                 if(is64BitItem)
354                 {
355                     readData1.Value = underlyingMemory.ReadDoubleWord(readAddress + 0x4);
356                 }
357             }
358         }
359 
TryGetOtpPartitionAndItem(uint readAddress, out OtpItem item, out OtpPartition partition, out int itemOffset, out int partitionOffset)360         private bool TryGetOtpPartitionAndItem(uint readAddress, out OtpItem item, out OtpPartition partition, out int itemOffset, out int partitionOffset)
361         {
362             itemOffset = 0;
363             partitionOffset = 0;
364             item = default(OtpItem);
365             partition = default(OtpPartition);
366 
367             return Misc.TryFindPreceedingEnumItem(readAddress, out item, out itemOffset) &&
368                    Misc.TryFindPreceedingEnumItem(readAddress, out partition, out partitionOffset);
369         }
370 
IsPartitionReadable(OtpPartition partition)371         private bool IsPartitionReadable(OtpPartition partition)
372         {
373             switch(partition)
374             {
375                 case OtpPartition.VendorTest:
376                     return vendorPartitionUnlockedFlag.Value;
377                 case OtpPartition.CreatorSoftwareConfig:
378                     return creatorPartitionUnlockedFlag.Value;
379                 case OtpPartition.OwnerSoftwareConfig:
380                     return ownerPartitionUnlockedFlag.Value;
381                 default:
382                     return true;
383             }
384         }
385 
RaisePartitionError(Error error, OtpPartition partition)386         private void RaisePartitionError(Error error, OtpPartition partition)
387         {
388             switch(partition)
389             {
390                 case OtpPartition.VendorTest:
391                     vendorPartitionError.Value = error;
392                     vendorPartitionErrorFlag.Value = true;
393                     break;
394                 case OtpPartition.OwnerSoftwareConfig:
395                     ownerPartitionError.Value = error;
396                     ownerPartitionErrorFlag.Value = true;
397                     break;
398                 case OtpPartition.CreatorSoftwareConfig:
399                     creatorPartitionError.Value = error;
400                     creatorPartitionErrorFlag.Value = true;
401                     break;
402                 case OtpPartition.HardwareConfig:
403                     hardwarePartitionError.Value = error;
404                     hardwarePartitionErrorFlag.Value = true;
405                     break;
406                 case OtpPartition.Secret0:
407                     secret0PartitionError.Value = error;
408                     secret0PartitionErrorFlag.Value = true;
409                     break;
410                 case OtpPartition.Secret1:
411                     secret1PartitionError.Value = error;
412                     secret1PartitionErrorFlag.Value = true;
413                     break;
414                 case OtpPartition.Secret2:
415                     secret2PartitionError.Value = error;
416                     secret2PartitionErrorFlag.Value = true;
417                     break;
418                 case OtpPartition.LifeCycle:
419                     lifeCyclePartitionError.Value = error;
420                     lifeCyclePartitionErrorFlag.Value = true;
421                     break;
422                 default:
423                     throw new ArgumentException($"Unknown partition {partition}");
424             }
425         }
426 
GetItemSizeAttribute(OtpItem item)427         private ItemSizeAttribute GetItemSizeAttribute(OtpItem item)
428         {
429             var type = item.GetType();
430             var name = Enum.GetName(type, item);
431             return type.GetField(name).GetCustomAttribute<ItemSizeAttribute>();
432         }
433 
SplitValueChainIntoWordsArray(string valueChain)434         private ushort[] SplitValueChainIntoWordsArray(string valueChain)
435         {
436             ushort[] outputArray;
437             if(!Misc.TryParseHexString(valueChain, out outputArray, sizeof(ushort)))
438             {
439                 throw new ConstructionException($"Values chain string must consist of ordered 4 character hex values. Length incorrect");
440             }
441             return outputArray;
442         }
443 
444         /*
445          * The LifeCycleStateTransitionCount is decoded/encoded using 24 words that are intially written with some combination of Cx/Dx values.
446          * Each transition cause one Cn value to be overwritten with Dn value when doing the n'th transition.
447          * Ex. when doing 6th transition the 6th word gets overwritten with D6 value. No more than 24 transitions are possible
448          *  */
DecodeLifeCycleTransitionCount()449         private ushort DecodeLifeCycleTransitionCount()
450         {
451             var transitionCount = GetOtpItem(OtpItem.LifeCycleTransitionCount);
452             if(transitionCount.All(x => x == 0))
453             {
454                 return 0;
455             }
456             var positionsConsumed = GetConsumedPositionsMap(cValues, dValues, transitionCount);
457             var stroke = MaximumTransitionsCount - Misc.CountTrailingZeroes(positionsConsumed);
458 
459             return (ushort)stroke;
460         }
461 
EncodeLifeCycleTransitionCount(uint newCount)462         private void EncodeLifeCycleTransitionCount(uint newCount)
463         {
464             // Need to consume only the last position,as the rest should already be set
465             var strokeIndex = newCount - 1;
466             var writeOffset = (uint)OtpItem.LifeCycleTransitionCount + strokeIndex * 2;
467             lock(memoryLock)
468             {
469                 underlyingMemory.WriteWord(writeOffset, (ushort)dValues[strokeIndex]);
470             }
471         }
472         /* End of LifeCycleTransitionCount functions
473          */
474 
475         /*
476          * The LifeCycleState is decoded/encoded using 20 words that are intially written with some combination of Ax/Bx values.
477          * Those values are organized in such a way that by advncing the state we consume the Ax val by overwriting it by Bx val,
478          * future transition to an allowed state is not possible without knowing the Ax values.
479          */
TryDecodeLifeCycleState(out OpenTitan_LifeCycleState state)480         private bool TryDecodeLifeCycleState(out OpenTitan_LifeCycleState state)
481         {
482             var lifeCycleState = GetOtpItem(OtpItem.LifeCycleState);
483             if(lifeCycleState.All(x => x == 0))
484             {
485                 state = OpenTitan_LifeCycleState.Raw;
486                 return true;
487             }
488             var positionsConsumed = GetConsumedPositionsMap(aValues, bValues, lifeCycleState);
489 
490             if(!positionsConsumedToLifeCycleState.ContainsKey(positionsConsumed))
491             {
492                 this.Log(LogLevel.Error, "Unable to convert the consumed positions [{0}] to the LifeCycleState", Convert.ToString(positionsConsumed, 2).PadLeft(4, '0'));
493                 state = default(OpenTitan_LifeCycleState);
494                 return false;
495             }
496             state = positionsConsumedToLifeCycleState[positionsConsumed];
497             return true;
498         }
499 
EncodeLifeCycleState(OpenTitan_LifeCycleState state)500         private void EncodeLifeCycleState(OpenTitan_LifeCycleState state)
501         {
502             var positionsConsumed = positionsConsumedToLifeCycleState.FirstOrDefault(x => x.Value == state).Key;
503             var mask = 1 << (aValues.Length - 1);
504             lock(memoryLock)
505             {
506                 for(int index = 0; index < aValues.Length; index++)
507                 {
508                     var currentPositionConsumed = (positionsConsumed & mask) != 0;
509                     var writeOffset = (uint)OtpItem.LifeCycleState + index * 2;
510                     underlyingMemory.WriteWord(writeOffset, currentPositionConsumed ? bValues[index] : aValues[index]);
511                     positionsConsumed <<= 1;
512                 }
513             }
514         }
515 
InitPositionConsumedToLifeCycleMapping()516         private void InitPositionConsumedToLifeCycleMapping()
517         {
518             positionsConsumedToLifeCycleState = new Dictionary<uint, OpenTitan_LifeCycleState>();
519             positionsConsumedToLifeCycleState.Add(0x80000, OpenTitan_LifeCycleState.TestUnlocked0);
520             positionsConsumedToLifeCycleState.Add(0xC0000, OpenTitan_LifeCycleState.TestLocked0);
521             positionsConsumedToLifeCycleState.Add(0xE0000, OpenTitan_LifeCycleState.TestUnlocked1);
522             positionsConsumedToLifeCycleState.Add(0xF0000, OpenTitan_LifeCycleState.TestLocked1);
523             positionsConsumedToLifeCycleState.Add(0xF8000, OpenTitan_LifeCycleState.TestUnlocked2);
524             positionsConsumedToLifeCycleState.Add(0xFC000, OpenTitan_LifeCycleState.TestLocked2);
525             positionsConsumedToLifeCycleState.Add(0xFE000, OpenTitan_LifeCycleState.TestUnlocked3);
526             positionsConsumedToLifeCycleState.Add(0xFF000, OpenTitan_LifeCycleState.TestLocked3);
527             positionsConsumedToLifeCycleState.Add(0xFF800, OpenTitan_LifeCycleState.TestUnlocked4);
528             positionsConsumedToLifeCycleState.Add(0xFFC00, OpenTitan_LifeCycleState.TestLocked4);
529             positionsConsumedToLifeCycleState.Add(0xFFE00, OpenTitan_LifeCycleState.TestUnlocked5);
530             positionsConsumedToLifeCycleState.Add(0xFFF00, OpenTitan_LifeCycleState.TestLocked5);
531             positionsConsumedToLifeCycleState.Add(0xFFF80, OpenTitan_LifeCycleState.TestUnlocked6);
532             positionsConsumedToLifeCycleState.Add(0xFFFC0, OpenTitan_LifeCycleState.TestLocked6);
533             positionsConsumedToLifeCycleState.Add(0xFFFE0, OpenTitan_LifeCycleState.TestUnlocked7);
534             positionsConsumedToLifeCycleState.Add(0xFFFF0, OpenTitan_LifeCycleState.Dev);
535             positionsConsumedToLifeCycleState.Add(0xFFFE8, OpenTitan_LifeCycleState.Prod);
536             positionsConsumedToLifeCycleState.Add(0xFFFE4, OpenTitan_LifeCycleState.Prod_end);
537             positionsConsumedToLifeCycleState.Add(0xFFFFB, OpenTitan_LifeCycleState.Rma);
538             positionsConsumedToLifeCycleState.Add(0xFFFFF, OpenTitan_LifeCycleState.Scrap);
539         }
540 
541         /* End of LifeCycleState related functions
542          */
543 
GetConsumedPositionsMap(ushort[] unconsumedValues, ushort[] consumedValues, byte[] array)544         private uint GetConsumedPositionsMap(ushort[] unconsumedValues, ushort[] consumedValues, byte[] array)
545         {
546             var valuesCount = consumedValues.Length;
547             if(valuesCount != unconsumedValues.Length)
548             {
549                 throw new ArgumentException($"Both unconsumed and consumed values arrays have to be the same length! Unconsumed values length : {unconsumedValues.Length}, consumed values length: {valuesCount}");
550             }
551             uint output = 0;
552             for(int index = 0; index < valuesCount; index++)
553             {
554                 var current = (array[index * 2 + 1] << 8) | array[index * 2];
555                 if(current == unconsumedValues[index])
556                 {
557                     output |= 0;
558                 }
559                 else if(current == consumedValues[index])
560                 {
561                     output |= 1;
562                 }
563                 else
564                 {
565                     throw new ArgumentException("Given value is now equal to any of the allowed states");
566                 }
567 
568                 if(index < (valuesCount - 1))
569                 {
570                     output <<= 1;
571                 }
572             }
573             return output;
574         }
575 
576         private IFlagRegisterField creatorPartitionUnlockedFlag;
577         private IFlagRegisterField daiIdleFlag;
578         private IFlagRegisterField ownerPartitionUnlockedFlag;
579         private IFlagRegisterField vendorPartitionUnlockedFlag;
580         private IValueRegisterField accessAddress;
581         private IValueRegisterField readData0;
582         private IValueRegisterField readData1;
583 
584         private IEnumRegisterField<Error> vendorPartitionError;
585         private IEnumRegisterField<Error> creatorPartitionError;
586         private IEnumRegisterField<Error> ownerPartitionError;
587         private IEnumRegisterField<Error> hardwarePartitionError;
588         private IEnumRegisterField<Error> secret0PartitionError;
589         private IEnumRegisterField<Error> secret1PartitionError;
590         private IEnumRegisterField<Error> secret2PartitionError;
591         private IEnumRegisterField<Error> lifeCyclePartitionError;
592         private IEnumRegisterField<Error> daiError;
593 
594         private IFlagRegisterField vendorPartitionErrorFlag;
595         private IFlagRegisterField creatorPartitionErrorFlag;
596         private IFlagRegisterField ownerPartitionErrorFlag;
597         private IFlagRegisterField hardwarePartitionErrorFlag;
598         private IFlagRegisterField secret0PartitionErrorFlag;
599         private IFlagRegisterField secret1PartitionErrorFlag;
600         private IFlagRegisterField secret2PartitionErrorFlag;
601         private IFlagRegisterField lifeCyclePartitionErrorFlag;
602         private IFlagRegisterField daiErrorFlag;
603 
604         private Dictionary<uint, OpenTitan_LifeCycleState> positionsConsumedToLifeCycleState;
605         private OpenTitan_LifeCycleState? cachedLifeCycleState;
606         private ushort? cachedTransitionCount;
607         private object memoryLock;
608         private object transitionCountLock;
609         private ushort[] aValues;
610         private ushort[] bValues;
611         private ushort[] cValues;
612         private ushort[] dValues;
613 
614         private readonly ArrayMemory underlyingMemory;
615 
616         private const int MaximumTransitionsCount = 24;
617         private const int ABValuesWordsCount = 20;
618         private const int CDValuesWordsCount = 24;
619 
620         public enum Registers
621         {
622             InterruptState = 0x0,
623             InterruptEnable = 0x4,
624             InterruptTest = 0x8,
625             AlertTest = 0xc,
626             Status = 0x10,
627             ErrorCode = 0x14,
628             DirectAccesssRegisterEnable = 0x18,
629             DirectAccessCommand = 0x1C,
630             DirectAccessAddress = 0x20,
631             DirectAccessWriteData_0 = 0x24,
632             DirectAccessWriteData_1 = 0x28,
633             DirectAccessReadData_0 = 0x2C,
634             DirectAccessReadData_1 = 0x30,
635             CheckTriggerRegisterWriteEnable = 0x34,
636             CheckTrigger = 0x38,
637             CheckRegistersWriteEnable = 0x3c,
638             CheckTimeout = 0x40,
639             IntegrityCheckPeriod = 0x44,
640             ConsistencyCheckPeriod = 0x48,
641             VendorTestReadLock = 0x4C,
642             CreatorSoftwareConfigReadLock = 0x50,
643             OwnerSoftwareConfigReadLock = 0x54,
644             VendorTestDigest0 = 0x58,
645             VendorTestDigest1 = 0x5C,
646             CreatorSoftwareConfigDigest0 = 0x60,
647             CreatorSoftwareConfigDigest1 = 0x64,
648             OwnerSoftwareConfigDigest0 = 0x68,
649             OwnerSoftwareConfigDigest1 = 0x6C,
650             HardwareConfigDigest0 = 0x70,
651             HardwareConfigDigest1 = 0x74,
652             Secret0Digest0 = 0x78,
653             Secret0Digest1 = 0x7C,
654             Secret1Digest0 = 0x80,
655             Secret1Digest1 = 0x84,
656             Secret2Digest0 = 0x88,
657             Secret2Digest1 = 0x8C,
658             SoftwareConfiguredWindowStart = 0x1000,
659         }
660 
661         public enum OtpItem
662         {
663             [ItemSizeAttribute(56)]
664             Scratch = 0x000,
665 
666             [ItemSizeAttribute(8, is64Bit: true)]
667             VendorTestDigest = 0x038,
668 
669             [ItemSizeAttribute(156)]
670             CreatorSoftwareConfigAstConfig = 0x040,
671 
672             [ItemSizeAttribute(4)]
673             CreatorSoftwareConfigAstInitEnable = 0x0DC,
674 
675             [ItemSizeAttribute(4)]
676             CreatorSoftwareConfigRomExtSku = 0x0E0,
677 
678             [ItemSizeAttribute(4)]
679             CreatorSoftwareConfigUseSwRsaVerify = 0x0E4,
680 
681             [ItemSizeAttribute(8)]
682             CreatorSoftwareConfigKeyIsValid = 0x0E8,
683 
684             [ItemSizeAttribute(4)]
685             CreatorSoftwareConfigFlashDataDefaultConfig = 0x0F0,
686 
687             [ItemSizeAttribute(4)]
688             CreatorSoftwareConfigFlashInfoBootDataConfig = 0x0F4,
689 
690             [ItemSizeAttribute(4)]
691             CreatorSoftwareConfigRngEnable = 0x0F8,
692 
693             [ItemSizeAttribute(4)]
694             CreatorSoftwareConfigJitterEnable = 0x0FC,
695 
696             [ItemSizeAttribute(4)]
697             CreatorSoftwareConfigRetRamResetMask = 0x100,
698 
699             [ItemSizeAttribute(8, is64Bit: true)]
700             CreatorSoftwareConfigDigest = 0x358,
701 
702             [ItemSizeAttribute(4)]
703             RomErrorReporting = 0x360,
704 
705             [ItemSizeAttribute(4)]
706             RomBootstrapEnable = 0x364,
707 
708             [ItemSizeAttribute(4)]
709             RomFaultResponse = 0x368,
710 
711             [ItemSizeAttribute(4)]
712             RomAlertClassEnable = 0x36C,
713 
714             [ItemSizeAttribute(4)]
715             RomAlertEscalation = 0x370,
716 
717             [ItemSizeAttribute(320)]
718             RomAlertClassification = 0x374,
719 
720             [ItemSizeAttribute(64)]
721             RomLocalAlertClassification = 0x4B4,
722 
723             [ItemSizeAttribute(16)]
724             RomAlertAccumThresh = 0x4F4,
725 
726             [ItemSizeAttribute(16)]
727             RomAlertTimeoutCycles = 0x504,
728 
729             [ItemSizeAttribute(64)]
730             RomAlertPhaseCycles = 0x514,
731 
732             [ItemSizeAttribute(4)]
733             RomWatchdogBiteThresholdCycles = 0x554,
734 
735             [ItemSizeAttribute(8, is64Bit: true)]
736             OwnerSwCfgDigest = 0x678,
737 
738             [ItemSizeAttribute(32)]
739             DeviceId = 0x680,
740 
741             [ItemSizeAttribute(32)]
742             ManufacturingState = 0x6A0,
743 
744             [ItemSizeAttribute(1)]
745             EnableSramIfetch = 0x6C0,
746 
747             [ItemSizeAttribute(1)]
748             EnableCsrngSwAppRead = 0x6C1,
749 
750             [ItemSizeAttribute(1)]
751             EnableEntropySrcFwRead = 0x6C2,
752 
753             [ItemSizeAttribute(1)]
754             EnableEntropySrcFwOver = 0x6C3,
755 
756             [ItemSizeAttribute(8, is64Bit: true)]
757             HwCfgDigest = 0x6C8,
758 
759             [ItemSizeAttribute(16, is64Bit: true)]
760             TestUnlockToken = 0x6D0,
761 
762             [ItemSizeAttribute(16, is64Bit: true)]
763             TestExitToken = 0x6E0,
764 
765             [ItemSizeAttribute(8, is64Bit: true)]
766             Secret0Digest = 0x6F0,
767 
768             [ItemSizeAttribute(32, is64Bit: true)]
769             FlashAddressKeySeed = 0x6F8,
770 
771             [ItemSizeAttribute(32, is64Bit: true)]
772             FlashDataKeySeed = 0x718,
773 
774             [ItemSizeAttribute(16, is64Bit: true)]
775             SramDataKeySeed = 0x738,
776 
777             [ItemSizeAttribute(8, is64Bit: true)]
778             Secret1Digest = 0x748,
779 
780             [ItemSizeAttribute(16, is64Bit: true)]
781             RmaToken = 0x750,
782 
783             [ItemSizeAttribute(32, is64Bit: true)]
784             CreatorRootKeyShare0 = 0x760,
785 
786             [ItemSizeAttribute(32, is64Bit: true)]
787             CreatorRootKeyShare1 = 0x780,
788 
789             [ItemSizeAttribute(8, is64Bit: true)]
790             Secret2Digest = 0x7A0,
791 
792             [ItemSizeAttribute(48)]
793             LifeCycleTransitionCount = 0x7A8,
794 
795             [ItemSizeAttribute(40)]
796             LifeCycleState = 0x7D8,
797         }
798 
799         private enum OtpPartition
800         {
801             VendorTest = OtpItem.Scratch,
802             CreatorSoftwareConfig = OtpItem.CreatorSoftwareConfigAstConfig,
803             OwnerSoftwareConfig = OtpItem.RomErrorReporting,
804             HardwareConfig = OtpItem.DeviceId,
805             Secret0 = OtpItem.TestUnlockToken,
806             Secret1 = OtpItem.FlashAddressKeySeed,
807             Secret2 = OtpItem.RmaToken,
808             LifeCycle = OtpItem.LifeCycleTransitionCount,
809         }
810 
811         private enum Error
812         {
813             No = 0x0,
814             Macro = 0x1,
815             MacroCorrectable = 0x2,
816             MacroUncorrectable = 0x3,
817             MacroWriteBlank = 0x4,
818             Access = 0x5,
819             CheckFail = 0x6,
820             FsmState = 0x7,
821         }
822 
823         private class ItemSizeAttribute: Attribute
824         {
ItemSizeAttribute(int byteLength, bool is64Bit = false)825             public ItemSizeAttribute(int byteLength, bool is64Bit = false)
826             {
827                 Is64Bit = is64Bit;
828                 ByteLength = byteLength;
829             }
830             public bool Is64Bit { get; }
831             public int ByteLength { get; }
832         }
833     }
834 }
835