Compare commits

..

3 Commits

Author SHA1 Message Date
lizzie 83fb1b8d9e aaaaaaaaaaarch 2026-09-01 10:09:53 +00:00
lizzie 8524a12f65 fix arm 2026-09-01 10:09:53 +00:00
lizzie 284ef78a8a [dynarmic] Coalesce Write/Read fallback functions
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-01 10:09:47 +00:00
80 changed files with 2935 additions and 2752 deletions
-3
View File
@@ -63,6 +63,3 @@ artifacts
/install*
vulkansdk*.exe
*.tar.zst
# Build-time generated external sources
externals/generated
+3 -3
View File
@@ -214,6 +214,8 @@ if(MSVC)
endif()
# TODO(crueter): Cleanup, each dep that has a bundled option should allow to choose between bundled, external, system
cmake_dependent_option(YUZU_USE_BUNDLED_SDL3 "Download bundled SDL3 build" "${MSVC}" "NOT ANDROID" OFF)
option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
set(EXT_DEFAULT OFF)
@@ -221,8 +223,6 @@ if (MSVC OR ANDROID)
set(EXT_DEFAULT ON)
endif()
option(YUZU_USE_BUNDLED_SDL3 "Download bundled SDL3 build" ${EXT_DEFAULT})
# ffmpeg
option(YUZU_USE_BUNDLED_FFMPEG "Download bundled FFmpeg" ${EXT_DEFAULT})
cmake_dependent_option(YUZU_USE_EXTERNAL_FFMPEG "Build FFmpeg from external source" "${SOLARIS}" "NOT WIN32 AND NOT ANDROID" OFF)
@@ -238,7 +238,7 @@ option(YUZU_USE_BUNDLED_SIRIT "Download bundled sirit" ${BUNDLED_SIRIT_DEFAULT})
# FreeBSD 15+ has libusb, versions below should disable it
cmake_dependent_option(ENABLE_LIBUSB "Enable the use of LibUSB" ON "WIN32 OR LINUX OR FREEBSD OR APPLE" OFF)
cmake_dependent_option(ENABLE_OPENGL "Enable OpenGL" ON "NOT (WIN32 AND ARCHITECTURE_arm64) AND NOT APPLE AND NOT ANDROID" OFF)
cmake_dependent_option(ENABLE_OPENGL "Enable OpenGL" ON "NOT (WIN32 AND ARCHITECTURE_arm64) AND NOT APPLE" OFF)
mark_as_advanced(FORCE ENABLE_OPENGL)
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
+6
View File
@@ -186,6 +186,12 @@
"repo": "eden-emulator/oaknut",
"version": "v2.0.3"
},
"oboe": {
"bundled": true,
"hash": "ce4011afe7345370d4ead3b891cd69a5ef224b129535783586c0ca75051d303ed446e6c7f10bde8da31fff58d6e307f1732a3ffd03b249f9ef1fd48fd4132715",
"repo": "google/oboe",
"version": "1.10.0"
},
"openssl": {
"hash": "29002ce50cb95a4f4f1d0e9d3f684401fbd4eac34203dc2eef3b6334af5d44aa46bf788b63a6f5c139c383eafb7269ae87a58a9a3ad5912903b9773e545ccc0a",
"min_version": "3.0.0",
+1 -1
View File
@@ -76,7 +76,7 @@ The following options are desktop only.
- `ENABLE_LIBUSB` (ON) Enable the use of the libusb input backend (HIGHLY RECOMMENDED)
- `ENABLE_OPENGL` (ON) Enable the OpenGL graphics backend
- Unavailable on Windows/ARM64 and on Android
- Unavailable on Windows/ARM64
- You probably shouldn't turn this off.
### Qt
+43 -37
View File
@@ -136,50 +136,49 @@ if(ENABLE_CUBEB)
endif()
endif()
if (NOT YUZU_USE_BUNDLED_SDL3)
if (NOT WIN32)
# Yuzu itself needs: Atomic Audio Events Joystick Haptic Sensor Threads Timers
# Since 2.0.18 Atomic+Threads required for HIDAPI/libusb (see https://github.com/libsdl-org/SDL/issues/5095)
# Yuzu-cmd also needs: Video (depends on Loadso/Dlopen)
# CPUinfo also required for SDL Audio, at least until 2.28.0 (see https://github.com/libsdl-org/SDL/issues/7809)
set(SDL_UNUSED_SUBSYSTEMS
File Filesystem
Locale Power Render)
foreach(_SUB ${SDL_UNUSED_SUBSYSTEMS})
string(TOUPPER ${_SUB} _OPT)
set(SDL_${_OPT} OFF)
endforeach()
if (NOT ANDROID)
if (NOT YUZU_USE_BUNDLED_SDL3)
if (NOT WIN32)
# Yuzu itself needs: Atomic Audio Events Joystick Haptic Sensor Threads Timers
# Since 2.0.18 Atomic+Threads required for HIDAPI/libusb (see https://github.com/libsdl-org/SDL/issues/5095)
# Yuzu-cmd also needs: Video (depends on Loadso/Dlopen)
# CPUinfo also required for SDL Audio, at least until 2.28.0 (see https://github.com/libsdl-org/SDL/issues/7809)
set(SDL_UNUSED_SUBSYSTEMS
File Filesystem
Locale Power Render)
foreach(_SUB ${SDL_UNUSED_SUBSYSTEMS})
string(TOUPPER ${_SUB} _OPT)
set(SDL_${_OPT} OFF)
endforeach()
set(HIDAPI ON)
endif()
set(HIDAPI ON)
endif()
if (APPLE)
set(SDL_FILE ON)
endif()
if (APPLE)
set(SDL_FILE ON)
endif()
AddJsonPackage(sdl3)
else()
message(STATUS "Using bundled SDL3")
AddJsonPackage(sdl3-ci)
# copy sdl java sources for consumption by Gradle
if (ANDROID)
file(COPY ${SDL3_SOURCE_DIR}/java DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/generated/sdl)
endif()
endif()
# Normalize SDL3 link target across package variants.
# Some SDL3 packages export only SDL3::SDL3-shared or SDL3::SDL3-static.
if (NOT TARGET SDL3::SDL3)
if (TARGET SDL3::SDL3-shared)
add_library(SDL3::SDL3 ALIAS SDL3::SDL3-shared)
elseif (TARGET SDL3::SDL3-static)
add_library(SDL3::SDL3 ALIAS SDL3::SDL3-static)
AddJsonPackage(sdl3)
else()
message(FATAL_ERROR "SDL3 package found, but no usable SDL3 target was exported")
message(STATUS "Using bundled SDL3")
AddJsonPackage(sdl3-ci)
endif()
# Normalize SDL3 link target across package variants.
# Some SDL3 packages export only SDL3::SDL3-shared or SDL3::SDL3-static.
if (NOT TARGET SDL3::SDL3)
if (TARGET SDL3::SDL3-shared)
add_library(SDL3::SDL3 ALIAS SDL3::SDL3-shared)
elseif (TARGET SDL3::SDL3-static)
add_library(SDL3::SDL3 ALIAS SDL3::SDL3-static)
else()
message(FATAL_ERROR "SDL3 package found, but no usable SDL3 target was exported")
endif()
endif()
endif()
set(BUILD_SHARED_LIBS OFF)
# SPIRV Headers
AddJsonPackage(spirv-headers)
@@ -386,6 +385,13 @@ if (YUZU_CRASH_DUMPS AND NOT TARGET libbreakpad_client)
endif()
endif()
# oboe
if (ANDROID)
AddJsonPackage(oboe)
add_library(oboe::oboe ALIAS oboe)
endif()
if (APPLE)
# moltenvk
if (NOT YUZU_USE_BUNDLED_MOLTENVK)
-7
View File
@@ -272,13 +272,6 @@ android {
resValue("string", "app_name_suffixed", "$currentName$suffix")
resValue("string", "app_name", "Eden$suffix")
}
sourceSets {
named("main") {
java.srcDir("${edenDir}/externals/generated/sdl/java")
kotlin.srcDir("${edenDir}/externals/generated/sdl/java")
}
}
}
idea {
-3
View File
@@ -9,9 +9,6 @@
-keep class org.ini4j.spi.IniBuilder
-keep class org.ini4j.spi.IniFormatter
-keep class org.libsdl.app.** { *; }
-keepclassmembers class org.libsdl.app.** { *; }
# Suppress warnings for R8
-dontwarn org.bouncycastle.jsse.BCSSLParameters
-dontwarn org.bouncycastle.jsse.BCSSLSocket
@@ -16,7 +16,6 @@ import android.widget.TextView
import androidx.annotation.Keep
import androidx.core.net.toUri
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.libsdl.app.SDL
import java.lang.ref.WeakReference
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.fragments.CoreErrorDialogFragment
@@ -53,8 +52,6 @@ object NativeLibrary {
init {
try {
System.loadLibrary("yuzu-android")
SDL.setupJNI()
initJvm()
} catch (ex: UnsatisfiedLinkError) {
error("[NativeLibrary] $ex")
}
@@ -370,7 +367,6 @@ object NativeLibrary {
NetPlayManager.clearChat()
}
external fun initJvm()
external fun initMultiplayer()
@Keep
+1 -1
View File
@@ -22,7 +22,7 @@ add_library(yuzu-android SHARED
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
target_link_libraries(yuzu-android PRIVATE audio_core common core input_common frontend_common video_core)
target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad jnigraphics log GLESv2)
target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad jnigraphics log)
if (ARCHITECTURE_arm64)
target_link_libraries(yuzu-android PRIVATE adrenotools)
endif()
-9
View File
@@ -1730,15 +1730,6 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_loadAmiibo(JNIEnv* env, jobject jobj,
return static_cast<jint>(info);
}
extern "C" JNIEXPORT void JNICALL
Java_org_yuzu_yuzu_1emu_NativeLibrary_initJvm(JNIEnv *env, jclass clazz) {
JavaVM *vm;
if (env->GetJavaVM(&vm) != JNI_OK)
return;
Common::Android::Initialize(vm, env);
}
JNIEXPORT void JNICALL
Java_org_yuzu_yuzu_1emu_NativeLibrary_initMultiplayer(
JNIEnv* env, [[maybe_unused]] jobject obj) {
@@ -800,6 +800,7 @@
<string name="theme_mode_dark">تاریک</string>
<!-- Audio output engines -->
<string name="oboe">oboe</string>
<string name="cubeb">cubeb</string>
<!-- Anisotropic filtering options -->
@@ -465,13 +465,13 @@
<string-array name="outputEngineEntries">
<item>@string/auto</item>
<item>@string/sdl3</item>
<item>@string/oboe</item>
<item>@string/cubeb</item>
<item>@string/string_null</item>
</string-array>
<integer-array name="outputEngineValues">
<item>0</item>
<item>2</item>
<item>4</item>
<item>1</item>
<item>3</item>
</integer-array>
@@ -1240,7 +1240,7 @@
<string name="theme_mode_dark">Dark</string>
<!-- Audio output engines -->
<string name="sdl3" translatable="false">SDL3</string>
<string name="oboe" translatable="false">oboe</string>
<string name="cubeb" translatable="false">cubeb</string>
<!-- Anisotropic filtering options -->
+14 -5
View File
@@ -238,11 +238,20 @@ if (ENABLE_CUBEB)
target_compile_definitions(audio_core PRIVATE HAVE_CUBEB=1)
endif()
target_sources(audio_core PRIVATE
sink/sdl3_sink.cpp
sink/sdl3_sink.h)
if(ANDROID)
target_sources(audio_core PRIVATE
sink/oboe_sink.cpp
sink/oboe_sink.h)
target_link_libraries(audio_core PRIVATE SDL3::SDL3)
target_compile_definitions(audio_core PRIVATE HAVE_SDL3)
target_link_libraries(audio_core PRIVATE oboe)
target_compile_definitions(audio_core PUBLIC HAVE_OBOE)
else()
target_sources(audio_core PRIVATE
sink/sdl3_sink.cpp
sink/sdl3_sink.h)
target_link_libraries(audio_core PRIVATE SDL3::SDL3)
target_compile_definitions(audio_core PRIVATE HAVE_SDL3)
endif()
create_target_directory_groups(audio_core)
+230
View File
@@ -0,0 +1,230 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <span>
#include <vector>
#include <oboe/Oboe.h>
#include "audio_core/common/common.h"
#include "audio_core/sink/oboe_sink.h"
#include "audio_core/sink/sink_stream.h"
#include "common/logging.h"
#include "common/scope_exit.h"
#include "core/core.h"
namespace AudioCore::Sink {
class OboeSinkStream final : public SinkStream,
public oboe::AudioStreamDataCallback,
public oboe::AudioStreamErrorCallback {
public:
explicit OboeSinkStream(Core::System& system_, StreamType type_, const std::string& name_,
u32 system_channels_)
: SinkStream(system_, type_) {
name = name_;
system_channels = system_channels_;
this->OpenStream();
}
~OboeSinkStream() override {
LOG_INFO(Audio_Sink, "Destroyed Oboe stream");
}
void Finalize() override {
this->Stop();
m_stream.reset();
}
void Start(bool resume = false) override {
if (!m_stream || !paused) {
return;
}
paused = false;
if (m_stream->start() != oboe::Result::OK) {
LOG_CRITICAL(Audio_Sink, "Error starting Oboe stream");
}
}
void Stop() override {
if (!m_stream || paused) {
return;
}
this->SignalPause();
if (m_stream->stop() != oboe::Result::OK) {
LOG_CRITICAL(Audio_Sink, "Error stopping Oboe stream");
}
}
public:
static s32 QueryChannelCount(oboe::Direction direction) {
std::shared_ptr<oboe::AudioStream> temp_stream;
oboe::AudioStreamBuilder builder;
const auto result = ConfigureBuilder(builder, direction)->openStream(temp_stream);
if (result == oboe::Result::OK) {
return temp_stream->getChannelCount() >= 6 ? 6 : 2;
}
LOG_ERROR(Audio_Sink, "Failed to open {} stream. Using default channel count 2",
direction == oboe::Direction::Output ? "output" : "input");
return 2;
}
protected:
oboe::DataCallbackResult onAudioReady(oboe::AudioStream*, void* audio_data,
s32 num_buffer_frames) override {
const size_t num_channels = this->GetDeviceChannels();
const size_t frame_size = num_channels;
const size_t num_frames = static_cast<size_t>(num_buffer_frames);
if (type == StreamType::In) {
std::span<const s16> input_buffer{reinterpret_cast<const s16*>(audio_data),
num_frames * frame_size};
this->ProcessAudioIn(input_buffer, num_frames);
} else {
std::span<s16> output_buffer{reinterpret_cast<s16*>(audio_data),
num_frames * frame_size};
this->ProcessAudioOutAndRender(output_buffer, num_frames);
}
return oboe::DataCallbackResult::Continue;
}
void onErrorAfterClose(oboe::AudioStream*, oboe::Result) override {
LOG_INFO(Audio_Sink, "Audio stream closed, reinitializing");
if (this->OpenStream()) {
m_stream->start();
}
}
private:
static oboe::AudioStreamBuilder* ConfigureBuilder(oboe::AudioStreamBuilder& builder,
oboe::Direction direction) {
// TODO: investigate callback delay issues when using AAudio
return builder.setPerformanceMode(oboe::PerformanceMode::LowLatency)
->setAudioApi(oboe::AudioApi::OpenSLES)
->setDirection(direction)
->setSampleRate(TargetSampleRate)
->setSampleRateConversionQuality(oboe::SampleRateConversionQuality::High)
->setFormat(oboe::AudioFormat::I16)
->setFormatConversionAllowed(true)
->setUsage(oboe::Usage::Game)
->setBufferCapacityInFrames(TargetSampleCount * 2);
}
bool OpenStream() {
const auto direction = [&]() {
switch (type) {
case StreamType::In:
return oboe::Direction::Input;
case StreamType::Out:
case StreamType::Render:
return oboe::Direction::Output;
default:
ASSERT(false);
return oboe::Direction::Output;
}
}();
const auto expected_channels = QueryChannelCount(direction);
const auto expected_mask = [&]() {
switch (expected_channels) {
case 1:
return oboe::ChannelMask::Mono;
case 2:
return oboe::ChannelMask::Stereo;
case 6:
return oboe::ChannelMask::CM5Point1;
default:
ASSERT(false);
return oboe::ChannelMask::Unspecified;
}
}();
oboe::AudioStreamBuilder builder;
const auto result = ConfigureBuilder(builder, direction)
->setChannelCount(expected_channels)
->setChannelMask(expected_mask)
->setChannelConversionAllowed(true)
->setDataCallback(this)
->setErrorCallback(this)
->openStream(m_stream);
ASSERT(result == oboe::Result::OK);
return result == oboe::Result::OK && this->SetStreamProperties();
}
bool SetStreamProperties() {
ASSERT(m_stream);
m_stream->setBufferSizeInFrames(TargetSampleCount * 2);
device_channels = m_stream->getChannelCount();
const auto sample_rate = m_stream->getSampleRate();
const auto buffer_capacity = m_stream->getBufferCapacityInFrames();
const auto stream_backend =
m_stream->getAudioApi() == oboe::AudioApi::AAudio ? "AAudio" : "OpenSLES";
LOG_INFO(Audio_Sink, "Opened Oboe {} stream with {} channels sample rate {} capacity {}",
stream_backend, device_channels, sample_rate, buffer_capacity);
return true;
}
std::shared_ptr<oboe::AudioStream> m_stream{};
};
OboeSink::OboeSink() {
// TODO: This is not generally knowable
// The channel count is distinct based on direction and can change
device_channels = OboeSinkStream::QueryChannelCount(oboe::Direction::Output);
}
OboeSink::~OboeSink() = default;
SinkStream* OboeSink::AcquireSinkStream(Core::System& system, u32 system_channels,
const std::string& name, StreamType type) {
SinkStreamPtr& stream = sink_streams.emplace_back(
std::make_unique<OboeSinkStream>(system, type, name, system_channels));
return stream.get();
}
void OboeSink::CloseStream(SinkStream* to_remove) {
sink_streams.remove_if([&](auto& stream) { return stream.get() == to_remove; });
}
void OboeSink::CloseStreams() {
sink_streams.clear();
}
f32 OboeSink::GetDeviceVolume() const {
if (sink_streams.empty()) {
return 1.0f;
}
return sink_streams.front()->GetDeviceVolume();
}
void OboeSink::SetDeviceVolume(f32 volume) {
for (auto& stream : sink_streams) {
stream->SetDeviceVolume(volume);
}
}
void OboeSink::SetSystemVolume(f32 volume) {
for (auto& stream : sink_streams) {
stream->SetSystemVolume(volume);
}
}
} // namespace AudioCore::Sink
+75
View File
@@ -0,0 +1,75 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <list>
#include <string>
#include "audio_core/sink/sink.h"
namespace Core {
class System;
}
namespace AudioCore::Sink {
class SinkStream;
class OboeSink final : public Sink {
public:
explicit OboeSink();
~OboeSink() override;
/**
* Create a new sink stream.
*
* @param system - Core system.
* @param system_channels - Number of channels the audio system expects.
* May differ from the device's channel count.
* @param name - Name of this stream.
* @param type - Type of this stream, render/in/out.
*
* @return A pointer to the created SinkStream
*/
SinkStream* AcquireSinkStream(Core::System& system, u32 system_channels,
const std::string& name, StreamType type) override;
/**
* Close a given stream.
*
* @param stream - The stream to close.
*/
void CloseStream(SinkStream* stream) override;
/**
* Close all streams.
*/
void CloseStreams() override;
/**
* Get the device volume. Set from calls to the IAudioDevice service.
*
* @return Volume of the device.
*/
f32 GetDeviceVolume() const override;
/**
* Set the device volume. Set from calls to the IAudioDevice service.
*
* @param volume - New volume of the device.
*/
void SetDeviceVolume(f32 volume) override;
/**
* Set the system volume. Comes from the audio system using this stream.
*
* @param volume - New volume of the system.
*/
void SetSystemVolume(f32 volume) override;
private:
/// List of streams managed by this sink
std::list<SinkStreamPtr> sink_streams{};
};
} // namespace AudioCore::Sink
+19
View File
@@ -10,6 +10,9 @@
#include <vector>
#include "audio_core/sink/sink_details.h"
#ifdef HAVE_OBOE
#include "audio_core/sink/oboe_sink.h"
#endif
#ifdef HAVE_CUBEB
#include "audio_core/sink/cubeb_sink.h"
#endif
@@ -40,8 +43,24 @@ struct SinkDetails {
/// SuitableFn is_suitable; // REVERTED FOR LatencyFn latency ABOVE - DIABLO 3 FIX
};
// NOTE TO PROBABLY FIX LATER FOR ANDROID - the return value "0u" for the first HAVE_OBOE
// section below was just copied from the null section so there's a somewhat valid value
// being returned, since the previous "true" value probably isn't compatible with the
// previous EA-3833 code. (HAVE_OBOE was introduced in a later release.) Eventually need
// to change "0u" for something else directly from the oboe_sink.cpp functions.
// sink_details is ordered in terms of desirability, with the best choice at the top.
constexpr SinkDetails sink_details[] = {
#ifdef HAVE_OBOE
SinkDetails{
Settings::AudioEngine::Oboe,
[](std::string_view device_id) -> std::unique_ptr<Sink> {
return std::make_unique<OboeSink>();
},
[](bool capture) { return std::vector<std::string>{"Default"}; },
[]() { return 0u; },
},
#endif
#ifdef HAVE_CUBEB
SinkDetails{
Settings::AudioEngine::Cubeb,
+1
View File
@@ -108,6 +108,7 @@ add_library(
settings_input.h
settings_setting.h
slot_vector.h
socket_types.h
spin_lock.h
stb.cpp
stb.h
+4 -4
View File
@@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "android_common.h"
@@ -51,7 +51,7 @@ jstring ToJString(JNIEnv* env, std::u16string_view str) {
}
double GetJDouble(JNIEnv* env, jobject jdouble) {
return env->CallDoubleMethod(jdouble, GetDoubleValueMethod());
return env->GetDoubleField(jdouble, GetDoubleValueField());
}
jobject ToJDouble(JNIEnv* env, double value) {
@@ -59,7 +59,7 @@ jobject ToJDouble(JNIEnv* env, double value) {
}
s32 GetJInteger(JNIEnv* env, jobject jinteger) {
return env->CallIntMethod(jinteger, GetIntegerValueMethod());
return env->GetIntField(jinteger, GetIntegerValueField());
}
jobject ToJInteger(JNIEnv* env, s32 value) {
@@ -67,7 +67,7 @@ jobject ToJInteger(JNIEnv* env, s32 value) {
}
bool GetJBoolean(JNIEnv* env, jobject jboolean) {
return env->CallBooleanMethod(jboolean, GetBooleanValueMethod());
return env->GetBooleanField(jboolean, GetBooleanValueField());
}
jobject ToJBoolean(JNIEnv* env, bool value) {
+192 -188
View File
@@ -63,15 +63,15 @@ static jfieldID s_patch_title_id_field;
static jclass s_double_class;
static jmethodID s_double_constructor;
static jmethodID s_double_value_method;
static jfieldID s_double_value_field;
static jclass s_integer_class;
static jmethodID s_integer_constructor;
static jmethodID s_integer_value_method;
static jfieldID s_integer_value_field;
static jclass s_boolean_class;
static jmethodID s_boolean_constructor;
static jmethodID s_boolean_value_method;
static jfieldID s_boolean_value_field;
static jclass s_player_input_class;
static jmethodID s_player_input_constructor;
@@ -103,6 +103,7 @@ static jmethodID s_clear_chat;
static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
namespace Common::Android {
JNIEnv *GetEnvForThread() {
thread_local static struct OwnedEnv {
OwnedEnv() {
@@ -298,8 +299,8 @@ namespace Common::Android {
return s_double_constructor;
}
jmethodID GetDoubleValueMethod() {
return s_double_value_method;
jfieldID GetDoubleValueField() {
return s_double_value_field;
}
jclass GetIntegerClass() {
@@ -310,8 +311,8 @@ namespace Common::Android {
return s_integer_constructor;
}
jmethodID GetIntegerValueMethod() {
return s_integer_value_method;
jfieldID GetIntegerValueField() {
return s_integer_value_field;
}
jclass GetBooleanClass() {
@@ -322,8 +323,8 @@ namespace Common::Android {
return s_boolean_constructor;
}
jmethodID GetBooleanValueMethod() {
return s_boolean_value_method;
jfieldID GetBooleanValueField() {
return s_boolean_value_field;
}
jclass GetPlayerInputClass() {
@@ -428,6 +429,188 @@ namespace Common::Android {
jint InitFFmpegOnLoad(JavaVM *vm);
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
s_java_vm = vm;
InitFFmpegOnLoad(vm);
JNIEnv *env;
if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION) != JNI_OK)
return JNI_ERR;
// Initialize Java classes
const jclass native_library_class = env->FindClass("org/yuzu/yuzu_emu/NativeLibrary");
s_native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
s_disk_cache_progress_class = reinterpret_cast<jclass>(env->NewGlobalRef(
env->FindClass("org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress")));
s_load_callback_stage_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(
"org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage")));
const jclass game_dir_class = env->FindClass("org/yuzu/yuzu_emu/model/GameDir");
s_game_dir_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_dir_class));
s_game_dir_constructor = env->GetMethodID(game_dir_class, "<init>",
"(Ljava/lang/String;Z)V");
env->DeleteLocalRef(game_dir_class);
// Initialize methods
s_exit_emulation_activity =
env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V");
s_disk_cache_load_progress =
env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(III)V");
s_copy_to_storage = env->GetStaticMethodID(s_native_library_class, "copyFileToStorage",
"(Ljava/lang/String;Ljava/lang/String;)Z");
s_file_exists = env->GetStaticMethodID(s_native_library_class, "exists",
"(Ljava/lang/String;)Z");
s_file_extension = env->GetStaticMethodID(s_native_library_class, "getFileExtension",
"(Ljava/lang/String;)Ljava/lang/String;");
s_on_emulation_started =
env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V");
s_on_emulation_stopped =
env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V");
s_on_program_changed =
env->GetStaticMethodID(s_native_library_class, "onProgramChanged", "(I)V");
const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game");
s_game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class));
s_game_constructor = env->GetMethodID(game_class, "<init>",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
"String;Ljava/lang/String;Ljava/lang/String;Z)V");
s_game_title_field = env->GetFieldID(game_class, "title", "Ljava/lang/String;");
s_game_path_field = env->GetFieldID(game_class, "path", "Ljava/lang/String;");
s_game_program_id_field = env->GetFieldID(game_class, "programId", "Ljava/lang/String;");
s_game_developer_field = env->GetFieldID(game_class, "developer", "Ljava/lang/String;");
s_game_version_field = env->GetFieldID(game_class, "version", "Ljava/lang/String;");
s_game_is_homebrew_field = env->GetFieldID(game_class, "isHomebrew", "Z");
env->DeleteLocalRef(game_class);
const jclass string_class = env->FindClass("java/lang/String");
s_string_class = reinterpret_cast<jclass>(env->NewGlobalRef(string_class));
env->DeleteLocalRef(string_class);
const jclass pair_class = env->FindClass("kotlin/Pair");
s_pair_class = reinterpret_cast<jclass>(env->NewGlobalRef(pair_class));
s_pair_constructor =
env->GetMethodID(pair_class, "<init>", "(Ljava/lang/Object;Ljava/lang/Object;)V");
s_pair_first_field = env->GetFieldID(pair_class, "first", "Ljava/lang/Object;");
s_pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;");
env->DeleteLocalRef(pair_class);
const jclass overlay_control_data_class =
env->FindClass("org/yuzu/yuzu_emu/overlay/model/OverlayControlData");
s_overlay_control_data_class =
reinterpret_cast<jclass>(env->NewGlobalRef(overlay_control_data_class));
s_overlay_control_data_constructor =
env->GetMethodID(overlay_control_data_class, "<init>",
"(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;F)V");
s_overlay_control_data_id_field =
env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;");
s_overlay_control_data_enabled_field =
env->GetFieldID(overlay_control_data_class, "enabled", "Z");
s_overlay_control_data_landscape_position_field =
env->GetFieldID(overlay_control_data_class, "landscapePosition", "Lkotlin/Pair;");
s_overlay_control_data_portrait_position_field =
env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;");
s_overlay_control_data_foldable_position_field =
env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;");
s_overlay_control_data_individual_scale_field =
env->GetFieldID(overlay_control_data_class, "individualScale", "F");
env->DeleteLocalRef(overlay_control_data_class);
const jclass patch_class = env->FindClass("org/yuzu/yuzu_emu/model/Patch");
s_patch_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_class));
s_patch_constructor = env->GetMethodID(
patch_class, "<init>",
"(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JI)V");
s_patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z");
s_patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;");
s_patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;");
s_patch_type_field = env->GetFieldID(patch_class, "type", "I");
s_patch_program_id_field = env->GetFieldID(patch_class, "programId", "Ljava/lang/String;");
s_patch_title_id_field = env->GetFieldID(patch_class, "titleId", "Ljava/lang/String;");
env->DeleteLocalRef(patch_class);
const jclass double_class = env->FindClass("java/lang/Double");
s_double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class));
s_double_constructor = env->GetMethodID(double_class, "<init>", "(D)V");
s_double_value_field = env->GetFieldID(double_class, "value", "D");
env->DeleteLocalRef(double_class);
const jclass int_class = env->FindClass("java/lang/Integer");
s_integer_class = reinterpret_cast<jclass>(env->NewGlobalRef(int_class));
s_integer_constructor = env->GetMethodID(int_class, "<init>", "(I)V");
s_integer_value_field = env->GetFieldID(int_class, "value", "I");
env->DeleteLocalRef(int_class);
const jclass boolean_class = env->FindClass("java/lang/Boolean");
s_boolean_class = reinterpret_cast<jclass>(env->NewGlobalRef(boolean_class));
s_boolean_constructor = env->GetMethodID(boolean_class, "<init>", "(Z)V");
s_boolean_value_field = env->GetFieldID(boolean_class, "value", "Z");
env->DeleteLocalRef(boolean_class);
const jclass player_input_class =
env->FindClass("org/yuzu/yuzu_emu/features/input/model/PlayerInput");
s_player_input_class = reinterpret_cast<jclass>(env->NewGlobalRef(player_input_class));
s_player_input_constructor = env->GetMethodID(
player_input_class, "<init>",
"(Z[Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;ZIJJJJLjava/lang/String;Z)V");
s_player_input_connected_field = env->GetFieldID(player_input_class, "connected", "Z");
s_player_input_buttons_field =
env->GetFieldID(player_input_class, "buttons", "[Ljava/lang/String;");
s_player_input_analogs_field =
env->GetFieldID(player_input_class, "analogs", "[Ljava/lang/String;");
s_player_input_motions_field =
env->GetFieldID(player_input_class, "motions", "[Ljava/lang/String;");
s_player_input_vibration_enabled_field =
env->GetFieldID(player_input_class, "vibrationEnabled", "Z");
s_player_input_vibration_strength_field =
env->GetFieldID(player_input_class, "vibrationStrength", "I");
s_player_input_body_color_left_field =
env->GetFieldID(player_input_class, "bodyColorLeft", "J");
s_player_input_body_color_right_field =
env->GetFieldID(player_input_class, "bodyColorRight", "J");
s_player_input_button_color_left_field =
env->GetFieldID(player_input_class, "buttonColorLeft", "J");
s_player_input_button_color_right_field =
env->GetFieldID(player_input_class, "buttonColorRight", "J");
s_player_input_profile_name_field =
env->GetFieldID(player_input_class, "profileName", "Ljava/lang/String;");
s_player_input_use_system_vibrator_field =
env->GetFieldID(player_input_class, "useSystemVibrator", "Z");
env->DeleteLocalRef(player_input_class);
const jclass yuzu_input_device_interface =
env->FindClass("org/yuzu/yuzu_emu/features/input/YuzuInputDevice");
s_yuzu_input_device_interface =
reinterpret_cast<jclass>(env->NewGlobalRef(yuzu_input_device_interface));
s_yuzu_input_device_get_name =
env->GetMethodID(yuzu_input_device_interface, "getName", "()Ljava/lang/String;");
s_yuzu_input_device_get_guid =
env->GetMethodID(yuzu_input_device_interface, "getGUID", "()Ljava/lang/String;");
s_yuzu_input_device_get_port = env->GetMethodID(yuzu_input_device_interface, "getPort",
"()I");
s_yuzu_input_device_get_supports_vibration =
env->GetMethodID(yuzu_input_device_interface, "getSupportsVibration", "()Z");
s_yuzu_input_device_vibrate = env->GetMethodID(yuzu_input_device_interface, "vibrate",
"(F)V");
s_yuzu_input_device_get_axes =
env->GetMethodID(yuzu_input_device_interface, "getAxes", "()[Ljava/lang/Integer;");
s_yuzu_input_device_has_keys =
env->GetMethodID(yuzu_input_device_interface, "hasKeys", "([I)[Z");
env->DeleteLocalRef(yuzu_input_device_interface);
s_add_netplay_message = env->GetStaticMethodID(s_native_library_class, "addNetPlayMessage",
"(ILjava/lang/String;)V");
s_clear_chat = env->GetStaticMethodID(s_native_library_class, "clearChat", "()V");
// Initialize Android Storage
Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
// Initialize applets
Common::Android::SoftwareKeyboard::InitJNI(env);
Common::Android::WebBrowser::InitJNI(env);
return JNI_VERSION;
}
void JNI_OnUnload(JavaVM *vm, void *reserved) {
JNIEnv *env;
if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION) != JNI_OK) {
@@ -462,183 +645,4 @@ namespace Common::Android {
}
#endif
void Initialize(JavaVM* vm, JNIEnv *env) {
s_java_vm = vm;
InitFFmpegOnLoad(vm);
if (env->ExceptionCheck()) {
env->ExceptionClear();
}
// Initialize Java classes
const jclass native_library_class = env->FindClass("org/yuzu/yuzu_emu/NativeLibrary");
s_native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
s_disk_cache_progress_class = reinterpret_cast<jclass>(env->NewGlobalRef(
env->FindClass("org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress")));
s_load_callback_stage_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(
"org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage")));
const jclass game_dir_class = env->FindClass("org/yuzu/yuzu_emu/model/GameDir");
s_game_dir_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_dir_class));
s_game_dir_constructor = env->GetMethodID(game_dir_class, "<init>",
"(Ljava/lang/String;Z)V");
env->DeleteLocalRef(game_dir_class);
// Initialize methods
s_exit_emulation_activity =
env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V");
s_disk_cache_load_progress =
env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(III)V");
s_copy_to_storage = env->GetStaticMethodID(s_native_library_class, "copyFileToStorage",
"(Ljava/lang/String;Ljava/lang/String;)Z");
s_file_exists = env->GetStaticMethodID(s_native_library_class, "exists",
"(Ljava/lang/String;)Z");
s_file_extension = env->GetStaticMethodID(s_native_library_class, "getFileExtension",
"(Ljava/lang/String;)Ljava/lang/String;");
s_on_emulation_started =
env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V");
s_on_emulation_stopped =
env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V");
s_on_program_changed =
env->GetStaticMethodID(s_native_library_class, "onProgramChanged", "(I)V");
const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game");
s_game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class));
s_game_constructor = env->GetMethodID(game_class, "<init>",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
"String;Ljava/lang/String;Ljava/lang/String;Z)V");
s_game_title_field = env->GetFieldID(game_class, "title", "Ljava/lang/String;");
s_game_path_field = env->GetFieldID(game_class, "path", "Ljava/lang/String;");
s_game_program_id_field = env->GetFieldID(game_class, "programId", "Ljava/lang/String;");
s_game_developer_field = env->GetFieldID(game_class, "developer", "Ljava/lang/String;");
s_game_version_field = env->GetFieldID(game_class, "version", "Ljava/lang/String;");
s_game_is_homebrew_field = env->GetFieldID(game_class, "isHomebrew", "Z");
env->DeleteLocalRef(game_class);
const jclass string_class = env->FindClass("java/lang/String");
s_string_class = reinterpret_cast<jclass>(env->NewGlobalRef(string_class));
env->DeleteLocalRef(string_class);
const jclass pair_class = env->FindClass("kotlin/Pair");
s_pair_class = reinterpret_cast<jclass>(env->NewGlobalRef(pair_class));
s_pair_constructor =
env->GetMethodID(pair_class, "<init>", "(Ljava/lang/Object;Ljava/lang/Object;)V");
s_pair_first_field = env->GetFieldID(pair_class, "first", "Ljava/lang/Object;");
s_pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;");
env->DeleteLocalRef(pair_class);
const jclass overlay_control_data_class =
env->FindClass("org/yuzu/yuzu_emu/overlay/model/OverlayControlData");
s_overlay_control_data_class =
reinterpret_cast<jclass>(env->NewGlobalRef(overlay_control_data_class));
s_overlay_control_data_constructor =
env->GetMethodID(overlay_control_data_class, "<init>",
"(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;F)V");
s_overlay_control_data_id_field =
env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;");
s_overlay_control_data_enabled_field =
env->GetFieldID(overlay_control_data_class, "enabled", "Z");
s_overlay_control_data_landscape_position_field =
env->GetFieldID(overlay_control_data_class, "landscapePosition", "Lkotlin/Pair;");
s_overlay_control_data_portrait_position_field =
env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;");
s_overlay_control_data_foldable_position_field =
env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;");
s_overlay_control_data_individual_scale_field =
env->GetFieldID(overlay_control_data_class, "individualScale", "F");
env->DeleteLocalRef(overlay_control_data_class);
const jclass patch_class = env->FindClass("org/yuzu/yuzu_emu/model/Patch");
s_patch_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_class));
s_patch_constructor = env->GetMethodID(
patch_class, "<init>",
"(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JI)V");
s_patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z");
s_patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;");
s_patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;");
s_patch_type_field = env->GetFieldID(patch_class, "type", "I");
s_patch_program_id_field = env->GetFieldID(patch_class, "programId", "Ljava/lang/String;");
s_patch_title_id_field = env->GetFieldID(patch_class, "titleId", "Ljava/lang/String;");
env->DeleteLocalRef(patch_class);
const jclass double_class = env->FindClass("java/lang/Double");
s_double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class));
s_double_constructor = env->GetMethodID(double_class, "<init>", "(D)V");
s_double_value_method = env->GetMethodID(double_class, "doubleValue", "()D");
env->DeleteLocalRef(double_class);
const jclass int_class = env->FindClass("java/lang/Integer");
s_integer_class = reinterpret_cast<jclass>(env->NewGlobalRef(int_class));
s_integer_constructor = env->GetMethodID(int_class, "<init>", "(I)V");
s_integer_value_method = env->GetMethodID(int_class, "intValue", "()I");
env->DeleteLocalRef(int_class);
const jclass boolean_class = env->FindClass("java/lang/Boolean");
s_boolean_class = reinterpret_cast<jclass>(env->NewGlobalRef(boolean_class));
s_boolean_constructor = env->GetMethodID(boolean_class, "<init>", "(Z)V");
s_boolean_value_method = env->GetMethodID(boolean_class, "booleanValue", "()Z");
env->DeleteLocalRef(boolean_class);
const jclass player_input_class =
env->FindClass("org/yuzu/yuzu_emu/features/input/model/PlayerInput");
s_player_input_class = reinterpret_cast<jclass>(env->NewGlobalRef(player_input_class));
s_player_input_constructor = env->GetMethodID(
player_input_class, "<init>",
"(Z[Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;ZIJJJJLjava/lang/String;Z)V");
s_player_input_connected_field = env->GetFieldID(player_input_class, "connected", "Z");
s_player_input_buttons_field =
env->GetFieldID(player_input_class, "buttons", "[Ljava/lang/String;");
s_player_input_analogs_field =
env->GetFieldID(player_input_class, "analogs", "[Ljava/lang/String;");
s_player_input_motions_field =
env->GetFieldID(player_input_class, "motions", "[Ljava/lang/String;");
s_player_input_vibration_enabled_field =
env->GetFieldID(player_input_class, "vibrationEnabled", "Z");
s_player_input_vibration_strength_field =
env->GetFieldID(player_input_class, "vibrationStrength", "I");
s_player_input_body_color_left_field =
env->GetFieldID(player_input_class, "bodyColorLeft", "J");
s_player_input_body_color_right_field =
env->GetFieldID(player_input_class, "bodyColorRight", "J");
s_player_input_button_color_left_field =
env->GetFieldID(player_input_class, "buttonColorLeft", "J");
s_player_input_button_color_right_field =
env->GetFieldID(player_input_class, "buttonColorRight", "J");
s_player_input_profile_name_field =
env->GetFieldID(player_input_class, "profileName", "Ljava/lang/String;");
s_player_input_use_system_vibrator_field =
env->GetFieldID(player_input_class, "useSystemVibrator", "Z");
env->DeleteLocalRef(player_input_class);
const jclass yuzu_input_device_interface =
env->FindClass("org/yuzu/yuzu_emu/features/input/YuzuInputDevice");
s_yuzu_input_device_interface =
reinterpret_cast<jclass>(env->NewGlobalRef(yuzu_input_device_interface));
s_yuzu_input_device_get_name =
env->GetMethodID(yuzu_input_device_interface, "getName", "()Ljava/lang/String;");
s_yuzu_input_device_get_guid =
env->GetMethodID(yuzu_input_device_interface, "getGUID", "()Ljava/lang/String;");
s_yuzu_input_device_get_port = env->GetMethodID(yuzu_input_device_interface, "getPort",
"()I");
s_yuzu_input_device_get_supports_vibration =
env->GetMethodID(yuzu_input_device_interface, "getSupportsVibration", "()Z");
s_yuzu_input_device_vibrate = env->GetMethodID(yuzu_input_device_interface, "vibrate",
"(F)V");
s_yuzu_input_device_get_axes =
env->GetMethodID(yuzu_input_device_interface, "getAxes", "()[Ljava/lang/Integer;");
s_yuzu_input_device_has_keys =
env->GetMethodID(yuzu_input_device_interface, "hasKeys", "([I)[Z");
env->DeleteLocalRef(yuzu_input_device_interface);
s_add_netplay_message = env->GetStaticMethodID(s_native_library_class, "addNetPlayMessage",
"(ILjava/lang/String;)V");
s_clear_chat = env->GetStaticMethodID(s_native_library_class, "clearChat", "()V");
// Initialize Android Storage
Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
// Initialize applets
Common::Android::SoftwareKeyboard::InitJNI(env);
Common::Android::WebBrowser::InitJNI(env);
}
} // namespace Common::Android
+3 -4
View File
@@ -11,7 +11,6 @@
namespace Common::Android {
void Initialize(JavaVM* vm, JNIEnv *env);
JNIEnv* GetEnvForThread();
/**
@@ -81,15 +80,15 @@ jfieldID GetPatchTitleIdField();
jclass GetDoubleClass();
jmethodID GetDoubleConstructor();
jmethodID GetDoubleValueMethod();
jfieldID GetDoubleValueField();
jclass GetIntegerClass();
jmethodID GetIntegerConstructor();
jmethodID GetIntegerValueMethod();
jfieldID GetIntegerValueField();
jclass GetBooleanClass();
jmethodID GetBooleanConstructor();
jmethodID GetBooleanValueMethod();
jfieldID GetBooleanValueField();
jclass GetPlayerInputClass();
jmethodID GetPlayerInputConstructor();
+2 -2
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
@@ -11,7 +11,7 @@
#include <string>
#include <vector>
#include "common/common_types.h"
#include "core/internal_network/socket_types.h"
#include "common/socket_types.h"
#include "web_service/web_result.h"
namespace AnnounceMultiplayerRoom {
+3 -3
View File
@@ -92,14 +92,14 @@ struct EnumMetadata {
// AudioEngine must be specified discretely due to having existing but slightly different
// canonicalizations
// TODO (lat9nq): Remove explicit definition of AudioEngine/sink_id
enum class AudioEngine : u32 { Auto, Cubeb, Sdl3, Null, };
enum class AudioEngine : u32 { Auto, Cubeb, Sdl3, Null, Oboe, };
template<>
inline std::vector<std::pair<std::string_view, AudioEngine>> EnumMetadata<AudioEngine>::Canonicalizations() {
return {
{"auto", AudioEngine::Auto},
{"cubeb", AudioEngine::Cubeb},
{"sdl3", AudioEngine::Sdl3},
{"null", AudioEngine::Null},
{"null", AudioEngine::Null}, {"oboe", AudioEngine::Oboe},
};
}
/// @brief This is just a sufficiently large number that is more than the number of other enums declared here
@@ -113,7 +113,7 @@ inline AudioEngine EnumMetadata<AudioEngine>::GetFirst() {
}
template<>
inline AudioEngine EnumMetadata<AudioEngine>::GetLast() {
return AudioEngine::Null;
return AudioEngine::Oboe;
}
ENUM(AudioMode, Mono, Stereo, Surround);
+178
View File
@@ -0,0 +1,178 @@
// 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
-7
View File
@@ -1129,7 +1129,6 @@ add_library(core STATIC
internal_network/network_interface.h
internal_network/socket_proxy.cpp
internal_network/socket_proxy.h
internal_network/socket_types.h
internal_network/sockets.h
internal_network/wifi_scanner.h
launch_timestamp_cache.cpp
@@ -1168,12 +1167,6 @@ add_library(core STATIC
tools/renderdoc.cpp
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)
target_sources(core PRIVATE internal_network/wifi_scanner.cpp)
if (LINUX)
+18 -34
View File
@@ -22,21 +22,15 @@ DynarmicCallbacks32::DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess
, m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()}
{}
u8 DynarmicCallbacks32::MemoryRead8(u32 vaddr) {
CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
return m_memory.Read8(vaddr);
}
u16 DynarmicCallbacks32::MemoryRead16(u32 vaddr) {
CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
return m_memory.Read16(vaddr);
}
u32 DynarmicCallbacks32::MemoryRead32(u32 vaddr) {
CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read);
return m_memory.Read32(vaddr);
}
u64 DynarmicCallbacks32::MemoryRead64(u32 vaddr) {
CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
return m_memory.Read64(vaddr);
u64 DynarmicCallbacks32::MemoryRead(u32 vaddr, size_t size) {
CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Read);
switch (size) {
case sizeof(u64): return m_memory.Read64(vaddr);
case sizeof(u32): return m_memory.Read32(vaddr);
case sizeof(u16): return m_memory.Read16(vaddr);
case sizeof(u8): return m_memory.Read8(vaddr);
default: UNREACHABLE();
}
}
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)];
}
void DynarmicCallbacks32::MemoryWrite8(u32 vaddr, u8 value) {
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
m_memory.Write8(vaddr, value);
void DynarmicCallbacks32::MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) {
if (CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Write)) {
switch (size) {
case sizeof(u64): return m_memory.Write64(vaddr, value);
case sizeof(u32): return m_memory.Write32(vaddr, u32(value));
case sizeof(u16): return m_memory.Write16(vaddr, u16(value));
case sizeof(u8): return m_memory.Write8(vaddr, u8(value));
default: UNREACHABLE();
}
}
}
void DynarmicCallbacks32::MemoryWrite16(u32 vaddr, u16 value) {
if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
m_memory.Write16(vaddr, value);
}
}
void DynarmicCallbacks32::MemoryWrite32(u32 vaddr, u32 value) {
if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) {
m_memory.Write32(vaddr, value);
}
}
void DynarmicCallbacks32::MemoryWrite64(u32 vaddr, u64 value) {
if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) {
m_memory.Write64(vaddr, value);
}
}
bool DynarmicCallbacks32::MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) {
return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) &&
m_memory.WriteExclusive8(vaddr, value, expected);
+2 -8
View File
@@ -30,18 +30,12 @@ class System;
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
public:
explicit DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess* process);
u8 MemoryRead8(u32 vaddr) override;
u16 MemoryRead16(u32 vaddr) override;
u32 MemoryRead32(u32 vaddr) override;
u64 MemoryRead64(u32 vaddr) override;
u64 MemoryRead(u32 vaddr, size_t size) override;
std::optional<u32> MemoryReadCode(u32 vaddr) override;
void InstructionSynchronizationBarrierRaised() override {
last_code_addr = u64(-1); //reset back, force refetch
}
void MemoryWrite8(u32 vaddr, u8 value) override;
void MemoryWrite16(u32 vaddr, u16 value) override;
void MemoryWrite32(u32 vaddr, u32 value) override;
void MemoryWrite64(u32 vaddr, u64 value) override;
void MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) override;
bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override;
bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override;
bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override;
+19 -33
View File
@@ -10,6 +10,7 @@
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
#include "core/core_timing.h"
#include "core/hle/kernel/k_process.h"
#include "dynarmic/interface/A64/config.h"
namespace Core {
@@ -21,21 +22,15 @@ DynarmicCallbacks64::DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess
, m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()}
{}
u8 DynarmicCallbacks64::MemoryRead8(u64 vaddr) {
CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
return m_memory.Read8(vaddr);
}
u16 DynarmicCallbacks64::MemoryRead16(u64 vaddr) {
CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
return m_memory.Read16(vaddr);
}
u32 DynarmicCallbacks64::MemoryRead32(u64 vaddr) {
CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read);
return m_memory.Read32(vaddr);
}
u64 DynarmicCallbacks64::MemoryRead64(u64 vaddr) {
CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
return m_memory.Read64(vaddr);
u64 DynarmicCallbacks64::MemoryRead(u64 vaddr, size_t size) {
CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Read);
switch (size) {
case sizeof(u64): return m_memory.Read64(vaddr);
case sizeof(u32): return m_memory.Read32(vaddr);
case sizeof(u16): return m_memory.Read16(vaddr);
case sizeof(u8): return m_memory.Read8(vaddr);
default: UNREACHABLE();
}
}
Dynarmic::A64::Vector DynarmicCallbacks64::MemoryRead128(u64 vaddr) {
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)];
}
void DynarmicCallbacks64::MemoryWrite8(u64 vaddr, u8 value) {
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
m_memory.Write8(vaddr, value);
}
}
void DynarmicCallbacks64::MemoryWrite16(u64 vaddr, u16 value) {
if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
m_memory.Write16(vaddr, value);
}
}
void DynarmicCallbacks64::MemoryWrite32(u64 vaddr, u32 value) {
if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) {
m_memory.Write32(vaddr, value);
}
}
void DynarmicCallbacks64::MemoryWrite64(u64 vaddr, u64 value) {
if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) {
m_memory.Write64(vaddr, value);
void DynarmicCallbacks64::MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, std::size_t size) {
if (CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Write)) {
switch (size) {
case sizeof(u64): return m_memory.Write64(vaddr, u64(value));
case sizeof(u32): return m_memory.Write32(vaddr, u32(value));
case sizeof(u16): return m_memory.Write16(vaddr, u16(value));
case sizeof(u8): return m_memory.Write8(vaddr, u8(value));
default: UNREACHABLE();
}
}
}
void DynarmicCallbacks64::MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) {
+3 -8
View File
@@ -16,6 +16,7 @@
#include "common/hash.h"
#include "core/arm/arm_interface.h"
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
#include "dynarmic/interface/A64/config.h"
namespace Core::Memory {
class Memory;
@@ -36,19 +37,13 @@ class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
public:
explicit DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess* process);
u8 MemoryRead8(u64 vaddr) override;
u16 MemoryRead16(u64 vaddr) override;
u32 MemoryRead32(u64 vaddr) override;
u64 MemoryRead64(u64 vaddr) override;
u64 MemoryRead(u64 vaddr, size_t size) override;
Dynarmic::A64::Vector MemoryRead128(u64 vaddr) override;
std::optional<u32> MemoryReadCode(u64 vaddr) override;
void InstructionSynchronizationBarrierRaised() override {
last_code_addr = u64(-1); //reset back, force refetch
}
void MemoryWrite8(u64 vaddr, u8 value) override;
void MemoryWrite16(u64 vaddr, u16 value) override;
void MemoryWrite32(u64 vaddr, u32 value) override;
void MemoryWrite64(u64 vaddr, u64 value) override;
void MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, std::size_t size) override;
void MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) override;
bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override;
bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override;
+1 -1
View File
@@ -358,7 +358,7 @@ private:
ConnectionState(boost::asio::ip::tcp::socket&& client_socket_, async_pipe signal_pipe_, Kernel::KernelCore& kernel)
: client_socket{std::move(client_socket_)}
, signal_pipe{std::move(signal_pipe_)}
, signal_pipe{signal_pipe_}
, active_thread{kernel, nullptr}
{}
-3
View File
@@ -1021,9 +1021,6 @@ Result KProcess::Run(KernelCore& kernel, s32 priority, size_t stack_size) {
// Suspend for debug, if we should.
if (kernel.System().DebuggerEnabled()) {
LOG_INFO(Debug_GDBStub,
"GDB stub enabled; suspending guest process until a debugger continues execution on port {}",
Settings::values.gdbstub_port.GetValue());
main_thread->RequestSuspend(kernel, SuspendType::Debug);
}
+28 -34
View File
@@ -71,17 +71,14 @@ public:
void InstructionSynchronizationBarrierRaised() override {
last_code_addr = u64(-1); //reset back, force refetch
}
u8 MemoryRead8(u64 vaddr) override {
return ReadMemory<u8>(vaddr);
}
u16 MemoryRead16(u64 vaddr) override {
return ReadMemory<u16>(vaddr);
}
u32 MemoryRead32(u64 vaddr) override {
return ReadMemory<u32>(vaddr);
}
u64 MemoryRead64(u64 vaddr) override {
return ReadMemory<u64>(vaddr);
u64 MemoryRead(u64 vaddr, size_t size) override {
switch (size) {
case sizeof(u64): return ReadMemory<u64>(vaddr);
case sizeof(u32): return ReadMemory<u32>(vaddr);
case sizeof(u16): return ReadMemory<u16>(vaddr);
case sizeof(u8): return ReadMemory<u8>(vaddr);
default: UNREACHABLE();
}
}
u128 MemoryRead128(u64 vaddr) override {
return ReadMemory<u128>(vaddr);
@@ -89,22 +86,19 @@ public:
std::string MemoryReadCString(u64 vaddr) {
std::string result{};
u8 next;
while ((next = MemoryRead8(vaddr++)) != 0)
while ((next = u8(MemoryRead(vaddr++, sizeof(u8)))) != 0)
result += char(next);
return result;
}
void MemoryWrite8(u64 vaddr, u8 value) override {
WriteMemory<u8>(vaddr, value);
}
void MemoryWrite16(u64 vaddr, u16 value) override {
WriteMemory<u16>(vaddr, value);
}
void MemoryWrite32(u64 vaddr, u32 value) override {
WriteMemory<u32>(vaddr, value);
}
void MemoryWrite64(u64 vaddr, u64 value) override {
WriteMemory<u64>(vaddr, value);
void MemoryWrite(u64 vaddr, u64 value, size_t size) override {
switch (size) {
case sizeof(u64): WriteMemory<u64>(vaddr, u64(value)); break;
case sizeof(u32): WriteMemory<u32>(vaddr, u32(value)); break;
case sizeof(u16): WriteMemory<u16>(vaddr, u16(value)); break;
case sizeof(u8): WriteMemory<u8>(vaddr, u8(value)); break;
default: UNREACHABLE();
}
}
void MemoryWrite128(u64 vaddr, u128 value) override {
WriteMemory<u128>(vaddr, value);
@@ -193,14 +187,14 @@ public:
// The loaded NRO file has ELF relocations that must be processed before it can run.
// Normally this would be processed by RTLD, but in HLE context, we don't have
// the linker available, so we have to do it ourselves.
const VAddr mod_offset{callbacks->MemoryRead32(4)};
if (callbacks->MemoryRead32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0'))
const VAddr mod_offset{callbacks->MemoryRead(4, sizeof(u32))};
if (callbacks->MemoryRead(mod_offset, sizeof(u32)) != Common::MakeMagic('M', 'O', 'D', '0'))
return false;
// For more info about dynamic entries, see the ELF ABI specification:
// https://refspecs.linuxbase.org/elf/gabi4+/ch5.dynamic.html
// https://refspecs.linuxbase.org/elf/gabi4+/ch4.reloc.html
VAddr dynamic_offset{mod_offset + callbacks->MemoryRead32(mod_offset + 4)};
VAddr dynamic_offset{mod_offset + callbacks->MemoryRead(mod_offset + 4, sizeof(u32))};
VAddr rela_dyn = 0, relr_dyn = 0;
size_t num_rela = 0, num_relr = 0;
while (true) {
@@ -222,8 +216,8 @@ public:
for (size_t i = 0; i < num_rela; i++) {
const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))};
if (Elf64RelType(rela.r_info) == ElfAArch64Relative) {
const VAddr contents{callbacks->MemoryRead64(rela.r_offset)};
callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend);
const VAddr contents{callbacks->MemoryRead(rela.r_offset, sizeof(u64))};
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++) {
const auto relr = callbacks->ReadMemory<Elf64_Relr>(relr_dyn + i * sizeof(Elf64_Relr));
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) {
// where pointer
@@ -294,7 +288,7 @@ public:
if (argument_stack.size() > 8) {
const VAddr new_sp = Common::AlignDown(top_of_stack - (argument_stack.size() - 8) * sizeof(u64), STACK_ALIGN);
for (size_t i = 8; i < argument_stack.size(); i++)
callbacks->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);
}
// Reset the call state for the next invocation
@@ -385,17 +379,17 @@ void DynarmicCallbacks64::CallSVC(u32 swi) {
if (dest < src) {
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 {
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)]) {
const VAddr dest{parent.jit->GetRegister(0)};
const u64 c{parent.jit->GetRegister(1)};
const size_t n{parent.jit->GetRegister(2)};
for (size_t i = 0; i < n; i++)
MemoryWrite8(dest + i, u8(c));
MemoryWrite(dest + i, u8(c), sizeof(u8));
} else if (pc == parent.helpers[size_t(HelperFn::Resolve)]) {
// X0 contains a char* for a symbol to resolve
const auto name{MemoryReadCString(parent.jit->GetRegister(0))};
@@ -422,7 +416,7 @@ void DynarmicCallbacks64::CallSVC(u32 swi) {
}
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);
parent.jit->HaltExecution();
}
+1 -1
View File
@@ -18,7 +18,7 @@
#include "common/container/unordered_map.h"
#include "common/logging.h"
#include "core/internal_network/socket_types.h"
#include "common/socket_types.h"
#include "core/hle/result.h"
#include "core/hle/service/ldn/ldn_results.h"
#include "core/hle/service/ldn/ldn_types.h"
+8 -7
View File
@@ -129,13 +129,6 @@ 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) {
server_manager->m_system.RunServer(std::move(server_manager));
}
@@ -252,6 +245,14 @@ Result ServerManager::ManageDeferral(Kernel::KEvent** out_event) {
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() {
SCOPE_EXIT {
m_stopped.Set();
+1 -4
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -51,8 +48,8 @@ public:
Result ManageDeferral(Kernel::KEvent** out_event);
Result LoopProcess();
void StartAdditionalHostThreads(const char* name, size_t num_threads);
static void RunServer(std::unique_ptr<ServerManager>&& server);
private:
+278 -241
View File
@@ -12,7 +12,7 @@
#include <fmt/ranges.h>
#include "common/logging.h"
#include "core/internal_network/socket_types.h"
#include "common/socket_types.h"
#include "core/core.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/service/ipc_helpers.h"
@@ -20,9 +20,6 @@
#include "core/hle/service/sockets/sockets_translate.h"
#include "core/internal_network/network.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 "network/network.h"
#include <common/settings.h>
@@ -31,18 +28,15 @@ namespace Service::Sockets {
namespace {
[[nodiscard]] bool IsConnectionBased(Network::Type type) noexcept {
bool IsConnectionBased(Type type) {
switch (type) {
case Network::Type::STREAM:
case Network::Type::SEQPACKET:
case Type::STREAM:
return true;
case Network::Type::RAW:
case Network::Type::DGRAM:
case Network::Type::RDM:
case Network::Type::Unspecified:
case Type::DGRAM:
return false;
default:
UNREACHABLE();
UNIMPLEMENTED_MSG("Unimplemented type={}", type);
return false;
}
}
@@ -98,7 +92,7 @@ void BSD_USA::ConnectWork::Execute(BSD_USA* bsd) {
void BSD_USA::ConnectWork::Response(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<s32>(bsd_errno == Network::Errno::E_SUCCESS ? 0 : -1);
rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1);
rb.PushEnum(bsd_errno);
}
@@ -176,9 +170,10 @@ void BSD_USA::Socket(HLERequestContext& ctx) {
const u32 domain = rp.Pop<u32>();
const u32 type = rp.Pop<u32>();
const u32 protocol = rp.Pop<u32>();
LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);
const auto [fd, bsd_errno] = SocketImpl(Network::Domain(domain), Network::Type(type), Network::Protocol(protocol));
const auto [fd, bsd_errno] = SocketImpl(Domain(domain), Type(type), Protocol(protocol));
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
@@ -194,8 +189,8 @@ void BSD_USA::SocketExempt(HLERequestContext& ctx) {
LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);
auto [fd, bsd_errno] = SocketImpl(Network::Domain(domain), Network::Type(type), Network::Protocol(protocol));
if (bsd_errno == Network::Errno::E_SUCCESS) {
auto [fd, bsd_errno] = SocketImpl(Domain(domain), Type(type), Protocol(protocol));
if (bsd_errno == Errno::SUCCESS) {
bsd_errno = ShutdownImpl(fd, 0);
}
@@ -269,13 +264,13 @@ void BSD_USA::GetPeerName(HLERequestContext& ctx) {
LOG_DEBUG(Service, "called. fd={}", fd);
std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
const Network::Errno bsd_errno = GetPeerNameImpl(fd, write_buffer);
const Errno bsd_errno = GetPeerNameImpl(fd, write_buffer);
ctx.WriteBuffer(write_buffer);
IPC::ResponseBuilder rb{ctx, 5};
rb.Push(ResultSuccess);
rb.Push<s32>(bsd_errno != Network::Errno::E_SUCCESS ? -1 : 0);
rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0);
rb.PushEnum(bsd_errno);
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
}
@@ -287,13 +282,13 @@ void BSD_USA::GetSockName(HLERequestContext& ctx) {
LOG_DEBUG(Service, "called. fd={}", fd);
std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
const Network::Errno bsd_errno = GetSockNameImpl(fd, write_buffer);
const Errno bsd_errno = GetSockNameImpl(fd, write_buffer);
ctx.WriteBuffer(write_buffer);
IPC::ResponseBuilder rb{ctx, 5};
rb.Push(ResultSuccess);
rb.Push<s32>(bsd_errno != Network::Errno::E_SUCCESS ? -1 : 0);
rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0);
rb.PushEnum(bsd_errno);
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
}
@@ -301,19 +296,21 @@ void BSD_USA::GetSockName(HLERequestContext& ctx) {
void BSD_USA::GetSockOpt(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>();
const auto level = Network::SocketLevel(rp.Pop<u32>());
const auto optname = Network::OptName(rp.Pop<u32>());
const u32 level = rp.Pop<u32>();
const auto optname = static_cast<OptName>(rp.Pop<u32>());
std::vector<u8> optval(ctx.GetWriteBufferSize());
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} len={:#x}", fd, level, optname, optval.size());
const Network::Errno err = GetSockOptImpl(fd, level, optname, optval);
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} len={:#x}", fd, level, optname,
optval.size());
const Errno err = GetSockOptImpl(fd, level, optname, optval);
ctx.WriteBuffer(optval);
IPC::ResponseBuilder rb{ctx, 5};
rb.Push(ResultSuccess);
rb.Push<s32>(err == Network::Errno::E_SUCCESS ? 0 : -1);
rb.Push<s32>(err == Errno::SUCCESS ? 0 : -1);
rb.PushEnum(err);
rb.Push<u32>(static_cast<u32>(optval.size()));
}
@@ -336,7 +333,7 @@ void BSD_USA::Fcntl(HLERequestContext& ctx) {
LOG_DEBUG(Service, "called. fd={} cmd={} arg={}", fd, cmd, arg);
const auto [ret, bsd_errno] = FcntlImpl(fd, Network::FcntlCmd(cmd), arg);
const auto [ret, bsd_errno] = FcntlImpl(fd, static_cast<FcntlCmd>(cmd), arg);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
@@ -348,11 +345,13 @@ void BSD_USA::SetSockOpt(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const s32 fd = rp.Pop<s32>();
const Network::SocketLevel level = Network::SocketLevel(rp.Pop<u32>());
const Network::OptName optname = Network::OptName(rp.Pop<u32>());
const u32 level = rp.Pop<u32>();
const OptName optname = static_cast<OptName>(rp.Pop<u32>());
const auto optval = ctx.ReadBuffer();
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} optlen={}", fd, level, u32(optname), optval.size());
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} optlen={}", fd, level,
static_cast<u32>(optname), optval.size());
BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optval));
}
@@ -474,7 +473,7 @@ void BSD_USA::DuplicateSocket(HLERequestContext& ctx) {
struct OutputParameters {
s32 ret;
Network::Errno bsd_errno;
Errno bsd_errno;
};
static_assert(sizeof(OutputParameters) == 0x8);
@@ -486,7 +485,7 @@ void BSD_USA::DuplicateSocket(HLERequestContext& ctx) {
if (is_user) {
rb.PushRaw(OutputParameters{
.ret = 0,
.bsd_errno = Network::Errno::E_INVAL,
.bsd_errno = Errno::INVAL,
});
return;
}
@@ -495,10 +494,10 @@ void BSD_USA::DuplicateSocket(HLERequestContext& ctx) {
if (auto* res = std::get_if<s32>(&res_v)) {
rb.PushRaw(OutputParameters{
.ret = *res,
.bsd_errno = Network::Errno::E_SUCCESS,
.bsd_errno = Errno::SUCCESS,
});
} else {
auto* err = std::get_if<Network::Errno>(&res_v);
auto* err = std::get_if<Errno>(&res_v);
rb.PushRaw(OutputParameters{
.ret = 0,
.bsd_errno = *err,
@@ -513,7 +512,7 @@ void BSD_USA::EventFd(HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called. initval={}, flags={}", initval, flags);
BuildErrnoResponse(ctx, Network::Errno::E_SUCCESS);
BuildErrnoResponse(ctx, Errno::SUCCESS);
}
template <typename Work>
@@ -522,143 +521,132 @@ void BSD_USA::ExecuteWork(HLERequestContext& ctx, Work work) {
work.Response(ctx);
}
std::pair<s32, Network::Errno> BSD_USA::SocketImpl(Network::Domain domain, Network::Type type, Network::Protocol protocol) {
std::pair<s32, Errno> BSD_USA::SocketImpl(Domain domain, Type type, Protocol protocol) {
// user bsd:u has restrictions on SOCK_SEQPACKET and SOCK_RAW
LOG_DEBUG(Network, "domain={},type={},protocol={}", u32(domain), u32(type), u32(protocol));
if (is_user && (type == Network::Type::SEQPACKET || type == Network::Type::RAW)) {
if (type == Network::Type::RAW && domain == Network::Domain::INET && protocol == Network::Protocol::ICMP) {
if (is_user && (type == Type::SEQPACKET || type == Type::RAW)) {
if (type == Type::RAW && domain == Domain::INET && protocol == Protocol::ICMP) {
// fine, can use on bsd:s and bsd:u
} else {
return {-1, Network::Errno::E_INVAL};
return {-1, Errno::INVAL};
}
}
[[maybe_unused]] const bool unk_flag = (u32(type) & 0x20000000) != 0;
[[maybe_unused]] const bool unk_flag = (static_cast<u32>(type) & 0x20000000) != 0;
UNIMPLEMENTED_IF_MSG(unk_flag, "Unknown flag in type");
type = Network::Type(u32(type) & ~0x20000000);
type = static_cast<Type>(static_cast<u32>(type) & ~0x20000000);
const s32 fd = FindFreeFileDescriptorHandle();
if (fd < 0) {
LOG_ERROR(Service, "No more file descriptors available");
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};
return {-1, Errno::MFILE};
}
file_descriptors[fd] = FileDescriptor{};
FileDescriptor& descriptor = *file_descriptors[fd];
// ENONMEM might be thrown here
LOG_INFO(Service, "New socket fd={},domain={},type={},prot={}", fd, domain, type, protocol);
// 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?
LOG_INFO(Service, "New socket fd={}", fd);
auto room_member = Network::GetRoomMember().lock();
if ((protocol != Network::Protocol::ICMP && protocol != Network::Protocol::ICMPV6)
&& (room_member && room_member->IsConnected())) {
if (room_member && room_member->IsConnected()) {
descriptor.socket = std::make_shared<Network::ProxySocket>();
descriptor.socket->fd = fd;
} else {
descriptor.socket = std::make_shared<Network::Socket>();
}
auto const bsd_errno = descriptor.socket->Initialize(domain, type, protocol);
#if defined(__unix__) && !defined(__APPLE__)
// ...only unix has this issue it seems, ICMP works otherwise fine on win
if ((protocol == Network::Protocol::ICMP || protocol == Network::Protocol::ICMPV6)
&& bsd_errno != Network::Errno::E_SUCCESS) {
LOG_WARNING(Network, "Using ICMP emulated socket");
descriptor.socket = std::make_shared<Network::IcmpSocket>();
descriptor.socket->fd = fd;
}
#endif
descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(protocol));
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};
if (Settings::values.airplane_mode.GetValue() && descriptor.is_connection_based) {
LOG_ERROR(Service, "Airplane mode is enabled, cannot create socket");
return {-1, Errno::NOTCONN};
}
return {fd, Network::Errno::E_SUCCESS};
return {fd, Errno::SUCCESS};
}
std::pair<s32, Network::Errno> BSD_USA::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, s32 nfds, s32 timeout) {
LOG_DEBUG(Network, "nfds={},timeout={}", nfds, timeout);
std::pair<s32, Errno> BSD_USA::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer,
s32 nfds, s32 timeout) {
if (nfds <= 0) {
// When no entries are provided, -1 is returned with errno zero
return {-1, Network::Errno::E_SUCCESS};
return {-1, Errno::SUCCESS};
}
if (read_buffer.size() < nfds * sizeof(Network::PollFD)) {
return {-1, Network::Errno::E_INVAL};
if (read_buffer.size() < nfds * sizeof(PollFD)) {
return {-1, Errno::INVAL};
}
if (write_buffer.size() < nfds * sizeof(Network::PollFD)) {
return {-1, Network::Errno::E_INVAL};
if (write_buffer.size() < nfds * sizeof(PollFD)) {
return {-1, Errno::INVAL};
}
std::span<const Network::PollFD> in_fds(reinterpret_cast<const Network::PollFD*>(read_buffer.data()), nfds);
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());
std::vector<PollFD> fds(nfds);
std::memcpy(fds.data(), read_buffer.data(), nfds * sizeof(PollFD));
if (timeout >= 0) {
const s64 seconds = timeout / 1000;
const u64 nanoseconds = 1'000'000 * (u64(timeout) % 1000);
const u64 nanoseconds = 1'000'000 * (static_cast<u64>(timeout) % 1000);
if (seconds < 0) {
return {-1, Network::Errno::E_INVAL};
return {-1, Errno::INVAL};
}
if (nanoseconds > 999'999'999) {
return {-1, Network::Errno::E_INVAL};
return {-1, Errno::INVAL};
}
} else if (timeout != -1) {
return {-1, Network::Errno::E_INVAL};
return {-1, Errno::INVAL};
}
for (size_t i = 0; i < in_fds.size(); ++i) {
ASSERT(out_fds[i].fd == in_fds[i].fd && False(in_fds[i].revents));
if (!IsFileDescriptorValid(in_fds[i].fd)) {
out_fds[i].revents = {};
if (!file_descriptors[in_fds[i].fd])
out_fds[i].revents = Network::PollEvents::NVAL;
return {0, Network::Errno::E_SUCCESS};
for (PollFD& pollfd : fds) {
ASSERT(False(pollfd.revents));
if (pollfd.fd > static_cast<s32>(MAX_FD) || pollfd.fd < 0) {
LOG_ERROR(Service, "File descriptor handle={} is invalid", pollfd.fd);
pollfd.revents = PollEvents{};
return {0, Errno::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::HostPollFD> host_pollfds(in_fds.size());
std::transform(in_fds.begin(), in_fds.end(), host_pollfds.begin(), [](auto const e) {
Network::HostPollFD result{};
result.socket = file_descriptors[e.fd]->socket.get();
result.events = e.events;
result.revents = {};
std::vector<Network::PollFD> host_pollfds(fds.size());
std::transform(fds.begin(), fds.end(), host_pollfds.begin(), [](PollFD pollfd) {
Network::PollFD result;
result.socket = file_descriptors[pollfd.fd]->socket.get();
result.events = Translate(pollfd.events);
result.revents = Network::PollEvents{};
return result;
});
auto const res = Network::Poll(host_pollfds, timeout);
for (size_t i = 0; i < in_fds.size(); ++i)
out_fds[i].revents = host_pollfds[i].revents;
return res;
const auto result = Network::Poll(host_pollfds, timeout);
const size_t num = host_pollfds.size();
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, Network::Errno> BSD_USA::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
LOG_DEBUG(Network, "fd={}", fd);
std::pair<s32, Errno> BSD_USA::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
if (!IsFileDescriptorValid(fd)) {
return {-1, Network::Errno::E_BADF};
return {-1, Errno::BADF};
}
const s32 new_fd = FindFreeFileDescriptorHandle();
if (new_fd < 0) {
LOG_ERROR(Service, "No more file descriptors available");
return {-1, Network::Errno::E_MFILE};
return {-1, Errno::MFILE};
}
FileDescriptor& descriptor = *file_descriptors[fd];
auto [result, bsd_errno] = descriptor.socket->Accept();
if (bsd_errno != Network::Errno::E_SUCCESS) {
return {-1, bsd_errno};
if (bsd_errno != Network::Errno::SUCCESS) {
return {-1, Translate(bsd_errno)};
}
file_descriptors[new_fd] = FileDescriptor{};
@@ -666,218 +654,267 @@ std::pair<s32, Network::Errno> BSD_USA::AcceptImpl(s32 fd, std::vector<u8>& writ
new_descriptor.socket = std::move(result.socket);
new_descriptor.is_connection_based = descriptor.is_connection_based;
PutValue(write_buffer, result.sockaddr_in);
return {new_fd, Network::Errno::E_SUCCESS};
const SockAddrIn guest_addr_in = Translate(result.sockaddr_in);
PutValue(write_buffer, guest_addr_in);
return {new_fd, Errno::SUCCESS};
}
Network::Errno BSD_USA::BindImpl(s32 fd, std::span<const u8> addr) {
LOG_DEBUG(Network, "fd={}", fd);
Errno BSD_USA::BindImpl(s32 fd, std::span<const u8> addr) {
if (!IsFileDescriptorValid(fd)) {
return Network::Errno::E_BADF;
return Errno::BADF;
}
ASSERT(addr.size() >= 16);
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Network::Errno::E_BADF;
return Errno::BADF;
}
auto addr_in = GetValue<Network::SockAddrIn>(addr);
return file_descriptors[fd]->socket->Bind(addr_in);
auto addr_in = GetValue<SockAddrIn>(addr);
return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in)));
}
Network::Errno BSD_USA::ConnectImpl(s32 fd, std::span<const u8> addr) {
LOG_DEBUG(Network, "fd={}", fd);
Errno BSD_USA::ConnectImpl(s32 fd, std::span<const u8> addr) {
if (!IsFileDescriptorValid(fd)) {
return Network::Errno::E_BADF;
return Errno::BADF;
}
ASSERT(addr.size() >= 16);
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Network::Errno::E_BADF;
return Errno::BADF;
}
auto addr_in = GetValue<Network::SockAddrIn>(addr);
const Network::Errno result = file_descriptors[fd]->socket->Connect(addr_in);
if (result == Network::Errno::E_ISCONN) {
auto addr_in = GetValue<SockAddrIn>(addr);
const Errno result = Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in)));
if (result == Errno::ISCONN) {
LOG_DEBUG(Service, "returned ISCONN - socket already connected");
return Network::Errno::E_SUCCESS;
return Errno::SUCCESS;
}
return result;
}
Network::Errno BSD_USA::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
LOG_DEBUG(Network, "fd={}", fd);
Errno BSD_USA::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
if (!IsFileDescriptorValid(fd)) {
return Network::Errno::E_BADF;
return Errno::BADF;
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Network::Errno::E_BADF;
return Errno::BADF;
}
const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetPeerName();
if (bsd_errno != Network::Errno::E_SUCCESS) {
return bsd_errno;
if (bsd_errno != Network::Errno::SUCCESS) {
return Translate(bsd_errno);
}
ASSERT(write_buffer.size() >= addr_in.len);
write_buffer.resize(addr_in.len);
PutValue(write_buffer, addr_in);
return bsd_errno;
const SockAddrIn guest_addrin = Translate(addr_in);
ASSERT(write_buffer.size() >= sizeof(guest_addrin));
write_buffer.resize(sizeof(guest_addrin));
PutValue(write_buffer, guest_addrin);
return Translate(bsd_errno);
}
Network::Errno BSD_USA::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
LOG_DEBUG(Network, "fd={}", fd);
Errno BSD_USA::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
if (!IsFileDescriptorValid(fd)) {
return Network::Errno::E_BADF;
return Errno::BADF;
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Network::Errno::E_BADF;
return Errno::BADF;
}
const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetSockName();
if (bsd_errno != Network::Errno::E_SUCCESS) {
return bsd_errno;
if (bsd_errno != Network::Errno::SUCCESS) {
return Translate(bsd_errno);
}
ASSERT(write_buffer.size() >= addr_in.len);
write_buffer.resize(addr_in.len);
PutValue(write_buffer, addr_in);
return bsd_errno;
const SockAddrIn guest_addrin = Translate(addr_in);
ASSERT(write_buffer.size() >= sizeof(guest_addrin));
write_buffer.resize(sizeof(guest_addrin));
PutValue(write_buffer, guest_addrin);
return Translate(bsd_errno);
}
Network::Errno BSD_USA::ListenImpl(s32 fd, s32 backlog) {
LOG_DEBUG(Network, "fd={},backlog={}", fd, backlog);
Errno BSD_USA::ListenImpl(s32 fd, s32 backlog) {
if (!IsFileDescriptorValid(fd)) {
return Network::Errno::E_BADF;
return Errno::BADF;
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Network::Errno::E_BADF;
return Errno::BADF;
}
return file_descriptors[fd]->socket->Listen(backlog);
return Translate(file_descriptors[fd]->socket->Listen(backlog));
}
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);
std::pair<s32, Errno> BSD_USA::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) {
if (!IsFileDescriptorValid(fd)) {
return {-1, Network::Errno::E_BADF};
return {-1, Errno::BADF};
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return {-1, Network::Errno::E_BADF};
return {-1, Errno::BADF};
}
FileDescriptor& descriptor = *file_descriptors[fd];
switch (cmd) {
case Network::FcntlCmd::GETFL:
case FcntlCmd::GETFL:
ASSERT(arg == 0);
return {descriptor.flags, Network::Errno::E_SUCCESS};
case Network::FcntlCmd::SETFL: {
const bool enable = (arg & u32(Network::FcntlFlags::NONBLOCK_NX)) != 0;
const Network::Errno bsd_errno = descriptor.socket->SetNonBlock(enable);
if (bsd_errno != Network::Errno::E_SUCCESS) {
return {descriptor.flags, Errno::SUCCESS};
case FcntlCmd::SETFL: {
const bool enable = (arg & Network::FLAG_O_NONBLOCK) != 0;
const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable));
if (bsd_errno != Errno::SUCCESS) {
return {-1, bsd_errno};
}
descriptor.flags = arg;
return {0, Network::Errno::E_SUCCESS};
return {0, Errno::SUCCESS};
}
default:
UNIMPLEMENTED_MSG("Unimplemented cmd={}", cmd);
return {-1, Network::Errno::E_SUCCESS};
return {-1, Errno::SUCCESS};
}
}
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));
Errno BSD_USA::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval) {
if (!IsFileDescriptorValid(fd)) {
return Network::Errno::E_BADF;
return Errno::BADF;
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Network::Errno::E_BADF;
return Errno::BADF;
}
if (level != Network::SocketLevel::SOCKET) {
LOG_WARNING(Service, "(stubbed) level fd={}, level={}, optname={}", fd, level, optname);
if (level != static_cast<u32>(SocketLevel::SOCKET)) {
UNIMPLEMENTED_MSG("Unknown getsockopt level");
return Errno::SUCCESS;
}
Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
switch (optname) {
case Network::OptName::ERROR_: {
case OptName::ERROR_: {
auto [pending_err, getsockopt_err] = socket->GetPendingError();
if (getsockopt_err == Network::Errno::E_SUCCESS) {
if (getsockopt_err == Network::Errno::SUCCESS) {
Errno translated_pending_err = Translate(pending_err);
ASSERT_OR_EXECUTE_MSG(
optval.size() == sizeof(Network::Errno), { return Network::Errno::E_INVAL; },
optval.size() == sizeof(Errno), { return Errno::INVAL; },
"Incorrect getsockopt option size");
optval.resize(sizeof(Network::Errno));
PutValue(optval, pending_err);
optval.resize(sizeof(Errno));
PutValue(optval, translated_pending_err);
}
return getsockopt_err;
return Translate(getsockopt_err);
}
default:
UNIMPLEMENTED_MSG("Unimplemented optname={}", optname);
return Network::Errno::E_SUCCESS;
return Errno::SUCCESS;
}
}
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);
Errno BSD_USA::SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval) {
if (!IsFileDescriptorValid(fd)) {
return Network::Errno::E_BADF;
return Errno::BADF;
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Network::Errno::E_BADF;
return Errno::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();
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;
}
}
Network::Errno BSD_USA::ShutdownImpl(s32 fd, s32 how) {
LOG_DEBUG(Network, "fd={},how={}", fd, how);
Errno BSD_USA::ShutdownImpl(s32 fd, s32 how) {
if (!IsFileDescriptorValid(fd)) {
return Network::Errno::E_BADF;
return Errno::BADF;
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Network::Errno::E_BADF;
return Errno::BADF;
}
return file_descriptors[fd]->socket->Shutdown(Network::ShutdownHow(how));
const Network::ShutdownHow host_how = Translate(static_cast<ShutdownHow>(how));
return Translate(file_descriptors[fd]->socket->Shutdown(host_how));
}
std::pair<s32, Network::Errno> BSD_USA::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) {
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
std::pair<s32, Errno> BSD_USA::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) {
if (!IsFileDescriptorValid(fd)) {
return {-1, Network::Errno::E_BADF};
return {-1, Errno::BADF};
}
FileDescriptor& descriptor = *file_descriptors[fd];
// Apply flags
if ((flags & u32(Network::MsgOpt::DONTWAIT)) != 0) {
flags &= ~u32(Network::MsgOpt::DONTWAIT);
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0) {
using Network::FLAG_MSG_DONTWAIT;
using Network::FLAG_O_NONBLOCK;
if ((flags & FLAG_MSG_DONTWAIT) != 0) {
flags &= ~FLAG_MSG_DONTWAIT;
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
descriptor.socket->SetNonBlock(true);
}
}
const auto [ret, bsd_errno] = descriptor.socket->Recv(flags, message);
const auto [ret, bsd_errno] = Translate(descriptor.socket->Recv(flags, message));
// Restore original state
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0)
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
descriptor.socket->SetNonBlock(false);
}
return {ret, bsd_errno};
}
std::pair<s32, Network::Errno> BSD_USA::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message, std::vector<u8>& addr) {
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
std::pair<s32, Errno> BSD_USA::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
std::vector<u8>& addr) {
if (!IsFileDescriptorValid(fd)) {
return {-1, Network::Errno::E_BADF};
return {-1, Errno::BADF};
}
FileDescriptor& descriptor = *file_descriptors[fd];
@@ -892,17 +929,19 @@ std::pair<s32, Network::Errno> BSD_USA::RecvFromImpl(s32 fd, u32 flags, std::vec
}
// Apply flags
if ((flags & u32(Network::MsgOpt::DONTWAIT)) != 0) {
flags &= ~u32(Network::MsgOpt::DONTWAIT);
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0) {
using Network::FLAG_MSG_DONTWAIT;
using Network::FLAG_O_NONBLOCK;
if ((flags & FLAG_MSG_DONTWAIT) != 0) {
flags &= ~FLAG_MSG_DONTWAIT;
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
descriptor.socket->SetNonBlock(true);
}
}
const auto [ret, bsd_errno] = descriptor.socket->RecvFrom(flags, message, p_addr_in);
const auto [ret, bsd_errno] = Translate(descriptor.socket->RecvFrom(flags, message, p_addr_in));
// Restore original state
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0) {
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
descriptor.socket->SetNonBlock(false);
}
@@ -911,59 +950,58 @@ std::pair<s32, Network::Errno> BSD_USA::RecvFromImpl(s32 fd, u32 flags, std::vec
addr.clear();
} else {
ASSERT(addr.size() >= 16);
PutValue(addr, addr_in);
const SockAddrIn result = Translate(addr_in);
PutValue(addr, result);
}
}
return {ret, bsd_errno};
}
std::pair<s32, Network::Errno> BSD_USA::SendImpl(s32 fd, u32 flags, std::span<const u8> message) {
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
std::pair<s32, Errno> BSD_USA::SendImpl(s32 fd, u32 flags, std::span<const u8> message) {
if (!IsFileDescriptorValid(fd)) {
return {-1, Network::Errno::E_BADF};
return {-1, Errno::BADF};
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return {-1, Network::Errno::E_BADF};
return {-1, Errno::BADF};
}
return file_descriptors[fd]->socket->Send(message, flags);
return Translate(file_descriptors[fd]->socket->Send(message, flags));
}
std::pair<s32, Network::Errno> BSD_USA::SendToImpl(s32 fd, u32 flags, std::span<const u8> message, std::span<const u8> addr) {
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
std::pair<s32, Errno> BSD_USA::SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
std::span<const u8> addr) {
if (!IsFileDescriptorValid(fd)) {
return {-1, Network::Errno::E_BADF};
return {-1, Errno::BADF};
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return {-1, Network::Errno::E_BADF};
return {-1, Errno::BADF};
}
Network::SockAddrIn addr_in{};
Network::SockAddrIn addr_in;
Network::SockAddrIn* p_addr_in = nullptr;
if (!addr.empty()) {
ASSERT(addr.size() >= 16);
auto guest_addr_in = GetValue<Network::SockAddrIn>(addr);
addr_in = guest_addr_in;
auto guest_addr_in = GetValue<SockAddrIn>(addr);
addr_in = Translate(guest_addr_in);
p_addr_in = &addr_in;
}
return file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in);
return Translate(file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in));
}
Network::Errno BSD_USA::CloseImpl(s32 fd) {
LOG_DEBUG(Network, "fd={}", fd);
Errno BSD_USA::CloseImpl(s32 fd) {
if (!IsFileDescriptorValid(fd)) {
return Network::Errno::E_BADF;
return Errno::BADF;
}
if (!file_descriptors[fd]->socket) {
LOG_WARNING(Service, "Uninitialized socket");
return Network::Errno::E_BADF;
return Errno::BADF;
}
auto const bsd_errno = file_descriptors[fd]->socket->Close();
if (bsd_errno != Network::Errno::E_SUCCESS) {
const Errno bsd_errno = Translate(file_descriptors[fd]->socket->Close());
if (bsd_errno != Errno::SUCCESS) {
return bsd_errno;
}
@@ -973,16 +1011,15 @@ Network::Errno BSD_USA::CloseImpl(s32 fd) {
return bsd_errno;
}
std::variant<s32, Network::Errno> BSD_USA::DuplicateSocketImpl(s32 fd) {
LOG_DEBUG(Network, "fd={}", fd);
std::variant<s32, Errno> BSD_USA::DuplicateSocketImpl(s32 fd) {
if (!IsFileDescriptorValid(fd)) {
return Network::Errno::E_BADF;
return Errno::BADF;
}
const s32 new_fd = FindFreeFileDescriptorHandle();
if (!IsFileDescriptorValid(new_fd)) {
if (new_fd < 0) {
LOG_ERROR(Service, "No more file descriptors available");
return Network::Errno::E_MFILE;
return Errno::MFILE;
}
file_descriptors[new_fd] = FileDescriptor{
@@ -994,7 +1031,6 @@ std::variant<s32, Network::Errno> BSD_USA::DuplicateSocketImpl(s32 fd) {
}
std::optional<std::shared_ptr<Network::SocketBase>> BSD_USA::GetSocket(s32 fd) {
LOG_DEBUG(Network, "fd={}", fd);
if (!IsFileDescriptorValid(fd)) {
return std::nullopt;
}
@@ -1006,40 +1042,41 @@ std::optional<std::shared_ptr<Network::SocketBase>> BSD_USA::GetSocket(s32 fd) {
}
s32 BSD_USA::FindFreeFileDescriptorHandle() noexcept {
// first three file descriptors are reserved for:
// STDOUT_FILENO, STDIN_FILENO and STDERR_FILENO
for (s32 fd = 0; fd < s32(file_descriptors.size()); ++fd)
if (!file_descriptors[fd])
for (s32 fd = 0; fd < static_cast<s32>(file_descriptors.size()); ++fd) {
if (!file_descriptors[fd]) {
return fd;
}
}
return -1;
}
bool BSD_USA::IsFileDescriptorValid(s32 fd) const noexcept {
if (fd < 0 || fd >= s32(file_descriptors.size())) {
LOG_ERROR(Service, "Invalid handle={}", fd);
if (fd > static_cast<s32>(MAX_FD) || fd < 0) {
LOG_ERROR(Service, "Invalid file descriptor handle={}", fd);
return false;
}
if (!file_descriptors[fd]) {
LOG_ERROR(Service, "handle={} is not allocated", fd);
LOG_ERROR(Service, "File descriptor handle={} is not allocated", fd);
return false;
}
return true;
}
void BSD_USA::BuildErrnoResponse(HLERequestContext& ctx, Network::Errno bsd_errno) const noexcept {
void BSD_USA::BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noexcept {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<s32>(bsd_errno == Network::Errno::E_SUCCESS ? 0 : -1);
rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1);
rb.PushEnum(bsd_errno);
}
void BSD_USA::OnProxyPacketReceived(const Network::ProxyPacket& packet) {
for (auto& optional_descriptor : file_descriptors) {
if (optional_descriptor.has_value()) {
FileDescriptor& descriptor = *optional_descriptor;
descriptor.socket.get()->HandleProxyPacket(packet);
if (!optional_descriptor.has_value()) {
continue;
}
FileDescriptor& descriptor = *optional_descriptor;
descriptor.socket.get()->HandleProxyPacket(packet);
}
}
+27 -27
View File
@@ -11,7 +11,7 @@
#include <variant>
#include "common/common_types.h"
#include "core/internal_network/socket_types.h"
#include "common/socket_types.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sockets/sockets.h"
#include "network/network.h"
@@ -35,8 +35,8 @@ public:
// These methods are called from SSL; the first two are also called from
// this class for the corresponding IPC methods.
// On the real device, the SSL service makes IPC calls to this service.
std::variant<s32, Network::Errno> DuplicateSocketImpl(s32 fd);
Network::Errno CloseImpl(s32 fd);
std::variant<s32, Errno> DuplicateSocketImpl(s32 fd);
Errno CloseImpl(s32 fd);
std::optional<std::shared_ptr<Network::SocketBase>> GetSocket(s32 fd);
private:
@@ -58,7 +58,7 @@ private:
std::span<const u8> read_buffer;
std::vector<u8> write_buffer;
s32 ret{};
Network::Errno bsd_errno{};
Errno bsd_errno{};
};
struct AcceptWork {
@@ -68,7 +68,7 @@ private:
s32 fd;
std::vector<u8> write_buffer;
s32 ret{};
Network::Errno bsd_errno{};
Errno bsd_errno{};
};
struct ConnectWork {
@@ -77,7 +77,7 @@ private:
s32 fd;
std::span<const u8> addr;
Network::Errno bsd_errno{};
Errno bsd_errno{};
};
struct RecvWork {
@@ -88,7 +88,7 @@ private:
u32 flags;
std::vector<u8> message;
s32 ret{};
Network::Errno bsd_errno{};
Errno bsd_errno{};
};
struct RecvFromWork {
@@ -100,7 +100,7 @@ private:
std::vector<u8> message;
std::vector<u8> addr;
s32 ret{};
Network::Errno bsd_errno{};
Errno bsd_errno{};
};
struct SendWork {
@@ -111,7 +111,7 @@ private:
u32 flags;
std::span<const u8> message;
s32 ret{};
Network::Errno bsd_errno{};
Errno bsd_errno{};
};
struct SendToWork {
@@ -123,7 +123,7 @@ private:
std::span<const u8> message;
std::span<const u8> addr;
s32 ret{};
Network::Errno bsd_errno{};
Errno bsd_errno{};
};
void RegisterClient(HLERequestContext& ctx);
@@ -155,29 +155,29 @@ private:
template <typename Work>
void ExecuteWork(HLERequestContext& ctx, Work work);
std::pair<s32, Network::Errno> SocketImpl(Network::Domain domain, Network::Type type, Network::Protocol protocol);
std::pair<s32, Network::Errno> PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, s32 nfds, s32 timeout);
std::pair<s32, Network::Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer);
Network::Errno BindImpl(s32 fd, std::span<const u8> addr);
Network::Errno ConnectImpl(s32 fd, std::span<const u8> addr);
Network::Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer);
Network::Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer);
Network::Errno ListenImpl(s32 fd, s32 backlog);
std::pair<s32, Network::Errno> FcntlImpl(s32 fd, Network::FcntlCmd cmd, s32 arg);
Network::Errno GetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::vector<u8>& optval);
Network::Errno SetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval);
Network::Errno ShutdownImpl(s32 fd, s32 how);
std::pair<s32, Network::Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
std::pair<s32, Network::Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
std::pair<s32, Errno> SocketImpl(Domain domain, Type type, 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, Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer);
Errno BindImpl(s32 fd, std::span<const u8> addr);
Errno ConnectImpl(s32 fd, std::span<const u8> addr);
Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer);
Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer);
Errno ListenImpl(s32 fd, s32 backlog);
std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg);
Errno GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval);
Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval);
Errno ShutdownImpl(s32 fd, s32 how);
std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
std::vector<u8>& addr);
std::pair<s32, Network::Errno> SendImpl(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::pair<s32, 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::span<const u8> addr);
s32 FindFreeFileDescriptorHandle() noexcept;
bool IsFileDescriptorValid(s32 fd) const noexcept;
void BuildErrnoResponse(HLERequestContext& ctx, Network::Errno bsd_errno) const noexcept;
void BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noexcept;
static inline std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors{};
+44 -40
View File
@@ -102,39 +102,39 @@ static bool IsBlockedHost(const std::string& host) {
[&host](const std::string& domain) { return host.find(domain) != std::string::npos; });
}
static NetDbError GetAddrInfoErrorToNetDbError(Network::GetAddrInfoError result) {
static NetDbError GetAddrInfoErrorToNetDbError(GetAddrInfoError result) {
// These combinations have been verified on console (but are not
// exhaustive).
switch (result) {
case Network::GetAddrInfoError::SUCCESS:
case GetAddrInfoError::SUCCESS:
return NetDbError::Success;
case Network::GetAddrInfoError::AGAIN:
case GetAddrInfoError::AGAIN:
return NetDbError::TryAgain;
case Network::GetAddrInfoError::NODATA:
case GetAddrInfoError::NODATA:
return NetDbError::HostNotFound;
case Network::GetAddrInfoError::SERVICE:
case GetAddrInfoError::SERVICE:
return NetDbError::Success;
default:
return NetDbError::HostNotFound;
}
}
static Network::Errno GetAddrInfoErrorToErrno(Network::GetAddrInfoError result) {
static Errno GetAddrInfoErrorToErrno(GetAddrInfoError result) {
// These combinations have been verified on console (but are not
// exhaustive).
switch (result) {
case Network::GetAddrInfoError::SUCCESS:
case GetAddrInfoError::SUCCESS:
// Note: Sometimes a successful lookup sets errno to EADDRNOTAVAIL for
// some reason, but that doesn't seem useful to implement.
return Network::Errno::E_SUCCESS;
case Network::GetAddrInfoError::AGAIN:
return Network::Errno::E_SUCCESS;
case Network::GetAddrInfoError::NODATA:
return Network::Errno::E_SUCCESS;
case Network::GetAddrInfoError::SERVICE:
return Network::Errno::E_INVAL;
return Errno::SUCCESS;
case GetAddrInfoError::AGAIN:
return Errno::SUCCESS;
case GetAddrInfoError::NODATA:
return Errno::SUCCESS;
case GetAddrInfoError::SERVICE:
return Errno::INVAL;
default:
return Network::Errno::E_SUCCESS;
return Errno::SUCCESS;
}
}
@@ -155,7 +155,9 @@ static void AppendNulTerminated(std::vector<u8>& vec, std::string_view str) {
// host's gethostbyname, because it simplifies portability: e.g., getaddrinfo
// behaves the same on Unix and Windows, unlike gethostbyname where Windows
// doesn't implement h_errno.
static std::vector<u8> SerializeAddrInfoAsHostEnt(std::span<const Network::AddrInfo> vec, std::string_view host) {
static std::vector<u8> SerializeAddrInfoAsHostEnt(const std::vector<Network::AddrInfo>& vec,
std::string_view host) {
std::vector<u8> data;
// h_name: use the input hostname (append nul-terminated)
AppendNulTerminated(data, host);
@@ -163,12 +165,12 @@ static std::vector<u8> SerializeAddrInfoAsHostEnt(std::span<const Network::AddrI
Append<u32_be>(data, 0); // count of h_aliases
// (If the count were nonzero, the aliases would be appended as nul-terminated here.)
Append<u16_be>(data, u16(Network::Domain::INET)); // h_addrtype
Append<u16_be>(data, static_cast<u16>(Domain::INET)); // h_addrtype
Append<u16_be>(data, sizeof(Network::IPv4Address)); // h_length
// h_addr_list:
size_t count = vec.size();
ASSERT(count <= UINT32_MAX);
Append<u32_be>(data, u32(count));
Append<u32_be>(data, static_cast<uint32_t>(count));
for (const Network::AddrInfo& addrinfo : vec) {
// On the Switch, this is passed through htonl despite already being
// big-endian, so it ends up as little-endian.
@@ -180,7 +182,7 @@ static std::vector<u8> SerializeAddrInfoAsHostEnt(std::span<const Network::AddrI
return data;
}
static std::pair<u32, Network::GetAddrInfoError> GetHostByNameRequestImpl(HLERequestContext& ctx) {
static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestContext& ctx) {
struct InputParameters {
u8 use_nsd_resolve;
u32 cancel_handle;
@@ -203,7 +205,7 @@ static std::pair<u32, Network::GetAddrInfoError> GetHostByNameRequestImpl(HLEReq
// Prevent resolution of Nintendo servers
if (IsBlockedHost(host)) {
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
return {0, Network::GetAddrInfoError::AGAIN};
return {0, GetAddrInfoError::AGAIN};
}
auto res_v = Network::GetAddressInfo(host, /*service*/ std::nullopt);
@@ -211,10 +213,10 @@ static std::pair<u32, Network::GetAddrInfoError> GetHostByNameRequestImpl(HLEReq
const std::vector<u8> data = SerializeAddrInfoAsHostEnt(*res, host);
const u32 data_size = u32(data.size());
ctx.WriteBuffer(data, 0);
return {data_size, Network::GetAddrInfoError::SUCCESS};
return {data_size, GetAddrInfoError::SUCCESS};
}
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
return {0, *err};
return {0, Translate(*err)};
}
void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) {
@@ -222,7 +224,7 @@ void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) {
struct OutputParameters {
NetDbError netdb_error;
Network::Errno bsd_errno;
Errno bsd_errno;
u32 data_size;
};
static_assert(sizeof(OutputParameters) == 0xc);
@@ -242,7 +244,7 @@ void SFDNSRES::GetHostByNameRequestWithOptions(HLERequestContext& ctx) {
struct OutputParameters {
u32 data_size;
NetDbError netdb_error;
Network::Errno bsd_errno;
Errno bsd_errno;
};
static_assert(sizeof(OutputParameters) == 0xc);
@@ -255,23 +257,24 @@ void SFDNSRES::GetHostByNameRequestWithOptions(HLERequestContext& ctx) {
});
}
static std::vector<u8> SerializeAddrInfo(std::span<const Network::AddrInfo> vec, std::string_view host) {
static std::vector<u8> SerializeAddrInfo(const std::vector<Network::AddrInfo>& vec,
std::string_view host) {
// Adapted from
// https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190
std::vector<u8> data;
for (const Network::AddrInfo& addrinfo : vec) {
// serialized addrinfo:
Append<u32_be>(data, 0xBEEFCAFE); // magic
Append<u32_be>(data, 0); // ai_flags
Append<u32_be>(data, u32(addrinfo.family)); // ai_family
Append<u32_be>(data, u32(addrinfo.socket_type)); // ai_socktype
Append<u32_be>(data, u32(addrinfo.protocol)); // ai_protocol
Append<u32_be>(data, 0xBEEFCAFE); // magic
Append<u32_be>(data, 0); // ai_flags
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.family))); // ai_family
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.socket_type))); // ai_socktype
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.protocol))); // ai_protocol
Append<u32_be>(data, 16); // ai_addrlen
// ^ *not* sizeof(SerializedSockAddrIn), not that it matters since they're the same size
// ai_addr:
Append<u16_be>(data, u16(addrinfo.addr.family)); // sin_family
Append<u16_be>(data, static_cast<u16>(Translate(addrinfo.addr.family))); // sin_family
// On the Switch, the following fields are passed through htonl despite
// already being big-endian, so they end up as little-endian.
Append<u16_le>(data, addrinfo.addr.portno); // sin_port
@@ -293,7 +296,7 @@ static std::vector<u8> SerializeAddrInfo(std::span<const Network::AddrInfo> vec,
return data;
}
static std::pair<u32, Network::GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext& ctx) {
static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext& ctx) {
struct InputParameters {
u8 use_nsd_resolve;
u32 cancel_handle;
@@ -318,7 +321,7 @@ static std::pair<u32, Network::GetAddrInfoError> GetAddrInfoRequestImpl(HLEReque
// Prevent resolution of Nintendo servers
if (IsBlockedHost(host)) {
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
return {0, Network::GetAddrInfoError::AGAIN};
return {0, GetAddrInfoError::AGAIN};
}
std::optional<std::string> service = std::nullopt;
@@ -328,23 +331,24 @@ static std::pair<u32, Network::GetAddrInfoError> GetAddrInfoRequestImpl(HLEReque
}
// Serialized hints are also passed in a buffer, but are ignored for now.
auto res_v = Network::GetAddressInfo(host, service);
if (auto* res = std::get_if<std::vector<Network::AddrInfo>>(&res_v)) {
const std::vector<u8> data = SerializeAddrInfo(*res, host);
const u32 data_size = u32(data.size());
ctx.WriteBuffer(data, 0);
return {data_size, Network::GetAddrInfoError::SUCCESS};
return {data_size, GetAddrInfoError::SUCCESS};
}
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
return {0, *err};
return {0, Translate(*err)};
}
void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) {
auto [data_size, emu_gai_err] = GetAddrInfoRequestImpl(ctx);
struct OutputParameters {
Network::Errno bsd_errno;
Network::GetAddrInfoError gai_error;
Errno bsd_errno;
GetAddrInfoError gai_error;
u32 data_size;
};
static_assert(sizeof(OutputParameters) == 0xc);
@@ -360,7 +364,7 @@ void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) {
void SFDNSRES::GetGaiStringErrorRequest(HLERequestContext& ctx) {
struct InputParameters {
Network::GetAddrInfoError gai_errno;
GetAddrInfoError gai_errno;
};
IPC::RequestParser rp{ctx};
auto input = rp.PopRaw<InputParameters>();
@@ -378,9 +382,9 @@ void SFDNSRES::GetAddrInfoRequestWithOptions(HLERequestContext& ctx) {
struct OutputParameters {
u32 data_size;
Network::GetAddrInfoError gai_error;
GetAddrInfoError gai_error;
NetDbError netdb_error;
Network::Errno bsd_errno;
Errno bsd_errno;
};
static_assert(sizeof(OutputParameters) == 0x10);
+236 -1
View File
@@ -8,7 +8,6 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/internal_network/socket_types.h"
namespace Core {
class System;
@@ -16,6 +15,242 @@ class System;
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);
} // namespace Service::Sockets
@@ -15,42 +15,388 @@
namespace Service::Sockets {
const char* Translate(Network::GetAddrInfoError error) {
// https://android.googlesource.com/platform/bionic/+/085543106/libc/dns/net/getaddrinfo.c#254
Errno Translate(Network::Errno value) {
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 "Success";
return GetAddrInfoError::SUCCESS;
case Network::GetAddrInfoError::ADDRFAMILY:
return "Address family for hostname not supported";
return GetAddrInfoError::ADDRFAMILY;
case Network::GetAddrInfoError::AGAIN:
return "Temporary failure in name resolution";
return GetAddrInfoError::AGAIN;
case Network::GetAddrInfoError::BADFLAGS:
return "Invalid value for ai_flags";
return GetAddrInfoError::BADFLAGS;
case Network::GetAddrInfoError::FAIL:
return "Non-recoverable failure in name resolution";
return GetAddrInfoError::FAIL;
case Network::GetAddrInfoError::FAMILY:
return "ai_family not supported";
return GetAddrInfoError::FAMILY;
case Network::GetAddrInfoError::MEMORY:
return "Memory allocation failure";
return GetAddrInfoError::MEMORY;
case Network::GetAddrInfoError::NODATA:
return "No address associated with hostname";
return GetAddrInfoError::NODATA;
case Network::GetAddrInfoError::NONAME:
return "hostname nor servname provided, or not known";
return GetAddrInfoError::NONAME;
case Network::GetAddrInfoError::SERVICE:
return "servname not supported for ai_socktype";
return GetAddrInfoError::SERVICE;
case Network::GetAddrInfoError::SOCKTYPE:
return "ai_socktype not supported";
return GetAddrInfoError::SOCKTYPE;
case Network::GetAddrInfoError::SYSTEM:
return "System error returned in errno";
return GetAddrInfoError::SYSTEM;
case Network::GetAddrInfoError::BADHINTS:
return "Invalid value for hints";
return GetAddrInfoError::BADHINTS;
case Network::GetAddrInfoError::PROTOCOL:
return "Resolved protocol is unknown";
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
switch (error) {
case GetAddrInfoError::SUCCESS:
return "Success";
case GetAddrInfoError::ADDRFAMILY:
return "Address family for hostname not supported";
case GetAddrInfoError::AGAIN:
return "Temporary failure in name resolution";
case GetAddrInfoError::BADFLAGS:
return "Invalid value for ai_flags";
case GetAddrInfoError::FAIL:
return "Non-recoverable failure in name resolution";
case GetAddrInfoError::FAMILY:
return "ai_family not supported";
case GetAddrInfoError::MEMORY:
return "Memory allocation failure";
case GetAddrInfoError::NODATA:
return "No address associated with hostname";
case GetAddrInfoError::NONAME:
return "hostname nor servname provided, or not known";
case GetAddrInfoError::SERVICE:
return "servname not supported for ai_socktype";
case GetAddrInfoError::SOCKTYPE:
return "ai_socktype not supported";
case GetAddrInfoError::SYSTEM:
return "System error returned in errno";
case GetAddrInfoError::BADHINTS:
return "Invalid value for hints";
case GetAddrInfoError::PROTOCOL:
return "Resolved protocol is unknown";
case GetAddrInfoError::OVERFLOW_:
return "Argument buffer overflow";
default:
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
@@ -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-License-Identifier: GPL-2.0-or-later
@@ -14,7 +11,49 @@
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
const char* Translate(Network::GetAddrInfoError value);
const char* Translate(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
+2 -2
View File
@@ -132,7 +132,7 @@ public:
auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD_USA>("bsd:u");
if (bsd) {
auto err = bsd->CloseImpl(fd);
if (err != Network::Errno::E_SUCCESS) {
if (err != Service::Sockets::Errno::SUCCESS) {
LOG_ERROR(Service_SSL, "Failed to close duplicated socket: {}", err);
}
}
@@ -203,7 +203,7 @@ private:
const bool non_block = mode == IoMode::NonBlocking;
const Network::Errno error = socket->SetNonBlock(non_block);
if (error != Network::Errno::E_SUCCESS) {
if (error != Network::Errno::SUCCESS) {
LOG_ERROR(Service_SSL, "Failed to set native socket non-block flag to {}", non_block);
}
return ResultSuccess;
@@ -290,10 +290,10 @@ public:
BIO_clear_retry_flags(bio);
auto [actual, err] = self->socket->Send({reinterpret_cast<const u8*>(buf), len}, 0);
switch (err) {
case Network::Errno::E_SUCCESS:
case Network::Errno::SUCCESS:
*actual_p = actual;
return 1;
case Network::Errno::E_AGAIN:
case Network::Errno::AGAIN:
BIO_set_flags(bio, BIO_FLAGS_WRITE | BIO_FLAGS_SHOULD_RETRY);
return 0;
default:
@@ -309,13 +309,13 @@ public:
BIO_clear_retry_flags(bio);
auto [actual, err] = self->socket->Recv(0, {reinterpret_cast<u8*>(buf), len});
switch (err) {
case Network::Errno::E_SUCCESS:
case Network::Errno::SUCCESS:
*actual_p = actual;
if (actual == 0) {
self->got_read_eof = true;
}
return actual ? 1 : 0;
case Network::Errno::E_AGAIN:
case Network::Errno::AGAIN:
BIO_set_flags(bio, BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY);
return 0;
default:
@@ -146,11 +146,11 @@ public:
const auto read_span = std::span(ciphertext_read_buf).subspan(offset, fill_size);
const auto [actual, err] = socket->Recv(0, read_span);
switch (err) {
case Network::Errno::E_SUCCESS:
case Network::Errno::SUCCESS:
ASSERT(static_cast<size_t>(actual) <= fill_size);
ciphertext_read_buf.resize(offset + actual);
return ResultSuccess;
case Network::Errno::E_AGAIN:
case Network::Errno::AGAIN:
ciphertext_read_buf.resize(offset);
return ResultWouldBlock;
default:
@@ -165,12 +165,12 @@ public:
while (!ciphertext_write_buf.empty()) {
const auto [actual, err] = socket->Send(ciphertext_write_buf, 0);
switch (err) {
case Network::Errno::E_SUCCESS:
case Network::Errno::SUCCESS:
ASSERT(static_cast<size_t>(actual) <= ciphertext_write_buf.size());
ciphertext_write_buf.erase(ciphertext_write_buf.begin(),
ciphertext_write_buf.begin() + actual);
break;
case Network::Errno::E_AGAIN:
case Network::Errno::AGAIN:
return ResultWouldBlock;
default:
LOG_ERROR(Service_SSL, "Socket send returned Network::Errno {}", err);
@@ -195,7 +195,7 @@ public:
LOG_CRITICAL(Service_SSL, "op={}, offset={} actual={}/{} err={}", is_read, offset,
actual, cur.size(), static_cast<s32>(err));
switch (err) {
case Network::Errno::E_SUCCESS:
case Network::Errno::SUCCESS:
offset += actual;
if (actual == 0) {
ASSERT(is_read);
@@ -203,7 +203,7 @@ public:
return errSecEndOfData;
}
break;
case Network::Errno::E_AGAIN:
case Network::Errno::AGAIN:
*dataLength = offset;
return errSSLWouldBlock;
default:
+2 -3
View File
@@ -1,11 +1,10 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <atomic>
#include <common/socket_types.h>
#include <mutex>
#include "core/internal_network/socket_types.h"
namespace Network {
File diff suppressed because it is too large Load Diff
+63 -9
View File
@@ -13,7 +13,7 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/internal_network/socket_types.h"
#include "common/socket_types.h"
#ifdef _WIN32
#include <winsock2.h>
@@ -31,10 +31,68 @@ namespace Network {
class SocketBase;
class Socket;
struct HostPollFD {
SocketBase* socket = nullptr;
Network::PollEvents events = {};
Network::PollEvents revents = {};
/// Error code for network functions
enum class Errno {
SUCCESS,
BADF,
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 {
@@ -43,10 +101,6 @@ public:
~NetworkInstance();
};
sockaddr_in TranslateFromSockAddrIn(Network::SockAddrIn input);
Network::SockAddrIn TranslateToSockAddrIn(sockaddr_in input);
s32 TranslateMsgOptToNative(s32 flags);
void CancelPendingSocketOperations();
void RestartSocketOperations();
@@ -46,7 +46,7 @@ namespace Network {
#ifdef _WIN32
std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
ULONG buf_size = 0;
if (GetAdaptersAddresses(
@@ -66,7 +66,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
return {};
}
std::vector<NetworkInterface> result;
std::vector<Network::NetworkInterface> result;
for (auto* a = addrs; a; a = a->Next) {
@@ -89,7 +89,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
gw = reinterpret_cast<sockaddr_in*>(a->FirstGatewayAddress->Address.lpSockaddr)
->sin_addr;
result.emplace_back(NetworkInterface{
result.emplace_back(Network::NetworkInterface{
.name = Common::UTF16ToUTF8(std::wstring{a->FriendlyName}),
.ip_address = ip,
.subnet_mask = mask,
@@ -103,7 +103,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
#else
std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
#if defined(__ANDROID__) || defined(__linux__)
struct ifaddrs* ifaddr = nullptr;
if (getifaddrs(&ifaddr) != 0) {
@@ -135,7 +135,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
LOG_WARNING(Network, "\"/proc/net/route\" not found - using gateway 0");
}
#endif
std::vector<NetworkInterface> ifaces;
std::vector<Network::NetworkInterface> ifaces;
for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
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 */
@@ -149,7 +149,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
});
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;
ifaces.emplace_back(NetworkInterface{
ifaces.emplace_back(Network::NetworkInterface{
.name = ifa->ifa_name,
.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,
@@ -159,7 +159,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
freeifaddrs(ifaddr);
return ifaces;
#elif defined(__FreeBSD__)
std::vector<NetworkInterface> ifaces;
std::vector<Network::NetworkInterface> ifaces;
int fd = ::socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
if (fd < 0) {
LOG_ERROR(Network, "socket: {}", std::strerror(errno));
@@ -191,7 +191,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
size_t msglen = rtm->rtm_msglen - sizeof(*ifm);
char const* p = (char const*)(ifm + 1);
NetworkInterface iface{};
Network::NetworkInterface iface{};
for (size_t i = 0; i < RTAX_MAX; i++)
if ((ifm->ifm_addrs & (1 << i)) != 0) {
struct sockaddr const* sa = reinterpret_cast<struct sockaddr const*>(p);
@@ -220,7 +220,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
#endif // _WIN32
std::optional<NetworkInterface> GetSelectedNetworkInterface() {
std::optional<Network::NetworkInterface> GetSelectedNetworkInterface() {
auto const& sel_if = Settings::values.network_interface.GetValue();
if (auto const ifaces = Network::GetAvailableNetworkInterfaces(); ifaces.size() > 0) {
if (sel_if.empty())
-277
View File
@@ -1,277 +0,0 @@
// 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
-56
View File
@@ -1,56 +0,0 @@
// 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
+108 -70
View File
@@ -47,82 +47,71 @@ void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) {
received_packets.push(decompressed);
}
Errno ProxySocket::SetNonBlock(bool enable) {
blocking = !enable;
return Errno::E_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;
template <typename T>
Errno ProxySocket::SetSockOpt(SOCKET fd_, int option, T value) {
LOG_DEBUG(Network, "(STUBBED) called");
return Errno::SUCCESS;
}
Errno ProxySocket::Initialize(Domain domain, Type type, Protocol socket_protocol) {
protocol = socket_protocol;
return Errno::E_SUCCESS;
SetSockOpt(fd, SO_TYPE, type);
return Errno::SUCCESS;
}
std::pair<ProxySocket::AcceptResult, Errno> ProxySocket::Accept() {
LOG_WARNING(Network, "(stubbed) called");
return {AcceptResult{}, Errno::E_SUCCESS};
LOG_WARNING(Network, "(STUBBED) called");
return {AcceptResult{}, Errno::SUCCESS};
}
Errno ProxySocket::Connect(Network::SockAddrIn addr_in) {
LOG_WARNING(Network, "(stubbed) called");
return Errno::E_SUCCESS;
Errno ProxySocket::Connect(SockAddrIn addr_in) {
LOG_WARNING(Network, "(STUBBED) called");
return Errno::SUCCESS;
}
std::pair<Network::SockAddrIn, Errno> ProxySocket::GetPeerName() {
LOG_WARNING(Network, "(stubbed) called");
return {Network::SockAddrIn{}, Errno::E_SUCCESS};
std::pair<SockAddrIn, Errno> ProxySocket::GetPeerName() {
LOG_WARNING(Network, "(STUBBED) called");
return {SockAddrIn{}, Errno::SUCCESS};
}
std::pair<Network::SockAddrIn, Errno> ProxySocket::GetSockName() {
LOG_WARNING(Network, "(stubbed) called");
return {Network::SockAddrIn{}, Errno::E_SUCCESS};
std::pair<SockAddrIn, Errno> ProxySocket::GetSockName() {
LOG_WARNING(Network, "(STUBBED) called");
return {SockAddrIn{}, Errno::SUCCESS};
}
Errno ProxySocket::Bind(Network::SockAddrIn addr) {
Errno ProxySocket::Bind(SockAddrIn addr) {
if (is_bound) {
LOG_WARNING(Network, "Rebinding Socket is unimplemented!");
return Errno::E_SUCCESS;
return Errno::SUCCESS;
}
local_endpoint = addr;
is_bound = true;
return Errno::E_SUCCESS;
return Errno::SUCCESS;
}
Errno ProxySocket::Listen(s32 backlog) {
LOG_WARNING(Network, "(stubbed) called");
return Errno::E_SUCCESS;
LOG_WARNING(Network, "(STUBBED) called");
return Errno::SUCCESS;
}
Errno ProxySocket::Shutdown(ShutdownHow how) {
LOG_WARNING(Network, "(stubbed) called");
return Errno::E_SUCCESS;
LOG_WARNING(Network, "(STUBBED) called");
return Errno::SUCCESS;
}
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(message.size() < std::size_t((std::numeric_limits<int>::max)()));
return {s32(0), Errno::E_SUCCESS};
ASSERT(message.size() < static_cast<size_t>((std::numeric_limits<int>::max)()));
return {static_cast<s32>(0), Errno::SUCCESS};
}
std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) {
std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) {
ASSERT(flags == 0);
ASSERT(message.size() < std::size_t((std::numeric_limits<int>::max)()));
ASSERT(message.size() < static_cast<size_t>((std::numeric_limits<int>::max)()));
// TODO (flTobi): Verify the timeout behavior and break when connection is lost
const auto timestamp = std::chrono::steady_clock::now();
@@ -139,85 +128,88 @@ std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, Ne
}
if (!blocking) {
return {-1, Errno::E_AGAIN};
return {-1, Errno::AGAIN};
}
std::this_thread::yield();
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();
const auto time_diff_ms =
std::chrono::duration_cast<std::chrono::milliseconds>(time_diff).count();
if (time_diff_ms > timeout) {
return {-1, Errno::E_TIMEDOUT};
return {-1, Errno::TIMEDOUT};
}
}
}
std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::span<u8> message, Network::SockAddrIn* addr, std::size_t max_length) {
LOG_DEBUG(Network, "called");
std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::span<u8> message, SockAddrIn* addr,
std::size_t max_length) {
ProxyPacket& packet = received_packets.front();
if (addr) {
addr->len = 16;
addr->family = u8(Network::Domain::INET);
addr->family = Domain::INET;
addr->ip = packet.local_endpoint.ip; // The senders ip address
addr->portno = packet.local_endpoint.portno; // The senders port number
addr->zeroes = {};
}
bool peek = (flags & u32(Network::MsgOpt::PEEK)) != 0;
bool peek = (flags & FLAG_MSG_PEEK) != 0;
std::size_t read_bytes;
if (packet.data.size() > max_length) {
read_bytes = max_length;
std::memcpy(message.data(), packet.data.data(), max_length);
memcpy(message.data(), packet.data.data(), max_length);
if (protocol == Protocol::UDP) {
if (!peek) {
received_packets.pop();
}
return {-1, Errno::E_MSGSIZE};
return {-1, Errno::MSGSIZE};
} else if (protocol == Protocol::TCP) {
std::vector<u8> numArray(packet.data.size() - max_length);
std::copy(packet.data.begin() + max_length, packet.data.end(), std::back_inserter(numArray));
std::copy(packet.data.begin() + max_length, packet.data.end(),
std::back_inserter(numArray));
packet.data = numArray;
}
} else {
read_bytes = packet.data.size();
std::memcpy(message.data(), packet.data.data(), read_bytes);
memcpy(message.data(), packet.data.data(), read_bytes);
if (!peek) {
received_packets.pop();
}
}
return {u32(read_bytes), Errno::E_SUCCESS};
return {static_cast<u32>(read_bytes), Errno::SUCCESS};
}
std::pair<s32, Errno> ProxySocket::Send(std::span<const u8> message, int flags) {
LOG_WARNING(Network, "(stubbed) called");
ASSERT(message.size() < size_t((std::numeric_limits<int>::max)()));
LOG_WARNING(Network, "(STUBBED) called");
ASSERT(message.size() < static_cast<size_t>((std::numeric_limits<int>::max)()));
ASSERT(flags == 0);
return {s32(0), Errno::E_SUCCESS};
return {static_cast<s32>(0), Errno::SUCCESS};
}
void ProxySocket::SendPacket(ProxyPacket& packet) {
if (auto room_member = Network::GetRoomMember().lock()) {
if (room_member->IsConnected()) {
packet.data = Common::Compression::CompressDataZSTDDefault(packet.data.data(), packet.data.size());
packet.data = Common::Compression::CompressDataZSTDDefault(packet.data.data(),
packet.data.size());
room_member->SendProxyPacket(packet);
}
}
}
std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) {
LOG_DEBUG(Network, "called");
std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message,
const SockAddrIn* addr) {
ASSERT(flags == 0);
if (!is_bound) {
LOG_ERROR(Network, "ProxySocket is not bound!");
return {s32(message.size()), Errno::E_SUCCESS};
return {static_cast<s32>(message.size()), Errno::SUCCESS};
}
if (auto room_member = Network::GetRoomMember().lock()) {
if (!room_member->IsConnected()) {
return {s32(message.size()), Errno::E_SUCCESS};
return {static_cast<s32>(message.size()), Errno::SUCCESS};
}
}
@@ -242,20 +234,66 @@ std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message
SendPacket(packet);
return {s32(message.size()), Errno::E_SUCCESS};
return {static_cast<s32>(message.size()), Errno::SUCCESS};
}
Errno ProxySocket::Close() {
LOG_DEBUG(Network, "called");
fd = INVALID_SOCKET;
closed = true;
return Errno::E_SUCCESS;
return Errno::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() {
LOG_DEBUG(Network, "called");
return {Errno::E_SUCCESS, Errno::E_SUCCESS};
LOG_DEBUG(Network, "(STUBBED) called");
return {Errno::SUCCESS, Errno::SUCCESS};
}
bool ProxySocket::IsOpened() const {
+27 -10
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -29,13 +29,13 @@ public:
std::pair<AcceptResult, Errno> Accept() override;
Errno Connect(Network::SockAddrIn addr_in) override;
Errno Connect(SockAddrIn addr_in) override;
std::pair<Network::SockAddrIn, Errno> GetPeerName() override;
std::pair<SockAddrIn, Errno> GetPeerName() override;
std::pair<Network::SockAddrIn, Errno> GetSockName() override;
std::pair<SockAddrIn, Errno> GetSockName() override;
Errno Bind(Network::SockAddrIn addr) override;
Errno Bind(SockAddrIn addr) 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> RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) override;
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) override;
std::pair<s32, Errno> ReceivePacket(int flags, std::span<u8> message, Network::SockAddrIn* addr,
std::pair<s32, Errno> ReceivePacket(int flags, std::span<u8> message, SockAddrIn* addr,
std::size_t max_length);
std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
@@ -53,11 +53,28 @@ public:
void SendPacket(ProxyPacket& packet);
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 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 SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) override;
template <typename T>
Errno SetSockOpt(SOCKET fd, int option, T value);
std::pair<Errno, Errno> GetPendingError() override;
@@ -69,7 +86,7 @@ private:
u32 send_timeout = 0;
u32 receive_timeout = 0;
bool is_bound = false;
Network::SockAddrIn local_endpoint{};
SockAddrIn local_endpoint{};
bool blocking = true;
std::queue<ProxyPacket> received_packets;
Protocol protocol;
-470
View File
@@ -1,470 +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"
#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
+54 -19
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
@@ -30,7 +30,7 @@ public:
struct AcceptResult {
std::unique_ptr<SocketBase> socket;
Network::SockAddrIn sockaddr_in;
SockAddrIn sockaddr_in;
};
SocketBase() = default;
@@ -46,13 +46,13 @@ public:
virtual std::pair<AcceptResult, Errno> Accept() = 0;
virtual Errno Connect(Network::SockAddrIn addr_in) = 0;
virtual Errno Connect(SockAddrIn addr_in) = 0;
virtual std::pair<Network::SockAddrIn, Errno> GetPeerName() = 0;
virtual std::pair<SockAddrIn, Errno> GetPeerName() = 0;
virtual std::pair<Network::SockAddrIn, Errno> GetSockName() = 0;
virtual std::pair<SockAddrIn, Errno> GetSockName() = 0;
virtual Errno Bind(Network::SockAddrIn addr) = 0;
virtual Errno Bind(SockAddrIn addr) = 0;
virtual Errno Listen(s32 backlog) = 0;
@@ -60,16 +60,31 @@ public:
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, Network::SockAddrIn* addr) = 0;
virtual std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) = 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, const Network::SockAddrIn* addr) = 0;
virtual std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
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 SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) = 0;
virtual std::pair<Errno, Errno> GetPendingError() = 0;
virtual bool IsOpened() const = 0;
@@ -80,6 +95,7 @@ public:
return fd;
}
protected:
SOCKET fd = INVALID_SOCKET;
};
@@ -98,13 +114,13 @@ public:
std::pair<AcceptResult, Errno> Accept() override;
Errno Connect(Network::SockAddrIn addr_in) override;
Errno Connect(SockAddrIn addr_in) override;
std::pair<Network::SockAddrIn, Errno> GetPeerName() override;
std::pair<SockAddrIn, Errno> GetPeerName() override;
std::pair<Network::SockAddrIn, Errno> GetSockName() override;
std::pair<SockAddrIn, Errno> GetSockName() override;
Errno Bind(Network::SockAddrIn addr) override;
Errno Bind(SockAddrIn addr) override;
Errno Listen(s32 backlog) override;
@@ -112,19 +128,38 @@ public:
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> RecvFrom(int flags, std::span<u8> message, 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;
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
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 SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) override;
template <typename T>
Errno SetSockOpt(SOCKET fd, int option, T value);
std::pair<Errno, Errno> GetPendingError() override;
Errno GetSockOpt(Network::SocketLevel level, Network::OptName optname, std::span<u8> value);
template <typename T>
std::pair<T, Errno> GetSockOpt(SOCKET fd, int option);
bool IsOpened() const override;
@@ -134,6 +169,6 @@ private:
bool is_non_blocking = false;
};
std::pair<s32, Errno> Poll(std::span<Network::HostPollFD> poll_fds, s32 timeout);
std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout);
} // namespace Network
+4 -12
View File
@@ -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) {
switch (width) {
case 1:
memory.Write8(addr, static_cast<u8>(value));
break;
case 2:
memory.Write16(addr, static_cast<u16>(value));
break;
case 4:
memory.Write32(addr, static_cast<u32>(value));
break;
case 8:
memory.Write64(addr, value);
break;
case sizeof(u64): return memory.Write64(addr, value);
case sizeof(u32): return memory.Write32(addr, u32(value));
case sizeof(u16): return memory.Write16(addr, u16(value));
case sizeof(u8): return memory.Write8(addr, u8(value));
default:
UNREACHABLE();
}
@@ -82,7 +82,7 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const
auto fn = [](const A32::UserConfig& conf, A32::VAddr vaddr) -> T {
return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T {
return (conf.callbacks->*callback)(vaddr);
return (conf.callbacks->*callback)(vaddr, sizeof(T));
});
};
@@ -136,12 +136,9 @@ static void* EmitExclusiveWriteCallTrampoline(oaknut::CodeGenerator& code, const
oaknut::Label l_addr, l_this;
auto fn = [](const A32::UserConfig& conf, A32::VAddr vaddr, T value) -> u32 {
return conf.global_monitor->DoExclusiveOperation<T>(conf.processor_id, vaddr,
[&](T expected) -> bool {
return (conf.callbacks->*callback)(vaddr, value, expected);
})
? 0
: 1;
return conf.global_monitor->DoExclusiveOperation<T>(conf.processor_id, vaddr, [&](T expected) -> bool {
return (conf.callbacks->*callback)(vaddr, value, expected);
}) ? 0 : 1;
};
void* target = code.xptr<void*>();
@@ -179,26 +176,14 @@ void A32AddressSpace::EmitPrelude() {
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_32 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead32>(code, conf.callbacks);
prelude_info.read_memory_64 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead64>(code, conf.callbacks);
prelude_info.wrapped_read_memory_8 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead8>(code, conf.callbacks);
prelude_info.wrapped_read_memory_16 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead16>(code, conf.callbacks);
prelude_info.wrapped_read_memory_32 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead32>(code, conf.callbacks);
prelude_info.wrapped_read_memory_64 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead64>(code, conf.callbacks);
prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead8, u8>(code, conf);
prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead16, u16>(code, conf);
prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead32, u32>(code, conf);
prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead64, u64>(code, conf);
prelude_info.write_memory_8 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite8>(code, conf.callbacks);
prelude_info.write_memory_16 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite16>(code, conf.callbacks);
prelude_info.write_memory_32 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite32>(code, conf.callbacks);
prelude_info.write_memory_64 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite64>(code, conf.callbacks);
prelude_info.wrapped_write_memory_8 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite8>(code, conf.callbacks);
prelude_info.wrapped_write_memory_16 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite16>(code, conf.callbacks);
prelude_info.wrapped_write_memory_32 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite32>(code, conf.callbacks);
prelude_info.wrapped_write_memory_64 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite64>(code, conf.callbacks);
prelude_info.read_memory = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks);
prelude_info.wrapped_read_memory = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks);
prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u8>(code, conf);
prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u16>(code, conf);
prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u32>(code, conf);
prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u64>(code, conf);
prelude_info.write_memory = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks);
prelude_info.wrapped_write_memory = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks);
prelude_info.exclusive_write_memory_8 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive8, u8>(code, conf);
prelude_info.exclusive_write_memory_16 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive16, u16>(code, conf);
prelude_info.exclusive_write_memory_32 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive32, u32>(code, conf);
@@ -81,7 +81,7 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const
auto fn = [](const A64::UserConfig& conf, A64::VAddr vaddr) -> T {
return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T {
return (conf.callbacks->*callback)(vaddr);
return (conf.callbacks->*callback)(vaddr, sizeof(T));
});
};
@@ -133,14 +133,10 @@ static void* EmitExclusiveWriteCallTrampoline(oaknut::CodeGenerator& code, const
using namespace oaknut::util;
oaknut::Label l_addr, l_this;
auto fn = [](const A64::UserConfig& conf, A64::VAddr vaddr, T value) -> u32 {
return conf.global_monitor->DoExclusiveOperation<T>(conf.processor_id, vaddr,
[&](T expected) -> bool {
return (conf.callbacks->*callback)(vaddr, value, expected);
})
? 0
: 1;
return conf.global_monitor->DoExclusiveOperation<T>(conf.processor_id, vaddr, [&](T expected) -> bool {
return (conf.callbacks->*callback)(vaddr, value, expected);
}) ? 0 : 1;
};
void* target = code.xptr<void*>();
@@ -346,30 +342,18 @@ void A64AddressSpace::EmitPrelude() {
UnprotectCodeMemory();
prelude_info.read_memory_8 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead8>(code, conf.callbacks);
prelude_info.read_memory_16 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead16>(code, conf.callbacks);
prelude_info.read_memory_32 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead32>(code, conf.callbacks);
prelude_info.read_memory_64 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead64>(code, conf.callbacks);
prelude_info.read_memory = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead>(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_16 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead16>(code, conf.callbacks);
prelude_info.wrapped_read_memory_32 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead32>(code, conf.callbacks);
prelude_info.wrapped_read_memory_64 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead64>(code, conf.callbacks);
prelude_info.wrapped_read_memory = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead>(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_16 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead16, u16>(code, conf);
prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead32, u32>(code, conf);
prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead64, u64>(code, conf);
prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u8>(code, conf);
prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u16>(code, conf);
prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u32>(code, conf);
prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u64>(code, conf);
prelude_info.exclusive_read_memory_128 = EmitExclusiveRead128CallTrampoline(code, conf);
prelude_info.write_memory_8 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite8>(code, conf.callbacks);
prelude_info.write_memory_16 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite16>(code, conf.callbacks);
prelude_info.write_memory_32 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite32>(code, conf.callbacks);
prelude_info.write_memory_64 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite64>(code, conf.callbacks);
prelude_info.write_memory = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite>(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_16 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite16>(code, conf.callbacks);
prelude_info.wrapped_write_memory_32 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite32>(code, conf.callbacks);
prelude_info.wrapped_write_memory_64 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite64>(code, conf.callbacks);
prelude_info.wrapped_write_memory = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks);
prelude_info.wrapped_write_memory_128 = EmitWrappedWrite128CallTrampoline(code, conf.callbacks);
prelude_info.exclusive_write_memory_8 = EmitExclusiveWriteCallTrampoline<&A64::UserCallbacks::MemoryWriteExclusive8, u8>(code, conf);
prelude_info.exclusive_write_memory_16 = EmitExclusiveWriteCallTrampoline<&A64::UserCallbacks::MemoryWriteExclusive16, u16>(code, conf);
@@ -145,32 +145,42 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
case LinkTarget::ReturnFromRunCode:
c.B(prelude_info.return_from_run_code);
break;
// { this, vaddr, size }
case LinkTarget::ReadMemory8:
c.BL(prelude_info.read_memory_8);
c.LDR(X2, 1);
c.BL(prelude_info.read_memory);
break;
case LinkTarget::ReadMemory16:
c.BL(prelude_info.read_memory_16);
c.LDR(X2, 2);
c.BL(prelude_info.read_memory);
break;
case LinkTarget::ReadMemory32:
c.BL(prelude_info.read_memory_32);
c.LDR(X2, 4);
c.BL(prelude_info.read_memory);
break;
case LinkTarget::ReadMemory64:
c.BL(prelude_info.read_memory_64);
c.LDR(X2, 8);
c.BL(prelude_info.read_memory);
break;
case LinkTarget::ReadMemory128:
c.BL(prelude_info.read_memory_128);
break;
// { this, vaddr, size }
case LinkTarget::WrappedReadMemory8:
c.BL(prelude_info.wrapped_read_memory_8);
c.LDR(X2, 1);
c.BL(prelude_info.wrapped_read_memory);
break;
case LinkTarget::WrappedReadMemory16:
c.BL(prelude_info.wrapped_read_memory_16);
c.LDR(X2, 2);
c.BL(prelude_info.wrapped_read_memory);
break;
case LinkTarget::WrappedReadMemory32:
c.BL(prelude_info.wrapped_read_memory_32);
c.LDR(X2, 4);
c.BL(prelude_info.wrapped_read_memory);
break;
case LinkTarget::WrappedReadMemory64:
c.BL(prelude_info.wrapped_read_memory_64);
c.LDR(X2, 8);
c.BL(prelude_info.wrapped_read_memory);
break;
case LinkTarget::WrappedReadMemory128:
c.BL(prelude_info.wrapped_read_memory_128);
@@ -190,32 +200,41 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
case LinkTarget::ExclusiveReadMemory128:
c.BL(prelude_info.exclusive_read_memory_128);
break;
// { this, vaddr, value, size }
case LinkTarget::WriteMemory8:
c.BL(prelude_info.write_memory_8);
c.LDR(X3, 1);
c.BL(prelude_info.write_memory);
break;
case LinkTarget::WriteMemory16:
c.BL(prelude_info.write_memory_16);
c.LDR(X3, 2);
c.BL(prelude_info.write_memory);
break;
case LinkTarget::WriteMemory32:
c.BL(prelude_info.write_memory_32);
c.LDR(X3, 4);
c.BL(prelude_info.write_memory);
break;
case LinkTarget::WriteMemory64:
c.BL(prelude_info.write_memory_64);
c.LDR(X3, 8);
c.BL(prelude_info.write_memory);
break;
case LinkTarget::WriteMemory128:
c.BL(prelude_info.write_memory_128);
break;
case LinkTarget::WrappedWriteMemory8:
c.BL(prelude_info.wrapped_write_memory_8);
c.LDR(X3, 1);
c.BL(prelude_info.wrapped_write_memory);
break;
case LinkTarget::WrappedWriteMemory16:
c.BL(prelude_info.wrapped_write_memory_16);
c.LDR(X3, 2);
c.BL(prelude_info.wrapped_write_memory);
break;
case LinkTarget::WrappedWriteMemory32:
c.BL(prelude_info.wrapped_write_memory_32);
c.LDR(X3, 4);
c.BL(prelude_info.wrapped_write_memory);
break;
case LinkTarget::WrappedWriteMemory64:
c.BL(prelude_info.wrapped_write_memory_64);
c.LDR(X3, 8);
c.BL(prelude_info.wrapped_write_memory);
break;
case LinkTarget::WrappedWriteMemory128:
c.BL(prelude_info.wrapped_write_memory_128);
@@ -93,30 +93,18 @@ protected:
void* return_to_dispatcher;
void* return_from_run_code;
void* read_memory_8;
void* read_memory_16;
void* read_memory_32;
void* read_memory_64;
void* read_memory;
void* read_memory_128;
void* wrapped_read_memory_8;
void* wrapped_read_memory_16;
void* wrapped_read_memory_32;
void* wrapped_read_memory_64;
void* wrapped_read_memory;
void* wrapped_read_memory_128;
void* exclusive_read_memory_8;
void* exclusive_read_memory_16;
void* exclusive_read_memory_32;
void* exclusive_read_memory_64;
void* exclusive_read_memory_128;
void* write_memory_8;
void* write_memory_16;
void* write_memory_32;
void* write_memory_64;
void* write_memory;
void* write_memory_128;
void* wrapped_write_memory_8;
void* wrapped_write_memory_16;
void* wrapped_write_memory_32;
void* wrapped_write_memory_64;
void* wrapped_write_memory;
void* wrapped_write_memory_128;
void* exclusive_write_memory_8;
void* exclusive_write_memory_16;
@@ -31,16 +31,16 @@ using namespace Xbyak::util;
void A32EmitX64::GenFastmemFallbacks() {
const std::initializer_list<int> idxes{0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
const std::array<std::pair<size_t, ArgCallback>, 4> read_callbacks{{
{8, Devirtualize<&A32::UserCallbacks::MemoryRead8>(conf.callbacks)},
{16, Devirtualize<&A32::UserCallbacks::MemoryRead16>(conf.callbacks)},
{32, Devirtualize<&A32::UserCallbacks::MemoryRead32>(conf.callbacks)},
{64, Devirtualize<&A32::UserCallbacks::MemoryRead64>(conf.callbacks)},
{8, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)},
{16, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)},
{32, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)},
{64, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)},
}};
const std::array<std::pair<size_t, ArgCallback>, 4> write_callbacks{{
{8, Devirtualize<&A32::UserCallbacks::MemoryWrite8>(conf.callbacks)},
{16, Devirtualize<&A32::UserCallbacks::MemoryWrite16>(conf.callbacks)},
{32, Devirtualize<&A32::UserCallbacks::MemoryWrite32>(conf.callbacks)},
{64, Devirtualize<&A32::UserCallbacks::MemoryWrite64>(conf.callbacks)},
{8, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)},
{16, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)},
{32, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)},
{64, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)},
}};
const std::array<std::pair<size_t, ArgCallback>, 4> exclusive_write_callbacks{{
{8, Devirtualize<&A32::UserCallbacks::MemoryWriteExclusive8>(conf.callbacks)},
@@ -56,12 +56,12 @@ void A32EmitX64::GenFastmemFallbacks() {
code.align();
read_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>();
ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocRegIdx(value_idx));
// params = { this, vaddr, size }
if (vaddr_idx != code.ABI_PARAM2.getIdx()) {
code.mov(code.ABI_PARAM2, Xbyak::Reg64{vaddr_idx});
}
if (ordered) {
code.mfence();
}
code.mov(code.ABI_PARAM3, bitsize / CHAR_BIT);
if (ordered) code.mfence();
callback.EmitCall(code);
if (value_idx != code.ABI_RETURN.getIdx()) {
code.mov(Xbyak::Reg64{value_idx}, code.ABI_RETURN);
@@ -76,6 +76,7 @@ void A32EmitX64::GenFastmemFallbacks() {
code.align();
write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>();
ABI_PushCallerSaveRegistersAndAdjustStack(code);
// params = { this, vaddr, value, size }
if (vaddr_idx == code.ABI_PARAM3.getIdx() && value_idx == code.ABI_PARAM2.getIdx()) {
code.xchg(code.ABI_PARAM2, code.ABI_PARAM3);
} else if (vaddr_idx == code.ABI_PARAM3.getIdx()) {
@@ -92,10 +93,9 @@ void A32EmitX64::GenFastmemFallbacks() {
}
}
code.ZeroExtendFrom(bitsize, code.ABI_PARAM3);
code.mov(code.ABI_PARAM4, bitsize / CHAR_BIT);
callback.EmitCall(code);
if (ordered) {
code.mfence();
}
if (ordered) code.mfence();
ABI_PopCallerSaveRegistersAndAdjustStack(code);
code.ret();
PerfMapRegister(write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)], code.getCurr(), fmt::format("a32_write_fallback_{}", bitsize));
@@ -138,35 +138,35 @@ void A32EmitX64::GenFastmemFallbacks() {
#undef Axx
void A32EmitX64::EmitA32ReadMemory8(A32EmitContext& ctx, IR::Inst* inst) {
EmitMemoryRead<8, &A32::UserCallbacks::MemoryRead8>(ctx, inst);
EmitMemoryRead<8, &A32::UserCallbacks::MemoryRead>(ctx, 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) {
EmitMemoryRead<32, &A32::UserCallbacks::MemoryRead32>(ctx, inst);
EmitMemoryRead<32, &A32::UserCallbacks::MemoryRead>(ctx, 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) {
EmitMemoryWrite<8, &A32::UserCallbacks::MemoryWrite8>(ctx, inst);
EmitMemoryWrite<8, &A32::UserCallbacks::MemoryWrite>(ctx, 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) {
EmitMemoryWrite<32, &A32::UserCallbacks::MemoryWrite32>(ctx, inst);
EmitMemoryWrite<32, &A32::UserCallbacks::MemoryWrite>(ctx, 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*) {
@@ -175,33 +175,33 @@ void A32EmitX64::EmitA32ClearExclusive(A32EmitContext&, IR::Inst*) {
void A32EmitX64::EmitA32ExclusiveReadMemory8(A32EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<8, &A32::UserCallbacks::MemoryRead8>(ctx, inst);
EmitExclusiveReadMemoryInline<8, &A32::UserCallbacks::MemoryRead>(ctx, inst);
} else {
EmitExclusiveReadMemory<8, &A32::UserCallbacks::MemoryRead8>(ctx, inst);
EmitExclusiveReadMemory<8, &A32::UserCallbacks::MemoryRead>(ctx, inst);
}
}
void A32EmitX64::EmitA32ExclusiveReadMemory16(A32EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<16, &A32::UserCallbacks::MemoryRead16>(ctx, inst);
EmitExclusiveReadMemoryInline<16, &A32::UserCallbacks::MemoryRead>(ctx, inst);
} else {
EmitExclusiveReadMemory<16, &A32::UserCallbacks::MemoryRead16>(ctx, inst);
EmitExclusiveReadMemory<16, &A32::UserCallbacks::MemoryRead>(ctx, inst);
}
}
void A32EmitX64::EmitA32ExclusiveReadMemory32(A32EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<32, &A32::UserCallbacks::MemoryRead32>(ctx, inst);
EmitExclusiveReadMemoryInline<32, &A32::UserCallbacks::MemoryRead>(ctx, inst);
} else {
EmitExclusiveReadMemory<32, &A32::UserCallbacks::MemoryRead32>(ctx, inst);
EmitExclusiveReadMemory<32, &A32::UserCallbacks::MemoryRead>(ctx, inst);
}
}
void A32EmitX64::EmitA32ExclusiveReadMemory64(A32EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<64, &A32::UserCallbacks::MemoryRead64>(ctx, inst);
EmitExclusiveReadMemoryInline<64, &A32::UserCallbacks::MemoryRead>(ctx, inst);
} 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() {
const std::initializer_list<int> idxes{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
const std::array<std::pair<size_t, ArgCallback>, 4> read_callbacks{{
{8, Devirtualize<&A64::UserCallbacks::MemoryRead8>(conf.callbacks)},
{16, Devirtualize<&A64::UserCallbacks::MemoryRead16>(conf.callbacks)},
{32, Devirtualize<&A64::UserCallbacks::MemoryRead32>(conf.callbacks)},
{64, Devirtualize<&A64::UserCallbacks::MemoryRead64>(conf.callbacks)},
{8, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)},
{16, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)},
{32, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)},
{64, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)},
}};
const std::array<std::pair<size_t, ArgCallback>, 4> write_callbacks{{
{8, Devirtualize<&A64::UserCallbacks::MemoryWrite8>(conf.callbacks)},
{16, Devirtualize<&A64::UserCallbacks::MemoryWrite16>(conf.callbacks)},
{32, Devirtualize<&A64::UserCallbacks::MemoryWrite32>(conf.callbacks)},
{64, Devirtualize<&A64::UserCallbacks::MemoryWrite64>(conf.callbacks)},
{8, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)},
{16, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)},
{32, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)},
{64, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)},
}};
const std::array<std::pair<size_t, ArgCallback>, 4> exclusive_write_callbacks{{
{8, Devirtualize<&A64::UserCallbacks::MemoryWriteExclusive8>(conf.callbacks)},
@@ -204,12 +204,12 @@ void A64EmitX64::GenFastmemFallbacks() {
code.align();
read_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>();
ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocRegIdx(value_idx));
// params = { this, vaddr, size }
if (vaddr_idx != code.ABI_PARAM2.getIdx()) {
code.mov(code.ABI_PARAM2, Xbyak::Reg64{vaddr_idx});
}
if (ordered) {
code.mfence();
}
code.mov(code.ABI_PARAM3, bitsize / CHAR_BIT);
if (ordered) code.mfence();
callback.EmitCall(code);
if (value_idx != code.ABI_RETURN.getIdx()) {
code.mov(Xbyak::Reg64{value_idx}, code.ABI_RETURN);
@@ -224,6 +224,7 @@ void A64EmitX64::GenFastmemFallbacks() {
code.align();
write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>();
ABI_PushCallerSaveRegistersAndAdjustStack(code);
// params = { this, vaddr, value, size }
if (vaddr_idx == code.ABI_PARAM3.getIdx() && value_idx == code.ABI_PARAM2.getIdx()) {
code.xchg(code.ABI_PARAM2, code.ABI_PARAM3);
} else if (vaddr_idx == code.ABI_PARAM3.getIdx()) {
@@ -240,10 +241,9 @@ void A64EmitX64::GenFastmemFallbacks() {
}
}
code.ZeroExtendFrom(bitsize, code.ABI_PARAM3);
code.mov(code.ABI_PARAM4, bitsize / CHAR_BIT);
callback.EmitCall(code);
if (ordered) {
code.mfence();
}
if (ordered) code.mfence();
ABI_PopCallerSaveRegistersAndAdjustStack(code);
code.ret();
PerfMapRegister(write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)], code.getCurr(), fmt::format("a64_write_fallback_{}", bitsize));
@@ -286,19 +286,19 @@ void A64EmitX64::GenFastmemFallbacks() {
#undef Axx
void A64EmitX64::EmitA64ReadMemory8(A64EmitContext& ctx, IR::Inst* inst) {
EmitMemoryRead<8, &A64::UserCallbacks::MemoryRead8>(ctx, inst);
EmitMemoryRead<8, &A64::UserCallbacks::MemoryRead>(ctx, 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) {
EmitMemoryRead<32, &A64::UserCallbacks::MemoryRead32>(ctx, inst);
EmitMemoryRead<32, &A64::UserCallbacks::MemoryRead>(ctx, 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) {
@@ -306,23 +306,23 @@ void A64EmitX64::EmitA64ReadMemory128(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) {
EmitMemoryWrite<16, &A64::UserCallbacks::MemoryWrite16>(ctx, inst);
EmitMemoryWrite<16, &A64::UserCallbacks::MemoryWrite>(ctx, 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) {
EmitMemoryWrite<64, &A64::UserCallbacks::MemoryWrite64>(ctx, inst);
EmitMemoryWrite<64, &A64::UserCallbacks::MemoryWrite>(ctx, 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*) {
@@ -331,33 +331,33 @@ void A64EmitX64::EmitA64ClearExclusive(A64EmitContext&, IR::Inst*) {
void A64EmitX64::EmitA64ExclusiveReadMemory8(A64EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<8, &A64::UserCallbacks::MemoryRead8>(ctx, inst);
EmitExclusiveReadMemoryInline<8, &A64::UserCallbacks::MemoryRead>(ctx, inst);
} else {
EmitExclusiveReadMemory<8, &A64::UserCallbacks::MemoryRead8>(ctx, inst);
EmitExclusiveReadMemory<8, &A64::UserCallbacks::MemoryRead>(ctx, inst);
}
}
void A64EmitX64::EmitA64ExclusiveReadMemory16(A64EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<16, &A64::UserCallbacks::MemoryRead16>(ctx, inst);
EmitExclusiveReadMemoryInline<16, &A64::UserCallbacks::MemoryRead>(ctx, inst);
} else {
EmitExclusiveReadMemory<16, &A64::UserCallbacks::MemoryRead16>(ctx, inst);
EmitExclusiveReadMemory<16, &A64::UserCallbacks::MemoryRead>(ctx, inst);
}
}
void A64EmitX64::EmitA64ExclusiveReadMemory32(A64EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<32, &A64::UserCallbacks::MemoryRead32>(ctx, inst);
EmitExclusiveReadMemoryInline<32, &A64::UserCallbacks::MemoryRead>(ctx, inst);
} else {
EmitExclusiveReadMemory<32, &A64::UserCallbacks::MemoryRead32>(ctx, inst);
EmitExclusiveReadMemory<32, &A64::UserCallbacks::MemoryRead>(ctx, inst);
}
}
void A64EmitX64::EmitA64ExclusiveReadMemory64(A64EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<64, &A64::UserCallbacks::MemoryRead64>(ctx, inst);
EmitExclusiveReadMemoryInline<64, &A64::UserCallbacks::MemoryRead>(ctx, inst);
} 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.
* Copyright (c) 2022 MerryMage
* SPDX-License-Identifier: 0BSD
@@ -56,16 +59,13 @@ void AxxEmitX64::EmitMemoryRead(AxxEmitContext& ctx, IR::Inst* inst) {
// Neither fastmem nor page table: Use callbacks
if constexpr (bitsize == 128) {
ctx.reg_alloc.HostCall(code, nullptr, {}, args[1]);
if (ordered) {
code.mfence();
}
if (ordered) code.mfence();
code.CallFunction(memory_read_128);
ctx.reg_alloc.DefineValue(code, inst, xmm1);
} else {
ctx.reg_alloc.HostCall(code, inst, {}, args[1]);
if (ordered) {
code.mfence();
}
code.mov(code.ABI_PARAM3.cvt32(), bitsize / CHAR_BIT);
if (ordered) code.mfence();
Devirtualize<callback>(conf.callbacks).EmitCall(code);
code.ZeroExtendFrom(bitsize, code.ABI_RETURN);
}
@@ -148,12 +148,12 @@ void AxxEmitX64::EmitMemoryWrite(AxxEmitContext& ctx, IR::Inst* inst) {
ctx.reg_alloc.HostCall(code, nullptr);
code.CallFunction(memory_write_128);
} else {
// { this, vaddr, value, size }
ctx.reg_alloc.HostCall(code, nullptr, {}, args[1], args[2]);
code.mov(code.ABI_PARAM4.cvt32(), bitsize / CHAR_BIT);
Devirtualize<callback>(conf.callbacks).EmitCall(code);
}
if (ordered) {
code.mfence();
}
if (ordered) code.mfence();
EmitCheckMemoryAbort(ctx, inst);
return;
}
@@ -230,12 +230,11 @@ void AxxEmitX64::EmitExclusiveReadMemory(AxxEmitContext& ctx, IR::Inst* inst) {
if (ordered) {
code.mfence();
}
code.CallLambda(
[](AxxUserConfig& conf, Axx::VAddr vaddr) -> T {
return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T {
return (conf.callbacks->*callback)(vaddr);
});
code.CallLambda([](AxxUserConfig& conf, Axx::VAddr vaddr) -> T {
return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T {
return (conf.callbacks->*callback)(vaddr, bitsize / CHAR_BIT);
});
});
code.ZeroExtendFrom(bitsize, code.ABI_RETURN);
} else {
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(code);
@@ -250,12 +249,11 @@ void AxxEmitX64::EmitExclusiveReadMemory(AxxEmitContext& ctx, IR::Inst* inst) {
if (ordered) {
code.mfence();
}
code.CallLambda(
[](AxxUserConfig& conf, Axx::VAddr vaddr, Vector& ret) {
ret = conf.global_monitor->ReadAndMark<Vector>(conf.processor_id, vaddr, [&]() -> Vector {
return (conf.callbacks->*callback)(vaddr);
});
code.CallLambda([](AxxUserConfig& conf, Axx::VAddr vaddr, Vector& ret) {
ret = conf.global_monitor->ReadAndMark<Vector>(conf.processor_id, vaddr, [&]() -> Vector {
return (conf.callbacks->*callback)(vaddr);
});
});
code.movups(result, xword[rsp + ABI_SHADOW_SPACE]);
ctx.reg_alloc.ReleaseStackSpace(code, 16 + ABI_SHADOW_SPACE);
@@ -66,7 +66,9 @@ struct UserCallbacks : public TranslateCallbacks {
// All reads through this callback are 4-byte aligned.
// Memory must be interpreted as little endian.
std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) override { return 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.
// 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.
// 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::uint16_t MemoryRead16(VAddr vaddr) = 0;
virtual std::uint32_t MemoryRead32(VAddr vaddr) = 0;
virtual std::uint64_t MemoryRead64(VAddr vaddr) = 0;
virtual std::uint64_t MemoryRead(VAddr vaddr, std::size_t size) = 0;
// Writes through these callbacks may not be aligned.
virtual void MemoryWrite8(VAddr vaddr, std::uint8_t value) = 0;
virtual void MemoryWrite16(VAddr vaddr, std::uint16_t value) = 0;
virtual void MemoryWrite32(VAddr vaddr, std::uint32_t value) = 0;
virtual void MemoryWrite64(VAddr vaddr, std::uint64_t value) = 0;
virtual void MemoryWrite(VAddr vaddr, std::uint64_t value, std::size_t size) = 0;
// Writes through these callbacks may not be aligned.
virtual bool MemoryWriteExclusive8(VAddr /*vaddr*/, std::uint8_t /*value*/, std::uint8_t /*expected*/) { return false; }
@@ -89,20 +89,16 @@ struct UserCallbacks {
// All reads through this callback are 4-byte aligned.
// Memory must be interpreted as little endian.
virtual std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) { return 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.
virtual std::uint8_t MemoryRead8(VAddr vaddr) = 0;
virtual std::uint16_t MemoryRead16(VAddr vaddr) = 0;
virtual std::uint32_t MemoryRead32(VAddr vaddr) = 0;
virtual std::uint64_t MemoryRead64(VAddr vaddr) = 0;
virtual std::uint64_t MemoryRead(VAddr vaddr, std::size_t size) = 0;
virtual Vector MemoryRead128(VAddr vaddr) = 0;
// Writes through these callbacks may not be aligned.
virtual void MemoryWrite8(VAddr vaddr, std::uint8_t value) = 0;
virtual void MemoryWrite16(VAddr vaddr, std::uint16_t value) = 0;
virtual void MemoryWrite32(VAddr vaddr, std::uint32_t value) = 0;
virtual void MemoryWrite64(VAddr vaddr, std::uint64_t value) = 0;
virtual void MemoryWrite(VAddr vaddr, std::uint64_t value, std::size_t size) = 0;
virtual void MemoryWrite128(VAddr vaddr, Vector value) = 0;
// Writes through these callbacks may not be aligned.
+4 -4
View File
@@ -40,7 +40,7 @@ static void ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) {
if (inst.AreAllArgsImmediates()) {
const u32 vaddr = inst.GetArg(1).GetU32();
if (cb->IsReadOnlyMemory(vaddr)) {
const u8 value_from_memory = cb->MemoryRead8(vaddr);
const u8 value_from_memory = u8(cb->MemoryRead(vaddr, sizeof(u8)));
inst.ReplaceUsesWith(IR::Value{value_from_memory});
}
}
@@ -51,7 +51,7 @@ static void ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) {
if (inst.AreAllArgsImmediates()) {
const u32 vaddr = inst.GetArg(1).GetU32();
if (cb->IsReadOnlyMemory(vaddr)) {
const u16 value_from_memory = cb->MemoryRead16(vaddr);
const u16 value_from_memory = u16(cb->MemoryRead(vaddr, sizeof(u16)));
inst.ReplaceUsesWith(IR::Value{value_from_memory});
}
}
@@ -62,7 +62,7 @@ static void ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) {
if (inst.AreAllArgsImmediates()) {
const u32 vaddr = inst.GetArg(1).GetU32();
if (cb->IsReadOnlyMemory(vaddr)) {
const u32 value_from_memory = cb->MemoryRead32(vaddr);
const u32 value_from_memory = u32(cb->MemoryRead(vaddr, sizeof(u32)));
inst.ReplaceUsesWith(IR::Value{value_from_memory});
}
}
@@ -73,7 +73,7 @@ static void ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) {
if (inst.AreAllArgsImmediates()) {
const u32 vaddr = inst.GetArg(1).GetU32();
if (cb->IsReadOnlyMemory(vaddr)) {
const u64 value_from_memory = cb->MemoryRead64(vaddr);
const u64 value_from_memory = u64(cb->MemoryRead(vaddr, sizeof(u64)));
inst.ReplaceUsesWith(IR::Value{value_from_memory});
}
}
+1 -1
View File
@@ -503,7 +503,7 @@ TEST_CASE("Fuzz Thumb32 instructions set", "[JitX64][Thumb][Thumb32]") {
}
}
TEST_CASE("Verify fix for off by one error in MemoryRead32 worked", "[Thumb][Thumb16]") {
TEST_CASE("Verify fix for off by one error in MemoryRead<32> worked", "[Thumb][Thumb16]") {
ThumbTestEnv test_env;
// Prepare test subjects
@@ -553,9 +553,9 @@ TEST_CASE("arm: Memory access (fastmem)", "[arm][A32]") {
memset(backing_memory, 0, memory_size);
memcpy(backing_memory + 0x100, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", 57);
env.MemoryWrite32(0, 0xE5904000); // LDR R4, [R0]
env.MemoryWrite32(4, 0xE5814000); // STR R4, [R1]
env.MemoryWrite32(8, 0xEAFFFFFE); // B .
env.MemoryWrite(0, 0xE5904000, sizeof(u32)); // LDR R4, [R0]
env.MemoryWrite(4, 0xE5814000, sizeof(u32)); // STR R4, [R1]
env.MemoryWrite(8, 0xEAFFFFFE, sizeof(u32)); // B .
jit.Regs()[0] = 0x100;
jit.Regs()[1] = 0x1F0;
jit.Regs()[15] = 0; // PC = 0
+66 -61
View File
@@ -16,6 +16,7 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "dynarmic/frontend/A32/translate/translate_callbacks.h"
#include "dynarmic/interface/A32/a32.h"
template<typename InstructionType_, u32 infinite_loop_u32>
@@ -59,42 +60,51 @@ public:
return infinite_loop_u32; // B .
}
std::uint8_t MemoryRead8(u32 vaddr) override {
if (IsInCodeMem(vaddr)) {
return reinterpret_cast<u8*>(code_mem.data())[vaddr];
u64 MemoryRead(u32 vaddr, size_t size) override {
switch (size) {
case sizeof(u64):
return MemoryRead(vaddr, sizeof(u32))
| MemoryRead(vaddr + sizeof(u32), sizeof(u32)) << 32;
case sizeof(u32):
return MemoryRead(vaddr, sizeof(u16))
| MemoryRead(vaddr + sizeof(u16), sizeof(u16)) << 16;
case sizeof(u16):
return MemoryRead(vaddr, sizeof(u8))
| MemoryRead(vaddr + sizeof(u8), sizeof(u8)) << 8;
case sizeof(u8): {
if (IsInCodeMem(vaddr))
return reinterpret_cast<u8*>(code_mem.data())[vaddr];
if (auto iter = modified_memory.find(vaddr); iter != modified_memory.end())
return iter->second;
return u8(vaddr);
}
if (auto iter = modified_memory.find(vaddr); iter != modified_memory.end()) {
return iter->second;
default:
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 {
if (vaddr < code_mem.size() * sizeof(u32)) {
code_mem_modified_by_guest = true;
void MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) override {
switch (size) {
case sizeof(u64):
MemoryWrite(vaddr, u32(value), sizeof(u32));
MemoryWrite(vaddr + 4, u32(value >> 32), sizeof(u32));
break;
case sizeof(u32):
MemoryWrite(vaddr, u16(value), sizeof(u16));
MemoryWrite(vaddr + 2, u16(value >> 16), sizeof(u16));
break;
case sizeof(u16):
MemoryWrite(vaddr, u8(value), sizeof(u8));
MemoryWrite(vaddr + 1, u8(value >> 8), sizeof(u8));
break;
case sizeof(u8):
if (vaddr < code_mem.size() * sizeof(u32))
code_mem_modified_by_guest = true;
modified_memory[vaddr] = value;
break;
default:
std::abort();
}
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 {
@@ -143,46 +153,41 @@ public:
return read<std::uint32_t>(vaddr);
}
std::uint8_t MemoryRead8(std::uint32_t vaddr) override {
return read<std::uint8_t>(vaddr);
}
std::uint16_t MemoryRead16(std::uint32_t vaddr) override {
return read<std::uint16_t>(vaddr);
}
std::uint32_t MemoryRead32(std::uint32_t vaddr) override {
return read<std::uint32_t>(vaddr);
}
std::uint64_t MemoryRead64(std::uint32_t vaddr) override {
return read<std::uint64_t>(vaddr);
u64 MemoryRead(u32 vaddr, size_t size) override {
switch (size) {
case sizeof(u64): return read<u64>(vaddr);
case sizeof(u32): return read<u32>(vaddr);
case sizeof(u16): return read<u16>(vaddr);
case sizeof(u8): return read<u8>(vaddr);
default:
std::abort();
}
}
void MemoryWrite8(std::uint32_t vaddr, std::uint8_t value) override {
write(vaddr, value);
}
void MemoryWrite16(std::uint32_t vaddr, std::uint16_t value) override {
write(vaddr, value);
}
void MemoryWrite32(std::uint32_t vaddr, std::uint32_t value) override {
write(vaddr, value);
}
void MemoryWrite64(std::uint32_t vaddr, std::uint64_t value) override {
write(vaddr, value);
void MemoryWrite(Dynarmic::A32::VAddr vaddr, std::uint64_t value, size_t size) override {
switch (size) {
case sizeof(u64): return write<u64>(vaddr, u64(value));
case sizeof(u32): return write<u32>(vaddr, u32(value));
case sizeof(u16): return write<u16>(vaddr, u16(value));
case sizeof(u8): return write<u8>(vaddr, u8(value));
default: std::abort();
}
}
bool MemoryWriteExclusive8(std::uint32_t vaddr, std::uint8_t value, [[maybe_unused]] std::uint8_t expected) override {
MemoryWrite8(vaddr, value);
bool MemoryWriteExclusive8(Dynarmic::A32::VAddr vaddr, std::uint8_t value, [[maybe_unused]] std::uint8_t expected) override {
MemoryWrite(vaddr, value, sizeof(u8));
return true;
}
bool MemoryWriteExclusive16(std::uint32_t vaddr, std::uint16_t value, [[maybe_unused]] std::uint16_t expected) override {
MemoryWrite16(vaddr, value);
bool MemoryWriteExclusive16(Dynarmic::A32::VAddr vaddr, std::uint16_t value, [[maybe_unused]] std::uint16_t expected) override {
MemoryWrite(vaddr, value, sizeof(u16));
return true;
}
bool MemoryWriteExclusive32(std::uint32_t vaddr, std::uint32_t value, [[maybe_unused]] std::uint32_t expected) override {
MemoryWrite32(vaddr, value);
bool MemoryWriteExclusive32(Dynarmic::A32::VAddr vaddr, std::uint32_t value, [[maybe_unused]] std::uint32_t expected) override {
MemoryWrite(vaddr, value, sizeof(u32));
return true;
}
bool MemoryWriteExclusive64(std::uint32_t vaddr, std::uint64_t value, [[maybe_unused]] std::uint64_t expected) override {
MemoryWrite64(vaddr, value);
bool MemoryWriteExclusive64(Dynarmic::A32::VAddr vaddr, std::uint64_t value, [[maybe_unused]] std::uint64_t expected) override {
MemoryWrite(vaddr, value, sizeof(u64));
return true;
}
File diff suppressed because one or more lines are too long
+44 -37
View File
@@ -25,48 +25,55 @@ public:
u64 ticks_left = 0;
::Common::unordered_map<u64, u8> memory{};
u8 MemoryRead8(u64 vaddr) override {
return memory[vaddr];
}
u16 MemoryRead16(u64 vaddr) override {
return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8;
}
u32 MemoryRead32(u64 vaddr) override {
return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16;
}
u64 MemoryRead64(u64 vaddr) override {
return u64(MemoryRead32(vaddr)) | u64(MemoryRead32(vaddr + 4)) << 32;
u64 MemoryRead(u64 vaddr, size_t size) override {
switch (size) {
case sizeof(u64):
return MemoryRead(vaddr, sizeof(u32))
| MemoryRead(vaddr + sizeof(u32), sizeof(u32)) << 32;
case sizeof(u32):
return MemoryRead(vaddr, sizeof(u16))
| MemoryRead(vaddr + sizeof(u16), sizeof(u16)) << 16;
case sizeof(u16):
return MemoryRead(vaddr, sizeof(u8))
| MemoryRead(vaddr + sizeof(u8), sizeof(u8)) << 8;
case sizeof(u8):
return memory[vaddr];
default:
std::abort();
}
}
std::array<u64, 2> MemoryRead128(u64 vaddr) override {
return {MemoryRead64(vaddr), MemoryRead64(vaddr + 8)};
return {
MemoryRead(vaddr, sizeof(u64)),
MemoryRead(vaddr + sizeof(u64), sizeof(u64))
};
}
void MemoryWrite8(u64 vaddr, u8 value) override {
memory[vaddr] = value;
void MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, size_t size) override {
switch (size) {
case sizeof(u64):
MemoryWrite(vaddr, u32(value), sizeof(u32));
MemoryWrite(vaddr + 4, u32(value >> 32), sizeof(u32));
break;
case sizeof(u32):
MemoryWrite(vaddr, u16(value), sizeof(u16));
MemoryWrite(vaddr + 2, u16(value >> 16), sizeof(u16));
break;
case sizeof(u16):
MemoryWrite(vaddr, u8(value), sizeof(u8));
MemoryWrite(vaddr + 1, u8(value >> 8), sizeof(u8));
break;
case sizeof(u8):
memory[vaddr] = value;
break;
default:
std::abort();
}
}
void MemoryWrite16(u64 vaddr, u16 value) override {
MemoryWrite8(vaddr, u8(value));
MemoryWrite8(vaddr + 1, u8(value >> 8));
}
void MemoryWrite32(u64 vaddr, u32 value) override {
MemoryWrite16(vaddr, u16(value));
MemoryWrite16(vaddr + 2, u16(value >> 16));
}
void MemoryWrite64(u64 vaddr, u64 value) override {
MemoryWrite32(vaddr, u32(value));
MemoryWrite32(vaddr + 4, u32(value >> 32));
}
void MemoryWrite128(u64 vaddr, std::array<u64, 2> value) override {
MemoryWrite64(vaddr, value[0]);
MemoryWrite64(vaddr + 8, value[1]);
MemoryWrite(vaddr, value[0], sizeof(u64));
MemoryWrite(vaddr + 8, value[1], sizeof(u64));
}
void CallSVC(u32) override {
@@ -135,9 +142,9 @@ TEST_CASE("A64: fibonacci", "[a64]") {
code.RET();
for (size_t i = 0; i < 1024; i++) {
env.MemoryWrite32(i * 4, instructions[i]);
env.MemoryWrite(i * 4, instructions[i], sizeof(u32));
}
env.MemoryWrite32(8888, 0xd4200000);
env.MemoryWrite(8888, 0xd4200000, sizeof(u32));
cpu.SetRegister(30, 8888);
cpu.SetRegister(0, 10);
+76 -66
View File
@@ -12,6 +12,7 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "dynarmic/interface/A64/a64.h"
#include "dynarmic/interface/A64/config.h"
using Vector = Dynarmic::A64::Vector;
@@ -34,64 +35,79 @@ public:
return code_mem[index];
}
std::uint8_t MemoryRead8(u64 vaddr) override {
if (IsInCodeMem(vaddr)) {
return reinterpret_cast<u8*>(code_mem.data())[vaddr - code_mem_start_address];
u64 MemoryRead(u64 vaddr, size_t size) override {
switch (size) {
case sizeof(u64):
return MemoryRead(vaddr, sizeof(u32))
| MemoryRead(vaddr + sizeof(u32), sizeof(u32)) << 32;
case sizeof(u32):
return MemoryRead(vaddr, sizeof(u16))
| MemoryRead(vaddr + sizeof(u16), sizeof(u16)) << 16;
case sizeof(u16):
return MemoryRead(vaddr, sizeof(u8))
| MemoryRead(vaddr + sizeof(u8), sizeof(u8)) << 8;
case sizeof(u8): {
if (IsInCodeMem(vaddr))
return reinterpret_cast<u8*>(code_mem.data())[vaddr - code_mem_start_address];
if (auto const it = modified_memory.find(vaddr); it != modified_memory.end())
return it->second;
return u8(vaddr);
}
default:
std::abort();
}
if (auto const it = modified_memory.find(vaddr); it != modified_memory.end())
return it->second;
return u8(vaddr);
}
std::uint16_t MemoryRead16(u64 vaddr) override {
return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8;
}
std::uint32_t MemoryRead32(u64 vaddr) override {
return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16;
}
std::uint64_t MemoryRead64(u64 vaddr) override {
return u64(MemoryRead32(vaddr)) | u64(MemoryRead32(vaddr + 4)) << 32;
}
Vector MemoryRead128(u64 vaddr) override {
return {MemoryRead64(vaddr), MemoryRead64(vaddr + 8)};
}
void MemoryWrite8(u64 vaddr, std::uint8_t value) override {
if (IsInCodeMem(vaddr)) {
code_mem_modified_by_guest = true;
Vector MemoryRead128(u64 vaddr) override {
return {
MemoryRead(vaddr, sizeof(u64)),
MemoryRead(vaddr + 8, sizeof(u64))
};
}
void MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, size_t size) override {
switch (size) {
case sizeof(u64):
MemoryWrite(vaddr, u32(value), sizeof(u32));
MemoryWrite(vaddr + 4, u32(value >> 32), sizeof(u32));
break;
case sizeof(u32):
MemoryWrite(vaddr, u16(value), sizeof(u16));
MemoryWrite(vaddr + 2, u16(value >> 16), sizeof(u16));
break;
case sizeof(u16):
MemoryWrite(vaddr, u8(value), sizeof(u8));
MemoryWrite(vaddr + 1, u8(value >> 8), sizeof(u8));
break;
case sizeof(u8):
if (IsInCodeMem(vaddr)) {
code_mem_modified_by_guest = true;
}
modified_memory[vaddr] = value;
break;
default:
std::abort();
}
modified_memory[vaddr] = value;
}
void MemoryWrite16(u64 vaddr, std::uint16_t value) override {
MemoryWrite8(vaddr, u8(value));
MemoryWrite8(vaddr + 1, u8(value >> 8));
}
void MemoryWrite32(u64 vaddr, std::uint32_t value) override {
MemoryWrite16(vaddr, u16(value));
MemoryWrite16(vaddr + 2, u16(value >> 16));
}
void MemoryWrite64(u64 vaddr, std::uint64_t value) override {
MemoryWrite32(vaddr, u32(value));
MemoryWrite32(vaddr + 4, u32(value >> 32));
}
void MemoryWrite128(u64 vaddr, Vector value) override {
MemoryWrite64(vaddr, value[0]);
MemoryWrite64(vaddr + 8, value[1]);
MemoryWrite(vaddr, value[0], sizeof(u64));
MemoryWrite(vaddr + 8, value[1], sizeof(u64));
}
bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, [[maybe_unused]] std::uint8_t expected) override {
MemoryWrite8(vaddr, value);
MemoryWrite(vaddr, value, sizeof(u8));
return true;
}
bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, [[maybe_unused]] std::uint16_t expected) override {
MemoryWrite16(vaddr, value);
MemoryWrite(vaddr, value, sizeof(u16));
return true;
}
bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, [[maybe_unused]] std::uint32_t expected) override {
MemoryWrite32(vaddr, value);
MemoryWrite(vaddr, value, sizeof(u32));
return true;
}
bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, [[maybe_unused]] std::uint64_t expected) override {
MemoryWrite64(vaddr, value);
MemoryWrite(vaddr, value, sizeof(u64));
return true;
}
bool MemoryWriteExclusive128(u64 vaddr, Vector value, [[maybe_unused]] Vector expected) override {
@@ -145,52 +161,46 @@ public:
return read<std::uint32_t>(vaddr);
}
std::uint8_t MemoryRead8(u64 vaddr) override {
return read<std::uint8_t>(vaddr);
}
std::uint16_t MemoryRead16(u64 vaddr) override {
return read<std::uint16_t>(vaddr);
}
std::uint32_t MemoryRead32(u64 vaddr) override {
return read<std::uint32_t>(vaddr);
}
std::uint64_t MemoryRead64(u64 vaddr) override {
return read<std::uint64_t>(vaddr);
u64 MemoryRead(u64 vaddr, size_t size) override {
switch (size) {
case sizeof(u64): return read<u64>(vaddr);
case sizeof(u32): return read<u32>(vaddr);
case sizeof(u16): return read<u16>(vaddr);
case sizeof(u8): return read<u8>(vaddr);
default: std::abort();
}
}
Vector MemoryRead128(u64 vaddr) override {
return read<Vector>(vaddr);
}
void MemoryWrite8(u64 vaddr, std::uint8_t value) override {
write(vaddr, value);
}
void MemoryWrite16(u64 vaddr, std::uint16_t value) override {
write(vaddr, value);
}
void MemoryWrite32(u64 vaddr, std::uint32_t value) override {
write(vaddr, value);
}
void MemoryWrite64(u64 vaddr, std::uint64_t value) override {
write(vaddr, value);
void MemoryWrite(u64 vaddr, std::uint64_t value, size_t size) override {
switch (size) {
case sizeof(u64): return write<u64>(vaddr, u64(value));
case sizeof(u32): return write<u32>(vaddr, u32(value));
case sizeof(u16): return write<u16>(vaddr, u16(value));
case sizeof(u8): return write<u8>(vaddr, u8(value));
default: std::abort();
}
}
void MemoryWrite128(u64 vaddr, Vector value) override {
write(vaddr, value);
}
bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, [[maybe_unused]] std::uint8_t expected) override {
MemoryWrite8(vaddr, value);
MemoryWrite(vaddr, value, sizeof(u8));
return true;
}
bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, [[maybe_unused]] std::uint16_t expected) override {
MemoryWrite16(vaddr, value);
MemoryWrite(vaddr, value, sizeof(u16));
return true;
}
bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, [[maybe_unused]] std::uint32_t expected) override {
MemoryWrite32(vaddr, value);
MemoryWrite(vaddr, value, sizeof(u32));
return true;
}
bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, [[maybe_unused]] std::uint64_t expected) override {
MemoryWrite64(vaddr, value);
MemoryWrite(vaddr, value, sizeof(u64));
return true;
}
bool MemoryWriteExclusive128(u64 vaddr, Vector value, [[maybe_unused]] Vector expected) override {
+42 -30
View File
@@ -18,6 +18,7 @@
#include <fmt/format.h>
#include <fmt/ostream.h>
#include <fmt/ranges.h>
#include "dynarmic/frontend/A32/translate/translate_callbacks.h"
#include "dynarmic/mcl/bit.hpp"
#include "common/common_types.h"
@@ -118,36 +119,47 @@ public:
u64 ticks_left = 0;
std::map<u32, u8> memory;
std::uint8_t MemoryRead8(u32 vaddr) override {
if (auto iter = memory.find(vaddr); iter != memory.end()) {
return iter->second;
u64 MemoryRead(Dynarmic::A32::VAddr vaddr, size_t size) override {
switch (size) {
case sizeof(u64):
return MemoryRead(vaddr, sizeof(u32))
| MemoryRead(vaddr + sizeof(u32), sizeof(u32)) << 32;
case sizeof(u32):
return MemoryRead(vaddr, sizeof(u16))
| MemoryRead(vaddr + sizeof(u16), sizeof(u16)) << 16;
case sizeof(u16):
return MemoryRead(vaddr, sizeof(u8))
| MemoryRead(vaddr + sizeof(u8), sizeof(u8)) << 8;
case sizeof(u8): {
if (auto const it = memory.find(vaddr); it != memory.end())
return it->second;
return 0;
}
default:
std::abort();
}
return 0;
}
std::uint16_t MemoryRead16(u32 vaddr) override {
return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8;
}
std::uint32_t MemoryRead32(u32 vaddr) override {
return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16;
}
std::uint64_t MemoryRead64(u32 vaddr) override {
return u64(MemoryRead32(vaddr)) | u64(MemoryRead32(vaddr + 4)) << 32;
}
void MemoryWrite8(u32 vaddr, std::uint8_t value) override {
memory[vaddr] = value;
}
void MemoryWrite16(u32 vaddr, std::uint16_t value) override {
MemoryWrite8(vaddr, static_cast<u8>(value));
MemoryWrite8(vaddr + 1, static_cast<u8>(value >> 8));
}
void MemoryWrite32(u32 vaddr, std::uint32_t value) override {
MemoryWrite16(vaddr, static_cast<u16>(value));
MemoryWrite16(vaddr + 2, static_cast<u16>(value >> 16));
}
void MemoryWrite64(u32 vaddr, std::uint64_t value) override {
MemoryWrite32(vaddr, static_cast<u32>(value));
MemoryWrite32(vaddr + 4, static_cast<u32>(value >> 32));
void MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) override {
switch (size) {
case sizeof(u64):
MemoryWrite(vaddr, u32(value), sizeof(u32));
MemoryWrite(vaddr + 4, u32(value >> 32), sizeof(u32));
break;
case sizeof(u32):
MemoryWrite(vaddr, u16(value), sizeof(u16));
MemoryWrite(vaddr + 2, u16(value >> 16), sizeof(u16));
break;
case sizeof(u16):
MemoryWrite(vaddr, u8(value), sizeof(u8));
MemoryWrite(vaddr + 1, u8(value >> 8), sizeof(u8));
break;
case sizeof(u8):
memory[vaddr] = value;
break;
default:
std::abort();
}
}
void CallSVC(std::uint32_t swi) override {
@@ -231,7 +243,7 @@ void ExecuteA32Instruction(u32 instruction) {
if (const auto address = get_value()) {
fmt::print("value: ");
if (const auto value = get_value()) {
env.MemoryWrite32(*address, *value);
env.MemoryWrite(*address, *value, sizeof(u32));
fmt::print("> mem[{:#08x}] = {:#08x}\n", *address, *value);
}
}
@@ -247,8 +259,8 @@ void ExecuteA32Instruction(u32 instruction) {
cpu.SetFpscr(fpscr);
const u32 initial_pc = regs[15];
env.MemoryWrite32(initial_pc + 0, instruction);
env.MemoryWrite32(initial_pc + 4, 0xEAFFFFFE); // B +0
env.MemoryWrite(initial_pc + 0, instruction, sizeof(u32));
env.MemoryWrite(initial_pc + 4, 0xEAFFFFFE, sizeof(u32)); // B +0
cpu.Run();
fmt::print("{}", fmt::join(cpu.Disassemble(), "\n"));
+2 -19
View File
@@ -290,7 +290,7 @@ bool A32Unicorn<TestEnvironment>::UnmappedMemoryHook(uc_engine* uc, uc_mem_type
auto page = std::make_unique<Page>();
page->address = base_address;
for (size_t i = 0; i < page->data.size(); ++i)
page->data[i] = this_->testenv.MemoryRead8(static_cast<u32>(base_address + i));
page->data[i] = u8(this_->testenv.MemoryRead(u32(base_address + i), sizeof(u8)));
uc_err err = uc_mem_map_ptr(uc, base_address, page->data.size(), permissions, page->data.data());
if (err == UC_ERR_MAP)
@@ -321,24 +321,7 @@ bool A32Unicorn<TestEnvironment>::UnmappedMemoryHook(uc_engine* uc, uc_mem_type
template<class TestEnvironment>
bool A32Unicorn<TestEnvironment>::MemoryWriteHook(uc_engine* /*uc*/, uc_mem_type /*type*/, u32 start_address, int size, u64 value, void* user_data) {
auto* this_ = static_cast<A32Unicorn*>(user_data);
switch (size) {
case 1:
this_->testenv.MemoryWrite8(start_address, static_cast<u8>(value));
break;
case 2:
this_->testenv.MemoryWrite16(start_address, static_cast<u16>(value));
break;
case 4:
this_->testenv.MemoryWrite32(start_address, static_cast<u32>(value));
break;
case 8:
this_->testenv.MemoryWrite64(start_address, value);
break;
default:
UNREACHABLE();
}
this_->testenv.MemoryWrite(start_address, value, size);
return true;
}
+2 -19
View File
@@ -197,7 +197,7 @@ bool A64Unicorn::UnmappedMemoryHook(uc_engine* uc, uc_mem_type /*type*/, u64 sta
auto page = std::make_unique<Page>();
page->address = base_address;
for (size_t i = 0; i < page->data.size(); ++i)
page->data[i] = this_->testenv.MemoryRead8(base_address + i);
page->data[i] = u8(this_->testenv.MemoryRead(base_address + i, sizeof(u8)));
uc_err err = uc_mem_map_ptr(uc, base_address, page->data.size(), permissions, page->data.data());
if (err == UC_ERR_MAP)
@@ -227,23 +227,6 @@ bool A64Unicorn::UnmappedMemoryHook(uc_engine* uc, uc_mem_type /*type*/, u64 sta
bool A64Unicorn::MemoryWriteHook(uc_engine* /*uc*/, uc_mem_type /*type*/, u64 start_address, int size, u64 value, void* user_data) {
auto* this_ = static_cast<A64Unicorn*>(user_data);
switch (size) {
case 1:
this_->testenv.MemoryWrite8(start_address, static_cast<u8>(value));
break;
case 2:
this_->testenv.MemoryWrite16(start_address, static_cast<u16>(value));
break;
case 4:
this_->testenv.MemoryWrite32(start_address, static_cast<u32>(value));
break;
case 8:
this_->testenv.MemoryWrite64(start_address, value);
break;
default:
UNREACHABLE();
}
this_->testenv.MemoryWrite(start_address, value, size);
return true;
}
+1 -1
View File
@@ -114,7 +114,7 @@ std::set<s32> Android::GetDeviceAxes(JNIEnv* env, jobject& j_device) const {
std::set<s32> axes;
for (int i = 0; i < env->GetArrayLength(j_axes); ++i) {
jobject axis = env->GetObjectArrayElement(j_axes, i);
axes.insert(env->CallIntMethod(axis, Common::Android::GetIntegerValueMethod()));
axes.insert(env->GetIntField(axis, Common::Android::GetIntegerValueField()));
}
return axes;
}
+2 -2
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
@@ -12,7 +12,7 @@
#include <vector>
#include "common/announce_multiplayer_room.h"
#include "common/common_types.h"
#include "core/internal_network/socket_types.h"
#include "common/socket_types.h"
#include "network/verify_user.h"
namespace Network {
+4 -8
View File
@@ -10,7 +10,7 @@
#include <thread>
#include "common/assert.h"
#include "common/polyfill_thread.h"
#include "core/internal_network/socket_types.h"
#include "common/socket_types.h"
#include "enet/enet.h"
#include "network/packet.h"
#include "network/room_member.h"
@@ -358,23 +358,19 @@ void RoomMember::RoomMemberImpl::HandleProxyPackets(const ENetEvent* event) {
// Parse the ProxyPacket from the packet
u8 local_family;
packet.Read(local_family);
proxy_packet.local_endpoint.len = 16;
proxy_packet.local_endpoint.family = u8(Network::Domain(local_family));
proxy_packet.local_endpoint.family = static_cast<Domain>(local_family);
packet.Read(proxy_packet.local_endpoint.ip);
packet.Read(proxy_packet.local_endpoint.portno);
proxy_packet.local_endpoint.zeroes = {};
u8 remote_family;
packet.Read(remote_family);
proxy_packet.remote_endpoint.len = 16;
proxy_packet.remote_endpoint.family = u8(Network::Domain(remote_family));
proxy_packet.remote_endpoint.family = static_cast<Domain>(remote_family);
packet.Read(proxy_packet.remote_endpoint.ip);
packet.Read(proxy_packet.remote_endpoint.portno);
proxy_packet.local_endpoint.zeroes = {};
u8 protocol_type;
packet.Read(protocol_type);
proxy_packet.protocol = Network::Protocol(protocol_type);
proxy_packet.protocol = static_cast<Protocol>(protocol_type);
packet.Read(proxy_packet.broadcast);
packet.Read(proxy_packet.data);
+4 -4
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
@@ -12,7 +12,7 @@
#include <vector>
#include "common/announce_multiplayer_room.h"
#include "common/common_types.h"
#include "core/internal_network/socket_types.h"
#include "common/socket_types.h"
#include "network/room.h"
namespace Network {
@@ -39,8 +39,8 @@ struct LDNPacket {
/// Information about the received proxy packets.
struct ProxyPacket {
Network::SockAddrIn local_endpoint;
Network::SockAddrIn remote_endpoint;
SockAddrIn local_endpoint;
SockAddrIn remote_endpoint;
Protocol protocol;
bool broadcast;
std::vector<u8> data;
+5 -10
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -15,18 +12,16 @@ TEST_CASE("Network::Errors", "[core]") {
Network::Socket socks[2];
for (Network::Socket& sock : socks) {
REQUIRE(sock.Initialize(Network::Domain::INET, Network::Type::STREAM,
Network::Protocol::TCP) == Network::Errno::E_SUCCESS);
Network::Protocol::TCP) == Network::Errno::SUCCESS);
}
Network::SockAddrIn addr{
16,
u8(Network::Domain::INET),
1, // hopefully nobody running this test has something listening on port 1
Network::Domain::INET,
{127, 0, 0, 1},
{},
1, // hopefully nobody running this test has something listening on port 1
};
REQUIRE(socks[0].Connect(addr) == Network::Errno::E_CONNREFUSED);
REQUIRE(socks[0].Connect(addr) == Network::Errno::CONNREFUSED);
std::vector<u8> message{1, 2, 3, 4};
REQUIRE(socks[1].Recv(0, message).second == Network::Errno::E_NOTCONN);
REQUIRE(socks[1].Recv(0, message).second == Network::Errno::NOTCONN);
}