1 //
2 // Copyright (c) 2010-2024 Antmicro
3 // Copyright (c) 2011-2015 Realtime Embedded
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.Threading;
11 using Antmicro.Migrant;
12 using System.Reflection;
13 using Antmicro.Renode.Peripherals.Bus.Wrappers;
14 using Antmicro.Renode.Core.Extensions;
15 using Antmicro.Renode.Debugging;
16 using Antmicro.Renode.Exceptions;
17 using System.Collections.Generic;
18 
19 using Endianess = ELFSharp.ELF.Endianess;
20 
21 namespace Antmicro.Renode.Peripherals.Bus
22 {
23     public class PeripheralAccessMethods
24     {
25         public BusAccess.ByteReadMethod ReadByte;
26         public BusAccess.ByteWriteMethod WriteByte;
27         public BusAccess.WordReadMethod ReadWord;
28         public BusAccess.WordWriteMethod WriteWord;
29         public BusAccess.DoubleWordReadMethod ReadDoubleWord;
30         public BusAccess.DoubleWordWriteMethod WriteDoubleWord;
31         public BusAccess.QuadWordReadMethod ReadQuadWord;
32         public BusAccess.QuadWordWriteMethod WriteQuadWord;
33         public Action<ulong> SetAbsoluteAddress;
34         public IBusPeripheral Peripheral;
35         public string Tag;
36         [Constructor(true)]
37         public SpinLock Lock;
38 
CreateWithLock()39         public static PeripheralAccessMethods CreateWithLock()
40         {
41             // Thread ownership tracking should be enabled. We use the IsHeldByCurrentThread
42             // property to simulate recursive locking on spinlocks
43             return new PeripheralAccessMethods { Lock = new SpinLock(true) };
44         }
45 
PeripheralAccessMethods()46         public PeripheralAccessMethods()
47         {
48             AllTranslationsEnabled = false;
49             byteAccessTranslationEnabledDynamically = false;
50             wordAccessTranslationEnabledDynamically = false;
51             doubleWordAccessTranslationEnabledDynamically = false;
52             quadWordAccessTranslationEnabledDynamically = false;
53         }
54 
SetMethod(MethodInfo i, object obj, BusAccess.Operation operation, BusAccess.Method method)55         public void SetMethod(MethodInfo i, object obj, BusAccess.Operation operation, BusAccess.Method method)
56         {
57             switch(method)
58             {
59             case BusAccess.Method.Byte:
60                 SetReadOrWriteMethod(i, obj, operation, ref ReadByte, ref WriteByte);
61                 break;
62             case BusAccess.Method.Word:
63                 SetReadOrWriteMethod(i, obj, operation, ref ReadWord, ref WriteWord);
64                 break;
65             case BusAccess.Method.DoubleWord:
66                 SetReadOrWriteMethod(i, obj, operation, ref ReadDoubleWord, ref WriteDoubleWord);
67                 break;
68             case BusAccess.Method.QuadWord:
69                 SetReadOrWriteMethod(i, obj, operation, ref ReadQuadWord, ref WriteQuadWord);
70                 break;
71             default:
72                 throw new ArgumentException(string.Format("Unsupported access method: {0}", method));
73             }
74         }
75 
EnableAllTranslations(bool enable, Endianess endianess)76         public void EnableAllTranslations(bool enable, Endianess endianess)
77         {
78             // Methods can be wrapped, e.g. in Read/WriteLoggingWrapper. Wrappers must be left intact.
79             // Therefore, we will only overwrite the "base method" at the bottom of the "wrapper stack".
80             // To achieve this, unwrap each wrapper onto a temporary stack, overwrite the base method,
81             // and then rewrap it in everything from the stack.
82             var wrapperStack = new Stack<Tuple<Type, Type>>(); // two types, for read and write
83             while(ReadByte.Target is HookWrapper)
84             {
85                 wrapperStack.Push(UnwrapMethods());
86             }
87 
88             if(enable)
89             {
90                 BuildMissingAccesses(endianess);
91             }
92             else
93             {
94                 DisableTranslatedAccesses();
95             }
96 
97             // Restore wrappers from stack
98             while(wrapperStack.Count != 0)
99             {
100                 var t = wrapperStack.Pop();
101                 WrapMethods(t.Item1, t.Item2);
102             }
103 
104             AllTranslationsEnabled = enable;
105         }
106 
BuildMissingAccesses(Endianess endianess)107         private void BuildMissingAccesses(Endianess endianess)
108         {
109             DebugHelper.Assert(!(ReadByte.Target is HookWrapper));
110 
111             var accssMethods = new dynamic []
112             {
113                 Tuple.Create(ReadByte, WriteByte, (BusAccess.ByteReadMethod)Peripheral.ReadByteNotTranslated),
114                 Tuple.Create(ReadWord, WriteWord, (BusAccess.WordReadMethod)Peripheral.ReadWordNotTranslated),
115                 Tuple.Create(ReadDoubleWord, WriteDoubleWord, (BusAccess.DoubleWordReadMethod)Peripheral.ReadDoubleWordNotTranslated),
116                 Tuple.Create(ReadQuadWord, WriteQuadWord, (BusAccess.QuadWordReadMethod)Peripheral.ReadQuadWordNotTranslated),
117             };
118 
119             // find implemented access methods
120             dynamic read = null;
121             dynamic write = null;
122 
123             foreach(var methods in accssMethods)
124             {
125                 var readMethod = methods.Item1;
126                 var writeMethod = methods.Item2;
127                 var notTranslated = methods.Item3;
128 
129                 if(readMethod != notTranslated)
130                 {
131                     read = readMethod;
132                     write = writeMethod;
133                     break;
134                 }
135             }
136 
137             if(read == null)
138             {
139                 throw new RecoverableException($"Peripheral {Peripheral} does not implement any memory mapped access methods");
140             }
141 
142             if(ReadByte == Peripheral.ReadByteNotTranslated)
143             {
144                 byteAccessTranslationEnabledDynamically = true;
145                 if(endianess == Endianess.LittleEndian)
146                 {
147                     ReadByte = ReadWriteExtensions.BuildByteReadUsing(read);
148                     WriteByte = ReadWriteExtensions.BuildByteWriteUsing(read, write);
149                 }
150                 else
151                 {
152                     ReadByte = ReadWriteExtensions.BuildByteReadBigEndianUsing(read);
153                     WriteByte = ReadWriteExtensions.BuildByteWriteBigEndianUsing(read, write);
154                 }
155             }
156 
157             if(ReadWord == Peripheral.ReadWordNotTranslated)
158             {
159                 wordAccessTranslationEnabledDynamically = true;
160                 if(endianess == Endianess.LittleEndian)
161                 {
162                     ReadWord = ReadWriteExtensions.BuildWordReadUsing(read);
163                     WriteWord = ReadWriteExtensions.BuildWordWriteUsing(read, write);
164                 }
165                 else
166                 {
167                     ReadWord = ReadWriteExtensions.BuildWordReadBigEndianUsing(read);
168                     WriteWord = ReadWriteExtensions.BuildWordWriteBigEndianUsing(read, write);
169                 }
170             }
171             if(ReadDoubleWord == Peripheral.ReadDoubleWordNotTranslated)
172             {
173                 doubleWordAccessTranslationEnabledDynamically = true;
174                 if(endianess == Endianess.LittleEndian)
175                 {
176                     ReadDoubleWord = ReadWriteExtensions.BuildDoubleWordReadUsing(read);
177                     WriteDoubleWord = ReadWriteExtensions.BuildDoubleWordWriteUsing(read, write);
178                 }
179                 else
180                 {
181                     ReadDoubleWord = ReadWriteExtensions.BuildDoubleWordReadBigEndianUsing(read);
182                     WriteDoubleWord = ReadWriteExtensions.BuildDoubleWordWriteBigEndianUsing(read, write);
183                 }
184             }
185             if(ReadQuadWord == Peripheral.ReadQuadWordNotTranslated)
186             {
187                 quadWordAccessTranslationEnabledDynamically = true;
188                 if(endianess == Endianess.LittleEndian)
189                 {
190                     ReadQuadWord = ReadWriteExtensions.BuildQuadWordReadUsing(read);
191                     WriteQuadWord = ReadWriteExtensions.BuildQuadWordWriteUsing(read, write);
192                 }
193                 else
194                 {
195                     ReadQuadWord = ReadWriteExtensions.BuildQuadWordReadBigEndianUsing(read);
196                     WriteQuadWord = ReadWriteExtensions.BuildQuadWordWriteBigEndianUsing(read, write);
197                 }
198             }
199         }
200 
DisableTranslatedAccesses()201         void DisableTranslatedAccesses()
202         {
203             DebugHelper.Assert(!(ReadByte.Target is HookWrapper));
204 
205             if(byteAccessTranslationEnabledDynamically)
206             {
207                 byteAccessTranslationEnabledDynamically = false;
208                 ReadByte = Peripheral.ReadByteNotTranslated;
209                 WriteByte = Peripheral.WriteByteNotTranslated;
210             }
211 
212             if(wordAccessTranslationEnabledDynamically)
213             {
214                 wordAccessTranslationEnabledDynamically = false;
215                 ReadWord = Peripheral.ReadWordNotTranslated;
216                 WriteWord = Peripheral.WriteWordNotTranslated;
217             }
218 
219             if(doubleWordAccessTranslationEnabledDynamically)
220             {
221                 doubleWordAccessTranslationEnabledDynamically = false;
222                 ReadDoubleWord = Peripheral.ReadDoubleWordNotTranslated;
223                 WriteDoubleWord = Peripheral.WriteDoubleWordNotTranslated;
224             }
225 
226             if(quadWordAccessTranslationEnabledDynamically)
227             {
228                 quadWordAccessTranslationEnabledDynamically = false;
229                 ReadQuadWord = Peripheral.ReadQuadWordNotTranslated;
230                 WriteQuadWord = Peripheral.WriteQuadWordNotTranslated;
231             }
232         }
233 
WrapMethods(Type readWrapperType, Type writeWrapperType)234         public void WrapMethods(Type readWrapperType, Type writeWrapperType)
235         {
236             // Make closed types by binding generic parameters of the wrapper classes
237             var byteReadWrapperType = readWrapperType.MakeGenericType(new [] {typeof(byte)});
238             var wordReadWrapperType = readWrapperType.MakeGenericType(new [] {typeof(ushort)});
239             var doubleWordReadWrapperType = readWrapperType.MakeGenericType(new [] {typeof(uint)});
240             var quadWordReadWrapperType = readWrapperType.MakeGenericType(new [] {typeof(ulong)});
241             var byteWriteWrapperType = writeWrapperType.MakeGenericType(new [] {typeof(byte)});
242             var wordWriteWrapperType = writeWrapperType.MakeGenericType(new [] {typeof(ushort)});
243             var doubleWordWriteWrapperType = writeWrapperType.MakeGenericType(new [] {typeof(uint)});
244             var quadWordWriteWrapperType = writeWrapperType.MakeGenericType(new [] {typeof(ulong)});
245 
246             // Prepare argument lists for each type's constructor
247             var byteReadWrapperArgs = new object[] {Peripheral, new Func<long, byte>(ReadByte)};
248             var wordReadWrapperArgs = new object[] {Peripheral, new Func<long, ushort>(ReadWord)};
249             var doubleWordReadWrapperArgs = new object[] {Peripheral, new Func<long, uint>(ReadDoubleWord)};
250             var quadWordReadWrapperArgs = new object[] {Peripheral, new Func<long, ulong>(ReadQuadWord)};
251             var byteWriteWrapperArgs = new object[] {Peripheral, new Action<long, byte>(WriteByte)};
252             var wordWriteWrapperArgs = new object[] {Peripheral, new Action<long, ushort>(WriteWord)};
253             var doubleWordWriteWrapperArgs = new object[] {Peripheral, new Action<long, uint>(WriteDoubleWord)};
254             var quadWordWriteWrapperArgs = new object[] {Peripheral, new Action<long, ulong>(WriteQuadWord)};
255 
256             // Instantiate each type
257             var byteReadWrapperObj = (ReadHookWrapper<byte>)Activator.CreateInstance(byteReadWrapperType, byteReadWrapperArgs);
258             var wordReadWrapperObj = (ReadHookWrapper<ushort>)Activator.CreateInstance(wordReadWrapperType, wordReadWrapperArgs);
259             var doubleWordReadWrapperObj = (ReadHookWrapper<uint>)Activator.CreateInstance(doubleWordReadWrapperType, doubleWordReadWrapperArgs);
260             var quadWordReadWrapperObj = (ReadHookWrapper<ulong>)Activator.CreateInstance(quadWordReadWrapperType, quadWordReadWrapperArgs);
261             var byteWriteWrapperObj = (WriteHookWrapper<byte>)Activator.CreateInstance(byteWriteWrapperType, byteWriteWrapperArgs);
262             var wordWriteWrapperObj = (WriteHookWrapper<ushort>)Activator.CreateInstance(wordWriteWrapperType, wordWriteWrapperArgs);
263             var doubleWordWriteWrapperObj = (WriteHookWrapper<uint>)Activator.CreateInstance(doubleWordWriteWrapperType, doubleWordWriteWrapperArgs);
264             var quadWordWriteWrapperObj = (WriteHookWrapper<ulong>)Activator.CreateInstance(quadWordWriteWrapperType, quadWordWriteWrapperArgs);
265 
266             // Replace methods with wrapped versions
267             ReadByte = new BusAccess.ByteReadMethod(byteReadWrapperObj.Read);
268             ReadWord = new BusAccess.WordReadMethod(wordReadWrapperObj.Read);
269             ReadDoubleWord = new BusAccess.DoubleWordReadMethod(doubleWordReadWrapperObj.Read);
270             ReadQuadWord = new BusAccess.QuadWordReadMethod(quadWordReadWrapperObj.Read);
271             WriteByte = new BusAccess.ByteWriteMethod(byteWriteWrapperObj.Write);
272             WriteWord = new BusAccess.WordWriteMethod(wordWriteWrapperObj.Write);
273             WriteDoubleWord = new BusAccess.DoubleWordWriteMethod(doubleWordWriteWrapperObj.Write);
274             WriteQuadWord = new BusAccess.QuadWordWriteMethod(quadWordWriteWrapperObj.Write);
275         }
276 
UnwrapMethods()277         public Tuple<Type, Type> UnwrapMethods()
278         {
279             var readWrapperType = ReadByte.Target.GetType().GetGenericTypeDefinition();
280             var writeWrapperType = WriteByte.Target.GetType().GetGenericTypeDefinition();
281             ReadByte = (BusAccess.ByteReadMethod)(((ReadHookWrapper<byte>)ReadByte.Target).OriginalMethod).Target;
282             ReadWord = (BusAccess.WordReadMethod)(((ReadHookWrapper<ushort>)ReadWord.Target).OriginalMethod).Target;
283             ReadDoubleWord = (BusAccess.DoubleWordReadMethod)(((ReadHookWrapper<uint>)ReadDoubleWord.Target).OriginalMethod).Target;
284             ReadQuadWord = (BusAccess.QuadWordReadMethod)(((ReadHookWrapper<ulong>)ReadQuadWord.Target).OriginalMethod).Target;
285             WriteByte = (BusAccess.ByteWriteMethod)(((WriteHookWrapper<byte>)WriteByte.Target).OriginalMethod).Target;
286             WriteWord = (BusAccess.WordWriteMethod)(((WriteHookWrapper<ushort>)WriteWord.Target).OriginalMethod).Target;
287             WriteDoubleWord = (BusAccess.DoubleWordWriteMethod)(((WriteHookWrapper<uint>)WriteDoubleWord.Target).OriginalMethod).Target;
288             WriteQuadWord = (BusAccess.QuadWordWriteMethod)(((WriteHookWrapper<ulong>)WriteQuadWord.Target).OriginalMethod).Target;
289             return new Tuple<Type, Type>(readWrapperType, writeWrapperType);
290         }
291 
RemoveWrappersOfType(Type readWrapperType, Type writeWrapperType)292         public void RemoveWrappersOfType(Type readWrapperType, Type writeWrapperType)
293         {
294             var wrapperStack = new Stack<Tuple<Type, Type>>();
295 
296             while(ReadByte.Target is HookWrapper)
297             {
298                 if(readWrapperType == ReadByte.Target.GetType().GetGenericTypeDefinition()
299                     && writeWrapperType == WriteByte.Target.GetType().GetGenericTypeDefinition())
300                 {
301                     UnwrapMethods();
302                     continue;
303                 }
304 
305                 wrapperStack.Push(UnwrapMethods());
306             }
307 
308             while(wrapperStack.Count != 0)
309             {
310                 var t = wrapperStack.Pop();
311                 WrapMethods(t.Item1, t.Item2);
312             }
313         }
314 
315         public bool AllTranslationsEnabled { get; private set; }
316 
SetReadOrWriteMethod(MethodInfo i, object obj, BusAccess.Operation operation, ref TR readMethod, ref TW writeMethod)317         private static void SetReadOrWriteMethod<TR, TW>(MethodInfo i, object obj, BusAccess.Operation operation, ref TR readMethod, ref TW writeMethod)
318         {
319             switch(operation)
320             {
321             case BusAccess.Operation.Read:
322                 readMethod = (TR)(object)i.CreateDelegate(typeof(TR), obj);
323                 break;
324             case BusAccess.Operation.Write:
325                 writeMethod = (TW)(object)i.CreateDelegate(typeof(TW), obj);
326                 break;
327             default:
328                 throw new ArgumentException(string.Format("Unsupported access operation: {0}", operation));
329             }
330         }
331 
332         private bool byteAccessTranslationEnabledDynamically;
333         private bool wordAccessTranslationEnabledDynamically;
334         private bool doubleWordAccessTranslationEnabledDynamically;
335         private bool quadWordAccessTranslationEnabledDynamically;
336     }
337 }
338 
339