blob: b4b09c91fc5e56b35374d72032aadedfaeb64059 [file]
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;
}
}