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 Antmicro.Renode.Peripherals.CPU; 9 using Antmicro.Renode.Peripherals.Bus; 10 using SysbusAccessWidth = Antmicro.Renode.Peripherals.Bus.SysbusAccessWidth; 11 using System.Collections.Generic; 12 using Antmicro.Renode.Logging; 13 14 namespace Antmicro.Renode.Utilities.GDB.Commands 15 { 16 internal class BreakpointCommand : Command 17 { BreakpointCommand(CommandsManager manager)18 public BreakpointCommand(CommandsManager manager) : base(manager) 19 { 20 watchpoints = new Dictionary<WatchpointDescriptor, int>(); 21 } 22 23 [Execute("Z")] InsertBreakpoint( [Argument(Separator = B)]BreakpointType type, [Argument(Separator = B, Encoding = ArgumentAttribute.ArgumentEncoding.HexNumber)]ulong address, [Argument(Separator = B, Encoding = ArgumentAttribute.ArgumentEncoding.HexNumber)]uint kind)24 public PacketData InsertBreakpoint( 25 [Argument(Separator = ',')]BreakpointType type, 26 [Argument(Separator = ',', Encoding = ArgumentAttribute.ArgumentEncoding.HexNumber)]ulong address, 27 [Argument(Separator = ';', Encoding = ArgumentAttribute.ArgumentEncoding.HexNumber)]uint kind) 28 { 29 // The kind is the size of the breakpoint in bytes. 30 // Currently, we only handle it for watchpoints, where it specifies the number of bytes to watch. 31 // Setting a 0-byte watchpoint, for example with `watch *&variable_of_unknown_type`, gives 32 // unexpected results: GDB says the watchpoint is set, but it can never be hit. 33 if(kind == 0 && (type == BreakpointType.AccessWatchpoint 34 || type == BreakpointType.ReadWatchpoint 35 || type == BreakpointType.WriteWatchpoint)) 36 { 37 return PacketData.ErrorReply(Error.InvalidArgument); 38 } 39 40 switch(type) 41 { 42 case BreakpointType.MemoryBreakpoint: 43 foreach(var cpu in manager.ManagedCpus) 44 { 45 cpu.AddHook(address, MemoryBreakpointHook); 46 } 47 break; 48 case BreakpointType.HardwareBreakpoint: 49 foreach(var cpu in manager.ManagedCpus) 50 { 51 cpu.AddHook(address, HardwareBreakpointHook); 52 } 53 break; 54 case BreakpointType.AccessWatchpoint: 55 AddWatchpointsCoveringMemoryArea(address, kind, Access.ReadAndWrite, AccessWatchpointHook); 56 break; 57 case BreakpointType.ReadWatchpoint: 58 AddWatchpointsCoveringMemoryArea(address, kind, Access.Read, ReadWatchpointHook); 59 break; 60 case BreakpointType.WriteWatchpoint: 61 AddWatchpointsCoveringMemoryArea(address, kind, Access.Write, WriteWatchpointHook); 62 break; 63 default: 64 Logger.LogAs(this, LogLevel.Warning, "Unsupported breakpoint type: {0}, not inserting.", type); 65 return PacketData.ErrorReply(); 66 } 67 68 return PacketData.Success; 69 } 70 71 [Execute("z")] RemoveBreakpoint( [Argument(Separator = B)]BreakpointType type, [Argument(Separator = B, Encoding = ArgumentAttribute.ArgumentEncoding.HexNumber)]ulong address, [Argument(Separator = B, Encoding = ArgumentAttribute.ArgumentEncoding.HexNumber)]uint kind)72 public PacketData RemoveBreakpoint( 73 [Argument(Separator = ',')]BreakpointType type, 74 [Argument(Separator = ',', Encoding = ArgumentAttribute.ArgumentEncoding.HexNumber)]ulong address, 75 [Argument(Separator = ';', Encoding = ArgumentAttribute.ArgumentEncoding.HexNumber)]uint kind) 76 { 77 switch(type) 78 { 79 case BreakpointType.MemoryBreakpoint: 80 foreach(var cpu in manager.ManagedCpus) 81 { 82 cpu.RemoveHook(address, MemoryBreakpointHook); 83 } 84 break; 85 case BreakpointType.HardwareBreakpoint: 86 foreach(var cpu in manager.ManagedCpus) 87 { 88 cpu.RemoveHook(address, HardwareBreakpointHook); 89 } 90 break; 91 case BreakpointType.AccessWatchpoint: 92 RemoveWatchpointsCoveringMemoryArea(address, kind, Access.ReadAndWrite, AccessWatchpointHook); 93 break; 94 case BreakpointType.ReadWatchpoint: 95 RemoveWatchpointsCoveringMemoryArea(address, kind, Access.Read, ReadWatchpointHook); 96 break; 97 case BreakpointType.WriteWatchpoint: 98 RemoveWatchpointsCoveringMemoryArea(address, kind, Access.Write, WriteWatchpointHook); 99 break; 100 default: 101 Logger.LogAs(this, LogLevel.Warning, "Unsupported breakpoint type: {0}, not removing.", type); 102 return PacketData.ErrorReply(); 103 } 104 105 return PacketData.Success; 106 } 107 HardwareBreakpointHook(ICpuSupportingGdb cpu, ulong address)108 private void HardwareBreakpointHook(ICpuSupportingGdb cpu, ulong address) 109 { 110 cpu.EnterSingleStepModeSafely(new HaltArguments(HaltReason.Breakpoint, cpu, breakpointType: BreakpointType.HardwareBreakpoint)); 111 } 112 MemoryBreakpointHook(ICpuSupportingGdb cpu, ulong address)113 private void MemoryBreakpointHook(ICpuSupportingGdb cpu, ulong address) 114 { 115 cpu.EnterSingleStepModeSafely(new HaltArguments(HaltReason.Breakpoint, cpu, breakpointType: BreakpointType.MemoryBreakpoint)); 116 } 117 AccessWatchpointHook(ICpuSupportingGdb cpu, ulong address, SysbusAccessWidth width, ulong value)118 private void AccessWatchpointHook(ICpuSupportingGdb cpu, ulong address, SysbusAccessWidth width, ulong value) 119 { 120 //? I See a possible problem here. 121 //? Here we call `Halt` event with T05 argument, but in a second we will call it once again with S05 in HandleStepping@TranlationCPU. 122 //? It seems to work fine with GDB, but... I don't know if it is fully correct. 123 cpu.EnterSingleStepModeSafely(new HaltArguments(HaltReason.Breakpoint, cpu, address, BreakpointType.AccessWatchpoint)); 124 } 125 WriteWatchpointHook(ICpuSupportingGdb cpu, ulong address, SysbusAccessWidth width, ulong value)126 private void WriteWatchpointHook(ICpuSupportingGdb cpu, ulong address, SysbusAccessWidth width, ulong value) 127 { 128 cpu.EnterSingleStepModeSafely(new HaltArguments(HaltReason.Breakpoint, cpu, address, BreakpointType.WriteWatchpoint)); 129 } 130 ReadWatchpointHook(ICpuSupportingGdb cpu, ulong address, SysbusAccessWidth width, ulong value)131 private void ReadWatchpointHook(ICpuSupportingGdb cpu, ulong address, SysbusAccessWidth width, ulong value) 132 { 133 cpu.EnterSingleStepModeSafely(new HaltArguments(HaltReason.Breakpoint, cpu, address, BreakpointType.ReadWatchpoint)); 134 } 135 AddWatchpointsCoveringMemoryArea(ulong address, uint kind, Access access, BusHookDelegate hook)136 private void AddWatchpointsCoveringMemoryArea(ulong address, uint kind, Access access, BusHookDelegate hook) 137 { 138 // we need to register hooks for all possible access widths covering memory fragment 139 // [address, address + kind) referred by GDB 140 foreach(var descriptor in CalculateAllCoveringAddressess(address, kind, access, hook)) 141 { 142 lock(watchpoints) 143 { 144 if(watchpoints.ContainsKey(descriptor)) 145 { 146 watchpoints[descriptor]++; 147 } 148 else 149 { 150 watchpoints.Add(descriptor, 1); 151 manager.Machine.SystemBus.AddWatchpointHook(descriptor.Address, descriptor.Width, access, hook); 152 } 153 } 154 } 155 } 156 RemoveWatchpointsCoveringMemoryArea(ulong address, uint kind, Access access, BusHookDelegate hook)157 private void RemoveWatchpointsCoveringMemoryArea(ulong address, uint kind, Access access, BusHookDelegate hook) 158 { 159 // we need to unregister hooks from all possible access widths convering memory fragment 160 // [address, address + kind) referred by GDB 161 foreach(var descriptor in CalculateAllCoveringAddressess(address, kind, access, hook)) 162 { 163 lock(watchpoints) 164 { 165 if(watchpoints[descriptor] > 1) 166 { 167 watchpoints[descriptor]--; 168 } 169 else 170 { 171 watchpoints.Remove(descriptor); 172 manager.Machine.SystemBus.RemoveWatchpointHook(descriptor.Address, hook); 173 } 174 } 175 } 176 } 177 CalculateAllCoveringAddressess(ulong address, uint kind, Access access, BusHookDelegate hook)178 private static IEnumerable<WatchpointDescriptor> CalculateAllCoveringAddressess(ulong address, uint kind, Access access, BusHookDelegate hook) 179 { 180 foreach(SysbusAccessWidth width in Enum.GetValues(typeof(SysbusAccessWidth))) 181 { 182 for(var offset = -(long)(address % (ulong)width); offset < kind; offset += (long)width) 183 { 184 yield return new WatchpointDescriptor(address - (ulong)(-offset), width, access, hook); 185 } 186 } 187 } 188 189 private readonly Dictionary<WatchpointDescriptor, int> watchpoints; 190 191 private class WatchpointDescriptor 192 { WatchpointDescriptor(ulong address, SysbusAccessWidth width, Access access, BusHookDelegate hook)193 public WatchpointDescriptor(ulong address, SysbusAccessWidth width, Access access, BusHookDelegate hook) 194 { 195 Address = address; 196 Width = width; 197 Access = access; 198 Hook = hook; 199 } 200 Equals(object obj)201 public override bool Equals(object obj) 202 { 203 var objAsBreakpointDescriptor = obj as WatchpointDescriptor; 204 if(objAsBreakpointDescriptor == null) 205 { 206 return false; 207 } 208 209 return objAsBreakpointDescriptor.Address == Address 210 && objAsBreakpointDescriptor.Width == Width 211 && objAsBreakpointDescriptor.Access == Access 212 && objAsBreakpointDescriptor.Hook == Hook; 213 } 214 GetHashCode()215 public override int GetHashCode() 216 { 217 return 17 * (int)Address 218 + 23 * (int)Width 219 + 17 * (int)Access 220 + 17 * Hook.GetHashCode(); 221 } 222 223 public readonly ulong Address; 224 public readonly SysbusAccessWidth Width; 225 public readonly Access Access; 226 public readonly BusHookDelegate Hook; 227 } 228 } 229 } 230 231