mirror of
https://github.com/skyline-emu/skyline.git
synced 2025-01-15 07:37:57 +03:00
Rearrange OS folder
This commit is contained in:
parent
7dd04afdcf
commit
da55a1d9ba
@ -16,7 +16,6 @@ add_library(lightswitch SHARED
|
|||||||
${source_DIR}/lightswitch.cpp
|
${source_DIR}/lightswitch.cpp
|
||||||
${source_DIR}/switch/os/os.cpp
|
${source_DIR}/switch/os/os.cpp
|
||||||
${source_DIR}/switch/os/ipc.cpp
|
${source_DIR}/switch/os/ipc.cpp
|
||||||
${source_DIR}/switch/os/kernel.cpp
|
|
||||||
${source_DIR}/switch/os/svc.cpp
|
${source_DIR}/switch/os/svc.cpp
|
||||||
${source_DIR}/switch/hw/cpu.cpp
|
${source_DIR}/switch/hw/cpu.cpp
|
||||||
${source_DIR}/switch/hw/memory.cpp
|
${source_DIR}/switch/hw/memory.cpp
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "switch/common.h"
|
#include "switch/common.h"
|
||||||
|
|
||||||
std::thread *emu_thread;
|
std::thread *emu_thread;
|
||||||
|
bool halt = false;
|
||||||
|
|
||||||
void thread_main(std::string rom_path, std::string pref_path, std::string log_path) {
|
void thread_main(std::string rom_path, std::string pref_path, std::string log_path) {
|
||||||
auto log = std::make_shared<lightSwitch::Logger>(log_path);
|
auto log = std::make_shared<lightSwitch::Logger>(log_path);
|
||||||
@ -27,16 +28,18 @@ void thread_main(std::string rom_path, std::string pref_path, std::string log_pa
|
|||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_emu_lightswitch_MainActivity_loadFile(JNIEnv *env, jobject instance, jstring rom_path_,
|
Java_emu_lightswitch_MainActivity_loadFile(JNIEnv *env, jobject instance, jstring rom_path_, jstring pref_path_, jstring log_path_) {
|
||||||
jstring pref_path_, jstring log_path_) {
|
|
||||||
const char *rom_path = env->GetStringUTFChars(rom_path_, 0);
|
const char *rom_path = env->GetStringUTFChars(rom_path_, 0);
|
||||||
const char *pref_path = env->GetStringUTFChars(pref_path_, 0);
|
const char *pref_path = env->GetStringUTFChars(pref_path_, 0);
|
||||||
const char *log_path = env->GetStringUTFChars(log_path_, 0);
|
const char *log_path = env->GetStringUTFChars(log_path_, 0);
|
||||||
|
|
||||||
if (emu_thread) pthread_kill(emu_thread->native_handle(), SIGABRT);
|
if (emu_thread) {
|
||||||
|
halt=true; // This'll cause execution to stop after the next breakpoint
|
||||||
|
emu_thread->join();
|
||||||
|
}
|
||||||
// Running on UI thread is not a good idea, any crashes and such will be propagated
|
// Running on UI thread is not a good idea, any crashes and such will be propagated
|
||||||
emu_thread = new std::thread(thread_main, std::string(rom_path, strlen(rom_path)), std::string(pref_path, strlen(pref_path)), std::string(log_path, strlen(log_path)));
|
emu_thread = new std::thread(thread_main, std::string(rom_path, strlen(rom_path)), std::string(pref_path, strlen(pref_path)), std::string(log_path, strlen(log_path)));
|
||||||
|
|
||||||
env->ReleaseStringUTFChars(rom_path_, rom_path);
|
env->ReleaseStringUTFChars(rom_path_, rom_path);
|
||||||
env->ReleaseStringUTFChars(pref_path_, pref_path);
|
env->ReleaseStringUTFChars(pref_path_, pref_path);
|
||||||
env->ReleaseStringUTFChars(log_path_, log_path);
|
env->ReleaseStringUTFChars(log_path_, log_path);
|
||||||
|
@ -66,10 +66,8 @@ namespace lightSwitch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Logger::write(Logger::LogLevel level, std::string str) {
|
void Logger::write(Logger::LogLevel level, std::string str) {
|
||||||
#ifdef NDEBUG
|
if (level == DEBUG && debug_build)
|
||||||
if (level == DEBUG)
|
|
||||||
return;
|
return;
|
||||||
#endif
|
|
||||||
syslog(level_syslog[level], "%s", str.c_str());
|
syslog(level_syslog[level], "%s", str.c_str());
|
||||||
log_file << "1|" << level_str[level] << "|" << str << "\n";
|
log_file << "1|" << level_str[level] << "|" << str << "\n";
|
||||||
log_file.flush();
|
log_file.flush();
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#include "hw/cpu.h"
|
#include "hw/cpu.h"
|
||||||
#include "hw/memory.h"
|
#include "hw/memory.h"
|
||||||
#include "constant.h"
|
|
||||||
|
|
||||||
namespace lightSwitch {
|
namespace lightSwitch {
|
||||||
class Settings {
|
class Settings {
|
||||||
|
@ -7,6 +7,12 @@
|
|||||||
namespace lightSwitch {
|
namespace lightSwitch {
|
||||||
typedef std::runtime_error exception;
|
typedef std::runtime_error exception;
|
||||||
|
|
||||||
|
#ifdef NDEBUG
|
||||||
|
constexpr bool debug_build = 0;
|
||||||
|
#else
|
||||||
|
constexpr bool debug_build = 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace constant {
|
namespace constant {
|
||||||
constexpr uint64_t base_addr = 0x80000000;
|
constexpr uint64_t base_addr = 0x80000000;
|
||||||
constexpr uint64_t stack_addr = 0x3000000;
|
constexpr uint64_t stack_addr = 0x3000000;
|
||||||
|
@ -21,7 +21,7 @@ namespace lightSwitch {
|
|||||||
if (rom_ext == "nro") loader::NroLoader loader(rom_file, state);
|
if (rom_ext == "nro") loader::NroLoader loader(rom_file, state);
|
||||||
else throw exception("Unsupported ROM extension.");
|
else throw exception("Unsupported ROM extension.");
|
||||||
|
|
||||||
cpu->Execute(hw::Memory::text, memory, os.SvcHandler, &state);
|
cpu->Execute(hw::Memory::text, memory, std::bind(&os::OS::SvcHandler, std::ref(os), std::placeholders::_1, std::placeholders::_2), &state);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
@ -1,5 +1,5 @@
|
|||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "../constant.h"
|
extern bool halt;
|
||||||
|
|
||||||
namespace lightSwitch::hw {
|
namespace lightSwitch::hw {
|
||||||
Cpu::~Cpu() {
|
Cpu::~Cpu() {
|
||||||
@ -8,30 +8,30 @@ namespace lightSwitch::hw {
|
|||||||
|
|
||||||
long *Cpu::ReadMemory(uint64_t address) { // Return a single word (32-bit)
|
long *Cpu::ReadMemory(uint64_t address) { // Return a single word (32-bit)
|
||||||
status = ptrace(PTRACE_PEEKDATA, child, address, NULL);
|
status = ptrace(PTRACE_PEEKDATA, child, address, NULL);
|
||||||
if (status == -1) throw std::runtime_error("Cannot read memory!");
|
if (status == -1) throw std::runtime_error("Cannot read memory");
|
||||||
return &status;
|
return &status;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cpu::WriteMemory(uint64_t address) { // Write a single word (32-bit)
|
void Cpu::WriteMemory(uint64_t address) { // Write a single word (32-bit)
|
||||||
status = ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, &iov);
|
status = ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, &iov);
|
||||||
if (status == -1) throw std::runtime_error("Cannot write memory!");
|
if (status == -1) throw std::runtime_error("Cannot write memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cpu::ReadRegisters() { // Read all registers into 'regs'
|
void Cpu::ReadRegisters() { // Read all registers into 'regs'
|
||||||
iov = {®s, sizeof(regs)};
|
iov = {®s, sizeof(regs)};
|
||||||
status = ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, &iov);
|
status = ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, &iov);
|
||||||
if (status == -1) throw std::runtime_error("Cannot read registers!");
|
if (status == -1) throw std::runtime_error("Cannot read registers");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cpu::WriteRegisters() { // Write all registers from 'regs'
|
void Cpu::WriteRegisters() { // Write all registers from 'regs'
|
||||||
iov = {®s, sizeof(regs)};
|
iov = {®s, sizeof(regs)};
|
||||||
status = ptrace(PTRACE_SETREGSET, child, NT_PRSTATUS, &iov);
|
status = ptrace(PTRACE_SETREGSET, child, NT_PRSTATUS, &iov);
|
||||||
if (status == -1) throw std::runtime_error("Cannot write registers!");
|
if (status == -1) throw std::runtime_error("Cannot write registers");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cpu::ResumeProcess() { // Resumes a process stopped due to a signal
|
void Cpu::ResumeProcess() { // Resumes a process stopped due to a signal
|
||||||
status = ptrace(PTRACE_CONT, child, NULL, NULL);
|
status = ptrace(PTRACE_CONT, child, NULL, NULL);
|
||||||
if (status == -1) throw std::runtime_error("Cannot resume process!");
|
if (status == -1) throw std::runtime_error("Cannot resume process");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cpu::WriteBreakpoint(uint64_t address_, uint64_t size) {
|
void Cpu::WriteBreakpoint(uint64_t address_, uint64_t size) {
|
||||||
@ -41,17 +41,13 @@ namespace lightSwitch::hw {
|
|||||||
auto instr_mrs = reinterpret_cast<instr::mrs *>(address + iter);
|
auto instr_mrs = reinterpret_cast<instr::mrs *>(address + iter);
|
||||||
|
|
||||||
if (instr_svc->verify()) {
|
if (instr_svc->verify()) {
|
||||||
#ifdef NDEBUG
|
if(debug_build)
|
||||||
syslog(LOG_WARNING, "Found SVC call: 0x%X, At location 0x%X", instr_svc->value, ((uint64_t)address)+iter);
|
syslog(LOG_WARNING, "Found SVC call: 0x%X, At location 0x%X", instr_svc->value, address+iter);
|
||||||
#endif
|
|
||||||
|
|
||||||
instr::brk brk(static_cast<uint16_t>(instr_svc->value));
|
instr::brk brk(static_cast<uint16_t>(instr_svc->value));
|
||||||
address[iter] = *reinterpret_cast<uint32_t *>(&brk);
|
address[iter] = *reinterpret_cast<uint32_t *>(&brk);
|
||||||
} else if (instr_mrs->verify() && instr_mrs->src_reg == constant::tpidrro_el0) {
|
} else if (instr_mrs->verify() && instr_mrs->src_reg == constant::tpidrro_el0) {
|
||||||
#ifdef NDEBUG
|
if(debug_build)
|
||||||
syslog(LOG_WARNING, "Found MRS call: 0x%X, At location 0x%X", instr_mrs->dst_reg, ((uint64_t)address)+iter);
|
syslog(LOG_WARNING, "Found MRS call: 0x%X, At location 0x%X", instr_mrs->dst_reg, address+iter);
|
||||||
#endif
|
|
||||||
|
|
||||||
instr::brk brk(static_cast<uint16_t>(constant::svc_last + 1 + instr_mrs->dst_reg));
|
instr::brk brk(static_cast<uint16_t>(constant::svc_last + 1 + instr_mrs->dst_reg));
|
||||||
address[iter] = *reinterpret_cast<uint32_t *>(&brk);
|
address[iter] = *reinterpret_cast<uint32_t *>(&brk);
|
||||||
}
|
}
|
||||||
@ -59,50 +55,40 @@ namespace lightSwitch::hw {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Cpu::Execute(Memory::Region region, std::shared_ptr<Memory> memory, std::function<void(uint16_t, void *)> svc_handler, void *device) {
|
void Cpu::Execute(Memory::Region region, std::shared_ptr<Memory> memory, std::function<void(uint16_t, void *)> svc_handler, void *device) {
|
||||||
tls = memory->region_map.at(hw::Memory::tls).address;
|
hw::Memory::RegionData exec = memory->region_map.at(region);
|
||||||
|
WriteBreakpoint(exec.address, exec.size); // We write the BRK instructions to replace SVC & MRS so we receive a breakpoint
|
||||||
hw::Memory::RegionData exec = memory->region_map.at(hw::Memory::text);
|
|
||||||
WriteBreakpoint(exec.address, exec.size);
|
|
||||||
|
|
||||||
child = ExecuteChild(exec.address);
|
child = ExecuteChild(exec.address);
|
||||||
|
while (waitpid(child, &pid_status, 0)) {
|
||||||
int stat = 0;
|
if (WIFSTOPPED(pid_status)) {
|
||||||
while (waitpid(child, &stat, 0)) {
|
|
||||||
if (WIFSTOPPED(stat)) {
|
|
||||||
ReadRegisters();
|
ReadRegisters();
|
||||||
|
if(debug_build)
|
||||||
#ifdef NDEBUG
|
syslog(LOG_INFO, "PC is at 0x%X", regs.pc);
|
||||||
syslog(LOG_INFO, "PC is at 0x%X", regs.pc);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!regs.pc || regs.pc == 0xBADC0DE) break;
|
if (!regs.pc || regs.pc == 0xBADC0DE) break;
|
||||||
|
|
||||||
// We store the instruction value as the immediate value. 0x0 to 0x7F are SVC, 0x80 to 0x9E is MRS for TPIDRRO_EL0.
|
// We store the instruction value as the immediate value. 0x0 to 0x7F are SVC, 0x80 to 0x9E is MRS for TPIDRRO_EL0.
|
||||||
auto instr = reinterpret_cast<instr::brk *>(ReadMemory(regs.pc));
|
auto instr = reinterpret_cast<instr::brk *>(ReadMemory(regs.pc));
|
||||||
if (instr->verify()) {
|
if (instr->verify()) {
|
||||||
if (instr->value <= constant::svc_last) {
|
if (instr->value <= constant::svc_last) {
|
||||||
svc_handler(static_cast<uint16_t>(instr->value), device);
|
svc_handler(static_cast<uint16_t>(instr->value), device);
|
||||||
syslog(LOG_ERR, "SVC has been called 0x%X", instr->value);
|
if (debug_build)
|
||||||
|
syslog(LOG_ERR, "SVC has been called 0x%X", instr->value);
|
||||||
if (halt) break;
|
if (halt) break;
|
||||||
} else if (instr->value > constant::svc_last && instr->value <= constant::svc_last + constant::num_regs) {
|
} else if (instr->value > constant::svc_last && instr->value <= constant::svc_last + constant::num_regs) {
|
||||||
// Catch MRS that reads the value of TPIDRRO_EL0 (TLS)
|
// Catch MRS that reads the value of TPIDRRO_EL0 (TLS)
|
||||||
// https://switchbrew.org/wiki/Thread_Local_Storage
|
// https://switchbrew.org/wiki/Thread_Local_Storage
|
||||||
SetRegister(xreg(instr->value - (constant::svc_last + 1)), tls);
|
SetRegister(xreg(instr->value - (constant::svc_last + 1)), tls);
|
||||||
syslog(LOG_ERR, "MRS has been called 0x%X", instr->value - (constant::svc_last + 1));
|
if (debug_build)
|
||||||
|
syslog(LOG_ERR, "MRS has been called 0x%X", instr->value - (constant::svc_last + 1));
|
||||||
} else syslog(LOG_ERR, "Received unhandled BRK 0x%X", instr->value);
|
} else syslog(LOG_ERR, "Received unhandled BRK 0x%X", instr->value);
|
||||||
}
|
}
|
||||||
|
|
||||||
regs.pc += 4; // Increment program counter by a single instruction (32 bits)
|
regs.pc += 4; // Increment program counter by a single instruction (32 bits)
|
||||||
WriteRegisters();
|
WriteRegisters();
|
||||||
} else if (WIFEXITED(stat)) break;
|
} else if (WIFEXITED(pid_status)) break;
|
||||||
|
|
||||||
ResumeProcess();
|
ResumeProcess();
|
||||||
}
|
}
|
||||||
|
|
||||||
kill(child, SIGABRT);
|
kill(child, SIGABRT);
|
||||||
child = 0;
|
child = 0;
|
||||||
halt = false; // TODO: Global variable
|
pid_status = 0;
|
||||||
|
halt = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pid_t Cpu::ExecuteChild(uint64_t address) {
|
pid_t Cpu::ExecuteChild(uint64_t address) {
|
||||||
@ -111,7 +97,6 @@ namespace lightSwitch::hw {
|
|||||||
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
|
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
|
||||||
asm volatile("br %0" :: "r"(address));
|
asm volatile("br %0" :: "r"(address));
|
||||||
}
|
}
|
||||||
|
|
||||||
return pid;
|
return pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,18 +7,18 @@
|
|||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <linux/uio.h>
|
#include <linux/uio.h>
|
||||||
#include <linux/elf.h>
|
#include <linux/elf.h>
|
||||||
#include <switch/constant.h>
|
#include "../constant.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
|
||||||
namespace lightSwitch::hw {
|
namespace lightSwitch::hw {
|
||||||
class Cpu {
|
class Cpu {
|
||||||
private:
|
private:
|
||||||
bool halt = false;
|
|
||||||
long status = 0;
|
long status = 0;
|
||||||
|
int pid_status = 0;
|
||||||
pid_t child;
|
pid_t child;
|
||||||
iovec iov;
|
iovec iov;
|
||||||
user_pt_regs regs;
|
user_pt_regs regs;
|
||||||
uint64_t tls;
|
uint64_t tls = constant::tls_addr;
|
||||||
|
|
||||||
static pid_t ExecuteChild(uint64_t address);
|
static pid_t ExecuteChild(uint64_t address);
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "../constant.h"
|
|
||||||
|
|
||||||
namespace lightSwitch::hw {
|
namespace lightSwitch::hw {
|
||||||
Memory::Memory() {
|
Memory::Memory() {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include "switch/common.h"
|
#include "switch/common.h"
|
||||||
|
|
||||||
namespace lightSwitch::os::ipc {
|
namespace lightSwitch::os::ipc {
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
#include "kernel.h"
|
|
||||||
|
|
||||||
namespace lightSwitch::os {
|
|
||||||
Kernel::Kernel(device_state state_) : state(state_) {}
|
|
||||||
|
|
||||||
uint32_t Kernel::NewHandle(KObjectPtr obj) {
|
|
||||||
handles.insert({handle_index, obj});
|
|
||||||
state.logger->write(Logger::DEBUG, "Creating new handle 0x{0:x}", handle_index);
|
|
||||||
return handle_index++;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <memory>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include "switch/common.h"
|
|
||||||
|
|
||||||
namespace lightSwitch::os {
|
|
||||||
class KObject {
|
|
||||||
private:
|
|
||||||
uint32_t handle;
|
|
||||||
public:
|
|
||||||
KObject(uint32_t handle) : handle(handle) {}
|
|
||||||
|
|
||||||
uint32_t Handle() { return handle; }
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef std::shared_ptr<KObject> KObjectPtr;
|
|
||||||
|
|
||||||
class Kernel {
|
|
||||||
private:
|
|
||||||
device_state state;
|
|
||||||
uint32_t handle_index = constant::base_handle_index;
|
|
||||||
std::unordered_map<uint32_t, KObjectPtr> handles;
|
|
||||||
public:
|
|
||||||
Kernel(device_state state_);
|
|
||||||
|
|
||||||
uint32_t NewHandle(KObjectPtr obj);
|
|
||||||
};
|
|
||||||
}
|
|
@ -14,7 +14,9 @@ namespace lightSwitch::os {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OS::HandleSvc(uint16_t svc) {
|
uint32_t OS::NewHandle(KObjectPtr obj) {
|
||||||
SvcHandler(svc, &state);
|
handles.insert({handle_index, obj});
|
||||||
|
state.logger->write(Logger::DEBUG, "Creating new handle 0x{0:x}", handle_index);
|
||||||
|
return handle_index++;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,21 +1,32 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
#include <unordered_map>
|
||||||
#include <err.h>
|
#include "../common.h"
|
||||||
#include "switch/common.h"
|
|
||||||
#include "ipc.h"
|
#include "ipc.h"
|
||||||
#include "kernel.h"
|
|
||||||
#include "svc.h"
|
#include "svc.h"
|
||||||
|
|
||||||
namespace lightSwitch::os {
|
namespace lightSwitch::os {
|
||||||
|
class KObject {
|
||||||
|
private:
|
||||||
|
uint32_t handle;
|
||||||
|
public:
|
||||||
|
KObject(uint32_t handle) : handle(handle) {}
|
||||||
|
|
||||||
|
uint32_t Handle() { return handle; }
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::shared_ptr<KObject> KObjectPtr;
|
||||||
|
|
||||||
class OS {
|
class OS {
|
||||||
private:
|
private:
|
||||||
device_state state;
|
device_state state;
|
||||||
|
uint32_t handle_index = constant::base_handle_index;
|
||||||
|
std::unordered_map<uint32_t, KObjectPtr> handles;
|
||||||
public:
|
public:
|
||||||
OS(device_state state_);
|
OS(device_state state_);
|
||||||
|
|
||||||
static void SvcHandler(uint16_t svc, void *vstate);
|
void SvcHandler(uint16_t svc, void *vstate);
|
||||||
|
|
||||||
void HandleSvc(uint16_t svc);
|
uint32_t NewHandle(KObjectPtr obj);
|
||||||
};
|
};
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user