diff --git a/CMakeLists.txt b/CMakeLists.txt index 90f33f41..d8fcfba0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,7 @@ if(NOT SFML_FOUND) endif() include_directories(${PROJECT_SOURCE_DIR}/include/) +include_directories(${PROJECT_SOURCE_DIR}/include/kernel) include_directories (${SFML_INCLUDE_DIR}) include_directories (${FMT_INCLUDE_DIR}) include_directories(third_party/elfio/) @@ -44,11 +45,12 @@ else() endif() set(SOURCE_FILES src/main.cpp src/emulator.cpp src/core/CPU/cpu_dynarmic.cpp src/core/memory.cpp src/core/elf.cpp - src/core/kernel.cpp + ) +set(KERNEL_SOURCE_FILES src/core/kernel/kernel.cpp src/core/kernel/resource_limits.cpp) set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/opengl.hpp include/termcolor.hpp - include/cpu.hpp include/cpu_dynarmic.hpp include/memory.hpp include/kernel.hpp - include/dynarmic_cp15.hpp + include/cpu.hpp include/cpu_dynarmic.hpp include/memory.hpp include/kernel/kernel.hpp + include/dynarmic_cp15.hpp include/kernel/resource_limits.hpp include/kernel/kernel_types.hpp ) set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp @@ -62,7 +64,8 @@ set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp #add_library(Alber ${HEADER_FILES}) source_group("Header Files\\Core" FILES ${HEADER_FILES}) source_group("Source Files\\Core" FILES ${SOURCE_FILES}) +source_group("Source Files\\Core\\Kernel" FILES ${KERNEL_SOURCE_FILES}) source_group("Source Files\\Third Party" FILES ${THIRD_PARTY_SOURCE_FILES}) -add_executable(Alber ${SOURCE_FILES} ${THIRD_PARTY_SOURCE_FILES} ${HEADER_FILES}) +add_executable(Alber ${SOURCE_FILES} ${KERNEL_SOURCE_FILES} ${THIRD_PARTY_SOURCE_FILES} ${HEADER_FILES}) target_link_libraries(Alber PRIVATE sfml-system sfml-network sfml-graphics sfml-window dynarmic) \ No newline at end of file diff --git a/include/kernel.hpp b/include/kernel.hpp deleted file mode 100644 index 7516f5b5..00000000 --- a/include/kernel.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once -#include -#include "helpers.hpp" -#include "memory.hpp" - -namespace SVCResult { - enum : u32 { - Success = 0 - }; -} - -class Kernel { - std::array& regs; - Memory& mem; - -public: - Kernel(std::array& regs, Memory& mem) : regs(regs), mem(mem) {} - void serviceSVC(u32 svc); - void reset(); - - void createAddressArbiter(); -}; \ No newline at end of file diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp new file mode 100644 index 00000000..bb75494a --- /dev/null +++ b/include/kernel/kernel.hpp @@ -0,0 +1,63 @@ +#pragma once +#include +#include +#include +#include +#include "kernel_types.hpp" +#include "helpers.hpp" +#include "memory.hpp" + +class Kernel { + std::array& regs; + Memory& mem; + + // The handle number for the next kernel object to be created + u32 handleCounter; + std::vector objects; + + u32 currentProcess; + + // Get pointer to the object with the specified handle + KernelObject* getObject(u32 handle) { + // Accessing an object that has not been created + if (handle >= objects.size()) [[unlikely]] { + return nullptr; + } + + return &objects[handle]; + } + + // Get pointer to the object with the specified handle and type + KernelObject* getObject(u32 handle, KernelObjectType type) { + if (handle >= objects.size() || objects[handle].type != type) [[unlikely]] { + return nullptr; + } + + return &objects[handle]; + } + + Handle makeObject(KernelObjectType type) { + if (handleCounter == std::numeric_limits::max()) [[unlikely]] { + Helpers::panic("Hlep we somehow created enough kernel objects to overflow this thing"); + } + + objects.push_back(KernelObject(handleCounter, type)); + printf("Created %s object with handle %d\n", kernelObjectTypeToString(type), handleCounter); + return handleCounter++; + } + + Handle makeProcess(); + KernelObject* getProcessFromPID(Handle handle); + + void createAddressArbiter(); + void getResourceLimit(); + void getResourceLimitLimitValues(); + std::string getProcessName(u32 pid); + +public: + Kernel(std::array& regs, Memory& mem) : regs(regs), mem(mem), handleCounter(0) { + objects.reserve(512); // Make room for a few objects to avoid further memory allocs later + } + void serviceSVC(u32 svc); + void reset(); +}; \ No newline at end of file diff --git a/include/kernel/kernel_types.hpp b/include/kernel/kernel_types.hpp new file mode 100644 index 00000000..63d44b52 --- /dev/null +++ b/include/kernel/kernel_types.hpp @@ -0,0 +1,75 @@ +#pragma once +#include "helpers.hpp" + +namespace SVCResult { + enum : u32 { + Success = 0, + Failure = 0xFFFFFFFF, + // Different calls return a different value + BadHandle = 0xD8E007F7, + BadHandleAlt = 0xD9001BF7 + }; +} + +namespace KernelHandles { + enum : u32 { + CurrentThread = 0xFFFF8000, + CurrentProcess = 0xFFFF8001 + }; +} + +enum class KernelObjectType : u8 { + ResourceLimit, Process +}; + +enum class ResourceLimitCategory : int { + Application = 0, + SystemApplet = 1, + LibraryApplet = 2, + Misc = 3 +}; + +enum ResourceTypes { + PRIORITY = 0, + COMMIT = 1, + THREAD = 2, + EVENT = 3, + MUTEX = 4, + SEMAPHORE = 5, + TIMER = 6, + SHARED_MEMORY = 7, + ADDRESS_ARBITER = 8, + CPU_TIME = 9 +}; + +using Handle = u32; + +struct ResourceLimits { + Handle handle; +}; + +struct ProcessData { + // Resource limits for this process + ResourceLimits limits; +}; + +static const char* kernelObjectTypeToString(KernelObjectType t) { + switch (t) { + case KernelObjectType::Process: return "process"; + case KernelObjectType::ResourceLimit: return "resource limit"; + default: return "unknown"; + } +} + +// Generic kernel object class +struct KernelObject { + Handle handle = 0; // A u32 the OS will use to identify objects + void* data = nullptr; + KernelObjectType type; + + KernelObject(Handle handle, KernelObjectType type) : handle(handle), type(type) {} + + // Our destructor does not free the data in order to avoid it being freed when our std::vector is expanded + // Thus, the kernel needs to delete it when appropriate + ~KernelObject() {} +}; \ No newline at end of file diff --git a/include/kernel/resource_limits.hpp b/include/kernel/resource_limits.hpp new file mode 100644 index 00000000..9d2077c9 --- /dev/null +++ b/include/kernel/resource_limits.hpp @@ -0,0 +1,2 @@ +#pragma once +#include "kernel_types.hpp" \ No newline at end of file diff --git a/src/core/kernel.cpp b/src/core/kernel.cpp deleted file mode 100644 index 465ae3f1..00000000 --- a/src/core/kernel.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "kernel.hpp" - -void Kernel::serviceSVC(u32 svc) { - switch (svc) { - case 0x21: createAddressArbiter(); break; - default: Helpers::panic("Unimplemented svc: %x @ %08X", svc, regs[15]); break; - } -} - -void Kernel::reset() { - -} - -// Result CreateAddressArbiter(Handle* arbiter) -// in: r0 -> Pointer to Handle object -// out: r0 -> result -void Kernel::createAddressArbiter() { - printf("Stubbed call to CreateAddressArbiter. Handle address: %08X\n", regs[0]); - regs[0] = SVCResult::Success; -} \ No newline at end of file diff --git a/src/core/kernel/kernel.cpp b/src/core/kernel/kernel.cpp new file mode 100644 index 00000000..08e8ed11 --- /dev/null +++ b/src/core/kernel/kernel.cpp @@ -0,0 +1,90 @@ +#include +#include "kernel.hpp" +#include "kernel_types.hpp" + +void Kernel::serviceSVC(u32 svc) { + switch (svc) { + case 0x21: createAddressArbiter(); break; + case 0x38: getResourceLimit(); break; + case 0x39: getResourceLimitLimitValues(); break; + default: Helpers::panic("Unimplemented svc: %x @ %08X", svc, regs[15]); break; + } +} + +Handle Kernel::makeProcess() { + const Handle processHandle = makeObject(KernelObjectType::Process); + const Handle resourceLimitHandle = makeObject(KernelObjectType::ResourceLimit); + + // Allocate data + objects[processHandle].data = new ProcessData(); + const auto processData = static_cast(objects[processHandle].data); + + // Link resource limit object with its parent process + objects[resourceLimitHandle].data = &processData->limits; + processData->limits.handle = resourceLimitHandle; + return processHandle; +} + +// Get a pointer to the process indicated by handle, taking into account that 0xFFFF8001 always refers to the current process +// Returns nullptr if the handle does not correspond to a process +KernelObject* Kernel::getProcessFromPID(Handle handle) { + if (handle == KernelHandles::CurrentProcess) [[likely]] { + return getObject(currentProcess, KernelObjectType::Process); + } else { + return getObject(handle, KernelObjectType::Process); + } +} + +void Kernel::reset() { + handleCounter = 0; + + for (auto& object : objects) { + if (object.data != nullptr) { + delete object.data; + } + } + objects.clear(); + + // Make a main process object + currentProcess = makeProcess(); +} + +// Result CreateAddressArbiter(Handle* arbiter) +// out: r0 -> result +void Kernel::createAddressArbiter() { + printf("Stubbed call to CreateAddressArbiter. Handle address: %08X\n", regs[0]); + regs[0] = SVCResult::Success; +} + +// Result GetResourceLimit(Handle* resourceLimit, Handle process) +// out: r0 -> result +void Kernel::getResourceLimit() { + const auto handlePointer = regs[0]; + const auto pid = regs[1]; + const auto process = getProcessFromPID(pid); + + if (process == nullptr) [[unlikely]] { + regs[0] = SVCResult::BadHandle; + return; + } + + const auto processData = static_cast(process->data); + + printf("GetResourceLimit (Handle pointer = %08X, process: %s)\n", handlePointer, getProcessName(pid).c_str()); + mem.write32(handlePointer, processData->limits.handle); + regs[0] = SVCResult::Success; + // Is the handle meant to be output through both r1 and memory? Libctru breaks otherwise. + regs[1] = processData->limits.handle; +} + +void Kernel::getResourceLimitLimitValues() { + Helpers::panic("Trying to getResourceLimitLimitValues from resourceLimit with handle %08X\n", regs[1]); +} + +std::string Kernel::getProcessName(u32 pid) { + if (pid == KernelHandles::CurrentProcess) { + return "current"; + } else { + Helpers::panic("Attempted to name non-current process"); + } +} \ No newline at end of file diff --git a/src/core/kernel/resource_limits.cpp b/src/core/kernel/resource_limits.cpp new file mode 100644 index 00000000..e69de29b