diff --git a/CMakeLists.txt b/CMakeLists.txt index 56f499a9..fd32e59b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,17 +44,16 @@ else() message(FATAL_ERROR "THIS IS NOT x64 WAIT FOR THE KVM IMPLEMENTATION") endif() -set(SOURCE_FILES src/main.cpp src/emulator.cpp src/core/CPU/cpu_dynarmic.cpp src/core/memory.cpp src/core/elf.cpp - - ) +set(SOURCE_FILES src/main.cpp src/emulator.cpp src/core/CPU/cpu_dynarmic.cpp src/core/memory.cpp src/core/elf.cpp) set(KERNEL_SOURCE_FILES src/core/kernel/kernel.cpp src/core/kernel/resource_limits.cpp src/core/kernel/memory_management.cpp src/core/kernel/ports.cpp ) +set(SERVICE_SOURCE_FILES src/core/services/service_manager.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/kernel.hpp include/dynarmic_cp15.hpp include/kernel/resource_limits.hpp include/kernel/kernel_types.hpp - include/kernel/config_mem.hpp + include/kernel/config_mem.hpp include/services/service_manager.hpp ) set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp @@ -69,7 +68,8 @@ set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp 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\\Core\\Services" FILES ${SERVICE_SOURCE_FILES}) source_group("Source Files\\Third Party" FILES ${THIRD_PARTY_SOURCE_FILES}) -add_executable(Alber ${SOURCE_FILES} ${KERNEL_SOURCE_FILES} ${THIRD_PARTY_SOURCE_FILES} ${HEADER_FILES}) +add_executable(Alber ${SOURCE_FILES} ${KERNEL_SOURCE_FILES} ${SERVICE_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/kernel.hpp b/include/kernel/kernel.hpp index c0097fc4..1e0fda80 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -6,6 +6,7 @@ #include "kernel_types.hpp" #include "helpers.hpp" #include "memory.hpp" +#include "services/service_manager.hpp" class Kernel { std::array& regs; @@ -17,6 +18,7 @@ class Kernel { std::vector portHandles; u32 currentProcess; + ServiceManager serviceManager; // Get pointer to the object with the specified handle KernelObject* getObject(u32 handle) { @@ -49,12 +51,14 @@ class Kernel { Handle makeProcess(); Handle makePort(const char* name); + Handle makeSession(Handle port); std::optional getPortHandle(const char* name); void deleteObjectData(KernelObject& object); KernelObject* getProcessFromPID(Handle handle); s32 getCurrentResourceValue(const KernelObject* limit, u32 resourceName); u32 getMaxForResource(const KernelObject* limit, u32 resourceName); + u32 getTLSPointer(); std::string getProcessName(u32 pid); // SVC implementations @@ -68,7 +72,7 @@ class Kernel { void connectToPort(); public: - Kernel(std::array& regs, Memory& mem) : regs(regs), mem(mem), handleCounter(0) { + Kernel(std::array& regs, Memory& mem) : regs(regs), mem(mem), handleCounter(0), serviceManager(regs, mem) { objects.reserve(512); // Make room for a few objects to avoid further memory allocs later portHandles.reserve(32); } diff --git a/include/kernel/kernel_types.hpp b/include/kernel/kernel_types.hpp index 9d9216ba..a6a379a8 100644 --- a/include/kernel/kernel_types.hpp +++ b/include/kernel/kernel_types.hpp @@ -57,15 +57,21 @@ struct ProcessData { ResourceLimits limits; }; +enum class PortType { + Generic, + ServiceManager // For the service manager port "srv:" +}; + struct PortData { static constexpr u32 maxNameLen = 11; char name[maxNameLen + 1] = {}; bool isPublic = false; // Setting name=NULL creates a private port not accessible from svcConnectToPort. + PortType type = PortType::Generic; }; struct SessionData { - + Handle portHandle; // The port this session is subscribed to }; static const char* kernelObjectTypeToString(KernelObjectType t) { diff --git a/include/memory.hpp b/include/memory.hpp index aae5fbde..d7e4cb56 100644 --- a/include/memory.hpp +++ b/include/memory.hpp @@ -74,4 +74,6 @@ public: static constexpr bool isAligned(u32 addr) { return (addr & pageMask) == 0; } + + std::string readString(u32 vaddr, u32 maxCharacters); }; \ No newline at end of file diff --git a/include/services/service_manager.hpp b/include/services/service_manager.hpp new file mode 100644 index 00000000..fee0bf03 --- /dev/null +++ b/include/services/service_manager.hpp @@ -0,0 +1,18 @@ +#pragma once +#include +#include "helpers.hpp" +#include "memory.hpp" + +class ServiceManager { + std::array& regs; + Memory& mem; + + // "srv:" commands + void getServiceHandle(u32 messagePointer); + void registerClient(u32 messagePointer); + +public: + ServiceManager(std::array& regs, Memory& mem); + void reset(); + void handleSyncRequest(u32 TLSPointer); +}; \ No newline at end of file diff --git a/src/core/kernel/kernel.cpp b/src/core/kernel/kernel.cpp index 8f3dd940..4bb486b2 100644 --- a/src/core/kernel/kernel.cpp +++ b/src/core/kernel/kernel.cpp @@ -59,6 +59,7 @@ void Kernel::reset() { } objects.clear(); portHandles.clear(); + serviceManager.reset(); // Allocate handle #0 to a dummy object and make a main process object makeObject(KernelObjectType::Dummy); @@ -68,6 +69,12 @@ void Kernel::reset() { makePort("srv:"); } +// Get pointer to thread-local storage +// TODO: Every thread should have its own TLS. We need to adjust for this when we add threads +u32 Kernel::getTLSPointer() { + return VirtualAddrs::TLSBase; +} + // Result CreateAddressArbiter(Handle* arbiter) void Kernel::createAddressArbiter() { printf("Stubbed call to CreateAddressArbiter. Handle address: %08X\n", regs[0]); diff --git a/src/core/kernel/ports.cpp b/src/core/kernel/ports.cpp index df4c45cc..cb975ab2 100644 --- a/src/core/kernel/ports.cpp +++ b/src/core/kernel/ports.cpp @@ -7,15 +7,40 @@ Handle Kernel::makePort(const char* name) { objects[ret].data = new PortData(); const auto data = static_cast(objects[ret].data); + std::strncpy(data->name, name, PortData::maxNameLen); // If the name is empty (ie the first char is the null terminator) then the port is private data->isPublic = name[0] != '\0'; - std::strncpy(data->name, name, PortData::maxNameLen); + + // Check if this a special port (Like the service manager port) or not, and cache its type + if (std::strncmp(name, "srv:", PortData::maxNameLen) == 0) { + data->type = PortType::ServiceManager; + } else { + data->type = PortType::Generic; + Helpers::panic("Created generic port %s\n", name); + } // printf("Created %s port \"%s\" with handle %d\n", data->isPublic ? "public" : "private", data->name, ret); return ret; } +Handle Kernel::makeSession(Handle portHandle) { + const auto port = getObject(portHandle, KernelObjectType::Port); + if (port == nullptr) [[unlikely]] { + Helpers::panic("Trying to make session for non-existent port"); + } + + // Allocate data for session + const Handle ret = makeObject(KernelObjectType::Session); + objects[ret].data = new SessionData(); + auto sessionData = static_cast(objects[ret].data); + + // Link session with its parent port + sessionData->portHandle = portHandle; + + return ret; +} + // Get the handle of a port based on its name // If there's no such port, return nullopt std::optional Kernel::getPortHandle(const char* name) { @@ -32,25 +57,33 @@ std::optional Kernel::getPortHandle(const char* name) { // Result ConnectToPort(Handle* out, const char* portName) void Kernel::connectToPort() { const u32 handlePointer = regs[0]; - const char* port = static_cast(mem.getReadPointer(regs[1])); - printf("ConnectToPort(handle pointer = %08X, port = \"%s\")\n", handlePointer, port); + // Read up to max + 1 characters to see if the name is too long + std::string port = mem.readString(regs[1], PortData::maxNameLen + 1); - // TODO: This is unsafe - if (std::strlen(port) > PortData::maxNameLen) { + if (port.size() > PortData::maxNameLen) { Helpers::panic("ConnectToPort: Port name too long\n"); regs[0] = SVCResult::PortNameTooLong; return; } - const auto portHandle = getPortHandle(port); - if (!portHandle.has_value()) [[unlikely]] { + // Try getting a handle to the port + std::optional optionalHandle = getPortHandle(port.c_str()); + if (!optionalHandle.has_value()) [[unlikely]] { Helpers::panic("ConnectToPort: Port doesn't exist\n"); regs[0] = SVCResult::ObjectNotFound; return; } - + + Handle portHandle = optionalHandle.value(); + printf("ConnectToPort(handle pointer = %08X, port = \"%s\")\n", handlePointer, port.c_str()); + + const auto portData = static_cast(objects[portHandle].data); + if (!portData->isPublic) { + Helpers::panic("ConnectToPort: Attempted to connect to private port"); + } + // TODO: Actually create session - Handle sessionHandle = makeObject(KernelObjectType::Session); + Handle sessionHandle = makeSession(portHandle); // TODO: Should the result be written back to [r0]? regs[0] = SVCResult::Success; @@ -70,8 +103,14 @@ void Kernel::sendSyncRequest() { } const auto sessionData = static_cast(session->data); - const u32 messagePointer = VirtualAddrs::TLSBase + 0x80; + const auto port = objects[sessionData->portHandle]; // Get parent port object + const auto portData = static_cast(port.data); + + if (portData->type == PortType::ServiceManager) { // Special-case SendSyncRequest targetting "srv:" + serviceManager.handleSyncRequest(getTLSPointer()); + } else { + Helpers::panic("SendSyncRequest targetting port %s\n", portData->name); + } - Helpers::panic("SendSyncRequest: Message header: %08X", mem.read32(messagePointer)); regs[0] = SVCResult::Success; } \ No newline at end of file diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 5b2cc284..31146cb8 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -202,4 +202,20 @@ void* Memory::getWritePointer(u32 address) { uintptr_t pointer = writeTable[page]; if (pointer == 0) return nullptr; return (void*)(pointer + offset); +} + +// Thank you Citra devs +std::string Memory::readString(u32 address, u32 maxSize) { + std::string string; + string.reserve(maxSize); + + for (std::size_t i = 0; i < maxSize; ++i) { + char c = read8(address++); + if (c == '\0') + break; + string.push_back(c); + } + string.shrink_to_fit(); + + return string; } \ No newline at end of file diff --git a/src/core/services/service_manager.cpp b/src/core/services/service_manager.cpp new file mode 100644 index 00000000..9a106a35 --- /dev/null +++ b/src/core/services/service_manager.cpp @@ -0,0 +1,59 @@ +#include "services/service_manager.hpp" + +ServiceManager::ServiceManager(std::array& regs, Memory& mem) : regs(regs), mem(mem) {} + +void ServiceManager::reset() { + +} + +// Match IPC messages to a "srv:" command based on their header +namespace Commands { + enum : u32 { + RegisterClient = 0x00010002, + EnableNotification = 0x00020000, + RegisterService = 0x00030100, + UnregisterService = 0x000400C0, + GetServiceHandle = 0x00050100, + RegisterPort = 0x000600C2, + UnregisterPort = 0x000700C0, + GetPort = 0x00080100, + Subscribe = 0x00090040, + Unsubscribe = 0x000A0040, + ReceiveNotification = 0x000B0000, + PublishToSubscriber = 0x000C0080, + PublishAndGetSubscriber = 0x000D0040, + IsServiceRegistered = 0x000E00C0 + }; +} + +// Handle an IPC message issued using the SendSyncRequest SVC +// The parameters are stored in thread-local storage in this format: https://www.3dbrew.org/wiki/IPC#Message_Structure +// TLSPointer: The base pointer for this thread's thread-local storage +void ServiceManager::handleSyncRequest(u32 TLSPointer) { + // The message is stored at TLS+0x80 in this format: https://www.3dbrew.org/wiki/IPC#Message_Structure + const u32 messagePointer = TLSPointer + 0x80; + const u32 header = mem.read32(messagePointer); + + switch (header) { + case Commands::RegisterClient: registerClient(messagePointer); break; + case Commands::GetServiceHandle: getServiceHandle(messagePointer); break; + default: Helpers::panic("Unknown \"srv:\" command: %08X", header); + } +} + +void ServiceManager::registerClient(u32 messagePointer) { + printf("srv: registerClient (Stubbed)\n"); +} + +void ServiceManager::getServiceHandle(u32 messagePointer) { + printf("srv: getServiceHandle\n"); + + std::string myBalls; + myBalls.resize(8); + + for (int i = 0; i < 8; i++) { + myBalls[i] = mem.read8(messagePointer + 4 + i); + } + + std::cout << "Requested handle for service " << myBalls << "\n"; +} \ No newline at end of file