mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-17 03:09:47 +12:00
revert formatting
This commit is contained in:
parent
124622cf18
commit
863edac152
7 changed files with 338 additions and 331 deletions
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -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).
|
||||||
|
|
Loading…
Add table
Reference in a new issue