diff --git a/include/kernel/handles.hpp b/include/kernel/handles.hpp index 5f93df16..ed7863b6 100644 --- a/include/kernel/handles.hpp +++ b/include/kernel/handles.hpp @@ -20,7 +20,17 @@ namespace KernelHandles { MinServiceHandle = APT, MaxServiceHandle = NDM, - GSPSharedMemHandle = MaxServiceHandle + 1 // Handle for the GSP shared memory + GSPSharedMemHandle = MaxServiceHandle + 1, // Handle for the GSP shared memory + HIDSharedMemHandle, + + MinSharedMemHandle = GSPSharedMemHandle, + MaxSharedMemHandle = HIDSharedMemHandle, + + HIDEvent0, + HIDEvent1, + HIDEvent2, + HIDEvent3, + HIDEvent4 }; // Returns whether "handle" belongs to one of the OS services @@ -28,6 +38,11 @@ namespace KernelHandles { return handle >= MinServiceHandle && handle <= MaxServiceHandle; } + // Returns whether "handle" belongs to one of the OS services' shared memory areas + static constexpr bool isSharedMemHandle(Handle handle) { + return handle >= MinSharedMemHandle && handle <= MaxSharedMemHandle; + } + // Returns the name of a handle as a string based on the given handle static const char* getServiceName(Handle handle) { switch (handle) { @@ -36,6 +51,7 @@ namespace KernelHandles { case FS: return "FS"; case GPU: return "GPU"; case LCD: return "LCD"; + case NDM: return "NDM"; default: return "Unknown"; } } diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index ba68615e..391406ef 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -86,6 +86,7 @@ class Kernel { MAKE_LOG_FUNCTION(log, kernelLogger) MAKE_LOG_FUNCTION(logSVC, svcLogger) + MAKE_LOG_FUNCTION(logDebugString, debugStringLogger) // SVC implementations void arbitrateAddress(); diff --git a/include/logger.hpp b/include/logger.hpp index 5f569cf2..7bcde212 100644 --- a/include/logger.hpp +++ b/include/logger.hpp @@ -19,6 +19,7 @@ namespace Log { // Our loggers here. Enable/disable by toggling the template param static Logger kernelLogger; + static Logger debugStringLogger; // Enables output for the outputDebugString SVC static Logger svcLogger; static Logger gpuLogger; diff --git a/include/memory.hpp b/include/memory.hpp index 55b52d59..e979a8a8 100644 --- a/include/memory.hpp +++ b/include/memory.hpp @@ -1,9 +1,11 @@ #pragma once +#include #include #include #include #include #include "helpers.hpp" +#include "handles.hpp" namespace PhysicalAddrs { enum : u32 { @@ -68,19 +70,33 @@ namespace KernelMemoryTypes { MemoryInfo(u32 baseAddr, u32 size, u32 perms, u32 state) : baseAddr(baseAddr), size(size) , perms(perms), state(state) {} }; + + // Shared memory block for HID, GSP:GPU etc + struct SharedMemoryBlock { + u32 paddr; // Physical address of this block's memory + u32 size; // Size of block + u32 handle; // The handle of the shared memory block + bool mapped; // Has this block been mapped at least once? + + SharedMemoryBlock(u32 paddr, u32 size, u32 handle) : paddr(paddr), size(size), handle(handle), mapped(false) {} + }; } class Memory { u8* fcram; u64& cpuTicks; // Reference to the CPU tick counter + using SharedMemoryBlock = KernelMemoryTypes::SharedMemoryBlock; // Our dynarmic core uses page tables for reads and writes with 4096 byte pages std::vector readTable, writeTable; // This tracks our OS' memory allocations std::vector memoryInfo; - // This tracks our physical memory reservations when the memory is not actually mapped to a vaddr - std::vector lockedMemoryInfo; + + std::array sharedMemBlocks = { + SharedMemoryBlock(0, 0x1000, KernelHandles::GSPSharedMemHandle), // GSP shared memory + SharedMemoryBlock(0, 0x1000, KernelHandles::HIDSharedMemHandle) // HID shared memory + }; static constexpr u32 pageShift = 12; static constexpr u32 pageSize = 1 << pageShift; @@ -99,7 +115,6 @@ class Memory { public: u16 kernelVersion = 0; u32 usedUserMemory = 0; - std::optional gspMemIndex; // Index of GSP shared mem in lockedMemoryInfo or nullopt if it's already reserved Memory(u64& cpuTicks); void reset(); @@ -139,12 +154,12 @@ public: // For internal use: // Reserve FCRAM linearly starting from physical address "paddr" (paddr == 0 is NOT special) with a size of "size" // Without actually mapping the memory to a vaddr - // r, w, x: Permissions for the reserved memory - // Returns the index of the allocation in lockedMemoryInfo if allocation succeeded and nullopt if it failed - std::optional reserveMemory(u32 paddr, u32 size, bool r, bool w, bool x); + // Returns true if the reservation succeeded and false if not + bool reserveMemory(u32 paddr, u32 size); - // Map GSP shared memory to virtual address vaddr with permissions "myPerms" + // Map a shared memory block to virtual address vaddr with permissions "myPerms" // The kernel has a second permission parameter in MapMemoryBlock but not sure what's used for // TODO: Find out - void mapGSPSharedMemory(u32 vaddr, u32 myPerms, u32 otherPerms); + // Returns a pointer to the FCRAM block used for the memory if allocation succeeded + u8* mapSharedMemory(Handle handle, u32 vaddr, u32 myPerms, u32 otherPerms); }; \ No newline at end of file diff --git a/include/services/apt.hpp b/include/services/apt.hpp index c245a090..875bdd7b 100644 --- a/include/services/apt.hpp +++ b/include/services/apt.hpp @@ -11,6 +11,7 @@ class APTService { // Service commands void getLockHandle(u32 messagePointer); + void checkNew3DS(u32 messagePointer); public: APTService(Memory& mem) : mem(mem) {} diff --git a/include/services/hid.hpp b/include/services/hid.hpp index 2f054379..5bf032ab 100644 --- a/include/services/hid.hpp +++ b/include/services/hid.hpp @@ -7,6 +7,8 @@ class HIDService { Handle handle = KernelHandles::HID; Memory& mem; + u8* sharedMem; // Pointer to HID shared memory + MAKE_LOG_FUNCTION(log, hidLogger) // Service commands @@ -16,4 +18,11 @@ public: HIDService(Memory& mem) : mem(mem) {} void reset(); void handleSyncRequest(u32 messagePointer); + + void setSharedMem(u8* ptr) { + sharedMem = ptr; + if (ptr != nullptr) { // Zero-fill shared memory in case the process tries to read stale service data or vice versa + std::memset(ptr, 0xff, 0x2b0); + } + } }; \ No newline at end of file diff --git a/include/services/service_manager.hpp b/include/services/service_manager.hpp index 8c7950c6..d0644f4d 100644 --- a/include/services/service_manager.hpp +++ b/include/services/service_manager.hpp @@ -34,6 +34,9 @@ public: // Forward a SendSyncRequest IPC message to the service with the respective handle void sendCommandToService(u32 messagePointer, Handle handle); + + // Wrappers for communicating with certain services void requestGPUInterrupt(GPUInterrupt type) { gsp_gpu.requestInterrupt(type); } void setGSPSharedMem(u8* ptr) { gsp_gpu.setSharedMem(ptr); } + void setHIDSharedMem(u8* ptr) { hid.setSharedMem(ptr); } }; \ No newline at end of file diff --git a/src/core/kernel/kernel.cpp b/src/core/kernel/kernel.cpp index d4eefd44..19dc137e 100644 --- a/src/core/kernel/kernel.cpp +++ b/src/core/kernel/kernel.cpp @@ -138,7 +138,7 @@ void Kernel::outputDebugString() { const u32 size = regs[1]; std::string message = mem.readString(pointer, size); - logSVC("[OutputDebugString] %s\n", message.c_str()); + logDebugString("[OutputDebugString] %s\n", message.c_str()); regs[0] = SVCResult::Success; } diff --git a/src/core/kernel/memory_management.cpp b/src/core/kernel/memory_management.cpp index 788bd013..0c0c0538 100644 --- a/src/core/kernel/memory_management.cpp +++ b/src/core/kernel/memory_management.cpp @@ -92,15 +92,23 @@ void Kernel::mapMemoryBlock() { const u32 addr = regs[1]; const u32 myPerms = regs[2]; const u32 otherPerms = regs[3]; - logSVC("MapMemoryBlock(block = %d, addr = %08X, myPerms = %X, otherPerms = %X\n", block, addr, myPerms, otherPerms); + logSVC("MapMemoryBlock(block = %X, addr = %08X, myPerms = %X, otherPerms = %X\n", block, addr, myPerms, otherPerms); if (!isAligned(addr)) [[unlikely]] { Helpers::panic("MapMemoryBlock: Unaligned address"); } - if (block == KernelHandles::GSPSharedMemHandle) { - mem.mapGSPSharedMemory(addr, myPerms, otherPerms); - serviceManager.setGSPSharedMem(static_cast(mem.getReadPointer(addr))); + if (KernelHandles::isSharedMemHandle(block)) { + u8* ptr = mem.mapSharedMemory(block, addr, myPerms, otherPerms); // Map shared memory block + + // Pass pointer to shared memory to the appropriate service + if (block == KernelHandles::HIDSharedMemHandle) { + serviceManager.setHIDSharedMem(ptr); + } else if (block == KernelHandles::GSPSharedMemHandle) { + serviceManager.setGSPSharedMem(ptr); + } else { + Helpers::panic("Mapping unknown shared memory block: %X", block); + } } else { Helpers::panic("MapMemoryBlock where the handle does not refer to GSP memory"); } diff --git a/src/core/kernel/ports.cpp b/src/core/kernel/ports.cpp index 4624f173..0bcb272c 100644 --- a/src/core/kernel/ports.cpp +++ b/src/core/kernel/ports.cpp @@ -6,7 +6,6 @@ Handle Kernel::makePort(const char* name) { portHandles.push_back(ret); // Push the port handle to our cache of port handles objects[ret].data = new Port(name); - // printf("Created %s port \"%s\" with handle %d\n", data->isPublic ? "public" : "private", data->name, ret); return ret; } diff --git a/src/core/memory.cpp b/src/core/memory.cpp index a694d24a..6d03b3f2 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -37,14 +37,18 @@ void Memory::reset() { allocateMemory(vaddr, basePaddrForTLS, VirtualAddrs::TLSSize, true); } - // Reserve 4KB of memory for GSP shared memory - constexpr u32 gspMemSize = 0x1000; - const std::optional gspMemPaddr = findPaddr(gspMemSize); - if (!gspMemPaddr.has_value()) Helpers::panic("Couldn't find paddr for GSP shared memory"); - - auto temp = reserveMemory(gspMemPaddr.value(), gspMemSize, true, true, false); - if (!temp.has_value()) Helpers::panic("Couldn't reserve FCRAM for GSP shared memory"); - gspMemIndex = temp.value(); + // Initialize shared memory blocks and reserve memory for them + for (auto& e : sharedMemBlocks) { + e.mapped = false; + + std::optional possiblePaddr = findPaddr(e.size); + if (!possiblePaddr.has_value()) Helpers::panic("Failed to find paddr for shared memory block"); + + e.paddr = possiblePaddr.value(); + if (!reserveMemory(e.paddr, e.size)) { + Helpers::panic("Failed to reserve memory for shared memory block"); + } + } } u8 Memory::read8(u32 vaddr) { @@ -291,7 +295,7 @@ std::optional Memory::findPaddr(u32 size) { return std::nullopt; } -std::optional Memory::reserveMemory(u32 paddr, u32 size, bool r, bool w, bool x) { +bool Memory::reserveMemory(u32 paddr, u32 size) { if (!isAligned(paddr) || !isAligned(size)) { Helpers::panic("Memory::reserveMemory: Physical address or size is not page aligned. Paddr: %08X, size: %08X", paddr, size); ; } @@ -303,18 +307,11 @@ std::optional Memory::reserveMemory(u32 paddr, u32 size, bool r, bool w, bo for (u32 i = 0; i < pageCount; i++) { if (usedFCRAMPages[startingPage + i]) Helpers::panic("Memory::reserveMemory: Trying to reserve already reserved memory"); - usedFCRAMPages[i] = true; + usedFCRAMPages[startingPage + i] = true; } - // Back up the info for this allocation in our memoryInfo vector - u32 perms = (r ? PERMISSION_R : 0) | (w ? PERMISSION_W : 0) | (x ? PERMISSION_X : 0); - u32 ret = lockedMemoryInfo.size(); - - // When we reserve but don't map memory, we store the alloc info in lockedMemoryInfo instead of memoryInfo - lockedMemoryInfo.push_back(std::move(MemoryInfo(paddr, size, perms, KernelMemoryTypes::Locked))); - usedUserMemory += size; - return ret; + return true; } // The way I understand how the kernel's QueryMemory is supposed to work is that you give it a vaddr @@ -334,33 +331,42 @@ MemoryInfo Memory::queryMemory(u32 vaddr) { return MemoryInfo(vaddr, pageSize, 0, KernelMemoryTypes::Free); } -void Memory::mapGSPSharedMemory(u32 vaddr, u32 myPerms, u32 otherPerms) { - if (!gspMemIndex.has_value()) - Helpers::panic("Tried to map already mapped GSP memory"); +u8* Memory::mapSharedMemory(Handle handle, u32 vaddr, u32 myPerms, u32 otherPerms) { + for (auto& e : sharedMemBlocks) { + if (e.handle == handle) { + if (e.mapped) Helpers::panic("Allocated shared memory block twice. Is this allowed?"); - const u32 index = gspMemIndex.value(); // Index of GSP shared memory in lockedMemoryInfo - const u32 paddr = lockedMemoryInfo[index].baseAddr; - const u32 size = lockedMemoryInfo[index].size; - // This memory was not actually used, we just didn't want QueryMemory, getResourceLimitCurrentValues and such - // To report memory sizes wrongly. We subtract the size from the usedUserMemory size so allocateMemory won't break - usedUserMemory -= size; + const u32 paddr = e.paddr; + const u32 size = e.size; - // Wipe the GSP memory allocation from existence - gspMemIndex = std::nullopt; - lockedMemoryInfo.erase(lockedMemoryInfo.begin() + index); + if (myPerms == 0x10000000) { + myPerms = 3; + Helpers::panic("Memory::mapSharedMemory with DONTCARE perms"); + } - if (myPerms == 0x10000000) { - myPerms = 3; - Helpers::panic("Memory::mapGSPSharedMemory with DONTCARE perms"); + bool r = myPerms & 0b001; + bool w = myPerms & 0b010; + bool x = myPerms & 0b100; + + // This memory was not actually used, we just didn't want QueryMemory, getResourceLimitCurrentValues and such + // To report memory sizes wrongly. We subtract the size from the usedUserMemory size so + // allocateMemory won't break + usedUserMemory -= size; + + const auto result = allocateMemory(vaddr, paddr, size, true, r, w, x); + e.mapped = true; + if (!result.has_value()) { + Helpers::panic("Memory::mapSharedMemory: Failed to map shared memory block"); + return nullptr; + } + + return &fcram[paddr]; + } } - bool r = myPerms & 0b001; - bool w = myPerms & 0b010; - bool x = myPerms & 0b100; - - const auto result = allocateMemory(vaddr, paddr, size, true, r, w, x); - if (!result.has_value()) - Helpers::panic("Failed to allocated GSP shared memory"); + // This should be unreachable but better safe than sorry + Helpers::panic("Memory::mapSharedMemory: Unknown shared memory handle %08X", handle); + return nullptr; } // Get the number of ms since Jan 1 1900 diff --git a/src/core/services/apt.cpp b/src/core/services/apt.cpp index 55093945..8906abef 100644 --- a/src/core/services/apt.cpp +++ b/src/core/services/apt.cpp @@ -2,7 +2,15 @@ namespace APTCommands { enum : u32 { - GetLockHandle = 0x00010040 + GetLockHandle = 0x00010040, + CheckNew3DS = 0x01020000 + }; +} + +namespace Model { + enum : u8 { + Old3DS = 0, + New3DS = 1 }; } @@ -18,11 +26,17 @@ void APTService::reset() {} void APTService::handleSyncRequest(u32 messagePointer) { const u32 command = mem.read32(messagePointer); switch (command) { + case APTCommands::CheckNew3DS: checkNew3DS(messagePointer); break; case APTCommands::GetLockHandle: getLockHandle(messagePointer); break; default: Helpers::panic("APT service requested. Command: %08X\n", command); } } +void APTService::checkNew3DS(u32 messagePointer) { + mem.write32(messagePointer + 4, Result::Success); + mem.write8(messagePointer + 8, Model::Old3DS); // u8, Status (0 = Old 3DS, 1 = New 3DS) +} + void APTService::getLockHandle(u32 messagePointer) { log("APT::GetLockHandle (Failure)\n"); mem.write32(messagePointer + 4, Result::Failure); // Result code diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 3cdb008b..8ae1aab0 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -10,7 +10,7 @@ namespace FSCommands { namespace Result { enum : u32 { Success = 0, - Failure = 0xFFFF'FFFF + Failure = 0xFFFFFFFF }; } diff --git a/src/core/services/hid.cpp b/src/core/services/hid.cpp index 87e38448..8fe7697e 100644 --- a/src/core/services/hid.cpp +++ b/src/core/services/hid.cpp @@ -13,7 +13,9 @@ namespace Result { }; } -void HIDService::reset() {} +void HIDService::reset() { + sharedMem = nullptr; +} void HIDService::handleSyncRequest(u32 messagePointer) { const u32 command = mem.read32(messagePointer); @@ -24,7 +26,15 @@ void HIDService::handleSyncRequest(u32 messagePointer) { } void HIDService::getIPCHandles(u32 messagePointer) { - log("HID::GetIPCHandles (Failure)\n"); - mem.write32(messagePointer + 4, Result::Failure); // Result code + log("HID::GetIPCHandles\n"); + mem.write32(messagePointer + 4, Result::Success); // Result code mem.write32(messagePointer + 8, 0x14000000); // Translation descriptor + mem.write32(messagePointer + 12, KernelHandles::HIDSharedMemHandle); // Shared memory handle + + // HID event handles + mem.write32(messagePointer + 16, KernelHandles::HIDEvent0); + mem.write32(messagePointer + 20, KernelHandles::HIDEvent1); + mem.write32(messagePointer + 24, KernelHandles::HIDEvent2); + mem.write32(messagePointer + 28, KernelHandles::HIDEvent3); + mem.write32(messagePointer + 32, KernelHandles::HIDEvent4); } \ No newline at end of file