From 4e64f722e5ff49e27ffcd44251bde3c7b17f0524 Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Thu, 26 Jan 2023 17:49:17 +0200 Subject: [PATCH] [Kernel] ExitThread [FS] Start implement archive 0x2345678A --- CMakeLists.txt | 8 +-- include/fs/archive_base.hpp | 5 +- include/fs/archive_ncch.hpp | 20 ++---- include/fs/archive_self_ncch.hpp | 27 ++++++++ include/kernel/kernel.hpp | 4 +- include/services/fs.hpp | 6 +- src/core/fs/archive_ncch.cpp | 110 +++--------------------------- src/core/fs/archive_self_ncch.cpp | 110 ++++++++++++++++++++++++++++++ src/core/kernel/kernel.cpp | 2 + src/core/kernel/threads.cpp | 53 +++++++++++--- src/core/services/fs.cpp | 1 + 11 files changed, 215 insertions(+), 131 deletions(-) create mode 100644 include/fs/archive_self_ncch.hpp create mode 100644 src/core/fs/archive_self_ncch.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e96014e2..a7a1b61b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,8 +67,8 @@ set(PICA_SOURCE_FILES src/core/PICA/gpu.cpp src/core/PICA/regs.cpp src/core/PICA set(RENDERER_GL_SOURCE_FILES src/core/renderer_gl/renderer_gl.cpp) set(LOADER_SOURCE_FILES src/core/loader/elf.cpp src/core/loader/ncsd.cpp src/core/loader/ncch.cpp) -set(FS_SOURCE_FILES src/core/fs/archive_ncch.cpp src/core/fs/archive_save_data.cpp src/core/fs/archive_sdmc.cpp - src/core/fs/archive_ext_save_data.cpp +set(FS_SOURCE_FILES src/core/fs/archive_self_ncch.cpp src/core/fs/archive_save_data.cpp src/core/fs/archive_sdmc.cpp + src/core/fs/archive_ext_save_data.cpp src/core/fs/archive_ncch.cpp ) set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/opengl.hpp include/termcolor.hpp @@ -80,13 +80,13 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/opengl.hpp inc include/PICA/gpu.hpp include/PICA/regs.hpp include/services/ndm.hpp include/PICA/shader.hpp include/PICA/shader_unit.hpp include/PICA/float_types.hpp include/logger.hpp include/loader/ncch.hpp include/loader/ncsd.hpp include/io_file.hpp - include/loader/lz77.hpp include/fs/archive_base.hpp include/fs/archive_ncch.hpp + include/loader/lz77.hpp include/fs/archive_base.hpp include/fs/archive_self_ncch.hpp include/services/dsp.hpp include/services/cfg.hpp include/services/region_codes.hpp include/fs/archive_save_data.hpp include/fs/archive_sdmc.hpp include/services/ptm.hpp include/services/mic.hpp include/services/cecd.hpp include/renderer_gl/renderer_gl.hpp include/renderer_gl/surfaces.hpp include/renderer_gl/surface_cache.hpp include/services/ac.hpp include/services/am.hpp include/services/boss.hpp include/services/frd.hpp include/services/nim.hpp - include/fs/archive_ext_save_data.hpp include/services/shared_font.hpp + include/fs/archive_ext_save_data.hpp include/services/shared_font.hpp include/fs/archive_ncch.hpp ) set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp diff --git a/include/fs/archive_base.hpp b/include/fs/archive_base.hpp index 5ca6b775..54284525 100644 --- a/include/fs/archive_base.hpp +++ b/include/fs/archive_base.hpp @@ -26,7 +26,9 @@ namespace ArchiveID { SharedExtSaveData = 7, SystemSaveData = 8, SDMC = 9, - SDMCWriteOnly = 0xA + SDMCWriteOnly = 0xA, + + SavedataAndNcch = 0x2345678A }; static std::string toString(u32 id) { @@ -38,6 +40,7 @@ namespace ArchiveID { case SystemSaveData: return "SystemSaveData"; case SDMC: return "SDMC"; case SDMCWriteOnly: return "SDMC (Write-only)"; + case SavedataAndNcch: return "Savedata & NCCH (archive 0x2345678A)"; default: return "Unknown archive"; } } diff --git a/include/fs/archive_ncch.hpp b/include/fs/archive_ncch.hpp index d76513e3..10919da5 100644 --- a/include/fs/archive_ncch.hpp +++ b/include/fs/archive_ncch.hpp @@ -1,27 +1,15 @@ #pragma once #include "archive_base.hpp" -class SelfNCCHArchive : public ArchiveBase { +class NCCHArchive : public ArchiveBase { public: - SelfNCCHArchive(Memory& mem) : ArchiveBase(mem) {} + NCCHArchive(Memory& mem) : ArchiveBase(mem) {} - u64 getFreeBytes() override { return 0; } - std::string name() override { return "SelfNCCH"; } + u64 getFreeBytes() override { Helpers::panic("NCCH::GetFreeBytes unimplemented"); return 0; } + std::string name() override { return "NCCH"; } ArchiveBase* openArchive(const FSPath& path) override; CreateFileResult createFile(const FSPath& path, u64 size) override; FileDescriptor openFile(const FSPath& path, const FilePerms& perms) 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()); - } - - // Returns whether the cart has an ExeFS (All executable carts should have an ExeFS. This is just here to be safe) - bool hasExeFS() { - auto cxi = mem.getCXI(); - return (cxi != nullptr && cxi->hasExeFS()); - } }; \ No newline at end of file diff --git a/include/fs/archive_self_ncch.hpp b/include/fs/archive_self_ncch.hpp new file mode 100644 index 00000000..d76513e3 --- /dev/null +++ b/include/fs/archive_self_ncch.hpp @@ -0,0 +1,27 @@ +#pragma once +#include "archive_base.hpp" + +class SelfNCCHArchive : public ArchiveBase { +public: + SelfNCCHArchive(Memory& mem) : ArchiveBase(mem) {} + + u64 getFreeBytes() override { return 0; } + std::string name() override { return "SelfNCCH"; } + + ArchiveBase* openArchive(const FSPath& path) override; + CreateFileResult createFile(const FSPath& path, u64 size) override; + FileDescriptor openFile(const FSPath& path, const FilePerms& perms) 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()); + } + + // Returns whether the cart has an ExeFS (All executable carts should have an ExeFS. This is just here to be safe) + bool hasExeFS() { + auto cxi = mem.getCXI(); + return (cxi != nullptr && cxi->hasExeFS()); + } +}; \ No newline at end of file diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index 539454a1..c7aa4e86 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -33,7 +33,8 @@ class Kernel { Handle errorPortHandle; // Handle for the err:f port used for displaying errors u32 arbiterCount; - u32 threadCount; + u32 threadCount; // How many threads in our thread pool have been used as of now (Up to 32) + u32 aliveThreadCount; // How many of these threads are actually alive? ServiceManager serviceManager; // Top 8 bits are the major version, bottom 8 are the minor version @@ -95,6 +96,7 @@ private: void createThread(); void controlMemory(); void duplicateHandle(); + void exitThread(); void mapMemoryBlock(); void queryMemory(); void getProcessID(); diff --git a/include/services/fs.hpp b/include/services/fs.hpp index b5a8ad98..823bc413 100644 --- a/include/services/fs.hpp +++ b/include/services/fs.hpp @@ -3,6 +3,7 @@ #include "fs/archive_ncch.hpp" #include "fs/archive_save_data.hpp" #include "fs/archive_sdmc.hpp" +#include "fs/archive_self_ncch.hpp" #include "helpers.hpp" #include "kernel_types.hpp" #include "logger.hpp" @@ -18,11 +19,12 @@ class FSService { MAKE_LOG_FUNCTION(log, fsLogger) - // The different filesystem archives (Save data, RomFS+ExeFS, etc) + // The different filesystem archives (Save data, SelfNCCH, SDMC, NCCH, ExtData, etc) SelfNCCHArchive selfNcch; SaveDataArchive saveData; ExtSaveDataArchive sharedExtSaveData; SDMCArchive sdmc; + NCCHArchive ncch; ArchiveBase* getArchiveFromID(u32 id); std::optional openArchiveHandle(u32 archiveID, const FSPath& path); @@ -45,7 +47,7 @@ class FSService { u32 priority; public: - FSService(Memory& mem, Kernel& kernel) : mem(mem), saveData(mem), sharedExtSaveData(mem), sdmc(mem), selfNcch(mem), + FSService(Memory& mem, Kernel& kernel) : mem(mem), saveData(mem), sharedExtSaveData(mem), sdmc(mem), selfNcch(mem), ncch(mem), kernel(kernel) { sharedExtSaveData.isShared = true; // Need to do this here because templates and virtual classes do not mix well diff --git a/src/core/fs/archive_ncch.cpp b/src/core/fs/archive_ncch.cpp index 7557b639..bed48b04 100644 --- a/src/core/fs/archive_ncch.cpp +++ b/src/core/fs/archive_ncch.cpp @@ -1,110 +1,22 @@ #include "fs/archive_ncch.hpp" #include -// The part of the NCCH archive we're trying to access. Depends on the first 4 bytes of the binary file path -namespace PathType { - enum : u32 { - RomFS = 0, - ExeFS = 2 - }; -}; - -CreateFileResult SelfNCCHArchive::createFile(const FSPath& path, u64 size) { - Helpers::panic("[SelfNCCH] CreateFile not yet supported"); +CreateFileResult NCCHArchive::createFile(const FSPath& path, u64 size) { + Helpers::panic("[NCCH] CreateFile not yet supported"); return CreateFileResult::Success; } -FileDescriptor SelfNCCHArchive::openFile(const FSPath& path, const FilePerms& perms) { - if (!hasRomFS()) { - printf("Tried to open a SelfNCCH file without a RomFS\n"); - return FileError; - } - - if (path.type != PathType::Binary || path.binary.size() != 12) { - printf("Invalid SelfNCCH path type\n"); - return FileError; - } - - // Where to read the file from. (https://www.3dbrew.org/wiki/Filesystem_services#SelfNCCH_File_Path_Data_Format) - // We currently only know how to read from an NCCH's RomFS, ie type = 0 - const u32 type = *(u32*)&path.binary[0]; // TODO: Get rid of UB here - if (type != PathType::RomFS && type != PathType::ExeFS) { - Helpers::panic("Read from NCCH's non-RomFS & non-exeFS section!"); - } - - return NoFile; // No file descriptor needed for RomFS +FileDescriptor NCCHArchive::openFile(const FSPath& path, const FilePerms& perms) { + Helpers::panic("NCCHArchive::OpenFile: Unimplemented"); + return FileError; } -ArchiveBase* SelfNCCHArchive::openArchive(const FSPath& path) { - if (path.type != PathType::Empty) { - printf("Invalid path type for SelfNCCH archive: %d\n", path.type); - return nullptr; - } - - return this; +ArchiveBase* NCCHArchive::openArchive(const FSPath& path) { + Helpers::panic("NCCHArchive::OpenArchive: Unimplemented"); + return nullptr; } -std::optional SelfNCCHArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) { - const FSPath& path = file->path; // Path of the file - const u32 type = *(u32*)&path.binary[0]; // Type of the path - - if (type == PathType::RomFS && !hasRomFS()) { - Helpers::panic("Tried to read file from non-existent RomFS"); - return std::nullopt; - } - - if (type == PathType::ExeFS && !hasExeFS()) { - Helpers::panic("Tried to read file from non-existent RomFS"); - return std::nullopt; - } - - if (!file->isOpen) { - printf("Tried to read from closed SelfNCCH file session"); - return std::nullopt; - } - - auto cxi = mem.getCXI(); - IOFile& ioFile = mem.CXIFile; - - // Seek to file offset depending on if we're reading from RomFS, ExeFS, etc - switch (type) { - case PathType::RomFS: { - const u32 romFSSize = cxi->romFS.size; - const u32 romFSOffset = cxi->romFS.offset; - if ((offset >> 32) || (offset >= romFSSize) || (offset + size >= romFSSize)) { - Helpers::panic("Tried to read from SelfNCCH with too big of an offset"); - } - - if (!ioFile.seek(cxi->fileOffset + romFSOffset + offset + 0x1000)) { - Helpers::panic("Failed to seek while reading from RomFS"); - } - break; - } - - case PathType::ExeFS: { - const u32 exeFSSize = cxi->exeFS.size; - const u32 exeFSOffset = cxi->exeFS.offset; - if ((offset >> 32) || (offset >= exeFSSize) || (offset + size >= exeFSSize)) { - Helpers::panic("Tried to read from SelfNCCH with too big of an offset"); - } - - if (!ioFile.seek(cxi->fileOffset + exeFSOffset + offset)) { // TODO: Not sure if this needs the + 0x1000 - Helpers::panic("Failed to seek while reading from ExeFS"); - } - break; - } - } - - std::unique_ptr data(new u8[size]); - auto [success, bytesRead] = ioFile.readBytes(&data[0], size); - - if (!success) { - Helpers::panic("Failed to read from NCCH archive"); - } - - for (u64 i = 0; i < bytesRead; i++) { - mem.write8(dataPointer + i, data[i]); - } - - return bytesRead; +std::optional NCCHArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) { + Helpers::panic("NCCHArchive::ReadFile: Unimplemented"); + return std::nullopt; } \ No newline at end of file diff --git a/src/core/fs/archive_self_ncch.cpp b/src/core/fs/archive_self_ncch.cpp new file mode 100644 index 00000000..47eaff9c --- /dev/null +++ b/src/core/fs/archive_self_ncch.cpp @@ -0,0 +1,110 @@ +#include "fs/archive_self_ncch.hpp" +#include + +// The part of the NCCH archive we're trying to access. Depends on the first 4 bytes of the binary file path +namespace PathType { + enum : u32 { + RomFS = 0, + ExeFS = 2 + }; +}; + +CreateFileResult SelfNCCHArchive::createFile(const FSPath& path, u64 size) { + Helpers::panic("[SelfNCCH] CreateFile not yet supported"); + return CreateFileResult::Success; +} + +FileDescriptor SelfNCCHArchive::openFile(const FSPath& path, const FilePerms& perms) { + if (!hasRomFS()) { + printf("Tried to open a SelfNCCH file without a RomFS\n"); + return FileError; + } + + if (path.type != PathType::Binary || path.binary.size() != 12) { + printf("Invalid SelfNCCH path type\n"); + return FileError; + } + + // Where to read the file from. (https://www.3dbrew.org/wiki/Filesystem_services#SelfNCCH_File_Path_Data_Format) + // We currently only know how to read from an NCCH's RomFS, ie type = 0 + const u32 type = *(u32*)&path.binary[0]; // TODO: Get rid of UB here + if (type != PathType::RomFS && type != PathType::ExeFS) { + Helpers::panic("Read from NCCH's non-RomFS & non-exeFS section!"); + } + + return NoFile; // No file descriptor needed for RomFS +} + +ArchiveBase* SelfNCCHArchive::openArchive(const FSPath& path) { + if (path.type != PathType::Empty) { + printf("Invalid path type for SelfNCCH archive: %d\n", path.type); + return nullptr; + } + + return this; +} + +std::optional SelfNCCHArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) { + const FSPath& path = file->path; // Path of the file + const u32 type = *(u32*)&path.binary[0]; // Type of the path + + if (type == PathType::RomFS && !hasRomFS()) { + Helpers::panic("Tried to read file from non-existent RomFS"); + return std::nullopt; + } + + if (type == PathType::ExeFS && !hasExeFS()) { + Helpers::panic("Tried to read file from non-existent RomFS"); + return std::nullopt; + } + + if (!file->isOpen) { + printf("Tried to read from closed SelfNCCH file session"); + return std::nullopt; + } + + auto cxi = mem.getCXI(); + IOFile& ioFile = mem.CXIFile; + + // Seek to file offset depending on if we're reading from RomFS, ExeFS, etc + switch (type) { + case PathType::RomFS: { + const u32 romFSSize = cxi->romFS.size; + const u32 romFSOffset = cxi->romFS.offset; + if ((offset >> 32) || (offset >= romFSSize) || (offset + size >= romFSSize)) { + Helpers::panic("Tried to read from SelfNCCH with too big of an offset"); + } + + if (!ioFile.seek(cxi->fileOffset + romFSOffset + offset + 0x1000)) { + Helpers::panic("Failed to seek while reading from RomFS"); + } + break; + } + + case PathType::ExeFS: { + const u32 exeFSSize = cxi->exeFS.size; + const u32 exeFSOffset = cxi->exeFS.offset; + if ((offset >> 32) || (offset >= exeFSSize) || (offset + size >= exeFSSize)) { + Helpers::panic("Tried to read from SelfNCCH with too big of an offset"); + } + + if (!ioFile.seek(cxi->fileOffset + exeFSOffset + offset)) { // TODO: Not sure if this needs the + 0x1000 + Helpers::panic("Failed to seek while reading from ExeFS"); + } + break; + } + } + + std::unique_ptr data(new u8[size]); + auto [success, bytesRead] = ioFile.readBytes(&data[0], size); + + if (!success) { + Helpers::panic("Failed to read from NCCH archive"); + } + + for (u64 i = 0; i < bytesRead; i++) { + mem.write8(dataPointer + i, data[i]); + } + + return bytesRead; +} \ No newline at end of file diff --git a/src/core/kernel/kernel.cpp b/src/core/kernel/kernel.cpp index a2fd7896..7afb5f9d 100644 --- a/src/core/kernel/kernel.cpp +++ b/src/core/kernel/kernel.cpp @@ -29,6 +29,7 @@ void Kernel::serviceSVC(u32 svc) { case 0x01: controlMemory(); break; case 0x02: queryMemory(); break; case 0x08: createThread(); break; + case 0x09: exitThread(); break; case 0x0A: svcSleepThread(); break; case 0x0B: getThreadPriority(); break; case 0x0C: setThreadPriority(); break; @@ -104,6 +105,7 @@ void Kernel::reset() { handleCounter = 0; arbiterCount = 0; threadCount = 0; + aliveThreadCount = 0; for (auto& t : threads) { t.status = ThreadStatus::Dead; diff --git a/src/core/kernel/threads.cpp b/src/core/kernel/threads.cpp index c42f6c06..3ff12af2 100644 --- a/src/core/kernel/threads.cpp +++ b/src/core/kernel/threads.cpp @@ -1,3 +1,4 @@ +#include #include #include "kernel.hpp" #include "arm_defs.hpp" @@ -81,7 +82,14 @@ void Kernel::switchToNextThread() { if (!newThreadIndex.has_value()) { log("Kernel tried to switch to the next thread but none found. Switching to random thread\n"); - switchThread(rand() % threadCount); + assert(aliveThreadCount != 0); + + int index; + do { + index = rand() % threadCount; + } while (threads[index].status == ThreadStatus::Dead); // TODO: Pray this doesn't hang + + switchThread(index); } else { switchThread(newThreadIndex.value()); } @@ -99,12 +107,25 @@ void Kernel::rescheduleThreads() { // Internal OS function to spawn a thread Handle Kernel::makeThread(u32 entrypoint, u32 initialSP, u32 priority, s32 id, u32 arg, ThreadStatus status) { - if (threadCount >= appResourceLimits.maxThreads) { - Helpers::panic("Overflowed the number of threads"); - } - threadIndices.push_back(threadCount); + int index; // Index of the created thread in the threads array - Thread& t = threads[threadCount++]; // Reference to thread data + if (threadCount < appResourceLimits.maxThreads) [[likely]] { // If we have not yet created over too many threads + index = threadCount++; + } else if (aliveThreadCount < appResourceLimits.maxThreads) { // If we have created many threads but at least one is dead & reusable + for (int i = 0; i < threads.size(); i++) { + if (threads[i].status == ThreadStatus::Dead) { + index = i; + break; + } + } + } else { // There is no thread we can use, we're screwed + Helpers::panic("Overflowed thread count!!"); + } + + aliveThreadCount++; + + threadIndices.push_back(index); + Thread& t = threads[index]; // Reference to thread data Handle ret = makeObject(KernelObjectType::Thread); objects[ret].data = &t; @@ -233,7 +254,7 @@ void Kernel::getThreadID() { void Kernel::getThreadPriority() { const Handle handle = regs[1]; - log("GetThreadPriority (handle = %X)\n", handle); + logSVC("GetThreadPriority (handle = %X)\n", handle); if (handle == KernelHandles::CurrentThread) { regs[0] = SVCResult::Success; @@ -252,7 +273,7 @@ void Kernel::getThreadPriority() { void Kernel::setThreadPriority() { const Handle handle = regs[0]; const u32 priority = regs[1]; - log("SetThreadPriority (handle = %X, priority = %X)\n", handle, priority); + logSVC("SetThreadPriority (handle = %X, priority = %X)\n", handle, priority); if (priority > 0x3F) { regs[0] = SVCResult::BadThreadPriority; @@ -276,6 +297,22 @@ void Kernel::setThreadPriority() { rescheduleThreads(); } +void Kernel::exitThread() { + logSVC("ExitThread\n"); + + // Remove the index of this thread from the thread indices vector + for (int i = 0; i < threadIndices.size(); i++) { + if (threadIndices[i] == currentThreadIndex) + threadIndices.erase(threadIndices.begin() + i); + } + + Thread& t = threads[currentThreadIndex]; + t.status = ThreadStatus::Dead; + aliveThreadCount--; + + switchToNextThread(); +} + void Kernel::createMutex() { bool locked = regs[1] != 0; logSVC("CreateMutex (locked = %s)\n", locked ? "yes" : "no"); diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 85178afc..ccca84c1 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -49,6 +49,7 @@ ArchiveBase* FSService::getArchiveFromID(u32 id) { case ArchiveID::SaveData: return &saveData; case ArchiveID::SharedExtSaveData: return &sharedExtSaveData; case ArchiveID::SDMC: return &sdmc; + case ArchiveID::SavedataAndNcch: return &ncch; // This can only access NCCH outside of FSPXI default: Helpers::panic("Unknown archive. ID: %d\n", id); return nullptr;