1 // 2 // Copyright (c) 2010-2024 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; 8 using System.Collections.Generic; 9 using System.Globalization; 10 using System.Linq; 11 using System.Reflection; 12 using System.Text; 13 using Antmicro.Renode.Logging; 14 using Antmicro.Renode.Peripherals.CPU; 15 using ELFSharp.ELF; 16 17 namespace Antmicro.Renode.Utilities.GDB 18 { 19 public abstract class Command : IAutoLoadType 20 { Execute(Command command, Packet packet)21 public static IEnumerable<PacketData> Execute(Command command, Packet packet) 22 { 23 var executeMethod = GetExecutingMethod(command, packet); 24 var mnemonic = packet.Data.Mnemonic; 25 var parsingContext = new ParsingContext(packet, mnemonic.Length); 26 var parameters = executeMethod.GetParameters().Select(x => HandleArgumentNotResolved(parsingContext, x)).ToArray(); 27 28 var result = executeMethod.Invoke(command, parameters); 29 if(result == null) 30 { 31 return Enumerable.Empty<PacketData>(); 32 } 33 else if(result is IEnumerable<PacketData> resultPackets) 34 { 35 return resultPackets; 36 } 37 else if(result is PacketData resultPacket) 38 { 39 return new [] { resultPacket }; 40 } 41 throw new ArgumentOutOfRangeException("result", 42 $"Result of GDB command method was of invalid type '{result.GetType().FullName}'"); 43 } 44 GetExecutingMethods(Type t)45 public static MethodInfo[] GetExecutingMethods(Type t) 46 { 47 if(t.GetConstructor(new[] { typeof(CommandsManager) }) == null) 48 { 49 return new MethodInfo[0]; 50 } 51 52 return t.GetMethods().Where(x => 53 x.GetCustomAttribute<ExecuteAttribute>() != null && 54 x.GetParameters().All(y => y.GetCustomAttribute<ArgumentAttribute>() != null)).ToArray(); 55 } 56 Command(CommandsManager manager)57 protected Command(CommandsManager manager) 58 { 59 this.manager = manager; 60 executingMethods = new Dictionary<string, MethodInfo>(); 61 } 62 TryTranslateAddress(ulong address, out ulong translatedAddress, bool write)63 protected bool TryTranslateAddress(ulong address, out ulong translatedAddress, bool write) 64 { 65 var cpu = manager.Cpu as ICPUWithMMU; 66 if(cpu == null) 67 { 68 translatedAddress = address; 69 return true; 70 } 71 72 var errorValue = ulong.MaxValue; 73 translatedAddress = errorValue; 74 75 if(write) 76 { 77 translatedAddress = cpu.TranslateAddress(address, MpuAccess.Write); 78 79 if(translatedAddress == errorValue) 80 { 81 Logger.LogAs(this, LogLevel.Warning, "Translation address failed for write access type!"); 82 } 83 } 84 else 85 { 86 var fetchAddress = cpu.TranslateAddress(address, MpuAccess.InstructionFetch); 87 var readAddress = cpu.TranslateAddress(address, MpuAccess.Read); 88 89 if(fetchAddress == errorValue && readAddress == errorValue) 90 { 91 Logger.LogAs(this, LogLevel.Warning, "Translation address failed for both read and instruction fetch access types!"); 92 } 93 else if(fetchAddress == errorValue || readAddress == errorValue) 94 { 95 var fetchFailed = fetchAddress == errorValue; 96 var failed = fetchFailed ? "instruction fetch" : "read"; 97 var fallback = fetchFailed ? "read" : "instruction fetch"; 98 Logger.LogAs(this, LogLevel.Debug, "Translation address failed for {0} access type! Returned translation for {1} access type.", failed, fallback); 99 100 translatedAddress = fetchFailed ? readAddress : fetchAddress; 101 } 102 else if(fetchAddress != readAddress) 103 { 104 Logger.LogAs(this, LogLevel.Warning, "Translation address missmatch for read and instruction fetch access types!"); 105 } 106 else 107 { 108 translatedAddress = fetchAddress; 109 } 110 } 111 112 return translatedAddress != errorValue; 113 } 114 GetTranslatedAccesses(ulong address, ulong length, bool write)115 protected IEnumerable<MemoryFragment> GetTranslatedAccesses(ulong address, ulong length, bool write) 116 { 117 var cpu = manager.Cpu as ICPUWithMMU; 118 if(cpu == null) 119 { 120 // NOTE: If given CPU doesn't support MMU, we just assume 1:1 translation 121 return new List<MemoryFragment>() 122 { 123 new MemoryFragment(address, length) 124 }; 125 } 126 127 var pageSize = (ulong)cpu.PageSize; 128 var accesses = new List<MemoryFragment>(); 129 var firstLength = Math.Min(length, pageSize - address % pageSize); 130 131 if(!TryAddTranslatedMemoryFragment(ref accesses, address, firstLength, write)) 132 { 133 return null; 134 } 135 address += firstLength; 136 length -= firstLength; 137 138 while(length > 0) 139 { 140 var translateLength = Math.Min(pageSize, length); 141 if(!TryAddTranslatedMemoryFragment(ref accesses, address, translateLength, write)) 142 { 143 return null; 144 } 145 length -= translateLength; 146 address += translateLength; 147 } 148 149 return accesses; 150 } 151 TryAddTranslatedMemoryFragment(ref List<MemoryFragment> accesses, ulong address, ulong length, bool write)152 protected bool TryAddTranslatedMemoryFragment(ref List<MemoryFragment> accesses, ulong address, ulong length, bool write) 153 { 154 if(length > 0) 155 { 156 if(!TryTranslateAddress(address, out var translatedAddress, write)) 157 { 158 Logger.LogAs(this, LogLevel.Warning, "Could not translate address 0x{0:X} to a valid physical address.", address); 159 return false; 160 } 161 162 accesses.Add(new MemoryFragment(translatedAddress, length)); 163 } 164 return true; 165 } 166 ExpandRegisterValue(ref StringBuilder data, int start, int end, int register)167 protected void ExpandRegisterValue(ref StringBuilder data, int start, int end, int register) 168 { 169 // register may have been reported with bigger Width, fill with zeros up to reported size 170 var isLittleEndian = manager.Cpu.Endianness == Endianess.LittleEndian; 171 var width = (end - start) * 4; 172 var reportedRegisters = manager.GetCompiledRegisters(register); 173 if(reportedRegisters.Any() && reportedRegisters.First().Size > width) 174 { 175 data.Insert(isLittleEndian ? end : start, "00", ((int)reportedRegisters.First().Size - width) / 8); 176 } 177 } 178 179 protected readonly CommandsManager manager; 180 GetExecutingMethod(Command command, Packet packet)181 private static MethodInfo GetExecutingMethod(Command command, Packet packet) 182 { 183 var mnemonic = packet.Data.Mnemonic; 184 if(mnemonic == null) 185 { 186 return null; 187 } 188 if(executingMethods.TryGetValue(mnemonic, out var output)) 189 { 190 return output; 191 } 192 193 var interestingMethods = GetExecutingMethods(command.GetType()); 194 if(!interestingMethods.Any()) 195 { 196 return null; 197 } 198 199 // May return null if the given mnemonic is not supported. We should still put it in the executeMethods to avoid repeating the lookup 200 var method = interestingMethods.FirstOrDefault(x => x.GetCustomAttribute<ExecuteAttribute>().Mnemonic == mnemonic); 201 202 executingMethods.Add(mnemonic, method); 203 return method; 204 } 205 HandleArgumentNotResolved(ParsingContext context, ParameterInfo parameterInfo)206 private static object HandleArgumentNotResolved(ParsingContext context, ParameterInfo parameterInfo) 207 { 208 var attribute = parameterInfo.GetCustomAttribute<ArgumentAttribute>(); 209 if(attribute == null) 210 { 211 throw new ArgumentException(string.Format("Could not resolve argument: {0}", parameterInfo.Name)); 212 } 213 214 var startPosition = context.CurrentPosition; 215 216 // we do not support multiple sets of operation+coreId parameters 217 if(startPosition > context.Packet.Data.DataAsString.Length) 218 { 219 return parameterInfo.DefaultValue; 220 } 221 222 var separatorPosition = attribute.Separator == '\0' ? -1 : context.Packet.Data.DataAsString.IndexOf(attribute.Separator, startPosition); 223 var length = (separatorPosition == -1 ? context.Packet.Data.DataAsString.Length : separatorPosition) - startPosition; 224 var valueToParse = context.Packet.Data.DataAsString.Substring(startPosition, length); 225 context.CurrentPosition += length + 1; 226 227 switch(attribute.Encoding) 228 { 229 case ArgumentAttribute.ArgumentEncoding.HexNumber: 230 return Parse(parameterInfo.ParameterType, valueToParse, NumberStyles.HexNumber); 231 case ArgumentAttribute.ArgumentEncoding.DecimalNumber: 232 return Parse(parameterInfo.ParameterType, valueToParse); 233 case ArgumentAttribute.ArgumentEncoding.BinaryBytes: 234 return context.Packet.Data.DataAsBinary.Skip(startPosition).ToArray(); 235 case ArgumentAttribute.ArgumentEncoding.HexBytesString: 236 return valueToParse.Split(2).Select(x => byte.Parse(x, NumberStyles.HexNumber)).ToArray(); 237 case ArgumentAttribute.ArgumentEncoding.HexString: 238 return Encoding.UTF8.GetString(valueToParse.Split(2).Select(x => byte.Parse(x, NumberStyles.HexNumber)).ToArray()); 239 case ArgumentAttribute.ArgumentEncoding.String: 240 return valueToParse; 241 case ArgumentAttribute.ArgumentEncoding.ThreadId: 242 return new PacketThreadId(valueToParse); 243 default: 244 throw new ArgumentException(string.Format("Unsupported argument type: {0}", parameterInfo.ParameterType.Name)); 245 } 246 } 247 Parse(Type type, string input, NumberStyles style = NumberStyles.Integer)248 private static object Parse(Type type, string input, NumberStyles style = NumberStyles.Integer) 249 { 250 if(type.IsEnum) 251 { 252 return Parse(type.GetEnumUnderlyingType(), input, style); 253 } 254 if(type == typeof(int)) 255 { 256 return int.Parse(input, style); 257 } 258 if(type == typeof(uint)) 259 { 260 return uint.Parse(input, style); 261 } 262 if(type == typeof(ulong)) 263 { 264 return ulong.Parse(input, style); 265 } 266 267 throw new ArgumentException(string.Format("Unsupported type for parsing: {0}", type.Name)); 268 } 269 270 private static Dictionary<string, MethodInfo> executingMethods; 271 272 private class ParsingContext 273 { ParsingContext(Packet packet, int currentPosition)274 public ParsingContext(Packet packet, int currentPosition) 275 { 276 Packet = packet; 277 CurrentPosition = currentPosition; 278 } 279 280 public int CurrentPosition { get; set; } 281 public Packet Packet { get; set; } 282 } 283 } 284 285 public struct MemoryFragment 286 { MemoryFragmentAntmicro.Renode.Utilities.GDB.MemoryFragment287 public MemoryFragment(ulong address, ulong length) 288 { 289 Address = address; 290 Length = length; 291 } 292 293 public ulong Address { get; } 294 public ulong Length { get; } 295 } 296 } 297 298