mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-27 03:12:02 +00:00
Compare commits
88 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0c1747c7b5 | |||
| e14ba10cf5 | |||
| ea8993f0f5 | |||
| c38418a029 | |||
| be9bcc8b46 | |||
| 1a7861d7c8 | |||
| 085be071b6 | |||
| 6eedc2402a | |||
| 99eb0aba75 | |||
| bfcabab10a | |||
| 7e54a2d722 | |||
| feeb612be3 | |||
| 9bca020268 | |||
| ccfddaf7aa | |||
| a7b7a3e4fd | |||
| 047d44170e | |||
| f6a45e0877 | |||
| 500dc6e52f | |||
| 4f2e2460cf | |||
| 5b4bbaa797 | |||
| 093d27b724 | |||
| 128b4a786d | |||
| 12625e0ce4 | |||
| 8412aa56e2 | |||
| b1cd5a0d63 | |||
| fc3b6b2a96 | |||
| 9974f11771 | |||
| dd2b105251 | |||
| 3c044ef92e | |||
| 3534a96d59 | |||
| e5593ec7e9 | |||
| 1fe8a8be70 | |||
| 381df6e648 | |||
| 5431050643 | |||
| bd65acc2ed | |||
| b413b84496 | |||
| a25f96650f | |||
| 867b3a1b55 | |||
| 72ff948db5 | |||
| 877c589986 | |||
| 140a9e33df | |||
| a75eff1256 | |||
| 0abaa67035 | |||
| f5c81f0c5c | |||
| ad0d3328bd | |||
| 361d1e1e72 | |||
| fee9502de8 | |||
| 9583b3e0e9 | |||
| 817eb244b3 | |||
| 1151febbaf | |||
| ebc76d5e91 | |||
| fdaaae8bef | |||
| d703da43c3 | |||
| 6f40649db2 | |||
| 1d320ba8d7 | |||
| 888bdc3c7c | |||
| 0e49e12125 | |||
| b9d4e1eb65 | |||
| 74b5e10dc5 | |||
| a277b62fe4 | |||
| 908b1e9a37 | |||
| 847e91c3a8 | |||
| 2d1eb0dab9 | |||
| 9a30172e45 | |||
| 07f40d5cac | |||
| 3c38afad74 | |||
| 0ce29be608 | |||
| 1203082a8f | |||
| 14235dc0d0 | |||
| 6374f7f51f | |||
| 73e004de6e | |||
| f7b8ade40e | |||
| 8c1194474e | |||
| 90aafeedc1 | |||
| 4fe5f62c38 | |||
| c3f1e6562b | |||
| ac35358b3f | |||
| 7bf95be2c2 | |||
| 4ce45b3b37 | |||
| fd34024f0e | |||
| d76f8f91c4 | |||
| a7061eb4c8 | |||
| fa4e7c6992 | |||
| b77308ced6 | |||
| defddec47f | |||
| c5405250d1 | |||
| 10c07d700c | |||
| 505b157647 |
@@ -1,13 +0,0 @@
|
|||||||
diff --git a/libs/cobalt/include/boost/cobalt/concepts.hpp b/libs/cobalt/include/boost/cobalt/concepts.hpp
|
|
||||||
index d49f2ec..a9bdb80 100644
|
|
||||||
--- a/libs/cobalt/include/boost/cobalt/concepts.hpp
|
|
||||||
+++ b/libs/cobalt/include/boost/cobalt/concepts.hpp
|
|
||||||
@@ -62,7 +62,7 @@ struct enable_awaitables
|
|
||||||
template <typename T>
|
|
||||||
concept with_get_executor = requires (T& t)
|
|
||||||
{
|
|
||||||
- {t.get_executor()} -> asio::execution::executor;
|
|
||||||
+ t.get_executor();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
+3
-7
@@ -417,12 +417,9 @@ AddJsonPackage(boost)
|
|||||||
set(BOOST_NO_HEADERS ${Boost_ADDED})
|
set(BOOST_NO_HEADERS ${Boost_ADDED})
|
||||||
|
|
||||||
if (Boost_ADDED)
|
if (Boost_ADDED)
|
||||||
if (MSVC OR ANDROID)
|
add_compile_definitions(YUZU_BOOST_v1)
|
||||||
add_compile_definitions(YUZU_BOOST_v1)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (NOT MSVC OR CXX_CLANG)
|
if (NOT MSVC OR CXX_CLANG)
|
||||||
# boost sucks
|
# solaris sucks
|
||||||
if (SOLARIS)
|
if (SOLARIS)
|
||||||
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-pthreads>)
|
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-pthreads>)
|
||||||
endif()
|
endif()
|
||||||
@@ -431,8 +428,7 @@ if (Boost_ADDED)
|
|||||||
target_compile_options(boost_icl INTERFACE $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-shadow>)
|
target_compile_options(boost_icl INTERFACE $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-shadow>)
|
||||||
target_compile_options(boost_asio INTERFACE
|
target_compile_options(boost_asio INTERFACE
|
||||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-conversion>
|
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-conversion>
|
||||||
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-implicit-fallthrough>
|
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-implicit-fallthrough>)
|
||||||
)
|
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|||||||
+6
-9
@@ -6,22 +6,19 @@
|
|||||||
"version": "v0.19.0"
|
"version": "v0.19.0"
|
||||||
},
|
},
|
||||||
"boost": {
|
"boost": {
|
||||||
"artifact": "%VERSION%-cmake.tar.xz",
|
"artifact": "boost-%VERSION%.tar.zst",
|
||||||
"find_args": "CONFIG OPTIONAL_COMPONENTS headers context system fiber filesystem",
|
"find_args": "CONFIG OPTIONAL_COMPONENTS headers context system fiber filesystem",
|
||||||
"hash": "6ae6e94664fe7f2fb01976b59b276ac5df8085c7503fa829d810fbfe495960cfec44fa2c36e2cb23480bc19c956ed199d4952b02639a00a6c07625d4e7130c2d",
|
"hash": "681d97be4386767662e230b2e7c9754d45e709cc5db4e747c23c27d1f5b0e87770bdf19b0bc44dcb0e8349324887bbb8fffb432098f4d0ce74a5043021a90afc",
|
||||||
"min_version": "1.57",
|
"min_version": "1.57",
|
||||||
"package": "Boost",
|
"package": "Boost",
|
||||||
"patches": [
|
"repo": "eden-emulator/ext-boost",
|
||||||
"0001-clang-cl.patch"
|
"version": "1.92.0"
|
||||||
],
|
|
||||||
"repo": "boostorg/boost",
|
|
||||||
"version": "boost-1.90.0"
|
|
||||||
},
|
},
|
||||||
"boost_headers": {
|
"boost_headers": {
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"hash": "4ef845775e2277a8104ded6ddf749aa262ce52cf8438042869a048f9a0156dd772fbbcfa74efa1378fecef339b7286f6fe4b4feb5c45d49966b35d08e3e83507",
|
"hash": "c5fa2cd72f6e6666b7963b97bc359c75284b8fb540c30f3629a028b85270c9bc66c8a051383964f2bd4c1e005a4691593d15696e7ef39ea87cf6cff9e5691fb2",
|
||||||
"repo": "boostorg/headers",
|
"repo": "boostorg/headers",
|
||||||
"version": "boost-1.90.0"
|
"version": "boost-1.91.0"
|
||||||
},
|
},
|
||||||
"catch2": {
|
"catch2": {
|
||||||
"hash": "7eea385d79d88a5690cde131fe7ccda97d5c54ea09d6f515000d7bf07c828809d61c1ac99912c1ee507cf933f61c1c47ecdcc45df7850ffa82714034b0fccf35",
|
"hash": "7eea385d79d88a5690cde131fe7ccda97d5c54ea09d6f515000d7bf07c828809d61c1ac99912c1ee507cf933f61c1c47ecdcc45df7850ffa82714034b0fccf35",
|
||||||
|
|||||||
-1
@@ -27,7 +27,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
|||||||
RENDERER_ASYNCHRONOUS_GPU_EMULATION("use_asynchronous_gpu_emulation"),
|
RENDERER_ASYNCHRONOUS_GPU_EMULATION("use_asynchronous_gpu_emulation"),
|
||||||
RENDERER_ASYNC_PRESENTATION("async_presentation"),
|
RENDERER_ASYNC_PRESENTATION("async_presentation"),
|
||||||
RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders"),
|
RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders"),
|
||||||
RENDERER_UNIFIED_MEMORY("use_unified_memory"),
|
|
||||||
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
|
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
|
||||||
ENABLE_BUFFER_HISTORY("enable_buffer_history"),
|
ENABLE_BUFFER_HISTORY("enable_buffer_history"),
|
||||||
USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"),
|
USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"),
|
||||||
|
|||||||
-7
@@ -785,13 +785,6 @@ abstract class SettingsItem(
|
|||||||
descriptionId = R.string.renderer_asynchronous_shaders_description
|
descriptionId = R.string.renderer_asynchronous_shaders_description
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
put(
|
|
||||||
SwitchSetting(
|
|
||||||
BooleanSetting.RENDERER_UNIFIED_MEMORY,
|
|
||||||
titleId = R.string.renderer_unified_memory,
|
|
||||||
descriptionId = R.string.renderer_unified_memory_description
|
|
||||||
)
|
|
||||||
)
|
|
||||||
put(
|
put(
|
||||||
SingleChoiceSetting(
|
SingleChoiceSetting(
|
||||||
IntSetting.FAST_GPU_TIME,
|
IntSetting.FAST_GPU_TIME,
|
||||||
|
|||||||
-1
@@ -558,7 +558,6 @@ class SettingsFragmentPresenter(
|
|||||||
add(BooleanSetting.FIX_BLOOM_EFFECTS.key)
|
add(BooleanSetting.FIX_BLOOM_EFFECTS.key)
|
||||||
add(BooleanSetting.EMULATE_BGR565.key)
|
add(BooleanSetting.EMULATE_BGR565.key)
|
||||||
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
|
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
|
||||||
add(BooleanSetting.RENDERER_UNIFIED_MEMORY.key)
|
|
||||||
add(IntSetting.ANDROID_PIPELINE_WORKERS.key)
|
add(IntSetting.ANDROID_PIPELINE_WORKERS.key)
|
||||||
add(BooleanSetting.RENDERER_ASYNCHRONOUS_GPU_EMULATION.key)
|
add(BooleanSetting.RENDERER_ASYNCHRONOUS_GPU_EMULATION.key)
|
||||||
add(BooleanSetting.RENDERER_ASYNC_PRESENTATION.key)
|
add(BooleanSetting.RENDERER_ASYNC_PRESENTATION.key)
|
||||||
|
|||||||
@@ -788,7 +788,7 @@ int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject
|
|||||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj,
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj,
|
||||||
jstring jprogramId,
|
jstring jprogramId,
|
||||||
jstring jupdatePath) {
|
jstring jupdatePath) {
|
||||||
u64 program_id = EmulationSession::GetProgramId(env, jprogramId);
|
const u64 program_id = FileSys::GetBaseTitleID(EmulationSession::GetProgramId(env, jprogramId));
|
||||||
std::string updatePath = Common::Android::GetJString(env, jupdatePath);
|
std::string updatePath = Common::Android::GetJString(env, jupdatePath);
|
||||||
std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
|
std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
|
||||||
EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
|
EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
|
||||||
@@ -796,7 +796,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* en
|
|||||||
for (const auto& item : nsp->GetNCAs()) {
|
for (const auto& item : nsp->GetNCAs()) {
|
||||||
for (const auto& nca_details : item.second) {
|
for (const auto& nca_details : item.second) {
|
||||||
if (nca_details.second->GetName().ends_with(".cnmt.nca")) {
|
if (nca_details.second->GetName().ends_with(".cnmt.nca")) {
|
||||||
auto update_id = nca_details.second->GetTitleId() & ~0xFFFULL;
|
const auto update_id = FileSys::GetBaseTitleID(nca_details.second->GetTitleId());
|
||||||
if (update_id == program_id) {
|
if (update_id == program_id) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -605,8 +605,6 @@
|
|||||||
<string name="rescale_hack_description">Enables a legacy handling for the rescale configuration pass for games by using a quick rescale path</string>
|
<string name="rescale_hack_description">Enables a legacy handling for the rescale configuration pass for games by using a quick rescale path</string>
|
||||||
<string name="renderer_asynchronous_shaders">Use asynchronous shaders</string>
|
<string name="renderer_asynchronous_shaders">Use asynchronous shaders</string>
|
||||||
<string name="renderer_asynchronous_shaders_description">Compiles shaders asynchronously. This may reduce stutters but may also introduce glitches.</string>
|
<string name="renderer_asynchronous_shaders_description">Compiles shaders asynchronously. This may reduce stutters but may also introduce glitches.</string>
|
||||||
<string name="renderer_unified_memory">Unified memory access</string>
|
|
||||||
<string name="renderer_unified_memory_description">Allows GPU write buffer readbacks directly into guest memory, skipping the CPU staging copy.</string>
|
|
||||||
<string name="gpu_unswizzle_settings">GPU Unswizzle Settings</string>
|
<string name="gpu_unswizzle_settings">GPU Unswizzle Settings</string>
|
||||||
<string name="gpu_unswizzle_settings_description">Configure GPU-based texture unswizzling parameters or disable it entirely. Adjust these settings to balance performance and texture loading quality.</string>
|
<string name="gpu_unswizzle_settings_description">Configure GPU-based texture unswizzling parameters or disable it entirely. Adjust these settings to balance performance and texture loading quality.</string>
|
||||||
<string name="gpu_unswizzle_enable">Enable GPU Unswizzle</string>
|
<string name="gpu_unswizzle_enable">Enable GPU Unswizzle</string>
|
||||||
|
|||||||
@@ -107,10 +107,8 @@ add_library(
|
|||||||
settings_input.h
|
settings_input.h
|
||||||
settings_setting.h
|
settings_setting.h
|
||||||
slot_vector.h
|
slot_vector.h
|
||||||
socket_types.h
|
|
||||||
sparse_large_vector.cpp
|
sparse_large_vector.cpp
|
||||||
sparse_large_vector.h
|
sparse_large_vector.h
|
||||||
spin_lock.h
|
|
||||||
stb.cpp
|
stb.cpp
|
||||||
stb.h
|
stb.h
|
||||||
steady_clock.cpp
|
steady_clock.cpp
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/socket_types.h"
|
#include "core/internal_network/socket_types.h"
|
||||||
#include "web_service/web_result.h"
|
#include "web_service/web_result.h"
|
||||||
|
|
||||||
namespace AnnounceMultiplayerRoom {
|
namespace AnnounceMultiplayerRoom {
|
||||||
|
|||||||
@@ -244,7 +244,8 @@ WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept
|
|||||||
, ns_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency_) : 0}
|
, ns_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency_) : 0}
|
||||||
, us_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency_) : 0}
|
, us_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency_) : 0}
|
||||||
, ms_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency_) : 0}
|
, ms_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency_) : 0}
|
||||||
, rdtsc_ns_factor{invariant_ ? GetFixedPoint64Factor(rdtsc_frequency_, NsRatio::den) : 1}
|
, rdtsc_ns_integer{invariant_ ? rdtsc_frequency_ / NsRatio::den : 1}
|
||||||
|
, rdtsc_ns_factor{invariant_ ? GetFixedPoint64Factor(rdtsc_frequency_ % NsRatio::den, NsRatio::den) : 0}
|
||||||
, cntpct_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency_) : 0}
|
, cntpct_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency_) : 0}
|
||||||
, gputick_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency_) : 0}
|
, gputick_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency_) : 0}
|
||||||
, invariant{invariant_}
|
, invariant{invariant_}
|
||||||
@@ -291,7 +292,7 @@ bool WallClock::IsNative() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const {
|
u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const {
|
||||||
return invariant ? MultiplyHigh(ns.count(), rdtsc_ns_factor) : ns.count();
|
return ns.count() * rdtsc_ns_integer + MultiplyHigh(ns.count(), rdtsc_ns_factor);
|
||||||
}
|
}
|
||||||
#elif defined(HAS_NCE)
|
#elif defined(HAS_NCE)
|
||||||
namespace {
|
namespace {
|
||||||
@@ -416,7 +417,7 @@ u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const {
|
|||||||
const WallClock g_wall_clock = [] {
|
const WallClock g_wall_clock = [] {
|
||||||
#if defined(ARCHITECTURE_x86_64)
|
#if defined(ARCHITECTURE_x86_64)
|
||||||
auto const& caps = Common::g_cpu_caps;
|
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)
|
#elif defined(HAS_NCE)
|
||||||
return WallClock(false, 1);
|
return WallClock(false, 1);
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ public:
|
|||||||
u64 ns_rdtsc_factor;
|
u64 ns_rdtsc_factor;
|
||||||
u64 us_rdtsc_factor;
|
u64 us_rdtsc_factor;
|
||||||
u64 ms_rdtsc_factor;
|
u64 ms_rdtsc_factor;
|
||||||
|
u64 rdtsc_ns_integer;
|
||||||
u64 rdtsc_ns_factor;
|
u64 rdtsc_ns_factor;
|
||||||
u64 cntpct_rdtsc_factor;
|
u64 cntpct_rdtsc_factor;
|
||||||
u64 gputick_rdtsc_factor;
|
u64 gputick_rdtsc_factor;
|
||||||
|
|||||||
+18
-20
@@ -17,35 +17,33 @@
|
|||||||
|
|
||||||
namespace Common {
|
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) {
|
std::string NativeErrorToString(int e) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
LPSTR err_str;
|
LPSTR err_str;
|
||||||
|
|
||||||
DWORD res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
DWORD res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
nullptr, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
nullptr, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
reinterpret_cast<LPSTR>(&err_str), 1, nullptr);
|
LPSTR(&err_str), 1, nullptr);
|
||||||
if (!res) {
|
if (res) {
|
||||||
return "(FormatMessageA failed to format error)";
|
std::string ret(err_str);
|
||||||
|
LocalFree(err_str);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
std::string ret(err_str);
|
return "(FormatMessageA failed to format error)";
|
||||||
LocalFree(err_str);
|
|
||||||
return ret;
|
|
||||||
#else
|
#else
|
||||||
char err_str[255];
|
char err_str[255];
|
||||||
#if defined(__ANDROID__) || \
|
return HandleStrerrorR(strerror_r(e, err_str, sizeof(err_str)), err_str);
|
||||||
(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
|
#endif // _WIN32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+66
-13
@@ -5,6 +5,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include "common/container/unordered_map.h"
|
#include "common/container/unordered_map.h"
|
||||||
@@ -480,28 +481,80 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se
|
|||||||
[type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),
|
[type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),
|
||||||
path.end());
|
path.end());
|
||||||
|
|
||||||
const bool absolute = !path.empty() && path[0] == type2;
|
std::string root;
|
||||||
std::vector<std::string_view> parts;
|
std::string_view components{path};
|
||||||
|
bool drive_relative = false;
|
||||||
|
|
||||||
for (const auto part : SplitPathComponents(path))
|
#ifdef _WIN32
|
||||||
{
|
const bool network = path.size() > 1 && path[0] == type2 && path[1] == type2;
|
||||||
if (part.empty() || part == ".")
|
const bool drive =
|
||||||
continue;
|
path.size() > 1 && std::isalpha(static_cast<unsigned char>(path[0])) && path[1] == ':';
|
||||||
if (part == ".." && !parts.empty() && parts.back() != "..")
|
|
||||||
parts.pop_back();
|
if (network) {
|
||||||
else if (part != "..") parts.push_back(part);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string resolved = absolute ? std::string(1, type2) : std::string{};
|
const auto path_parts = SplitPathComponents(components);
|
||||||
for (std::size_t i = 0; i < parts.size(); ++i)
|
std::size_t root_component_count = 0;
|
||||||
{
|
#ifdef _WIN32
|
||||||
if (i != 0)
|
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
|
||||||
|
|
||||||
|
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 == ".") {
|
||||||
|
continue;
|
||||||
|
} else if (part == "..") {
|
||||||
|
if (parts.size() > root_component_count) {
|
||||||
|
parts.pop_back();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
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))
|
||||||
resolved += type2;
|
resolved += type2;
|
||||||
resolved.append(parts[i].data(), parts[i].size());
|
resolved.append(parts[i].data(), parts[i].size());
|
||||||
}
|
}
|
||||||
|
|
||||||
path = std::move(resolved);
|
path = std::move(resolved);
|
||||||
|
|
||||||
|
if (!path.empty() && path.size() == root_length) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
return std::string(RemoveTrailingSlash(path));
|
return std::string(RemoveTrailingSlash(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -347,8 +347,9 @@ enum class DirectorySeparator {
|
|||||||
// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
|
// 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);
|
[[nodiscard]] std::vector<std::string> SplitPathComponentsCopy(std::string_view filename);
|
||||||
|
|
||||||
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
|
// Normalizes directory separators, removes duplicate and non-root trailing separators, and resolves
|
||||||
// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows
|
// '.' and '..' components without traversing above the path root. Windows drive and UNC roots are
|
||||||
|
// preserved.
|
||||||
[[nodiscard]] std::string SanitizePath(
|
[[nodiscard]] std::string SanitizePath(
|
||||||
std::string_view path,
|
std::string_view path,
|
||||||
DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
|
DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
|
||||||
|
|||||||
+28
-22
@@ -4,17 +4,34 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
#include "common/heap_tracker.h"
|
#include "common/heap_tracker.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "common/memory_detect.h"
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
s64 GetMaxPermissibleResidentMapCount() {
|
||||||
|
// Default value.
|
||||||
|
s64 value = 65530;
|
||||||
|
|
||||||
|
// Try to read how many mappings we can make.
|
||||||
|
std::ifstream s("/proc/sys/vm/max_map_count");
|
||||||
|
s >> value;
|
||||||
|
|
||||||
|
// Print, for debug.
|
||||||
|
LOG_INFO(HW_Memory, "Current maximum map count: {}", value);
|
||||||
|
|
||||||
|
// Allow 20000 maps for other code and to account for split inaccuracy.
|
||||||
|
return std::max<s64>(value - 20000, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
HeapTracker::HeapTracker(Common::HostMemory& buffer)
|
HeapTracker::HeapTracker(Common::HostMemory& buffer)
|
||||||
: m_buffer(buffer),
|
: m_buffer(buffer), m_max_resident_map_count(GetMaxPermissibleResidentMapCount()) {}
|
||||||
m_has_hardware_buffer_backing(!buffer.BackingHardwareBuffers().empty()),
|
|
||||||
m_max_resident_map_count(static_cast<s64>(GetPermissibleMapCount())) {}
|
|
||||||
HeapTracker::~HeapTracker() = default;
|
HeapTracker::~HeapTracker() = default;
|
||||||
|
|
||||||
void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length,
|
void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length,
|
||||||
@@ -68,8 +85,7 @@ void HeapTracker::Unmap(size_t virtual_offset, size_t size, bool is_separate_hea
|
|||||||
|
|
||||||
// If resident, erase from resident map.
|
// If resident, erase from resident map.
|
||||||
if (item->is_resident) {
|
if (item->is_resident) {
|
||||||
m_resident_map_count -= this->HostMapCount(item->paddr, item->size);
|
ASSERT(--m_resident_map_count >= 0);
|
||||||
ASSERT(m_resident_map_count >= 0);
|
|
||||||
m_resident_mappings.erase(m_resident_mappings.iterator_to(*item));
|
m_resident_mappings.erase(m_resident_mappings.iterator_to(*item));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,7 +191,7 @@ bool HeapTracker::DeferredMapSeparateHeap(size_t virtual_offset) {
|
|||||||
|
|
||||||
// This map is now resident.
|
// This map is now resident.
|
||||||
it->is_resident = true;
|
it->is_resident = true;
|
||||||
m_resident_map_count += this->HostMapCount(it->paddr, it->size);
|
m_resident_map_count++;
|
||||||
m_resident_mappings.insert(*it);
|
m_resident_mappings.insert(*it);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,17 +213,17 @@ void HeapTracker::RebuildSeparateHeapAddressSpace() {
|
|||||||
// Despite being worse in theory, this has proven to be better in practice than more
|
// Despite being worse in theory, this has proven to be better in practice than more
|
||||||
// regularly dumping a smaller amount, because it significantly reduces average case
|
// regularly dumping a smaller amount, because it significantly reduces average case
|
||||||
// lock contention.
|
// lock contention.
|
||||||
s64 const desired_count = (std::min)(m_resident_map_count, m_max_resident_map_count) / 2;
|
std::size_t const desired_count = (std::min)(m_resident_map_count, m_max_resident_map_count) / 2;
|
||||||
|
std::size_t const evict_count = m_resident_map_count - desired_count;
|
||||||
auto it = m_resident_mappings.begin();
|
auto it = m_resident_mappings.begin();
|
||||||
|
|
||||||
while (m_resident_map_count > desired_count && it != m_resident_mappings.end()) {
|
for (size_t i = 0; i < evict_count && it != m_resident_mappings.end(); i++) {
|
||||||
// Unmark and unmap.
|
// Unmark and unmap.
|
||||||
it->is_resident = false;
|
it->is_resident = false;
|
||||||
m_buffer.Unmap(it->vaddr, it->size, false);
|
m_buffer.Unmap(it->vaddr, it->size, false);
|
||||||
|
|
||||||
// Advance.
|
// Advance.
|
||||||
m_resident_map_count -= this->HostMapCount(it->paddr, it->size);
|
ASSERT(--m_resident_map_count >= 0);
|
||||||
ASSERT(m_resident_map_count >= 0);
|
|
||||||
it = m_resident_mappings.erase(it);
|
it = m_resident_mappings.erase(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -229,7 +245,6 @@ void HeapTracker::SplitHeapMapLocked(VAddr offset) {
|
|||||||
// Cache the original values.
|
// Cache the original values.
|
||||||
auto* const left = std::addressof(*it);
|
auto* const left = std::addressof(*it);
|
||||||
const size_t orig_size = left->size;
|
const size_t orig_size = left->size;
|
||||||
const s64 orig_host_map_count = this->HostMapCount(left->paddr, orig_size);
|
|
||||||
|
|
||||||
// Adjust the left map.
|
// Adjust the left map.
|
||||||
const size_t left_size = offset - left->vaddr;
|
const size_t left_size = offset - left->vaddr;
|
||||||
@@ -251,20 +266,11 @@ void HeapTracker::SplitHeapMapLocked(VAddr offset) {
|
|||||||
|
|
||||||
// If resident, also insert into resident map.
|
// If resident, also insert into resident map.
|
||||||
if (right->is_resident) {
|
if (right->is_resident) {
|
||||||
m_resident_map_count += this->HostMapCount(left->paddr, left->size) +
|
m_resident_map_count++;
|
||||||
this->HostMapCount(right->paddr, right->size) -
|
|
||||||
orig_host_map_count;
|
|
||||||
m_resident_mappings.insert(*right);
|
m_resident_mappings.insert(*right);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s64 HeapTracker::HostMapCount(PAddr paddr, size_t size) const {
|
|
||||||
if (!m_has_hardware_buffer_backing) {
|
|
||||||
return size != 0 ? 1 : 0;
|
|
||||||
}
|
|
||||||
return static_cast<s64>(m_buffer.BackingMapCount(paddr, size));
|
|
||||||
}
|
|
||||||
|
|
||||||
HeapTracker::AddrTree::iterator HeapTracker::GetNearestHeapMapLocked(VAddr offset) {
|
HeapTracker::AddrTree::iterator HeapTracker::GetNearestHeapMapLocked(VAddr offset) {
|
||||||
const SeparateHeapMap key{
|
const SeparateHeapMap key{
|
||||||
.vaddr = offset,
|
.vaddr = offset,
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -85,13 +82,10 @@ private:
|
|||||||
|
|
||||||
AddrTree::iterator GetNearestHeapMapLocked(VAddr offset);
|
AddrTree::iterator GetNearestHeapMapLocked(VAddr offset);
|
||||||
|
|
||||||
s64 HostMapCount(PAddr paddr, size_t size) const;
|
|
||||||
|
|
||||||
void RebuildSeparateHeapAddressSpace();
|
void RebuildSeparateHeapAddressSpace();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Common::HostMemory& m_buffer;
|
Common::HostMemory& m_buffer;
|
||||||
const bool m_has_hardware_buffer_backing;
|
|
||||||
const s64 m_max_resident_map_count;
|
const s64 m_max_resident_map_count;
|
||||||
|
|
||||||
std::shared_mutex m_rebuild_lock{};
|
std::shared_mutex m_rebuild_lock{};
|
||||||
|
|||||||
+6
-393
@@ -51,65 +51,14 @@
|
|||||||
|
|
||||||
#endif // ^^^ POSIX ^^^
|
#endif // ^^^ POSIX ^^^
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "common/alignment.h"
|
#include "common/alignment.h"
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/free_region_manager.h"
|
#include "common/free_region_manager.h"
|
||||||
#include "common/host_memory.h"
|
#include "common/host_memory.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "common/memory_detect.h"
|
|
||||||
#include "common/settings.h"
|
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
#include <cerrno>
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <android/hardware_buffer.h>
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
struct NativeHandle {
|
|
||||||
int version;
|
|
||||||
int numFds;
|
|
||||||
int numInts;
|
|
||||||
int data[1];
|
|
||||||
};
|
|
||||||
|
|
||||||
using PFN_AHardwareBuffer_getNativeHandle = const NativeHandle* (*)(const AHardwareBuffer*);
|
|
||||||
|
|
||||||
PFN_AHardwareBuffer_getNativeHandle ResolveGetNativeHandle() {
|
|
||||||
void* const lib = dlopen("libnativewindow.so", RTLD_NOW);
|
|
||||||
if (lib == nullptr) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return reinterpret_cast<PFN_AHardwareBuffer_getNativeHandle>(
|
|
||||||
dlsym(lib, "AHardwareBuffer_getNativeHandle"));
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DmaBufSync {
|
|
||||||
u64 flags;
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr u64 DmaBufSyncRead = 1ULL << 0;
|
|
||||||
constexpr u64 DmaBufSyncWrite = 1ULL << 1;
|
|
||||||
constexpr u64 DmaBufSyncStart = 0ULL << 2;
|
|
||||||
constexpr u64 DmaBufSyncEnd = 1ULL << 2;
|
|
||||||
|
|
||||||
void SyncDmaBufCpuAccess(int fd, u64 phase) {
|
|
||||||
DmaBufSync sync{.flags = phase | DmaBufSyncRead | DmaBufSyncWrite};
|
|
||||||
while (ioctl(fd, _IOW('b', 0, DmaBufSync), &sync) != 0) {
|
|
||||||
if (errno != EINTR) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__ANDROID__) && __ANDROID_API__ < 30
|
#if defined(__ANDROID__) && __ANDROID_API__ < 30
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
@@ -126,12 +75,6 @@ namespace Common {
|
|||||||
[[maybe_unused]] constexpr size_t PageAlignment = 0x1000;
|
[[maybe_unused]] constexpr size_t PageAlignment = 0x1000;
|
||||||
[[maybe_unused]] constexpr size_t HugePageSize = 0x200000;
|
[[maybe_unused]] constexpr size_t HugePageSize = 0x200000;
|
||||||
|
|
||||||
static std::atomic<u64> committed_backing_size{};
|
|
||||||
|
|
||||||
u64 GetCommittedBackingSize() noexcept {
|
|
||||||
return committed_backing_size.load(std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
// Manually imported for MinGW compatibility
|
// Manually imported for MinGW compatibility
|
||||||
@@ -179,7 +122,7 @@ static void GetFuncAddress(Common::DynamicLibrary& dll, const char* name, T& pfn
|
|||||||
|
|
||||||
class HostMemory::Impl {
|
class HostMemory::Impl {
|
||||||
public:
|
public:
|
||||||
explicit Impl(size_t backing_size_, size_t virtual_size_, size_t)
|
explicit Impl(size_t backing_size_, size_t virtual_size_)
|
||||||
: backing_size{backing_size_}
|
: backing_size{backing_size_}
|
||||||
, virtual_size{virtual_size_}
|
, virtual_size{virtual_size_}
|
||||||
, process{GetCurrentProcess()}
|
, process{GetCurrentProcess()}
|
||||||
@@ -293,10 +236,6 @@ public:
|
|||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsBackingShared() const noexcept {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t backing_size; ///< Size of the backing memory in bytes
|
const size_t backing_size; ///< Size of the backing memory in bytes
|
||||||
const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
|
const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
|
||||||
|
|
||||||
@@ -573,10 +512,9 @@ static int shm_open_anon(int flags, mode_t mode) {
|
|||||||
|
|
||||||
class HostMemory::Impl {
|
class HostMemory::Impl {
|
||||||
public:
|
public:
|
||||||
explicit Impl(size_t backing_size_, size_t virtual_size_, size_t preferred_offset_)
|
explicit Impl(size_t backing_size_, size_t virtual_size_)
|
||||||
: backing_size{backing_size_}
|
: backing_size{backing_size_}
|
||||||
, virtual_size{virtual_size_}
|
, virtual_size{virtual_size_}
|
||||||
, preferred_offset{preferred_offset_}
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool Init() {
|
bool Init() {
|
||||||
@@ -616,15 +554,10 @@ public:
|
|||||||
LOG_WARNING(Common_Memory, "Using private mappings instead of shared ones");
|
LOG_WARNING(Common_Memory, "Using private mappings instead of shared ones");
|
||||||
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_NOCORE, -1, 0));
|
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_NOCORE, -1, 0));
|
||||||
if (fd > 0) {
|
if (fd > 0) {
|
||||||
|
fd = -1;
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
fd = -1;
|
|
||||||
} else {
|
} else {
|
||||||
#ifdef __ANDROID__
|
|
||||||
if (InitAhbBacking()) {
|
|
||||||
return InitVirtual();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NOCORE, fd, 0));
|
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NOCORE, fd, 0));
|
||||||
}
|
}
|
||||||
if (backing_base == MAP_FAILED) {
|
if (backing_base == MAP_FAILED) {
|
||||||
@@ -632,10 +565,7 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return InitVirtual();
|
// Virtual memory initialization
|
||||||
}
|
|
||||||
|
|
||||||
bool InitVirtual() {
|
|
||||||
virtual_base = virtual_map_base = static_cast<u8*>(ChooseVirtualBase(virtual_size));
|
virtual_base = virtual_map_base = static_cast<u8*>(ChooseVirtualBase(virtual_size));
|
||||||
if (virtual_base == MAP_FAILED) {
|
if (virtual_base == MAP_FAILED) {
|
||||||
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
|
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
|
||||||
@@ -648,244 +578,6 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
static AHardwareBuffer_Desc MakeBlobDesc(size_t len) {
|
|
||||||
return AHardwareBuffer_Desc{
|
|
||||||
.width = static_cast<u32>(len),
|
|
||||||
.height = 1,
|
|
||||||
.layers = 1,
|
|
||||||
.format = AHARDWAREBUFFER_FORMAT_BLOB,
|
|
||||||
.usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
|
|
||||||
AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
|
|
||||||
AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER,
|
|
||||||
.stride = 0,
|
|
||||||
.rfu0 = 0,
|
|
||||||
.rfu1 = 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool ProbeAhbBacking(PFN_AHardwareBuffer_getNativeHandle get_native_handle) {
|
|
||||||
const AHardwareBuffer_Desc desc = MakeBlobDesc(PageAlignment * 2);
|
|
||||||
AHardwareBuffer* buffer{};
|
|
||||||
if (AHardwareBuffer_allocate(&desc, &buffer) != 0 || buffer == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const NativeHandle* const handle = get_native_handle(buffer);
|
|
||||||
if (handle == nullptr || handle->numFds < 1) {
|
|
||||||
AHardwareBuffer_release(buffer);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const int probe_fd = handle->data[0];
|
|
||||||
bool ok = true;
|
|
||||||
const auto try_map = [&](int prot, off_t offset) {
|
|
||||||
if (!ok) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
void* const ptr = mmap(nullptr, PageAlignment, prot, MAP_SHARED, probe_fd, offset);
|
|
||||||
if (ptr == MAP_FAILED) {
|
|
||||||
ok = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
munmap(ptr, PageAlignment);
|
|
||||||
};
|
|
||||||
try_map(PROT_READ | PROT_WRITE, 0);
|
|
||||||
try_map(PROT_READ | PROT_WRITE, static_cast<off_t>(PageAlignment));
|
|
||||||
#ifdef ARCHITECTURE_arm64
|
|
||||||
try_map(PROT_READ | PROT_EXEC, 0);
|
|
||||||
#endif
|
|
||||||
AHardwareBuffer_release(buffer);
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t ComputeAhbBudget(size_t window_size) const {
|
|
||||||
const u64 total_physical = Common::GetMemInfo().TotalPhysicalMemory;
|
|
||||||
constexpr u64 BaselineFootprint = 6ULL << 30;
|
|
||||||
if (total_physical <= BaselineFootprint) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
const u64 permissible_maps = Common::GetPermissibleMapCount();
|
|
||||||
if (permissible_maps == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
u64 budget = (total_physical - BaselineFootprint) / 2;
|
|
||||||
constexpr u64 MapSlotsPerWindow = 64;
|
|
||||||
const u64 affordable_windows = permissible_maps / MapSlotsPerWindow;
|
|
||||||
budget = (std::min)(budget, affordable_windows * window_size);
|
|
||||||
const u64 available = Common::GetAvailablePhysicalMemory();
|
|
||||||
if (available != 0) {
|
|
||||||
budget = (std::min)(budget, available / 2);
|
|
||||||
}
|
|
||||||
budget = (std::min)(budget, static_cast<u64>(backing_size));
|
|
||||||
budget = Common::AlignDown(budget, window_size);
|
|
||||||
constexpr u64 MinimumBudget = 256ULL << 20;
|
|
||||||
if (budget < MinimumBudget) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return static_cast<size_t>(budget);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InitAhbBacking() {
|
|
||||||
if (!Settings::values.use_unified_memory.GetValue()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
static const PFN_AHardwareBuffer_getNativeHandle get_native_handle =
|
|
||||||
ResolveGetNativeHandle();
|
|
||||||
if (get_native_handle == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
constexpr size_t window_size = 256ULL << 20;
|
|
||||||
const size_t budget = ComputeAhbBudget(window_size);
|
|
||||||
if (budget == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!ProbeAhbBacking(get_native_handle)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const size_t aligned_backing = Common::AlignDown(backing_size, window_size);
|
|
||||||
const size_t max_windows = (std::min)(budget, aligned_backing) / window_size;
|
|
||||||
|
|
||||||
std::vector<AHardwareBuffer*> buffers;
|
|
||||||
std::vector<int> buffer_fds;
|
|
||||||
const auto cleanup = [&] {
|
|
||||||
for (AHardwareBuffer* buffer : buffers) {
|
|
||||||
AHardwareBuffer_release(buffer);
|
|
||||||
}
|
|
||||||
buffers.clear();
|
|
||||||
buffer_fds.clear();
|
|
||||||
};
|
|
||||||
for (size_t i = 0; i < max_windows; ++i) {
|
|
||||||
const AHardwareBuffer_Desc desc = MakeBlobDesc(window_size);
|
|
||||||
AHardwareBuffer* buffer{};
|
|
||||||
if (AHardwareBuffer_allocate(&desc, &buffer) != 0 || buffer == nullptr) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const NativeHandle* const handle = get_native_handle(buffer);
|
|
||||||
if (handle == nullptr || handle->numFds < 1) {
|
|
||||||
AHardwareBuffer_release(buffer);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const int buffer_fd = handle->data[0];
|
|
||||||
const off_t buffer_len = lseek(buffer_fd, 0, SEEK_END);
|
|
||||||
if (buffer_len < static_cast<off_t>(window_size)) {
|
|
||||||
AHardwareBuffer_release(buffer);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
buffers.push_back(buffer);
|
|
||||||
buffer_fds.push_back(buffer_fd);
|
|
||||||
}
|
|
||||||
const size_t num_windows = buffers.size();
|
|
||||||
if (num_windows == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const size_t region_size = num_windows * window_size;
|
|
||||||
const size_t region_base = Common::AlignDown(
|
|
||||||
(std::min)(preferred_offset, aligned_backing - region_size), window_size);
|
|
||||||
u8* const base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_NONE,
|
|
||||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0));
|
|
||||||
if (base == MAP_FAILED) {
|
|
||||||
cleanup();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const auto map_over_reservation = [&](size_t offset, size_t len, int map_fd,
|
|
||||||
off_t map_offset) {
|
|
||||||
if (len == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (mmap(base + offset, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, map_fd,
|
|
||||||
map_offset) == MAP_FAILED) {
|
|
||||||
munmap(base, backing_size);
|
|
||||||
cleanup();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
if (!map_over_reservation(0, region_base, fd, 0)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (size_t i = 0; i < num_windows; ++i) {
|
|
||||||
if (!map_over_reservation(region_base + i * window_size, window_size, buffer_fds[i],
|
|
||||||
0)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const size_t tail_offset = region_base + region_size;
|
|
||||||
if (!map_over_reservation(tail_offset, backing_size - tail_offset, fd,
|
|
||||||
static_cast<off_t>(tail_offset))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
backing_base = base;
|
|
||||||
ahb_windows = std::move(buffers);
|
|
||||||
ahb_fds = std::move(buffer_fds);
|
|
||||||
ahb_window_size = window_size;
|
|
||||||
ahb_base = region_base;
|
|
||||||
ahb_bytes = region_size;
|
|
||||||
committed_backing_size.store(region_size, std::memory_order_relaxed);
|
|
||||||
for (const int window_fd : ahb_fds) {
|
|
||||||
SyncDmaBufCpuAccess(window_fd, DmaBufSyncStart);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MapBackingRange(size_t virtual_offset, size_t host_offset, size_t length, int prot_flags) {
|
|
||||||
while (length > 0) {
|
|
||||||
int map_fd = fd;
|
|
||||||
off_t map_offset = static_cast<off_t>(host_offset);
|
|
||||||
size_t chunk = length;
|
|
||||||
if (host_offset < ahb_base) {
|
|
||||||
chunk = (std::min)(chunk, ahb_base - host_offset);
|
|
||||||
} else if (host_offset < ahb_base + ahb_bytes) {
|
|
||||||
const size_t relative = host_offset - ahb_base;
|
|
||||||
const size_t window = relative / ahb_window_size;
|
|
||||||
const size_t local = relative % ahb_window_size;
|
|
||||||
map_fd = ahb_fds[window];
|
|
||||||
map_offset = static_cast<off_t>(local);
|
|
||||||
chunk = (std::min)(chunk, ahb_window_size - local);
|
|
||||||
}
|
|
||||||
void* const ret = mmap(virtual_base + virtual_offset, chunk, prot_flags,
|
|
||||||
MAP_SHARED | MAP_FIXED, map_fd, map_offset);
|
|
||||||
ASSERT_MSG(ret != MAP_FAILED, "mmap: {}", strerror(errno));
|
|
||||||
virtual_offset += chunk;
|
|
||||||
host_offset += chunk;
|
|
||||||
length -= chunk;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t BackingMapCount(size_t host_offset, size_t length) const noexcept {
|
|
||||||
if (length == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (ahb_bytes == 0) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
size_t count = 0;
|
|
||||||
while (length > 0) {
|
|
||||||
size_t chunk = length;
|
|
||||||
if (host_offset < ahb_base) {
|
|
||||||
chunk = (std::min)(chunk, ahb_base - host_offset);
|
|
||||||
} else if (host_offset < ahb_base + ahb_bytes) {
|
|
||||||
const size_t local = (host_offset - ahb_base) % ahb_window_size;
|
|
||||||
chunk = (std::min)(chunk, ahb_window_size - local);
|
|
||||||
}
|
|
||||||
host_offset += chunk;
|
|
||||||
length -= chunk;
|
|
||||||
++count;
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::span<AHardwareBuffer* const> AhbWindows() const noexcept {
|
|
||||||
return ahb_windows;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t AhbWindowSize() const noexcept {
|
|
||||||
return ahb_bytes != 0 ? ahb_window_size : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t AhbBase() const noexcept {
|
|
||||||
return ahb_base;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
~Impl() {
|
~Impl() {
|
||||||
Release();
|
Release();
|
||||||
}
|
}
|
||||||
@@ -914,12 +606,6 @@ public:
|
|||||||
#ifdef ARCHITECTURE_arm64
|
#ifdef ARCHITECTURE_arm64
|
||||||
if (True(perms & MemoryPermission::Execute))
|
if (True(perms & MemoryPermission::Execute))
|
||||||
prot_flags |= PROT_EXEC;
|
prot_flags |= PROT_EXEC;
|
||||||
#endif
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
if (ahb_bytes != 0) {
|
|
||||||
MapBackingRange(virtual_offset, host_offset, length, prot_flags);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
int flags = (fd >= 0 ? MAP_SHARED : MAP_PRIVATE) | MAP_FIXED;
|
int flags = (fd >= 0 ? MAP_SHARED : MAP_PRIVATE) | MAP_FIXED;
|
||||||
void* ret = mmap(virtual_base + virtual_offset, length, prot_flags, flags, fd, host_offset);
|
void* ret = mmap(virtual_base + virtual_offset, length, prot_flags, flags, fd, host_offset);
|
||||||
@@ -965,18 +651,8 @@ public:
|
|||||||
virtual_base = nullptr;
|
virtual_base = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsBackingShared() const noexcept {
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
if (ahb_bytes != 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return fd >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t backing_size; ///< Size of the backing memory in bytes
|
const size_t backing_size; ///< Size of the backing memory in bytes
|
||||||
const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
|
const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
|
||||||
const size_t preferred_offset;
|
|
||||||
|
|
||||||
u8* backing_base{reinterpret_cast<u8*>(MAP_FAILED)};
|
u8* backing_base{reinterpret_cast<u8*>(MAP_FAILED)};
|
||||||
u8* virtual_base{reinterpret_cast<u8*>(MAP_FAILED)};
|
u8* virtual_base{reinterpret_cast<u8*>(MAP_FAILED)};
|
||||||
@@ -999,21 +675,6 @@ private:
|
|||||||
int ret = close(fd);
|
int ret = close(fd);
|
||||||
ASSERT_MSG(ret == 0, "close failed: {}", strerror(errno));
|
ASSERT_MSG(ret == 0, "close failed: {}", strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
for (const int window_fd : ahb_fds) {
|
|
||||||
SyncDmaBufCpuAccess(window_fd, DmaBufSyncEnd);
|
|
||||||
}
|
|
||||||
for (AHardwareBuffer* buffer : ahb_windows) {
|
|
||||||
AHardwareBuffer_release(buffer);
|
|
||||||
}
|
|
||||||
ahb_windows.clear();
|
|
||||||
ahb_fds.clear();
|
|
||||||
if (ahb_bytes != 0) {
|
|
||||||
committed_backing_size.store(0, std::memory_order_relaxed);
|
|
||||||
ahb_bytes = 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdjustMap(size_t* virtual_offset, size_t* length) {
|
void AdjustMap(size_t* virtual_offset, size_t* length) {
|
||||||
@@ -1039,19 +700,11 @@ private:
|
|||||||
|
|
||||||
int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create
|
int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create
|
||||||
FreeRegionManager free_manager{};
|
FreeRegionManager free_manager{};
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
std::vector<AHardwareBuffer*> ahb_windows;
|
|
||||||
std::vector<int> ahb_fds;
|
|
||||||
size_t ahb_window_size{};
|
|
||||||
size_t ahb_base{};
|
|
||||||
size_t ahb_bytes{};
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ^^^ POSIX ^^^
|
#endif // ^^^ POSIX ^^^
|
||||||
|
|
||||||
HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_, size_t preferred_offset_)
|
HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
|
||||||
: backing_size(backing_size_)
|
: backing_size(backing_size_)
|
||||||
, virtual_size(virtual_size_)
|
, virtual_size(virtual_size_)
|
||||||
{
|
{
|
||||||
@@ -1061,7 +714,7 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_, size_t prefer
|
|||||||
virtual_base = nullptr;
|
virtual_base = nullptr;
|
||||||
#else
|
#else
|
||||||
// Try to allocate a fastmem arena.
|
// Try to allocate a fastmem arena.
|
||||||
impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment), AlignUp(virtual_size, PageAlignment) + HugePageSize, preferred_offset_);
|
impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment), AlignUp(virtual_size, PageAlignment) + HugePageSize);
|
||||||
if (impl->Init()) {
|
if (impl->Init()) {
|
||||||
backing_base = impl->backing_base;
|
backing_base = impl->backing_base;
|
||||||
virtual_base = impl->virtual_base;
|
virtual_base = impl->virtual_base;
|
||||||
@@ -1141,46 +794,6 @@ void HostMemory::ClearBackingRegion(size_t physical_offset, size_t length, u32 f
|
|||||||
std::memset(backing_base + physical_offset, fill_value, length);
|
std::memset(backing_base + physical_offset, fill_value, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::span<AHardwareBuffer* const> HostMemory::BackingHardwareBuffers() const noexcept {
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
return impl ? impl->AhbWindows() : std::span<AHardwareBuffer* const>{};
|
|
||||||
#else
|
|
||||||
return {};
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t HostMemory::BackingMapCount(size_t host_offset, size_t length) const noexcept {
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
return impl ? impl->BackingMapCount(host_offset, length) : (length != 0 ? 1 : 0);
|
|
||||||
#else
|
|
||||||
return length != 0 ? 1 : 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t HostMemory::BackingHardwareBufferWindowSize() const noexcept {
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
return impl ? impl->AhbWindowSize() : 0;
|
|
||||||
#else
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HostMemory::IsBackingShared() const noexcept {
|
|
||||||
#if defined(__OPENORBIS__) || defined(__managarm__)
|
|
||||||
return false;
|
|
||||||
#else
|
|
||||||
return impl && impl->IsBackingShared();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t HostMemory::BackingHardwareBufferBase() const noexcept {
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
return impl ? impl->AhbBase() : 0;
|
|
||||||
#else
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void HostMemory::EnableDirectMappedAddress() {
|
void HostMemory::EnableDirectMappedAddress() {
|
||||||
#if !(defined(__OPENORBIS__) || defined(__managarm__))
|
#if !(defined(__OPENORBIS__) || defined(__managarm__))
|
||||||
if (impl) {
|
if (impl) {
|
||||||
|
|||||||
@@ -8,16 +8,11 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <span>
|
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
struct AHardwareBuffer;
|
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
[[nodiscard]] u64 GetCommittedBackingSize() noexcept;
|
|
||||||
|
|
||||||
enum class MemoryPermission : u32 {
|
enum class MemoryPermission : u32 {
|
||||||
Read = 1 << 0,
|
Read = 1 << 0,
|
||||||
Write = 1 << 1,
|
Write = 1 << 1,
|
||||||
@@ -32,7 +27,7 @@ DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission)
|
|||||||
*/
|
*/
|
||||||
class HostMemory {
|
class HostMemory {
|
||||||
public:
|
public:
|
||||||
explicit HostMemory(size_t backing_size_, size_t virtual_size_, size_t preferred_offset_ = 0);
|
explicit HostMemory(size_t backing_size_, size_t virtual_size_);
|
||||||
~HostMemory();
|
~HostMemory();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,20 +61,6 @@ public:
|
|||||||
return backing_base;
|
return backing_base;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] size_t BackingSize() const noexcept {
|
|
||||||
return backing_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] size_t BackingMapCount(size_t host_offset, size_t length) const noexcept;
|
|
||||||
|
|
||||||
[[nodiscard]] std::span<AHardwareBuffer* const> BackingHardwareBuffers() const noexcept;
|
|
||||||
|
|
||||||
[[nodiscard]] size_t BackingHardwareBufferWindowSize() const noexcept;
|
|
||||||
|
|
||||||
[[nodiscard]] size_t BackingHardwareBufferBase() const noexcept;
|
|
||||||
|
|
||||||
[[nodiscard]] bool IsBackingShared() const noexcept;
|
|
||||||
|
|
||||||
[[nodiscard]] u8* VirtualBasePointer() noexcept {
|
[[nodiscard]] u8* VirtualBasePointer() noexcept {
|
||||||
return virtual_base;
|
return virtual_base;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -20,10 +17,6 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
#include "common/memory_detect.h"
|
#include "common/memory_detect.h"
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
@@ -76,61 +69,4 @@ const MemoryInfo& GetMemInfo() {
|
|||||||
return mem_info;
|
return mem_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 GetPermissibleMapCount() {
|
|
||||||
constexpr u64 DefaultMapCount = 65530;
|
|
||||||
constexpr u64 ReservedMaps = 20000;
|
|
||||||
u64 count = DefaultMapCount;
|
|
||||||
#ifdef __linux__
|
|
||||||
if (std::FILE* const file = std::fopen("/proc/sys/vm/max_map_count", "re")) {
|
|
||||||
char line[32];
|
|
||||||
if (std::fgets(line, sizeof(line), file) != nullptr) {
|
|
||||||
const u64 parsed = std::strtoull(line, nullptr, 10);
|
|
||||||
if (parsed != 0) {
|
|
||||||
count = parsed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::fclose(file);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (count <= ReservedMaps) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return count - ReservedMaps;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 GetAvailablePhysicalMemory() {
|
|
||||||
#ifdef _WIN32
|
|
||||||
MEMORYSTATUSEX memorystatus;
|
|
||||||
memorystatus.dwLength = sizeof(memorystatus);
|
|
||||||
if (GlobalMemoryStatusEx(&memorystatus) == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return memorystatus.ullAvailPhys;
|
|
||||||
#elif defined(__linux__)
|
|
||||||
static constexpr char AvailableKey[] = "MemAvailable:";
|
|
||||||
if (std::FILE* const file = std::fopen("/proc/meminfo", "re")) {
|
|
||||||
char line[256];
|
|
||||||
u64 available = 0;
|
|
||||||
while (std::fgets(line, sizeof(line), file) != nullptr) {
|
|
||||||
if (std::strncmp(line, AvailableKey, sizeof(AvailableKey) - 1) != 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
available = std::strtoull(line + sizeof(AvailableKey) - 1, nullptr, 10) * 1024;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
std::fclose(file);
|
|
||||||
if (available != 0) {
|
|
||||||
return available;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
struct sysinfo meminfo;
|
|
||||||
if (sysinfo(&meminfo) != 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return static_cast<u64>(meminfo.freeram) * static_cast<u64>(meminfo.mem_unit);
|
|
||||||
#else
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -21,8 +18,4 @@ struct MemoryInfo {
|
|||||||
*/
|
*/
|
||||||
[[nodiscard]] const MemoryInfo& GetMemInfo();
|
[[nodiscard]] const MemoryInfo& GetMemInfo();
|
||||||
|
|
||||||
[[nodiscard]] u64 GetPermissibleMapCount();
|
|
||||||
|
|
||||||
[[nodiscard]] u64 GetAvailablePhysicalMemory();
|
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|||||||
@@ -96,15 +96,15 @@ struct PageTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Write page info atomically
|
/// Write page info atomically
|
||||||
constexpr void Store(bool marked, PageType type, u16 block, uintptr_t pointer) noexcept {
|
inline void Store(bool marked, PageType type, u16 block, uintptr_t pointer) noexcept {
|
||||||
data_raw.store(std::bit_cast<u64>(Data{marked, type, block, pointer}));
|
data_raw.store(std::bit_cast<u64>(Data{marked, type, block, pointer}));
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr void MarkRasterizerCached() noexcept {
|
inline void MarkRasterizerCached() noexcept {
|
||||||
data_raw.fetch_or(0b111);
|
data_raw.fetch_or(0b111);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr void MarkDebug(u64 ptr, u16 block) noexcept {
|
inline void MarkDebug(u64 ptr, u16 block) noexcept {
|
||||||
Store(true, PageType::DebugMemory, block, ptr);
|
Store(true, PageType::DebugMemory, block, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -666,9 +666,6 @@ struct Values {
|
|||||||
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
|
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
|
||||||
Category::RendererHacks};
|
Category::RendererHacks};
|
||||||
|
|
||||||
SwitchableSetting<bool> use_unified_memory{linkage, false, "use_unified_memory",
|
|
||||||
Category::RendererHacks};
|
|
||||||
|
|
||||||
SwitchableSetting<GpuUnswizzleSize> gpu_unswizzle_texture_size{linkage,
|
SwitchableSetting<GpuUnswizzleSize> gpu_unswizzle_texture_size{linkage,
|
||||||
GpuUnswizzleSize::Large,
|
GpuUnswizzleSize::Large,
|
||||||
"gpu_unswizzle_texture_size",
|
"gpu_unswizzle_texture_size",
|
||||||
|
|||||||
@@ -1,178 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "common/common_types.h"
|
|
||||||
|
|
||||||
namespace Network {
|
|
||||||
|
|
||||||
/// Address families
|
|
||||||
enum class Domain : u8 {
|
|
||||||
Unspecified, ///< Represents 0, used in getaddrinfo hints
|
|
||||||
INET, ///< Address family for IPv4
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Socket types
|
|
||||||
enum class Type {
|
|
||||||
Unspecified, ///< Represents 0, used in getaddrinfo hints
|
|
||||||
STREAM,
|
|
||||||
DGRAM,
|
|
||||||
RAW,
|
|
||||||
SEQPACKET,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Protocol values for sockets
|
|
||||||
enum class Protocol : u8 {
|
|
||||||
Unspecified, ///< Represents 0, usable in various places
|
|
||||||
IP,
|
|
||||||
ICMP,
|
|
||||||
TCP,
|
|
||||||
UDP,
|
|
||||||
IPV6,
|
|
||||||
RAW,
|
|
||||||
IGMP,
|
|
||||||
GGP,
|
|
||||||
IPV4,
|
|
||||||
ST,
|
|
||||||
EGP,
|
|
||||||
PIGP,
|
|
||||||
RCCMON,
|
|
||||||
NVPII,
|
|
||||||
PUP,
|
|
||||||
ARGUS,
|
|
||||||
EMCON,
|
|
||||||
XNET,
|
|
||||||
CHAOS,
|
|
||||||
MUX,
|
|
||||||
MEAS,
|
|
||||||
HMP,
|
|
||||||
PRM,
|
|
||||||
IDP,
|
|
||||||
TRUNK1,
|
|
||||||
TRUNK2,
|
|
||||||
LEAF1,
|
|
||||||
LEAF2,
|
|
||||||
RDP,
|
|
||||||
IRTP,
|
|
||||||
TP,
|
|
||||||
BLT,
|
|
||||||
NSP,
|
|
||||||
INP,
|
|
||||||
DCCP,
|
|
||||||
//TODO: 3PC,
|
|
||||||
IDPR,
|
|
||||||
XTP,
|
|
||||||
DDP,
|
|
||||||
CMTP,
|
|
||||||
TPXX,
|
|
||||||
IL,
|
|
||||||
SDRP,
|
|
||||||
ROUTING,
|
|
||||||
FRAGMENT,
|
|
||||||
IDRP,
|
|
||||||
RSVP,
|
|
||||||
GRE,
|
|
||||||
MHRP,
|
|
||||||
BHA,
|
|
||||||
ESP,
|
|
||||||
AH,
|
|
||||||
INLSP,
|
|
||||||
SWIPE,
|
|
||||||
NHRP,
|
|
||||||
MOBILE,
|
|
||||||
TLSP,
|
|
||||||
SKIP,
|
|
||||||
ICMPV6,
|
|
||||||
NONE,
|
|
||||||
DSTOPTS,
|
|
||||||
AHIP,
|
|
||||||
CFTP,
|
|
||||||
HELLO,
|
|
||||||
SATEXPAK,
|
|
||||||
KRYPTOLAN,
|
|
||||||
RVD,
|
|
||||||
IPPC,
|
|
||||||
ADFS,
|
|
||||||
SATMON,
|
|
||||||
VISA,
|
|
||||||
IPCV,
|
|
||||||
CPNX,
|
|
||||||
CPHB,
|
|
||||||
WSN,
|
|
||||||
PVP,
|
|
||||||
BRSATMON,
|
|
||||||
ND,
|
|
||||||
WBMON,
|
|
||||||
WBEXPAK,
|
|
||||||
EON,
|
|
||||||
VMTP,
|
|
||||||
SVMTP,
|
|
||||||
VINES,
|
|
||||||
TTP,
|
|
||||||
IGP,
|
|
||||||
DGP,
|
|
||||||
TCF,
|
|
||||||
IGRP,
|
|
||||||
OSPFIGP,
|
|
||||||
SRPC,
|
|
||||||
LARP,
|
|
||||||
MTP,
|
|
||||||
AX25,
|
|
||||||
IPEIP,
|
|
||||||
MICP,
|
|
||||||
SCCSP,
|
|
||||||
ETHERIP,
|
|
||||||
ENCAP,
|
|
||||||
APES,
|
|
||||||
GMTP,
|
|
||||||
IPCOMP,
|
|
||||||
SCTP,
|
|
||||||
MH,
|
|
||||||
UDPLITE,
|
|
||||||
HIP,
|
|
||||||
SHIM6,
|
|
||||||
PIM,
|
|
||||||
CARP,
|
|
||||||
PGM,
|
|
||||||
MPLS,
|
|
||||||
PFSYNC
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Shutdown mode
|
|
||||||
enum class ShutdownHow {
|
|
||||||
RD,
|
|
||||||
WR,
|
|
||||||
RDWR,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Array of IPv4 address
|
|
||||||
using IPv4Address = std::array<u8, 4>;
|
|
||||||
|
|
||||||
/// Cross-platform sockaddr structure
|
|
||||||
struct SockAddrIn {
|
|
||||||
Domain family;
|
|
||||||
IPv4Address ip;
|
|
||||||
u16 portno;
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr u32 FLAG_MSG_PEEK = 0x2;
|
|
||||||
constexpr u32 FLAG_MSG_DONTWAIT = 0x80;
|
|
||||||
constexpr u32 FLAG_O_NONBLOCK = 0x800;
|
|
||||||
|
|
||||||
/// Cross-platform addrinfo structure
|
|
||||||
struct AddrInfo {
|
|
||||||
Domain family;
|
|
||||||
Type socket_type;
|
|
||||||
Protocol protocol;
|
|
||||||
SockAddrIn addr;
|
|
||||||
std::optional<std::string> canon_name;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Network
|
|
||||||
@@ -38,7 +38,8 @@ void FreeMemoryPages(void* base, std::size_t size) noexcept;
|
|||||||
|
|
||||||
/// A large page-aligned buffer that has optimized memory usage for zero-writes.
|
/// A large page-aligned buffer that has optimized memory usage for zero-writes.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
requires std::is_trivially_copyable_v<T>
|
// MSVC doesn't regard structs with atomics as trivially copyable
|
||||||
|
// requires std::is_trivially_copyable_v<T>
|
||||||
class SparseLargeVector final {
|
class SparseLargeVector final {
|
||||||
public:
|
public:
|
||||||
constexpr SparseLargeVector() = default;
|
constexpr SparseLargeVector() = default;
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
// 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
|
|
||||||
+2
-2
@@ -208,9 +208,9 @@ UUID UUID::MakeRandomRFC4122V4() {
|
|||||||
return uuid;
|
return uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
UUID UUID::MakeRFC4122V5(std::span<u8, 20> sha1) {
|
UUID UUID::MakeRFC4122V5(std::span<u8, 16> sha1) {
|
||||||
UUID uuid{};
|
UUID uuid{};
|
||||||
std::memcpy(&uuid.uuid, sha1.data(), sizeof(UUID));
|
std::memcpy(&uuid.uuid, sha1.data(), sha1.size());
|
||||||
uuid.uuid[8] = 0x80 | (uuid.uuid[8] & 0x3F);
|
uuid.uuid[8] = 0x80 | (uuid.uuid[8] & 0x3F);
|
||||||
uuid.uuid[6] = 0x50 | (uuid.uuid[6] & 0xF);
|
uuid.uuid[6] = 0x50 | (uuid.uuid[6] & 0xF);
|
||||||
return uuid;
|
return uuid;
|
||||||
|
|||||||
+1
-1
@@ -104,7 +104,7 @@ struct UUID {
|
|||||||
/// @returns A random UUID that is RFC 4122 Version 4 compliant.
|
/// @returns A random UUID that is RFC 4122 Version 4 compliant.
|
||||||
[[nodiscard]] static UUID MakeRandomRFC4122V4();
|
[[nodiscard]] static UUID MakeRandomRFC4122V4();
|
||||||
|
|
||||||
[[nodiscard]] static UUID MakeRFC4122V5(std::span<u8, 20> sha1);
|
[[nodiscard]] static UUID MakeRFC4122V5(std::span<u8, 16> sha1);
|
||||||
|
|
||||||
friend constexpr bool operator==(const UUID& lhs, const UUID& rhs) = default;
|
friend constexpr bool operator==(const UUID& lhs, const UUID& rhs) = default;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -796,6 +796,8 @@ add_library(core STATIC
|
|||||||
hle/service/ns/application_manager_interface.h
|
hle/service/ns/application_manager_interface.h
|
||||||
hle/service/ns/application_version_interface.cpp
|
hle/service/ns/application_version_interface.cpp
|
||||||
hle/service/ns/application_version_interface.h
|
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.cpp
|
||||||
hle/service/ns/content_management_interface.h
|
hle/service/ns/content_management_interface.h
|
||||||
hle/service/ns/develop_interface.cpp
|
hle/service/ns/develop_interface.cpp
|
||||||
@@ -1123,6 +1125,7 @@ add_library(core STATIC
|
|||||||
internal_network/network_interface.h
|
internal_network/network_interface.h
|
||||||
internal_network/socket_proxy.cpp
|
internal_network/socket_proxy.cpp
|
||||||
internal_network/socket_proxy.h
|
internal_network/socket_proxy.h
|
||||||
|
internal_network/socket_types.h
|
||||||
internal_network/sockets.h
|
internal_network/sockets.h
|
||||||
internal_network/wifi_scanner.h
|
internal_network/wifi_scanner.h
|
||||||
launch_timestamp_cache.cpp
|
launch_timestamp_cache.cpp
|
||||||
@@ -1161,6 +1164,12 @@ add_library(core STATIC
|
|||||||
tools/renderdoc.cpp
|
tools/renderdoc.cpp
|
||||||
tools/renderdoc.h)
|
tools/renderdoc.h)
|
||||||
|
|
||||||
|
if (UNIX AND NOT APPLE)
|
||||||
|
target_sources(core PRIVATE
|
||||||
|
internal_network/socket_icmp.cpp
|
||||||
|
internal_network/socket_icmp.h)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (ENABLE_WIFI_SCAN)
|
if (ENABLE_WIFI_SCAN)
|
||||||
target_sources(core PRIVATE internal_network/wifi_scanner.cpp)
|
target_sources(core PRIVATE internal_network/wifi_scanner.cpp)
|
||||||
if (LINUX)
|
if (LINUX)
|
||||||
|
|||||||
@@ -22,21 +22,15 @@ DynarmicCallbacks32::DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess
|
|||||||
, m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()}
|
, m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()}
|
||||||
{}
|
{}
|
||||||
|
|
||||||
u8 DynarmicCallbacks32::MemoryRead8(u32 vaddr) {
|
u64 DynarmicCallbacks32::MemoryRead(u32 vaddr, size_t size) {
|
||||||
CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
|
CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Read);
|
||||||
return m_memory.Read8(vaddr);
|
switch (size) {
|
||||||
}
|
case sizeof(u64): return m_memory.Read64(vaddr);
|
||||||
u16 DynarmicCallbacks32::MemoryRead16(u32 vaddr) {
|
case sizeof(u32): return m_memory.Read32(vaddr);
|
||||||
CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
|
case sizeof(u16): return m_memory.Read16(vaddr);
|
||||||
return m_memory.Read16(vaddr);
|
case sizeof(u8): return m_memory.Read8(vaddr);
|
||||||
}
|
default: UNREACHABLE();
|
||||||
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) {
|
std::optional<u32> DynarmicCallbacks32::MemoryReadCode(u32 vaddr) {
|
||||||
@@ -50,27 +44,17 @@ std::optional<u32> DynarmicCallbacks32::MemoryReadCode(u32 vaddr) {
|
|||||||
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
|
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
|
||||||
}
|
}
|
||||||
|
|
||||||
void DynarmicCallbacks32::MemoryWrite8(u32 vaddr, u8 value) {
|
void DynarmicCallbacks32::MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) {
|
||||||
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
|
if (CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Write)) {
|
||||||
m_memory.Write8(vaddr, value);
|
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::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) {
|
bool DynarmicCallbacks32::MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) {
|
||||||
return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) &&
|
return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) &&
|
||||||
m_memory.WriteExclusive8(vaddr, value, expected);
|
m_memory.WriteExclusive8(vaddr, value, expected);
|
||||||
@@ -177,7 +161,7 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
|
|||||||
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NumPageTableEntries>*>(
|
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NumPageTableEntries>*>(
|
||||||
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
|
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
|
||||||
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
|
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
|
||||||
config.page_table_marked_bit = 0;
|
config.page_table_marked_bit = uint8_t(0);
|
||||||
config.absolute_offset_page_table = true;
|
config.absolute_offset_page_table = true;
|
||||||
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
||||||
config.only_detect_misalignment_via_page_table_on_page_boundary = true;
|
config.only_detect_misalignment_via_page_table_on_page_boundary = true;
|
||||||
@@ -193,7 +177,7 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
|
|||||||
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) {
|
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,
|
// 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.
|
// we have to manually sign extend when our actual pointer is negative.
|
||||||
config.page_table_sign_extension = Common::PageTable::SIGN_BIT;
|
config.page_table_sign_extension = std::uint8_t(Common::PageTable::SIGN_BIT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,18 +30,12 @@ class System;
|
|||||||
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
|
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
|
||||||
public:
|
public:
|
||||||
explicit DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess* process);
|
explicit DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess* process);
|
||||||
u8 MemoryRead8(u32 vaddr) override;
|
u64 MemoryRead(u32 vaddr, size_t size) override;
|
||||||
u16 MemoryRead16(u32 vaddr) override;
|
|
||||||
u32 MemoryRead32(u32 vaddr) override;
|
|
||||||
u64 MemoryRead64(u32 vaddr) override;
|
|
||||||
std::optional<u32> MemoryReadCode(u32 vaddr) override;
|
std::optional<u32> MemoryReadCode(u32 vaddr) override;
|
||||||
void InstructionSynchronizationBarrierRaised() override {
|
void InstructionSynchronizationBarrierRaised() override {
|
||||||
last_code_addr = u64(-1); //reset back, force refetch
|
last_code_addr = u64(-1); //reset back, force refetch
|
||||||
}
|
}
|
||||||
void MemoryWrite8(u32 vaddr, u8 value) override;
|
void MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) 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 MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override;
|
||||||
bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override;
|
bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override;
|
||||||
bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override;
|
bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
|
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
#include "core/hle/kernel/k_process.h"
|
#include "core/hle/kernel/k_process.h"
|
||||||
|
#include "dynarmic/interface/A64/config.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
@@ -21,21 +22,15 @@ DynarmicCallbacks64::DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess
|
|||||||
, m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()}
|
, m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()}
|
||||||
{}
|
{}
|
||||||
|
|
||||||
u8 DynarmicCallbacks64::MemoryRead8(u64 vaddr) {
|
u64 DynarmicCallbacks64::MemoryRead(u64 vaddr, size_t size) {
|
||||||
CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
|
CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Read);
|
||||||
return m_memory.Read8(vaddr);
|
switch (size) {
|
||||||
}
|
case sizeof(u64): return m_memory.Read64(vaddr);
|
||||||
u16 DynarmicCallbacks64::MemoryRead16(u64 vaddr) {
|
case sizeof(u32): return m_memory.Read32(vaddr);
|
||||||
CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
|
case sizeof(u16): return m_memory.Read16(vaddr);
|
||||||
return m_memory.Read16(vaddr);
|
case sizeof(u8): return m_memory.Read8(vaddr);
|
||||||
}
|
default: UNREACHABLE();
|
||||||
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) {
|
Dynarmic::A64::Vector DynarmicCallbacks64::MemoryRead128(u64 vaddr) {
|
||||||
CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read);
|
CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read);
|
||||||
@@ -53,24 +48,15 @@ std::optional<u32> DynarmicCallbacks64::MemoryReadCode(u64 vaddr) {
|
|||||||
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
|
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
|
||||||
}
|
}
|
||||||
|
|
||||||
void DynarmicCallbacks64::MemoryWrite8(u64 vaddr, u8 value) {
|
void DynarmicCallbacks64::MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, std::size_t size) {
|
||||||
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
|
if (CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Write)) {
|
||||||
m_memory.Write8(vaddr, value);
|
switch (size) {
|
||||||
}
|
case sizeof(u64): return m_memory.Write64(vaddr, u64(value));
|
||||||
}
|
case sizeof(u32): return m_memory.Write32(vaddr, u32(value));
|
||||||
void DynarmicCallbacks64::MemoryWrite16(u64 vaddr, u16 value) {
|
case sizeof(u16): return m_memory.Write16(vaddr, u16(value));
|
||||||
if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
|
case sizeof(u8): return m_memory.Write8(vaddr, u8(value));
|
||||||
m_memory.Write16(vaddr, value);
|
default: UNREACHABLE();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
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) {
|
void DynarmicCallbacks64::MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) {
|
||||||
@@ -216,7 +202,7 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
|
|||||||
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
|
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
|
||||||
config.page_table_address_space_bits = std::uint32_t(address_space_bits);
|
config.page_table_address_space_bits = std::uint32_t(address_space_bits);
|
||||||
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
|
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
|
||||||
config.page_table_marked_bit = 0;
|
config.page_table_marked_bit = uint8_t(0);
|
||||||
config.silently_mirror_page_table = false;
|
config.silently_mirror_page_table = false;
|
||||||
config.absolute_offset_page_table = true;
|
config.absolute_offset_page_table = true;
|
||||||
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
||||||
@@ -235,7 +221,7 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
|
|||||||
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) {
|
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,
|
// 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.
|
// we have to manually sign extend when our actual pointer is negative.
|
||||||
config.page_table_sign_extension = Common::PageTable::SIGN_BIT;
|
config.page_table_sign_extension = std::uint8_t(Common::PageTable::SIGN_BIT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include "common/hash.h"
|
#include "common/hash.h"
|
||||||
#include "core/arm/arm_interface.h"
|
#include "core/arm/arm_interface.h"
|
||||||
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
|
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
|
||||||
|
#include "dynarmic/interface/A64/config.h"
|
||||||
|
|
||||||
namespace Core::Memory {
|
namespace Core::Memory {
|
||||||
class Memory;
|
class Memory;
|
||||||
@@ -36,19 +37,13 @@ class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
|
|||||||
public:
|
public:
|
||||||
explicit DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess* process);
|
explicit DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess* process);
|
||||||
|
|
||||||
u8 MemoryRead8(u64 vaddr) override;
|
u64 MemoryRead(u64 vaddr, size_t size) override;
|
||||||
u16 MemoryRead16(u64 vaddr) override;
|
|
||||||
u32 MemoryRead32(u64 vaddr) override;
|
|
||||||
u64 MemoryRead64(u64 vaddr) override;
|
|
||||||
Dynarmic::A64::Vector MemoryRead128(u64 vaddr) override;
|
Dynarmic::A64::Vector MemoryRead128(u64 vaddr) override;
|
||||||
std::optional<u32> MemoryReadCode(u64 vaddr) override;
|
std::optional<u32> MemoryReadCode(u64 vaddr) override;
|
||||||
void InstructionSynchronizationBarrierRaised() override {
|
void InstructionSynchronizationBarrierRaised() override {
|
||||||
last_code_addr = u64(-1); //reset back, force refetch
|
last_code_addr = u64(-1); //reset back, force refetch
|
||||||
}
|
}
|
||||||
void MemoryWrite8(u64 vaddr, u8 value) override;
|
void MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, std::size_t size) 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;
|
void MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) override;
|
||||||
bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) 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;
|
bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override;
|
||||||
|
|||||||
+1
-5
@@ -120,7 +120,6 @@ struct System::Impl {
|
|||||||
|
|
||||||
is_multicore = Settings::values.use_multi_core.GetValue();
|
is_multicore = Settings::values.use_multi_core.GetValue();
|
||||||
extended_memory_layout = Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb;
|
extended_memory_layout = Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb;
|
||||||
unified_memory = Settings::values.use_unified_memory.GetValue();
|
|
||||||
|
|
||||||
core_timing.SetMulticore(is_multicore);
|
core_timing.SetMulticore(is_multicore);
|
||||||
core_timing.Initialize([&system]() { system.RegisterHostThread(); });
|
core_timing.Initialize([&system]() { system.RegisterHostThread(); });
|
||||||
@@ -148,8 +147,7 @@ struct System::Impl {
|
|||||||
!device_memory.has_value() ||
|
!device_memory.has_value() ||
|
||||||
is_multicore != Settings::values.use_multi_core.GetValue() ||
|
is_multicore != Settings::values.use_multi_core.GetValue() ||
|
||||||
extended_memory_layout != (Settings::values.memory_layout_mode.GetValue() !=
|
extended_memory_layout != (Settings::values.memory_layout_mode.GetValue() !=
|
||||||
Settings::MemoryLayout::Memory_4Gb) ||
|
Settings::MemoryLayout::Memory_4Gb);
|
||||||
unified_memory != Settings::values.use_unified_memory.GetValue();
|
|
||||||
|
|
||||||
if (!must_reinitialize) {
|
if (!must_reinitialize) {
|
||||||
return;
|
return;
|
||||||
@@ -160,7 +158,6 @@ struct System::Impl {
|
|||||||
is_multicore = Settings::values.use_multi_core.GetValue();
|
is_multicore = Settings::values.use_multi_core.GetValue();
|
||||||
extended_memory_layout =
|
extended_memory_layout =
|
||||||
Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb;
|
Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb;
|
||||||
unified_memory = Settings::values.use_unified_memory.GetValue();
|
|
||||||
|
|
||||||
Initialize(system);
|
Initialize(system);
|
||||||
}
|
}
|
||||||
@@ -538,7 +535,6 @@ struct System::Impl {
|
|||||||
std::atomic_bool is_powered_on{};
|
std::atomic_bool is_powered_on{};
|
||||||
bool is_multicore : 1 = false;
|
bool is_multicore : 1 = false;
|
||||||
bool extended_memory_layout : 1 = false;
|
bool extended_memory_layout : 1 = false;
|
||||||
bool unified_memory : 1 = false;
|
|
||||||
bool exit_locked : 1 = false;
|
bool exit_locked : 1 = false;
|
||||||
bool exit_requested : 1 = false;
|
bool exit_requested : 1 = false;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -15,18 +12,9 @@ constexpr size_t VirtualReserveSize = 1ULL << 38;
|
|||||||
constexpr size_t VirtualReserveSize = 1ULL << 39;
|
constexpr size_t VirtualReserveSize = 1ULL << 39;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace {
|
|
||||||
size_t ApplicationPoolOffset() {
|
|
||||||
using Init = Kernel::Board::Nintendo::Nx::KSystemControl::Init;
|
|
||||||
const size_t dram_size = Init::GetIntendedMemorySize();
|
|
||||||
const size_t application_pool_size = Init::GetApplicationPoolSize();
|
|
||||||
return dram_size > application_pool_size ? dram_size - application_pool_size : 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DeviceMemory::DeviceMemory()
|
DeviceMemory::DeviceMemory()
|
||||||
: buffer{Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize(),
|
: buffer{Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize(),
|
||||||
VirtualReserveSize, ApplicationPoolOffset()} {}
|
VirtualReserveSize} {}
|
||||||
|
|
||||||
DeviceMemory::~DeviceMemory() = default;
|
DeviceMemory::~DeviceMemory() = default;
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,6 @@
|
|||||||
#include "common/scratch_buffer.h"
|
#include "common/scratch_buffer.h"
|
||||||
#include "common/sparse_large_vector.h"
|
#include "common/sparse_large_vector.h"
|
||||||
|
|
||||||
struct AHardwareBuffer;
|
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
constexpr size_t DEVICE_PAGEBITS = 12ULL;
|
constexpr size_t DEVICE_PAGEBITS = 12ULL;
|
||||||
@@ -97,34 +95,6 @@ public:
|
|||||||
ApplyOpOnPAddr(address, buffer, operation);
|
ApplyOpOnPAddr(address, buffer, operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
u8* GetPhysicalBase() noexcept {
|
|
||||||
return reinterpret_cast<u8*>(physical_base);
|
|
||||||
}
|
|
||||||
|
|
||||||
const u8* GetPhysicalBase() const noexcept {
|
|
||||||
return reinterpret_cast<const u8*>(physical_base);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t GetPhysicalSize() const noexcept {
|
|
||||||
return physical_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::span<AHardwareBuffer* const> GetBackingHardwareBuffers() const noexcept {
|
|
||||||
return ahb_windows;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t GetBackingHardwareBufferWindowSize() const noexcept {
|
|
||||||
return ahb_window_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t GetBackingHardwareBufferBase() const noexcept {
|
|
||||||
return ahb_base;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsBackingShared() const noexcept {
|
|
||||||
return backing_is_shared;
|
|
||||||
}
|
|
||||||
|
|
||||||
PAddr GetPhysicalRawAddressFromDAddr(DAddr address) const {
|
PAddr GetPhysicalRawAddressFromDAddr(DAddr address) const {
|
||||||
PAddr subbits = PAddr(address & page_mask);
|
PAddr subbits = PAddr(address & page_mask);
|
||||||
auto paddr = tracked_entries[(address >> page_bits)].compressed_physical_ptr;
|
auto paddr = tracked_entries[(address >> page_bits)].compressed_physical_ptr;
|
||||||
@@ -201,11 +171,6 @@ private:
|
|||||||
std::unique_ptr<DeviceMemoryManagerAllocator<Traits>> impl;
|
std::unique_ptr<DeviceMemoryManagerAllocator<Traits>> impl;
|
||||||
|
|
||||||
const uintptr_t physical_base;
|
const uintptr_t physical_base;
|
||||||
const size_t physical_size;
|
|
||||||
const std::span<AHardwareBuffer* const> ahb_windows;
|
|
||||||
const size_t ahb_window_size;
|
|
||||||
const size_t ahb_base;
|
|
||||||
const bool backing_is_shared;
|
|
||||||
DeviceInterface* device_inter;
|
DeviceInterface* device_inter;
|
||||||
|
|
||||||
struct TrackedEntry {
|
struct TrackedEntry {
|
||||||
|
|||||||
@@ -171,11 +171,6 @@ struct DeviceMemoryManagerAllocator {
|
|||||||
template <typename Traits>
|
template <typename Traits>
|
||||||
DeviceMemoryManager<Traits>::DeviceMemoryManager(const DeviceMemory& device_memory_)
|
DeviceMemoryManager<Traits>::DeviceMemoryManager(const DeviceMemory& device_memory_)
|
||||||
: physical_base{uintptr_t(device_memory_.buffer.BackingBasePointer())}
|
: physical_base{uintptr_t(device_memory_.buffer.BackingBasePointer())}
|
||||||
, physical_size{device_memory_.buffer.BackingSize()}
|
|
||||||
, ahb_windows{device_memory_.buffer.BackingHardwareBuffers()}
|
|
||||||
, ahb_window_size{device_memory_.buffer.BackingHardwareBufferWindowSize()}
|
|
||||||
, ahb_base{device_memory_.buffer.BackingHardwareBufferBase()}
|
|
||||||
, backing_is_shared{device_memory_.buffer.IsBackingShared()}
|
|
||||||
, device_inter{nullptr}
|
, device_inter{nullptr}
|
||||||
, compressed_device_addr(1ULL << ((Settings::values.memory_layout_mode.GetValue() == Settings::MemoryLayout::Memory_4Gb ? physical_min_bits : physical_max_bits) - Memory::YUZU_PAGEBITS))
|
, compressed_device_addr(1ULL << ((Settings::values.memory_layout_mode.GetValue() == Settings::MemoryLayout::Memory_4Gb ? physical_min_bits : physical_max_bits) - Memory::YUZU_PAGEBITS))
|
||||||
, tracked_entries(device_as_size >> Memory::YUZU_PAGEBITS)
|
, tracked_entries(device_as_size >> Memory::YUZU_PAGEBITS)
|
||||||
|
|||||||
@@ -100,8 +100,7 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
|
|||||||
|
|
||||||
|
|
||||||
struct IPSwitchRecord {
|
struct IPSwitchRecord {
|
||||||
std::array<uint8_t, 256 - sizeof(size_t)> data;
|
std::vector<uint8_t> data;
|
||||||
size_t count;
|
|
||||||
};
|
};
|
||||||
struct IPSwitchCompiler::IPSwitchPatch {
|
struct IPSwitchCompiler::IPSwitchPatch {
|
||||||
::Common::unordered_map<u32, IPSwitchRecord> records;
|
::Common::unordered_map<u32, IPSwitchRecord> records;
|
||||||
@@ -122,22 +121,23 @@ static IPSwitchRecord EscapeStringSequences(std::string_view sv) {
|
|||||||
IPSwitchRecord r{};
|
IPSwitchRecord r{};
|
||||||
for (auto it = sv.cbegin(); it < sv.cend(); ) {
|
for (auto it = sv.cbegin(); it < sv.cend(); ) {
|
||||||
if (*it == '\\' && it + 1 < sv.cend()) {
|
if (*it == '\\' && it + 1 < sv.cend()) {
|
||||||
switch (it[1]) {
|
r.data.push_back([it]() {
|
||||||
case 'a': r.data[r.count] = '\a'; break;
|
switch (it[1]) {
|
||||||
case 'b': r.data[r.count] = '\b'; break;
|
case 'a': return '\a';
|
||||||
case 'e': r.data[r.count] = '\e'; break;
|
case 'b': return '\b';
|
||||||
case 'f': r.data[r.count] = '\f'; break;
|
case 'e': return '\e';
|
||||||
case 'n': r.data[r.count] = '\n'; break;
|
case 'f': return '\f';
|
||||||
case 'r': r.data[r.count] = '\r'; break;
|
case 'n': return '\n';
|
||||||
case 't': r.data[r.count] = '\t'; break;
|
case 'r': return '\r';
|
||||||
case 'v': r.data[r.count] = '\v'; break;
|
case 't': return '\t';
|
||||||
case '?': r.data[r.count] = '\?'; break;
|
case 'v': return '\v';
|
||||||
default: r.data[r.count] = it[1]; break;
|
case '?': return '\?';
|
||||||
}
|
default: return it[1];
|
||||||
++r.count;
|
}
|
||||||
|
}());
|
||||||
it += 2;
|
it += 2;
|
||||||
} else {
|
} else {
|
||||||
++r.count;
|
r.data.push_back(*it);
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -223,8 +223,8 @@ void IPSwitchCompiler::Parse(std::span<u8 const> bytes) {
|
|||||||
if (start <= line.cend() && end <= line.cend()) {
|
if (start <= line.cend() && end <= line.cend()) {
|
||||||
// Actually IPS wants ordering from {lsb, ..., msb} -- so LE and BE are inverted, fun!
|
// Actually IPS wants ordering from {lsb, ..., msb} -- so LE and BE are inverted, fun!
|
||||||
auto const hs = Common::HexStringToVector({start, end}, is_little_endian);
|
auto const hs = Common::HexStringToVector({start, end}, is_little_endian);
|
||||||
|
r.data.resize(hs.size());
|
||||||
std::memcpy(r.data.data(), hs.data(), hs.size());
|
std::memcpy(r.data.data(), hs.data(), hs.size());
|
||||||
r.count = hs.size();
|
|
||||||
LOG_INFO(Loader, "[H] value @ {:#08X}", offset);
|
LOG_INFO(Loader, "[H] value @ {:#08X}", offset);
|
||||||
patches.back().records.insert_or_assign(u32(offset), std::move(r));
|
patches.back().records.insert_or_assign(u32(offset), std::move(r));
|
||||||
} else {
|
} else {
|
||||||
@@ -293,7 +293,7 @@ VirtualFile IPSwitchCompiler::Apply(const VirtualFile& in) const {
|
|||||||
if (patch.enabled) {
|
if (patch.enabled) {
|
||||||
for (const auto& record : patch.records) {
|
for (const auto& record : patch.records) {
|
||||||
if (record.first < in_data.size()) {
|
if (record.first < in_data.size()) {
|
||||||
auto replace_size = record.second.count;
|
auto replace_size = record.second.data.size();
|
||||||
if (record.first + replace_size > in_data.size())
|
if (record.first + replace_size > in_data.size())
|
||||||
replace_size = in_data.size() - record.first;
|
replace_size = in_data.size() - record.first;
|
||||||
std::memcpy(in_data.data() + record.first, record.second.data.data(), replace_size);
|
std::memcpy(in_data.data() + record.first, record.second.data.data(), replace_size);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
#include "common/hex_util.h"
|
#include "common/hex_util.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
@@ -156,12 +157,15 @@ std::string GetUpdateVersionStringFromSlot(const ContentProvider* provider, u64
|
|||||||
NACP nacp{nacp_file};
|
NACP nacp{nacp_file};
|
||||||
return nacp.GetVersionString();
|
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
|
} // Anonymous namespace
|
||||||
|
|
||||||
PatchManager::PatchManager(u64 title_id_,
|
PatchManager::PatchManager(u64 title_id_,
|
||||||
const Service::FileSystem::FileSystemController& fs_controller_,
|
const Service::FileSystem::FileSystemController& fs_controller_,
|
||||||
const ContentProvider& content_provider_)
|
const ContentProvider& content_provider_)
|
||||||
: title_id{title_id_}, fs_controller{fs_controller_}, content_provider{content_provider_} {}
|
: title_id{title_id_}, parent_title_id{GetParentApplicationId(std::addressof(content_provider_), title_id_)}, fs_controller{fs_controller_}, content_provider{content_provider_} {}
|
||||||
|
|
||||||
PatchManager::~PatchManager() = default;
|
PatchManager::~PatchManager() = default;
|
||||||
|
|
||||||
@@ -169,13 +173,35 @@ u64 PatchManager::GetTitleID() const {
|
|||||||
return title_id;
|
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 {
|
VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||||
LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
|
LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
|
||||||
|
|
||||||
if (exefs == nullptr)
|
if (exefs == nullptr)
|
||||||
return exefs;
|
return exefs;
|
||||||
|
|
||||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
const auto disabled = GetDisabledAddons();
|
||||||
|
|
||||||
bool update_disabled = true;
|
bool update_disabled = true;
|
||||||
std::optional<u32> enabled_version;
|
std::optional<u32> enabled_version;
|
||||||
@@ -303,8 +329,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LayeredExeFS
|
// LayeredExeFS
|
||||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
const auto load_dir = GetModificationLoadRoot();
|
||||||
const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
|
const auto sdmc_load_dir = GetModificationLoadRoot(true);
|
||||||
|
|
||||||
std::vector<VirtualDir> patch_dirs = {sdmc_load_dir};
|
std::vector<VirtualDir> patch_dirs = {sdmc_load_dir};
|
||||||
if (load_dir != nullptr) {
|
if (load_dir != nullptr) {
|
||||||
@@ -346,7 +372,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs, const std::string& build_id) const {
|
std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs, const std::string& build_id) const {
|
||||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
const auto disabled = GetDisabledAddons();
|
||||||
const auto nso_build_id = fmt::format("{:0<64}", build_id);
|
const auto nso_build_id = fmt::format("{:0<64}", build_id);
|
||||||
|
|
||||||
std::vector<VirtualFile> out;
|
std::vector<VirtualFile> out;
|
||||||
@@ -405,7 +431,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);
|
LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
|
||||||
|
|
||||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
const auto load_dir = GetModificationLoadRoot();
|
||||||
if (load_dir == nullptr) {
|
if (load_dir == nullptr) {
|
||||||
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
||||||
return nso;
|
return nso;
|
||||||
@@ -448,7 +474,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);
|
LOG_INFO(Loader, "Querying NSO patch existence for build_id={}, name={}", build_id, name);
|
||||||
|
|
||||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
const auto load_dir = GetModificationLoadRoot();
|
||||||
if (load_dir == nullptr) {
|
if (load_dir == nullptr) {
|
||||||
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
||||||
return false;
|
return false;
|
||||||
@@ -462,13 +488,13 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_, std::string_view name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildID& build_id_) const {
|
std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildID& build_id_) const {
|
||||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
const auto load_dir = GetModificationLoadRoot();
|
||||||
if (load_dir == nullptr) {
|
if (load_dir == nullptr) {
|
||||||
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
const auto disabled = GetDisabledAddons();
|
||||||
auto patch_dirs = load_dir->GetSubdirectories();
|
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(); });
|
std::sort(patch_dirs.begin(), patch_dirs.end(), [](auto const& l, auto const& r) { return l->GetName() < r->GetName(); });
|
||||||
|
|
||||||
@@ -502,17 +528,16 @@ std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildI
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type,
|
void PatchManager::ApplyLayeredFS(VirtualFile& romfs, ContentRecordType type) const {
|
||||||
const Service::FileSystem::FileSystemController& fs_controller) {
|
const auto load_dir = GetModificationLoadRoot();
|
||||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
const auto sdmc_load_dir = GetModificationLoadRoot(true);
|
||||||
const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
|
|
||||||
if ((type != ContentRecordType::Program && type != ContentRecordType::Data &&
|
if ((type != ContentRecordType::Program && type != ContentRecordType::Data &&
|
||||||
type != ContentRecordType::HtmlDocument) ||
|
type != ContentRecordType::HtmlDocument) ||
|
||||||
(load_dir == nullptr && sdmc_load_dir == nullptr)) {
|
(load_dir == nullptr && sdmc_load_dir == nullptr)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
const auto disabled = GetDisabledAddons();
|
||||||
std::vector<VirtualDir> patch_dirs = load_dir->GetSubdirectories();
|
std::vector<VirtualDir> patch_dirs = load_dir->GetSubdirectories();
|
||||||
if (std::find(disabled.cbegin(), disabled.cend(), "SDMC") == disabled.cend()) {
|
if (std::find(disabled.cbegin(), disabled.cend(), "SDMC") == disabled.cend()) {
|
||||||
patch_dirs.push_back(sdmc_load_dir);
|
patch_dirs.push_back(sdmc_load_dir);
|
||||||
@@ -591,7 +616,7 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
|
|||||||
|
|
||||||
// Game Updates
|
// Game Updates
|
||||||
const auto update_tid = GetUpdateTitleID(title_id);
|
const auto update_tid = GetUpdateTitleID(title_id);
|
||||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
const auto disabled = GetDisabledAddons();
|
||||||
|
|
||||||
bool update_disabled = true;
|
bool update_disabled = true;
|
||||||
std::optional<u32> enabled_version;
|
std::optional<u32> enabled_version;
|
||||||
@@ -698,7 +723,7 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
|
|||||||
|
|
||||||
// LayeredFS
|
// LayeredFS
|
||||||
if (apply_layeredfs) {
|
if (apply_layeredfs) {
|
||||||
ApplyLayeredFS(romfs, title_id, type, fs_controller);
|
ApplyLayeredFS(romfs, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
return romfs;
|
return romfs;
|
||||||
@@ -1072,15 +1097,22 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
|||||||
std::optional<u32> PatchManager::GetGameVersion() const {
|
std::optional<u32> PatchManager::GetGameVersion() const {
|
||||||
const auto update_tid = GetUpdateTitleID(title_id);
|
const auto update_tid = GetUpdateTitleID(title_id);
|
||||||
if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
|
if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
|
||||||
return content_provider.GetEntryVersion(update_tid);
|
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(title_id);
|
const auto version = content_provider.GetEntryVersion(title_id);
|
||||||
|
return version || !parent_title_id ? version : content_provider.GetEntryVersion(*parent_title_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
PatchManager::Metadata PatchManager::GetControlMetadata() const {
|
PatchManager::Metadata PatchManager::GetControlMetadata() const {
|
||||||
const auto base_control_nca = content_provider.GetEntry(title_id, ContentRecordType::Control);
|
const auto base_control_nca = content_provider.GetEntry(title_id, ContentRecordType::Control);
|
||||||
if (base_control_nca == nullptr) {
|
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 {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -109,10 +109,14 @@ public:
|
|||||||
[[nodiscard]] static PatchManager::Metadata GetMetadataFromBaseOrUpdate(Core::System& system, u64 application_id) noexcept;
|
[[nodiscard]] static PatchManager::Metadata GetMetadataFromBaseOrUpdate(Core::System& system, u64 application_id) noexcept;
|
||||||
|
|
||||||
private:
|
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,
|
[[nodiscard]] std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
|
||||||
const std::string& build_id) const;
|
const std::string& build_id) const;
|
||||||
|
|
||||||
u64 title_id;
|
u64 title_id;
|
||||||
|
std::optional<u64> parent_title_id;
|
||||||
const Service::FileSystem::FileSystemController& fs_controller;
|
const Service::FileSystem::FileSystemController& fs_controller;
|
||||||
const ContentProvider& content_provider;
|
const ContentProvider& content_provider;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <limits>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <openssl/evp.h>
|
#include <openssl/evp.h>
|
||||||
@@ -348,6 +349,22 @@ std::vector<ContentProviderEntry> ContentProvider::ListEntries() const {
|
|||||||
return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt);
|
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_)) {}
|
PlaceholderCache::PlaceholderCache(VirtualDir dir_) : dir(std::move(dir_)) {}
|
||||||
|
|
||||||
bool PlaceholderCache::Create(const NcaID& id, u64 size) const {
|
bool PlaceholderCache::Create(const NcaID& id, u64 size) const {
|
||||||
|
|||||||
@@ -100,6 +100,8 @@ public:
|
|||||||
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
|
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
|
||||||
std::optional<u64> title_id = {}) const = 0;
|
std::optional<u64> title_id = {}) const = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] std::optional<u64> GetParentApplicationId(u64 program_id) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// A single instance of KeyManager to be used by GetEntry()
|
// A single instance of KeyManager to be used by GetEntry()
|
||||||
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
|
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "hid_core/frontend/emulated_controller.h"
|
#include "hid_core/frontend/emulated_controller.h"
|
||||||
#include "hid_core/hid_core.h"
|
#include "hid_core/hid_core.h"
|
||||||
#include "hid_core/hid_types.h"
|
#include "hid_core/hid_types.h"
|
||||||
|
#include <array>
|
||||||
|
|
||||||
namespace Core::Frontend {
|
namespace Core::Frontend {
|
||||||
|
|
||||||
@@ -29,23 +30,39 @@ void DefaultControllerApplet::ReconfigureControllers(ReconfigureCallback callbac
|
|||||||
|
|
||||||
const std::size_t min_supported_players =
|
const std::size_t min_supported_players =
|
||||||
parameters.enable_single_mode ? 1 : parameters.min_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{};
|
||||||
|
|
||||||
// Disconnect Handheld first.
|
// 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];
|
||||||
|
}
|
||||||
auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
|
auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
|
||||||
handheld->Disconnect();
|
if (!keep_connected[hid_core.available_controllers - 2]) handheld->Disconnect();
|
||||||
|
|
||||||
// Deduce the best configuration based on the input parameters.
|
// Deduce the best configuration based on the input parameters.
|
||||||
for (std::size_t index = 0; index < hid_core.available_controllers - 2; ++index) {
|
for (std::size_t index = 0; index < hid_core.available_controllers - 2; ++index) {
|
||||||
auto* controller = hid_core.GetEmulatedControllerByIndex(index);
|
auto* controller = hid_core.GetEmulatedControllerByIndex(index);
|
||||||
|
|
||||||
// First, disconnect all controllers regardless of the value of keep_controllers_connected.
|
if (keep_connected[index]) continue;
|
||||||
// This makes it easy to connect the desired controllers.
|
|
||||||
controller->Disconnect();
|
controller->Disconnect();
|
||||||
|
|
||||||
// Only connect the minimum number of required players.
|
// only add players still needed to reach the minimum
|
||||||
if (index >= min_supported_players) {
|
if (num_selected_players >= min_supported_players) continue;
|
||||||
continue;
|
++num_selected_players;
|
||||||
}
|
|
||||||
|
|
||||||
// Connect controllers based on the following priority list from highest to lowest priority:
|
// Connect controllers based on the following priority list from highest to lowest priority:
|
||||||
// Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
|
// Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
|
||||||
|
|||||||
@@ -465,6 +465,10 @@ void KScheduler::ScheduleImplFiber(KernelCore& kernel) {
|
|||||||
// Check if we need scheduling. If we do, then we can't complete the switch and should
|
// Check if we need scheduling. If we do, then we can't complete the switch and should
|
||||||
// retry.
|
// retry.
|
||||||
if (m_state.needs_scheduling.load(std::memory_order_seq_cst)) {
|
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.
|
// Our switch failed.
|
||||||
// We should unlock the thread context, and then retry.
|
// We should unlock the thread context, and then retry.
|
||||||
highest_priority_thread->m_context_guard.unlock();
|
highest_priority_thread->m_context_guard.unlock();
|
||||||
@@ -496,6 +500,10 @@ void KScheduler::Unload(KernelCore& kernel, KThread* thread) {
|
|||||||
|
|
||||||
// Check if the thread is terminated by checking the DPC flags.
|
// Check if the thread is terminated by checking the DPC flags.
|
||||||
if ((thread->GetStackParameters().dpc_flags & static_cast<u32>(DpcFlag::Terminated)) == 0) {
|
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.
|
// The thread isn't terminated, so we want to unlock it.
|
||||||
thread->m_context_guard.unlock();
|
thread->m_context_guard.unlock();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
@@ -12,7 +12,6 @@
|
|||||||
#include "common/atomic_ops.h"
|
#include "common/atomic_ops.h"
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/spin_lock.h"
|
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
@@ -30,7 +29,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
constexpr KSlabHeapImpl() = default;
|
KSlabHeapImpl() = default;
|
||||||
|
|
||||||
void Initialize() {
|
void Initialize() {
|
||||||
ASSERT(m_head == nullptr);
|
ASSERT(m_head == nullptr);
|
||||||
@@ -68,7 +67,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::atomic<Node*> m_head{};
|
std::atomic<Node*> m_head{};
|
||||||
Common::SpinLock m_lock;
|
std::mutex m_lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace impl
|
} // namespace impl
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
#include "common/intrusive_red_black_tree.h"
|
#include "common/intrusive_red_black_tree.h"
|
||||||
#include "common/scratch_buffer.h"
|
#include "common/scratch_buffer.h"
|
||||||
#include "common/spin_lock.h"
|
|
||||||
#include "core/arm/arm_interface.h"
|
#include "core/arm/arm_interface.h"
|
||||||
#include "core/hle/kernel/k_affinity_mask.h"
|
#include "core/hle/kernel/k_affinity_mask.h"
|
||||||
#include "core/hle/kernel/k_light_lock.h"
|
#include "core/hle/kernel/k_light_lock.h"
|
||||||
@@ -920,7 +919,7 @@ private:
|
|||||||
bool m_resource_limit_release_hint{};
|
bool m_resource_limit_release_hint{};
|
||||||
bool m_is_kernel_address_key{};
|
bool m_is_kernel_address_key{};
|
||||||
StackParameters m_stack_parameters{};
|
StackParameters m_stack_parameters{};
|
||||||
Common::SpinLock m_context_guard{};
|
std::mutex m_context_guard{};
|
||||||
|
|
||||||
// For emulation
|
// For emulation
|
||||||
std::shared_ptr<Common::Fiber> m_host_context{};
|
std::shared_ptr<Common::Fiber> m_host_context{};
|
||||||
|
|||||||
@@ -23,6 +23,12 @@ constexpr std::size_t profile_username_size{32};
|
|||||||
using ProfileUsername = std::array<u8, profile_username_size>;
|
using ProfileUsername = std::array<u8, profile_username_size>;
|
||||||
using UserIDArray = std::array<Common::UUID, MAX_USERS>;
|
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.
|
/// Contains extra data related to a user.
|
||||||
/// TODO: RE this structure
|
/// TODO: RE this structure
|
||||||
struct UserData {
|
struct UserData {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include "core/hle/service/am/frontend/applet_web_browser.h"
|
#include "core/hle/service/am/frontend/applet_web_browser.h"
|
||||||
#include "core/hle/service/am/frontend/applets.h"
|
#include "core/hle/service/am/frontend/applets.h"
|
||||||
#include "core/hle/service/am/service/storage.h"
|
#include "core/hle/service/am/service/storage.h"
|
||||||
|
#include "core/hle/service/am/window_system.h"
|
||||||
#include "core/hle/service/sm/sm.h"
|
#include "core/hle/service/sm/sm.h"
|
||||||
|
|
||||||
namespace Service::AM::Frontend {
|
namespace Service::AM::Frontend {
|
||||||
@@ -72,10 +73,16 @@ void FrontendApplet::PushInteractiveOutData(std::shared_ptr<IStorage> storage) {
|
|||||||
|
|
||||||
void FrontendApplet::Exit() {
|
void FrontendApplet::Exit() {
|
||||||
auto applet_ = applet.lock();
|
auto applet_ = applet.lock();
|
||||||
|
{
|
||||||
std::scoped_lock lk{applet_->lock};
|
std::scoped_lock lk{applet_->lock};
|
||||||
applet_->is_completed = true;
|
applet_->is_completed = true;
|
||||||
applet_->state_changed_event.Signal(system.Kernel());
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
FrontendAppletSet::FrontendAppletSet() = default;
|
FrontendAppletSet::FrontendAppletSet() = default;
|
||||||
|
|||||||
@@ -92,6 +92,13 @@ 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);
|
void OnOperationAndPerformanceModeChanged(Kernel::KernelCore& kernel);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -347,21 +347,22 @@ Result IApplicationFunctions::NotifyRunning(Out<bool> out_became_running) {
|
|||||||
|
|
||||||
Result IApplicationFunctions::GetPseudoDeviceId(Out<Common::UUID> out_pseudo_device_id) {
|
Result IApplicationFunctions::GetPseudoDeviceId(Out<Common::UUID> out_pseudo_device_id) {
|
||||||
LOG_WARNING(Service_AM, "(stubbed)");
|
LOG_WARNING(Service_AM, "(stubbed)");
|
||||||
R_UNLESS(out_pseudo_device_id, ResultUnknown);
|
R_UNLESS(out_pseudo_device_id != nullptr, ResultUnknown);
|
||||||
|
|
||||||
// This should be hashed with the device specific hash
|
// This should be hashed with the device specific hash
|
||||||
// for now this will do
|
// for now this will do
|
||||||
const auto res = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, m_applet->program_id);
|
const auto res = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, m_applet->program_id);
|
||||||
u8 hash[EVP_MAX_MD_SIZE];
|
R_UNLESS(res.first != nullptr, ResultUnknown);
|
||||||
|
std::array<u8, EVP_MAX_MD_SIZE> hash;
|
||||||
unsigned int hash_len = 0;
|
unsigned int hash_len = 0;
|
||||||
auto const seed = res.first->raw.seed_for_pseudo_device_id;
|
auto const seed = res.first->raw.seed_for_pseudo_device_id;
|
||||||
EVP_MD_CTX *ctx = EVP_MD_CTX_new();
|
EVP_MD_CTX *ctx = EVP_MD_CTX_new();
|
||||||
auto const algorithm = EVP_sha1();
|
auto const algorithm = EVP_sha1();
|
||||||
EVP_DigestInit_ex(ctx, algorithm, nullptr);
|
EVP_DigestInit_ex(ctx, algorithm, nullptr);
|
||||||
EVP_DigestUpdate(ctx, &seed, sizeof(seed));
|
EVP_DigestUpdate(ctx, &seed, sizeof(seed));
|
||||||
EVP_DigestFinal_ex(ctx, hash, &hash_len);
|
EVP_DigestFinal_ex(ctx, hash.data(), &hash_len);
|
||||||
EVP_MD_CTX_free(ctx);
|
EVP_MD_CTX_free(ctx);
|
||||||
*out_pseudo_device_id = Common::UUID::MakeRFC4122V5(std::span<u8, 20>{hash, std::size(hash)});
|
*out_pseudo_device_id = Common::UUID::MakeRFC4122V5(std::span<u8, 16>{hash.begin(), hash.begin() + 16});
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -164,14 +164,14 @@ Result ILibraryAppletAccessor::PushInData(SharedPointer<IStorage> storage) {
|
|||||||
Result ILibraryAppletAccessor::PopOutData(Out<SharedPointer<IStorage>> out_storage) {
|
Result ILibraryAppletAccessor::PopOutData(Out<SharedPointer<IStorage>> out_storage) {
|
||||||
LOG_DEBUG(Service_AM, "called");
|
LOG_DEBUG(Service_AM, "called");
|
||||||
|
|
||||||
if (auto caller_applet = m_applet->caller_applet.lock(); caller_applet) {
|
|
||||||
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()));
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
if (m_applet->applet_id == AppletId::ProfileSelect && *out_storage) {
|
if (m_applet->applet_id == AppletId::ProfileSelect && *out_storage) {
|
||||||
auto impl = (*out_storage)->GetImpl();
|
auto impl = (*out_storage)->GetImpl();
|
||||||
|
|||||||
@@ -122,7 +122,10 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
|
|||||||
auto broker = std::make_shared<AppletDataBroker>(system);
|
auto broker = std::make_shared<AppletDataBroker>(system);
|
||||||
applet->caller_applet = caller_applet;
|
applet->caller_applet = caller_applet;
|
||||||
applet->caller_applet_broker = broker;
|
applet->caller_applet_broker = broker;
|
||||||
caller_applet->child_applets.push_back(applet);
|
{
|
||||||
|
std::scoped_lock lk{caller_applet->lock};
|
||||||
|
caller_applet->child_applets.push_back(applet);
|
||||||
|
}
|
||||||
window_system.TrackApplet(applet, false);
|
window_system.TrackApplet(applet, false);
|
||||||
return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
|
return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
|
||||||
}
|
}
|
||||||
@@ -148,10 +151,10 @@ std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(Core::System& syste
|
|||||||
applet->caller_applet = caller_applet;
|
applet->caller_applet = caller_applet;
|
||||||
applet->caller_applet_broker = storage;
|
applet->caller_applet_broker = storage;
|
||||||
applet->frontend = system.GetFrontendAppletHolder().GetApplet(applet, applet_id, mode);
|
applet->frontend = system.GetFrontendAppletHolder().GetApplet(applet, applet_id, mode);
|
||||||
caller_applet->child_applets.push_back(applet);
|
{
|
||||||
|
std::scoped_lock lk{caller_applet->lock};
|
||||||
window_system.TrackApplet(applet, false);
|
caller_applet->child_applets.push_back(applet);
|
||||||
|
}
|
||||||
return std::make_shared<ILibraryAppletAccessor>(system, storage, applet);
|
return std::make_shared<ILibraryAppletAccessor>(system, storage, applet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -154,6 +154,9 @@ FSP_SRV::FSP_SRV(Core::System& system_)
|
|||||||
{720, nullptr, "AbandonAccessFailure"},
|
{720, nullptr, "AbandonAccessFailure"},
|
||||||
{800, nullptr, "GetAndClearFileSystemProxyErrorInfo"},
|
{800, nullptr, "GetAndClearFileSystemProxyErrorInfo"},
|
||||||
{810, nullptr, "RegisterProgramIndexMapInfo"},
|
{810, nullptr, "RegisterProgramIndexMapInfo"},
|
||||||
|
{820, nullptr, "GetContentStorageInfoIndex"},
|
||||||
|
{830, nullptr, "EncryptStreamPlaySaveData"},
|
||||||
|
{831, nullptr, "DecryptStreamPlaySaveData"},
|
||||||
{1000, nullptr, "SetBisRootForHost"},
|
{1000, nullptr, "SetBisRootForHost"},
|
||||||
{1001, nullptr, "SetSaveDataSize"},
|
{1001, nullptr, "SetSaveDataSize"},
|
||||||
{1002, nullptr, "SetSaveDataRootPath"},
|
{1002, nullptr, "SetSaveDataRootPath"},
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
|
|||||||
{547, nullptr, "GetAllowedBluetoothLinksCount"},
|
{547, nullptr, "GetAllowedBluetoothLinksCount"},
|
||||||
{548, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevices"},
|
{548, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevices"},
|
||||||
{549, nullptr, "GetConnectableRegisteredDevices"},
|
{549, nullptr, "GetConnectableRegisteredDevices"},
|
||||||
{551, nullptr, "GetRegisteredDevicesForControllerSupport"}, //20.0.0+
|
{551, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevicesForControllerSupport"}, //20.0.0+ //mocked via 548 for Diablo 3 (at least)
|
||||||
{700, nullptr, "ActivateUniquePad"},
|
{700, nullptr, "ActivateUniquePad"},
|
||||||
{702, &IHidSystemServer::AcquireUniquePadConnectionEventHandle, "AcquireUniquePadConnectionEventHandle"},
|
{702, &IHidSystemServer::AcquireUniquePadConnectionEventHandle, "AcquireUniquePadConnectionEventHandle"},
|
||||||
{703, &IHidSystemServer::GetUniquePadIds, "GetUniquePadIds"},
|
{703, &IHidSystemServer::GetUniquePadIds, "GetUniquePadIds"},
|
||||||
@@ -758,7 +758,7 @@ void IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport(HLEReque
|
|||||||
}
|
}
|
||||||
|
|
||||||
void IHidSystemServer::GetRegisteredDevices(HLERequestContext& ctx) {
|
void IHidSystemServer::GetRegisteredDevices(HLERequestContext& ctx) {
|
||||||
LOG_WARNING(Service_HID, "(STUBBED) called");
|
LOG_WARNING(Service_HID, "(STUBBED) called, command={}", ctx.GetCommand()); //548 or 551
|
||||||
|
|
||||||
struct RegisterData {
|
struct RegisterData {
|
||||||
std::array<u8, 0x68> data;
|
std::array<u8, 0x68> data;
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
namespace IPC {
|
namespace IPC {
|
||||||
|
|
||||||
|
constexpr Result ResultNotSupported{ErrorModule::HIPC, 1};
|
||||||
constexpr Result ResultSessionClosed{ErrorModule::HIPC, 301};
|
constexpr Result ResultSessionClosed{ErrorModule::HIPC, 301};
|
||||||
|
|
||||||
struct ResponseBuilder {
|
struct ResponseBuilder {
|
||||||
|
|||||||
@@ -71,17 +71,14 @@ public:
|
|||||||
void InstructionSynchronizationBarrierRaised() override {
|
void InstructionSynchronizationBarrierRaised() override {
|
||||||
last_code_addr = u64(-1); //reset back, force refetch
|
last_code_addr = u64(-1); //reset back, force refetch
|
||||||
}
|
}
|
||||||
u8 MemoryRead8(u64 vaddr) override {
|
u64 MemoryRead(u64 vaddr, size_t size) override {
|
||||||
return ReadMemory<u8>(vaddr);
|
switch (size) {
|
||||||
}
|
case sizeof(u64): return ReadMemory<u64>(vaddr);
|
||||||
u16 MemoryRead16(u64 vaddr) override {
|
case sizeof(u32): return ReadMemory<u32>(vaddr);
|
||||||
return ReadMemory<u16>(vaddr);
|
case sizeof(u16): return ReadMemory<u16>(vaddr);
|
||||||
}
|
case sizeof(u8): return ReadMemory<u8>(vaddr);
|
||||||
u32 MemoryRead32(u64 vaddr) override {
|
default: UNREACHABLE();
|
||||||
return ReadMemory<u32>(vaddr);
|
}
|
||||||
}
|
|
||||||
u64 MemoryRead64(u64 vaddr) override {
|
|
||||||
return ReadMemory<u64>(vaddr);
|
|
||||||
}
|
}
|
||||||
u128 MemoryRead128(u64 vaddr) override {
|
u128 MemoryRead128(u64 vaddr) override {
|
||||||
return ReadMemory<u128>(vaddr);
|
return ReadMemory<u128>(vaddr);
|
||||||
@@ -89,22 +86,19 @@ public:
|
|||||||
std::string MemoryReadCString(u64 vaddr) {
|
std::string MemoryReadCString(u64 vaddr) {
|
||||||
std::string result{};
|
std::string result{};
|
||||||
u8 next;
|
u8 next;
|
||||||
while ((next = MemoryRead8(vaddr++)) != 0)
|
while ((next = u8(MemoryRead(vaddr++, sizeof(u8)))) != 0)
|
||||||
result += char(next);
|
result += char(next);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemoryWrite8(u64 vaddr, u8 value) override {
|
void MemoryWrite(u64 vaddr, u64 value, size_t size) override {
|
||||||
WriteMemory<u8>(vaddr, value);
|
switch (size) {
|
||||||
}
|
case sizeof(u64): WriteMemory<u64>(vaddr, u64(value)); break;
|
||||||
void MemoryWrite16(u64 vaddr, u16 value) override {
|
case sizeof(u32): WriteMemory<u32>(vaddr, u32(value)); break;
|
||||||
WriteMemory<u16>(vaddr, value);
|
case sizeof(u16): WriteMemory<u16>(vaddr, u16(value)); break;
|
||||||
}
|
case sizeof(u8): WriteMemory<u8>(vaddr, u8(value)); break;
|
||||||
void MemoryWrite32(u64 vaddr, u32 value) override {
|
default: UNREACHABLE();
|
||||||
WriteMemory<u32>(vaddr, value);
|
}
|
||||||
}
|
|
||||||
void MemoryWrite64(u64 vaddr, u64 value) override {
|
|
||||||
WriteMemory<u64>(vaddr, value);
|
|
||||||
}
|
}
|
||||||
void MemoryWrite128(u64 vaddr, u128 value) override {
|
void MemoryWrite128(u64 vaddr, u128 value) override {
|
||||||
WriteMemory<u128>(vaddr, value);
|
WriteMemory<u128>(vaddr, value);
|
||||||
@@ -193,14 +187,14 @@ public:
|
|||||||
// The loaded NRO file has ELF relocations that must be processed before it can run.
|
// 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
|
// 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.
|
// the linker available, so we have to do it ourselves.
|
||||||
const VAddr mod_offset{callbacks->MemoryRead32(4)};
|
const VAddr mod_offset{callbacks->MemoryRead(4, sizeof(u32))};
|
||||||
if (callbacks->MemoryRead32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0'))
|
if (callbacks->MemoryRead(mod_offset, sizeof(u32)) != Common::MakeMagic('M', 'O', 'D', '0'))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// For more info about dynamic entries, see the ELF ABI specification:
|
// 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+/ch5.dynamic.html
|
||||||
// https://refspecs.linuxbase.org/elf/gabi4+/ch4.reloc.html
|
// https://refspecs.linuxbase.org/elf/gabi4+/ch4.reloc.html
|
||||||
VAddr dynamic_offset{mod_offset + callbacks->MemoryRead32(mod_offset + 4)};
|
VAddr dynamic_offset{mod_offset + callbacks->MemoryRead(mod_offset + 4, sizeof(u32))};
|
||||||
VAddr rela_dyn = 0, relr_dyn = 0;
|
VAddr rela_dyn = 0, relr_dyn = 0;
|
||||||
size_t num_rela = 0, num_relr = 0;
|
size_t num_rela = 0, num_relr = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -222,8 +216,8 @@ public:
|
|||||||
for (size_t i = 0; i < num_rela; i++) {
|
for (size_t i = 0; i < num_rela; i++) {
|
||||||
const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))};
|
const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))};
|
||||||
if (Elf64RelType(rela.r_info) == ElfAArch64Relative) {
|
if (Elf64RelType(rela.r_info) == ElfAArch64Relative) {
|
||||||
const VAddr contents{callbacks->MemoryRead64(rela.r_offset)};
|
const VAddr contents{callbacks->MemoryRead(rela.r_offset, sizeof(u64))};
|
||||||
callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend);
|
callbacks->MemoryWrite(rela.r_offset, contents + rela.r_addend, sizeof(u64));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,7 +225,7 @@ public:
|
|||||||
for (size_t i = 0; i < num_relr; i++) {
|
for (size_t i = 0; i < num_relr; i++) {
|
||||||
const auto relr = callbacks->ReadMemory<Elf64_Relr>(relr_dyn + i * sizeof(Elf64_Relr));
|
const auto relr = callbacks->ReadMemory<Elf64_Relr>(relr_dyn + i * sizeof(Elf64_Relr));
|
||||||
const auto incr = [&](VAddr where) {
|
const auto incr = [&](VAddr where) {
|
||||||
callbacks->MemoryWrite64(where, callbacks->MemoryRead64(where) + relocbase);
|
callbacks->MemoryWrite(where, callbacks->MemoryRead(where, sizeof(u64)) + relocbase, sizeof(u64));
|
||||||
};
|
};
|
||||||
if ((relr & 1) == 0) {
|
if ((relr & 1) == 0) {
|
||||||
// where pointer
|
// where pointer
|
||||||
@@ -294,7 +288,7 @@ public:
|
|||||||
if (argument_stack.size() > 8) {
|
if (argument_stack.size() > 8) {
|
||||||
const VAddr new_sp = Common::AlignDown(top_of_stack - (argument_stack.size() - 8) * sizeof(u64), STACK_ALIGN);
|
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++)
|
for (size_t i = 8; i < argument_stack.size(); i++)
|
||||||
callbacks->MemoryWrite64(new_sp + (i - 8) * sizeof(u64), argument_stack[i]);
|
callbacks->MemoryWrite(new_sp + (i - 8) * sizeof(u64), argument_stack[i], sizeof(u64));
|
||||||
jit->SetSP(new_sp);
|
jit->SetSP(new_sp);
|
||||||
}
|
}
|
||||||
// Reset the call state for the next invocation
|
// Reset the call state for the next invocation
|
||||||
@@ -385,17 +379,17 @@ void DynarmicCallbacks64::CallSVC(u32 swi) {
|
|||||||
|
|
||||||
if (dest < src) {
|
if (dest < src) {
|
||||||
for (size_t i = 0; i < n; i++)
|
for (size_t i = 0; i < n; i++)
|
||||||
MemoryWrite8(dest + i, MemoryRead8(src + i));
|
MemoryWrite(dest + i, u8(MemoryRead(src + i, sizeof(u8))), sizeof(u8));
|
||||||
} else {
|
} else {
|
||||||
for (size_t i = n; i > 0; i--)
|
for (size_t i = n; i > 0; i--)
|
||||||
MemoryWrite8(dest + i - 1, MemoryRead8(src + i - 1));
|
MemoryWrite(dest + i - 1, u8(MemoryRead(src + i - 1, sizeof(u8))), sizeof(u8));
|
||||||
}
|
}
|
||||||
} else if (pc == parent.helpers[size_t(HelperFn::Memset)]) {
|
} else if (pc == parent.helpers[size_t(HelperFn::Memset)]) {
|
||||||
const VAddr dest{parent.jit->GetRegister(0)};
|
const VAddr dest{parent.jit->GetRegister(0)};
|
||||||
const u64 c{parent.jit->GetRegister(1)};
|
const u64 c{parent.jit->GetRegister(1)};
|
||||||
const size_t n{parent.jit->GetRegister(2)};
|
const size_t n{parent.jit->GetRegister(2)};
|
||||||
for (size_t i = 0; i < n; i++)
|
for (size_t i = 0; i < n; i++)
|
||||||
MemoryWrite8(dest + i, u8(c));
|
MemoryWrite(dest + i, u8(c), sizeof(u8));
|
||||||
} else if (pc == parent.helpers[size_t(HelperFn::Resolve)]) {
|
} else if (pc == parent.helpers[size_t(HelperFn::Resolve)]) {
|
||||||
// X0 contains a char* for a symbol to resolve
|
// X0 contains a char* for a symbol to resolve
|
||||||
const auto name{MemoryReadCString(parent.jit->GetRegister(0))};
|
const auto name{MemoryReadCString(parent.jit->GetRegister(0))};
|
||||||
@@ -422,7 +416,7 @@ void DynarmicCallbacks64::CallSVC(u32 swi) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DynarmicCallbacks64::ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) {
|
void DynarmicCallbacks64::ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) {
|
||||||
auto const inst = MemoryRead32(pc);
|
auto const inst = MemoryRead(pc, sizeof(u32));
|
||||||
LOG_CRITICAL(Service_JIT, "{} PC @ {:08x}, data = {:08x}", exception, pc, inst);
|
LOG_CRITICAL(Service_JIT, "{} PC @ {:08x}, data = {:08x}", exception, pc, inst);
|
||||||
parent.jit->HaltExecution();
|
parent.jit->HaltExecution();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
#include "common/container/unordered_map.h"
|
#include "common/container/unordered_map.h"
|
||||||
|
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "common/socket_types.h"
|
#include "core/internal_network/socket_types.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
#include "core/hle/service/ldn/ldn_results.h"
|
#include "core/hle/service/ldn/ldn_results.h"
|
||||||
#include "core/hle/service/ldn/ldn_types.h"
|
#include "core/hle/service/ldn/ldn_types.h"
|
||||||
|
|||||||
@@ -6,10 +6,16 @@
|
|||||||
|
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
#include "core/core.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/ipc_helpers.h"
|
||||||
#include "core/hle/service/ngc/ngc.h"
|
#include "core/hle/service/ngc/ngc.h"
|
||||||
#include "core/hle/service/server_manager.h"
|
#include "core/hle/service/server_manager.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
|
#include "frontend_common/firmware_manager.h"
|
||||||
|
|
||||||
namespace Service::NGC {
|
namespace Service::NGC {
|
||||||
|
|
||||||
@@ -166,12 +172,123 @@ 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) {
|
void LoopProcess(Core::System& system) {
|
||||||
auto server_manager = std::make_unique<ServerManager>(system);
|
auto server_manager = std::make_unique<ServerManager>(system);
|
||||||
|
|
||||||
server_manager->RegisterNamedService("ngct:u", std::make_shared<IService>(system), 4);
|
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("ngct:s", std::make_shared<IServiceWithManagementApi>(system), 4);
|
||||||
server_manager->RegisterNamedService("ngc:u", std::make_shared<NgcServiceImpl>(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));
|
ServerManager::RunServer(std::move(server_manager));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -228,9 +228,9 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
|
|||||||
{930, nullptr, "Unknown930"}, //20.0.0+
|
{930, nullptr, "Unknown930"}, //20.0.0+
|
||||||
{931, nullptr, "Unknown931"}, //20.0.0+
|
{931, nullptr, "Unknown931"}, //20.0.0+
|
||||||
{933, nullptr, "Unknown933"}, //20.0.0+
|
{933, nullptr, "Unknown933"}, //20.0.0+
|
||||||
{934, nullptr, "Unknown934"}, //20.0.0+
|
{934, nullptr, "Unknown934"}, //21.0.0+
|
||||||
{935, nullptr, "Unknown935"}, //20.0.0+
|
{935, nullptr, "Unknown935"}, //21.0.0+
|
||||||
{936, nullptr, "Unknown936"}, //20.0.0+
|
{936, D<&IApplicationManagerInterface::Unknown936>, "Unknown936"}, //21.0.0+
|
||||||
{1000, nullptr, "RequestVerifyApplicationDeprecated"},
|
{1000, nullptr, "RequestVerifyApplicationDeprecated"},
|
||||||
{1001, nullptr, "CorruptApplicationForDebug"},
|
{1001, nullptr, "CorruptApplicationForDebug"},
|
||||||
{1002, nullptr, "RequestVerifyAddOnContentsRights"},
|
{1002, nullptr, "RequestVerifyAddOnContentsRights"},
|
||||||
@@ -422,7 +422,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
|
|||||||
{4039, nullptr, "Unknown4039"}, //20.0.0+
|
{4039, nullptr, "Unknown4039"}, //20.0.0+
|
||||||
{4040, nullptr, "Unknown4040"}, //20.0.0+
|
{4040, nullptr, "Unknown4040"}, //20.0.0+
|
||||||
{4041, nullptr, "Unknown4041"}, //20.0.0+
|
{4041, nullptr, "Unknown4041"}, //20.0.0+
|
||||||
{4042, nullptr, "Unknown4042"}, //20.0.0+
|
{4042, D<&IApplicationManagerInterface::Unknown4042>, "Unknown4042"}, //20.0.0+
|
||||||
{4043, nullptr, "Unknown4043"}, //20.0.0+
|
{4043, nullptr, "Unknown4043"}, //20.0.0+
|
||||||
{4044, nullptr, "Unknown4044"}, //20.0.0+
|
{4044, nullptr, "Unknown4044"}, //20.0.0+
|
||||||
{4045, nullptr, "Unknown4045"}, //20.0.0+
|
{4045, nullptr, "Unknown4045"}, //20.0.0+
|
||||||
@@ -639,6 +639,12 @@ Result IApplicationManagerInterface::IsGameCardApplicationRunning(Out<bool> out_
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result IApplicationManagerInterface::Unknown936(Out<u64> out_result) {
|
||||||
|
LOG_WARNING(Service_NS, "(STUBBED) called.");
|
||||||
|
*out_result = 0;
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
Result IApplicationManagerInterface::IsAnyApplicationEntityInstalled(
|
Result IApplicationManagerInterface::IsAnyApplicationEntityInstalled(
|
||||||
Out<bool> out_is_any_application_entity_installed) {
|
Out<bool> out_is_any_application_entity_installed) {
|
||||||
LOG_WARNING(Service_NS, "(STUBBED) called");
|
LOG_WARNING(Service_NS, "(STUBBED) called");
|
||||||
@@ -862,6 +868,15 @@ Result IApplicationManagerInterface::Unknown4023(Out<u64> out_result) {
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result IApplicationManagerInterface::Unknown4042(OutInterface<IAsyncResult> out_interface,
|
||||||
|
OutCopyHandle<Kernel::KReadableEvent> out_event,
|
||||||
|
u64 arg1, u64 arg2) {
|
||||||
|
LOG_WARNING(Service_NS, "(STUBBED) called, arg1={:016X}, arg2={:016X}", arg1, arg2);
|
||||||
|
*out_event = unknown_event.GetHandle();
|
||||||
|
*out_interface = std::make_shared<IAsyncResult>(system, &unknown_event);
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
Result IApplicationManagerInterface::Unknown4053() {
|
Result IApplicationManagerInterface::Unknown4053() {
|
||||||
LOG_WARNING(Service_NS, "(STUBBED) called.");
|
LOG_WARNING(Service_NS, "(STUBBED) called.");
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "core/hle/service/cmif_types.h"
|
#include "core/hle/service/cmif_types.h"
|
||||||
|
#include "core/hle/service/ns/async_result.h"
|
||||||
#include "core/hle/service/ns/language.h"
|
#include "core/hle/service/ns/language.h"
|
||||||
#include "core/hle/service/ns/ns_types.h"
|
#include "core/hle/service/ns/ns_types.h"
|
||||||
#include "core/hle/service/os/event.h"
|
#include "core/hle/service/os/event.h"
|
||||||
@@ -34,6 +35,7 @@ public:
|
|||||||
Result GetGameCardMountFailureEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
Result GetGameCardMountFailureEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||||
Result GetGameCardWakenReadyEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
Result GetGameCardWakenReadyEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||||
Result IsGameCardApplicationRunning(Out<bool> out_is_running);
|
Result IsGameCardApplicationRunning(Out<bool> out_is_running);
|
||||||
|
Result Unknown936(Out<u64> out_result);
|
||||||
Result IsAnyApplicationEntityInstalled(Out<bool> out_is_any_application_entity_installed);
|
Result IsAnyApplicationEntityInstalled(Out<bool> out_is_any_application_entity_installed);
|
||||||
Result GetApplicationViewDeprecated(
|
Result GetApplicationViewDeprecated(
|
||||||
OutArray<ApplicationViewV19, BufferAttr_HipcMapAlias> out_application_views,
|
OutArray<ApplicationViewV19, BufferAttr_HipcMapAlias> out_application_views,
|
||||||
@@ -71,6 +73,9 @@ public:
|
|||||||
InBuffer<BufferAttr_HipcMapAlias> logo_path_buffer);
|
InBuffer<BufferAttr_HipcMapAlias> logo_path_buffer);
|
||||||
Result Unknown4022(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
Result Unknown4022(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||||
Result Unknown4023(Out<u64> out_result);
|
Result Unknown4023(Out<u64> out_result);
|
||||||
|
Result Unknown4042(OutInterface<IAsyncResult> out_interface,
|
||||||
|
OutCopyHandle<Kernel::KReadableEvent> out_event,
|
||||||
|
u64 arg1, u64 arg2);
|
||||||
Result Unknown4053();
|
Result Unknown4053();
|
||||||
Result Unknown4105();
|
Result Unknown4105();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "core/hle/service/cmif_serialization.h"
|
||||||
|
#include "core/hle/service/ns/async_result.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace Service::NS {
|
||||||
|
|
||||||
|
IAsyncResult::IAsyncResult(Core::System& system_, Service::Event* event_)
|
||||||
|
: ServiceFramework{system_, "nn::ns::detail::IAsyncResult"}, event{event_} {
|
||||||
|
// clang-format off
|
||||||
|
static const FunctionInfo functions[] = {
|
||||||
|
{0, nullptr, "Get"},
|
||||||
|
{1, D<&IAsyncResult::Cancel>, "Cancel"},
|
||||||
|
{2, nullptr, "GetErrorContext"}, // 4.0.0+
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
RegisterHandlers(functions);
|
||||||
|
}
|
||||||
|
|
||||||
|
IAsyncResult::~IAsyncResult() = default;
|
||||||
|
|
||||||
|
Result IAsyncResult::Cancel() {
|
||||||
|
LOG_DEBUG(Service_NS, "called");
|
||||||
|
if (event != nullptr) {
|
||||||
|
event->Signal(system.Kernel());
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Service::NS
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "core/hle/service/service.h"
|
||||||
|
|
||||||
|
namespace Service::NS {
|
||||||
|
|
||||||
|
class IAsyncResult final : public ServiceFramework<IAsyncResult> {
|
||||||
|
public:
|
||||||
|
explicit IAsyncResult(Core::System& system_, Service::Event* event_);
|
||||||
|
~IAsyncResult() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Result Cancel();
|
||||||
|
|
||||||
|
Service::Event* event{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Service::NS
|
||||||
@@ -311,27 +311,30 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationIcon(HLERequestCon
|
|||||||
// u64 - app count
|
// u64 - app count
|
||||||
memory.WriteBlock(t_mem_address + out_length, &app_count, sizeof(u64));
|
memory.WriteBlock(t_mem_address + out_length, &app_count, sizeof(u64));
|
||||||
out_length += sizeof(u64);
|
out_length += sizeof(u64);
|
||||||
|
ASSERT(out_length <= t_mem->GetSize());
|
||||||
// [list of u64] - size of icons
|
// [list of u64] - size of icons
|
||||||
for (size_t i = 0; i < app_count; ++i) {
|
for (size_t i = 0; i < app_count; ++i) {
|
||||||
const u64 app_id = app_ids_buffer[i];
|
const u64 app_id = app_ids_buffer[i];
|
||||||
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
|
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
|
||||||
const auto control = pm.GetControlMetadata();
|
if (const auto control = pm.GetControlMetadata(); control.second) {
|
||||||
u64 full_size = control.second->GetSize();
|
u64 full_size = control.second->GetSize();
|
||||||
memory.WriteBlock(t_mem_address + out_length, &full_size, sizeof(u64));
|
memory.WriteBlock(t_mem_address + out_length, &full_size, sizeof(u64));
|
||||||
|
}
|
||||||
out_length += sizeof(u64);
|
out_length += sizeof(u64);
|
||||||
|
ASSERT(out_length <= t_mem->GetSize());
|
||||||
}
|
}
|
||||||
// [list of raw icon data]
|
// [list of raw icon data]
|
||||||
std::vector<u8> full_icon_data;
|
|
||||||
for (size_t i = 0; i < app_count; ++i) {
|
for (size_t i = 0; i < app_count; ++i) {
|
||||||
const u64 app_id = app_ids_buffer[i];
|
const u64 app_id = app_ids_buffer[i];
|
||||||
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
|
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
|
||||||
const auto control = pm.GetControlMetadata();
|
if (const auto control = pm.GetControlMetadata(); control.second) {
|
||||||
auto const full_size = control.second->GetSize();
|
if (auto const full_size = control.second->GetSize(); full_size > 0) {
|
||||||
if (full_size > 0) {
|
std::vector<u8> full_icon_data(full_size);
|
||||||
full_icon_data.resize(full_size);
|
control.second->Read(full_icon_data.data(), full_size, 0);
|
||||||
control.second->Read(full_icon_data.data(), full_size, 0);
|
memory.WriteBlock(t_mem_address + out_length, full_icon_data.data(), full_size);
|
||||||
memory.WriteBlock(t_mem_address + out_length, full_icon_data.data(), full_size);
|
out_length += full_size;
|
||||||
out_length += full_size;
|
ASSERT(out_length <= t_mem->GetSize());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -345,6 +348,12 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationIcon(HLERequestCon
|
|||||||
void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestContext& ctx) {
|
void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestContext& ctx) {
|
||||||
const auto app_ids_buffer = ctx.ReadBuffer();
|
const auto app_ids_buffer = ctx.ReadBuffer();
|
||||||
const size_t app_count = app_ids_buffer.size() / sizeof(u64);
|
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_obj = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(ctx.GetCopyHandle(0));
|
||||||
auto* t_mem = t_mem_obj.GetPointerUnsafe();
|
auto* t_mem = t_mem_obj.GetPointerUnsafe();
|
||||||
constexpr size_t title_entry_size = sizeof(FileSys::LanguageEntry);
|
constexpr size_t title_entry_size = sizeof(FileSys::LanguageEntry);
|
||||||
@@ -354,8 +363,9 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestCo
|
|||||||
auto& memory = system.ApplicationMemory();
|
auto& memory = system.ApplicationMemory();
|
||||||
const auto t_mem_address = t_mem->GetSourceAddress();
|
const auto t_mem_address = t_mem->GetSourceAddress();
|
||||||
for (size_t i = 0; i < app_count; ++i) {
|
for (size_t i = 0; i < app_count; ++i) {
|
||||||
const u64 app_id = app_ids_buffer[i];
|
const u64 app_id = application_ids[i];
|
||||||
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
|
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(),
|
||||||
|
system.GetContentProvider()};
|
||||||
const auto control = pm.GetControlMetadata();
|
const auto control = pm.GetControlMetadata();
|
||||||
FileSys::LanguageEntry entry{};
|
FileSys::LanguageEntry entry{};
|
||||||
if (control.first != nullptr) {
|
if (control.first != nullptr) {
|
||||||
|
|||||||
@@ -129,6 +129,13 @@ ServerManager::~ServerManager() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ServerManager::StartAdditionalHostThreads(const char* name, size_t num_threads) {
|
||||||
|
for (size_t i = 0; i < num_threads; i++) {
|
||||||
|
auto thread_name = fmt::format("{}:{}", name, i + 1);
|
||||||
|
m_threads.emplace_back(m_system.Kernel().RunOnHostCoreThread(std::move(thread_name), [&] { this->LoopProcessImpl(); }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ServerManager::RunServer(std::unique_ptr<ServerManager>&& server_manager) {
|
void ServerManager::RunServer(std::unique_ptr<ServerManager>&& server_manager) {
|
||||||
server_manager->m_system.RunServer(std::move(server_manager));
|
server_manager->m_system.RunServer(std::move(server_manager));
|
||||||
}
|
}
|
||||||
@@ -245,14 +252,6 @@ Result ServerManager::ManageDeferral(Kernel::KEvent** out_event) {
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerManager::StartAdditionalHostThreads(const char* name, size_t num_threads) {
|
|
||||||
for (size_t i = 0; i < num_threads; i++) {
|
|
||||||
auto thread_name = fmt::format("{}:{}", name, i + 1);
|
|
||||||
m_threads.emplace_back(m_system.Kernel().RunOnHostCoreThread(
|
|
||||||
std::move(thread_name), [&] { this->LoopProcessImpl(); }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result ServerManager::LoopProcess() {
|
Result ServerManager::LoopProcess() {
|
||||||
SCOPE_EXIT {
|
SCOPE_EXIT {
|
||||||
m_stopped.Set();
|
m_stopped.Set();
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -48,8 +51,8 @@ public:
|
|||||||
Result ManageDeferral(Kernel::KEvent** out_event);
|
Result ManageDeferral(Kernel::KEvent** out_event);
|
||||||
|
|
||||||
Result LoopProcess();
|
Result LoopProcess();
|
||||||
void StartAdditionalHostThreads(const char* name, size_t num_threads);
|
|
||||||
|
|
||||||
|
void StartAdditionalHostThreads(const char* name, size_t num_threads);
|
||||||
static void RunServer(std::unique_ptr<ServerManager>&& server);
|
static void RunServer(std::unique_ptr<ServerManager>&& server);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
#include <fmt/ranges.h>
|
#include <fmt/ranges.h>
|
||||||
|
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "common/socket_types.h"
|
#include "core/internal_network/socket_types.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/hle/kernel/k_thread.h"
|
#include "core/hle/kernel/k_thread.h"
|
||||||
#include "core/hle/service/ipc_helpers.h"
|
#include "core/hle/service/ipc_helpers.h"
|
||||||
@@ -20,6 +20,9 @@
|
|||||||
#include "core/hle/service/sockets/sockets_translate.h"
|
#include "core/hle/service/sockets/sockets_translate.h"
|
||||||
#include "core/internal_network/network.h"
|
#include "core/internal_network/network.h"
|
||||||
#include "core/internal_network/socket_proxy.h"
|
#include "core/internal_network/socket_proxy.h"
|
||||||
|
#if defined(__unix__) && !defined(__APPLE__)
|
||||||
|
#include "core/internal_network/socket_icmp.h"
|
||||||
|
#endif
|
||||||
#include "core/internal_network/sockets.h"
|
#include "core/internal_network/sockets.h"
|
||||||
#include "network/network.h"
|
#include "network/network.h"
|
||||||
#include <common/settings.h>
|
#include <common/settings.h>
|
||||||
@@ -28,15 +31,18 @@ namespace Service::Sockets {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
bool IsConnectionBased(Type type) {
|
[[nodiscard]] bool IsConnectionBased(Network::Type type) noexcept {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Type::STREAM:
|
case Network::Type::STREAM:
|
||||||
|
case Network::Type::SEQPACKET:
|
||||||
return true;
|
return true;
|
||||||
case Type::DGRAM:
|
case Network::Type::RAW:
|
||||||
|
case Network::Type::DGRAM:
|
||||||
|
case Network::Type::RDM:
|
||||||
|
case Network::Type::Unspecified:
|
||||||
return false;
|
return false;
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Unimplemented type={}", type);
|
UNREACHABLE();
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +98,7 @@ void BSD_USA::ConnectWork::Execute(BSD_USA* bsd) {
|
|||||||
void BSD_USA::ConnectWork::Response(HLERequestContext& ctx) {
|
void BSD_USA::ConnectWork::Response(HLERequestContext& ctx) {
|
||||||
IPC::ResponseBuilder rb{ctx, 4};
|
IPC::ResponseBuilder rb{ctx, 4};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1);
|
rb.Push<s32>(bsd_errno == Network::Errno::E_SUCCESS ? 0 : -1);
|
||||||
rb.PushEnum(bsd_errno);
|
rb.PushEnum(bsd_errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,10 +176,9 @@ void BSD_USA::Socket(HLERequestContext& ctx) {
|
|||||||
const u32 domain = rp.Pop<u32>();
|
const u32 domain = rp.Pop<u32>();
|
||||||
const u32 type = rp.Pop<u32>();
|
const u32 type = rp.Pop<u32>();
|
||||||
const u32 protocol = rp.Pop<u32>();
|
const u32 protocol = rp.Pop<u32>();
|
||||||
|
|
||||||
LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);
|
LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);
|
||||||
|
|
||||||
const auto [fd, bsd_errno] = SocketImpl(Domain(domain), Type(type), Protocol(protocol));
|
const auto [fd, bsd_errno] = SocketImpl(Network::Domain(domain), Network::Type(type), Network::Protocol(protocol));
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 4};
|
IPC::ResponseBuilder rb{ctx, 4};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
@@ -189,8 +194,8 @@ void BSD_USA::SocketExempt(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);
|
LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);
|
||||||
|
|
||||||
auto [fd, bsd_errno] = SocketImpl(Domain(domain), Type(type), Protocol(protocol));
|
auto [fd, bsd_errno] = SocketImpl(Network::Domain(domain), Network::Type(type), Network::Protocol(protocol));
|
||||||
if (bsd_errno == Errno::SUCCESS) {
|
if (bsd_errno == Network::Errno::E_SUCCESS) {
|
||||||
bsd_errno = ShutdownImpl(fd, 0);
|
bsd_errno = ShutdownImpl(fd, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,13 +269,13 @@ void BSD_USA::GetPeerName(HLERequestContext& ctx) {
|
|||||||
LOG_DEBUG(Service, "called. fd={}", fd);
|
LOG_DEBUG(Service, "called. fd={}", fd);
|
||||||
|
|
||||||
std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
|
std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
|
||||||
const Errno bsd_errno = GetPeerNameImpl(fd, write_buffer);
|
const Network::Errno bsd_errno = GetPeerNameImpl(fd, write_buffer);
|
||||||
|
|
||||||
ctx.WriteBuffer(write_buffer);
|
ctx.WriteBuffer(write_buffer);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 5};
|
IPC::ResponseBuilder rb{ctx, 5};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0);
|
rb.Push<s32>(bsd_errno != Network::Errno::E_SUCCESS ? -1 : 0);
|
||||||
rb.PushEnum(bsd_errno);
|
rb.PushEnum(bsd_errno);
|
||||||
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
|
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
|
||||||
}
|
}
|
||||||
@@ -282,13 +287,13 @@ void BSD_USA::GetSockName(HLERequestContext& ctx) {
|
|||||||
LOG_DEBUG(Service, "called. fd={}", fd);
|
LOG_DEBUG(Service, "called. fd={}", fd);
|
||||||
|
|
||||||
std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
|
std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
|
||||||
const Errno bsd_errno = GetSockNameImpl(fd, write_buffer);
|
const Network::Errno bsd_errno = GetSockNameImpl(fd, write_buffer);
|
||||||
|
|
||||||
ctx.WriteBuffer(write_buffer);
|
ctx.WriteBuffer(write_buffer);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 5};
|
IPC::ResponseBuilder rb{ctx, 5};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0);
|
rb.Push<s32>(bsd_errno != Network::Errno::E_SUCCESS ? -1 : 0);
|
||||||
rb.PushEnum(bsd_errno);
|
rb.PushEnum(bsd_errno);
|
||||||
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
|
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
|
||||||
}
|
}
|
||||||
@@ -296,21 +301,19 @@ void BSD_USA::GetSockName(HLERequestContext& ctx) {
|
|||||||
void BSD_USA::GetSockOpt(HLERequestContext& ctx) {
|
void BSD_USA::GetSockOpt(HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const s32 fd = rp.Pop<s32>();
|
const s32 fd = rp.Pop<s32>();
|
||||||
const u32 level = rp.Pop<u32>();
|
const auto level = Network::SocketLevel(rp.Pop<u32>());
|
||||||
const auto optname = static_cast<OptName>(rp.Pop<u32>());
|
const auto optname = Network::OptName(rp.Pop<u32>());
|
||||||
|
|
||||||
std::vector<u8> optval(ctx.GetWriteBufferSize());
|
std::vector<u8> optval(ctx.GetWriteBufferSize());
|
||||||
|
|
||||||
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} len={:#x}", fd, level, optname,
|
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} len={:#x}", fd, level, optname, optval.size());
|
||||||
optval.size());
|
const Network::Errno err = GetSockOptImpl(fd, level, optname, optval);
|
||||||
|
|
||||||
const Errno err = GetSockOptImpl(fd, level, optname, optval);
|
|
||||||
|
|
||||||
ctx.WriteBuffer(optval);
|
ctx.WriteBuffer(optval);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 5};
|
IPC::ResponseBuilder rb{ctx, 5};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push<s32>(err == Errno::SUCCESS ? 0 : -1);
|
rb.Push<s32>(err == Network::Errno::E_SUCCESS ? 0 : -1);
|
||||||
rb.PushEnum(err);
|
rb.PushEnum(err);
|
||||||
rb.Push<u32>(static_cast<u32>(optval.size()));
|
rb.Push<u32>(static_cast<u32>(optval.size()));
|
||||||
}
|
}
|
||||||
@@ -333,7 +336,7 @@ void BSD_USA::Fcntl(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
LOG_DEBUG(Service, "called. fd={} cmd={} arg={}", fd, cmd, arg);
|
LOG_DEBUG(Service, "called. fd={} cmd={} arg={}", fd, cmd, arg);
|
||||||
|
|
||||||
const auto [ret, bsd_errno] = FcntlImpl(fd, static_cast<FcntlCmd>(cmd), arg);
|
const auto [ret, bsd_errno] = FcntlImpl(fd, Network::FcntlCmd(cmd), arg);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 4};
|
IPC::ResponseBuilder rb{ctx, 4};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
@@ -345,13 +348,11 @@ void BSD_USA::SetSockOpt(HLERequestContext& ctx) {
|
|||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
|
|
||||||
const s32 fd = rp.Pop<s32>();
|
const s32 fd = rp.Pop<s32>();
|
||||||
const u32 level = rp.Pop<u32>();
|
const Network::SocketLevel level = Network::SocketLevel(rp.Pop<u32>());
|
||||||
const OptName optname = static_cast<OptName>(rp.Pop<u32>());
|
const Network::OptName optname = Network::OptName(rp.Pop<u32>());
|
||||||
const auto optval = ctx.ReadBuffer();
|
const auto optval = ctx.ReadBuffer();
|
||||||
|
|
||||||
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} optlen={}", fd, level,
|
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} optlen={}", fd, level, u32(optname), optval.size());
|
||||||
static_cast<u32>(optname), optval.size());
|
|
||||||
|
|
||||||
BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optval));
|
BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optval));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -473,7 +474,7 @@ void BSD_USA::DuplicateSocket(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
struct OutputParameters {
|
struct OutputParameters {
|
||||||
s32 ret;
|
s32 ret;
|
||||||
Errno bsd_errno;
|
Network::Errno bsd_errno;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(OutputParameters) == 0x8);
|
static_assert(sizeof(OutputParameters) == 0x8);
|
||||||
|
|
||||||
@@ -485,7 +486,7 @@ void BSD_USA::DuplicateSocket(HLERequestContext& ctx) {
|
|||||||
if (is_user) {
|
if (is_user) {
|
||||||
rb.PushRaw(OutputParameters{
|
rb.PushRaw(OutputParameters{
|
||||||
.ret = 0,
|
.ret = 0,
|
||||||
.bsd_errno = Errno::INVAL,
|
.bsd_errno = Network::Errno::E_INVAL,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -494,10 +495,10 @@ void BSD_USA::DuplicateSocket(HLERequestContext& ctx) {
|
|||||||
if (auto* res = std::get_if<s32>(&res_v)) {
|
if (auto* res = std::get_if<s32>(&res_v)) {
|
||||||
rb.PushRaw(OutputParameters{
|
rb.PushRaw(OutputParameters{
|
||||||
.ret = *res,
|
.ret = *res,
|
||||||
.bsd_errno = Errno::SUCCESS,
|
.bsd_errno = Network::Errno::E_SUCCESS,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
auto* err = std::get_if<Errno>(&res_v);
|
auto* err = std::get_if<Network::Errno>(&res_v);
|
||||||
rb.PushRaw(OutputParameters{
|
rb.PushRaw(OutputParameters{
|
||||||
.ret = 0,
|
.ret = 0,
|
||||||
.bsd_errno = *err,
|
.bsd_errno = *err,
|
||||||
@@ -512,7 +513,7 @@ void BSD_USA::EventFd(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
LOG_WARNING(Service, "(STUBBED) called. initval={}, flags={}", initval, flags);
|
LOG_WARNING(Service, "(STUBBED) called. initval={}, flags={}", initval, flags);
|
||||||
|
|
||||||
BuildErrnoResponse(ctx, Errno::SUCCESS);
|
BuildErrnoResponse(ctx, Network::Errno::E_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Work>
|
template <typename Work>
|
||||||
@@ -521,132 +522,143 @@ void BSD_USA::ExecuteWork(HLERequestContext& ctx, Work work) {
|
|||||||
work.Response(ctx);
|
work.Response(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> BSD_USA::SocketImpl(Domain domain, Type type, Protocol protocol) {
|
std::pair<s32, Network::Errno> BSD_USA::SocketImpl(Network::Domain domain, Network::Type type, Network::Protocol protocol) {
|
||||||
// user bsd:u has restrictions on SOCK_SEQPACKET and SOCK_RAW
|
// user bsd:u has restrictions on SOCK_SEQPACKET and SOCK_RAW
|
||||||
if (is_user && (type == Type::SEQPACKET || type == Type::RAW)) {
|
LOG_DEBUG(Network, "domain={},type={},protocol={}", u32(domain), u32(type), u32(protocol));
|
||||||
if (type == Type::RAW && domain == Domain::INET && protocol == Protocol::ICMP) {
|
if (is_user && (type == Network::Type::SEQPACKET || type == Network::Type::RAW)) {
|
||||||
|
if (type == Network::Type::RAW && domain == Network::Domain::INET && protocol == Network::Protocol::ICMP) {
|
||||||
// fine, can use on bsd:s and bsd:u
|
// fine, can use on bsd:s and bsd:u
|
||||||
} else {
|
} else {
|
||||||
return {-1, Errno::INVAL};
|
return {-1, Network::Errno::E_INVAL};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[[maybe_unused]] const bool unk_flag = (static_cast<u32>(type) & 0x20000000) != 0;
|
[[maybe_unused]] const bool unk_flag = (u32(type) & 0x20000000) != 0;
|
||||||
UNIMPLEMENTED_IF_MSG(unk_flag, "Unknown flag in type");
|
UNIMPLEMENTED_IF_MSG(unk_flag, "Unknown flag in type");
|
||||||
type = static_cast<Type>(static_cast<u32>(type) & ~0x20000000);
|
type = Network::Type(u32(type) & ~0x20000000);
|
||||||
|
|
||||||
const s32 fd = FindFreeFileDescriptorHandle();
|
const s32 fd = FindFreeFileDescriptorHandle();
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
LOG_ERROR(Service, "No more file descriptors available");
|
LOG_ERROR(Service, "No more file descriptors available");
|
||||||
return {-1, Errno::MFILE};
|
return {-1, Network::Errno::E_MFILE};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Settings::values.airplane_mode.GetValue() && IsConnectionBased(type)) {
|
||||||
|
LOG_ERROR(Service, "Airplane mode is enabled, cannot create socket");
|
||||||
|
file_descriptors[fd].reset();
|
||||||
|
return {-1, Network::Errno::E_NOTCONN};
|
||||||
}
|
}
|
||||||
|
|
||||||
file_descriptors[fd] = FileDescriptor{};
|
file_descriptors[fd] = FileDescriptor{};
|
||||||
FileDescriptor& descriptor = *file_descriptors[fd];
|
FileDescriptor& descriptor = *file_descriptors[fd];
|
||||||
|
|
||||||
// ENONMEM might be thrown here
|
// ENONMEM might be thrown here
|
||||||
|
LOG_INFO(Service, "New socket fd={},domain={},type={},prot={}", fd, domain, type, protocol);
|
||||||
|
|
||||||
LOG_INFO(Service, "New socket fd={}", fd);
|
// While room is important -- we need to remember ICMP takes priority over **everything else**
|
||||||
|
// TODO: rework this so proxy sockets can be done transparently? -- like what if i need
|
||||||
|
// to browse the internet while playing LDN or something stupid like that?
|
||||||
auto room_member = Network::GetRoomMember().lock();
|
auto room_member = Network::GetRoomMember().lock();
|
||||||
if (room_member && room_member->IsConnected()) {
|
if ((protocol != Network::Protocol::ICMP && protocol != Network::Protocol::ICMPV6)
|
||||||
|
&& (room_member && room_member->IsConnected())) {
|
||||||
descriptor.socket = std::make_shared<Network::ProxySocket>();
|
descriptor.socket = std::make_shared<Network::ProxySocket>();
|
||||||
|
descriptor.socket->fd = fd;
|
||||||
} else {
|
} else {
|
||||||
descriptor.socket = std::make_shared<Network::Socket>();
|
descriptor.socket = std::make_shared<Network::Socket>();
|
||||||
}
|
}
|
||||||
|
auto const bsd_errno = descriptor.socket->Initialize(domain, type, protocol);
|
||||||
|
|
||||||
descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(protocol));
|
#if defined(__unix__) && !defined(__APPLE__)
|
||||||
descriptor.is_connection_based = IsConnectionBased(type);
|
// ...only unix has this issue it seems, ICMP works otherwise fine on win
|
||||||
|
if ((protocol == Network::Protocol::ICMP || protocol == Network::Protocol::ICMPV6)
|
||||||
if (Settings::values.airplane_mode.GetValue() && descriptor.is_connection_based) {
|
&& bsd_errno != Network::Errno::E_SUCCESS) {
|
||||||
LOG_ERROR(Service, "Airplane mode is enabled, cannot create socket");
|
LOG_WARNING(Network, "Using ICMP emulated socket");
|
||||||
return {-1, Errno::NOTCONN};
|
descriptor.socket = std::make_shared<Network::IcmpSocket>();
|
||||||
|
descriptor.socket->fd = fd;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
return {fd, Errno::SUCCESS};
|
descriptor.is_connection_based = IsConnectionBased(type);
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (descriptor.is_connection_based && descriptor.socket->fd == INVALID_SOCKET) {
|
||||||
|
#else
|
||||||
|
if (descriptor.is_connection_based && descriptor.socket->fd == Network::Socket::INVALID_SOCKET) {
|
||||||
|
#endif
|
||||||
|
file_descriptors[fd].reset();
|
||||||
|
return {-1, bsd_errno};
|
||||||
|
}
|
||||||
|
return {fd, Network::Errno::E_SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> BSD_USA::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer,
|
std::pair<s32, Network::Errno> BSD_USA::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, s32 nfds, s32 timeout) {
|
||||||
s32 nfds, s32 timeout) {
|
LOG_DEBUG(Network, "nfds={},timeout={}", nfds, timeout);
|
||||||
if (nfds <= 0) {
|
if (nfds <= 0) {
|
||||||
// When no entries are provided, -1 is returned with errno zero
|
// When no entries are provided, -1 is returned with errno zero
|
||||||
return {-1, Errno::SUCCESS};
|
return {-1, Network::Errno::E_SUCCESS};
|
||||||
}
|
}
|
||||||
if (read_buffer.size() < nfds * sizeof(PollFD)) {
|
if (read_buffer.size() < nfds * sizeof(Network::PollFD)) {
|
||||||
return {-1, Errno::INVAL};
|
return {-1, Network::Errno::E_INVAL};
|
||||||
}
|
}
|
||||||
if (write_buffer.size() < nfds * sizeof(PollFD)) {
|
if (write_buffer.size() < nfds * sizeof(Network::PollFD)) {
|
||||||
return {-1, Errno::INVAL};
|
return {-1, Network::Errno::E_INVAL};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<PollFD> fds(nfds);
|
std::span<const Network::PollFD> in_fds(reinterpret_cast<const Network::PollFD*>(read_buffer.data()), nfds);
|
||||||
std::memcpy(fds.data(), read_buffer.data(), nfds * sizeof(PollFD));
|
std::span<Network::PollFD> out_fds(reinterpret_cast<Network::PollFD*>(write_buffer.data()), nfds);
|
||||||
|
std::copy(in_fds.begin(), in_fds.end(), out_fds.begin());
|
||||||
|
|
||||||
if (timeout >= 0) {
|
if (timeout >= 0) {
|
||||||
const s64 seconds = timeout / 1000;
|
const s64 seconds = timeout / 1000;
|
||||||
const u64 nanoseconds = 1'000'000 * (static_cast<u64>(timeout) % 1000);
|
const u64 nanoseconds = 1'000'000 * (u64(timeout) % 1000);
|
||||||
|
|
||||||
if (seconds < 0) {
|
if (seconds < 0) {
|
||||||
return {-1, Errno::INVAL};
|
return {-1, Network::Errno::E_INVAL};
|
||||||
}
|
}
|
||||||
if (nanoseconds > 999'999'999) {
|
if (nanoseconds > 999'999'999) {
|
||||||
return {-1, Errno::INVAL};
|
return {-1, Network::Errno::E_INVAL};
|
||||||
}
|
}
|
||||||
} else if (timeout != -1) {
|
} else if (timeout != -1) {
|
||||||
return {-1, Errno::INVAL};
|
return {-1, Network::Errno::E_INVAL};
|
||||||
}
|
}
|
||||||
|
|
||||||
for (PollFD& pollfd : fds) {
|
for (size_t i = 0; i < in_fds.size(); ++i) {
|
||||||
ASSERT(False(pollfd.revents));
|
ASSERT(out_fds[i].fd == in_fds[i].fd && False(in_fds[i].revents));
|
||||||
|
if (!IsFileDescriptorValid(in_fds[i].fd)) {
|
||||||
if (pollfd.fd > static_cast<s32>(MAX_FD) || pollfd.fd < 0) {
|
out_fds[i].revents = {};
|
||||||
LOG_ERROR(Service, "File descriptor handle={} is invalid", pollfd.fd);
|
if (!file_descriptors[in_fds[i].fd])
|
||||||
pollfd.revents = PollEvents{};
|
out_fds[i].revents = Network::PollEvents::NVAL;
|
||||||
return {0, Errno::SUCCESS};
|
return {0, Network::Errno::E_SUCCESS};
|
||||||
}
|
|
||||||
|
|
||||||
const std::optional<FileDescriptor>& descriptor = file_descriptors[pollfd.fd];
|
|
||||||
if (!descriptor) {
|
|
||||||
LOG_TRACE(Service, "File descriptor handle={} is not allocated", pollfd.fd);
|
|
||||||
pollfd.revents = PollEvents::Nval;
|
|
||||||
return {0, Errno::SUCCESS};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Network::PollFD> host_pollfds(fds.size());
|
std::vector<Network::HostPollFD> host_pollfds(in_fds.size());
|
||||||
std::transform(fds.begin(), fds.end(), host_pollfds.begin(), [](PollFD pollfd) {
|
std::transform(in_fds.begin(), in_fds.end(), host_pollfds.begin(), [](auto const e) {
|
||||||
Network::PollFD result;
|
Network::HostPollFD result{};
|
||||||
result.socket = file_descriptors[pollfd.fd]->socket.get();
|
result.socket = file_descriptors[e.fd]->socket.get();
|
||||||
result.events = Translate(pollfd.events);
|
result.events = e.events;
|
||||||
result.revents = Network::PollEvents{};
|
result.revents = {};
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
|
auto const res = Network::Poll(host_pollfds, timeout);
|
||||||
const auto result = Network::Poll(host_pollfds, timeout);
|
for (size_t i = 0; i < in_fds.size(); ++i)
|
||||||
|
out_fds[i].revents = host_pollfds[i].revents;
|
||||||
const size_t num = host_pollfds.size();
|
return res;
|
||||||
for (size_t i = 0; i < num; ++i) {
|
|
||||||
fds[i].revents = Translate(host_pollfds[i].revents);
|
|
||||||
}
|
|
||||||
std::memcpy(write_buffer.data(), fds.data(), nfds * sizeof(PollFD));
|
|
||||||
|
|
||||||
return Translate(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> BSD_USA::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
|
std::pair<s32, Network::Errno> BSD_USA::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
|
||||||
|
LOG_DEBUG(Network, "fd={}", fd);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return {-1, Errno::BADF};
|
return {-1, Network::Errno::E_BADF};
|
||||||
}
|
}
|
||||||
|
|
||||||
const s32 new_fd = FindFreeFileDescriptorHandle();
|
const s32 new_fd = FindFreeFileDescriptorHandle();
|
||||||
if (new_fd < 0) {
|
if (new_fd < 0) {
|
||||||
LOG_ERROR(Service, "No more file descriptors available");
|
LOG_ERROR(Service, "No more file descriptors available");
|
||||||
return {-1, Errno::MFILE};
|
return {-1, Network::Errno::E_MFILE};
|
||||||
}
|
}
|
||||||
|
|
||||||
FileDescriptor& descriptor = *file_descriptors[fd];
|
FileDescriptor& descriptor = *file_descriptors[fd];
|
||||||
auto [result, bsd_errno] = descriptor.socket->Accept();
|
auto [result, bsd_errno] = descriptor.socket->Accept();
|
||||||
if (bsd_errno != Network::Errno::SUCCESS) {
|
if (bsd_errno != Network::Errno::E_SUCCESS) {
|
||||||
return {-1, Translate(bsd_errno)};
|
return {-1, bsd_errno};
|
||||||
}
|
}
|
||||||
|
|
||||||
file_descriptors[new_fd] = FileDescriptor{};
|
file_descriptors[new_fd] = FileDescriptor{};
|
||||||
@@ -654,267 +666,218 @@ std::pair<s32, Errno> BSD_USA::AcceptImpl(s32 fd, std::vector<u8>& write_buffer)
|
|||||||
new_descriptor.socket = std::move(result.socket);
|
new_descriptor.socket = std::move(result.socket);
|
||||||
new_descriptor.is_connection_based = descriptor.is_connection_based;
|
new_descriptor.is_connection_based = descriptor.is_connection_based;
|
||||||
|
|
||||||
const SockAddrIn guest_addr_in = Translate(result.sockaddr_in);
|
PutValue(write_buffer, result.sockaddr_in);
|
||||||
PutValue(write_buffer, guest_addr_in);
|
return {new_fd, Network::Errno::E_SUCCESS};
|
||||||
|
|
||||||
return {new_fd, Errno::SUCCESS};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno BSD_USA::BindImpl(s32 fd, std::span<const u8> addr) {
|
Network::Errno BSD_USA::BindImpl(s32 fd, std::span<const u8> addr) {
|
||||||
|
LOG_DEBUG(Network, "fd={}", fd);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
ASSERT(addr.size() >= 16);
|
ASSERT(addr.size() >= 16);
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto addr_in = GetValue<SockAddrIn>(addr);
|
auto addr_in = GetValue<Network::SockAddrIn>(addr);
|
||||||
|
return file_descriptors[fd]->socket->Bind(addr_in);
|
||||||
return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno BSD_USA::ConnectImpl(s32 fd, std::span<const u8> addr) {
|
Network::Errno BSD_USA::ConnectImpl(s32 fd, std::span<const u8> addr) {
|
||||||
|
LOG_DEBUG(Network, "fd={}", fd);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(addr.size() >= 16);
|
ASSERT(addr.size() >= 16);
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto addr_in = GetValue<SockAddrIn>(addr);
|
auto addr_in = GetValue<Network::SockAddrIn>(addr);
|
||||||
|
const Network::Errno result = file_descriptors[fd]->socket->Connect(addr_in);
|
||||||
const Errno result = Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in)));
|
if (result == Network::Errno::E_ISCONN) {
|
||||||
|
|
||||||
if (result == Errno::ISCONN) {
|
|
||||||
LOG_DEBUG(Service, "returned ISCONN - socket already connected");
|
LOG_DEBUG(Service, "returned ISCONN - socket already connected");
|
||||||
return Errno::SUCCESS;
|
return Network::Errno::E_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno BSD_USA::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
|
Network::Errno BSD_USA::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
|
||||||
|
LOG_DEBUG(Network, "fd={}", fd);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetPeerName();
|
const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetPeerName();
|
||||||
if (bsd_errno != Network::Errno::SUCCESS) {
|
if (bsd_errno != Network::Errno::E_SUCCESS) {
|
||||||
return Translate(bsd_errno);
|
return bsd_errno;
|
||||||
}
|
}
|
||||||
const SockAddrIn guest_addrin = Translate(addr_in);
|
ASSERT(write_buffer.size() >= addr_in.len);
|
||||||
|
write_buffer.resize(addr_in.len);
|
||||||
ASSERT(write_buffer.size() >= sizeof(guest_addrin));
|
PutValue(write_buffer, addr_in);
|
||||||
write_buffer.resize(sizeof(guest_addrin));
|
return bsd_errno;
|
||||||
PutValue(write_buffer, guest_addrin);
|
|
||||||
return Translate(bsd_errno);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno BSD_USA::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
|
Network::Errno BSD_USA::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
|
||||||
|
LOG_DEBUG(Network, "fd={}", fd);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetSockName();
|
const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetSockName();
|
||||||
if (bsd_errno != Network::Errno::SUCCESS) {
|
if (bsd_errno != Network::Errno::E_SUCCESS) {
|
||||||
return Translate(bsd_errno);
|
return bsd_errno;
|
||||||
}
|
}
|
||||||
const SockAddrIn guest_addrin = Translate(addr_in);
|
ASSERT(write_buffer.size() >= addr_in.len);
|
||||||
|
write_buffer.resize(addr_in.len);
|
||||||
ASSERT(write_buffer.size() >= sizeof(guest_addrin));
|
PutValue(write_buffer, addr_in);
|
||||||
write_buffer.resize(sizeof(guest_addrin));
|
return bsd_errno;
|
||||||
PutValue(write_buffer, guest_addrin);
|
|
||||||
return Translate(bsd_errno);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno BSD_USA::ListenImpl(s32 fd, s32 backlog) {
|
Network::Errno BSD_USA::ListenImpl(s32 fd, s32 backlog) {
|
||||||
|
LOG_DEBUG(Network, "fd={},backlog={}", fd, backlog);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
return Translate(file_descriptors[fd]->socket->Listen(backlog));
|
return file_descriptors[fd]->socket->Listen(backlog);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> BSD_USA::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) {
|
std::pair<s32, Network::Errno> BSD_USA::FcntlImpl(s32 fd, Network::FcntlCmd cmd, s32 arg) {
|
||||||
|
LOG_DEBUG(Network, "fd={},cmd={},arg={}", fd, u32(cmd), arg);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return {-1, Errno::BADF};
|
return {-1, Network::Errno::E_BADF};
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return {-1, Errno::BADF};
|
return {-1, Network::Errno::E_BADF};
|
||||||
}
|
}
|
||||||
|
|
||||||
FileDescriptor& descriptor = *file_descriptors[fd];
|
FileDescriptor& descriptor = *file_descriptors[fd];
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case FcntlCmd::GETFL:
|
case Network::FcntlCmd::GETFL:
|
||||||
ASSERT(arg == 0);
|
ASSERT(arg == 0);
|
||||||
return {descriptor.flags, Errno::SUCCESS};
|
return {descriptor.flags, Network::Errno::E_SUCCESS};
|
||||||
case FcntlCmd::SETFL: {
|
case Network::FcntlCmd::SETFL: {
|
||||||
const bool enable = (arg & Network::FLAG_O_NONBLOCK) != 0;
|
const bool enable = (arg & u32(Network::FcntlFlags::NONBLOCK_NX)) != 0;
|
||||||
const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable));
|
const Network::Errno bsd_errno = descriptor.socket->SetNonBlock(enable);
|
||||||
if (bsd_errno != Errno::SUCCESS) {
|
if (bsd_errno != Network::Errno::E_SUCCESS) {
|
||||||
return {-1, bsd_errno};
|
return {-1, bsd_errno};
|
||||||
}
|
}
|
||||||
descriptor.flags = arg;
|
descriptor.flags = arg;
|
||||||
return {0, Errno::SUCCESS};
|
return {0, Network::Errno::E_SUCCESS};
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Unimplemented cmd={}", cmd);
|
UNIMPLEMENTED_MSG("Unimplemented cmd={}", cmd);
|
||||||
return {-1, Errno::SUCCESS};
|
return {-1, Network::Errno::E_SUCCESS};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno BSD_USA::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval) {
|
Network::Errno BSD_USA::GetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::vector<u8>& optval) {
|
||||||
|
LOG_DEBUG(Network, "fd={},level={},optname={}", fd, u32(level), u32(optname));
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (level != static_cast<u32>(SocketLevel::SOCKET)) {
|
if (level != Network::SocketLevel::SOCKET) {
|
||||||
UNIMPLEMENTED_MSG("Unknown getsockopt level");
|
LOG_WARNING(Service, "(stubbed) level fd={}, level={}, optname={}", fd, level, optname);
|
||||||
return Errno::SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
|
Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
|
||||||
|
|
||||||
switch (optname) {
|
switch (optname) {
|
||||||
case OptName::ERROR_: {
|
case Network::OptName::ERROR_: {
|
||||||
auto [pending_err, getsockopt_err] = socket->GetPendingError();
|
auto [pending_err, getsockopt_err] = socket->GetPendingError();
|
||||||
if (getsockopt_err == Network::Errno::SUCCESS) {
|
if (getsockopt_err == Network::Errno::E_SUCCESS) {
|
||||||
Errno translated_pending_err = Translate(pending_err);
|
|
||||||
ASSERT_OR_EXECUTE_MSG(
|
ASSERT_OR_EXECUTE_MSG(
|
||||||
optval.size() == sizeof(Errno), { return Errno::INVAL; },
|
optval.size() == sizeof(Network::Errno), { return Network::Errno::E_INVAL; },
|
||||||
"Incorrect getsockopt option size");
|
"Incorrect getsockopt option size");
|
||||||
optval.resize(sizeof(Errno));
|
optval.resize(sizeof(Network::Errno));
|
||||||
PutValue(optval, translated_pending_err);
|
PutValue(optval, pending_err);
|
||||||
}
|
}
|
||||||
return Translate(getsockopt_err);
|
return getsockopt_err;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Unimplemented optname={}", optname);
|
UNIMPLEMENTED_MSG("Unimplemented optname={}", optname);
|
||||||
return Errno::SUCCESS;
|
return Network::Errno::E_SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno BSD_USA::SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval) {
|
Network::Errno BSD_USA::SetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval) {
|
||||||
|
LOG_DEBUG(Service, "fd={},level={},optname={}", fd, level, optname);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
|
||||||
|
|
||||||
if (level != static_cast<u32>(SocketLevel::SOCKET)) {
|
|
||||||
LOG_WARNING(Service, "(STUBBED) setsockopt with level={}, optname={}", level, optname);
|
|
||||||
return Errno::SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
|
Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
|
||||||
|
return socket->SetSockOpt(level, optname, optval);
|
||||||
if (optname == OptName::LINGER) {
|
|
||||||
ASSERT(optval.size() == sizeof(Linger));
|
|
||||||
auto linger = GetValue<Linger>(optval);
|
|
||||||
ASSERT(linger.onoff == 0 || linger.onoff == 1);
|
|
||||||
|
|
||||||
return Translate(socket->SetLinger(linger.onoff != 0, linger.linger));
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT(optval.size() == sizeof(u32));
|
|
||||||
auto value = GetValue<u32>(optval);
|
|
||||||
|
|
||||||
switch (optname) {
|
|
||||||
case OptName::REUSEADDR:
|
|
||||||
ASSERT(value == 0 || value == 1);
|
|
||||||
return Translate(socket->SetReuseAddr(value != 0));
|
|
||||||
case OptName::KEEPALIVE:
|
|
||||||
ASSERT(value == 0 || value == 1);
|
|
||||||
return Translate(socket->SetKeepAlive(value != 0));
|
|
||||||
case OptName::BROADCAST:
|
|
||||||
ASSERT(value == 0 || value == 1);
|
|
||||||
return Translate(socket->SetBroadcast(value != 0));
|
|
||||||
case OptName::SNDBUF:
|
|
||||||
return Translate(socket->SetSndBuf(value));
|
|
||||||
case OptName::RCVBUF:
|
|
||||||
return Translate(socket->SetRcvBuf(value));
|
|
||||||
case OptName::SNDTIMEO:
|
|
||||||
return Translate(socket->SetSndTimeo(value));
|
|
||||||
case OptName::RCVTIMEO:
|
|
||||||
return Translate(socket->SetRcvTimeo(value));
|
|
||||||
case OptName::NOSIGPIPE:
|
|
||||||
LOG_WARNING(Service, "(STUBBED) setting NOSIGPIPE to {}", value);
|
|
||||||
return Errno::SUCCESS;
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented optname={}", optname);
|
|
||||||
return Errno::SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno BSD_USA::ShutdownImpl(s32 fd, s32 how) {
|
Network::Errno BSD_USA::ShutdownImpl(s32 fd, s32 how) {
|
||||||
|
LOG_DEBUG(Network, "fd={},how={}", fd, how);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
const Network::ShutdownHow host_how = Translate(static_cast<ShutdownHow>(how));
|
return file_descriptors[fd]->socket->Shutdown(Network::ShutdownHow(how));
|
||||||
return Translate(file_descriptors[fd]->socket->Shutdown(host_how));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> BSD_USA::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) {
|
std::pair<s32, Network::Errno> BSD_USA::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) {
|
||||||
|
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return {-1, Errno::BADF};
|
return {-1, Network::Errno::E_BADF};
|
||||||
}
|
}
|
||||||
|
|
||||||
FileDescriptor& descriptor = *file_descriptors[fd];
|
FileDescriptor& descriptor = *file_descriptors[fd];
|
||||||
|
|
||||||
// Apply flags
|
// Apply flags
|
||||||
using Network::FLAG_MSG_DONTWAIT;
|
if ((flags & u32(Network::MsgOpt::DONTWAIT)) != 0) {
|
||||||
using Network::FLAG_O_NONBLOCK;
|
flags &= ~u32(Network::MsgOpt::DONTWAIT);
|
||||||
if ((flags & FLAG_MSG_DONTWAIT) != 0) {
|
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0) {
|
||||||
flags &= ~FLAG_MSG_DONTWAIT;
|
|
||||||
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
|
|
||||||
descriptor.socket->SetNonBlock(true);
|
descriptor.socket->SetNonBlock(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto [ret, bsd_errno] = Translate(descriptor.socket->Recv(flags, message));
|
const auto [ret, bsd_errno] = descriptor.socket->Recv(flags, message);
|
||||||
|
|
||||||
// Restore original state
|
// Restore original state
|
||||||
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
|
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0)
|
||||||
descriptor.socket->SetNonBlock(false);
|
descriptor.socket->SetNonBlock(false);
|
||||||
}
|
|
||||||
|
|
||||||
return {ret, bsd_errno};
|
return {ret, bsd_errno};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> BSD_USA::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
|
std::pair<s32, Network::Errno> BSD_USA::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message, std::vector<u8>& addr) {
|
||||||
std::vector<u8>& addr) {
|
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return {-1, Errno::BADF};
|
return {-1, Network::Errno::E_BADF};
|
||||||
}
|
}
|
||||||
|
|
||||||
FileDescriptor& descriptor = *file_descriptors[fd];
|
FileDescriptor& descriptor = *file_descriptors[fd];
|
||||||
@@ -929,19 +892,17 @@ std::pair<s32, Errno> BSD_USA::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>&
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Apply flags
|
// Apply flags
|
||||||
using Network::FLAG_MSG_DONTWAIT;
|
if ((flags & u32(Network::MsgOpt::DONTWAIT)) != 0) {
|
||||||
using Network::FLAG_O_NONBLOCK;
|
flags &= ~u32(Network::MsgOpt::DONTWAIT);
|
||||||
if ((flags & FLAG_MSG_DONTWAIT) != 0) {
|
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0) {
|
||||||
flags &= ~FLAG_MSG_DONTWAIT;
|
|
||||||
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
|
|
||||||
descriptor.socket->SetNonBlock(true);
|
descriptor.socket->SetNonBlock(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto [ret, bsd_errno] = Translate(descriptor.socket->RecvFrom(flags, message, p_addr_in));
|
const auto [ret, bsd_errno] = descriptor.socket->RecvFrom(flags, message, p_addr_in);
|
||||||
|
|
||||||
// Restore original state
|
// Restore original state
|
||||||
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
|
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0) {
|
||||||
descriptor.socket->SetNonBlock(false);
|
descriptor.socket->SetNonBlock(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -950,58 +911,59 @@ std::pair<s32, Errno> BSD_USA::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>&
|
|||||||
addr.clear();
|
addr.clear();
|
||||||
} else {
|
} else {
|
||||||
ASSERT(addr.size() >= 16);
|
ASSERT(addr.size() >= 16);
|
||||||
const SockAddrIn result = Translate(addr_in);
|
PutValue(addr, addr_in);
|
||||||
PutValue(addr, result);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {ret, bsd_errno};
|
return {ret, bsd_errno};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> BSD_USA::SendImpl(s32 fd, u32 flags, std::span<const u8> message) {
|
std::pair<s32, Network::Errno> BSD_USA::SendImpl(s32 fd, u32 flags, std::span<const u8> message) {
|
||||||
|
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return {-1, Errno::BADF};
|
return {-1, Network::Errno::E_BADF};
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return {-1, Errno::BADF};
|
return {-1, Network::Errno::E_BADF};
|
||||||
}
|
}
|
||||||
return Translate(file_descriptors[fd]->socket->Send(message, flags));
|
return file_descriptors[fd]->socket->Send(message, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> BSD_USA::SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
|
std::pair<s32, Network::Errno> BSD_USA::SendToImpl(s32 fd, u32 flags, std::span<const u8> message, std::span<const u8> addr) {
|
||||||
std::span<const u8> addr) {
|
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return {-1, Errno::BADF};
|
return {-1, Network::Errno::E_BADF};
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return {-1, Errno::BADF};
|
return {-1, Network::Errno::E_BADF};
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::SockAddrIn addr_in;
|
Network::SockAddrIn addr_in{};
|
||||||
Network::SockAddrIn* p_addr_in = nullptr;
|
Network::SockAddrIn* p_addr_in = nullptr;
|
||||||
if (!addr.empty()) {
|
if (!addr.empty()) {
|
||||||
ASSERT(addr.size() >= 16);
|
ASSERT(addr.size() >= 16);
|
||||||
auto guest_addr_in = GetValue<SockAddrIn>(addr);
|
auto guest_addr_in = GetValue<Network::SockAddrIn>(addr);
|
||||||
addr_in = Translate(guest_addr_in);
|
addr_in = guest_addr_in;
|
||||||
p_addr_in = &addr_in;
|
p_addr_in = &addr_in;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Translate(file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in));
|
return file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in);
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno BSD_USA::CloseImpl(s32 fd) {
|
Network::Errno BSD_USA::CloseImpl(s32 fd) {
|
||||||
|
LOG_DEBUG(Network, "fd={}", fd);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Errno bsd_errno = Translate(file_descriptors[fd]->socket->Close());
|
auto const bsd_errno = file_descriptors[fd]->socket->Close();
|
||||||
if (bsd_errno != Errno::SUCCESS) {
|
if (bsd_errno != Network::Errno::E_SUCCESS) {
|
||||||
return bsd_errno;
|
return bsd_errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1011,26 +973,27 @@ Errno BSD_USA::CloseImpl(s32 fd) {
|
|||||||
return bsd_errno;
|
return bsd_errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::variant<s32, Errno> BSD_USA::DuplicateSocketImpl(s32 fd) {
|
std::variant<s32, Network::Errno> BSD_USA::DuplicateSocketImpl(s32 fd) {
|
||||||
|
LOG_DEBUG(Network, "fd={}", fd);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Errno::BADF;
|
return Network::Errno::E_BADF;
|
||||||
}
|
}
|
||||||
|
// Don't use IsFileDescriptorValid as new_fd isn't a proper handle yet
|
||||||
const s32 new_fd = FindFreeFileDescriptorHandle();
|
if (s32 const new_fd = FindFreeFileDescriptorHandle(); new_fd >= 0) {
|
||||||
if (new_fd < 0) {
|
file_descriptors[new_fd] = FileDescriptor{
|
||||||
|
.socket = file_descriptors[fd]->socket,
|
||||||
|
.flags = file_descriptors[fd]->flags,
|
||||||
|
.is_connection_based = file_descriptors[fd]->is_connection_based,
|
||||||
|
};
|
||||||
|
return new_fd;
|
||||||
|
} else {
|
||||||
LOG_ERROR(Service, "No more file descriptors available");
|
LOG_ERROR(Service, "No more file descriptors available");
|
||||||
return Errno::MFILE;
|
return Network::Errno::E_MFILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
file_descriptors[new_fd] = FileDescriptor{
|
|
||||||
.socket = file_descriptors[fd]->socket,
|
|
||||||
.flags = file_descriptors[fd]->flags,
|
|
||||||
.is_connection_based = file_descriptors[fd]->is_connection_based,
|
|
||||||
};
|
|
||||||
return new_fd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::shared_ptr<Network::SocketBase>> BSD_USA::GetSocket(s32 fd) {
|
std::optional<std::shared_ptr<Network::SocketBase>> BSD_USA::GetSocket(s32 fd) {
|
||||||
|
LOG_DEBUG(Network, "fd={}", fd);
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
@@ -1042,41 +1005,40 @@ std::optional<std::shared_ptr<Network::SocketBase>> BSD_USA::GetSocket(s32 fd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
s32 BSD_USA::FindFreeFileDescriptorHandle() noexcept {
|
s32 BSD_USA::FindFreeFileDescriptorHandle() noexcept {
|
||||||
for (s32 fd = 0; fd < static_cast<s32>(file_descriptors.size()); ++fd) {
|
// first three file descriptors are reserved for:
|
||||||
if (!file_descriptors[fd]) {
|
// STDOUT_FILENO, STDIN_FILENO and STDERR_FILENO
|
||||||
|
for (s32 fd = 0; fd < s32(file_descriptors.size()); ++fd)
|
||||||
|
if (!file_descriptors[fd])
|
||||||
return fd;
|
return fd;
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BSD_USA::IsFileDescriptorValid(s32 fd) const noexcept {
|
bool BSD_USA::IsFileDescriptorValid(s32 fd) const noexcept {
|
||||||
if (fd > static_cast<s32>(MAX_FD) || fd < 0) {
|
if (fd < 0 || fd >= s32(file_descriptors.size())) {
|
||||||
LOG_ERROR(Service, "Invalid file descriptor handle={}", fd);
|
LOG_ERROR(Service, "Invalid handle={}", fd);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]) {
|
if (!file_descriptors[fd]) {
|
||||||
LOG_ERROR(Service, "File descriptor handle={} is not allocated", fd);
|
LOG_ERROR(Service, "handle={} is not allocated", fd);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BSD_USA::BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noexcept {
|
void BSD_USA::BuildErrnoResponse(HLERequestContext& ctx, Network::Errno bsd_errno) const noexcept {
|
||||||
IPC::ResponseBuilder rb{ctx, 4};
|
IPC::ResponseBuilder rb{ctx, 4};
|
||||||
|
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1);
|
rb.Push<s32>(bsd_errno == Network::Errno::E_SUCCESS ? 0 : -1);
|
||||||
rb.PushEnum(bsd_errno);
|
rb.PushEnum(bsd_errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BSD_USA::OnProxyPacketReceived(const Network::ProxyPacket& packet) {
|
void BSD_USA::OnProxyPacketReceived(const Network::ProxyPacket& packet) {
|
||||||
for (auto& optional_descriptor : file_descriptors) {
|
for (auto& optional_descriptor : file_descriptors) {
|
||||||
if (!optional_descriptor.has_value()) {
|
if (optional_descriptor.has_value()) {
|
||||||
continue;
|
FileDescriptor& descriptor = *optional_descriptor;
|
||||||
|
descriptor.socket.get()->HandleProxyPacket(packet);
|
||||||
}
|
}
|
||||||
FileDescriptor& descriptor = *optional_descriptor;
|
|
||||||
descriptor.socket.get()->HandleProxyPacket(packet);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/socket_types.h"
|
#include "core/internal_network/socket_types.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
#include "core/hle/service/sockets/sockets.h"
|
#include "core/hle/service/sockets/sockets.h"
|
||||||
#include "network/network.h"
|
#include "network/network.h"
|
||||||
@@ -35,8 +35,8 @@ public:
|
|||||||
// These methods are called from SSL; the first two are also called from
|
// These methods are called from SSL; the first two are also called from
|
||||||
// this class for the corresponding IPC methods.
|
// this class for the corresponding IPC methods.
|
||||||
// On the real device, the SSL service makes IPC calls to this service.
|
// On the real device, the SSL service makes IPC calls to this service.
|
||||||
std::variant<s32, Errno> DuplicateSocketImpl(s32 fd);
|
std::variant<s32, Network::Errno> DuplicateSocketImpl(s32 fd);
|
||||||
Errno CloseImpl(s32 fd);
|
Network::Errno CloseImpl(s32 fd);
|
||||||
std::optional<std::shared_ptr<Network::SocketBase>> GetSocket(s32 fd);
|
std::optional<std::shared_ptr<Network::SocketBase>> GetSocket(s32 fd);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -58,7 +58,7 @@ private:
|
|||||||
std::span<const u8> read_buffer;
|
std::span<const u8> read_buffer;
|
||||||
std::vector<u8> write_buffer;
|
std::vector<u8> write_buffer;
|
||||||
s32 ret{};
|
s32 ret{};
|
||||||
Errno bsd_errno{};
|
Network::Errno bsd_errno{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AcceptWork {
|
struct AcceptWork {
|
||||||
@@ -68,7 +68,7 @@ private:
|
|||||||
s32 fd;
|
s32 fd;
|
||||||
std::vector<u8> write_buffer;
|
std::vector<u8> write_buffer;
|
||||||
s32 ret{};
|
s32 ret{};
|
||||||
Errno bsd_errno{};
|
Network::Errno bsd_errno{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ConnectWork {
|
struct ConnectWork {
|
||||||
@@ -77,7 +77,7 @@ private:
|
|||||||
|
|
||||||
s32 fd;
|
s32 fd;
|
||||||
std::span<const u8> addr;
|
std::span<const u8> addr;
|
||||||
Errno bsd_errno{};
|
Network::Errno bsd_errno{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RecvWork {
|
struct RecvWork {
|
||||||
@@ -88,7 +88,7 @@ private:
|
|||||||
u32 flags;
|
u32 flags;
|
||||||
std::vector<u8> message;
|
std::vector<u8> message;
|
||||||
s32 ret{};
|
s32 ret{};
|
||||||
Errno bsd_errno{};
|
Network::Errno bsd_errno{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RecvFromWork {
|
struct RecvFromWork {
|
||||||
@@ -100,7 +100,7 @@ private:
|
|||||||
std::vector<u8> message;
|
std::vector<u8> message;
|
||||||
std::vector<u8> addr;
|
std::vector<u8> addr;
|
||||||
s32 ret{};
|
s32 ret{};
|
||||||
Errno bsd_errno{};
|
Network::Errno bsd_errno{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SendWork {
|
struct SendWork {
|
||||||
@@ -111,7 +111,7 @@ private:
|
|||||||
u32 flags;
|
u32 flags;
|
||||||
std::span<const u8> message;
|
std::span<const u8> message;
|
||||||
s32 ret{};
|
s32 ret{};
|
||||||
Errno bsd_errno{};
|
Network::Errno bsd_errno{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SendToWork {
|
struct SendToWork {
|
||||||
@@ -123,7 +123,7 @@ private:
|
|||||||
std::span<const u8> message;
|
std::span<const u8> message;
|
||||||
std::span<const u8> addr;
|
std::span<const u8> addr;
|
||||||
s32 ret{};
|
s32 ret{};
|
||||||
Errno bsd_errno{};
|
Network::Errno bsd_errno{};
|
||||||
};
|
};
|
||||||
|
|
||||||
void RegisterClient(HLERequestContext& ctx);
|
void RegisterClient(HLERequestContext& ctx);
|
||||||
@@ -155,29 +155,29 @@ private:
|
|||||||
template <typename Work>
|
template <typename Work>
|
||||||
void ExecuteWork(HLERequestContext& ctx, Work work);
|
void ExecuteWork(HLERequestContext& ctx, Work work);
|
||||||
|
|
||||||
std::pair<s32, Errno> SocketImpl(Domain domain, Type type, Protocol protocol);
|
std::pair<s32, Network::Errno> SocketImpl(Network::Domain domain, Network::Type type, Network::Protocol protocol);
|
||||||
std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, s32 nfds, s32 timeout);
|
std::pair<s32, Network::Errno> PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, s32 nfds, s32 timeout);
|
||||||
std::pair<s32, Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer);
|
std::pair<s32, Network::Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer);
|
||||||
Errno BindImpl(s32 fd, std::span<const u8> addr);
|
Network::Errno BindImpl(s32 fd, std::span<const u8> addr);
|
||||||
Errno ConnectImpl(s32 fd, std::span<const u8> addr);
|
Network::Errno ConnectImpl(s32 fd, std::span<const u8> addr);
|
||||||
Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer);
|
Network::Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer);
|
||||||
Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer);
|
Network::Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer);
|
||||||
Errno ListenImpl(s32 fd, s32 backlog);
|
Network::Errno ListenImpl(s32 fd, s32 backlog);
|
||||||
std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg);
|
std::pair<s32, Network::Errno> FcntlImpl(s32 fd, Network::FcntlCmd cmd, s32 arg);
|
||||||
Errno GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval);
|
Network::Errno GetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::vector<u8>& optval);
|
||||||
Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval);
|
Network::Errno SetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval);
|
||||||
Errno ShutdownImpl(s32 fd, s32 how);
|
Network::Errno ShutdownImpl(s32 fd, s32 how);
|
||||||
std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
|
std::pair<s32, Network::Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
|
||||||
std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
|
std::pair<s32, Network::Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
|
||||||
std::vector<u8>& addr);
|
std::vector<u8>& addr);
|
||||||
std::pair<s32, Errno> SendImpl(s32 fd, u32 flags, std::span<const u8> message);
|
std::pair<s32, Network::Errno> SendImpl(s32 fd, u32 flags, std::span<const u8> message);
|
||||||
std::pair<s32, Errno> SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
|
std::pair<s32, Network::Errno> SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
|
||||||
std::span<const u8> addr);
|
std::span<const u8> addr);
|
||||||
|
|
||||||
s32 FindFreeFileDescriptorHandle() noexcept;
|
s32 FindFreeFileDescriptorHandle() noexcept;
|
||||||
bool IsFileDescriptorValid(s32 fd) const noexcept;
|
bool IsFileDescriptorValid(s32 fd) const noexcept;
|
||||||
|
|
||||||
void BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noexcept;
|
void BuildErrnoResponse(HLERequestContext& ctx, Network::Errno bsd_errno) const noexcept;
|
||||||
|
|
||||||
static inline std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors{};
|
static inline std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors{};
|
||||||
|
|
||||||
|
|||||||
@@ -102,39 +102,39 @@ static bool IsBlockedHost(const std::string& host) {
|
|||||||
[&host](const std::string& domain) { return host.find(domain) != std::string::npos; });
|
[&host](const std::string& domain) { return host.find(domain) != std::string::npos; });
|
||||||
}
|
}
|
||||||
|
|
||||||
static NetDbError GetAddrInfoErrorToNetDbError(GetAddrInfoError result) {
|
static NetDbError GetAddrInfoErrorToNetDbError(Network::GetAddrInfoError result) {
|
||||||
// These combinations have been verified on console (but are not
|
// These combinations have been verified on console (but are not
|
||||||
// exhaustive).
|
// exhaustive).
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case GetAddrInfoError::SUCCESS:
|
case Network::GetAddrInfoError::SUCCESS:
|
||||||
return NetDbError::Success;
|
return NetDbError::Success;
|
||||||
case GetAddrInfoError::AGAIN:
|
case Network::GetAddrInfoError::AGAIN:
|
||||||
return NetDbError::TryAgain;
|
return NetDbError::TryAgain;
|
||||||
case GetAddrInfoError::NODATA:
|
case Network::GetAddrInfoError::NODATA:
|
||||||
return NetDbError::HostNotFound;
|
return NetDbError::HostNotFound;
|
||||||
case GetAddrInfoError::SERVICE:
|
case Network::GetAddrInfoError::SERVICE:
|
||||||
return NetDbError::Success;
|
return NetDbError::Success;
|
||||||
default:
|
default:
|
||||||
return NetDbError::HostNotFound;
|
return NetDbError::HostNotFound;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Errno GetAddrInfoErrorToErrno(GetAddrInfoError result) {
|
static Network::Errno GetAddrInfoErrorToErrno(Network::GetAddrInfoError result) {
|
||||||
// These combinations have been verified on console (but are not
|
// These combinations have been verified on console (but are not
|
||||||
// exhaustive).
|
// exhaustive).
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case GetAddrInfoError::SUCCESS:
|
case Network::GetAddrInfoError::SUCCESS:
|
||||||
// Note: Sometimes a successful lookup sets errno to EADDRNOTAVAIL for
|
// Note: Sometimes a successful lookup sets errno to EADDRNOTAVAIL for
|
||||||
// some reason, but that doesn't seem useful to implement.
|
// some reason, but that doesn't seem useful to implement.
|
||||||
return Errno::SUCCESS;
|
return Network::Errno::E_SUCCESS;
|
||||||
case GetAddrInfoError::AGAIN:
|
case Network::GetAddrInfoError::AGAIN:
|
||||||
return Errno::SUCCESS;
|
return Network::Errno::E_SUCCESS;
|
||||||
case GetAddrInfoError::NODATA:
|
case Network::GetAddrInfoError::NODATA:
|
||||||
return Errno::SUCCESS;
|
return Network::Errno::E_SUCCESS;
|
||||||
case GetAddrInfoError::SERVICE:
|
case Network::GetAddrInfoError::SERVICE:
|
||||||
return Errno::INVAL;
|
return Network::Errno::E_INVAL;
|
||||||
default:
|
default:
|
||||||
return Errno::SUCCESS;
|
return Network::Errno::E_SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,9 +155,7 @@ static void AppendNulTerminated(std::vector<u8>& vec, std::string_view str) {
|
|||||||
// host's gethostbyname, because it simplifies portability: e.g., getaddrinfo
|
// host's gethostbyname, because it simplifies portability: e.g., getaddrinfo
|
||||||
// behaves the same on Unix and Windows, unlike gethostbyname where Windows
|
// behaves the same on Unix and Windows, unlike gethostbyname where Windows
|
||||||
// doesn't implement h_errno.
|
// doesn't implement h_errno.
|
||||||
static std::vector<u8> SerializeAddrInfoAsHostEnt(const std::vector<Network::AddrInfo>& vec,
|
static std::vector<u8> SerializeAddrInfoAsHostEnt(std::span<const Network::AddrInfo> vec, std::string_view host) {
|
||||||
std::string_view host) {
|
|
||||||
|
|
||||||
std::vector<u8> data;
|
std::vector<u8> data;
|
||||||
// h_name: use the input hostname (append nul-terminated)
|
// h_name: use the input hostname (append nul-terminated)
|
||||||
AppendNulTerminated(data, host);
|
AppendNulTerminated(data, host);
|
||||||
@@ -165,12 +163,12 @@ static std::vector<u8> SerializeAddrInfoAsHostEnt(const std::vector<Network::Add
|
|||||||
|
|
||||||
Append<u32_be>(data, 0); // count of h_aliases
|
Append<u32_be>(data, 0); // count of h_aliases
|
||||||
// (If the count were nonzero, the aliases would be appended as nul-terminated here.)
|
// (If the count were nonzero, the aliases would be appended as nul-terminated here.)
|
||||||
Append<u16_be>(data, static_cast<u16>(Domain::INET)); // h_addrtype
|
Append<u16_be>(data, u16(Network::Domain::INET)); // h_addrtype
|
||||||
Append<u16_be>(data, sizeof(Network::IPv4Address)); // h_length
|
Append<u16_be>(data, sizeof(Network::IPv4Address)); // h_length
|
||||||
// h_addr_list:
|
// h_addr_list:
|
||||||
size_t count = vec.size();
|
size_t count = vec.size();
|
||||||
ASSERT(count <= UINT32_MAX);
|
ASSERT(count <= UINT32_MAX);
|
||||||
Append<u32_be>(data, static_cast<uint32_t>(count));
|
Append<u32_be>(data, u32(count));
|
||||||
for (const Network::AddrInfo& addrinfo : vec) {
|
for (const Network::AddrInfo& addrinfo : vec) {
|
||||||
// On the Switch, this is passed through htonl despite already being
|
// On the Switch, this is passed through htonl despite already being
|
||||||
// big-endian, so it ends up as little-endian.
|
// big-endian, so it ends up as little-endian.
|
||||||
@@ -182,7 +180,7 @@ static std::vector<u8> SerializeAddrInfoAsHostEnt(const std::vector<Network::Add
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestContext& ctx) {
|
static std::pair<u32, Network::GetAddrInfoError> GetHostByNameRequestImpl(HLERequestContext& ctx) {
|
||||||
struct InputParameters {
|
struct InputParameters {
|
||||||
u8 use_nsd_resolve;
|
u8 use_nsd_resolve;
|
||||||
u32 cancel_handle;
|
u32 cancel_handle;
|
||||||
@@ -205,7 +203,7 @@ static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestConte
|
|||||||
// Prevent resolution of Nintendo servers
|
// Prevent resolution of Nintendo servers
|
||||||
if (IsBlockedHost(host)) {
|
if (IsBlockedHost(host)) {
|
||||||
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
|
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
|
||||||
return {0, GetAddrInfoError::AGAIN};
|
return {0, Network::GetAddrInfoError::AGAIN};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto res_v = Network::GetAddressInfo(host, /*service*/ std::nullopt);
|
auto res_v = Network::GetAddressInfo(host, /*service*/ std::nullopt);
|
||||||
@@ -213,10 +211,10 @@ static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestConte
|
|||||||
const std::vector<u8> data = SerializeAddrInfoAsHostEnt(*res, host);
|
const std::vector<u8> data = SerializeAddrInfoAsHostEnt(*res, host);
|
||||||
const u32 data_size = u32(data.size());
|
const u32 data_size = u32(data.size());
|
||||||
ctx.WriteBuffer(data, 0);
|
ctx.WriteBuffer(data, 0);
|
||||||
return {data_size, GetAddrInfoError::SUCCESS};
|
return {data_size, Network::GetAddrInfoError::SUCCESS};
|
||||||
}
|
}
|
||||||
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
|
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
|
||||||
return {0, Translate(*err)};
|
return {0, *err};
|
||||||
}
|
}
|
||||||
|
|
||||||
void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) {
|
void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) {
|
||||||
@@ -224,7 +222,7 @@ void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
struct OutputParameters {
|
struct OutputParameters {
|
||||||
NetDbError netdb_error;
|
NetDbError netdb_error;
|
||||||
Errno bsd_errno;
|
Network::Errno bsd_errno;
|
||||||
u32 data_size;
|
u32 data_size;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(OutputParameters) == 0xc);
|
static_assert(sizeof(OutputParameters) == 0xc);
|
||||||
@@ -244,7 +242,7 @@ void SFDNSRES::GetHostByNameRequestWithOptions(HLERequestContext& ctx) {
|
|||||||
struct OutputParameters {
|
struct OutputParameters {
|
||||||
u32 data_size;
|
u32 data_size;
|
||||||
NetDbError netdb_error;
|
NetDbError netdb_error;
|
||||||
Errno bsd_errno;
|
Network::Errno bsd_errno;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(OutputParameters) == 0xc);
|
static_assert(sizeof(OutputParameters) == 0xc);
|
||||||
|
|
||||||
@@ -257,24 +255,23 @@ void SFDNSRES::GetHostByNameRequestWithOptions(HLERequestContext& ctx) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<u8> SerializeAddrInfo(const std::vector<Network::AddrInfo>& vec,
|
static std::vector<u8> SerializeAddrInfo(std::span<const Network::AddrInfo> vec, std::string_view host) {
|
||||||
std::string_view host) {
|
|
||||||
// Adapted from
|
// Adapted from
|
||||||
// https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190
|
// https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190
|
||||||
std::vector<u8> data;
|
std::vector<u8> data;
|
||||||
|
|
||||||
for (const Network::AddrInfo& addrinfo : vec) {
|
for (const Network::AddrInfo& addrinfo : vec) {
|
||||||
// serialized addrinfo:
|
// serialized addrinfo:
|
||||||
Append<u32_be>(data, 0xBEEFCAFE); // magic
|
Append<u32_be>(data, 0xBEEFCAFE); // magic
|
||||||
Append<u32_be>(data, 0); // ai_flags
|
Append<u32_be>(data, 0); // ai_flags
|
||||||
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.family))); // ai_family
|
Append<u32_be>(data, u32(addrinfo.family)); // ai_family
|
||||||
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.socket_type))); // ai_socktype
|
Append<u32_be>(data, u32(addrinfo.socket_type)); // ai_socktype
|
||||||
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.protocol))); // ai_protocol
|
Append<u32_be>(data, u32(addrinfo.protocol)); // ai_protocol
|
||||||
Append<u32_be>(data, 16); // ai_addrlen
|
Append<u32_be>(data, 16); // ai_addrlen
|
||||||
// ^ *not* sizeof(SerializedSockAddrIn), not that it matters since they're the same size
|
// ^ *not* sizeof(SerializedSockAddrIn), not that it matters since they're the same size
|
||||||
|
|
||||||
// ai_addr:
|
// ai_addr:
|
||||||
Append<u16_be>(data, static_cast<u16>(Translate(addrinfo.addr.family))); // sin_family
|
Append<u16_be>(data, u16(addrinfo.addr.family)); // sin_family
|
||||||
// On the Switch, the following fields are passed through htonl despite
|
// On the Switch, the following fields are passed through htonl despite
|
||||||
// already being big-endian, so they end up as little-endian.
|
// already being big-endian, so they end up as little-endian.
|
||||||
Append<u16_le>(data, addrinfo.addr.portno); // sin_port
|
Append<u16_le>(data, addrinfo.addr.portno); // sin_port
|
||||||
@@ -296,7 +293,7 @@ static std::vector<u8> SerializeAddrInfo(const std::vector<Network::AddrInfo>& v
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext& ctx) {
|
static std::pair<u32, Network::GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext& ctx) {
|
||||||
struct InputParameters {
|
struct InputParameters {
|
||||||
u8 use_nsd_resolve;
|
u8 use_nsd_resolve;
|
||||||
u32 cancel_handle;
|
u32 cancel_handle;
|
||||||
@@ -321,7 +318,7 @@ static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext
|
|||||||
// Prevent resolution of Nintendo servers
|
// Prevent resolution of Nintendo servers
|
||||||
if (IsBlockedHost(host)) {
|
if (IsBlockedHost(host)) {
|
||||||
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
|
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
|
||||||
return {0, GetAddrInfoError::AGAIN};
|
return {0, Network::GetAddrInfoError::AGAIN};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string> service = std::nullopt;
|
std::optional<std::string> service = std::nullopt;
|
||||||
@@ -331,24 +328,23 @@ static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Serialized hints are also passed in a buffer, but are ignored for now.
|
// Serialized hints are also passed in a buffer, but are ignored for now.
|
||||||
|
|
||||||
auto res_v = Network::GetAddressInfo(host, service);
|
auto res_v = Network::GetAddressInfo(host, service);
|
||||||
if (auto* res = std::get_if<std::vector<Network::AddrInfo>>(&res_v)) {
|
if (auto* res = std::get_if<std::vector<Network::AddrInfo>>(&res_v)) {
|
||||||
const std::vector<u8> data = SerializeAddrInfo(*res, host);
|
const std::vector<u8> data = SerializeAddrInfo(*res, host);
|
||||||
const u32 data_size = u32(data.size());
|
const u32 data_size = u32(data.size());
|
||||||
ctx.WriteBuffer(data, 0);
|
ctx.WriteBuffer(data, 0);
|
||||||
return {data_size, GetAddrInfoError::SUCCESS};
|
return {data_size, Network::GetAddrInfoError::SUCCESS};
|
||||||
}
|
}
|
||||||
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
|
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
|
||||||
return {0, Translate(*err)};
|
return {0, *err};
|
||||||
}
|
}
|
||||||
|
|
||||||
void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) {
|
void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) {
|
||||||
auto [data_size, emu_gai_err] = GetAddrInfoRequestImpl(ctx);
|
auto [data_size, emu_gai_err] = GetAddrInfoRequestImpl(ctx);
|
||||||
|
|
||||||
struct OutputParameters {
|
struct OutputParameters {
|
||||||
Errno bsd_errno;
|
Network::Errno bsd_errno;
|
||||||
GetAddrInfoError gai_error;
|
Network::GetAddrInfoError gai_error;
|
||||||
u32 data_size;
|
u32 data_size;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(OutputParameters) == 0xc);
|
static_assert(sizeof(OutputParameters) == 0xc);
|
||||||
@@ -364,7 +360,7 @@ void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
void SFDNSRES::GetGaiStringErrorRequest(HLERequestContext& ctx) {
|
void SFDNSRES::GetGaiStringErrorRequest(HLERequestContext& ctx) {
|
||||||
struct InputParameters {
|
struct InputParameters {
|
||||||
GetAddrInfoError gai_errno;
|
Network::GetAddrInfoError gai_errno;
|
||||||
};
|
};
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
auto input = rp.PopRaw<InputParameters>();
|
auto input = rp.PopRaw<InputParameters>();
|
||||||
@@ -382,9 +378,9 @@ void SFDNSRES::GetAddrInfoRequestWithOptions(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
struct OutputParameters {
|
struct OutputParameters {
|
||||||
u32 data_size;
|
u32 data_size;
|
||||||
GetAddrInfoError gai_error;
|
Network::GetAddrInfoError gai_error;
|
||||||
NetDbError netdb_error;
|
NetDbError netdb_error;
|
||||||
Errno bsd_errno;
|
Network::Errno bsd_errno;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(OutputParameters) == 0x10);
|
static_assert(sizeof(OutputParameters) == 0x10);
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "core/internal_network/socket_types.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
class System;
|
class System;
|
||||||
@@ -15,242 +16,6 @@ class System;
|
|||||||
|
|
||||||
namespace Service::Sockets {
|
namespace Service::Sockets {
|
||||||
|
|
||||||
enum class Errno : u32 {
|
|
||||||
SUCCESS = 0,
|
|
||||||
BADF = 9,
|
|
||||||
AGAIN = 11,
|
|
||||||
INVAL = 22,
|
|
||||||
MFILE = 24,
|
|
||||||
PIPE = 32,
|
|
||||||
MSGSIZE = 90,
|
|
||||||
CONNABORTED = 103,
|
|
||||||
CONNRESET = 104,
|
|
||||||
NOTCONN = 107,
|
|
||||||
TIMEDOUT = 110,
|
|
||||||
CONNREFUSED = 111,
|
|
||||||
INPROGRESS = 115,
|
|
||||||
ISCONN = 106,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class GetAddrInfoError : s32 {
|
|
||||||
SUCCESS = 0,
|
|
||||||
ADDRFAMILY = 1,
|
|
||||||
AGAIN = 2,
|
|
||||||
BADFLAGS = 3,
|
|
||||||
FAIL = 4,
|
|
||||||
FAMILY = 5,
|
|
||||||
MEMORY = 6,
|
|
||||||
NODATA = 7,
|
|
||||||
NONAME = 8,
|
|
||||||
SERVICE = 9,
|
|
||||||
SOCKTYPE = 10,
|
|
||||||
SYSTEM = 11,
|
|
||||||
BADHINTS = 12,
|
|
||||||
PROTOCOL = 13,
|
|
||||||
OVERFLOW_ = 14, // avoid name collision with Windows macro
|
|
||||||
OTHER = 15,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class Domain : u32 {
|
|
||||||
Unspecified = 0,
|
|
||||||
INET = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class Type : u32 {
|
|
||||||
Unspecified = 0,
|
|
||||||
STREAM = 1,
|
|
||||||
DGRAM = 2,
|
|
||||||
RAW = 3,
|
|
||||||
SEQPACKET = 5,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class Protocol : u32 {
|
|
||||||
IP = 0,
|
|
||||||
ICMP = 1,
|
|
||||||
TCP = 6,
|
|
||||||
UDP = 17,
|
|
||||||
//
|
|
||||||
IPV6 = 41,
|
|
||||||
RAW = 255,
|
|
||||||
//
|
|
||||||
HOPOPTS = 0,
|
|
||||||
IGMP = 2,
|
|
||||||
GGP = 3,
|
|
||||||
IPV4 = 4,
|
|
||||||
ST = 7,
|
|
||||||
EGP = 8,
|
|
||||||
PIGP = 9,
|
|
||||||
RCCMON = 10,
|
|
||||||
NVPII = 11,
|
|
||||||
PUP = 12,
|
|
||||||
ARGUS = 13,
|
|
||||||
EMCON = 14,
|
|
||||||
XNET = 15,
|
|
||||||
CHAOS = 16,
|
|
||||||
MUX = 18,
|
|
||||||
MEAS = 19,
|
|
||||||
HMP = 20,
|
|
||||||
PRM = 21,
|
|
||||||
IDP = 22,
|
|
||||||
TRUNK1 = 23,
|
|
||||||
TRUNK2 = 24,
|
|
||||||
LEAF1 = 25,
|
|
||||||
LEAF2 = 26,
|
|
||||||
RDP = 27,
|
|
||||||
IRTP = 28,
|
|
||||||
TP = 29,
|
|
||||||
BLT = 30,
|
|
||||||
NSP = 31,
|
|
||||||
INP = 32,
|
|
||||||
DCCP = 33,
|
|
||||||
//3PC = 34,
|
|
||||||
IDPR = 35,
|
|
||||||
XTP = 36,
|
|
||||||
DDP = 37,
|
|
||||||
CMTP = 38,
|
|
||||||
TPXX = 39,
|
|
||||||
IL = 40,
|
|
||||||
SDRP = 42,
|
|
||||||
ROUTING = 43,
|
|
||||||
FRAGMENT = 44,
|
|
||||||
IDRP = 45,
|
|
||||||
RSVP = 46,
|
|
||||||
GRE = 47,
|
|
||||||
MHRP = 48,
|
|
||||||
BHA = 49,
|
|
||||||
ESP = 50,
|
|
||||||
AH = 51,
|
|
||||||
INLSP = 52,
|
|
||||||
SWIPE = 53,
|
|
||||||
NHRP = 54,
|
|
||||||
MOBILE = 55,
|
|
||||||
TLSP = 56,
|
|
||||||
SKIP = 57,
|
|
||||||
ICMPV6 = 58,
|
|
||||||
NONE = 59,
|
|
||||||
DSTOPTS = 60,
|
|
||||||
AHIP = 61,
|
|
||||||
CFTP = 62,
|
|
||||||
HELLO = 63,
|
|
||||||
SATEXPAK = 64,
|
|
||||||
KRYPTOLAN = 65,
|
|
||||||
RVD = 66,
|
|
||||||
IPPC = 67,
|
|
||||||
ADFS = 68,
|
|
||||||
SATMON = 69,
|
|
||||||
VISA = 70,
|
|
||||||
IPCV = 71,
|
|
||||||
CPNX = 72,
|
|
||||||
CPHB = 73,
|
|
||||||
WSN = 74,
|
|
||||||
PVP = 75,
|
|
||||||
BRSATMON = 76,
|
|
||||||
ND = 77,
|
|
||||||
WBMON = 78,
|
|
||||||
WBEXPAK = 79,
|
|
||||||
EON = 80,
|
|
||||||
VMTP = 81,
|
|
||||||
SVMTP = 82,
|
|
||||||
VINES = 83,
|
|
||||||
TTP = 84,
|
|
||||||
IGP = 85,
|
|
||||||
DGP = 86,
|
|
||||||
TCF = 87,
|
|
||||||
IGRP = 88,
|
|
||||||
OSPFIGP = 89,
|
|
||||||
SRPC = 90,
|
|
||||||
LARP = 91,
|
|
||||||
MTP = 92,
|
|
||||||
AX25 = 93,
|
|
||||||
IPEIP = 94,
|
|
||||||
MICP = 95,
|
|
||||||
SCCSP = 96,
|
|
||||||
ETHERIP = 97,
|
|
||||||
ENCAP = 98,
|
|
||||||
APES = 99,
|
|
||||||
GMTP = 100,
|
|
||||||
IPCOMP = 108,
|
|
||||||
SCTP = 132,
|
|
||||||
MH = 135,
|
|
||||||
UDPLITE = 136,
|
|
||||||
HIP = 139,
|
|
||||||
SHIM6 = 140,
|
|
||||||
PIM = 103,
|
|
||||||
CARP = 112,
|
|
||||||
PGM = 113,
|
|
||||||
MPLS = 137,
|
|
||||||
PFSYNC = 240,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class SocketLevel : u32 {
|
|
||||||
IP = 0,
|
|
||||||
TCP = 6,
|
|
||||||
SOCKET = 0xffff, // i.e. SOL_SOCKET
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class OptName : u32 {
|
|
||||||
REUSEADDR = 0x4,
|
|
||||||
KEEPALIVE = 0x8,
|
|
||||||
BROADCAST = 0x20,
|
|
||||||
LINGER = 0x80,
|
|
||||||
SNDBUF = 0x1001,
|
|
||||||
RCVBUF = 0x1002,
|
|
||||||
SNDTIMEO = 0x1005,
|
|
||||||
RCVTIMEO = 0x1006,
|
|
||||||
ERROR_ = 0x1007, // avoid name collision with Windows macro
|
|
||||||
NOSIGPIPE = 0x800, // at least according to libnx
|
|
||||||
ACCEPTFILTER = 0x1000,
|
|
||||||
BINTIME = 0x2000,
|
|
||||||
NO_OFFLOAD = 0x4000,
|
|
||||||
NO_DDP = 0x8000,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class ShutdownHow : s32 {
|
|
||||||
RD = 0,
|
|
||||||
WR = 1,
|
|
||||||
RDWR = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class FcntlCmd : s32 {
|
|
||||||
GETFL = 3,
|
|
||||||
SETFL = 4,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SockAddrIn {
|
|
||||||
u8 len;
|
|
||||||
u8 family;
|
|
||||||
u16 portno;
|
|
||||||
std::array<u8, 4> ip;
|
|
||||||
std::array<u8, 248> zeroes;
|
|
||||||
};
|
|
||||||
static_assert(sizeof(SockAddrIn) == 0x100);
|
|
||||||
|
|
||||||
enum class PollEvents : u16 {
|
|
||||||
// Using Pascal case because IN is a macro on Windows.
|
|
||||||
In = 1 << 0,
|
|
||||||
Pri = 1 << 1,
|
|
||||||
Out = 1 << 2,
|
|
||||||
Err = 1 << 3,
|
|
||||||
Hup = 1 << 4,
|
|
||||||
Nval = 1 << 5,
|
|
||||||
RdNorm = 1 << 6,
|
|
||||||
RdBand = 1 << 7,
|
|
||||||
WrBand = 1 << 8,
|
|
||||||
};
|
|
||||||
|
|
||||||
DECLARE_ENUM_FLAG_OPERATORS(PollEvents);
|
|
||||||
|
|
||||||
struct PollFD {
|
|
||||||
s32 fd;
|
|
||||||
PollEvents events;
|
|
||||||
PollEvents revents;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Linger {
|
|
||||||
u32 onoff;
|
|
||||||
u32 linger;
|
|
||||||
};
|
|
||||||
|
|
||||||
void LoopProcess(Core::System& system);
|
void LoopProcess(Core::System& system);
|
||||||
|
|
||||||
} // namespace Service::Sockets
|
} // namespace Service::Sockets
|
||||||
|
|||||||
@@ -15,388 +15,42 @@
|
|||||||
|
|
||||||
namespace Service::Sockets {
|
namespace Service::Sockets {
|
||||||
|
|
||||||
Errno Translate(Network::Errno value) {
|
const char* Translate(Network::GetAddrInfoError error) {
|
||||||
switch (value) {
|
|
||||||
case Network::Errno::SUCCESS:
|
|
||||||
return Errno::SUCCESS;
|
|
||||||
case Network::Errno::BADF:
|
|
||||||
return Errno::BADF;
|
|
||||||
case Network::Errno::AGAIN:
|
|
||||||
return Errno::AGAIN;
|
|
||||||
case Network::Errno::INVAL:
|
|
||||||
return Errno::INVAL;
|
|
||||||
case Network::Errno::MFILE:
|
|
||||||
return Errno::MFILE;
|
|
||||||
case Network::Errno::PIPE:
|
|
||||||
return Errno::PIPE;
|
|
||||||
case Network::Errno::CONNREFUSED:
|
|
||||||
return Errno::CONNREFUSED;
|
|
||||||
case Network::Errno::NOTCONN:
|
|
||||||
return Errno::NOTCONN;
|
|
||||||
case Network::Errno::TIMEDOUT:
|
|
||||||
return Errno::TIMEDOUT;
|
|
||||||
case Network::Errno::CONNABORTED:
|
|
||||||
return Errno::CONNABORTED;
|
|
||||||
case Network::Errno::CONNRESET:
|
|
||||||
return Errno::CONNRESET;
|
|
||||||
case Network::Errno::INPROGRESS:
|
|
||||||
return Errno::INPROGRESS;
|
|
||||||
case Network::Errno::ISCONN:
|
|
||||||
return Errno::ISCONN;
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented errno={}", value);
|
|
||||||
return Errno::SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value) {
|
|
||||||
return {value.first, Translate(value.second)};
|
|
||||||
}
|
|
||||||
|
|
||||||
GetAddrInfoError Translate(Network::GetAddrInfoError error) {
|
|
||||||
switch (error) {
|
|
||||||
case Network::GetAddrInfoError::SUCCESS:
|
|
||||||
return GetAddrInfoError::SUCCESS;
|
|
||||||
case Network::GetAddrInfoError::ADDRFAMILY:
|
|
||||||
return GetAddrInfoError::ADDRFAMILY;
|
|
||||||
case Network::GetAddrInfoError::AGAIN:
|
|
||||||
return GetAddrInfoError::AGAIN;
|
|
||||||
case Network::GetAddrInfoError::BADFLAGS:
|
|
||||||
return GetAddrInfoError::BADFLAGS;
|
|
||||||
case Network::GetAddrInfoError::FAIL:
|
|
||||||
return GetAddrInfoError::FAIL;
|
|
||||||
case Network::GetAddrInfoError::FAMILY:
|
|
||||||
return GetAddrInfoError::FAMILY;
|
|
||||||
case Network::GetAddrInfoError::MEMORY:
|
|
||||||
return GetAddrInfoError::MEMORY;
|
|
||||||
case Network::GetAddrInfoError::NODATA:
|
|
||||||
return GetAddrInfoError::NODATA;
|
|
||||||
case Network::GetAddrInfoError::NONAME:
|
|
||||||
return GetAddrInfoError::NONAME;
|
|
||||||
case Network::GetAddrInfoError::SERVICE:
|
|
||||||
return GetAddrInfoError::SERVICE;
|
|
||||||
case Network::GetAddrInfoError::SOCKTYPE:
|
|
||||||
return GetAddrInfoError::SOCKTYPE;
|
|
||||||
case Network::GetAddrInfoError::SYSTEM:
|
|
||||||
return GetAddrInfoError::SYSTEM;
|
|
||||||
case Network::GetAddrInfoError::BADHINTS:
|
|
||||||
return GetAddrInfoError::BADHINTS;
|
|
||||||
case Network::GetAddrInfoError::PROTOCOL:
|
|
||||||
return GetAddrInfoError::PROTOCOL;
|
|
||||||
case Network::GetAddrInfoError::OVERFLOW_:
|
|
||||||
return GetAddrInfoError::OVERFLOW_;
|
|
||||||
case Network::GetAddrInfoError::OTHER:
|
|
||||||
return GetAddrInfoError::OTHER;
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented GetAddrInfoError={}", error);
|
|
||||||
return GetAddrInfoError::OTHER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* Translate(GetAddrInfoError error) {
|
|
||||||
// https://android.googlesource.com/platform/bionic/+/085543106/libc/dns/net/getaddrinfo.c#254
|
// https://android.googlesource.com/platform/bionic/+/085543106/libc/dns/net/getaddrinfo.c#254
|
||||||
switch (error) {
|
switch (error) {
|
||||||
case GetAddrInfoError::SUCCESS:
|
case Network::GetAddrInfoError::SUCCESS:
|
||||||
return "Success";
|
return "Success";
|
||||||
case GetAddrInfoError::ADDRFAMILY:
|
case Network::GetAddrInfoError::ADDRFAMILY:
|
||||||
return "Address family for hostname not supported";
|
return "Address family for hostname not supported";
|
||||||
case GetAddrInfoError::AGAIN:
|
case Network::GetAddrInfoError::AGAIN:
|
||||||
return "Temporary failure in name resolution";
|
return "Temporary failure in name resolution";
|
||||||
case GetAddrInfoError::BADFLAGS:
|
case Network::GetAddrInfoError::BADFLAGS:
|
||||||
return "Invalid value for ai_flags";
|
return "Invalid value for ai_flags";
|
||||||
case GetAddrInfoError::FAIL:
|
case Network::GetAddrInfoError::FAIL:
|
||||||
return "Non-recoverable failure in name resolution";
|
return "Non-recoverable failure in name resolution";
|
||||||
case GetAddrInfoError::FAMILY:
|
case Network::GetAddrInfoError::FAMILY:
|
||||||
return "ai_family not supported";
|
return "ai_family not supported";
|
||||||
case GetAddrInfoError::MEMORY:
|
case Network::GetAddrInfoError::MEMORY:
|
||||||
return "Memory allocation failure";
|
return "Memory allocation failure";
|
||||||
case GetAddrInfoError::NODATA:
|
case Network::GetAddrInfoError::NODATA:
|
||||||
return "No address associated with hostname";
|
return "No address associated with hostname";
|
||||||
case GetAddrInfoError::NONAME:
|
case Network::GetAddrInfoError::NONAME:
|
||||||
return "hostname nor servname provided, or not known";
|
return "hostname nor servname provided, or not known";
|
||||||
case GetAddrInfoError::SERVICE:
|
case Network::GetAddrInfoError::SERVICE:
|
||||||
return "servname not supported for ai_socktype";
|
return "servname not supported for ai_socktype";
|
||||||
case GetAddrInfoError::SOCKTYPE:
|
case Network::GetAddrInfoError::SOCKTYPE:
|
||||||
return "ai_socktype not supported";
|
return "ai_socktype not supported";
|
||||||
case GetAddrInfoError::SYSTEM:
|
case Network::GetAddrInfoError::SYSTEM:
|
||||||
return "System error returned in errno";
|
return "System error returned in errno";
|
||||||
case GetAddrInfoError::BADHINTS:
|
case Network::GetAddrInfoError::BADHINTS:
|
||||||
return "Invalid value for hints";
|
return "Invalid value for hints";
|
||||||
case GetAddrInfoError::PROTOCOL:
|
case Network::GetAddrInfoError::PROTOCOL:
|
||||||
return "Resolved protocol is unknown";
|
return "Resolved protocol is unknown";
|
||||||
case GetAddrInfoError::OVERFLOW_:
|
case Network::GetAddrInfoError::OVERFLOW_:
|
||||||
return "Argument buffer overflow";
|
return "Argument buffer overflow";
|
||||||
default:
|
default:
|
||||||
return "Unknown error";
|
return "Unknown error";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::Domain Translate(Domain domain) {
|
|
||||||
switch (domain) {
|
|
||||||
case Domain::Unspecified:
|
|
||||||
return Network::Domain::Unspecified;
|
|
||||||
case Domain::INET:
|
|
||||||
return Network::Domain::INET;
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented domain={}", domain);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Domain Translate(Network::Domain domain) {
|
|
||||||
switch (domain) {
|
|
||||||
case Network::Domain::Unspecified:
|
|
||||||
return Domain::Unspecified;
|
|
||||||
case Network::Domain::INET:
|
|
||||||
return Domain::INET;
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented domain={}", domain);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Network::Type Translate(Type type) {
|
|
||||||
switch (type) {
|
|
||||||
case Type::Unspecified:
|
|
||||||
return Network::Type::Unspecified;
|
|
||||||
case Type::STREAM:
|
|
||||||
return Network::Type::STREAM;
|
|
||||||
case Type::DGRAM:
|
|
||||||
return Network::Type::DGRAM;
|
|
||||||
case Type::RAW:
|
|
||||||
return Network::Type::RAW;
|
|
||||||
case Type::SEQPACKET:
|
|
||||||
return Network::Type::SEQPACKET;
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented type={}", type);
|
|
||||||
return Network::Type{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Type Translate(Network::Type type) {
|
|
||||||
switch (type) {
|
|
||||||
case Network::Type::Unspecified: return Type::Unspecified;
|
|
||||||
case Network::Type::STREAM: return Type::STREAM;
|
|
||||||
case Network::Type::DGRAM: return Type::DGRAM;
|
|
||||||
case Network::Type::RAW: return Type::RAW;
|
|
||||||
case Network::Type::SEQPACKET: return Type::SEQPACKET;
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented type={}", type);
|
|
||||||
return Type{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#define NETWORK_PROTOCOL_TRANSLATE_LIST \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ICMP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TCP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(UDP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPV6) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(RAW) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IGMP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(GGP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPV4) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ST) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(EGP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(PIGP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(RCCMON) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(NVPII) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(PUP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ARGUS) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(EMCON) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(XNET) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(CHAOS) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MUX) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MEAS) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(HMP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(PRM) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IDP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TRUNK1) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TRUNK2) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(LEAF1) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(LEAF2) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(RDP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IRTP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(BLT) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(NSP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(INP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(DCCP) \
|
|
||||||
/*NETWORK_PROTOCOL_TRANSLATE_ELEM(3PC)*/ \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IDPR) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(XTP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(DDP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(CMTP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TPXX) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IL) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SDRP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ROUTING) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(FRAGMENT) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IDRP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(RSVP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(GRE) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MHRP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(BHA) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ESP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(AH) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(INLSP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SWIPE) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(NHRP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MOBILE) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TLSP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SKIP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ICMPV6) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(NONE) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(DSTOPTS) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(AHIP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(CFTP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(HELLO) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SATEXPAK) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(KRYPTOLAN) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(RVD) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPPC) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ADFS) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SATMON) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(VISA) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPCV) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(CPNX) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(CPHB) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(WSN) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(PVP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(BRSATMON) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ND) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(WBMON) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(WBEXPAK) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(EON) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(VMTP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SVMTP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(VINES) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TTP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IGP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(DGP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TCF) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IGRP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(OSPFIGP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SRPC) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(LARP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MTP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(AX25) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPEIP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MICP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SCCSP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ETHERIP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ENCAP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(APES) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(GMTP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPCOMP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SCTP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MH) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(UDPLITE) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(HIP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SHIM6) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(PIM) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(CARP) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(PGM) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MPLS) \
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(PFSYNC)
|
|
||||||
[[nodiscard]] Network::Protocol Translate(Protocol protocol) {
|
|
||||||
switch (protocol) {
|
|
||||||
#define NETWORK_PROTOCOL_TRANSLATE_ELEM(name) case Protocol::name: return Network::Protocol::name;
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_LIST
|
|
||||||
#undef NETWORK_PROTOCOL_TRANSLATE_ELEM
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[[nodiscard]] Protocol Translate(Network::Protocol protocol) {
|
|
||||||
switch (protocol) {
|
|
||||||
#define NETWORK_PROTOCOL_TRANSLATE_ELEM(name) case Network::Protocol::name: return Protocol::name;
|
|
||||||
NETWORK_PROTOCOL_TRANSLATE_LIST
|
|
||||||
#undef NETWORK_PROTOCOL_TRANSLATE_ELEM
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#undef NETWORK_PROTOCOL_TRANSLATE_LIST
|
|
||||||
|
|
||||||
Network::PollEvents Translate(PollEvents flags) {
|
|
||||||
Network::PollEvents result{};
|
|
||||||
const auto translate = [&result, &flags](PollEvents from, Network::PollEvents to) {
|
|
||||||
if (True(flags & from)) {
|
|
||||||
flags &= ~from;
|
|
||||||
result |= to;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
translate(PollEvents::In, Network::PollEvents::In);
|
|
||||||
translate(PollEvents::Pri, Network::PollEvents::Pri);
|
|
||||||
translate(PollEvents::Out, Network::PollEvents::Out);
|
|
||||||
translate(PollEvents::Err, Network::PollEvents::Err);
|
|
||||||
translate(PollEvents::Hup, Network::PollEvents::Hup);
|
|
||||||
translate(PollEvents::Nval, Network::PollEvents::Nval);
|
|
||||||
translate(PollEvents::RdNorm, Network::PollEvents::RdNorm);
|
|
||||||
translate(PollEvents::RdBand, Network::PollEvents::RdBand);
|
|
||||||
translate(PollEvents::WrBand, Network::PollEvents::WrBand);
|
|
||||||
|
|
||||||
UNIMPLEMENTED_IF_MSG((u16)flags != 0, "Unimplemented flags={}", (u16)flags);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
PollEvents Translate(Network::PollEvents flags) {
|
|
||||||
PollEvents result{};
|
|
||||||
const auto translate = [&result, &flags](Network::PollEvents from, PollEvents to) {
|
|
||||||
if (True(flags & from)) {
|
|
||||||
flags &= ~from;
|
|
||||||
result |= to;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
translate(Network::PollEvents::In, PollEvents::In);
|
|
||||||
translate(Network::PollEvents::Pri, PollEvents::Pri);
|
|
||||||
translate(Network::PollEvents::Out, PollEvents::Out);
|
|
||||||
translate(Network::PollEvents::Err, PollEvents::Err);
|
|
||||||
translate(Network::PollEvents::Hup, PollEvents::Hup);
|
|
||||||
translate(Network::PollEvents::Nval, PollEvents::Nval);
|
|
||||||
translate(Network::PollEvents::RdNorm, PollEvents::RdNorm);
|
|
||||||
translate(Network::PollEvents::RdBand, PollEvents::RdBand);
|
|
||||||
translate(Network::PollEvents::WrBand, PollEvents::WrBand);
|
|
||||||
|
|
||||||
UNIMPLEMENTED_IF_MSG((u16)flags != 0, "Unimplemented flags={}", (u16)flags);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Network::SockAddrIn Translate(SockAddrIn value) {
|
|
||||||
// All lengths are valid, from [0 upto 256]
|
|
||||||
return {
|
|
||||||
.family = Translate(Domain(value.family)),
|
|
||||||
.ip = value.ip,
|
|
||||||
.portno = static_cast<u16>(value.portno >> 8 | value.portno << 8),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
SockAddrIn Translate(Network::SockAddrIn value) {
|
|
||||||
return {
|
|
||||||
.len = 16,
|
|
||||||
.family = static_cast<u8>(Translate(value.family)),
|
|
||||||
.portno = static_cast<u16>(value.portno >> 8 | value.portno << 8),
|
|
||||||
.ip = value.ip,
|
|
||||||
.zeroes = {},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Network::ShutdownHow Translate(ShutdownHow how) {
|
|
||||||
switch (how) {
|
|
||||||
case ShutdownHow::RD:
|
|
||||||
return Network::ShutdownHow::RD;
|
|
||||||
case ShutdownHow::WR:
|
|
||||||
return Network::ShutdownHow::WR;
|
|
||||||
case ShutdownHow::RDWR:
|
|
||||||
return Network::ShutdownHow::RDWR;
|
|
||||||
default:
|
|
||||||
UNIMPLEMENTED_MSG("Unimplemented how={}", how);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Service::Sockets
|
} // namespace Service::Sockets
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -11,49 +14,7 @@
|
|||||||
|
|
||||||
namespace Service::Sockets {
|
namespace Service::Sockets {
|
||||||
|
|
||||||
/// Translate abstract errno to guest errno
|
|
||||||
Errno Translate(Network::Errno value);
|
|
||||||
|
|
||||||
/// Translate abstract return value errno pair to guest return value errno pair
|
|
||||||
std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value);
|
|
||||||
|
|
||||||
/// Translate abstract getaddrinfo error to guest getaddrinfo error
|
|
||||||
GetAddrInfoError Translate(Network::GetAddrInfoError value);
|
|
||||||
|
|
||||||
/// Translate guest error to string
|
/// Translate guest error to string
|
||||||
const char* Translate(GetAddrInfoError value);
|
const char* Translate(Network::GetAddrInfoError value);
|
||||||
|
|
||||||
/// Translate guest domain to abstract domain
|
|
||||||
Network::Domain Translate(Domain domain);
|
|
||||||
|
|
||||||
/// Translate abstract domain to guest domain
|
|
||||||
Domain Translate(Network::Domain domain);
|
|
||||||
|
|
||||||
/// Translate guest type to abstract type
|
|
||||||
Network::Type Translate(Type type);
|
|
||||||
|
|
||||||
/// Translate abstract type to guest type
|
|
||||||
Type Translate(Network::Type type);
|
|
||||||
|
|
||||||
/// Translate guest protocol to abstract protocol
|
|
||||||
Network::Protocol Translate(Protocol protocol);
|
|
||||||
|
|
||||||
/// Translate abstract protocol to guest protocol
|
|
||||||
Protocol Translate(Network::Protocol protocol);
|
|
||||||
|
|
||||||
/// Translate guest poll event flags to abstract poll event flags
|
|
||||||
Network::PollEvents Translate(PollEvents flags);
|
|
||||||
|
|
||||||
/// Translate abstract poll event flags to guest poll event flags
|
|
||||||
PollEvents Translate(Network::PollEvents flags);
|
|
||||||
|
|
||||||
/// Translate guest socket address structure to abstract socket address structure
|
|
||||||
Network::SockAddrIn Translate(SockAddrIn value);
|
|
||||||
|
|
||||||
/// Translate abstract socket address structure to guest socket address structure
|
|
||||||
SockAddrIn Translate(Network::SockAddrIn value);
|
|
||||||
|
|
||||||
/// Translate guest shutdown mode to abstract shutdown mode
|
|
||||||
Network::ShutdownHow Translate(ShutdownHow how);
|
|
||||||
|
|
||||||
} // namespace Service::Sockets
|
} // namespace Service::Sockets
|
||||||
|
|||||||
+219
-245
@@ -48,6 +48,12 @@ enum class OptionType : u32 {
|
|||||||
EnableAlpn = 3,
|
EnableAlpn = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This is nn::ssl::sf::RenegotiationMode
|
||||||
|
enum RenegotiationMode : u32 {
|
||||||
|
None = 0, ///< None
|
||||||
|
Secure = 1, ///< Secure
|
||||||
|
};
|
||||||
|
|
||||||
// This is nn::ssl::sf::SslVersion
|
// This is nn::ssl::sf::SslVersion
|
||||||
struct SslVersion {
|
struct SslVersion {
|
||||||
union {
|
union {
|
||||||
@@ -75,34 +81,34 @@ public:
|
|||||||
shared_data{shared_data_in}, backend{std::move(backend_in)} {
|
shared_data{shared_data_in}, backend{std::move(backend_in)} {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, &ISslConnection::SetSocketDescriptor, "SetSocketDescriptor"},
|
{0, D<&ISslConnection::SetSocketDescriptor>, "SetSocketDescriptor"},
|
||||||
{1, &ISslConnection::SetHostName, "SetHostName"},
|
{1, D<&ISslConnection::SetHostName>, "SetHostName"},
|
||||||
{2, &ISslConnection::SetVerifyOption, "SetVerifyOption"},
|
{2, D<&ISslConnection::SetVerifyOption>, "SetVerifyOption"},
|
||||||
{3, &ISslConnection::SetIoMode, "SetIoMode"},
|
{3, D<&ISslConnection::SetIoMode>, "SetIoMode"},
|
||||||
{4, nullptr, "GetSocketDescriptor"},
|
{4, D<&ISslConnection::GetSocketDescriptor>, "GetSocketDescriptor"},
|
||||||
{5, nullptr, "GetHostName"},
|
{5, D<&ISslConnection::GetHostName>, "GetHostName"},
|
||||||
{6, nullptr, "GetVerifyOption"},
|
{6, nullptr, "GetVerifyOption"},
|
||||||
{7, nullptr, "GetIoMode"},
|
{7, D<&ISslConnection::GetIoMode>, "GetIoMode"},
|
||||||
{8, &ISslConnection::DoHandshake, "DoHandshake"},
|
{8, D<&ISslConnection::DoHandshake>, "DoHandshake"},
|
||||||
{9, &ISslConnection::DoHandshakeGetServerCert, "DoHandshakeGetServerCert"},
|
{9, &ISslConnection::DoHandshakeGetServerCert, "DoHandshakeGetServerCert"},
|
||||||
{10, &ISslConnection::Read, "Read"},
|
{10, D<&ISslConnection::Read>, "Read"},
|
||||||
{11, &ISslConnection::Write, "Write"},
|
{11, D<&ISslConnection::Write>, "Write"},
|
||||||
{12, &ISslConnection::Pending, "Pending"},
|
{12, D<&ISslConnection::Pending>, "Pending"},
|
||||||
{13, nullptr, "Peek"},
|
{13, D<&ISslConnection::Peek>, "Peek"},
|
||||||
{14, nullptr, "Poll"},
|
{14, D<&ISslConnection::Poll>, "Poll"},
|
||||||
{15, nullptr, "GetVerifyCertError"},
|
{15, D<&ISslConnection::GetVerifyCertError>, "GetVerifyCertError"},
|
||||||
{16, nullptr, "GetNeededServerCertBufferSize"},
|
{16, D<&ISslConnection::GetNeededServerCertBufferSize>, "GetNeededServerCertBufferSize"},
|
||||||
{17, &ISslConnection::SetSessionCacheMode, "SetSessionCacheMode"},
|
{17, D<&ISslConnection::SetSessionCacheMode>, "SetSessionCacheMode"},
|
||||||
{18, nullptr, "GetSessionCacheMode"},
|
{18, D<&ISslConnection::GetSessionCacheMode>, "GetSessionCacheMode"},
|
||||||
{19, nullptr, "FlushSessionCache"},
|
{19, D<&ISslConnection::FlushSessionCache>, "FlushSessionCache"},
|
||||||
{20, nullptr, "SetRenegotiationMode"},
|
{20, D<&ISslConnection::SetRenegotiationMode>, "SetRenegotiationMode"},
|
||||||
{21, nullptr, "GetRenegotiationMode"},
|
{21, D<&ISslConnection::GetRenegotiationMode>, "GetRenegotiationMode"},
|
||||||
{22, &ISslConnection::SetOption, "SetOption"},
|
{22, D<&ISslConnection::SetOption>, "SetOption"},
|
||||||
{23, &ISslConnection::GetOption, "GetOption"},
|
{23, D<&ISslConnection::GetOption>, "GetOption"},
|
||||||
{24, nullptr, "GetVerifyCertErrors"},
|
{24, nullptr, "GetVerifyCertErrors"},
|
||||||
{25, nullptr, "GetCipherInfo"},
|
{25, nullptr, "GetCipherInfo"},
|
||||||
{26, &ISslConnection::SetNextAlpnProto, "SetNextAlpnProto"},
|
{26, D<&ISslConnection::SetNextAlpnProto>, "SetNextAlpnProto"},
|
||||||
{27, &ISslConnection::GetNextAlpnProto, "GetNextAlpnProto"},
|
{27, D<&ISslConnection::GetNextAlpnProto>, "GetNextAlpnProto"},
|
||||||
{28, nullptr, "SetDtlsSocketDescriptor"},
|
{28, nullptr, "SetDtlsSocketDescriptor"},
|
||||||
{29, nullptr, "GetDtlsHandshakeTimeout"},
|
{29, nullptr, "GetDtlsHandshakeTimeout"},
|
||||||
{30, nullptr, "SetPrivateOption"},
|
{30, nullptr, "SetPrivateOption"},
|
||||||
@@ -132,7 +138,7 @@ public:
|
|||||||
auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD_USA>("bsd:u");
|
auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD_USA>("bsd:u");
|
||||||
if (bsd) {
|
if (bsd) {
|
||||||
auto err = bsd->CloseImpl(fd);
|
auto err = bsd->CloseImpl(fd);
|
||||||
if (err != Service::Sockets::Errno::SUCCESS) {
|
if (err != Network::Errno::E_SUCCESS) {
|
||||||
LOG_ERROR(Service_SSL, "Failed to close duplicated socket: {}", err);
|
LOG_ERROR(Service_SSL, "Failed to close duplicated socket: {}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -141,80 +147,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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() {
|
Result DoHandshakeImpl() {
|
||||||
ASSERT_OR_EXECUTE(!did_handshake && socket, { return ResultNoSocket; });
|
ASSERT_OR_EXECUTE(!did_handshake && socket, { return ResultNoSocket; });
|
||||||
Result res = backend->DoHandshake();
|
Result res = backend->DoHandshake();
|
||||||
@@ -234,19 +166,17 @@ private:
|
|||||||
};
|
};
|
||||||
if (!get_server_cert_chain) {
|
if (!get_server_cert_chain) {
|
||||||
// Just return the first one, unencoded.
|
// Just return the first one, unencoded.
|
||||||
ASSERT_OR_EXECUTE_MSG(
|
ASSERT_OR_EXECUTE_MSG(!certs.empty(), { return {}; }, "Should be at least one server cert");
|
||||||
!certs.empty(), { return {}; }, "Should be at least one server cert");
|
|
||||||
return certs[0];
|
return certs[0];
|
||||||
}
|
}
|
||||||
std::vector<u8> ret;
|
std::vector<u8> ret;
|
||||||
Header header{0x4E4D684374726543, static_cast<u32>(certs.size()), 0};
|
Header header{0x4E4D684374726543, u32(certs.size()), 0};
|
||||||
ret.insert(ret.end(), reinterpret_cast<u8*>(&header), reinterpret_cast<u8*>(&header + 1));
|
ret.insert(ret.end(), reinterpret_cast<u8*>(&header), reinterpret_cast<u8*>(&header + 1));
|
||||||
size_t data_offset = sizeof(Header) + certs.size() * sizeof(EntryHeader);
|
size_t data_offset = sizeof(Header) + certs.size() * sizeof(EntryHeader);
|
||||||
for (auto& cert : certs) {
|
for (auto& cert : certs) {
|
||||||
EntryHeader entry_header{static_cast<u32>(cert.size()), static_cast<u32>(data_offset)};
|
EntryHeader entry_header{u32(cert.size()), u32(data_offset)};
|
||||||
data_offset += cert.size();
|
data_offset += cert.size();
|
||||||
ret.insert(ret.end(), reinterpret_cast<u8*>(&entry_header),
|
ret.insert(ret.end(), reinterpret_cast<u8*>(&entry_header), reinterpret_cast<u8*>(&entry_header + 1));
|
||||||
reinterpret_cast<u8*>(&entry_header + 1));
|
|
||||||
}
|
}
|
||||||
for (auto& cert : certs) {
|
for (auto& cert : certs) {
|
||||||
ret.insert(ret.end(), cert.begin(), cert.end());
|
ret.insert(ret.end(), cert.begin(), cert.end());
|
||||||
@@ -254,65 +184,77 @@ private:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ReadImpl(std::vector<u8>* out_data) {
|
Result SetSocketDescriptor(s32 in_fd, Out<s32> out_fd) {
|
||||||
ASSERT_OR_EXECUTE(did_handshake, { return ResultInternalError; });
|
LOG_DEBUG(Service_SSL, "called, fd={}", in_fd);
|
||||||
size_t actual_size{};
|
ASSERT(!did_handshake);
|
||||||
Result res = backend->Read(&actual_size, *out_data);
|
auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD_USA>("bsd:u");
|
||||||
if (res != ResultSuccess) {
|
ASSERT_OR_EXECUTE(bsd, { return ResultInternalError; });
|
||||||
return res;
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
out_data->resize(actual_size);
|
LOG_ERROR(Service_SSL, "Failed to duplicate socket with fd {}", in_fd);
|
||||||
return res;
|
return ResultInvalidSocket;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result WriteImpl(size_t* out_size, std::span<const u8> data) {
|
Result SetHostName(InBuffer<BufferAttr_HipcMapAlias> buf) {
|
||||||
ASSERT_OR_EXECUTE(did_handshake, { return ResultInternalError; });
|
auto const hostname = Common::StringFromBuffer(buf);
|
||||||
return backend->Write(out_size, data);
|
LOG_DEBUG(Service_SSL, "called. hostname={}", hostname);
|
||||||
|
ASSERT(!did_handshake);
|
||||||
|
return backend->SetHostName(hostname.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
Result PendingImpl(s32* out_pending) {
|
Result SetVerifyOption(u32 option) {
|
||||||
LOG_WARNING(Service_SSL, "(STUBBED) called.");
|
LOG_DEBUG(Service_SSL, "called. option={} (forcing 0)", option);
|
||||||
*out_pending = 0;
|
ASSERT(!did_handshake);
|
||||||
return ResultSuccess;
|
verify_option = 0;
|
||||||
|
backend->SetVerifyOption(0);
|
||||||
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetSocketDescriptor(HLERequestContext& ctx) {
|
Result SetIoMode(u32 input_mode) {
|
||||||
IPC::RequestParser rp{ctx};
|
auto mode = IoMode(input_mode);
|
||||||
const s32 in_fd = rp.Pop<s32>();
|
ASSERT(mode == IoMode::Blocking || mode == IoMode::NonBlocking);
|
||||||
s32 out_fd{-1};
|
R_UNLESS(socket, ResultNoSocket);
|
||||||
const Result res = SetSocketDescriptorImpl(&out_fd, in_fd);
|
const bool non_block = mode == IoMode::NonBlocking;
|
||||||
IPC::ResponseBuilder rb{ctx, 3};
|
const Network::Errno error = socket->SetNonBlock(non_block);
|
||||||
rb.Push(res);
|
if (error != Network::Errno::E_SUCCESS) {
|
||||||
rb.Push<s32>(out_fd);
|
LOG_ERROR(Service_SSL, "Failed to set native socket non-block flag to {}", non_block);
|
||||||
|
}
|
||||||
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetHostName(HLERequestContext& ctx) {
|
Result GetSocketDescriptor(Out<u32> out_fd) {
|
||||||
const std::string hostname = Common::StringFromBuffer(ctx.ReadBuffer());
|
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||||
const Result res = SetHostNameImpl(hostname);
|
*out_fd = uint32_t(socket->GetFD());
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
R_SUCCEED();
|
||||||
rb.Push(res);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetVerifyOption(HLERequestContext& ctx) {
|
Result GetHostName(OutBuffer<BufferAttr_HipcMapAlias> data, Out<u32> out_size) {
|
||||||
IPC::RequestParser rp{ctx};
|
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||||
const u32 option = rp.Pop<u32>();
|
ASSERT(!did_handshake);
|
||||||
const Result res = SetVerifyOptionImpl(option);
|
return backend->GetHostName(std::span<u8>{data.begin(), data.end()}, out_size);
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
|
||||||
rb.Push(res);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetIoMode(HLERequestContext& ctx) {
|
Result GetIoMode(Out<u32> out_mode) {
|
||||||
IPC::RequestParser rp{ctx};
|
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||||
const u32 mode = rp.Pop<u32>();
|
R_SUCCEED();
|
||||||
const Result res = SetIoModeImpl(mode);
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
|
||||||
rb.Push(res);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoHandshake(HLERequestContext& ctx) {
|
Result DoHandshake() {
|
||||||
const Result res = DoHandshakeImpl();
|
return DoHandshakeImpl();
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
|
||||||
rb.Push(res);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoHandshakeGetServerCert(HLERequestContext& ctx) {
|
void DoHandshakeGetServerCert(HLERequestContext& ctx) {
|
||||||
@@ -351,131 +293,158 @@ private:
|
|||||||
rb.PushRaw(out);
|
rb.PushRaw(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Read(HLERequestContext& ctx) {
|
Result Read(OutBuffer<BufferAttr_HipcMapAlias> data, Out<u32> out_size) {
|
||||||
std::vector<u8> output_bytes(ctx.GetWriteBufferSize());
|
R_UNLESS(did_handshake, ResultInternalError);
|
||||||
const Result res = ReadImpl(&output_bytes);
|
size_t tmp{};
|
||||||
IPC::ResponseBuilder rb{ctx, 3};
|
auto const res = backend->Read(&tmp, data);
|
||||||
rb.Push(res);
|
*out_size = u32(tmp);
|
||||||
if (res == ResultSuccess) {
|
return res;
|
||||||
rb.Push(static_cast<u32>(output_bytes.size()));
|
|
||||||
ctx.WriteBuffer(output_bytes);
|
|
||||||
} else {
|
|
||||||
rb.Push(static_cast<u32>(0));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Write(HLERequestContext& ctx) {
|
Result Write(InBuffer<BufferAttr_HipcMapAlias> data, Out<u32> out_size) {
|
||||||
size_t write_size{0};
|
R_UNLESS(did_handshake, ResultInternalError);
|
||||||
const Result res = WriteImpl(&write_size, ctx.ReadBuffer());
|
size_t tmp{};
|
||||||
IPC::ResponseBuilder rb{ctx, 3};
|
auto const res = backend->Write(&tmp, data);
|
||||||
rb.Push(res);
|
*out_size = u32(tmp);
|
||||||
rb.Push(static_cast<u32>(write_size));
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pending(HLERequestContext& ctx) {
|
Result Pending(Out<s32> out_pending_size) {
|
||||||
s32 pending_size{0};
|
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||||
const Result res = PendingImpl(&pending_size);
|
*out_pending_size = s32(backend->Pending());
|
||||||
IPC::ResponseBuilder rb{ctx, 3};
|
R_SUCCEED();
|
||||||
rb.Push(res);
|
|
||||||
rb.Push<s32>(pending_size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetSessionCacheMode(HLERequestContext& ctx) {
|
Result Peek(OutBuffer<BufferAttr_HipcMapAlias> data, Out<u32> out_size) {
|
||||||
IPC::RequestParser rp{ctx};
|
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||||
const u32 mode = rp.Pop<u32>();
|
size_t tmp{};
|
||||||
const Result res = SetSessionCacheModeImpl(mode);
|
auto const res = backend->Peek(&tmp, data);
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
*out_size = u32(tmp);
|
||||||
rb.Push(res);
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetOption(HLERequestContext& ctx) {
|
Result Poll(u32 in_pollevent, u32 timer, Out<u32> out_pollevent) {
|
||||||
struct Parameters {
|
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||||
OptionType option;
|
R_SUCCEED();
|
||||||
s32 value;
|
|
||||||
};
|
|
||||||
static_assert(sizeof(Parameters) == 0x8, "Parameters is an invalid size");
|
|
||||||
|
|
||||||
IPC::RequestParser rp{ctx};
|
|
||||||
const auto parameters = rp.PopRaw<Parameters>();
|
|
||||||
|
|
||||||
switch (parameters.option) {
|
|
||||||
case OptionType::DoNotCloseSocket:
|
|
||||||
do_not_close_socket = static_cast<bool>(parameters.value);
|
|
||||||
break;
|
|
||||||
case OptionType::GetServerCertChain:
|
|
||||||
get_server_cert_chain = static_cast<bool>(parameters.value);
|
|
||||||
break;
|
|
||||||
case OptionType::SkipDefaultVerify:
|
|
||||||
skip_default_verify = static_cast<bool>(parameters.value);
|
|
||||||
break;
|
|
||||||
case OptionType::EnableAlpn:
|
|
||||||
enable_alpn = static_cast<bool>(parameters.value);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LOG_WARNING(Service_SSL, "Unknown option={}, value={}", parameters.option,
|
|
||||||
parameters.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
|
||||||
rb.Push(ResultSuccess);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetOption(HLERequestContext& ctx) {
|
Result GetVerifyCertError() {
|
||||||
IPC::RequestParser rp{ctx};
|
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||||
const auto option = rp.PopRaw<OptionType>();
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
u8 value = 0;
|
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 (option) {
|
||||||
case OptionType::DoNotCloseSocket:
|
case OptionType::DoNotCloseSocket:
|
||||||
value = static_cast<u8>(do_not_close_socket);
|
do_not_close_socket = bool(value);
|
||||||
break;
|
break;
|
||||||
case OptionType::GetServerCertChain:
|
case OptionType::GetServerCertChain:
|
||||||
value = static_cast<u8>(get_server_cert_chain);
|
get_server_cert_chain = bool(value);
|
||||||
break;
|
break;
|
||||||
case OptionType::SkipDefaultVerify:
|
case OptionType::SkipDefaultVerify:
|
||||||
value = static_cast<u8>(skip_default_verify);
|
skip_default_verify = bool(value);
|
||||||
break;
|
break;
|
||||||
case OptionType::EnableAlpn:
|
case OptionType::EnableAlpn:
|
||||||
value = static_cast<u8>(enable_alpn);
|
enable_alpn = bool(value);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG_WARNING(Service_SSL, "Unknown option={}", option);
|
LOG_WARNING(Service_SSL, "Unknown option={}, value={}", option, value);
|
||||||
value = 0;
|
}
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GetOption(OptionType option, Out<u8> value) {
|
||||||
|
switch (option) {
|
||||||
|
case OptionType::DoNotCloseSocket:
|
||||||
|
*value = u8(do_not_close_socket);
|
||||||
|
break;
|
||||||
|
case OptionType::GetServerCertChain:
|
||||||
|
*value = u8(get_server_cert_chain);
|
||||||
|
break;
|
||||||
|
case OptionType::SkipDefaultVerify:
|
||||||
|
*value = u8(skip_default_verify);
|
||||||
|
break;
|
||||||
|
case OptionType::EnableAlpn:
|
||||||
|
*value = u8(enable_alpn);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_WARNING(Service_SSL, "Unknown option={}", u32(option));
|
||||||
|
*value = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
LOG_DEBUG(Service_SSL, "GetOption called, option={}, ret value={}", u32(option), *value);
|
||||||
LOG_DEBUG(Service_SSL, "GetOption called, option={}, ret value={}", option, value);
|
R_SUCCEED();
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 3};
|
|
||||||
rb.Push(ResultSuccess);
|
|
||||||
rb.Push<u8>(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetNextAlpnProto(HLERequestContext& ctx) {
|
Result SetNextAlpnProto(InBuffer<BufferAttr_HipcMapAlias> data) {
|
||||||
const auto data = ctx.ReadBuffer(0);
|
auto const to_write = u32((std::min)(next_alpn_proto.size(), data.size()));
|
||||||
next_alpn_proto.assign(data.begin(), data.end());
|
next_alpn_proto.assign(data.begin(), data.begin() + to_write);
|
||||||
|
|
||||||
LOG_DEBUG(Service_SSL, "SetNextAlpnProto called, size={}", next_alpn_proto.size());
|
LOG_DEBUG(Service_SSL, "SetNextAlpnProto called, size={}", next_alpn_proto.size());
|
||||||
|
R_SUCCEED();
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
|
||||||
rb.Push(ResultSuccess);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetNextAlpnProto(HLERequestContext& ctx) {
|
Result GetNextAlpnProto(OutBuffer<BufferAttr_HipcMapAlias> data, Out<u32> to_write) {
|
||||||
const size_t writable = ctx.GetWriteBufferSize();
|
*to_write = u32((std::min)(next_alpn_proto.size(), data.size()));
|
||||||
const size_t to_write = (std::min)(next_alpn_proto.size(), writable);
|
next_alpn_proto.assign(data.begin(), data.begin() + *to_write);
|
||||||
|
LOG_DEBUG(Service_SSL, "GetNextAlpnProto called, size={}", *to_write);
|
||||||
if (to_write != 0) {
|
R_SUCCEED();
|
||||||
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> {
|
class ISslContext final : public ServiceFramework<ISslContext> {
|
||||||
@@ -492,7 +461,7 @@ public:
|
|||||||
{5, &ISslContext::ImportClientPki, "ImportClientPki"},
|
{5, &ISslContext::ImportClientPki, "ImportClientPki"},
|
||||||
{6, nullptr, "RemoveServerPki"},
|
{6, nullptr, "RemoveServerPki"},
|
||||||
{7, nullptr, "RemoveClientPki"},
|
{7, nullptr, "RemoveClientPki"},
|
||||||
{8, nullptr, "RegisterInternalPki"},
|
{8, D<&ISslContext::RegisterInternalPki>, "RegisterInternalPki"},
|
||||||
{9, nullptr, "AddPolicyOid"},
|
{9, nullptr, "AddPolicyOid"},
|
||||||
{10, nullptr, "ImportCrl"},
|
{10, nullptr, "ImportCrl"},
|
||||||
{11, nullptr, "RemoveCrl"},
|
{11, nullptr, "RemoveCrl"},
|
||||||
@@ -587,6 +556,11 @@ private:
|
|||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push(client_id);
|
rb.Push(client_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result RegisterInternalPki() {
|
||||||
|
LOG_WARNING(Service_SSL, "(STUBBED) called");
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class ISslService final : public ServiceFramework<ISslService> {
|
class ISslService final : public ServiceFramework<ISslService> {
|
||||||
|
|||||||
@@ -36,12 +36,17 @@ class SSLConnectionBackend {
|
|||||||
public:
|
public:
|
||||||
virtual ~SSLConnectionBackend() {}
|
virtual ~SSLConnectionBackend() {}
|
||||||
virtual void SetSocket(std::shared_ptr<Network::SocketBase> socket) = 0;
|
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 void SetVerifyOption(u32 option) = 0;
|
||||||
virtual Result DoHandshake() = 0;
|
virtual Result DoHandshake() = 0;
|
||||||
virtual Result Read(size_t* out_size, std::span<u8> data) = 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 Write(size_t* out_size, std::span<const u8> data) = 0;
|
||||||
virtual Result GetServerCerts(std::vector<std::vector<u8>>* out_certs) = 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);
|
Result CreateSSLConnectionBackend(std::unique_ptr<SSLConnectionBackend>* out_backend);
|
||||||
|
|||||||
@@ -157,18 +157,30 @@ public:
|
|||||||
socket = std::move(socket_in);
|
socket = std::move(socket_in);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result SetHostName(const std::string& hostname) override {
|
Result SetHostName(const char* hostname) override {
|
||||||
if (!skip_cert_verification) {
|
if (!skip_cert_verification) {
|
||||||
if (!SSL_set1_host(ssl, hostname.c_str())) {
|
if (!SSL_set1_host(ssl, hostname)) {
|
||||||
LOG_ERROR(Service_SSL, "SSL_set1_host({}) failed", hostname);
|
LOG_ERROR(Service_SSL, "SSL_set1_host({}) failed", hostname);
|
||||||
return CheckOpenSSLErrors();
|
return CheckOpenSSLErrors();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!SSL_set_tlsext_host_name(ssl, hostname.c_str())) { // hostname for SNI
|
if (!SSL_set_tlsext_host_name(ssl, hostname)) { // hostname for SNI
|
||||||
LOG_ERROR(Service_SSL, "SSL_set_tlsext_host_name({}) failed", hostname);
|
LOG_ERROR(Service_SSL, "SSL_set_tlsext_host_name({}) failed", hostname);
|
||||||
return CheckOpenSSLErrors();
|
return CheckOpenSSLErrors();
|
||||||
}
|
}
|
||||||
return ResultSuccess;
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetVerifyOption(u32 option) override {
|
void SetVerifyOption(u32 option) override {
|
||||||
@@ -213,6 +225,13 @@ public:
|
|||||||
return HandleReturn("SSL_read_ex", out_size, ret);
|
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 {
|
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);
|
const int ret = SSL_write_ex(ssl, data.data(), data.size(), out_size);
|
||||||
return HandleReturn("SSL_write_ex", out_size, ret);
|
return HandleReturn("SSL_write_ex", out_size, ret);
|
||||||
@@ -263,7 +282,25 @@ public:
|
|||||||
out_certs->emplace_back(buf, buf + len);
|
out_certs->emplace_back(buf, buf + len);
|
||||||
OPENSSL_free(buf);
|
OPENSSL_free(buf);
|
||||||
}
|
}
|
||||||
return ResultSuccess;
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
~SSLConnectionBackendOpenSSL() {
|
~SSLConnectionBackendOpenSSL() {
|
||||||
@@ -290,10 +327,10 @@ public:
|
|||||||
BIO_clear_retry_flags(bio);
|
BIO_clear_retry_flags(bio);
|
||||||
auto [actual, err] = self->socket->Send({reinterpret_cast<const u8*>(buf), len}, 0);
|
auto [actual, err] = self->socket->Send({reinterpret_cast<const u8*>(buf), len}, 0);
|
||||||
switch (err) {
|
switch (err) {
|
||||||
case Network::Errno::SUCCESS:
|
case Network::Errno::E_SUCCESS:
|
||||||
*actual_p = actual;
|
*actual_p = actual;
|
||||||
return 1;
|
return 1;
|
||||||
case Network::Errno::AGAIN:
|
case Network::Errno::E_AGAIN:
|
||||||
BIO_set_flags(bio, BIO_FLAGS_WRITE | BIO_FLAGS_SHOULD_RETRY);
|
BIO_set_flags(bio, BIO_FLAGS_WRITE | BIO_FLAGS_SHOULD_RETRY);
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
@@ -309,13 +346,13 @@ public:
|
|||||||
BIO_clear_retry_flags(bio);
|
BIO_clear_retry_flags(bio);
|
||||||
auto [actual, err] = self->socket->Recv(0, {reinterpret_cast<u8*>(buf), len});
|
auto [actual, err] = self->socket->Recv(0, {reinterpret_cast<u8*>(buf), len});
|
||||||
switch (err) {
|
switch (err) {
|
||||||
case Network::Errno::SUCCESS:
|
case Network::Errno::E_SUCCESS:
|
||||||
*actual_p = actual;
|
*actual_p = actual;
|
||||||
if (actual == 0) {
|
if (actual == 0) {
|
||||||
self->got_read_eof = true;
|
self->got_read_eof = true;
|
||||||
}
|
}
|
||||||
return actual ? 1 : 0;
|
return actual ? 1 : 0;
|
||||||
case Network::Errno::AGAIN:
|
case Network::Errno::E_AGAIN:
|
||||||
BIO_set_flags(bio, BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY);
|
BIO_set_flags(bio, BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY);
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <common/socket_types.h>
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include "core/internal_network/socket_types.h"
|
||||||
|
|
||||||
namespace Network {
|
namespace Network {
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/socket_types.h"
|
#include "core/internal_network/socket_types.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
@@ -31,68 +31,10 @@ namespace Network {
|
|||||||
class SocketBase;
|
class SocketBase;
|
||||||
class Socket;
|
class Socket;
|
||||||
|
|
||||||
/// Error code for network functions
|
struct HostPollFD {
|
||||||
enum class Errno {
|
SocketBase* socket = nullptr;
|
||||||
SUCCESS,
|
Network::PollEvents events = {};
|
||||||
BADF,
|
Network::PollEvents revents = {};
|
||||||
INVAL,
|
|
||||||
MFILE,
|
|
||||||
PIPE,
|
|
||||||
NOTCONN,
|
|
||||||
AGAIN,
|
|
||||||
CONNREFUSED,
|
|
||||||
CONNRESET,
|
|
||||||
CONNABORTED,
|
|
||||||
HOSTUNREACH,
|
|
||||||
NETDOWN,
|
|
||||||
NETUNREACH,
|
|
||||||
TIMEDOUT,
|
|
||||||
MSGSIZE,
|
|
||||||
INPROGRESS,
|
|
||||||
ISCONN,
|
|
||||||
OTHER,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class GetAddrInfoError {
|
|
||||||
SUCCESS,
|
|
||||||
ADDRFAMILY,
|
|
||||||
AGAIN,
|
|
||||||
BADFLAGS,
|
|
||||||
FAIL,
|
|
||||||
FAMILY,
|
|
||||||
MEMORY,
|
|
||||||
NODATA,
|
|
||||||
NONAME,
|
|
||||||
SERVICE,
|
|
||||||
SOCKTYPE,
|
|
||||||
SYSTEM,
|
|
||||||
BADHINTS,
|
|
||||||
PROTOCOL,
|
|
||||||
OVERFLOW_,
|
|
||||||
OTHER,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Cross-platform poll fd structure
|
|
||||||
|
|
||||||
enum class PollEvents : u16 {
|
|
||||||
// Using Pascal case because IN is a macro on Windows.
|
|
||||||
In = 1 << 0,
|
|
||||||
Pri = 1 << 1,
|
|
||||||
Out = 1 << 2,
|
|
||||||
Err = 1 << 3,
|
|
||||||
Hup = 1 << 4,
|
|
||||||
Nval = 1 << 5,
|
|
||||||
RdNorm = 1 << 6,
|
|
||||||
RdBand = 1 << 7,
|
|
||||||
WrBand = 1 << 8,
|
|
||||||
};
|
|
||||||
|
|
||||||
DECLARE_ENUM_FLAG_OPERATORS(PollEvents);
|
|
||||||
|
|
||||||
struct PollFD {
|
|
||||||
SocketBase* socket;
|
|
||||||
PollEvents events;
|
|
||||||
PollEvents revents;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class NetworkInstance {
|
class NetworkInstance {
|
||||||
@@ -101,6 +43,10 @@ public:
|
|||||||
~NetworkInstance();
|
~NetworkInstance();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
sockaddr_in TranslateFromSockAddrIn(Network::SockAddrIn input);
|
||||||
|
Network::SockAddrIn TranslateToSockAddrIn(sockaddr_in input);
|
||||||
|
s32 TranslateMsgOptToNative(s32 flags);
|
||||||
|
|
||||||
void CancelPendingSocketOperations();
|
void CancelPendingSocketOperations();
|
||||||
void RestartSocketOperations();
|
void RestartSocketOperations();
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ namespace Network {
|
|||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
|
||||||
|
|
||||||
ULONG buf_size = 0;
|
ULONG buf_size = 0;
|
||||||
if (GetAdaptersAddresses(
|
if (GetAdaptersAddresses(
|
||||||
@@ -66,7 +66,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Network::NetworkInterface> result;
|
std::vector<NetworkInterface> result;
|
||||||
|
|
||||||
for (auto* a = addrs; a; a = a->Next) {
|
for (auto* a = addrs; a; a = a->Next) {
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
|||||||
gw = reinterpret_cast<sockaddr_in*>(a->FirstGatewayAddress->Address.lpSockaddr)
|
gw = reinterpret_cast<sockaddr_in*>(a->FirstGatewayAddress->Address.lpSockaddr)
|
||||||
->sin_addr;
|
->sin_addr;
|
||||||
|
|
||||||
result.emplace_back(Network::NetworkInterface{
|
result.emplace_back(NetworkInterface{
|
||||||
.name = Common::UTF16ToUTF8(std::wstring{a->FriendlyName}),
|
.name = Common::UTF16ToUTF8(std::wstring{a->FriendlyName}),
|
||||||
.ip_address = ip,
|
.ip_address = ip,
|
||||||
.subnet_mask = mask,
|
.subnet_mask = mask,
|
||||||
@@ -103,7 +103,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
|
||||||
#if defined(__ANDROID__) || defined(__linux__)
|
#if defined(__ANDROID__) || defined(__linux__)
|
||||||
struct ifaddrs* ifaddr = nullptr;
|
struct ifaddrs* ifaddr = nullptr;
|
||||||
if (getifaddrs(&ifaddr) != 0) {
|
if (getifaddrs(&ifaddr) != 0) {
|
||||||
@@ -135,7 +135,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
|||||||
LOG_WARNING(Network, "\"/proc/net/route\" not found - using gateway 0");
|
LOG_WARNING(Network, "\"/proc/net/route\" not found - using gateway 0");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
std::vector<Network::NetworkInterface> ifaces;
|
std::vector<NetworkInterface> ifaces;
|
||||||
for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
|
for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
|
||||||
if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr /* Have a netmask and address */
|
if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr /* Have a netmask and address */
|
||||||
|| ifa->ifa_addr->sa_family != AF_INET /* Must be of kind AF_INET */
|
|| ifa->ifa_addr->sa_family != AF_INET /* Must be of kind AF_INET */
|
||||||
@@ -149,7 +149,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
|||||||
});
|
});
|
||||||
in_addr gw; // Solaris defines s_addr as a macro, can't use special C++ shenanigans here
|
in_addr gw; // Solaris defines s_addr as a macro, can't use special C++ shenanigans here
|
||||||
gw.s_addr = it != routes.end() ? it->gateway : 0;
|
gw.s_addr = it != routes.end() ? it->gateway : 0;
|
||||||
ifaces.emplace_back(Network::NetworkInterface{
|
ifaces.emplace_back(NetworkInterface{
|
||||||
.name = ifa->ifa_name,
|
.name = ifa->ifa_name,
|
||||||
.ip_address = std::bit_cast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr,
|
.ip_address = std::bit_cast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr,
|
||||||
.subnet_mask = std::bit_cast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr,
|
.subnet_mask = std::bit_cast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr,
|
||||||
@@ -159,7 +159,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
|||||||
freeifaddrs(ifaddr);
|
freeifaddrs(ifaddr);
|
||||||
return ifaces;
|
return ifaces;
|
||||||
#elif defined(__FreeBSD__)
|
#elif defined(__FreeBSD__)
|
||||||
std::vector<Network::NetworkInterface> ifaces;
|
std::vector<NetworkInterface> ifaces;
|
||||||
int fd = ::socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
|
int fd = ::socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
LOG_ERROR(Network, "socket: {}", std::strerror(errno));
|
LOG_ERROR(Network, "socket: {}", std::strerror(errno));
|
||||||
@@ -191,7 +191,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
|||||||
size_t msglen = rtm->rtm_msglen - sizeof(*ifm);
|
size_t msglen = rtm->rtm_msglen - sizeof(*ifm);
|
||||||
char const* p = (char const*)(ifm + 1);
|
char const* p = (char const*)(ifm + 1);
|
||||||
|
|
||||||
Network::NetworkInterface iface{};
|
NetworkInterface iface{};
|
||||||
for (size_t i = 0; i < RTAX_MAX; i++)
|
for (size_t i = 0; i < RTAX_MAX; i++)
|
||||||
if ((ifm->ifm_addrs & (1 << i)) != 0) {
|
if ((ifm->ifm_addrs & (1 << i)) != 0) {
|
||||||
struct sockaddr const* sa = reinterpret_cast<struct sockaddr const*>(p);
|
struct sockaddr const* sa = reinterpret_cast<struct sockaddr const*>(p);
|
||||||
@@ -220,7 +220,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
|||||||
|
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
|
||||||
std::optional<Network::NetworkInterface> GetSelectedNetworkInterface() {
|
std::optional<NetworkInterface> GetSelectedNetworkInterface() {
|
||||||
auto const& sel_if = Settings::values.network_interface.GetValue();
|
auto const& sel_if = Settings::values.network_interface.GetValue();
|
||||||
if (auto const ifaces = Network::GetAvailableNetworkInterfaces(); ifaces.size() > 0) {
|
if (auto const ifaces = Network::GetAvailableNetworkInterfaces(); ifaces.size() > 0) {
|
||||||
if (sel_if.empty())
|
if (sel_if.empty())
|
||||||
|
|||||||
@@ -0,0 +1,277 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cerrno>
|
||||||
|
#include <chrono>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
#ifdef __unix__
|
||||||
|
#include <spawn.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/logging.h"
|
||||||
|
#include "core/internal_network/socket_icmp.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
extern char **environ;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Network {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
u16 ComputeChecksum(std::span<const u8> data) {
|
||||||
|
u32 sum = 0;
|
||||||
|
for (size_t i = 0; i < data.size(); i += 2) {
|
||||||
|
u32 value = (u32(data[i + 0]) << 8ull) | u32(data[i + 1]); //big endian
|
||||||
|
sum += value;
|
||||||
|
}
|
||||||
|
if (data.size() % 2 != 0){
|
||||||
|
sum += u16(data[data.size() - 1]) << 8;
|
||||||
|
}
|
||||||
|
while ((sum >> 16) != 0)
|
||||||
|
sum = (sum & 0xffff) + (sum >> 16);
|
||||||
|
return (~sum) & 0xffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
IcmpSocket::~IcmpSocket() {
|
||||||
|
if (fd == INVALID_SOCKET) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fd = INVALID_SOCKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
Errno IcmpSocket::SetSockOpt(Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval) {
|
||||||
|
LOG_WARNING(Network, "(stubbed) level={},optname={},optval={}", level, optname, optval.size());
|
||||||
|
if (optname == Network::OptName::RCVTIMEO) {
|
||||||
|
if (optval.size() < sizeof(Network::Timeval))
|
||||||
|
return Errno::E_INVAL;
|
||||||
|
std::memcpy(&rcv_timeo, optval.data(), sizeof(rcv_timeo));
|
||||||
|
}
|
||||||
|
return Errno::E_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
Errno IcmpSocket::Initialize(Domain domain, Type type, Protocol socket_protocol) {
|
||||||
|
return Errno::E_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<IcmpSocket::AcceptResult, Errno> IcmpSocket::Accept() {
|
||||||
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
|
return {AcceptResult{}, Errno::E_SUCCESS};
|
||||||
|
}
|
||||||
|
|
||||||
|
Errno IcmpSocket::Connect(Network::SockAddrIn addr_in) {
|
||||||
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
|
connected_addr = addr_in;
|
||||||
|
return Errno::E_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<Network::SockAddrIn, Errno> IcmpSocket::GetPeerName() {
|
||||||
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
|
return {Network::SockAddrIn{}, Errno::E_SUCCESS};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<Network::SockAddrIn, Errno> IcmpSocket::GetSockName() {
|
||||||
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
|
return {Network::SockAddrIn{}, Errno::E_SUCCESS};
|
||||||
|
}
|
||||||
|
|
||||||
|
Errno IcmpSocket::Bind(Network::SockAddrIn addr) {
|
||||||
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
|
return Errno::E_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
Errno IcmpSocket::Listen(s32 backlog) {
|
||||||
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
|
return Errno::E_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
Errno IcmpSocket::Shutdown(ShutdownHow how) {
|
||||||
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
|
return Errno::E_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<s32, Errno> IcmpSocket::Recv(int flags, std::span<u8> message) {
|
||||||
|
LOG_DEBUG(Network, "(stubbed) called");
|
||||||
|
return connected_addr.has_value()
|
||||||
|
? RecvFrom(flags, message, nullptr)
|
||||||
|
: std::make_pair(s32(0), Errno::E_NOTCONN);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<s32, Errno> IcmpSocket::RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) {
|
||||||
|
LOG_DEBUG(Network, "(stubbed) called");
|
||||||
|
ASSERT(flags == 0);
|
||||||
|
ASSERT(message.size() < std::size_t((std::numeric_limits<int>::max)()));
|
||||||
|
|
||||||
|
#if !defined(__OPENORBIS__) && (defined(__FreeBSD__) || defined(__linux__))
|
||||||
|
const auto rcv_timeout_ms = (s64(rcv_timeo.tv_sec) * 1000) + (s64(rcv_timeo.tv_usec) / 1000);
|
||||||
|
const auto timestamp = std::chrono::steady_clock::now();
|
||||||
|
while (true) {
|
||||||
|
{
|
||||||
|
std::lock_guard guard(pings_mutex);
|
||||||
|
// find ping process that is finished running
|
||||||
|
for (auto it = pings.begin(); it != pings.end();) {
|
||||||
|
pid_t result = waitpid(it->ping_pid, &it->ping_status, WNOHANG);
|
||||||
|
// ping process is still running, go to next
|
||||||
|
if (result != it->ping_pid) {
|
||||||
|
++it;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// ping process is finished, remove and handle it
|
||||||
|
it = pings.erase(it);
|
||||||
|
if (it->ping_status == 0) {
|
||||||
|
if (addr) {
|
||||||
|
addr->family = it->family;
|
||||||
|
addr->ip = it->ip;
|
||||||
|
addr->portno = it->portno;
|
||||||
|
addr->len = 16;
|
||||||
|
addr->zeroes = {};
|
||||||
|
}
|
||||||
|
std::array<u8, 8> data{
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, //checksum
|
||||||
|
0,
|
||||||
|
it->seq_ident[0],
|
||||||
|
it->seq_ident[1],
|
||||||
|
it->seq_ident[2],
|
||||||
|
it->seq_ident[3]
|
||||||
|
};
|
||||||
|
auto const csum = ComputeChecksum(std::span<const u8>{data.begin(), data.end()});
|
||||||
|
data[2] = u8(csum >> 8); //hi
|
||||||
|
data[3] = u8(csum); //lo
|
||||||
|
auto const n = std::min(data.size(), message.size());
|
||||||
|
std::copy(data.begin(), data.begin() + n, message.begin());
|
||||||
|
return {s32(n), Errno::E_SUCCESS};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!blocking)
|
||||||
|
return {-1, Errno::E_AGAIN};
|
||||||
|
|
||||||
|
const auto time_diff = std::chrono::steady_clock::now() - timestamp;
|
||||||
|
const auto time_diff_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_diff).count();
|
||||||
|
if (time_diff_ms > rcv_timeout_ms)
|
||||||
|
return {-1, Errno::E_TIMEDOUT};
|
||||||
|
|
||||||
|
std::this_thread::yield();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return {-1, Errno::E_INVAL};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<s32, Errno> IcmpSocket::Send(std::span<const u8> message, int flags) {
|
||||||
|
LOG_DEBUG(Network, "(stubbed) called");
|
||||||
|
if (connected_addr.has_value())
|
||||||
|
return SendTo(flags, message, std::addressof(connected_addr.value()));
|
||||||
|
return {s32(0), Errno::E_NOTCONN};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<s32, Errno> IcmpSocket::SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) {
|
||||||
|
LOG_DEBUG(Network, "(stubbed) called");
|
||||||
|
ASSERT(message.size() < size_t((std::numeric_limits<int>::max)()));
|
||||||
|
// 0 -> 8 (IPv4), 128 (IPv6)
|
||||||
|
// 1 -> 0
|
||||||
|
// 2..4 -> checksum
|
||||||
|
// 4..6 -> ident
|
||||||
|
// 6..8 -> seq
|
||||||
|
|
||||||
|
// PLEASE DON'T KILL ME, I SWEAR THIS IS LEGITIMATELY THE BEST WAY TO DO IT
|
||||||
|
// IF YOU OPEN socket() GOOGLE WILL STRAIGHT UP IP BAN YOU AFTER 2 HOURS
|
||||||
|
#if !(defined(__OPENORBIS__) || defined(__ANDROID__)) && (defined(__FreeBSD__) || defined(__linux__))
|
||||||
|
const auto rcv_timeout_ms = (s64(rcv_timeo.tv_sec) * 1000) + (s64(rcv_timeo.tv_usec) / 1000);
|
||||||
|
if (!addr)
|
||||||
|
return {-1, Errno::E_DESTADDRREQ};
|
||||||
|
|
||||||
|
if (message.size() >= 8) {
|
||||||
|
std::string ip_str = fmt::format(
|
||||||
|
"{}.{}.{}.{}",
|
||||||
|
addr->ip[0],
|
||||||
|
addr->ip[1],
|
||||||
|
addr->ip[2],
|
||||||
|
addr->ip[3]
|
||||||
|
);
|
||||||
|
#ifdef __FreeBSD__
|
||||||
|
// ping -W option is a nonfractional int (milliseconds)
|
||||||
|
std::string timeout_str = fmt::format("{}", rcv_timeout_ms);
|
||||||
|
std::vector<char*> argv = {
|
||||||
|
const_cast<char*>("ping"),
|
||||||
|
const_cast<char*>("-c"),
|
||||||
|
const_cast<char*>("1"),
|
||||||
|
const_cast<char*>("-W"),
|
||||||
|
timeout_str.data(),
|
||||||
|
ip_str.data(),
|
||||||
|
nullptr
|
||||||
|
};
|
||||||
|
#elif defined(__linux__)
|
||||||
|
// ping -W option is a fractional float (seconds)
|
||||||
|
auto const rcv_timeout_s = f64(rcv_timeout_ms) / 1000.0;
|
||||||
|
std::string timeout_str = fmt::format("{}", rcv_timeout_s);
|
||||||
|
std::vector<char*> argv = {
|
||||||
|
const_cast<char*>("ping"),
|
||||||
|
const_cast<char*>("-c"),
|
||||||
|
const_cast<char*>("1"),
|
||||||
|
const_cast<char*>("-W"),
|
||||||
|
timeout_str.data(),
|
||||||
|
ip_str.data(),
|
||||||
|
nullptr
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
pid_t ping_pid;
|
||||||
|
// we should pass in attributes to stop stdout spam, but im too lazy to figure that out
|
||||||
|
if (posix_spawnp(&ping_pid, "ping", nullptr, nullptr, argv.data(), environ) != 0) {
|
||||||
|
LOG_ERROR(Network, "Unable to start ping process for emulated ICMP socket");
|
||||||
|
return {-1, Errno::E_INVAL};
|
||||||
|
}
|
||||||
|
std::lock_guard guard(pings_mutex);
|
||||||
|
if (pings.size() >= pings.max_size())
|
||||||
|
pings.erase(pings.begin());
|
||||||
|
pings.push_back(PingProcessData{
|
||||||
|
.ip = addr->ip,
|
||||||
|
.portno = addr->portno,
|
||||||
|
.ping_pid = ping_pid,
|
||||||
|
.ping_status = 0,
|
||||||
|
.seq_ident = {
|
||||||
|
message[4],
|
||||||
|
message[5],
|
||||||
|
message[6],
|
||||||
|
message[7]
|
||||||
|
},
|
||||||
|
.family = addr->family,
|
||||||
|
});
|
||||||
|
return {s32(message.size()), Errno::E_SUCCESS};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return {-1, Errno::E_INVAL};
|
||||||
|
}
|
||||||
|
|
||||||
|
Errno IcmpSocket::Close() {
|
||||||
|
LOG_DEBUG(Network, "called");
|
||||||
|
fd = INVALID_SOCKET;
|
||||||
|
return Errno::E_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<Errno, Errno> IcmpSocket::GetPendingError() {
|
||||||
|
LOG_DEBUG(Network, "called");
|
||||||
|
return {Errno::E_SUCCESS, Errno::E_SUCCESS};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IcmpSocket::IsOpened() const {
|
||||||
|
return fd != INVALID_SOCKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IcmpSocket::HandleProxyPacket(const ProxyPacket& packet) {
|
||||||
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
|
}
|
||||||
|
Errno IcmpSocket::SetNonBlock(bool enable) {
|
||||||
|
blocking = !enable;
|
||||||
|
return Errno::E_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Network
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <span>
|
||||||
|
#include <utility>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <mutex>
|
||||||
|
#include <boost/container/static_vector.hpp>
|
||||||
|
#include "core/internal_network/socket_types.h"
|
||||||
|
#include "core/internal_network/sockets.h"
|
||||||
|
|
||||||
|
namespace Network {
|
||||||
|
|
||||||
|
struct PingProcessData {
|
||||||
|
IPv4Address ip;
|
||||||
|
u16 portno;
|
||||||
|
pid_t ping_pid;
|
||||||
|
pid_t ping_status;
|
||||||
|
std::array<u8, 4> seq_ident;
|
||||||
|
u8 family;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IcmpSocket : public Network::SocketBase {
|
||||||
|
public:
|
||||||
|
explicit IcmpSocket() noexcept = default;
|
||||||
|
~IcmpSocket() override;
|
||||||
|
Errno Initialize(Domain domain, Type type, Protocol socket_protocol) override;
|
||||||
|
Errno Close() override;
|
||||||
|
std::pair<AcceptResult, Errno> Accept() override;
|
||||||
|
Errno Connect(Network::SockAddrIn addr_in) override;
|
||||||
|
std::pair<Network::SockAddrIn, Errno> GetPeerName() override;
|
||||||
|
std::pair<Network::SockAddrIn, Errno> GetSockName() override;
|
||||||
|
Errno Bind(Network::SockAddrIn addr) override;
|
||||||
|
Errno Listen(s32 backlog) override;
|
||||||
|
Errno Shutdown(ShutdownHow how) override;
|
||||||
|
std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override;
|
||||||
|
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) override;
|
||||||
|
std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
|
||||||
|
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) override;
|
||||||
|
Errno SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) override;
|
||||||
|
std::pair<Errno, Errno> GetPendingError() override;
|
||||||
|
bool IsOpened() const override;
|
||||||
|
void HandleProxyPacket(const ProxyPacket& packet) override;
|
||||||
|
Errno SetNonBlock(bool enable) override;
|
||||||
|
|
||||||
|
boost::container::static_vector<PingProcessData, 128> pings;
|
||||||
|
std::optional<SockAddrIn> connected_addr;
|
||||||
|
std::mutex pings_mutex;
|
||||||
|
Network::Timeval rcv_timeo;
|
||||||
|
bool blocking = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Network
|
||||||
@@ -47,71 +47,82 @@ void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) {
|
|||||||
received_packets.push(decompressed);
|
received_packets.push(decompressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
Errno ProxySocket::SetNonBlock(bool enable) {
|
||||||
Errno ProxySocket::SetSockOpt(SOCKET fd_, int option, T value) {
|
blocking = !enable;
|
||||||
LOG_DEBUG(Network, "(STUBBED) called");
|
return Errno::E_SUCCESS;
|
||||||
return Errno::SUCCESS;
|
}
|
||||||
|
|
||||||
|
Errno ProxySocket::SetSockOpt(Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval) {
|
||||||
|
LOG_DEBUG(Network, "level={},optname={},optval={}", level, optname, optval.size());
|
||||||
|
// numeric values?
|
||||||
|
if (optval.size() >= sizeof(u32)) {
|
||||||
|
u32 value;
|
||||||
|
std::memcpy(&value, optval.data(), sizeof(value));
|
||||||
|
if (optname == Network::OptName::BROADCAST)
|
||||||
|
broadcast = bool(value);
|
||||||
|
if (optname == Network::OptName::SNDTIMEO)
|
||||||
|
send_timeout = value;
|
||||||
|
if (optname == Network::OptName::RCVTIMEO)
|
||||||
|
receive_timeout = value;
|
||||||
|
}
|
||||||
|
return Errno::E_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno ProxySocket::Initialize(Domain domain, Type type, Protocol socket_protocol) {
|
Errno ProxySocket::Initialize(Domain domain, Type type, Protocol socket_protocol) {
|
||||||
protocol = socket_protocol;
|
protocol = socket_protocol;
|
||||||
SetSockOpt(fd, SO_TYPE, type);
|
return Errno::E_SUCCESS;
|
||||||
|
|
||||||
return Errno::SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<ProxySocket::AcceptResult, Errno> ProxySocket::Accept() {
|
std::pair<ProxySocket::AcceptResult, Errno> ProxySocket::Accept() {
|
||||||
LOG_WARNING(Network, "(STUBBED) called");
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
return {AcceptResult{}, Errno::SUCCESS};
|
return {AcceptResult{}, Errno::E_SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno ProxySocket::Connect(SockAddrIn addr_in) {
|
Errno ProxySocket::Connect(Network::SockAddrIn addr_in) {
|
||||||
LOG_WARNING(Network, "(STUBBED) called");
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
return Errno::SUCCESS;
|
return Errno::E_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<SockAddrIn, Errno> ProxySocket::GetPeerName() {
|
std::pair<Network::SockAddrIn, Errno> ProxySocket::GetPeerName() {
|
||||||
LOG_WARNING(Network, "(STUBBED) called");
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
return {SockAddrIn{}, Errno::SUCCESS};
|
return {Network::SockAddrIn{}, Errno::E_SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<SockAddrIn, Errno> ProxySocket::GetSockName() {
|
std::pair<Network::SockAddrIn, Errno> ProxySocket::GetSockName() {
|
||||||
LOG_WARNING(Network, "(STUBBED) called");
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
return {SockAddrIn{}, Errno::SUCCESS};
|
return {Network::SockAddrIn{}, Errno::E_SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno ProxySocket::Bind(SockAddrIn addr) {
|
Errno ProxySocket::Bind(Network::SockAddrIn addr) {
|
||||||
if (is_bound) {
|
if (is_bound) {
|
||||||
LOG_WARNING(Network, "Rebinding Socket is unimplemented!");
|
LOG_WARNING(Network, "Rebinding Socket is unimplemented!");
|
||||||
return Errno::SUCCESS;
|
return Errno::E_SUCCESS;
|
||||||
}
|
}
|
||||||
local_endpoint = addr;
|
local_endpoint = addr;
|
||||||
is_bound = true;
|
is_bound = true;
|
||||||
|
return Errno::E_SUCCESS;
|
||||||
return Errno::SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno ProxySocket::Listen(s32 backlog) {
|
Errno ProxySocket::Listen(s32 backlog) {
|
||||||
LOG_WARNING(Network, "(STUBBED) called");
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
return Errno::SUCCESS;
|
return Errno::E_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno ProxySocket::Shutdown(ShutdownHow how) {
|
Errno ProxySocket::Shutdown(ShutdownHow how) {
|
||||||
LOG_WARNING(Network, "(STUBBED) called");
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
return Errno::SUCCESS;
|
return Errno::E_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> ProxySocket::Recv(int flags, std::span<u8> message) {
|
std::pair<s32, Errno> ProxySocket::Recv(int flags, std::span<u8> message) {
|
||||||
LOG_WARNING(Network, "(STUBBED) called");
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
ASSERT(flags == 0);
|
ASSERT(flags == 0);
|
||||||
ASSERT(message.size() < static_cast<size_t>((std::numeric_limits<int>::max)()));
|
ASSERT(message.size() < std::size_t((std::numeric_limits<int>::max)()));
|
||||||
|
return {s32(0), Errno::E_SUCCESS};
|
||||||
return {static_cast<s32>(0), Errno::SUCCESS};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) {
|
std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) {
|
||||||
ASSERT(flags == 0);
|
ASSERT(flags == 0);
|
||||||
ASSERT(message.size() < static_cast<size_t>((std::numeric_limits<int>::max)()));
|
ASSERT(message.size() < std::size_t((std::numeric_limits<int>::max)()));
|
||||||
|
|
||||||
// TODO (flTobi): Verify the timeout behavior and break when connection is lost
|
// TODO (flTobi): Verify the timeout behavior and break when connection is lost
|
||||||
const auto timestamp = std::chrono::steady_clock::now();
|
const auto timestamp = std::chrono::steady_clock::now();
|
||||||
@@ -128,88 +139,85 @@ std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, So
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!blocking) {
|
if (!blocking) {
|
||||||
return {-1, Errno::AGAIN};
|
return {-1, Errno::E_AGAIN};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
|
|
||||||
const auto time_diff = std::chrono::steady_clock::now() - timestamp;
|
const auto time_diff = std::chrono::steady_clock::now() - timestamp;
|
||||||
const auto time_diff_ms =
|
const auto time_diff_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_diff).count();
|
||||||
std::chrono::duration_cast<std::chrono::milliseconds>(time_diff).count();
|
|
||||||
|
|
||||||
if (time_diff_ms > timeout) {
|
if (time_diff_ms > timeout) {
|
||||||
return {-1, Errno::TIMEDOUT};
|
return {-1, Errno::E_TIMEDOUT};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::span<u8> message, SockAddrIn* addr,
|
std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::span<u8> message, Network::SockAddrIn* addr, std::size_t max_length) {
|
||||||
std::size_t max_length) {
|
LOG_DEBUG(Network, "called");
|
||||||
ProxyPacket& packet = received_packets.front();
|
ProxyPacket& packet = received_packets.front();
|
||||||
if (addr) {
|
if (addr) {
|
||||||
addr->family = Domain::INET;
|
addr->len = 16;
|
||||||
|
addr->family = u8(Network::Domain::INET);
|
||||||
addr->ip = packet.local_endpoint.ip; // The senders ip address
|
addr->ip = packet.local_endpoint.ip; // The senders ip address
|
||||||
addr->portno = packet.local_endpoint.portno; // The senders port number
|
addr->portno = packet.local_endpoint.portno; // The senders port number
|
||||||
|
addr->zeroes = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool peek = (flags & FLAG_MSG_PEEK) != 0;
|
bool peek = (flags & u32(Network::MsgOpt::PEEK)) != 0;
|
||||||
std::size_t read_bytes;
|
std::size_t read_bytes;
|
||||||
if (packet.data.size() > max_length) {
|
if (packet.data.size() > max_length) {
|
||||||
read_bytes = max_length;
|
read_bytes = max_length;
|
||||||
memcpy(message.data(), packet.data.data(), max_length);
|
std::memcpy(message.data(), packet.data.data(), max_length);
|
||||||
|
|
||||||
if (protocol == Protocol::UDP) {
|
if (protocol == Protocol::UDP) {
|
||||||
if (!peek) {
|
if (!peek) {
|
||||||
received_packets.pop();
|
received_packets.pop();
|
||||||
}
|
}
|
||||||
return {-1, Errno::MSGSIZE};
|
return {-1, Errno::E_MSGSIZE};
|
||||||
} else if (protocol == Protocol::TCP) {
|
} else if (protocol == Protocol::TCP) {
|
||||||
std::vector<u8> numArray(packet.data.size() - max_length);
|
std::vector<u8> numArray(packet.data.size() - max_length);
|
||||||
std::copy(packet.data.begin() + max_length, packet.data.end(),
|
std::copy(packet.data.begin() + max_length, packet.data.end(), std::back_inserter(numArray));
|
||||||
std::back_inserter(numArray));
|
|
||||||
packet.data = numArray;
|
packet.data = numArray;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
read_bytes = packet.data.size();
|
read_bytes = packet.data.size();
|
||||||
memcpy(message.data(), packet.data.data(), read_bytes);
|
std::memcpy(message.data(), packet.data.data(), read_bytes);
|
||||||
if (!peek) {
|
if (!peek) {
|
||||||
received_packets.pop();
|
received_packets.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {static_cast<u32>(read_bytes), Errno::SUCCESS};
|
return {u32(read_bytes), Errno::E_SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> ProxySocket::Send(std::span<const u8> message, int flags) {
|
std::pair<s32, Errno> ProxySocket::Send(std::span<const u8> message, int flags) {
|
||||||
LOG_WARNING(Network, "(STUBBED) called");
|
LOG_WARNING(Network, "(stubbed) called");
|
||||||
ASSERT(message.size() < static_cast<size_t>((std::numeric_limits<int>::max)()));
|
ASSERT(message.size() < size_t((std::numeric_limits<int>::max)()));
|
||||||
ASSERT(flags == 0);
|
ASSERT(flags == 0);
|
||||||
|
return {s32(0), Errno::E_SUCCESS};
|
||||||
return {static_cast<s32>(0), Errno::SUCCESS};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxySocket::SendPacket(ProxyPacket& packet) {
|
void ProxySocket::SendPacket(ProxyPacket& packet) {
|
||||||
if (auto room_member = Network::GetRoomMember().lock()) {
|
if (auto room_member = Network::GetRoomMember().lock()) {
|
||||||
if (room_member->IsConnected()) {
|
if (room_member->IsConnected()) {
|
||||||
packet.data = Common::Compression::CompressDataZSTDDefault(packet.data.data(),
|
packet.data = Common::Compression::CompressDataZSTDDefault(packet.data.data(), packet.data.size());
|
||||||
packet.data.size());
|
|
||||||
room_member->SendProxyPacket(packet);
|
room_member->SendProxyPacket(packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message,
|
std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) {
|
||||||
const SockAddrIn* addr) {
|
LOG_DEBUG(Network, "called");
|
||||||
ASSERT(flags == 0);
|
ASSERT(flags == 0);
|
||||||
|
|
||||||
if (!is_bound) {
|
if (!is_bound) {
|
||||||
LOG_ERROR(Network, "ProxySocket is not bound!");
|
LOG_ERROR(Network, "ProxySocket is not bound!");
|
||||||
return {static_cast<s32>(message.size()), Errno::SUCCESS};
|
return {s32(message.size()), Errno::E_SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto room_member = Network::GetRoomMember().lock()) {
|
if (auto room_member = Network::GetRoomMember().lock()) {
|
||||||
if (!room_member->IsConnected()) {
|
if (!room_member->IsConnected()) {
|
||||||
return {static_cast<s32>(message.size()), Errno::SUCCESS};
|
return {s32(message.size()), Errno::E_SUCCESS};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,66 +242,20 @@ std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message
|
|||||||
|
|
||||||
SendPacket(packet);
|
SendPacket(packet);
|
||||||
|
|
||||||
return {static_cast<s32>(message.size()), Errno::SUCCESS};
|
return {s32(message.size()), Errno::E_SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno ProxySocket::Close() {
|
Errno ProxySocket::Close() {
|
||||||
|
LOG_DEBUG(Network, "called");
|
||||||
fd = INVALID_SOCKET;
|
fd = INVALID_SOCKET;
|
||||||
closed = true;
|
closed = true;
|
||||||
|
|
||||||
return Errno::SUCCESS;
|
return Errno::E_SUCCESS;
|
||||||
}
|
|
||||||
|
|
||||||
Errno ProxySocket::SetLinger(bool enable, u32 linger) {
|
|
||||||
struct Linger {
|
|
||||||
u16 linger_enable;
|
|
||||||
u16 linger_time;
|
|
||||||
} values;
|
|
||||||
values.linger_enable = enable ? 1 : 0;
|
|
||||||
values.linger_time = static_cast<u16>(linger);
|
|
||||||
|
|
||||||
return SetSockOpt(fd, SO_LINGER, values);
|
|
||||||
}
|
|
||||||
|
|
||||||
Errno ProxySocket::SetReuseAddr(bool enable) {
|
|
||||||
return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Errno ProxySocket::SetBroadcast(bool enable) {
|
|
||||||
broadcast = enable;
|
|
||||||
return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Errno ProxySocket::SetSndBuf(u32 value) {
|
|
||||||
return SetSockOpt(fd, SO_SNDBUF, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
Errno ProxySocket::SetKeepAlive(bool enable) {
|
|
||||||
return Errno::SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
Errno ProxySocket::SetRcvBuf(u32 value) {
|
|
||||||
return SetSockOpt(fd, SO_RCVBUF, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
Errno ProxySocket::SetSndTimeo(u32 value) {
|
|
||||||
send_timeout = value;
|
|
||||||
return SetSockOpt(fd, SO_SNDTIMEO, static_cast<int>(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
Errno ProxySocket::SetRcvTimeo(u32 value) {
|
|
||||||
receive_timeout = value;
|
|
||||||
return SetSockOpt(fd, SO_RCVTIMEO, static_cast<int>(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
Errno ProxySocket::SetNonBlock(bool enable) {
|
|
||||||
blocking = !enable;
|
|
||||||
return Errno::SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<Errno, Errno> ProxySocket::GetPendingError() {
|
std::pair<Errno, Errno> ProxySocket::GetPendingError() {
|
||||||
LOG_DEBUG(Network, "(STUBBED) called");
|
LOG_DEBUG(Network, "called");
|
||||||
return {Errno::SUCCESS, Errno::SUCCESS};
|
return {Errno::E_SUCCESS, Errno::E_SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProxySocket::IsOpened() const {
|
bool ProxySocket::IsOpened() const {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
@@ -29,13 +29,13 @@ public:
|
|||||||
|
|
||||||
std::pair<AcceptResult, Errno> Accept() override;
|
std::pair<AcceptResult, Errno> Accept() override;
|
||||||
|
|
||||||
Errno Connect(SockAddrIn addr_in) override;
|
Errno Connect(Network::SockAddrIn addr_in) override;
|
||||||
|
|
||||||
std::pair<SockAddrIn, Errno> GetPeerName() override;
|
std::pair<Network::SockAddrIn, Errno> GetPeerName() override;
|
||||||
|
|
||||||
std::pair<SockAddrIn, Errno> GetSockName() override;
|
std::pair<Network::SockAddrIn, Errno> GetSockName() override;
|
||||||
|
|
||||||
Errno Bind(SockAddrIn addr) override;
|
Errno Bind(Network::SockAddrIn addr) override;
|
||||||
|
|
||||||
Errno Listen(s32 backlog) override;
|
Errno Listen(s32 backlog) override;
|
||||||
|
|
||||||
@@ -43,9 +43,9 @@ public:
|
|||||||
|
|
||||||
std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override;
|
std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override;
|
||||||
|
|
||||||
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) override;
|
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) override;
|
||||||
|
|
||||||
std::pair<s32, Errno> ReceivePacket(int flags, std::span<u8> message, SockAddrIn* addr,
|
std::pair<s32, Errno> ReceivePacket(int flags, std::span<u8> message, Network::SockAddrIn* addr,
|
||||||
std::size_t max_length);
|
std::size_t max_length);
|
||||||
|
|
||||||
std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
|
std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
|
||||||
@@ -53,28 +53,11 @@ public:
|
|||||||
void SendPacket(ProxyPacket& packet);
|
void SendPacket(ProxyPacket& packet);
|
||||||
|
|
||||||
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
|
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
|
||||||
const SockAddrIn* addr) override;
|
const Network::SockAddrIn* addr) override;
|
||||||
|
|
||||||
Errno SetLinger(bool enable, u32 linger) override;
|
|
||||||
|
|
||||||
Errno SetReuseAddr(bool enable) override;
|
|
||||||
|
|
||||||
Errno SetBroadcast(bool enable) override;
|
|
||||||
|
|
||||||
Errno SetKeepAlive(bool enable) override;
|
|
||||||
|
|
||||||
Errno SetSndBuf(u32 value) override;
|
|
||||||
|
|
||||||
Errno SetRcvBuf(u32 value) override;
|
|
||||||
|
|
||||||
Errno SetSndTimeo(u32 value) override;
|
|
||||||
|
|
||||||
Errno SetRcvTimeo(u32 value) override;
|
|
||||||
|
|
||||||
Errno SetNonBlock(bool enable) override;
|
Errno SetNonBlock(bool enable) override;
|
||||||
|
|
||||||
template <typename T>
|
Errno SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) override;
|
||||||
Errno SetSockOpt(SOCKET fd, int option, T value);
|
|
||||||
|
|
||||||
std::pair<Errno, Errno> GetPendingError() override;
|
std::pair<Errno, Errno> GetPendingError() override;
|
||||||
|
|
||||||
@@ -86,7 +69,7 @@ private:
|
|||||||
u32 send_timeout = 0;
|
u32 send_timeout = 0;
|
||||||
u32 receive_timeout = 0;
|
u32 receive_timeout = 0;
|
||||||
bool is_bound = false;
|
bool is_bound = false;
|
||||||
SockAddrIn local_endpoint{};
|
Network::SockAddrIn local_endpoint{};
|
||||||
bool blocking = true;
|
bool blocking = true;
|
||||||
std::queue<ProxyPacket> received_packets;
|
std::queue<ProxyPacket> received_packets;
|
||||||
Protocol protocol;
|
Protocol protocol;
|
||||||
|
|||||||
@@ -0,0 +1,470 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "common/common_funcs.h"
|
||||||
|
|
||||||
|
// Most of these structures are direct mappings of guest's
|
||||||
|
// expectations for these values, in other words, they're the
|
||||||
|
// values that HOS is expected to use AND handle.
|
||||||
|
|
||||||
|
namespace Network {
|
||||||
|
|
||||||
|
enum class Errno : u32 {
|
||||||
|
E_SUCCESS = 0,
|
||||||
|
E_PERM = 1,
|
||||||
|
E_NOENT = 2,
|
||||||
|
E_SRCH = 3,
|
||||||
|
E_INTR = 4,
|
||||||
|
E_IO = 5,
|
||||||
|
E_NXIO = 6,
|
||||||
|
E_2BIG = 7,
|
||||||
|
E_NOEXEC = 8,
|
||||||
|
E_BADF = 9,
|
||||||
|
E_CHILD = 10,
|
||||||
|
E_AGAIN = 11,
|
||||||
|
E_NOMEM = 12,
|
||||||
|
E_ACCES = 13,
|
||||||
|
E_FAULT = 14,
|
||||||
|
E_NOTBLK = 15,
|
||||||
|
E_BUSY = 16,
|
||||||
|
E_EXIST = 17,
|
||||||
|
E_XDEV = 18,
|
||||||
|
E_NODEV = 19,
|
||||||
|
E_NOTDIR = 20,
|
||||||
|
E_ISDIR = 21,
|
||||||
|
E_INVAL = 22,
|
||||||
|
E_NFILE = 23,
|
||||||
|
E_MFILE = 24,
|
||||||
|
E_NOTTY = 25,
|
||||||
|
E_TXTBSY = 26,
|
||||||
|
E_FBIG = 27,
|
||||||
|
E_NOSPC = 28,
|
||||||
|
E_SPIPE = 29,
|
||||||
|
E_ROFS = 30,
|
||||||
|
E_MLINK = 31,
|
||||||
|
E_PIPE = 32,
|
||||||
|
E_DOM = 33,
|
||||||
|
E_RANGE = 34,
|
||||||
|
E_DEADLK = 35,
|
||||||
|
E_NAMETOOLONG = 36,
|
||||||
|
E_NOLCK = 37,
|
||||||
|
E_NOSYS = 38,
|
||||||
|
E_NOTEMPTY = 39,
|
||||||
|
E_LOOP = 40,
|
||||||
|
E_NOMSG = 42,
|
||||||
|
E_IDRM = 43,
|
||||||
|
E_CHRNG = 44,
|
||||||
|
E_L2NSYNC = 45,
|
||||||
|
E_L3HLT = 46,
|
||||||
|
E_L3RST = 47,
|
||||||
|
E_LNRNG = 48,
|
||||||
|
E_UNATCH = 49,
|
||||||
|
E_NOCSI = 50,
|
||||||
|
E_L2HLT = 51,
|
||||||
|
E_BADE = 52,
|
||||||
|
E_BADR = 53,
|
||||||
|
E_XFULL = 54,
|
||||||
|
E_NOANO = 55,
|
||||||
|
E_BADRQC = 56,
|
||||||
|
E_BADSSL = 57,
|
||||||
|
E_BFONT = 59,
|
||||||
|
E_NOSTR = 60,
|
||||||
|
E_NODATA = 61,
|
||||||
|
E_TIME = 62,
|
||||||
|
E_NOSR = 63,
|
||||||
|
E_NONET = 64,
|
||||||
|
E_NOPKG = 65,
|
||||||
|
E_REMOTE = 66,
|
||||||
|
E_NOLINK = 67,
|
||||||
|
E_ADV = 68,
|
||||||
|
E_SRMNT = 69,
|
||||||
|
E_COMM = 70,
|
||||||
|
E_PROTO = 71,
|
||||||
|
E_MULTIHOP = 72,
|
||||||
|
E_DOTDOT = 73,
|
||||||
|
E_BADMSG = 74,
|
||||||
|
E_OVERFLOW = 75,
|
||||||
|
E_NOTUNUQ = 76,
|
||||||
|
E_BADFD = 77,
|
||||||
|
E_REMCHG = 78,
|
||||||
|
E_LIBACC = 79,
|
||||||
|
E_LIBBAD = 80,
|
||||||
|
E_LIBSCN = 81,
|
||||||
|
E_LIBMAX = 82,
|
||||||
|
E_LIBEXEC = 83,
|
||||||
|
E_ILSEQ = 84,
|
||||||
|
E_RESTART = 85,
|
||||||
|
E_STRPIPE = 86,
|
||||||
|
E_USERS = 87,
|
||||||
|
E_NOTSOCK = 88,
|
||||||
|
E_DESTADDRREQ = 89,
|
||||||
|
E_MSGSIZE = 90,
|
||||||
|
E_PROTOTYPE = 91,
|
||||||
|
E_NOPROTOOPT = 92,
|
||||||
|
E_PROTONOSUPPORT = 93,
|
||||||
|
E_SOCKTNOSUPPORT = 94,
|
||||||
|
E_OPNOTSUPP = 95,
|
||||||
|
E_PFNOSUPPORT = 96,
|
||||||
|
E_AFNOSUPPORT = 97,
|
||||||
|
E_ADDRINUSE = 98,
|
||||||
|
E_ADDRNOTAVAIL = 99,
|
||||||
|
E_NETDOWN = 100,
|
||||||
|
E_NETUNREACH = 101,
|
||||||
|
E_NETRESET = 102,
|
||||||
|
E_CONNABORTED = 103,
|
||||||
|
E_CONNRESET = 104,
|
||||||
|
E_NOBUFS = 105,
|
||||||
|
E_ISCONN = 106,
|
||||||
|
E_NOTCONN = 107,
|
||||||
|
E_SHUTDOWN = 108,
|
||||||
|
E_TOOMANYREFS = 109,
|
||||||
|
E_TIMEDOUT = 110,
|
||||||
|
E_CONNREFUSED = 111,
|
||||||
|
E_HOSTDOWN = 112,
|
||||||
|
E_HOSTUNREACH = 113,
|
||||||
|
E_ALREADY = 114,
|
||||||
|
E_INPROGRESS = 115,
|
||||||
|
E_STALE = 116,
|
||||||
|
E_UCLEAN = 117,
|
||||||
|
E_NOTNAM = 118,
|
||||||
|
E_NAVAIL = 119,
|
||||||
|
E_ISNAM = 120,
|
||||||
|
E_REMOTEIO = 121,
|
||||||
|
E_DQUOT = 122,
|
||||||
|
E_NOMEDIUM = 123,
|
||||||
|
E_MEDIUMTYPE = 124,
|
||||||
|
E_CANCELED = 125,
|
||||||
|
E_NOKEY = 126,
|
||||||
|
E_KEYEXPIRED = 127,
|
||||||
|
E_KEYREVOKED = 128,
|
||||||
|
E_KEYREJECTED = 129,
|
||||||
|
E_OWNERDEAD = 130,
|
||||||
|
E_NOTRECOVERABLE = 131,
|
||||||
|
E_RFKILL = 132,
|
||||||
|
E_HWPOISON = 133,
|
||||||
|
E_PROCLIM = 156,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class GetAddrInfoError : s32 {
|
||||||
|
SUCCESS = 0,
|
||||||
|
ADDRFAMILY = 1,
|
||||||
|
AGAIN = 2,
|
||||||
|
BADFLAGS = 3,
|
||||||
|
FAIL = 4,
|
||||||
|
FAMILY = 5,
|
||||||
|
MEMORY = 6,
|
||||||
|
NODATA = 7,
|
||||||
|
NONAME = 8,
|
||||||
|
SERVICE = 9,
|
||||||
|
SOCKTYPE = 10,
|
||||||
|
SYSTEM = 11,
|
||||||
|
BADHINTS = 12,
|
||||||
|
PROTOCOL = 13,
|
||||||
|
OVERFLOW_ = 14, // avoid name collision with Windows macro
|
||||||
|
OTHER = 15,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Domain : u32 {
|
||||||
|
Unspecified = 0,
|
||||||
|
UNIX = 1,
|
||||||
|
INET = 2,
|
||||||
|
IMPLINK = 3,
|
||||||
|
PUP = 4,
|
||||||
|
CHAOS = 5,
|
||||||
|
NETBIOS = 6,
|
||||||
|
ISO = 7,
|
||||||
|
ECMA = 8,
|
||||||
|
DATAKIT = 9,
|
||||||
|
CCITT = 10,
|
||||||
|
SNA = 11,
|
||||||
|
DECnet = 12,
|
||||||
|
DLI = 13,
|
||||||
|
LAT = 14,
|
||||||
|
HYLINK = 15,
|
||||||
|
APPLETALK = 16,
|
||||||
|
ROUTE = 17,
|
||||||
|
LINK = 18,
|
||||||
|
COIP = 20,
|
||||||
|
CNT = 21,
|
||||||
|
IPX = 23,
|
||||||
|
SIP = 24,
|
||||||
|
ISDN = 26,
|
||||||
|
INET6 = 28,
|
||||||
|
NATM = 29,
|
||||||
|
ATM = 30,
|
||||||
|
NETGRAPH = 32,
|
||||||
|
SLOW = 33,
|
||||||
|
SCLUSTER = 34,
|
||||||
|
ARP = 35,
|
||||||
|
BLUETOOTH = 36,
|
||||||
|
IEEE80211 = 37,
|
||||||
|
NETLINK = 38,
|
||||||
|
INET_SDP = 40,
|
||||||
|
INET6_SDP = 42,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Type : u32 {
|
||||||
|
Unspecified = 0,
|
||||||
|
STREAM = 1,
|
||||||
|
DGRAM = 2,
|
||||||
|
RAW = 3,
|
||||||
|
RDM = 4,
|
||||||
|
SEQPACKET = 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Protocol : u32 {
|
||||||
|
IP = 0,
|
||||||
|
ICMP = 1,
|
||||||
|
TCP = 6,
|
||||||
|
UDP = 17,
|
||||||
|
//
|
||||||
|
IPV6 = 41,
|
||||||
|
RAW = 255,
|
||||||
|
//
|
||||||
|
HOPOPTS = 0,
|
||||||
|
IGMP = 2,
|
||||||
|
GGP = 3,
|
||||||
|
IPV4 = 4,
|
||||||
|
ST = 7,
|
||||||
|
EGP = 8,
|
||||||
|
PIGP = 9,
|
||||||
|
RCCMON = 10,
|
||||||
|
NVPII = 11,
|
||||||
|
PUP = 12,
|
||||||
|
ARGUS = 13,
|
||||||
|
EMCON = 14,
|
||||||
|
XNET = 15,
|
||||||
|
CHAOS = 16,
|
||||||
|
MUX = 18,
|
||||||
|
MEAS = 19,
|
||||||
|
HMP = 20,
|
||||||
|
PRM = 21,
|
||||||
|
IDP = 22,
|
||||||
|
TRUNK1 = 23,
|
||||||
|
TRUNK2 = 24,
|
||||||
|
LEAF1 = 25,
|
||||||
|
LEAF2 = 26,
|
||||||
|
RDP = 27,
|
||||||
|
IRTP = 28,
|
||||||
|
TP = 29,
|
||||||
|
BLT = 30,
|
||||||
|
NSP = 31,
|
||||||
|
INP = 32,
|
||||||
|
DCCP = 33,
|
||||||
|
//3PC = 34,
|
||||||
|
IDPR = 35,
|
||||||
|
XTP = 36,
|
||||||
|
DDP = 37,
|
||||||
|
CMTP = 38,
|
||||||
|
TPXX = 39,
|
||||||
|
IL = 40,
|
||||||
|
SDRP = 42,
|
||||||
|
ROUTING = 43,
|
||||||
|
FRAGMENT = 44,
|
||||||
|
IDRP = 45,
|
||||||
|
RSVP = 46,
|
||||||
|
GRE = 47,
|
||||||
|
MHRP = 48,
|
||||||
|
BHA = 49,
|
||||||
|
ESP = 50,
|
||||||
|
AH = 51,
|
||||||
|
INLSP = 52,
|
||||||
|
SWIPE = 53,
|
||||||
|
NHRP = 54,
|
||||||
|
MOBILE = 55,
|
||||||
|
TLSP = 56,
|
||||||
|
SKIP = 57,
|
||||||
|
ICMPV6 = 58,
|
||||||
|
NONE = 59,
|
||||||
|
DSTOPTS = 60,
|
||||||
|
AHIP = 61,
|
||||||
|
CFTP = 62,
|
||||||
|
HELLO = 63,
|
||||||
|
SATEXPAK = 64,
|
||||||
|
KRYPTOLAN = 65,
|
||||||
|
RVD = 66,
|
||||||
|
IPPC = 67,
|
||||||
|
ADFS = 68,
|
||||||
|
SATMON = 69,
|
||||||
|
VISA = 70,
|
||||||
|
IPCV = 71,
|
||||||
|
CPNX = 72,
|
||||||
|
CPHB = 73,
|
||||||
|
WSN = 74,
|
||||||
|
PVP = 75,
|
||||||
|
BRSATMON = 76,
|
||||||
|
ND = 77,
|
||||||
|
WBMON = 78,
|
||||||
|
WBEXPAK = 79,
|
||||||
|
EON = 80,
|
||||||
|
VMTP = 81,
|
||||||
|
SVMTP = 82,
|
||||||
|
VINES = 83,
|
||||||
|
TTP = 84,
|
||||||
|
IGP = 85,
|
||||||
|
DGP = 86,
|
||||||
|
TCF = 87,
|
||||||
|
IGRP = 88,
|
||||||
|
OSPFIGP = 89,
|
||||||
|
SRPC = 90,
|
||||||
|
LARP = 91,
|
||||||
|
MTP = 92,
|
||||||
|
AX25 = 93,
|
||||||
|
IPEIP = 94,
|
||||||
|
MICP = 95,
|
||||||
|
SCCSP = 96,
|
||||||
|
ETHERIP = 97,
|
||||||
|
ENCAP = 98,
|
||||||
|
APES = 99,
|
||||||
|
GMTP = 100,
|
||||||
|
IPCOMP = 108,
|
||||||
|
SCTP = 132,
|
||||||
|
MH = 135,
|
||||||
|
UDPLITE = 136,
|
||||||
|
HIP = 139,
|
||||||
|
SHIM6 = 140,
|
||||||
|
PIM = 103,
|
||||||
|
CARP = 112,
|
||||||
|
PGM = 113,
|
||||||
|
MPLS = 137,
|
||||||
|
PFSYNC = 240,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class SocketLevel : u32 {
|
||||||
|
IP = 0,
|
||||||
|
ICMP = 1,
|
||||||
|
TCP = 6,
|
||||||
|
UDP = 17,
|
||||||
|
CONFIG = 0xfffe,
|
||||||
|
SOCKET = 0xffff, // i.e. SOL_SOCKET
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class MsgOpt : u32 {
|
||||||
|
OOB = 0x00001,
|
||||||
|
PEEK = 0x00002,
|
||||||
|
DONTROUTE = 0x00004,
|
||||||
|
EOR_ = 0x00008,
|
||||||
|
TRUNC = 0x00010,
|
||||||
|
CTRUNC = 0x00020,
|
||||||
|
WAITALL = 0x00040,
|
||||||
|
DONTWAIT = 0x00080,
|
||||||
|
EOF_ = 0x00100,
|
||||||
|
NOSIGNAL = 0x20000,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class OptName : u32 {
|
||||||
|
DEBUG = 0x0001,
|
||||||
|
ACCEPTCONN = 0x0002,
|
||||||
|
REUSEADDR = 0x0004,
|
||||||
|
KEEPALIVE = 0x0008,
|
||||||
|
DONTROUTE = 0x0010,
|
||||||
|
BROADCAST = 0x0020,
|
||||||
|
USELOOPBACK = 0x0040,
|
||||||
|
LINGER = 0x0080,
|
||||||
|
OOBINLINE = 0x0100,
|
||||||
|
REUSEPORT = 0x0200,
|
||||||
|
TIMESTAMP = 0x0400,
|
||||||
|
NOSIGPIPE = 0x0800, // at least according to libnx
|
||||||
|
ACCEPTFILER = 0x1000,
|
||||||
|
SNDBUF = 0x1001,
|
||||||
|
RCVBUF = 0x1002,
|
||||||
|
SNDTIMEO = 0x1005,
|
||||||
|
RCVTIMEO = 0x1006,
|
||||||
|
ERROR_ = 0x1007, // avoid name collision with Windows macro
|
||||||
|
ACCEPTFILTER = 0x1000,
|
||||||
|
BINTIME = 0x2000,
|
||||||
|
NO_OFFLOAD = 0x4000,
|
||||||
|
NO_DDP = 0x8000,
|
||||||
|
};
|
||||||
|
enum class TcpOptName : u32 {
|
||||||
|
NODELAY = 0x0001,
|
||||||
|
MAXSEG = 0x0002,
|
||||||
|
NOPUSH = 0x0004,
|
||||||
|
NOOPT = 0x0008,
|
||||||
|
MS5SIG = 0x0010,
|
||||||
|
INFO = 0x0020
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ShutdownHow : s32 {
|
||||||
|
RD = 0,
|
||||||
|
WR = 1,
|
||||||
|
RDWR = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class FcntlCmd : s32 {
|
||||||
|
GETFL = 3,
|
||||||
|
SETFL = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class FcntlFlags : u32 {
|
||||||
|
NONBLOCK = 0x004,
|
||||||
|
NONBLOCK_NX = 0x800,
|
||||||
|
// Provided for convenience
|
||||||
|
NONBLOCK_ANY = u32(NONBLOCK) | u32(NONBLOCK_NX),
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Array of IPv4 address
|
||||||
|
using IPv4Address = std::array<u8, 4>;
|
||||||
|
|
||||||
|
struct SockAddrIn {
|
||||||
|
u8 len;
|
||||||
|
u8 family;
|
||||||
|
u16 portno;
|
||||||
|
IPv4Address ip;
|
||||||
|
std::array<u8, 248> zeroes;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(SockAddrIn) == 0x100);
|
||||||
|
|
||||||
|
enum class PollEvents : u16 {
|
||||||
|
// Using Pascal case because IN is a macro on Windows.
|
||||||
|
IN_ = 0x0001,
|
||||||
|
PRI_ = 0x0002,
|
||||||
|
OUT_ = 0x0004,
|
||||||
|
ERR_ = 0x0008,
|
||||||
|
HUP_ = 0x0010,
|
||||||
|
NVAL = 0x0020,
|
||||||
|
RDNORM = 0x0040,
|
||||||
|
RDBAND = 0x0080,
|
||||||
|
WRBAND = 0x0100,
|
||||||
|
IGNEOF = 0x2000,
|
||||||
|
};
|
||||||
|
DECLARE_ENUM_FLAG_OPERATORS(PollEvents);
|
||||||
|
|
||||||
|
struct PollFD {
|
||||||
|
s32 fd;
|
||||||
|
Network::PollEvents events;
|
||||||
|
Network::PollEvents revents;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(PollFD) == 8);
|
||||||
|
|
||||||
|
struct Linger {
|
||||||
|
s32 onoff;
|
||||||
|
s32 linger;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(Linger) == 8);
|
||||||
|
|
||||||
|
struct Timeval {
|
||||||
|
u64 tv_sec;
|
||||||
|
u64 tv_usec;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(Timeval) == 16);
|
||||||
|
|
||||||
|
/// @brief Cross-platform addrinfo structure (not guest)
|
||||||
|
struct AddrInfo {
|
||||||
|
Domain family;
|
||||||
|
Type socket_type;
|
||||||
|
Protocol protocol;
|
||||||
|
SockAddrIn addr;
|
||||||
|
std::optional<std::string> canon_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Network
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
@@ -30,7 +30,7 @@ public:
|
|||||||
|
|
||||||
struct AcceptResult {
|
struct AcceptResult {
|
||||||
std::unique_ptr<SocketBase> socket;
|
std::unique_ptr<SocketBase> socket;
|
||||||
SockAddrIn sockaddr_in;
|
Network::SockAddrIn sockaddr_in;
|
||||||
};
|
};
|
||||||
|
|
||||||
SocketBase() = default;
|
SocketBase() = default;
|
||||||
@@ -46,13 +46,13 @@ public:
|
|||||||
|
|
||||||
virtual std::pair<AcceptResult, Errno> Accept() = 0;
|
virtual std::pair<AcceptResult, Errno> Accept() = 0;
|
||||||
|
|
||||||
virtual Errno Connect(SockAddrIn addr_in) = 0;
|
virtual Errno Connect(Network::SockAddrIn addr_in) = 0;
|
||||||
|
|
||||||
virtual std::pair<SockAddrIn, Errno> GetPeerName() = 0;
|
virtual std::pair<Network::SockAddrIn, Errno> GetPeerName() = 0;
|
||||||
|
|
||||||
virtual std::pair<SockAddrIn, Errno> GetSockName() = 0;
|
virtual std::pair<Network::SockAddrIn, Errno> GetSockName() = 0;
|
||||||
|
|
||||||
virtual Errno Bind(SockAddrIn addr) = 0;
|
virtual Errno Bind(Network::SockAddrIn addr) = 0;
|
||||||
|
|
||||||
virtual Errno Listen(s32 backlog) = 0;
|
virtual Errno Listen(s32 backlog) = 0;
|
||||||
|
|
||||||
@@ -60,31 +60,16 @@ public:
|
|||||||
|
|
||||||
virtual std::pair<s32, Errno> Recv(int flags, std::span<u8> message) = 0;
|
virtual std::pair<s32, Errno> Recv(int flags, std::span<u8> message) = 0;
|
||||||
|
|
||||||
virtual std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) = 0;
|
virtual std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) = 0;
|
||||||
|
|
||||||
virtual std::pair<s32, Errno> Send(std::span<const u8> message, int flags) = 0;
|
virtual std::pair<s32, Errno> Send(std::span<const u8> message, int flags) = 0;
|
||||||
|
|
||||||
virtual std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
|
virtual std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) = 0;
|
||||||
const SockAddrIn* addr) = 0;
|
|
||||||
|
|
||||||
virtual Errno SetLinger(bool enable, u32 linger) = 0;
|
|
||||||
|
|
||||||
virtual Errno SetReuseAddr(bool enable) = 0;
|
|
||||||
|
|
||||||
virtual Errno SetKeepAlive(bool enable) = 0;
|
|
||||||
|
|
||||||
virtual Errno SetBroadcast(bool enable) = 0;
|
|
||||||
|
|
||||||
virtual Errno SetSndBuf(u32 value) = 0;
|
|
||||||
|
|
||||||
virtual Errno SetRcvBuf(u32 value) = 0;
|
|
||||||
|
|
||||||
virtual Errno SetSndTimeo(u32 value) = 0;
|
|
||||||
|
|
||||||
virtual Errno SetRcvTimeo(u32 value) = 0;
|
|
||||||
|
|
||||||
virtual Errno SetNonBlock(bool enable) = 0;
|
virtual Errno SetNonBlock(bool enable) = 0;
|
||||||
|
|
||||||
|
virtual Errno SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) = 0;
|
||||||
|
|
||||||
virtual std::pair<Errno, Errno> GetPendingError() = 0;
|
virtual std::pair<Errno, Errno> GetPendingError() = 0;
|
||||||
|
|
||||||
virtual bool IsOpened() const = 0;
|
virtual bool IsOpened() const = 0;
|
||||||
@@ -95,7 +80,6 @@ public:
|
|||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
|
||||||
SOCKET fd = INVALID_SOCKET;
|
SOCKET fd = INVALID_SOCKET;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -114,13 +98,13 @@ public:
|
|||||||
|
|
||||||
std::pair<AcceptResult, Errno> Accept() override;
|
std::pair<AcceptResult, Errno> Accept() override;
|
||||||
|
|
||||||
Errno Connect(SockAddrIn addr_in) override;
|
Errno Connect(Network::SockAddrIn addr_in) override;
|
||||||
|
|
||||||
std::pair<SockAddrIn, Errno> GetPeerName() override;
|
std::pair<Network::SockAddrIn, Errno> GetPeerName() override;
|
||||||
|
|
||||||
std::pair<SockAddrIn, Errno> GetSockName() override;
|
std::pair<Network::SockAddrIn, Errno> GetSockName() override;
|
||||||
|
|
||||||
Errno Bind(SockAddrIn addr) override;
|
Errno Bind(Network::SockAddrIn addr) override;
|
||||||
|
|
||||||
Errno Listen(s32 backlog) override;
|
Errno Listen(s32 backlog) override;
|
||||||
|
|
||||||
@@ -128,38 +112,19 @@ public:
|
|||||||
|
|
||||||
std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override;
|
std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override;
|
||||||
|
|
||||||
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) override;
|
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) override;
|
||||||
|
|
||||||
std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
|
std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
|
||||||
|
|
||||||
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
|
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) override;
|
||||||
const SockAddrIn* addr) override;
|
|
||||||
|
|
||||||
Errno SetLinger(bool enable, u32 linger) override;
|
|
||||||
|
|
||||||
Errno SetReuseAddr(bool enable) override;
|
|
||||||
|
|
||||||
Errno SetKeepAlive(bool enable) override;
|
|
||||||
|
|
||||||
Errno SetBroadcast(bool enable) override;
|
|
||||||
|
|
||||||
Errno SetSndBuf(u32 value) override;
|
|
||||||
|
|
||||||
Errno SetRcvBuf(u32 value) override;
|
|
||||||
|
|
||||||
Errno SetSndTimeo(u32 value) override;
|
|
||||||
|
|
||||||
Errno SetRcvTimeo(u32 value) override;
|
|
||||||
|
|
||||||
Errno SetNonBlock(bool enable) override;
|
Errno SetNonBlock(bool enable) override;
|
||||||
|
|
||||||
template <typename T>
|
Errno SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) override;
|
||||||
Errno SetSockOpt(SOCKET fd, int option, T value);
|
|
||||||
|
|
||||||
std::pair<Errno, Errno> GetPendingError() override;
|
std::pair<Errno, Errno> GetPendingError() override;
|
||||||
|
|
||||||
template <typename T>
|
Errno GetSockOpt(Network::SocketLevel level, Network::OptName optname, std::span<u8> value);
|
||||||
std::pair<T, Errno> GetSockOpt(SOCKET fd, int option);
|
|
||||||
|
|
||||||
bool IsOpened() const override;
|
bool IsOpened() const override;
|
||||||
|
|
||||||
@@ -169,6 +134,6 @@ private:
|
|||||||
bool is_non_blocking = false;
|
bool is_non_blocking = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout);
|
std::pair<s32, Errno> Poll(std::span<Network::HostPollFD> poll_fds, s32 timeout);
|
||||||
|
|
||||||
} // namespace Network
|
} // namespace Network
|
||||||
|
|||||||
@@ -33,18 +33,10 @@ u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) {
|
|||||||
|
|
||||||
void MemoryWriteWidth(Core::Memory::Memory& memory, u32 width, VAddr addr, u64 value) {
|
void MemoryWriteWidth(Core::Memory::Memory& memory, u32 width, VAddr addr, u64 value) {
|
||||||
switch (width) {
|
switch (width) {
|
||||||
case 1:
|
case sizeof(u64): return memory.Write64(addr, value);
|
||||||
memory.Write8(addr, static_cast<u8>(value));
|
case sizeof(u32): return memory.Write32(addr, u32(value));
|
||||||
break;
|
case sizeof(u16): return memory.Write16(addr, u16(value));
|
||||||
case 2:
|
case sizeof(u8): return memory.Write8(addr, u8(value));
|
||||||
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:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,29 +24,65 @@
|
|||||||
namespace Dynarmic::Backend::Arm64 {
|
namespace Dynarmic::Backend::Arm64 {
|
||||||
|
|
||||||
template<auto mfp, typename T>
|
template<auto mfp, typename T>
|
||||||
static void* EmitCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
|
static void* EmitCallReadTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
|
||||||
using namespace oaknut::util;
|
using namespace oaknut::util;
|
||||||
|
|
||||||
const auto info = Devirtualize<mfp>(this_);
|
const auto info = Devirtualize<mfp>(this_);
|
||||||
|
|
||||||
oaknut::Label l_addr, l_this;
|
oaknut::Label l_addr, l_this;
|
||||||
|
|
||||||
void* target = code.xptr<void*>();
|
void* target = code.xptr<void*>();
|
||||||
|
// params = { this, vaddr, bitsize }
|
||||||
code.LDR(X0, l_this);
|
code.LDR(X0, l_this);
|
||||||
|
// X1 = vaddr
|
||||||
|
code.MOV(X2, bitsize);
|
||||||
code.LDR(Xscratch0, l_addr);
|
code.LDR(Xscratch0, l_addr);
|
||||||
code.BR(Xscratch0);
|
code.BR(Xscratch0);
|
||||||
|
|
||||||
code.align(8);
|
code.align(8);
|
||||||
code.l(l_this);
|
code.l(l_this);
|
||||||
code.dx(info.this_ptr);
|
code.dx(info.this_ptr);
|
||||||
code.l(l_addr);
|
code.l(l_addr);
|
||||||
code.dx(info.fn_ptr);
|
code.dx(info.fn_ptr);
|
||||||
|
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<auto mfp, typename T>
|
template<auto mfp, typename T>
|
||||||
static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
|
static void* EmitCallWriteTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
|
||||||
|
using namespace oaknut::util;
|
||||||
|
const auto info = Devirtualize<mfp>(this_);
|
||||||
|
oaknut::Label l_addr, l_this;
|
||||||
|
void* target = code.xptr<void*>();
|
||||||
|
// params = { this, vaddr, value, bitsize }
|
||||||
|
code.LDR(X0, l_this);
|
||||||
|
// X1 = vaddr
|
||||||
|
// X2 = value
|
||||||
|
code.MOV(X3, bitsize);
|
||||||
|
code.LDR(Xscratch0, l_addr);
|
||||||
|
code.BR(Xscratch0);
|
||||||
|
code.align(8);
|
||||||
|
code.l(l_this);
|
||||||
|
code.dx(info.this_ptr);
|
||||||
|
code.l(l_addr);
|
||||||
|
code.dx(info.fn_ptr);
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<auto mfp, typename T>
|
||||||
|
static void* EmitCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
|
||||||
|
using namespace oaknut::util;
|
||||||
|
const auto info = Devirtualize<mfp>(this_);
|
||||||
|
oaknut::Label l_addr, l_this;
|
||||||
|
void* target = code.xptr<void*>();
|
||||||
|
code.LDR(X0, l_this);
|
||||||
|
code.LDR(Xscratch0, l_addr);
|
||||||
|
code.BR(Xscratch0);
|
||||||
|
code.align(8);
|
||||||
|
code.l(l_this);
|
||||||
|
code.dx(info.this_ptr);
|
||||||
|
code.l(l_addr);
|
||||||
|
code.dx(info.fn_ptr);
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<auto mfp, typename T>
|
||||||
|
static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
|
||||||
using namespace oaknut::util;
|
using namespace oaknut::util;
|
||||||
|
|
||||||
const auto info = Devirtualize<mfp>(this_);
|
const auto info = Devirtualize<mfp>(this_);
|
||||||
@@ -59,6 +95,7 @@ static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_
|
|||||||
ABI_PushRegisters(code, save_regs, 0);
|
ABI_PushRegisters(code, save_regs, 0);
|
||||||
code.LDR(X0, l_this);
|
code.LDR(X0, l_this);
|
||||||
code.MOV(X1, Xscratch0);
|
code.MOV(X1, Xscratch0);
|
||||||
|
code.MOV(X2, bitsize);
|
||||||
code.LDR(Xscratch0, l_addr);
|
code.LDR(Xscratch0, l_addr);
|
||||||
code.BLR(Xscratch0);
|
code.BLR(Xscratch0);
|
||||||
code.MOV(Xscratch0, X0);
|
code.MOV(Xscratch0, X0);
|
||||||
@@ -82,7 +119,7 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const
|
|||||||
|
|
||||||
auto fn = [](const A32::UserConfig& conf, A32::VAddr vaddr) -> T {
|
auto fn = [](const A32::UserConfig& conf, A32::VAddr vaddr) -> T {
|
||||||
return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T {
|
return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T {
|
||||||
return (conf.callbacks->*callback)(vaddr);
|
return (conf.callbacks->*callback)(vaddr, sizeof(T));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -101,7 +138,7 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<auto mfp, typename T>
|
template<auto mfp, typename T>
|
||||||
static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
|
static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
|
||||||
using namespace oaknut::util;
|
using namespace oaknut::util;
|
||||||
|
|
||||||
const auto info = Devirtualize<mfp>(this_);
|
const auto info = Devirtualize<mfp>(this_);
|
||||||
@@ -115,6 +152,7 @@ static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this
|
|||||||
code.LDR(X0, l_this);
|
code.LDR(X0, l_this);
|
||||||
code.MOV(X1, Xscratch0);
|
code.MOV(X1, Xscratch0);
|
||||||
code.MOV(X2, Xscratch1);
|
code.MOV(X2, Xscratch1);
|
||||||
|
code.MOV(X3, bitsize);
|
||||||
code.LDR(Xscratch0, l_addr);
|
code.LDR(Xscratch0, l_addr);
|
||||||
code.BLR(Xscratch0);
|
code.BLR(Xscratch0);
|
||||||
ABI_PopRegisters(code, save_regs, 0);
|
ABI_PopRegisters(code, save_regs, 0);
|
||||||
@@ -136,12 +174,9 @@ static void* EmitExclusiveWriteCallTrampoline(oaknut::CodeGenerator& code, const
|
|||||||
oaknut::Label l_addr, l_this;
|
oaknut::Label l_addr, l_this;
|
||||||
|
|
||||||
auto fn = [](const A32::UserConfig& conf, A32::VAddr vaddr, T value) -> u32 {
|
auto fn = [](const A32::UserConfig& conf, A32::VAddr vaddr, T value) -> u32 {
|
||||||
return conf.global_monitor->DoExclusiveOperation<T>(conf.processor_id, vaddr,
|
return conf.global_monitor->DoExclusiveOperation<T>(conf.processor_id, vaddr, [&](T expected) -> bool {
|
||||||
[&](T expected) -> bool {
|
return (conf.callbacks->*callback)(vaddr, value, expected);
|
||||||
return (conf.callbacks->*callback)(vaddr, value, expected);
|
}) ? 0 : 1;
|
||||||
})
|
|
||||||
? 0
|
|
||||||
: 1;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void* target = code.xptr<void*>();
|
void* target = code.xptr<void*>();
|
||||||
@@ -179,26 +214,27 @@ void A32AddressSpace::EmitPrelude() {
|
|||||||
|
|
||||||
UnprotectCodeMemory();
|
UnprotectCodeMemory();
|
||||||
|
|
||||||
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_8 = EmitCallReadTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u8));
|
||||||
prelude_info.read_memory_32 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead32>(code, conf.callbacks);
|
prelude_info.read_memory_16 = EmitCallReadTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u16));
|
||||||
prelude_info.read_memory_64 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead64>(code, conf.callbacks);
|
prelude_info.read_memory_32 = EmitCallReadTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u32));
|
||||||
prelude_info.wrapped_read_memory_8 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead8>(code, conf.callbacks);
|
prelude_info.read_memory_64 = EmitCallReadTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u64));
|
||||||
prelude_info.wrapped_read_memory_16 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead16>(code, conf.callbacks);
|
prelude_info.wrapped_read_memory_8 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u8));
|
||||||
prelude_info.wrapped_read_memory_32 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead32>(code, conf.callbacks);
|
prelude_info.wrapped_read_memory_16 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u16));
|
||||||
prelude_info.wrapped_read_memory_64 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead64>(code, conf.callbacks);
|
prelude_info.wrapped_read_memory_32 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u32));
|
||||||
prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead8, u8>(code, conf);
|
prelude_info.wrapped_read_memory_64 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u64));
|
||||||
prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead16, u16>(code, conf);
|
prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u8>(code, conf);
|
||||||
prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead32, u32>(code, conf);
|
prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u16>(code, conf);
|
||||||
prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead64, u64>(code, conf);
|
prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u32>(code, conf);
|
||||||
prelude_info.write_memory_8 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite8>(code, conf.callbacks);
|
prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u64>(code, conf);
|
||||||
prelude_info.write_memory_16 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite16>(code, conf.callbacks);
|
prelude_info.write_memory_8 = EmitCallWriteTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u8));
|
||||||
prelude_info.write_memory_32 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite32>(code, conf.callbacks);
|
prelude_info.write_memory_16 = EmitCallWriteTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u16));
|
||||||
prelude_info.write_memory_64 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite64>(code, conf.callbacks);
|
prelude_info.write_memory_32 = EmitCallWriteTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u32));
|
||||||
prelude_info.wrapped_write_memory_8 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite8>(code, conf.callbacks);
|
prelude_info.write_memory_64 = EmitCallWriteTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u64));
|
||||||
prelude_info.wrapped_write_memory_16 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite16>(code, conf.callbacks);
|
prelude_info.wrapped_write_memory_8 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u8));
|
||||||
prelude_info.wrapped_write_memory_32 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite32>(code, conf.callbacks);
|
prelude_info.wrapped_write_memory_16 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u16));
|
||||||
prelude_info.wrapped_write_memory_64 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite64>(code, conf.callbacks);
|
prelude_info.wrapped_write_memory_32 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u32));
|
||||||
|
prelude_info.wrapped_write_memory_64 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u64));
|
||||||
prelude_info.exclusive_write_memory_8 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive8, u8>(code, conf);
|
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_16 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive16, u16>(code, conf);
|
||||||
prelude_info.exclusive_write_memory_32 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive32, u32>(code, conf);
|
prelude_info.exclusive_write_memory_32 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive32, u32>(code, conf);
|
||||||
|
|||||||
@@ -23,29 +23,65 @@
|
|||||||
namespace Dynarmic::Backend::Arm64 {
|
namespace Dynarmic::Backend::Arm64 {
|
||||||
|
|
||||||
template<auto mfp, typename T>
|
template<auto mfp, typename T>
|
||||||
static void* EmitCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
|
static void* EmitCallReadTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
|
||||||
using namespace oaknut::util;
|
using namespace oaknut::util;
|
||||||
|
|
||||||
const auto info = Devirtualize<mfp>(this_);
|
const auto info = Devirtualize<mfp>(this_);
|
||||||
|
|
||||||
oaknut::Label l_addr, l_this;
|
oaknut::Label l_addr, l_this;
|
||||||
|
|
||||||
void* target = code.xptr<void*>();
|
void* target = code.xptr<void*>();
|
||||||
|
// params = { this, vaddr, bitsize }
|
||||||
code.LDR(X0, l_this);
|
code.LDR(X0, l_this);
|
||||||
|
// X1 = vaddr
|
||||||
|
code.MOV(X2, bitsize);
|
||||||
code.LDR(Xscratch0, l_addr);
|
code.LDR(Xscratch0, l_addr);
|
||||||
code.BR(Xscratch0);
|
code.BR(Xscratch0);
|
||||||
|
|
||||||
code.align(8);
|
code.align(8);
|
||||||
code.l(l_this);
|
code.l(l_this);
|
||||||
code.dx(info.this_ptr);
|
code.dx(info.this_ptr);
|
||||||
code.l(l_addr);
|
code.l(l_addr);
|
||||||
code.dx(info.fn_ptr);
|
code.dx(info.fn_ptr);
|
||||||
|
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<auto mfp, typename T>
|
template<auto mfp, typename T>
|
||||||
static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
|
static void* EmitCallWriteTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
|
||||||
|
using namespace oaknut::util;
|
||||||
|
const auto info = Devirtualize<mfp>(this_);
|
||||||
|
oaknut::Label l_addr, l_this;
|
||||||
|
void* target = code.xptr<void*>();
|
||||||
|
// params = { this, vaddr, value, bitsize }
|
||||||
|
code.LDR(X0, l_this);
|
||||||
|
// X1 = vaddr
|
||||||
|
// X2 = value
|
||||||
|
code.MOV(X3, bitsize);
|
||||||
|
code.LDR(Xscratch0, l_addr);
|
||||||
|
code.BR(Xscratch0);
|
||||||
|
code.align(8);
|
||||||
|
code.l(l_this);
|
||||||
|
code.dx(info.this_ptr);
|
||||||
|
code.l(l_addr);
|
||||||
|
code.dx(info.fn_ptr);
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<auto mfp, typename T>
|
||||||
|
static void* EmitCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
|
||||||
|
using namespace oaknut::util;
|
||||||
|
const auto info = Devirtualize<mfp>(this_);
|
||||||
|
oaknut::Label l_addr, l_this;
|
||||||
|
void* target = code.xptr<void*>();
|
||||||
|
code.LDR(X0, l_this);
|
||||||
|
code.LDR(Xscratch0, l_addr);
|
||||||
|
code.BR(Xscratch0);
|
||||||
|
code.align(8);
|
||||||
|
code.l(l_this);
|
||||||
|
code.dx(info.this_ptr);
|
||||||
|
code.l(l_addr);
|
||||||
|
code.dx(info.fn_ptr);
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<auto mfp, typename T>
|
||||||
|
static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
|
||||||
using namespace oaknut::util;
|
using namespace oaknut::util;
|
||||||
|
|
||||||
const auto info = Devirtualize<mfp>(this_);
|
const auto info = Devirtualize<mfp>(this_);
|
||||||
@@ -58,6 +94,7 @@ static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_
|
|||||||
ABI_PushRegisters(code, save_regs, 0);
|
ABI_PushRegisters(code, save_regs, 0);
|
||||||
code.LDR(X0, l_this);
|
code.LDR(X0, l_this);
|
||||||
code.MOV(X1, Xscratch0);
|
code.MOV(X1, Xscratch0);
|
||||||
|
code.MOV(X2, bitsize);
|
||||||
code.LDR(Xscratch0, l_addr);
|
code.LDR(Xscratch0, l_addr);
|
||||||
code.BLR(Xscratch0);
|
code.BLR(Xscratch0);
|
||||||
code.MOV(Xscratch0, X0);
|
code.MOV(Xscratch0, X0);
|
||||||
@@ -81,7 +118,7 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const
|
|||||||
|
|
||||||
auto fn = [](const A64::UserConfig& conf, A64::VAddr vaddr) -> T {
|
auto fn = [](const A64::UserConfig& conf, A64::VAddr vaddr) -> T {
|
||||||
return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T {
|
return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T {
|
||||||
return (conf.callbacks->*callback)(vaddr);
|
return (conf.callbacks->*callback)(vaddr, sizeof(T));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -100,7 +137,7 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<auto mfp, typename T>
|
template<auto mfp, typename T>
|
||||||
static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
|
static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
|
||||||
using namespace oaknut::util;
|
using namespace oaknut::util;
|
||||||
|
|
||||||
const auto info = Devirtualize<mfp>(this_);
|
const auto info = Devirtualize<mfp>(this_);
|
||||||
@@ -114,6 +151,7 @@ static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this
|
|||||||
code.LDR(X0, l_this);
|
code.LDR(X0, l_this);
|
||||||
code.MOV(X1, Xscratch0);
|
code.MOV(X1, Xscratch0);
|
||||||
code.MOV(X2, Xscratch1);
|
code.MOV(X2, Xscratch1);
|
||||||
|
code.MOV(X3, bitsize);
|
||||||
code.LDR(Xscratch0, l_addr);
|
code.LDR(Xscratch0, l_addr);
|
||||||
code.BLR(Xscratch0);
|
code.BLR(Xscratch0);
|
||||||
ABI_PopRegisters(code, save_regs, 0);
|
ABI_PopRegisters(code, save_regs, 0);
|
||||||
@@ -133,14 +171,10 @@ static void* EmitExclusiveWriteCallTrampoline(oaknut::CodeGenerator& code, const
|
|||||||
using namespace oaknut::util;
|
using namespace oaknut::util;
|
||||||
|
|
||||||
oaknut::Label l_addr, l_this;
|
oaknut::Label l_addr, l_this;
|
||||||
|
|
||||||
auto fn = [](const A64::UserConfig& conf, A64::VAddr vaddr, T value) -> u32 {
|
auto fn = [](const A64::UserConfig& conf, A64::VAddr vaddr, T value) -> u32 {
|
||||||
return conf.global_monitor->DoExclusiveOperation<T>(conf.processor_id, vaddr,
|
return conf.global_monitor->DoExclusiveOperation<T>(conf.processor_id, vaddr, [&](T expected) -> bool {
|
||||||
[&](T expected) -> bool {
|
return (conf.callbacks->*callback)(vaddr, value, expected);
|
||||||
return (conf.callbacks->*callback)(vaddr, value, expected);
|
}) ? 0 : 1;
|
||||||
})
|
|
||||||
? 0
|
|
||||||
: 1;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void* target = code.xptr<void*>();
|
void* target = code.xptr<void*>();
|
||||||
@@ -346,30 +380,30 @@ void A64AddressSpace::EmitPrelude() {
|
|||||||
|
|
||||||
UnprotectCodeMemory();
|
UnprotectCodeMemory();
|
||||||
|
|
||||||
prelude_info.read_memory_8 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead8>(code, conf.callbacks);
|
prelude_info.read_memory_8 = EmitCallReadTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u8));
|
||||||
prelude_info.read_memory_16 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead16>(code, conf.callbacks);
|
prelude_info.read_memory_16 = EmitCallReadTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u16));
|
||||||
prelude_info.read_memory_32 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead32>(code, conf.callbacks);
|
prelude_info.read_memory_32 = EmitCallReadTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u32));
|
||||||
prelude_info.read_memory_64 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead64>(code, conf.callbacks);
|
prelude_info.read_memory_64 = EmitCallReadTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u64));
|
||||||
prelude_info.read_memory_128 = EmitRead128CallTrampoline(code, conf.callbacks);
|
prelude_info.read_memory_128 = EmitRead128CallTrampoline(code, conf.callbacks);
|
||||||
prelude_info.wrapped_read_memory_8 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead8>(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::MemoryRead16>(code, conf.callbacks);
|
prelude_info.wrapped_read_memory_16 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u16));
|
||||||
prelude_info.wrapped_read_memory_32 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead32>(code, conf.callbacks);
|
prelude_info.wrapped_read_memory_32 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u32));
|
||||||
prelude_info.wrapped_read_memory_64 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead64>(code, conf.callbacks);
|
prelude_info.wrapped_read_memory_64 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u64));
|
||||||
prelude_info.wrapped_read_memory_128 = EmitWrappedRead128CallTrampoline(code, conf.callbacks);
|
prelude_info.wrapped_read_memory_128 = EmitWrappedRead128CallTrampoline(code, conf.callbacks);
|
||||||
prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead8, u8>(code, conf);
|
prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u8>(code, conf);
|
||||||
prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead16, u16>(code, conf);
|
prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u16>(code, conf);
|
||||||
prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead32, u32>(code, conf);
|
prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u32>(code, conf);
|
||||||
prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead64, u64>(code, conf);
|
prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u64>(code, conf);
|
||||||
prelude_info.exclusive_read_memory_128 = EmitExclusiveRead128CallTrampoline(code, conf);
|
prelude_info.exclusive_read_memory_128 = EmitExclusiveRead128CallTrampoline(code, conf);
|
||||||
prelude_info.write_memory_8 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite8>(code, conf.callbacks);
|
prelude_info.write_memory_8 = EmitCallWriteTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u8));
|
||||||
prelude_info.write_memory_16 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite16>(code, conf.callbacks);
|
prelude_info.write_memory_16 = EmitCallWriteTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u16));
|
||||||
prelude_info.write_memory_32 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite32>(code, conf.callbacks);
|
prelude_info.write_memory_32 = EmitCallWriteTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u32));
|
||||||
prelude_info.write_memory_64 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite64>(code, conf.callbacks);
|
prelude_info.write_memory_64 = EmitCallWriteTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u64));
|
||||||
prelude_info.write_memory_128 = EmitWrite128CallTrampoline(code, conf.callbacks);
|
prelude_info.write_memory_128 = EmitWrite128CallTrampoline(code, conf.callbacks);
|
||||||
prelude_info.wrapped_write_memory_8 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite8>(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::MemoryWrite16>(code, conf.callbacks);
|
prelude_info.wrapped_write_memory_16 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u16));
|
||||||
prelude_info.wrapped_write_memory_32 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite32>(code, conf.callbacks);
|
prelude_info.wrapped_write_memory_32 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u32));
|
||||||
prelude_info.wrapped_write_memory_64 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite64>(code, conf.callbacks);
|
prelude_info.wrapped_write_memory_64 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u64));
|
||||||
prelude_info.wrapped_write_memory_128 = EmitWrappedWrite128CallTrampoline(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_8 = EmitExclusiveWriteCallTrampoline<&A64::UserCallbacks::MemoryWriteExclusive8, u8>(code, conf);
|
||||||
prelude_info.exclusive_write_memory_16 = EmitExclusiveWriteCallTrampoline<&A64::UserCallbacks::MemoryWriteExclusive16, u16>(code, conf);
|
prelude_info.exclusive_write_memory_16 = EmitExclusiveWriteCallTrampoline<&A64::UserCallbacks::MemoryWriteExclusive16, u16>(code, conf);
|
||||||
|
|||||||
@@ -145,6 +145,7 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
|
|||||||
case LinkTarget::ReturnFromRunCode:
|
case LinkTarget::ReturnFromRunCode:
|
||||||
c.B(prelude_info.return_from_run_code);
|
c.B(prelude_info.return_from_run_code);
|
||||||
break;
|
break;
|
||||||
|
// { this, vaddr, size }
|
||||||
case LinkTarget::ReadMemory8:
|
case LinkTarget::ReadMemory8:
|
||||||
c.BL(prelude_info.read_memory_8);
|
c.BL(prelude_info.read_memory_8);
|
||||||
break;
|
break;
|
||||||
@@ -160,6 +161,7 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
|
|||||||
case LinkTarget::ReadMemory128:
|
case LinkTarget::ReadMemory128:
|
||||||
c.BL(prelude_info.read_memory_128);
|
c.BL(prelude_info.read_memory_128);
|
||||||
break;
|
break;
|
||||||
|
// { this, vaddr, size }
|
||||||
case LinkTarget::WrappedReadMemory8:
|
case LinkTarget::WrappedReadMemory8:
|
||||||
c.BL(prelude_info.wrapped_read_memory_8);
|
c.BL(prelude_info.wrapped_read_memory_8);
|
||||||
break;
|
break;
|
||||||
@@ -175,6 +177,7 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
|
|||||||
case LinkTarget::WrappedReadMemory128:
|
case LinkTarget::WrappedReadMemory128:
|
||||||
c.BL(prelude_info.wrapped_read_memory_128);
|
c.BL(prelude_info.wrapped_read_memory_128);
|
||||||
break;
|
break;
|
||||||
|
//
|
||||||
case LinkTarget::ExclusiveReadMemory8:
|
case LinkTarget::ExclusiveReadMemory8:
|
||||||
c.BL(prelude_info.exclusive_read_memory_8);
|
c.BL(prelude_info.exclusive_read_memory_8);
|
||||||
break;
|
break;
|
||||||
@@ -190,6 +193,7 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
|
|||||||
case LinkTarget::ExclusiveReadMemory128:
|
case LinkTarget::ExclusiveReadMemory128:
|
||||||
c.BL(prelude_info.exclusive_read_memory_128);
|
c.BL(prelude_info.exclusive_read_memory_128);
|
||||||
break;
|
break;
|
||||||
|
// { this, vaddr, value, size }
|
||||||
case LinkTarget::WriteMemory8:
|
case LinkTarget::WriteMemory8:
|
||||||
c.BL(prelude_info.write_memory_8);
|
c.BL(prelude_info.write_memory_8);
|
||||||
break;
|
break;
|
||||||
@@ -205,6 +209,7 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
|
|||||||
case LinkTarget::WriteMemory128:
|
case LinkTarget::WriteMemory128:
|
||||||
c.BL(prelude_info.write_memory_128);
|
c.BL(prelude_info.write_memory_128);
|
||||||
break;
|
break;
|
||||||
|
//
|
||||||
case LinkTarget::WrappedWriteMemory8:
|
case LinkTarget::WrappedWriteMemory8:
|
||||||
c.BL(prelude_info.wrapped_write_memory_8);
|
c.BL(prelude_info.wrapped_write_memory_8);
|
||||||
break;
|
break;
|
||||||
@@ -220,6 +225,7 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
|
|||||||
case LinkTarget::WrappedWriteMemory128:
|
case LinkTarget::WrappedWriteMemory128:
|
||||||
c.BL(prelude_info.wrapped_write_memory_128);
|
c.BL(prelude_info.wrapped_write_memory_128);
|
||||||
break;
|
break;
|
||||||
|
//
|
||||||
case LinkTarget::ExclusiveWriteMemory8:
|
case LinkTarget::ExclusiveWriteMemory8:
|
||||||
c.BL(prelude_info.exclusive_write_memory_8);
|
c.BL(prelude_info.exclusive_write_memory_8);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -31,16 +31,16 @@ using namespace Xbyak::util;
|
|||||||
void A32EmitX64::GenFastmemFallbacks() {
|
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::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{{
|
const std::array<std::pair<size_t, ArgCallback>, 4> read_callbacks{{
|
||||||
{8, Devirtualize<&A32::UserCallbacks::MemoryRead8>(conf.callbacks)},
|
{8, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)},
|
||||||
{16, Devirtualize<&A32::UserCallbacks::MemoryRead16>(conf.callbacks)},
|
{16, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)},
|
||||||
{32, Devirtualize<&A32::UserCallbacks::MemoryRead32>(conf.callbacks)},
|
{32, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)},
|
||||||
{64, Devirtualize<&A32::UserCallbacks::MemoryRead64>(conf.callbacks)},
|
{64, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)},
|
||||||
}};
|
}};
|
||||||
const std::array<std::pair<size_t, ArgCallback>, 4> write_callbacks{{
|
const std::array<std::pair<size_t, ArgCallback>, 4> write_callbacks{{
|
||||||
{8, Devirtualize<&A32::UserCallbacks::MemoryWrite8>(conf.callbacks)},
|
{8, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)},
|
||||||
{16, Devirtualize<&A32::UserCallbacks::MemoryWrite16>(conf.callbacks)},
|
{16, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)},
|
||||||
{32, Devirtualize<&A32::UserCallbacks::MemoryWrite32>(conf.callbacks)},
|
{32, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)},
|
||||||
{64, Devirtualize<&A32::UserCallbacks::MemoryWrite64>(conf.callbacks)},
|
{64, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)},
|
||||||
}};
|
}};
|
||||||
const std::array<std::pair<size_t, ArgCallback>, 4> exclusive_write_callbacks{{
|
const std::array<std::pair<size_t, ArgCallback>, 4> exclusive_write_callbacks{{
|
||||||
{8, Devirtualize<&A32::UserCallbacks::MemoryWriteExclusive8>(conf.callbacks)},
|
{8, Devirtualize<&A32::UserCallbacks::MemoryWriteExclusive8>(conf.callbacks)},
|
||||||
@@ -56,12 +56,12 @@ void A32EmitX64::GenFastmemFallbacks() {
|
|||||||
code.align();
|
code.align();
|
||||||
read_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>();
|
read_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>();
|
||||||
ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocRegIdx(value_idx));
|
ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocRegIdx(value_idx));
|
||||||
|
// params = { this, vaddr, size }
|
||||||
if (vaddr_idx != code.ABI_PARAM2.getIdx()) {
|
if (vaddr_idx != code.ABI_PARAM2.getIdx()) {
|
||||||
code.mov(code.ABI_PARAM2, Xbyak::Reg64{vaddr_idx});
|
code.mov(code.ABI_PARAM2, Xbyak::Reg64{vaddr_idx});
|
||||||
}
|
}
|
||||||
if (ordered) {
|
code.mov(code.ABI_PARAM3, bitsize / CHAR_BIT);
|
||||||
code.mfence();
|
if (ordered) code.mfence();
|
||||||
}
|
|
||||||
callback.EmitCall(code);
|
callback.EmitCall(code);
|
||||||
if (value_idx != code.ABI_RETURN.getIdx()) {
|
if (value_idx != code.ABI_RETURN.getIdx()) {
|
||||||
code.mov(Xbyak::Reg64{value_idx}, code.ABI_RETURN);
|
code.mov(Xbyak::Reg64{value_idx}, code.ABI_RETURN);
|
||||||
@@ -76,6 +76,7 @@ void A32EmitX64::GenFastmemFallbacks() {
|
|||||||
code.align();
|
code.align();
|
||||||
write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>();
|
write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>();
|
||||||
ABI_PushCallerSaveRegistersAndAdjustStack(code);
|
ABI_PushCallerSaveRegistersAndAdjustStack(code);
|
||||||
|
// params = { this, vaddr, value, size }
|
||||||
if (vaddr_idx == code.ABI_PARAM3.getIdx() && value_idx == code.ABI_PARAM2.getIdx()) {
|
if (vaddr_idx == code.ABI_PARAM3.getIdx() && value_idx == code.ABI_PARAM2.getIdx()) {
|
||||||
code.xchg(code.ABI_PARAM2, code.ABI_PARAM3);
|
code.xchg(code.ABI_PARAM2, code.ABI_PARAM3);
|
||||||
} else if (vaddr_idx == code.ABI_PARAM3.getIdx()) {
|
} else if (vaddr_idx == code.ABI_PARAM3.getIdx()) {
|
||||||
@@ -92,10 +93,9 @@ void A32EmitX64::GenFastmemFallbacks() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
code.ZeroExtendFrom(bitsize, code.ABI_PARAM3);
|
code.ZeroExtendFrom(bitsize, code.ABI_PARAM3);
|
||||||
|
code.mov(code.ABI_PARAM4, bitsize / CHAR_BIT);
|
||||||
callback.EmitCall(code);
|
callback.EmitCall(code);
|
||||||
if (ordered) {
|
if (ordered) code.mfence();
|
||||||
code.mfence();
|
|
||||||
}
|
|
||||||
ABI_PopCallerSaveRegistersAndAdjustStack(code);
|
ABI_PopCallerSaveRegistersAndAdjustStack(code);
|
||||||
code.ret();
|
code.ret();
|
||||||
PerfMapRegister(write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)], code.getCurr(), fmt::format("a32_write_fallback_{}", bitsize));
|
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
|
#undef Axx
|
||||||
|
|
||||||
void A32EmitX64::EmitA32ReadMemory8(A32EmitContext& ctx, IR::Inst* inst) {
|
void A32EmitX64::EmitA32ReadMemory8(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
EmitMemoryRead<8, &A32::UserCallbacks::MemoryRead8>(ctx, inst);
|
EmitMemoryRead<8, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32EmitX64::EmitA32ReadMemory16(A32EmitContext& ctx, IR::Inst* inst) {
|
void A32EmitX64::EmitA32ReadMemory16(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
EmitMemoryRead<16, &A32::UserCallbacks::MemoryRead16>(ctx, inst);
|
EmitMemoryRead<16, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32EmitX64::EmitA32ReadMemory32(A32EmitContext& ctx, IR::Inst* inst) {
|
void A32EmitX64::EmitA32ReadMemory32(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
EmitMemoryRead<32, &A32::UserCallbacks::MemoryRead32>(ctx, inst);
|
EmitMemoryRead<32, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32EmitX64::EmitA32ReadMemory64(A32EmitContext& ctx, IR::Inst* inst) {
|
void A32EmitX64::EmitA32ReadMemory64(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
EmitMemoryRead<64, &A32::UserCallbacks::MemoryRead64>(ctx, inst);
|
EmitMemoryRead<64, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32EmitX64::EmitA32WriteMemory8(A32EmitContext& ctx, IR::Inst* inst) {
|
void A32EmitX64::EmitA32WriteMemory8(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
EmitMemoryWrite<8, &A32::UserCallbacks::MemoryWrite8>(ctx, inst);
|
EmitMemoryWrite<8, &A32::UserCallbacks::MemoryWrite>(ctx, inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32EmitX64::EmitA32WriteMemory16(A32EmitContext& ctx, IR::Inst* inst) {
|
void A32EmitX64::EmitA32WriteMemory16(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
EmitMemoryWrite<16, &A32::UserCallbacks::MemoryWrite16>(ctx, inst);
|
EmitMemoryWrite<16, &A32::UserCallbacks::MemoryWrite>(ctx, inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32EmitX64::EmitA32WriteMemory32(A32EmitContext& ctx, IR::Inst* inst) {
|
void A32EmitX64::EmitA32WriteMemory32(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
EmitMemoryWrite<32, &A32::UserCallbacks::MemoryWrite32>(ctx, inst);
|
EmitMemoryWrite<32, &A32::UserCallbacks::MemoryWrite>(ctx, inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32EmitX64::EmitA32WriteMemory64(A32EmitContext& ctx, IR::Inst* inst) {
|
void A32EmitX64::EmitA32WriteMemory64(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
EmitMemoryWrite<64, &A32::UserCallbacks::MemoryWrite64>(ctx, inst);
|
EmitMemoryWrite<64, &A32::UserCallbacks::MemoryWrite>(ctx, inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32EmitX64::EmitA32ClearExclusive(A32EmitContext&, IR::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) {
|
void A32EmitX64::EmitA32ExclusiveReadMemory8(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
if (conf.fastmem_exclusive_access) {
|
if (conf.fastmem_exclusive_access) {
|
||||||
EmitExclusiveReadMemoryInline<8, &A32::UserCallbacks::MemoryRead8>(ctx, inst);
|
EmitExclusiveReadMemoryInline<8, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||||
} else {
|
} else {
|
||||||
EmitExclusiveReadMemory<8, &A32::UserCallbacks::MemoryRead8>(ctx, inst);
|
EmitExclusiveReadMemory<8, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32EmitX64::EmitA32ExclusiveReadMemory16(A32EmitContext& ctx, IR::Inst* inst) {
|
void A32EmitX64::EmitA32ExclusiveReadMemory16(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
if (conf.fastmem_exclusive_access) {
|
if (conf.fastmem_exclusive_access) {
|
||||||
EmitExclusiveReadMemoryInline<16, &A32::UserCallbacks::MemoryRead16>(ctx, inst);
|
EmitExclusiveReadMemoryInline<16, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||||
} else {
|
} else {
|
||||||
EmitExclusiveReadMemory<16, &A32::UserCallbacks::MemoryRead16>(ctx, inst);
|
EmitExclusiveReadMemory<16, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32EmitX64::EmitA32ExclusiveReadMemory32(A32EmitContext& ctx, IR::Inst* inst) {
|
void A32EmitX64::EmitA32ExclusiveReadMemory32(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
if (conf.fastmem_exclusive_access) {
|
if (conf.fastmem_exclusive_access) {
|
||||||
EmitExclusiveReadMemoryInline<32, &A32::UserCallbacks::MemoryRead32>(ctx, inst);
|
EmitExclusiveReadMemoryInline<32, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||||
} else {
|
} else {
|
||||||
EmitExclusiveReadMemory<32, &A32::UserCallbacks::MemoryRead32>(ctx, inst);
|
EmitExclusiveReadMemory<32, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32EmitX64::EmitA32ExclusiveReadMemory64(A32EmitContext& ctx, IR::Inst* inst) {
|
void A32EmitX64::EmitA32ExclusiveReadMemory64(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
if (conf.fastmem_exclusive_access) {
|
if (conf.fastmem_exclusive_access) {
|
||||||
EmitExclusiveReadMemoryInline<64, &A32::UserCallbacks::MemoryRead64>(ctx, inst);
|
EmitExclusiveReadMemoryInline<64, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||||
} else {
|
} else {
|
||||||
EmitExclusiveReadMemory<64, &A32::UserCallbacks::MemoryRead64>(ctx, inst);
|
EmitExclusiveReadMemory<64, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -115,16 +115,16 @@ void A64EmitX64::GenMemory128Accessors() {
|
|||||||
void A64EmitX64::GenFastmemFallbacks() {
|
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::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{{
|
const std::array<std::pair<size_t, ArgCallback>, 4> read_callbacks{{
|
||||||
{8, Devirtualize<&A64::UserCallbacks::MemoryRead8>(conf.callbacks)},
|
{8, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)},
|
||||||
{16, Devirtualize<&A64::UserCallbacks::MemoryRead16>(conf.callbacks)},
|
{16, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)},
|
||||||
{32, Devirtualize<&A64::UserCallbacks::MemoryRead32>(conf.callbacks)},
|
{32, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)},
|
||||||
{64, Devirtualize<&A64::UserCallbacks::MemoryRead64>(conf.callbacks)},
|
{64, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)},
|
||||||
}};
|
}};
|
||||||
const std::array<std::pair<size_t, ArgCallback>, 4> write_callbacks{{
|
const std::array<std::pair<size_t, ArgCallback>, 4> write_callbacks{{
|
||||||
{8, Devirtualize<&A64::UserCallbacks::MemoryWrite8>(conf.callbacks)},
|
{8, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)},
|
||||||
{16, Devirtualize<&A64::UserCallbacks::MemoryWrite16>(conf.callbacks)},
|
{16, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)},
|
||||||
{32, Devirtualize<&A64::UserCallbacks::MemoryWrite32>(conf.callbacks)},
|
{32, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)},
|
||||||
{64, Devirtualize<&A64::UserCallbacks::MemoryWrite64>(conf.callbacks)},
|
{64, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)},
|
||||||
}};
|
}};
|
||||||
const std::array<std::pair<size_t, ArgCallback>, 4> exclusive_write_callbacks{{
|
const std::array<std::pair<size_t, ArgCallback>, 4> exclusive_write_callbacks{{
|
||||||
{8, Devirtualize<&A64::UserCallbacks::MemoryWriteExclusive8>(conf.callbacks)},
|
{8, Devirtualize<&A64::UserCallbacks::MemoryWriteExclusive8>(conf.callbacks)},
|
||||||
@@ -204,12 +204,12 @@ void A64EmitX64::GenFastmemFallbacks() {
|
|||||||
code.align();
|
code.align();
|
||||||
read_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>();
|
read_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>();
|
||||||
ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocRegIdx(value_idx));
|
ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocRegIdx(value_idx));
|
||||||
|
// params = { this, vaddr, size }
|
||||||
if (vaddr_idx != code.ABI_PARAM2.getIdx()) {
|
if (vaddr_idx != code.ABI_PARAM2.getIdx()) {
|
||||||
code.mov(code.ABI_PARAM2, Xbyak::Reg64{vaddr_idx});
|
code.mov(code.ABI_PARAM2, Xbyak::Reg64{vaddr_idx});
|
||||||
}
|
}
|
||||||
if (ordered) {
|
code.mov(code.ABI_PARAM3, bitsize / CHAR_BIT);
|
||||||
code.mfence();
|
if (ordered) code.mfence();
|
||||||
}
|
|
||||||
callback.EmitCall(code);
|
callback.EmitCall(code);
|
||||||
if (value_idx != code.ABI_RETURN.getIdx()) {
|
if (value_idx != code.ABI_RETURN.getIdx()) {
|
||||||
code.mov(Xbyak::Reg64{value_idx}, code.ABI_RETURN);
|
code.mov(Xbyak::Reg64{value_idx}, code.ABI_RETURN);
|
||||||
@@ -224,6 +224,7 @@ void A64EmitX64::GenFastmemFallbacks() {
|
|||||||
code.align();
|
code.align();
|
||||||
write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>();
|
write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>();
|
||||||
ABI_PushCallerSaveRegistersAndAdjustStack(code);
|
ABI_PushCallerSaveRegistersAndAdjustStack(code);
|
||||||
|
// params = { this, vaddr, value, size }
|
||||||
if (vaddr_idx == code.ABI_PARAM3.getIdx() && value_idx == code.ABI_PARAM2.getIdx()) {
|
if (vaddr_idx == code.ABI_PARAM3.getIdx() && value_idx == code.ABI_PARAM2.getIdx()) {
|
||||||
code.xchg(code.ABI_PARAM2, code.ABI_PARAM3);
|
code.xchg(code.ABI_PARAM2, code.ABI_PARAM3);
|
||||||
} else if (vaddr_idx == code.ABI_PARAM3.getIdx()) {
|
} else if (vaddr_idx == code.ABI_PARAM3.getIdx()) {
|
||||||
@@ -240,10 +241,9 @@ void A64EmitX64::GenFastmemFallbacks() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
code.ZeroExtendFrom(bitsize, code.ABI_PARAM3);
|
code.ZeroExtendFrom(bitsize, code.ABI_PARAM3);
|
||||||
|
code.mov(code.ABI_PARAM4, bitsize / CHAR_BIT);
|
||||||
callback.EmitCall(code);
|
callback.EmitCall(code);
|
||||||
if (ordered) {
|
if (ordered) code.mfence();
|
||||||
code.mfence();
|
|
||||||
}
|
|
||||||
ABI_PopCallerSaveRegistersAndAdjustStack(code);
|
ABI_PopCallerSaveRegistersAndAdjustStack(code);
|
||||||
code.ret();
|
code.ret();
|
||||||
PerfMapRegister(write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)], code.getCurr(), fmt::format("a64_write_fallback_{}", bitsize));
|
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
|
#undef Axx
|
||||||
|
|
||||||
void A64EmitX64::EmitA64ReadMemory8(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64ReadMemory8(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
EmitMemoryRead<8, &A64::UserCallbacks::MemoryRead8>(ctx, inst);
|
EmitMemoryRead<8, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void A64EmitX64::EmitA64ReadMemory16(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64ReadMemory16(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
EmitMemoryRead<16, &A64::UserCallbacks::MemoryRead16>(ctx, inst);
|
EmitMemoryRead<16, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void A64EmitX64::EmitA64ReadMemory32(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64ReadMemory32(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
EmitMemoryRead<32, &A64::UserCallbacks::MemoryRead32>(ctx, inst);
|
EmitMemoryRead<32, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void A64EmitX64::EmitA64ReadMemory64(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64ReadMemory64(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
EmitMemoryRead<64, &A64::UserCallbacks::MemoryRead64>(ctx, inst);
|
EmitMemoryRead<64, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void A64EmitX64::EmitA64ReadMemory128(A64EmitContext& ctx, IR::Inst* 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) {
|
void A64EmitX64::EmitA64WriteMemory8(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
EmitMemoryWrite<8, &A64::UserCallbacks::MemoryWrite8>(ctx, inst);
|
EmitMemoryWrite<8, &A64::UserCallbacks::MemoryWrite>(ctx, inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void A64EmitX64::EmitA64WriteMemory16(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64WriteMemory16(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
EmitMemoryWrite<16, &A64::UserCallbacks::MemoryWrite16>(ctx, inst);
|
EmitMemoryWrite<16, &A64::UserCallbacks::MemoryWrite>(ctx, inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void A64EmitX64::EmitA64WriteMemory32(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64WriteMemory32(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
EmitMemoryWrite<32, &A64::UserCallbacks::MemoryWrite32>(ctx, inst);
|
EmitMemoryWrite<32, &A64::UserCallbacks::MemoryWrite>(ctx, inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void A64EmitX64::EmitA64WriteMemory64(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64WriteMemory64(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
EmitMemoryWrite<64, &A64::UserCallbacks::MemoryWrite64>(ctx, inst);
|
EmitMemoryWrite<64, &A64::UserCallbacks::MemoryWrite>(ctx, inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void A64EmitX64::EmitA64WriteMemory128(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64WriteMemory128(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
EmitMemoryWrite<128, &A64::UserCallbacks::MemoryWrite64>(ctx, inst);
|
EmitMemoryWrite<128, &A64::UserCallbacks::MemoryWrite>(ctx, inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void A64EmitX64::EmitA64ClearExclusive(A64EmitContext&, IR::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) {
|
void A64EmitX64::EmitA64ExclusiveReadMemory8(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
if (conf.fastmem_exclusive_access) {
|
if (conf.fastmem_exclusive_access) {
|
||||||
EmitExclusiveReadMemoryInline<8, &A64::UserCallbacks::MemoryRead8>(ctx, inst);
|
EmitExclusiveReadMemoryInline<8, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||||
} else {
|
} else {
|
||||||
EmitExclusiveReadMemory<8, &A64::UserCallbacks::MemoryRead8>(ctx, inst);
|
EmitExclusiveReadMemory<8, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void A64EmitX64::EmitA64ExclusiveReadMemory16(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64ExclusiveReadMemory16(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
if (conf.fastmem_exclusive_access) {
|
if (conf.fastmem_exclusive_access) {
|
||||||
EmitExclusiveReadMemoryInline<16, &A64::UserCallbacks::MemoryRead16>(ctx, inst);
|
EmitExclusiveReadMemoryInline<16, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||||
} else {
|
} else {
|
||||||
EmitExclusiveReadMemory<16, &A64::UserCallbacks::MemoryRead16>(ctx, inst);
|
EmitExclusiveReadMemory<16, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void A64EmitX64::EmitA64ExclusiveReadMemory32(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64ExclusiveReadMemory32(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
if (conf.fastmem_exclusive_access) {
|
if (conf.fastmem_exclusive_access) {
|
||||||
EmitExclusiveReadMemoryInline<32, &A64::UserCallbacks::MemoryRead32>(ctx, inst);
|
EmitExclusiveReadMemoryInline<32, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||||
} else {
|
} else {
|
||||||
EmitExclusiveReadMemory<32, &A64::UserCallbacks::MemoryRead32>(ctx, inst);
|
EmitExclusiveReadMemory<32, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void A64EmitX64::EmitA64ExclusiveReadMemory64(A64EmitContext& ctx, IR::Inst* inst) {
|
void A64EmitX64::EmitA64ExclusiveReadMemory64(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
if (conf.fastmem_exclusive_access) {
|
if (conf.fastmem_exclusive_access) {
|
||||||
EmitExclusiveReadMemoryInline<64, &A64::UserCallbacks::MemoryRead64>(ctx, inst);
|
EmitExclusiveReadMemoryInline<64, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||||
} else {
|
} else {
|
||||||
EmitExclusiveReadMemory<64, &A64::UserCallbacks::MemoryRead64>(ctx, inst);
|
EmitExclusiveReadMemory<64, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
/* This file is part of the dynarmic project.
|
/* This file is part of the dynarmic project.
|
||||||
* Copyright (c) 2022 MerryMage
|
* Copyright (c) 2022 MerryMage
|
||||||
* SPDX-License-Identifier: 0BSD
|
* SPDX-License-Identifier: 0BSD
|
||||||
@@ -56,16 +59,13 @@ void AxxEmitX64::EmitMemoryRead(AxxEmitContext& ctx, IR::Inst* inst) {
|
|||||||
// Neither fastmem nor page table: Use callbacks
|
// Neither fastmem nor page table: Use callbacks
|
||||||
if constexpr (bitsize == 128) {
|
if constexpr (bitsize == 128) {
|
||||||
ctx.reg_alloc.HostCall(code, nullptr, {}, args[1]);
|
ctx.reg_alloc.HostCall(code, nullptr, {}, args[1]);
|
||||||
if (ordered) {
|
if (ordered) code.mfence();
|
||||||
code.mfence();
|
|
||||||
}
|
|
||||||
code.CallFunction(memory_read_128);
|
code.CallFunction(memory_read_128);
|
||||||
ctx.reg_alloc.DefineValue(code, inst, xmm1);
|
ctx.reg_alloc.DefineValue(code, inst, xmm1);
|
||||||
} else {
|
} else {
|
||||||
ctx.reg_alloc.HostCall(code, inst, {}, args[1]);
|
ctx.reg_alloc.HostCall(code, inst, {}, args[1]);
|
||||||
if (ordered) {
|
code.mov(code.ABI_PARAM3.cvt32(), bitsize / CHAR_BIT);
|
||||||
code.mfence();
|
if (ordered) code.mfence();
|
||||||
}
|
|
||||||
Devirtualize<callback>(conf.callbacks).EmitCall(code);
|
Devirtualize<callback>(conf.callbacks).EmitCall(code);
|
||||||
code.ZeroExtendFrom(bitsize, code.ABI_RETURN);
|
code.ZeroExtendFrom(bitsize, code.ABI_RETURN);
|
||||||
}
|
}
|
||||||
@@ -148,12 +148,12 @@ void AxxEmitX64::EmitMemoryWrite(AxxEmitContext& ctx, IR::Inst* inst) {
|
|||||||
ctx.reg_alloc.HostCall(code, nullptr);
|
ctx.reg_alloc.HostCall(code, nullptr);
|
||||||
code.CallFunction(memory_write_128);
|
code.CallFunction(memory_write_128);
|
||||||
} else {
|
} else {
|
||||||
|
// { this, vaddr, value, size }
|
||||||
ctx.reg_alloc.HostCall(code, nullptr, {}, args[1], args[2]);
|
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);
|
Devirtualize<callback>(conf.callbacks).EmitCall(code);
|
||||||
}
|
}
|
||||||
if (ordered) {
|
if (ordered) code.mfence();
|
||||||
code.mfence();
|
|
||||||
}
|
|
||||||
EmitCheckMemoryAbort(ctx, inst);
|
EmitCheckMemoryAbort(ctx, inst);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -230,12 +230,11 @@ void AxxEmitX64::EmitExclusiveReadMemory(AxxEmitContext& ctx, IR::Inst* inst) {
|
|||||||
if (ordered) {
|
if (ordered) {
|
||||||
code.mfence();
|
code.mfence();
|
||||||
}
|
}
|
||||||
code.CallLambda(
|
code.CallLambda([](AxxUserConfig& conf, Axx::VAddr vaddr) -> T {
|
||||||
[](AxxUserConfig& conf, Axx::VAddr vaddr) -> T {
|
return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T {
|
||||||
return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T {
|
return (conf.callbacks->*callback)(vaddr, bitsize / CHAR_BIT);
|
||||||
return (conf.callbacks->*callback)(vaddr);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
code.ZeroExtendFrom(bitsize, code.ABI_RETURN);
|
code.ZeroExtendFrom(bitsize, code.ABI_RETURN);
|
||||||
} else {
|
} else {
|
||||||
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(code);
|
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(code);
|
||||||
@@ -250,12 +249,11 @@ void AxxEmitX64::EmitExclusiveReadMemory(AxxEmitContext& ctx, IR::Inst* inst) {
|
|||||||
if (ordered) {
|
if (ordered) {
|
||||||
code.mfence();
|
code.mfence();
|
||||||
}
|
}
|
||||||
code.CallLambda(
|
code.CallLambda([](AxxUserConfig& conf, Axx::VAddr vaddr, Vector& ret) {
|
||||||
[](AxxUserConfig& conf, Axx::VAddr vaddr, Vector& ret) {
|
ret = conf.global_monitor->ReadAndMark<Vector>(conf.processor_id, vaddr, [&]() -> Vector {
|
||||||
ret = conf.global_monitor->ReadAndMark<Vector>(conf.processor_id, vaddr, [&]() -> Vector {
|
return (conf.callbacks->*callback)(vaddr);
|
||||||
return (conf.callbacks->*callback)(vaddr);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
code.movups(result, xword[rsp + ABI_SHADOW_SPACE]);
|
code.movups(result, xword[rsp + ABI_SHADOW_SPACE]);
|
||||||
ctx.reg_alloc.ReleaseStackSpace(code, 16 + 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.shl(tmp, int(ctx.conf.page_table_log2_stride));
|
||||||
code.mov(page, qword[r14 + tmp.cvt64()]);
|
code.mov(page, qword[r14 + tmp.cvt64()]);
|
||||||
} else {
|
} else {
|
||||||
code.mov(page, qword[r14 + tmp.cvt64() * int(ctx.conf.page_table_log2_stride)]);
|
code.mov(page, qword[r14 + tmp.cvt64() * int(1 << ctx.conf.page_table_log2_stride)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for marked bit, use as unmapped if marked
|
// check for marked bit, use as unmapped if marked
|
||||||
@@ -161,8 +161,12 @@ template<>
|
|||||||
code.jnz(abort, code.T_NEAR);
|
code.jnz(abort, code.T_NEAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
code.shl(tmp, int(ctx.conf.page_table_log2_stride));
|
if (ctx.conf.page_table_log2_stride > 3) {
|
||||||
code.mov(page, qword[r14 + tmp]);
|
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)]);
|
||||||
|
}
|
||||||
|
|
||||||
// check for marked bit, use as unmapped if marked
|
// check for marked bit, use as unmapped if marked
|
||||||
if (ctx.conf.page_table_marked_bit) {
|
if (ctx.conf.page_table_marked_bit) {
|
||||||
@@ -178,6 +182,7 @@ template<>
|
|||||||
code.mov(tmp, ctx.conf.page_table_pointer_mask);
|
code.mov(tmp, ctx.conf.page_table_pointer_mask);
|
||||||
code.and_(page, tmp);
|
code.and_(page, tmp);
|
||||||
}
|
}
|
||||||
|
// check for sign bit, apply sign extension as needed
|
||||||
if (ctx.conf.page_table_sign_extension) {
|
if (ctx.conf.page_table_sign_extension) {
|
||||||
code.shl(page, *ctx.conf.page_table_sign_extension);
|
code.shl(page, *ctx.conf.page_table_sign_extension);
|
||||||
code.sar(page, *ctx.conf.page_table_sign_extension);
|
code.sar(page, *ctx.conf.page_table_sign_extension);
|
||||||
|
|||||||
@@ -66,7 +66,9 @@ struct UserCallbacks : public TranslateCallbacks {
|
|||||||
|
|
||||||
// All reads through this callback are 4-byte aligned.
|
// All reads through this callback are 4-byte aligned.
|
||||||
// Memory must be interpreted as little endian.
|
// Memory must be interpreted as little endian.
|
||||||
std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) override { return MemoryRead32(vaddr); }
|
std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) override {
|
||||||
|
return std::uint32_t(MemoryRead(vaddr, sizeof(std::uint32_t)));
|
||||||
|
}
|
||||||
|
|
||||||
// This function is called before the instruction at pc is read.
|
// This function is called before the instruction at pc is read.
|
||||||
// IR code can be emitted by the callee prior to instruction handling.
|
// IR code can be emitted by the callee prior to instruction handling.
|
||||||
@@ -80,16 +82,10 @@ struct UserCallbacks : public TranslateCallbacks {
|
|||||||
|
|
||||||
// Reads through these callbacks may not be aligned.
|
// Reads through these callbacks may not be aligned.
|
||||||
// Memory must be interpreted as if ENDIANSTATE == 0, endianness will be corrected by the JIT.
|
// Memory must be interpreted as if ENDIANSTATE == 0, endianness will be corrected by the JIT.
|
||||||
virtual std::uint8_t MemoryRead8(VAddr vaddr) = 0;
|
virtual std::uint64_t MemoryRead(VAddr vaddr, std::size_t size) = 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.
|
// Writes through these callbacks may not be aligned.
|
||||||
virtual void MemoryWrite8(VAddr vaddr, std::uint8_t value) = 0;
|
virtual void MemoryWrite(VAddr vaddr, std::uint64_t value, std::size_t size) = 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.
|
// Writes through these callbacks may not be aligned.
|
||||||
virtual bool MemoryWriteExclusive8(VAddr /*vaddr*/, std::uint8_t /*value*/, std::uint8_t /*expected*/) { return false; }
|
virtual bool MemoryWriteExclusive8(VAddr /*vaddr*/, std::uint8_t /*value*/, std::uint8_t /*expected*/) { return false; }
|
||||||
|
|||||||
@@ -89,20 +89,16 @@ struct UserCallbacks {
|
|||||||
|
|
||||||
// All reads through this callback are 4-byte aligned.
|
// All reads through this callback are 4-byte aligned.
|
||||||
// Memory must be interpreted as little endian.
|
// Memory must be interpreted as little endian.
|
||||||
virtual std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) { return MemoryRead32(vaddr); }
|
virtual std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) {
|
||||||
|
return std::uint32_t(MemoryRead(vaddr, sizeof(std::uint32_t)));
|
||||||
|
}
|
||||||
|
|
||||||
// Reads through these callbacks may not be aligned.
|
// Reads through these callbacks may not be aligned.
|
||||||
virtual std::uint8_t MemoryRead8(VAddr vaddr) = 0;
|
virtual std::uint64_t MemoryRead(VAddr vaddr, std::size_t size) = 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;
|
virtual Vector MemoryRead128(VAddr vaddr) = 0;
|
||||||
|
|
||||||
// Writes through these callbacks may not be aligned.
|
// Writes through these callbacks may not be aligned.
|
||||||
virtual void MemoryWrite8(VAddr vaddr, std::uint8_t value) = 0;
|
virtual void MemoryWrite(VAddr vaddr, std::uint64_t value, std::size_t size) = 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;
|
virtual void MemoryWrite128(VAddr vaddr, Vector value) = 0;
|
||||||
|
|
||||||
// Writes through these callbacks may not be aligned.
|
// Writes through these callbacks may not be aligned.
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ static void ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) {
|
|||||||
if (inst.AreAllArgsImmediates()) {
|
if (inst.AreAllArgsImmediates()) {
|
||||||
const u32 vaddr = inst.GetArg(1).GetU32();
|
const u32 vaddr = inst.GetArg(1).GetU32();
|
||||||
if (cb->IsReadOnlyMemory(vaddr)) {
|
if (cb->IsReadOnlyMemory(vaddr)) {
|
||||||
const u8 value_from_memory = cb->MemoryRead8(vaddr);
|
const u8 value_from_memory = u8(cb->MemoryRead(vaddr, sizeof(u8)));
|
||||||
inst.ReplaceUsesWith(IR::Value{value_from_memory});
|
inst.ReplaceUsesWith(IR::Value{value_from_memory});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -51,7 +51,7 @@ static void ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) {
|
|||||||
if (inst.AreAllArgsImmediates()) {
|
if (inst.AreAllArgsImmediates()) {
|
||||||
const u32 vaddr = inst.GetArg(1).GetU32();
|
const u32 vaddr = inst.GetArg(1).GetU32();
|
||||||
if (cb->IsReadOnlyMemory(vaddr)) {
|
if (cb->IsReadOnlyMemory(vaddr)) {
|
||||||
const u16 value_from_memory = cb->MemoryRead16(vaddr);
|
const u16 value_from_memory = u16(cb->MemoryRead(vaddr, sizeof(u16)));
|
||||||
inst.ReplaceUsesWith(IR::Value{value_from_memory});
|
inst.ReplaceUsesWith(IR::Value{value_from_memory});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -62,7 +62,7 @@ static void ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) {
|
|||||||
if (inst.AreAllArgsImmediates()) {
|
if (inst.AreAllArgsImmediates()) {
|
||||||
const u32 vaddr = inst.GetArg(1).GetU32();
|
const u32 vaddr = inst.GetArg(1).GetU32();
|
||||||
if (cb->IsReadOnlyMemory(vaddr)) {
|
if (cb->IsReadOnlyMemory(vaddr)) {
|
||||||
const u32 value_from_memory = cb->MemoryRead32(vaddr);
|
const u32 value_from_memory = u32(cb->MemoryRead(vaddr, sizeof(u32)));
|
||||||
inst.ReplaceUsesWith(IR::Value{value_from_memory});
|
inst.ReplaceUsesWith(IR::Value{value_from_memory});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,7 +73,7 @@ static void ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) {
|
|||||||
if (inst.AreAllArgsImmediates()) {
|
if (inst.AreAllArgsImmediates()) {
|
||||||
const u32 vaddr = inst.GetArg(1).GetU32();
|
const u32 vaddr = inst.GetArg(1).GetU32();
|
||||||
if (cb->IsReadOnlyMemory(vaddr)) {
|
if (cb->IsReadOnlyMemory(vaddr)) {
|
||||||
const u64 value_from_memory = cb->MemoryRead64(vaddr);
|
const u64 value_from_memory = u64(cb->MemoryRead(vaddr, sizeof(u64)));
|
||||||
inst.ReplaceUsesWith(IR::Value{value_from_memory});
|
inst.ReplaceUsesWith(IR::Value{value_from_memory});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -503,7 +503,7 @@ TEST_CASE("Fuzz Thumb32 instructions set", "[JitX64][Thumb][Thumb32]") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Verify fix for off by one error in MemoryRead32 worked", "[Thumb][Thumb16]") {
|
TEST_CASE("Verify fix for off by one error in MemoryRead<32> worked", "[Thumb][Thumb16]") {
|
||||||
ThumbTestEnv test_env;
|
ThumbTestEnv test_env;
|
||||||
|
|
||||||
// Prepare test subjects
|
// Prepare test subjects
|
||||||
|
|||||||
@@ -553,9 +553,9 @@ TEST_CASE("arm: Memory access (fastmem)", "[arm][A32]") {
|
|||||||
memset(backing_memory, 0, memory_size);
|
memset(backing_memory, 0, memory_size);
|
||||||
memcpy(backing_memory + 0x100, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", 57);
|
memcpy(backing_memory + 0x100, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", 57);
|
||||||
|
|
||||||
env.MemoryWrite32(0, 0xE5904000); // LDR R4, [R0]
|
env.MemoryWrite(0, 0xE5904000, sizeof(u32)); // LDR R4, [R0]
|
||||||
env.MemoryWrite32(4, 0xE5814000); // STR R4, [R1]
|
env.MemoryWrite(4, 0xE5814000, sizeof(u32)); // STR R4, [R1]
|
||||||
env.MemoryWrite32(8, 0xEAFFFFFE); // B .
|
env.MemoryWrite(8, 0xEAFFFFFE, sizeof(u32)); // B .
|
||||||
jit.Regs()[0] = 0x100;
|
jit.Regs()[0] = 0x100;
|
||||||
jit.Regs()[1] = 0x1F0;
|
jit.Regs()[1] = 0x1F0;
|
||||||
jit.Regs()[15] = 0; // PC = 0
|
jit.Regs()[15] = 0; // PC = 0
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "dynarmic/frontend/A32/translate/translate_callbacks.h"
|
||||||
#include "dynarmic/interface/A32/a32.h"
|
#include "dynarmic/interface/A32/a32.h"
|
||||||
|
|
||||||
template<typename InstructionType_, u32 infinite_loop_u32>
|
template<typename InstructionType_, u32 infinite_loop_u32>
|
||||||
@@ -59,42 +60,51 @@ public:
|
|||||||
return infinite_loop_u32; // B .
|
return infinite_loop_u32; // B .
|
||||||
}
|
}
|
||||||
|
|
||||||
std::uint8_t MemoryRead8(u32 vaddr) override {
|
u64 MemoryRead(u32 vaddr, size_t size) override {
|
||||||
if (IsInCodeMem(vaddr)) {
|
switch (size) {
|
||||||
return reinterpret_cast<u8*>(code_mem.data())[vaddr];
|
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);
|
||||||
}
|
}
|
||||||
if (auto iter = modified_memory.find(vaddr); iter != modified_memory.end()) {
|
default:
|
||||||
return iter->second;
|
std::abort();
|
||||||
}
|
}
|
||||||
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 MemoryWrite8(u32 vaddr, std::uint8_t value) override {
|
void MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) override {
|
||||||
if (vaddr < code_mem.size() * sizeof(u32)) {
|
switch (size) {
|
||||||
code_mem_modified_by_guest = true;
|
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();
|
||||||
}
|
}
|
||||||
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 {
|
void CallSVC(std::uint32_t swi) override {
|
||||||
@@ -143,46 +153,41 @@ public:
|
|||||||
return read<std::uint32_t>(vaddr);
|
return read<std::uint32_t>(vaddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::uint8_t MemoryRead8(std::uint32_t vaddr) override {
|
u64 MemoryRead(u32 vaddr, size_t size) override {
|
||||||
return read<std::uint8_t>(vaddr);
|
switch (size) {
|
||||||
}
|
case sizeof(u64): return read<u64>(vaddr);
|
||||||
std::uint16_t MemoryRead16(std::uint32_t vaddr) override {
|
case sizeof(u32): return read<u32>(vaddr);
|
||||||
return read<std::uint16_t>(vaddr);
|
case sizeof(u16): return read<u16>(vaddr);
|
||||||
}
|
case sizeof(u8): return read<u8>(vaddr);
|
||||||
std::uint32_t MemoryRead32(std::uint32_t vaddr) override {
|
default:
|
||||||
return read<std::uint32_t>(vaddr);
|
std::abort();
|
||||||
}
|
}
|
||||||
std::uint64_t MemoryRead64(std::uint32_t vaddr) override {
|
|
||||||
return read<std::uint64_t>(vaddr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemoryWrite8(std::uint32_t vaddr, std::uint8_t value) override {
|
void MemoryWrite(Dynarmic::A32::VAddr vaddr, std::uint64_t value, size_t size) override {
|
||||||
write(vaddr, value);
|
switch (size) {
|
||||||
}
|
case sizeof(u64): return write<u64>(vaddr, u64(value));
|
||||||
void MemoryWrite16(std::uint32_t vaddr, std::uint16_t value) override {
|
case sizeof(u32): return write<u32>(vaddr, u32(value));
|
||||||
write(vaddr, value);
|
case sizeof(u16): return write<u16>(vaddr, u16(value));
|
||||||
}
|
case sizeof(u8): return write<u8>(vaddr, u8(value));
|
||||||
void MemoryWrite32(std::uint32_t vaddr, std::uint32_t value) override {
|
default: std::abort();
|
||||||
write(vaddr, value);
|
}
|
||||||
}
|
|
||||||
void MemoryWrite64(std::uint32_t vaddr, std::uint64_t value) override {
|
|
||||||
write(vaddr, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MemoryWriteExclusive8(std::uint32_t vaddr, std::uint8_t value, [[maybe_unused]] std::uint8_t expected) override {
|
bool MemoryWriteExclusive8(Dynarmic::A32::VAddr vaddr, std::uint8_t value, [[maybe_unused]] std::uint8_t expected) override {
|
||||||
MemoryWrite8(vaddr, value);
|
MemoryWrite(vaddr, value, sizeof(u8));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool MemoryWriteExclusive16(std::uint32_t vaddr, std::uint16_t value, [[maybe_unused]] std::uint16_t expected) override {
|
bool MemoryWriteExclusive16(Dynarmic::A32::VAddr vaddr, std::uint16_t value, [[maybe_unused]] std::uint16_t expected) override {
|
||||||
MemoryWrite16(vaddr, value);
|
MemoryWrite(vaddr, value, sizeof(u16));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool MemoryWriteExclusive32(std::uint32_t vaddr, std::uint32_t value, [[maybe_unused]] std::uint32_t expected) override {
|
bool MemoryWriteExclusive32(Dynarmic::A32::VAddr vaddr, std::uint32_t value, [[maybe_unused]] std::uint32_t expected) override {
|
||||||
MemoryWrite32(vaddr, value);
|
MemoryWrite(vaddr, value, sizeof(u32));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool MemoryWriteExclusive64(std::uint32_t vaddr, std::uint64_t value, [[maybe_unused]] std::uint64_t expected) override {
|
bool MemoryWriteExclusive64(Dynarmic::A32::VAddr vaddr, std::uint64_t value, [[maybe_unused]] std::uint64_t expected) override {
|
||||||
MemoryWrite64(vaddr, value);
|
MemoryWrite(vaddr, value, sizeof(u64));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user