Skip to content

Commit

Permalink
GDB Server (#1223)
Browse files Browse the repository at this point in the history
This PR adds a generic GDB server to ares with the ability to be used by
all systems.
For now only the N64 systems was implemented here.

Most documentation on how the server/TCP socket works can be found in
comments.
A detailed documentation for ares developers on how to add this to other
systems is included in the `Readme.md` file.

The feature itself was also already tested by a few people on both linux
and windows (with WSL) and seems to be stable now.
  • Loading branch information
HailToDodongo authored Sep 17, 2023
1 parent 85488a7 commit 1215b37
Show file tree
Hide file tree
Showing 31 changed files with 1,721 additions and 16 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
*.moc
*.user
.vs/
.vscode/
.idea/
cmake-*/
obj/
out/
obj-amd64/
obj-arm64/
out-amd64/
out-arm64/
out-arm64/
1 change: 1 addition & 0 deletions ares/ares/ares.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <ares/ares.hpp>
#include <ares/debug/debug.cpp>
#include <nall/gdb/server.cpp>
#include <ares/node/node.cpp>
#include <ares/resource/resource.cpp>

Expand Down
5 changes: 3 additions & 2 deletions ares/n64/cpu/cpu.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <n64/n64.hpp>
#include <nall/gdb/server.hpp>

namespace ares::Nintendo64 {

Expand Down Expand Up @@ -30,7 +31,7 @@ auto CPU::unload() -> void {
}

auto CPU::main() -> void {
while(!vi.refreshed) {
while(!vi.refreshed && GDB::server.reportPC(ipu.pc & 0xFFFFFFFF)) {
instruction();
synchronize();
}
Expand Down Expand Up @@ -104,7 +105,7 @@ auto CPU::instruction() -> void {
}

if (auto address = devirtualize(ipu.pc)) {
auto block = recompiler.block(ipu.pc, *address);
auto block = recompiler.block(ipu.pc, *address, GDB::server.hasBreakpoints());
block->execute(*this);
}
}
Expand Down
14 changes: 10 additions & 4 deletions ares/n64/cpu/cpu.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@ struct CPU : Thread {
template<u32 Size> auto write(u32 vaddr, u32 address, u64 data) -> void;
auto power(bool reset) -> void;

auto readDebug(u32 vaddr, u32 address) -> u8;

//8KB
struct Line {
auto hit(u32 address) const -> bool;
Expand Down Expand Up @@ -242,8 +244,9 @@ struct CPU : Thread {
} entry[TLB::Entries];

//tlb.cpp
auto load(u64 vaddr) -> Match;
auto load(u64 vaddr, const Entry& entry) -> maybe<Match>;
auto load(u64 vaddr, bool noExceptions = false) -> Match;
auto load(u64 vaddr, const Entry& entry, bool noExceptions = false) -> maybe<Match>;

auto loadFast(u64 vaddr) -> Match;
auto store(u64 vaddr) -> Match;
auto store(u64 vaddr, const Entry& entry) -> maybe<Match>;
Expand Down Expand Up @@ -292,6 +295,7 @@ struct CPU : Thread {
auto segment(u64 vaddr) -> Context::Segment;
auto devirtualize(u64 vaddr) -> maybe<u64>;
alwaysinline auto devirtualizeFast(u64 vaddr) -> u64;
auto devirtualizeDebug(u64 vaddr) -> u64;

auto fetch(u64 vaddr) -> maybe<u32>;
template<u32 Size> auto busWrite(u32 address, u64 data) -> void;
Expand All @@ -301,6 +305,8 @@ struct CPU : Thread {
template<u32 Size> auto vaddrAlignedError(u64 vaddr, bool write) -> bool;
auto addressException(u64 vaddr) -> void;

auto readDebug(u64 vaddr) -> u8;

//serialization.cpp
auto serialize(serializer&) -> void;

Expand Down Expand Up @@ -894,10 +900,10 @@ struct CPU : Thread {
}

auto pool(u32 address) -> Pool*;
auto block(u32 vaddr, u32 address) -> Block*;
auto block(u32 vaddr, u32 address, bool singleInstruction = false) -> Block*;
auto fastFetchBlock(u32 address) -> Block*;

auto emit(u32 vaddr, u32 address) -> Block*;
auto emit(u32 vaddr, u32 address, bool singleInstruction = false) -> Block*;
auto emitEXECUTE(u32 instruction) -> bool;
auto emitSPECIAL(u32 instruction) -> bool;
auto emitREGIMM(u32 instruction) -> bool;
Expand Down
9 changes: 9 additions & 0 deletions ares/n64/cpu/dcache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,15 @@ auto CPU::DataCache::read(u32 vaddr, u32 address) -> u64 {
return line.read<Size>(address);
}

auto CPU::DataCache::readDebug(u32 vaddr, u32 address) -> u8 {
auto& line = this->line(vaddr);
if(!line.hit(address)) {
Thread dummyThread{};
return bus.read<Byte>(address, dummyThread);
}
return line.read<Byte>(address);
}

template<u32 Size>
auto CPU::DataCache::write(u32 vaddr, u32 address, u64 data) -> void {
auto& line = this->line(vaddr);
Expand Down
5 changes: 5 additions & 0 deletions ares/n64/cpu/exceptions.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
auto CPU::Exception::trigger(u32 code, u32 coprocessor, bool tlbMiss) -> void {
self.debugger.exception(code);

if(code != 0) {
auto sig = (code == 2 || code == 3) ? GDB::Signal::SEGV : GDB::Signal::TRAP;
GDB::server.reportSignal(sig, self.ipu.pc);
}

u64 vectorBase = !self.scc.status.vectorLocation ? (s32)0x8000'0000 : (s32)0xbfc0'0200;

u16 vectorOffset = 0x0180;
Expand Down
33 changes: 33 additions & 0 deletions ares/n64/cpu/memory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ auto CPU::devirtualizeFast(u64 vaddr) -> u64 {
return devirtualizeCache.pbase = 0;
}

auto CPU::devirtualizeDebug(u64 vaddr) -> u64 {
return devirtualizeFast(vaddr); // this wrapper preserves the inlining of 'devirtualizeFast'
}

template<u32 Size>
inline auto CPU::busWrite(u32 address, u64 data) -> void {
bus.write<Size>(address, data, *this);
Expand Down Expand Up @@ -185,6 +189,8 @@ auto CPU::fetch(u64 vaddr) -> maybe<u32> {
template<u32 Size>
auto CPU::read(u64 vaddr) -> maybe<u64> {
if(vaddrAlignedError<Size>(vaddr, false)) return nothing;
GDB::server.reportMemRead(vaddr, Size);

switch(segment(vaddr)) {
case Context::Segment::Unused:
step(1 * 2);
Expand Down Expand Up @@ -215,10 +221,37 @@ auto CPU::read(u64 vaddr) -> maybe<u64> {
unreachable;
}

auto CPU::readDebug(u64 vaddr) -> u8 {
Thread dummyThread{};

switch(segment(vaddr)) {
case Context::Segment::Unused: return 0;
case Context::Segment::Mapped:
if(auto match = tlb.load(vaddr, true)) {
if(match.cache) return dcache.readDebug(vaddr, match.address & context.physMask);
return bus.read<Byte>(match.address & context.physMask, dummyThread);
}
return 0;
case Context::Segment::Cached:
return dcache.readDebug(vaddr, vaddr & 0x1fff'ffff);
case Context::Segment::Cached32:
return dcache.readDebug(vaddr, vaddr & 0xffff'ffff);
case Context::Segment::Direct:
return bus.read<Byte>(vaddr & 0x1fff'ffff, dummyThread);
case Context::Segment::Direct32:
return bus.read<Byte>(vaddr & 0xffff'ffff, dummyThread);
}

unreachable;
}

template<u32 Size>
auto CPU::write(u64 vaddr0, u64 data, bool alignedError) -> bool {
if(alignedError && vaddrAlignedError<Size>(vaddr0, true)) return false;
u64 vaddr = vaddr0 & ~((u64)Size - 1);

GDB::server.reportMemWrite(vaddr0, Size);

switch(segment(vaddr)) {
case Context::Segment::Unused:
step(1 * 2);
Expand Down
8 changes: 4 additions & 4 deletions ares/n64/cpu/recompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ auto CPU::Recompiler::pool(u32 address) -> Pool* {
return pool;
}

auto CPU::Recompiler::block(u32 vaddr, u32 address) -> Block* {
auto CPU::Recompiler::block(u32 vaddr, u32 address, bool singleInstruction) -> Block* {
if(auto block = pool(address)->blocks[address >> 2 & 0x3f]) return block;
auto block = emit(vaddr, address);
auto block = emit(vaddr, address, singleInstruction);
pool(address)->blocks[address >> 2 & 0x3f] = block;
memory::jitprotect(true);
return block;
Expand All @@ -18,7 +18,7 @@ auto CPU::Recompiler::fastFetchBlock(u32 address) -> Block* {
return nullptr;
}

auto CPU::Recompiler::emit(u32 vaddr, u32 address) -> Block* {
auto CPU::Recompiler::emit(u32 vaddr, u32 address, bool singleInstruction) -> Block* {
if(unlikely(allocator.available() < 1_MiB)) {
print("CPU allocator flush\n");
memory::jitprotect(false);
Expand All @@ -44,7 +44,7 @@ auto CPU::Recompiler::emit(u32 vaddr, u32 address) -> Block* {
call(&CPU::instructionEpilogue);
vaddr += 4;
address += 4;
if(hasBranched || (address & 0xfc) == 0) break; //block boundary
if(hasBranched || (address & 0xfc) == 0 || singleInstruction) break; //block boundary
hasBranched = branched;
testJumpEpilog();
}
Expand Down
12 changes: 8 additions & 4 deletions ares/n64/cpu/tlb.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@

auto CPU::TLB::load(u64 vaddr, const Entry& entry) -> maybe<Match> {
auto CPU::TLB::load(u64 vaddr, const Entry& entry, bool noExceptions) -> maybe<Match> {
if(!entry.globals && entry.addressSpaceID != self.scc.tlb.addressSpaceID) return nothing;
if((vaddr & entry.addressMaskHi) != entry.virtualAddress) return nothing;
if(vaddr >> 62 != entry.region) return nothing;
bool lo = vaddr & entry.addressSelect;
if(!entry.valid[lo]) {
if(noExceptions)return Match{false};

self.addressException(vaddr);
self.debugger.tlbLoadInvalid(vaddr);
self.exception.tlbLoadInvalid();
Expand All @@ -15,22 +17,24 @@ auto CPU::TLB::load(u64 vaddr, const Entry& entry) -> maybe<Match> {
return Match{true, entry.cacheAlgorithm[lo] != 2, physicalAddress};
}

auto CPU::TLB::load(u64 vaddr) -> Match {
auto CPU::TLB::load(u64 vaddr, bool noExceptions) -> Match {
for(auto& entry : this->tlbCache.entry) {
if(!entry.entry) continue;
if(auto match = load(vaddr, *entry.entry)) {
if(auto match = load(vaddr, *entry.entry, noExceptions)) {
entry.frequency++;
return *match;
}
}

for(auto& entry : this->entry) {
if(auto match = load(vaddr, entry)) {
if(auto match = load(vaddr, entry, noExceptions)) {
this->tlbCache.insert(entry);
return *match;
}
}

if(noExceptions)return {false};

self.addressException(vaddr);
self.debugger.tlbLoadMiss(vaddr);
self.exception.tlbLoadMiss();
Expand Down
Loading

0 comments on commit 1215b37

Please sign in to comment.