diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 0ff56f84..ca6a3d96 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -16,7 +16,6 @@ add_library(lightswitch SHARED ${source_DIR}/lightswitch.cpp ${source_DIR}/switch/os/os.cpp ${source_DIR}/switch/os/ipc.cpp - ${source_DIR}/switch/os/kernel.cpp ${source_DIR}/switch/os/svc.cpp ${source_DIR}/switch/hw/cpu.cpp ${source_DIR}/switch/hw/memory.cpp diff --git a/app/src/main/cpp/lightswitch.cpp b/app/src/main/cpp/lightswitch.cpp index 46675663..294f8d8a 100644 --- a/app/src/main/cpp/lightswitch.cpp +++ b/app/src/main/cpp/lightswitch.cpp @@ -7,6 +7,7 @@ #include "switch/common.h" std::thread *emu_thread; +bool halt = false; void thread_main(std::string rom_path, std::string pref_path, std::string log_path) { auto log = std::make_shared(log_path); @@ -27,16 +28,18 @@ void thread_main(std::string rom_path, std::string pref_path, std::string log_pa extern "C" JNIEXPORT void JNICALL -Java_emu_lightswitch_MainActivity_loadFile(JNIEnv *env, jobject instance, jstring rom_path_, - jstring pref_path_, jstring log_path_) { +Java_emu_lightswitch_MainActivity_loadFile(JNIEnv *env, jobject instance, jstring rom_path_, jstring pref_path_, jstring log_path_) { const char *rom_path = env->GetStringUTFChars(rom_path_, 0); const char *pref_path = env->GetStringUTFChars(pref_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 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(pref_path_, pref_path); env->ReleaseStringUTFChars(log_path_, log_path); diff --git a/app/src/main/cpp/switch/common.cpp b/app/src/main/cpp/switch/common.cpp index 4d8f511a..70df95f2 100644 --- a/app/src/main/cpp/switch/common.cpp +++ b/app/src/main/cpp/switch/common.cpp @@ -66,10 +66,8 @@ namespace lightSwitch { } void Logger::write(Logger::LogLevel level, std::string str) { -#ifdef NDEBUG - if (level == DEBUG) + if (level == DEBUG && debug_build) return; -#endif syslog(level_syslog[level], "%s", str.c_str()); log_file << "1|" << level_str[level] << "|" << str << "\n"; log_file.flush(); diff --git a/app/src/main/cpp/switch/common.h b/app/src/main/cpp/switch/common.h index fd6911d3..4e40274d 100644 --- a/app/src/main/cpp/switch/common.h +++ b/app/src/main/cpp/switch/common.h @@ -9,7 +9,6 @@ #include #include "hw/cpu.h" #include "hw/memory.h" -#include "constant.h" namespace lightSwitch { class Settings { diff --git a/app/src/main/cpp/switch/constant.h b/app/src/main/cpp/switch/constant.h index a5b7410a..bdd4d55e 100644 --- a/app/src/main/cpp/switch/constant.h +++ b/app/src/main/cpp/switch/constant.h @@ -7,6 +7,12 @@ namespace lightSwitch { typedef std::runtime_error exception; +#ifdef NDEBUG + constexpr bool debug_build = 0; +#else + constexpr bool debug_build = 1; +#endif + namespace constant { constexpr uint64_t base_addr = 0x80000000; constexpr uint64_t stack_addr = 0x3000000; diff --git a/app/src/main/cpp/switch/device.h b/app/src/main/cpp/switch/device.h index 4b4ce192..088719fb 100644 --- a/app/src/main/cpp/switch/device.h +++ b/app/src/main/cpp/switch/device.h @@ -21,7 +21,7 @@ namespace lightSwitch { if (rom_ext == "nro") loader::NroLoader loader(rom_file, state); 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); } }; }; \ No newline at end of file diff --git a/app/src/main/cpp/switch/hw/cpu.cpp b/app/src/main/cpp/switch/hw/cpu.cpp index 7ca7857a..96be8a21 100644 --- a/app/src/main/cpp/switch/hw/cpu.cpp +++ b/app/src/main/cpp/switch/hw/cpu.cpp @@ -1,5 +1,5 @@ #include "cpu.h" -#include "../constant.h" +extern bool halt; namespace lightSwitch::hw { Cpu::~Cpu() { @@ -8,30 +8,30 @@ namespace lightSwitch::hw { long *Cpu::ReadMemory(uint64_t address) { // Return a single word (32-bit) 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; } void Cpu::WriteMemory(uint64_t address) { // Write a single word (32-bit) 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' iov = {®s, sizeof(regs)}; 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' iov = {®s, sizeof(regs)}; 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 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) { @@ -41,17 +41,13 @@ namespace lightSwitch::hw { auto instr_mrs = reinterpret_cast(address + iter); if (instr_svc->verify()) { -#ifdef NDEBUG - syslog(LOG_WARNING, "Found SVC call: 0x%X, At location 0x%X", instr_svc->value, ((uint64_t)address)+iter); -#endif - + if(debug_build) + syslog(LOG_WARNING, "Found SVC call: 0x%X, At location 0x%X", instr_svc->value, address+iter); instr::brk brk(static_cast(instr_svc->value)); address[iter] = *reinterpret_cast(&brk); } else if (instr_mrs->verify() && instr_mrs->src_reg == constant::tpidrro_el0) { -#ifdef NDEBUG - syslog(LOG_WARNING, "Found MRS call: 0x%X, At location 0x%X", instr_mrs->dst_reg, ((uint64_t)address)+iter); -#endif - + if(debug_build) + syslog(LOG_WARNING, "Found MRS call: 0x%X, At location 0x%X", instr_mrs->dst_reg, address+iter); instr::brk brk(static_cast(constant::svc_last + 1 + instr_mrs->dst_reg)); address[iter] = *reinterpret_cast(&brk); } @@ -59,50 +55,40 @@ namespace lightSwitch::hw { } void Cpu::Execute(Memory::Region region, std::shared_ptr memory, std::function svc_handler, void *device) { - tls = memory->region_map.at(hw::Memory::tls).address; - - hw::Memory::RegionData exec = memory->region_map.at(hw::Memory::text); - WriteBreakpoint(exec.address, exec.size); - + 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 child = ExecuteChild(exec.address); - - int stat = 0; - while (waitpid(child, &stat, 0)) { - if (WIFSTOPPED(stat)) { + while (waitpid(child, &pid_status, 0)) { + if (WIFSTOPPED(pid_status)) { ReadRegisters(); - -#ifdef NDEBUG - syslog(LOG_INFO, "PC is at 0x%X", regs.pc); -#endif - + if(debug_build) + syslog(LOG_INFO, "PC is at 0x%X", regs.pc); 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. auto instr = reinterpret_cast(ReadMemory(regs.pc)); if (instr->verify()) { if (instr->value <= constant::svc_last) { svc_handler(static_cast(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; } 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) // https://switchbrew.org/wiki/Thread_Local_Storage 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); } - regs.pc += 4; // Increment program counter by a single instruction (32 bits) WriteRegisters(); - } else if (WIFEXITED(stat)) break; - + } else if (WIFEXITED(pid_status)) break; ResumeProcess(); } - kill(child, SIGABRT); child = 0; - halt = false; // TODO: Global variable + pid_status = 0; + halt = false; } pid_t Cpu::ExecuteChild(uint64_t address) { @@ -111,7 +97,6 @@ namespace lightSwitch::hw { ptrace(PTRACE_TRACEME, 0, NULL, NULL); asm volatile("br %0" :: "r"(address)); } - return pid; } diff --git a/app/src/main/cpp/switch/hw/cpu.h b/app/src/main/cpp/switch/hw/cpu.h index 3fa84e5f..e821f695 100644 --- a/app/src/main/cpp/switch/hw/cpu.h +++ b/app/src/main/cpp/switch/hw/cpu.h @@ -7,18 +7,18 @@ #include #include #include -#include +#include "../constant.h" #include "memory.h" namespace lightSwitch::hw { class Cpu { private: - bool halt = false; long status = 0; + int pid_status = 0; pid_t child; iovec iov; user_pt_regs regs; - uint64_t tls; + uint64_t tls = constant::tls_addr; static pid_t ExecuteChild(uint64_t address); diff --git a/app/src/main/cpp/switch/hw/memory.cpp b/app/src/main/cpp/switch/hw/memory.cpp index 4bb1b562..1311e5b4 100644 --- a/app/src/main/cpp/switch/hw/memory.cpp +++ b/app/src/main/cpp/switch/hw/memory.cpp @@ -1,7 +1,6 @@ #include #include #include "memory.h" -#include "../constant.h" namespace lightSwitch::hw { Memory::Memory() { diff --git a/app/src/main/cpp/switch/os/ipc.h b/app/src/main/cpp/switch/os/ipc.h index 90d4c683..be1a452d 100644 --- a/app/src/main/cpp/switch/os/ipc.h +++ b/app/src/main/cpp/switch/os/ipc.h @@ -1,6 +1,5 @@ #pragma once -#include #include "switch/common.h" namespace lightSwitch::os::ipc { diff --git a/app/src/main/cpp/switch/os/kernel.cpp b/app/src/main/cpp/switch/os/kernel.cpp deleted file mode 100644 index ff220292..00000000 --- a/app/src/main/cpp/switch/os/kernel.cpp +++ /dev/null @@ -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++; - } -} \ No newline at end of file diff --git a/app/src/main/cpp/switch/os/kernel.h b/app/src/main/cpp/switch/os/kernel.h deleted file mode 100644 index 0c33f659..00000000 --- a/app/src/main/cpp/switch/os/kernel.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include -#include -#include -#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 KObjectPtr; - - class Kernel { - private: - device_state state; - uint32_t handle_index = constant::base_handle_index; - std::unordered_map handles; - public: - Kernel(device_state state_); - - uint32_t NewHandle(KObjectPtr obj); - }; -} \ No newline at end of file diff --git a/app/src/main/cpp/switch/os/os.cpp b/app/src/main/cpp/switch/os/os.cpp index cd7b3b61..4df6e622 100644 --- a/app/src/main/cpp/switch/os/os.cpp +++ b/app/src/main/cpp/switch/os/os.cpp @@ -14,7 +14,9 @@ namespace lightSwitch::os { } } - void OS::HandleSvc(uint16_t svc) { - SvcHandler(svc, &state); + uint32_t OS::NewHandle(KObjectPtr obj) { + handles.insert({handle_index, obj}); + state.logger->write(Logger::DEBUG, "Creating new handle 0x{0:x}", handle_index); + return handle_index++; } } \ No newline at end of file diff --git a/app/src/main/cpp/switch/os/os.h b/app/src/main/cpp/switch/os/os.h index 189c32e1..006a85f6 100644 --- a/app/src/main/cpp/switch/os/os.h +++ b/app/src/main/cpp/switch/os/os.h @@ -1,21 +1,32 @@ #pragma once -#include -#include -#include "switch/common.h" +#include +#include "../common.h" #include "ipc.h" -#include "kernel.h" #include "svc.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 KObjectPtr; + class OS { private: device_state state; + uint32_t handle_index = constant::base_handle_index; + std::unordered_map handles; public: 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); }; } \ No newline at end of file