mirror of
https://github.com/reswitched/Mephisto.git
synced 2024-05-11 17:24:52 -04:00
Initial commit. Mephisto lives.
This commit is contained in:
commit
42296ddcbd
333
Cpu.cpp
Normal file
333
Cpu.cpp
Normal file
|
@ -0,0 +1,333 @@
|
|||
#include "Ctu.h"
|
||||
|
||||
void intrHook(uc_engine *uc, uint32_t intNo, void *user_data) {
|
||||
((Cpu *) user_data)->interruptHook(intNo);
|
||||
}
|
||||
|
||||
bool unmpdHook(uc_engine *uc, uc_mem_type type, gptr addr, int size, guint value, void *user_data) {
|
||||
return ((Cpu *) user_data)->unmappedHook(type, addr, size, value);
|
||||
}
|
||||
|
||||
void mmioHook(uc_engine *uc, uc_mem_type type, gptr address, int size, gptr value, void *user_data) {
|
||||
gptr physicalAddress = ((Cpu *) user_data)->mmioHandler->getPhysicalAddressFromVirtual(address);
|
||||
MmioBase *mmio = ((Cpu *) user_data)->mmioHandler->getMMIOFromPhysicalAddress(address);
|
||||
assert(mmio != nullptr);
|
||||
switch(type) {
|
||||
case UC_MEM_READ:
|
||||
LOG_DEBUG(Cpu, "MMIO Read at " ADDRFMT " size %x", physicalAddress, size);
|
||||
((Cpu *) user_data)->readmem(address, &value, size);
|
||||
LOG_DEBUG(Cpu, "Stored value %x", (int) ((Cpu *) user_data)->read8(address));
|
||||
break;
|
||||
case UC_MEM_WRITE:
|
||||
LOG_DEBUG(Cpu, "MMIO Write at " ADDRFMT " size %x data %lx", physicalAddress, size, value);
|
||||
/*if() {
|
||||
((Cpu *) user_data)->writemem(address, &value, size);
|
||||
}*/
|
||||
//mmio->swrite(physicalAddress, size, &value);
|
||||
((Cpu *) user_data)->writemem(address, &value, size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void codeBpHook(uc_engine *uc, uint64_t address, uint32_t size, void *user_data) {
|
||||
auto ctu = (Ctu *) user_data;
|
||||
cout << "Hit breakpoint at ... " << hex << address << endl;
|
||||
auto thread = ctu->tm.current();
|
||||
assert(thread != nullptr);
|
||||
ctu->tm.requeue();
|
||||
thread->regs.PC = address;
|
||||
ctu->cpu.stop();
|
||||
ctu->gdbStub._break();
|
||||
}
|
||||
|
||||
Cpu::Cpu(Ctu *_ctu) : ctu(_ctu) {
|
||||
CHECKED(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc));
|
||||
|
||||
CHECKED(uc_mem_map(uc, TERMADDR, 0x1000, UC_PROT_ALL));
|
||||
guestptr<uint32_t>(TERMADDR) = 0xd503201f; // nop
|
||||
|
||||
auto fpv = 3 << 20;
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_CPACR_EL1, &fpv));
|
||||
|
||||
uc_hook hookHandle;
|
||||
CHECKED(uc_hook_add(uc, &hookHandle, UC_HOOK_INTR, (void *) intrHook, this, 0, -1));
|
||||
CHECKED(uc_hook_add(uc, &hookHandle, UC_HOOK_MEM_INVALID, (void *) unmpdHook, this, 0, -1));
|
||||
|
||||
for(auto i = 0; i < 0x80; ++i)
|
||||
svcHandlers[i] = nullptr;
|
||||
}
|
||||
|
||||
Cpu::~Cpu() {
|
||||
CHECKED(uc_close(uc));
|
||||
}
|
||||
|
||||
guint Cpu::call(gptr _pc, guint x0, guint x1, guint x2, guint x3) {
|
||||
reg(0, x0);
|
||||
reg(1, x1);
|
||||
reg(2, x2);
|
||||
reg(3, x3);
|
||||
reg(30, TERMADDR);
|
||||
CHECKED(uc_emu_start(uc, _pc, TERMADDR + 4, 0, 0));
|
||||
return reg(0);
|
||||
}
|
||||
|
||||
void Cpu::setMmio(Mmio *_mmioHandler) {
|
||||
mmioHandler = _mmioHandler;
|
||||
uc_hook hookHandle;
|
||||
CHECKED(uc_hook_add(uc, &hookHandle, UC_HOOK_MEM_READ, (void *)mmioHook, this, mmioHandler->GetBase(), mmioHandler->GetBase()+mmioHandler->GetSize() ));
|
||||
CHECKED(uc_hook_add(uc, &hookHandle, UC_HOOK_MEM_WRITE, (void *)mmioHook, this, mmioHandler->GetBase(), mmioHandler->GetBase()+mmioHandler->GetSize() ));
|
||||
}
|
||||
|
||||
void Cpu::exec(size_t insnCount) {
|
||||
CHECKED(uc_emu_start(uc, pc(), TERMADDR + 4, 0, insnCount));
|
||||
}
|
||||
|
||||
void Cpu::stop() {
|
||||
CHECKED(uc_emu_stop(uc));
|
||||
}
|
||||
|
||||
bool Cpu::map(gptr addr, guint size) {
|
||||
CHECKED(uc_mem_map(uc, addr, size, UC_PROT_ALL));
|
||||
auto temp = new uint8_t[size];
|
||||
memset(temp, 0, size);
|
||||
writemem(addr, temp, size);
|
||||
delete[] temp;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Cpu::unmap(gptr addr, guint size) {
|
||||
CHECKED(uc_mem_unmap(uc, addr, size));
|
||||
return true;
|
||||
}
|
||||
|
||||
list<tuple<gptr, gptr, int>> Cpu::regions() {
|
||||
list<tuple<gptr, gptr, int>> ret;
|
||||
|
||||
uc_mem_region *regions;
|
||||
uint32_t count;
|
||||
|
||||
CHECKED(uc_mem_regions(uc, ®ions, &count));
|
||||
list<tuple<gptr, gptr>> temp;
|
||||
for(auto i = 0; i < count; ++i) {
|
||||
auto region = regions[i];
|
||||
temp.push_back(make_tuple(region.begin, region.end));
|
||||
}
|
||||
uc_free(regions);
|
||||
|
||||
temp.sort([](auto a, auto b) { auto [ab, _] = a; auto [bb, __] = b; return ab < bb; });
|
||||
|
||||
gptr last = 0;
|
||||
for(auto [begin, end] : temp) {
|
||||
if(last != begin)
|
||||
ret.push_back(make_tuple(last, begin - 1, -1));
|
||||
ret.push_back(make_tuple(begin, end, 0));
|
||||
last = end + 1;
|
||||
}
|
||||
|
||||
if(last != 0xFFFFFFFFFFFFFFFF)
|
||||
ret.push_back(make_tuple(last, 0xFFFFFFFFFFFFFFFF, -1));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Cpu::readmem(gptr addr, void *dest, guint size) {
|
||||
return uc_mem_read(uc, addr, dest, size) == UC_ERR_OK;
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wformat-nonliteral"
|
||||
void Cpu::dumpmem(gptr addr, guint size) {
|
||||
char *data = (char*)malloc(size);
|
||||
memset(data, 0, size);
|
||||
readmem(addr, data, size);
|
||||
|
||||
auto hfmt = "%08lx | ";
|
||||
if((addr + size) & 0xFFFF000000000000)
|
||||
hfmt = "%016lx | ";
|
||||
else if((addr + size) & 0xFFFFFFFF00000000)
|
||||
hfmt = "%012lx | ";
|
||||
|
||||
for(uint32_t i = 0; i < size; i += 16) {
|
||||
printf(hfmt, addr+i);
|
||||
string ascii = "";
|
||||
for(uint8_t j = 0; j < 16; j++) {
|
||||
if((i+j) < size) {
|
||||
printf("%02x ", (uint8_t)data[i+j]);
|
||||
if(isprint(data[i+j]))
|
||||
ascii += data[i+j];
|
||||
else
|
||||
ascii += ".";
|
||||
} else {
|
||||
printf(" ");
|
||||
ascii += " ";
|
||||
}
|
||||
if(j==7) {
|
||||
printf(" ");
|
||||
ascii += " ";
|
||||
}
|
||||
}
|
||||
printf("| %s\n", ascii.c_str());
|
||||
}
|
||||
|
||||
free(data);
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
guchar Cpu::read8(gptr addr) {
|
||||
return *guestptr<guchar>(addr);
|
||||
}
|
||||
|
||||
std::string Cpu::readstring(gptr addr) {
|
||||
std::string out;
|
||||
uint32_t offset = 0;
|
||||
while(guchar c = read8(addr+offset)) {
|
||||
if(c == 0)
|
||||
break;
|
||||
out += c;
|
||||
offset++;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
bool Cpu::writemem(gptr addr, void *src, guint size) {
|
||||
return uc_mem_write(uc, addr, src, size) == UC_ERR_OK;
|
||||
}
|
||||
|
||||
gptr Cpu::pc() {
|
||||
gptr val;
|
||||
CHECKED(uc_reg_read(uc, UC_ARM64_REG_PC, &val));
|
||||
return val;
|
||||
}
|
||||
|
||||
void Cpu::pc(gptr val) {
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &val));
|
||||
}
|
||||
|
||||
guint Cpu::reg(uint regn) {
|
||||
guint val;
|
||||
auto treg = UC_ARM64_REG_SP;
|
||||
if(regn <= 28)
|
||||
treg = (uc_arm64_reg) (UC_ARM64_REG_X0 + regn);
|
||||
else if(regn < 31)
|
||||
treg = (uc_arm64_reg) (UC_ARM64_REG_X29 + regn - 29);
|
||||
CHECKED(uc_reg_read(uc, treg, &val));
|
||||
return val;
|
||||
}
|
||||
|
||||
void Cpu::reg(uint regn, guint val) {
|
||||
auto treg = UC_ARM64_REG_SP;
|
||||
if(regn <= 28)
|
||||
treg = (uc_arm64_reg) (UC_ARM64_REG_X0 + regn);
|
||||
else if(regn < 31)
|
||||
treg = (uc_arm64_reg) (UC_ARM64_REG_X29 + regn - 29);
|
||||
CHECKED(uc_reg_write(uc, treg, &val));
|
||||
}
|
||||
|
||||
void Cpu::loadRegs(ThreadRegisters ®s) {
|
||||
int uregs[32];
|
||||
void *tregs[32];
|
||||
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_SP, ®s.SP));
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, ®s.PC));
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, ®s.NZCV));
|
||||
|
||||
for(auto i = 0; i < 29; ++i) {
|
||||
uregs[i] = UC_ARM64_REG_X0 + i;
|
||||
tregs[i] = ®s.gprs[i];
|
||||
}
|
||||
CHECKED(uc_reg_write_batch(uc, uregs, tregs, 29));
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_X29, ®s.X29));
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_X30, ®s.X30));
|
||||
|
||||
for(auto i = 0; i < 32; ++i) {
|
||||
uregs[i] = UC_ARM64_REG_Q0 + i;
|
||||
tregs[i] = ®s.fprs[i];
|
||||
}
|
||||
CHECKED(uc_reg_write_batch(uc, uregs, tregs, 32));
|
||||
}
|
||||
|
||||
void Cpu::storeRegs(ThreadRegisters ®s) {
|
||||
int uregs[32];
|
||||
void *tregs[32];
|
||||
|
||||
CHECKED(uc_reg_read(uc, UC_ARM64_REG_SP, ®s.SP));
|
||||
CHECKED(uc_reg_read(uc, UC_ARM64_REG_PC, ®s.PC));
|
||||
CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, ®s.NZCV));
|
||||
|
||||
for(auto i = 0; i < 29; ++i) {
|
||||
uregs[i] = UC_ARM64_REG_X0 + i;
|
||||
tregs[i] = ®s.gprs[i];
|
||||
}
|
||||
CHECKED(uc_reg_read_batch(uc, uregs, tregs, 29));
|
||||
CHECKED(uc_reg_read(uc, UC_ARM64_REG_X29, ®s.X29));
|
||||
CHECKED(uc_reg_read(uc, UC_ARM64_REG_X30, ®s.X30));
|
||||
|
||||
for(auto i = 0; i < 32; ++i) {
|
||||
uregs[i] = UC_ARM64_REG_Q0 + i;
|
||||
tregs[i] = ®s.fprs[i];
|
||||
}
|
||||
CHECKED(uc_reg_read_batch(uc, uregs, tregs, 32));
|
||||
}
|
||||
|
||||
gptr Cpu::tlsBase() {
|
||||
gptr base;
|
||||
CHECKED(uc_reg_read(uc, UC_ARM64_REG_TPIDRRO_EL0, &base));
|
||||
return base;
|
||||
}
|
||||
|
||||
void Cpu::tlsBase(gptr base) {
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDRRO_EL0, &base));
|
||||
}
|
||||
|
||||
void Cpu::interruptHook(uint32_t intNo) {
|
||||
uint32_t esr;
|
||||
CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr));
|
||||
auto ec = esr >> 26;
|
||||
auto iss = esr & 0xFFFFFF;
|
||||
switch(ec) {
|
||||
case 0x15: // SVC
|
||||
if(iss >= 0x80 || svcHandlers[iss] == nullptr)
|
||||
LOG_ERROR(Cpu, "Unhandled SVC 0x%02x", iss);
|
||||
svcHandlers[iss](this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool Cpu::unmappedHook(uc_mem_type type, gptr addr, int size, guint value) {
|
||||
cout << "!!!!!!!!!!!!!!!!!!!!!!!! Unmapped !!!!!!!!!!!!!!!!!!!!!!!" << endl;
|
||||
switch(type) {
|
||||
case UC_MEM_READ_UNMAPPED:
|
||||
case UC_MEM_READ_PROT:
|
||||
LOG_INFO(Cpu, "Attempted to read from %s memory at " ADDRFMT " from " ADDRFMT, (type == UC_MEM_READ_UNMAPPED ? "unmapped" : "protected"), addr, pc());
|
||||
break;
|
||||
case UC_MEM_FETCH_UNMAPPED:
|
||||
case UC_MEM_FETCH_PROT:
|
||||
LOG_INFO(Cpu, "Attempted to fetch from %s memory at " ADDRFMT " from " ADDRFMT, (type == UC_MEM_READ_UNMAPPED ? "unmapped" : "protected"), addr, pc());
|
||||
break;
|
||||
case UC_MEM_WRITE_UNMAPPED:
|
||||
case UC_MEM_WRITE_PROT:
|
||||
LOG_INFO(Cpu, "Attempted to write to %s memory at " ADDRFMT " from " ADDRFMT, (type == UC_MEM_READ_UNMAPPED ? "unmapped" : "protected"), addr, pc());
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Cpu::registerSvcHandler(int num, std::function<void()> handler) {
|
||||
registerSvcHandler(num, [=](auto _) { handler(); });
|
||||
}
|
||||
|
||||
void Cpu::registerSvcHandler(int num, std::function<void(Cpu *)> handler) {
|
||||
svcHandlers[num] = handler;
|
||||
}
|
||||
|
||||
hook_t Cpu::addCodeBreakpoint(gptr addr) {
|
||||
assert(ctu->gdbStub.enabled);
|
||||
|
||||
hook_t hookHandle;
|
||||
CHECKED(uc_hook_add(uc, &hookHandle, UC_HOOK_CODE, (void *)codeBpHook, ctu, addr, addr + 2));
|
||||
return hookHandle;
|
||||
}
|
||||
|
||||
void Cpu::removeCodeBreakpoint(hook_t hook) {
|
||||
CHECKED(uc_hook_del(uc, hook));
|
||||
}
|
100
Cpu.h
Normal file
100
Cpu.h
Normal file
|
@ -0,0 +1,100 @@
|
|||
#pragma once
|
||||
#include "Ctu.h"
|
||||
#include <unicorn/unicorn.h>
|
||||
|
||||
typedef uc_hook hook_t;
|
||||
|
||||
#define CHECKED(expr) do { if(auto _cerr = (expr)) { printf("Call " #expr " failed with error: %u (%s)\n", _cerr, uc_strerror(_cerr)); exit(1); } } while(0)
|
||||
|
||||
template<typename T> class Guest;
|
||||
|
||||
class Cpu {
|
||||
public:
|
||||
Cpu(Ctu *_ctu);
|
||||
~Cpu();
|
||||
|
||||
uint64_t call(gptr addr, uint64_t x0=0, uint64_t x1=0, uint64_t x2=0, uint64_t x3=0);
|
||||
void exec(size_t insnCount=0);
|
||||
void stop();
|
||||
|
||||
bool map(gptr addr, guint size);
|
||||
bool unmap(gptr addr, guint size);
|
||||
list<tuple<gptr, guint, int>> regions();
|
||||
bool readmem(gptr addr, void *dest, guint size);
|
||||
guchar read8(gptr addr);
|
||||
void dumpmem(gptr addr, guint size);
|
||||
std::string readstring(gptr addr);
|
||||
bool writemem(gptr addr, void *src, guint size);
|
||||
|
||||
gptr pc();
|
||||
void pc(gptr val);
|
||||
guint reg(uint reg);
|
||||
void reg(uint reg, guint val);
|
||||
|
||||
void loadRegs(ThreadRegisters ®s);
|
||||
void storeRegs(ThreadRegisters ®s);
|
||||
|
||||
gptr tlsBase();
|
||||
void tlsBase(gptr base);
|
||||
|
||||
template<typename T> Guest<T> guestptr(gptr addr) {
|
||||
Guest<T> ret(this, addr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void registerSvcHandler(int num, std::function<void()> handler);
|
||||
void registerSvcHandler(int num, std::function<void(Cpu *)> handler);
|
||||
|
||||
hook_t addCodeBreakpoint(gptr addr);
|
||||
void removeCodeBreakpoint(hook_t hook);
|
||||
|
||||
void interruptHook(uint32_t intNo);
|
||||
bool unmappedHook(uc_mem_type type, gptr addr, int size, guint value);
|
||||
|
||||
void setMmio(Mmio *_mmioHandler);// { mmioHandler = _mmioHandler; }
|
||||
Mmio *mmioHandler;
|
||||
|
||||
private:
|
||||
Ctu *ctu;
|
||||
uc_engine *uc;
|
||||
std::function<void(Cpu *)> svcHandlers[0x80];
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class Guest {
|
||||
public:
|
||||
Guest(Cpu *cpu, gptr addr) : cpu(cpu), addr(addr) {
|
||||
}
|
||||
|
||||
const T& operator*() {
|
||||
cpu->readmem(addr, &store, sizeof(T));
|
||||
return store;
|
||||
}
|
||||
|
||||
T* operator->() {
|
||||
cpu->readmem(addr, &store, sizeof(T));
|
||||
return &store;
|
||||
}
|
||||
|
||||
Guest<T> &operator=(const T &v) {
|
||||
cpu->writemem(addr, (void *) &v, sizeof(T));
|
||||
return *this;
|
||||
}
|
||||
|
||||
const T &operator[](int i) {
|
||||
return *Guest<T>(cpu, addr + i * sizeof(T));
|
||||
}
|
||||
|
||||
Guest<T> operator+(const int &i) {
|
||||
return Guest<T>(cpu, addr + i * sizeof(T));
|
||||
}
|
||||
|
||||
void writeback() {
|
||||
cpu->writemem(addr, &store, sizeof(T));
|
||||
}
|
||||
|
||||
private:
|
||||
Cpu *cpu;
|
||||
gptr addr;
|
||||
T store;
|
||||
};
|
37
Ctu.cpp
Normal file
37
Ctu.cpp
Normal file
|
@ -0,0 +1,37 @@
|
|||
#include "Ctu.h"
|
||||
|
||||
LogLevel g_LogLevel = Info;
|
||||
|
||||
Ctu::Ctu() : cpu(this), svc(this), ipc(this), tm(this), mmiohandler(this), bridge(this), gdbStub(this), handleId(0xde00), heapsize(0x0) {
|
||||
handles[0xffff8001] = make_shared<Process>(this);
|
||||
}
|
||||
|
||||
void Ctu::execProgram(gptr ep) {
|
||||
auto sp = 7 << 24;
|
||||
auto ss = 8 * 1024 * 1024;
|
||||
|
||||
cpu.map(sp - ss, ss);
|
||||
cpu.setMmio(&mmiohandler);
|
||||
mmiohandler.MMIOInitialize();
|
||||
|
||||
auto mainThread = tm.create(ep, sp);
|
||||
mainThread->regs.X1 = mainThread->handle;
|
||||
mainThread->resume();
|
||||
|
||||
tm.start();
|
||||
}
|
||||
|
||||
ghandle Ctu::duplicateHandle(KObject *ptr) {
|
||||
for(auto elem : handles)
|
||||
if(elem.second.get() == ptr)
|
||||
return newHandle(elem.second);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Ctu::deleteHandle(ghandle handle) {
|
||||
if(handles.find(handle) != handles.end()) {
|
||||
auto hnd = getHandle<KObject>(handle);
|
||||
handles.erase(handle);
|
||||
hnd->close();
|
||||
}
|
||||
}
|
171
Ctu.h
Normal file
171
Ctu.h
Normal file
|
@ -0,0 +1,171 @@
|
|||
#pragma once
|
||||
|
||||
#include <byteswap.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <climits>
|
||||
#include <forward_list>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <numeric>
|
||||
#include <unordered_map>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
using namespace std;
|
||||
|
||||
typedef __int128_t int128_t;
|
||||
typedef __uint128_t uint128_t;
|
||||
typedef float float32_t;
|
||||
typedef double float64_t;
|
||||
typedef uint64_t gptr;
|
||||
typedef uint64_t guint;
|
||||
typedef uint8_t guchar;
|
||||
typedef uint16_t gushort;
|
||||
typedef uint32_t ghandle;
|
||||
typedef uint64_t gpid;
|
||||
const gptr TERMADDR = 1ULL << 61;
|
||||
|
||||
#define FOURCC(a, b, c, d) (((d) << 24) | ((c) << 16) | ((b) << 8) | (a))
|
||||
|
||||
#define ADDRFMT "%016lx"
|
||||
#define LONGFMT "%lx"
|
||||
|
||||
enum LogLevel {
|
||||
None = 0,
|
||||
Error = 1,
|
||||
Warn = 2,
|
||||
Debug = 3,
|
||||
Info = 4
|
||||
};
|
||||
|
||||
extern LogLevel g_LogLevel;
|
||||
|
||||
#define LOG_ERROR(module, msg, ...) do { \
|
||||
if(g_LogLevel >= Error) { \
|
||||
fprintf(stderr, "[" #module "] ERROR: " msg "\n", ##__VA_ARGS__); \
|
||||
cerr << endl; \
|
||||
} \
|
||||
exit(1); \
|
||||
} while(0)
|
||||
#define LOG_INFO(module, msg, ...) do { \
|
||||
if(g_LogLevel >= Info) { \
|
||||
printf("[" #module "] INFO: " msg, ##__VA_ARGS__); \
|
||||
cout << endl; \
|
||||
} \
|
||||
} while(0)
|
||||
#define LOG_DEBUG(module, msg, ...) do { \
|
||||
if(g_LogLevel >= Debug) { \
|
||||
printf("[" #module "] DEBUG: " msg, ##__VA_ARGS__); \
|
||||
cout << endl; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
class Ctu;
|
||||
|
||||
#include "optionparser.h"
|
||||
#include "Lisparser.h"
|
||||
#include "KObject.h"
|
||||
#include "ThreadManager.h"
|
||||
#include "Mmio.h"
|
||||
#include "Cpu.h"
|
||||
#include "Sync.h"
|
||||
#include "Svc.h"
|
||||
#include "Ipc.h"
|
||||
#include "Nxo.h"
|
||||
#include "IpcBridge.h"
|
||||
#include "GdbStub.h"
|
||||
|
||||
template<unsigned long N>
|
||||
void hexdump(shared_ptr<array<uint8_t, N>> buf, unsigned long count=N) {
|
||||
if(g_LogLevel < Debug)
|
||||
return;
|
||||
|
||||
for(auto i = 0; i < count; i += 16) {
|
||||
printf("%04x | ", i);
|
||||
for(auto j = 0; j < 16; ++j) {
|
||||
printf("%02x ", buf->data()[i + j]);
|
||||
if(j == 7)
|
||||
printf(" ");
|
||||
}
|
||||
printf("| ");
|
||||
for(auto j = 0; j < 16; ++j) {
|
||||
auto val = buf->data()[i + j];
|
||||
if(isprint(val))
|
||||
printf("%c", val);
|
||||
else
|
||||
printf(".");
|
||||
if(j == 7)
|
||||
printf(" ");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
printf("%04x\n", (int) count);
|
||||
}
|
||||
|
||||
class Ctu {
|
||||
public:
|
||||
Ctu();
|
||||
void execProgram(gptr ep);
|
||||
|
||||
template<typename T>
|
||||
ghandle newHandle(shared_ptr<T> obj) {
|
||||
static_assert(std::is_base_of<KObject, T>::value, "T must derive from KObject");
|
||||
auto hnd = handleId++;
|
||||
handles[hnd] = dynamic_pointer_cast<KObject>(obj);
|
||||
return hnd;
|
||||
}
|
||||
template<typename T>
|
||||
shared_ptr<T> getHandle(ghandle handle) {
|
||||
static_assert(std::is_base_of<KObject, T>::value, "T must derive from KObject");
|
||||
if(handles.find(handle) == handles.end())
|
||||
LOG_ERROR(Ctu, "Could not find handle with ID 0x%x !", handle);
|
||||
auto obj = handles[handle];
|
||||
auto faux = dynamic_pointer_cast<FauxHandle>(obj);
|
||||
if(faux != nullptr)
|
||||
LOG_ERROR(Ctu, "Accessing faux handle! 0x%x", faux->val);
|
||||
auto temp = dynamic_pointer_cast<T>(obj);
|
||||
if(temp == nullptr)
|
||||
LOG_ERROR(Ctu, "Got null pointer after cast. Before: 0x%p", (void *) obj.get());
|
||||
return temp;
|
||||
}
|
||||
ghandle duplicateHandle(KObject *ptr);
|
||||
void deleteHandle(ghandle handle);
|
||||
|
||||
Mmio mmiohandler;
|
||||
Cpu cpu;
|
||||
Svc svc;
|
||||
Ipc ipc;
|
||||
ThreadManager tm;
|
||||
IpcBridge bridge;
|
||||
GdbStub gdbStub;
|
||||
|
||||
guint heapsize;
|
||||
gptr loadbase, loadsize;
|
||||
|
||||
private:
|
||||
ghandle handleId;
|
||||
unordered_map<ghandle, shared_ptr<KObject>> handles;
|
||||
};
|
||||
|
||||
#include "IpcStubs.h"
|
720
GdbStub.cpp
Normal file
720
GdbStub.cpp
Normal file
|
@ -0,0 +1,720 @@
|
|||
// Copyright 2013 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
// Originally written by Sven Peter <sven@fail0verflow.com> for anergistic.
|
||||
// Integrated into Mephisto/CTUv2 by Cody Brocious
|
||||
|
||||
#include "Ctu.h"
|
||||
|
||||
const char GDB_STUB_START = '$';
|
||||
const char GDB_STUB_END = '#';
|
||||
const char GDB_STUB_ACK = '+';
|
||||
const char GDB_STUB_NACK = '-';
|
||||
|
||||
#ifndef SIGTRAP
|
||||
const uint32_t SIGTRAP = 5;
|
||||
#endif
|
||||
|
||||
#ifndef SIGTERM
|
||||
const uint32_t SIGTERM = 15;
|
||||
#endif
|
||||
|
||||
#ifndef MSG_WAITALL
|
||||
const uint32_t MSG_WAITALL = 8;
|
||||
#endif
|
||||
|
||||
// For sample XML files see the GDB source /gdb/features
|
||||
// GDB also wants the l character at the start
|
||||
// This XML defines what the registers are for this specific ARM device
|
||||
static const char* target_xml =
|
||||
R"(<?xml version="1.0"?>
|
||||
<!DOCTYPE target SYSTEM "gdb-target.dtd">
|
||||
<target version="1.0">
|
||||
<feature name="org.gnu.gdb.aarch64.core">
|
||||
<reg name="x0" bitsize="64"/>
|
||||
<reg name="x1" bitsize="64"/>
|
||||
<reg name="x2" bitsize="64"/>
|
||||
<reg name="x3" bitsize="64"/>
|
||||
<reg name="x4" bitsize="64"/>
|
||||
<reg name="x5" bitsize="64"/>
|
||||
<reg name="x6" bitsize="64"/>
|
||||
<reg name="x7" bitsize="64"/>
|
||||
<reg name="x8" bitsize="64"/>
|
||||
<reg name="x9" bitsize="64"/>
|
||||
<reg name="x10" bitsize="64"/>
|
||||
<reg name="x11" bitsize="64"/>
|
||||
<reg name="x12" bitsize="64"/>
|
||||
<reg name="x13" bitsize="64"/>
|
||||
<reg name="x14" bitsize="64"/>
|
||||
<reg name="x15" bitsize="64"/>
|
||||
<reg name="x16" bitsize="64"/>
|
||||
<reg name="x17" bitsize="64"/>
|
||||
<reg name="x18" bitsize="64"/>
|
||||
<reg name="x19" bitsize="64"/>
|
||||
<reg name="x20" bitsize="64"/>
|
||||
<reg name="x21" bitsize="64"/>
|
||||
<reg name="x22" bitsize="64"/>
|
||||
<reg name="x23" bitsize="64"/>
|
||||
<reg name="x24" bitsize="64"/>
|
||||
<reg name="x25" bitsize="64"/>
|
||||
<reg name="x26" bitsize="64"/>
|
||||
<reg name="x27" bitsize="64"/>
|
||||
<reg name="x28" bitsize="64"/>
|
||||
<reg name="x29" bitsize="64"/>
|
||||
<reg name="x30" bitsize="64"/>
|
||||
<reg name="sp" bitsize="64" type="data_ptr"/>
|
||||
<reg name="pc" bitsize="64" type="code_ptr"/>
|
||||
<reg name="cpsr" bitsize="32"/>
|
||||
</feature>
|
||||
</target>)";
|
||||
|
||||
uint8_t hexCharToValue(uint8_t hex) {
|
||||
if(hex >= '0' && hex <= '9')
|
||||
return hex - '0';
|
||||
else if(hex >= 'a' && hex <= 'f')
|
||||
return hex - 'a' + 0xA;
|
||||
else if(hex >= 'A' && hex <= 'F')
|
||||
return hex - 'A' + 0xA;
|
||||
|
||||
LOG_ERROR(GdbStub, "Invalid nibble: %c (%02x)", hex, hex);
|
||||
}
|
||||
|
||||
uint8_t nibbleToHex(uint8_t n) {
|
||||
n &= 0xF;
|
||||
if(n < 0xA)
|
||||
return '0' + n;
|
||||
else
|
||||
return 'A' + n - 0xA;
|
||||
}
|
||||
|
||||
uint64_t hexToInt(const uint8_t* src, size_t len) {
|
||||
uint64_t output = 0;
|
||||
while(len-- > 0) {
|
||||
output = (output << 4) | hexCharToValue(src[0]);
|
||||
src++;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
void memToGdbHex(uint8_t* dest, const uint8_t* src, size_t len) {
|
||||
while(len-- > 0) {
|
||||
auto tmp = *src++;
|
||||
*dest++ = nibbleToHex(tmp >> 4);
|
||||
*dest++ = nibbleToHex(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
void gdbHexToMem(uint8_t* dest, const uint8_t* src, size_t len) {
|
||||
while(len-- > 0) {
|
||||
*dest++ = (uint8_t) ((hexCharToValue(src[0]) << 4) | hexCharToValue(src[1]));
|
||||
src += 2;
|
||||
}
|
||||
}
|
||||
|
||||
void intToGdbHex(uint8_t* dest, uint64_t v) {
|
||||
for(auto i = 0; i < 16; i += 2) {
|
||||
dest[i + 1] = nibbleToHex((uint8_t) (v >> (4 * i)));
|
||||
dest[i] = nibbleToHex((uint8_t) (v >> (4 * (i + 1))));
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t gdbHexToInt(const uint8_t* src) {
|
||||
uint64_t output = 0;
|
||||
|
||||
for(int i = 0; i < 16; i += 2) {
|
||||
output = (output << 4) | hexCharToValue(src[15 - i - 1]);
|
||||
output = (output << 4) | hexCharToValue(src[15 - i]);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
uint8_t calculateChecksum(const uint8_t* buffer, size_t length) {
|
||||
return static_cast<uint8_t>(accumulate(buffer, buffer + length, 0, plus<uint8_t>()));
|
||||
}
|
||||
|
||||
GdbStub::GdbStub(Ctu *_ctu) : ctu(_ctu) {
|
||||
memoryBreak = false;
|
||||
haltLoop = stepLoop = false;
|
||||
enabled = false;
|
||||
latestSignal = 0;
|
||||
}
|
||||
|
||||
void GdbStub::enable(uint16_t port) {
|
||||
LOG_INFO(GdbStub, "Starting GDB server on port %d...", port);
|
||||
|
||||
sockaddr_in saddr_server = {};
|
||||
saddr_server.sin_family = AF_INET;
|
||||
saddr_server.sin_port = htons(port);
|
||||
saddr_server.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
auto tmpsock = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if(tmpsock == -1)
|
||||
LOG_ERROR(GdbStub, "Failed to create gdb socket");
|
||||
|
||||
auto reuse_enabled = 1;
|
||||
if(setsockopt(tmpsock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse_enabled, sizeof(reuse_enabled)) < 0)
|
||||
LOG_ERROR(GdbStub, "Failed to set gdb socket option");
|
||||
|
||||
auto server_addr = reinterpret_cast<const sockaddr*>(&saddr_server);
|
||||
socklen_t server_addrlen = sizeof(saddr_server);
|
||||
if(bind(tmpsock, server_addr, server_addrlen) < 0)
|
||||
LOG_ERROR(GdbStub, "Failed to bind gdb socket");
|
||||
|
||||
if(listen(tmpsock, 1) < 0)
|
||||
LOG_ERROR(GdbStub, "Failed to listen to gdb socket");
|
||||
|
||||
LOG_INFO(GdbStub, "Waiting for gdb to connect...");
|
||||
sockaddr_in saddr_client;
|
||||
sockaddr* client_addr = reinterpret_cast<sockaddr*>(&saddr_client);
|
||||
socklen_t client_addrlen = sizeof(saddr_client);
|
||||
client = accept(tmpsock, client_addr, &client_addrlen);
|
||||
if(client < 0)
|
||||
LOG_ERROR(GdbStub, "Failed to accept gdb client");
|
||||
else
|
||||
LOG_INFO(GdbStub, "Client connected.");
|
||||
|
||||
enabled = true;
|
||||
haltLoop = true;
|
||||
}
|
||||
|
||||
uint8_t GdbStub::readByte() {
|
||||
uint8_t c;
|
||||
auto size = recv(client, reinterpret_cast<char*>(&c), 1, MSG_WAITALL);
|
||||
if(size != 1)
|
||||
LOG_ERROR(GdbStub, "recv failed : %ld", size);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
guint GdbStub::reg(int x) {
|
||||
auto thread = ctu->tm.current();
|
||||
if(thread == nullptr)
|
||||
thread = ctu->tm.last();
|
||||
if(thread == nullptr)
|
||||
return 0;
|
||||
switch(x) {
|
||||
case 31:
|
||||
return thread->regs.SP;
|
||||
case 32:
|
||||
return thread->regs.PC;
|
||||
default:
|
||||
assert(x < 31);
|
||||
return thread->regs.gprs[x];
|
||||
}
|
||||
}
|
||||
|
||||
void GdbStub::reg(int x, guint v) {
|
||||
auto thread = ctu->tm.current();
|
||||
if(thread == nullptr)
|
||||
thread = ctu->tm.last();
|
||||
if(thread == nullptr)
|
||||
return;
|
||||
switch(x) {
|
||||
case 31:
|
||||
thread->regs.SP = v;
|
||||
break;
|
||||
case 32:
|
||||
thread->regs.PC = v;
|
||||
break;
|
||||
default:
|
||||
assert(x < 31);
|
||||
thread->regs.gprs[x] = v;
|
||||
}
|
||||
}
|
||||
|
||||
auto& GdbStub::getBreakpointList(BreakpointType type) {
|
||||
switch(type) {
|
||||
case BreakpointType::Execute:
|
||||
return breakpointsExecute;
|
||||
case BreakpointType::Write:
|
||||
return breakpointsWrite;
|
||||
case BreakpointType::Read:
|
||||
case BreakpointType::Access:
|
||||
case BreakpointType::None: // Should never happen
|
||||
return breakpointsRead;
|
||||
}
|
||||
}
|
||||
|
||||
void GdbStub::removeBreakpoint(BreakpointType type, gptr addr) {
|
||||
auto& p = getBreakpointList(type);
|
||||
|
||||
auto bp = p.find(addr);
|
||||
if(bp != p.end()) {
|
||||
LOG_DEBUG(GdbStub, "gdb: removed a breakpoint: %016lx bytes at %016lx of type %d",
|
||||
bp->second.len, bp->second.addr, type);
|
||||
ctu->cpu.removeCodeBreakpoint(bp->second.hook);
|
||||
p.erase(addr);
|
||||
}
|
||||
}
|
||||
|
||||
auto GdbStub::getNextBreakpointFromAddress(gptr addr, BreakpointType type) {
|
||||
auto& p = getBreakpointList(type);
|
||||
auto next_breakpoint = p.lower_bound(addr);
|
||||
BreakpointAddress breakpoint;
|
||||
|
||||
if(next_breakpoint != p.end()) {
|
||||
breakpoint.address = next_breakpoint->first;
|
||||
breakpoint.type = type;
|
||||
} else {
|
||||
breakpoint.address = 0;
|
||||
breakpoint.type = BreakpointType::None;
|
||||
}
|
||||
|
||||
return breakpoint;
|
||||
}
|
||||
|
||||
bool GdbStub::checkBreakpoint(gptr addr, BreakpointType type) {
|
||||
auto& p = getBreakpointList(type);
|
||||
|
||||
auto bp = p.find(addr);
|
||||
if(bp != p.end()) {
|
||||
guint len = bp->second.len;
|
||||
|
||||
if(bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) {
|
||||
LOG_DEBUG(GdbStub,
|
||||
"Found breakpoint type %d @ %016lx, range: %016lx - %016lx (%d bytes)", type,
|
||||
addr, bp->second.addr, bp->second.addr + len, (uint32_t) len);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GdbStub::sendPacket(const char packet) {
|
||||
if(send(client, &packet, 1, 0) != 1)
|
||||
LOG_ERROR(GdbStub, "send failed");
|
||||
}
|
||||
|
||||
void GdbStub::sendReply(const char* reply) {
|
||||
memset(commandBuffer, 0, sizeof(commandBuffer));
|
||||
|
||||
commandLength = static_cast<uint32_t>(strlen(reply));
|
||||
if(commandLength + 4 > sizeof(commandBuffer)) {
|
||||
LOG_DEBUG(GdbStub, "commandBuffer overflow in sendReply");
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(commandBuffer + 1, reply, commandLength);
|
||||
|
||||
auto checksum = calculateChecksum(commandBuffer, commandLength + 1);
|
||||
commandBuffer[0] = GDB_STUB_START;
|
||||
commandBuffer[commandLength + 1] = GDB_STUB_END;
|
||||
commandBuffer[commandLength + 2] = nibbleToHex(checksum >> 4);
|
||||
commandBuffer[commandLength + 3] = nibbleToHex(checksum);
|
||||
|
||||
auto ptr = commandBuffer;
|
||||
auto left = commandLength + 4;
|
||||
while(left > 0) {
|
||||
auto sent_size = send(client, reinterpret_cast<char*>(ptr), left, 0);
|
||||
if(sent_size < 0)
|
||||
LOG_ERROR(GdbStub, "gdb: send failed");
|
||||
|
||||
left -= sent_size;
|
||||
ptr += sent_size;
|
||||
}
|
||||
}
|
||||
|
||||
void GdbStub::handleQuery() {
|
||||
LOG_DEBUG(GdbStub, "gdb: query '%s'", commandBuffer + 1);
|
||||
|
||||
auto query = reinterpret_cast<const char*>(commandBuffer + 1);
|
||||
|
||||
if(strcmp(query, "TStatus") == 0)
|
||||
sendReply("T0");
|
||||
else if(strncmp(query, "Supported", strlen("Supported")) == 0)
|
||||
sendReply("PacketSize=1600");
|
||||
else if(strncmp(query, "Xfer:features:read:target.xml:",
|
||||
strlen("Xfer:features:read:target.xml:")) == 0)
|
||||
sendReply(target_xml);
|
||||
else
|
||||
sendReply("");
|
||||
}
|
||||
|
||||
void GdbStub::handleSetThread() {
|
||||
if(memcmp(commandBuffer, "Hg0", 3) == 0 || memcmp(commandBuffer, "Hc-1", 4) == 0 ||
|
||||
memcmp(commandBuffer, "Hc0", 4) == 0 || memcmp(commandBuffer, "Hc1", 4) == 0)
|
||||
return sendReply("OK");
|
||||
|
||||
sendReply("E01");
|
||||
}
|
||||
|
||||
auto stringFromFormat(const char* format, ...) {
|
||||
char *buf;
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
if(vasprintf(&buf, format, args) < 0)
|
||||
LOG_ERROR(GdbStub, "Unable to allocate memory for string");
|
||||
va_end(args);
|
||||
|
||||
string ret = buf;
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void GdbStub::sendSignal(uint32_t signal) {
|
||||
latestSignal = signal;
|
||||
|
||||
string buffer = stringFromFormat("T%02x%02x:%016lx;%02x:%016lx;", latestSignal, 32,
|
||||
bswap_64(reg(32)), 31, bswap_64(reg(31)));
|
||||
LOG_DEBUG(GdbStub, "Response: %s", buffer.c_str());
|
||||
sendReply(buffer.c_str());
|
||||
}
|
||||
|
||||
void GdbStub::readCommand() {
|
||||
commandLength = 0;
|
||||
memset(commandBuffer, 0, sizeof(commandBuffer));
|
||||
|
||||
uint8_t c = readByte();
|
||||
if(c == '+') {
|
||||
// ignore ack
|
||||
return;
|
||||
} else if(c == 0x03) {
|
||||
LOG_INFO(GdbStub, "gdb: found break command");
|
||||
haltLoop = true;
|
||||
sendSignal(SIGTRAP);
|
||||
return;
|
||||
} else if(c != GDB_STUB_START) {
|
||||
LOG_DEBUG(GdbStub, "gdb: read invalid byte %02x", c);
|
||||
return;
|
||||
}
|
||||
|
||||
while((c = readByte()) != GDB_STUB_END) {
|
||||
if(commandLength >= sizeof(commandBuffer)) {
|
||||
LOG_ERROR(GdbStub, "gdb: commandBuffer overflow");
|
||||
sendPacket(GDB_STUB_NACK);
|
||||
return;
|
||||
}
|
||||
commandBuffer[commandLength++] = c;
|
||||
}
|
||||
|
||||
auto checksum_received = hexCharToValue(readByte()) << 4;
|
||||
checksum_received |= hexCharToValue(readByte());
|
||||
|
||||
auto checksum_calculated = calculateChecksum(commandBuffer, commandLength);
|
||||
|
||||
if(checksum_received != checksum_calculated)
|
||||
LOG_ERROR(GdbStub,
|
||||
"gdb: invalid checksum: calculated %02x and read %02x for $%s# (length: %d)",
|
||||
checksum_calculated, checksum_received, commandBuffer, commandLength);
|
||||
|
||||
sendPacket(GDB_STUB_ACK);
|
||||
}
|
||||
|
||||
bool GdbStub::isDataAvailable() {
|
||||
fd_set fd_socket;
|
||||
|
||||
FD_ZERO(&fd_socket);
|
||||
FD_SET(client, &fd_socket);
|
||||
|
||||
struct timeval t;
|
||||
t.tv_sec = 0;
|
||||
t.tv_usec = 0;
|
||||
|
||||
if(select(client + 1, &fd_socket, nullptr, nullptr, &t) < 0) {
|
||||
LOG_ERROR(GdbStub, "select failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return FD_ISSET(client, &fd_socket) != 0;
|
||||
}
|
||||
|
||||
void GdbStub::readRegister() {
|
||||
uint8_t reply[64];
|
||||
memset(reply, 0, sizeof(reply));
|
||||
|
||||
uint32_t id = hexCharToValue(commandBuffer[1]);
|
||||
if(commandBuffer[2] != '\0') {
|
||||
id <<= 4;
|
||||
id |= hexCharToValue(commandBuffer[2]);
|
||||
}
|
||||
|
||||
if(id <= 32)
|
||||
intToGdbHex(reply, reg(id));
|
||||
else if(id == 33)
|
||||
memset(reply, '0', 8);
|
||||
else
|
||||
return sendReply("E01");
|
||||
|
||||
sendReply(reinterpret_cast<char*>(reply));
|
||||
}
|
||||
|
||||
void GdbStub::readRegisters() {
|
||||
uint8_t buffer[GDB_BUFFER_SIZE - 4];
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
|
||||
uint8_t* bufptr = buffer;
|
||||
for(int i = 0; i <= 32; i++) {
|
||||
intToGdbHex(bufptr + i * 16, reg(i));
|
||||
}
|
||||
|
||||
bufptr += (33 * 16);
|
||||
|
||||
memset(bufptr, '0', 8);
|
||||
|
||||
sendReply(reinterpret_cast<char*>(buffer));
|
||||
}
|
||||
|
||||
void GdbStub::writeRegister() {
|
||||
const uint8_t* buffer_ptr = commandBuffer + 3;
|
||||
|
||||
uint32_t id = hexCharToValue(commandBuffer[1]);
|
||||
if(commandBuffer[2] != '=') {
|
||||
++buffer_ptr;
|
||||
id <<= 4;
|
||||
id |= hexCharToValue(commandBuffer[2]);
|
||||
}
|
||||
|
||||
auto val = gdbHexToInt(buffer_ptr);
|
||||
|
||||
if(id <= 32)
|
||||
reg(id, val);
|
||||
else if(id == 33) {
|
||||
}
|
||||
else
|
||||
return sendReply("E01");
|
||||
|
||||
sendReply("OK");
|
||||
}
|
||||
|
||||
void GdbStub::writeRegisters() {
|
||||
const uint8_t* buffer_ptr = commandBuffer + 1;
|
||||
|
||||
if(commandBuffer[0] != 'G')
|
||||
return sendReply("E01");
|
||||
for(auto i = 0; i < 33; ++i)
|
||||
if(i <= 32)
|
||||
reg(i, gdbHexToInt(buffer_ptr + i * 16));
|
||||
|
||||
sendReply("OK");
|
||||
}
|
||||
|
||||
void GdbStub::readMemory() {
|
||||
uint8_t reply[GDB_BUFFER_SIZE - 4];
|
||||
|
||||
auto start_offset = commandBuffer + 1;
|
||||
auto addr_pos = find(start_offset, commandBuffer + commandLength, ',');
|
||||
auto addr = hexToInt(start_offset, static_cast<uint32_t>(addr_pos - start_offset));
|
||||
|
||||
start_offset = addr_pos + 1;
|
||||
auto len = hexToInt(start_offset, static_cast<uint32_t>((commandBuffer + commandLength) - start_offset));
|
||||
|
||||
LOG_DEBUG(GdbStub, "gdb: addr: %016lx len: %016lx", addr, len);
|
||||
|
||||
if(len * 2 > sizeof(reply)) {
|
||||
sendReply("E01");
|
||||
}
|
||||
|
||||
auto data = new uint8_t[len];
|
||||
if(ctu->cpu.readmem(addr, data, len)) {
|
||||
memToGdbHex(reply, data, len);
|
||||
reply[len * 2] = '\0';
|
||||
sendReply(reinterpret_cast<char*>(reply));
|
||||
} else
|
||||
sendReply("E00");
|
||||
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
void GdbStub::writeMemory() {
|
||||
auto start_offset = commandBuffer + 1;
|
||||
auto addr_pos = find(start_offset, commandBuffer + commandLength, ',');
|
||||
gptr addr = hexToInt(start_offset, static_cast<uint32_t>(addr_pos - start_offset));
|
||||
|
||||
start_offset = addr_pos + 1;
|
||||
auto len_pos = find(start_offset, commandBuffer + commandLength, ':');
|
||||
auto len = hexToInt(start_offset, static_cast<uint32_t>(len_pos - start_offset));
|
||||
|
||||
auto dst = new uint8_t[len];
|
||||
gdbHexToMem(dst, len_pos + 1, len);
|
||||
|
||||
if(ctu->cpu.writemem(addr, dst, len))
|
||||
sendReply("OK");
|
||||
else
|
||||
sendReply("E00");
|
||||
|
||||
delete[] dst;
|
||||
}
|
||||
|
||||
void GdbStub::_break(bool is_memoryBreak) {
|
||||
if(!haltLoop) {
|
||||
haltLoop = true;
|
||||
sendSignal(SIGTRAP);
|
||||
}
|
||||
|
||||
memoryBreak = is_memoryBreak;
|
||||
}
|
||||
|
||||
void GdbStub::step() {
|
||||
stepLoop = true;
|
||||
haltLoop = true;
|
||||
}
|
||||
|
||||
void GdbStub::_continue() {
|
||||
memoryBreak = false;
|
||||
stepLoop = false;
|
||||
haltLoop = false;
|
||||
}
|
||||
|
||||
bool GdbStub::commitBreakpoint(BreakpointType type, gptr addr, uint32_t len) {
|
||||
auto& p = getBreakpointList(type);
|
||||
|
||||
Breakpoint breakpoint;
|
||||
breakpoint.active = true;
|
||||
breakpoint.addr = addr;
|
||||
breakpoint.len = len;
|
||||
|
||||
if(type == BreakpointType::Execute)
|
||||
breakpoint.hook = ctu->cpu.addCodeBreakpoint(addr);
|
||||
|
||||
p.insert({addr, breakpoint});
|
||||
|
||||
LOG_DEBUG(GdbStub, "gdb: added %d breakpoint: %016lx bytes at %016lx", type, breakpoint.len,
|
||||
breakpoint.addr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GdbStub::addBreakpoint() {
|
||||
BreakpointType type;
|
||||
|
||||
uint8_t type_id = hexCharToValue(commandBuffer[1]);
|
||||
switch (type_id) {
|
||||
case 0:
|
||||
case 1:
|
||||
type = BreakpointType::Execute;
|
||||
break;
|
||||
case 2:
|
||||
type = BreakpointType::Write;
|
||||
break;
|
||||
case 3:
|
||||
type = BreakpointType::Read;
|
||||
break;
|
||||
case 4:
|
||||
type = BreakpointType::Access;
|
||||
break;
|
||||
default:
|
||||
return sendReply("E01");
|
||||
}
|
||||
|
||||
auto start_offset = commandBuffer + 3;
|
||||
auto addr_pos = find(start_offset, commandBuffer + commandLength, ',');
|
||||
gptr addr = hexToInt(start_offset, static_cast<uint32_t>(addr_pos - start_offset));
|
||||
|
||||
start_offset = addr_pos + 1;
|
||||
auto len = (uint32_t) hexToInt(start_offset, static_cast<uint32_t>((commandBuffer + commandLength) - start_offset));
|
||||
|
||||
if(type == BreakpointType::Access) {
|
||||
type = BreakpointType::Read;
|
||||
|
||||
if(!commitBreakpoint(type, addr, len)) {
|
||||
return sendReply("E02");
|
||||
}
|
||||
|
||||
type = BreakpointType::Write;
|
||||
}
|
||||
|
||||
if(!commitBreakpoint(type, addr, len)) {
|
||||
return sendReply("E02");
|
||||
}
|
||||
|
||||
sendReply("OK");
|
||||
}
|
||||
|
||||
void GdbStub::removeBreakpoint() {
|
||||
BreakpointType type;
|
||||
|
||||
uint8_t type_id = hexCharToValue(commandBuffer[1]);
|
||||
switch (type_id) {
|
||||
case 0:
|
||||
case 1:
|
||||
type = BreakpointType::Execute;
|
||||
break;
|
||||
case 2:
|
||||
type = BreakpointType::Write;
|
||||
break;
|
||||
case 3:
|
||||
type = BreakpointType::Read;
|
||||
break;
|
||||
case 4:
|
||||
type = BreakpointType::Access;
|
||||
break;
|
||||
default:
|
||||
return sendReply("E01");
|
||||
}
|
||||
|
||||
auto start_offset = commandBuffer + 3;
|
||||
auto addr_pos = find(start_offset, commandBuffer + commandLength, ',');
|
||||
gptr addr = hexToInt(start_offset, static_cast<uint32_t>(addr_pos - start_offset));
|
||||
|
||||
if(type == BreakpointType::Access) {
|
||||
type = BreakpointType::Read;
|
||||
removeBreakpoint(type, addr);
|
||||
|
||||
type = BreakpointType::Write;
|
||||
}
|
||||
|
||||
removeBreakpoint(type, addr);
|
||||
sendReply("OK");
|
||||
}
|
||||
|
||||
void GdbStub::handlePacket() {
|
||||
if(!isDataAvailable())
|
||||
return;
|
||||
|
||||
readCommand();
|
||||
if(commandLength == 0)
|
||||
return;
|
||||
|
||||
LOG_DEBUG(GdbStub, "Packet: %s", commandBuffer);
|
||||
|
||||
switch(commandBuffer[0]) {
|
||||
case 'q':
|
||||
handleQuery();
|
||||
break;
|
||||
case 'H':
|
||||
handleSetThread();
|
||||
break;
|
||||
case '?':
|
||||
sendSignal(latestSignal);
|
||||
break;
|
||||
case 'k':
|
||||
LOG_ERROR(GdbStub, "killed by gdb");
|
||||
case 'g':
|
||||
readRegisters();
|
||||
break;
|
||||
case 'G':
|
||||
writeRegisters();
|
||||
break;
|
||||
case 'p':
|
||||
readRegister();
|
||||
break;
|
||||
case 'P':
|
||||
writeRegister();
|
||||
break;
|
||||
case 'm':
|
||||
readMemory();
|
||||
break;
|
||||
case 'M':
|
||||
writeMemory();
|
||||
break;
|
||||
case 's':
|
||||
step();
|
||||
return;
|
||||
case 'C':
|
||||
case 'c':
|
||||
_continue();
|
||||
return;
|
||||
case 'z':
|
||||
removeBreakpoint();
|
||||
break;
|
||||
case 'Z':
|
||||
addBreakpoint();
|
||||
break;
|
||||
default:
|
||||
sendReply("");
|
||||
break;
|
||||
}
|
||||
}
|
85
GdbStub.h
Normal file
85
GdbStub.h
Normal file
|
@ -0,0 +1,85 @@
|
|||
// Copyright 2013 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
// Originally written by Sven Peter <sven@fail0verflow.com> for anergistic.
|
||||
// Integrated into Mephisto/CTUv2 by Cody Brocious
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Ctu.h"
|
||||
|
||||
#define GDB_BUFFER_SIZE 10000
|
||||
|
||||
/// Breakpoint Method
|
||||
enum class BreakpointType {
|
||||
None,
|
||||
Execute,
|
||||
Read,
|
||||
Write,
|
||||
Access
|
||||
};
|
||||
|
||||
struct BreakpointAddress {
|
||||
gptr address;
|
||||
BreakpointType type;
|
||||
};
|
||||
|
||||
struct Breakpoint {
|
||||
bool active;
|
||||
gptr addr;
|
||||
guint len;
|
||||
hook_t hook;
|
||||
};
|
||||
|
||||
class GdbStub {
|
||||
public:
|
||||
GdbStub(Ctu *_ctu);
|
||||
void enable(uint16_t port);
|
||||
void _break(bool is_memory_break = false);
|
||||
bool isMemoryBreak();
|
||||
void handlePacket();
|
||||
auto getNextBreakpointFromAddress(gptr addr, BreakpointType type);
|
||||
bool checkBreakpoint(gptr addr, BreakpointType type);
|
||||
|
||||
bool memoryBreak, haltLoop, stepLoop, enabled;
|
||||
|
||||
private:
|
||||
auto& getBreakpointList(BreakpointType type);
|
||||
void removeBreakpoint(BreakpointType type, gptr addr);
|
||||
uint8_t readByte();
|
||||
void sendPacket(const char packet);
|
||||
void sendReply(const char* reply);
|
||||
void handleQuery();
|
||||
void handleSetThread();
|
||||
void sendSignal(uint32_t signal);
|
||||
void readCommand();
|
||||
bool isDataAvailable();
|
||||
void readRegister();
|
||||
void readRegisters();
|
||||
void writeRegister();
|
||||
void writeRegisters();
|
||||
void readMemory();
|
||||
void writeMemory();
|
||||
void step();
|
||||
void _continue();
|
||||
bool commitBreakpoint(BreakpointType type, gptr addr, uint32_t len);
|
||||
void addBreakpoint();
|
||||
void removeBreakpoint();
|
||||
|
||||
guint reg(int x);
|
||||
void reg(int x, guint v);
|
||||
|
||||
Ctu *ctu;
|
||||
|
||||
map<gptr, Breakpoint> breakpointsExecute;
|
||||
map<gptr, Breakpoint> breakpointsRead;
|
||||
map<gptr, Breakpoint> breakpointsWrite;
|
||||
|
||||
int client;
|
||||
|
||||
uint8_t commandBuffer[GDB_BUFFER_SIZE];
|
||||
uint32_t commandLength;
|
||||
|
||||
uint32_t latestSignal;
|
||||
};
|
235
Ipc.cpp
Normal file
235
Ipc.cpp
Normal file
|
@ -0,0 +1,235 @@
|
|||
#define DEFINE_STUBS
|
||||
#include "Ctu.h"
|
||||
|
||||
string bufferToString(uint8_t *buf, uint size) {
|
||||
std::stringstream ss;
|
||||
auto allValid = true;
|
||||
auto hitNull = false;
|
||||
for(auto i = 0; i < size; ++i)
|
||||
if(hitNull && buf[i] != 0) {
|
||||
allValid = false;
|
||||
break;
|
||||
} else if(buf[i] == 0)
|
||||
hitNull = true;
|
||||
else if(buf[i] < 0x20 || buf[i] > 0x7E) {
|
||||
allValid = false;
|
||||
break;
|
||||
}
|
||||
if(allValid) {
|
||||
ss << '"';
|
||||
for(auto i = 0; i < size && buf[i] != 0; ++i)
|
||||
ss << (char) buf[i];
|
||||
ss << '"';
|
||||
} else {
|
||||
ss << "[";
|
||||
for(auto i = 0; i < size; ++i) {
|
||||
if(i != 0)
|
||||
ss << ", ";
|
||||
ss << "0x" << hex << setw(2) << setfill('0') << (int) buf[i];
|
||||
}
|
||||
ss << "]";
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void NPipe::messageAsync(shared_ptr<array<uint8_t, 0x100>> buf, function<void(uint32_t, bool closeHandle)> cb) {
|
||||
acquire();
|
||||
client.wait([=] {
|
||||
auto obuf = client.pop();
|
||||
memcpy(buf->data(), obuf->data(), 0x100);
|
||||
cb(0, false); // XXX: HANDLE RETCODES AND CLOSE
|
||||
return 1;
|
||||
});
|
||||
server.push(buf);
|
||||
signal(true);
|
||||
release();
|
||||
}
|
||||
|
||||
shared_ptr<IPipe> NPort::connectSync() {
|
||||
acquire();
|
||||
auto pipe = make_shared<NPipe>(ctu, name);
|
||||
available.push(pipe);
|
||||
signal(true);
|
||||
release();
|
||||
return pipe;
|
||||
}
|
||||
|
||||
shared_ptr<NPipe> NPort::accept() {
|
||||
acquire();
|
||||
assert(available.size() > 0);
|
||||
auto val = available.front();
|
||||
available.pop();
|
||||
release();
|
||||
return val;
|
||||
}
|
||||
|
||||
IncomingIpcMessage::IncomingIpcMessage(uint8_t *_ptr, bool isDomainObject) : ptr(_ptr) {
|
||||
auto buf = (uint32_t *) ptr;
|
||||
type = buf[0] & 0xFFFF;
|
||||
xCount = (buf[0] >> 16) & 0xF;
|
||||
aCount = (buf[0] >> 20) & 0xF;
|
||||
bCount = (buf[0] >> 24) & 0xF;
|
||||
wlen = buf[1] & 0x3FF;
|
||||
hasC = ((buf[1] >> 10) & 0x3) != 0;
|
||||
domainHandle = 0;
|
||||
domainCommand = 0;
|
||||
auto hasHD = (buf[1] >> 31) == 1;
|
||||
auto pos = 2;
|
||||
|
||||
if(hasHD) {
|
||||
auto hd = buf[pos++];
|
||||
hasPid = hd & 1;
|
||||
copyCount = (hd >> 1) & 0xF;
|
||||
moveCount = hd >> 5;
|
||||
if(hasPid) {
|
||||
pid = *((uint64_t *) &buf[pos]);
|
||||
pos += 2;
|
||||
}
|
||||
copyOffset = pos * 4;
|
||||
pos += copyCount;
|
||||
moveOffset = pos * 4;
|
||||
pos += moveCount;
|
||||
}
|
||||
|
||||
descOffset = pos * 4;
|
||||
|
||||
pos += xCount * 2;
|
||||
pos += aCount * 3;
|
||||
pos += bCount * 3;
|
||||
rawOffset = pos * 4;
|
||||
if(pos & 3)
|
||||
pos += 4 - (pos & 3);
|
||||
if(isDomainObject && type == 4) {
|
||||
domainHandle = buf[pos + 1];
|
||||
domainCommand = buf[pos] & 0xFF;
|
||||
pos += 4;
|
||||
}
|
||||
|
||||
assert(type == 2 || (isDomainObject && domainCommand == 2) || buf[pos] == FOURCC('S', 'F', 'C', 'I'));
|
||||
sfciOffset = pos * 4;
|
||||
|
||||
cmdId = getData<uint32_t>(0);
|
||||
}
|
||||
|
||||
OutgoingIpcMessage::OutgoingIpcMessage(uint8_t *_ptr, bool _isDomainObject) : ptr(_ptr), isDomainObject(_isDomainObject) {
|
||||
}
|
||||
|
||||
void OutgoingIpcMessage::initialize(uint _moveCount, uint _copyCount, uint dataBytes) {
|
||||
moveCount = _moveCount;
|
||||
copyCount = _copyCount;
|
||||
|
||||
auto buf = (uint32_t *) ptr;
|
||||
buf[0] = 0;
|
||||
if(moveCount != 0 || copyCount != 0) {
|
||||
buf[1] = ((moveCount != 0 && !isDomainObject) || copyCount != 0) ? (1U << 31) : 0;
|
||||
buf[2] = (copyCount << 1) | ((isDomainObject ? 0 : moveCount) << 5);
|
||||
}
|
||||
|
||||
auto pos = 2 + (((moveCount != 0 && !isDomainObject) || copyCount != 0) ? (1 + moveCount + copyCount) : 0);
|
||||
auto start = pos;
|
||||
if(pos & 3)
|
||||
pos += 4 - (pos & 3);
|
||||
if(isDomainObject)
|
||||
pos += 4;
|
||||
realDataOffset = isDomainObject ? moveCount << 2 : 0;
|
||||
auto dataWords = (realDataOffset >> 2) + (dataBytes & 3) ? (dataBytes >> 2) + 1 : (dataBytes >> 2);
|
||||
|
||||
buf[1] |= pos - start + 4 + dataWords;
|
||||
|
||||
sfcoOffset = pos * 4;
|
||||
buf[pos] = FOURCC('S', 'F', 'C', 'O');
|
||||
}
|
||||
|
||||
void OutgoingIpcMessage::commit() {
|
||||
auto buf = (uint32_t *) ptr;
|
||||
buf[(sfcoOffset >> 2) + 2] = errCode;
|
||||
}
|
||||
|
||||
uint32_t IpcService::messageSync(shared_ptr<array<uint8_t, 0x100>> buf, bool& closeHandle) {
|
||||
uint8_t obuf[0x100];
|
||||
memset(obuf, 0, 0x100);
|
||||
//hexdump(buf, 0x50);
|
||||
IncomingIpcMessage msg(buf->data(), isDomainObject);
|
||||
OutgoingIpcMessage resp(obuf, isDomainObject);
|
||||
auto ret = 0xf601;
|
||||
IpcService *target = this;
|
||||
if(isDomainObject && msg.domainHandle != thisHandle && msg.type == 4) {
|
||||
if(domainHandles.find(msg.domainHandle) != domainHandles.end())
|
||||
target = dynamic_pointer_cast<IpcService>(domainHandles[msg.domainHandle]).get();
|
||||
else
|
||||
LOG_ERROR(Ipc, "Unknown domain handle! 0x%x", msg.domainHandle);
|
||||
}
|
||||
if(!isDomainObject || msg.domainCommand == 1 || msg.type == 2 || msg.type == 5)
|
||||
switch(msg.type) {
|
||||
case 2: // Close
|
||||
closeHandle = true;
|
||||
resp.initialize(0, 0, 0);
|
||||
resp.errCode = 0;
|
||||
ret = 0;
|
||||
break;
|
||||
case 4: // Normal
|
||||
ret = target->dispatch(msg, resp);
|
||||
break;
|
||||
case 5: // Control
|
||||
switch(msg.cmdId) {
|
||||
case 0: // ConvertSessionToDomain
|
||||
LOG_DEBUG(Ipc, "ConvertSessionToDomain");
|
||||
resp.initialize(0, 0, 4);
|
||||
isDomainObject = true;
|
||||
*resp.getDataPointer<uint32_t *>(8) = thisHandle;
|
||||
resp.errCode = 0;
|
||||
ret = 0;
|
||||
break;
|
||||
case 2: // DuplicateSession
|
||||
LOG_DEBUG(Ipc, "DuplicateSession");
|
||||
resp.isDomainObject = false;
|
||||
resp.initialize(1, 0, 0);
|
||||
resp.move(0, ctu->duplicateHandle(dynamic_cast<KObject *>(this)));
|
||||
resp.errCode = 0;
|
||||
ret = 0;
|
||||
break;
|
||||
case 3: // QueryPointerBufferSize
|
||||
LOG_DEBUG(Ipc, "QueryPointerBufferSize");
|
||||
resp.initialize(0, 0, 4);
|
||||
*resp.getDataPointer<uint32_t *>(8) = 0x500;
|
||||
resp.errCode = 0;
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Ipc, "Unknown cmdId to control %u", msg.cmdId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
switch(msg.domainCommand) {
|
||||
case 2:
|
||||
domainHandles.erase(msg.domainHandle);
|
||||
resp.initialize(0, 0, 0);
|
||||
resp.errCode = 0;
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Ipc, "Unknown cmdId to domain %u", msg.domainCommand);
|
||||
}
|
||||
|
||||
if(ret == 0) {
|
||||
resp.commit();
|
||||
memcpy(buf->data(), obuf, 0x100);
|
||||
//hexdump(buf, 0x50);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ghandle IpcService::fauxNewHandle(shared_ptr<KObject> obj) {
|
||||
return ctu->newHandle(obj);
|
||||
}
|
||||
|
||||
Ipc::Ipc(Ctu *_ctu) : ctu(_ctu) {
|
||||
sm = make_shared<SmService>(ctu);
|
||||
}
|
||||
|
||||
ghandle Ipc::ConnectToPort(string name) {
|
||||
if(name != "sm:")
|
||||
LOG_ERROR(Ipc, "Attempt to connect to unknown port: \"%s\"", name.c_str());
|
||||
return ctu->newHandle(sm);
|
||||
}
|
249
Ipc.h
Normal file
249
Ipc.h
Normal file
|
@ -0,0 +1,249 @@
|
|||
#pragma once
|
||||
|
||||
#include "Ctu.h"
|
||||
|
||||
// Used to clarify IPC stubs
|
||||
#define IN
|
||||
#define OUT
|
||||
|
||||
#define buildInterface(cls, ...) make_shared<cls>(ctu, ##__VA_ARGS__)
|
||||
|
||||
string bufferToString(uint8_t *buf, uint size);
|
||||
|
||||
class IPipe : public Waitable {
|
||||
public:
|
||||
virtual ~IPipe() {}
|
||||
virtual bool isAsync() = 0;
|
||||
virtual uint32_t messageSync(shared_ptr<array<uint8_t, 0x100>> buf, bool& closeHandle) { return 0; }
|
||||
virtual void messageAsync(shared_ptr<array<uint8_t, 0x100>> buf, function<void(uint32_t, bool closeHandle)> cb) {}
|
||||
};
|
||||
|
||||
class IPort : public Waitable {
|
||||
public:
|
||||
virtual ~IPort() {}
|
||||
virtual bool isAsync() = 0;
|
||||
virtual shared_ptr<IPipe> connectSync() { return nullptr; }
|
||||
virtual void connectAsync(function<void(shared_ptr<IPipe>)> cb) {}
|
||||
};
|
||||
|
||||
class PipeEnd : public Waitable {
|
||||
public:
|
||||
PipeEnd() : closed(false) {}
|
||||
void close() override {
|
||||
if(closed)
|
||||
return;
|
||||
closed = true;
|
||||
signal();
|
||||
}
|
||||
|
||||
void push(shared_ptr<array<uint8_t, 0x100>> message) {
|
||||
acquire();
|
||||
messages.push(message);
|
||||
release();
|
||||
}
|
||||
shared_ptr<array<uint8_t, 0x100>> pop() {
|
||||
acquire();
|
||||
if(closed) {
|
||||
release();
|
||||
return nullptr;
|
||||
}
|
||||
auto temp = messages.front();
|
||||
messages.pop();
|
||||
release();
|
||||
return temp;
|
||||
}
|
||||
bool closed;
|
||||
|
||||
private:
|
||||
queue<shared_ptr<array<uint8_t, 0x100>>> messages;
|
||||
};
|
||||
|
||||
class NPipe : public IPipe {
|
||||
public:
|
||||
NPipe(Ctu *_ctu, string _name) : ctu(_ctu), name(_name), closed(false) {}
|
||||
bool isAsync() override { return true; }
|
||||
|
||||
void messageAsync(shared_ptr<array<uint8_t, 0x100>> buf, function<void(uint32_t, bool closeHandle)> cb) override;
|
||||
void close() override {
|
||||
if(closed)
|
||||
return;
|
||||
closed = true;
|
||||
server.close();
|
||||
client.close();
|
||||
signal();
|
||||
}
|
||||
|
||||
string name;
|
||||
PipeEnd server, client;
|
||||
bool closed;
|
||||
|
||||
private:
|
||||
Ctu *ctu;
|
||||
};
|
||||
|
||||
class NPort : public IPort {
|
||||
public:
|
||||
NPort(Ctu *_ctu, string _name) : ctu(_ctu), name(_name) {}
|
||||
~NPort() override {}
|
||||
bool isAsync() override { return false; }
|
||||
shared_ptr<IPipe> connectSync() override;
|
||||
|
||||
shared_ptr<NPipe> accept();
|
||||
|
||||
string name;
|
||||
|
||||
private:
|
||||
Ctu *ctu;
|
||||
queue<shared_ptr<NPipe>> available;
|
||||
};
|
||||
|
||||
class IncomingIpcMessage {
|
||||
public:
|
||||
IncomingIpcMessage(uint8_t *_ptr, bool isDomainObject);
|
||||
template<typename T>
|
||||
T getData(uint offset) {
|
||||
return *((T *) (ptr + sfciOffset + 8 + offset));
|
||||
}
|
||||
template<typename T>
|
||||
T getDataPointer(uint offset) {
|
||||
return (T) (ptr + sfciOffset + 8 + offset);
|
||||
}
|
||||
gptr getBuffer(int btype, int num, guint& size) {
|
||||
size = 0;
|
||||
auto ax = (btype & 3) == 1;
|
||||
auto flags_ = btype & 0xC0;
|
||||
auto flags = flags_ == 0x80 ? 3 : (flags_ == 0x40 ? 1 : 0);
|
||||
auto cx = (btype & 0xC) == 8;
|
||||
switch((ax << 1) | cx) {
|
||||
case 0: { // B
|
||||
auto t = (uint32_t *) (ptr + descOffset + xCount * 8 + aCount * 12 + num * 12);
|
||||
gptr a = t[0], b = t[1], c = t[2];
|
||||
size = (guint) (a | (((c >> 24) & 0xF) << 32));
|
||||
if((c & 0x3) != flags)
|
||||
LOG_ERROR(Ipc, "B descriptor flags don't match: %u vs expected %u", (uint) (c & 0x3), flags);
|
||||
return b | (((((c >> 2) << 4) & 0x70) | ((c >> 28) & 0xF)) << 32);
|
||||
}
|
||||
case 1: { // C
|
||||
assert(num == 0);
|
||||
auto t = (uint32_t *) (ptr + rawOffset + wlen * 4);
|
||||
gptr a = t[0], b = t[1];
|
||||
size = b >> 16;
|
||||
return a | ((b & 0xFFFF) << 32);
|
||||
}
|
||||
case 2: { // A
|
||||
auto t = (uint32_t *) (ptr + descOffset + xCount * 8 + num * 12);
|
||||
gptr a = t[0], b = t[1], c = t[2];
|
||||
size = (guint) (a | (((c >> 24) & 0xF) << 32));
|
||||
if((c & 0x3) != flags)
|
||||
LOG_ERROR(Ipc, "A descriptor flags don't match: %u vs expected %u", (uint) (c & 0x3), flags);
|
||||
return b | (((((c >> 2) << 4) & 0x70) | ((c >> 28) & 0xF)) << 32);
|
||||
}
|
||||
case 3: { // X
|
||||
auto t = (uint32_t *) (ptr + descOffset + num * 8);
|
||||
gptr a = t[0], b = t[1];
|
||||
size = (guint) (a >> 16);
|
||||
return b | ((((a >> 12) & 0xF) | ((a >> 2) & 0x70)) << 32);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
ghandle getMoved(int off) {
|
||||
return *(ghandle *) (ptr + moveOffset + off * 4);
|
||||
}
|
||||
ghandle getCopied(int off) {
|
||||
return *(ghandle *) (ptr + copyOffset + off * 4);
|
||||
}
|
||||
|
||||
uint cmdId, type;
|
||||
uint aCount, bCount, cCount, hasC, xCount, hasPid, moveCount, copyCount;
|
||||
gpid pid;
|
||||
|
||||
ghandle domainHandle;
|
||||
uint domainCommand;
|
||||
|
||||
private:
|
||||
uint wlen, rawOffset, sfciOffset, descOffset, copyOffset, moveOffset;
|
||||
uint8_t *ptr;
|
||||
};
|
||||
|
||||
class OutgoingIpcMessage {
|
||||
public:
|
||||
OutgoingIpcMessage(uint8_t *_ptr, bool _isDomainObject);
|
||||
void initialize(uint _moveCount, uint _copyCount, uint dataBytes);
|
||||
void commit();
|
||||
template<typename T>
|
||||
T getDataPointer(uint offset) {
|
||||
return (T) (ptr + sfcoOffset + 8 + offset + (offset < 8 ? 0 : realDataOffset));
|
||||
}
|
||||
void copy(int offset, ghandle hnd) {
|
||||
auto buf = (uint32_t *) ptr;
|
||||
buf[3 + offset] = hnd;
|
||||
}
|
||||
void move(int offset, ghandle hnd) {
|
||||
auto buf = (uint32_t *) ptr;
|
||||
if(isDomainObject)
|
||||
buf[(sfcoOffset >> 2) + 4 + offset] = hnd;
|
||||
else
|
||||
buf[3 + copyCount + offset] = hnd;
|
||||
}
|
||||
|
||||
uint32_t errCode;
|
||||
uint moveCount, copyCount;
|
||||
bool isDomainObject;
|
||||
|
||||
private:
|
||||
uint sfcoOffset;
|
||||
uint realDataOffset;
|
||||
uint8_t *ptr;
|
||||
};
|
||||
|
||||
class IpcService : public IPipe {
|
||||
public:
|
||||
IpcService(Ctu *_ctu) : ctu(_ctu), domainOwner(nullptr), isDomainObject(false), domainHandleIter(0xf001), thisHandle(0xf000) {}
|
||||
~IpcService() override {}
|
||||
bool isAsync() override { return false; }
|
||||
uint32_t messageSync(shared_ptr<array<uint8_t, 0x100>> buf, bool& closeHandle) override;
|
||||
virtual uint32_t dispatch(IncomingIpcMessage &req, OutgoingIpcMessage &resp) { return 0xF601; }
|
||||
|
||||
protected:
|
||||
Ctu *ctu;
|
||||
|
||||
IpcService *domainOwner;
|
||||
bool isDomainObject;
|
||||
int domainHandleIter, thisHandle;
|
||||
unordered_map<uint32_t, shared_ptr<KObject>> domainHandles;
|
||||
|
||||
template<typename T>
|
||||
ghandle createHandle(shared_ptr<T> obj) {
|
||||
static_assert(std::is_base_of<KObject, T>::value, "T must derive from KObject");
|
||||
if(domainOwner)
|
||||
return domainOwner->createHandle(obj);
|
||||
else if(isDomainObject) {
|
||||
auto hnd = domainHandleIter++;
|
||||
|
||||
auto temp = dynamic_pointer_cast<IpcService>(obj);
|
||||
if(temp != nullptr)
|
||||
temp->domainOwner = this;
|
||||
|
||||
domainHandles[hnd] = dynamic_pointer_cast<KObject>(obj);
|
||||
return hnd;
|
||||
} else
|
||||
return fauxNewHandle(dynamic_pointer_cast<KObject>(obj));
|
||||
}
|
||||
|
||||
ghandle fauxNewHandle(shared_ptr<KObject> obj);
|
||||
};
|
||||
|
||||
class IUnknown : public IpcService {
|
||||
};
|
||||
|
||||
class SmService;
|
||||
|
||||
class Ipc {
|
||||
public:
|
||||
Ipc(Ctu *_ctu);
|
||||
ghandle ConnectToPort(string name);
|
||||
shared_ptr<SmService> sm;
|
||||
private:
|
||||
Ctu *ctu;
|
||||
};
|
218
IpcBridge.cpp
Normal file
218
IpcBridge.cpp
Normal file
|
@ -0,0 +1,218 @@
|
|||
#include "Ctu.h"
|
||||
|
||||
#include "IpcStubs.h"
|
||||
|
||||
#define BRIDGE_PORT 31337
|
||||
|
||||
IpcBridge::IpcBridge(Ctu *_ctu) : ctu(_ctu) {
|
||||
struct sockaddr_in addr;
|
||||
serv = socket(AF_INET, SOCK_STREAM, 0);
|
||||
auto enable = 1;
|
||||
setsockopt(serv, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(BRIDGE_PORT);
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
memset(addr.sin_zero, '\0', sizeof addr.sin_zero);
|
||||
bind(serv, (struct sockaddr *) &addr, sizeof addr);
|
||||
listen(serv, 10);
|
||||
client = -1;
|
||||
waitingForAsync = false;
|
||||
}
|
||||
|
||||
void IpcBridge::start() {
|
||||
LOG_INFO(IpcBridge, "Starting");
|
||||
|
||||
for(auto i = 0; i < 24; ++i) {
|
||||
auto addr = (i + 1) * (1 << 20) + (1 << 28);
|
||||
ctu->cpu.map(addr, 1024 * 1024);
|
||||
buffers[i] = addr;
|
||||
}
|
||||
|
||||
auto thread = ctu->tm.createNative([this] { run(); });
|
||||
thread->resume();
|
||||
}
|
||||
|
||||
bool isReadable(int fd) {
|
||||
struct timeval tv;
|
||||
fd_set rfds;
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(fd, &rfds);
|
||||
|
||||
tv.tv_sec = tv.tv_usec = 0;
|
||||
return select(fd + 1, &rfds, nullptr, nullptr, &tv) != -1 && FD_ISSET(fd, &rfds);
|
||||
}
|
||||
|
||||
uint64_t IpcBridge::readint() {
|
||||
uint64_t val = 0;
|
||||
recv(client, &val, 8, 0);
|
||||
return val;
|
||||
}
|
||||
|
||||
uint64_t IpcBridge::readint(bool &closed) {
|
||||
uint64_t val = 0;
|
||||
if(recv(client, &val, 8, 0) == 0)
|
||||
closed = true;
|
||||
return val;
|
||||
}
|
||||
|
||||
string IpcBridge::readstring() {
|
||||
auto size = readint();
|
||||
auto buf = new char[size];
|
||||
recv(client, buf, size, 0);
|
||||
string ret(buf, size);
|
||||
delete[] buf;
|
||||
return ret;
|
||||
}
|
||||
|
||||
IpcData IpcBridge::readdata() {
|
||||
auto size = readint();
|
||||
auto buf = new uint8_t[size];
|
||||
recv(client, buf, size, 0);
|
||||
auto bufptr = buffers[bufferOff++];
|
||||
ctu->cpu.writemem(bufptr, buf, size);
|
||||
delete[] buf;
|
||||
return IpcData(bufptr, size);
|
||||
}
|
||||
|
||||
void IpcBridge::writeint(uint64_t val) {
|
||||
send(client, &val, 8, 0);
|
||||
}
|
||||
|
||||
void IpcBridge::writedesc(vector<tuple<IpcData, int>> descs) {
|
||||
writeint(descs.size());
|
||||
for(auto [data, flag] : descs) {
|
||||
writeint(data.size);
|
||||
auto buf = new uint8_t[data.size];
|
||||
ctu->cpu.readmem(data.ptr, buf, data.size);
|
||||
send(client, buf, data.size, 0);
|
||||
writeint(flag);
|
||||
}
|
||||
}
|
||||
|
||||
void IpcBridge::writedesc(vector<IpcData> descs) {
|
||||
writeint(descs.size());
|
||||
if(descs.size() > 0)
|
||||
LOG_ERROR(IpcBridge, "C descriptors unsupported");
|
||||
}
|
||||
|
||||
void IpcBridge::sendResponse(ghandle hnd, uint32_t res, bool close, shared_ptr<array<uint8_t, 0x100>> buf, const OutgoingBridgeMessage &orig) {
|
||||
if(close) {
|
||||
openHandles.erase(hnd);
|
||||
ctu->deleteHandle(hnd);
|
||||
writeint(0xf601);
|
||||
return;
|
||||
}
|
||||
if(res != 0) {
|
||||
writeint(res);
|
||||
return;
|
||||
}
|
||||
|
||||
hexdump(buf);
|
||||
|
||||
IncomingBridgeMessage msg(buf);
|
||||
|
||||
writeint(0);
|
||||
writeint(msg.data.size()); for(auto v : msg.data) writeint(v);
|
||||
writeint(msg.copiedHandles.size()); for(auto v : msg.copiedHandles) writeint(v);
|
||||
writeint(msg.movedHandles.size());
|
||||
for(auto v : msg.movedHandles) {
|
||||
openHandles[v] = ctu->getHandle<KObject>(v);
|
||||
writeint(v);
|
||||
}
|
||||
|
||||
writedesc(orig.a);
|
||||
writedesc(orig.b);
|
||||
writedesc(orig.c);
|
||||
writedesc(orig.x);
|
||||
|
||||
writeint(msg.type);
|
||||
}
|
||||
|
||||
void IpcBridge::run() {
|
||||
if(waitingForAsync)
|
||||
return;
|
||||
|
||||
if(client == -1) {
|
||||
if(!isReadable(serv))
|
||||
return;
|
||||
struct sockaddr_storage client_addr;
|
||||
socklen_t addr_size = sizeof client_addr;
|
||||
client = accept(serv, (struct sockaddr *) &client_addr, &addr_size);
|
||||
LOG_INFO(IpcBridge, "IPC bridge got connection");
|
||||
} else {
|
||||
if(!isReadable(client))
|
||||
return;
|
||||
auto closed = false;
|
||||
auto cmd = readint(closed);
|
||||
if(closed) {
|
||||
LOG_INFO(IpcBridge, "Client disconnected");
|
||||
client = -1;
|
||||
for(auto [hnd, _] : openHandles)
|
||||
ctu->deleteHandle(hnd);
|
||||
openHandles.clear();
|
||||
return;
|
||||
}
|
||||
switch(cmd) {
|
||||
case 0: { // Open service
|
||||
auto name = readstring();
|
||||
LOG_DEBUG(IpcBridge, "Attempting to open service %s", name.c_str());
|
||||
auto sm = ctu->ipc.sm;
|
||||
if(sm->ports.find(name) == sm->ports.end()) {
|
||||
LOG_DEBUG(IpcBridge, "Unknown service!");
|
||||
writeint(0);
|
||||
return;
|
||||
}
|
||||
auto obj = sm->ports[name]->connectSync();
|
||||
auto hnd = ctu->newHandle(obj);
|
||||
openHandles[hnd] = obj;
|
||||
writeint(hnd);
|
||||
break;
|
||||
}
|
||||
case 1: { // Close handle
|
||||
auto hnd = (ghandle) readint();
|
||||
openHandles.erase(hnd);
|
||||
ctu->deleteHandle(hnd);
|
||||
break;
|
||||
}
|
||||
case 2: { // Message
|
||||
OutgoingBridgeMessage msg;
|
||||
bufferOff = 0;
|
||||
msg.type = readint();
|
||||
msg.data = readarray([this] { return readint(); });
|
||||
msg.pid = readint();
|
||||
msg.copiedHandles = readarray([this] { return (ghandle) readint(); });
|
||||
msg.movedHandles = readarray([this] { return (ghandle) readint(); });
|
||||
msg.a = readarray([this] { return tuple{readdata(), (int) readint()}; });
|
||||
msg.b = readarray([this] { return tuple{readdata(), (int) readint()}; });
|
||||
msg.c = readarray([this] { return readdata(); });
|
||||
msg.x = readarray([this] { return tuple{readdata(), (int) readint()}; });
|
||||
auto hnd = (ghandle) readint();
|
||||
|
||||
auto packed = msg.pack();
|
||||
|
||||
if(openHandles.find(hnd) == openHandles.end()) {
|
||||
writeint(0xe401); // Bad handle
|
||||
break;
|
||||
}
|
||||
|
||||
auto obj = dynamic_pointer_cast<IPipe>(openHandles[hnd]);
|
||||
if(obj->isAsync()) {
|
||||
waitingForAsync = true;
|
||||
obj->messageAsync(packed, [=](auto res, auto close) {
|
||||
sendResponse(hnd, res, close, packed, msg);
|
||||
waitingForAsync = false;
|
||||
});
|
||||
} else {
|
||||
auto close = false;
|
||||
auto ret = obj->messageSync(packed, close);
|
||||
sendResponse(hnd, ret, close, packed, msg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
close(client);
|
||||
client = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
169
IpcBridge.h
Normal file
169
IpcBridge.h
Normal file
|
@ -0,0 +1,169 @@
|
|||
#pragma once
|
||||
|
||||
#include "Ctu.h"
|
||||
|
||||
class IpcData {
|
||||
public:
|
||||
IpcData(gptr _ptr, uint64_t _size) : ptr(_ptr), size(_size) {}
|
||||
|
||||
gptr ptr;
|
||||
uint64_t size;
|
||||
};
|
||||
|
||||
class OutgoingBridgeMessage {
|
||||
public:
|
||||
auto pack() {
|
||||
auto ret = make_shared<array<uint8_t, 0x100>>();
|
||||
|
||||
auto pos = 0;
|
||||
auto buf = (uint32_t *) ret->data();
|
||||
buf[0] = (uint32_t) (type | (x.size() << 16) | (a.size() << 20) | (b.size() << 24));
|
||||
buf[1] = (pid != (uint64_t) -1 || copiedHandles.size() > 0 || movedHandles.size() > 0) ? 1U << 31 : 0;
|
||||
buf[1] |= c.size() > 0 ? 2 << 10 : 0;
|
||||
pos = 2;
|
||||
if(pid != (uint64_t) -1 || copiedHandles.size() > 0 || movedHandles.size() > 0) {
|
||||
buf[pos] = pid != (uint64_t) -1 ? 1 : 0;
|
||||
buf[pos] |= copiedHandles.size() << 1;
|
||||
buf[pos++] |= movedHandles.size() << 5;
|
||||
if(pid != (uint64_t) -1) {
|
||||
buf[pos++] = pid & 0xFFFFFFFF;
|
||||
buf[pos++] = pid >> 32;
|
||||
}
|
||||
for(auto hnd : copiedHandles)
|
||||
buf[pos++] = hnd;
|
||||
for(auto hnd : movedHandles)
|
||||
buf[pos++] = hnd;
|
||||
}
|
||||
|
||||
for(auto [ed, ec] : x) {
|
||||
auto laddr = ed.ptr & 0xFFFFFFFF, haddr = ed.ptr >> 32;
|
||||
buf[pos++] = (uint32_t) (
|
||||
(ec & 0x3F) |
|
||||
(((haddr & 0x70) >> 4) << 6) |
|
||||
(ec & 0xE00) |
|
||||
((haddr & 0xF) << 12) |
|
||||
(ed.size << 16)
|
||||
);
|
||||
buf[pos++] = (uint32_t) laddr;
|
||||
}
|
||||
|
||||
vector<tuple<IpcData, int>> ab;
|
||||
ab.insert(ab.end(), a.begin(), a.end());
|
||||
ab.insert(ab.end(), b.begin(), b.end());
|
||||
for(auto [ad, af] : ab) {
|
||||
auto laddr = ad.ptr & 0xFFFFFFFF, haddr = ad.ptr >> 32;
|
||||
auto lsize = ad.size & 0xFFFFFFFF, hsize = ad.size >> 32;
|
||||
buf[pos++] = (uint32_t) lsize;
|
||||
buf[pos++] = (uint32_t) laddr;
|
||||
buf[pos++] = (uint32_t) (
|
||||
af |
|
||||
(((haddr & 0x70) >> 4) << 2) |
|
||||
((hsize & 0xF) << 24) |
|
||||
((haddr & 0xF) << 28)
|
||||
);
|
||||
}
|
||||
|
||||
auto epos = pos;
|
||||
while(pos & 3)
|
||||
buf[pos++] = 0;
|
||||
buf[pos++] = FOURCC('S', 'F', 'C', 'I');
|
||||
buf[pos++] = 0;
|
||||
for(auto elem : data) {
|
||||
buf[pos++] = (uint32_t) elem;
|
||||
buf[pos++] = (uint32_t) (elem >> 32);
|
||||
}
|
||||
|
||||
assert(c.size() == 0); // XXX: Add c descriptor packing eventually. One day.
|
||||
|
||||
buf[1] |= pos - epos + 2;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64_t type, pid;
|
||||
vector<uint64_t> data;
|
||||
vector<ghandle> copiedHandles, movedHandles;
|
||||
vector<tuple<IpcData, int>> a, b, x;
|
||||
vector<IpcData> c;
|
||||
};
|
||||
|
||||
class IncomingBridgeMessage {
|
||||
public:
|
||||
IncomingBridgeMessage(shared_ptr<array<uint8_t, 0x100>> _buf) {
|
||||
auto buf = (uint32_t *) _buf->data();
|
||||
type = buf[0] & 0xFFFF;
|
||||
auto nx = (buf[0] >> 16) & 0xF, na = (buf[0] >> 20) & 0xF, nb = (buf[0] >> 24) & 0xF;
|
||||
auto hasHD = buf[1] >> 31;
|
||||
auto hasC = (buf[1] >> 10) & 3;
|
||||
|
||||
if(nx || na || nb || hasC)
|
||||
LOG_DEBUG(IpcBridge, "Warning! Message coming back from IPC bridge has descriptors. This is unhandled and may lose data.");
|
||||
|
||||
auto pos = 2;
|
||||
|
||||
if(hasHD) {
|
||||
auto hasPid = buf[pos] & 1, nc = (buf[pos] >> 1) & 0xF, nm = (buf[pos++] >> 5) & 0xF;
|
||||
if(hasPid) {
|
||||
pid = buf[pos] | (((uint64_t) buf[pos+1]) << 32);
|
||||
pos += 2;
|
||||
}
|
||||
for(auto i = 0; i < nc; ++i)
|
||||
copiedHandles.push_back((ghandle) buf[pos++]);
|
||||
for(auto i = 0; i < nm; ++i)
|
||||
movedHandles.push_back((ghandle) buf[pos++]);
|
||||
}
|
||||
|
||||
pos += nx * 2 + na * 3 + nb * 3;
|
||||
|
||||
auto epos = pos + (buf[1] & 0x3FF) - 2;
|
||||
while(pos & 3)
|
||||
pos++;
|
||||
assert(buf[pos++] == FOURCC('S', 'F', 'C', 'O'));
|
||||
pos++;
|
||||
|
||||
for( ; pos < epos; pos += 2)
|
||||
data.push_back(buf[pos] | (((uint64_t) buf[pos+1]) << 32));
|
||||
}
|
||||
|
||||
uint64_t type, pid;
|
||||
vector<uint64_t> data;
|
||||
vector<ghandle> copiedHandles, movedHandles;
|
||||
};
|
||||
|
||||
class IpcBridge {
|
||||
public:
|
||||
IpcBridge(Ctu *_ctu);
|
||||
|
||||
void start();
|
||||
|
||||
private:
|
||||
void run();
|
||||
|
||||
uint64_t readint();
|
||||
uint64_t readint(bool &closed);
|
||||
template<typename T>
|
||||
auto readarray(T &&cb) {
|
||||
vector<decltype(cb())> vec;
|
||||
auto count = readint();
|
||||
vec.reserve(count);
|
||||
for(auto i = 0; i < count; ++i)
|
||||
vec.push_back(cb());
|
||||
return vec;
|
||||
}
|
||||
string readstring();
|
||||
IpcData readdata();
|
||||
void writeint(uint64_t val);
|
||||
void writedesc(vector<tuple<IpcData, int>> descs);
|
||||
void writedesc(vector<IpcData> descs);
|
||||
|
||||
void sendResponse(ghandle hnd, uint32_t res, bool closed, shared_ptr<array<uint8_t, 0x100>> buf, const OutgoingBridgeMessage &orig);
|
||||
|
||||
Ctu *ctu;
|
||||
int serv;
|
||||
int client;
|
||||
|
||||
unordered_map<ghandle, shared_ptr<KObject>> openHandles;
|
||||
gptr buffers[24];
|
||||
int bufferOff;
|
||||
bool waitingForAsync;
|
||||
};
|
24
KObject.h
Normal file
24
KObject.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include "Ctu.h"
|
||||
|
||||
class KObject {
|
||||
public:
|
||||
virtual ~KObject() {}
|
||||
virtual void close() {}
|
||||
};
|
||||
|
||||
class Process : public KObject {
|
||||
public:
|
||||
Process(Ctu *_ctu) : ctu(_ctu) {}
|
||||
|
||||
private:
|
||||
Ctu *ctu;
|
||||
};
|
||||
|
||||
class FauxHandle : public KObject {
|
||||
public:
|
||||
FauxHandle(uint32_t _val) : val(_val) {}
|
||||
|
||||
uint32_t val;
|
||||
};
|
86
Lisparser.cpp
Normal file
86
Lisparser.cpp
Normal file
|
@ -0,0 +1,86 @@
|
|||
#include "Ctu.h"
|
||||
|
||||
#define BETWEEN(x, a, b) (((x) >= (a)) && ((x) <= (b)))
|
||||
|
||||
string nextToken(string &code, int &off) {
|
||||
auto clen = code.length();
|
||||
while(off < clen && (code[off] == ' ' || code[off] == '\n' || code[off] == '\t' || code[off] == '\r'))
|
||||
++off;
|
||||
auto rlen = clen - off;
|
||||
auto orig = off;
|
||||
if(rlen <= 0)
|
||||
return "";
|
||||
if(code[off] == '(') {
|
||||
off++;
|
||||
return "(";
|
||||
} else if(code[off] == ')') {
|
||||
off++;
|
||||
return ")";
|
||||
} else if(rlen >= 3 && code[off] == '0' && code[off+1] == 'x') {
|
||||
int te;
|
||||
for(te = off + 2; te < clen; ++te) {
|
||||
if(code[te] == ' ' || code[te] == '\n' || code[te] == '\r' || code[te] == '\t' || code[te] == ')')
|
||||
break;
|
||||
assert(BETWEEN(code[te], '0', '9') || BETWEEN(code[te], 'a', 'f') || BETWEEN(code[te], 'A', 'F'));
|
||||
}
|
||||
off = te;
|
||||
return code.substr(orig, off - orig);
|
||||
} else if(BETWEEN(code[off], '0', '9')) {
|
||||
int te;
|
||||
for(te = off + 1; te < clen; ++te) {
|
||||
if(code[te] == ' ' || code[te] == '\n' || code[te] == '\r' || code[te] == '\t' || code[te] == ')')
|
||||
break;
|
||||
assert(BETWEEN(code[te], '0', '9'));
|
||||
}
|
||||
off = te;
|
||||
return code.substr(orig, off - orig);
|
||||
} else if(code[off] == '"') {
|
||||
string out = "\""; // Signal to the parser that this should be a String rather than Symbol
|
||||
for(++off ; off < clen && code[off] != '"'; ++off) {
|
||||
if(code[off] == '\\') {
|
||||
} else
|
||||
out += code[off];
|
||||
}
|
||||
assert(code[off++] == '"');
|
||||
return out;
|
||||
} else if(BETWEEN(code[off], '!', '\'') || BETWEEN(code[off], '*', '~')) {
|
||||
int te;
|
||||
for(te = off + 1; te < clen; ++te) {
|
||||
if(code[te] == ' ' || code[te] == '\n' || code[te] == '\r' || code[te] == '\t' || code[te] == ')')
|
||||
break;
|
||||
assert(BETWEEN(code[off], '!', '\'') || BETWEEN(code[off], '*', '~'));
|
||||
}
|
||||
off = te;
|
||||
return code.substr(orig, off - orig);
|
||||
}
|
||||
LOG_ERROR(Lisparser, "Unknown token at offset %i", off);
|
||||
}
|
||||
|
||||
shared_ptr<Atom> parseLisp(string code) {
|
||||
auto off = 0;
|
||||
auto cur = make_shared<Atom>();
|
||||
forward_list<shared_ptr<Atom>> stack;
|
||||
while(true) {
|
||||
auto token = nextToken(code, off);
|
||||
if(token == "")
|
||||
break;
|
||||
else if(token == "(") {
|
||||
auto ne = make_shared<Atom>();
|
||||
cur->children.push_back(ne);
|
||||
stack.push_front(cur);
|
||||
cur = ne;
|
||||
} else if(token == ")") {
|
||||
cur = stack.front();
|
||||
stack.pop_front();
|
||||
} else if(token[0] == '"')
|
||||
cur->children.push_back(make_shared<Atom>(String, token.substr(1)));
|
||||
else if(token.length() >= 3 && token[0] == '0' && token[1] == 'x')
|
||||
cur->children.push_back(make_shared<Atom>(stoull(token.substr(2), nullptr, 16)));
|
||||
else if(BETWEEN(token[0], '0', '9'))
|
||||
cur->children.push_back(make_shared<Atom>(stoull(token, nullptr, 10)));
|
||||
else
|
||||
cur->children.push_back(make_shared<Atom>(Symbol, token));
|
||||
}
|
||||
assert(stack.empty());
|
||||
return cur;
|
||||
}
|
29
Lisparser.h
Normal file
29
Lisparser.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
|
||||
#include "Ctu.h"
|
||||
|
||||
enum AtomType {
|
||||
Number, String, Symbol, List
|
||||
};
|
||||
|
||||
class Atom {
|
||||
public:
|
||||
Atom() {
|
||||
type = List;
|
||||
}
|
||||
Atom(AtomType _type, string val) {
|
||||
assert(_type == String || _type == Symbol);
|
||||
type = _type;
|
||||
strVal = val;
|
||||
}
|
||||
Atom(guint val) {
|
||||
type = Number;
|
||||
numVal = val;
|
||||
}
|
||||
AtomType type;
|
||||
guint numVal;
|
||||
string strVal;
|
||||
vector<shared_ptr<Atom>> children;
|
||||
};
|
||||
|
||||
shared_ptr<Atom> parseLisp(string code);
|
64
Mmio.cpp
Normal file
64
Mmio.cpp
Normal file
|
@ -0,0 +1,64 @@
|
|||
#include "Ctu.h"
|
||||
|
||||
Mmio::Mmio(Ctu *_ctu) {
|
||||
MMIORegister(0x70000000, 0x1000, new ApbMmio());
|
||||
|
||||
MMIORegister(0x702ec000, 0x2000, new ApbMmio());
|
||||
MMIORegister(0x70030000, 0x8000, new ApbMmio());
|
||||
MMIORegister(0x702ef700, 0x40, new ApbMmio());
|
||||
MMIORegister(0x702f9000, 0x1000, new ApbMmio());
|
||||
|
||||
//MMIORegister(0x70030000, 0x8000, new ApbMmio());
|
||||
ctu = _ctu;
|
||||
//MMIOInitialize();
|
||||
}
|
||||
|
||||
void Mmio::MMIORegister(gptr base, guint size, MmioBase *mmioBase) {
|
||||
LOG_DEBUG(Mmio, "Registered MMIO " ADDRFMT, base);
|
||||
mmioBase->setSize(size);
|
||||
mmioBases[base] = mmioBase;
|
||||
}
|
||||
|
||||
void Mmio::MMIOInitialize() {
|
||||
for(auto item : mmioBases) {
|
||||
item.second->setOffset(mmioBaseSize);
|
||||
mmioBaseSize += item.second->mmioSize;
|
||||
}
|
||||
if(mmioBaseSize & 0xFFF)
|
||||
mmioBaseSize = (mmioBaseSize & ~0xFFF) + 0x1000;
|
||||
|
||||
LOG_DEBUG(Mmio, "Mapping 0x" ADDRFMT " size 0x%x", mmioBaseAddr, (uint) mmioBaseSize);
|
||||
ctu->cpu.map(mmioBaseAddr, mmioBaseSize);
|
||||
}
|
||||
|
||||
gptr Mmio::getVirtualAddressFromAddr(gptr addr) {
|
||||
for(auto item : mmioBases) {
|
||||
if(addr >= item.first && addr <= (item.first+item.second->mmioSize)) {
|
||||
return mmioBaseAddr+item.second->offsetFromMMIO;
|
||||
}
|
||||
}
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
gptr Mmio::getPhysicalAddressFromVirtual(gptr addr) {
|
||||
if(addr < mmioBaseAddr) return 0x0;
|
||||
gptr offset = addr - mmioBaseAddr;
|
||||
for(auto item : mmioBases) {
|
||||
if(item.second->offsetFromMMIO >= offset && offset <= item.second->offsetFromMMIO+item.second->mmioSize) {
|
||||
return item.first+offset;
|
||||
}
|
||||
}
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
|
||||
MmioBase *Mmio::getMMIOFromPhysicalAddress(gptr addr) {
|
||||
if(addr < mmioBaseAddr) return nullptr;
|
||||
gptr offset = addr - mmioBaseAddr;
|
||||
for(auto item : mmioBases) {
|
||||
if(item.second->offsetFromMMIO >= offset && offset <= item.second->offsetFromMMIO+item.second->mmioSize) {
|
||||
return item.second;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
71
Mmio.h
Normal file
71
Mmio.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
#pragma once
|
||||
|
||||
#include "Ctu.h"
|
||||
|
||||
class MmioBase {
|
||||
public:
|
||||
MmioBase() {}
|
||||
virtual ~MmioBase() {
|
||||
for(auto entry : storedValues) {
|
||||
free(entry.second);
|
||||
}
|
||||
}
|
||||
void Setup();
|
||||
|
||||
virtual bool sread(gptr addr, guint size, void *out) {
|
||||
return false;
|
||||
}
|
||||
virtual bool swrite(gptr addr, guint size, void *value) {
|
||||
cout << "no" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
void setSize(guint _size) {
|
||||
mmioSize = _size;
|
||||
}
|
||||
|
||||
void setOffset(guint _offset) {
|
||||
offsetFromMMIO = _offset;
|
||||
}
|
||||
guint offsetFromMMIO;
|
||||
guint mmioSize;
|
||||
|
||||
private:
|
||||
|
||||
unordered_map<gptr, void *> storedValues;
|
||||
};
|
||||
|
||||
class Mmio {
|
||||
public:
|
||||
Mmio(Ctu *ctu);
|
||||
virtual ~Mmio() {}
|
||||
gptr getVirtualAddressFromAddr(gptr addr);
|
||||
gptr getPhysicalAddressFromVirtual(gptr addr);
|
||||
MmioBase *getMMIOFromPhysicalAddress(gptr addr);
|
||||
void MMIOInitialize();
|
||||
|
||||
gptr GetBase() {
|
||||
return mmioBaseAddr;
|
||||
}
|
||||
|
||||
guint GetSize() {
|
||||
return mmioBaseSize;
|
||||
}
|
||||
private:
|
||||
Ctu *ctu;
|
||||
void MMIORegister(gptr base, guint size, MmioBase *mmioBase);
|
||||
gptr mmioBaseAddr = 0x4000000;//1 << 58;
|
||||
guint mmioBaseSize = 0;
|
||||
unordered_map<gptr, MmioBase *> mmioBases;
|
||||
};
|
||||
|
||||
class ApbMmio : public MmioBase {
|
||||
public:
|
||||
bool sread(gptr addr, guint size, void *out) {
|
||||
return false;
|
||||
}
|
||||
bool swrite(gptr addr, guint size, void *value) {
|
||||
//*value = 0xdeadbeef;
|
||||
return false;
|
||||
}
|
||||
};
|
54
Nxo.cpp
Normal file
54
Nxo.cpp
Normal file
|
@ -0,0 +1,54 @@
|
|||
#include "Ctu.h"
|
||||
#include <lz4.h>
|
||||
|
||||
Nxo::Nxo(string fn) {
|
||||
fp.open(fn, ios::in | ios::binary);
|
||||
fp.seekg(0, ios_base::end);
|
||||
length = (uint32_t) fp.tellg();
|
||||
fp.seekg(0);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint32_t magic, pad0, pad1, pad2;
|
||||
uint32_t textOff, textLoc, textSize, pad3;
|
||||
uint32_t rdataOff, rdataLoc, rdataSize, pad4;
|
||||
uint32_t dataOff, dataLoc, dataSize;
|
||||
uint32_t bssSize;
|
||||
} NsoHeader;
|
||||
|
||||
char *decompress(ifstream &fp, uint32_t offset, uint32_t csize, uint32_t usize) {
|
||||
fp.seekg(offset);
|
||||
char *buf = new char[csize];
|
||||
char *obuf = new char[usize];
|
||||
fp.read(buf, csize);
|
||||
assert(LZ4_decompress_safe(buf, obuf, csize, usize) == usize);
|
||||
delete[] buf;
|
||||
return obuf;
|
||||
}
|
||||
|
||||
guint Nso::load(Ctu &ctu, gptr base, bool relocate) {
|
||||
NsoHeader hdr;
|
||||
fp.read((char *) &hdr, sizeof(NsoHeader));
|
||||
if(hdr.magic != FOURCC('N', 'S', 'O', '0'))
|
||||
return 0;
|
||||
|
||||
gptr tsize = hdr.dataLoc + hdr.dataSize + hdr.bssSize;
|
||||
if(tsize & 0xFFF)
|
||||
tsize = (tsize & ~0xFFF) + 0x1000;
|
||||
|
||||
ctu.cpu.map(base, tsize);
|
||||
|
||||
char *text = decompress(fp, hdr.textOff, hdr.rdataOff - hdr.textOff, hdr.textSize);
|
||||
ctu.cpu.writemem(base + hdr.textLoc, text, hdr.textSize);
|
||||
delete[] text;
|
||||
|
||||
char *rdata = decompress(fp, hdr.rdataOff, hdr.dataOff - hdr.rdataOff, hdr.rdataSize);
|
||||
ctu.cpu.writemem(base + hdr.rdataLoc, rdata, hdr.rdataSize);
|
||||
delete[] rdata;
|
||||
|
||||
char *data = decompress(fp, hdr.dataOff, length - hdr.dataOff, hdr.dataSize);
|
||||
ctu.cpu.writemem(base + hdr.dataLoc, data, hdr.dataSize);
|
||||
delete[] data;
|
||||
|
||||
return tsize;
|
||||
}
|
23
Nxo.h
Normal file
23
Nxo.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#include "Ctu.h"
|
||||
|
||||
class Nxo {
|
||||
public:
|
||||
Nxo(string fn);
|
||||
virtual ~Nxo() {}
|
||||
|
||||
virtual guint load(Ctu &ctu, gptr base, bool relocate=false) = 0;
|
||||
|
||||
protected:
|
||||
ifstream fp;
|
||||
uint32_t length;
|
||||
};
|
||||
|
||||
class Nso : Nxo {
|
||||
public:
|
||||
Nso(string fn) : Nxo(fn) {}
|
||||
virtual ~Nso() override {}
|
||||
|
||||
guint load(Ctu &ctu, gptr base, bool relocate=false) override;
|
||||
};
|
627
Svc.cpp
Normal file
627
Svc.cpp
Normal file
|
@ -0,0 +1,627 @@
|
|||
#include "Ctu.h"
|
||||
|
||||
#define IX0 (ctu->cpu.reg(0))
|
||||
#define IX1 (ctu->cpu.reg(1))
|
||||
#define IX2 (ctu->cpu.reg(2))
|
||||
#define IX3 (ctu->cpu.reg(3))
|
||||
#define IX4 (ctu->cpu.reg(4))
|
||||
#define IX5 (ctu->cpu.reg(5))
|
||||
|
||||
#define registerSvc(num, func, ...) do { \
|
||||
ctu->cpu.registerSvcHandler((num), [&] { \
|
||||
(func)(__VA_ARGS__); \
|
||||
}); \
|
||||
} while(0)
|
||||
|
||||
#define registerSvc_ret_X0_X1_X2(num, func, ...) do { \
|
||||
ctu->cpu.registerSvcHandler((num), [&] { \
|
||||
ctu->tm.switched = false; \
|
||||
auto [_0, _1, _2] = (func)(__VA_ARGS__); \
|
||||
if(ctu->tm.switched) { \
|
||||
ctu->tm.switched = false; \
|
||||
return; \
|
||||
} \
|
||||
ctu->cpu.reg(0, _0); \
|
||||
ctu->cpu.reg(1, _1); \
|
||||
ctu->cpu.reg(2, _2); \
|
||||
}); \
|
||||
} while(0)
|
||||
|
||||
#define registerSvc_ret_X0(num, func, ...) do { \
|
||||
ctu->cpu.registerSvcHandler((num), [&] { \
|
||||
ctu->tm.switched = false; \
|
||||
auto v = (func)(__VA_ARGS__); \
|
||||
if(ctu->tm.switched) { \
|
||||
ctu->tm.switched = false; \
|
||||
return; \
|
||||
} \
|
||||
ctu->cpu.reg(0, v); \
|
||||
}); \
|
||||
} while(0)
|
||||
|
||||
#define registerSvc_ret_X1(num, func, ...) do { \
|
||||
ctu->cpu.registerSvcHandler((num), [&] { \
|
||||
ctu->tm.switched = false; \
|
||||
auto v = (func)(__VA_ARGS__); \
|
||||
if(ctu->tm.switched) { \
|
||||
ctu->tm.switched = false; \
|
||||
return; \
|
||||
} \
|
||||
ctu->cpu.reg(1, v); \
|
||||
}); \
|
||||
} while(0)
|
||||
|
||||
#define registerSvc_ret_X0_X1(num, func, ...) do { \
|
||||
ctu->cpu.registerSvcHandler((num), [&] { \
|
||||
ctu->tm.switched = false; \
|
||||
auto [_0, _1] = (func)(__VA_ARGS__); \
|
||||
if(ctu->tm.switched) { \
|
||||
ctu->tm.switched = false; \
|
||||
return; \
|
||||
} \
|
||||
ctu->cpu.reg(0, _0); \
|
||||
ctu->cpu.reg(1, _1); \
|
||||
}); \
|
||||
} while(0)
|
||||
|
||||
Svc::Svc(Ctu *_ctu) : ctu(_ctu) {
|
||||
registerSvc_ret_X0_X1( 0x01, SetHeapSize, IX1);
|
||||
registerSvc_ret_X0( 0x03, SetMemoryAttribute, IX0, IX1, IX2, IX3);
|
||||
registerSvc_ret_X0( 0x04, MirrorStack, IX0, IX1, IX2);
|
||||
registerSvc_ret_X0( 0x05, UnmapMemory, IX0, IX1, IX2);
|
||||
registerSvc_ret_X0_X1( 0x06, QueryMemory, IX0, IX1, IX2);
|
||||
registerSvc( 0x07, ExitProcess);
|
||||
registerSvc_ret_X0_X1( 0x08, CreateThread, IX1, IX2, IX3, IX4, IX5);
|
||||
registerSvc_ret_X0( 0x09, StartThread, (ghandle) IX0);
|
||||
registerSvc( 0x0A, ExitThread);
|
||||
registerSvc_ret_X0( 0x0B, SleepThread, IX0);
|
||||
registerSvc_ret_X0_X1( 0x0C, GetThreadPriority, (ghandle) IX0);
|
||||
registerSvc_ret_X0( 0x0D, SetThreadPriority, (ghandle) IX0, IX1);
|
||||
registerSvc_ret_X0_X1_X2( 0x0E, GetThreadCoreMask, IX0);
|
||||
registerSvc_ret_X0( 0x0F, SetThreadCoreMask, IX0);
|
||||
registerSvc_ret_X0( 0x10, GetCurrentProcessorNumber, IX0);
|
||||
registerSvc_ret_X0( 0x11, SignalEvent, (ghandle) IX0);
|
||||
registerSvc_ret_X0( 0x12, ClearEvent, (ghandle) IX0);
|
||||
registerSvc_ret_X0( 0x13, MapMemoryBlock, (ghandle) IX0, IX1, IX2, IX3);
|
||||
registerSvc_ret_X0_X1( 0x15, CreateTransferMemory, IX0, IX1, IX2);
|
||||
registerSvc_ret_X0( 0x16, CloseHandle, (ghandle) IX0);
|
||||
registerSvc_ret_X0( 0x17, ResetSignal, (ghandle) IX0);
|
||||
registerSvc_ret_X0_X1( 0x18, WaitSynchronization, IX1, IX2, IX3);
|
||||
registerSvc_ret_X0( 0x19, CancelSynchronization, (ghandle) IX0);
|
||||
registerSvc_ret_X0( 0x1A, LockMutex, (ghandle) IX0, IX1, (ghandle) IX2);
|
||||
registerSvc( 0x1B, UnlockMutex, IX0);
|
||||
registerSvc( 0x1C, WaitProcessWideKeyAtomic, IX0, IX1, (ghandle) IX2, IX3);
|
||||
registerSvc_ret_X0( 0x1D, SignalProcessWideKey, IX0, IX1);
|
||||
registerSvc_ret_X0_X1( 0x1F, ConnectToPort, IX1);
|
||||
registerSvc_ret_X0( 0x21, SendSyncRequest, (ghandle) IX0);
|
||||
registerSvc_ret_X0( 0x22, SendSyncRequestEx, IX0, IX1, (ghandle) IX2);
|
||||
registerSvc_ret_X0_X1( 0x24, GetProcessID, (ghandle) IX1);
|
||||
registerSvc_ret_X0_X1( 0x25, GetThreadId);
|
||||
registerSvc_ret_X0( 0x26, Break, IX0, IX1, IX2);
|
||||
registerSvc_ret_X0( 0x27, OutputDebugString, IX0, IX1);
|
||||
registerSvc_ret_X0_X1( 0x29, GetInfo, IX1, (ghandle) IX2, IX3);
|
||||
registerSvc_ret_X0_X1_X2( 0x40, CreateSession, (ghandle) IX0, (ghandle) IX1, IX2);
|
||||
registerSvc_ret_X0_X1( 0x41, AcceptSession, (ghandle) IX1);
|
||||
registerSvc_ret_X0_X1( 0x43, ReplyAndReceive, IX1, IX2, (ghandle) IX3, IX4);
|
||||
registerSvc_ret_X0_X1_X2( 0x45, CreateEvent, (ghandle) IX0, (ghandle) IX1, IX2);
|
||||
registerSvc_ret_X0_X1( 0x4E, ReadWriteRegister, IX1, IX2, IX3);
|
||||
registerSvc_ret_X0_X1( 0x50, CreateMemoryBlock, IX1, IX2);
|
||||
registerSvc_ret_X0( 0x51, MapTransferMemory, (ghandle) IX0, IX1, IX2, IX3);
|
||||
registerSvc_ret_X0( 0x52, UnmapTransferMemory, (ghandle) IX0, IX1, IX2);
|
||||
registerSvc_ret_X0_X1( 0x53, CreateInterruptEvent, IX1);
|
||||
registerSvc_ret_X0_X1( 0x55, QueryIoMapping, IX1, IX2);
|
||||
registerSvc_ret_X0_X1( 0x56, CreateDeviceAddressSpace, IX1, IX2);
|
||||
registerSvc_ret_X0_X1( 0x57, AttachDeviceAddressSpace, (ghandle) IX0, IX1, IX2);
|
||||
registerSvc_ret_X0_X1( 0x59, MapDeviceAddressSpaceByForce, (ghandle) IX0, (ghandle) IX1, IX2, IX3, IX4, IX5);
|
||||
registerSvc_ret_X0( 0x5c, UnmapDeviceAddressSpace, IX0, (ghandle) IX1, IX2, IX3);
|
||||
registerSvc_ret_X0( 0x74, MapProcessMemory, IX0, (ghandle) IX1, IX2, IX3);
|
||||
registerSvc_ret_X0( 0x75, UnmapProcessMemory, IX0, (ghandle) IX1, IX2, IX3);
|
||||
registerSvc_ret_X0( 0x77, MapProcessCodeMemory, (ghandle) IX0, IX1, IX2, IX3);
|
||||
registerSvc_ret_X0( 0x78, UnmapProcessCodeMemory, (ghandle) IX0, IX1, IX2, IX3);
|
||||
}
|
||||
|
||||
tuple<guint, guint> Svc::SetHeapSize(guint size) {
|
||||
LOG_DEBUG(Svc[0x01], "SetHeapSize 0x" LONGFMT, size);
|
||||
ctu->heapsize = size;
|
||||
ctu->cpu.map(0xaa0000000, size);
|
||||
return make_tuple(0, 0xaa0000000);
|
||||
}
|
||||
|
||||
guint Svc::SetMemoryAttribute(gptr addr, guint size, guint state0, guint state1) {
|
||||
LOG_DEBUG(Svc[0x03], "SetMemoryAttribute 0x" ADDRFMT " 0x" LONGFMT " -> 0x" LONGFMT " 0x" LONGFMT, addr, size, state0, state1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
guint Svc::MirrorStack(gptr dest, gptr src, guint size) {
|
||||
LOG_DEBUG(Svc[0x04], "MirrorStack 0x" ADDRFMT " 0x" ADDRFMT " - 0x" LONGFMT, dest, src, size);
|
||||
ctu->cpu.map(dest, size);
|
||||
auto temp = new uint8_t[size];
|
||||
ctu->cpu.readmem(src, temp, size);
|
||||
ctu->cpu.writemem(dest, temp, size);
|
||||
delete[] temp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
guint Svc::UnmapMemory(gptr dest, gptr src, guint size) {
|
||||
LOG_DEBUG(Svc[0x05], "UnmapMemory 0x" ADDRFMT " 0x" ADDRFMT " - 0x" LONGFMT, dest, src, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
gptr begin;
|
||||
gptr size;
|
||||
gptr perms;
|
||||
guint cperm;
|
||||
} MemInfo;
|
||||
|
||||
tuple<guint, guint> Svc::QueryMemory(gptr meminfo, gptr pageinfo, gptr addr) {
|
||||
LOG_DEBUG(Svc[0x06], "QueryMemory 0x" ADDRFMT, addr);
|
||||
for(auto [begin, end, perm] : ctu->cpu.regions()) {
|
||||
if(begin <= addr && addr <= end) {
|
||||
LOG_DEBUG(Svc[0x06], "Found region at 0x" ADDRFMT "-0x" ADDRFMT, begin, end);
|
||||
MemInfo minfo;
|
||||
minfo.begin = begin;
|
||||
minfo.size = end - begin + 1;
|
||||
minfo.perms = perm == -1 ? 0 : 3; // FREE or CODE
|
||||
minfo.cperm = 0;
|
||||
|
||||
if(perm != -1) {
|
||||
auto offset = *ctu->cpu.guestptr<uint32_t>(begin + 4);
|
||||
if(begin + offset + 4 < end && *ctu->cpu.guestptr<uint32_t>(begin + offset) == FOURCC('M', 'O', 'D', '0'))
|
||||
minfo.cperm = 5;
|
||||
else
|
||||
minfo.cperm = 3;
|
||||
}
|
||||
ctu->cpu.guestptr<MemInfo>(meminfo) = minfo;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return make_tuple(0, 0);
|
||||
}
|
||||
|
||||
void Svc::ExitProcess() {
|
||||
LOG_DEBUG(Svc[0x07], "ExitProcess");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
tuple<guint, guint> Svc::CreateThread(guint pc, guint x0, guint sp, guint prio, guint proc) {
|
||||
LOG_DEBUG(Svc[0x08], "CreateThread pc=0x" LONGFMT " X0=0x" LONGFMT " SP=0x" LONGFMT, pc, x0, sp);
|
||||
auto thread = ctu->tm.create(pc, sp);
|
||||
thread->regs.X0 = x0;
|
||||
return make_tuple(0, thread->handle);
|
||||
}
|
||||
|
||||
guint Svc::StartThread(ghandle handle) {
|
||||
LOG_DEBUG(Svc[0x09], "StartThread 0x%x", handle);
|
||||
auto thread = ctu->getHandle<Thread>(handle);
|
||||
thread->resume();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Svc::ExitThread() {
|
||||
LOG_DEBUG(Svc[0x0A], "ExitThread");
|
||||
ctu->tm.current()->terminate();
|
||||
}
|
||||
|
||||
guint Svc::SleepThread(guint ns) {
|
||||
LOG_DEBUG(Svc[0x0B], "SleepThread 0x" LONGFMT " ns", ns);
|
||||
return 0;
|
||||
}
|
||||
|
||||
tuple<guint, guint> Svc::GetThreadPriority(ghandle handle) {
|
||||
LOG_DEBUG(Svc[0x0C], "GetThreadPriority 0x%x", handle);
|
||||
return make_tuple(0, 0);
|
||||
}
|
||||
|
||||
guint Svc::SetThreadPriority(ghandle handle, guint priority) {
|
||||
LOG_DEBUG(Svc[0x0D], "SetThreadPriority 0x%x -> 0x" LONGFMT, handle, priority);
|
||||
return 0;
|
||||
}
|
||||
|
||||
tuple<guint, guint, guint> Svc::GetThreadCoreMask(guint tmp) {
|
||||
LOG_DEBUG(Svc[0x0E], "GetThreadCoreMask");
|
||||
return make_tuple(0, 0xFF, 0xFF);
|
||||
}
|
||||
|
||||
guint Svc::SetThreadCoreMask(guint tmp) {
|
||||
LOG_DEBUG(Svc[0x0F], "GetThreadCoreMask");
|
||||
return 0;
|
||||
}
|
||||
|
||||
guint Svc::GetCurrentProcessorNumber(guint tmp) {
|
||||
LOG_DEBUG(Svc[0x10], "GetCurrentProcessorNumber");
|
||||
return 0;
|
||||
}
|
||||
|
||||
guint Svc::SignalEvent(ghandle handle) {
|
||||
LOG_DEBUG(Svc[0x11], "SignalEvent 0x%x", handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
guint Svc::ClearEvent(ghandle handle) {
|
||||
LOG_DEBUG(Svc[0x12], "ClearEvent 0x%x", handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
guint Svc::MapMemoryBlock(ghandle handle, gptr addr, guint size, guint perm) {
|
||||
LOG_DEBUG(Svc[0x13], "MapMemoryBlock 0x%x 0x" LONGFMT " 0x" LONGFMT " 0x" LONGFMT, handle, addr, size, perm);
|
||||
auto obj = ctu->getHandle<MemoryBlock>(handle);
|
||||
assert(obj->size == size);
|
||||
assert(obj->addr == 0);
|
||||
ctu->cpu.map(addr, size);
|
||||
obj->addr = addr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
tuple<guint, guint> Svc::CreateTransferMemory(gptr addr, guint size, guint perm) {
|
||||
LOG_DEBUG(Svc[0x15], "CreateTransferMemory 0x" LONGFMT " 0x" LONGFMT " 0x" LONGFMT, addr, size, perm);
|
||||
return make_tuple(0, 0);
|
||||
}
|
||||
|
||||
guint Svc::CloseHandle(ghandle handle) {
|
||||
LOG_DEBUG(Svc[0x16], "CloseHandle 0x%x", handle);
|
||||
ctu->deleteHandle(handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
guint Svc::ResetSignal(ghandle handle) {
|
||||
LOG_DEBUG(Svc[0x17], "ResetSignal 0x%x", handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
tuple<guint, guint> Svc::WaitSynchronization(gptr handles, guint numHandles, guint timeout) {
|
||||
LOG_DEBUG(Svc[0x18], "WaitSynchronization %u", (uint) numHandles);
|
||||
|
||||
auto thread = ctu->tm.current();
|
||||
thread->suspend([=] {
|
||||
auto triggered = make_shared<bool>(false);
|
||||
auto cb = [=](int i, bool canceled) {
|
||||
if(*triggered)
|
||||
return -1;
|
||||
*triggered = true;
|
||||
thread->resume([=] {
|
||||
if(canceled) {
|
||||
thread->regs.X0 = 0xec01;
|
||||
thread->regs.X1 = 0;
|
||||
} else {
|
||||
thread->regs.X0 = 0;
|
||||
thread->regs.X1 = i;
|
||||
}
|
||||
});
|
||||
return 1;
|
||||
};
|
||||
auto hdls = ctu->cpu.guestptr<ghandle>(handles);
|
||||
for(auto i = 0; i < numHandles; ++i) {
|
||||
auto hnd = ctu->getHandle<Waitable>(hdls[i]);
|
||||
auto port = dynamic_pointer_cast<NPort>(hnd);
|
||||
if(port != nullptr)
|
||||
LOG_DEBUG(Svc[0x18], "Waiting on port %s", port->name.c_str());
|
||||
hnd->wait([=](auto canceled) { return cb(i, canceled); });
|
||||
}
|
||||
});
|
||||
|
||||
return make_tuple(0, 0);
|
||||
}
|
||||
|
||||
guint Svc::CancelSynchronization(ghandle handle) {
|
||||
LOG_DEBUG(Svc[0x19], "CancelSynchronization 0x%x", handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
shared_ptr<Mutex> Svc::ensureMutex(gptr mutexAddr) {
|
||||
if(mutexes.find(mutexAddr) == mutexes.end()) {
|
||||
LOG_DEBUG(Svc, "Making new mutex for 0x" LONGFMT, mutexAddr);
|
||||
mutexes[mutexAddr] = make_shared<Mutex>(ctu->cpu.guestptr<uint32_t>(mutexAddr));
|
||||
}
|
||||
return mutexes[mutexAddr];
|
||||
}
|
||||
|
||||
guint Svc::LockMutex(ghandle curthread, gptr mutexAddr, ghandle reqthread) {
|
||||
LOG_DEBUG(Svc[0x1A], "LockMutex 0x%x 0x" LONGFMT " 0x%x", curthread, mutexAddr, reqthread);
|
||||
|
||||
auto mutex = ensureMutex(mutexAddr);
|
||||
auto owner = mutex->owner();
|
||||
|
||||
auto thread = ctu->getHandle<Thread>(reqthread);
|
||||
|
||||
if(owner != 0 && owner != reqthread) {
|
||||
LOG_DEBUG(Svc[0x1A], "Could not get mutex lock. Waiting.");
|
||||
mutex->hasWaiters(true);
|
||||
thread->suspend([=] {
|
||||
mutex->wait([=] {
|
||||
if(mutex->owner() == 0) {
|
||||
mutex->owner(reqthread);
|
||||
thread->resume([=] {
|
||||
thread->regs.X0 = 0;
|
||||
});
|
||||
return 1;
|
||||
} else
|
||||
return 0;
|
||||
});
|
||||
});
|
||||
} else {
|
||||
mutex->owner(reqthread);
|
||||
if(!thread->active) {
|
||||
thread->regs.X0 = 0;
|
||||
thread->resume();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Svc::UnlockMutex(gptr mutexAddr) {
|
||||
LOG_DEBUG(Svc[0x1B], "UnlockMutex 0x" ADDRFMT, mutexAddr);
|
||||
|
||||
auto mutex = ensureMutex(mutexAddr);
|
||||
auto owner = mutex->owner();
|
||||
assert(owner == 0 || owner == ctu->tm.current()->handle);
|
||||
|
||||
mutex->guestRelease();
|
||||
}
|
||||
|
||||
shared_ptr<Semaphore> Svc::ensureSemaphore(gptr semaAddr) {
|
||||
if(semaphores.find(semaAddr) == semaphores.end()) {
|
||||
LOG_DEBUG(Svc, "Making new semaphore for 0x" LONGFMT, semaAddr);
|
||||
semaphores[semaAddr] = make_shared<Semaphore>(ctu->cpu.guestptr<uint32_t>(semaAddr));
|
||||
}
|
||||
return semaphores[semaAddr];
|
||||
}
|
||||
|
||||
void Svc::WaitProcessWideKeyAtomic(gptr mutexAddr, gptr semaAddr, ghandle threadHandle, guint timeout) {
|
||||
LOG_DEBUG(Svc[0x1C], "WaitProcessWideKeyAtomic 0x" LONGFMT " 0x" LONGFMT " 0x%x 0x" LONGFMT, mutexAddr, semaAddr, threadHandle, timeout);
|
||||
|
||||
auto mutex = ensureMutex(mutexAddr);
|
||||
auto semaphore = ensureSemaphore(semaAddr);
|
||||
|
||||
assert(mutex->owner() == threadHandle);
|
||||
|
||||
if(semaphore->value() > 0) {
|
||||
semaphore->decrement();
|
||||
return;
|
||||
}
|
||||
|
||||
auto thread = ctu->getHandle<Thread>(threadHandle);
|
||||
thread->suspend([=] {
|
||||
semaphore->wait([=] {
|
||||
semaphore->decrement();
|
||||
thread->resume([=] {
|
||||
LockMutex(0, mutexAddr, threadHandle);
|
||||
});
|
||||
return 1;
|
||||
});
|
||||
});
|
||||
|
||||
mutex->guestRelease();
|
||||
}
|
||||
|
||||
guint Svc::SignalProcessWideKey(gptr semaAddr, guint target) {
|
||||
LOG_DEBUG(Svc[0x1D], "SignalProcessWideKey 0x" LONGFMT " 0x" LONGFMT, semaAddr, target);
|
||||
auto semaphore = ensureSemaphore(semaAddr);
|
||||
semaphore->increment();
|
||||
if(target == 1)
|
||||
semaphore->signal(true);
|
||||
else if(target == 0xffffffff)
|
||||
semaphore->signal();
|
||||
return 0;
|
||||
}
|
||||
|
||||
tuple<guint, ghandle> Svc::ConnectToPort(guint name) {
|
||||
LOG_DEBUG(Svc[0x1F], "ConnectToPort");
|
||||
return make_tuple(0, ctu->ipc.ConnectToPort(ctu->cpu.readstring(name)));
|
||||
}
|
||||
|
||||
guint Svc::SendSyncRequest(ghandle handle) {
|
||||
LOG_DEBUG(Svc[0x21], "SendSyncRequest 0x%x", handle);
|
||||
auto thread = ctu->tm.current();
|
||||
auto hnd = ctu->getHandle<IPipe>(handle);
|
||||
auto buf = make_shared<array<uint8_t, 0x100>>();
|
||||
ctu->cpu.readmem(thread->tlsBase, buf->data(), 0x100);
|
||||
if(hnd->isAsync()) {
|
||||
thread->suspend([=] {
|
||||
hnd->messageAsync(buf, [=](auto res, auto close) {
|
||||
ctu->cpu.writemem(thread->tlsBase, buf->data(), 0x100);
|
||||
if(close)
|
||||
ctu->deleteHandle(handle);
|
||||
thread->resume([=] {
|
||||
thread->regs.X0 = res;
|
||||
});
|
||||
});
|
||||
});
|
||||
return 0;
|
||||
} else {
|
||||
auto close = false;
|
||||
auto ret = hnd->messageSync(buf, close);
|
||||
if(close)
|
||||
ctu->deleteHandle(handle);
|
||||
ctu->cpu.writemem(thread->tlsBase, buf->data(), 0x100);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
guint Svc::SendSyncRequestEx(gptr buf, guint size, ghandle handle) {
|
||||
LOG_ERROR(Svc[0x22], "SendSyncRequestEx not implemented");
|
||||
return 0xf601;
|
||||
}
|
||||
|
||||
tuple<guint, guint> Svc::GetProcessID(ghandle handle) {
|
||||
LOG_DEBUG(Svc[0x24], "GetProcessID 0x%x", handle);
|
||||
return make_tuple(0, 0);
|
||||
}
|
||||
|
||||
tuple<guint, guint> Svc::GetThreadId() {
|
||||
LOG_DEBUG(Svc[0x25], "GetThreadId");
|
||||
return make_tuple(0, ctu->tm.current()->id);
|
||||
}
|
||||
|
||||
guint Svc::Break(guint X0, guint X1, guint info) {
|
||||
LOG_DEBUG(Svc[0x26], "Break");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
guint Svc::OutputDebugString(guint ptr, guint size) {
|
||||
LOG_DEBUG(Svc[0x27], "OutputDebugString");
|
||||
char *debugStr = (char*) malloc(size + 1);
|
||||
memset(debugStr, 0, size+1);
|
||||
ctu->cpu.readmem(ptr, debugStr, size);
|
||||
LOG_DEBUG(Svc[0x27], "Debug String: %s", debugStr);
|
||||
free(debugStr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define matchone(a, v) do { if(id1 == (a)) return make_tuple(0, (v)); } while(0)
|
||||
#define matchpair(a, b, v) do { if(id1 == (a) && id2 == (b)) return make_tuple(0, (v)); } while(0)
|
||||
|
||||
tuple<guint, guint> Svc::GetInfo(guint id1, ghandle handle, guint id2) {
|
||||
LOG_DEBUG(Svc[0x29], "GetInfo handle=0x%x id1=0x" LONGFMT " id2=" LONGFMT, handle, id1, id2);
|
||||
matchpair(0, 0, 0xF);
|
||||
matchpair(1, 0, 0xFFFFFFFF00000000);
|
||||
matchpair(2, 0, 0x7100000000);
|
||||
matchpair(3, 0, 0x1000000000);
|
||||
matchpair(4, 0, 0xaa0000000);
|
||||
matchpair(5, 0, ctu->heapsize); // Heap region size
|
||||
matchpair(6, 0, 0x100000);
|
||||
matchpair(7, 0, 0x10000);
|
||||
matchpair(12, 0, 0x8000000);
|
||||
matchpair(13, 0, 0x7ff8000000);
|
||||
matchpair(14, 0, ctu->loadbase);
|
||||
matchpair(15, 0, ctu->loadsize);
|
||||
matchpair(18, 0, 0x0100000000000036); // Title ID
|
||||
matchone(11, 0);
|
||||
|
||||
LOG_ERROR(Svc[0x29], "Unknown getinfo");
|
||||
}
|
||||
|
||||
tuple<guint, guint, guint> Svc::CreateSession(ghandle clientOut, ghandle serverOut, guint unk) {
|
||||
LOG_DEBUG(Svc[0x40], "CreateSession");
|
||||
auto pipe = make_shared<NPipe>(ctu, "Session");
|
||||
return make_tuple(0, ctu->newHandle(pipe), ctu->newHandle(pipe));
|
||||
}
|
||||
|
||||
tuple<guint, guint> Svc::AcceptSession(ghandle hnd) {
|
||||
LOG_DEBUG(Svc[0x41], "AcceptSession 0x%x", hnd);
|
||||
auto pipe = ctu->getHandle<NPort>(hnd)->accept();
|
||||
return make_tuple(0, ctu->newHandle(pipe));
|
||||
}
|
||||
|
||||
tuple<guint, guint> Svc::ReplyAndReceive(gptr handles, guint numHandles, ghandle replySession, guint timeout) {
|
||||
LOG_DEBUG(Svc[0x43], "ReplyAndReceive");
|
||||
|
||||
auto thread = ctu->tm.current();
|
||||
if(replySession != 0) {
|
||||
LOG_DEBUG(Svc[0x43], "Sending message back from native service");
|
||||
auto hnd = ctu->getHandle<NPipe>(replySession);
|
||||
auto buf = make_shared<array<uint8_t, 0x100>>();
|
||||
ctu->cpu.readmem(thread->tlsBase, buf->data(), 0x100);
|
||||
hnd->client.push(buf);
|
||||
hnd->client.signal(true);
|
||||
}
|
||||
|
||||
if(numHandles == 0)
|
||||
return make_tuple(0xf601, 0);
|
||||
|
||||
assert(numHandles == 1);
|
||||
auto handle = *ctu->cpu.guestptr<ghandle>(handles);
|
||||
auto hnd = ctu->getHandle<NPipe>(handle);
|
||||
|
||||
auto buf = hnd->server.pop();
|
||||
if(buf == nullptr) {
|
||||
ctu->deleteHandle(handle);
|
||||
return make_tuple(0xf601, 0);
|
||||
}
|
||||
LOG_DEBUG(Svc[0x43], "Got message for native service");
|
||||
hexdump(buf);
|
||||
ctu->cpu.writemem(thread->tlsBase, buf->data(), 0x100);
|
||||
|
||||
return make_tuple(0, 0);
|
||||
}
|
||||
|
||||
tuple<guint, guint, guint> Svc::CreateEvent(ghandle clientOut, ghandle serverOut, guint unk) {
|
||||
LOG_DEBUG(Svc[0x45], "CreateEvent");
|
||||
return make_tuple(0, 0, 0);
|
||||
}
|
||||
|
||||
tuple<guint, guint> Svc::ReadWriteRegister(guint reg, guint rwm, guint val) {
|
||||
LOG_DEBUG(Svc[0x4E], "ReadWriteRegister");
|
||||
return make_tuple(0, 0);
|
||||
}
|
||||
|
||||
tuple<guint, guint> Svc::CreateMemoryBlock(guint size, guint perm) {
|
||||
LOG_DEBUG(Svc[0x50], "CreateMemoryBlock");
|
||||
return make_tuple(0, ctu->newHandle(make_shared<MemoryBlock>(size, perm)));
|
||||
}
|
||||
|
||||
guint Svc::MapTransferMemory(ghandle handle, gptr addr, guint size, guint perm) {
|
||||
LOG_DEBUG(Svc[0x51], "MapTransferMemory");
|
||||
ctu->cpu.map(addr, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
guint Svc::UnmapTransferMemory(ghandle handle, gptr addr, guint size) {
|
||||
LOG_DEBUG(Svc[0x52], "UnmapTransferMemory");
|
||||
ctu->cpu.unmap(addr, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
tuple<guint, guint> Svc::CreateInterruptEvent(guint irq) {
|
||||
LOG_DEBUG(Svc[0x53], "CreateInterruptEvent");
|
||||
return make_tuple(0, 0);
|
||||
}
|
||||
|
||||
tuple<guint, guint> Svc::QueryIoMapping(gptr physaddr, guint size) {
|
||||
LOG_DEBUG(Svc[0x55], "QueryIoMapping");
|
||||
gptr addr = ctu->mmiohandler.getVirtualAddressFromAddr(physaddr);
|
||||
if(addr == 0x0) { // force exit for now
|
||||
cout << "!Unknown physical address!" << endl;
|
||||
exit(1);
|
||||
}
|
||||
return make_tuple(0x0, addr);
|
||||
}
|
||||
|
||||
tuple<guint, guint> Svc::CreateDeviceAddressSpace(guint base, guint size) {
|
||||
LOG_DEBUG(Svc[0x56], "CreateDeviceAddressSpace");
|
||||
return make_tuple(0, 0);
|
||||
}
|
||||
|
||||
tuple<guint, guint> Svc::AttachDeviceAddressSpace(ghandle handle, guint dev, gptr addr) {
|
||||
LOG_DEBUG(Svc[0x57], "AttachDeviceAddressSpace");
|
||||
return make_tuple(0, 0);
|
||||
}
|
||||
|
||||
tuple<guint, guint> Svc::MapDeviceAddressSpaceByForce(ghandle handle, ghandle phandle, gptr paddr, guint size, gptr maddr, guint perm) {
|
||||
LOG_DEBUG(Svc[0x59], "MapDeviceAddressSpaceByForce");
|
||||
return make_tuple(0, 0);
|
||||
}
|
||||
|
||||
guint Svc::UnmapDeviceAddressSpace(guint unk0, ghandle phandle, gptr maddr, guint size) {
|
||||
LOG_DEBUG(Svc[0x5c], "UnmapDeviceAddressSpace");
|
||||
return 0;
|
||||
}
|
||||
|
||||
guint Svc::MapProcessMemory(gptr dstaddr, ghandle handle, gptr srcaddr, guint size) {
|
||||
LOG_DEBUG(Svc[0x74], "MapProcessMemory");
|
||||
ctu->cpu.map(dstaddr, size);
|
||||
char *mem = (char *) malloc(size);
|
||||
memset(mem, 0, size);
|
||||
ctu->cpu.readmem(srcaddr, mem, size);
|
||||
ctu->cpu.writemem(dstaddr, mem, size);
|
||||
free(mem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
guint Svc::UnmapProcessMemory(gptr dstaddr, ghandle handle, gptr srcaddr, guint size) {
|
||||
LOG_DEBUG(Svc[0x75], "UnmapProcessMemory");
|
||||
ctu->cpu.unmap(dstaddr, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
guint Svc::MapProcessCodeMemory(ghandle handle, gptr dstaddr, gptr srcaddr, guint size) {
|
||||
LOG_DEBUG(Svc[0x77], "MapProcessCodeMemory");
|
||||
ctu->cpu.map(dstaddr, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
guint Svc::UnmapProcessCodeMemory(ghandle handle, gptr dstaddr, gptr srcaddr, guint size) {
|
||||
LOG_DEBUG(Svc[0x78], "UnmapProcessCodeMemory");
|
||||
ctu->cpu.unmap(dstaddr, size);
|
||||
return 0;
|
||||
}
|
78
Svc.h
Normal file
78
Svc.h
Normal file
|
@ -0,0 +1,78 @@
|
|||
#pragma once
|
||||
|
||||
#include "Ctu.h"
|
||||
|
||||
class MemoryBlock : public KObject {
|
||||
public:
|
||||
MemoryBlock(guint _size, guint _perms) : size(_size), perms(_perms), addr(0) {}
|
||||
|
||||
guint size, perms;
|
||||
gptr addr;
|
||||
};
|
||||
|
||||
class Svc {
|
||||
public:
|
||||
Svc(Ctu *ctu);
|
||||
|
||||
private:
|
||||
Ctu *ctu;
|
||||
unordered_map<gptr, shared_ptr<Semaphore>> semaphores;
|
||||
unordered_map<gptr, shared_ptr<Mutex>> mutexes;
|
||||
|
||||
shared_ptr<Mutex> ensureMutex(gptr mutexAddr);
|
||||
shared_ptr<Semaphore> ensureSemaphore(gptr semaAddr);
|
||||
|
||||
tuple<guint, guint> SetHeapSize(guint size); // 0x01
|
||||
guint SetMemoryAttribute(gptr addr, guint size, guint state0, guint state1); // 0x03
|
||||
guint MirrorStack(gptr dest, gptr src, guint size); // 0x04
|
||||
guint UnmapMemory(gptr dest, gptr src, guint size); // 0x05
|
||||
tuple<guint, guint> QueryMemory(gptr meminfo, gptr pageinfo, gptr addr); // 0x06
|
||||
void ExitProcess(); // 0x07
|
||||
tuple<guint, guint> CreateThread(guint pc, guint x0, guint sp, guint prio, guint proc); // 0x08
|
||||
guint StartThread(ghandle handle); // 0x09
|
||||
void ExitThread(); // 0x0A
|
||||
guint SleepThread(guint ns); // 0x0B
|
||||
tuple<guint, guint> GetThreadPriority(ghandle handle); // 0x0C
|
||||
guint SetThreadPriority(ghandle handle, guint priority); // 0x0D
|
||||
tuple<guint, guint, guint> GetThreadCoreMask(guint); // 0x0E
|
||||
guint SetThreadCoreMask(guint); // 0x0F
|
||||
guint GetCurrentProcessorNumber(guint); // 0x10
|
||||
guint SignalEvent(ghandle handle); // 0x11
|
||||
guint ClearEvent(ghandle handle); // 0x12
|
||||
guint MapMemoryBlock(ghandle handle, gptr addr, guint size, guint perm); // 0x13
|
||||
tuple<guint, guint> CreateTransferMemory(gptr addr, guint size, guint perm); // 0x15
|
||||
guint CloseHandle(ghandle handle); // 0x16
|
||||
guint ResetSignal(ghandle handle); // 0x17
|
||||
tuple<guint, guint> WaitSynchronization(gptr handles, guint numHandles, guint timeout); // 0x18
|
||||
guint CancelSynchronization(ghandle handle); // 0x19
|
||||
guint LockMutex(ghandle curthread, gptr mutexAddr, ghandle reqthread); // 0x1A
|
||||
void UnlockMutex(gptr mutexAddr); // 0x1B
|
||||
void WaitProcessWideKeyAtomic(gptr mutexAddr, gptr semaAddr, ghandle threadHandle, guint timeout); // 0x1C
|
||||
guint SignalProcessWideKey(gptr semaAddr, guint target); // 0x1D
|
||||
tuple<guint, ghandle> ConnectToPort(guint name); // 0x1F
|
||||
guint SendSyncRequest(ghandle handle); // 0x21
|
||||
guint SendSyncRequestEx(gptr buf, guint size, ghandle handle); // 0x22
|
||||
tuple<guint, guint> GetProcessID(ghandle handle); // 0x24
|
||||
tuple<guint, guint> GetThreadId(); // 0x25
|
||||
guint Break(guint X0, guint X1, guint info); // 0x26
|
||||
guint OutputDebugString(guint ptr, guint size); // 0x27
|
||||
tuple<guint, guint> GetInfo(guint id1, ghandle handle, guint id2); // 0x29
|
||||
tuple<guint, guint, guint> CreateSession(ghandle clientOut, ghandle serverOut, guint unk); // 0x40
|
||||
tuple<guint, guint> AcceptSession(ghandle port); // 0x41
|
||||
tuple<guint, guint> ReplyAndReceive(gptr handles, guint numHandles, ghandle replySession, guint timeout); // 0x43
|
||||
tuple<guint, guint, guint> CreateEvent(ghandle clientOut, ghandle serverOut, guint unk); // 0x45
|
||||
tuple<guint, guint> ReadWriteRegister(guint reg, guint rwm, guint val); // 0x4E
|
||||
tuple<guint, guint> CreateMemoryBlock(guint size, guint perm); // 0x50
|
||||
guint MapTransferMemory(ghandle handle, gptr addr, guint size, guint perm); // 0x51
|
||||
guint UnmapTransferMemory(ghandle handle, gptr addr, guint size); // 0x52
|
||||
tuple<guint, guint> CreateInterruptEvent(guint irq); // 0x53
|
||||
tuple<guint, guint> QueryIoMapping(gptr physaddr, guint size); // 0x55
|
||||
tuple<guint, guint> CreateDeviceAddressSpace(guint base, guint size); // 0x56
|
||||
tuple<guint, guint> AttachDeviceAddressSpace(ghandle handle, guint dev, gptr addr); // 0x57
|
||||
tuple<guint, guint> MapDeviceAddressSpaceByForce(ghandle handle, ghandle phandle, gptr paddr, guint size, gptr maddr, guint perm); // 0x59
|
||||
guint UnmapDeviceAddressSpace(guint unk0, ghandle phandle, gptr maddr, guint size); // 0x5c
|
||||
guint MapProcessMemory(gptr dstaddr, ghandle handle, gptr srcaddr, guint size); // 0x74
|
||||
guint UnmapProcessMemory(gptr dstaddr, ghandle handle, gptr srcaddr, guint size); // 0x75
|
||||
guint MapProcessCodeMemory(ghandle handle, gptr dstaddr, gptr srcaddr, guint size); // 0x77
|
||||
guint UnmapProcessCodeMemory(ghandle handle, gptr dstaddr, gptr srcaddr, guint size); // 0x78
|
||||
};
|
105
Sync.cpp
Normal file
105
Sync.cpp
Normal file
|
@ -0,0 +1,105 @@
|
|||
#include "Ctu.h"
|
||||
|
||||
Waitable::Waitable() : presignaled(false), canceled(false) {
|
||||
}
|
||||
|
||||
void Waitable::acquire() {
|
||||
lock.lock();
|
||||
}
|
||||
|
||||
void Waitable::release() {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
void Waitable::wait(function<int()> cb) {
|
||||
wait([cb](auto _) { return cb(); });
|
||||
}
|
||||
|
||||
void Waitable::wait(function<int(bool)> cb) {
|
||||
acquire();
|
||||
if(!presignaled || (presignaled && cb(canceled) == 0))
|
||||
waiters.push_back(cb);
|
||||
presignaled = false;
|
||||
canceled = false;
|
||||
release();
|
||||
}
|
||||
|
||||
void Waitable::signal(bool one) {
|
||||
acquire();
|
||||
if(waiters.size() == 0 && presignalable())
|
||||
presignaled = true;
|
||||
else {
|
||||
auto realhit = false;
|
||||
for(auto iter = waiters.begin(); iter != waiters.end();) {
|
||||
auto res = (*iter)(canceled);
|
||||
if(res != 0) {
|
||||
iter = waiters.erase(iter);
|
||||
} else {
|
||||
iter++;
|
||||
}
|
||||
if(res != -1) {
|
||||
realhit = true;
|
||||
if(one)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!realhit && presignalable())
|
||||
presignaled = true;
|
||||
}
|
||||
release();
|
||||
}
|
||||
|
||||
void Waitable::cancel() {
|
||||
acquire();
|
||||
canceled = true;
|
||||
signal();
|
||||
release();
|
||||
}
|
||||
|
||||
Semaphore::Semaphore(Guest<uint32_t> _vptr) : vptr(_vptr) {
|
||||
}
|
||||
|
||||
void Semaphore::increment() {
|
||||
acquire();
|
||||
vptr = *vptr + 1;
|
||||
release();
|
||||
}
|
||||
|
||||
void Semaphore::decrement() {
|
||||
acquire();
|
||||
vptr = *vptr - 1;
|
||||
release();
|
||||
}
|
||||
|
||||
uint32_t Semaphore::value() {
|
||||
return *vptr;
|
||||
}
|
||||
|
||||
Mutex::Mutex(Guest<uint32_t> _vptr) : vptr(_vptr) {
|
||||
}
|
||||
|
||||
uint32_t Mutex::value() {
|
||||
return *vptr;
|
||||
}
|
||||
void Mutex::value(uint32_t val) {
|
||||
vptr = val;
|
||||
}
|
||||
|
||||
ghandle Mutex::owner() {
|
||||
return value() & 0xBFFFFFFF;
|
||||
}
|
||||
void Mutex::owner(ghandle val) {
|
||||
value((value() & 0x40000000) | val);
|
||||
}
|
||||
|
||||
bool Mutex::hasWaiters() {
|
||||
return (value() >> 28) != 0;
|
||||
}
|
||||
void Mutex::hasWaiters(bool val) {
|
||||
value((value() & 0xBFFFFFFF) | ((int) val << 30));
|
||||
}
|
||||
|
||||
void Mutex::guestRelease() {
|
||||
owner(0);
|
||||
signal();
|
||||
}
|
65
Sync.h
Normal file
65
Sync.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
#pragma once
|
||||
|
||||
#include "Ctu.h"
|
||||
|
||||
class Waitable : public KObject {
|
||||
public:
|
||||
Waitable();
|
||||
|
||||
void acquire();
|
||||
void release();
|
||||
|
||||
void wait(function<int()> cb);
|
||||
void wait(function<int(bool)> cb);
|
||||
|
||||
void signal(bool one=false);
|
||||
void cancel();
|
||||
|
||||
virtual void close() override {}
|
||||
|
||||
protected:
|
||||
virtual bool presignalable() { return true; }
|
||||
|
||||
private:
|
||||
recursive_mutex lock;
|
||||
list<function<int(bool)>> waiters;
|
||||
bool presignaled, canceled;
|
||||
};
|
||||
|
||||
class Semaphore : public Waitable {
|
||||
public:
|
||||
Semaphore(Guest<uint32_t> _vptr);
|
||||
|
||||
void increment();
|
||||
void decrement();
|
||||
|
||||
uint32_t value();
|
||||
|
||||
protected:
|
||||
bool presignalable() override { return false; }
|
||||
|
||||
private:
|
||||
Guest<uint32_t> vptr;
|
||||
};
|
||||
|
||||
class Mutex : public Waitable {
|
||||
public:
|
||||
Mutex(Guest<uint32_t> _vptr);
|
||||
|
||||
uint32_t value();
|
||||
void value(uint32_t val);
|
||||
|
||||
ghandle owner();
|
||||
void owner(ghandle val);
|
||||
|
||||
bool hasWaiters();
|
||||
void hasWaiters(bool val);
|
||||
|
||||
void guestRelease();
|
||||
|
||||
protected:
|
||||
bool presignalable() override { return false; }
|
||||
|
||||
private:
|
||||
Guest<uint32_t> vptr;
|
||||
};
|
265
ThreadManager.cpp
Normal file
265
ThreadManager.cpp
Normal file
|
@ -0,0 +1,265 @@
|
|||
#include "Ctu.h"
|
||||
|
||||
#define INSN_PER_SLICE 1000000
|
||||
|
||||
Thread::Thread(Ctu *_ctu, int _id) : ctu(_ctu), id(_id) {
|
||||
active = false;
|
||||
memset(®s, 0, sizeof(ThreadRegisters));
|
||||
|
||||
auto tlsSize = 1024 * 1024;
|
||||
tlsBase = (1 << 24) + tlsSize * _id;
|
||||
ctu->cpu.map(tlsBase, tlsSize);
|
||||
ctu->cpu.guestptr<gptr>(tlsBase + 0x1F8) = tlsBase + 0x200;
|
||||
}
|
||||
|
||||
void Thread::assignHandle(uint32_t _handle) {
|
||||
handle = _handle;
|
||||
ctu->cpu.guestptr<uint32_t>(tlsBase + 0x3B8) = _handle;
|
||||
}
|
||||
|
||||
void Thread::terminate() {
|
||||
ctu->tm.terminate(id);
|
||||
}
|
||||
|
||||
void Thread::suspend(function<void()> cb) {
|
||||
if(!active)
|
||||
return;
|
||||
|
||||
active = false;
|
||||
if(id == ctu->tm.current()->id) {
|
||||
freeze();
|
||||
if(cb != nullptr)
|
||||
cb();
|
||||
ctu->tm.next(true);
|
||||
} else if(cb != nullptr)
|
||||
cb();
|
||||
}
|
||||
|
||||
void Thread::resume(function<void()> cb) {
|
||||
if(active)
|
||||
return;
|
||||
|
||||
if(cb != nullptr)
|
||||
onWake(cb);
|
||||
active = true;
|
||||
ctu->tm.enqueue(id);
|
||||
}
|
||||
|
||||
void Thread::freeze() {
|
||||
ctu->cpu.storeRegs(regs);
|
||||
LOG_DEBUG(Thread, "Froze thread 0x%x with PC 0x" ADDRFMT, id, regs.PC);
|
||||
}
|
||||
|
||||
void Thread::thaw() {
|
||||
ctu->cpu.tlsBase(tlsBase);
|
||||
if(wakeCallbacks.size()) {
|
||||
for(auto cb : wakeCallbacks)
|
||||
cb();
|
||||
wakeCallbacks.clear();
|
||||
}
|
||||
ctu->cpu.loadRegs(regs);
|
||||
LOG_DEBUG(Thread, "Thawed thread 0x%x with PC 0x" ADDRFMT, id, regs.PC);
|
||||
}
|
||||
|
||||
void Thread::onWake(function<void()> cb) {
|
||||
wakeCallbacks.push_back(cb);
|
||||
}
|
||||
|
||||
NativeThread::NativeThread(Ctu *_ctu, function<void()> _runner, int _id) : ctu(_ctu), runner(_runner), id(_id), active(false) {
|
||||
}
|
||||
|
||||
void NativeThread::terminate() {
|
||||
ctu->tm.terminate(id);
|
||||
}
|
||||
|
||||
void NativeThread::suspend() {
|
||||
active = false;
|
||||
}
|
||||
|
||||
void NativeThread::resume() {
|
||||
if(active)
|
||||
return;
|
||||
|
||||
active = true;
|
||||
ctu->tm.enqueue(id);
|
||||
}
|
||||
|
||||
void NativeThread::run() {
|
||||
runner();
|
||||
}
|
||||
|
||||
ThreadManager::ThreadManager(Ctu *_ctu) : ctu(_ctu) {
|
||||
threadId = 0;
|
||||
_current = nullptr;
|
||||
_last = nullptr;
|
||||
switched = false;
|
||||
first = true;
|
||||
wasNativeLast = false;
|
||||
}
|
||||
|
||||
void ThreadManager::start() {
|
||||
next();
|
||||
while(true) {
|
||||
if(ctu->gdbStub.enabled) {
|
||||
ctu->gdbStub.handlePacket();
|
||||
if(ctu->gdbStub.haltLoop && !ctu->gdbStub.stepLoop)
|
||||
continue;
|
||||
auto wasStep = ctu->gdbStub.stepLoop;
|
||||
ctu->gdbStub.haltLoop = ctu->gdbStub.stepLoop = false;
|
||||
if(_current == nullptr) {
|
||||
next();
|
||||
continue;
|
||||
}
|
||||
ctu->cpu.exec(ctu->gdbStub.stepLoop ? 1 : INSN_PER_SLICE);
|
||||
if(wasStep) {
|
||||
ctu->gdbStub._break();
|
||||
ctu->gdbStub.haltLoop = ctu->gdbStub.stepLoop = false;
|
||||
}
|
||||
} else {
|
||||
next();
|
||||
ctu->cpu.exec(INSN_PER_SLICE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<Thread> ThreadManager::create(gptr pc, gptr sp) {
|
||||
auto thread = make_shared<Thread>(ctu, ++threadId);
|
||||
thread->assignHandle(ctu->newHandle(thread));
|
||||
threads[thread->id] = thread;
|
||||
thread->regs.PC = pc;
|
||||
thread->regs.SP = sp;
|
||||
thread->regs.X30 = TERMADDR;
|
||||
return thread;
|
||||
}
|
||||
|
||||
shared_ptr<NativeThread> ThreadManager::createNative(function<void()> runner) {
|
||||
auto thread = make_shared<NativeThread>(ctu, runner, ++threadId);
|
||||
nativeThreads[thread->id] = thread;
|
||||
return thread;
|
||||
}
|
||||
|
||||
void ThreadManager::requeue() {
|
||||
if(_current == nullptr)
|
||||
return;
|
||||
|
||||
_current->freeze();
|
||||
running.push_back(_current);
|
||||
_last = _current;
|
||||
_current = nullptr;
|
||||
}
|
||||
|
||||
void ThreadManager::enqueue(int id) {
|
||||
if(isNative(id)) {
|
||||
for(auto other : runningNative)
|
||||
if(other->id == id)
|
||||
return;
|
||||
runningNative.push_back(nativeThreads[id]);
|
||||
return;
|
||||
}
|
||||
if(threads.find(id) == threads.end())
|
||||
return;
|
||||
enqueue(threads[id]);
|
||||
}
|
||||
|
||||
void ThreadManager::enqueue(shared_ptr<Thread> thread) {
|
||||
for(auto other : running)
|
||||
if(other->id == thread->id)
|
||||
return;
|
||||
running.push_back(thread);
|
||||
}
|
||||
|
||||
void ThreadManager::tryRunNative() {
|
||||
if(wasNativeLast)
|
||||
wasNativeLast = false;
|
||||
else {
|
||||
while(runningNative.size() > 0) {
|
||||
auto native = runningNative.front();
|
||||
runningNative.pop_front();
|
||||
if(!native->active)
|
||||
continue;
|
||||
native->run();
|
||||
if(native->active)
|
||||
runningNative.push_back(native);
|
||||
break;
|
||||
}
|
||||
wasNativeLast = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadManager::next(bool force) {
|
||||
tryRunNative();
|
||||
|
||||
if(force) {
|
||||
if(_current != nullptr)
|
||||
_last = _current;
|
||||
_current = nullptr;
|
||||
}
|
||||
|
||||
shared_ptr<Thread> nt = nullptr;
|
||||
while(true) {
|
||||
if(running.size() == 0) {
|
||||
tryRunNative();
|
||||
if(ctu->gdbStub.enabled) {
|
||||
if(ctu->gdbStub.haltLoop) {
|
||||
ctu->cpu.stop();
|
||||
return;
|
||||
}
|
||||
ctu->gdbStub.handlePacket();
|
||||
if(ctu->gdbStub.haltLoop) {
|
||||
ctu->cpu.stop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(_current == nullptr) {
|
||||
if(first) {
|
||||
LOG_DEBUG(Thread, "No threads left to run!");
|
||||
ctu->bridge.start();
|
||||
}
|
||||
first = false;
|
||||
} else
|
||||
return;
|
||||
usleep(50);
|
||||
continue;
|
||||
}
|
||||
nt = running.front();
|
||||
running.pop_front();
|
||||
if(nt->active)
|
||||
break;
|
||||
}
|
||||
|
||||
switched = true;
|
||||
|
||||
if(_current != nullptr) {
|
||||
_current->freeze();
|
||||
if(_current->active)
|
||||
enqueue(_current);
|
||||
_last = _current;
|
||||
}
|
||||
nt->thaw();
|
||||
_current = nt;
|
||||
}
|
||||
|
||||
void ThreadManager::terminate(int id) {
|
||||
if(threads.find(id) == threads.end()) {
|
||||
if(isNative(id)) {
|
||||
nativeThreads[id]->active = false;
|
||||
nativeThreads.erase(id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
threads[id]->active = false;
|
||||
threads.erase(id);
|
||||
next(true);
|
||||
}
|
||||
|
||||
shared_ptr<Thread> ThreadManager::current() {
|
||||
return _current;
|
||||
}
|
||||
|
||||
shared_ptr<Thread> ThreadManager::last() {
|
||||
return _last;
|
||||
}
|
||||
|
||||
bool ThreadManager::isNative(int id) {
|
||||
return nativeThreads.find(id) != nativeThreads.end();
|
||||
}
|
106
ThreadManager.h
Normal file
106
ThreadManager.h
Normal file
|
@ -0,0 +1,106 @@
|
|||
#pragma once
|
||||
#include "Ctu.h"
|
||||
|
||||
typedef struct {
|
||||
float64_t low;
|
||||
float64_t high;
|
||||
} float128;
|
||||
|
||||
typedef struct {
|
||||
guint SP, PC, NZCV;
|
||||
union {
|
||||
struct {
|
||||
guint gprs[31];
|
||||
float128 fprs[32];
|
||||
};
|
||||
struct {
|
||||
guint X0, X1, X2, X3,
|
||||
X4, X5, X6, X7,
|
||||
X8, X9, X10, X11,
|
||||
X12, X13, X14, X15,
|
||||
X16, X17, X18, X19,
|
||||
X20, X21, X22, X23,
|
||||
X24, X25, X26, X27,
|
||||
X28, X29, X30;
|
||||
float128 Q0, Q1, Q2, Q3,
|
||||
Q4, Q5, Q6, Q7,
|
||||
Q8, Q9, Q10, Q11,
|
||||
Q12, Q13, Q14, Q15,
|
||||
Q16, Q17, Q18, Q19,
|
||||
Q20, Q21, Q22, Q23,
|
||||
Q24, Q25, Q26, Q27,
|
||||
Q28, Q29, Q30, Q31;
|
||||
};
|
||||
};
|
||||
} ThreadRegisters;
|
||||
|
||||
class Thread : public KObject {
|
||||
public:
|
||||
Thread(Ctu *_ctu, int _id);
|
||||
void assignHandle(ghandle handle);
|
||||
void terminate();
|
||||
void suspend(function<void()> cb=nullptr);
|
||||
void resume(function<void()> cb=nullptr);
|
||||
void freeze();
|
||||
void thaw();
|
||||
|
||||
void onWake(function<void()> cb);
|
||||
|
||||
int id;
|
||||
ghandle handle;
|
||||
bool active;
|
||||
ThreadRegisters regs;
|
||||
gptr tlsBase;
|
||||
|
||||
private:
|
||||
Ctu *ctu;
|
||||
list<function<void()>> wakeCallbacks;
|
||||
};
|
||||
|
||||
class NativeThread {
|
||||
public:
|
||||
NativeThread(Ctu *_ctu, function<void()> _runner, int _id);
|
||||
void terminate();
|
||||
void suspend();
|
||||
void resume();
|
||||
|
||||
void run();
|
||||
|
||||
int id;
|
||||
bool active;
|
||||
|
||||
private:
|
||||
Ctu *ctu;
|
||||
function<void()> runner;
|
||||
};
|
||||
|
||||
class ThreadManager {
|
||||
public:
|
||||
ThreadManager(Ctu *_ctu);
|
||||
void start();
|
||||
shared_ptr<Thread> create(gptr pc=0, gptr sp=0);
|
||||
shared_ptr<NativeThread> createNative(function<void()> runner);
|
||||
void requeue();
|
||||
void enqueue(int id);
|
||||
void enqueue(shared_ptr<Thread> thread);
|
||||
void next(bool force=false);
|
||||
void terminate(int id);
|
||||
shared_ptr<Thread> current();
|
||||
shared_ptr<Thread> last();
|
||||
|
||||
bool switched;
|
||||
|
||||
private:
|
||||
void tryRunNative();
|
||||
bool isNative(int id);
|
||||
|
||||
Ctu *ctu;
|
||||
unordered_map<int, shared_ptr<Thread>> threads;
|
||||
unordered_map<int, shared_ptr<NativeThread>> nativeThreads;
|
||||
int threadId;
|
||||
list<shared_ptr<Thread>> running;
|
||||
list<shared_ptr<NativeThread>> runningNative;
|
||||
shared_ptr<Thread> _current, _last;
|
||||
bool wasNativeLast;
|
||||
bool first;
|
||||
};
|
2548
genallipc.py
Normal file
2548
genallipc.py
Normal file
File diff suppressed because it is too large
Load diff
394
generateIpcStubs.py
Normal file
394
generateIpcStubs.py
Normal file
|
@ -0,0 +1,394 @@
|
|||
import glob, hashlib, json, os, os.path, re, sys
|
||||
from pprint import pprint
|
||||
import idparser, partialparser
|
||||
|
||||
def emitInt(x):
|
||||
return '0x%x' % x if x > 9 else str(x)
|
||||
|
||||
typemap = dict(
|
||||
i8='int8_t',
|
||||
i16='int16_t',
|
||||
i32='int32_t',
|
||||
i64='int64_t',
|
||||
i128='int128_t',
|
||||
u8='uint8_t',
|
||||
u16='uint16_t',
|
||||
u32='uint32_t',
|
||||
u64='uint64_t',
|
||||
u128='uint128_t',
|
||||
f32='float32_t',
|
||||
pid='gpid',
|
||||
bool='bool',
|
||||
)
|
||||
|
||||
typesizes = dict(
|
||||
i8=1,
|
||||
i16=2,
|
||||
i32=4,
|
||||
i64=8,
|
||||
i128=16,
|
||||
u8=1,
|
||||
u16=2,
|
||||
u32=4,
|
||||
u64=8,
|
||||
u128=16,
|
||||
f32=4,
|
||||
pid=8,
|
||||
bool=1,
|
||||
)
|
||||
|
||||
allTypes = None
|
||||
|
||||
def typeSize(type):
|
||||
if type[0] in ('unknown', 'i8', 'u8'):
|
||||
return 1
|
||||
elif type[0] == 'bytes':
|
||||
return type[1]
|
||||
elif type[0] in allTypes:
|
||||
return typeSize(allTypes[type[0]])
|
||||
elif type[0] in typesizes:
|
||||
return typesizes[type[0]]
|
||||
return 1
|
||||
|
||||
def splitByNs(obj):
|
||||
ons = {}
|
||||
for type, x in obj.items():
|
||||
ns = type.rsplit('::', 1)[0] if '::' in type else None
|
||||
name = type.rsplit('::', 1)[-1]
|
||||
if ns not in ons:
|
||||
ons[ns] = {}
|
||||
ons[ns][name] = x
|
||||
return ons
|
||||
|
||||
def retype(spec, noIndex=False):
|
||||
if spec[0] == 'unknown':
|
||||
return 'uint8_t'
|
||||
elif spec[0] == 'bytes':
|
||||
return 'uint8_t%s' % ('[%s]' % emitInt(spec[1]) if not noIndex else ' *')
|
||||
else:
|
||||
return typemap[spec[0]] if spec[0] in typemap else spec[0];
|
||||
|
||||
def formatParam(param, input, i):
|
||||
name, spec = param
|
||||
if name is None:
|
||||
name = '_%i' % i
|
||||
|
||||
hasSize = False
|
||||
|
||||
if spec[0] == 'align':
|
||||
return formatParam((name, spec[2]), input, i)
|
||||
|
||||
if spec[0] == 'bytes':
|
||||
type = 'uint8_t *'
|
||||
elif spec[0] == 'unknown':
|
||||
assert False
|
||||
elif spec[0] == 'buffer':
|
||||
type = '%s *' % retype(spec[1])
|
||||
hasSize = True
|
||||
elif spec[0] == 'array':
|
||||
type = retype(spec[1]) + ' *'
|
||||
hasSize = True
|
||||
elif spec[0] == 'object':
|
||||
type = 'shared_ptr<%s>' % spec[1][0]
|
||||
elif spec[0] == 'KObject':
|
||||
type = 'shared_ptr<KObject>'
|
||||
else:
|
||||
type = typemap[spec[0]] if spec[0] in typemap else spec[0]
|
||||
|
||||
if type.endswith(']'):
|
||||
arrspec = type[type.index('['):]
|
||||
type = type[:-len(arrspec)]
|
||||
else:
|
||||
arrspec = ''
|
||||
|
||||
return '%s %s%s %s%s%s' % ('IN' if input else 'OUT', type, '&' if not input and (not type.endswith('*') and not arrspec) else '', name, arrspec, ', guint %s_size' % name if hasSize else '')
|
||||
|
||||
def generatePrototype(func):
|
||||
return ', '.join([formatParam(x, True, i) for i, x in enumerate(func['inputs'])] + [formatParam(x, False, i + len(func['inputs'])) for i, x in enumerate(func['outputs'])])
|
||||
|
||||
def isPointerType(type):
|
||||
if type[0] in typesizes:
|
||||
return False
|
||||
elif type[0] == 'bytes':
|
||||
return True
|
||||
elif type[0] in allTypes:
|
||||
return isPointerType(allTypes[type[0]])
|
||||
return True
|
||||
|
||||
AFTER = 'AFTER'
|
||||
|
||||
def generateCaller(qname, fname, func):
|
||||
def tempname():
|
||||
tempI[0] += 1
|
||||
return 'temp%i' % tempI[0]
|
||||
params = []
|
||||
logFmt, logElems = [], []
|
||||
tempI = [0]
|
||||
inpOffset = 8
|
||||
bufOffs = {}
|
||||
hndOff = 0
|
||||
objOff = 0
|
||||
bufSizes = 0
|
||||
for name, elem in func['inputs']:
|
||||
type, rest = elem[0], elem[1:]
|
||||
if type in ('array', 'buffer'):
|
||||
if rest[1] not in bufOffs:
|
||||
bufOffs[rest[1]] = 0
|
||||
cbo = bufOffs[rest[1]]
|
||||
bufOffs[rest[1]] += 1
|
||||
an, sn, bn = tempname(), tempname(), tempname()
|
||||
yield 'guint %s;' % sn
|
||||
yield 'auto %s = req.getBuffer(%s, %i, %s);' % (an, emitInt(rest[1]), cbo, sn)
|
||||
yield 'auto %s = new uint8_t[%s];' % (bn, sn)
|
||||
yield 'ctu->cpu.readmem(%s, %s, %s);' % (an, bn, sn)
|
||||
params.append('(%s *) %s' % (retype(rest[0]), bn))
|
||||
params.append(sn)
|
||||
logFmt.append('%s *%s= buffer<0x" ADDRFMT ">' % (retype(rest[0]), '%s ' % name if name else ''))
|
||||
logElems.append(sn)
|
||||
bufSizes += 1
|
||||
yield AFTER, 'delete[] %s;' % bn
|
||||
elif type == 'object':
|
||||
params.append('ctu->getHandle<%s>(req.getMoved(%i))' % (rest[0][0], objOff))
|
||||
logFmt.append('%s %s= 0x%%x' % (rest[0][0], '%s ' % name if name else ''))
|
||||
logElems.append('req.getMoved(%i)' % objOff)
|
||||
objOff += 1
|
||||
elif type == 'KObject':
|
||||
params.append('ctu->getHandle<KObject>(req.getCopied(%i))' % hndOff)
|
||||
logFmt.append('KObject %s= 0x%%x' % ('%s ' % name if name else ''))
|
||||
logElems.append('req.getCopied(%i)' % objOff)
|
||||
hndOff += 1
|
||||
elif type == 'pid':
|
||||
params.append('req.pid')
|
||||
else:
|
||||
if elem[0] == 'align':
|
||||
alignment = elem[1]
|
||||
elem = elem[2]
|
||||
else:
|
||||
alignment = min(8, typeSize(elem))
|
||||
while inpOffset % alignment:
|
||||
inpOffset += 1
|
||||
if isPointerType(elem):
|
||||
params.append('req.getDataPointer<%s>(%s)' % (retype(elem, noIndex=True), emitInt(inpOffset)))
|
||||
logFmt.append('%s %s= %%s' % (retype(elem), '%s ' % name if name else ''))
|
||||
logElems.append('bufferToString(req.getDataPointer<uint8_t *>(%s), %s).c_str()' % (emitInt(inpOffset), emitInt(typeSize(elem))))
|
||||
else:
|
||||
params.append('req.getData<%s>(%s)' % (retype(elem), emitInt(inpOffset)))
|
||||
if typeSize(elem) == 16:
|
||||
logFmt.append('%s %s= %%s' % (retype(elem), '%s ' % name if name else ''))
|
||||
logElems.append('bufferToString(req.getDataPointer<uint8_t *>(%s), %s).c_str()' % (emitInt(inpOffset), emitInt(typeSize(elem))))
|
||||
else:
|
||||
type = retype(elem)
|
||||
ct = '0x%x'
|
||||
if type == 'float32_t':
|
||||
ct = '%f'
|
||||
elif typeSize(elem) == 8:
|
||||
ct = '0x" ADDRFMT "'
|
||||
logFmt.append('%s %s= %s' % (type, '%s ' % name if name else '', ct))
|
||||
logElems.append('%sreq.getData<%s>(%s)' % ('(double) ' if type == 'float32_t' else '', type, emitInt(inpOffset)))
|
||||
inpOffset += typeSize(elem)
|
||||
|
||||
outOffset = 8
|
||||
hndOff = 0
|
||||
objOff = 0
|
||||
for _, elem in func['outputs']:
|
||||
type, rest = elem[0], elem[1:]
|
||||
if type in ('array', 'buffer'):
|
||||
if rest[1] not in bufOffs:
|
||||
bufOffs[rest[1]] = 0
|
||||
cbo = bufOffs[rest[1]]
|
||||
bufOffs[rest[1]] += 1
|
||||
an, sn, bn = tempname(), tempname(), tempname()
|
||||
yield 'guint %s;' % sn
|
||||
yield 'auto %s = req.getBuffer(%s, %i, %s);' % (an, emitInt(rest[1]), cbo, sn)
|
||||
yield 'auto %s = new uint8_t[%s];' % (bn, sn)
|
||||
params.append('(%s *) %s' % (retype(rest[0]), bn))
|
||||
params.append(sn)
|
||||
bufSizes += 1
|
||||
yield AFTER, 'ctu->cpu.writemem(%s, %s, %s);' % (an, bn, sn)
|
||||
yield AFTER, 'delete[] %s;' % bn
|
||||
elif type == 'object':
|
||||
tn = tempname()
|
||||
yield 'shared_ptr<%s> %s;' % (rest[0][0], tn)
|
||||
params.append(tn)
|
||||
yield AFTER, 'if(%s != nullptr)' % tn
|
||||
yield AFTER, '\tresp.move(%i, createHandle(%s));' % (objOff, tn)
|
||||
objOff += 1
|
||||
elif type == 'KObject':
|
||||
tn = tempname()
|
||||
yield 'shared_ptr<KObject> %s;' % tn
|
||||
params.append(tn)
|
||||
yield AFTER, 'if(%s != nullptr)' % tn
|
||||
yield AFTER, '\tresp.copy(%i, ctu->newHandle(%s));' % (hndOff, tn)
|
||||
hndOff += 1
|
||||
elif type == 'pid':
|
||||
assert False
|
||||
else:
|
||||
if elem[0] == 'align':
|
||||
alignment = elem[1]
|
||||
elem = elem[2]
|
||||
else:
|
||||
alignment = min(8, typeSize(elem))
|
||||
while outOffset % alignment:
|
||||
outOffset += 1
|
||||
if isPointerType(elem):
|
||||
tn = tempname()
|
||||
yield 'auto %s = resp.getDataPointer<%s>(%s);' % (tn, retype(elem, noIndex=True), emitInt(outOffset))
|
||||
params.append(tn)
|
||||
else:
|
||||
params.append('*resp.getDataPointer<%s *>(%s)' % (retype(elem), emitInt(outOffset)))
|
||||
outOffset += typeSize(elem)
|
||||
|
||||
if len(func['outputs']) + len(func['inputs']) + bufSizes != len(params):
|
||||
yield 'return 0xf601;'
|
||||
return
|
||||
|
||||
yield 'resp.initialize(%i, %i, %i);' % (objOff, hndOff, outOffset - 8)
|
||||
if len(logFmt):
|
||||
yield 'LOG_DEBUG(IpcStubs, "IPC message to %s: %s"%s);' % (qname + '::' + fname, ', '.join(logFmt), (', ' + ', '.join(logElems)) if logElems else '')
|
||||
else:
|
||||
yield 'LOG_DEBUG(IpcStubs, "IPC message to %s");' % (qname + '::' + fname)
|
||||
yield 'resp.errCode = %s(%s);' % (fname, ', '.join(params))
|
||||
yield AFTER
|
||||
yield 'return 0;'
|
||||
|
||||
def reorder(gen):
|
||||
after = []
|
||||
for x in gen:
|
||||
if x == AFTER:
|
||||
for elem in after:
|
||||
yield elem
|
||||
elif isinstance(x, tuple) and x[0] == AFTER:
|
||||
after.append(x[1])
|
||||
else:
|
||||
yield x
|
||||
|
||||
def parsePartials(code):
|
||||
code = '\n'.join(re.findall(r'/\*\$IPC\$(.*?)\*/', code, re.M|re.S))
|
||||
return partialparser.parse(code)
|
||||
|
||||
usedInts = []
|
||||
def uniqInt(*args):
|
||||
args = ''.join(map(str, args))
|
||||
i = int(hashlib.md5(args).hexdigest()[:8], 16)
|
||||
while True:
|
||||
if i not in usedInts:
|
||||
usedInts.append(i)
|
||||
return i
|
||||
i += 1
|
||||
|
||||
def main():
|
||||
global allTypes
|
||||
|
||||
fns = ['ipcdefs/auto.id'] + [x for x in glob.glob('ipcdefs/*.id') if x != 'ipcdefs/auto.id']
|
||||
|
||||
if os.path.exists('ipcdefs/cache') and all(os.path.getmtime('ipcdefs/cache') > os.path.getmtime(x) for x in fns):
|
||||
res = json.load(file('ipcdefs/cache'))
|
||||
else:
|
||||
res = idparser.parse('\n'.join(file(fn).read() for fn in fns))
|
||||
with file('ipcdefs/cache', 'w') as fp:
|
||||
json.dump(res, fp)
|
||||
types, ifaces, services = res
|
||||
|
||||
allTypes = types
|
||||
|
||||
typesByNs = splitByNs(types)
|
||||
ifacesByNs = splitByNs(ifaces)
|
||||
|
||||
namespaces = {x : [] for x in typesByNs.keys() + ifacesByNs.keys()}
|
||||
|
||||
for ns, types in typesByNs.items():
|
||||
for name, spec in sorted(types.items(), key=lambda x: x[0]):
|
||||
retyped, plain = retype(spec, noIndex=True), retype(spec)
|
||||
namespaces[ns].append('using %s = %s;%s' % (name, retyped, ' // ' + plain if retyped != plain else ''))
|
||||
|
||||
for ns, ifaces in ifacesByNs.items():
|
||||
for name in sorted(ifaces.keys()):
|
||||
namespaces[ns].append('class %s;' % name)
|
||||
|
||||
with file('IpcStubs.h', 'w') as fp:
|
||||
print >>fp, '#pragma once'
|
||||
print >>fp, '#include "Ctu.h"'
|
||||
print >>fp
|
||||
|
||||
print >>fp, '#define SERVICE_MAPPING() do { \\'
|
||||
for iname, snames in sorted(services.items(), key=lambda x: x[0]):
|
||||
for sname in snames:
|
||||
print >>fp, '\tSERVICE("%s", %s); \\' % (sname, iname)
|
||||
print >>fp, '} while(0)'
|
||||
print >>fp
|
||||
|
||||
for ns, elems in sorted(namespaces.items(), key=lambda x: x[0]):
|
||||
if ns is not None:
|
||||
print >>fp, 'namespace %s {' % ns
|
||||
hasUsing = False
|
||||
for elem in elems:
|
||||
if not hasUsing and elem.startswith('using'):
|
||||
hasUsing = True
|
||||
elif hasUsing and elem.startswith('class'):
|
||||
print >>fp
|
||||
hasUsing = False
|
||||
print >>fp, ('\t' if ns is not None else '') + elem
|
||||
if ns is not None:
|
||||
print >>fp, '}'
|
||||
|
||||
print >>fp
|
||||
|
||||
allcode = '\n'.join(file(fn, 'r').read() for fn in glob.glob('ipcimpl/*.cpp'))
|
||||
|
||||
partials = parsePartials(allcode)
|
||||
|
||||
for ns, ifaces in sorted(ifacesByNs.items(), key=lambda x: x[0]):
|
||||
print >>fp, '%snamespace %s {' % ('//// ' if ns is None else '', ns)
|
||||
for name, funcs in sorted(ifaces.items(), key=lambda x: x[0]):
|
||||
qname = '%s::%s' % (ns, name) if ns else name
|
||||
partial = partials[qname] if qname in partials else None
|
||||
print >>fp, '\tclass %s : public IpcService {' % name
|
||||
print >>fp, '\tpublic:'
|
||||
if re.search('(^|[^a-zA-Z0-9:])%s::%s[^a-zA-Z0-9:]' % (qname, name), allcode):
|
||||
print >>fp, '\t\t%s(Ctu *_ctu%s);' % (name, ', ' + ', '.join('%s _%s' % (k, v) for k, v in partial[1]) if partial and partial[1] else '')
|
||||
else:
|
||||
print >>fp, '\t\t%s(Ctu *_ctu%s) : IpcService(_ctu)%s {}' % (name, ', ' + ', '.join('%s _%s' % (k, v) for k, v in partial[1]) if partial and partial[1] else '', ', ' + ', '.join('%s(_%s)' % (v, v) for k, v in partial[1]) if partial and partial[1] else '')
|
||||
print >>fp, '\t\tuint32_t dispatch(IncomingIpcMessage &req, OutgoingIpcMessage &resp) {'
|
||||
print >>fp, '\t\t\tswitch(req.cmdId) {'
|
||||
for fname, func in sorted(funcs.items(), key=lambda x: x[1]['cmdId']):
|
||||
print >>fp, '\t\t\tcase %i: {' % func['cmdId'];
|
||||
print >>fp, '\n'.join('\t\t\t\t' + x for x in reorder(generateCaller(qname, fname, func)))
|
||||
print >>fp, '\t\t\t}'
|
||||
print >>fp, '\t\t\tdefault:'
|
||||
print >>fp, '\t\t\t\tLOG_ERROR(IpcStubs, "Unknown message cmdId %%u to interface %s", req.cmdId);' % ('%s::%s' % (ns, name) if ns else name)
|
||||
print >>fp, '\t\t\t}'
|
||||
print >>fp, '\t\t}'
|
||||
for fname, func in sorted(funcs.items(), key=lambda x: x[0]):
|
||||
implemented = re.search('[^a-zA-Z0-9:]%s::%s[^a-zA-Z0-9:]' % (qname, fname), allcode)
|
||||
print >>fp, '\t\tuint32_t %s(%s);' % (fname, generatePrototype(func))
|
||||
if partial:
|
||||
for x in partial[0]:
|
||||
print >>fp, '\t\t%s' % x
|
||||
print >>fp, '\t};'
|
||||
print >>fp, '%s}' % ('//// ' if ns is None else '')
|
||||
|
||||
print >>fp, '#ifdef DEFINE_STUBS'
|
||||
for name, funcs in sorted(ifaces.items(), key=lambda x: x[0]):
|
||||
qname = '%s::%s' % (ns, name) if ns else name
|
||||
partial = partials[qname] if qname in partials else None
|
||||
for fname, func in sorted(funcs.items(), key=lambda x: x[0]):
|
||||
implemented = re.search('[^a-zA-Z0-9:]%s::%s[^a-zA-Z0-9:]' % (qname, fname), allcode)
|
||||
if not implemented:
|
||||
print >>fp, 'uint32_t %s::%s(%s) {' % (qname, fname, generatePrototype(func))
|
||||
print >>fp, '\tLOG_DEBUG(IpcStubs, "Stub implementation for %s::%s");' % (qname, fname)
|
||||
for i, (name, elem) in enumerate(func['outputs']):
|
||||
if elem[0] == 'object' and elem[1][0] != 'IUnknown':
|
||||
name = name if name else '_%i' % (len(func['inputs']) + i)
|
||||
print >>fp, '\t%s = buildInterface(%s);' % (name, elem[1][0])
|
||||
if elem[1][0] in partials and partials[elem[1][0]][1]:
|
||||
print 'Bare construction of interface %s requiring parameters. Created in %s::%s for parameter %s' % (elem[1][0], qname, fname, name)
|
||||
sys.exit(1)
|
||||
elif elem[0] == 'KObject':
|
||||
name = name if name else '_%i' % (len(func['inputs']) + i)
|
||||
print >>fp, '\t%s = make_shared<FauxHandle>(0x%x);' % (name, uniqInt(qname, fname, name))
|
||||
print >>fp, '\treturn 0;'
|
||||
print >>fp, '}'
|
||||
print >>fp, '#endif // DEFINE_STUBS'
|
||||
|
||||
if __name__=='__main__':
|
||||
main(*sys.argv[1:])
|
95
idparser.py
Normal file
95
idparser.py
Normal file
|
@ -0,0 +1,95 @@
|
|||
import sys, tatsu
|
||||
|
||||
grammar = '''
|
||||
start = { def }+ $ ;
|
||||
|
||||
number
|
||||
=
|
||||
| /0x[0-9a-fA-F]+/
|
||||
| /[0-9]+/
|
||||
;
|
||||
|
||||
def
|
||||
=
|
||||
| typeDef
|
||||
| interface
|
||||
;
|
||||
|
||||
expression
|
||||
=
|
||||
| type
|
||||
| number
|
||||
;
|
||||
|
||||
name = /[a-zA-Z_][a-zA-Z0-9_:]*/ ;
|
||||
sname = /[a-zA-Z_][a-zA-Z0-9_:\-]*/ ;
|
||||
serviceNameList = @:','.{ sname } ;
|
||||
template = '<' @:','.{ expression } '>' ;
|
||||
type = name:name template:[ template ] ;
|
||||
|
||||
typeDef = 'type' name:name '=' type:type ';' ;
|
||||
|
||||
interface = 'interface' name:name [ 'is' serviceNames:serviceNameList ] '{' functions:{ funcDef }* '}' ;
|
||||
namedTuple = '(' @:','.{ type [ name ] } ')' ;
|
||||
namedType = type [ name ] ;
|
||||
funcDef = '[' cmdId:number ']' name:name inputs:namedTuple [ '->' outputs:( namedType | namedTuple ) ] ';' ;
|
||||
'''
|
||||
|
||||
class Semantics(object):
|
||||
def number(self, ast):
|
||||
if ast.startswith('0x'):
|
||||
return int(ast[2:], 16)
|
||||
return int(ast)
|
||||
|
||||
def namedTuple(self, ast):
|
||||
return [elem if isinstance(elem, list) else [elem, None] for elem in ast]
|
||||
|
||||
def namedType(self, ast):
|
||||
return [ast if isinstance(ast, list) else [ast, None]]
|
||||
|
||||
def parseType(type):
|
||||
if not isinstance(type, tatsu.ast.AST) or 'template' not in type:
|
||||
return type
|
||||
name, template = type['name'], type['template']
|
||||
if template is None:
|
||||
return [name]
|
||||
else:
|
||||
return [name] + map(parseType, template)
|
||||
|
||||
def parse(data):
|
||||
ast = tatsu.parse(grammar, data, semantics=Semantics(), eol_comments_re=r'\/\/.*?$')
|
||||
|
||||
types = {}
|
||||
for elem in ast:
|
||||
if 'type' not in elem:
|
||||
continue
|
||||
#assert elem['name'] not in types
|
||||
types[elem['name']] = parseType(elem['type'])
|
||||
|
||||
ifaces = {}
|
||||
services = {}
|
||||
for elem in ast:
|
||||
if 'functions' not in elem:
|
||||
continue
|
||||
#assert elem['name'] not in ifaces
|
||||
ifaces[elem['name']] = iface = {}
|
||||
if elem['serviceNames']:
|
||||
services[elem['name']] = list(elem['serviceNames'])
|
||||
|
||||
for func in elem['functions']:
|
||||
if func['name'] in iface:
|
||||
print >>sys.stderr, 'Duplicate function %s in %s' % (func['name'], elem['name'])
|
||||
sys.exit(1)
|
||||
|
||||
assert func['name'] not in iface
|
||||
iface[func['name']] = fdef = {}
|
||||
fdef['cmdId'] = func['cmdId']
|
||||
fdef['inputs'] = [(name, parseType(type)) for type, name in func['inputs']]
|
||||
if func['outputs'] is None:
|
||||
fdef['outputs'] = []
|
||||
elif isinstance(func['outputs'], tatsu.ast.AST):
|
||||
fdef['outputs'] = [(None, parseType(func['outputs']))]
|
||||
else:
|
||||
fdef['outputs'] = [(name, parseType(type)) for type, name in func['outputs']]
|
||||
|
||||
return types, ifaces, services
|
8
ipcdefs/bgtc.id
Normal file
8
ipcdefs/bgtc.id
Normal file
|
@ -0,0 +1,8 @@
|
|||
interface nn::bgtc::IStateControlService is bgtc:sc {
|
||||
}
|
||||
|
||||
interface nn::bgtc::ITaskService is bgtc:t {
|
||||
[3] Unknown3() -> KObject;
|
||||
[5] Unknown5(buffer<unknown, 9, 0>);
|
||||
[14] Unknown14() -> KObject;
|
||||
}
|
7
ipcdefs/capsrv.id
Normal file
7
ipcdefs/capsrv.id
Normal file
|
@ -0,0 +1,7 @@
|
|||
interface nn::capsrv::sf::IAlbumControlService is caps:c {
|
||||
|
||||
}
|
||||
|
||||
interface nn::capsrv::sf::IAlbumAccessorService is caps:a {
|
||||
|
||||
}
|
2
ipcdefs/es.id
Normal file
2
ipcdefs/es.id
Normal file
|
@ -0,0 +1,2 @@
|
|||
interface nn::es::IETicketService is es {
|
||||
}
|
5
ipcdefs/fatal.id
Normal file
5
ipcdefs/fatal.id
Normal file
|
@ -0,0 +1,5 @@
|
|||
interface nn::fatalsrv::IService is fatal:u {
|
||||
[0] Unknown0(u64, u64, pid);
|
||||
[1] Unknown1(u64, u64, pid);
|
||||
[2] TransitionToFatalError(u64 errorCode, u64, buffer<unknown, 0x15, 0x110> errorBuf, pid);
|
||||
}
|
156
ipcdefs/fspsrv.id
Normal file
156
ipcdefs/fspsrv.id
Normal file
|
@ -0,0 +1,156 @@
|
|||
type nn::fssrv::sf::SaveStruct = bytes<0x40>;
|
||||
type nn::fssrv::sf::SaveCreateStruct = bytes<0x40>;
|
||||
type nn::fssrv::sf::Partition = u32;
|
||||
|
||||
// --------------------------------------------- FSP-SRV ---------------------------------------------
|
||||
|
||||
interface nn::fssrv::sf::IFileSystemProxy is fsp-srv {
|
||||
[1] Initialize(u64, pid);
|
||||
[2] OpenDataFileSystemByCurrentProcess() -> object<nn::fssrv::sf::IFileSystem>;
|
||||
[7] MountContent7(nn::ApplicationId tid, u32 ncaType) -> object<nn::fssrv::sf::IFileSystem>;
|
||||
[8] MountContent(nn::ApplicationId tid, u32 flag, buffer<i8, 0x19, 0x301> path) -> object<nn::fssrv::sf::IFileSystem> contentFs;
|
||||
[9] OpenDataFileSystemByApplicationId(nn::ApplicationId tid) -> object<nn::fssrv::sf::IFileSystem> dataFiles;
|
||||
[11] MountBis(nn::fssrv::sf::Partition partitionID, buffer<i8, 0x19, 0x301> path) -> object<nn::fssrv::sf::IFileSystem> Bis;
|
||||
[12] OpenBisPartition(nn::fssrv::sf::Partition partitionID) -> object<nn::fssrv::sf::IStorage> BisPartition;
|
||||
[13] InvalidateBisCache();
|
||||
[17] OpenHostFileSystemImpl(buffer<i8, 0x19, 0x301> path) -> object<nn::fssrv::sf::IFileSystem>;
|
||||
[18] MountSdCard() -> object<nn::fssrv::sf::IFileSystem> sdCard;
|
||||
[19] FormatSdCard();
|
||||
[21] DeleteSaveData(nn::ApplicationId tid);
|
||||
[22] CreateSaveData(nn::fssrv::sf::SaveStruct saveStruct, nn::fssrv::sf::SaveCreateStruct saveCreate, u128 input);
|
||||
[23] CreateSystemSaveData(nn::fssrv::sf::SaveStruct saveStruct, nn::fssrv::sf::SaveCreateStruct saveCreate);
|
||||
[24] RegisterSaveDataAtomicDeletion(buffer<void, 5, 0>);
|
||||
[25] DeleteSaveDataWithSpaceId(u8, u64);
|
||||
[26] FormatSdCardDryRun();
|
||||
[27] IsExFatSupported() -> u8 isSupported;
|
||||
[30] OpenGameCardPartition(nn::fssrv::sf::Partition partitionID, u32) -> object<nn::fssrv::sf::IStorage> gameCardFs;
|
||||
[31] MountGameCardPartition(u32, u32) -> object<nn::fssrv::sf::IFileSystem> gameCardPartitionFs;
|
||||
[32] ExtendSaveData(u8, u64, u64, u64);
|
||||
[51] MountSaveData(u8 input, nn::fssrv::sf::SaveStruct saveStruct) -> object<nn::fssrv::sf::IFileSystem> saveDataFs;
|
||||
[52] MountSystemSaveData(u8 input, nn::fssrv::sf::SaveStruct saveStruct) -> object<nn::fssrv::sf::IFileSystem> systemSaveDataFs;
|
||||
[53] MountSaveDataReadOnly(u8 input, nn::fssrv::sf::SaveStruct saveStruct) -> object<nn::fssrv::sf::IFileSystem> saveDataFs;
|
||||
[57] ReadSaveDataFileSystemExtraDataWithSpaceId (u8, u64) -> buffer<void, 6, 0>;
|
||||
[58] ReadSaveDataFileSystemExtraData(u64) -> buffer<void, 6, 0>;
|
||||
[59] WriteSaveDataFileSystemExtraData(u64, u8, buffer<void, 5, 0>);
|
||||
[60] OpenSaveDataInfoReader() -> object<nn::fssrv::sf::ISaveDataInfoReader>;
|
||||
[61] OpenSaveDataIterator(u8) -> object<IUnknown>;
|
||||
[80] OpenSaveDataThumbnailFile(u8, bytes<0x40>, u32) -> object<nn::fssrv::sf::IFile> thumbnail;
|
||||
[100] MountImageDirectory(u32) -> object<nn::fssrv::sf::IFileSystem> imageFs;
|
||||
[110] MountContentStorage(u32 contentStorageID) -> object<nn::fssrv::sf::IFileSystem> contentFs;
|
||||
[200] OpenDataStorageByCurrentProcess() -> object<nn::fssrv::sf::IStorage> dataStorage;
|
||||
[201] OpenDataStorageByApplicationId(nn::ApplicationId tid) -> object<nn::fssrv::sf::IStorage> dataStorage;
|
||||
[202] OpenDataStorageByDataId(nn::ApplicationId tid, u8 storageId) -> object<nn::fssrv::sf::IStorage> dataStorage;
|
||||
[203] OpenRomStorage() -> object<nn::fssrv::sf::IStorage>;
|
||||
[400] OpenDeviceOperator() -> object<nn::fssrv::sf::IDeviceOperator>;
|
||||
[500] OpenSdCardDetectionEventNotifier() -> object<nn::fssrv::sf::IEventNotifier> SdEventNotify;
|
||||
[501] OpenGameCardDetectionEventNotifier() -> object<nn::fssrv::sf::IEventNotifier> GameCardEventNotify;
|
||||
[600] SetCurrentPosixTime(u64 time);
|
||||
[601] QuerySaveDataTotalSize(u64, u64) -> u64 saveDataSize;
|
||||
[602] VerifySaveData(nn::ApplicationId tid) -> buffer<void, 6, 0>;
|
||||
[603] CorruptSaveDataForDebug(nn::ApplicationId tid);
|
||||
[604] CreatePaddingFile(u64 size);
|
||||
[605] DeleteAllPaddingFiles();
|
||||
[606] GetRightsId(u64, u8) -> u128 rights;
|
||||
[607] RegisterExternalKey(u128, u128);
|
||||
[608] UnregisterExternalKey();
|
||||
[609] GetRightsIdByPath(buffer<i8, 0x19, 0x301> path) -> u128 rights;
|
||||
[610] GetRightsIdByPath2(buffer<i8, 0x19, 0x301> path) -> (u128 rights, u8);
|
||||
[620] SetSdCardEncryptionSeed(u128 seedmaybe);
|
||||
[800] GetAndClearFileSystemProxyErrorInfo() -> bytes<0x80> errorInfo;
|
||||
[1000] SetBisRootForHost(u32, buffer<i8, 0x19, 0x301> path);
|
||||
[1001] SetSaveDataSize(u64, u64);
|
||||
[1002] SetSaveDataRootPath(buffer<i8, 0x19, 0x301> path);
|
||||
[1003] DisableAutoSaveDataCreation();
|
||||
[1004] SetGlobalAccessLogMode(u32 mode);
|
||||
[1005] GetGlobalAccessLogMode() -> u32 logMode;
|
||||
[1006] OutputAccessLogToSdCard(buffer<void, 5, 0> logText);
|
||||
}
|
||||
|
||||
interface nn::fssrv::sf::IStorage {
|
||||
[0] Read(u64 offset, u64 length) -> buffer<i8, 0x46, 0> buffer;
|
||||
[1] Write(u64 offset, u64 length, buffer<i8, 0x45, 0> data);
|
||||
[2] Flush();
|
||||
[3] SetSize(u64 size);
|
||||
[4] GetSize() -> u64 size;
|
||||
}
|
||||
|
||||
interface nn::fssrv::sf::IFileSystem {
|
||||
[0] CreateFile(u64 mode, u32 size, buffer<i8, 0x19, 0x301> path);
|
||||
[1] DeleteFile(buffer<i8, 0x19, 0x301> path);
|
||||
[2] CreateDirectory(buffer<i8, 0x19, 0x301> path);
|
||||
[3] DeleteDirectory(buffer<i8, 0x19, 0x301> path);
|
||||
[4] DeleteDirectoryRecursively(buffer<i8, 0x19, 0x301> path);
|
||||
[5] RenameFile(buffer<i8, 0x19, 0x301> oldPath, buffer<i8, 0x19, 0x301> newPath);
|
||||
[6] RenameDirectory(buffer<i8, 0x19, 0x301> oldPath, buffer<i8, 0x19, 0x301> newPath);
|
||||
[7] GetEntryType(buffer<i8, 0x19, 0x301> path) -> u32;
|
||||
[8] OpenFile(u32 mode, buffer<i8, 0x19, 0x301> path) -> object<nn::fssrv::sf::IFile> file;
|
||||
[9] OpenDirectory(u32, buffer<i8, 0x19, 0x301> path) -> object<nn::fssrv::sf::IDirectory> directory;
|
||||
[10] Commit();
|
||||
[11] GetFreeSpaceSize(buffer<i8, 0x19, 0x301> path) -> u64 totalFreeSpace;
|
||||
[12] GetTotalSpaceSize(buffer<i8, 0x19, 0x301> path) -> u64 totalSize;
|
||||
[13] CleanDirectoryRecursively(buffer<i8, 0x19, 0x301> path);
|
||||
[14] GetFileTimeStampRaw(buffer<i8, 0x19, 0x301> path) -> bytes<0x20> timestamp;
|
||||
}
|
||||
|
||||
interface nn::fssrv::sf::IDeviceOperator {
|
||||
[0] IsSdCardInserted() -> u8 isSdInserted;
|
||||
[1] GetSdCardSpeedMode() -> u64 sdSpeed;
|
||||
[2] GetSdCardCid(u64) -> buffer<unknown, 6, 0> cid;
|
||||
[3] GetSdCardUserAreaSize() -> u64 size;
|
||||
[4] GetSdCardProtectedAreaSize() -> u64 protectedSize;
|
||||
[5] GetAndClearSdCardErrorInfo(u64) -> (u128, u64, buffer<unknown, 6, 0>);
|
||||
[100] GetMmcCid(u64) -> buffer<unknown, 6, 0> cid;
|
||||
[101] GetMmcSpeedMode() -> u64 speedMode;
|
||||
[110] EraseMmc(u32);
|
||||
[111] GetMmcPartitionSize(u32) -> u64 paritionSize;
|
||||
[112] GetMmcPatrolCount() -> u32 patrolCount;
|
||||
[113] GetAndClearMmcErrorInfo(u64) -> (u128, u64, buffer<unknown, 6, 0>);
|
||||
[114] GetMmcExtendedCsd(u64) -> buffer<unknown, 6, 0>;
|
||||
[200] IsGameCardInserted() -> u8 isGameInserted;
|
||||
[201] EraseGameCard(u32, u64);
|
||||
[202] GetGameCardHandle() -> u32 gamecardHandle;
|
||||
[203] GetGameCardUpdatePartitionInfo(u32) -> (u32 version, nn::ApplicationId TID);
|
||||
[204] FinalizeGameCardDriver();
|
||||
[205] GetGameCardAttribute(u32) -> u8 attribute;
|
||||
[206] GetGameCardDeviceCertificate(u64, u32) -> buffer<unknown, 6, 0> certificate;
|
||||
[207] GetGameCardAsicInfo(u64, u64, buffer<unknown, 5, 0>) -> buffer<unknown, 6, 0>;
|
||||
[208] GetGameCardIdSet(u64) -> buffer<unknown, 6, 0>;
|
||||
[209] WriteToGameCard(u64, u64) -> buffer<unknown, 6, 0>;
|
||||
[210] SetVerifyWriteEnalbleFlag(u8 flag);
|
||||
[211] GetGameCardImageHash(u64, u32) -> buffer<unknown, 6, 0> imageHash;
|
||||
[212] GetGameCardDeviceIdForProdCard(u64, u64, buffer<unknown, 5, 0>) -> buffer<unknown, 6, 0> errorInfo;
|
||||
[213] EraseAndWriteParamDirectly(u64, buffer<unknown, 5, 0>);
|
||||
[214] GetGameCardCid(u64) -> buffer<unknown, 6, 0> cid;
|
||||
[215] ForceEraseGameCard();
|
||||
[216] GetGameCardErrorInfo() -> u128 errorInfo;
|
||||
[217] GetGameCardErrorReportInfo() -> bytes<0x40> errorReportInfo;
|
||||
[218] GetGameCardDeviceId(u64) -> buffer<unknown, 6, 0> deviceID;
|
||||
[300] SetSpeedEmulationMode(u32 mode);
|
||||
[301] GetSpeedEmulationMode() -> u32 emuMode;
|
||||
}
|
||||
|
||||
interface nn::fssrv::sf::IDirectory {
|
||||
[0] Read() -> (u64, buffer<unknown, 6, 0>);
|
||||
[1] GetEntryCount() -> u64;
|
||||
}
|
||||
|
||||
interface nn::fssrv::sf::IFile {
|
||||
[0] Read(u64, u64 offset, u32 size) -> (u64 out_size, buffer<i8, 0x46, 0> out_buf);
|
||||
[1] Write(u64, u64 offset, u32 size, buffer<i8, 0x45, 0> buf);
|
||||
[2] Flush();
|
||||
[3] SetSize(u64 size);
|
||||
[4] GetSize() -> u64 fileSize;
|
||||
}
|
||||
|
||||
// --------------------------------------------- FSP-PR ---------------------------------------------
|
||||
interface nn::fssrv::sf::IProgramRegistry {
|
||||
[0] SetFsPermissions(u64, u64, u8, u64, u64, buffer<unknown, 5, 0>, buffer<unknown, 5, 0>);
|
||||
[1] ClearFsPermissions(u64 pid);
|
||||
[256] SetEnabledProgramVerification(u8 enabled);
|
||||
}
|
||||
|
||||
// --------------------------------------------- FSP-LDR ---------------------------------------------
|
||||
interface nn::fssrv::sf::IFileSystemProxyForLoader {
|
||||
[0] MountCode(nn::ApplicationId TID, buffer<i8, 0x19, 0x301> contentPath) -> object<nn::fssrv::sf::IFileSystem> contentFs;
|
||||
[1] IsCodeMounted(nn::ApplicationId TID) -> u8 isMounted;
|
||||
}
|
0
ipcdefs/general.id
Normal file
0
ipcdefs/general.id
Normal file
28
ipcdefs/gpio.id
Normal file
28
ipcdefs/gpio.id
Normal file
|
@ -0,0 +1,28 @@
|
|||
interface nn::gpio::IManager is gpio {
|
||||
[0] Unknown0(u32) -> object<IUnknown>;
|
||||
[1] GetPadSession(u32) -> object<nn::gpio::IPadSession>;
|
||||
[2] Unknown2(u32) -> object<IUnknown>;
|
||||
[3] Unknown3(u32) -> u8;
|
||||
[4] Unknown4() -> u128;
|
||||
[5] Unknown5(u32, u8);
|
||||
[6] Unknown6(u8);
|
||||
}
|
||||
|
||||
interface nn::gpio::IPadSession {
|
||||
[0] Unknown0(u32);
|
||||
[1] Unknown1() -> u32;
|
||||
[2] Unknown2(u32);
|
||||
[3] Unknown3() -> u32;
|
||||
[4] Unknown4(u8);
|
||||
[5] Unknown5() -> u8;
|
||||
[6] Unknown6() -> u32;
|
||||
[7] Unknown7();
|
||||
[8] Unknown8(u32);
|
||||
[9] Unknown9() -> u32;
|
||||
[10] Unknown10() -> KObject;
|
||||
[11] Unknown11();
|
||||
[12] Unknown12(u8);
|
||||
[13] Unknown13() -> u8;
|
||||
[14] Unknown14(u32);
|
||||
[15] Unknown15() -> u32;
|
||||
}
|
4
ipcdefs/ldr.id
Normal file
4
ipcdefs/ldr.id
Normal file
|
@ -0,0 +1,4 @@
|
|||
interface nn::ro::detail::ILdrShellInterface is ldr:shel {
|
||||
[0] AddProcessToLaunchQueue(buffer<unknown, 0x19, 0x200>, u32 size, nn::ncm::ApplicationId appID);
|
||||
[1] ClearLaunchQueue();
|
||||
}
|
8
ipcdefs/lm.id
Normal file
8
ipcdefs/lm.id
Normal file
|
@ -0,0 +1,8 @@
|
|||
interface nn::lm::ILogService is lm {
|
||||
[0] Initialize(u64, pid) -> object<nn::lm::ILogger> Log;
|
||||
}
|
||||
|
||||
interface nn::lm::ILogger {
|
||||
[0] Unknown0(buffer<unknown, 0x21, 0>);
|
||||
[1] Unknown1(u32);
|
||||
}
|
24
ipcdefs/ncm.id
Normal file
24
ipcdefs/ncm.id
Normal file
|
@ -0,0 +1,24 @@
|
|||
interface nn::ncm::detail::INcmInterface4Unknown {
|
||||
[10] Unknown10();
|
||||
[13] Unknown13() -> u64;
|
||||
}
|
||||
|
||||
interface nn::ncm::detail::INcmInterface5Unknown {
|
||||
[5] Unknown5() -> u64;
|
||||
[7] Unknown7() -> u64;
|
||||
[8] Unknown8();
|
||||
|
||||
[15] Unknown15();
|
||||
}
|
||||
|
||||
interface nn::ncm::detail::INcmInterface is ncm {
|
||||
[2] Unknown2() -> u64;
|
||||
[3] Unknown3() -> u64;
|
||||
[4] Unknown4() -> object<nn::ncm::detail::INcmInterface4Unknown>;
|
||||
[5] Unknown5() -> object<nn::ncm::detail::INcmInterface5Unknown>;
|
||||
[9] Unknown9() -> u64;
|
||||
[11] Unknown11() -> u64;
|
||||
}
|
||||
|
||||
interface nn::ncm::detail::LocationResolverInterface is lr {
|
||||
}
|
9
ipcdefs/nim.id
Normal file
9
ipcdefs/nim.id
Normal file
|
@ -0,0 +1,9 @@
|
|||
interface nn::nim::detail::INetworkInstallManager is nim {
|
||||
[2] Unknown2(buffer<unknown, 6, 0x200>) -> (u64, u64);
|
||||
[8] Unknown8() -> (u64, u64);
|
||||
[40] Unknown40(buffer<unknown, 6, 0x200>) -> (u64, u64);
|
||||
}
|
||||
|
||||
interface nn::nim::detail::IShopServiceManager is nim:shp {
|
||||
|
||||
}
|
8
ipcdefs/npns.id
Normal file
8
ipcdefs/npns.id
Normal file
|
@ -0,0 +1,8 @@
|
|||
interface nn::npns::Weird {
|
||||
|
||||
}
|
||||
interface nn::npns::INpnsSystem is npns:s {
|
||||
[5] SetInterfaceVersion() -> KObject;
|
||||
[7] Unknown7() -> KObject;
|
||||
[103] Unknown103() -> KObject;
|
||||
}
|
3
ipcdefs/ovln.id
Normal file
3
ipcdefs/ovln.id
Normal file
|
@ -0,0 +1,3 @@
|
|||
interface nn::ovln::ISender is ovln:snd {
|
||||
[0] Unknown0(u64 unk1, u64 unk2, u64 unk3, u64 unk4, u64 unk5, u64 unk6, u64 unk7, u64 unk8, u64 unk9, u64 unk10, u64 unk11, u64 unk12, u64 unk13, u64 unk14, u64 unk15, u64 unk16, u64 unk17);
|
||||
}
|
9
ipcdefs/pm.id
Normal file
9
ipcdefs/pm.id
Normal file
|
@ -0,0 +1,9 @@
|
|||
interface Pm::Shell is pm:shell {
|
||||
[0] LaunchTitle(u64, nn::ApplicationId tid);
|
||||
[3] GetProcessEventWaiter() -> KObject;
|
||||
}
|
||||
|
||||
interface Pm::Bm is pm:bm {
|
||||
[0] Init() -> (u64);
|
||||
[1] EnableMaintenanceMode();
|
||||
}
|
10
ipcdefs/psc.id
Normal file
10
ipcdefs/psc.id
Normal file
|
@ -0,0 +1,10 @@
|
|||
interface nn::psc::sf::IPmControl is psc:c {
|
||||
}
|
||||
|
||||
interface nn::psc::sf::IPmModule {
|
||||
[0] Unknown0(u32, buffer<unknown, 5, 0>) -> KObject;
|
||||
}
|
||||
|
||||
interface nn::psc::sf::IPmService is psc:m {
|
||||
[0] GetIPmModule() -> object<nn::psc::sf::IPmModule>;
|
||||
}
|
8
ipcdefs/sm.id
Normal file
8
ipcdefs/sm.id
Normal file
|
@ -0,0 +1,8 @@
|
|||
type ServiceName = bytes<8>;
|
||||
|
||||
interface SmService {
|
||||
[0] Initialize();
|
||||
[1] GetService(ServiceName name) -> object<IPipe>;
|
||||
[2] RegisterService(ServiceName name) -> object<NPort>;
|
||||
[3] UnregisterService(ServiceName name);
|
||||
}
|
7
ipcimpl/account.cpp
Normal file
7
ipcimpl/account.cpp
Normal file
|
@ -0,0 +1,7 @@
|
|||
#include "Ctu.h"
|
||||
|
||||
uint32_t nn::account::detail::INotifier::GetSystemEvent(OUT shared_ptr<KObject>& _0) {
|
||||
LOG_INFO(Account, "Stub implementation for nn::account::detail::INotifier::GetSystemEvent");
|
||||
_0 = make_shared<Waitable>();
|
||||
return 0;
|
||||
}
|
7
ipcimpl/bgtc.cpp
Normal file
7
ipcimpl/bgtc.cpp
Normal file
|
@ -0,0 +1,7 @@
|
|||
#include "Ctu.h"
|
||||
|
||||
uint32_t nn::bgtc::ITaskService::Unknown14(OUT shared_ptr<KObject>& _0) {
|
||||
LOG_DEBUG(IpcStubs, "Stub implementation for nn::bgtc::ITaskService::Unknown14");
|
||||
_0 = make_shared<Waitable>();
|
||||
return 0;
|
||||
}
|
14
ipcimpl/fatal.cpp
Normal file
14
ipcimpl/fatal.cpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
#include "Ctu.h"
|
||||
|
||||
uint32_t nn::fatalsrv::IService::TransitionToFatalError(IN uint64_t errorCode, IN uint64_t _1, IN uint8_t * errorBuf, guint errorBuf_size, IN gpid _3) {
|
||||
LOG_DEBUG(Fatal, "!!! FATAL ERROR !!! ERROR CODE: " ADDRFMT, errorCode);
|
||||
uint64_t stackSize = *(uint64_t *)(&errorBuf[0x240]);
|
||||
LOG_DEBUG(Fatal, "Stack trace");
|
||||
LOG_DEBUG(Fatal, "----------------------------");
|
||||
for(uint64_t i = 0; i < stackSize; i++) {
|
||||
uint64_t addr = *(uint64_t *)(&errorBuf[0x130 + (i*8)]);
|
||||
LOG_DEBUG(Fatal, "\t[%x] " ADDRFMT, (uint) i, addr);
|
||||
}
|
||||
LOG_DEBUG(Fatal, "----------------------------");
|
||||
return 0;
|
||||
}
|
371
ipcimpl/fsp.cpp
Normal file
371
ipcimpl/fsp.cpp
Normal file
|
@ -0,0 +1,371 @@
|
|||
#include "Ctu.h"
|
||||
|
||||
/*$IPC$
|
||||
partial nn::fssrv::sf::IFile {
|
||||
[ctor] string fn;
|
||||
[ctor] uint32_t mode;
|
||||
void *fp;
|
||||
bool isOpen;
|
||||
long bufferOffset;
|
||||
}
|
||||
|
||||
partial nn::fssrv::sf::IFileSystem {
|
||||
[ctor] string fnPath;
|
||||
}
|
||||
|
||||
partial nn::fssrv::sf::IStorage {
|
||||
[ctor] string fn;
|
||||
void *fp;
|
||||
bool isOpen;
|
||||
long bufferOffset;
|
||||
}
|
||||
*/
|
||||
|
||||
/* ---------------------------------------- Start of IFileSystem ---------------------------------------- */
|
||||
// Interface
|
||||
nn::fssrv::sf::IStorage::IStorage(Ctu *_ctu, string _fn) : IpcService(_ctu), fn(_fn) {
|
||||
fn = "SwitchFS/" + _fn;
|
||||
LOG_DEBUG(Fsp, "Open IStorage \"%s\"", fn.c_str());
|
||||
fp = fopen(fn.c_str(), "r+");
|
||||
if(fp) {
|
||||
isOpen = true;
|
||||
} else {
|
||||
LOG_DEBUG(Fsp, "FILE NOT FOUND!");
|
||||
isOpen = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
uint32_t nn::fssrv::sf::IStorage::Flush() {
|
||||
if(isOpen && fp != nullptr)
|
||||
fflush((FILE *)fp);
|
||||
return 0;
|
||||
}
|
||||
uint32_t nn::fssrv::sf::IStorage::GetSize(OUT uint64_t& size) {
|
||||
if(isOpen && fp != nullptr) {
|
||||
bufferOffset = ftell((FILE *)fp);
|
||||
fseek((FILE *)fp, 0, SEEK_END);
|
||||
long fSize = ftell((FILE *)fp);
|
||||
fseek((FILE *)fp, bufferOffset, SEEK_SET);
|
||||
size = fSize;
|
||||
return 0;
|
||||
}
|
||||
LOG_DEBUG(Fsp, "Failed to get file size!");
|
||||
return 0;
|
||||
}
|
||||
uint32_t nn::fssrv::sf::IStorage::Read(IN uint64_t offset, IN uint64_t length, OUT int8_t * buffer, guint buffer_size) {
|
||||
if(isOpen && fp != nullptr) {
|
||||
uint32_t s = ((uint32_t)buffer_size < (uint32_t)length ? (uint32_t)buffer_size : (uint32_t)length);
|
||||
bufferOffset = offset;
|
||||
fseek((FILE *)fp, offset, SEEK_SET);
|
||||
fread(buffer, 1, s, (FILE *)fp);
|
||||
bufferOffset = ftell((FILE *)fp);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
uint32_t nn::fssrv::sf::IStorage::SetSize(IN uint64_t size) {
|
||||
if(isOpen && fp != nullptr) {
|
||||
fseek((FILE *)fp, 0, SEEK_END);
|
||||
uint32_t curSize = (uint32_t)ftell((FILE *)fp);
|
||||
|
||||
if(curSize < (uint32_t)size) {
|
||||
uint32_t remaining = (uint32_t)size-curSize;
|
||||
char *buf = (char*)malloc(remaining);
|
||||
memset(buf, 0, remaining);
|
||||
fwrite(buf, 1, remaining, (FILE *)fp);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
fseek((FILE *)fp, bufferOffset, SEEK_SET);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
uint32_t nn::fssrv::sf::IStorage::Write(IN uint64_t offset, IN uint64_t length, IN int8_t * data, guint data_size) {
|
||||
if(isOpen && fp != nullptr) {
|
||||
bufferOffset = offset;
|
||||
uint32_t s = ((uint32_t)data_size < (uint32_t)length ? (uint32_t)data_size : (uint32_t)length);
|
||||
fseek((FILE *)fp, offset, SEEK_SET);
|
||||
fwrite(data, 1, s, (FILE *)fp);
|
||||
if(length-s > 0) {
|
||||
char *buf2 = (char*)malloc(length-s);
|
||||
memset(buf2, 0, length-s);
|
||||
fwrite(buf2, 1, length-s, (FILE *)fp);
|
||||
free(buf2);
|
||||
}
|
||||
bufferOffset = ftell((FILE *)fp);
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Funcs
|
||||
uint32_t nn::fssrv::sf::IFileSystemProxy::OpenBisPartition(IN nn::fssrv::sf::Partition partitionID, OUT shared_ptr<nn::fssrv::sf::IStorage>& BisPartition) {
|
||||
LOG_DEBUG(Fsp, "Stub implementation for nn::fssrv::sf::IFileSystemProxy::OpenBisPartition");
|
||||
BisPartition = buildInterface(nn::fssrv::sf::IStorage, "bis.istorage");
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
uint32_t nn::fssrv::sf::IFileSystemProxy::OpenDataStorageByApplicationId(IN nn::ApplicationId tid, OUT shared_ptr<nn::fssrv::sf::IStorage>& dataStorage) {
|
||||
LOG_DEBUG(Fsp, "Stub implementation for nn::fssrv::sf::IFileSystemProxy::OpenDataStorageByApplicationId 0x" ADDRFMT, tid);
|
||||
std::stringstream ss;
|
||||
ss << "tid_archives_" << hex << tid << ".istorage";
|
||||
dataStorage = buildInterface(nn::fssrv::sf::IStorage, ss.str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t nn::fssrv::sf::IFileSystemProxy::OpenDataStorageByCurrentProcess(OUT shared_ptr<nn::fssrv::sf::IStorage>& dataStorage) {
|
||||
LOG_DEBUG(Fsp, "Stub implementation for nn::fssrv::sf::IFileSystemProxy::OpenDataStorageByCurrentProcess");
|
||||
LOG_ERROR(Fsp, "UNIMPLEMENTED!!!");
|
||||
dataStorage = buildInterface(nn::fssrv::sf::IStorage, "");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t nn::fssrv::sf::IFileSystemProxy::OpenDataStorageByDataId(IN nn::ApplicationId tid, IN uint8_t storageId, OUT shared_ptr<nn::fssrv::sf::IStorage>& dataStorage) {
|
||||
LOG_DEBUG(Fsp, "Stub implementation for nn::fssrv::sf::IFileSystemProxy::OpenDataStorageByDataId 0x" ADDRFMT, 0x0100000000000800+(uint64_t)storageId);
|
||||
std::stringstream ss;
|
||||
ss << "archives/" << hex << setw(16) << setfill('0') << 0x0100000000000800+(uint64_t)storageId << ".istorage";
|
||||
dataStorage = buildInterface(nn::fssrv::sf::IStorage, ss.str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t nn::fssrv::sf::IFileSystemProxy::OpenGameCardPartition(IN nn::fssrv::sf::Partition partitionID, IN uint32_t _1, OUT shared_ptr<nn::fssrv::sf::IStorage>& gameCardFs) {
|
||||
LOG_DEBUG(Fsp, "Stub implementation for nn::fssrv::sf::IFileSystemProxy::OpenGameCardPartition");
|
||||
gameCardFs = buildInterface(nn::fssrv::sf::IStorage, "GamePartition.istorage");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t nn::fssrv::sf::IFileSystemProxy::OpenRomStorage(OUT shared_ptr<nn::fssrv::sf::IStorage>& _0) {
|
||||
LOG_DEBUG(Fsp, "Stub implementation for nn::fssrv::sf::IFileSystemProxy::OpenRomStorage");
|
||||
_0 = buildInterface(nn::fssrv::sf::IStorage, "RomStorage.istorage");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------------------------------------- End of IStorage ---------------------------------------- */
|
||||
|
||||
/* ---------------------------------------- Start of IFileSystem ---------------------------------------- */
|
||||
// Interface
|
||||
nn::fssrv::sf::IFileSystem::IFileSystem(Ctu *_ctu, string _fnPath) : IpcService(_ctu), fnPath(_fnPath) {
|
||||
fnPath = "SwitchFS/" + _fnPath;
|
||||
LOG_DEBUG(Fsp, "Open path %s", fnPath.c_str());
|
||||
}
|
||||
|
||||
uint32_t nn::fssrv::sf::IFileSystem::DeleteFile(IN int8_t * path, guint path_size) {
|
||||
LOG_DEBUG(Fsp, "Delete file %s", (fnPath+string((char*)path)).c_str());
|
||||
remove((fnPath+string((char*)path)).c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t nn::fssrv::sf::IFileSystem::CreateFile(IN uint64_t mode, IN uint32_t size, IN int8_t * path, guint path_size) {
|
||||
LOG_DEBUG(Fsp, "Create file %s", (fnPath+string((char*)path)).c_str());
|
||||
FILE *fp = fopen((fnPath+string((char*)path)).c_str(), "wb");
|
||||
if(!fp)
|
||||
return 0x7d402;
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Funcs
|
||||
uint32_t nn::fssrv::sf::IFileSystemProxy::OpenDataFileSystemByCurrentProcess(OUT shared_ptr<nn::fssrv::sf::IFileSystem>& _0) {
|
||||
LOG_DEBUG(Fsp, "Stub implementation for nn::fssrv::sf::IFileSystemProxy::OpenDataFileSystemByCurrentProcess");
|
||||
_0 = buildInterface(nn::fssrv::sf::IFileSystem, "");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t nn::fssrv::sf::IFileSystemProxy::MountContent7(IN nn::ApplicationId tid, IN uint32_t ncaType, OUT shared_ptr<nn::fssrv::sf::IFileSystem>& _2) {
|
||||
LOG_DEBUG(Fsp, "Stub implementation for nn::fssrv::sf::IFileSystemProxy::MountContent7");
|
||||
_2 = buildInterface(nn::fssrv::sf::IFileSystem, "");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t nn::fssrv::sf::IFileSystemProxy::MountContent(IN nn::ApplicationId tid, IN uint32_t flag, IN int8_t * path, guint path_size, OUT shared_ptr<nn::fssrv::sf::IFileSystem>& contentFs) {
|
||||
LOG_DEBUG(Fsp, "Stub implementation for nn::fssrv::sf::IFileSystemProxy::MountContent");
|
||||
contentFs = buildInterface(nn::fssrv::sf::IFileSystem, "");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t nn::fssrv::sf::IFileSystemProxy::OpenDataFileSystemByApplicationId(IN nn::ApplicationId tid, OUT shared_ptr<nn::fssrv::sf::IFileSystem>& dataFiles) {
|
||||
LOG_DEBUG(Fsp, "Stub implementation for nn::fssrv::sf::IFileSystemProxy::OpenDataFileSystemByApplicationId");
|
||||
dataFiles = buildInterface(nn::fssrv::sf::IFileSystem, "");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t nn::fssrv::sf::IFileSystemProxy::MountBis(IN nn::fssrv::sf::Partition partitionID, IN int8_t * path, guint path_size, OUT shared_ptr<nn::fssrv::sf::IFileSystem>& Bis) {
|
||||
LOG_DEBUG(Fsp, "Stub implementation for nn::fssrv::sf::IFileSystemProxy::MountBis");
|
||||
Bis = buildInterface(nn::fssrv::sf::IFileSystem, string("BIS/") + to_string(partitionID));
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t nn::fssrv::sf::IFileSystemProxy::OpenHostFileSystemImpl(IN int8_t * path, guint path_size, OUT shared_ptr<nn::fssrv::sf::IFileSystem>& _1) {
|
||||
LOG_DEBUG(Fsp, "Stub implementation for nn::fssrv::sf::IFileSystemProxy::OpenHostFileSystemImpl");
|
||||
_1 = buildInterface(nn::fssrv::sf::IFileSystem, "");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t nn::fssrv::sf::IFileSystemProxy::MountSdCard(OUT shared_ptr<nn::fssrv::sf::IFileSystem>& sdCard) {
|
||||
LOG_DEBUG(Fsp, "Stub implementation for nn::fssrv::sf::IFileSystemProxy::MountSdCard");
|
||||
sdCard = buildInterface(nn::fssrv::sf::IFileSystem, "SDCard");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t nn::fssrv::sf::IFileSystemProxy::MountGameCardPartition(IN uint32_t _0, IN uint32_t _1, OUT shared_ptr<nn::fssrv::sf::IFileSystem>& gameCardPartitionFs) {
|
||||
LOG_DEBUG(Fsp, "Stub implementation for nn::fssrv::sf::IFileSystemProxy::MountGameCardPartition");
|
||||
gameCardPartitionFs = buildInterface(nn::fssrv::sf::IFileSystem, "GameCard");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t nn::fssrv::sf::IFileSystemProxy::MountSaveData(IN uint8_t input, IN nn::fssrv::sf::SaveStruct saveStruct, OUT shared_ptr<nn::fssrv::sf::IFileSystem>& saveDataFs) {
|
||||
uint64_t tid = *(uint64_t *)(&saveStruct[0x18]);
|
||||
LOG_DEBUG(Fsp, "Stub implementation for nn::fssrv::sf::IFileSystemProxy::MountSaveData 0x" ADDRFMT, tid);
|
||||
std::stringstream ss;
|
||||
ss << "save_" << hex << tid;
|
||||
saveDataFs = buildInterface(nn::fssrv::sf::IFileSystem, ss.str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t nn::fssrv::sf::IFileSystemProxy::MountSystemSaveData(IN uint8_t input, IN nn::fssrv::sf::SaveStruct saveStruct, OUT shared_ptr<nn::fssrv::sf::IFileSystem>& systemSaveDataFs) {
|
||||
uint64_t tid = *(uint64_t *)(&saveStruct[0x18]);
|
||||
LOG_DEBUG(Fsp, "Stub implementation for nn::fssrv::sf::IFileSystemProxy::MountSystemSaveData 0x" ADDRFMT, tid);
|
||||
std::stringstream ss;
|
||||
ss << "syssave_" << hex << tid;
|
||||
systemSaveDataFs = buildInterface(nn::fssrv::sf::IFileSystem, ss.str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t nn::fssrv::sf::IFileSystemProxy::MountSaveDataReadOnly(IN uint8_t input, IN nn::fssrv::sf::SaveStruct saveStruct, OUT shared_ptr<nn::fssrv::sf::IFileSystem>& saveDataFs) {
|
||||
uint64_t tid = *(uint64_t *)(&saveStruct[0x18]);
|
||||
LOG_DEBUG(Fsp, "Stub implementation for nn::fssrv::sf::IFileSystemProxy::MountSaveDataReadOnly 0x" ADDRFMT, tid);
|
||||
std::stringstream ss;
|
||||
ss << "save_" << hex << tid;
|
||||
saveDataFs = buildInterface(nn::fssrv::sf::IFileSystem, ss.str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t nn::fssrv::sf::IFileSystemProxy::MountImageDirectory(IN uint32_t _0, OUT shared_ptr<nn::fssrv::sf::IFileSystem>& imageFs) {
|
||||
LOG_DEBUG(Fsp, "Stub implementation for nn::fssrv::sf::IFileSystemProxy::MountImageDirectory");
|
||||
imageFs = buildInterface(nn::fssrv::sf::IFileSystem, string("Image_") + to_string(_0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t nn::fssrv::sf::IFileSystemProxy::MountContentStorage(IN uint32_t contentStorageID, OUT shared_ptr<nn::fssrv::sf::IFileSystem>& contentFs) {
|
||||
LOG_DEBUG(Fsp, "Stub implementation for nn::fssrv::sf::IFileSystemProxy::MountContentStorage");
|
||||
contentFs = buildInterface(nn::fssrv::sf::IFileSystem, string("CS_") + to_string(contentStorageID));
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t nn::fssrv::sf::IFileSystemProxyForLoader::MountCode(IN nn::ApplicationId TID, IN int8_t * contentPath, guint contentPath_size, OUT shared_ptr<nn::fssrv::sf::IFileSystem>& contentFs) {
|
||||
LOG_DEBUG(Fsp, "Stub implementation for nn::fssrv::sf::IFileSystemProxyForLoader::MountCode");
|
||||
contentFs = buildInterface(nn::fssrv::sf::IFileSystem, "");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------------------------------------- End of IFileSystem ---------------------------------------- */
|
||||
|
||||
|
||||
/* ---------------------------------------- Start of IFile ---------------------------------------- */
|
||||
// Interface
|
||||
nn::fssrv::sf::IFile::IFile(Ctu *_ctu, string _fn, uint32_t _mode) : IpcService(_ctu), fn(_fn), mode(_mode) {
|
||||
LOG_DEBUG(Fsp, "IFile: File path \"%s\"", fn.c_str());
|
||||
string openModes[] = {"", "rb", "wb+", "wb+", "ab+", "ab+", "ab+", "ab+"};
|
||||
fp = fopen(fn.c_str(), openModes[_mode].c_str());
|
||||
if(fp) {
|
||||
isOpen = true;
|
||||
} else {
|
||||
LOG_DEBUG(Fsp, "FILE NOT FOUND!");
|
||||
isOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t nn::fssrv::sf::IFile::GetSize(OUT uint64_t& fileSize) {
|
||||
if(isOpen && fp != nullptr) {
|
||||
bufferOffset = ftell((FILE *)fp);
|
||||
fseek((FILE *)fp, 0, SEEK_END);
|
||||
long fSize = ftell((FILE *)fp);
|
||||
fseek((FILE *)fp, bufferOffset, SEEK_SET);
|
||||
fileSize = fSize;
|
||||
return 0;
|
||||
}
|
||||
LOG_DEBUG(Fsp, "Failed to get file size!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t nn::fssrv::sf::IFile::Read(IN uint64_t _0, IN uint64_t offset, IN uint32_t size, OUT uint64_t& out_size, OUT int8_t * out_buf, guint out_buf_size) {
|
||||
if(isOpen && fp != nullptr) {
|
||||
uint32_t s = ((uint32_t)out_buf_size < size ? (uint32_t)out_buf_size : size);
|
||||
bufferOffset = offset;
|
||||
fseek((FILE *)fp, offset, SEEK_SET);
|
||||
fread(out_buf, 1, s, (FILE *)fp);
|
||||
bufferOffset = ftell((FILE *)fp);
|
||||
out_size = (uint32_t)s;
|
||||
}
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
uint32_t nn::fssrv::sf::IFile::Write(IN uint64_t _0, IN uint64_t offset, IN uint32_t size, IN int8_t * buf, guint buf_size) {
|
||||
LOG_DEBUG(Fsp, "Stub implementation for nn::fssrv::sf::IFile::Write");
|
||||
if(isOpen && fp != nullptr) {
|
||||
bufferOffset = offset;
|
||||
uint32_t s = ((uint32_t)buf_size < size ? (uint32_t)buf_size : size);
|
||||
fseek((FILE *)fp, offset, SEEK_SET);
|
||||
fwrite(buf, 1, s, (FILE *)fp);
|
||||
if(size-s > 0) {
|
||||
char *buf2 = (char*)malloc(size-s);
|
||||
memset(buf2, 0, size-s);
|
||||
fwrite(buf2, 1, size-s, (FILE *)fp);
|
||||
free(buf2);
|
||||
}
|
||||
bufferOffset = ftell((FILE *)fp);
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t nn::fssrv::sf::IFile::Flush() {
|
||||
if(isOpen && fp != nullptr)
|
||||
fflush((FILE *)fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t nn::fssrv::sf::IFile::SetSize(IN uint64_t size) {
|
||||
if(isOpen && fp != nullptr) {
|
||||
fseek((FILE *)fp, 0, SEEK_END);
|
||||
uint32_t curSize = (uint32_t)ftell((FILE *)fp);
|
||||
|
||||
if(curSize < (uint32_t)size) {
|
||||
uint32_t remaining = (uint32_t)size-curSize;
|
||||
char *buf = (char*)malloc(remaining);
|
||||
memset(buf, 0, remaining);
|
||||
fwrite(buf, 1, remaining, (FILE *)fp);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
fseek((FILE *)fp, bufferOffset, SEEK_SET);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Funcs
|
||||
uint32_t nn::fssrv::sf::IFileSystem::OpenFile(IN uint32_t mode, IN int8_t * path, guint path_size, OUT shared_ptr<nn::fssrv::sf::IFile>& file) {
|
||||
LOG_DEBUG(Fsp, "OpenFile %s", path);
|
||||
auto tempi = buildInterface(nn::fssrv::sf::IFile, fnPath + "/" + string((char*)path), mode);
|
||||
if(tempi->isOpen) {
|
||||
file = tempi;
|
||||
return 0;
|
||||
} else
|
||||
return 0x7d402;
|
||||
}
|
||||
|
||||
uint32_t nn::fssrv::sf::IFileSystemProxy::OpenSaveDataThumbnailFile(IN uint8_t _0, IN uint8_t * _1, IN uint32_t _2, OUT shared_ptr<nn::fssrv::sf::IFile>& thumbnail) {
|
||||
LOG_DEBUG(Fsp, "Stub implementation for nn::fssrv::sf::IFileSystemProxy::OpenSaveDataThumbnailFile");
|
||||
thumbnail = buildInterface(nn::fssrv::sf::IFile, string((char*)_1), _0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------- End of IFile ---------------------------------------- */
|
||||
|
||||
|
||||
uint32_t nn::fssrv::sf::IEventNotifier::Unknown0(OUT shared_ptr<KObject>& _0) {
|
||||
LOG_DEBUG(IpcStubs, "Stub implementation for nn::fssrv::sf::IEventNotifier::Unknown0");
|
||||
_0 = make_shared<Waitable>();
|
||||
return 0;
|
||||
}
|
9
ipcimpl/nim.cpp
Normal file
9
ipcimpl/nim.cpp
Normal file
|
@ -0,0 +1,9 @@
|
|||
#include "Ctu.h"
|
||||
|
||||
uint32_t nn::nim::detail::INetworkInstallManager::Unknown40(IN uint8_t * _0, guint _0_size, OUT uint64_t& _1, OUT uint64_t& _2) {
|
||||
LOG_DEBUG(IpcStubs, "Stub implementation for nn::nim::detail::INetworkInstallManager::Unknown40");
|
||||
memset(_0, 0xDE, _0_size);
|
||||
_1 = 0;
|
||||
_2 = 0;
|
||||
return 0;
|
||||
}
|
12
ipcimpl/pm.cpp
Normal file
12
ipcimpl/pm.cpp
Normal file
|
@ -0,0 +1,12 @@
|
|||
#include "Ctu.h"
|
||||
|
||||
uint32_t Pm::Shell::LaunchTitle(IN uint64_t _0, IN nn::ApplicationId tid) {
|
||||
LOG_DEBUG(Pm::Shell, "Attempted to launch title " ADDRFMT, tid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t Pm::Shell::GetProcessEventWaiter(OUT shared_ptr<KObject>& _0) {
|
||||
LOG_DEBUG(IpcStubs, "Stub implementation for Pm::Shell::GetProcessEventWaiter");
|
||||
_0 = make_shared<Waitable>();
|
||||
return 0;
|
||||
}
|
7
ipcimpl/psc.cpp
Normal file
7
ipcimpl/psc.cpp
Normal file
|
@ -0,0 +1,7 @@
|
|||
#include "Ctu.h"
|
||||
|
||||
uint32_t nn::psc::sf::IPmModule::Unknown0(IN uint32_t _0, IN uint8_t * _1, guint _1_size, OUT shared_ptr<KObject>& _2) {
|
||||
LOG_DEBUG(IpcStubs, "Stub implementation for nn::psc::sf::IPmModule::Unknown0");
|
||||
_2 = make_shared<Waitable>();
|
||||
return 0;
|
||||
}
|
15
ipcimpl/set.cpp
Normal file
15
ipcimpl/set.cpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
#include "Ctu.h"
|
||||
|
||||
uint32_t nn::settings::ISystemSettingsServer::GetSettingsItemValue(IN nn::settings::SettingsName* cls, guint cls_size, IN nn::settings::SettingsItemKey* key, guint key_size, OUT uint64_t& size, OUT uint8_t* data, guint data_size) {
|
||||
LOG_DEBUG(Settings, "Attempting to read setting %s!%s", cls, key);
|
||||
memset(data, 0, data_size);
|
||||
size = data_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t nn::settings::ISystemSettingsServer::GetMiiAuthorId(OUT nn::util::Uuid& _0) {
|
||||
auto buf = (uint64_t *) &_0;
|
||||
buf[0] = 0xdeadbeefcafebabe;
|
||||
buf[1] = 0x000000d00db3c001;
|
||||
return 0;
|
||||
}
|
34
ipcimpl/sm.cpp
Normal file
34
ipcimpl/sm.cpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
#include "Ctu.h"
|
||||
|
||||
/*$IPC$
|
||||
partial SmService {
|
||||
unordered_map<string, shared_ptr<NPort>> ports;
|
||||
}
|
||||
*/
|
||||
|
||||
uint32_t SmService::Initialize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SERVICE(str, iface) do { if(name == (str)) { svc = buildInterface(iface); return 0; } } while(0)
|
||||
|
||||
uint32_t SmService::GetService(IN ServiceName _name, OUT shared_ptr<IPipe>& svc) {
|
||||
string name((char *) _name, strnlen((char *) _name, 8));
|
||||
|
||||
if(ports.find(name) != ports.end()) {
|
||||
LOG_DEBUG(Sm, "Connecting to native IPC service!");
|
||||
svc = ports[name]->connectSync();
|
||||
return 0;
|
||||
}
|
||||
SERVICE_MAPPING();
|
||||
|
||||
LOG_ERROR(Sm, "Unknown service name %s", name.c_str());
|
||||
}
|
||||
|
||||
uint32_t SmService::RegisterService(IN ServiceName _name, OUT shared_ptr<NPort>& port) {
|
||||
string name((char *) _name, strnlen((char *) _name, 8));
|
||||
LOG_DEBUG(Sm, "Registering service %s", name.c_str());
|
||||
port = buildInterface(NPort, name);
|
||||
ports[name] = port;
|
||||
return 0;
|
||||
}
|
128
main.cpp
Normal file
128
main.cpp
Normal file
|
@ -0,0 +1,128 @@
|
|||
#include "Ctu.h"
|
||||
|
||||
struct Arg: public option::Arg
|
||||
{
|
||||
static void printError(const char* msg1, const option::Option& opt, const char* msg2)
|
||||
{
|
||||
fprintf(stderr, "%s", msg1);
|
||||
fwrite(opt.name, opt.namelen, 1, stderr);
|
||||
fprintf(stderr, "%s", msg2);
|
||||
}
|
||||
static option::ArgStatus Unknown(const option::Option& option, bool msg)
|
||||
{
|
||||
if (msg) printError("Unknown option '", option, "'\n");
|
||||
return option::ARG_ILLEGAL;
|
||||
}
|
||||
static option::ArgStatus Required(const option::Option& option, bool msg)
|
||||
{
|
||||
if (option.arg != nullptr)
|
||||
return option::ARG_OK;
|
||||
if (msg) printError("Option '", option, "' requires an argument\n");
|
||||
return option::ARG_ILLEGAL;
|
||||
}
|
||||
static option::ArgStatus NonEmpty(const option::Option& option, bool msg)
|
||||
{
|
||||
if (option.arg != nullptr && option.arg[0] != 0)
|
||||
return option::ARG_OK;
|
||||
if (msg) printError("Option '", option, "' requires a non-empty argument\n");
|
||||
return option::ARG_ILLEGAL;
|
||||
}
|
||||
static option::ArgStatus Numeric(const option::Option& option, bool msg)
|
||||
{
|
||||
char* endptr = nullptr;
|
||||
if (option.arg != nullptr && strtol(option.arg, &endptr, 10)){};
|
||||
if (endptr != option.arg && *endptr == 0)
|
||||
return option::ARG_OK;
|
||||
if (msg) printError("Option '", option, "' requires a numeric argument\n");
|
||||
return option::ARG_ILLEGAL;
|
||||
}
|
||||
};
|
||||
|
||||
enum optionIndex { UNKNOWN, HELP, ENABLE_GDB, PORT, NSO };
|
||||
const option::Descriptor usage[] =
|
||||
{
|
||||
{UNKNOWN, 0, "", "",Arg::None, "USAGE: ctu [options] <load-directory>\n\n"
|
||||
"Options:" },
|
||||
{HELP, 0,"","help",Arg::None, " --help \tUnsurprisingly, print this message." },
|
||||
{ENABLE_GDB, 0,"g","enable-gdb",Arg::None, " --enable-gdb, -g \tEnable GDB stub." },
|
||||
{PORT, 0,"p","gdb-port",Arg::Numeric, " --gdb-port, -p \tSet port for GDB; default 24689." },
|
||||
{NSO, 0,"","load-nso",Arg::NonEmpty, " --load-nso \tLoad an NSO without load directory"},
|
||||
{0,0,nullptr,nullptr,nullptr,nullptr}
|
||||
};
|
||||
|
||||
bool exists(string fn) {
|
||||
struct stat buffer;
|
||||
return stat(fn.c_str(), &buffer) == 0;
|
||||
}
|
||||
|
||||
void loadNso(Ctu &ctu, const string &lfn, gptr raddr) {
|
||||
assert(exists(lfn));
|
||||
Nso file(lfn);
|
||||
file.load(ctu, raddr, false);
|
||||
ctu.loadbase = min(raddr, ctu.loadbase);
|
||||
auto top = raddr + 0x100000000;
|
||||
ctu.loadsize = max(top - ctu.loadbase, ctu.loadsize);
|
||||
}
|
||||
|
||||
void runLisp(Ctu &ctu, const string &dir, shared_ptr<Atom> code) {
|
||||
assert(code->type == List && code->children.size() >= 1);
|
||||
auto head = code->children[0];
|
||||
assert(head->type == Symbol);
|
||||
if(head->strVal == "load-nso") {
|
||||
assert(code->children.size() == 3);
|
||||
auto fn = code->children[1], addr = code->children[2];
|
||||
assert(fn->type == String && addr->type == Number);
|
||||
auto raddr = addr->numVal;
|
||||
auto lfn = dir + "/" + fn->strVal;
|
||||
loadNso(ctu, lfn, raddr);
|
||||
} else if(head->strVal == "run-from") {
|
||||
assert(code->children.size() == 2 && code->children[1]->type == Number);
|
||||
ctu.execProgram(code->children[1]->numVal);
|
||||
} else
|
||||
LOG_ERROR(Main, "Unknown function in load script: '%s'", head->strVal.c_str());
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
argc -= argc > 0;
|
||||
argv += argc > 0;
|
||||
|
||||
option::Stats stats(usage, argc, argv);
|
||||
vector<option::Option> options(stats.options_max);
|
||||
vector<option::Option> buffer(stats.buffer_max);
|
||||
option::Parser parse(usage, argc, argv, &options[0], &buffer[0]);
|
||||
|
||||
if(parse.error())
|
||||
return 1;
|
||||
else if(options[HELP].count() || options[UNKNOWN].count() || (options[NSO].count() == 0 && parse.nonOptionsCount() != 1) || (options[NSO].count() == 1 && parse.nonOptionsCount() != 0)) {
|
||||
option::printUsage(cout, usage);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Ctu ctu;
|
||||
ctu.loadbase = 0xFFFFFFFFFFFFFFFF;
|
||||
ctu.loadsize = 0;
|
||||
|
||||
if(options[ENABLE_GDB].count()) {
|
||||
ctu.gdbStub.enable(options[PORT].count() == 0 ? 24689 : (uint16_t) atoi(options[PORT][0].arg));
|
||||
} else
|
||||
assert(options[PORT].count() == 0);
|
||||
|
||||
if(options[NSO].count()) {
|
||||
loadNso(ctu, options[NSO][0].arg, 0x7100000000);
|
||||
ctu.execProgram(0x7100000000);
|
||||
} else {
|
||||
string dir = parse.nonOption(0);
|
||||
auto lfn = dir + "/load.meph";
|
||||
if(!exists(lfn))
|
||||
LOG_ERROR(Main, "File does not exist: %s", lfn.c_str());
|
||||
auto fp = ifstream(lfn);
|
||||
auto code = string(istreambuf_iterator<char>(fp), istreambuf_iterator<char>());
|
||||
|
||||
auto atom = parseLisp(code);
|
||||
assert(atom->type == List);
|
||||
for(auto elem : atom->children)
|
||||
runLisp(ctu, dir, elem);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
2832
optionparser.h
Normal file
2832
optionparser.h
Normal file
File diff suppressed because it is too large
Load diff
20
partialparser.py
Normal file
20
partialparser.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
import re
|
||||
|
||||
def parse(data):
|
||||
partials = {}
|
||||
for name, body in re.findall('^\s*partial (.*?)\s*{(.*?)}', data, re.M|re.S):
|
||||
if name not in partials:
|
||||
partials[name] = [], []
|
||||
members, params = partials[name]
|
||||
for elem in body.split(';'):
|
||||
elem = elem.strip()
|
||||
if not elem:
|
||||
continue
|
||||
if elem.startswith('[ctor]'):
|
||||
elem = elem[6:].strip()
|
||||
type, name = re.match('^(.*?)([_a-zA-Z][_a-zA-Z0-9]+)$', elem).groups()
|
||||
params.append((type, name))
|
||||
|
||||
members.append(elem + ';')
|
||||
|
||||
return partials
|
Loading…
Reference in a new issue