652 lines
24 KiB
C#
652 lines
24 KiB
C#
//
|
|
// Copyright (c) 2010-2024 Antmicro
|
|
//
|
|
// This file is licensed under the MIT License.
|
|
// Full license text is available in 'licenses/MIT.txt'.
|
|
//
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Reflection;
|
|
using Antmicro.Renode.Core;
|
|
using Antmicro.Renode.Logging;
|
|
using Antmicro.Renode.Exceptions;
|
|
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 ICpuSupportingGdb @this, ulong? debugThreadNameSyscallId = null)
|
|
{
|
|
EmulationManager.Instance.CurrentEmulation.ExternalsManager.AddExternal(new SeL4DebugHelper(@this, debugThreadNameSyscallId), "seL4");
|
|
}
|
|
}
|
|
|
|
public class SeL4DebugHelper : IExternal
|
|
{
|
|
public SeL4DebugHelper(ICpuSupportingGdb cpu, ulong? debugThreadNameSyscallId)
|
|
{
|
|
if(cpu is Arm)
|
|
{
|
|
this.callingConvention = new ArmCallingConvention(cpu);
|
|
}
|
|
else if(cpu is RiscV32)
|
|
{
|
|
this.callingConvention = new RiscVCallingConvention(cpu);
|
|
}
|
|
else
|
|
{
|
|
throw new RecoverableException("Only ARM and RV32 based platforms are supported by the seL4 extension");
|
|
}
|
|
|
|
this.debugThreadNameSyscall = debugThreadNameSyscallId ?? DefaultDebugThreadNameSyscall;
|
|
|
|
this.cpu = cpu;
|
|
this.mapping = new Dictionary<ulong, string>();
|
|
this.breakpoints = new Dictionary<ulong, HashSet<string>>();
|
|
this.temporaryBreakpoints = new Dictionary<ulong, HashSet<string>>();
|
|
|
|
// Save restore_user_context as we will be using it pretty often
|
|
this.restoreUserContextAddress = cpu.Bus.GetSymbolAddress("restore_user_context");
|
|
|
|
// handleUnknownSyscall function is handling seL4_DebugThreadName syscall.
|
|
// We are using this hook to inspect thread's TCB after it was initialized
|
|
var handleUnknownSyscallAddress = cpu.Bus.GetSymbolAddress("handleUnknownSyscall");
|
|
this.cpu.AddHook(handleUnknownSyscallAddress, HandleUnknownSyscall);
|
|
// When everything is set up and none of threads is working, this function will be called
|
|
// It seems to be always called after initialization of all CAmkES components
|
|
// so we can use it to check "readiness".
|
|
var idleThreadAddresss = cpu.Bus.GetSymbolAddress("idle_thread");
|
|
this.cpu.AddHook(idleThreadAddresss, Finalize);
|
|
}
|
|
|
|
public string CurrentThread()
|
|
{
|
|
if(callingConvention.PrivilegeMode == PrivilegeMode.Supervisor)
|
|
{
|
|
return "kernel";
|
|
}
|
|
return CurrentThreadUnsafe();
|
|
}
|
|
|
|
public void BreakOnNamingThread(string threadName)
|
|
{
|
|
pendingThreadName = threadName;
|
|
}
|
|
|
|
public void BreakOnExittingUserspace(ExitUserspaceMode mode)
|
|
{
|
|
if(mode == exitUserspaceMode)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(exitUserspaceMode == ExitUserspaceMode.Never)
|
|
{
|
|
cpu.AddHook(callingConvention.SyscallTrapAddress, HandleExitUserspace);
|
|
}
|
|
else if(mode == ExitUserspaceMode.Never)
|
|
{
|
|
cpu.RemoveHook(callingConvention.SyscallTrapAddress, HandleExitUserspace);
|
|
}
|
|
|
|
exitUserspaceMode = mode;
|
|
}
|
|
|
|
// Sets the breakpoint on given address in chosen thread
|
|
// If address is not given, the breakpoint is set right after
|
|
// on the first instruction after context switch
|
|
public void SetBreakpoint(string threadName, ulong address = WildcardAddress)
|
|
{
|
|
SetBreakpointHelper(threadName, address, breakpoints);
|
|
}
|
|
|
|
// Similiar to SetBreakpoint, but for temporary breakpoints
|
|
public void SetTemporaryBreakpoint(string threadName, ulong address = WildcardAddress)
|
|
{
|
|
SetBreakpointHelper(threadName, address, temporaryBreakpoints);
|
|
}
|
|
|
|
// Removes existing breakpoint on given address in chosen thread
|
|
// If address is not given, then breakpoint which happens on context switch
|
|
// is removed (see SetBreakpoint). If removeAll is set to true, all breakpoints for
|
|
// given thread are removed.
|
|
public void RemoveBreakpoint(string threadName, ulong address = WildcardAddress)
|
|
{
|
|
RemoveBreakpointHelper(threadName, address, breakpoints);
|
|
}
|
|
|
|
public void RemoveTemporaryBreakpoint(string threadName, ulong address = WildcardAddress)
|
|
{
|
|
RemoveBreakpointHelper(threadName, address, temporaryBreakpoints);
|
|
}
|
|
|
|
public void RemoveAllBreakpoints(string threadName = null)
|
|
{
|
|
string realThreadName = null;
|
|
if(threadName != null && !TryGetRealThreadName(threadName, out realThreadName))
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach(var item in breakpoints.ToList())
|
|
{
|
|
if(realThreadName != null)
|
|
{
|
|
item.Value.Remove(realThreadName);
|
|
}
|
|
if(realThreadName == null || item.Value.Count == 0)
|
|
{
|
|
breakpoints.Remove(item.Key);
|
|
}
|
|
if(GetBreakpointsCount(item.Key) == 0)
|
|
{
|
|
RemoveHook(item.Key);
|
|
}
|
|
}
|
|
foreach(var item in temporaryBreakpoints.ToList())
|
|
{
|
|
if(realThreadName != null)
|
|
{
|
|
item.Value.Remove(realThreadName);
|
|
}
|
|
if(realThreadName == null || item.Value.Count == 0)
|
|
{
|
|
temporaryBreakpoints.Remove(item.Key);
|
|
}
|
|
if(GetBreakpointsCount(item.Key) == 0)
|
|
{
|
|
RemoveHook(item.Key);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Returns table with all the breakpoints. If threadName is set,
|
|
// returns only breakpoints set in given thread.
|
|
public string[,] GetBreakpoints(string threadName = null)
|
|
{
|
|
var entries = breakpoints.SelectMany(t => t.Value, (entry, thread) => new { Thread = thread, Address = entry.Key, Temporary = false })
|
|
.Concat(temporaryBreakpoints.SelectMany(t => t.Value, (entry, thread) => new { Thread = thread, Address = entry.Key, Temporary = true }));
|
|
|
|
if(threadName != null)
|
|
{
|
|
entries = entries.Where(x => x.Thread.Contains(threadName));
|
|
}
|
|
var table = new Table().AddRow("Thread", "Address", "Temporary");
|
|
table.AddRows(entries,
|
|
x => x.Thread == AnyThreadName ? "any" : x.Thread,
|
|
x => x.Address == WildcardAddress ? "any" : "0x{0:X}".FormatWith(x.Address),
|
|
x => x.Temporary.ToString());
|
|
if(exitUserspaceMode != ExitUserspaceMode.Never)
|
|
{
|
|
table.AddRow("kernel", "any", (exitUserspaceMode == ExitUserspaceMode.Once).ToString());
|
|
}
|
|
return table.ToArray();
|
|
}
|
|
|
|
// Returns list of all the breakpoints in script-friendly format: <THREAD_NAME>:<ADDRESS>\n.
|
|
// If threadName is set, returns only breakpoints set in given thread.
|
|
public string GetBreakpointsPlain(string threadName = null)
|
|
{
|
|
var entries = breakpoints.SelectMany(t => t.Value, (entry, thread) => new { Thread = thread, Address = entry.Key })
|
|
.Concat(temporaryBreakpoints.SelectMany(t => t.Value, (entry, thread) => new { Thread = thread, Address = entry.Key }));
|
|
|
|
if(threadName != null)
|
|
{
|
|
entries = entries.Where(x => x.Thread.Contains(threadName));
|
|
}
|
|
var output = entries.Select(entry => "{0}:{1}".FormatWith(
|
|
entry.Thread,
|
|
entry.Address == WildcardAddress ? "any" : "0x{0:X}".FormatWith(entry.Address)));
|
|
return string.Join("\n", output);
|
|
}
|
|
|
|
public string[] Threads => mapping.Values.ToArray();
|
|
|
|
public bool Ready { get; private set; }
|
|
|
|
private ulong TryTranslateAddress(ICpuSupportingGdb cpu, ulong virtualAddress)
|
|
{
|
|
if(cpu is ICPUWithMMU cpuWithMmu)
|
|
{
|
|
virtualAddress = cpuWithMmu.TranslateAddress(virtualAddress, MpuAccess.Read);
|
|
}
|
|
return virtualAddress;
|
|
}
|
|
|
|
private void HandleUnknownSyscall(ICpuSupportingGdb cpu, ulong address)
|
|
{
|
|
// Check if seL4_DebugThreadName was called
|
|
if((callingConvention.FirstArgument & 0xFFFFFFFF) != debugThreadNameSyscall)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// We are in seL4_DebugThreadName handler, we don't need this hook anymore
|
|
cpu.RemoveHook(address, HandleUnknownSyscall);
|
|
|
|
// This function will now call lookupIPCBuffer and lookupCapAndSlot
|
|
// We can temporarily hook those functions, and save theirs
|
|
// return addresses (which will be somewhere in handleUnknownSyscall)
|
|
// so we can use them later to "scrape" thread information.
|
|
// Additionally, we are getting address of ksCurThread variable
|
|
// which stores address of TCB of current thread.
|
|
var ksCurThreadAddress = cpu.Bus.GetSymbolAddress("ksCurThread");
|
|
var lookupIPCBufferAddress = cpu.Bus.GetSymbolAddress("lookupIPCBuffer");
|
|
var lookupCapAndSlotAddress = cpu.Bus.GetSymbolAddress("lookupCapAndSlot");
|
|
|
|
// At this point we are sure, that we are in kernel context and ksCurrThread symbol vaddr
|
|
// will resolve properly. Therefore we can translate virtual address to physical address
|
|
// and use it to read memory. That allow us to check current TCB no matter in which
|
|
// context/privilege mode we are currently in, ignoring MMU completely.
|
|
ksCurThreadPhysAddress = TryTranslateAddress(cpu, ksCurThreadAddress);
|
|
|
|
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 HandleRestoreUserContext(ICpuSupportingGdb cpu, ulong address)
|
|
{
|
|
var threadName = CurrentThreadUnsafe();
|
|
if(!DoBreakpointExists(WildcardAddress, threadName))
|
|
{
|
|
return;
|
|
}
|
|
|
|
ulong tcbAddress = cpu.Bus.ReadDoubleWord(this.ksCurThreadPhysAddress, context: cpu);
|
|
if(!IsValidAddress(tcbAddress))
|
|
{
|
|
this.Log(LogLevel.Debug, "Got invalid address for TCB, skipping");
|
|
return;
|
|
}
|
|
|
|
var nextPCAddress = TryTranslateAddress(cpu, tcbAddress + callingConvention.TCBNextPCOffset);
|
|
|
|
if(!IsValidAddress(nextPCAddress))
|
|
{
|
|
this.Log(LogLevel.Debug, "NextPC address in TCB is invalid, skipping");
|
|
return;
|
|
}
|
|
|
|
var pc = cpu.Bus.ReadDoubleWord(nextPCAddress, context: cpu);
|
|
cpu.AddHook(pc, HandleThreadSwitch);
|
|
}
|
|
|
|
private void HandleThreadSwitch(ICpuSupportingGdb cpu, ulong address)
|
|
{
|
|
var threadName = CurrentThread();
|
|
// Remove temporary breakpoint if exists
|
|
ClearTemporaryBreakpoint(WildcardAddress, threadName);
|
|
// We changed context, remove this hook as we don't need it anymore
|
|
cpu.RemoveHook(address, HandleThreadSwitch);
|
|
cpu.Pause();
|
|
cpu.EnterSingleStepModeSafely(new HaltArguments(HaltReason.Breakpoint, cpu, address, BreakpointType.MemoryBreakpoint));
|
|
}
|
|
|
|
private void HandleBreakpoint(ICpuSupportingGdb cpu, ulong address)
|
|
{
|
|
var threadName = CurrentThread();
|
|
if(!DoBreakpointExists(address, threadName))
|
|
{
|
|
return;
|
|
}
|
|
|
|
ClearTemporaryBreakpoint(address, threadName);
|
|
cpu.Pause();
|
|
cpu.EnterSingleStepModeSafely(new HaltArguments(HaltReason.Breakpoint, cpu, address, BreakpointType.MemoryBreakpoint));
|
|
}
|
|
|
|
private void HandleExitUserspace(ICpuSupportingGdb cpu, ulong address)
|
|
{
|
|
if(callingConvention.PrivilegeMode != PrivilegeMode.Supervisor)
|
|
{
|
|
return;
|
|
}
|
|
|
|
cpu.Pause();
|
|
cpu.EnterSingleStepModeSafely(new HaltArguments(HaltReason.Breakpoint, cpu, address, BreakpointType.MemoryBreakpoint));
|
|
if(exitUserspaceMode == ExitUserspaceMode.Once)
|
|
{
|
|
cpu.RemoveHook(address, HandleExitUserspace);
|
|
exitUserspaceMode = ExitUserspaceMode.Never;
|
|
}
|
|
}
|
|
|
|
private void HandleLookupCapAndSlotAddress(ICpuSupportingGdb cpu, ulong address)
|
|
{
|
|
// Save address to instruction in handleUnknownSyscall after call to lookupCapAndSlot
|
|
cpu.RemoveHook(address, HandleLookupCapAndSlotAddress);
|
|
cpu.AddHook(callingConvention.ReturnAddress, HandlePostLookupCapAndSlotAddress);
|
|
}
|
|
|
|
private void HandlePostLookupCapAndSlotAddress(ICpuSupportingGdb cpu, ulong address)
|
|
{
|
|
// Return value of lookupCapAndSlot is a structure
|
|
// with size of two machine words. We are interested in second value
|
|
// which is address of the capability (in this case TCB)
|
|
var luRet = callingConvention.ReturnValue;
|
|
var paddr = TryTranslateAddress(cpu, luRet + 0x4UL);
|
|
var underlying = cpu.Bus.ReadDoubleWord(paddr, context: cpu);
|
|
currentTCB = underlying & 0xffffffffffffff00;
|
|
}
|
|
|
|
private void HandleLookupIPCBuffer(ICpuSupportingGdb cpu, ulong address)
|
|
{
|
|
// Save address to instruction in handleUnknownSyscall after call to lookupIPCBuffer
|
|
cpu.RemoveHook(address, HandleLookupIPCBuffer);
|
|
cpu.AddHook(callingConvention.ReturnAddress, HandlePostLookupIPCBuffer);
|
|
}
|
|
|
|
private void HandlePostLookupIPCBuffer(ICpuSupportingGdb cpu, ulong address)
|
|
{
|
|
// In A0 register address to IPC buffer is returned.
|
|
// As seL4_DebugThreadName saves pointer to the string in IPC buffer,
|
|
// we can now just recover and read it.
|
|
var paddr = TryTranslateAddress(cpu, callingConvention.ReturnValue + 0x4UL);
|
|
var buffer = new List<byte>();
|
|
|
|
// Maximum string size is MaximumMesageLength * size of machine word - 1
|
|
for(ulong i = 0; i < MaximumMessageLength * 4 - 1; ++i)
|
|
{
|
|
var c = cpu.Bus.ReadByte(paddr + i, context: cpu);
|
|
if(c == 0)
|
|
{
|
|
break;
|
|
}
|
|
buffer.Add(c);
|
|
}
|
|
|
|
var threadName = System.Text.Encoding.ASCII.GetString(buffer.ToArray());
|
|
|
|
// This function is called _after_ lookupCapAndSlot, therefore we now
|
|
// have both TCB address and thread's name. We can add it to our list
|
|
// of known threads.
|
|
if(!mapping.ContainsKey(currentTCB) || threadName.Contains("_control"))
|
|
{
|
|
mapping[currentTCB] = threadName;
|
|
}
|
|
|
|
// There was pendingThreadName set by WaitForThread function. As we have now all
|
|
// necessary information for requested thread, we can enter SingleStepMode
|
|
// (and thus return to prompt in GDB) so user can do something with it,
|
|
// e.g. create breakpoint on this thread.
|
|
if(pendingThreadName != null && threadName.Contains(pendingThreadName))
|
|
{
|
|
pendingThreadName = null;
|
|
cpu.Pause();
|
|
cpu.EnterSingleStepModeSafely(new HaltArguments(HaltReason.Breakpoint, cpu, address, BreakpointType.MemoryBreakpoint));
|
|
}
|
|
}
|
|
|
|
private int GetBreakpointsCount(ulong address)
|
|
{
|
|
breakpoints.TryGetValue(address, out var bp);
|
|
temporaryBreakpoints.TryGetValue(address, out var tbp);
|
|
return (bp?.Count ?? 0) + (tbp?.Count ?? 0);
|
|
}
|
|
|
|
private bool TryGetRealThreadName(string threadName, out string realThreadName)
|
|
{
|
|
if(threadName == AnyThreadName)
|
|
{
|
|
realThreadName = AnyThreadName;
|
|
return true;
|
|
}
|
|
|
|
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 false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool DoBreakpointExists(ulong address, string threadName)
|
|
{
|
|
return (breakpoints.TryGetValue(address, out var bpList) && (bpList.Contains(AnyThreadName) || bpList.Contains(threadName))) ||
|
|
(temporaryBreakpoints.TryGetValue(address, out var tbpList) && (tbpList.Contains(AnyThreadName) || tbpList.Contains(threadName)));
|
|
}
|
|
|
|
private void AddContextSwitchHook()
|
|
{
|
|
cpu.AddHook(restoreUserContextAddress, HandleRestoreUserContext);
|
|
}
|
|
|
|
private void RemoveContextSwitchHook()
|
|
{
|
|
cpu.RemoveHook(restoreUserContextAddress, HandleRestoreUserContext);
|
|
}
|
|
|
|
private void ClearTemporaryBreakpoint(ulong address, string threadName)
|
|
{
|
|
if(!temporaryBreakpoints.ContainsKey(address))
|
|
{
|
|
return;
|
|
}
|
|
|
|
temporaryBreakpoints[address].Remove(threadName);
|
|
temporaryBreakpoints[address].Remove(AnyThreadName);
|
|
|
|
if(GetBreakpointsCount(address) == 0)
|
|
{
|
|
RemoveHook(address);
|
|
}
|
|
}
|
|
|
|
private void SetBreakpointHelper(string threadName, ulong address, Dictionary<ulong, HashSet<string>> breakpointsSource)
|
|
{
|
|
if(!TryGetRealThreadName(threadName, out var realThreadName))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(!breakpointsSource.ContainsKey(address))
|
|
{
|
|
breakpointsSource.Add(address, new HashSet<string>());
|
|
}
|
|
|
|
if(!breakpointsSource[address].Add(realThreadName))
|
|
{
|
|
this.Log(LogLevel.Warning, "This breakpoint already exists.");
|
|
return;
|
|
}
|
|
|
|
var breakpointsNum = GetBreakpointsCount(address);
|
|
|
|
// Ignore if we already registered breakpoint for this address
|
|
if(breakpointsNum != 1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
AddHook(address);
|
|
}
|
|
|
|
private void RemoveBreakpointHelper(string threadName, ulong address, Dictionary<ulong, HashSet<string>> breakpointsSource)
|
|
{
|
|
if(!breakpointsSource.TryGetValue(address, out var breakpoint))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(!TryGetRealThreadName(threadName, out var realThreadName))
|
|
{
|
|
return;
|
|
}
|
|
|
|
breakpoint.Remove(realThreadName);
|
|
var breakpointsNum = GetBreakpointsCount(address);
|
|
if(breakpointsNum != 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
RemoveHook(address);
|
|
}
|
|
|
|
private void AddHook(ulong address)
|
|
{
|
|
if(address != WildcardAddress)
|
|
{
|
|
cpu.AddHook(address, HandleBreakpoint);
|
|
}
|
|
else
|
|
{
|
|
AddContextSwitchHook();
|
|
}
|
|
}
|
|
|
|
private void RemoveHook(ulong address)
|
|
{
|
|
if(address != WildcardAddress)
|
|
{
|
|
cpu.RemoveHook(address, HandleBreakpoint);
|
|
}
|
|
else
|
|
{
|
|
RemoveContextSwitchHook();
|
|
}
|
|
}
|
|
|
|
private bool IsValidAddress(ulong address)
|
|
{
|
|
return !(address == 0x00000000 || address == 0xFFFFFFFF);
|
|
}
|
|
|
|
private string CurrentThreadUnsafe()
|
|
{
|
|
var tcb = cpu.Bus.ReadDoubleWord(this.ksCurThreadPhysAddress, context: cpu);
|
|
if(mapping.ContainsKey(tcb))
|
|
{
|
|
return mapping[tcb];
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
public enum ExitUserspaceMode
|
|
{
|
|
Never,
|
|
Once,
|
|
Always,
|
|
}
|
|
|
|
private const uint DefaultDebugThreadNameSyscall = 0xfffffff2;
|
|
private const string AnyThreadName = "<any>";
|
|
private const uint WildcardAddress = 0x00000000;
|
|
private const uint MaximumMessageLength = 120;
|
|
|
|
private readonly Dictionary<ulong, HashSet<string>> breakpoints;
|
|
private readonly Dictionary<ulong, HashSet<string>> temporaryBreakpoints;
|
|
private readonly Dictionary<ulong, string> mapping;
|
|
private readonly ICpuSupportingGdb cpu;
|
|
private readonly ICallingConvention callingConvention;
|
|
private readonly ulong debugThreadNameSyscall;
|
|
|
|
private ExitUserspaceMode exitUserspaceMode;
|
|
private bool breakpointsEnabled;
|
|
private ulong ksCurThreadPhysAddress;
|
|
private ulong restoreUserContextAddress;
|
|
private string pendingThreadName;
|
|
private ulong currentTCB;
|
|
|
|
private interface ICallingConvention
|
|
{
|
|
ulong FirstArgument { get; }
|
|
ulong ReturnValue { get; }
|
|
ulong ReturnAddress { get; }
|
|
ulong SyscallTrapAddress { get; }
|
|
ulong TCBNextPCOffset { get; }
|
|
PrivilegeMode PrivilegeMode { get; }
|
|
}
|
|
|
|
private enum PrivilegeMode
|
|
{
|
|
Userspace,
|
|
Supervisor,
|
|
Other,
|
|
}
|
|
|
|
private class RiscVCallingConvention : ICallingConvention
|
|
{
|
|
public RiscVCallingConvention(ICpuSupportingGdb cpu)
|
|
{
|
|
this.cpu = cpu;
|
|
// Assumes that symbols for kernel are loaded
|
|
syscallTrapAddress = cpu.Bus.GetSymbolAddress("trap_entry");
|
|
}
|
|
|
|
public ulong FirstArgument => cpu.A[0];
|
|
public ulong ReturnValue => cpu.A[0];
|
|
public ulong ReturnAddress => cpu.RA;
|
|
public ulong SyscallTrapAddress => syscallTrapAddress;
|
|
public PrivilegeMode PrivilegeMode
|
|
{
|
|
get
|
|
{
|
|
switch((byte)cpu.PRIV)
|
|
{
|
|
case 0b00:
|
|
return PrivilegeMode.Userspace;
|
|
case 0b01:
|
|
return PrivilegeMode.Supervisor;
|
|
default:
|
|
return PrivilegeMode.Other;
|
|
}
|
|
}
|
|
}
|
|
public ulong TCBNextPCOffset => 34 * 4;
|
|
|
|
private readonly ulong syscallTrapAddress;
|
|
private readonly dynamic cpu;
|
|
}
|
|
|
|
private class ArmCallingConvention : ICallingConvention
|
|
{
|
|
public ArmCallingConvention(ICpuSupportingGdb cpu)
|
|
{
|
|
this.cpu = (Arm)cpu;
|
|
// Assumes that symbols for kernel are loaded
|
|
syscallTrapAddress = cpu.Bus.GetSymbolAddress("arm_swi_syscall");
|
|
}
|
|
|
|
public ulong FirstArgument => cpu.R[0];
|
|
public ulong ReturnValue => cpu.R[0];
|
|
public ulong ReturnAddress => cpu.R[14];
|
|
public ulong SyscallTrapAddress => syscallTrapAddress;
|
|
public PrivilegeMode PrivilegeMode
|
|
{
|
|
get
|
|
{
|
|
switch(cpu.CPSR & 0xfUL)
|
|
{
|
|
case 0b00:
|
|
return PrivilegeMode.Userspace;
|
|
case 0b11:
|
|
return PrivilegeMode.Supervisor;
|
|
default:
|
|
return PrivilegeMode.Other;
|
|
}
|
|
}
|
|
}
|
|
public ulong TCBNextPCOffset => 15 * 4;
|
|
|
|
private readonly ulong syscallTrapAddress;
|
|
private readonly Arm cpu;
|
|
}
|
|
}
|
|
}
|
|
|