| using System; | |
| using System.Collections.Generic; | |
| using System.Linq; | |
| using System.Text; | |
| using Antmicro.Renode.Core; | |
| using Antmicro.Renode.Logging; | |
| using Antmicro.Renode.Peripherals.CPU; | |
| using Antmicro.Renode.Peripherals.Bus; | |
| using Antmicro.Renode.Utilities; | |
| namespace Antmicro.Renode.Debug | |
| { | |
| public static class seL4Extensions | |
| { | |
| public static void CreateseL4(this RiscV32 @this) | |
| { | |
| EmulationManager.Instance.CurrentEmulation.ExternalsManager.AddExternal(new seL4(@this), "seL4"); | |
| } | |
| } | |
| public class seL4 : IExternal | |
| { | |
| public seL4(RiscV32 cpu) | |
| { | |
| this.cpu = cpu; | |
| this.mapping = new Dictionary<ulong, string>(); | |
| this.breakpoints = new Dictionary<string, SortedSet<ulong?>>(); | |
| var handleUnknownSyscallAddress = bus.GetSymbolAddress("handleUnknownSyscall"); | |
| this.cpu.AddHook(handleUnknownSyscallAddress, HandleUnknownSyscall); | |
| var idleThreadAddresss = bus.GetSymbolAddress("idle_thread"); | |
| this.cpu.AddHook(idleThreadAddresss, Finalize); | |
| } | |
| public string CurrentThread() | |
| { | |
| var tcb = bus.ReadDoubleWord(this.ksCurThreadPhysAddress, context: cpu); | |
| if(!mapping.ContainsKey(tcb)) | |
| { | |
| return "default"; | |
| } | |
| return mapping[tcb]; | |
| } | |
| public void WaitForThread(string threadName) | |
| { | |
| pendingThreadName = threadName; | |
| } | |
| public void SetBreakpoint(string threadName, ulong? address = null) | |
| { | |
| var realThreadName = mapping.Values.Where(thread => thread.Contains(threadName)).FirstOrDefault(); | |
| if(String.IsNullOrEmpty(realThreadName)) | |
| { | |
| this.Log(LogLevel.Warning, "No thread with name '{0}' found.", threadName); | |
| return; | |
| } | |
| if(!breakpoints.ContainsKey(realThreadName)) | |
| { | |
| breakpoints.Add(realThreadName, new SortedSet<ulong?>()); | |
| } | |
| if(!breakpoints[realThreadName].Add(address)) | |
| { | |
| this.Log(LogLevel.Warning, "This breakpoint already exists."); | |
| } | |
| } | |
| public void RemoveBreakpoint(string threadName, ulong? address = null, bool removeAll = false) | |
| { | |
| var realThreadName = mapping.Values.Where(thread => thread.Contains(threadName)).FirstOrDefault(); | |
| if(String.IsNullOrEmpty(realThreadName)) | |
| { | |
| this.Log(LogLevel.Warning, "No thread with name '{0}' found.", threadName); | |
| return; | |
| } | |
| if(!breakpoints.ContainsKey(realThreadName)) | |
| { | |
| return; | |
| } | |
| if(removeAll) | |
| { | |
| foreach(var addr in breakpoints[realThreadName]) | |
| { | |
| if(addr != null) | |
| { | |
| cpu.RemoveHook(addr.Value, HandleBreakpoint); | |
| } | |
| } | |
| breakpoints.Remove(realThreadName); | |
| } | |
| else | |
| { | |
| if(breakpoints[realThreadName].Remove(address) && address != null) | |
| { | |
| cpu.RemoveHook(address.Value, HandleBreakpoint); | |
| } | |
| } | |
| } | |
| public string[,] GetBreakpoints(string threadName = null) | |
| { | |
| var entries = breakpoints.SelectMany(t => t.Value, (thread, address) => new { Thread = thread.Key, Address = address }); | |
| if(threadName != null) | |
| { | |
| entries = entries.Where(x => x.Thread.Contains(threadName)); | |
| } | |
| var table = new Table().AddRow("Thread", "Address"); | |
| table.AddRows(entries, x => x.Thread, x => x.Address == null ? "null" : "0x{0:X}".FormatWith(x.Address)); | |
| return table.ToArray(); | |
| } | |
| private void HandleUnknownSyscall(ICpuSupportingGdb _cpu, ulong address) | |
| { | |
| if(cpu.A[0] != DebugThreadNameSyscall) | |
| { | |
| return; | |
| } | |
| cpu.RemoveHook(address, HandleUnknownSyscall); | |
| var ksCurThreadAddress = bus.GetSymbolAddress("ksCurThread"); | |
| var lookupIPCBufferAddress = bus.GetSymbolAddress("lookupIPCBuffer"); | |
| var lookupCapAndSlotAddress = bus.GetSymbolAddress("lookupCapAndSlot"); | |
| ksCurThreadPhysAddress = cpu.TranslateAddress(ksCurThreadAddress, MpuAccess.Read); | |
| cpu.AddHook(lookupCapAndSlotAddress, HandleLookupCapAndSlotAddress); | |
| cpu.AddHook(lookupIPCBufferAddress, HandleLookupIPCBuffer); | |
| } | |
| private void Finalize(ICpuSupportingGdb _cpu, ulong address) | |
| { | |
| cpu.RemoveHook(address, Finalize); | |
| Ready = true; | |
| this.Log(LogLevel.Info, "Initialization complete."); | |
| } | |
| private void ksCurThreadChanged(ICpuSupportingGdb _cpu, ulong address) | |
| { | |
| foreach(var addr in breakpoints.Values.SelectMany(v => v).Where(v => v != null)) | |
| { | |
| cpu.RemoveHook(addr.Value, HandleBreakpoint); | |
| } | |
| var thread = CurrentThread(); | |
| if(!breakpoints.ContainsKey(thread)) | |
| { | |
| return; | |
| } | |
| var tcb = cpu.TranslateAddress((ulong)bus.ReadDoubleWord(this.ksCurThreadPhysAddress, context: cpu) + 0x88, MpuAccess.Read); | |
| var pc = bus.ReadDoubleWord(tcb, context: cpu); | |
| cpu.AddHook(pc, HandleThreadSwitch); | |
| } | |
| private void HandleThreadSwitch(ICpuSupportingGdb _cpu, ulong address) | |
| { | |
| cpu.RemoveHook(address, HandleThreadSwitch); | |
| var thread = CurrentThread(); | |
| foreach(var addr in breakpoints[thread]) | |
| { | |
| if(addr == null) | |
| { | |
| continue; | |
| } | |
| cpu.AddHook(addr.Value, HandleBreakpoint); | |
| } | |
| if(breakpoints[thread].Contains(null)) | |
| { | |
| cpu.Pause(); | |
| cpu.EnterSingleStepModeSafely(new HaltArguments(HaltReason.Breakpoint, cpu.Id, address, BreakpointType.MemoryBreakpoint), true); | |
| } | |
| } | |
| private void HandleBreakpoint(ICpuSupportingGdb _cpu, ulong address) | |
| { | |
| cpu.Pause(); | |
| cpu.EnterSingleStepModeSafely(new HaltArguments(HaltReason.Breakpoint, cpu.Id, address, BreakpointType.MemoryBreakpoint), true); | |
| } | |
| private void HandleLookupCapAndSlotAddress(ICpuSupportingGdb _cpu, ulong address) | |
| { | |
| cpu.RemoveHook(address, HandleLookupCapAndSlotAddress); | |
| cpu.AddHook(cpu.RA, HandlePostLookupCapAndSlotAddress); | |
| } | |
| private void HandlePostLookupCapAndSlotAddress(ICpuSupportingGdb _cpu, ulong address) | |
| { | |
| var luRet = cpu.A[0]; | |
| var paddr = cpu.TranslateAddress(luRet + 0x4UL, MpuAccess.Read); | |
| var underlying = bus.ReadDoubleWord(paddr, context: cpu); | |
| currentTCB = underlying & 0xffffff00; | |
| } | |
| private void HandleLookupIPCBuffer(ICpuSupportingGdb _cpu, ulong address) | |
| { | |
| cpu.RemoveHook(address, HandleLookupIPCBuffer); | |
| cpu.AddHook(cpu.RA, HandlePostLookupIPCBuffer); | |
| } | |
| private void HandlePostLookupIPCBuffer(ICpuSupportingGdb _cpu, ulong address) | |
| { | |
| var paddr = cpu.TranslateAddress(cpu.A[0] + 0x4UL, MpuAccess.Read); | |
| // NOTE(mwitkowski): 100 bytes is arbitrary | |
| var bytes = bus.ReadBytes(paddr, 100, context: cpu) | |
| .TakeWhile(b => b != 0) | |
| .ToArray(); | |
| var threadName = System.Text.Encoding.ASCII.GetString(bytes); | |
| if(!mapping.ContainsKey(currentTCB)) | |
| { | |
| mapping.Add(currentTCB, threadName); | |
| } | |
| else if(threadName.Contains("_control")) | |
| { | |
| mapping[currentTCB] = threadName; | |
| } | |
| if(pendingThreadName != null && threadName.Contains(pendingThreadName)) | |
| { | |
| pendingThreadName = null; | |
| cpu.Pause(); | |
| cpu.EnterSingleStepModeSafely(new HaltArguments(HaltReason.Breakpoint, cpu.Id, address, BreakpointType.MemoryBreakpoint), true); | |
| } | |
| } | |
| public bool BreakpointsEnabled | |
| { | |
| get | |
| { | |
| return breakpointsEnabled; | |
| } | |
| set | |
| { | |
| if(breakpointsEnabled == value) | |
| { | |
| return; | |
| } | |
| breakpointsEnabled = value; | |
| var address = bus.GetSymbolAddress("restore_user_context"); | |
| if(value) | |
| { | |
| cpu.AddHook(address, ksCurThreadChanged); | |
| } | |
| else | |
| { | |
| cpu.RemoveHook(address, ksCurThreadChanged); | |
| } | |
| } | |
| } | |
| public string[] Threads => mapping.Values.ToArray(); | |
| public bool Ready { get; private set; } | |
| private const uint DebugThreadNameSyscall = 0xffffffef; | |
| private readonly Dictionary<string, SortedSet<ulong?>> breakpoints; | |
| private readonly Dictionary<ulong, string> mapping; | |
| private readonly RiscV32 cpu; | |
| private SystemBus bus => cpu.Bus; | |
| private bool breakpointsEnabled; | |
| private ulong ksCurThreadPhysAddress; | |
| private string pendingThreadName; | |
| private ulong currentTCB; | |
| } | |
| } | |