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