1 // 2 // Copyright (c) 2010-2022 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.Text; 9 using Antmicro.Renode.Peripherals.CPU; 10 using System.Collections.Generic; 11 using System.Collections.ObjectModel; 12 using System.Linq; 13 14 namespace Antmicro.Renode.Utilities.GDB 15 { 16 public class PacketData 17 { PacketData()18 static PacketData() 19 { 20 Success = new PacketData("OK"); 21 Empty = new PacketData(string.Empty); 22 } 23 ErrorReply(Error err = Error.Unknown)24 public static PacketData ErrorReply(Error err = Error.Unknown) 25 { 26 return new PacketData(string.Format("E{0}", (int)err)); 27 } 28 AbortReply(int signal)29 public static PacketData AbortReply(int signal) 30 { 31 return new PacketData(string.Format("X{0:X2}", signal)); 32 } 33 StopReply(int signal)34 public static PacketData StopReply(int signal) 35 { 36 return new PacketData(string.Format("S{0:X2}", signal)); 37 } 38 StopReply(int signal, uint id)39 public static PacketData StopReply(int signal, uint id) 40 { 41 return new PacketData(string.Format("T{0:X2}thread:{1:X2};", signal, id)); 42 } 43 StopReply(BreakpointType reason, ulong? address)44 public static PacketData StopReply(BreakpointType reason, ulong? address) 45 { 46 return new PacketData(string.Format("T05{0}:{1};", reason.GetStopReason(), 47 !address.HasValue ? string.Empty : string.Format("{0:X2}", address))); 48 } 49 StopReply(BreakpointType reason, uint cpuId, ulong? address)50 public static PacketData StopReply(BreakpointType reason, uint cpuId, ulong? address) 51 { 52 return new PacketData(string.Format("T05{0}:{1};thread:{2:X2};", reason.GetStopReason(), 53 !address.HasValue ? string.Empty : string.Format("{0:X2}", address), cpuId)); 54 } 55 PacketData(string data)56 public PacketData(string data) 57 { 58 cachedString = data; 59 rawBytes = bytes = new List<byte>(Encoding.UTF8.GetBytes(data)); 60 RawDataAsBinary = DataAsBinary = new ReadOnlyCollection<byte>(rawBytes); 61 } 62 PacketData()63 public PacketData() 64 { 65 RawDataAsBinary = new ReadOnlyCollection<byte>(rawBytes = new List<byte>()); 66 DataAsBinary = new ReadOnlyCollection<byte>(bytes = new List<byte>()); 67 } 68 AddByte(byte b)69 public bool AddByte(byte b) 70 { 71 rawBytes.Add(b); 72 if(escapeNextByte) 73 { 74 bytes.Add((byte)(b ^ EscapeOffset)); 75 escapeNextByte = false; 76 } 77 else if(b == EscapeSymbol) 78 { 79 escapeNextByte = true; 80 } 81 else 82 { 83 bytes.Add(b); 84 } 85 if(!escapeNextByte) 86 { 87 cachedString = null; 88 Mnemonic = null; 89 return true; 90 } 91 return false; 92 } 93 GetDataAsStringLimited()94 public string GetDataAsStringLimited() 95 { 96 var cs = cachedString; 97 if(cs == null) 98 { 99 var counter = 0; 100 // take only ASCII characters and truncate the size to fit nicely in the log 101 return Encoding.ASCII.GetString(bytes.TakeWhile(b => b >= 0x20 && b <= 0x7e && counter++ < DataAsStringLimit).ToArray()); 102 } 103 else 104 { 105 return cs.Substring(0, Math.Min(cs.Length, DataAsStringLimit)); 106 } 107 } 108 MatchMnemonicFromList(List<string> mnemonicList)109 public string MatchMnemonicFromList(List<string> mnemonicList) 110 { 111 // Start from the longest command to properly distinguish between commands that 112 // have a similar start eg. qC and qCRC 113 foreach(var entry in mnemonicList.OrderByDescending(x => x.Length)) 114 { 115 if(DataAsString.StartsWith(entry)) 116 { 117 Mnemonic = entry; 118 break; 119 } 120 } 121 return Mnemonic; 122 } 123 124 public static PacketData Success { get; private set; } 125 public static PacketData Empty { get; private set; } 126 127 public IEnumerable<byte> RawDataAsBinary { get; private set; } 128 public IEnumerable<byte> DataAsBinary { get; private set; } 129 130 public string DataAsString 131 { 132 get 133 { 134 var cs = cachedString; 135 if(cs == null) 136 { 137 // take only intial ASCII characters and truncate at the first binary byte; 138 // it's ok, since `DataAsString` is used to parse the beginning of a command 139 // (which is always a string) and all potential binary arguments are handled separately 140 cs = Encoding.ASCII.GetString(bytes.TakeWhile(b => b >= 0x20 && b <= 0x7e).ToArray()); 141 cachedString = cs; 142 } 143 return cs; 144 } 145 } 146 147 public string Mnemonic { get; private set; } 148 149 private const byte EscapeOffset = 0x20; 150 private const byte EscapeSymbol = (byte)'}'; 151 private const int DataAsStringLimit = 100; 152 153 private string cachedString; 154 private bool escapeNextByte; 155 private readonly List<byte> rawBytes; 156 private readonly List<byte> bytes; 157 } 158 159 public enum Error : int 160 { 161 OperationNotPermitted = 1, 162 NoSuchFileOrDirectory = 2, 163 InterruptedSystemCall = 4, 164 BadFileNumber = 9, 165 PermissionDenied = 13, 166 BadAddress = 14, 167 DeviceOrResourceBusy = 16, 168 FileExists = 17, 169 NoSuchDevice = 19, 170 NotADirectory = 20, 171 IsADirectory = 21, 172 InvalidArgument = 22, 173 FileTableOverflow = 23, 174 TooManyOpenFiles = 24, 175 FileTooLarge = 27, 176 NoSpaceLeftOnDevice = 28, 177 IllegalSeek = 29, 178 ReadOnlyFileSystem = 30, 179 NameTooLong = 91, 180 Unknown = 9999 181 } 182 } 183