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