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.Linq;
10 
11 using Antmicro.Renode.Logging;
12 using Antmicro.Renode.Peripherals.CPU;
13 using ELFSharp.ELF;
14 
15 namespace Antmicro.Renode.Utilities.GDB.Commands
16 {
17     internal class Trace32Commands : Command
18     {
Trace32Commands(CommandsManager manager)19         public Trace32Commands(CommandsManager manager) : base(manager)
20         {
21         }
22 
23         [Execute("mspr:")]  // get AArch64 system register
Execute( [Argument(Separator = B, Encoding = ArgumentAttribute.ArgumentEncoding.HexNumber)]uint trace32Encoding, [Argument(Encoding = ArgumentAttribute.ArgumentEncoding.HexNumber)]int sizeInBytes)24         public PacketData Execute(
25             [Argument(Separator = ',', Encoding = ArgumentAttribute.ArgumentEncoding.HexNumber)]uint trace32Encoding,
26             [Argument(Encoding = ArgumentAttribute.ArgumentEncoding.HexNumber)]int sizeInBytes)
27         {
28             if(!(manager.Cpu is ICPUWithAArch64Support cpu))
29             {
30                 manager.Cpu.Log(LogLevel.Error, "GDBStub: This CPU doesn't support getting AArch64 system registers.");
31                 return PacketData.ErrorReply(Error.OperationNotPermitted);
32             }
33 
34             // Multiples of 8 can be used to read registers with incrementing encodings.
35             if(sizeInBytes != 4 && (sizeInBytes % 8) != 0)
36             {
37                 manager.Cpu.Log(LogLevel.Error, "GDBStub: Invalid size: 0x{0:X}", sizeInBytes);
38                 return PacketData.ErrorReply(Error.InvalidArgument);
39             }
40             var registersToRead = sizeInBytes <= 8 ? 1 : sizeInBytes / 8;
41             var valueSize = sizeInBytes <= 8 ? sizeInBytes : 8;
42 
43             var bytes = new List<byte>(registersToRead * valueSize);
44             for(var encoding = CreateAArch64Encoding(trace32Encoding); registersToRead > 0; registersToRead--, encoding.Op2++)
45             {
46                 if(!cpu.TryGetSystemRegisterValue(encoding, out var value))
47                 {
48                     // The 'TryGet' method will log failure details.
49                     return PacketData.ErrorReply(Error.InvalidArgument);
50                 }
51                 bytes = bytes.Append(BitHelper.GetBytesFromValue(value, valueSize, reverse: cpu.Endianness == Endianess.LittleEndian)).ToList();
52             }
53             return new PacketData(string.Join("", bytes.Select(x => x.ToString("X2"))));
54         }
55 
56         [Execute("Mspr:")]  // set AArch64 system register
Execute( [Argument(Separator = B, Encoding = ArgumentAttribute.ArgumentEncoding.HexNumber)]uint trace32Encoding, [Argument(Separator = B, Encoding = ArgumentAttribute.ArgumentEncoding.DecimalNumber)]int sizeInBytes, [Argument(Encoding = ArgumentAttribute.ArgumentEncoding.HexBytesString)]byte[] valueBytes)57         public PacketData Execute(
58             [Argument(Separator = ',', Encoding = ArgumentAttribute.ArgumentEncoding.HexNumber)]uint trace32Encoding,
59             [Argument(Separator = ':', Encoding = ArgumentAttribute.ArgumentEncoding.DecimalNumber)]int sizeInBytes,
60             [Argument(Encoding = ArgumentAttribute.ArgumentEncoding.HexBytesString)]byte[] valueBytes)
61         {
62             if(!(manager.Cpu is ICPUWithAArch64Support cpu))
63             {
64                 manager.Cpu.Log(LogLevel.Error, "GDBStub: This CPU doesn't support setting AArch64 system registers.");
65                 return PacketData.ErrorReply(Error.OperationNotPermitted);
66             }
67 
68             if(sizeInBytes != 4 && sizeInBytes != 8)
69             {
70                 manager.Cpu.Log(LogLevel.Error, "GDBStub: Invalid size: 0x{0:X}", sizeInBytes);
71                 return PacketData.ErrorReply(Error.InvalidArgument);
72             }
73 
74             var value = BitHelper.ToUInt64(valueBytes, index: 0, length: sizeInBytes, reverse: cpu.Endianness == Endianess.LittleEndian);
75             if(!cpu.TrySetSystemRegisterValue(CreateAArch64Encoding(trace32Encoding), value))
76             {
77                 // The 'TrySet' method will log failure details.
78                 return PacketData.ErrorReply(Error.InvalidArgument);
79             }
80             return PacketData.Success;
81         }
82 
CreateAArch64Encoding(uint trace32Encoding)83         private static AArch64SystemRegisterEncoding CreateAArch64Encoding(uint trace32Encoding)
84         {
85             // In Trace32's system register encoding, each hex digit refers to op0, op1... values from MRS/MSR instructions accessing
86             // the given register. For example, 0x30040 refers to op0=3, op1=0, crn=0, crm=4, op2=0 (ID_AA64PFR0_EL1).
87             var nibbles = BitHelper.GetNibbles(trace32Encoding).Reverse().TakeLast(5);
88 
89             var op0 = nibbles.ElementAt(0);
90             var op1 = nibbles.ElementAt(1);
91             var crn = nibbles.ElementAt(2);
92             var crm = nibbles.ElementAt(3);
93             var op2 = nibbles.ElementAt(4);
94             return new AArch64SystemRegisterEncoding(op0, op1, crn, crm, op2);
95         }
96     }
97 }
98