mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-15 13:16:43 +00:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 23709f36b7 | |||
| f5d1a78846 | |||
| 5d3ad94e18 | |||
| 479df9809a | |||
| 5b4c29b123 | |||
| 5b4dc32c1a | |||
| a90ba7f6ac | |||
| 9b2a36791d | |||
| b85f289048 |
@@ -0,0 +1,5 @@
|
||||
- [ ] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
|
||||
- [ ] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
|
||||
- [ ] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.
|
||||
|
||||
-------------------
|
||||
@@ -2,6 +2,8 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
|
||||
|
||||
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
-->
|
||||
|
||||
@@ -1000,7 +1000,6 @@
|
||||
|
||||
<!-- Renderer Accuracy -->
|
||||
<string name="renderer_accuracy_low">سريع</string>
|
||||
<string name="renderer_accuracy_medium">متوازن</string>
|
||||
<string name="renderer_accuracy_high">دقيق</string>
|
||||
|
||||
<!-- DMA Accuracy -->
|
||||
|
||||
@@ -707,7 +707,6 @@
|
||||
|
||||
<!-- Renderer Accuracy -->
|
||||
<string name="renderer_accuracy_low">Rychlý</string>
|
||||
<string name="renderer_accuracy_medium">Vyvážený</string>
|
||||
<string name="renderer_accuracy_high">Přesný</string>
|
||||
|
||||
<!-- DMA Accuracy -->
|
||||
|
||||
@@ -927,7 +927,6 @@ Wirklich fortfahren?</string>
|
||||
|
||||
<!-- Renderer Accuracy -->
|
||||
<string name="renderer_accuracy_low">Schnell</string>
|
||||
<string name="renderer_accuracy_medium">Ausgeglichen</string>
|
||||
<string name="renderer_accuracy_high">Genau</string>
|
||||
|
||||
<!-- DMA Accuracy -->
|
||||
|
||||
@@ -993,7 +993,6 @@
|
||||
|
||||
<!-- Renderer Accuracy -->
|
||||
<string name="renderer_accuracy_low">Rápido</string>
|
||||
<string name="renderer_accuracy_medium">Equilibrado</string>
|
||||
<string name="renderer_accuracy_high">Preciso</string>
|
||||
|
||||
<!-- DMA Accuracy -->
|
||||
|
||||
@@ -939,7 +939,6 @@
|
||||
|
||||
<!-- Renderer Accuracy -->
|
||||
<string name="renderer_accuracy_low">Rapide</string>
|
||||
<string name="renderer_accuracy_medium">Moyen</string>
|
||||
<string name="renderer_accuracy_high">Précis</string>
|
||||
|
||||
<!-- DMA Accuracy -->
|
||||
|
||||
@@ -893,7 +893,6 @@
|
||||
|
||||
<!-- Renderer Accuracy -->
|
||||
<string name="renderer_accuracy_low">Szybkie</string>
|
||||
<string name="renderer_accuracy_medium">Zrównoważony</string>
|
||||
<string name="renderer_accuracy_high">Dokładny</string>
|
||||
|
||||
<!-- DMA Accuracy -->
|
||||
|
||||
@@ -840,7 +840,6 @@
|
||||
|
||||
<string name="renderer_none">Nenhum</string>
|
||||
|
||||
<string name="renderer_accuracy_medium">Média</string>
|
||||
<string name="renderer_accuracy_high">Alta</string>
|
||||
|
||||
<!-- DMA Accuracy -->
|
||||
|
||||
@@ -983,7 +983,6 @@
|
||||
|
||||
<!-- Renderer Accuracy -->
|
||||
<string name="renderer_accuracy_low">Быстрый</string>
|
||||
<string name="renderer_accuracy_medium">Сбалансированный</string>
|
||||
<string name="renderer_accuracy_high">Точный</string>
|
||||
|
||||
<!-- DMA Accuracy -->
|
||||
|
||||
@@ -986,7 +986,6 @@
|
||||
|
||||
<!-- Renderer Accuracy -->
|
||||
<string name="renderer_accuracy_low">Швидко</string>
|
||||
<string name="renderer_accuracy_medium">Збалансовано</string>
|
||||
<string name="renderer_accuracy_high">Точно</string>
|
||||
|
||||
<!-- DMA Accuracy -->
|
||||
|
||||
@@ -990,7 +990,6 @@
|
||||
|
||||
<!-- Renderer Accuracy -->
|
||||
<string name="renderer_accuracy_low">快速</string>
|
||||
<string name="renderer_accuracy_medium">均衡</string>
|
||||
<string name="renderer_accuracy_high">精确</string>
|
||||
|
||||
<!-- DMA Accuracy -->
|
||||
|
||||
@@ -843,7 +843,6 @@
|
||||
|
||||
<string name="renderer_none">無</string>
|
||||
|
||||
<string name="renderer_accuracy_medium">平衡</string>
|
||||
<string name="renderer_accuracy_high">準確</string>
|
||||
|
||||
<!-- DMA Accuracy -->
|
||||
|
||||
@@ -103,14 +103,12 @@
|
||||
|
||||
<string-array name="rendererAccuracyNames">
|
||||
<item>@string/renderer_accuracy_low</item>
|
||||
<item>@string/renderer_accuracy_medium</item>
|
||||
<item>@string/renderer_accuracy_high</item>
|
||||
</string-array>
|
||||
|
||||
<integer-array name="rendererAccuracyValues">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
</integer-array>
|
||||
|
||||
<!-- VRAM USAGE MODE CHOICES -->
|
||||
|
||||
@@ -479,7 +479,7 @@
|
||||
<string name="advanced">Advanced</string>
|
||||
|
||||
<string name="renderer_accuracy">GPU Mode</string>
|
||||
<string name="renderer_accuracy_description">Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode.</string>
|
||||
<string name="renderer_accuracy_description">Controls the GPU emulation mode. Most games render fine with Fast, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode.</string>
|
||||
<string name="dma_accuracy">DMA Accuracy</string>
|
||||
<string name="dma_accuracy_description">Controls the DMA precision accuracy. Safe precision can fix issues in some games, but it can also impact performance in some cases. If unsure, leave this on Default.</string>
|
||||
<string name="gpu_fence_behavior">GPU Fence Behavior</string>
|
||||
@@ -1039,7 +1039,6 @@
|
||||
|
||||
<!-- Renderer Accuracy -->
|
||||
<string name="renderer_accuracy_low">Fast</string>
|
||||
<string name="renderer_accuracy_medium">Balanced</string>
|
||||
<string name="renderer_accuracy_high">Accurate</string>
|
||||
|
||||
<!-- DMA Accuracy -->
|
||||
|
||||
@@ -476,6 +476,29 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se
|
||||
path.erase(std::unique(start, path.end(),
|
||||
[type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),
|
||||
path.end());
|
||||
|
||||
const bool absolute = !path.empty() && path[0] == type2;
|
||||
std::vector<std::string_view> parts;
|
||||
|
||||
for (const auto part : SplitPathComponents(path))
|
||||
{
|
||||
if (part.empty() || part == ".")
|
||||
continue;
|
||||
if (part == ".." && !parts.empty() && parts.back() != "..")
|
||||
parts.pop_back();
|
||||
else if (part != "..") parts.push_back(part);
|
||||
}
|
||||
|
||||
std::string resolved = absolute ? std::string(1, type2) : std::string{};
|
||||
for (std::size_t i = 0; i < parts.size(); ++i)
|
||||
{
|
||||
if (i != 0)
|
||||
resolved += type2;
|
||||
resolved.append(parts[i].data(), parts[i].size());
|
||||
}
|
||||
|
||||
path = std::move(resolved);
|
||||
|
||||
return std::string(RemoveTrailingSlash(path));
|
||||
}
|
||||
|
||||
|
||||
@@ -145,7 +145,14 @@ bool Patcher::PatchText(std::span<const u8> program_image, const Kernel::CodeSet
|
||||
|
||||
// MRS Xn, CNTFRQ_EL0
|
||||
if (auto mrs = MRS{inst}; mrs.Verify() && mrs.GetSystemReg() == CntfrqEl0) {
|
||||
UNREACHABLE();
|
||||
bool pre_buffer = false;
|
||||
auto ret = AddRelocations(pre_buffer);
|
||||
if (pre_buffer) {
|
||||
WriteCntfrqHandler(ret, oaknut::XReg{static_cast<int>(mrs.GetRt())}, c_pre);
|
||||
} else {
|
||||
WriteCntfrqHandler(ret, oaknut::XReg{static_cast<int>(mrs.GetRt())}, c);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// MSR TPIDR_EL0, Xn
|
||||
@@ -577,6 +584,16 @@ void Patcher::WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg,
|
||||
this->BranchToModule(module_dest);
|
||||
}
|
||||
|
||||
void Patcher::WriteCntfrqHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::VectorCodeGenerator& cg) {
|
||||
cg.MOV(dest_reg, Common::WallClock::CNTFRQ);
|
||||
|
||||
// Jump back to the instruction after the emulated MRS.
|
||||
if (&cg == &c_pre)
|
||||
this->BranchToModulePre(module_dest);
|
||||
else
|
||||
this->BranchToModule(module_dest);
|
||||
}
|
||||
|
||||
void Patcher::WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::VectorCodeGenerator& cg) {
|
||||
#if defined(HAS_NCE)
|
||||
static Common::WallClock clock(false, 1);
|
||||
|
||||
@@ -80,6 +80,7 @@ private:
|
||||
void WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id, oaknut::VectorCodeGenerator& code, oaknut::Label& save_ctx, oaknut::Label& load_ctx);
|
||||
void WriteMrsHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::SystemReg src_reg, oaknut::VectorCodeGenerator& code);
|
||||
void WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg, oaknut::VectorCodeGenerator& code);
|
||||
void WriteCntfrqHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::VectorCodeGenerator& code);
|
||||
void WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::VectorCodeGenerator& code);
|
||||
|
||||
// Convenience wrappers using default code generator
|
||||
@@ -90,6 +91,7 @@ private:
|
||||
void WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id) { WriteSvcTrampoline(module_dest, svc_id, c, m_save_context, m_load_context); }
|
||||
void WriteMrsHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::SystemReg src_reg) { WriteMrsHandler(module_dest, dest_reg, src_reg, c); }
|
||||
void WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg) { WriteMsrHandler(module_dest, src_reg, c); }
|
||||
void WriteCntfrqHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg) { WriteCntfrqHandler(module_dest, dest_reg, c); }
|
||||
void WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg) { WriteCntpctHandler(module_dest, dest_reg, c); }
|
||||
|
||||
private:
|
||||
|
||||
@@ -984,6 +984,22 @@ bool RegisteredCache::RemoveExistingEntry(u64 title_id) const {
|
||||
return removed_data;
|
||||
}
|
||||
|
||||
bool RegisteredCache::Delete(const NcaID& id) const {
|
||||
const auto path = GetRelativePathFromNcaID(id, false, true, false);
|
||||
|
||||
const bool is_file = dir->GetFileRelative(path) != nullptr;
|
||||
const bool is_dir = dir->GetDirectoryRelative(path) != nullptr;
|
||||
|
||||
if (is_file) {
|
||||
return dir->DeleteFile(path);
|
||||
}
|
||||
if (is_dir) {
|
||||
return dir->DeleteSubdirectoryRecursive(path);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFunction& copy,
|
||||
bool overwrite_if_exists,
|
||||
std::optional<NcaID> override_id) {
|
||||
|
||||
@@ -188,6 +188,7 @@ public:
|
||||
|
||||
// Removes an existing entry based on title id
|
||||
bool RemoveExistingEntry(u64 title_id) const;
|
||||
bool Delete(const NcaID& id) const;
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
|
||||
@@ -35,6 +35,16 @@ namespace {
|
||||
|
||||
constexpr size_t MaxOpenFiles = 8192;
|
||||
|
||||
bool IsWithinRoot(std::string_view root, std::string_view full_path) {
|
||||
if (root.empty())
|
||||
return true;
|
||||
|
||||
if (full_path.size() < root.size() || full_path.substr(0, root.size()) != root)
|
||||
return false;
|
||||
|
||||
return full_path.size() == root.size() || full_path[root.size()] == '/' || full_path[root.size()] == '\\';
|
||||
}
|
||||
|
||||
constexpr FS::FileAccessMode ModeFlagsToFileAccessMode(OpenMode mode) {
|
||||
switch (mode) {
|
||||
case OpenMode::Read:
|
||||
@@ -403,7 +413,8 @@ RealVfsDirectory::~RealVfsDirectory() = default;
|
||||
|
||||
VirtualFile RealVfsDirectory::GetFileRelative(std::string_view relative_path) const {
|
||||
const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path));
|
||||
if (!FS::Exists(full_path) || FS::IsDir(full_path)) {
|
||||
if (!FS::Exists(full_path) || FS::IsDir(full_path)
|
||||
|| !IsWithinRoot(FS::SanitizePath(path), full_path)) {
|
||||
return nullptr;
|
||||
}
|
||||
return base.OpenFile(full_path, perms);
|
||||
@@ -411,7 +422,8 @@ VirtualFile RealVfsDirectory::GetFileRelative(std::string_view relative_path) co
|
||||
|
||||
VirtualDir RealVfsDirectory::GetDirectoryRelative(std::string_view relative_path) const {
|
||||
const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path));
|
||||
if (!FS::Exists(full_path) || !FS::IsDir(full_path)) {
|
||||
if (!FS::Exists(full_path) || !FS::IsDir(full_path)
|
||||
|| !IsWithinRoot(FS::SanitizePath(path), full_path)) {
|
||||
return nullptr;
|
||||
}
|
||||
return base.OpenDirectory(full_path, perms);
|
||||
@@ -427,7 +439,7 @@ VirtualDir RealVfsDirectory::GetSubdirectory(std::string_view name) const {
|
||||
|
||||
VirtualFile RealVfsDirectory::CreateFileRelative(std::string_view relative_path) {
|
||||
const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path));
|
||||
if (!FS::CreateParentDirs(full_path)) {
|
||||
if (!FS::CreateParentDirs(full_path) || !IsWithinRoot(FS::SanitizePath(path), full_path)) {
|
||||
return nullptr;
|
||||
}
|
||||
return base.CreateFile(full_path, perms);
|
||||
@@ -440,7 +452,7 @@ VirtualDir RealVfsDirectory::CreateDirectoryRelative(std::string_view relative_p
|
||||
|
||||
bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) {
|
||||
const auto full_path = FS::SanitizePath(this->path + '/' + std::string(name));
|
||||
return base.DeleteDirectory(full_path);
|
||||
return FS::RemoveDirRecursively(full_path);
|
||||
}
|
||||
|
||||
std::vector<VirtualFile> RealVfsDirectory::GetFiles() const {
|
||||
@@ -506,7 +518,7 @@ VirtualFile RealVfsDirectory::CreateFile(std::string_view name) {
|
||||
|
||||
bool RealVfsDirectory::DeleteSubdirectory(std::string_view name) {
|
||||
const std::string subdir_path = (path + '/').append(name);
|
||||
return base.DeleteDirectory(subdir_path);
|
||||
return FS::RemoveDir(subdir_path);
|
||||
}
|
||||
|
||||
bool RealVfsDirectory::DeleteFile(std::string_view name) {
|
||||
|
||||
@@ -57,7 +57,7 @@ ISelfController::ISelfController(Core::System& system_, std::shared_ptr<Applet>
|
||||
{64, nullptr, "SetInputDetectionSourceSet"},
|
||||
{65, D<&ISelfController::ReportUserIsActive>, "ReportUserIsActive"},
|
||||
{66, nullptr, "GetCurrentIlluminance"},
|
||||
{67, nullptr, "IsIlluminanceAvailable"},
|
||||
{67, D<&ISelfController::IsIlluminanceAvailable>, "IsIlluminanceAvailable"},
|
||||
{68, D<&ISelfController::SetAutoSleepDisabled>, "SetAutoSleepDisabled"},
|
||||
{69, D<&ISelfController::IsAutoSleepDisabled>, "IsAutoSleepDisabled"},
|
||||
{70, nullptr, "ReportMultimediaError"},
|
||||
@@ -347,6 +347,12 @@ Result ISelfController::IsAutoSleepDisabled(Out<bool> out_is_auto_sleep_disabled
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ISelfController::IsIlluminanceAvailable(Out<bool> out_is_illuminance_available) {
|
||||
LOG_WARNING(Service_AM, "(stubbed)");
|
||||
*out_is_illuminance_available = false;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ISelfController::SetInputDetectionPolicy(InputDetectionPolicy input_detection_policy) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
R_SUCCEED();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -60,6 +60,7 @@ private:
|
||||
Result ReportUserIsActive();
|
||||
Result SetAutoSleepDisabled(bool is_auto_sleep_disabled);
|
||||
Result IsAutoSleepDisabled(Out<bool> out_is_auto_sleep_disabled);
|
||||
Result IsIlluminanceAvailable(Out<bool> out_is_illuminance_available);
|
||||
Result SetInputDetectionPolicy(InputDetectionPolicy input_detection_policy);
|
||||
Result GetAccumulatedSuspendedTickValue(Out<u64> out_accumulated_suspended_tick_value);
|
||||
Result GetAccumulatedSuspendedTickChangedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
|
||||
@@ -43,6 +43,16 @@ static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
|
||||
return base->GetDirectoryRelative(dir_name);
|
||||
}
|
||||
|
||||
static std::string_view GetGuestParentPath(std::string_view path) {
|
||||
const auto name_index = path.find_last_of("\\/");
|
||||
return name_index == std::string_view::npos ? std::string_view{} : path.substr(0, name_index);
|
||||
}
|
||||
|
||||
static std::string_view GetGuestFilename(std::string_view path) {
|
||||
const auto name_index = path.find_last_of("\\/");
|
||||
return name_index == std::string_view::npos ? path : path.substr(name_index + 1);
|
||||
}
|
||||
|
||||
VfsDirectoryServiceWrapper::VfsDirectoryServiceWrapper(FileSys::VirtualDir backing_)
|
||||
: backing(std::move(backing_)) {}
|
||||
|
||||
@@ -83,11 +93,12 @@ Result VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
|
||||
if (dir == nullptr || dir->GetFile(Common::FS::GetFilename(path)) == nullptr) {
|
||||
const auto filename = GetGuestFilename(path);
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, GetGuestParentPath(path));
|
||||
if (filename.empty() || dir == nullptr || dir->GetFile(filename) == nullptr) {
|
||||
return FileSys::ResultPathNotFound;
|
||||
}
|
||||
if (!dir->DeleteFile(Common::FS::GetFilename(path))) {
|
||||
if (!dir->DeleteFile(filename)) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
return ResultUnknown;
|
||||
}
|
||||
@@ -97,7 +108,9 @@ Result VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
|
||||
|
||||
Result VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const {
|
||||
std::string path(Common::FS::SanitizePath(path_));
|
||||
|
||||
if (GetDirectoryRelativeWrapped(backing, path) != nullptr) {
|
||||
return FileSys::ResultPathAlreadyExists;
|
||||
}
|
||||
// NOTE: This is inaccurate behavior. CreateDirectory is not recursive.
|
||||
// CreateDirectory should return PathNotFound if the parent directory does not exist.
|
||||
// This is here temporarily in order to have UMM "work" in the meantime.
|
||||
@@ -117,8 +130,19 @@ Result VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) con
|
||||
|
||||
Result VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const {
|
||||
std::string path(Common::FS::SanitizePath(path_));
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
|
||||
if (!dir->DeleteSubdirectory(Common::FS::GetFilename(path))) {
|
||||
const auto dirname = GetGuestFilename(path);
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, GetGuestParentPath(path));
|
||||
FileSys::VirtualDir target{};
|
||||
if (!dirname.empty() && dir != nullptr) {
|
||||
target = dir->GetSubdirectory(dirname);
|
||||
}
|
||||
if (target == nullptr) {
|
||||
return FileSys::ResultPathNotFound;
|
||||
}
|
||||
if (!target->GetFiles().empty() || !target->GetSubdirectories().empty()) {
|
||||
return ResultUnknown;
|
||||
}
|
||||
if (!dir->DeleteSubdirectory(dirname)) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
return ResultUnknown;
|
||||
}
|
||||
@@ -127,8 +151,12 @@ Result VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) con
|
||||
|
||||
Result VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const {
|
||||
std::string path(Common::FS::SanitizePath(path_));
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
|
||||
if (!dir->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path))) {
|
||||
const auto dirname = GetGuestFilename(path);
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, GetGuestParentPath(path));
|
||||
if (dirname.empty() || dir == nullptr || dir->GetSubdirectory(dirname) == nullptr) {
|
||||
return FileSys::ResultPathNotFound;
|
||||
}
|
||||
if (!dir->DeleteSubdirectoryRecursive(dirname)) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
return ResultUnknown;
|
||||
}
|
||||
@@ -137,9 +165,13 @@ Result VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string&
|
||||
|
||||
Result VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const {
|
||||
const std::string sanitized_path(Common::FS::SanitizePath(path));
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(sanitized_path));
|
||||
const auto dirname = GetGuestFilename(sanitized_path);
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, GetGuestParentPath(sanitized_path));
|
||||
|
||||
if (!dir->CleanSubdirectoryRecursive(Common::FS::GetFilename(sanitized_path))) {
|
||||
if (dirname.empty() || dir == nullptr || dir->GetSubdirectory(dirname) == nullptr) {
|
||||
return FileSys::ResultPathNotFound;
|
||||
}
|
||||
if (!dir->CleanSubdirectoryRecursive(dirname)) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
return ResultUnknown;
|
||||
}
|
||||
|
||||
@@ -237,7 +237,7 @@ IHidServer::IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> r
|
||||
{3013, nullptr, "SetDebugPadGenericPadMap"}, //21.0.0+
|
||||
{3014, nullptr, "GetDebugPadKeyboardMap"}, //21.0.0+
|
||||
{3015, nullptr, "SetDebugPadKeyboardMap"}, //21.0.0+
|
||||
{3150, nullptr, "SetMouseLibraryVersion"}, //21.0.0+
|
||||
{3150, C<&IHidServer::SetMouseLibraryVersion>, "SetMouseLibraryVersion"}, //21.0.0+
|
||||
// What? -- {12010, nullptr, "SetButtonConfigLeft"},
|
||||
};
|
||||
// clang-format on
|
||||
@@ -1471,6 +1471,12 @@ Result IHidServer::SetTouchScreenResolution(u32 width, u32 height,
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IHidServer::SetMouseLibraryVersion(ClientAppletResourceUserId aruid) {
|
||||
LOG_INFO(Service_HID, "(STUBBED) called, applet_resource_user_id={}", aruid.pid);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
std::shared_ptr<ResourceManager> IHidServer::GetResourceManager() {
|
||||
resource_manager->Initialize();
|
||||
return resource_manager;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
@@ -263,6 +263,7 @@ private:
|
||||
Result IsFirmwareUpdateNeededForNotification(Out<bool> out_is_firmware_update_needed,
|
||||
s32 unknown, ClientAppletResourceUserId aruid);
|
||||
Result SetTouchScreenResolution(u32 width, u32 height, ClientAppletResourceUserId aruid);
|
||||
Result SetMouseLibraryVersion(ClientAppletResourceUserId aruid);
|
||||
|
||||
std::shared_ptr<ResourceManager> resource_manager;
|
||||
std::shared_ptr<HidFirmwareSettings> firmware_settings;
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/file_sys/romfs_factory.h"
|
||||
#include "core/hle/api_version.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/ncm/ncm.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
@@ -88,6 +98,268 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class IContentStorage final : public ServiceFramework<IContentStorage> {
|
||||
public:
|
||||
explicit IContentStorage(Core::System& system_, FileSys::StorageId id)
|
||||
: ServiceFramework{system_, "IContentStorage"}, storage{id} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IContentStorage::GeneratePlaceHolderId, "GeneratePlaceHolderId"},
|
||||
{1, &IContentStorage::CreatePlaceHolder, "CreatePlaceHolder"},
|
||||
{2, &IContentStorage::DeletePlaceHolder, "DeletePlaceHolder"},
|
||||
{4, &IContentStorage::WritePlaceHolder, "WritePlaceHolder"},
|
||||
{5, &IContentStorage::Register, "Register"},
|
||||
{6, &IContentStorage::Delete, "Delete"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void GeneratePlaceHolderId(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NCM, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw(FileSys::PlaceholderCache::Generate());
|
||||
}
|
||||
|
||||
void CreatePlaceHolder(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
[[maybe_unused]] FileSys::NcaID content_id{};
|
||||
FileSys::NcaID placeholder_id{};
|
||||
if constexpr (HLE::ApiVersion::HOS_VERSION_MAJOR >= 16) {
|
||||
placeholder_id = rp.PopRaw<FileSys::NcaID>();
|
||||
content_id = rp.PopRaw<FileSys::NcaID>();
|
||||
} else {
|
||||
content_id = rp.PopRaw<FileSys::NcaID>();
|
||||
placeholder_id = rp.PopRaw<FileSys::NcaID>();
|
||||
}
|
||||
const auto size = rp.Pop<s64>();
|
||||
|
||||
auto* const placeholder_cache =
|
||||
system.GetFileSystemController().GetPlaceholderCacheForStorage(storage);
|
||||
const bool succeeded =
|
||||
placeholder_cache != nullptr && size >= 0 &&
|
||||
(placeholder_cache->Exists(placeholder_id) ||
|
||||
placeholder_cache->Create(placeholder_id, static_cast<u64>(size)));
|
||||
|
||||
if (succeeded) {
|
||||
LOG_DEBUG(Service_NCM, "called, storage_id={}, size={}", static_cast<u32>(storage),
|
||||
size);
|
||||
} else {
|
||||
LOG_WARNING(Service_NCM, "failed, storage_id={}, size={}", static_cast<u32>(storage),
|
||||
size);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(succeeded ? ResultSuccess : ResultUnknown);
|
||||
}
|
||||
|
||||
void DeletePlaceHolder(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto placeholder_id = rp.PopRaw<FileSys::NcaID>();
|
||||
|
||||
auto* const placeholder_cache =
|
||||
system.GetFileSystemController().GetPlaceholderCacheForStorage(storage);
|
||||
const bool succeeded =
|
||||
placeholder_cache != nullptr &&
|
||||
(!placeholder_cache->Exists(placeholder_id) || placeholder_cache->Delete(placeholder_id));
|
||||
|
||||
if (succeeded) {
|
||||
LOG_DEBUG(Service_NCM, "called, storage_id={}", static_cast<u32>(storage));
|
||||
} else {
|
||||
LOG_WARNING(Service_NCM, "failed, storage_id={}", static_cast<u32>(storage));
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(succeeded ? ResultSuccess : ResultUnknown);
|
||||
}
|
||||
|
||||
void WritePlaceHolder(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto placeholder_id = rp.PopRaw<FileSys::NcaID>();
|
||||
const auto offset = rp.Pop<u64>();
|
||||
const auto data = ctx.ReadBuffer();
|
||||
|
||||
auto* const placeholder_cache =
|
||||
system.GetFileSystemController().GetPlaceholderCacheForStorage(storage);
|
||||
const std::vector<u8> write_data{data.begin(), data.end()};
|
||||
const bool succeeded =
|
||||
placeholder_cache != nullptr &&
|
||||
placeholder_cache->Write(placeholder_id, offset, write_data);
|
||||
|
||||
if (succeeded) {
|
||||
LOG_DEBUG(Service_NCM, "called, storage_id={}, offset={}, size={}",
|
||||
static_cast<u32>(storage), offset, data.size());
|
||||
} else {
|
||||
LOG_WARNING(Service_NCM, "failed, storage_id={}, offset={}, size={}",
|
||||
static_cast<u32>(storage), offset, data.size());
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(succeeded ? ResultSuccess : ResultUnknown);
|
||||
}
|
||||
|
||||
void Register(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
FileSys::NcaID content_id{};
|
||||
FileSys::NcaID placeholder_id{};
|
||||
if constexpr (HLE::ApiVersion::HOS_VERSION_MAJOR >= 16) {
|
||||
placeholder_id = rp.PopRaw<FileSys::NcaID>();
|
||||
content_id = rp.PopRaw<FileSys::NcaID>();
|
||||
} else {
|
||||
content_id = rp.PopRaw<FileSys::NcaID>();
|
||||
placeholder_id = rp.PopRaw<FileSys::NcaID>();
|
||||
}
|
||||
|
||||
auto& fsc = system.GetFileSystemController();
|
||||
auto* const placeholder_cache = fsc.GetPlaceholderCacheForStorage(storage);
|
||||
auto* const registered_cache = fsc.GetRegisteredCacheForStorage(storage);
|
||||
const bool succeeded =
|
||||
placeholder_cache != nullptr && registered_cache != nullptr &&
|
||||
placeholder_cache->Register(registered_cache, placeholder_id, content_id);
|
||||
|
||||
if (succeeded) {
|
||||
LOG_DEBUG(Service_NCM, "called, storage_id={}", static_cast<u32>(storage));
|
||||
} else {
|
||||
LOG_WARNING(Service_NCM, "failed, storage_id={}", static_cast<u32>(storage));
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(succeeded ? ResultSuccess : ResultUnknown);
|
||||
}
|
||||
|
||||
void Delete(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto content_id = rp.PopRaw<FileSys::NcaID>();
|
||||
|
||||
auto* const registered_cache =
|
||||
system.GetFileSystemController().GetRegisteredCacheForStorage(storage);
|
||||
const bool succeeded = registered_cache != nullptr && registered_cache->Delete(content_id);
|
||||
if (succeeded) {
|
||||
registered_cache->Refresh();
|
||||
}
|
||||
|
||||
if (succeeded) {
|
||||
LOG_DEBUG(Service_NCM, "called, storage_id={}", static_cast<u32>(storage));
|
||||
} else {
|
||||
LOG_WARNING(Service_NCM, "failed, storage_id={}", static_cast<u32>(storage));
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(succeeded ? ResultSuccess : ResultUnknown);
|
||||
}
|
||||
|
||||
FileSys::StorageId storage;
|
||||
};
|
||||
|
||||
class IContentMetaDatabase final : public ServiceFramework<IContentMetaDatabase> {
|
||||
public:
|
||||
explicit IContentMetaDatabase(Core::System& system_, FileSys::StorageId id)
|
||||
: ServiceFramework{system_, "IContentMetaDatabase"}, storage{id} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IContentMetaDatabase::Set, "Set"},
|
||||
{2, &IContentMetaDatabase::Remove, "Remove"},
|
||||
{8, &IContentMetaDatabase::Has, "Has"},
|
||||
{15, &IContentMetaDatabase::Commit, "Commit"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
struct ContentMetaKey {
|
||||
u64 id;
|
||||
u32 version;
|
||||
FileSys::TitleType type;
|
||||
u8 install_type;
|
||||
std::array<u8, 2> padding;
|
||||
};
|
||||
static_assert(sizeof(ContentMetaKey) == 0x10);
|
||||
|
||||
void Set(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto key = rp.PopRaw<ContentMetaKey>();
|
||||
|
||||
const auto entry_matches = [&key](const ContentMetaKey& entry) {
|
||||
return entry.id == key.id && entry.version == key.version && entry.type == key.type &&
|
||||
entry.install_type == key.install_type;
|
||||
};
|
||||
if (std::find_if(entries.begin(), entries.end(), entry_matches) == entries.end()) {
|
||||
entries.push_back(key);
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_NCM,
|
||||
"called, storage_id={}, title_id={:016X}, version={}, type={}, size={}",
|
||||
static_cast<u32>(storage), key.id, key.version, static_cast<u8>(key.type),
|
||||
ctx.GetReadBufferSize());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void Remove(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto key = rp.PopRaw<ContentMetaKey>();
|
||||
|
||||
std::erase_if(entries, [&key](const ContentMetaKey& entry) {
|
||||
return entry.id == key.id && entry.version == key.version && entry.type == key.type &&
|
||||
entry.install_type == key.install_type;
|
||||
});
|
||||
|
||||
LOG_DEBUG(Service_NCM, "called, storage_id={}, title_id={:016X}, version={}, type={}",
|
||||
static_cast<u32>(storage), key.id, key.version, static_cast<u8>(key.type));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void Has(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto key = rp.PopRaw<ContentMetaKey>();
|
||||
|
||||
const bool has_pending =
|
||||
std::find_if(entries.begin(), entries.end(), [&key](const ContentMetaKey& entry) {
|
||||
return entry.id == key.id && entry.version == key.version &&
|
||||
entry.type == key.type && entry.install_type == key.install_type;
|
||||
}) != entries.end();
|
||||
|
||||
auto* const registered_cache =
|
||||
system.GetFileSystemController().GetRegisteredCacheForStorage(storage);
|
||||
const bool has_registered =
|
||||
registered_cache != nullptr &&
|
||||
registered_cache->HasEntry(key.id, FileSys::ContentRecordType::Meta);
|
||||
|
||||
LOG_DEBUG(Service_NCM, "called, storage_id={}, title_id={:016X}, version={}, type={}, has={}",
|
||||
static_cast<u32>(storage), key.id, key.version, static_cast<u8>(key.type),
|
||||
has_pending || has_registered);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(has_pending || has_registered);
|
||||
}
|
||||
|
||||
void Commit(HLERequestContext& ctx) {
|
||||
auto* const registered_cache =
|
||||
system.GetFileSystemController().GetRegisteredCacheForStorage(storage);
|
||||
if (registered_cache != nullptr) {
|
||||
registered_cache->Refresh();
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_NCM, "called, storage_id={}", static_cast<u32>(storage));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
FileSys::StorageId storage;
|
||||
std::vector<ContentMetaKey> entries;
|
||||
};
|
||||
|
||||
class LR final : public ServiceFramework<LR> {
|
||||
public:
|
||||
explicit LR(Core::System& system_) : ServiceFramework{system_, "lr"} {
|
||||
@@ -113,8 +385,8 @@ public:
|
||||
{1, nullptr, "CreateContentMetaDatabase"},
|
||||
{2, nullptr, "VerifyContentStorage"},
|
||||
{3, nullptr, "VerifyContentMetaDatabase"},
|
||||
{4, nullptr, "OpenContentStorage"},
|
||||
{5, nullptr, "OpenContentMetaDatabase"},
|
||||
{4, &NCM::OpenContentStorage, "OpenContentStorage"},
|
||||
{5, &NCM::OpenContentMetaDatabase, "OpenContentMetaDatabase"},
|
||||
{6, nullptr, "CloseContentStorageForcibly"},
|
||||
{7, nullptr, "CloseContentMetaDatabaseForcibly"},
|
||||
{8, nullptr, "CleanupContentMetaDatabase"},
|
||||
@@ -130,6 +402,29 @@ public:
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void OpenContentStorage(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto storage_id = rp.PopEnum<FileSys::StorageId>();
|
||||
|
||||
LOG_DEBUG(Service_NCM, "called, storage_id={}", static_cast<u32>(storage_id));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IContentStorage>(ctx, system, storage_id);
|
||||
}
|
||||
|
||||
void OpenContentMetaDatabase(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto storage_id = rp.PopEnum<FileSys::StorageId>();
|
||||
|
||||
LOG_DEBUG(Service_NCM, "called, storage_id={}", static_cast<u32>(storage_id));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IContentMetaDatabase>(ctx, system, storage_id);
|
||||
}
|
||||
};
|
||||
|
||||
void LoopProcess(Core::System& system) {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/ns/application_manager_interface.h"
|
||||
|
||||
#include "core/file_sys/content_archive.h"
|
||||
@@ -19,6 +20,7 @@
|
||||
#include "core/launch_timestamp_cache.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
namespace Service::NS {
|
||||
@@ -36,14 +38,14 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
|
||||
{1, nullptr, "GenerateApplicationRecordCount"},
|
||||
{2, D<&IApplicationManagerInterface::GetApplicationRecordUpdateSystemEvent>, "GetApplicationRecordUpdateSystemEvent"},
|
||||
{3, nullptr, "GetApplicationViewDeprecated"},
|
||||
{4, nullptr, "DeleteApplicationEntity"},
|
||||
{5, nullptr, "DeleteApplicationCompletely"},
|
||||
{4, D<&IApplicationManagerInterface::DeleteApplicationEntity>, "DeleteApplicationEntity"},
|
||||
{5, D<&IApplicationManagerInterface::DeleteApplicationCompletely>, "DeleteApplicationCompletely"},
|
||||
{6, nullptr, "IsAnyApplicationEntityRedundant"},
|
||||
{7, nullptr, "DeleteRedundantApplicationEntity"},
|
||||
{8, nullptr, "IsApplicationEntityMovable"},
|
||||
{9, nullptr, "MoveApplicationEntity"},
|
||||
{11, nullptr, "CalculateApplicationOccupiedSize"},
|
||||
{16, nullptr, "PushApplicationRecord"},
|
||||
{16, &IApplicationManagerInterface::PushApplicationRecord, "PushApplicationRecord"},
|
||||
{17, nullptr, "ListApplicationRecordContentMeta"},
|
||||
{19, nullptr, "LaunchApplicationOld"},
|
||||
{21, nullptr, "GetApplicationContentPath"},
|
||||
@@ -643,6 +645,27 @@ Result IApplicationManagerInterface::IsAnyApplicationEntityInstalled(
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::DeleteApplicationEntity(u64 application_id) {
|
||||
LOG_DEBUG(Service_NS, "called, application_id={:016X}", application_id);
|
||||
|
||||
auto& fsc = system.GetFileSystemController();
|
||||
if (auto* const user_cache = fsc.GetUserNANDContents(); user_cache != nullptr) {
|
||||
user_cache->RemoveExistingEntry(application_id);
|
||||
user_cache->Refresh();
|
||||
}
|
||||
if (auto* const sdmc_cache = fsc.GetSDMCContents(); sdmc_cache != nullptr) {
|
||||
sdmc_cache->RemoveExistingEntry(application_id);
|
||||
sdmc_cache->Refresh();
|
||||
}
|
||||
|
||||
record_update_system_event.Signal(system.Kernel());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::DeleteApplicationCompletely(u64 application_id) {
|
||||
R_RETURN(DeleteApplicationEntity(application_id));
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::GetApplicationViewDeprecated(
|
||||
OutArray<ApplicationViewV19, BufferAttr_HipcMapAlias> out_application_views,
|
||||
InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
|
||||
@@ -843,6 +866,29 @@ Result IApplicationManagerInterface::Unknown4053() {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void IApplicationManagerInterface::PushApplicationRecord(HLERequestContext& ctx) {
|
||||
const auto record = ctx.ReadBuffer();
|
||||
u64 application_id{};
|
||||
if (record.size() >= sizeof(application_id)) {
|
||||
std::memcpy(&application_id, record.data(), sizeof(application_id));
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_NS, "called, application_id={:016X}, size={}", application_id, record.size());
|
||||
|
||||
auto& fsc = system.GetFileSystemController();
|
||||
if (auto* const user_cache = fsc.GetUserNANDContents(); user_cache != nullptr) {
|
||||
user_cache->Refresh();
|
||||
}
|
||||
if (auto* const sdmc_cache = fsc.GetSDMCContents(); sdmc_cache != nullptr) {
|
||||
sdmc_cache->Refresh();
|
||||
}
|
||||
|
||||
record_update_system_event.Signal(system.Kernel());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IApplicationManagerInterface::ListApplicationTitle(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
IReadOnlyApplicationControlDataInterface(system).ListApplicationTitle(ctx);
|
||||
|
||||
@@ -56,6 +56,8 @@ public:
|
||||
Result ResumeAll();
|
||||
Result IsQualificationTransitionSupportedByProcessId(Out<bool> out_is_supported,
|
||||
u64 process_id);
|
||||
Result DeleteApplicationEntity(u64 application_id);
|
||||
Result DeleteApplicationCompletely(u64 application_id);
|
||||
Result GetStorageSize(Out<s64> out_total_space_size, Out<s64> out_free_space_size,
|
||||
FileSys::StorageId storage_id);
|
||||
Result TouchApplication(u64 application_id);
|
||||
@@ -74,6 +76,7 @@ public:
|
||||
Result RequestDownloadApplicationControlDataInBackground(u64 control_source,
|
||||
u64 application_id);
|
||||
|
||||
void PushApplicationRecord(HLERequestContext& ctx);
|
||||
void ListApplicationTitle(HLERequestContext& ctx);
|
||||
|
||||
private:
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -28,9 +31,9 @@ SPL_MIG::SPL_MIG(Core::System& system_, std::shared_ptr<Module> module_)
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &SPL::GetConfig, "GetConfig"},
|
||||
{1, &SPL::ModularExponentiate, "ModularExponentiate"},
|
||||
{2, nullptr, "GenerateAesKek"},
|
||||
{2, &SPL::GenerateAesKek, "GenerateAesKek"},
|
||||
{3, nullptr, "LoadAesKey"},
|
||||
{4, nullptr, "GenerateAesKey"},
|
||||
{4, &SPL::GenerateAesKey, "GenerateAesKey"},
|
||||
{5, &SPL::SetConfig, "SetConfig"},
|
||||
{7, &SPL::GenerateRandomBytes, "GenerateRandomBytes"},
|
||||
{11, &SPL::IsDevelopment, "IsDevelopment"},
|
||||
|
||||
@@ -59,6 +59,36 @@ void Module::Interface::ModularExponentiate(HLERequestContext& ctx) {
|
||||
rb.Push(ResultSecureMonitorNotImplemented);
|
||||
}
|
||||
|
||||
void Module::Interface::GenerateAesKek(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
[[maybe_unused]] const auto key_source = rp.PopRaw<KeySource>();
|
||||
const auto generation = rp.Pop<u32>();
|
||||
const auto option = rp.Pop<u32>();
|
||||
|
||||
LOG_WARNING(Service_SPL, "(STUBBED) called, generation={:#x}, option={:#x}", generation,
|
||||
option);
|
||||
|
||||
AccessKey access_key{};
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw(access_key);
|
||||
}
|
||||
|
||||
void Module::Interface::GenerateAesKey(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
[[maybe_unused]] const auto access_key = rp.PopRaw<AccessKey>();
|
||||
[[maybe_unused]] const auto key_source = rp.PopRaw<KeySource>();
|
||||
|
||||
LOG_WARNING(Service_SPL, "(STUBBED) called");
|
||||
|
||||
AesKey aes_key{};
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw(aes_key);
|
||||
}
|
||||
|
||||
void Module::Interface::SetConfig(HLERequestContext& ctx) {
|
||||
UNIMPLEMENTED_MSG("SetConfig is not implemented!");
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -25,6 +28,8 @@ public:
|
||||
// General
|
||||
void GetConfig(HLERequestContext& ctx);
|
||||
void ModularExponentiate(HLERequestContext& ctx);
|
||||
void GenerateAesKek(HLERequestContext& ctx);
|
||||
void GenerateAesKey(HLERequestContext& ctx);
|
||||
void SetConfig(HLERequestContext& ctx);
|
||||
void GenerateRandomBytes(HLERequestContext& ctx);
|
||||
void IsDevelopment(HLERequestContext& ctx);
|
||||
|
||||
@@ -34,8 +34,7 @@ oaknut::Label EmitA32Cond(oaknut::CodeGenerator& code, EmitContext&, IR::Cond co
|
||||
return pass;
|
||||
}
|
||||
|
||||
void EmitA32LeafTerminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step);
|
||||
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step);
|
||||
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step);
|
||||
|
||||
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) {
|
||||
EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher);
|
||||
@@ -126,53 +125,31 @@ void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Fa
|
||||
|
||||
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
oaknut::Label pass = EmitA32Cond(code, ctx, terminal.if_);
|
||||
EmitA32LeafTerminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||
EmitA32Terminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||
code.l(pass);
|
||||
EmitA32LeafTerminal(code, ctx, terminal.then_, initial_location, is_single_step);
|
||||
EmitA32Terminal(code, ctx, terminal.then_, initial_location, is_single_step);
|
||||
}
|
||||
|
||||
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
oaknut::Label fail;
|
||||
code.LDRB(Wscratch0, SP, offsetof(StackLayout, check_bit));
|
||||
code.CBZ(Wscratch0, fail);
|
||||
EmitA32LeafTerminal(code, ctx, terminal.then_, initial_location, is_single_step);
|
||||
EmitA32Terminal(code, ctx, terminal.then_, initial_location, is_single_step);
|
||||
code.l(fail);
|
||||
EmitA32LeafTerminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||
EmitA32Terminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||
}
|
||||
|
||||
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
oaknut::Label fail;
|
||||
code.LDAR(Wscratch0, Xhalt);
|
||||
code.CBNZ(Wscratch0, fail);
|
||||
EmitA32LeafTerminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||
EmitA32Terminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||
code.l(fail);
|
||||
EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher);
|
||||
}
|
||||
|
||||
void EmitA32LeafTerminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
if (auto const x = std::get_if<IR::Term::ReturnToDispatch>(&terminal))
|
||||
return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::LinkBlock>(&terminal))
|
||||
return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::LinkBlockFast>(&terminal))
|
||||
return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::PopRSBHint>(&terminal))
|
||||
return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::FastDispatchHint>(&terminal))
|
||||
return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
if (auto const x = std::get_if<IR::Term::LeafTerminal>(&terminal))
|
||||
return EmitA32LeafTerminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::If>(&terminal))
|
||||
return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::CheckBit>(&terminal))
|
||||
return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::CheckHalt>(&terminal))
|
||||
return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
UNREACHABLE();
|
||||
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
boost::apply_visitor([&](const auto& t) { EmitA32Terminal(code, ctx, t, initial_location, is_single_step); }, terminal);
|
||||
}
|
||||
|
||||
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx) {
|
||||
|
||||
@@ -33,8 +33,7 @@ oaknut::Label EmitA64Cond(oaknut::CodeGenerator& code, EmitContext&, IR::Cond co
|
||||
return pass;
|
||||
}
|
||||
|
||||
void EmitA64LeafTerminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step);
|
||||
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step);
|
||||
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step);
|
||||
|
||||
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) {
|
||||
EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher);
|
||||
@@ -109,53 +108,31 @@ void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Fa
|
||||
|
||||
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
oaknut::Label pass = EmitA64Cond(code, ctx, terminal.if_);
|
||||
EmitA64LeafTerminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||
EmitA64Terminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||
code.l(pass);
|
||||
EmitA64LeafTerminal(code, ctx, terminal.then_, initial_location, is_single_step);
|
||||
EmitA64Terminal(code, ctx, terminal.then_, initial_location, is_single_step);
|
||||
}
|
||||
|
||||
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
oaknut::Label fail;
|
||||
code.LDRB(Wscratch0, SP, offsetof(StackLayout, check_bit));
|
||||
code.CBZ(Wscratch0, fail);
|
||||
EmitA64LeafTerminal(code, ctx, terminal.then_, initial_location, is_single_step);
|
||||
EmitA64Terminal(code, ctx, terminal.then_, initial_location, is_single_step);
|
||||
code.l(fail);
|
||||
EmitA64LeafTerminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||
EmitA64Terminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||
}
|
||||
|
||||
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
oaknut::Label fail;
|
||||
code.LDAR(Wscratch0, Xhalt);
|
||||
code.CBNZ(Wscratch0, fail);
|
||||
EmitA64LeafTerminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||
EmitA64Terminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||
code.l(fail);
|
||||
EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher);
|
||||
}
|
||||
|
||||
void EmitA64LeafTerminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
if (auto const x = std::get_if<IR::Term::ReturnToDispatch>(&terminal))
|
||||
return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::LinkBlock>(&terminal))
|
||||
return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::LinkBlockFast>(&terminal))
|
||||
return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::PopRSBHint>(&terminal))
|
||||
return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::FastDispatchHint>(&terminal))
|
||||
return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
if (auto const x = std::get_if<IR::Term::LeafTerminal>(&terminal))
|
||||
return EmitA64LeafTerminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::If>(&terminal))
|
||||
return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::CheckBit>(&terminal))
|
||||
return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::CheckHalt>(&terminal))
|
||||
return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
UNREACHABLE();
|
||||
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
boost::apply_visitor([&](const auto& t) { EmitA64Terminal(code, ctx, t, initial_location, is_single_step); }, terminal);
|
||||
}
|
||||
|
||||
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx) {
|
||||
|
||||
@@ -112,7 +112,6 @@ void EmitA32Cond(biscuit::Assembler& as, EmitContext&, IR::Cond cond, biscuit::L
|
||||
}
|
||||
}
|
||||
|
||||
void EmitA32LeafTerminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::LeafTerminal terminal, IR::LocationDescriptor initial_location, bool is_single_step);
|
||||
void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step);
|
||||
|
||||
void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) {
|
||||
@@ -171,18 +170,18 @@ void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::FastDis
|
||||
void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
biscuit::Label pass;
|
||||
EmitA32Cond(as, ctx, terminal.if_, &pass);
|
||||
EmitA32LeafTerminal(as, ctx, terminal.else_, initial_location, is_single_step);
|
||||
EmitA32Terminal(as, ctx, terminal.else_, initial_location, is_single_step);
|
||||
as.Bind(&pass);
|
||||
EmitA32LeafTerminal(as, ctx, terminal.then_, initial_location, is_single_step);
|
||||
EmitA32Terminal(as, ctx, terminal.then_, initial_location, is_single_step);
|
||||
}
|
||||
|
||||
void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
biscuit::Label fail;
|
||||
as.LBU(Xscratch0, offsetof(StackLayout, check_bit), Xstate);
|
||||
as.BEQZ(Xscratch0, &fail);
|
||||
EmitA32LeafTerminal(as, ctx, terminal.then_, initial_location, is_single_step);
|
||||
EmitA32Terminal(as, ctx, terminal.then_, initial_location, is_single_step);
|
||||
as.Bind(&fail);
|
||||
EmitA32LeafTerminal(as, ctx, terminal.else_, initial_location, is_single_step);
|
||||
EmitA32Terminal(as, ctx, terminal.else_, initial_location, is_single_step);
|
||||
}
|
||||
|
||||
void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
@@ -190,35 +189,13 @@ void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::CheckHa
|
||||
as.LWU(Xscratch0, 0, Xhalt);
|
||||
as.FENCE(biscuit::FenceOrder::RW, biscuit::FenceOrder::RW);
|
||||
as.BNEZ(Xscratch0, &fail);
|
||||
EmitA32LeafTerminal(as, ctx, terminal.else_, initial_location, is_single_step);
|
||||
EmitA32Terminal(as, ctx, terminal.else_, initial_location, is_single_step);
|
||||
as.Bind(&fail);
|
||||
EmitRelocation(as, ctx, LinkTarget::ReturnFromRunCode);
|
||||
}
|
||||
|
||||
void EmitA32LeafTerminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::LeafTerminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
if (auto const x = std::get_if<IR::Term::ReturnToDispatch>(&terminal))
|
||||
return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::LinkBlock>(&terminal))
|
||||
return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::LinkBlockFast>(&terminal))
|
||||
return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::PopRSBHint>(&terminal))
|
||||
return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::FastDispatchHint>(&terminal))
|
||||
return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step);
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
if (auto const x = std::get_if<IR::Term::LeafTerminal>(&terminal))
|
||||
return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::If>(&terminal))
|
||||
return EmitA32Terminal(as, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::CheckBit>(&terminal))
|
||||
return EmitA32Terminal(as, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::CheckHalt>(&terminal))
|
||||
return EmitA32Terminal(as, ctx, *x, initial_location, is_single_step);
|
||||
UNREACHABLE();
|
||||
boost::apply_visitor([&](const auto& t) { EmitA32Terminal(as, ctx, t, initial_location, is_single_step); }, terminal);
|
||||
}
|
||||
|
||||
void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx) {
|
||||
|
||||
@@ -175,7 +175,7 @@ finish_this_inst:
|
||||
if (conf.enable_cycle_counting)
|
||||
EmitAddCycles(block.CycleCount());
|
||||
code.mov(rbp, code.qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, abi_base_pointer)]);
|
||||
EmitTerminal(block.terminal, ctx.Location().SetSingleStepping(false), ctx.IsSingleStep());
|
||||
EmitTerminal(block.GetTerminal(), ctx.Location().SetSingleStepping(false), ctx.IsSingleStep());
|
||||
code.int3();
|
||||
|
||||
for (auto& deferred_emit : ctx.deferred_emits)
|
||||
@@ -219,7 +219,7 @@ void A32EmitX64::EmitCondPrelude(const A32EmitContext& ctx) {
|
||||
if (conf.enable_cycle_counting) {
|
||||
EmitAddCycles(ctx.block.ConditionFailedCycleCount());
|
||||
}
|
||||
EmitLeafTerminal(IR::Term::LinkBlock{ctx.block.ConditionFailedLocation()}, ctx.Location().SetSingleStepping(false), ctx.IsSingleStep());
|
||||
EmitTerminal(IR::Term::LinkBlock{ctx.block.ConditionFailedLocation()}, ctx.Location().SetSingleStepping(false), ctx.IsSingleStep());
|
||||
code.L(pass);
|
||||
}
|
||||
|
||||
@@ -1155,12 +1155,11 @@ void A32EmitX64::EmitSetUpperLocationDescriptor(IR::LocationDescriptor new_locat
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool EmitTerminalImpl(A32EmitX64& e, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) {
|
||||
void EmitTerminalImpl(A32EmitX64& e, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) {
|
||||
e.code.ReturnFromRunCode();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmitTerminalImpl(A32EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
void EmitTerminalImpl(A32EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
e.EmitSetUpperLocationDescriptor(terminal.next, initial_location);
|
||||
if (!e.conf.HasOptimization(OptimizationFlag::BlockLinking) || is_single_step) {
|
||||
e.code.mov(MJitStateReg(A32::Reg::PC), A32::LocationDescriptor{terminal.next}.PC());
|
||||
@@ -1187,10 +1186,9 @@ bool EmitTerminalImpl(A32EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationD
|
||||
e.PushRSBHelper(rax, rbx, terminal.next);
|
||||
e.code.ForceReturnFromRunCode();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmitTerminalImpl(A32EmitX64& e, IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
void EmitTerminalImpl(A32EmitX64& e, IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
e.EmitSetUpperLocationDescriptor(terminal.next, initial_location);
|
||||
if (!e.conf.HasOptimization(OptimizationFlag::BlockLinking) || is_single_step) {
|
||||
e.code.mov(MJitStateReg(A32::Reg::PC), A32::LocationDescriptor{terminal.next}.PC());
|
||||
@@ -1203,78 +1201,55 @@ bool EmitTerminalImpl(A32EmitX64& e, IR::Term::LinkBlockFast terminal, IR::Locat
|
||||
e.EmitPatchJmp(terminal.next);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmitTerminalImpl(A32EmitX64& e, IR::Term::PopRSBHint, IR::LocationDescriptor, bool is_single_step) {
|
||||
void EmitTerminalImpl(A32EmitX64& e, IR::Term::PopRSBHint, IR::LocationDescriptor, bool is_single_step) {
|
||||
if (!e.conf.HasOptimization(OptimizationFlag::ReturnStackBuffer) || is_single_step) {
|
||||
e.code.ReturnFromRunCode();
|
||||
} else {
|
||||
e.code.jmp(e.terminal_handler_pop_rsb_hint);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmitTerminalImpl(A32EmitX64& e, IR::Term::FastDispatchHint, IR::LocationDescriptor, bool is_single_step) {
|
||||
void EmitTerminalImpl(A32EmitX64& e, IR::Term::FastDispatchHint, IR::LocationDescriptor, bool is_single_step) {
|
||||
if (!e.conf.HasOptimization(OptimizationFlag::FastDispatch) || is_single_step) {
|
||||
e.code.ReturnFromRunCode();
|
||||
} else {
|
||||
e.code.jmp(e.terminal_handler_fast_dispatch_hint);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmitTerminalImpl(A32EmitX64& e, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
void EmitTerminalImpl(A32EmitX64& e, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
Xbyak::Label pass = e.EmitCond(terminal.if_);
|
||||
e.EmitLeafTerminal(terminal.else_, initial_location, is_single_step);
|
||||
e.EmitTerminal(terminal.else_, initial_location, is_single_step);
|
||||
e.code.L(pass);
|
||||
e.EmitLeafTerminal(terminal.then_, initial_location, is_single_step);
|
||||
return true;
|
||||
e.EmitTerminal(terminal.then_, initial_location, is_single_step);
|
||||
}
|
||||
|
||||
bool EmitTerminalImpl(A32EmitX64& e, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
void EmitTerminalImpl(A32EmitX64& e, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
Xbyak::Label fail;
|
||||
e.code.cmp(e.code.byte[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, check_bit)], u8(0));
|
||||
e.code.jz(fail);
|
||||
e.EmitLeafTerminal(terminal.then_, initial_location, is_single_step);
|
||||
e.EmitTerminal(terminal.then_, initial_location, is_single_step);
|
||||
e.code.L(fail);
|
||||
e.EmitLeafTerminal(terminal.else_, initial_location, is_single_step);
|
||||
return true;
|
||||
e.EmitTerminal(terminal.else_, initial_location, is_single_step);
|
||||
}
|
||||
|
||||
bool EmitTerminalImpl(A32EmitX64& e, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
void EmitTerminalImpl(A32EmitX64& e, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
e.code.cmp(dword[e.code.ABI_JIT_PTR + offsetof(A32JitState, halt_reason)], 0);
|
||||
e.code.jne(e.code.GetForceReturnFromRunCodeAddress());
|
||||
e.EmitLeafTerminal(terminal.else_, initial_location, is_single_step);
|
||||
return true;
|
||||
e.EmitTerminal(terminal.else_, initial_location, is_single_step);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool A32EmitX64::EmitLeafTerminal(IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept {
|
||||
if (auto const x = std::get_if<IR::Term::ReturnToDispatch>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::LinkBlock>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::LinkBlockFast>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::PopRSBHint>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::FastDispatchHint>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
void EmitTerminalImpl(A32EmitX64&, IR::Term::Invalid, IR::LocationDescriptor, bool) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
bool A32EmitX64::EmitTerminal(IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept {
|
||||
if (auto const e = std::get_if<IR::Term::LeafTerminal>(&terminal))
|
||||
return EmitLeafTerminal(*e, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::If>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::CheckBit>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::CheckHalt>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
UNREACHABLE();
|
||||
void A32EmitX64::EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept {
|
||||
boost::apply_visitor([this, initial_location, is_single_step](auto x) {
|
||||
EmitTerminalImpl(*this, x, initial_location, is_single_step);
|
||||
}, terminal);
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitPatchJg(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr) {
|
||||
|
||||
@@ -112,8 +112,7 @@ public:
|
||||
|
||||
// Terminal instruction emitters
|
||||
void EmitSetUpperLocationDescriptor(IR::LocationDescriptor new_location, IR::LocationDescriptor old_location);
|
||||
bool EmitLeafTerminal(IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept override;
|
||||
bool EmitTerminal(IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept override;
|
||||
void EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept override;
|
||||
|
||||
// Patching
|
||||
void Unpatch(const IR::LocationDescriptor& target_desc) override;
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <fmt/ostream.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "dynarmic/ir/terminal.h"
|
||||
#include "dynarmic/mcl/integer_of_size.hpp"
|
||||
#include <boost/container/static_vector.hpp>
|
||||
|
||||
@@ -148,7 +147,7 @@ finish_this_inst:
|
||||
if (conf.enable_cycle_counting)
|
||||
EmitAddCycles(block.CycleCount());
|
||||
code.mov(rbp, code.qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, abi_base_pointer)]);
|
||||
EmitTerminal(block.terminal, ctx.Location().SetSingleStepping(false), ctx.IsSingleStep());
|
||||
EmitTerminal(block.GetTerminal(), ctx.Location().SetSingleStepping(false), ctx.IsSingleStep());
|
||||
code.int3();
|
||||
for (auto& deferred_emit : ctx.deferred_emits)
|
||||
deferred_emit();
|
||||
@@ -618,12 +617,11 @@ std::string A64EmitX64::LocationDescriptorToFriendlyName(const IR::LocationDescr
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool EmitTerminalImpl(A64EmitX64& e, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) {
|
||||
void EmitTerminalImpl(A64EmitX64& e, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) {
|
||||
e.code.ReturnFromRunCode();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmitTerminalImpl(A64EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationDescriptor, bool is_single_step) {
|
||||
void EmitTerminalImpl(A64EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationDescriptor, bool is_single_step) {
|
||||
// Used for patches and linking
|
||||
if (e.conf.HasOptimization(OptimizationFlag::BlockLinking) && !is_single_step) {
|
||||
if (e.conf.enable_cycle_counting) {
|
||||
@@ -651,10 +649,9 @@ bool EmitTerminalImpl(A64EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationD
|
||||
e.code.mov(qword[e.code.ABI_JIT_PTR + offsetof(A64JitState, pc)], rax);
|
||||
e.code.ReturnFromRunCode();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmitTerminalImpl(A64EmitX64& e, IR::Term::LinkBlockFast terminal, IR::LocationDescriptor, bool is_single_step) {
|
||||
void EmitTerminalImpl(A64EmitX64& e, IR::Term::LinkBlockFast terminal, IR::LocationDescriptor, bool is_single_step) {
|
||||
if (e.conf.HasOptimization(OptimizationFlag::BlockLinking) && !is_single_step) {
|
||||
e.patch_information[terminal.next].jmp.push_back(e.code.getCurr());
|
||||
if (auto next_bb = e.GetBasicBlock(terminal.next)) {
|
||||
@@ -667,86 +664,63 @@ bool EmitTerminalImpl(A64EmitX64& e, IR::Term::LinkBlockFast terminal, IR::Locat
|
||||
e.code.mov(qword[e.code.ABI_JIT_PTR + offsetof(A64JitState, pc)], rax);
|
||||
e.code.ReturnFromRunCode();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmitTerminalImpl(A64EmitX64& e, IR::Term::PopRSBHint, IR::LocationDescriptor, bool is_single_step) {
|
||||
void EmitTerminalImpl(A64EmitX64& e, IR::Term::PopRSBHint, IR::LocationDescriptor, bool is_single_step) {
|
||||
if (e.conf.HasOptimization(OptimizationFlag::ReturnStackBuffer) && !is_single_step) {
|
||||
e.code.jmp(e.terminal_handler_pop_rsb_hint);
|
||||
} else {
|
||||
e.code.ReturnFromRunCode();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmitTerminalImpl(A64EmitX64& e, IR::Term::FastDispatchHint, IR::LocationDescriptor, bool is_single_step) {
|
||||
void EmitTerminalImpl(A64EmitX64& e, IR::Term::FastDispatchHint, IR::LocationDescriptor, bool is_single_step) {
|
||||
if (!e.conf.HasOptimization(OptimizationFlag::FastDispatch) || is_single_step) {
|
||||
e.code.ReturnFromRunCode();
|
||||
} else {
|
||||
e.code.jmp(e.terminal_handler_fast_dispatch_hint);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmitTerminalImpl(A64EmitX64& e, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
void EmitTerminalImpl(A64EmitX64& e, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
switch (terminal.if_) {
|
||||
case IR::Cond::AL:
|
||||
case IR::Cond::NV:
|
||||
e.EmitLeafTerminal(terminal.then_, initial_location, is_single_step);
|
||||
e.EmitTerminal(terminal.then_, initial_location, is_single_step);
|
||||
break;
|
||||
default:
|
||||
Xbyak::Label pass = e.EmitCond(terminal.if_);
|
||||
e.EmitLeafTerminal(terminal.else_, initial_location, is_single_step);
|
||||
e.EmitTerminal(terminal.else_, initial_location, is_single_step);
|
||||
e.code.L(pass);
|
||||
e.EmitLeafTerminal(terminal.then_, initial_location, is_single_step);
|
||||
e.EmitTerminal(terminal.then_, initial_location, is_single_step);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmitTerminalImpl(A64EmitX64& e, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
void EmitTerminalImpl(A64EmitX64& e, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
Xbyak::Label fail;
|
||||
e.code.cmp(e.code.byte[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, check_bit)], u8(0));
|
||||
e.code.jz(fail);
|
||||
e.EmitLeafTerminal(terminal.then_, initial_location, is_single_step);
|
||||
e.EmitTerminal(terminal.then_, initial_location, is_single_step);
|
||||
e.code.L(fail);
|
||||
e.EmitLeafTerminal(terminal.else_, initial_location, is_single_step);
|
||||
return true;
|
||||
e.EmitTerminal(terminal.else_, initial_location, is_single_step);
|
||||
}
|
||||
|
||||
bool EmitTerminalImpl(A64EmitX64& e, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
void EmitTerminalImpl(A64EmitX64& e, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
e.code.cmp(dword[e.code.ABI_JIT_PTR + offsetof(A64JitState, halt_reason)], 0);
|
||||
e.code.jne(e.code.GetForceReturnFromRunCodeAddress());
|
||||
e.EmitLeafTerminal(terminal.else_, initial_location, is_single_step);
|
||||
return true;
|
||||
e.EmitTerminal(terminal.else_, initial_location, is_single_step);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool A64EmitX64::EmitLeafTerminal(IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept {
|
||||
if (auto const x = std::get_if<IR::Term::ReturnToDispatch>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::LinkBlock>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::LinkBlockFast>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::PopRSBHint>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::FastDispatchHint>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
void EmitTerminalImpl(A64EmitX64&, IR::Term::Invalid, IR::LocationDescriptor, bool) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
bool A64EmitX64::EmitTerminal(IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept {
|
||||
if (auto const x = std::get_if<IR::Term::LeafTerminal>(&terminal))
|
||||
return EmitLeafTerminal(*x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::If>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::CheckBit>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::CheckHalt>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
UNREACHABLE();
|
||||
void A64EmitX64::EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept {
|
||||
boost::apply_visitor([this, initial_location, is_single_step](auto x) {
|
||||
EmitTerminalImpl(*this, x, initial_location, is_single_step);
|
||||
}, terminal);
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitPatchJg(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr) {
|
||||
|
||||
@@ -107,8 +107,7 @@ public:
|
||||
void EmitExclusiveWriteMemoryInline(A64EmitContext& ctx, IR::Inst* inst);
|
||||
|
||||
// Terminal instruction emitters
|
||||
bool EmitLeafTerminal(IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept override;
|
||||
bool EmitTerminal(IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept override;
|
||||
void EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept override;
|
||||
|
||||
// Patching
|
||||
void Unpatch(const IR::LocationDescriptor& target_desc) override;
|
||||
|
||||
@@ -111,8 +111,7 @@ public:
|
||||
#ifndef NDEBUG
|
||||
void EmitVerboseDebuggingOutput(RegAlloc& reg_alloc);
|
||||
#endif
|
||||
virtual bool EmitLeafTerminal(IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept = 0;
|
||||
virtual bool EmitTerminal(IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept = 0;
|
||||
virtual void EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept = 0;
|
||||
|
||||
// Patching
|
||||
struct PatchInformation {
|
||||
|
||||
@@ -66,44 +66,43 @@ void Block::Reset(LocationDescriptor location_) noexcept {
|
||||
location = location_;
|
||||
end_location = location_;
|
||||
cond = Cond::AL;
|
||||
terminal = std::monostate{};
|
||||
terminal = Term::Invalid{};
|
||||
cond_failed_cycle_count = 0;
|
||||
cycle_count = 0;
|
||||
ASSERT(instructions.size() == 0);
|
||||
}
|
||||
|
||||
static std::string TerminalToString(const Term::Terminal& terminal_variant) noexcept {
|
||||
// struct : boost::static_visitor<std::string> {
|
||||
// std::string operator()(const std::monostate&) const {
|
||||
// return "<invalid>";
|
||||
// }
|
||||
// std::string operator()(const Term::ReturnToDispatch&) const {
|
||||
// return "ReturnToDispatch{}";
|
||||
// }
|
||||
// std::string operator()(const Term::LinkBlock& terminal) const {
|
||||
// return fmt::format("LinkBlock{{{}}}", terminal.next);
|
||||
// }
|
||||
// std::string operator()(const Term::LinkBlockFast& terminal) const {
|
||||
// return fmt::format("LinkBlockFast{{{}}}", terminal.next);
|
||||
// }
|
||||
// std::string operator()(const Term::PopRSBHint&) const {
|
||||
// return "PopRSBHint{}";
|
||||
// }
|
||||
// std::string operator()(const Term::FastDispatchHint&) const {
|
||||
// return "FastDispatchHint{}";
|
||||
// }
|
||||
// std::string operator()(const Term::If& terminal) const {
|
||||
// return fmt::format("If{{{}, {}, {}}}", A64::CondToString(terminal.if_), TerminalToString(terminal.then_), TerminalToString(terminal.else_));
|
||||
// }
|
||||
// std::string operator()(const Term::CheckBit& terminal) const {
|
||||
// return fmt::format("CheckBit{{{}, {}}}", TerminalToString(terminal.then_), TerminalToString(terminal.else_));
|
||||
// }
|
||||
// std::string operator()(const Term::CheckHalt& terminal) const {
|
||||
// return fmt::format("CheckHalt{{{}}}", TerminalToString(terminal.else_));
|
||||
// }
|
||||
// } visitor;
|
||||
// return boost::apply_visitor(visitor, terminal_variant);
|
||||
return "";
|
||||
static std::string TerminalToString(const Terminal& terminal_variant) noexcept {
|
||||
struct : boost::static_visitor<std::string> {
|
||||
std::string operator()(const Term::Invalid&) const {
|
||||
return "<invalid terminal>";
|
||||
}
|
||||
std::string operator()(const Term::ReturnToDispatch&) const {
|
||||
return "ReturnToDispatch{}";
|
||||
}
|
||||
std::string operator()(const Term::LinkBlock& terminal) const {
|
||||
return fmt::format("LinkBlock{{{}}}", terminal.next);
|
||||
}
|
||||
std::string operator()(const Term::LinkBlockFast& terminal) const {
|
||||
return fmt::format("LinkBlockFast{{{}}}", terminal.next);
|
||||
}
|
||||
std::string operator()(const Term::PopRSBHint&) const {
|
||||
return "PopRSBHint{}";
|
||||
}
|
||||
std::string operator()(const Term::FastDispatchHint&) const {
|
||||
return "FastDispatchHint{}";
|
||||
}
|
||||
std::string operator()(const Term::If& terminal) const {
|
||||
return fmt::format("If{{{}, {}, {}}}", A64::CondToString(terminal.if_), TerminalToString(terminal.then_), TerminalToString(terminal.else_));
|
||||
}
|
||||
std::string operator()(const Term::CheckBit& terminal) const {
|
||||
return fmt::format("CheckBit{{{}, {}}}", TerminalToString(terminal.then_), TerminalToString(terminal.else_));
|
||||
}
|
||||
std::string operator()(const Term::CheckHalt& terminal) const {
|
||||
return fmt::format("CheckHalt{{{}}}", TerminalToString(terminal.else_));
|
||||
}
|
||||
} visitor;
|
||||
return boost::apply_visitor(visitor, terminal_variant);
|
||||
}
|
||||
|
||||
std::string DumpBlock(const IR::Block& block) noexcept {
|
||||
|
||||
@@ -114,22 +114,22 @@ public:
|
||||
}
|
||||
|
||||
/// Gets the terminal instruction for this basic block.
|
||||
inline Term::Terminal GetTerminal() const noexcept {
|
||||
inline Terminal GetTerminal() const noexcept {
|
||||
return terminal;
|
||||
}
|
||||
/// Sets the terminal instruction for this basic block.
|
||||
inline void SetTerminal(Term::Terminal term) noexcept {
|
||||
inline void SetTerminal(Terminal term) noexcept {
|
||||
ASSERT(!HasTerminal() && "Terminal has already been set.");
|
||||
terminal = std::move(term);
|
||||
}
|
||||
/// Replaces the terminal instruction for this basic block.
|
||||
inline void ReplaceTerminal(Term::Terminal term) noexcept {
|
||||
inline void ReplaceTerminal(Terminal term) noexcept {
|
||||
ASSERT(HasTerminal() && "Terminal has not been set.");
|
||||
terminal = std::move(term);
|
||||
}
|
||||
/// Determines whether or not this basic block has a terminal instruction.
|
||||
inline bool HasTerminal() const noexcept {
|
||||
return !std::holds_alternative<std::monostate>(terminal);
|
||||
return terminal.which() != 0;
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the cycle count for this basic block.
|
||||
@@ -156,7 +156,7 @@ public:
|
||||
/// Conditional to pass in order to execute this block
|
||||
Cond cond = Cond::AL;
|
||||
/// Terminal instruction of this block.
|
||||
Term::Terminal terminal = std::monostate{};
|
||||
Terminal terminal = Term::Invalid{};
|
||||
/// Number of cycles this block takes to execute if the conditional fails.
|
||||
size_t cond_failed_cycle_count = 0;
|
||||
/// Number of cycles this block takes to execute.
|
||||
|
||||
@@ -2943,7 +2943,7 @@ public:
|
||||
Inst(Opcode::CallHostFunction, Imm64(std::bit_cast<u64>(fn)), arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
void SetTerm(const Term::Terminal& terminal) {
|
||||
void SetTerm(const Terminal& terminal) {
|
||||
block.SetTerminal(terminal);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <variant>
|
||||
#include <boost/variant.hpp>
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "dynarmic/ir/cond.h"
|
||||
@@ -17,89 +17,106 @@
|
||||
namespace Dynarmic::IR {
|
||||
namespace Term {
|
||||
|
||||
/// This terminal instruction returns control to the dispatcher.
|
||||
/// The dispatcher will use the current cpu state to determine what comes next.
|
||||
struct Invalid {};
|
||||
|
||||
/**
|
||||
* This terminal instruction returns control to the dispatcher.
|
||||
* The dispatcher will use the current cpu state to determine what comes next.
|
||||
*/
|
||||
struct ReturnToDispatch {};
|
||||
|
||||
/// This terminal instruction jumps to the basic block described by `next` if we have enough
|
||||
/// cycles remaining. If we do not have enough cycles remaining, we return to the
|
||||
/// dispatcher, which will return control to the host.
|
||||
/**
|
||||
* This terminal instruction jumps to the basic block described by `next` if we have enough
|
||||
* cycles remaining. If we do not have enough cycles remaining, we return to the
|
||||
* dispatcher, which will return control to the host.
|
||||
*/
|
||||
struct LinkBlock {
|
||||
explicit LinkBlock(const LocationDescriptor& next_) : next(next_) {}
|
||||
explicit LinkBlock(const LocationDescriptor& next_)
|
||||
: next(next_) {}
|
||||
LocationDescriptor next; ///< Location descriptor for next block.
|
||||
};
|
||||
|
||||
/// This terminal instruction jumps to the basic block described by `next` unconditionally.
|
||||
/// This is an optimization and MUST only be emitted when this is guaranteed not to result
|
||||
/// in hanging, even in the face of other optimizations. (In practice, this means that only
|
||||
/// forward jumps to short-ish blocks would use this instruction.)
|
||||
/// A backend that doesn't support this optimization may choose to implement this exactly
|
||||
/// as LinkBlock.
|
||||
/**
|
||||
* This terminal instruction jumps to the basic block described by `next` unconditionally.
|
||||
* This is an optimization and MUST only be emitted when this is guaranteed not to result
|
||||
* in hanging, even in the face of other optimizations. (In practice, this means that only
|
||||
* forward jumps to short-ish blocks would use this instruction.)
|
||||
* A backend that doesn't support this optimization may choose to implement this exactly
|
||||
* as LinkBlock.
|
||||
*/
|
||||
struct LinkBlockFast {
|
||||
explicit LinkBlockFast(const LocationDescriptor& next_) : next(next_) {}
|
||||
explicit LinkBlockFast(const LocationDescriptor& next_)
|
||||
: next(next_) {}
|
||||
LocationDescriptor next; ///< Location descriptor for next block.
|
||||
};
|
||||
|
||||
/// This terminal instruction checks the top of the Return Stack Buffer against the current
|
||||
/// location descriptor. If RSB lookup fails, control is returned to the dispatcher.
|
||||
/// This is an optimization for faster function calls. A backend that doesn't support
|
||||
/// this optimization or doesn't have a RSB may choose to implement this exactly as
|
||||
/// ReturnToDispatch.
|
||||
/**
|
||||
* This terminal instruction checks the top of the Return Stack Buffer against the current
|
||||
* location descriptor. If RSB lookup fails, control is returned to the dispatcher.
|
||||
* This is an optimization for faster function calls. A backend that doesn't support
|
||||
* this optimization or doesn't have a RSB may choose to implement this exactly as
|
||||
* ReturnToDispatch.
|
||||
*/
|
||||
struct PopRSBHint {};
|
||||
|
||||
/// This terminal instruction performs a lookup of the current location descriptor in the
|
||||
/// fast dispatch lookup table. A backend that doesn't support this optimization may choose
|
||||
/// to implement this exactly as ReturnToDispatch.
|
||||
/**
|
||||
* This terminal instruction performs a lookup of the current location descriptor in the
|
||||
* fast dispatch lookup table. A backend that doesn't support this optimization may choose
|
||||
* to implement this exactly as ReturnToDispatch.
|
||||
*/
|
||||
struct FastDispatchHint {};
|
||||
|
||||
struct If;
|
||||
struct CheckBit;
|
||||
struct CheckHalt;
|
||||
|
||||
/// Non recursive kind of terminal
|
||||
using LeafTerminal = std::variant<
|
||||
std::monostate,
|
||||
/// A Terminal is the terminal instruction in a MicroBlock.
|
||||
using Terminal = boost::variant<
|
||||
Invalid,
|
||||
ReturnToDispatch,
|
||||
LinkBlock,
|
||||
LinkBlockFast,
|
||||
PopRSBHint,
|
||||
FastDispatchHint
|
||||
>;
|
||||
FastDispatchHint,
|
||||
boost::recursive_wrapper<If>,
|
||||
boost::recursive_wrapper<CheckBit>,
|
||||
boost::recursive_wrapper<CheckHalt>>;
|
||||
|
||||
/// A Terminal is the terminal instruction in a MicroBlock.
|
||||
using Terminal = std::variant<
|
||||
std::monostate,
|
||||
LeafTerminal,
|
||||
If,
|
||||
CheckBit,
|
||||
CheckHalt
|
||||
>;
|
||||
|
||||
/// This terminal instruction conditionally executes one terminal or another depending
|
||||
/// on the run-time state of the ARM flags.
|
||||
/**
|
||||
* This terminal instruction conditionally executes one terminal or another depending
|
||||
* on the run-time state of the ARM flags.
|
||||
*/
|
||||
struct If {
|
||||
explicit If(Cond if_, LeafTerminal then_, LeafTerminal else_) : if_(if_), then_(std::move(then_)), else_(std::move(else_)) {}
|
||||
If(Cond if_, Terminal then_, Terminal else_)
|
||||
: if_(if_), then_(std::move(then_)), else_(std::move(else_)) {}
|
||||
Cond if_;
|
||||
LeafTerminal then_;
|
||||
LeafTerminal else_;
|
||||
Terminal then_;
|
||||
Terminal else_;
|
||||
};
|
||||
|
||||
/// This terminal instruction conditionally executes one terminal or another depending
|
||||
/// on the run-time state of the check bit.
|
||||
/// then_ is executed if the check bit is non-zero, otherwise else_ is executed.
|
||||
/**
|
||||
* This terminal instruction conditionally executes one terminal or another depending
|
||||
* on the run-time state of the check bit.
|
||||
* then_ is executed if the check bit is non-zero, otherwise else_ is executed.
|
||||
*/
|
||||
struct CheckBit {
|
||||
explicit CheckBit(LeafTerminal then_, LeafTerminal else_) : then_(std::move(then_)), else_(std::move(else_)) {}
|
||||
LeafTerminal then_;
|
||||
LeafTerminal else_;
|
||||
CheckBit(Terminal then_, Terminal else_)
|
||||
: then_(std::move(then_)), else_(std::move(else_)) {}
|
||||
Terminal then_;
|
||||
Terminal else_;
|
||||
};
|
||||
|
||||
/// This terminal instruction checks if a halt was requested. If it wasn't, else_ is
|
||||
/// executed.
|
||||
/**
|
||||
* This terminal instruction checks if a halt was requested. If it wasn't, else_ is
|
||||
* executed.
|
||||
*/
|
||||
struct CheckHalt {
|
||||
explicit CheckHalt(LeafTerminal else_) : else_(std::move(else_)) {}
|
||||
LeafTerminal else_;
|
||||
explicit CheckHalt(Terminal else_)
|
||||
: else_(std::move(else_)) {}
|
||||
Terminal else_;
|
||||
};
|
||||
|
||||
} // namespace Term
|
||||
|
||||
using Term::Terminal;
|
||||
|
||||
} // namespace Dynarmic::IR
|
||||
|
||||
@@ -43,20 +43,32 @@ namespace {
|
||||
using namespace Dynarmic;
|
||||
|
||||
template<typename Fn>
|
||||
bool AnyLocationDescriptorForTerminalHas(IR::Term::Terminal terminal, Fn fn) {
|
||||
if (auto const e = std::get_if<IR::Term::LeafTerminal>(&terminal)) {
|
||||
if (auto const x = std::get_if<IR::Term::LinkBlock>(e))
|
||||
return fn(x->next);
|
||||
if (auto const x = std::get_if<IR::Term::LinkBlockFast>(e))
|
||||
return fn(x->next);
|
||||
}
|
||||
if (auto const x = std::get_if<IR::Term::If>(&terminal))
|
||||
return AnyLocationDescriptorForTerminalHas(x->then_, fn) || AnyLocationDescriptorForTerminalHas(x->else_, fn);
|
||||
if (auto const x = std::get_if<IR::Term::CheckBit>(&terminal))
|
||||
return AnyLocationDescriptorForTerminalHas(x->then_, fn) || AnyLocationDescriptorForTerminalHas(x->else_, fn);
|
||||
if (auto const x = std::get_if<IR::Term::CheckHalt>(&terminal))
|
||||
return AnyLocationDescriptorForTerminalHas(x->else_, fn);
|
||||
return false;
|
||||
bool AnyLocationDescriptorForTerminalHas(IR::Terminal terminal, Fn fn) {
|
||||
return boost::apply_visitor([&](auto t) -> bool {
|
||||
using T = std::decay_t<decltype(t)>;
|
||||
if constexpr (std::is_same_v<T, IR::Term::Invalid>) {
|
||||
return false;
|
||||
} else if constexpr (std::is_same_v<T, IR::Term::ReturnToDispatch>) {
|
||||
return false;
|
||||
} else if constexpr (std::is_same_v<T, IR::Term::LinkBlock>) {
|
||||
return fn(t.next);
|
||||
} else if constexpr (std::is_same_v<T, IR::Term::LinkBlockFast>) {
|
||||
return fn(t.next);
|
||||
} else if constexpr (std::is_same_v<T, IR::Term::PopRSBHint>) {
|
||||
return false;
|
||||
} else if constexpr (std::is_same_v<T, IR::Term::FastDispatchHint>) {
|
||||
return false;
|
||||
} else if constexpr (std::is_same_v<T, IR::Term::If>) {
|
||||
return AnyLocationDescriptorForTerminalHas(t.then_, fn) || AnyLocationDescriptorForTerminalHas(t.else_, fn);
|
||||
} else if constexpr (std::is_same_v<T, IR::Term::CheckBit>) {
|
||||
return AnyLocationDescriptorForTerminalHas(t.then_, fn) || AnyLocationDescriptorForTerminalHas(t.else_, fn);
|
||||
} else if constexpr (std::is_same_v<T, IR::Term::CheckHalt>) {
|
||||
return AnyLocationDescriptorForTerminalHas(t.else_, fn);
|
||||
} else {
|
||||
ASSERT(false && "Invalid terminal type");
|
||||
return false;
|
||||
}
|
||||
}, terminal);
|
||||
}
|
||||
|
||||
bool ShouldTestInst(u32 instruction, u32 pc, bool is_thumb, bool is_last_inst, A32::ITState it_state = {}) {
|
||||
|
||||
@@ -16,6 +16,26 @@
|
||||
#include "video_core/memory_manager.h"
|
||||
|
||||
namespace Tegra::Control {
|
||||
namespace {
|
||||
|
||||
// Match NVK/Nouveau's initial pushbuffer subchannel layout.
|
||||
constexpr u32 Nvk3DSubchannel = 0;
|
||||
constexpr u32 NvkComputeSubchannel = 1;
|
||||
constexpr u32 Nvk2DSubchannel = 3;
|
||||
constexpr u32 NvkCopySubchannel = 4;
|
||||
|
||||
void BindNvkDefaultSubchannels(ChannelState::Payload& payload) {
|
||||
auto& dma_pusher = payload.dma_pusher;
|
||||
dma_pusher.BindSubchannel(&payload.maxwell_3d, Nvk3DSubchannel, Engines::EngineTypes::Maxwell3D);
|
||||
dma_pusher.BindSubchannel(&payload.kepler_compute, NvkComputeSubchannel,
|
||||
Engines::EngineTypes::KeplerCompute);
|
||||
// Subchannel 2 is M2MF there; Eden does not expose a 0x9039 engine yet.
|
||||
dma_pusher.BindSubchannel(&payload.fermi_2d, Nvk2DSubchannel, Engines::EngineTypes::Fermi2D);
|
||||
dma_pusher.BindSubchannel(&payload.maxwell_dma, NvkCopySubchannel,
|
||||
Engines::EngineTypes::MaxwellDMA);
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
ChannelState::Payload::Payload(Core::System& system, MemoryManager& memory_manager, ChannelState& channel_state)
|
||||
: maxwell_3d(memory_manager)
|
||||
@@ -35,6 +55,7 @@ void ChannelState::Init(Core::System& system, u64 program_id_) {
|
||||
ASSERT(memory_manager);
|
||||
program_id = program_id_;
|
||||
payload.emplace(system, *memory_manager, *this);
|
||||
BindNvkDefaultSubchannels(*payload);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,10 +22,21 @@
|
||||
|
||||
namespace Tegra::Engines {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr u32 Gf100BindClassMask = 0xffff;
|
||||
constexpr u32 Gf100BindValidMask = 0x1f0000 | Gf100BindClassMask;
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
void Puller::ProcessBindMethod(DmaPusher& dma_pusher, const MethodCall& method_call) {
|
||||
// Bind the current subchannel to the desired engine id.
|
||||
LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", method_call.subchannel, method_call.argument);
|
||||
const auto engine_id = static_cast<EngineID>(method_call.argument);
|
||||
LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {:#x}", method_call.subchannel, method_call.argument);
|
||||
u32 engine = method_call.argument;
|
||||
if ((engine & ~Gf100BindClassMask) != 0 && (engine & ~Gf100BindValidMask) == 0) {
|
||||
engine &= Gf100BindClassMask;
|
||||
}
|
||||
|
||||
const auto engine_id = static_cast<EngineID>(engine);
|
||||
bound_engines[method_call.subchannel] = engine_id;
|
||||
switch (engine_id) {
|
||||
case EngineID::FERMI_TWOD_A:
|
||||
|
||||
Reference in New Issue
Block a user