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.Text;
9 using System.Linq;
10 using System.Collections.Generic;
11 using Antmicro.Renode.Peripherals;
12 using Antmicro.Renode.Peripherals.CPU;
13 
14 namespace Antmicro.Renode.Utilities.GDB.Commands
15 {
16     internal class QueryCommand : Command
17     {
QueryCommand(CommandsManager manager)18         public QueryCommand(CommandsManager manager) : base(manager)
19         {
20         }
21 
22         [Execute("qXfer")]
SendQueryXml( [Argument(Separator = B, Encoding = ArgumentAttribute.ArgumentEncoding.String)]string command, [Argument(Separator = B, Encoding = ArgumentAttribute.ArgumentEncoding.String)]string objectType, [Argument(Separator = B, Encoding = ArgumentAttribute.ArgumentEncoding.String)]string operation, [Argument(Separator = B, Encoding = ArgumentAttribute.ArgumentEncoding.String)]string annex, [Argument(Separator = B, Encoding = ArgumentAttribute.ArgumentEncoding.HexNumber)]int offset, [Argument(Encoding = ArgumentAttribute.ArgumentEncoding.HexNumber)]int length )23         public PacketData SendQueryXml(
24             [Argument(Separator = ':', Encoding = ArgumentAttribute.ArgumentEncoding.String)]string command,
25             [Argument(Separator = ':', Encoding = ArgumentAttribute.ArgumentEncoding.String)]string objectType,
26             [Argument(Separator = ':', Encoding = ArgumentAttribute.ArgumentEncoding.String)]string operation,
27             [Argument(Separator = ':', Encoding = ArgumentAttribute.ArgumentEncoding.String)]string annex,
28             [Argument(Separator = ',', Encoding = ArgumentAttribute.ArgumentEncoding.HexNumber)]int offset,
29             [Argument(Encoding = ArgumentAttribute.ArgumentEncoding.HexNumber)]int length
30         )
31         {
32             if((objectType != "features" && objectType != "threads") || operation != "read")
33             {
34                 return PacketData.Empty;
35             }
36             if(objectType == "features" && annex != "target.xml")
37             {
38                 return PacketData.ErrorReply();
39             }
40             var xmlFile = new StringBuilder();
41             if(objectType == "features")
42             {
43                 xmlFile.Append("<?xml version=\"1.0\"?>\n<!DOCTYPE feature SYSTEM \"gdb-target.dtd\">\n<target version=\"1.0\">\n");
44                 xmlFile.Append($"<architecture>{manager.Cpu.GDBArchitecture}</architecture>\n");
45                 foreach(var feature in manager.GetCompiledFeatures())
46                 {
47                     AppendFeature(ref xmlFile, feature);
48                 }
49                 xmlFile.Append("</target>\n");
50             }
51             else if(objectType == "threads")
52             {
53                 xmlFile.Append("<?xml version=\"1.0\"?>\n<threads>\n");
54                 foreach(var gdbCpuId in manager.ManagedCpus.GdbCpuIds)
55                 {
56                     xmlFile.Append($"<thread id=\"{gdbCpuId:x}\" core=\"{gdbCpuId - 1}\" name=\"{manager.ManagedCpus[gdbCpuId].GetName()}\"></thread>\n");
57                 }
58                 xmlFile.Append("</threads>\n");
59             }
60 
61             var prefix = offset + length >= xmlFile.Length ? "l" : "m";
62             var xmlSubstring = xmlFile.ToString().Substring(offset, Math.Min(length, xmlFile.Length - offset));
63             return new PacketData(prefix + xmlSubstring);
64         }
65 
AppendFeature(ref StringBuilder xmlFile, GDBFeatureDescriptor feature)66         private static void AppendFeature(ref StringBuilder xmlFile, GDBFeatureDescriptor feature)
67         {
68             xmlFile.Append($"<feature name=\"{feature.Name}\">\n");
69             foreach(var type in feature.Types)
70             {
71                 if(type.Fields?.Any() ?? false)
72                 {
73                     var tagName = type.Type == "enum" ? "evalue" : "field";
74                     AppendTag(ref xmlFile, type.Type, type.Attributes, false);
75                     foreach(var field in type.Fields)
76                     {
77                         AppendTag(ref xmlFile, tagName, field);
78 
79                     }
80                     xmlFile.Append($"</{type.Type}>\n");
81                 }
82                 else
83                 {
84                     AppendTag(ref xmlFile, type.Type, type.Attributes);
85                 }
86             }
87             foreach(var register in feature.Registers)
88             {
89                 xmlFile.Append($"<reg name=\"{register.Name}\" bitsize=\"{register.Size}\" regnum=\"{register.Number}\" ");
90                 if(!String.IsNullOrEmpty(register.Type))
91                 {
92                     xmlFile.Append($"type=\"{register.Type}\" ");
93                 }
94                 if(!String.IsNullOrEmpty(register.Group))
95                 {
96                     xmlFile.Append($"group=\"{register.Group}\" ");
97                 }
98                 xmlFile.Append("/>\n");
99             }
100             xmlFile.Append("</feature>\n");
101         }
102 
AppendTag(ref StringBuilder xmlFile, string name, IReadOnlyDictionary<string, string> attributes, bool closed = true)103         private static void AppendTag(ref StringBuilder xmlFile, string name, IReadOnlyDictionary<string, string> attributes, bool closed = true)
104         {
105             xmlFile.Append($"<{name}");
106             foreach(var pair in attributes)
107             {
108                 xmlFile.Append($" {pair.Key}=\"{pair.Value}\"");
109             }
110             xmlFile.Append(closed ? "/>\n" : ">\n");
111         }
112     }
113 }
114 
115