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.Collections.Generic; 9 using System.Linq; 10 using System.Text; 11 using System.Reflection; 12 using Antmicro.Renode.Core; 13 using Antmicro.Renode.Logging; 14 using Antmicro.Renode.Exceptions; 15 using Antmicro.Renode.Peripherals.CPU; 16 using Antmicro.Renode.Peripherals.Bus; 17 using Antmicro.Renode.Utilities; 18 19 namespace Antmicro.Renode.Debug 20 { 21 public static class SeL4Extensions 22 { CreateSeL4(this ICpuSupportingGdb @this, ulong? debugThreadNameSyscallId = null)23 public static void CreateSeL4(this ICpuSupportingGdb @this, ulong? debugThreadNameSyscallId = null) 24 { 25 EmulationManager.Instance.CurrentEmulation.ExternalsManager.AddExternal(new SeL4DebugHelper(@this, debugThreadNameSyscallId), "seL4"); 26 } 27 } 28 29 public class SeL4DebugHelper : IExternal 30 { SeL4DebugHelper(ICpuSupportingGdb cpu, ulong? debugThreadNameSyscallId)31 public SeL4DebugHelper(ICpuSupportingGdb cpu, ulong? debugThreadNameSyscallId) 32 { 33 if(cpu is Arm) 34 { 35 this.callingConvention = new ArmCallingConvention(cpu); 36 } 37 else if(cpu is RiscV32) 38 { 39 this.callingConvention = new RiscVCallingConvention(cpu); 40 } 41 else 42 { 43 throw new RecoverableException("Only ARM and RV32 based platforms are supported by the seL4 extension"); 44 } 45 46 this.debugThreadNameSyscall = debugThreadNameSyscallId ?? DefaultDebugThreadNameSyscall; 47 48 this.cpu = cpu; 49 this.mapping = new Dictionary<ulong, string>(); 50 this.breakpoints = new Dictionary<ulong, HashSet<string>>(); 51 this.temporaryBreakpoints = new Dictionary<ulong, HashSet<string>>(); 52 53 // Save restore_user_context as we will be using it pretty often 54 this.restoreUserContextAddress = cpu.Bus.GetSymbolAddress("restore_user_context"); 55 56 // handleUnknownSyscall function is handling seL4_DebugThreadName syscall. 57 // We are using this hook to inspect thread's TCB after it was initialized 58 var handleUnknownSyscallAddress = cpu.Bus.GetSymbolAddress("handleUnknownSyscall"); 59 this.cpu.AddHook(handleUnknownSyscallAddress, HandleUnknownSyscall); 60 // When everything is set up and none of threads is working, this function will be called 61 // It seems to be always called after initialization of all CAmkES components 62 // so we can use it to check "readiness". 63 var idleThreadAddresss = cpu.Bus.GetSymbolAddress("idle_thread"); 64 this.cpu.AddHook(idleThreadAddresss, Finalize); 65 } 66 CurrentThread()67 public string CurrentThread() 68 { 69 if(callingConvention.PrivilegeMode == PrivilegeMode.Supervisor) 70 { 71 return "kernel"; 72 } 73 return CurrentThreadUnsafe(); 74 } 75 BreakOnNamingThread(string threadName)76 public void BreakOnNamingThread(string threadName) 77 { 78 pendingThreadName = threadName; 79 } 80 BreakOnExittingUserspace(ExitUserspaceMode mode)81 public void BreakOnExittingUserspace(ExitUserspaceMode mode) 82 { 83 if(mode == exitUserspaceMode) 84 { 85 return; 86 } 87 88 if(exitUserspaceMode == ExitUserspaceMode.Never) 89 { 90 cpu.AddHook(callingConvention.SyscallTrapAddress, HandleExitUserspace); 91 } 92 else if(mode == ExitUserspaceMode.Never) 93 { 94 cpu.RemoveHook(callingConvention.SyscallTrapAddress, HandleExitUserspace); 95 } 96 97 exitUserspaceMode = mode; 98 } 99 100 // Sets the breakpoint on given address in chosen thread 101 // If address is not given, the breakpoint is set right after 102 // on the first instruction after context switch SetBreakpoint(string threadName, ulong address = WildcardAddress)103 public void SetBreakpoint(string threadName, ulong address = WildcardAddress) 104 { 105 SetBreakpointHelper(threadName, address, breakpoints); 106 } 107 108 // Similiar to SetBreakpoint, but for temporary breakpoints SetTemporaryBreakpoint(string threadName, ulong address = WildcardAddress)109 public void SetTemporaryBreakpoint(string threadName, ulong address = WildcardAddress) 110 { 111 SetBreakpointHelper(threadName, address, temporaryBreakpoints); 112 } 113 114 // Removes existing breakpoint on given address in chosen thread 115 // If address is not given, then breakpoint which happens on context switch 116 // is removed (see SetBreakpoint). If removeAll is set to true, all breakpoints for 117 // given thread are removed. RemoveBreakpoint(string threadName, ulong address = WildcardAddress)118 public void RemoveBreakpoint(string threadName, ulong address = WildcardAddress) 119 { 120 RemoveBreakpointHelper(threadName, address, breakpoints); 121 } 122 RemoveTemporaryBreakpoint(string threadName, ulong address = WildcardAddress)123 public void RemoveTemporaryBreakpoint(string threadName, ulong address = WildcardAddress) 124 { 125 RemoveBreakpointHelper(threadName, address, temporaryBreakpoints); 126 } 127 RemoveAllBreakpoints(string threadName = null)128 public void RemoveAllBreakpoints(string threadName = null) 129 { 130 string realThreadName = null; 131 if(threadName != null && !TryGetRealThreadName(threadName, out realThreadName)) 132 { 133 return; 134 } 135 136 foreach(var item in breakpoints.ToList()) 137 { 138 if(realThreadName != null) 139 { 140 item.Value.Remove(realThreadName); 141 } 142 if(realThreadName == null || item.Value.Count == 0) 143 { 144 breakpoints.Remove(item.Key); 145 } 146 if(GetBreakpointsCount(item.Key) == 0) 147 { 148 RemoveHook(item.Key); 149 } 150 } 151 foreach(var item in temporaryBreakpoints.ToList()) 152 { 153 if(realThreadName != null) 154 { 155 item.Value.Remove(realThreadName); 156 } 157 if(realThreadName == null || item.Value.Count == 0) 158 { 159 temporaryBreakpoints.Remove(item.Key); 160 } 161 if(GetBreakpointsCount(item.Key) == 0) 162 { 163 RemoveHook(item.Key); 164 } 165 } 166 } 167 168 // Returns table with all the breakpoints. If threadName is set, 169 // returns only breakpoints set in given thread. GetBreakpoints(string threadName = null)170 public string[,] GetBreakpoints(string threadName = null) 171 { 172 var entries = breakpoints.SelectMany(t => t.Value, (entry, thread) => new { Thread = thread, Address = entry.Key, Temporary = false }) 173 .Concat(temporaryBreakpoints.SelectMany(t => t.Value, (entry, thread) => new { Thread = thread, Address = entry.Key, Temporary = true })); 174 175 if(threadName != null) 176 { 177 entries = entries.Where(x => x.Thread.Contains(threadName)); 178 } 179 var table = new Table().AddRow("Thread", "Address", "Temporary"); 180 table.AddRows(entries, 181 x => x.Thread == AnyThreadName ? "any" : x.Thread, 182 x => x.Address == WildcardAddress ? "any" : "0x{0:X}".FormatWith(x.Address), 183 x => x.Temporary.ToString()); 184 if(exitUserspaceMode != ExitUserspaceMode.Never) 185 { 186 table.AddRow("kernel", "any", (exitUserspaceMode == ExitUserspaceMode.Once).ToString()); 187 } 188 return table.ToArray(); 189 } 190 191 // Returns list of all the breakpoints in script-friendly format: <THREAD_NAME>:<ADDRESS>\n. 192 // If threadName is set, returns only breakpoints set in given thread. GetBreakpointsPlain(string threadName = null)193 public string GetBreakpointsPlain(string threadName = null) 194 { 195 var entries = breakpoints.SelectMany(t => t.Value, (entry, thread) => new { Thread = thread, Address = entry.Key }) 196 .Concat(temporaryBreakpoints.SelectMany(t => t.Value, (entry, thread) => new { Thread = thread, Address = entry.Key })); 197 198 if(threadName != null) 199 { 200 entries = entries.Where(x => x.Thread.Contains(threadName)); 201 } 202 var output = entries.Select(entry => "{0}:{1}".FormatWith( 203 entry.Thread, 204 entry.Address == WildcardAddress ? "any" : "0x{0:X}".FormatWith(entry.Address))); 205 return string.Join("\n", output); 206 } 207 208 public string[] Threads => mapping.Values.ToArray(); 209 210 public bool Ready { get; private set; } 211 TryTranslateAddress(ICpuSupportingGdb cpu, ulong virtualAddress)212 private ulong TryTranslateAddress(ICpuSupportingGdb cpu, ulong virtualAddress) 213 { 214 if(cpu is ICPUWithMMU cpuWithMmu) 215 { 216 virtualAddress = cpuWithMmu.TranslateAddress(virtualAddress, MpuAccess.Read); 217 } 218 return virtualAddress; 219 } 220 HandleUnknownSyscall(ICpuSupportingGdb cpu, ulong address)221 private void HandleUnknownSyscall(ICpuSupportingGdb cpu, ulong address) 222 { 223 // Check if seL4_DebugThreadName was called 224 if((callingConvention.FirstArgument & 0xFFFFFFFF) != debugThreadNameSyscall) 225 { 226 return; 227 } 228 229 // We are in seL4_DebugThreadName handler, we don't need this hook anymore 230 cpu.RemoveHook(address, HandleUnknownSyscall); 231 232 // This function will now call lookupIPCBuffer and lookupCapAndSlot 233 // We can temporarily hook those functions, and save theirs 234 // return addresses (which will be somewhere in handleUnknownSyscall) 235 // so we can use them later to "scrape" thread information. 236 // Additionally, we are getting address of ksCurThread variable 237 // which stores address of TCB of current thread. 238 var ksCurThreadAddress = cpu.Bus.GetSymbolAddress("ksCurThread"); 239 var lookupIPCBufferAddress = cpu.Bus.GetSymbolAddress("lookupIPCBuffer"); 240 var lookupCapAndSlotAddress = cpu.Bus.GetSymbolAddress("lookupCapAndSlot"); 241 242 // At this point we are sure, that we are in kernel context and ksCurrThread symbol vaddr 243 // will resolve properly. Therefore we can translate virtual address to physical address 244 // and use it to read memory. That allow us to check current TCB no matter in which 245 // context/privilege mode we are currently in, ignoring MMU completely. 246 ksCurThreadPhysAddress = TryTranslateAddress(cpu, ksCurThreadAddress); 247 248 cpu.AddHook(lookupCapAndSlotAddress, HandleLookupCapAndSlotAddress); 249 cpu.AddHook(lookupIPCBufferAddress, HandleLookupIPCBuffer); 250 } 251 Finalize(ICpuSupportingGdb cpu, ulong address)252 private void Finalize(ICpuSupportingGdb cpu, ulong address) 253 { 254 cpu.RemoveHook(address, Finalize); 255 Ready = true; 256 this.Log(LogLevel.Info, "Initialization complete."); 257 } 258 HandleRestoreUserContext(ICpuSupportingGdb cpu, ulong address)259 private void HandleRestoreUserContext(ICpuSupportingGdb cpu, ulong address) 260 { 261 var threadName = CurrentThreadUnsafe(); 262 if(!DoBreakpointExists(WildcardAddress, threadName)) 263 { 264 return; 265 } 266 267 ulong tcbAddress = cpu.Bus.ReadDoubleWord(this.ksCurThreadPhysAddress, context: cpu); 268 if(!IsValidAddress(tcbAddress)) 269 { 270 this.Log(LogLevel.Debug, "Got invalid address for TCB, skipping"); 271 return; 272 } 273 274 var nextPCAddress = TryTranslateAddress(cpu, tcbAddress + callingConvention.TCBNextPCOffset); 275 276 if(!IsValidAddress(nextPCAddress)) 277 { 278 this.Log(LogLevel.Debug, "NextPC address in TCB is invalid, skipping"); 279 return; 280 } 281 282 var pc = cpu.Bus.ReadDoubleWord(nextPCAddress, context: cpu); 283 cpu.AddHook(pc, HandleThreadSwitch); 284 } 285 HandleThreadSwitch(ICpuSupportingGdb cpu, ulong address)286 private void HandleThreadSwitch(ICpuSupportingGdb cpu, ulong address) 287 { 288 var threadName = CurrentThread(); 289 // Remove temporary breakpoint if exists 290 ClearTemporaryBreakpoint(WildcardAddress, threadName); 291 // We changed context, remove this hook as we don't need it anymore 292 cpu.RemoveHook(address, HandleThreadSwitch); 293 cpu.Pause(); 294 cpu.EnterSingleStepModeSafely(new HaltArguments(HaltReason.Breakpoint, cpu, address, BreakpointType.MemoryBreakpoint)); 295 } 296 HandleBreakpoint(ICpuSupportingGdb cpu, ulong address)297 private void HandleBreakpoint(ICpuSupportingGdb cpu, ulong address) 298 { 299 var threadName = CurrentThread(); 300 if(!DoBreakpointExists(address, threadName)) 301 { 302 return; 303 } 304 305 ClearTemporaryBreakpoint(address, threadName); 306 cpu.Pause(); 307 cpu.EnterSingleStepModeSafely(new HaltArguments(HaltReason.Breakpoint, cpu, address, BreakpointType.MemoryBreakpoint)); 308 } 309 HandleExitUserspace(ICpuSupportingGdb cpu, ulong address)310 private void HandleExitUserspace(ICpuSupportingGdb cpu, ulong address) 311 { 312 if(callingConvention.PrivilegeMode != PrivilegeMode.Supervisor) 313 { 314 return; 315 } 316 317 cpu.Pause(); 318 cpu.EnterSingleStepModeSafely(new HaltArguments(HaltReason.Breakpoint, cpu, address, BreakpointType.MemoryBreakpoint)); 319 if(exitUserspaceMode == ExitUserspaceMode.Once) 320 { 321 cpu.RemoveHook(address, HandleExitUserspace); 322 exitUserspaceMode = ExitUserspaceMode.Never; 323 } 324 } 325 HandleLookupCapAndSlotAddress(ICpuSupportingGdb cpu, ulong address)326 private void HandleLookupCapAndSlotAddress(ICpuSupportingGdb cpu, ulong address) 327 { 328 // Save address to instruction in handleUnknownSyscall after call to lookupCapAndSlot 329 cpu.RemoveHook(address, HandleLookupCapAndSlotAddress); 330 cpu.AddHook(callingConvention.ReturnAddress, HandlePostLookupCapAndSlotAddress); 331 } 332 HandlePostLookupCapAndSlotAddress(ICpuSupportingGdb cpu, ulong address)333 private void HandlePostLookupCapAndSlotAddress(ICpuSupportingGdb cpu, ulong address) 334 { 335 // Return value of lookupCapAndSlot is a structure 336 // with size of two machine words. We are interested in second value 337 // which is address of the capability (in this case TCB) 338 var luRet = callingConvention.ReturnValue; 339 var paddr = TryTranslateAddress(cpu, luRet + 0x4UL); 340 var underlying = cpu.Bus.ReadDoubleWord(paddr, context: cpu); 341 currentTCB = underlying & 0xffffffffffffff00; 342 } 343 HandleLookupIPCBuffer(ICpuSupportingGdb cpu, ulong address)344 private void HandleLookupIPCBuffer(ICpuSupportingGdb cpu, ulong address) 345 { 346 // Save address to instruction in handleUnknownSyscall after call to lookupIPCBuffer 347 cpu.RemoveHook(address, HandleLookupIPCBuffer); 348 cpu.AddHook(callingConvention.ReturnAddress, HandlePostLookupIPCBuffer); 349 } 350 HandlePostLookupIPCBuffer(ICpuSupportingGdb cpu, ulong address)351 private void HandlePostLookupIPCBuffer(ICpuSupportingGdb cpu, ulong address) 352 { 353 // In A0 register address to IPC buffer is returned. 354 // As seL4_DebugThreadName saves pointer to the string in IPC buffer, 355 // we can now just recover and read it. 356 var paddr = TryTranslateAddress(cpu, callingConvention.ReturnValue + 0x4UL); 357 var buffer = new List<byte>(); 358 359 // Maximum string size is MaximumMesageLength * size of machine word - 1 360 for(ulong i = 0; i < MaximumMessageLength * 4 - 1; ++i) 361 { 362 var c = cpu.Bus.ReadByte(paddr + i, context: cpu); 363 if(c == 0) 364 { 365 break; 366 } 367 buffer.Add(c); 368 } 369 370 var threadName = System.Text.Encoding.ASCII.GetString(buffer.ToArray()); 371 372 // This function is called _after_ lookupCapAndSlot, therefore we now 373 // have both TCB address and thread's name. We can add it to our list 374 // of known threads. 375 if(!mapping.ContainsKey(currentTCB) || threadName.Contains("_control")) 376 { 377 mapping[currentTCB] = threadName; 378 } 379 380 // There was pendingThreadName set by WaitForThread function. As we have now all 381 // necessary information for requested thread, we can enter SingleStepMode 382 // (and thus return to prompt in GDB) so user can do something with it, 383 // e.g. create breakpoint on this thread. 384 if(pendingThreadName != null && threadName.Contains(pendingThreadName)) 385 { 386 pendingThreadName = null; 387 cpu.Pause(); 388 cpu.EnterSingleStepModeSafely(new HaltArguments(HaltReason.Breakpoint, cpu, address, BreakpointType.MemoryBreakpoint)); 389 } 390 } 391 GetBreakpointsCount(ulong address)392 private int GetBreakpointsCount(ulong address) 393 { 394 breakpoints.TryGetValue(address, out var bp); 395 temporaryBreakpoints.TryGetValue(address, out var tbp); 396 return (bp?.Count ?? 0) + (tbp?.Count ?? 0); 397 } 398 TryGetRealThreadName(string threadName, out string realThreadName)399 private bool TryGetRealThreadName(string threadName, out string realThreadName) 400 { 401 if(threadName == AnyThreadName) 402 { 403 realThreadName = AnyThreadName; 404 return true; 405 } 406 407 realThreadName = mapping.Values.Where(thread => thread.Contains(threadName)).FirstOrDefault(); 408 if(String.IsNullOrEmpty(realThreadName)) 409 { 410 this.Log(LogLevel.Warning, "No thread with name '{0}' found.", threadName); 411 return false; 412 } 413 414 return true; 415 } 416 DoBreakpointExists(ulong address, string threadName)417 private bool DoBreakpointExists(ulong address, string threadName) 418 { 419 return (breakpoints.TryGetValue(address, out var bpList) && (bpList.Contains(AnyThreadName) || bpList.Contains(threadName))) || 420 (temporaryBreakpoints.TryGetValue(address, out var tbpList) && (tbpList.Contains(AnyThreadName) || tbpList.Contains(threadName))); 421 } 422 AddContextSwitchHook()423 private void AddContextSwitchHook() 424 { 425 cpu.AddHook(restoreUserContextAddress, HandleRestoreUserContext); 426 } 427 RemoveContextSwitchHook()428 private void RemoveContextSwitchHook() 429 { 430 cpu.RemoveHook(restoreUserContextAddress, HandleRestoreUserContext); 431 } 432 ClearTemporaryBreakpoint(ulong address, string threadName)433 private void ClearTemporaryBreakpoint(ulong address, string threadName) 434 { 435 if(!temporaryBreakpoints.ContainsKey(address)) 436 { 437 return; 438 } 439 440 temporaryBreakpoints[address].Remove(threadName); 441 temporaryBreakpoints[address].Remove(AnyThreadName); 442 443 if(GetBreakpointsCount(address) == 0) 444 { 445 RemoveHook(address); 446 } 447 } 448 SetBreakpointHelper(string threadName, ulong address, Dictionary<ulong, HashSet<string>> breakpointsSource)449 private void SetBreakpointHelper(string threadName, ulong address, Dictionary<ulong, HashSet<string>> breakpointsSource) 450 { 451 if(!TryGetRealThreadName(threadName, out var realThreadName)) 452 { 453 return; 454 } 455 456 if(!breakpointsSource.ContainsKey(address)) 457 { 458 breakpointsSource.Add(address, new HashSet<string>()); 459 } 460 461 if(!breakpointsSource[address].Add(realThreadName)) 462 { 463 this.Log(LogLevel.Warning, "This breakpoint already exists."); 464 return; 465 } 466 467 var breakpointsNum = GetBreakpointsCount(address); 468 469 // Ignore if we already registered breakpoint for this address 470 if(breakpointsNum != 1) 471 { 472 return; 473 } 474 475 AddHook(address); 476 } 477 RemoveBreakpointHelper(string threadName, ulong address, Dictionary<ulong, HashSet<string>> breakpointsSource)478 private void RemoveBreakpointHelper(string threadName, ulong address, Dictionary<ulong, HashSet<string>> breakpointsSource) 479 { 480 if(!breakpointsSource.TryGetValue(address, out var breakpoint)) 481 { 482 return; 483 } 484 485 if(!TryGetRealThreadName(threadName, out var realThreadName)) 486 { 487 return; 488 } 489 490 breakpoint.Remove(realThreadName); 491 var breakpointsNum = GetBreakpointsCount(address); 492 if(breakpointsNum != 0) 493 { 494 return; 495 } 496 497 RemoveHook(address); 498 } 499 AddHook(ulong address)500 private void AddHook(ulong address) 501 { 502 if(address != WildcardAddress) 503 { 504 cpu.AddHook(address, HandleBreakpoint); 505 } 506 else 507 { 508 AddContextSwitchHook(); 509 } 510 } 511 RemoveHook(ulong address)512 private void RemoveHook(ulong address) 513 { 514 if(address != WildcardAddress) 515 { 516 cpu.RemoveHook(address, HandleBreakpoint); 517 } 518 else 519 { 520 RemoveContextSwitchHook(); 521 } 522 } 523 IsValidAddress(ulong address)524 private bool IsValidAddress(ulong address) 525 { 526 return !(address == 0x00000000 || address == 0xFFFFFFFF); 527 } 528 CurrentThreadUnsafe()529 private string CurrentThreadUnsafe() 530 { 531 var tcb = cpu.Bus.ReadDoubleWord(this.ksCurThreadPhysAddress, context: cpu); 532 if(mapping.ContainsKey(tcb)) 533 { 534 return mapping[tcb]; 535 } 536 return "unknown"; 537 } 538 539 public enum ExitUserspaceMode 540 { 541 Never, 542 Once, 543 Always, 544 } 545 546 private const uint DefaultDebugThreadNameSyscall = 0xfffffff2; 547 private const string AnyThreadName = "<any>"; 548 private const uint WildcardAddress = 0x00000000; 549 private const uint MaximumMessageLength = 120; 550 551 private readonly Dictionary<ulong, HashSet<string>> breakpoints; 552 private readonly Dictionary<ulong, HashSet<string>> temporaryBreakpoints; 553 private readonly Dictionary<ulong, string> mapping; 554 private readonly ICpuSupportingGdb cpu; 555 private readonly ICallingConvention callingConvention; 556 private readonly ulong debugThreadNameSyscall; 557 558 private ExitUserspaceMode exitUserspaceMode; 559 private bool breakpointsEnabled; 560 private ulong ksCurThreadPhysAddress; 561 private ulong restoreUserContextAddress; 562 private string pendingThreadName; 563 private ulong currentTCB; 564 565 private interface ICallingConvention 566 { 567 ulong FirstArgument { get; } 568 ulong ReturnValue { get; } 569 ulong ReturnAddress { get; } 570 ulong SyscallTrapAddress { get; } 571 ulong TCBNextPCOffset { get; } 572 PrivilegeMode PrivilegeMode { get; } 573 } 574 575 private enum PrivilegeMode 576 { 577 Userspace, 578 Supervisor, 579 Other, 580 } 581 582 private class RiscVCallingConvention : ICallingConvention 583 { RiscVCallingConvention(ICpuSupportingGdb cpu)584 public RiscVCallingConvention(ICpuSupportingGdb cpu) 585 { 586 this.cpu = cpu; 587 // Assumes that symbols for kernel are loaded 588 syscallTrapAddress = cpu.Bus.GetSymbolAddress("trap_entry"); 589 } 590 591 public ulong FirstArgument => cpu.A[0]; 592 public ulong ReturnValue => cpu.A[0]; 593 public ulong ReturnAddress => cpu.RA; 594 public ulong SyscallTrapAddress => syscallTrapAddress; 595 public PrivilegeMode PrivilegeMode 596 { 597 get 598 { 599 switch((byte)cpu.PRIV) 600 { 601 case 0b00: 602 return PrivilegeMode.Userspace; 603 case 0b01: 604 return PrivilegeMode.Supervisor; 605 default: 606 return PrivilegeMode.Other; 607 } 608 } 609 } 610 public ulong TCBNextPCOffset => 34 * 4; 611 612 private readonly ulong syscallTrapAddress; 613 private readonly dynamic cpu; 614 } 615 616 private class ArmCallingConvention : ICallingConvention 617 { ArmCallingConvention(ICpuSupportingGdb cpu)618 public ArmCallingConvention(ICpuSupportingGdb cpu) 619 { 620 this.cpu = (Arm)cpu; 621 // Assumes that symbols for kernel are loaded 622 syscallTrapAddress = cpu.Bus.GetSymbolAddress("arm_swi_syscall"); 623 } 624 625 public ulong FirstArgument => cpu.R[0]; 626 public ulong ReturnValue => cpu.R[0]; 627 public ulong ReturnAddress => cpu.R[14]; 628 public ulong SyscallTrapAddress => syscallTrapAddress; 629 public PrivilegeMode PrivilegeMode 630 { 631 get 632 { 633 switch(cpu.CPSR & 0xfUL) 634 { 635 case 0b00: 636 return PrivilegeMode.Userspace; 637 case 0b11: 638 return PrivilegeMode.Supervisor; 639 default: 640 return PrivilegeMode.Other; 641 } 642 } 643 } 644 public ulong TCBNextPCOffset => 15 * 4; 645 646 private readonly ulong syscallTrapAddress; 647 private readonly Arm cpu; 648 } 649 } 650 } 651 652