Compare commits

..

3 Commits

Author SHA1 Message Date
lizzie c9d5ddab57 2026-09-15 19:47:11
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-15 19:47:11 +00:00
lizzie 1eb44e3ea6 2026-09-15 19:39:00
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-15 19:39:00 +00:00
lizzie 6c96d9f43d 2026-09-15 07:59:31
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-15 07:59:31 +00:00
72 changed files with 1012 additions and 1298 deletions
+1 -2
View File
@@ -110,12 +110,11 @@ add_library(
socket_types.h
sparse_large_vector.cpp
sparse_large_vector.h
spin_lock.h
stb.cpp
stb.h
steady_clock.cpp
steady_clock.h
stream.cpp
stream.h
string_util.cpp
string_util.h
swap.h
+3 -4
View File
@@ -244,8 +244,7 @@ WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept
, ns_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency_) : 0}
, us_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency_) : 0}
, ms_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency_) : 0}
, rdtsc_ns_integer{invariant_ ? rdtsc_frequency_ / NsRatio::den : 1}
, rdtsc_ns_factor{invariant_ ? GetFixedPoint64Factor(rdtsc_frequency_ % NsRatio::den, NsRatio::den) : 0}
, rdtsc_ns_factor{invariant_ ? GetFixedPoint64Factor(rdtsc_frequency_, NsRatio::den) : 1}
, cntpct_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency_) : 0}
, gputick_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency_) : 0}
, invariant{invariant_}
@@ -292,7 +291,7 @@ bool WallClock::IsNative() const {
}
u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const {
return ns.count() * rdtsc_ns_integer + MultiplyHigh(ns.count(), rdtsc_ns_factor);
return invariant ? MultiplyHigh(ns.count(), rdtsc_ns_factor) : ns.count();
}
#elif defined(HAS_NCE)
namespace {
@@ -417,7 +416,7 @@ u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const {
const WallClock g_wall_clock = [] {
#if defined(ARCHITECTURE_x86_64)
auto const& caps = Common::g_cpu_caps;
return WallClock(caps.invariant_tsc && caps.tsc_frequency > std::nano::den, caps.tsc_frequency);
return WallClock(caps.invariant_tsc && caps.tsc_frequency >= std::nano::den, caps.tsc_frequency);
#elif defined(HAS_NCE)
return WallClock(false, 1);
#else
-1
View File
@@ -96,7 +96,6 @@ public:
u64 ns_rdtsc_factor;
u64 us_rdtsc_factor;
u64 ms_rdtsc_factor;
u64 rdtsc_ns_integer;
u64 rdtsc_ns_factor;
u64 cntpct_rdtsc_factor;
u64 gputick_rdtsc_factor;
+20 -18
View File
@@ -17,33 +17,35 @@
namespace Common {
// glibc, mlibc, musl, and newlib all define their own variants of strerror_r
// We don't need to use the preprocessor, we can just select depending on return type
template<typename T> std::string HandleStrerrorR(T r, char *err_str);
template<> std::string HandleStrerrorR(char* r, char *) { return std::string{r}; }
template<> std::string HandleStrerrorR(const char* r, char *) { return std::string{r}; }
template<> std::string HandleStrerrorR(int r, char *err_str) {
return std::string{r != 0
? "(strerror_r failed to format error)"
: err_str};
}
std::string NativeErrorToString(int e) {
#ifdef _WIN32
LPSTR err_str;
DWORD res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
LPSTR(&err_str), 1, nullptr);
if (res) {
std::string ret(err_str);
LocalFree(err_str);
return ret;
reinterpret_cast<LPSTR>(&err_str), 1, nullptr);
if (!res) {
return "(FormatMessageA failed to format error)";
}
return "(FormatMessageA failed to format error)";
std::string ret(err_str);
LocalFree(err_str);
return ret;
#else
char err_str[255];
return HandleStrerrorR(strerror_r(e, err_str, sizeof(err_str)), err_str);
#if defined(__ANDROID__) || \
(defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600)))
// Thread safe (GNU-specific)
const char* str = strerror_r(e, err_str, sizeof(err_str));
return std::string(str);
#else
// Thread safe (XSI-compliant)
int second_err = strerror_r(e, err_str, sizeof(err_str));
if (second_err != 0) {
return "(strerror_r failed to format error)";
}
return std::string(err_str);
#endif // GLIBC etc.
#endif // _WIN32
}
+12 -65
View File
@@ -5,7 +5,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cctype>
#include <iostream>
#include <sstream>
#include "common/container/unordered_map.h"
@@ -481,80 +480,28 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se
[type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),
path.end());
std::string root;
std::string_view components{path};
bool drive_relative = false;
#ifdef _WIN32
const bool network = path.size() > 1 && path[0] == type2 && path[1] == type2;
const bool drive =
path.size() > 1 && std::isalpha(static_cast<unsigned char>(path[0])) && path[1] == ':';
if (network) {
root.assign(2, type2);
components.remove_prefix(2);
} else if (drive) {
root.assign(path.data(), 2);
components.remove_prefix(2);
if (!components.empty() && components.front() == type2) {
root += type2;
components.remove_prefix(1);
} else {
drive_relative = true;
}
}
#endif
if (root.empty() && !components.empty() && components.front() == type2) {
root += type2;
components.remove_prefix(1);
}
const auto path_parts = SplitPathComponents(components);
std::size_t root_component_count = 0;
#ifdef _WIN32
if (network) {
root_component_count = 2;
const auto is_unc = [](std::string_view part) {
return part.size() == 3 && (part[0] == 'U' || part[0] == 'u') &&
(part[1] == 'N' || part[1] == 'n') && (part[2] == 'C' || part[2] == 'c');
};
if (path_parts.size() >= 2 && path_parts[0] == "?" && is_unc(path_parts[1])) {
root_component_count = 4;
}
}
#endif
const bool absolute = !path.empty() && path[0] == type2;
std::vector<std::string_view> parts;
for (std::size_t i = 0; i < path_parts.size(); ++i) {
const auto part = path_parts[i];
if (i < root_component_count) {
parts.push_back(part);
} else if (part.empty() || part == ".") {
for (const auto part : SplitPathComponents(path))
{
if (part.empty() || part == ".")
continue;
} else if (part == "..") {
if (parts.size() > root_component_count) {
parts.pop_back();
}
} else {
parts.push_back(part);
}
if (part == ".." && !parts.empty() && parts.back() != "..")
parts.pop_back();
else if (part != "..") parts.push_back(part);
}
const std::size_t root_length = root.size();
std::string resolved = std::move(root);
for (std::size_t i = 0; i < parts.size(); ++i) {
if (i != 0 || (!resolved.empty() && resolved.back() != type2 && !drive_relative))
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);
if (!path.empty() && path.size() == root_length) {
return path;
}
return std::string(RemoveTrailingSlash(path));
}
+2 -3
View File
@@ -347,9 +347,8 @@ enum class DirectorySeparator {
// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
[[nodiscard]] std::vector<std::string> SplitPathComponentsCopy(std::string_view filename);
// Normalizes directory separators, removes duplicate and non-root trailing separators, and resolves
// '.' and '..' components without traversing above the path root. Windows drive and UNC roots are
// preserved.
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows
[[nodiscard]] std::string SanitizePath(
std::string_view path,
DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
+3 -3
View File
@@ -96,15 +96,15 @@ struct PageTable {
}
/// Write page info atomically
inline void Store(bool marked, PageType type, u16 block, uintptr_t pointer) noexcept {
constexpr void Store(bool marked, PageType type, u16 block, uintptr_t pointer) noexcept {
data_raw.store(std::bit_cast<u64>(Data{marked, type, block, pointer}));
}
inline void MarkRasterizerCached() noexcept {
constexpr void MarkRasterizerCached() noexcept {
data_raw.fetch_or(0b111);
}
inline void MarkDebug(u64 ptr, u16 block) noexcept {
constexpr void MarkDebug(u64 ptr, u16 block) noexcept {
Store(true, PageType::DebugMemory, block, ptr);
}
+1 -2
View File
@@ -38,8 +38,7 @@ void FreeMemoryPages(void* base, std::size_t size) noexcept;
/// A large page-aligned buffer that has optimized memory usage for zero-writes.
template <typename T>
// MSVC doesn't regard structs with atomics as trivially copyable
// requires std::is_trivially_copyable_v<T>
requires std::is_trivially_copyable_v<T>
class SparseLargeVector final {
public:
constexpr SparseLargeVector() = default;
+50
View File
@@ -0,0 +1,50 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#ifdef _MSC_VER
#include <intrin.h>
#elif defined(ARCHITECTURE_x86_64)
#include <xmmintrin.h>
#endif
#include <atomic>
namespace Common {
/// @brief A lock similar to mutex that forces a thread to spin wait instead calling the
/// supervisor. Should be used on short sequences of code.
struct SpinLock {
SpinLock() noexcept = default;
SpinLock(const SpinLock&) noexcept = delete;
SpinLock& operator=(const SpinLock&) noexcept = delete;
SpinLock(SpinLock&&) noexcept = delete;
SpinLock& operator=(SpinLock&&) noexcept = delete;
inline void lock() noexcept {
while (lck.test_and_set(std::memory_order_acquire)) {
#if defined(ARCHITECTURE_x86_64)
_mm_pause();
#elif defined(ARCHITECTURE_arm64) && defined(_MSC_VER)
__yield();
#elif defined(ARCHITECTURE_arm64)
asm("yield");
#endif
}
}
inline void unlock() noexcept {
lck.clear(std::memory_order_release);
}
[[nodiscard]] inline bool try_lock() noexcept {
return !lck.test_and_set(std::memory_order_acquire);
}
std::atomic_flag lck = ATOMIC_FLAG_INIT;
};
} // namespace Common
-46
View File
@@ -1,46 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <stdexcept>
#include "common/common_types.h"
#include "common/stream.h"
namespace Common {
Stream::Stream() = default;
Stream::~Stream() = default;
void Stream::Seek(s32 offset, SeekOrigin origin) {
if (origin == SeekOrigin::SetOrigin) {
if (offset < 0) {
position = 0;
} else if (position >= buffer.size()) {
position = buffer.size();
} else {
position = offset;
}
} else if (origin == SeekOrigin::FromCurrentPos) {
Seek(static_cast<s32>(position) + offset, SeekOrigin::SetOrigin);
} else if (origin == SeekOrigin::FromEnd) {
Seek(static_cast<s32>(buffer.size()) - offset, SeekOrigin::SetOrigin);
}
}
u8 Stream::ReadByte() {
if (position < buffer.size()) {
return buffer[position++];
} else {
throw std::out_of_range("Attempting to read a byte not within the buffer range");
}
}
void Stream::WriteByte(u8 byte) {
if (position == buffer.size()) {
buffer.push_back(byte);
position++;
} else {
buffer.insert(buffer.begin() + position, byte);
}
}
} // namespace Common
-55
View File
@@ -1,55 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <vector>
#include "common/common_types.h"
namespace Common {
enum class SeekOrigin {
SetOrigin,
FromCurrentPos,
FromEnd,
};
class Stream {
public:
/// Stream creates a bitstream and provides common functionality on the stream.
explicit Stream();
~Stream();
Stream(const Stream&) = delete;
Stream& operator=(const Stream&) = delete;
Stream(Stream&&) = default;
Stream& operator=(Stream&&) = default;
/// Reposition bitstream "cursor" to the specified offset from origin
void Seek(s32 offset, SeekOrigin origin);
/// Reads next byte in the stream buffer and increments position
u8 ReadByte();
/// Writes byte at current position
void WriteByte(u8 byte);
[[nodiscard]] std::size_t GetPosition() const {
return position;
}
[[nodiscard]] std::vector<u8>& GetBuffer() {
return buffer;
}
[[nodiscard]] const std::vector<u8>& GetBuffer() const {
return buffer;
}
private:
std::vector<u8> buffer;
std::size_t position{0};
};
} // namespace Common
+2 -2
View File
@@ -208,9 +208,9 @@ UUID UUID::MakeRandomRFC4122V4() {
return uuid;
}
UUID UUID::MakeRFC4122V5(std::span<u8, 16> sha1) {
UUID UUID::MakeRFC4122V5(std::span<u8, 20> sha1) {
UUID uuid{};
std::memcpy(&uuid.uuid, sha1.data(), sha1.size());
std::memcpy(&uuid.uuid, sha1.data(), sizeof(UUID));
uuid.uuid[8] = 0x80 | (uuid.uuid[8] & 0x3F);
uuid.uuid[6] = 0x50 | (uuid.uuid[6] & 0xF);
return uuid;
+1 -1
View File
@@ -104,7 +104,7 @@ struct UUID {
/// @returns A random UUID that is RFC 4122 Version 4 compliant.
[[nodiscard]] static UUID MakeRandomRFC4122V4();
[[nodiscard]] static UUID MakeRFC4122V5(std::span<u8, 16> sha1);
[[nodiscard]] static UUID MakeRFC4122V5(std::span<u8, 20> sha1);
friend constexpr bool operator==(const UUID& lhs, const UUID& rhs) = default;
};
+2 -2
View File
@@ -796,8 +796,6 @@ add_library(core STATIC
hle/service/ns/application_manager_interface.h
hle/service/ns/application_version_interface.cpp
hle/service/ns/application_version_interface.h
hle/service/ns/async_result.cpp
hle/service/ns/async_result.h
hle/service/ns/content_management_interface.cpp
hle/service/ns/content_management_interface.h
hle/service/ns/develop_interface.cpp
@@ -812,6 +810,8 @@ add_library(core STATIC
hle/service/ns/ecommerce_interface.h
hle/service/ns/factory_reset_interface.cpp
hle/service/ns/factory_reset_interface.h
hle/service/ns/i_async_result.cpp
hle/service/ns/i_async_result.h
hle/service/ns/language.cpp
hle/service/ns/language.h
hle/service/ns/ns.cpp
+36 -20
View File
@@ -22,15 +22,21 @@ DynarmicCallbacks32::DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess
, m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()}
{}
u64 DynarmicCallbacks32::MemoryRead(u32 vaddr, size_t size) {
CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Read);
switch (size) {
case sizeof(u64): return m_memory.Read64(vaddr);
case sizeof(u32): return m_memory.Read32(vaddr);
case sizeof(u16): return m_memory.Read16(vaddr);
case sizeof(u8): return m_memory.Read8(vaddr);
default: UNREACHABLE();
}
u8 DynarmicCallbacks32::MemoryRead8(u32 vaddr) {
CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
return m_memory.Read8(vaddr);
}
u16 DynarmicCallbacks32::MemoryRead16(u32 vaddr) {
CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
return m_memory.Read16(vaddr);
}
u32 DynarmicCallbacks32::MemoryRead32(u32 vaddr) {
CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read);
return m_memory.Read32(vaddr);
}
u64 DynarmicCallbacks32::MemoryRead64(u32 vaddr) {
CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
return m_memory.Read64(vaddr);
}
std::optional<u32> DynarmicCallbacks32::MemoryReadCode(u32 vaddr) {
@@ -44,17 +50,27 @@ std::optional<u32> DynarmicCallbacks32::MemoryReadCode(u32 vaddr) {
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
}
void DynarmicCallbacks32::MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) {
if (CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Write)) {
switch (size) {
case sizeof(u64): return m_memory.Write64(vaddr, value);
case sizeof(u32): return m_memory.Write32(vaddr, u32(value));
case sizeof(u16): return m_memory.Write16(vaddr, u16(value));
case sizeof(u8): return m_memory.Write8(vaddr, u8(value));
default: UNREACHABLE();
}
void DynarmicCallbacks32::MemoryWrite8(u32 vaddr, u8 value) {
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
m_memory.Write8(vaddr, value);
}
}
void DynarmicCallbacks32::MemoryWrite16(u32 vaddr, u16 value) {
if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
m_memory.Write16(vaddr, value);
}
}
void DynarmicCallbacks32::MemoryWrite32(u32 vaddr, u32 value) {
if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) {
m_memory.Write32(vaddr, value);
}
}
void DynarmicCallbacks32::MemoryWrite64(u32 vaddr, u64 value) {
if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) {
m_memory.Write64(vaddr, value);
}
}
bool DynarmicCallbacks32::MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) {
return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) &&
m_memory.WriteExclusive8(vaddr, value, expected);
@@ -161,7 +177,7 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NumPageTableEntries>*>(
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
config.page_table_marked_bit = uint8_t(0);
config.page_table_marked_bit = 0;
config.absolute_offset_page_table = true;
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
config.only_detect_misalignment_via_page_table_on_page_boundary = true;
@@ -177,7 +193,7 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) {
// Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries,
// we have to manually sign extend when our actual pointer is negative.
config.page_table_sign_extension = std::uint8_t(Common::PageTable::SIGN_BIT);
config.page_table_sign_extension = Common::PageTable::SIGN_BIT;
}
}
+8 -2
View File
@@ -30,12 +30,18 @@ class System;
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
public:
explicit DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess* process);
u64 MemoryRead(u32 vaddr, size_t size) override;
u8 MemoryRead8(u32 vaddr) override;
u16 MemoryRead16(u32 vaddr) override;
u32 MemoryRead32(u32 vaddr) override;
u64 MemoryRead64(u32 vaddr) override;
std::optional<u32> MemoryReadCode(u32 vaddr) override;
void InstructionSynchronizationBarrierRaised() override {
last_code_addr = u64(-1); //reset back, force refetch
}
void MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) override;
void MemoryWrite8(u32 vaddr, u8 value) override;
void MemoryWrite16(u32 vaddr, u16 value) override;
void MemoryWrite32(u32 vaddr, u32 value) override;
void MemoryWrite64(u32 vaddr, u64 value) override;
bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override;
bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override;
bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override;
+35 -21
View File
@@ -10,7 +10,6 @@
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
#include "core/core_timing.h"
#include "core/hle/kernel/k_process.h"
#include "dynarmic/interface/A64/config.h"
namespace Core {
@@ -22,15 +21,21 @@ DynarmicCallbacks64::DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess
, m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()}
{}
u64 DynarmicCallbacks64::MemoryRead(u64 vaddr, size_t size) {
CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Read);
switch (size) {
case sizeof(u64): return m_memory.Read64(vaddr);
case sizeof(u32): return m_memory.Read32(vaddr);
case sizeof(u16): return m_memory.Read16(vaddr);
case sizeof(u8): return m_memory.Read8(vaddr);
default: UNREACHABLE();
}
u8 DynarmicCallbacks64::MemoryRead8(u64 vaddr) {
CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
return m_memory.Read8(vaddr);
}
u16 DynarmicCallbacks64::MemoryRead16(u64 vaddr) {
CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
return m_memory.Read16(vaddr);
}
u32 DynarmicCallbacks64::MemoryRead32(u64 vaddr) {
CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read);
return m_memory.Read32(vaddr);
}
u64 DynarmicCallbacks64::MemoryRead64(u64 vaddr) {
CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
return m_memory.Read64(vaddr);
}
Dynarmic::A64::Vector DynarmicCallbacks64::MemoryRead128(u64 vaddr) {
CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read);
@@ -48,15 +53,24 @@ std::optional<u32> DynarmicCallbacks64::MemoryReadCode(u64 vaddr) {
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
}
void DynarmicCallbacks64::MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, std::size_t size) {
if (CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Write)) {
switch (size) {
case sizeof(u64): return m_memory.Write64(vaddr, u64(value));
case sizeof(u32): return m_memory.Write32(vaddr, u32(value));
case sizeof(u16): return m_memory.Write16(vaddr, u16(value));
case sizeof(u8): return m_memory.Write8(vaddr, u8(value));
default: UNREACHABLE();
}
void DynarmicCallbacks64::MemoryWrite8(u64 vaddr, u8 value) {
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
m_memory.Write8(vaddr, value);
}
}
void DynarmicCallbacks64::MemoryWrite16(u64 vaddr, u16 value) {
if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
m_memory.Write16(vaddr, value);
}
}
void DynarmicCallbacks64::MemoryWrite32(u64 vaddr, u32 value) {
if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) {
m_memory.Write32(vaddr, value);
}
}
void DynarmicCallbacks64::MemoryWrite64(u64 vaddr, u64 value) {
if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) {
m_memory.Write64(vaddr, value);
}
}
void DynarmicCallbacks64::MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) {
@@ -202,7 +216,7 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
config.page_table_address_space_bits = std::uint32_t(address_space_bits);
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
config.page_table_marked_bit = uint8_t(0);
config.page_table_marked_bit = 0;
config.silently_mirror_page_table = false;
config.absolute_offset_page_table = true;
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
@@ -221,7 +235,7 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) {
// Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries,
// we have to manually sign extend when our actual pointer is negative.
config.page_table_sign_extension = std::uint8_t(Common::PageTable::SIGN_BIT);
config.page_table_sign_extension = Common::PageTable::SIGN_BIT;
}
}
+8 -3
View File
@@ -16,7 +16,6 @@
#include "common/hash.h"
#include "core/arm/arm_interface.h"
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
#include "dynarmic/interface/A64/config.h"
namespace Core::Memory {
class Memory;
@@ -37,13 +36,19 @@ class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
public:
explicit DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess* process);
u64 MemoryRead(u64 vaddr, size_t size) override;
u8 MemoryRead8(u64 vaddr) override;
u16 MemoryRead16(u64 vaddr) override;
u32 MemoryRead32(u64 vaddr) override;
u64 MemoryRead64(u64 vaddr) override;
Dynarmic::A64::Vector MemoryRead128(u64 vaddr) override;
std::optional<u32> MemoryReadCode(u64 vaddr) override;
void InstructionSynchronizationBarrierRaised() override {
last_code_addr = u64(-1); //reset back, force refetch
}
void MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, std::size_t size) override;
void MemoryWrite8(u64 vaddr, u8 value) override;
void MemoryWrite16(u64 vaddr, u16 value) override;
void MemoryWrite32(u64 vaddr, u32 value) override;
void MemoryWrite64(u64 vaddr, u64 value) override;
void MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) override;
bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override;
bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override;
+18 -18
View File
@@ -100,7 +100,8 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
struct IPSwitchRecord {
std::vector<uint8_t> data;
std::array<uint8_t, 256 - sizeof(size_t)> data;
size_t count;
};
struct IPSwitchCompiler::IPSwitchPatch {
::Common::unordered_map<u32, IPSwitchRecord> records;
@@ -121,23 +122,22 @@ static IPSwitchRecord EscapeStringSequences(std::string_view sv) {
IPSwitchRecord r{};
for (auto it = sv.cbegin(); it < sv.cend(); ) {
if (*it == '\\' && it + 1 < sv.cend()) {
r.data.push_back([it]() {
switch (it[1]) {
case 'a': return '\a';
case 'b': return '\b';
case 'e': return '\e';
case 'f': return '\f';
case 'n': return '\n';
case 'r': return '\r';
case 't': return '\t';
case 'v': return '\v';
case '?': return '\?';
default: return it[1];
}
}());
switch (it[1]) {
case 'a': r.data[r.count] = '\a'; break;
case 'b': r.data[r.count] = '\b'; break;
case 'e': r.data[r.count] = '\e'; break;
case 'f': r.data[r.count] = '\f'; break;
case 'n': r.data[r.count] = '\n'; break;
case 'r': r.data[r.count] = '\r'; break;
case 't': r.data[r.count] = '\t'; break;
case 'v': r.data[r.count] = '\v'; break;
case '?': r.data[r.count] = '\?'; break;
default: r.data[r.count] = it[1]; break;
}
++r.count;
it += 2;
} else {
r.data.push_back(*it);
++r.count;
++it;
}
}
@@ -223,8 +223,8 @@ void IPSwitchCompiler::Parse(std::span<u8 const> bytes) {
if (start <= line.cend() && end <= line.cend()) {
// Actually IPS wants ordering from {lsb, ..., msb} -- so LE and BE are inverted, fun!
auto const hs = Common::HexStringToVector({start, end}, is_little_endian);
r.data.resize(hs.size());
std::memcpy(r.data.data(), hs.data(), hs.size());
r.count = hs.size();
LOG_INFO(Loader, "[H] value @ {:#08X}", offset);
patches.back().records.insert_or_assign(u32(offset), std::move(r));
} else {
@@ -293,7 +293,7 @@ VirtualFile IPSwitchCompiler::Apply(const VirtualFile& in) const {
if (patch.enabled) {
for (const auto& record : patch.records) {
if (record.first < in_data.size()) {
auto replace_size = record.second.data.size();
auto replace_size = record.second.count;
if (record.first + replace_size > in_data.size())
replace_size = in_data.size() - record.first;
std::memcpy(in_data.data() + record.first, record.second.data.data(), replace_size);
+18 -50
View File
@@ -9,7 +9,6 @@
#include <cstddef>
#include <cstring>
#include "common/assert.h"
#include "common/hex_util.h"
#include "common/logging.h"
#include "common/settings.h"
@@ -157,15 +156,12 @@ std::string GetUpdateVersionStringFromSlot(const ContentProvider* provider, u64
NACP nacp{nacp_file};
return nacp.GetVersionString();
}
YUZU_NO_INLINE std::optional<u64> GetParentApplicationId(const ContentProvider* provider, u64 title_id) {
return provider == nullptr ? std::nullopt : provider->GetParentApplicationId(title_id);
}
} // Anonymous namespace
PatchManager::PatchManager(u64 title_id_,
const Service::FileSystem::FileSystemController& fs_controller_,
const ContentProvider& content_provider_)
: title_id{title_id_}, parent_title_id{GetParentApplicationId(std::addressof(content_provider_), title_id_)}, fs_controller{fs_controller_}, content_provider{content_provider_} {}
: title_id{title_id_}, fs_controller{fs_controller_}, content_provider{content_provider_} {}
PatchManager::~PatchManager() = default;
@@ -173,35 +169,13 @@ u64 PatchManager::GetTitleID() const {
return title_id;
}
VirtualDir PatchManager::GetModificationLoadRoot(bool sdmc) const {
const auto get_root = [&](u64 id) {
return sdmc ? fs_controller.GetSDMCModificationLoadRoot(id) : fs_controller.GetModificationLoadRoot(id);
};
auto root = get_root(title_id);
if (!parent_title_id) {
return root;
}
std::vector<VirtualDir> roots{std::move(root), get_root(*parent_title_id)};
std::erase(roots, nullptr);
return LayeredVfsDirectory::MakeLayeredDirectory(std::move(roots));
}
std::vector<std::string> PatchManager::GetDisabledAddons() const {
auto disabled = Settings::values.disabled_addons[title_id];
if (parent_title_id) {
const auto& shared = Settings::values.disabled_addons[*parent_title_id];
disabled.insert(disabled.end(), shared.begin(), shared.end());
}
return disabled;
}
VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
if (exefs == nullptr)
return exefs;
const auto disabled = GetDisabledAddons();
const auto& disabled = Settings::values.disabled_addons[title_id];
bool update_disabled = true;
std::optional<u32> enabled_version;
@@ -329,8 +303,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
}
// LayeredExeFS
const auto load_dir = GetModificationLoadRoot();
const auto sdmc_load_dir = GetModificationLoadRoot(true);
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
std::vector<VirtualDir> patch_dirs = {sdmc_load_dir};
if (load_dir != nullptr) {
@@ -372,7 +346,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
}
std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs, const std::string& build_id) const {
const auto disabled = GetDisabledAddons();
const auto& disabled = Settings::values.disabled_addons[title_id];
const auto nso_build_id = fmt::format("{:0<64}", build_id);
std::vector<VirtualFile> out;
@@ -431,7 +405,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
const auto load_dir = GetModificationLoadRoot();
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
if (load_dir == nullptr) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
return nso;
@@ -474,7 +448,7 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_, std::string_view name)
LOG_INFO(Loader, "Querying NSO patch existence for build_id={}, name={}", build_id, name);
const auto load_dir = GetModificationLoadRoot();
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
if (load_dir == nullptr) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
return false;
@@ -488,13 +462,13 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_, std::string_view name)
}
std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildID& build_id_) const {
const auto load_dir = GetModificationLoadRoot();
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
if (load_dir == nullptr) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
return {};
}
const auto disabled = GetDisabledAddons();
const auto& disabled = Settings::values.disabled_addons[title_id];
auto patch_dirs = load_dir->GetSubdirectories();
std::sort(patch_dirs.begin(), patch_dirs.end(), [](auto const& l, auto const& r) { return l->GetName() < r->GetName(); });
@@ -528,16 +502,17 @@ std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildI
return out;
}
void PatchManager::ApplyLayeredFS(VirtualFile& romfs, ContentRecordType type) const {
const auto load_dir = GetModificationLoadRoot();
const auto sdmc_load_dir = GetModificationLoadRoot(true);
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type,
const Service::FileSystem::FileSystemController& fs_controller) {
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
if ((type != ContentRecordType::Program && type != ContentRecordType::Data &&
type != ContentRecordType::HtmlDocument) ||
(load_dir == nullptr && sdmc_load_dir == nullptr)) {
return;
}
const auto disabled = GetDisabledAddons();
const auto& disabled = Settings::values.disabled_addons[title_id];
std::vector<VirtualDir> patch_dirs = load_dir->GetSubdirectories();
if (std::find(disabled.cbegin(), disabled.cend(), "SDMC") == disabled.cend()) {
patch_dirs.push_back(sdmc_load_dir);
@@ -616,7 +591,7 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
// Game Updates
const auto update_tid = GetUpdateTitleID(title_id);
const auto disabled = GetDisabledAddons();
const auto& disabled = Settings::values.disabled_addons[title_id];
bool update_disabled = true;
std::optional<u32> enabled_version;
@@ -723,7 +698,7 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
// LayeredFS
if (apply_layeredfs) {
ApplyLayeredFS(romfs, type);
ApplyLayeredFS(romfs, title_id, type, fs_controller);
}
return romfs;
@@ -1097,22 +1072,15 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
std::optional<u32> PatchManager::GetGameVersion() const {
const auto update_tid = GetUpdateTitleID(title_id);
if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
const auto version = content_provider.GetEntryVersion(update_tid);
return version || !parent_title_id ? version : content_provider.GetEntryVersion(GetUpdateTitleID(*parent_title_id));
return content_provider.GetEntryVersion(update_tid);
}
const auto version = content_provider.GetEntryVersion(title_id);
return version || !parent_title_id ? version : content_provider.GetEntryVersion(*parent_title_id);
return content_provider.GetEntryVersion(title_id);
}
PatchManager::Metadata PatchManager::GetControlMetadata() const {
const auto base_control_nca = content_provider.GetEntry(title_id, ContentRecordType::Control);
if (base_control_nca == nullptr) {
if (parent_title_id) {
const auto control_id = content_provider.HasEntry(*parent_title_id, ContentRecordType::Control) ? *parent_title_id : GetUpdateTitleID(*parent_title_id);
const PatchManager parent{control_id, fs_controller, content_provider};
return parent.GetControlMetadata();
}
return {};
}
-4
View File
@@ -109,14 +109,10 @@ public:
[[nodiscard]] static PatchManager::Metadata GetMetadataFromBaseOrUpdate(Core::System& system, u64 application_id) noexcept;
private:
[[nodiscard]] VirtualDir GetModificationLoadRoot(bool sdmc = false) const;
[[nodiscard]] std::vector<std::string> GetDisabledAddons() const;
void ApplyLayeredFS(VirtualFile& romfs, ContentRecordType type) const;
[[nodiscard]] std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
const std::string& build_id) const;
u64 title_id;
std::optional<u64> parent_title_id;
const Service::FileSystem::FileSystemController& fs_controller;
const ContentProvider& content_provider;
};
-17
View File
@@ -5,7 +5,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <limits>
#include <random>
#include <regex>
#include <openssl/evp.h>
@@ -349,22 +348,6 @@ std::vector<ContentProviderEntry> ContentProvider::ListEntries() const {
return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt);
}
std::optional<u64> ContentProvider::GetParentApplicationId(u64 program_id) const {
const auto application_id = GetBaseTitleID(program_id);
const auto program_index = program_id - application_id;
if (program_index == 0 || program_index > std::numeric_limits<u8>::max()) {
return std::nullopt;
}
if (!ListEntriesFilter(TitleType::Application, ContentRecordType::Meta, program_id).empty()) {
return std::nullopt;
}
if ((!ListEntriesFilter(TitleType::Application, ContentRecordType::Meta, application_id).empty() && HasEntry(program_id, ContentRecordType::Program))
|| (!ListEntriesFilter(TitleType::Update, ContentRecordType::Meta, GetUpdateTitleID(application_id)).empty() && HasEntry(GetUpdateTitleID(program_id), ContentRecordType::Program))) {
return application_id;
}
return std::nullopt;
}
PlaceholderCache::PlaceholderCache(VirtualDir dir_) : dir(std::move(dir_)) {}
bool PlaceholderCache::Create(const NcaID& id, u64 size) const {
-2
View File
@@ -100,8 +100,6 @@ public:
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
std::optional<u64> title_id = {}) const = 0;
[[nodiscard]] std::optional<u64> GetParentApplicationId(u64 program_id) const;
protected:
// A single instance of KeyManager to be used by GetEntry()
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
+8 -14
View File
@@ -9,7 +9,6 @@
#include "common/logging.h"
#include "common/uuid.h"
#include "core/core.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/vfs/vfs.h"
@@ -62,24 +61,17 @@ SaveDataFactory::SaveDataFactory(Core::System& system_, ProgramId program_id_,
SaveDataFactory::~SaveDataFactory() = default;
std::string SaveDataFactory::GetSaveDataPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, u128 user_id, u64 save_id) const {
if (type == SaveDataType::Account || type == SaveDataType::Device) {
const auto requested_id = title_id != 0 ? title_id : program_id;
const auto parent_id = system.GetContentProvider().GetParentApplicationId(requested_id);
title_id = parent_id.value_or(requested_id);
}
return GetFullPath(program_id, dir, space, type, title_id, user_id, save_id);
}
VirtualDir SaveDataFactory::Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const {
const auto save_directory = GetSaveDataPath(space, meta.type, meta.program_id, meta.user_id, meta.system_save_data_id);
const auto save_directory = GetFullPath(program_id, dir, space, meta.type, meta.program_id,
meta.user_id, meta.system_save_data_id);
return dir->CreateDirectoryRelative(save_directory);
}
VirtualDir SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const {
const auto save_directory = GetSaveDataPath(space, meta.type, meta.program_id, meta.user_id, meta.system_save_data_id);
const auto save_directory = GetFullPath(program_id, dir, space, meta.type, meta.program_id,
meta.user_id, meta.system_save_data_id);
auto out = dir->GetDirectoryRelative(save_directory);
@@ -162,7 +154,8 @@ std::string SaveDataFactory::GetUserGameSaveDataRoot(u128 user_id, bool future)
SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
u128 user_id) const {
const auto path = GetSaveDataPath(SaveDataSpaceId::User, type, title_id, user_id, 0);
const auto path =
GetFullPath(program_id, dir, SaveDataSpaceId::User, type, title_id, user_id, 0);
const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
const auto size_file = relative_dir->GetFile(GetSaveDataSizeFileName());
@@ -180,7 +173,8 @@ SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
SaveDataSize new_value) const {
const auto path = GetSaveDataPath(SaveDataSpaceId::User, type, title_id, user_id, 0);
const auto path =
GetFullPath(program_id, dir, SaveDataSpaceId::User, type, title_id, user_id, 0);
const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
const auto size_file = relative_dir->CreateFile(GetSaveDataSizeFileName());
-4
View File
@@ -1,6 +1,3 @@
// 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
@@ -50,7 +47,6 @@ public:
void SetAutoCreate(bool state);
private:
std::string GetSaveDataPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, u128 user_id, u64 save_id) const;
Core::System& system;
ProgramId program_id;
VirtualDir dir;
+8 -25
View File
@@ -12,7 +12,6 @@
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"
#include "hid_core/hid_types.h"
#include <array>
namespace Core::Frontend {
@@ -30,39 +29,23 @@ void DefaultControllerApplet::ReconfigureControllers(ReconfigureCallback callbac
const std::size_t min_supported_players =
parameters.enable_single_mode ? 1 : parameters.min_players;
using Core::HID::NpadStyleIndex;
const std::size_t max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;
std::size_t num_selected_players = 0;
std::array<bool, HID::HIDCore::available_controllers> keep_connected{};
// reserve existing AND valid players before filling slots. include Handheld, but not other
for (std::size_t index = 0; index < hid_core.available_controllers - 1; ++index) {
const auto* controller = hid_core.GetEmulatedControllerByIndex(index);
if (!parameters.keep_controllers_connected || !controller->IsConnected() || num_selected_players >= max_supported_players) continue;
const auto style = controller->GetNpadStyleIndex();
keep_connected[index] =
(style == NpadStyleIndex::Fullkey && parameters.allow_pro_controller) ||
(style == NpadStyleIndex::JoyconDual && parameters.allow_dual_joycons) ||
(style == NpadStyleIndex::JoyconLeft && parameters.allow_left_joycon) ||
(style == NpadStyleIndex::JoyconRight && parameters.allow_right_joycon) ||
(style == NpadStyleIndex::Handheld && parameters.enable_single_mode && parameters.allow_handheld && !Settings::IsDockedMode()) ||
(style == NpadStyleIndex::GameCube && parameters.allow_gamecube_controller);
num_selected_players += keep_connected[index];
}
// Disconnect Handheld first.
auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
if (!keep_connected[hid_core.available_controllers - 2]) handheld->Disconnect();
handheld->Disconnect();
// Deduce the best configuration based on the input parameters.
for (std::size_t index = 0; index < hid_core.available_controllers - 2; ++index) {
auto* controller = hid_core.GetEmulatedControllerByIndex(index);
if (keep_connected[index]) continue;
// First, disconnect all controllers regardless of the value of keep_controllers_connected.
// This makes it easy to connect the desired controllers.
controller->Disconnect();
// only add players still needed to reach the minimum
if (num_selected_players >= min_supported_players) continue;
++num_selected_players;
// Only connect the minimum number of required players.
if (index >= min_supported_players) {
continue;
}
// Connect controllers based on the following priority list from highest to lowest priority:
// Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
-8
View File
@@ -465,10 +465,6 @@ void KScheduler::ScheduleImplFiber(KernelCore& kernel) {
// Check if we need scheduling. If we do, then we can't complete the switch and should
// retry.
if (m_state.needs_scheduling.load(std::memory_order_seq_cst)) {
// Some libc++ lazily init mutex
[[maybe_unused]] auto const can_lock = highest_priority_thread->m_context_guard.try_lock();
DEBUG_ASSERT(!can_lock);
// Our switch failed.
// We should unlock the thread context, and then retry.
highest_priority_thread->m_context_guard.unlock();
@@ -500,10 +496,6 @@ void KScheduler::Unload(KernelCore& kernel, KThread* thread) {
// Check if the thread is terminated by checking the DPC flags.
if ((thread->GetStackParameters().dpc_flags & static_cast<u32>(DpcFlag::Terminated)) == 0) {
// Some libc++ lazily init mutex
[[maybe_unused]] auto const can_lock = thread->m_context_guard.try_lock();
DEBUG_ASSERT(!can_lock);
// The thread isn't terminated, so we want to unlock it.
thread->m_context_guard.unlock();
}
+4 -3
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
@@ -12,6 +12,7 @@
#include "common/atomic_ops.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/spin_lock.h"
namespace Kernel {
@@ -29,7 +30,7 @@ public:
};
public:
KSlabHeapImpl() = default;
constexpr KSlabHeapImpl() = default;
void Initialize() {
ASSERT(m_head == nullptr);
@@ -67,7 +68,7 @@ public:
private:
std::atomic<Node*> m_head{};
std::mutex m_lock;
Common::SpinLock m_lock;
};
} // namespace impl
+2 -1
View File
@@ -19,6 +19,7 @@
#include "common/intrusive_red_black_tree.h"
#include "common/scratch_buffer.h"
#include "common/spin_lock.h"
#include "core/arm/arm_interface.h"
#include "core/hle/kernel/k_affinity_mask.h"
#include "core/hle/kernel/k_light_lock.h"
@@ -919,7 +920,7 @@ private:
bool m_resource_limit_release_hint{};
bool m_is_kernel_address_key{};
StackParameters m_stack_parameters{};
std::mutex m_context_guard{};
Common::SpinLock m_context_guard{};
// For emulation
std::shared_ptr<Common::Fiber> m_host_context{};
@@ -23,12 +23,6 @@ constexpr std::size_t profile_username_size{32};
using ProfileUsername = std::array<u8, profile_username_size>;
using UserIDArray = std::array<Common::UUID, MAX_USERS>;
// This is nn::account::Uid
struct Uid {
std::array<u8, 0x10> unk0;
};
static_assert(sizeof(Uid) == 0x10);
/// Contains extra data related to a user.
/// TODO: RE this structure
struct UserData {
+4 -11
View File
@@ -29,7 +29,6 @@
#include "core/hle/service/am/frontend/applet_web_browser.h"
#include "core/hle/service/am/frontend/applets.h"
#include "core/hle/service/am/service/storage.h"
#include "core/hle/service/am/window_system.h"
#include "core/hle/service/sm/sm.h"
namespace Service::AM::Frontend {
@@ -73,16 +72,10 @@ void FrontendApplet::PushInteractiveOutData(std::shared_ptr<IStorage> storage) {
void FrontendApplet::Exit() {
auto applet_ = applet.lock();
{
std::scoped_lock lk{applet_->lock};
applet_->is_completed = true;
applet_->state_changed_event.Signal(system.Kernel());
}
if (auto caller_applet = applet_->caller_applet.lock()) {
std::scoped_lock lk{caller_applet->lock};
std::erase(caller_applet->child_applets, applet_);
}
if (auto* window_system = system.GetAppletManager().GetWindowSystem()) window_system->RequestUpdate();
std::scoped_lock lk{applet_->lock};
applet_->is_completed = true;
applet_->state_changed_event.Signal(system.Kernel());
}
FrontendAppletSet::FrontendAppletSet() = default;
@@ -92,13 +92,6 @@ public:
}
}
void RequestFocusStateChangedNotification(Kernel::KernelCore& kernel) {
if (m_focus_state_changed_notification_enabled) {
m_has_focus_state_changed = true;
this->SignalSystemEventIfNeeded(kernel);
}
}
void OnOperationAndPerformanceModeChanged(Kernel::KernelCore& kernel);
public:
@@ -347,22 +347,21 @@ Result IApplicationFunctions::NotifyRunning(Out<bool> out_became_running) {
Result IApplicationFunctions::GetPseudoDeviceId(Out<Common::UUID> out_pseudo_device_id) {
LOG_WARNING(Service_AM, "(stubbed)");
R_UNLESS(out_pseudo_device_id != nullptr, ResultUnknown);
R_UNLESS(out_pseudo_device_id, ResultUnknown);
// This should be hashed with the device specific hash
// for now this will do
const auto res = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, m_applet->program_id);
R_UNLESS(res.first != nullptr, ResultUnknown);
std::array<u8, EVP_MAX_MD_SIZE> hash;
u8 hash[EVP_MAX_MD_SIZE];
unsigned int hash_len = 0;
auto const seed = res.first->raw.seed_for_pseudo_device_id;
EVP_MD_CTX *ctx = EVP_MD_CTX_new();
auto const algorithm = EVP_sha1();
EVP_DigestInit_ex(ctx, algorithm, nullptr);
EVP_DigestUpdate(ctx, &seed, sizeof(seed));
EVP_DigestFinal_ex(ctx, hash.data(), &hash_len);
EVP_DigestFinal_ex(ctx, hash, &hash_len);
EVP_MD_CTX_free(ctx);
*out_pseudo_device_id = Common::UUID::MakeRFC4122V5(std::span<u8, 16>{hash.begin(), hash.begin() + 16});
*out_pseudo_device_id = Common::UUID::MakeRFC4122V5(std::span<u8, 20>{hash, std::size(hash)});
R_SUCCEED();
}
@@ -164,15 +164,15 @@ Result ILibraryAppletAccessor::PushInData(SharedPointer<IStorage> storage) {
Result ILibraryAppletAccessor::PopOutData(Out<SharedPointer<IStorage>> out_storage) {
LOG_DEBUG(Service_AM, "called");
R_TRY(m_broker->GetOutData().Pop(system.Kernel(), out_storage.Get()));
if (auto caller_applet = m_applet->caller_applet.lock(); caller_applet) {
std::scoped_lock lk{caller_applet->lock};
const bool focus_state_changed = caller_applet->lifecycle_manager.UpdateRequestedFocusState();
const bool is_front_app = m_applet->frontend && caller_applet->lifecycle_manager.IsApplication();
if (focus_state_changed) caller_applet->lifecycle_manager.SignalSystemEventIfNeeded(system.Kernel());
else if (is_front_app) caller_applet->lifecycle_manager.RequestFocusStateChangedNotification(system.Kernel());
caller_applet->lifecycle_manager.GetSystemEvent().Signal(system.Kernel());
caller_applet->lifecycle_manager.RequestResumeNotification();
caller_applet->lifecycle_manager.GetSystemEvent().Clear(system.Kernel());
caller_applet->lifecycle_manager.UpdateRequestedFocusState();
}
R_TRY(m_broker->GetOutData().Pop(system.Kernel(), out_storage.Get()));
if (m_applet->applet_id == AppletId::ProfileSelect && *out_storage) {
auto impl = (*out_storage)->GetImpl();
@@ -122,10 +122,7 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
auto broker = std::make_shared<AppletDataBroker>(system);
applet->caller_applet = caller_applet;
applet->caller_applet_broker = broker;
{
std::scoped_lock lk{caller_applet->lock};
caller_applet->child_applets.push_back(applet);
}
caller_applet->child_applets.push_back(applet);
window_system.TrackApplet(applet, false);
return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
}
@@ -151,10 +148,10 @@ std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(Core::System& syste
applet->caller_applet = caller_applet;
applet->caller_applet_broker = storage;
applet->frontend = system.GetFrontendAppletHolder().GetApplet(applet, applet_id, mode);
{
std::scoped_lock lk{caller_applet->lock};
caller_applet->child_applets.push_back(applet);
}
caller_applet->child_applets.push_back(applet);
window_system.TrackApplet(applet, false);
return std::make_shared<ILibraryAppletAccessor>(system, storage, applet);
}
@@ -154,9 +154,6 @@ FSP_SRV::FSP_SRV(Core::System& system_)
{720, nullptr, "AbandonAccessFailure"},
{800, nullptr, "GetAndClearFileSystemProxyErrorInfo"},
{810, nullptr, "RegisterProgramIndexMapInfo"},
{820, nullptr, "GetContentStorageInfoIndex"},
{830, nullptr, "EncryptStreamPlaySaveData"},
{831, nullptr, "DecryptStreamPlaySaveData"},
{1000, nullptr, "SetBisRootForHost"},
{1001, nullptr, "SetSaveDataSize"},
{1002, nullptr, "SetSaveDataRootPath"},
@@ -121,7 +121,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
{547, nullptr, "GetAllowedBluetoothLinksCount"},
{548, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevices"},
{549, nullptr, "GetConnectableRegisteredDevices"},
{551, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevicesForControllerSupport"}, //20.0.0+ //mocked via 548 for Diablo 3 (at least)
{551, nullptr, "GetRegisteredDevicesForControllerSupport"}, //20.0.0+
{700, nullptr, "ActivateUniquePad"},
{702, &IHidSystemServer::AcquireUniquePadConnectionEventHandle, "AcquireUniquePadConnectionEventHandle"},
{703, &IHidSystemServer::GetUniquePadIds, "GetUniquePadIds"},
@@ -758,7 +758,7 @@ void IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport(HLEReque
}
void IHidSystemServer::GetRegisteredDevices(HLERequestContext& ctx) {
LOG_WARNING(Service_HID, "(STUBBED) called, command={}", ctx.GetCommand()); //548 or 551
LOG_WARNING(Service_HID, "(STUBBED) called");
struct RegisterData {
std::array<u8, 0x68> data;
-1
View File
@@ -22,7 +22,6 @@
namespace IPC {
constexpr Result ResultNotSupported{ErrorModule::HIPC, 1};
constexpr Result ResultSessionClosed{ErrorModule::HIPC, 301};
struct ResponseBuilder {
+34 -28
View File
@@ -71,14 +71,17 @@ public:
void InstructionSynchronizationBarrierRaised() override {
last_code_addr = u64(-1); //reset back, force refetch
}
u64 MemoryRead(u64 vaddr, size_t size) override {
switch (size) {
case sizeof(u64): return ReadMemory<u64>(vaddr);
case sizeof(u32): return ReadMemory<u32>(vaddr);
case sizeof(u16): return ReadMemory<u16>(vaddr);
case sizeof(u8): return ReadMemory<u8>(vaddr);
default: UNREACHABLE();
}
u8 MemoryRead8(u64 vaddr) override {
return ReadMemory<u8>(vaddr);
}
u16 MemoryRead16(u64 vaddr) override {
return ReadMemory<u16>(vaddr);
}
u32 MemoryRead32(u64 vaddr) override {
return ReadMemory<u32>(vaddr);
}
u64 MemoryRead64(u64 vaddr) override {
return ReadMemory<u64>(vaddr);
}
u128 MemoryRead128(u64 vaddr) override {
return ReadMemory<u128>(vaddr);
@@ -86,19 +89,22 @@ public:
std::string MemoryReadCString(u64 vaddr) {
std::string result{};
u8 next;
while ((next = u8(MemoryRead(vaddr++, sizeof(u8)))) != 0)
while ((next = MemoryRead8(vaddr++)) != 0)
result += char(next);
return result;
}
void MemoryWrite(u64 vaddr, u64 value, size_t size) override {
switch (size) {
case sizeof(u64): WriteMemory<u64>(vaddr, u64(value)); break;
case sizeof(u32): WriteMemory<u32>(vaddr, u32(value)); break;
case sizeof(u16): WriteMemory<u16>(vaddr, u16(value)); break;
case sizeof(u8): WriteMemory<u8>(vaddr, u8(value)); break;
default: UNREACHABLE();
}
void MemoryWrite8(u64 vaddr, u8 value) override {
WriteMemory<u8>(vaddr, value);
}
void MemoryWrite16(u64 vaddr, u16 value) override {
WriteMemory<u16>(vaddr, value);
}
void MemoryWrite32(u64 vaddr, u32 value) override {
WriteMemory<u32>(vaddr, value);
}
void MemoryWrite64(u64 vaddr, u64 value) override {
WriteMemory<u64>(vaddr, value);
}
void MemoryWrite128(u64 vaddr, u128 value) override {
WriteMemory<u128>(vaddr, value);
@@ -187,14 +193,14 @@ public:
// The loaded NRO file has ELF relocations that must be processed before it can run.
// Normally this would be processed by RTLD, but in HLE context, we don't have
// the linker available, so we have to do it ourselves.
const VAddr mod_offset{callbacks->MemoryRead(4, sizeof(u32))};
if (callbacks->MemoryRead(mod_offset, sizeof(u32)) != Common::MakeMagic('M', 'O', 'D', '0'))
const VAddr mod_offset{callbacks->MemoryRead32(4)};
if (callbacks->MemoryRead32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0'))
return false;
// For more info about dynamic entries, see the ELF ABI specification:
// https://refspecs.linuxbase.org/elf/gabi4+/ch5.dynamic.html
// https://refspecs.linuxbase.org/elf/gabi4+/ch4.reloc.html
VAddr dynamic_offset{mod_offset + callbacks->MemoryRead(mod_offset + 4, sizeof(u32))};
VAddr dynamic_offset{mod_offset + callbacks->MemoryRead32(mod_offset + 4)};
VAddr rela_dyn = 0, relr_dyn = 0;
size_t num_rela = 0, num_relr = 0;
while (true) {
@@ -216,8 +222,8 @@ public:
for (size_t i = 0; i < num_rela; i++) {
const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))};
if (Elf64RelType(rela.r_info) == ElfAArch64Relative) {
const VAddr contents{callbacks->MemoryRead(rela.r_offset, sizeof(u64))};
callbacks->MemoryWrite(rela.r_offset, contents + rela.r_addend, sizeof(u64));
const VAddr contents{callbacks->MemoryRead64(rela.r_offset)};
callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend);
}
}
@@ -225,7 +231,7 @@ public:
for (size_t i = 0; i < num_relr; i++) {
const auto relr = callbacks->ReadMemory<Elf64_Relr>(relr_dyn + i * sizeof(Elf64_Relr));
const auto incr = [&](VAddr where) {
callbacks->MemoryWrite(where, callbacks->MemoryRead(where, sizeof(u64)) + relocbase, sizeof(u64));
callbacks->MemoryWrite64(where, callbacks->MemoryRead64(where) + relocbase);
};
if ((relr & 1) == 0) {
// where pointer
@@ -288,7 +294,7 @@ public:
if (argument_stack.size() > 8) {
const VAddr new_sp = Common::AlignDown(top_of_stack - (argument_stack.size() - 8) * sizeof(u64), STACK_ALIGN);
for (size_t i = 8; i < argument_stack.size(); i++)
callbacks->MemoryWrite(new_sp + (i - 8) * sizeof(u64), argument_stack[i], sizeof(u64));
callbacks->MemoryWrite64(new_sp + (i - 8) * sizeof(u64), argument_stack[i]);
jit->SetSP(new_sp);
}
// Reset the call state for the next invocation
@@ -379,17 +385,17 @@ void DynarmicCallbacks64::CallSVC(u32 swi) {
if (dest < src) {
for (size_t i = 0; i < n; i++)
MemoryWrite(dest + i, u8(MemoryRead(src + i, sizeof(u8))), sizeof(u8));
MemoryWrite8(dest + i, MemoryRead8(src + i));
} else {
for (size_t i = n; i > 0; i--)
MemoryWrite(dest + i - 1, u8(MemoryRead(src + i - 1, sizeof(u8))), sizeof(u8));
MemoryWrite8(dest + i - 1, MemoryRead8(src + i - 1));
}
} else if (pc == parent.helpers[size_t(HelperFn::Memset)]) {
const VAddr dest{parent.jit->GetRegister(0)};
const u64 c{parent.jit->GetRegister(1)};
const size_t n{parent.jit->GetRegister(2)};
for (size_t i = 0; i < n; i++)
MemoryWrite(dest + i, u8(c), sizeof(u8));
MemoryWrite8(dest + i, u8(c));
} else if (pc == parent.helpers[size_t(HelperFn::Resolve)]) {
// X0 contains a char* for a symbol to resolve
const auto name{MemoryReadCString(parent.jit->GetRegister(0))};
@@ -416,7 +422,7 @@ void DynarmicCallbacks64::CallSVC(u32 swi) {
}
void DynarmicCallbacks64::ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) {
auto const inst = MemoryRead(pc, sizeof(u32));
auto const inst = MemoryRead32(pc);
LOG_CRITICAL(Service_JIT, "{} PC @ {:08x}, data = {:08x}", exception, pc, inst);
parent.jit->HaltExecution();
}
-117
View File
@@ -6,16 +6,10 @@
#include "common/string_util.h"
#include "core/core.h"
#include "core/hle/kernel/k_client_session.h"
#include "core/hle/result.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/ngc/ngc.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
#include "frontend_common/firmware_manager.h"
namespace Service::NGC {
@@ -172,123 +166,12 @@ public:
}
};
struct SaveDataHandle {
u64 unk0;
};
static_assert(sizeof(SaveDataHandle) == 0x08);
class IUserShimScopedObject final : public ServiceFramework<IUserShimScopedObject> {
public:
explicit IUserShimScopedObject(Core::System& system_) : ServiceFramework(system_, "IUserShimScopedObject") {
// clang-format off
static const FunctionInfo functions[] = {
{450, nullptr, "InitializeForSaveData"},
{451, nullptr, "FinalizeForSaveData"},
{452, D<&IUserShimScopedObject::OpenSaveData>, "OpenSaveData"},
{453, nullptr, "CloseSaveData"},
{454, D<&IUserShimScopedObject::ReadSaveSlot>, "ReadSaveSlot"},
{455, D<&IUserShimScopedObject::WriteSaveSlot>, "WriteSaveSlot"},
{456, nullptr, "FlushSaveSlot"},
{457, nullptr, "CommitSaveData"},
};
// clang-format on
RegisterHandlers(functions);
}
Result OpenSaveData(Account::Uid unk0, Out<SaveDataHandle> unk1) {
LOG_WARNING(Service_NGC, "stubbed");
R_THROW(IPC::ResultNotSupported);
}
Result ReadSaveSlot(s32 offset, SaveDataHandle handle, OutBuffer<BufferAttr_HipcAutoSelect> out_data, Out<u32> out_size) {
LOG_WARNING(Service_NGC, "stubbed");
R_THROW(IPC::ResultNotSupported);
}
Result WriteSaveSlot(s32 offset, SaveDataHandle handle, InBuffer<BufferAttr_HipcAutoSelect> out_data) {
LOG_WARNING(Service_NGC, "stubbed");
// to implement
R_SUCCEED();
}
};
class IUserService final : public ServiceFramework<IUserService> {
public:
explicit IUserService(Core::System& system_) : ServiceFramework(system_, "stpl:u") {
// clang-format off
static const FunctionInfo functions[] = {
{0 , D<&IUserService::Cmd0>, "Cmd0"},
};
// clang-format on
RegisterHandlers(functions);
}
Result Cmd0(u32 unk0, OutInterface<IUserShimScopedObject> out_interface) {
LOG_WARNING(Service_NGC, "stubbed");
*out_interface = std::make_shared<IUserShimScopedObject>(system);
R_SUCCEED();
}
};
class ISystemShimScopedObject final : public ServiceFramework<ISystemShimScopedObject> {
public:
explicit ISystemShimScopedObject(Core::System& system_) : ServiceFramework(system_, "ISystemShimScopedObject") {
// clang-format off
static const FunctionInfo functions[] = {
{106, nullptr, "Cmd106"},
{107, nullptr, "Cmd107"},
{108, D<&ISystemShimScopedObject::Cmd108>, "Cmd108"},
{207, nullptr, "Cmd207"},
{208, D<&ISystemShimScopedObject::Cmd208>, "Cmd208"},
{209, nullptr, "Cmd209"},
{210, nullptr, "Cmd210"},
{211, nullptr, "Cmd211"},
{212, nullptr, "Cmd212"},
};
// clang-format on
RegisterHandlers(functions);
}
Result Cmd108() {
LOG_WARNING(Service_NGC, "stubbed");
R_THROW(IPC::ResultNotSupported);
}
Result Cmd208(Out<std::array<u8, 0x20>> unk0) {
LOG_WARNING(Service_NGC, "stubbed");
R_THROW(IPC::ResultNotSupported);
}
};
class ISystemService final : public ServiceFramework<ISystemService> {
public:
explicit ISystemService(Core::System& system_) : ServiceFramework(system_, "stpl:sys") {
// clang-format off
static const FunctionInfo functions[] = {
{0 , D<&ISystemService::Cmd0>, "Cmd0"},
};
// clang-format on
RegisterHandlers(functions);
}
Result Cmd0(OutInterface<ISystemShimScopedObject> out_interface) {
LOG_WARNING(Service_NGC, "stubbed");
*out_interface = std::make_shared<ISystemShimScopedObject>(system);
R_SUCCEED();
}
};
void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("ngct:u", std::make_shared<IService>(system), 4);
server_manager->RegisterNamedService("ngct:s", std::make_shared<IServiceWithManagementApi>(system), 4);
server_manager->RegisterNamedService("ngc:u", std::make_shared<NgcServiceImpl>(system), 4);
// +23.0.0
if (FirmwareManager::GetFirmwareVersion(system).first.major >= 23) {
server_manager->RegisterNamedService("stpl:u", std::make_shared<IUserService>(system), 4);
server_manager->RegisterNamedService("stpl:sys", std::make_shared<ISystemService>(system), 4);
}
ServerManager::RunServer(std::move(server_manager));
}
@@ -7,7 +7,7 @@
#pragma once
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/ns/async_result.h"
#include "core/hle/service/ns/i_async_result.h"
#include "core/hle/service/ns/language.h"
#include "core/hle/service/ns/ns_types.h"
#include "core/hle/service/os/event.h"
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ns/async_result.h"
#include "core/hle/service/ns/i_async_result.h"
#include <cstring>
@@ -32,4 +32,4 @@ Result IAsyncResult::Cancel() {
R_SUCCEED();
}
} // namespace Service::NS
} // namespace Service::NS
@@ -311,30 +311,27 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationIcon(HLERequestCon
// u64 - app count
memory.WriteBlock(t_mem_address + out_length, &app_count, sizeof(u64));
out_length += sizeof(u64);
ASSERT(out_length <= t_mem->GetSize());
// [list of u64] - size of icons
for (size_t i = 0; i < app_count; ++i) {
const u64 app_id = app_ids_buffer[i];
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
if (const auto control = pm.GetControlMetadata(); control.second) {
u64 full_size = control.second->GetSize();
memory.WriteBlock(t_mem_address + out_length, &full_size, sizeof(u64));
}
const auto control = pm.GetControlMetadata();
u64 full_size = control.second->GetSize();
memory.WriteBlock(t_mem_address + out_length, &full_size, sizeof(u64));
out_length += sizeof(u64);
ASSERT(out_length <= t_mem->GetSize());
}
// [list of raw icon data]
std::vector<u8> full_icon_data;
for (size_t i = 0; i < app_count; ++i) {
const u64 app_id = app_ids_buffer[i];
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
if (const auto control = pm.GetControlMetadata(); control.second) {
if (auto const full_size = control.second->GetSize(); full_size > 0) {
std::vector<u8> full_icon_data(full_size);
control.second->Read(full_icon_data.data(), full_size, 0);
memory.WriteBlock(t_mem_address + out_length, full_icon_data.data(), full_size);
out_length += full_size;
ASSERT(out_length <= t_mem->GetSize());
}
const auto control = pm.GetControlMetadata();
auto const full_size = control.second->GetSize();
if (full_size > 0) {
full_icon_data.resize(full_size);
control.second->Read(full_icon_data.data(), full_size, 0);
memory.WriteBlock(t_mem_address + out_length, full_icon_data.data(), full_size);
out_length += full_size;
}
}
}
@@ -348,12 +345,6 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationIcon(HLERequestCon
void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestContext& ctx) {
const auto app_ids_buffer = ctx.ReadBuffer();
const size_t app_count = app_ids_buffer.size() / sizeof(u64);
std::vector<u64> application_ids(app_count);
if (app_count > 0) {
std::memcpy(application_ids.data(), app_ids_buffer.data(), app_count * sizeof(u64));
}
auto t_mem_obj = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(ctx.GetCopyHandle(0));
auto* t_mem = t_mem_obj.GetPointerUnsafe();
constexpr size_t title_entry_size = sizeof(FileSys::LanguageEntry);
@@ -363,9 +354,8 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestCo
auto& memory = system.ApplicationMemory();
const auto t_mem_address = t_mem->GetSourceAddress();
for (size_t i = 0; i < app_count; ++i) {
const u64 app_id = application_ids[i];
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(),
system.GetContentProvider()};
const u64 app_id = app_ids_buffer[i];
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
const auto control = pm.GetControlMetadata();
FileSys::LanguageEntry entry{};
if (control.first != nullptr) {
+234 -208
View File
@@ -48,12 +48,6 @@ enum class OptionType : u32 {
EnableAlpn = 3,
};
// This is nn::ssl::sf::RenegotiationMode
enum RenegotiationMode : u32 {
None = 0, ///< None
Secure = 1, ///< Secure
};
// This is nn::ssl::sf::SslVersion
struct SslVersion {
union {
@@ -81,34 +75,34 @@ public:
shared_data{shared_data_in}, backend{std::move(backend_in)} {
// clang-format off
static const FunctionInfo functions[] = {
{0, D<&ISslConnection::SetSocketDescriptor>, "SetSocketDescriptor"},
{1, D<&ISslConnection::SetHostName>, "SetHostName"},
{2, D<&ISslConnection::SetVerifyOption>, "SetVerifyOption"},
{3, D<&ISslConnection::SetIoMode>, "SetIoMode"},
{4, D<&ISslConnection::GetSocketDescriptor>, "GetSocketDescriptor"},
{5, D<&ISslConnection::GetHostName>, "GetHostName"},
{0, &ISslConnection::SetSocketDescriptor, "SetSocketDescriptor"},
{1, &ISslConnection::SetHostName, "SetHostName"},
{2, &ISslConnection::SetVerifyOption, "SetVerifyOption"},
{3, &ISslConnection::SetIoMode, "SetIoMode"},
{4, nullptr, "GetSocketDescriptor"},
{5, nullptr, "GetHostName"},
{6, nullptr, "GetVerifyOption"},
{7, D<&ISslConnection::GetIoMode>, "GetIoMode"},
{8, D<&ISslConnection::DoHandshake>, "DoHandshake"},
{7, nullptr, "GetIoMode"},
{8, &ISslConnection::DoHandshake, "DoHandshake"},
{9, &ISslConnection::DoHandshakeGetServerCert, "DoHandshakeGetServerCert"},
{10, D<&ISslConnection::Read>, "Read"},
{11, D<&ISslConnection::Write>, "Write"},
{12, D<&ISslConnection::Pending>, "Pending"},
{13, D<&ISslConnection::Peek>, "Peek"},
{14, D<&ISslConnection::Poll>, "Poll"},
{15, D<&ISslConnection::GetVerifyCertError>, "GetVerifyCertError"},
{16, D<&ISslConnection::GetNeededServerCertBufferSize>, "GetNeededServerCertBufferSize"},
{17, D<&ISslConnection::SetSessionCacheMode>, "SetSessionCacheMode"},
{18, D<&ISslConnection::GetSessionCacheMode>, "GetSessionCacheMode"},
{19, D<&ISslConnection::FlushSessionCache>, "FlushSessionCache"},
{20, D<&ISslConnection::SetRenegotiationMode>, "SetRenegotiationMode"},
{21, D<&ISslConnection::GetRenegotiationMode>, "GetRenegotiationMode"},
{22, D<&ISslConnection::SetOption>, "SetOption"},
{23, D<&ISslConnection::GetOption>, "GetOption"},
{10, &ISslConnection::Read, "Read"},
{11, &ISslConnection::Write, "Write"},
{12, &ISslConnection::Pending, "Pending"},
{13, nullptr, "Peek"},
{14, nullptr, "Poll"},
{15, nullptr, "GetVerifyCertError"},
{16, nullptr, "GetNeededServerCertBufferSize"},
{17, &ISslConnection::SetSessionCacheMode, "SetSessionCacheMode"},
{18, nullptr, "GetSessionCacheMode"},
{19, nullptr, "FlushSessionCache"},
{20, nullptr, "SetRenegotiationMode"},
{21, nullptr, "GetRenegotiationMode"},
{22, &ISslConnection::SetOption, "SetOption"},
{23, &ISslConnection::GetOption, "GetOption"},
{24, nullptr, "GetVerifyCertErrors"},
{25, nullptr, "GetCipherInfo"},
{26, D<&ISslConnection::SetNextAlpnProto>, "SetNextAlpnProto"},
{27, D<&ISslConnection::GetNextAlpnProto>, "GetNextAlpnProto"},
{26, &ISslConnection::SetNextAlpnProto, "SetNextAlpnProto"},
{27, &ISslConnection::GetNextAlpnProto, "GetNextAlpnProto"},
{28, nullptr, "SetDtlsSocketDescriptor"},
{29, nullptr, "GetDtlsHandshakeTimeout"},
{30, nullptr, "SetPrivateOption"},
@@ -147,6 +141,80 @@ public:
}
private:
SslVersion ssl_version;
std::shared_ptr<SslContextSharedData> shared_data;
std::unique_ptr<SSLConnectionBackend> backend;
std::optional<int> fd_to_close;
bool do_not_close_socket = false;
bool get_server_cert_chain = false;
bool skip_default_verify = false;
bool enable_alpn = false;
std::shared_ptr<Network::SocketBase> socket;
std::vector<u8> next_alpn_proto;
bool did_handshake = false;
u32 verify_option = 0;
Result SetSocketDescriptorImpl(s32* out_fd, s32 fd) {
LOG_DEBUG(Service_SSL, "called, fd={}", fd);
ASSERT(!did_handshake);
auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD_USA>("bsd:u");
ASSERT_OR_EXECUTE(bsd, { return ResultInternalError; });
auto const res_v = bsd->DuplicateSocketImpl(fd);
if (auto *res = std::get_if<s32>(&res_v)) {
const s32 duplicated_fd = *res;
if (do_not_close_socket) {
*out_fd = duplicated_fd;
} else {
*out_fd = -1;
fd_to_close = duplicated_fd;
}
std::optional<std::shared_ptr<Network::SocketBase>> sock = bsd->GetSocket(duplicated_fd);
if (!sock.has_value()) {
LOG_ERROR(Service_SSL, "invalid socket fd {} after duplication", duplicated_fd);
return ResultInvalidSocket;
}
socket = std::move(*sock);
backend->SetSocket(socket);
return ResultSuccess;
}
LOG_ERROR(Service_SSL, "Failed to duplicate socket with fd {}", fd);
return ResultInvalidSocket;
}
Result SetHostNameImpl(const std::string& hostname) {
LOG_DEBUG(Service_SSL, "called. hostname={}", hostname);
ASSERT(!did_handshake);
return backend->SetHostName(hostname);
}
Result SetVerifyOptionImpl(u32 option) {
ASSERT(!did_handshake);
LOG_DEBUG(Service_SSL, "called. option={} (forcing 0)", option);
verify_option = 0;
backend->SetVerifyOption(0);
return ResultSuccess;
}
Result SetIoModeImpl(u32 input_mode) {
auto mode = static_cast<IoMode>(input_mode);
ASSERT(mode == IoMode::Blocking || mode == IoMode::NonBlocking);
ASSERT_OR_EXECUTE(socket, { return ResultNoSocket; });
const bool non_block = mode == IoMode::NonBlocking;
const Network::Errno error = socket->SetNonBlock(non_block);
if (error != Network::Errno::SUCCESS) {
LOG_ERROR(Service_SSL, "Failed to set native socket non-block flag to {}", non_block);
}
return ResultSuccess;
}
Result SetSessionCacheModeImpl(u32 mode) {
ASSERT(!did_handshake);
LOG_WARNING(Service_SSL, "(STUBBED) called. value={}", mode);
return ResultSuccess;
}
Result DoHandshakeImpl() {
ASSERT_OR_EXECUTE(!did_handshake && socket, { return ResultNoSocket; });
Result res = backend->DoHandshake();
@@ -166,17 +234,19 @@ private:
};
if (!get_server_cert_chain) {
// Just return the first one, unencoded.
ASSERT_OR_EXECUTE_MSG(!certs.empty(), { return {}; }, "Should be at least one server cert");
ASSERT_OR_EXECUTE_MSG(
!certs.empty(), { return {}; }, "Should be at least one server cert");
return certs[0];
}
std::vector<u8> ret;
Header header{0x4E4D684374726543, u32(certs.size()), 0};
Header header{0x4E4D684374726543, static_cast<u32>(certs.size()), 0};
ret.insert(ret.end(), reinterpret_cast<u8*>(&header), reinterpret_cast<u8*>(&header + 1));
size_t data_offset = sizeof(Header) + certs.size() * sizeof(EntryHeader);
for (auto& cert : certs) {
EntryHeader entry_header{u32(cert.size()), u32(data_offset)};
EntryHeader entry_header{static_cast<u32>(cert.size()), static_cast<u32>(data_offset)};
data_offset += cert.size();
ret.insert(ret.end(), reinterpret_cast<u8*>(&entry_header), reinterpret_cast<u8*>(&entry_header + 1));
ret.insert(ret.end(), reinterpret_cast<u8*>(&entry_header),
reinterpret_cast<u8*>(&entry_header + 1));
}
for (auto& cert : certs) {
ret.insert(ret.end(), cert.begin(), cert.end());
@@ -184,77 +254,65 @@ private:
return ret;
}
Result SetSocketDescriptor(s32 in_fd, Out<s32> out_fd) {
LOG_DEBUG(Service_SSL, "called, fd={}", in_fd);
ASSERT(!did_handshake);
auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD_USA>("bsd:u");
ASSERT_OR_EXECUTE(bsd, { return ResultInternalError; });
auto const res_v = bsd->DuplicateSocketImpl(in_fd);
if (auto *res = std::get_if<s32>(&res_v)) {
const s32 dup_fd = *res;
*out_fd = do_not_close_socket ? dup_fd : -1;
if (!do_not_close_socket)
fd_to_close = dup_fd;
auto const sock = bsd->GetSocket(dup_fd);
if (!sock.has_value()) {
LOG_ERROR(Service_SSL, "invalid socket fd {} after duplication", dup_fd);
return ResultInvalidSocket;
}
socket = std::move(*sock);
backend->SetSocket(std::move(socket));
return ResultSuccess;
Result ReadImpl(std::vector<u8>* out_data) {
ASSERT_OR_EXECUTE(did_handshake, { return ResultInternalError; });
size_t actual_size{};
Result res = backend->Read(&actual_size, *out_data);
if (res != ResultSuccess) {
return res;
}
LOG_ERROR(Service_SSL, "Failed to duplicate socket with fd {}", in_fd);
return ResultInvalidSocket;
out_data->resize(actual_size);
return res;
}
Result SetHostName(InBuffer<BufferAttr_HipcMapAlias> buf) {
auto const hostname = Common::StringFromBuffer(buf);
LOG_DEBUG(Service_SSL, "called. hostname={}", hostname);
ASSERT(!did_handshake);
return backend->SetHostName(hostname.c_str());
Result WriteImpl(size_t* out_size, std::span<const u8> data) {
ASSERT_OR_EXECUTE(did_handshake, { return ResultInternalError; });
return backend->Write(out_size, data);
}
Result SetVerifyOption(u32 option) {
LOG_DEBUG(Service_SSL, "called. option={} (forcing 0)", option);
ASSERT(!did_handshake);
verify_option = 0;
backend->SetVerifyOption(0);
R_SUCCEED();
Result PendingImpl(s32* out_pending) {
LOG_WARNING(Service_SSL, "(STUBBED) called.");
*out_pending = 0;
return ResultSuccess;
}
Result SetIoMode(u32 input_mode) {
auto mode = IoMode(input_mode);
ASSERT(mode == IoMode::Blocking || mode == IoMode::NonBlocking);
R_UNLESS(socket, ResultNoSocket);
const bool non_block = mode == IoMode::NonBlocking;
const Network::Errno error = socket->SetNonBlock(non_block);
if (error != Network::Errno::SUCCESS) {
LOG_ERROR(Service_SSL, "Failed to set native socket non-block flag to {}", non_block);
}
R_SUCCEED();
void SetSocketDescriptor(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const s32 in_fd = rp.Pop<s32>();
s32 out_fd{-1};
const Result res = SetSocketDescriptorImpl(&out_fd, in_fd);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(res);
rb.Push<s32>(out_fd);
}
Result GetSocketDescriptor(Out<u32> out_fd) {
LOG_WARNING(Service_SSL, "(STUBBED)");
*out_fd = uint32_t(socket->GetFD());
R_SUCCEED();
void SetHostName(HLERequestContext& ctx) {
const std::string hostname = Common::StringFromBuffer(ctx.ReadBuffer());
const Result res = SetHostNameImpl(hostname);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
Result GetHostName(OutBuffer<BufferAttr_HipcMapAlias> data, Out<u32> out_size) {
LOG_WARNING(Service_SSL, "(STUBBED)");
ASSERT(!did_handshake);
return backend->GetHostName(std::span<u8>{data.begin(), data.end()}, out_size);
void SetVerifyOption(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u32 option = rp.Pop<u32>();
const Result res = SetVerifyOptionImpl(option);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
Result GetIoMode(Out<u32> out_mode) {
LOG_WARNING(Service_SSL, "(STUBBED)");
R_SUCCEED();
void SetIoMode(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u32 mode = rp.Pop<u32>();
const Result res = SetIoModeImpl(mode);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
Result DoHandshake() {
return DoHandshakeImpl();
void DoHandshake(HLERequestContext& ctx) {
const Result res = DoHandshakeImpl();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
void DoHandshakeGetServerCert(HLERequestContext& ctx) {
@@ -293,158 +351,131 @@ private:
rb.PushRaw(out);
}
Result Read(OutBuffer<BufferAttr_HipcMapAlias> data, Out<u32> out_size) {
R_UNLESS(did_handshake, ResultInternalError);
size_t tmp{};
auto const res = backend->Read(&tmp, data);
*out_size = u32(tmp);
return res;
void Read(HLERequestContext& ctx) {
std::vector<u8> output_bytes(ctx.GetWriteBufferSize());
const Result res = ReadImpl(&output_bytes);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(res);
if (res == ResultSuccess) {
rb.Push(static_cast<u32>(output_bytes.size()));
ctx.WriteBuffer(output_bytes);
} else {
rb.Push(static_cast<u32>(0));
}
}
Result Write(InBuffer<BufferAttr_HipcMapAlias> data, Out<u32> out_size) {
R_UNLESS(did_handshake, ResultInternalError);
size_t tmp{};
auto const res = backend->Write(&tmp, data);
*out_size = u32(tmp);
return res;
void Write(HLERequestContext& ctx) {
size_t write_size{0};
const Result res = WriteImpl(&write_size, ctx.ReadBuffer());
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(res);
rb.Push(static_cast<u32>(write_size));
}
Result Pending(Out<s32> out_pending_size) {
LOG_WARNING(Service_SSL, "(STUBBED)");
*out_pending_size = s32(backend->Pending());
R_SUCCEED();
void Pending(HLERequestContext& ctx) {
s32 pending_size{0};
const Result res = PendingImpl(&pending_size);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(res);
rb.Push<s32>(pending_size);
}
Result Peek(OutBuffer<BufferAttr_HipcMapAlias> data, Out<u32> out_size) {
LOG_WARNING(Service_SSL, "(STUBBED)");
size_t tmp{};
auto const res = backend->Peek(&tmp, data);
*out_size = u32(tmp);
return res;
void SetSessionCacheMode(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u32 mode = rp.Pop<u32>();
const Result res = SetSessionCacheModeImpl(mode);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
Result Poll(u32 in_pollevent, u32 timer, Out<u32> out_pollevent) {
LOG_WARNING(Service_SSL, "(STUBBED)");
R_SUCCEED();
}
void SetOption(HLERequestContext& ctx) {
struct Parameters {
OptionType option;
s32 value;
};
static_assert(sizeof(Parameters) == 0x8, "Parameters is an invalid size");
Result GetVerifyCertError() {
LOG_WARNING(Service_SSL, "(STUBBED)");
R_SUCCEED();
}
IPC::RequestParser rp{ctx};
const auto parameters = rp.PopRaw<Parameters>();
Result GetNeededServerCertBufferSize(Out<u32> out_needed_buffer_size) {
LOG_WARNING(Service_SSL, "(STUBBED)");
R_SUCCEED();
}
Result SetSessionCacheMode(u32 mode) {
LOG_WARNING(Service_SSL, "(STUBBED) called. value={}", mode);
R_UNLESS(!did_handshake, ResultInternalError);
R_SUCCEED();
}
Result GetSessionCacheMode(Out<u32> mode) {
LOG_WARNING(Service_SSL, "(STUBBED)");
R_UNLESS(!did_handshake, ResultInternalError);
R_SUCCEED();
}
Result FlushSessionCache() {
LOG_WARNING(Service_SSL, "(STUBBED)");
R_UNLESS(!did_handshake, ResultInternalError);
R_SUCCEED();
}
Result SetRenegotiationMode(RenegotiationMode mode) {
LOG_WARNING(Service_SSL, "(STUBBED)");
backend->SetRenegotiationMode(u32(mode));
R_SUCCEED();
}
Result GetRenegotiationMode(Out<RenegotiationMode> mode) {
LOG_WARNING(Service_SSL, "(STUBBED)");
u32 tmp{};
auto const res = backend->GetRenegotiationMode(&tmp);
*mode = RenegotiationMode(tmp);
return res;
}
Result SetOption(OptionType option, s32 value) {
switch (option) {
switch (parameters.option) {
case OptionType::DoNotCloseSocket:
do_not_close_socket = bool(value);
do_not_close_socket = static_cast<bool>(parameters.value);
break;
case OptionType::GetServerCertChain:
get_server_cert_chain = bool(value);
get_server_cert_chain = static_cast<bool>(parameters.value);
break;
case OptionType::SkipDefaultVerify:
skip_default_verify = bool(value);
skip_default_verify = static_cast<bool>(parameters.value);
break;
case OptionType::EnableAlpn:
enable_alpn = bool(value);
enable_alpn = static_cast<bool>(parameters.value);
break;
default:
LOG_WARNING(Service_SSL, "Unknown option={}, value={}", option, value);
LOG_WARNING(Service_SSL, "Unknown option={}, value={}", parameters.option,
parameters.value);
}
R_SUCCEED();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
Result GetOption(OptionType option, Out<u8> value) {
void GetOption(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto option = rp.PopRaw<OptionType>();
u8 value = 0;
switch (option) {
case OptionType::DoNotCloseSocket:
*value = u8(do_not_close_socket);
value = static_cast<u8>(do_not_close_socket);
break;
case OptionType::GetServerCertChain:
*value = u8(get_server_cert_chain);
value = static_cast<u8>(get_server_cert_chain);
break;
case OptionType::SkipDefaultVerify:
*value = u8(skip_default_verify);
value = static_cast<u8>(skip_default_verify);
break;
case OptionType::EnableAlpn:
*value = u8(enable_alpn);
value = static_cast<u8>(enable_alpn);
break;
default:
LOG_WARNING(Service_SSL, "Unknown option={}", u32(option));
*value = 0;
LOG_WARNING(Service_SSL, "Unknown option={}", option);
value = 0;
break;
}
LOG_DEBUG(Service_SSL, "GetOption called, option={}, ret value={}", u32(option), *value);
R_SUCCEED();
LOG_DEBUG(Service_SSL, "GetOption called, option={}, ret value={}", option, value);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u8>(value);
}
Result SetNextAlpnProto(InBuffer<BufferAttr_HipcMapAlias> data) {
auto const to_write = u32((std::min)(next_alpn_proto.size(), data.size()));
next_alpn_proto.assign(data.begin(), data.begin() + to_write);
void SetNextAlpnProto(HLERequestContext& ctx) {
const auto data = ctx.ReadBuffer(0);
next_alpn_proto.assign(data.begin(), data.end());
LOG_DEBUG(Service_SSL, "SetNextAlpnProto called, size={}", next_alpn_proto.size());
R_SUCCEED();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
Result GetNextAlpnProto(OutBuffer<BufferAttr_HipcMapAlias> data, Out<u32> to_write) {
*to_write = u32((std::min)(next_alpn_proto.size(), data.size()));
next_alpn_proto.assign(data.begin(), data.begin() + *to_write);
LOG_DEBUG(Service_SSL, "GetNextAlpnProto called, size={}", *to_write);
R_SUCCEED();
void GetNextAlpnProto(HLERequestContext& ctx) {
const size_t writable = ctx.GetWriteBufferSize();
const size_t to_write = (std::min)(next_alpn_proto.size(), writable);
if (to_write != 0) {
ctx.WriteBuffer(std::span<const u8>(next_alpn_proto.data(), to_write));
}
LOG_DEBUG(Service_SSL, "GetNextAlpnProto called, size={}", to_write);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u32>(static_cast<u32>(to_write));
}
Result GetVerifyCertErrors(OutBuffer<BufferAttr_HipcMapAlias> unk0, Out<u32> unk1, Out<u32> unk2) {
LOG_WARNING(Service_SSL, "(STUBBED)");
R_SUCCEED();
}
SslVersion ssl_version;
std::shared_ptr<SslContextSharedData> shared_data;
std::unique_ptr<SSLConnectionBackend> backend;
std::optional<int> fd_to_close;
std::shared_ptr<Network::SocketBase> socket;
std::vector<u8> next_alpn_proto;
u32 verify_option = 0;
bool do_not_close_socket = false;
bool get_server_cert_chain = false;
bool skip_default_verify = false;
bool enable_alpn = false;
bool did_handshake = false;
};
class ISslContext final : public ServiceFramework<ISslContext> {
@@ -461,7 +492,7 @@ public:
{5, &ISslContext::ImportClientPki, "ImportClientPki"},
{6, nullptr, "RemoveServerPki"},
{7, nullptr, "RemoveClientPki"},
{8, D<&ISslContext::RegisterInternalPki>, "RegisterInternalPki"},
{8, nullptr, "RegisterInternalPki"},
{9, nullptr, "AddPolicyOid"},
{10, nullptr, "ImportCrl"},
{11, nullptr, "RemoveCrl"},
@@ -556,11 +587,6 @@ private:
rb.Push(ResultSuccess);
rb.Push(client_id);
}
Result RegisterInternalPki() {
LOG_WARNING(Service_SSL, "(STUBBED) called");
R_SUCCEED();
}
};
class ISslService final : public ServiceFramework<ISslService> {
+1 -6
View File
@@ -36,17 +36,12 @@ class SSLConnectionBackend {
public:
virtual ~SSLConnectionBackend() {}
virtual void SetSocket(std::shared_ptr<Network::SocketBase> socket) = 0;
virtual Result SetHostName(const std::string& hostname) = 0;
virtual void SetVerifyOption(u32 option) = 0;
virtual Result DoHandshake() = 0;
virtual Result Read(size_t* out_size, std::span<u8> data) = 0;
virtual Result Peek(size_t* out_size, std::span<u8> data) = 0;
virtual Result Write(size_t* out_size, std::span<const u8> data) = 0;
virtual Result GetServerCerts(std::vector<std::vector<u8>>* out_certs) = 0;
virtual Result SetHostName(const char* hostname) = 0;
virtual Result GetHostName(std::span<u8> hostname, u32* out_size) = 0;
virtual int Pending() = 0;
virtual Result SetRenegotiationMode(u32 mode) = 0;
virtual Result GetRenegotiationMode(u32* mode) = 0;
};
Result CreateSSLConnectionBackend(std::unique_ptr<SSLConnectionBackend>* out_backend);
@@ -157,30 +157,18 @@ public:
socket = std::move(socket_in);
}
Result SetHostName(const char* hostname) override {
Result SetHostName(const std::string& hostname) override {
if (!skip_cert_verification) {
if (!SSL_set1_host(ssl, hostname)) {
if (!SSL_set1_host(ssl, hostname.c_str())) {
LOG_ERROR(Service_SSL, "SSL_set1_host({}) failed", hostname);
return CheckOpenSSLErrors();
}
}
if (!SSL_set_tlsext_host_name(ssl, hostname)) { // hostname for SNI
if (!SSL_set_tlsext_host_name(ssl, hostname.c_str())) { // hostname for SNI
LOG_ERROR(Service_SSL, "SSL_set_tlsext_host_name({}) failed", hostname);
return CheckOpenSSLErrors();
}
R_SUCCEED();
}
Result GetHostName(std::span<u8> data, u32* out_size) override {
auto const peer_name = SSL_get0_peername(ssl);
if (peer_name == nullptr) {
LOG_ERROR(Service_SSL, "SSL_get0_peername()");
return CheckOpenSSLErrors();
}
auto const s = std::string{peer_name};
*out_size = u32(s.size());
std::memcpy(data.data(), s.data(), (std::min)(s.size(), data.size()));
R_SUCCEED();
return ResultSuccess;
}
void SetVerifyOption(u32 option) override {
@@ -225,13 +213,6 @@ public:
return HandleReturn("SSL_read_ex", out_size, ret);
}
Result Peek(size_t* out_size, std::span<u8> data) override {
auto const n = (std::min)(data.size(), *out_size);
const int ret = SSL_peek(ssl, data.data(), int(n));
*out_size = n;
return HandleReturn("SSL_write_ex", out_size, ret);
}
Result Write(size_t* out_size, std::span<const u8> data) override {
const int ret = SSL_write_ex(ssl, data.data(), data.size(), out_size);
return HandleReturn("SSL_write_ex", out_size, ret);
@@ -282,25 +263,7 @@ public:
out_certs->emplace_back(buf, buf + len);
OPENSSL_free(buf);
}
R_SUCCEED();
}
int Pending() override {
return SSL_pending(ssl);
}
Result SetRenegotiationMode(u32 mode) override {
if (mode == 0) {
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_RENEGOTIATION);
} else {
SSL_CTX_set_options(ssl_ctx, SSL_OP_ALLOW_CLIENT_RENEGOTIATION);
}
R_SUCCEED();
}
Result GetRenegotiationMode(u32* mode) override {
*mode = SSL_get_secure_renegotiation_support(ssl) ? 1 : 0;
R_SUCCEED();
return ResultSuccess;
}
~SSLConnectionBackendOpenSSL() {
+12 -4
View File
@@ -33,10 +33,18 @@ u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) {
void MemoryWriteWidth(Core::Memory::Memory& memory, u32 width, VAddr addr, u64 value) {
switch (width) {
case sizeof(u64): return memory.Write64(addr, value);
case sizeof(u32): return memory.Write32(addr, u32(value));
case sizeof(u16): return memory.Write16(addr, u16(value));
case sizeof(u8): return memory.Write8(addr, u8(value));
case 1:
memory.Write8(addr, static_cast<u8>(value));
break;
case 2:
memory.Write16(addr, static_cast<u16>(value));
break;
case 4:
memory.Write32(addr, static_cast<u32>(value));
break;
case 8:
memory.Write64(addr, value);
break;
default:
UNREACHABLE();
}
@@ -82,7 +82,7 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const
auto fn = [](const A32::UserConfig& conf, A32::VAddr vaddr) -> T {
return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T {
return (conf.callbacks->*callback)(vaddr, sizeof(T));
return (conf.callbacks->*callback)(vaddr);
});
};
@@ -136,9 +136,12 @@ static void* EmitExclusiveWriteCallTrampoline(oaknut::CodeGenerator& code, const
oaknut::Label l_addr, l_this;
auto fn = [](const A32::UserConfig& conf, A32::VAddr vaddr, T value) -> u32 {
return conf.global_monitor->DoExclusiveOperation<T>(conf.processor_id, vaddr, [&](T expected) -> bool {
return (conf.callbacks->*callback)(vaddr, value, expected);
}) ? 0 : 1;
return conf.global_monitor->DoExclusiveOperation<T>(conf.processor_id, vaddr,
[&](T expected) -> bool {
return (conf.callbacks->*callback)(vaddr, value, expected);
})
? 0
: 1;
};
void* target = code.xptr<void*>();
@@ -176,14 +179,26 @@ void A32AddressSpace::EmitPrelude() {
UnprotectCodeMemory();
prelude_info.read_memory = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks);
prelude_info.wrapped_read_memory = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks);
prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u8>(code, conf);
prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u16>(code, conf);
prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u32>(code, conf);
prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u64>(code, conf);
prelude_info.write_memory = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks);
prelude_info.wrapped_write_memory = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks);
prelude_info.read_memory_8 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead8>(code, conf.callbacks);
prelude_info.read_memory_16 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead16>(code, conf.callbacks);
prelude_info.read_memory_32 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead32>(code, conf.callbacks);
prelude_info.read_memory_64 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead64>(code, conf.callbacks);
prelude_info.wrapped_read_memory_8 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead8>(code, conf.callbacks);
prelude_info.wrapped_read_memory_16 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead16>(code, conf.callbacks);
prelude_info.wrapped_read_memory_32 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead32>(code, conf.callbacks);
prelude_info.wrapped_read_memory_64 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead64>(code, conf.callbacks);
prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead8, u8>(code, conf);
prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead16, u16>(code, conf);
prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead32, u32>(code, conf);
prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead64, u64>(code, conf);
prelude_info.write_memory_8 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite8>(code, conf.callbacks);
prelude_info.write_memory_16 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite16>(code, conf.callbacks);
prelude_info.write_memory_32 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite32>(code, conf.callbacks);
prelude_info.write_memory_64 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite64>(code, conf.callbacks);
prelude_info.wrapped_write_memory_8 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite8>(code, conf.callbacks);
prelude_info.wrapped_write_memory_16 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite16>(code, conf.callbacks);
prelude_info.wrapped_write_memory_32 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite32>(code, conf.callbacks);
prelude_info.wrapped_write_memory_64 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite64>(code, conf.callbacks);
prelude_info.exclusive_write_memory_8 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive8, u8>(code, conf);
prelude_info.exclusive_write_memory_16 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive16, u16>(code, conf);
prelude_info.exclusive_write_memory_32 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive32, u32>(code, conf);
@@ -23,7 +23,7 @@
namespace Dynarmic::Backend::Arm64 {
template<auto mfp, typename T>
static void* EmitCallTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize = 0) {
static void* EmitCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
using namespace oaknut::util;
const auto info = Devirtualize<mfp>(this_);
@@ -32,9 +32,6 @@ static void* EmitCallTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bi
void* target = code.xptr<void*>();
code.LDR(X0, l_this);
if (bitsize) {
code.MOV(X1, bitsize);
}
code.LDR(Xscratch0, l_addr);
code.BR(Xscratch0);
@@ -48,7 +45,7 @@ static void* EmitCallTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bi
}
template<auto mfp, typename T>
static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
using namespace oaknut::util;
const auto info = Devirtualize<mfp>(this_);
@@ -61,7 +58,6 @@ static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_
ABI_PushRegisters(code, save_regs, 0);
code.LDR(X0, l_this);
code.MOV(X1, Xscratch0);
code.MOV(X2, bitsize);
code.LDR(Xscratch0, l_addr);
code.BLR(Xscratch0);
code.MOV(Xscratch0, X0);
@@ -85,7 +81,7 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const
auto fn = [](const A64::UserConfig& conf, A64::VAddr vaddr) -> T {
return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T {
return (conf.callbacks->*callback)(vaddr, sizeof(T));
return (conf.callbacks->*callback)(vaddr);
});
};
@@ -104,7 +100,7 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const
}
template<auto mfp, typename T>
static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
using namespace oaknut::util;
const auto info = Devirtualize<mfp>(this_);
@@ -118,7 +114,6 @@ static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this
code.LDR(X0, l_this);
code.MOV(X1, Xscratch0);
code.MOV(X2, Xscratch1);
code.MOV(X3, bitsize);
code.LDR(Xscratch0, l_addr);
code.BLR(Xscratch0);
ABI_PopRegisters(code, save_regs, 0);
@@ -138,10 +133,14 @@ static void* EmitExclusiveWriteCallTrampoline(oaknut::CodeGenerator& code, const
using namespace oaknut::util;
oaknut::Label l_addr, l_this;
auto fn = [](const A64::UserConfig& conf, A64::VAddr vaddr, T value) -> u32 {
return conf.global_monitor->DoExclusiveOperation<T>(conf.processor_id, vaddr, [&](T expected) -> bool {
return (conf.callbacks->*callback)(vaddr, value, expected);
}) ? 0 : 1;
return conf.global_monitor->DoExclusiveOperation<T>(conf.processor_id, vaddr,
[&](T expected) -> bool {
return (conf.callbacks->*callback)(vaddr, value, expected);
})
? 0
: 1;
};
void* target = code.xptr<void*>();
@@ -347,30 +346,30 @@ void A64AddressSpace::EmitPrelude() {
UnprotectCodeMemory();
prelude_info.read_memory_8 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u8));
prelude_info.read_memory_16 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u16));
prelude_info.read_memory_32 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u32));
prelude_info.read_memory_64 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u64));
prelude_info.read_memory_8 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead8>(code, conf.callbacks);
prelude_info.read_memory_16 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead16>(code, conf.callbacks);
prelude_info.read_memory_32 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead32>(code, conf.callbacks);
prelude_info.read_memory_64 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead64>(code, conf.callbacks);
prelude_info.read_memory_128 = EmitRead128CallTrampoline(code, conf.callbacks);
prelude_info.wrapped_read_memory_8 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u8));
prelude_info.wrapped_read_memory_16 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u16));
prelude_info.wrapped_read_memory_32 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u32));
prelude_info.wrapped_read_memory_64 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u64));
prelude_info.wrapped_read_memory_8 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead8>(code, conf.callbacks);
prelude_info.wrapped_read_memory_16 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead16>(code, conf.callbacks);
prelude_info.wrapped_read_memory_32 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead32>(code, conf.callbacks);
prelude_info.wrapped_read_memory_64 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead64>(code, conf.callbacks);
prelude_info.wrapped_read_memory_128 = EmitWrappedRead128CallTrampoline(code, conf.callbacks);
prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u8>(code, conf);
prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u16>(code, conf);
prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u32>(code, conf);
prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u64>(code, conf);
prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead8, u8>(code, conf);
prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead16, u16>(code, conf);
prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead32, u32>(code, conf);
prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead64, u64>(code, conf);
prelude_info.exclusive_read_memory_128 = EmitExclusiveRead128CallTrampoline(code, conf);
prelude_info.write_memory_8 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u8));
prelude_info.write_memory_16 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u16));
prelude_info.write_memory_32 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u32));
prelude_info.write_memory_64 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u64));
prelude_info.write_memory_8 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite8>(code, conf.callbacks);
prelude_info.write_memory_16 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite16>(code, conf.callbacks);
prelude_info.write_memory_32 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite32>(code, conf.callbacks);
prelude_info.write_memory_64 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite64>(code, conf.callbacks);
prelude_info.write_memory_128 = EmitWrite128CallTrampoline(code, conf.callbacks);
prelude_info.wrapped_write_memory_8 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u8));
prelude_info.wrapped_write_memory_16 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u16));
prelude_info.wrapped_write_memory_32 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u32));
prelude_info.wrapped_write_memory_64 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u64));
prelude_info.wrapped_write_memory_8 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite8>(code, conf.callbacks);
prelude_info.wrapped_write_memory_16 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite16>(code, conf.callbacks);
prelude_info.wrapped_write_memory_32 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite32>(code, conf.callbacks);
prelude_info.wrapped_write_memory_64 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite64>(code, conf.callbacks);
prelude_info.wrapped_write_memory_128 = EmitWrappedWrite128CallTrampoline(code, conf.callbacks);
prelude_info.exclusive_write_memory_8 = EmitExclusiveWriteCallTrampoline<&A64::UserCallbacks::MemoryWriteExclusive8, u8>(code, conf);
prelude_info.exclusive_write_memory_16 = EmitExclusiveWriteCallTrampoline<&A64::UserCallbacks::MemoryWriteExclusive16, u16>(code, conf);
@@ -145,7 +145,6 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
case LinkTarget::ReturnFromRunCode:
c.B(prelude_info.return_from_run_code);
break;
// { this, vaddr, size }
case LinkTarget::ReadMemory8:
c.BL(prelude_info.read_memory_8);
break;
@@ -161,7 +160,6 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
case LinkTarget::ReadMemory128:
c.BL(prelude_info.read_memory_128);
break;
// { this, vaddr, size }
case LinkTarget::WrappedReadMemory8:
c.BL(prelude_info.wrapped_read_memory_8);
break;
@@ -192,7 +190,6 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
case LinkTarget::ExclusiveReadMemory128:
c.BL(prelude_info.exclusive_read_memory_128);
break;
// { this, vaddr, value, size }
case LinkTarget::WriteMemory8:
c.BL(prelude_info.write_memory_8);
break;
@@ -31,16 +31,16 @@ using namespace Xbyak::util;
void A32EmitX64::GenFastmemFallbacks() {
const std::initializer_list<int> idxes{0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
const std::array<std::pair<size_t, ArgCallback>, 4> read_callbacks{{
{8, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)},
{16, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)},
{32, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)},
{64, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)},
{8, Devirtualize<&A32::UserCallbacks::MemoryRead8>(conf.callbacks)},
{16, Devirtualize<&A32::UserCallbacks::MemoryRead16>(conf.callbacks)},
{32, Devirtualize<&A32::UserCallbacks::MemoryRead32>(conf.callbacks)},
{64, Devirtualize<&A32::UserCallbacks::MemoryRead64>(conf.callbacks)},
}};
const std::array<std::pair<size_t, ArgCallback>, 4> write_callbacks{{
{8, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)},
{16, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)},
{32, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)},
{64, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)},
{8, Devirtualize<&A32::UserCallbacks::MemoryWrite8>(conf.callbacks)},
{16, Devirtualize<&A32::UserCallbacks::MemoryWrite16>(conf.callbacks)},
{32, Devirtualize<&A32::UserCallbacks::MemoryWrite32>(conf.callbacks)},
{64, Devirtualize<&A32::UserCallbacks::MemoryWrite64>(conf.callbacks)},
}};
const std::array<std::pair<size_t, ArgCallback>, 4> exclusive_write_callbacks{{
{8, Devirtualize<&A32::UserCallbacks::MemoryWriteExclusive8>(conf.callbacks)},
@@ -56,12 +56,12 @@ void A32EmitX64::GenFastmemFallbacks() {
code.align();
read_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>();
ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocRegIdx(value_idx));
// params = { this, vaddr, size }
if (vaddr_idx != code.ABI_PARAM2.getIdx()) {
code.mov(code.ABI_PARAM2, Xbyak::Reg64{vaddr_idx});
}
code.mov(code.ABI_PARAM3, bitsize / CHAR_BIT);
if (ordered) code.mfence();
if (ordered) {
code.mfence();
}
callback.EmitCall(code);
if (value_idx != code.ABI_RETURN.getIdx()) {
code.mov(Xbyak::Reg64{value_idx}, code.ABI_RETURN);
@@ -76,7 +76,6 @@ void A32EmitX64::GenFastmemFallbacks() {
code.align();
write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>();
ABI_PushCallerSaveRegistersAndAdjustStack(code);
// params = { this, vaddr, value, size }
if (vaddr_idx == code.ABI_PARAM3.getIdx() && value_idx == code.ABI_PARAM2.getIdx()) {
code.xchg(code.ABI_PARAM2, code.ABI_PARAM3);
} else if (vaddr_idx == code.ABI_PARAM3.getIdx()) {
@@ -93,9 +92,10 @@ void A32EmitX64::GenFastmemFallbacks() {
}
}
code.ZeroExtendFrom(bitsize, code.ABI_PARAM3);
code.mov(code.ABI_PARAM4, bitsize / CHAR_BIT);
callback.EmitCall(code);
if (ordered) code.mfence();
if (ordered) {
code.mfence();
}
ABI_PopCallerSaveRegistersAndAdjustStack(code);
code.ret();
PerfMapRegister(write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)], code.getCurr(), fmt::format("a32_write_fallback_{}", bitsize));
@@ -138,35 +138,35 @@ void A32EmitX64::GenFastmemFallbacks() {
#undef Axx
void A32EmitX64::EmitA32ReadMemory8(A32EmitContext& ctx, IR::Inst* inst) {
EmitMemoryRead<8, &A32::UserCallbacks::MemoryRead>(ctx, inst);
EmitMemoryRead<8, &A32::UserCallbacks::MemoryRead8>(ctx, inst);
}
void A32EmitX64::EmitA32ReadMemory16(A32EmitContext& ctx, IR::Inst* inst) {
EmitMemoryRead<16, &A32::UserCallbacks::MemoryRead>(ctx, inst);
EmitMemoryRead<16, &A32::UserCallbacks::MemoryRead16>(ctx, inst);
}
void A32EmitX64::EmitA32ReadMemory32(A32EmitContext& ctx, IR::Inst* inst) {
EmitMemoryRead<32, &A32::UserCallbacks::MemoryRead>(ctx, inst);
EmitMemoryRead<32, &A32::UserCallbacks::MemoryRead32>(ctx, inst);
}
void A32EmitX64::EmitA32ReadMemory64(A32EmitContext& ctx, IR::Inst* inst) {
EmitMemoryRead<64, &A32::UserCallbacks::MemoryRead>(ctx, inst);
EmitMemoryRead<64, &A32::UserCallbacks::MemoryRead64>(ctx, inst);
}
void A32EmitX64::EmitA32WriteMemory8(A32EmitContext& ctx, IR::Inst* inst) {
EmitMemoryWrite<8, &A32::UserCallbacks::MemoryWrite>(ctx, inst);
EmitMemoryWrite<8, &A32::UserCallbacks::MemoryWrite8>(ctx, inst);
}
void A32EmitX64::EmitA32WriteMemory16(A32EmitContext& ctx, IR::Inst* inst) {
EmitMemoryWrite<16, &A32::UserCallbacks::MemoryWrite>(ctx, inst);
EmitMemoryWrite<16, &A32::UserCallbacks::MemoryWrite16>(ctx, inst);
}
void A32EmitX64::EmitA32WriteMemory32(A32EmitContext& ctx, IR::Inst* inst) {
EmitMemoryWrite<32, &A32::UserCallbacks::MemoryWrite>(ctx, inst);
EmitMemoryWrite<32, &A32::UserCallbacks::MemoryWrite32>(ctx, inst);
}
void A32EmitX64::EmitA32WriteMemory64(A32EmitContext& ctx, IR::Inst* inst) {
EmitMemoryWrite<64, &A32::UserCallbacks::MemoryWrite>(ctx, inst);
EmitMemoryWrite<64, &A32::UserCallbacks::MemoryWrite64>(ctx, inst);
}
void A32EmitX64::EmitA32ClearExclusive(A32EmitContext&, IR::Inst*) {
@@ -175,33 +175,33 @@ void A32EmitX64::EmitA32ClearExclusive(A32EmitContext&, IR::Inst*) {
void A32EmitX64::EmitA32ExclusiveReadMemory8(A32EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<8, &A32::UserCallbacks::MemoryRead>(ctx, inst);
EmitExclusiveReadMemoryInline<8, &A32::UserCallbacks::MemoryRead8>(ctx, inst);
} else {
EmitExclusiveReadMemory<8, &A32::UserCallbacks::MemoryRead>(ctx, inst);
EmitExclusiveReadMemory<8, &A32::UserCallbacks::MemoryRead8>(ctx, inst);
}
}
void A32EmitX64::EmitA32ExclusiveReadMemory16(A32EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<16, &A32::UserCallbacks::MemoryRead>(ctx, inst);
EmitExclusiveReadMemoryInline<16, &A32::UserCallbacks::MemoryRead16>(ctx, inst);
} else {
EmitExclusiveReadMemory<16, &A32::UserCallbacks::MemoryRead>(ctx, inst);
EmitExclusiveReadMemory<16, &A32::UserCallbacks::MemoryRead16>(ctx, inst);
}
}
void A32EmitX64::EmitA32ExclusiveReadMemory32(A32EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<32, &A32::UserCallbacks::MemoryRead>(ctx, inst);
EmitExclusiveReadMemoryInline<32, &A32::UserCallbacks::MemoryRead32>(ctx, inst);
} else {
EmitExclusiveReadMemory<32, &A32::UserCallbacks::MemoryRead>(ctx, inst);
EmitExclusiveReadMemory<32, &A32::UserCallbacks::MemoryRead32>(ctx, inst);
}
}
void A32EmitX64::EmitA32ExclusiveReadMemory64(A32EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<64, &A32::UserCallbacks::MemoryRead>(ctx, inst);
EmitExclusiveReadMemoryInline<64, &A32::UserCallbacks::MemoryRead64>(ctx, inst);
} else {
EmitExclusiveReadMemory<64, &A32::UserCallbacks::MemoryRead>(ctx, inst);
EmitExclusiveReadMemory<64, &A32::UserCallbacks::MemoryRead64>(ctx, inst);
}
}
@@ -115,16 +115,16 @@ void A64EmitX64::GenMemory128Accessors() {
void A64EmitX64::GenFastmemFallbacks() {
const std::initializer_list<int> idxes{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
const std::array<std::pair<size_t, ArgCallback>, 4> read_callbacks{{
{8, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)},
{16, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)},
{32, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)},
{64, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)},
{8, Devirtualize<&A64::UserCallbacks::MemoryRead8>(conf.callbacks)},
{16, Devirtualize<&A64::UserCallbacks::MemoryRead16>(conf.callbacks)},
{32, Devirtualize<&A64::UserCallbacks::MemoryRead32>(conf.callbacks)},
{64, Devirtualize<&A64::UserCallbacks::MemoryRead64>(conf.callbacks)},
}};
const std::array<std::pair<size_t, ArgCallback>, 4> write_callbacks{{
{8, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)},
{16, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)},
{32, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)},
{64, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)},
{8, Devirtualize<&A64::UserCallbacks::MemoryWrite8>(conf.callbacks)},
{16, Devirtualize<&A64::UserCallbacks::MemoryWrite16>(conf.callbacks)},
{32, Devirtualize<&A64::UserCallbacks::MemoryWrite32>(conf.callbacks)},
{64, Devirtualize<&A64::UserCallbacks::MemoryWrite64>(conf.callbacks)},
}};
const std::array<std::pair<size_t, ArgCallback>, 4> exclusive_write_callbacks{{
{8, Devirtualize<&A64::UserCallbacks::MemoryWriteExclusive8>(conf.callbacks)},
@@ -204,12 +204,12 @@ void A64EmitX64::GenFastmemFallbacks() {
code.align();
read_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>();
ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocRegIdx(value_idx));
// params = { this, vaddr, size }
if (vaddr_idx != code.ABI_PARAM2.getIdx()) {
code.mov(code.ABI_PARAM2, Xbyak::Reg64{vaddr_idx});
}
code.mov(code.ABI_PARAM3, bitsize / CHAR_BIT);
if (ordered) code.mfence();
if (ordered) {
code.mfence();
}
callback.EmitCall(code);
if (value_idx != code.ABI_RETURN.getIdx()) {
code.mov(Xbyak::Reg64{value_idx}, code.ABI_RETURN);
@@ -224,7 +224,6 @@ void A64EmitX64::GenFastmemFallbacks() {
code.align();
write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>();
ABI_PushCallerSaveRegistersAndAdjustStack(code);
// params = { this, vaddr, value, size }
if (vaddr_idx == code.ABI_PARAM3.getIdx() && value_idx == code.ABI_PARAM2.getIdx()) {
code.xchg(code.ABI_PARAM2, code.ABI_PARAM3);
} else if (vaddr_idx == code.ABI_PARAM3.getIdx()) {
@@ -241,9 +240,10 @@ void A64EmitX64::GenFastmemFallbacks() {
}
}
code.ZeroExtendFrom(bitsize, code.ABI_PARAM3);
code.mov(code.ABI_PARAM4, bitsize / CHAR_BIT);
callback.EmitCall(code);
if (ordered) code.mfence();
if (ordered) {
code.mfence();
}
ABI_PopCallerSaveRegistersAndAdjustStack(code);
code.ret();
PerfMapRegister(write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)], code.getCurr(), fmt::format("a64_write_fallback_{}", bitsize));
@@ -286,19 +286,19 @@ void A64EmitX64::GenFastmemFallbacks() {
#undef Axx
void A64EmitX64::EmitA64ReadMemory8(A64EmitContext& ctx, IR::Inst* inst) {
EmitMemoryRead<8, &A64::UserCallbacks::MemoryRead>(ctx, inst);
EmitMemoryRead<8, &A64::UserCallbacks::MemoryRead8>(ctx, inst);
}
void A64EmitX64::EmitA64ReadMemory16(A64EmitContext& ctx, IR::Inst* inst) {
EmitMemoryRead<16, &A64::UserCallbacks::MemoryRead>(ctx, inst);
EmitMemoryRead<16, &A64::UserCallbacks::MemoryRead16>(ctx, inst);
}
void A64EmitX64::EmitA64ReadMemory32(A64EmitContext& ctx, IR::Inst* inst) {
EmitMemoryRead<32, &A64::UserCallbacks::MemoryRead>(ctx, inst);
EmitMemoryRead<32, &A64::UserCallbacks::MemoryRead32>(ctx, inst);
}
void A64EmitX64::EmitA64ReadMemory64(A64EmitContext& ctx, IR::Inst* inst) {
EmitMemoryRead<64, &A64::UserCallbacks::MemoryRead>(ctx, inst);
EmitMemoryRead<64, &A64::UserCallbacks::MemoryRead64>(ctx, inst);
}
void A64EmitX64::EmitA64ReadMemory128(A64EmitContext& ctx, IR::Inst* inst) {
@@ -306,23 +306,23 @@ void A64EmitX64::EmitA64ReadMemory128(A64EmitContext& ctx, IR::Inst* inst) {
}
void A64EmitX64::EmitA64WriteMemory8(A64EmitContext& ctx, IR::Inst* inst) {
EmitMemoryWrite<8, &A64::UserCallbacks::MemoryWrite>(ctx, inst);
EmitMemoryWrite<8, &A64::UserCallbacks::MemoryWrite8>(ctx, inst);
}
void A64EmitX64::EmitA64WriteMemory16(A64EmitContext& ctx, IR::Inst* inst) {
EmitMemoryWrite<16, &A64::UserCallbacks::MemoryWrite>(ctx, inst);
EmitMemoryWrite<16, &A64::UserCallbacks::MemoryWrite16>(ctx, inst);
}
void A64EmitX64::EmitA64WriteMemory32(A64EmitContext& ctx, IR::Inst* inst) {
EmitMemoryWrite<32, &A64::UserCallbacks::MemoryWrite>(ctx, inst);
EmitMemoryWrite<32, &A64::UserCallbacks::MemoryWrite32>(ctx, inst);
}
void A64EmitX64::EmitA64WriteMemory64(A64EmitContext& ctx, IR::Inst* inst) {
EmitMemoryWrite<64, &A64::UserCallbacks::MemoryWrite>(ctx, inst);
EmitMemoryWrite<64, &A64::UserCallbacks::MemoryWrite64>(ctx, inst);
}
void A64EmitX64::EmitA64WriteMemory128(A64EmitContext& ctx, IR::Inst* inst) {
EmitMemoryWrite<128, &A64::UserCallbacks::MemoryWrite>(ctx, inst);
EmitMemoryWrite<128, &A64::UserCallbacks::MemoryWrite64>(ctx, inst);
}
void A64EmitX64::EmitA64ClearExclusive(A64EmitContext&, IR::Inst*) {
@@ -331,33 +331,33 @@ void A64EmitX64::EmitA64ClearExclusive(A64EmitContext&, IR::Inst*) {
void A64EmitX64::EmitA64ExclusiveReadMemory8(A64EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<8, &A64::UserCallbacks::MemoryRead>(ctx, inst);
EmitExclusiveReadMemoryInline<8, &A64::UserCallbacks::MemoryRead8>(ctx, inst);
} else {
EmitExclusiveReadMemory<8, &A64::UserCallbacks::MemoryRead>(ctx, inst);
EmitExclusiveReadMemory<8, &A64::UserCallbacks::MemoryRead8>(ctx, inst);
}
}
void A64EmitX64::EmitA64ExclusiveReadMemory16(A64EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<16, &A64::UserCallbacks::MemoryRead>(ctx, inst);
EmitExclusiveReadMemoryInline<16, &A64::UserCallbacks::MemoryRead16>(ctx, inst);
} else {
EmitExclusiveReadMemory<16, &A64::UserCallbacks::MemoryRead>(ctx, inst);
EmitExclusiveReadMemory<16, &A64::UserCallbacks::MemoryRead16>(ctx, inst);
}
}
void A64EmitX64::EmitA64ExclusiveReadMemory32(A64EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<32, &A64::UserCallbacks::MemoryRead>(ctx, inst);
EmitExclusiveReadMemoryInline<32, &A64::UserCallbacks::MemoryRead32>(ctx, inst);
} else {
EmitExclusiveReadMemory<32, &A64::UserCallbacks::MemoryRead>(ctx, inst);
EmitExclusiveReadMemory<32, &A64::UserCallbacks::MemoryRead32>(ctx, inst);
}
}
void A64EmitX64::EmitA64ExclusiveReadMemory64(A64EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<64, &A64::UserCallbacks::MemoryRead>(ctx, inst);
EmitExclusiveReadMemoryInline<64, &A64::UserCallbacks::MemoryRead64>(ctx, inst);
} else {
EmitExclusiveReadMemory<64, &A64::UserCallbacks::MemoryRead>(ctx, inst);
EmitExclusiveReadMemory<64, &A64::UserCallbacks::MemoryRead64>(ctx, inst);
}
}
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
* Copyright (c) 2022 MerryMage
* SPDX-License-Identifier: 0BSD
@@ -59,13 +56,16 @@ void AxxEmitX64::EmitMemoryRead(AxxEmitContext& ctx, IR::Inst* inst) {
// Neither fastmem nor page table: Use callbacks
if constexpr (bitsize == 128) {
ctx.reg_alloc.HostCall(code, nullptr, {}, args[1]);
if (ordered) code.mfence();
if (ordered) {
code.mfence();
}
code.CallFunction(memory_read_128);
ctx.reg_alloc.DefineValue(code, inst, xmm1);
} else {
ctx.reg_alloc.HostCall(code, inst, {}, args[1]);
code.mov(code.ABI_PARAM3.cvt32(), bitsize / CHAR_BIT);
if (ordered) code.mfence();
if (ordered) {
code.mfence();
}
Devirtualize<callback>(conf.callbacks).EmitCall(code);
code.ZeroExtendFrom(bitsize, code.ABI_RETURN);
}
@@ -148,12 +148,12 @@ void AxxEmitX64::EmitMemoryWrite(AxxEmitContext& ctx, IR::Inst* inst) {
ctx.reg_alloc.HostCall(code, nullptr);
code.CallFunction(memory_write_128);
} else {
// { this, vaddr, value, size }
ctx.reg_alloc.HostCall(code, nullptr, {}, args[1], args[2]);
code.mov(code.ABI_PARAM4.cvt32(), bitsize / CHAR_BIT);
Devirtualize<callback>(conf.callbacks).EmitCall(code);
}
if (ordered) code.mfence();
if (ordered) {
code.mfence();
}
EmitCheckMemoryAbort(ctx, inst);
return;
}
@@ -230,11 +230,12 @@ void AxxEmitX64::EmitExclusiveReadMemory(AxxEmitContext& ctx, IR::Inst* inst) {
if (ordered) {
code.mfence();
}
code.CallLambda([](AxxUserConfig& conf, Axx::VAddr vaddr) -> T {
return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T {
return (conf.callbacks->*callback)(vaddr, bitsize / CHAR_BIT);
code.CallLambda(
[](AxxUserConfig& conf, Axx::VAddr vaddr) -> T {
return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T {
return (conf.callbacks->*callback)(vaddr);
});
});
});
code.ZeroExtendFrom(bitsize, code.ABI_RETURN);
} else {
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(code);
@@ -249,11 +250,12 @@ void AxxEmitX64::EmitExclusiveReadMemory(AxxEmitContext& ctx, IR::Inst* inst) {
if (ordered) {
code.mfence();
}
code.CallLambda([](AxxUserConfig& conf, Axx::VAddr vaddr, Vector& ret) {
ret = conf.global_monitor->ReadAndMark<Vector>(conf.processor_id, vaddr, [&]() -> Vector {
return (conf.callbacks->*callback)(vaddr);
code.CallLambda(
[](AxxUserConfig& conf, Axx::VAddr vaddr, Vector& ret) {
ret = conf.global_monitor->ReadAndMark<Vector>(conf.processor_id, vaddr, [&]() -> Vector {
return (conf.callbacks->*callback)(vaddr);
});
});
});
code.movups(result, xword[rsp + ABI_SHADOW_SPACE]);
ctx.reg_alloc.ReleaseStackSpace(code, 16 + ABI_SHADOW_SPACE);
@@ -90,7 +90,7 @@ template<>
code.shl(tmp, int(ctx.conf.page_table_log2_stride));
code.mov(page, qword[r14 + tmp.cvt64()]);
} else {
code.mov(page, qword[r14 + tmp.cvt64() * int(1 << ctx.conf.page_table_log2_stride)]);
code.mov(page, qword[r14 + tmp.cvt64() * int(ctx.conf.page_table_log2_stride)]);
}
// check for marked bit, use as unmapped if marked
@@ -161,12 +161,8 @@ template<>
code.jnz(abort, code.T_NEAR);
}
if (ctx.conf.page_table_log2_stride > 3) {
code.shl(tmp, int(ctx.conf.page_table_log2_stride));
code.mov(page, qword[r14 + tmp.cvt64()]);
} else {
code.mov(page, qword[r14 + tmp.cvt64() * int(1 << ctx.conf.page_table_log2_stride)]);
}
code.shl(tmp, int(ctx.conf.page_table_log2_stride));
code.mov(page, qword[r14 + tmp]);
// check for marked bit, use as unmapped if marked
if (ctx.conf.page_table_marked_bit) {
@@ -182,7 +178,6 @@ template<>
code.mov(tmp, ctx.conf.page_table_pointer_mask);
code.and_(page, tmp);
}
// check for sign bit, apply sign extension as needed
if (ctx.conf.page_table_sign_extension) {
code.shl(page, *ctx.conf.page_table_sign_extension);
code.sar(page, *ctx.conf.page_table_sign_extension);
@@ -66,9 +66,7 @@ struct UserCallbacks : public TranslateCallbacks {
// All reads through this callback are 4-byte aligned.
// Memory must be interpreted as little endian.
std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) override {
return std::uint32_t(MemoryRead(vaddr, sizeof(std::uint32_t)));
}
std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) override { return MemoryRead32(vaddr); }
// This function is called before the instruction at pc is read.
// IR code can be emitted by the callee prior to instruction handling.
@@ -82,10 +80,16 @@ struct UserCallbacks : public TranslateCallbacks {
// Reads through these callbacks may not be aligned.
// Memory must be interpreted as if ENDIANSTATE == 0, endianness will be corrected by the JIT.
virtual std::uint64_t MemoryRead(VAddr vaddr, std::size_t size) = 0;
virtual std::uint8_t MemoryRead8(VAddr vaddr) = 0;
virtual std::uint16_t MemoryRead16(VAddr vaddr) = 0;
virtual std::uint32_t MemoryRead32(VAddr vaddr) = 0;
virtual std::uint64_t MemoryRead64(VAddr vaddr) = 0;
// Writes through these callbacks may not be aligned.
virtual void MemoryWrite(VAddr vaddr, std::uint64_t value, std::size_t size) = 0;
virtual void MemoryWrite8(VAddr vaddr, std::uint8_t value) = 0;
virtual void MemoryWrite16(VAddr vaddr, std::uint16_t value) = 0;
virtual void MemoryWrite32(VAddr vaddr, std::uint32_t value) = 0;
virtual void MemoryWrite64(VAddr vaddr, std::uint64_t value) = 0;
// Writes through these callbacks may not be aligned.
virtual bool MemoryWriteExclusive8(VAddr /*vaddr*/, std::uint8_t /*value*/, std::uint8_t /*expected*/) { return false; }
@@ -89,16 +89,20 @@ struct UserCallbacks {
// All reads through this callback are 4-byte aligned.
// Memory must be interpreted as little endian.
virtual std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) {
return std::uint32_t(MemoryRead(vaddr, sizeof(std::uint32_t)));
}
virtual std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) { return MemoryRead32(vaddr); }
// Reads through these callbacks may not be aligned.
virtual std::uint64_t MemoryRead(VAddr vaddr, std::size_t size) = 0;
virtual std::uint8_t MemoryRead8(VAddr vaddr) = 0;
virtual std::uint16_t MemoryRead16(VAddr vaddr) = 0;
virtual std::uint32_t MemoryRead32(VAddr vaddr) = 0;
virtual std::uint64_t MemoryRead64(VAddr vaddr) = 0;
virtual Vector MemoryRead128(VAddr vaddr) = 0;
// Writes through these callbacks may not be aligned.
virtual void MemoryWrite(VAddr vaddr, std::uint64_t value, std::size_t size) = 0;
virtual void MemoryWrite8(VAddr vaddr, std::uint8_t value) = 0;
virtual void MemoryWrite16(VAddr vaddr, std::uint16_t value) = 0;
virtual void MemoryWrite32(VAddr vaddr, std::uint32_t value) = 0;
virtual void MemoryWrite64(VAddr vaddr, std::uint64_t value) = 0;
virtual void MemoryWrite128(VAddr vaddr, Vector value) = 0;
// Writes through these callbacks may not be aligned.
+4 -4
View File
@@ -40,7 +40,7 @@ static void ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) {
if (inst.AreAllArgsImmediates()) {
const u32 vaddr = inst.GetArg(1).GetU32();
if (cb->IsReadOnlyMemory(vaddr)) {
const u8 value_from_memory = u8(cb->MemoryRead(vaddr, sizeof(u8)));
const u8 value_from_memory = cb->MemoryRead8(vaddr);
inst.ReplaceUsesWith(IR::Value{value_from_memory});
}
}
@@ -51,7 +51,7 @@ static void ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) {
if (inst.AreAllArgsImmediates()) {
const u32 vaddr = inst.GetArg(1).GetU32();
if (cb->IsReadOnlyMemory(vaddr)) {
const u16 value_from_memory = u16(cb->MemoryRead(vaddr, sizeof(u16)));
const u16 value_from_memory = cb->MemoryRead16(vaddr);
inst.ReplaceUsesWith(IR::Value{value_from_memory});
}
}
@@ -62,7 +62,7 @@ static void ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) {
if (inst.AreAllArgsImmediates()) {
const u32 vaddr = inst.GetArg(1).GetU32();
if (cb->IsReadOnlyMemory(vaddr)) {
const u32 value_from_memory = u32(cb->MemoryRead(vaddr, sizeof(u32)));
const u32 value_from_memory = cb->MemoryRead32(vaddr);
inst.ReplaceUsesWith(IR::Value{value_from_memory});
}
}
@@ -73,7 +73,7 @@ static void ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) {
if (inst.AreAllArgsImmediates()) {
const u32 vaddr = inst.GetArg(1).GetU32();
if (cb->IsReadOnlyMemory(vaddr)) {
const u64 value_from_memory = u64(cb->MemoryRead(vaddr, sizeof(u64)));
const u64 value_from_memory = cb->MemoryRead64(vaddr);
inst.ReplaceUsesWith(IR::Value{value_from_memory});
}
}
+1 -1
View File
@@ -503,7 +503,7 @@ TEST_CASE("Fuzz Thumb32 instructions set", "[JitX64][Thumb][Thumb32]") {
}
}
TEST_CASE("Verify fix for off by one error in MemoryRead<32> worked", "[Thumb][Thumb16]") {
TEST_CASE("Verify fix for off by one error in MemoryRead32 worked", "[Thumb][Thumb16]") {
ThumbTestEnv test_env;
// Prepare test subjects
@@ -553,9 +553,9 @@ TEST_CASE("arm: Memory access (fastmem)", "[arm][A32]") {
memset(backing_memory, 0, memory_size);
memcpy(backing_memory + 0x100, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", 57);
env.MemoryWrite(0, 0xE5904000, sizeof(u32)); // LDR R4, [R0]
env.MemoryWrite(4, 0xE5814000, sizeof(u32)); // STR R4, [R1]
env.MemoryWrite(8, 0xEAFFFFFE, sizeof(u32)); // B .
env.MemoryWrite32(0, 0xE5904000); // LDR R4, [R0]
env.MemoryWrite32(4, 0xE5814000); // STR R4, [R1]
env.MemoryWrite32(8, 0xEAFFFFFE); // B .
jit.Regs()[0] = 0x100;
jit.Regs()[1] = 0x1F0;
jit.Regs()[15] = 0; // PC = 0
+61 -66
View File
@@ -16,7 +16,6 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "dynarmic/frontend/A32/translate/translate_callbacks.h"
#include "dynarmic/interface/A32/a32.h"
template<typename InstructionType_, u32 infinite_loop_u32>
@@ -60,51 +59,42 @@ public:
return infinite_loop_u32; // B .
}
u64 MemoryRead(u32 vaddr, size_t size) override {
switch (size) {
case sizeof(u64):
return MemoryRead(vaddr, sizeof(u32))
| MemoryRead(vaddr + sizeof(u32), sizeof(u32)) << 32;
case sizeof(u32):
return MemoryRead(vaddr, sizeof(u16))
| MemoryRead(vaddr + sizeof(u16), sizeof(u16)) << 16;
case sizeof(u16):
return MemoryRead(vaddr, sizeof(u8))
| MemoryRead(vaddr + sizeof(u8), sizeof(u8)) << 8;
case sizeof(u8): {
if (IsInCodeMem(vaddr))
return reinterpret_cast<u8*>(code_mem.data())[vaddr];
if (auto iter = modified_memory.find(vaddr); iter != modified_memory.end())
return iter->second;
return u8(vaddr);
std::uint8_t MemoryRead8(u32 vaddr) override {
if (IsInCodeMem(vaddr)) {
return reinterpret_cast<u8*>(code_mem.data())[vaddr];
}
default:
std::abort();
if (auto iter = modified_memory.find(vaddr); iter != modified_memory.end()) {
return iter->second;
}
return static_cast<u8>(vaddr);
}
std::uint16_t MemoryRead16(u32 vaddr) override {
return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8;
}
std::uint32_t MemoryRead32(u32 vaddr) override {
return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16;
}
std::uint64_t MemoryRead64(u32 vaddr) override {
return u64(MemoryRead32(vaddr)) | u64(MemoryRead32(vaddr + 4)) << 32;
}
void MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) override {
switch (size) {
case sizeof(u64):
MemoryWrite(vaddr, u32(value), sizeof(u32));
MemoryWrite(vaddr + 4, u32(value >> 32), sizeof(u32));
break;
case sizeof(u32):
MemoryWrite(vaddr, u16(value), sizeof(u16));
MemoryWrite(vaddr + 2, u16(value >> 16), sizeof(u16));
break;
case sizeof(u16):
MemoryWrite(vaddr, u8(value), sizeof(u8));
MemoryWrite(vaddr + 1, u8(value >> 8), sizeof(u8));
break;
case sizeof(u8):
if (vaddr < code_mem.size() * sizeof(u32))
code_mem_modified_by_guest = true;
modified_memory[vaddr] = value;
break;
default:
std::abort();
void MemoryWrite8(u32 vaddr, std::uint8_t value) override {
if (vaddr < code_mem.size() * sizeof(u32)) {
code_mem_modified_by_guest = true;
}
modified_memory[vaddr] = value;
}
void MemoryWrite16(u32 vaddr, std::uint16_t value) override {
MemoryWrite8(vaddr, static_cast<u8>(value));
MemoryWrite8(vaddr + 1, static_cast<u8>(value >> 8));
}
void MemoryWrite32(u32 vaddr, std::uint32_t value) override {
MemoryWrite16(vaddr, static_cast<u16>(value));
MemoryWrite16(vaddr + 2, static_cast<u16>(value >> 16));
}
void MemoryWrite64(u32 vaddr, std::uint64_t value) override {
MemoryWrite32(vaddr, static_cast<u32>(value));
MemoryWrite32(vaddr + 4, static_cast<u32>(value >> 32));
}
void CallSVC(std::uint32_t swi) override {
@@ -153,41 +143,46 @@ public:
return read<std::uint32_t>(vaddr);
}
u64 MemoryRead(u32 vaddr, size_t size) override {
switch (size) {
case sizeof(u64): return read<u64>(vaddr);
case sizeof(u32): return read<u32>(vaddr);
case sizeof(u16): return read<u16>(vaddr);
case sizeof(u8): return read<u8>(vaddr);
default:
std::abort();
}
std::uint8_t MemoryRead8(std::uint32_t vaddr) override {
return read<std::uint8_t>(vaddr);
}
std::uint16_t MemoryRead16(std::uint32_t vaddr) override {
return read<std::uint16_t>(vaddr);
}
std::uint32_t MemoryRead32(std::uint32_t vaddr) override {
return read<std::uint32_t>(vaddr);
}
std::uint64_t MemoryRead64(std::uint32_t vaddr) override {
return read<std::uint64_t>(vaddr);
}
void MemoryWrite(Dynarmic::A32::VAddr vaddr, std::uint64_t value, size_t size) override {
switch (size) {
case sizeof(u64): return write<u64>(vaddr, u64(value));
case sizeof(u32): return write<u32>(vaddr, u32(value));
case sizeof(u16): return write<u16>(vaddr, u16(value));
case sizeof(u8): return write<u8>(vaddr, u8(value));
default: std::abort();
}
void MemoryWrite8(std::uint32_t vaddr, std::uint8_t value) override {
write(vaddr, value);
}
void MemoryWrite16(std::uint32_t vaddr, std::uint16_t value) override {
write(vaddr, value);
}
void MemoryWrite32(std::uint32_t vaddr, std::uint32_t value) override {
write(vaddr, value);
}
void MemoryWrite64(std::uint32_t vaddr, std::uint64_t value) override {
write(vaddr, value);
}
bool MemoryWriteExclusive8(Dynarmic::A32::VAddr vaddr, std::uint8_t value, [[maybe_unused]] std::uint8_t expected) override {
MemoryWrite(vaddr, value, sizeof(u8));
bool MemoryWriteExclusive8(std::uint32_t vaddr, std::uint8_t value, [[maybe_unused]] std::uint8_t expected) override {
MemoryWrite8(vaddr, value);
return true;
}
bool MemoryWriteExclusive16(Dynarmic::A32::VAddr vaddr, std::uint16_t value, [[maybe_unused]] std::uint16_t expected) override {
MemoryWrite(vaddr, value, sizeof(u16));
bool MemoryWriteExclusive16(std::uint32_t vaddr, std::uint16_t value, [[maybe_unused]] std::uint16_t expected) override {
MemoryWrite16(vaddr, value);
return true;
}
bool MemoryWriteExclusive32(Dynarmic::A32::VAddr vaddr, std::uint32_t value, [[maybe_unused]] std::uint32_t expected) override {
MemoryWrite(vaddr, value, sizeof(u32));
bool MemoryWriteExclusive32(std::uint32_t vaddr, std::uint32_t value, [[maybe_unused]] std::uint32_t expected) override {
MemoryWrite32(vaddr, value);
return true;
}
bool MemoryWriteExclusive64(Dynarmic::A32::VAddr vaddr, std::uint64_t value, [[maybe_unused]] std::uint64_t expected) override {
MemoryWrite(vaddr, value, sizeof(u64));
bool MemoryWriteExclusive64(std::uint32_t vaddr, std::uint64_t value, [[maybe_unused]] std::uint64_t expected) override {
MemoryWrite64(vaddr, value);
return true;
}
File diff suppressed because one or more lines are too long
+37 -44
View File
@@ -25,55 +25,48 @@ public:
u64 ticks_left = 0;
::Common::unordered_map<u64, u8> memory{};
u64 MemoryRead(u64 vaddr, size_t size) override {
switch (size) {
case sizeof(u64):
return MemoryRead(vaddr, sizeof(u32))
| MemoryRead(vaddr + sizeof(u32), sizeof(u32)) << 32;
case sizeof(u32):
return MemoryRead(vaddr, sizeof(u16))
| MemoryRead(vaddr + sizeof(u16), sizeof(u16)) << 16;
case sizeof(u16):
return MemoryRead(vaddr, sizeof(u8))
| MemoryRead(vaddr + sizeof(u8), sizeof(u8)) << 8;
case sizeof(u8):
return memory[vaddr];
default:
std::abort();
}
u8 MemoryRead8(u64 vaddr) override {
return memory[vaddr];
}
u16 MemoryRead16(u64 vaddr) override {
return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8;
}
u32 MemoryRead32(u64 vaddr) override {
return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16;
}
u64 MemoryRead64(u64 vaddr) override {
return u64(MemoryRead32(vaddr)) | u64(MemoryRead32(vaddr + 4)) << 32;
}
std::array<u64, 2> MemoryRead128(u64 vaddr) override {
return {
MemoryRead(vaddr, sizeof(u64)),
MemoryRead(vaddr + sizeof(u64), sizeof(u64))
};
return {MemoryRead64(vaddr), MemoryRead64(vaddr + 8)};
}
void MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, size_t size) override {
switch (size) {
case sizeof(u64):
MemoryWrite(vaddr, u32(value), sizeof(u32));
MemoryWrite(vaddr + 4, u32(value >> 32), sizeof(u32));
break;
case sizeof(u32):
MemoryWrite(vaddr, u16(value), sizeof(u16));
MemoryWrite(vaddr + 2, u16(value >> 16), sizeof(u16));
break;
case sizeof(u16):
MemoryWrite(vaddr, u8(value), sizeof(u8));
MemoryWrite(vaddr + 1, u8(value >> 8), sizeof(u8));
break;
case sizeof(u8):
memory[vaddr] = value;
break;
default:
std::abort();
}
void MemoryWrite8(u64 vaddr, u8 value) override {
memory[vaddr] = value;
}
void MemoryWrite16(u64 vaddr, u16 value) override {
MemoryWrite8(vaddr, u8(value));
MemoryWrite8(vaddr + 1, u8(value >> 8));
}
void MemoryWrite32(u64 vaddr, u32 value) override {
MemoryWrite16(vaddr, u16(value));
MemoryWrite16(vaddr + 2, u16(value >> 16));
}
void MemoryWrite64(u64 vaddr, u64 value) override {
MemoryWrite32(vaddr, u32(value));
MemoryWrite32(vaddr + 4, u32(value >> 32));
}
void MemoryWrite128(u64 vaddr, std::array<u64, 2> value) override {
MemoryWrite(vaddr, value[0], sizeof(u64));
MemoryWrite(vaddr + 8, value[1], sizeof(u64));
MemoryWrite64(vaddr, value[0]);
MemoryWrite64(vaddr + 8, value[1]);
}
void CallSVC(u32) override {
@@ -142,9 +135,9 @@ TEST_CASE("A64: fibonacci", "[a64]") {
code.RET();
for (size_t i = 0; i < 1024; i++) {
env.MemoryWrite(i * 4, instructions[i], sizeof(u32));
env.MemoryWrite32(i * 4, instructions[i]);
}
env.MemoryWrite(8888, 0xd4200000, sizeof(u32));
env.MemoryWrite32(8888, 0xd4200000);
cpu.SetRegister(30, 8888);
cpu.SetRegister(0, 10);
+64 -74
View File
@@ -12,7 +12,6 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "dynarmic/interface/A64/a64.h"
#include "dynarmic/interface/A64/config.h"
using Vector = Dynarmic::A64::Vector;
@@ -35,79 +34,64 @@ public:
return code_mem[index];
}
u64 MemoryRead(u64 vaddr, size_t size) override {
switch (size) {
case sizeof(u64):
return MemoryRead(vaddr, sizeof(u32))
| MemoryRead(vaddr + sizeof(u32), sizeof(u32)) << 32;
case sizeof(u32):
return MemoryRead(vaddr, sizeof(u16))
| MemoryRead(vaddr + sizeof(u16), sizeof(u16)) << 16;
case sizeof(u16):
return MemoryRead(vaddr, sizeof(u8))
| MemoryRead(vaddr + sizeof(u8), sizeof(u8)) << 8;
case sizeof(u8): {
if (IsInCodeMem(vaddr))
return reinterpret_cast<u8*>(code_mem.data())[vaddr - code_mem_start_address];
if (auto const it = modified_memory.find(vaddr); it != modified_memory.end())
return it->second;
return u8(vaddr);
}
default:
std::abort();
std::uint8_t MemoryRead8(u64 vaddr) override {
if (IsInCodeMem(vaddr)) {
return reinterpret_cast<u8*>(code_mem.data())[vaddr - code_mem_start_address];
}
if (auto const it = modified_memory.find(vaddr); it != modified_memory.end())
return it->second;
return u8(vaddr);
}
std::uint16_t MemoryRead16(u64 vaddr) override {
return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8;
}
std::uint32_t MemoryRead32(u64 vaddr) override {
return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16;
}
std::uint64_t MemoryRead64(u64 vaddr) override {
return u64(MemoryRead32(vaddr)) | u64(MemoryRead32(vaddr + 4)) << 32;
}
Vector MemoryRead128(u64 vaddr) override {
return {
MemoryRead(vaddr, sizeof(u64)),
MemoryRead(vaddr + 8, sizeof(u64))
};
return {MemoryRead64(vaddr), MemoryRead64(vaddr + 8)};
}
void MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, size_t size) override {
switch (size) {
case sizeof(u64):
MemoryWrite(vaddr, u32(value), sizeof(u32));
MemoryWrite(vaddr + 4, u32(value >> 32), sizeof(u32));
break;
case sizeof(u32):
MemoryWrite(vaddr, u16(value), sizeof(u16));
MemoryWrite(vaddr + 2, u16(value >> 16), sizeof(u16));
break;
case sizeof(u16):
MemoryWrite(vaddr, u8(value), sizeof(u8));
MemoryWrite(vaddr + 1, u8(value >> 8), sizeof(u8));
break;
case sizeof(u8):
if (IsInCodeMem(vaddr)) {
code_mem_modified_by_guest = true;
}
modified_memory[vaddr] = value;
break;
default:
std::abort();
void MemoryWrite8(u64 vaddr, std::uint8_t value) override {
if (IsInCodeMem(vaddr)) {
code_mem_modified_by_guest = true;
}
modified_memory[vaddr] = value;
}
void MemoryWrite16(u64 vaddr, std::uint16_t value) override {
MemoryWrite8(vaddr, u8(value));
MemoryWrite8(vaddr + 1, u8(value >> 8));
}
void MemoryWrite32(u64 vaddr, std::uint32_t value) override {
MemoryWrite16(vaddr, u16(value));
MemoryWrite16(vaddr + 2, u16(value >> 16));
}
void MemoryWrite64(u64 vaddr, std::uint64_t value) override {
MemoryWrite32(vaddr, u32(value));
MemoryWrite32(vaddr + 4, u32(value >> 32));
}
void MemoryWrite128(u64 vaddr, Vector value) override {
MemoryWrite(vaddr, value[0], sizeof(u64));
MemoryWrite(vaddr + 8, value[1], sizeof(u64));
MemoryWrite64(vaddr, value[0]);
MemoryWrite64(vaddr + 8, value[1]);
}
bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, [[maybe_unused]] std::uint8_t expected) override {
MemoryWrite(vaddr, value, sizeof(u8));
MemoryWrite8(vaddr, value);
return true;
}
bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, [[maybe_unused]] std::uint16_t expected) override {
MemoryWrite(vaddr, value, sizeof(u16));
MemoryWrite16(vaddr, value);
return true;
}
bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, [[maybe_unused]] std::uint32_t expected) override {
MemoryWrite(vaddr, value, sizeof(u32));
MemoryWrite32(vaddr, value);
return true;
}
bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, [[maybe_unused]] std::uint64_t expected) override {
MemoryWrite(vaddr, value, sizeof(u64));
MemoryWrite64(vaddr, value);
return true;
}
bool MemoryWriteExclusive128(u64 vaddr, Vector value, [[maybe_unused]] Vector expected) override {
@@ -161,46 +145,52 @@ public:
return read<std::uint32_t>(vaddr);
}
u64 MemoryRead(u64 vaddr, size_t size) override {
switch (size) {
case sizeof(u64): return read<u64>(vaddr);
case sizeof(u32): return read<u32>(vaddr);
case sizeof(u16): return read<u16>(vaddr);
case sizeof(u8): return read<u8>(vaddr);
default: std::abort();
}
std::uint8_t MemoryRead8(u64 vaddr) override {
return read<std::uint8_t>(vaddr);
}
std::uint16_t MemoryRead16(u64 vaddr) override {
return read<std::uint16_t>(vaddr);
}
std::uint32_t MemoryRead32(u64 vaddr) override {
return read<std::uint32_t>(vaddr);
}
std::uint64_t MemoryRead64(u64 vaddr) override {
return read<std::uint64_t>(vaddr);
}
Vector MemoryRead128(u64 vaddr) override {
return read<Vector>(vaddr);
}
void MemoryWrite(u64 vaddr, std::uint64_t value, size_t size) override {
switch (size) {
case sizeof(u64): return write<u64>(vaddr, u64(value));
case sizeof(u32): return write<u32>(vaddr, u32(value));
case sizeof(u16): return write<u16>(vaddr, u16(value));
case sizeof(u8): return write<u8>(vaddr, u8(value));
default: std::abort();
}
void MemoryWrite8(u64 vaddr, std::uint8_t value) override {
write(vaddr, value);
}
void MemoryWrite16(u64 vaddr, std::uint16_t value) override {
write(vaddr, value);
}
void MemoryWrite32(u64 vaddr, std::uint32_t value) override {
write(vaddr, value);
}
void MemoryWrite64(u64 vaddr, std::uint64_t value) override {
write(vaddr, value);
}
void MemoryWrite128(u64 vaddr, Vector value) override {
write(vaddr, value);
}
bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, [[maybe_unused]] std::uint8_t expected) override {
MemoryWrite(vaddr, value, sizeof(u8));
MemoryWrite8(vaddr, value);
return true;
}
bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, [[maybe_unused]] std::uint16_t expected) override {
MemoryWrite(vaddr, value, sizeof(u16));
MemoryWrite16(vaddr, value);
return true;
}
bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, [[maybe_unused]] std::uint32_t expected) override {
MemoryWrite(vaddr, value, sizeof(u32));
MemoryWrite32(vaddr, value);
return true;
}
bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, [[maybe_unused]] std::uint64_t expected) override {
MemoryWrite(vaddr, value, sizeof(u64));
MemoryWrite64(vaddr, value);
return true;
}
bool MemoryWriteExclusive128(u64 vaddr, Vector value, [[maybe_unused]] Vector expected) override {
+30 -42
View File
@@ -18,7 +18,6 @@
#include <fmt/format.h>
#include <fmt/ostream.h>
#include <fmt/ranges.h>
#include "dynarmic/frontend/A32/translate/translate_callbacks.h"
#include "dynarmic/mcl/bit.hpp"
#include "common/common_types.h"
@@ -119,47 +118,36 @@ public:
u64 ticks_left = 0;
std::map<u32, u8> memory;
u64 MemoryRead(Dynarmic::A32::VAddr vaddr, size_t size) override {
switch (size) {
case sizeof(u64):
return MemoryRead(vaddr, sizeof(u32))
| MemoryRead(vaddr + sizeof(u32), sizeof(u32)) << 32;
case sizeof(u32):
return MemoryRead(vaddr, sizeof(u16))
| MemoryRead(vaddr + sizeof(u16), sizeof(u16)) << 16;
case sizeof(u16):
return MemoryRead(vaddr, sizeof(u8))
| MemoryRead(vaddr + sizeof(u8), sizeof(u8)) << 8;
case sizeof(u8): {
if (auto const it = memory.find(vaddr); it != memory.end())
return it->second;
return 0;
}
default:
std::abort();
std::uint8_t MemoryRead8(u32 vaddr) override {
if (auto iter = memory.find(vaddr); iter != memory.end()) {
return iter->second;
}
return 0;
}
std::uint16_t MemoryRead16(u32 vaddr) override {
return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8;
}
std::uint32_t MemoryRead32(u32 vaddr) override {
return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16;
}
std::uint64_t MemoryRead64(u32 vaddr) override {
return u64(MemoryRead32(vaddr)) | u64(MemoryRead32(vaddr + 4)) << 32;
}
void MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) override {
switch (size) {
case sizeof(u64):
MemoryWrite(vaddr, u32(value), sizeof(u32));
MemoryWrite(vaddr + 4, u32(value >> 32), sizeof(u32));
break;
case sizeof(u32):
MemoryWrite(vaddr, u16(value), sizeof(u16));
MemoryWrite(vaddr + 2, u16(value >> 16), sizeof(u16));
break;
case sizeof(u16):
MemoryWrite(vaddr, u8(value), sizeof(u8));
MemoryWrite(vaddr + 1, u8(value >> 8), sizeof(u8));
break;
case sizeof(u8):
memory[vaddr] = value;
break;
default:
std::abort();
}
void MemoryWrite8(u32 vaddr, std::uint8_t value) override {
memory[vaddr] = value;
}
void MemoryWrite16(u32 vaddr, std::uint16_t value) override {
MemoryWrite8(vaddr, static_cast<u8>(value));
MemoryWrite8(vaddr + 1, static_cast<u8>(value >> 8));
}
void MemoryWrite32(u32 vaddr, std::uint32_t value) override {
MemoryWrite16(vaddr, static_cast<u16>(value));
MemoryWrite16(vaddr + 2, static_cast<u16>(value >> 16));
}
void MemoryWrite64(u32 vaddr, std::uint64_t value) override {
MemoryWrite32(vaddr, static_cast<u32>(value));
MemoryWrite32(vaddr + 4, static_cast<u32>(value >> 32));
}
void CallSVC(std::uint32_t swi) override {
@@ -243,7 +231,7 @@ void ExecuteA32Instruction(u32 instruction) {
if (const auto address = get_value()) {
fmt::print("value: ");
if (const auto value = get_value()) {
env.MemoryWrite(*address, *value, sizeof(u32));
env.MemoryWrite32(*address, *value);
fmt::print("> mem[{:#08x}] = {:#08x}\n", *address, *value);
}
}
@@ -259,8 +247,8 @@ void ExecuteA32Instruction(u32 instruction) {
cpu.SetFpscr(fpscr);
const u32 initial_pc = regs[15];
env.MemoryWrite(initial_pc + 0, instruction, sizeof(u32));
env.MemoryWrite(initial_pc + 4, 0xEAFFFFFE, sizeof(u32)); // B +0
env.MemoryWrite32(initial_pc + 0, instruction);
env.MemoryWrite32(initial_pc + 4, 0xEAFFFFFE); // B +0
cpu.Run();
fmt::print("{}", fmt::join(cpu.Disassemble(), "\n"));
+19 -2
View File
@@ -290,7 +290,7 @@ bool A32Unicorn<TestEnvironment>::UnmappedMemoryHook(uc_engine* uc, uc_mem_type
auto page = std::make_unique<Page>();
page->address = base_address;
for (size_t i = 0; i < page->data.size(); ++i)
page->data[i] = u8(this_->testenv.MemoryRead(u32(base_address + i), sizeof(u8)));
page->data[i] = this_->testenv.MemoryRead8(static_cast<u32>(base_address + i));
uc_err err = uc_mem_map_ptr(uc, base_address, page->data.size(), permissions, page->data.data());
if (err == UC_ERR_MAP)
@@ -321,7 +321,24 @@ bool A32Unicorn<TestEnvironment>::UnmappedMemoryHook(uc_engine* uc, uc_mem_type
template<class TestEnvironment>
bool A32Unicorn<TestEnvironment>::MemoryWriteHook(uc_engine* /*uc*/, uc_mem_type /*type*/, u32 start_address, int size, u64 value, void* user_data) {
auto* this_ = static_cast<A32Unicorn*>(user_data);
this_->testenv.MemoryWrite(start_address, value, size);
switch (size) {
case 1:
this_->testenv.MemoryWrite8(start_address, static_cast<u8>(value));
break;
case 2:
this_->testenv.MemoryWrite16(start_address, static_cast<u16>(value));
break;
case 4:
this_->testenv.MemoryWrite32(start_address, static_cast<u32>(value));
break;
case 8:
this_->testenv.MemoryWrite64(start_address, value);
break;
default:
UNREACHABLE();
}
return true;
}
+19 -2
View File
@@ -197,7 +197,7 @@ bool A64Unicorn::UnmappedMemoryHook(uc_engine* uc, uc_mem_type /*type*/, u64 sta
auto page = std::make_unique<Page>();
page->address = base_address;
for (size_t i = 0; i < page->data.size(); ++i)
page->data[i] = u8(this_->testenv.MemoryRead(base_address + i, sizeof(u8)));
page->data[i] = this_->testenv.MemoryRead8(base_address + i);
uc_err err = uc_mem_map_ptr(uc, base_address, page->data.size(), permissions, page->data.data());
if (err == UC_ERR_MAP)
@@ -227,6 +227,23 @@ bool A64Unicorn::UnmappedMemoryHook(uc_engine* uc, uc_mem_type /*type*/, u64 sta
bool A64Unicorn::MemoryWriteHook(uc_engine* /*uc*/, uc_mem_type /*type*/, u64 start_address, int size, u64 value, void* user_data) {
auto* this_ = static_cast<A64Unicorn*>(user_data);
this_->testenv.MemoryWrite(start_address, value, size);
switch (size) {
case 1:
this_->testenv.MemoryWrite8(start_address, static_cast<u8>(value));
break;
case 2:
this_->testenv.MemoryWrite16(start_address, static_cast<u16>(value));
break;
case 4:
this_->testenv.MemoryWrite32(start_address, static_cast<u32>(value));
break;
case 8:
this_->testenv.MemoryWrite64(start_address, value);
break;
default:
UNREACHABLE();
}
return true;
}
+8 -8
View File
@@ -398,21 +398,21 @@ void NPad::InitNewlyAddedController(Kernel::KernelCore& kernel, u64 aruid, Core:
void NPad::WriteEmptyEntry(NpadInternalState* npad) {
NPadGenericState dummy_pad_state{};
NpadGcTriggerState dummy_gc_state{};
dummy_pad_state.sampling_number = npad->fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
dummy_pad_state.sampling_number = npad->fullkey_lifo.ReadCurrentEntry().sampling_number + 1;
npad->fullkey_lifo.WriteNextEntry(dummy_pad_state);
dummy_pad_state.sampling_number = npad->handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
dummy_pad_state.sampling_number = npad->handheld_lifo.ReadCurrentEntry().sampling_number + 1;
npad->handheld_lifo.WriteNextEntry(dummy_pad_state);
dummy_pad_state.sampling_number = npad->joy_dual_lifo.ReadCurrentEntry().state.sampling_number + 1;
dummy_pad_state.sampling_number = npad->joy_dual_lifo.ReadCurrentEntry().sampling_number + 1;
npad->joy_dual_lifo.WriteNextEntry(dummy_pad_state);
dummy_pad_state.sampling_number = npad->joy_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
dummy_pad_state.sampling_number = npad->joy_left_lifo.ReadCurrentEntry().sampling_number + 1;
npad->joy_left_lifo.WriteNextEntry(dummy_pad_state);
dummy_pad_state.sampling_number = npad->joy_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
dummy_pad_state.sampling_number = npad->joy_right_lifo.ReadCurrentEntry().sampling_number + 1;
npad->joy_right_lifo.WriteNextEntry(dummy_pad_state);
dummy_pad_state.sampling_number = npad->palma_lifo.ReadCurrentEntry().state.sampling_number + 1;
dummy_pad_state.sampling_number = npad->palma_lifo.ReadCurrentEntry().sampling_number + 1;
npad->palma_lifo.WriteNextEntry(dummy_pad_state);
dummy_pad_state.sampling_number = npad->system_ext_lifo.ReadCurrentEntry().state.sampling_number + 1;
dummy_pad_state.sampling_number = npad->system_ext_lifo.ReadCurrentEntry().sampling_number + 1;
npad->system_ext_lifo.WriteNextEntry(dummy_pad_state);
dummy_gc_state.sampling_number = npad->gc_trigger_lifo.ReadCurrentEntry().state.sampling_number + 1;
dummy_gc_state.sampling_number = npad->gc_trigger_lifo.ReadCurrentEntry().sampling_number + 1;
npad->gc_trigger_lifo.WriteNextEntry(dummy_gc_state);
}
+20 -19
View File
@@ -892,20 +892,28 @@ void VpxRangeEncoder::Write(bool bit, s32 probability) {
count += shift;
if (count >= 0) {
const s32 offset = shift - count;
if (((low_value << (offset - 1)) >> 31) != 0) {
const s32 current_pos = static_cast<s32>(base_stream.GetPosition());
base_stream.Seek(-1, Common::SeekOrigin::FromCurrentPos);
while (PeekByte() == 0xff) {
base_stream.WriteByte(0);
base_stream.Seek(-2, Common::SeekOrigin::FromCurrentPos);
auto const write_byte = [&](u8 byte) {
if (stream_pos == stream_data.size()) {
stream_data.push_back(byte);
++stream_pos;
} else {
stream_data.insert(stream_data.begin() + stream_pos, byte);
}
base_stream.WriteByte(static_cast<u8>((PeekByte() + 1)));
base_stream.Seek(current_pos, Common::SeekOrigin::SetOrigin);
};
const s32 offset = shift - count;
if (((low_value << (offset - 1)) >> 31) != 0) {
auto const current_pos = stream_pos;
--stream_pos;
while (stream_data[stream_pos] == 0xff) {
write_byte(0);
stream_pos -= 2;
}
write_byte(stream_data[stream_pos] + 1);
stream_pos = current_pos;
}
base_stream.WriteByte(static_cast<u8>((low_value >> (24 - offset))));
write_byte(u8((low_value >> (24 - offset))));
++stream_pos;
low_value <<= offset;
shift = count;
@@ -923,13 +931,6 @@ void VpxRangeEncoder::End() {
}
}
u8 VpxRangeEncoder::PeekByte() {
const u8 value = base_stream.ReadByte();
base_stream.Seek(-1, Common::SeekOrigin::FromCurrentPos);
return value;
}
VpxBitStreamWriter::VpxBitStreamWriter() = default;
VpxBitStreamWriter::~VpxBitStreamWriter() = default;
+6 -4
View File
@@ -12,7 +12,6 @@
#include "common/common_types.h"
#include "common/scratch_buffer.h"
#include "common/stream.h"
#include "video_core/host1x/codecs/decoder.h"
#include "video_core/host1x/codec_types.h"
#include "video_core/host1x/nvdec_common.h"
@@ -52,16 +51,19 @@ public:
void End();
[[nodiscard]] std::vector<u8>& GetBuffer() {
return base_stream.GetBuffer();
return stream_data;
}
[[nodiscard]] const std::vector<u8>& GetBuffer() const {
return base_stream.GetBuffer();
return stream_data;
}
private:
u8 PeekByte();
Common::Stream base_stream{};
std::vector<u8> stream_data{};
size_t stream_pos;
u32 low_value{};
u32 range{0xff};
s32 count{-24};
@@ -2850,6 +2850,8 @@ Sampler::VariantKey Sampler::MakeKey(const ImageView& image_view, bool is_depth)
VariantKey key{};
key.reduce_anisotropy = has_added_anisotropy && !image_view.SupportsAnisotropy();
key.force_nearest = has_linear_filtering && IsPixelFormatInteger(image_view.format);
key.drop_depth_comparison =
is_depth && has_depth_comparison && !image_view.SupportsDepthComparison();
key.drop_reduction = has_minmax_reduction && !image_view.SupportsMinmaxFilter();
key.drop_custom_border = has_custom_border_colors && image_view.RequiresBorderColorFormat();
key.srgb_border = has_srgb_border_color && IsPixelFormatSRGB(image_view.format);
@@ -2927,6 +2929,9 @@ VkSampler Sampler::Emplace(VariantKey key) {
create_info.anisotropyEnable = static_cast<VkBool32>(default_anisotropy > 1.0f);
create_info.maxAnisotropy = default_anisotropy;
}
if (key.drop_depth_comparison) {
create_info.compareEnable = VK_FALSE;
}
if (!custom_border) {
create_info.borderColor = ConvertBorderColor(color);
}
@@ -536,6 +536,7 @@ private:
struct VariantKey {
bool reduce_anisotropy;
bool force_nearest;
bool drop_depth_comparison;
bool drop_reduction;
bool drop_custom_border;
bool srgb_border;