revert formatting

This commit is contained in:
Samuliak 2024-07-02 19:19:37 +02:00
parent 124622cf18
commit 863edac152
7 changed files with 338 additions and 331 deletions

View file

@ -33,8 +33,8 @@ namespace Audio {
SampleFormat format; SampleFormat format;
SourceType sourceType; SourceType sourceType;
bool fromQueue = false; // Is this buffer from the buffer queue or an embedded buffer? 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 hasPlayedOnce = false; // Has the buffer been played at least once before?
bool operator<(const Buffer& other) const { bool operator<(const Buffer& other) const {
// Lower ID = Higher priority // Lower ID = Higher priority
@ -136,7 +136,7 @@ namespace Audio {
const auto counter0 = dspRam.region0.frameCounter; const auto counter0 = dspRam.region0.frameCounter;
const auto counter1 = dspRam.region1.frameCounter; const auto counter1 = dspRam.region1.frameCounter;
// HandleType wraparound cases first // Handle wraparound cases first
if (counter0 == 0xffff && counter1 != 0xfffe) { if (counter0 == 0xffff && counter1 != 0xfffe) {
return 1; return 1;
} else if (counter1 == 0xffff && counter0 != 0xfffe) { } else if (counter1 == 0xffff && counter0 != 0xfffe) {

View file

@ -8,7 +8,7 @@
namespace Common { namespace Common {
class CapstoneDisassembler { class CapstoneDisassembler {
csh handle; // HandleType to our disassembler object csh handle; // Handle to our disassembler object
cs_insn* instructions = nullptr; // Pointer to instruction object cs_insn* instructions = nullptr; // Pointer to instruction object
bool initialized = false; bool initialized = false;

View file

@ -7,7 +7,6 @@
#include <string> #include <string>
#include <type_traits> #include <type_traits>
#include <vector> #include <vector>
#include "helpers.hpp" #include "helpers.hpp"
#include "memory.hpp" #include "memory.hpp"
#include "result.hpp" #include "result.hpp"
@ -16,108 +15,108 @@
using Result::HorizonResult; using Result::HorizonResult;
namespace PathType { namespace PathType {
enum : u32 { enum : u32 {
Invalid = 0, Invalid = 0,
Empty = 1, Empty = 1,
Binary = 2, Binary = 2,
ASCII = 3, ASCII = 3,
UTF16 = 4, UTF16 = 4,
}; };
} }
namespace ArchiveID { namespace ArchiveID {
enum : u32 { enum : u32 {
SelfNCCH = 3, SelfNCCH = 3,
SaveData = 4, SaveData = 4,
ExtSaveData = 6, ExtSaveData = 6,
SharedExtSaveData = 7, SharedExtSaveData = 7,
SystemSaveData = 8, SystemSaveData = 8,
SDMC = 9, SDMC = 9,
SDMCWriteOnly = 0xA, SDMCWriteOnly = 0xA,
SavedataAndNcch = 0x2345678A, 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 // 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. // lowpath.
UserSaveData1 = 0x567890B2, UserSaveData1 = 0x567890B2,
// 3DBrew: Similar to 0x567890B2 but can only access Accessible Save specified in exheader? // 3DBrew: Similar to 0x567890B2 but can only access Accessible Save specified in exheader?
UserSaveData2 = 0x567890B4, UserSaveData2 = 0x567890B4,
}; };
static std::string toString(u32 id) { static std::string toString(u32 id) {
switch (id) { switch (id) {
case SelfNCCH: return "SelfNCCH"; case SelfNCCH: return "SelfNCCH";
case SaveData: return "SaveData"; case SaveData: return "SaveData";
case ExtSaveData: return "ExtSaveData"; case ExtSaveData: return "ExtSaveData";
case SharedExtSaveData: return "SharedExtSaveData"; case SharedExtSaveData: return "SharedExtSaveData";
case SystemSaveData: return "SystemSaveData"; case SystemSaveData: return "SystemSaveData";
case SDMC: return "SDMC"; case SDMC: return "SDMC";
case SDMCWriteOnly: return "SDMC (Write-only)"; case SDMCWriteOnly: return "SDMC (Write-only)";
case SavedataAndNcch: return "Savedata & NCCH (archive 0x2345678A)"; case SavedataAndNcch: return "Savedata & NCCH (archive 0x2345678A)";
default: return "Unknown archive"; default: return "Unknown archive";
} }
} }
} // namespace ArchiveID }
struct FSPath { struct FSPath {
u32 type = PathType::Invalid; u32 type = PathType::Invalid;
std::vector<u8> binary; // Path data for binary paths std::vector<u8> binary; // Path data for binary paths
std::string string; // Path data for ASCII paths std::string string; // Path data for ASCII paths
std::u16string utf16_string; std::u16string utf16_string;
FSPath() {} FSPath() {}
FSPath(u32 type, const std::vector<u8>& vec) : type(type) { FSPath(u32 type, const std::vector<u8>& vec) : type(type) {
switch (type) { switch (type) {
case PathType::Binary: binary = std::move(vec); break; case PathType::Binary: binary = std::move(vec); break;
case PathType::ASCII: case PathType::ASCII:
string.resize(vec.size() - 1); // -1 because of the null terminator string.resize(vec.size() - 1); // -1 because of the null terminator
std::memcpy(string.data(), vec.data(), vec.size() - 1); // Copy string data std::memcpy(string.data(), vec.data(), vec.size() - 1); // Copy string data
break; break;
case PathType::UTF16: { case PathType::UTF16: {
const size_t size = vec.size() / sizeof(u16) - 1; // Character count. -1 because null terminator here too const size_t size = vec.size() / sizeof(u16) - 1; // Character count. -1 because null terminator here too
utf16_string.resize(size); utf16_string.resize(size);
std::memcpy(utf16_string.data(), vec.data(), size * sizeof(u16)); std::memcpy(utf16_string.data(), vec.data(), size * sizeof(u16));
break; break;
}; };
} }
} }
}; };
struct FilePerms { struct FilePerms {
u32 raw; u32 raw;
FilePerms(u32 val) : raw(val) {} FilePerms(u32 val) : raw(val) {}
bool read() const { return (raw & 1) != 0; } bool read() const { return (raw & 1) != 0; }
bool write() const { return (raw & 2) != 0; } bool write() const { return (raw & 2) != 0; }
bool create() const { return (raw & 4) != 0; } bool create() const { return (raw & 4) != 0; }
}; };
class ArchiveBase; class ArchiveBase;
struct FileSession { struct FileSession {
ArchiveBase* archive = nullptr; ArchiveBase* archive = nullptr;
FILE* fd = nullptr; // File descriptor for file sessions that require them. FILE* fd = nullptr; // File descriptor for file sessions that require them.
FSPath path; FSPath path;
FSPath archivePath; FSPath archivePath;
u32 priority = 0; // TODO: What does this even do u32 priority = 0; // TODO: What does this even do
bool isOpen; bool isOpen;
FileSession(ArchiveBase* archive, const FSPath& filePath, const FSPath& archivePath, FILE* fd, bool isOpen = true) 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) {} : archive(archive), path(filePath), archivePath(archivePath), fd(fd), isOpen(isOpen), priority(0) {}
// For cloning a file session // For cloning a file session
FileSession(const FileSession& other) FileSession(const FileSession& other)
: archive(other.archive), path(other.path), archivePath(other.archivePath), fd(other.fd), isOpen(other.isOpen), priority(other.priority) {} : archive(other.archive), path(other.path), archivePath(other.archivePath), fd(other.fd), isOpen(other.isOpen), priority(other.priority) {}
}; };
struct ArchiveSession { struct ArchiveSession {
ArchiveBase* archive = nullptr; ArchiveBase* archive = nullptr;
FSPath path; FSPath path;
bool isOpen; 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 { struct DirectoryEntry {
@ -155,104 +154,104 @@ struct DirectorySession {
using FileDescriptor = std::optional<FILE*>; using FileDescriptor = std::optional<FILE*>;
class ArchiveBase { class ArchiveBase {
public: public:
struct FormatInfo { struct FormatInfo {
u32 size; // Archive size u32 size; // Archive size
u32 numOfDirectories; // Number of directories u32 numOfDirectories; // Number of directories
u32 numOfFiles; // Number of files u32 numOfFiles; // Number of files
bool duplicateData; // Whether to duplicate data or not bool duplicateData; // Whether to duplicate data or not
}; };
protected: protected:
using HandleType = u32; using HandleType = u32;
static constexpr FileDescriptor NoFile = nullptr; static constexpr FileDescriptor NoFile = nullptr;
static constexpr FileDescriptor FileError = std::nullopt; static constexpr FileDescriptor FileError = std::nullopt;
Memory& mem; Memory& mem;
// Returns if a specified 3DS path in UTF16 or ASCII format is safe or not // 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 // 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 // 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 // tree And access files outside of the emulator's app data folder
template <u32 format> template <u32 format>
bool isPathSafe(const FSPath& path) { bool isPathSafe(const FSPath& path) {
static_assert(format == PathType::ASCII || format == PathType::UTF16); static_assert(format == PathType::ASCII || format == PathType::UTF16);
using String = typename std::conditional<format == PathType::UTF16, std::u16string, std::string>::type; // String type for the path using String = typename std::conditional<format == PathType::UTF16, std::u16string, std::string>::type; // String type for the path
using Char = typename String::value_type; // Char type for the path using Char = typename String::value_type; // Char type for the path
String pathString, dots; String pathString, dots;
if constexpr (std::is_same<String, std::u16string>()) { if constexpr (std::is_same<String, std::u16string>()) {
pathString = path.utf16_string; pathString = path.utf16_string;
dots = u".."; dots = u"..";
} else { } else {
pathString = path.string; pathString = path.string;
dots = ".."; dots = "..";
} }
// If the path string doesn't begin with / then that means it's accessing outside the FS root, which is invalid & unsafe // 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; if (pathString[0] != Char('/')) return false;
// Counts how many folders sans the root our file is nested under. // 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 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 this is the FS root.
// If it's > 0 then we're in a subdirectory of the root. // If it's > 0 then we're in a subdirectory of the root.
int level = 0; int level = 0;
// Split the string on / characters and see how many of the substrings are ".." // Split the string on / characters and see how many of the substrings are ".."
size_t pos = 0; size_t pos = 0;
while ((pos = pathString.find(Char('/'))) != String::npos) { while ((pos = pathString.find(Char('/'))) != String::npos) {
String token = pathString.substr(0, pos); String token = pathString.substr(0, pos);
pathString.erase(0, pos + 1); pathString.erase(0, pos + 1);
if (token == dots) { if (token == dots) {
level--; level--;
if (level < 0) return false; if (level < 0) return false;
} else { } else {
level++; level++;
} }
} }
return true; return true;
} }
public: public:
virtual std::string name() = 0; virtual std::string name() = 0;
virtual u64 getFreeBytes() = 0; virtual u64 getFreeBytes() = 0;
virtual HorizonResult createFile(const FSPath& path, u64 size) = 0; virtual HorizonResult createFile(const FSPath& path, u64 size) = 0;
virtual HorizonResult deleteFile(const FSPath& path) = 0; virtual HorizonResult deleteFile(const FSPath& path) = 0;
virtual Rust::Result<FormatInfo, HorizonResult> getFormatInfo(const FSPath& path) { virtual Rust::Result<FormatInfo, HorizonResult> getFormatInfo(const FSPath& path) {
Helpers::panic("Unimplemented GetFormatInfo for %s archive", name().c_str()); 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 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}); return Ok(FormatInfo{.size = 0, .numOfDirectories = 0, .numOfFiles = 0, .duplicateData = false});
} }
virtual HorizonResult createDirectory(const FSPath& path) { virtual HorizonResult createDirectory(const FSPath& path) {
Helpers::panic("Unimplemented CreateDirectory for %s archive", name().c_str()); Helpers::panic("Unimplemented CreateDirectory for %s archive", name().c_str());
return Result::FS::AlreadyExists; return Result::FS::AlreadyExists;
} }
// Returns nullopt if opening the file failed, otherwise returns a file descriptor to it (nullptr if none is needed) // 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 FileDescriptor openFile(const FSPath& path, const FilePerms& perms) = 0;
virtual Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) = 0; virtual Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) = 0;
virtual Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) { virtual Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) {
Helpers::panic("Unimplemented OpenDirectory for %s archive", name().c_str()); Helpers::panic("Unimplemented OpenDirectory for %s archive", name().c_str());
return Err(Result::FS::FileNotFoundAlt); 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 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) { virtual HorizonResult renameFile(const FSPath& oldPath, const FSPath& newPath) {
Helpers::panic("Unimplemented RenameFile for %s archive", name().c_str()); Helpers::panic("Unimplemented RenameFile for %s archive", name().c_str());
return Result::Success; return Result::Success;
} }
// Read size bytes from a file starting at offset "offset" into a certain buffer in memory // 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 // Returns the number of bytes read, or nullopt if the read failed
virtual std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) = 0; virtual std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) = 0;
ArchiveBase(Memory& mem) : mem(mem) {} ArchiveBase(Memory& mem) : mem(mem) {}
}; };
struct ArchiveResource { struct ArchiveResource {

View file

@ -5,19 +5,19 @@ using HandleType = u32;
namespace KernelHandles { namespace KernelHandles {
enum : u32 { enum : u32 {
Max = 0xFFFF7FFF, // Max handle the kernel can automagically allocate Max = 0xFFFF7FFF, // Max handle the kernel can automagically allocate
// Hardcoded handles // Hardcoded handles
CurrentThread = 0xFFFF8000, // Used by the original kernel CurrentThread = 0xFFFF8000, // Used by the original kernel
CurrentProcess = 0xFFFF8001, // Used by the original kernel CurrentProcess = 0xFFFF8001, // Used by the original kernel
AC, // Something network related AC, // Something network related
ACT, // Handles NNID accounts ACT, // Handles NNID accounts
AM, // Application manager AM, // Application manager
APT, // App Title something service? APT, // App Title something service?
BOSS, // Streetpass stuff? BOSS, // Streetpass stuff?
CAM, // Camera service CAM, // Camera service
CECD, // More Streetpass stuff? CECD, // More Streetpass stuff?
CFG_U, // CFG service (Console & region info) CFG_U, // CFG service (Console & region info)
CFG_I, CFG_I,
CFG_S, // Used by most system apps in lieu of cfg:u CFG_S, // Used by most system apps in lieu of cfg:u
CSND, // Plays audio directly from PCM samples CSND, // Plays audio directly from PCM samples
@ -50,10 +50,10 @@ namespace KernelHandles {
MinServiceHandle = AC, MinServiceHandle = AC,
MaxServiceHandle = Y2R, MaxServiceHandle = Y2R,
GSPSharedMemHandle = MaxServiceHandle + 1, // HandleType for the GSP shared memory GSPSharedMemHandle = MaxServiceHandle + 1, // Handle for the GSP shared memory
FontSharedMemHandle, FontSharedMemHandle,
CSNDSharedMemHandle, CSNDSharedMemHandle,
APTCaptureSharedMemHandle, // Shared memory for display capture info, APTCaptureSharedMemHandle, // Shared memory for display capture info,
HIDSharedMemHandle, HIDSharedMemHandle,
MinSharedMemHandle = GSPSharedMemHandle, MinSharedMemHandle = GSPSharedMemHandle,
@ -61,10 +61,14 @@ namespace KernelHandles {
}; };
// Returns whether "handle" belongs to one of the OS services // 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 // 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 // Returns the name of a handle as a string based on the given handle
static const char* getServiceName(HandleType handle) { static const char* getServiceName(HandleType handle) {
@ -106,4 +110,4 @@ namespace KernelHandles {
default: return "Unknown"; default: return "Unknown";
} }
} }
} // namespace KernelHandles }

View file

@ -45,12 +45,12 @@ class Kernel {
HandleType currentProcess; HandleType currentProcess;
HandleType mainThread; HandleType mainThread;
int currentThreadIndex; int currentThreadIndex;
HandleType srvHandle; // HandleType for the special service manager port "srv:" HandleType srvHandle; // Handle for the special service manager port "srv:"
HandleType errorPortHandle; // HandleType for the err:f port used for displaying errors HandleType errorPortHandle; // Handle for the err:f port used for displaying errors
u32 arbiterCount; u32 arbiterCount;
u32 threadCount; // How many threads in our thread pool have been used as of now (Up to 32) 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 aliveThreadCount; // How many of these threads are actually alive?
ServiceManager serviceManager; ServiceManager serviceManager;
// Top 8 bits are the major version, bottom 8 are the minor version // 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 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); HandleType makeMemoryBlock(u32 addr, u32 size, u32 myPermission, u32 otherPermission);
public: public:
// Needs to be public to be accessible to the APT/HID services // Needs to be public to be accessible to the APT/HID services
HandleType makeEvent(ResetType resetType, Event::CallbackType callback = Event::CallbackType::None); HandleType makeEvent(ResetType resetType, Event::CallbackType callback = Event::CallbackType::None);
// Needs to be public to be accessible to the APT/DSP services // 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 signalArbiter(u32 waitingAddress, s32 threadCount);
void sleepThread(s64 ns); void sleepThread(s64 ns);
void sleepThreadOnArbiter(u32 waitingAddress); void sleepThreadOnArbiter(u32 waitingAddress);
@ -194,7 +194,7 @@ class Kernel {
void closeDirectory(u32 messagePointer, HandleType directory); void closeDirectory(u32 messagePointer, HandleType directory);
void readDirectory(u32 messagePointer, HandleType directory); void readDirectory(u32 messagePointer, HandleType directory);
public: public:
Kernel(CPU& cpu, Memory& mem, GPU& gpu, const EmulatorConfig& config); Kernel(CPU& cpu, Memory& mem, GPU& gpu, const EmulatorConfig& config);
void initializeFS() { return serviceManager.initializeFS(); } void initializeFS() { return serviceManager.initializeFS(); }
void setVersion(u8 major, u8 minor); void setVersion(u8 major, u8 minor);

View file

@ -1,102 +1,99 @@
#pragma once #pragma once
#include <array> #include <array>
#include <cstring> #include <cstring>
#include "fs/archive_base.hpp" #include "fs/archive_base.hpp"
#include "handles.hpp" #include "handles.hpp"
#include "helpers.hpp" #include "helpers.hpp"
#include "result/result.hpp" #include "result/result.hpp"
enum class KernelObjectType : u8 { enum class KernelObjectType : u8 {
AddressArbiter, AddressArbiter, Archive, Directory, File, MemoryBlock, Process, ResourceLimit, Session, Dummy,
Archive,
Directory,
File,
MemoryBlock,
Process,
ResourceLimit,
Session,
Dummy,
// Bundle waitable objects together in the enum to let the compiler optimize certain checks better // Bundle waitable objects together in the enum to let the compiler optimize certain checks better
Event, Event, Mutex, Port, Semaphore, Timer, Thread
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) // Reset types (for use with events and timers)
enum class ResetType { enum class ResetType {
OneShot = 0, // When the primitive is signaled, it will wake up exactly one thread and will clear itself automatically. 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. 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. 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 { enum class ProcessorID : s32 {
AllCPUs = -1, AllCPUs = -1,
Default = -2, Default = -2,
AppCore = 0, AppCore = 0,
Syscore = 1, Syscore = 1,
New3DSExtra1 = 2, New3DSExtra1 = 2,
New3DSExtra2 = 3 New3DSExtra2 = 3
}; };
struct AddressArbiter {}; struct AddressArbiter {};
struct ResourceLimits { struct ResourceLimits {
HandleType handle; HandleType handle;
s32 currentCommit = 0; s32 currentCommit = 0;
}; };
struct Process { struct Process {
// Resource limits for this process // Resource limits for this process
ResourceLimits limits; ResourceLimits limits;
// Process ID // Process ID
u32 id; u32 id;
Process(u32 id) : id(id) {} Process(u32 id) : id(id) {}
}; };
struct Event { struct Event {
// Some events (for now, only the DSP semaphore events) need to execute a callback when signalled // 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 // This enum stores what kind of callback they should execute
enum class CallbackType : u32 { enum class CallbackType : u32 {
None, None,
DSPSemaphore, DSPSemaphore,
}; };
u64 waitlist; // A bitfield where each bit symbolizes if the thread with thread with the corresponding index is waiting on the event 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; ResetType resetType = ResetType::OneShot;
CallbackType callback = CallbackType::None; CallbackType callback = CallbackType::None;
bool fired = false; bool fired = false;
Event(ResetType resetType) : resetType(resetType), waitlist(0) {} Event(ResetType resetType) : resetType(resetType), waitlist(0) {}
Event(ResetType resetType, CallbackType cb) : resetType(resetType), waitlist(0), callback(cb) {} Event(ResetType resetType, CallbackType cb) : resetType(resetType), waitlist(0), callback(cb) {}
}; };
struct Port { struct Port {
static constexpr u32 maxNameLen = 11; static constexpr u32 maxNameLen = 11;
char name[maxNameLen + 1] = {}; char name[maxNameLen + 1] = {};
bool isPublic = false; // Setting name=NULL creates a private port not accessible from svcConnectToPort. bool isPublic = false; // Setting name=NULL creates a private port not accessible from svcConnectToPort.
Port(const char* name) { Port(const char* name) {
// If the name is empty (ie the first char is the null terminator) then the port is private // If the name is empty (ie the first char is the null terminator) then the port is private
isPublic = name[0] != '\0'; isPublic = name[0] != '\0';
std::strncpy(this->name, name, maxNameLen); std::strncpy(this->name, name, maxNameLen);
} }
}; };
struct Session { struct Session {
HandleType portHandle; // The port this session is subscribed to HandleType portHandle; // The port this session is subscribed to
Session(HandleType portHandle) : portHandle(portHandle) {} Session(HandleType portHandle) : portHandle(portHandle) {}
}; };
enum class ThreadStatus { enum class ThreadStatus {
@ -145,102 +142,101 @@ struct Thread {
}; };
static const char* kernelObjectTypeToString(KernelObjectType t) { static const char* kernelObjectTypeToString(KernelObjectType t) {
switch (t) { switch (t) {
case KernelObjectType::AddressArbiter: return "address arbiter"; case KernelObjectType::AddressArbiter: return "address arbiter";
case KernelObjectType::Archive: return "archive"; case KernelObjectType::Archive: return "archive";
case KernelObjectType::Directory: return "directory"; case KernelObjectType::Directory: return "directory";
case KernelObjectType::Event: return "event"; case KernelObjectType::Event: return "event";
case KernelObjectType::File: return "file"; case KernelObjectType::File: return "file";
case KernelObjectType::MemoryBlock: return "memory block"; case KernelObjectType::MemoryBlock: return "memory block";
case KernelObjectType::Port: return "port"; case KernelObjectType::Port: return "port";
case KernelObjectType::Process: return "process"; case KernelObjectType::Process: return "process";
case KernelObjectType::ResourceLimit: return "resource limit"; case KernelObjectType::ResourceLimit: return "resource limit";
case KernelObjectType::Session: return "session"; case KernelObjectType::Session: return "session";
case KernelObjectType::Mutex: return "mutex"; case KernelObjectType::Mutex: return "mutex";
case KernelObjectType::Semaphore: return "semaphore"; case KernelObjectType::Semaphore: return "semaphore";
case KernelObjectType::Thread: return "thread"; case KernelObjectType::Thread: return "thread";
case KernelObjectType::Dummy: return "dummy"; case KernelObjectType::Dummy: return "dummy";
default: return "unknown"; default: return "unknown";
} }
} }
struct Mutex { struct Mutex {
u64 waitlist; // Refer to the getWaitlist function below for documentation 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 ownerThread = 0; // Index of the thread that holds the mutex if it's locked
HandleType handle; // HandleType of the mutex itself HandleType handle; // HandleType of the mutex itself
u32 lockCount; // Number of times this mutex has been locked by its daddy. 0 = not locked u32 lockCount; // Number of times this mutex has been locked by its daddy. 0 = not locked
bool 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 { struct Semaphore {
u64 waitlist; // Refer to the getWaitlist function below for documentation u64 waitlist; // Refer to the getWaitlist function below for documentation
s32 availableCount; s32 availableCount;
s32 maximumCount; 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 { struct Timer {
u64 waitlist; // Refer to the getWaitlist function below for documentation u64 waitlist; // Refer to the getWaitlist function below for documentation
ResetType resetType = ResetType::OneShot; ResetType resetType = ResetType::OneShot;
u64 fireTick; // CPU tick the timer will be fired u64 fireTick; // CPU tick the timer will be fired
u64 interval; // Number of ns until the timer fires for the second and future times u64 interval; // Number of ns until the timer fires for the second and future times
bool fired; // Has this timer been signalled? bool fired; // Has this timer been signalled?
bool running; // Is this timer running or stopped? 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 { struct MemoryBlock {
u32 addr = 0; u32 addr = 0;
u32 size = 0; u32 size = 0;
u32 myPermission = 0; u32 myPermission = 0;
u32 otherPermission = 0; u32 otherPermission = 0;
bool mapped = false; bool mapped = false;
MemoryBlock(u32 addr, u32 size, u32 myPerm, u32 otherPerm) MemoryBlock(u32 addr, u32 size, u32 myPerm, u32 otherPerm) : addr(addr), size(size), myPermission(myPerm), otherPermission(otherPerm), mapped(false) {}
: addr(addr), size(size), myPermission(myPerm), otherPermission(otherPerm), mapped(false) {}
}; };
// Generic kernel object class // Generic kernel object class
struct KernelObject { struct KernelObject {
HandleType handle = 0; // A u32 the OS will use to identify objects HandleType handle = 0; // A u32 the OS will use to identify objects
void* data = nullptr; void* data = nullptr;
KernelObjectType type; KernelObjectType type;
KernelObject(HandleType handle, KernelObjectType type) : handle(handle), type(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 // 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 // Thus, the kernel needs to delete it when appropriate
~KernelObject() {} ~KernelObject() {}
template <typename T> template <typename T>
T* getData() { T* getData() {
return static_cast<T*>(data); return static_cast<T*>(data);
} }
const char* getTypeName() const { return kernelObjectTypeToString(type); } const char* getTypeName() const { return kernelObjectTypeToString(type); }
// Retrieves a reference to the waitlist for a specified object // 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 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. // 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. // 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 // 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 // For example if bit 0 of the wait list is set, then the thread with index 0 is waiting on our object
u64& getWaitlist() { u64& getWaitlist() {
// This code is actually kinda trash but eh good enough // This code is actually kinda trash but eh good enough
switch (type) { switch (type) {
case KernelObjectType::Event: return getData<Event>()->waitlist; case KernelObjectType::Event: return getData<Event>()->waitlist;
case KernelObjectType::Mutex: return getData<Mutex>()->waitlist; case KernelObjectType::Mutex: return getData<Mutex>()->waitlist;
case KernelObjectType::Semaphore: return getData<Mutex>()->waitlist; case KernelObjectType::Semaphore: return getData<Mutex>()->waitlist;
case KernelObjectType::Thread: return getData<Thread>()->threadsWaitingForTermination; case KernelObjectType::Thread: return getData<Thread>()->threadsWaitingForTermination;
case KernelObjectType::Timer: return getData<Timer>()->waitlist; case KernelObjectType::Timer: return getData<Timer>()->waitlist;
// This should be unreachable once we fully implement sync objects // 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()); default: [[unlikely]] Helpers::panic("Called GetWaitList on kernel object without a waitlist (Type: %s)", getTypeName());
} }
} }
}; };

View file

@ -10,8 +10,8 @@
#include "crypto/aes_engine.hpp" #include "crypto/aes_engine.hpp"
#include "handles.hpp" #include "handles.hpp"
#include "helpers.hpp" #include "helpers.hpp"
#include "loader/3dsx.hpp"
#include "loader/ncsd.hpp" #include "loader/ncsd.hpp"
#include "loader/3dsx.hpp"
#include "services/region_codes.hpp" #include "services/region_codes.hpp"
namespace PhysicalAddrs { namespace PhysicalAddrs {
@ -38,7 +38,7 @@ namespace VirtualAddrs {
DefaultStackSize = 0x4000, DefaultStackSize = 0x4000,
NormalHeapStart = 0x08000000, NormalHeapStart = 0x08000000,
LinearHeapStartOld = 0x14000000, // If kernel version < 0x22C LinearHeapStartOld = 0x14000000, // If kernel version < 0x22C
LinearHeapEndOld = 0x1C000000, LinearHeapEndOld = 0x1C000000,
LinearHeapStartNew = 0x30000000, 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. // 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 // If it's for multiple allocations, it also makes no sense
struct MemoryInfo { 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 size; // Of what?
u32 perms; // Is this referring to a single page or? u32 perms; // Is this referring to a single page or?
u32 state; u32 state;
@ -91,10 +91,10 @@ namespace KernelMemoryTypes {
// Shared memory block for HID, GSP:GPU etc // Shared memory block for HID, GSP:GPU etc
struct SharedMemoryBlock { struct SharedMemoryBlock {
u32 paddr; // Physical address of this block's memory u32 paddr; // Physical address of this block's memory
u32 size; // Size of block u32 size; // Size of block
u32 handle; // The handle of the shared memory block u32 handle; // The handle of the shared memory block
bool mapped; // Has this block been mapped at least once? 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) {} 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* dspRam; // Provided to us by Audio
u8* vram; // Provided to the memory class by the GPU class 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; using SharedMemoryBlock = KernelMemoryTypes::SharedMemoryBlock;
// Our dynarmic core uses page tables for reads and writes with 4096 byte pages // 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 SharedMemoryBlock(0, 0xE7000, KernelHandles::APTCaptureSharedMemHandle), // APT Capture Buffer memory
}; };
public: public:
static constexpr u32 pageShift = 12; static constexpr u32 pageShift = 12;
static constexpr u32 pageSize = 1 << pageShift; static constexpr u32 pageSize = 1 << pageShift;
static constexpr u32 pageMask = pageSize - 1; 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_CODE_MEMORY_OFFSET = u32(0_KB);
static constexpr u32 DSP_DATA_MEMORY_OFFSET = u32(256_KB); static constexpr u32 DSP_DATA_MEMORY_OFFSET = u32(256_KB);
private: private:
std::bitset<FCRAM_PAGE_COUNT> usedFCRAMPages; std::bitset<FCRAM_PAGE_COUNT> usedFCRAMPages;
std::optional<u32> findPaddr(u32 size); std::optional<u32> findPaddr(u32 size);
u64 timeSince3DSEpoch(); u64 timeSince3DSEpoch();
@ -150,7 +150,7 @@ class Memory {
// Stored in Configuration Memory starting @ 0x1FF80060 // Stored in Configuration Memory starting @ 0x1FF80060
struct FirmwareInfo { struct FirmwareInfo {
u8 unk; // Usually 0 according to 3DBrew u8 unk; // Usually 0 according to 3DBrew
u8 revision; u8 revision;
u8 minor; u8 minor;
u8 major; u8 major;
@ -168,8 +168,8 @@ class Memory {
public: public:
u16 kernelVersion = 0; u16 kernelVersion = 0;
u32 usedUserMemory = u32(0_MB); // How much of the APPLICATION FCRAM range is used (allocated to the appcore) 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 usedSystemMemory = u32(0_MB); // Similar for the SYSTEM range (reserved for the syscore)
Memory(u64& cpuTicks, const EmulatorConfig& config); Memory(u64& cpuTicks, const EmulatorConfig& config);
void reset(); void reset();
@ -198,20 +198,26 @@ class Memory {
u8* getFCRAM() { return fcram; } 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) // 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 // 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 // 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 // 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; } 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 getBatteryState(bool adapterConnected, bool charging, BatteryLevel batteryLevel) {
u8 value = static_cast<u8>(batteryLevel) << 2; // Bits 2:4 are the battery level from 0 to 5 u8 value = static_cast<u8>(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 (adapterConnected) value |= 1 << 0; // Bit 0 shows if the charger is connected
if (charging) value |= 1 << 1; // Bit 1 shows if we're charging if (charging) value |= 1 << 1; // Bit 1 shows if we're charging
return value; return value;
} }
@ -233,7 +239,9 @@ class Memory {
} }
// Returns whether "addr" is aligned to a page (4096 byte) boundary // 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) // 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). // And map them to virtual address "vaddr" (We also pick it ourself if vaddr == 0).