From 5d15efe72c69dbbbebf539064a11795682fdeaf4 Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Sat, 15 Oct 2022 13:28:29 +0300 Subject: [PATCH] Stub GPU DMA, fix up some FS stuff. horribly wrong savedata implementation --- include/fs/archive_base.hpp | 3 +- include/fs/archive_ncch.hpp | 6 ++++ include/fs/archive_save_data.hpp | 6 ++++ include/kernel/kernel.hpp | 38 +++++++++++----------- include/loader/ncch.hpp | 3 ++ include/memory.hpp | 2 ++ include/services/fs.hpp | 2 ++ include/services/gsp_gpu.hpp | 1 + src/core/fs/archive_ncch.cpp | 18 +++++++---- src/core/fs/archive_save_data.cpp | 54 +++++++++++++++++++++++++++---- src/core/loader/ncch.cpp | 6 ++++ src/core/services/fs.cpp | 53 ++++++++++++++++++++++++++++-- src/core/services/gsp_gpu.cpp | 12 +++++++ 13 files changed, 169 insertions(+), 35 deletions(-) diff --git a/include/fs/archive_base.hpp b/include/fs/archive_base.hpp index 6d6d8cf3..7f6ea484 100644 --- a/include/fs/archive_base.hpp +++ b/include/fs/archive_base.hpp @@ -9,7 +9,7 @@ namespace PathType { Empty = 1, Binary = 2, ASCII = 3, - UTF8 = 4, + UTF16 = 4, }; } @@ -68,7 +68,6 @@ struct ArchiveSession { class ArchiveBase { protected: - using Result = u32; using Handle = u32; Memory& mem; diff --git a/include/fs/archive_ncch.hpp b/include/fs/archive_ncch.hpp index 3b17331b..be9a5d83 100644 --- a/include/fs/archive_ncch.hpp +++ b/include/fs/archive_ncch.hpp @@ -12,4 +12,10 @@ public: bool openFile(const FSPath& path) override; ArchiveBase* openArchive(const FSPath& path) override; std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override; + + // Returns whether the cart has a RomFS + bool hasRomFS() { + auto cxi = mem.getCXI(); + return (cxi != nullptr && cxi->hasRomFS()); + } }; \ No newline at end of file diff --git a/include/fs/archive_save_data.hpp b/include/fs/archive_save_data.hpp index ef40a370..6c2b2df5 100644 --- a/include/fs/archive_save_data.hpp +++ b/include/fs/archive_save_data.hpp @@ -12,4 +12,10 @@ public: bool openFile(const FSPath& path) override; ArchiveBase* openArchive(const FSPath& path) override; std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override; + + // Returns whether the cart has save data or not + bool cartHasSaveData() { + auto cxi = mem.getCXI(); + return (cxi != nullptr && cxi->hasSaveData()); // We need to have a CXI file with more than 0 bytes of save data + } }; \ No newline at end of file diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index 8c052224..a1e60404 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -39,25 +39,6 @@ class Kernel { // Top 8 bits are the major version, bottom 8 are the minor version u16 kernelVersion = 0; - // Get pointer to the object with the specified handle - KernelObject* getObject(Handle 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(Handle handle, KernelObjectType type) { - if (handle >= objects.size() || objects[handle].type != type) [[unlikely]] { - return nullptr; - } - - return &objects[handle]; - } - Handle makeArbiter(); Handle makeEvent(ResetType resetType); Handle makeProcess(u32 id); @@ -148,5 +129,24 @@ public: return objects; } + // Get pointer to the object with the specified handle + KernelObject* getObject(Handle 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(Handle handle, KernelObjectType type) { + if (handle >= objects.size() || objects[handle].type != type) [[unlikely]] { + return nullptr; + } + + return &objects[handle]; + } + void sendGPUInterrupt(GPUInterrupt type) { serviceManager.requestGPUInterrupt(type); } }; \ No newline at end of file diff --git a/include/loader/ncch.hpp b/include/loader/ncch.hpp index ccc49e08..2d05602d 100644 --- a/include/loader/ncch.hpp +++ b/include/loader/ncch.hpp @@ -47,6 +47,8 @@ struct NCCH { // Contents of the .code file in the ExeFS std::vector codeFile; + // Contains of the cart's save data + std::vector saveData; // Header: 0x200 + 0x800 byte NCCH header + exheadr // Returns true on success, false on failure @@ -57,6 +59,7 @@ struct NCCH { bool hasExeFS() { return exeFS.size != 0; } bool hasRomFS() { return romFS.size != 0; } bool hasCode() { return codeFile.size() != 0; } + bool hasSaveData() { return saveData.size() != 0; } private: std::array primaryKey = {}; // For exheader, ExeFS header and icons diff --git a/include/memory.hpp b/include/memory.hpp index 556f03dc..10745544 100644 --- a/include/memory.hpp +++ b/include/memory.hpp @@ -36,6 +36,8 @@ namespace VirtualAddrs { TLSBase = 0xFF400000, TLSSize = 0x1000, + VramStart = 0x1F000000, + VramSize = 0x00600000, DSPMemStart = 0x1FF00000 }; } diff --git a/include/services/fs.hpp b/include/services/fs.hpp index d14ee4ec..c62328b6 100644 --- a/include/services/fs.hpp +++ b/include/services/fs.hpp @@ -27,8 +27,10 @@ class FSService { std::optional openFileHandle(ArchiveBase* archive, const FSPath& path); // Service commands + void closeArchive(u32 messagePointer); void initialize(u32 messagePointer); void openArchive(u32 messagePointer); + void openFile(u32 messagePointer); void openFileDirectly(u32 messagePointer); public: diff --git a/include/services/gsp_gpu.hpp b/include/services/gsp_gpu.hpp index f3fc57bb..24b6c904 100644 --- a/include/services/gsp_gpu.hpp +++ b/include/services/gsp_gpu.hpp @@ -44,6 +44,7 @@ class GPUService { void processCommandList(u32* cmd); void memoryFill(u32* cmd); void triggerDisplayTransfer(u32* cmd); + void triggerDMARequest(u32* cmd); void flushCacheRegions(u32* cmd); public: diff --git a/src/core/fs/archive_ncch.cpp b/src/core/fs/archive_ncch.cpp index 86cf3cba..e91333ae 100644 --- a/src/core/fs/archive_ncch.cpp +++ b/src/core/fs/archive_ncch.cpp @@ -2,8 +2,13 @@ #include bool SelfNCCHArchive::openFile(const FSPath& path) { + if (!hasRomFS()) { + printf("Tried to open a SelfNCCH file without a RomFS\n"); + return false; + } + if (path.type != PathType::Binary) { - printf("Invalid SelfNCCH path type"); + printf("Invalid SelfNCCH path type\n"); return false; } @@ -25,16 +30,17 @@ ArchiveBase* SelfNCCHArchive::openArchive(const FSPath& path) { } std::optional SelfNCCHArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) { - auto cxi = mem.getCXI(); - if (cxi == nullptr) { - Helpers::panic("Tried to read file from non-existent CXI"); + if (!hasRomFS()) { + Helpers::panic("Tried to read file from non-existent RomFS"); return std::nullopt; } - if (!cxi->hasRomFS()) { - Helpers::panic("Tried to read from CXI without RomFS"); + if (!file->isOpen) { + printf("Tried to read from closed SelfNCCH file session"); + return std::nullopt; } + auto cxi = mem.getCXI(); const u32 romFSSize = cxi->romFS.size; const u32 romFSOffset = cxi->romFS.offset; if ((offset >> 32) || (offset >= romFSSize) || (offset + size >= romFSSize)) { diff --git a/src/core/fs/archive_save_data.cpp b/src/core/fs/archive_save_data.cpp index ff114cfa..451d7972 100644 --- a/src/core/fs/archive_save_data.cpp +++ b/src/core/fs/archive_save_data.cpp @@ -1,17 +1,59 @@ #include "fs/archive_save_data.hpp" +#include #include bool SaveDataArchive::openFile(const FSPath& path) { - Helpers::panic("SaveDataArchive::OpenFile"); - return false; + if (!cartHasSaveData()) { + printf("Tried to read SaveData FS without save data\n"); + return false; + } + + if (path.type == PathType::UTF16 && mem.readString(path.pointer, path.size) == "/") { + printf("Reading root save data dir\n"); + return true; + } + + if (path.type != PathType::Binary) { + printf("Unimplemented SaveData path type: %d\n", path.type); + return false; + } + + return true; } ArchiveBase* SaveDataArchive::openArchive(const FSPath& path) { - Helpers::panic("SaveDataArchive::OpenArchive"); - return nullptr; + if (path.type != PathType::Empty) { + Helpers::panic("Unimplemented path type for SaveData archive: %d\n", path.type); + return nullptr; + } + + return this; } std::optional SaveDataArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) { - Helpers::panic("SaveDataArchive::ReadFile"); - return std::nullopt; + if (!cartHasSaveData()) { + printf("Tried to read SaveData FS without save data\n"); + return std::nullopt; + } + + auto cxi = mem.getCXI(); + const u64 saveSize = cxi->saveData.size(); + + if (offset >= saveSize) { + printf("Tried to read from past the end of save data\n"); + return std::nullopt; + } + + const u64 endOffset = std::min(saveSize, offset + size); // Don't go past the end of the save file + const u32 bytesRead = endOffset - offset; + + if (bytesRead != 0x22) Helpers::panic("Might want to actually implement SaveData"); + + static constexpr std::array saveDataStub = { 0x00, 0x23, 0x3C, 0x77, 0x67, 0x28, 0x30, 0x33, 0x58, 0x61, 0x39, 0x61, 0x48, 0x59, 0x36, 0x55, 0x43, 0x76, 0x58, 0x61, 0x6F, 0x65, 0x48, 0x6D, 0x2B, 0x5E, 0x6F, 0x62, 0x3E, 0x6F, 0x34, 0x00, 0x77, 0x09}; + + for (u32 i = 0; i < bytesRead; i++) { + mem.write8(dataPointer + i, saveDataStub[i]); + } + + return bytesRead; } \ No newline at end of file diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index 78d94157..5b275d5d 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -9,6 +9,9 @@ bool NCCH::loadFromHeader(u8* header, IOFile& file) { return false; } + codeFile.clear(); + saveData.clear(); + size = u64(*(u32*)&header[0x104]) * mediaUnit; // TODO: Maybe don't type pun because big endian will break exheaderSize = *(u32*)&header[0x180]; @@ -46,6 +49,9 @@ bool NCCH::loadFromHeader(u8* header, IOFile& file) { Helpers::panic("Encrypted NCSD file"); } + const u64 saveDataSize = *(u64*)&exheader[0x1C0 + 0x0]; // Size of save data in bytes + saveData.resize(saveDataSize, 0xff); + compressCode = (exheader[0xD] & 1) != 0; stackSize = *(u32*)&exheader[0x1C]; bssSize = *(u32*)&exheader[0x3C]; diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 90b299af..633c52b8 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -4,8 +4,10 @@ namespace FSCommands { enum : u32 { Initialize = 0x08010002, + OpenFile = 0x080201C2, OpenFileDirectly = 0x08030204, - OpenArchive = 0x080C00C2 + OpenArchive = 0x080C00C2, + CloseArchive = 0x080E0080 }; } @@ -66,8 +68,10 @@ std::optional FSService::openArchiveHandle(u32 archiveID, const FSPath& void FSService::handleSyncRequest(u32 messagePointer) { const u32 command = mem.read32(messagePointer); switch (command) { + case FSCommands::CloseArchive: closeArchive(messagePointer); break; case FSCommands::Initialize: initialize(messagePointer); break; case FSCommands::OpenArchive: openArchive(messagePointer); break; + case FSCommands::OpenFile: openFile(messagePointer); break; case FSCommands::OpenFileDirectly: openFileDirectly(messagePointer); break; default: Helpers::panic("FS service requested. Command: %08X\n", command); } @@ -78,6 +82,20 @@ void FSService::initialize(u32 messagePointer) { mem.write32(messagePointer + 4, Result::Success); } +void FSService::closeArchive(u32 messagePointer) { + const Handle handle = static_cast(mem.read64(messagePointer + 4)); // TODO: archive handles should be 64-bit + const auto object = kernel.getObject(handle, KernelObjectType::Archive); + log("FSService::CloseArchive(handle = %X)\n", handle); + + if (object == nullptr) { + log("FSService::CloseArchive: Tried to close invalid archive %X\n", handle); + mem.write32(messagePointer + 4, Result::Failure); + } else { + object->getData()->isOpen = false; + mem.write32(messagePointer + 4, Result::Success); + } +} + void FSService::openArchive(u32 messagePointer) { const u32 archiveID = mem.read32(messagePointer + 4); const u32 archivePathType = mem.read32(messagePointer + 8); @@ -85,7 +103,7 @@ void FSService::openArchive(u32 messagePointer) { const u32 archivePathPointer = mem.read32(messagePointer + 20); FSPath archivePath{ .type = archivePathType, .size = archivePathSize, .pointer = archivePathPointer }; - log("FS::OpenArchive (archive ID = %d, archive path type = %d)\n", archiveID, archivePathType); + log("FS::OpenArchive(archive ID = %d, archive path type = %d)\n", archiveID, archivePathType); std::optional handle = openArchiveHandle(archiveID, archivePath); if (handle.has_value()) { @@ -97,6 +115,37 @@ void FSService::openArchive(u32 messagePointer) { } } +void FSService::openFile(u32 messagePointer) { + const u32 archiveHandle = mem.read64(messagePointer + 8); + const u32 filePathType = mem.read32(messagePointer + 16); + const u32 filePathSize = mem.read32(messagePointer + 20); + const u32 openFlags = mem.read32(messagePointer + 24); + const u32 attributes = mem.read32(messagePointer + 28); + const u32 filePathPointer = mem.read32(messagePointer + 36); + + log("FS::OpenFile\n"); + + auto archiveObject = kernel.getObject(archiveHandle, KernelObjectType::Archive); + if (archiveObject == nullptr) [[unlikely]] { + log("FS::OpenFile: Invalid archive handle %d\n", archiveHandle); + mem.write32(messagePointer + 4, Result::Failure); + return; + } + + ArchiveBase* archive = archiveObject->getData()->archive; + FSPath filePath{ .type = filePathType, .size = filePathSize, .pointer = filePathPointer }; + + std::optional handle = openFileHandle(archive, filePath); + if (!handle.has_value()) { + Helpers::panic("OpenFile: Failed to open file with given path"); + } + else { + mem.write32(messagePointer + 4, Result::Success); + mem.write32(messagePointer + 8, 0x10); // "Move handle descriptor" + mem.write32(messagePointer + 12, handle.value()); + } +} + void FSService::openFileDirectly(u32 messagePointer) { const u32 archiveID = mem.read32(messagePointer + 8); const u32 archivePathType = mem.read32(messagePointer + 12); diff --git a/src/core/services/gsp_gpu.cpp b/src/core/services/gsp_gpu.cpp index 13153a53..e1d5f4cc 100644 --- a/src/core/services/gsp_gpu.cpp +++ b/src/core/services/gsp_gpu.cpp @@ -17,6 +17,7 @@ namespace ServiceCommands { // Commands written to shared memory and processed by TriggerCmdReqQueue namespace GXCommands { enum : u32 { + TriggerDMARequest = 0, ProcessCommandList = 1, MemoryFill = 2, TriggerDisplayTransfer = 3, @@ -223,6 +224,7 @@ void GPUService::processCommandBuffer() { case GXCommands::ProcessCommandList: processCommandList(cmd); break; case GXCommands::MemoryFill: memoryFill(cmd); break; case GXCommands::TriggerDisplayTransfer: triggerDisplayTransfer(cmd); break; + case GXCommands::TriggerDMARequest: triggerDMARequest(cmd); break; case GXCommands::FlushCacheRegions: flushCacheRegions(cmd); break; default: Helpers::panic("GSP::GPU::ProcessCommands: Unknown cmd ID %d", cmdID); } @@ -262,6 +264,16 @@ void GPUService::triggerDisplayTransfer(u32* cmd) { requestInterrupt(GPUInterrupt::PPF); // Send "Display transfer finished" interrupt } +void GPUService::triggerDMARequest(u32* cmd) { + u32 source = cmd[1]; + u32 dest = cmd[2]; + u32 size = cmd[3]; + bool flush = cmd[7] == 1; + + log("GSP::GPU::TriggerDMARequest (source = %08X, dest = %08X, size = %08X) (Unimplemented)\n", source, dest, size); + requestInterrupt(GPUInterrupt::DMA); +} + void GPUService::flushCacheRegions(u32* cmd) { log("GSP::GPU::FlushCacheRegions (Stubbed)\n"); }