lazarus/components/fpdebug/fpdbgriscvclasses.pas
2024-07-16 14:18:42 +02:00

298 lines
8.6 KiB
ObjectPascal

unit FpDbgRiscvClasses;
{$mode objfpc}{$H+}
{$packrecords c}
{$modeswitch advancedrecords}
interface
uses
Classes,
SysUtils,
FpDbgClasses,
DbgIntfBaseTypes, DbgIntfDebuggerBase,
{$ifdef FORCE_LAZLOGGER_DUMMY} LazLoggerDummy {$else} LazLoggerBase {$endif},
FpDbgRsp, FpDbgRspClasses, FpDbgCommon, FpdMemoryTools,
FpErrorMessages;
const
SPindex = 2;
SPindexDwarf = 2; // Also known as a1, Index refers to active window
ReturnAddressIndex = 1;
ReturnAddressIndexDwarf = 1;
PCIndexDwarf = 32; // Dwarf index
nPC = 'pc';
nReturnPC = 'ra';
nSP = 'sp';
type
{ TDbgRiscvThread }
TDbgRiscvThread = class(TDbgRspThread)
private
const
lastCPURegIndex = 32; // 32 registers + PC
// Offsets to load specific registers from register data
// These are byte offsets, to be used when reading from the raw byte register data
RegArrayByteLength = 33*4; // Depends on qemu options, but this seems to be the smallest size to handle. Only show basic registers, so rest can be ignored for now.
PCindex = 32;
protected
procedure LoadRegisterCache; override;
procedure SaveRegisterCache; override;
function GetReturnPC: TDbgPtr;
function GetStackUnwinder: TDbgStackUnwinder; override;
public
destructor Destroy; override;
procedure LoadRegisterValues; override;
procedure SetRegisterValue(AName: string; AValue: QWord); override;
function GetInstructionPointerRegisterValue: TDbgPtr; override;
function GetStackBasePointerRegisterValue: TDbgPtr; override;
// procedure SetInstructionPointerRegisterValue(AValue: TDbgPtr); override;
function GetStackPointerRegisterValue: TDbgPtr; override;
// procedure SetStackPointerRegisterValue(AValue: TDbgPtr); override;
end;
{ TDbgRiscvProcess }
TDbgRiscvProcess = class(TDbgRspProcess)
private const
FNumRegisters = 33; // x0..x31,pc
protected
function CreateThread(AthreadIdentifier: THandle; out IsMainThread: boolean): TDbgThread; override;
function CreateBreakPointTargetHandler: TFpBreakPointTargetHandler; override;
public
class function isSupported(target: TTargetDescriptor): boolean; override;
constructor Create(const AFileName: string; AnOsClasses: TOSDbgClasses;
AMemManager: TFpDbgMemManager; AMemModel: TFpDbgMemModel;
AProcessConfig: TDbgProcessConfig = nil); override;
destructor Destroy; override;
end;
TRiscvBreakInfo = object
const
_CODE: Word = $9002; // c.ebreak -> this is a 16 bit instruction
end;
TRiscvBreakPointTargetHandler = specialize TRspBreakPointTargetHandler<Word, TRiscvBreakInfo>;
implementation
uses
FpDbgDisasRiscv, FpDbgDwarfDataClasses;
var
DBG_VERBOSE, DBG_WARNINGS: PLazLoggerLogGroup;
{ TDbgRiscvThread }
procedure TDbgRiscvThread.LoadRegisterCache;
var
regs: TBytes;
i: integer;
begin
if not FRegs.Initialized then
begin
SetLength(regs, RegArrayByteLength);
FRegs.Initialized := TDbgRiscvProcess(Process).RspConnection.ReadRegisters(regs[0], length(regs));
// 32 bit LE registers
for i := 0 to lastCPURegIndex do // x0..x31, PC
FRegs.regs[i] := regs[4*i] + (regs[4*i + 1] shl 8) + (regs[4*i + 2] shl 16) + (regs[4*i + 3] shl 24);
end;
end;
procedure TDbgRiscvThread.SaveRegisterCache;
procedure CopyDWordToByteArray( const val: DWord; regs: PByte);
begin
regs[0] := val;
regs[1] := val shr 8;
regs[2] := val shr 16;
regs[3] := val shr 24;
end;
var
regs: TBytes;
i: Integer;
begin
exit; // TODO: Need to determine which other registers will also change in case of a subroutine call on the debugger side.
if FRegsChanged then
begin
SetLength(regs, RegArrayByteLength);
for i := 0 to lastCPURegIndex do
CopyDWordToByteArray(FRegs.regs[i], @regs[4*i]);
end;
end;
function TDbgRiscvThread.GetReturnPC: TDbgPtr;
begin
Result := 0;
if TDbgRiscvProcess(Process).FIsTerminating then
begin
DebugLn(DBG_WARNINGS, 'TDbgRiscvProcess.GetStackPointerRegisterValue called while FIsTerminating is set.');
exit;
end;
if not ReadThreadState then
exit;
DebugLn(DBG_VERBOSE, 'TDbgRiscvProcess.GetStackPointerRegisterValue requesting stack registers.');
ReadDebugReg(PCindex, result);
end;
function TDbgRiscvThread.GetStackUnwinder: TDbgStackUnwinder;
begin
if FUnwinder = nil then
FUnwinder := TDbgStackUnwinderRiscv.Create(Process);
Result := FUnwinder;
end;
destructor TDbgRiscvThread.Destroy;
begin
if Assigned(FUnwinder) then
FreeAndNil(FUnwinder);
inherited Destroy;
end;
procedure TDbgRiscvThread.LoadRegisterValues;
var
i: integer;
begin
if TDbgRiscvProcess(Process).FIsTerminating or (TDbgRiscvProcess(Process).FStatus = SIGHUP) then
begin
DebugLn(DBG_WARNINGS, 'TDbgRiscvProcess.LoadRegisterValues called while FIsTerminating is set.');
exit;
end;
if not ReadThreadState then
exit;
LoadRegisterCache;
if FRegs.Initialized then
begin
{ FRegs.regs starts with 32 core registers, then PC
Todo: Where does floating point and vector registers fit in? }
for i := 0 to 32 do
FRegisterValueList.DbgRegisterAutoCreate[RiscvABIRegisterNames[i]].SetValue(FRegs.regs[i], IntToStr(FRegs.regs[i]), 4, i);
FRegisterValueListValid := true;
end
else
DebugLn(DBG_WARNINGS, 'Warning: Could not update registers');
end;
procedure TDbgRiscvThread.SetRegisterValue(AName: string; AValue: QWord);
var
i: integer;
res: boolean;
begin
res := false;
for i := low(RiscvABIRegisterNames) to high(RiscvABIRegisterNames) do
if AName = RiscvABIRegisterNames[i] then
begin
res := TDbgRspProcess(Process).RspConnection.WriteDebugReg(i, byte(AValue));
break;
end;
if not res then
DebugLn(DBG_WARNINGS, 'Error setting register %s to %u', [AName, AValue]);
end;
function TDbgRiscvThread.GetInstructionPointerRegisterValue: TDbgPtr;
begin
Result := 0;
if TDbgRiscvProcess(Process).FIsTerminating then
begin
DebugLn(DBG_WARNINGS, 'TDbgRiscvProcess.GetInstructionPointerRegisterValue called while FIsTerminating is set.');
exit;
end;
if not ReadThreadState then
exit;
DebugLn(DBG_VERBOSE, 'TDbgRiscvProcess.GetInstructionPointerRegisterValue requesting PC.');
ReadDebugReg(PCindex, Result);
end;
function TDbgRiscvThread.GetStackBasePointerRegisterValue: TDbgPtr;
begin
Result := 0;
if TDbgRiscvProcess(Process).FIsTerminating then
begin
DebugLn(DBG_WARNINGS, 'TDbgRiscvProcess.GetStackBasePointerRegisterValue called while FIsTerminating is set.');
exit;
end;
if not ReadThreadState then
exit;
DebugLn(DBG_VERBOSE, 'TDbgRiscvProcess.GetStackBasePointerRegisterValue requesting base registers.');
// Todo: check FPC implementation of stack frame for riscv
Result := GetStackPointerRegisterValue;
end;
function TDbgRiscvThread.GetStackPointerRegisterValue: TDbgPtr;
begin
Result := 0;
if TDbgRiscvProcess(Process).FIsTerminating then
begin
DebugLn(DBG_WARNINGS, 'TDbgRiscvProcess.GetStackPointerRegisterValue called while FIsTerminating is set.');
exit;
end;
if not ReadThreadState then
exit;
DebugLn(DBG_VERBOSE, 'TDbgRiscvProcess.GetStackPointerRegisterValue requesting stack registers.');
if not ReadDebugReg(SPindex, result) then
Result := 0;
end;
{ TDbgRiscvProcess }
function TDbgRiscvProcess.CreateThread(AthreadIdentifier: THandle; out IsMainThread: boolean): TDbgThread;
begin
IsMainThread:=False;
if AthreadIdentifier<>feInvalidHandle then
begin
IsMainThread := AthreadIdentifier=ProcessID;
result := TDbgRiscvThread.Create(Self, AthreadIdentifier, AthreadIdentifier);
end
else
result := nil;
end;
function
TDbgRiscvProcess.CreateBreakPointTargetHandler: TFpBreakPointTargetHandler;
begin
Result := TRiscvBreakPointTargetHandler.Create(Self);
end;
constructor TDbgRiscvProcess.Create(const AFileName: string;
AnOsClasses: TOSDbgClasses; AMemManager: TFpDbgMemManager;
AMemModel: TFpDbgMemModel; AProcessConfig: TDbgProcessConfig);
begin
FRegArrayLength := FNumRegisters;
inherited Create(AFileName, AnOsClasses, AMemManager, AMemModel, AProcessConfig);
end;
destructor TDbgRiscvProcess.Destroy;
begin
inherited Destroy;
end;
class function TDbgRiscvProcess.isSupported(target: TTargetDescriptor): boolean;
begin
result := (target.OS = osEmbedded) and (target.machineType = mtRISCV);
end;
initialization
DBG_VERBOSE := DebugLogger.FindOrRegisterLogGroup('DBG_VERBOSE' {$IFDEF DBG_VERBOSE} , True {$ENDIF} );
DBG_WARNINGS := DebugLogger.FindOrRegisterLogGroup('DBG_WARNINGS' {$IFDEF DBG_WARNINGS} , True {$ENDIF} );
RegisterDbgOsClasses(TOSDbgClasses.Create(
TDbgRiscvProcess,
TDbgRiscvThread,
TRiscvAsmDecoder));
end.