From 863edac15209753fed83986669f40894f6139851 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Tue, 2 Jul 2024 19:19:37 +0200 Subject: [PATCH] revert formatting --- include/audio/hle_core.hpp | 6 +- include/capstone.hpp | 2 +- include/fs/archive_base.hpp | 367 ++++++++++++++++---------------- include/kernel/handles.hpp | 36 ++-- include/kernel/kernel.hpp | 14 +- include/kernel/kernel_types.hpp | 266 ++++++++++++----------- include/memory.hpp | 48 +++-- 7 files changed, 373 insertions(+), 366 deletions(-) diff --git a/include/audio/hle_core.hpp b/include/audio/hle_core.hpp index 26b78cd2e..b59dc811c 100644 --- a/include/audio/hle_core.hpp +++ b/include/audio/hle_core.hpp @@ -33,8 +33,8 @@ namespace Audio { SampleFormat format; SourceType sourceType; - bool fromQueue = false; // Is this buffer from the buffer queue or an embedded buffer? - bool hasPlayedOnce = false; // Has the buffer been played at least once before? + bool fromQueue = false; // Is this buffer from the buffer queue or an embedded buffer? + bool hasPlayedOnce = false; // Has the buffer been played at least once before? bool operator<(const Buffer& other) const { // Lower ID = Higher priority @@ -136,7 +136,7 @@ namespace Audio { const auto counter0 = dspRam.region0.frameCounter; const auto counter1 = dspRam.region1.frameCounter; - // HandleType wraparound cases first + // Handle wraparound cases first if (counter0 == 0xffff && counter1 != 0xfffe) { return 1; } else if (counter1 == 0xffff && counter0 != 0xfffe) { diff --git a/include/capstone.hpp b/include/capstone.hpp index 3bbdff2fa..32ca404f2 100644 --- a/include/capstone.hpp +++ b/include/capstone.hpp @@ -8,7 +8,7 @@ namespace Common { class CapstoneDisassembler { - csh handle; // HandleType to our disassembler object + csh handle; // Handle to our disassembler object cs_insn* instructions = nullptr; // Pointer to instruction object bool initialized = false; diff --git a/include/fs/archive_base.hpp b/include/fs/archive_base.hpp index d315d2921..cbffc14c1 100644 --- a/include/fs/archive_base.hpp +++ b/include/fs/archive_base.hpp @@ -7,7 +7,6 @@ #include #include #include - #include "helpers.hpp" #include "memory.hpp" #include "result.hpp" @@ -16,108 +15,108 @@ using Result::HorizonResult; namespace PathType { - enum : u32 { - Invalid = 0, - Empty = 1, - Binary = 2, - ASCII = 3, - UTF16 = 4, - }; + enum : u32 { + Invalid = 0, + Empty = 1, + Binary = 2, + ASCII = 3, + UTF16 = 4, + }; } namespace ArchiveID { - enum : u32 { - SelfNCCH = 3, - SaveData = 4, - ExtSaveData = 6, - SharedExtSaveData = 7, - SystemSaveData = 8, - SDMC = 9, - SDMCWriteOnly = 0xA, - - SavedataAndNcch = 0x2345678A, - // 3DBrew: This is the same as the regular SaveData archive, except with this the savedata ID and mediatype is loaded from the input archive - // lowpath. - UserSaveData1 = 0x567890B2, - // 3DBrew: Similar to 0x567890B2 but can only access Accessible Save specified in exheader? - UserSaveData2 = 0x567890B4, - }; - - static std::string toString(u32 id) { - switch (id) { - case SelfNCCH: return "SelfNCCH"; - case SaveData: return "SaveData"; - case ExtSaveData: return "ExtSaveData"; - case SharedExtSaveData: return "SharedExtSaveData"; - 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"; - } - } -} // namespace ArchiveID + enum : u32 { + SelfNCCH = 3, + SaveData = 4, + ExtSaveData = 6, + SharedExtSaveData = 7, + SystemSaveData = 8, + SDMC = 9, + SDMCWriteOnly = 0xA, + + SavedataAndNcch = 0x2345678A, + // 3DBrew: This is the same as the regular SaveData archive, except with this the savedata ID and mediatype is loaded from the input archive + // lowpath. + UserSaveData1 = 0x567890B2, + // 3DBrew: Similar to 0x567890B2 but can only access Accessible Save specified in exheader? + UserSaveData2 = 0x567890B4, + }; + + static std::string toString(u32 id) { + switch (id) { + case SelfNCCH: return "SelfNCCH"; + case SaveData: return "SaveData"; + case ExtSaveData: return "ExtSaveData"; + case SharedExtSaveData: return "SharedExtSaveData"; + 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"; + } + } +} struct FSPath { - u32 type = PathType::Invalid; - - std::vector binary; // Path data for binary paths - std::string string; // Path data for ASCII paths - std::u16string utf16_string; - - FSPath() {} - - FSPath(u32 type, const std::vector& vec) : type(type) { - switch (type) { - case PathType::Binary: binary = std::move(vec); break; - - case PathType::ASCII: - string.resize(vec.size() - 1); // -1 because of the null terminator - std::memcpy(string.data(), vec.data(), vec.size() - 1); // Copy string data - break; - - case PathType::UTF16: { - const size_t size = vec.size() / sizeof(u16) - 1; // Character count. -1 because null terminator here too - utf16_string.resize(size); - std::memcpy(utf16_string.data(), vec.data(), size * sizeof(u16)); - break; - }; - } - } + u32 type = PathType::Invalid; + + std::vector binary; // Path data for binary paths + std::string string; // Path data for ASCII paths + std::u16string utf16_string; + + FSPath() {} + + FSPath(u32 type, const std::vector& vec) : type(type) { + switch (type) { + case PathType::Binary: binary = std::move(vec); break; + + case PathType::ASCII: + string.resize(vec.size() - 1); // -1 because of the null terminator + std::memcpy(string.data(), vec.data(), vec.size() - 1); // Copy string data + break; + + case PathType::UTF16: { + const size_t size = vec.size() / sizeof(u16) - 1; // Character count. -1 because null terminator here too + utf16_string.resize(size); + std::memcpy(utf16_string.data(), vec.data(), size * sizeof(u16)); + break; + }; + } + } }; struct FilePerms { - u32 raw; + u32 raw; - FilePerms(u32 val) : raw(val) {} - bool read() const { return (raw & 1) != 0; } - bool write() const { return (raw & 2) != 0; } - bool create() const { return (raw & 4) != 0; } + FilePerms(u32 val) : raw(val) {} + bool read() const { return (raw & 1) != 0; } + bool write() const { return (raw & 2) != 0; } + bool create() const { return (raw & 4) != 0; } }; class ArchiveBase; struct FileSession { - ArchiveBase* archive = nullptr; - FILE* fd = nullptr; // File descriptor for file sessions that require them. - FSPath path; - FSPath archivePath; - u32 priority = 0; // TODO: What does this even do - bool isOpen; - - FileSession(ArchiveBase* archive, const FSPath& filePath, const FSPath& archivePath, FILE* fd, bool isOpen = true) - : archive(archive), path(filePath), archivePath(archivePath), fd(fd), isOpen(isOpen), priority(0) {} - - // For cloning a file session - FileSession(const FileSession& other) - : archive(other.archive), path(other.path), archivePath(other.archivePath), fd(other.fd), isOpen(other.isOpen), priority(other.priority) {} + ArchiveBase* archive = nullptr; + FILE* fd = nullptr; // File descriptor for file sessions that require them. + FSPath path; + FSPath archivePath; + u32 priority = 0; // TODO: What does this even do + bool isOpen; + + FileSession(ArchiveBase* archive, const FSPath& filePath, const FSPath& archivePath, FILE* fd, bool isOpen = true) + : archive(archive), path(filePath), archivePath(archivePath), fd(fd), isOpen(isOpen), priority(0) {} + + // For cloning a file session + FileSession(const FileSession& other) + : archive(other.archive), path(other.path), archivePath(other.archivePath), fd(other.fd), isOpen(other.isOpen), priority(other.priority) {} }; struct ArchiveSession { - ArchiveBase* archive = nullptr; - FSPath path; - bool isOpen; + ArchiveBase* archive = nullptr; + FSPath path; + bool isOpen; - ArchiveSession(ArchiveBase* archive, const FSPath& filePath, bool isOpen = true) : archive(archive), path(filePath), isOpen(isOpen) {} + ArchiveSession(ArchiveBase* archive, const FSPath& filePath, bool isOpen = true) : archive(archive), path(filePath), isOpen(isOpen) {} }; struct DirectoryEntry { @@ -155,104 +154,104 @@ struct DirectorySession { using FileDescriptor = std::optional; class ArchiveBase { - public: - struct FormatInfo { - u32 size; // Archive size - u32 numOfDirectories; // Number of directories - u32 numOfFiles; // Number of files - bool duplicateData; // Whether to duplicate data or not - }; - - protected: - using HandleType = u32; - - static constexpr FileDescriptor NoFile = nullptr; - static constexpr FileDescriptor FileError = std::nullopt; - Memory& mem; - - // Returns if a specified 3DS path in UTF16 or ASCII format is safe or not - // A 3DS path is considered safe if its first character is '/' which means we're not trying to access anything outside the root of the fs - // And if it doesn't contain enough instances of ".." (Indicating "climb up a folder" in filesystems) to let the software climb up the directory - // tree And access files outside of the emulator's app data folder - template - bool isPathSafe(const FSPath& path) { - static_assert(format == PathType::ASCII || format == PathType::UTF16); - using String = typename std::conditional::type; // String type for the path - using Char = typename String::value_type; // Char type for the path - - String pathString, dots; - if constexpr (std::is_same()) { - pathString = path.utf16_string; - dots = u".."; - } else { - pathString = path.string; - dots = ".."; - } - - // If the path string doesn't begin with / then that means it's accessing outside the FS root, which is invalid & unsafe - if (pathString[0] != Char('/')) return false; - - // Counts how many folders sans the root our file is nested under. - // If it's < 0 at any point of parsing, then the path is unsafe and tries to crawl outside our file sandbox. - // If it's 0 then this is the FS root. - // If it's > 0 then we're in a subdirectory of the root. - int level = 0; - - // Split the string on / characters and see how many of the substrings are ".." - size_t pos = 0; - while ((pos = pathString.find(Char('/'))) != String::npos) { - String token = pathString.substr(0, pos); - pathString.erase(0, pos + 1); - - if (token == dots) { - level--; - if (level < 0) return false; - } else { - level++; - } - } - - return true; - } - - public: - virtual std::string name() = 0; - virtual u64 getFreeBytes() = 0; - virtual HorizonResult createFile(const FSPath& path, u64 size) = 0; - virtual HorizonResult deleteFile(const FSPath& path) = 0; - - virtual Rust::Result getFormatInfo(const FSPath& path) { - Helpers::panic("Unimplemented GetFormatInfo for %s archive", name().c_str()); - // Return a dummy struct just to avoid the UB of not returning anything, even if we panic - return Ok(FormatInfo{.size = 0, .numOfDirectories = 0, .numOfFiles = 0, .duplicateData = false}); - } - - virtual HorizonResult createDirectory(const FSPath& path) { - Helpers::panic("Unimplemented CreateDirectory for %s archive", name().c_str()); - return Result::FS::AlreadyExists; - } - - // Returns nullopt if opening the file failed, otherwise returns a file descriptor to it (nullptr if none is needed) - virtual FileDescriptor openFile(const FSPath& path, const FilePerms& perms) = 0; - virtual Rust::Result openArchive(const FSPath& path) = 0; - - virtual Rust::Result openDirectory(const FSPath& path) { - Helpers::panic("Unimplemented OpenDirectory for %s archive", name().c_str()); - return Err(Result::FS::FileNotFoundAlt); - } - - virtual void format(const FSPath& path, const FormatInfo& info) { Helpers::panic("Unimplemented Format for %s archive", name().c_str()); } - - virtual HorizonResult renameFile(const FSPath& oldPath, const FSPath& newPath) { - Helpers::panic("Unimplemented RenameFile for %s archive", name().c_str()); - return Result::Success; - } - - // Read size bytes from a file starting at offset "offset" into a certain buffer in memory - // Returns the number of bytes read, or nullopt if the read failed - virtual std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) = 0; - - ArchiveBase(Memory& mem) : mem(mem) {} +public: + struct FormatInfo { + u32 size; // Archive size + u32 numOfDirectories; // Number of directories + u32 numOfFiles; // Number of files + bool duplicateData; // Whether to duplicate data or not + }; + +protected: + using HandleType = u32; + + static constexpr FileDescriptor NoFile = nullptr; + static constexpr FileDescriptor FileError = std::nullopt; + Memory& mem; + + // Returns if a specified 3DS path in UTF16 or ASCII format is safe or not + // A 3DS path is considered safe if its first character is '/' which means we're not trying to access anything outside the root of the fs + // And if it doesn't contain enough instances of ".." (Indicating "climb up a folder" in filesystems) to let the software climb up the directory + // tree And access files outside of the emulator's app data folder + template + bool isPathSafe(const FSPath& path) { + static_assert(format == PathType::ASCII || format == PathType::UTF16); + using String = typename std::conditional::type; // String type for the path + using Char = typename String::value_type; // Char type for the path + + String pathString, dots; + if constexpr (std::is_same()) { + pathString = path.utf16_string; + dots = u".."; + } else { + pathString = path.string; + dots = ".."; + } + + // If the path string doesn't begin with / then that means it's accessing outside the FS root, which is invalid & unsafe + if (pathString[0] != Char('/')) return false; + + // Counts how many folders sans the root our file is nested under. + // If it's < 0 at any point of parsing, then the path is unsafe and tries to crawl outside our file sandbox. + // If it's 0 then this is the FS root. + // If it's > 0 then we're in a subdirectory of the root. + int level = 0; + + // Split the string on / characters and see how many of the substrings are ".." + size_t pos = 0; + while ((pos = pathString.find(Char('/'))) != String::npos) { + String token = pathString.substr(0, pos); + pathString.erase(0, pos + 1); + + if (token == dots) { + level--; + if (level < 0) return false; + } else { + level++; + } + } + + return true; + } + +public: + virtual std::string name() = 0; + virtual u64 getFreeBytes() = 0; + virtual HorizonResult createFile(const FSPath& path, u64 size) = 0; + virtual HorizonResult deleteFile(const FSPath& path) = 0; + + virtual Rust::Result getFormatInfo(const FSPath& path) { + Helpers::panic("Unimplemented GetFormatInfo for %s archive", name().c_str()); + // Return a dummy struct just to avoid the UB of not returning anything, even if we panic + return Ok(FormatInfo{.size = 0, .numOfDirectories = 0, .numOfFiles = 0, .duplicateData = false}); + } + + virtual HorizonResult createDirectory(const FSPath& path) { + Helpers::panic("Unimplemented CreateDirectory for %s archive", name().c_str()); + return Result::FS::AlreadyExists; + } + + // Returns nullopt if opening the file failed, otherwise returns a file descriptor to it (nullptr if none is needed) + virtual FileDescriptor openFile(const FSPath& path, const FilePerms& perms) = 0; + virtual Rust::Result openArchive(const FSPath& path) = 0; + + virtual Rust::Result openDirectory(const FSPath& path) { + Helpers::panic("Unimplemented OpenDirectory for %s archive", name().c_str()); + return Err(Result::FS::FileNotFoundAlt); + } + + virtual void format(const FSPath& path, const FormatInfo& info) { Helpers::panic("Unimplemented Format for %s archive", name().c_str()); } + + virtual HorizonResult renameFile(const FSPath& oldPath, const FSPath& newPath) { + Helpers::panic("Unimplemented RenameFile for %s archive", name().c_str()); + return Result::Success; + } + + // Read size bytes from a file starting at offset "offset" into a certain buffer in memory + // Returns the number of bytes read, or nullopt if the read failed + virtual std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) = 0; + + ArchiveBase(Memory& mem) : mem(mem) {} }; struct ArchiveResource { diff --git a/include/kernel/handles.hpp b/include/kernel/handles.hpp index 8827a5812..86275389e 100644 --- a/include/kernel/handles.hpp +++ b/include/kernel/handles.hpp @@ -5,19 +5,19 @@ using HandleType = u32; namespace KernelHandles { enum : u32 { - Max = 0xFFFF7FFF, // Max handle the kernel can automagically allocate + Max = 0xFFFF7FFF, // Max handle the kernel can automagically allocate // Hardcoded handles - CurrentThread = 0xFFFF8000, // Used by the original kernel - CurrentProcess = 0xFFFF8001, // Used by the original kernel - AC, // Something network related - ACT, // Handles NNID accounts - AM, // Application manager - APT, // App Title something service? - BOSS, // Streetpass stuff? - CAM, // Camera service - CECD, // More Streetpass stuff? - CFG_U, // CFG service (Console & region info) + CurrentThread = 0xFFFF8000, // Used by the original kernel + CurrentProcess = 0xFFFF8001, // Used by the original kernel + AC, // Something network related + ACT, // Handles NNID accounts + AM, // Application manager + APT, // App Title something service? + BOSS, // Streetpass stuff? + CAM, // Camera service + CECD, // More Streetpass stuff? + CFG_U, // CFG service (Console & region info) CFG_I, CFG_S, // Used by most system apps in lieu of cfg:u CSND, // Plays audio directly from PCM samples @@ -50,10 +50,10 @@ namespace KernelHandles { MinServiceHandle = AC, MaxServiceHandle = Y2R, - GSPSharedMemHandle = MaxServiceHandle + 1, // HandleType for the GSP shared memory + GSPSharedMemHandle = MaxServiceHandle + 1, // Handle for the GSP shared memory FontSharedMemHandle, CSNDSharedMemHandle, - APTCaptureSharedMemHandle, // Shared memory for display capture info, + APTCaptureSharedMemHandle, // Shared memory for display capture info, HIDSharedMemHandle, MinSharedMemHandle = GSPSharedMemHandle, @@ -61,10 +61,14 @@ namespace KernelHandles { }; // Returns whether "handle" belongs to one of the OS services - static constexpr bool isServiceHandle(HandleType handle) { return handle >= MinServiceHandle && handle <= MaxServiceHandle; } + static constexpr bool isServiceHandle(HandleType handle) { + return handle >= MinServiceHandle && handle <= MaxServiceHandle; + } // Returns whether "handle" belongs to one of the OS services' shared memory areas - static constexpr bool isSharedMemHandle(HandleType handle) { return handle >= MinSharedMemHandle && handle <= MaxSharedMemHandle; } + static constexpr bool isSharedMemHandle(HandleType handle) { + return handle >= MinSharedMemHandle && handle <= MaxSharedMemHandle; + } // Returns the name of a handle as a string based on the given handle static const char* getServiceName(HandleType handle) { @@ -106,4 +110,4 @@ namespace KernelHandles { default: return "Unknown"; } } -} // namespace KernelHandles +} diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index 199d978a1..958d325e0 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -45,12 +45,12 @@ class Kernel { HandleType currentProcess; HandleType mainThread; int currentThreadIndex; - HandleType srvHandle; // HandleType for the special service manager port "srv:" - HandleType errorPortHandle; // HandleType for the err:f port used for displaying errors + HandleType srvHandle; // Handle for the special service manager port "srv:" + HandleType errorPortHandle; // Handle for the err:f port used for displaying errors u32 arbiterCount; - 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? + 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 @@ -66,7 +66,7 @@ class Kernel { HandleType makeThread(u32 entrypoint, u32 initialSP, u32 priority, ProcessorID id, u32 arg, ThreadStatus status = ThreadStatus::Dormant); HandleType makeMemoryBlock(u32 addr, u32 size, u32 myPermission, u32 otherPermission); - public: +public: // Needs to be public to be accessible to the APT/HID services HandleType makeEvent(ResetType resetType, Event::CallbackType callback = Event::CallbackType::None); // Needs to be public to be accessible to the APT/DSP services @@ -88,7 +88,7 @@ class Kernel { } } - private: +private: void signalArbiter(u32 waitingAddress, s32 threadCount); void sleepThread(s64 ns); void sleepThreadOnArbiter(u32 waitingAddress); @@ -194,7 +194,7 @@ class Kernel { void closeDirectory(u32 messagePointer, HandleType directory); void readDirectory(u32 messagePointer, HandleType directory); - public: +public: Kernel(CPU& cpu, Memory& mem, GPU& gpu, const EmulatorConfig& config); void initializeFS() { return serviceManager.initializeFS(); } void setVersion(u8 major, u8 minor); diff --git a/include/kernel/kernel_types.hpp b/include/kernel/kernel_types.hpp index 157017de7..eaf91b9ed 100644 --- a/include/kernel/kernel_types.hpp +++ b/include/kernel/kernel_types.hpp @@ -1,102 +1,99 @@ #pragma once #include #include - #include "fs/archive_base.hpp" #include "handles.hpp" #include "helpers.hpp" #include "result/result.hpp" enum class KernelObjectType : u8 { - AddressArbiter, - Archive, - Directory, - File, - MemoryBlock, - Process, - ResourceLimit, - Session, - Dummy, + AddressArbiter, Archive, Directory, File, MemoryBlock, Process, ResourceLimit, Session, Dummy, // Bundle waitable objects together in the enum to let the compiler optimize certain checks better - Event, - Mutex, - Port, - Semaphore, - Timer, - Thread + Event, Mutex, Port, Semaphore, Timer, Thread }; -enum class ResourceLimitCategory : int { Application = 0, SystemApplet = 1, LibraryApplet = 2, Misc = 3 }; +enum class ResourceLimitCategory : int { + Application = 0, + SystemApplet = 1, + LibraryApplet = 2, + Misc = 3 +}; // Reset types (for use with events and timers) enum class ResetType { - OneShot = 0, // When the primitive is signaled, it will wake up exactly one thread and will clear itself automatically. - Sticky = 1, // When the primitive is signaled, it will wake up all threads and it won't clear itself automatically. - Pulse = 2, // Only meaningful for timers: same as ONESHOT but it will periodically signal the timer instead of just once. + OneShot = 0, // When the primitive is signaled, it will wake up exactly one thread and will clear itself automatically. + Sticky = 1, // When the primitive is signaled, it will wake up all threads and it won't clear itself automatically. + Pulse = 2, // Only meaningful for timers: same as ONESHOT but it will periodically signal the timer instead of just once. }; -enum class ArbitrationType { Signal = 0, WaitIfLess = 1, DecrementAndWaitIfLess = 2, WaitIfLessTimeout = 3, DecrementAndWaitIfLessTimeout = 4 }; +enum class ArbitrationType { + Signal = 0, + WaitIfLess = 1, + DecrementAndWaitIfLess = 2, + WaitIfLessTimeout = 3, + DecrementAndWaitIfLessTimeout = 4 +}; enum class ProcessorID : s32 { - AllCPUs = -1, - Default = -2, + AllCPUs = -1, + Default = -2, - AppCore = 0, - Syscore = 1, - New3DSExtra1 = 2, - New3DSExtra2 = 3 + AppCore = 0, + Syscore = 1, + New3DSExtra1 = 2, + New3DSExtra2 = 3 }; struct AddressArbiter {}; struct ResourceLimits { - HandleType handle; + HandleType handle; - s32 currentCommit = 0; + s32 currentCommit = 0; }; struct Process { - // Resource limits for this process - ResourceLimits limits; - // Process ID - u32 id; + // Resource limits for this process + ResourceLimits limits; + // Process ID + u32 id; - Process(u32 id) : id(id) {} + Process(u32 id) : id(id) {} }; struct Event { - // Some events (for now, only the DSP semaphore events) need to execute a callback when signalled - // This enum stores what kind of callback they should execute - enum class CallbackType : u32 { - None, - DSPSemaphore, - }; - - u64 waitlist; // A bitfield where each bit symbolizes if the thread with thread with the corresponding index is waiting on the event - ResetType resetType = ResetType::OneShot; - CallbackType callback = CallbackType::None; - bool fired = false; - - Event(ResetType resetType) : resetType(resetType), waitlist(0) {} - Event(ResetType resetType, CallbackType cb) : resetType(resetType), waitlist(0), callback(cb) {} + // Some events (for now, only the DSP semaphore events) need to execute a callback when signalled + // This enum stores what kind of callback they should execute + enum class CallbackType : u32 { + None, + DSPSemaphore, + }; + + u64 waitlist; // A bitfield where each bit symbolizes if the thread with thread with the corresponding index is waiting on the event + ResetType resetType = ResetType::OneShot; + CallbackType callback = CallbackType::None; + bool fired = false; + + Event(ResetType resetType) : resetType(resetType), waitlist(0) {} + Event(ResetType resetType, CallbackType cb) : resetType(resetType), waitlist(0), callback(cb) {} }; struct Port { - static constexpr u32 maxNameLen = 11; + static constexpr u32 maxNameLen = 11; - char name[maxNameLen + 1] = {}; - bool isPublic = false; // Setting name=NULL creates a private port not accessible from svcConnectToPort. + char name[maxNameLen + 1] = {}; + bool isPublic = false; // Setting name=NULL creates a private port not accessible from svcConnectToPort. - Port(const char* name) { - // If the name is empty (ie the first char is the null terminator) then the port is private - isPublic = name[0] != '\0'; - std::strncpy(this->name, name, maxNameLen); - } + Port(const char* name) { + // If the name is empty (ie the first char is the null terminator) then the port is private + isPublic = name[0] != '\0'; + std::strncpy(this->name, name, maxNameLen); + } }; struct Session { - HandleType portHandle; // The port this session is subscribed to - Session(HandleType portHandle) : portHandle(portHandle) {} + HandleType portHandle; // The port this session is subscribed to + Session(HandleType portHandle) : portHandle(portHandle) {} }; enum class ThreadStatus { @@ -145,102 +142,101 @@ struct Thread { }; static const char* kernelObjectTypeToString(KernelObjectType t) { - switch (t) { - case KernelObjectType::AddressArbiter: return "address arbiter"; - case KernelObjectType::Archive: return "archive"; - case KernelObjectType::Directory: return "directory"; - case KernelObjectType::Event: return "event"; - case KernelObjectType::File: return "file"; - case KernelObjectType::MemoryBlock: return "memory block"; - case KernelObjectType::Port: return "port"; - case KernelObjectType::Process: return "process"; - case KernelObjectType::ResourceLimit: return "resource limit"; - case KernelObjectType::Session: return "session"; - case KernelObjectType::Mutex: return "mutex"; - case KernelObjectType::Semaphore: return "semaphore"; - case KernelObjectType::Thread: return "thread"; - case KernelObjectType::Dummy: return "dummy"; - default: return "unknown"; - } + switch (t) { + case KernelObjectType::AddressArbiter: return "address arbiter"; + case KernelObjectType::Archive: return "archive"; + case KernelObjectType::Directory: return "directory"; + case KernelObjectType::Event: return "event"; + case KernelObjectType::File: return "file"; + case KernelObjectType::MemoryBlock: return "memory block"; + case KernelObjectType::Port: return "port"; + case KernelObjectType::Process: return "process"; + case KernelObjectType::ResourceLimit: return "resource limit"; + case KernelObjectType::Session: return "session"; + case KernelObjectType::Mutex: return "mutex"; + case KernelObjectType::Semaphore: return "semaphore"; + case KernelObjectType::Thread: return "thread"; + case KernelObjectType::Dummy: return "dummy"; + default: return "unknown"; + } } struct Mutex { - u64 waitlist; // Refer to the getWaitlist function below for documentation - HandleType ownerThread = 0; // Index of the thread that holds the mutex if it's locked - HandleType handle; // HandleType of the mutex itself - u32 lockCount; // Number of times this mutex has been locked by its daddy. 0 = not locked - bool locked; + u64 waitlist; // Refer to the getWaitlist function below for documentation + HandleType ownerThread = 0; // Index of the thread that holds the mutex if it's locked + HandleType handle; // HandleType of the mutex itself + u32 lockCount; // Number of times this mutex has been locked by its daddy. 0 = not locked + bool locked; - Mutex(bool lock, HandleType handle) : locked(lock), waitlist(0), lockCount(lock ? 1 : 0), handle(handle) {} + Mutex(bool lock, HandleType handle) : locked(lock), waitlist(0), lockCount(lock ? 1 : 0), handle(handle) {} }; struct Semaphore { - u64 waitlist; // Refer to the getWaitlist function below for documentation - s32 availableCount; - s32 maximumCount; + u64 waitlist; // Refer to the getWaitlist function below for documentation + s32 availableCount; + s32 maximumCount; - Semaphore(s32 initialCount, s32 maximumCount) : availableCount(initialCount), maximumCount(maximumCount), waitlist(0) {} + Semaphore(s32 initialCount, s32 maximumCount) : availableCount(initialCount), maximumCount(maximumCount), waitlist(0) {} }; struct Timer { - u64 waitlist; // Refer to the getWaitlist function below for documentation - ResetType resetType = ResetType::OneShot; + u64 waitlist; // Refer to the getWaitlist function below for documentation + ResetType resetType = ResetType::OneShot; - u64 fireTick; // CPU tick the timer will be fired - u64 interval; // Number of ns until the timer fires for the second and future times - bool fired; // Has this timer been signalled? - bool running; // Is this timer running or stopped? + u64 fireTick; // CPU tick the timer will be fired + u64 interval; // Number of ns until the timer fires for the second and future times + bool fired; // Has this timer been signalled? + bool running; // Is this timer running or stopped? - Timer(ResetType type) : resetType(type), fireTick(0), interval(0), waitlist(0), fired(false), running(false) {} + Timer(ResetType type) : resetType(type), fireTick(0), interval(0), waitlist(0), fired(false), running(false) {} }; struct MemoryBlock { - u32 addr = 0; - u32 size = 0; - u32 myPermission = 0; - u32 otherPermission = 0; - bool mapped = false; + u32 addr = 0; + u32 size = 0; + u32 myPermission = 0; + u32 otherPermission = 0; + bool mapped = false; - MemoryBlock(u32 addr, u32 size, u32 myPerm, u32 otherPerm) - : addr(addr), size(size), myPermission(myPerm), otherPermission(otherPerm), mapped(false) {} + MemoryBlock(u32 addr, u32 size, u32 myPerm, u32 otherPerm) : addr(addr), size(size), myPermission(myPerm), otherPermission(otherPerm), mapped(false) {} }; // Generic kernel object class struct KernelObject { - HandleType handle = 0; // A u32 the OS will use to identify objects - void* data = nullptr; - KernelObjectType type; - - KernelObject(HandleType 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() {} - - template - T* getData() { - return static_cast(data); - } - - const char* getTypeName() const { return kernelObjectTypeToString(type); } - - // Retrieves a reference to the waitlist for a specified object - // We return a reference because this function is only called in the kernel threading internals - // We want the kernel to be able to easily manage waitlists, by reading/parsing them or setting/clearing bits. - // As we mention in the definition of the "Event" struct, the format for wailists is very simple and made to be efficient. - // Each bit corresponds to a thread index and denotes whether the corresponding thread is waiting on this object - // For example if bit 0 of the wait list is set, then the thread with index 0 is waiting on our object - u64& getWaitlist() { - // This code is actually kinda trash but eh good enough - switch (type) { - case KernelObjectType::Event: return getData()->waitlist; - case KernelObjectType::Mutex: return getData()->waitlist; - case KernelObjectType::Semaphore: return getData()->waitlist; - case KernelObjectType::Thread: return getData()->threadsWaitingForTermination; - case KernelObjectType::Timer: return getData()->waitlist; - - // This should be unreachable once we fully implement sync objects - default: [[unlikely]] Helpers::panic("Called GetWaitList on kernel object without a waitlist (Type: %s)", getTypeName()); - } - } + HandleType handle = 0; // A u32 the OS will use to identify objects + void* data = nullptr; + KernelObjectType type; + + KernelObject(HandleType 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() {} + + template + T* getData() { + return static_cast(data); + } + + const char* getTypeName() const { return kernelObjectTypeToString(type); } + + // Retrieves a reference to the waitlist for a specified object + // We return a reference because this function is only called in the kernel threading internals + // We want the kernel to be able to easily manage waitlists, by reading/parsing them or setting/clearing bits. + // As we mention in the definition of the "Event" struct, the format for wailists is very simple and made to be efficient. + // Each bit corresponds to a thread index and denotes whether the corresponding thread is waiting on this object + // For example if bit 0 of the wait list is set, then the thread with index 0 is waiting on our object + u64& getWaitlist() { + // This code is actually kinda trash but eh good enough + switch (type) { + case KernelObjectType::Event: return getData()->waitlist; + case KernelObjectType::Mutex: return getData()->waitlist; + case KernelObjectType::Semaphore: return getData()->waitlist; + case KernelObjectType::Thread: return getData()->threadsWaitingForTermination; + case KernelObjectType::Timer: return getData()->waitlist; + + // This should be unreachable once we fully implement sync objects + default: [[unlikely]] Helpers::panic("Called GetWaitList on kernel object without a waitlist (Type: %s)", getTypeName()); + } + } }; diff --git a/include/memory.hpp b/include/memory.hpp index d8dc519f4..2258b488c 100644 --- a/include/memory.hpp +++ b/include/memory.hpp @@ -10,8 +10,8 @@ #include "crypto/aes_engine.hpp" #include "handles.hpp" #include "helpers.hpp" -#include "loader/3dsx.hpp" #include "loader/ncsd.hpp" +#include "loader/3dsx.hpp" #include "services/region_codes.hpp" namespace PhysicalAddrs { @@ -38,7 +38,7 @@ namespace VirtualAddrs { DefaultStackSize = 0x4000, NormalHeapStart = 0x08000000, - LinearHeapStartOld = 0x14000000, // If kernel version < 0x22C + LinearHeapStartOld = 0x14000000, // If kernel version < 0x22C LinearHeapEndOld = 0x1C000000, LinearHeapStartNew = 0x30000000, @@ -80,7 +80,7 @@ namespace KernelMemoryTypes { // I assume this is referring to a single piece of allocated memory? If it's for pages, it makes no sense. // If it's for multiple allocations, it also makes no sense struct MemoryInfo { - u32 baseAddr; // Base process virtual address. Used as a paddr in lockedMemoryInfo instead + u32 baseAddr; // Base process virtual address. Used as a paddr in lockedMemoryInfo instead u32 size; // Of what? u32 perms; // Is this referring to a single page or? u32 state; @@ -91,10 +91,10 @@ namespace KernelMemoryTypes { // 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? + 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) {} }; @@ -105,7 +105,7 @@ class Memory { u8* dspRam; // Provided to us by Audio u8* vram; // Provided to the memory class by the GPU class - u64& cpuTicks; // Reference to the CPU tick counter + 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 @@ -124,7 +124,7 @@ class Memory { SharedMemoryBlock(0, 0xE7000, KernelHandles::APTCaptureSharedMemHandle), // APT Capture Buffer memory }; - public: +public: static constexpr u32 pageShift = 12; static constexpr u32 pageSize = 1 << pageShift; static constexpr u32 pageMask = pageSize - 1; @@ -139,7 +139,7 @@ class Memory { static constexpr u32 DSP_CODE_MEMORY_OFFSET = u32(0_KB); static constexpr u32 DSP_DATA_MEMORY_OFFSET = u32(256_KB); - private: +private: std::bitset usedFCRAMPages; std::optional findPaddr(u32 size); u64 timeSince3DSEpoch(); @@ -150,7 +150,7 @@ class Memory { // Stored in Configuration Memory starting @ 0x1FF80060 struct FirmwareInfo { - u8 unk; // Usually 0 according to 3DBrew + u8 unk; // Usually 0 according to 3DBrew u8 revision; u8 minor; u8 major; @@ -168,8 +168,8 @@ class Memory { public: u16 kernelVersion = 0; - u32 usedUserMemory = u32(0_MB); // How much of the APPLICATION FCRAM range is used (allocated to the appcore) - u32 usedSystemMemory = u32(0_MB); // Similar for the SYSTEM range (reserved for the syscore) + u32 usedUserMemory = u32(0_MB); // How much of the APPLICATION FCRAM range is used (allocated to the appcore) + u32 usedSystemMemory = u32(0_MB); // Similar for the SYSTEM range (reserved for the syscore) Memory(u64& cpuTicks, const EmulatorConfig& config); void reset(); @@ -198,20 +198,26 @@ class Memory { u8* getFCRAM() { return fcram; } // Total amount of OS-only FCRAM available (Can vary depending on how much FCRAM the app requests via the cart exheader) - u32 totalSysFCRAM() { return FCRAM_SIZE - FCRAM_APPLICATION_SIZE; } + u32 totalSysFCRAM() { + return FCRAM_SIZE - FCRAM_APPLICATION_SIZE; + } // Amount of OS-only FCRAM currently available - u32 remainingSysFCRAM() { return totalSysFCRAM() - usedSystemMemory; } + u32 remainingSysFCRAM() { + return totalSysFCRAM() - usedSystemMemory; + } // Physical FCRAM index to the start of OS FCRAM // We allocate the first part of physical FCRAM for the application, and the rest to the OS. So the index for the OS = application ram size u32 sysFCRAMIndex() { return FCRAM_APPLICATION_SIZE; } - enum class BatteryLevel { Empty = 0, AlmostEmpty, OneBar, TwoBars, ThreeBars, FourBars }; + enum class BatteryLevel { + Empty = 0, AlmostEmpty, OneBar, TwoBars, ThreeBars, FourBars + }; u8 getBatteryState(bool adapterConnected, bool charging, BatteryLevel batteryLevel) { - u8 value = static_cast(batteryLevel) << 2; // Bits 2:4 are the battery level from 0 to 5 - if (adapterConnected) value |= 1 << 0; // Bit 0 shows if the charger is connected - if (charging) value |= 1 << 1; // Bit 1 shows if we're charging + u8 value = static_cast(batteryLevel) << 2; // Bits 2:4 are the battery level from 0 to 5 + if (adapterConnected) value |= 1 << 0; // Bit 0 shows if the charger is connected + if (charging) value |= 1 << 1; // Bit 1 shows if we're charging return value; } @@ -233,7 +239,9 @@ class Memory { } // Returns whether "addr" is aligned to a page (4096 byte) boundary - static constexpr bool isAligned(u32 addr) { return (addr & pageMask) == 0; } + static constexpr bool isAligned(u32 addr) { + return (addr & pageMask) == 0; + } // Allocate "size" bytes of RAM starting from FCRAM index "paddr" (We pick it ourself if paddr == 0) // And map them to virtual address "vaddr" (We also pick it ourself if vaddr == 0).