Compare commits

..

34 commits

Author SHA1 Message Date
lizzie
331348da3d fixups 2026-06-05 09:02:58 +00:00
lizzie
77b8e9c532 license + fix stuffs 2026-06-05 08:01:51 +00:00
lizzie
f6f79aaf7b remove m_kernel reference from global scheduler 2026-06-05 07:27:53 +00:00
lizzie
080ebb1760 fixups 2026-06-05 07:27:31 +00:00
lizzie
f14aa02243 fix license 2026-06-05 07:27:31 +00:00
lizzie
50474508d4 case stupidity in windows 2026-06-05 07:27:31 +00:00
lizzie
3890984588 correct defines for d3d9 2026-06-05 07:27:31 +00:00
lizzie
bc6348afd4 fs 2026-06-05 07:27:31 +00:00
lizzie
7e9ad47207 fix msvc 2026-06-05 07:27:31 +00:00
lizzie
0b557964d7 public 2026-06-05 07:27:31 +00:00
lizzie
62334622a5 Trigger Build 2026-06-05 07:27:31 +00:00
lizzie
9629573caf better inlining 2026-06-05 07:27:31 +00:00
lizzie
47186b6a16 maxwell macros 2026-06-05 07:27:31 +00:00
lizzie
d5a06a4889 remove implicit system saved in struct, pass as first param 2026-06-05 07:27:31 +00:00
lizzie
b64ebc71f7 ffs 2026-06-05 07:27:31 +00:00
lizzie
83feb9ac8a extra 2026-06-05 07:27:31 +00:00
lizzie
64247068f7 extra fixups 2026-06-05 07:27:31 +00:00
lizzie
9ee484b375 less load, fix nv01 timer being kepler 2026-06-05 07:27:31 +00:00
lizzie
02d16582f8 fx 2026-06-05 07:27:31 +00:00
lizzie
83581f8df2 [video_core] Remove redundant references in GPU engine structs
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-06-05 07:27:31 +00:00
maufeat
82463a1550 [kernel, hle] Initial 22.0.0 kernel changes and cmd stubs (#3761)
- KProcess::Run() and CreateThread() SVC now write the current thread handle to TLS+0x110
- KPageTableBase::LockForMapDeviceAddressSpace now checks for a new KPageTableBase boolean, m_allowed_exec_device_mapping
- Stub `am` + `acc` + `settings` cmd module that needs to work for qlaunch

Thanks to: @alula and @yellows8
Source for changes: https://github.com/Atmosphere-NX/Atmosphere/pull/2744, https://switchbrew.org/, https://yls8.mtheall.com/

Co-authored-by: PavelBARABANOV <pavelbarabanov94@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3761
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-06-05 07:27:30 +00:00
lizzie
88dcbb05b2 llicense fix 2026-06-05 07:27:30 +00:00
lizzie
cada2d4e76 fx 2026-06-05 07:27:30 +00:00
lizzie
0da996528a [audio_core, hle/services, video_core/compute] Inline std::unique_ptr<> allocs into std::optional<>
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-06-05 07:27:30 +00:00
Yang Liu
48219f348c
[dynarmic, loongarch64] Add minimal toy implementation enough to execute LSLS (#4054)
Some checks are pending
tx-src / sources (push) Waiting to run
Check Strings / check-strings (push) Waiting to run
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4054
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: crueter <crueter@eden-emu.dev>
2026-06-05 02:32:06 +02:00
crueter
661346503b
[net] Add support for future macOS PGO shenanigans (#4050)
Rudimentary tests showed that using PGO on macOS *does* have an
appreciable impact on performance. So we're probably going to introduce
it soon.

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4050
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
2026-06-05 02:24:26 +02:00
lizzie
a4e9b08fe7
[docs] clang-cl with MSVC, misc docs updates (#4034)
Some checks are pending
tx-src / sources (push) Waiting to run
Check Strings / check-strings (push) Waiting to run
- vtable bounce is all gone
- factual corrections to HosKernel.md

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4034
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-06-04 19:39:21 +02:00
lizzie
ce9c7c196d
[cmake] fix OpenSSL not using <openssl/cert.h> when applicable because it tries to use the system one instead of being explicitly linked (#4053)
should fix hiccups with self-built OpenSSL

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4053
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-06-04 19:39:09 +02:00
crueter
efc7472330
[cmake] Bump minimum version to 3.31 (#4055)
Ubuntu 24.04 can't hurt us anymore.

Some other deps may have their versions raised, but there's not much of
an incentive so we'll keep it as is for now.

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4055
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-06-04 15:28:39 +02:00
Duncan Ogilvie
aadcc24aac
[core/debugger] Protocol-compliant vCont support (#3896)
Some checks are pending
tx-src / sources (push) Waiting to run
Check Strings / check-strings (push) Waiting to run
(gdb) set scheduler-locking on
(gdb) continue

As discussed in #3848, follow-up to implement vCont support according to spec.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3896
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: crueter <crueter@eden-emu.dev>
2026-06-04 05:49:23 +02:00
lizzie
89199f4d27
[*] basic in-house cpp linting (#4039)
- add `#pragma once` to remainder files
- "correcter" defines (ANDROID), see https://groups.google.com/g/android-ndk/c/cf9_f1SLXls
- extra miscelly fixups

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4039
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: crueter <crueter@eden-emu.dev>
2026-06-04 05:49:07 +02:00
lizzie
978d9d935d
[vk/vma] force ANV to have HOST_CACHED stream buffers (#3792)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3792
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: crueter <crueter@eden-emu.dev>
2026-06-04 05:48:41 +02:00
lizzie
ad9af25027
[dynarmic] fix pre-SSE4.1 having errors on CMHI/CMLO, fix extra nuisances and add INTERP testcase (#4025)
does a bit of code dedup
fixes pre-SSE4.1 having horrific CMHI/CMLO

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4025
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-06-03 22:53:21 +02:00
lizzie
f729dbb3c3
[common] use abi::__cxa_demangle for demangling using the system's glibcxx/libc++ (#3894)
most stdlibc++ already provide this functionality out of the box, very consistently and well implemented (usually)

my main irk is that the llvm itanium demangle is totally unescesary when there is a perfectly stable, tested and well documented equivalent functionality in the standard stdc++ provided in most UNIX oses

its mostly to reduce binary size by a very thin margin, but he stdc++ is more than capable of doing he same behaviour we use a dpeendency for

for mingw or such howeger,, demangling becomies trickier so we have to exclude windows entirely because well windows likes to do things differently dont they

Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3894
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-06-03 22:53:09 +02:00
135 changed files with 2351 additions and 755 deletions

View file

@ -1,7 +1,7 @@
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
cmake_minimum_required(VERSION 3.22)
cmake_minimum_required(VERSION 3.31)
project(yuzu)
@ -306,7 +306,7 @@ if (YUZU_ROOM)
add_compile_definitions(YUZU_ROOM)
endif()
if ((ANDROID OR APPLE OR UNIX) AND (NOT PLATFORM_LINUX OR ANDROID) AND NOT WIN32)
if (UNIX AND NOT (PLATFORM_LINUX OR WIN32))
if(CXX_APPLE OR CXX_CLANG)
# libc++ has stop_token and jthread as experimental
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexperimental-library")
@ -524,6 +524,8 @@ elseif (WIN32)
# PSAPI is the Process Status API
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version crypt32 rpcrt4 gdi32 wldap32 mswsock)
endif()
elseif (PLATFORM_MANAGARM)
set(PLATFORM_LIBRARIES iconv intl)
elseif (PLATFORM_HAIKU)
# Haiku is so special :)
set(PLATFORM_LIBRARIES bsd /boot/system/lib/libnetwork.so)

View file

@ -1,3 +1,6 @@
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2023 Alexandre Bouvier <contact@amb.tf>
#
# SPDX-License-Identifier: GPL-3.0-or-later
@ -13,7 +16,8 @@ endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(LLVM HANDLE_COMPONENTS CONFIG_MODE)
if (LLVM_FOUND AND LLVM_Demangle_FOUND AND NOT TARGET LLVM::Demangle)
# Demangle only for Windows targets
if (WIN32 AND LLVM_FOUND AND LLVM_Demangle_FOUND AND NOT TARGET LLVM::Demangle)
add_library(LLVM::Demangle INTERFACE IMPORTED)
target_compile_definitions(LLVM::Demangle INTERFACE ${LLVM_DEFINITIONS})
target_include_directories(LLVM::Demangle INTERFACE ${LLVM_INCLUDE_DIRS})

View file

@ -136,6 +136,16 @@ cmake -S . -B build -G "<GENERATOR>" -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COM
<img src="https://user-images.githubusercontent.com/42481638/216899275-d514ec6a-e563-470e-81e2-3e04f0429b68.png" width="500">
</details>
#### Option D: Visual Studio with clang-cl
<details>
1. Install `"x64 Native Tools Command Prompt"` for VS from the installer and also install `cmake-gui`.
2. Open `"x64 Native Tools Command Prompt"` and type `cmake-gui`.
3. Click configure choose ninja generator > specify native compilers.
4. Put `"C:/Program Files/Microsoft Visual Studio/18/Community/VC/Tools/Llvm/x64/bin/clang-cl.exe"` as your C/C++ compiler path.
5. Open `Visual studio > Open project` or Solution > Change to search for the CMake project file (`CMakeList.txt`) file on the cloned directory, and then build.
</details>
## Troubleshooting
If your initial configure failed:

View file

@ -16,7 +16,7 @@ To build Eden, you MUST have a C++ compiler.
The following additional tools are also required:
* **[CMake](https://www.cmake.org/)** 3.22+ - already included with the Android SDK
* **[CMake](https://www.cmake.org/)** 3.31+ - already included with the Android SDK
* **[Git](https://git-scm.com/)** for version control
* **[Windows installer](https://gitforwindows.org)**
* **[Python3](https://www.python.org/downloads/)** 3.10+ - necessary to download external repositories

View file

@ -1,6 +1,6 @@
# HOS Kernel
In brief, the HOS kernel is a microkernel, some services and programs run in userspace, the primary way to do communication between these is via `HIPC` (not covered here); otherwise most of the primitives reside in the forms of syscalls invoked via `svc #imm`. The kernel supports both 32-bit and 64-bit programs, and has the capacity to use 32, 36 and 39 bits of address space for spawned processes. Most of the networking stack is based off FreeBSD's network stack.
In brief, the HOS kernel is a microkernel, all services and programs run in userspace, the primary way to do communication between these is via `HIPC` (not covered here); otherwise most of the primitives reside in the forms of syscalls invoked via `svc #imm`. The kernel supports both 32-bit and 64-bit programs, and has the capacity to use 32, 36 and 39 bits of address space for spawned processes. Most of the networking stack is based off FreeBSD's network stack.
The emulator implements the majority of the syscalls pertaining to the HOS kernel itself. When we talk about the HOS Kernel (in the context of the emulator) we are strictly speaking about the mechanisms from which syscalls are handled (and it's subsequent side effects, such as the page table book-keeping). The emulator at it's current state is unable to load a custom low-level kernel and do supervisor-level emulation.
@ -28,4 +28,4 @@ Every process keeps it's own tracking of the following structures:
The emulator willingly restricts itself to only use 4 threads (to emulate 4 cores), this is because most existing applications do not benefit greatly from the added core count, and in fact can be detrimental due to extra contention. This translates equitatively to about 4 `ArmInterface` slots for each process, these are then redirected to whatever is the last `pc` of the last thread running on the core is meant to be; proceed to run it, then when returning (due to halt or interruption), proceed to reschedule the thread.
The scheduler as-is isn't 100% faithful to the original, and has great timing variance (especially due to the fact the emulator can run in systems with wildly different timings).
The scheduler as-is isn't 100% faithful to the original (for example the original is cooperative and not preemptive), and has great timing variance (especially due to the fact the emulator can run in systems with wildly different timings).

View file

@ -30,7 +30,6 @@ Before touching the settings, please see the game boots with stock options. We t
## CPU
- `CPU/Virtual table bouncing`: Some games have the tendency to crash on loading due to an indirect bad jump (Pokemon ZA being the worst offender); this option lies to the game and tells it to just pretend it never executed a given function. This is fine for most casual users, but developers of switch applications **must** disable this. This temporary "hack" should hopefully be gone in 6-7 months from now on.
- `Fastmem`, aka. `CPU/Enable Host MMU`: Enables "fastmem"; a detailed description of fastmem can be found [here](../dynarmic/Design.md#fast-memory-fastmem).
- `CPU/Unsafe FMA`: Enables deliberate innacurate FMA behaviour which may affect how FMA returns any given operation - this may introduce tiny floating point errors which can cascade in sensitive code (i.e FFmpeg).
- `CPU/Faster FRSQRTE and FRECPE`: Introduces accuracy errors on square root and reciprocals in exchange for less checks - this introduces inaccuracies with some cases but it's mostly safe.

View file

@ -49,8 +49,8 @@ if (NOT TARGET stb::headers)
add_library(stb::headers ALIAS stb)
endif()
# ItaniumDemangle
if (NOT TARGET LLVM::Demangle)
# ItaniumDemangle (Windows only)
if (WIN32 AND NOT TARGET LLVM::Demangle)
add_library(demangle demangle/ItaniumDemangle.cpp)
target_include_directories(demangle PUBLIC ./demangle)
if (NOT MSVC)

View file

@ -256,7 +256,7 @@ android {
externalNativeBuild {
cmake {
version = "3.22.1"
version = "3.31.6"
path = file("${edenDir}/CMakeLists.txt")
}
}

View file

@ -235,7 +235,7 @@ if (BOOST_NO_HEADERS)
else()
target_link_libraries(common PUBLIC Boost::headers)
endif()
target_link_libraries(common PRIVATE OpenSSL::SSL)
target_link_libraries(common PUBLIC Boost::filesystem Boost::context httplib::httplib nlohmann_json::nlohmann_json)
if (lz4_ADDED)
@ -243,7 +243,12 @@ if (lz4_ADDED)
endif()
target_link_libraries(common PUBLIC fmt::fmt stb::headers Threads::Threads unordered_dense::unordered_dense)
target_link_libraries(common PRIVATE lz4::lz4 LLVM::Demangle zstd::zstd)
target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd)
# Please refer to src/common/demangle.cpp
if (WIN32)
target_link_libraries(common PRIVATE LLVM::Demangle)
endif()
if(ANDROID)
# For ASharedMemory_create

View file

@ -303,7 +303,7 @@ namespace {
}
[[nodiscard]] s64 GetHostCNTFRQ() noexcept {
u64 cntfrq_el0 = 0;
#ifdef ANDROID
#ifdef __ANDROID__
std::string_view board{""};
char buffer[PROP_VALUE_MAX];
int len{__system_property_get("ro.product.board", buffer)};

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
@ -6,30 +6,44 @@
#include <string>
#include <string_view>
#ifdef _WIN32
#include <llvm/Demangle/Demangle.h>
#else
#include <cxxabi.h>
#endif
#include "common/demangle.h"
#include "common/scope_exit.h"
static bool IsItanium(std::string_view name) {
// A valid Itanium encoding requires 1-4 leading underscores, followed by 'Z'.
auto const pos = name.find_first_not_of('_');
return pos > 0 && pos <= 4 && pos < name.size() && name[pos] == 'Z';
}
namespace Common {
std::string DemangleSymbol(const std::string& mangled) {
if (mangled.size() > 0) {
auto const is_itanium = [](std::string_view name) -> bool {
// A valid Itanium encoding requires 1-4 leading underscores, followed by 'Z'.
auto const pos = name.find_first_not_of('_');
return pos > 0 && pos <= 4 && pos < name.size() && name[pos] == 'Z';
};
std::string ret = mangled;
if (is_itanium(mangled)) {
if (IsItanium(mangled)) {
#ifdef _WIN32
// requires the use of llvm
if (char* p = llvm::itaniumDemangle(mangled); p != nullptr) {
ret = std::string{p};
std::string ret = std::string{p};
std::free(p);
return ret;
}
#else
// can safely use libc++ and glibcxx provided demangling functions
// it's available since 2008(!) so no system should have issues with it
// see https://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.3/a01696.html
int status;
if (char* p = abi::__cxa_demangle(mangled.c_str(), NULL, NULL, &status); p != nullptr) {
std::string ret = std::string{p};
std::free(p);
return ret;
}
#endif
}
return ret;
return mangled;
}
return std::string{};
}
} // namespace Common

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -30,7 +33,7 @@ std::string NativeErrorToString(int e) {
return ret;
#else
char err_str[255];
#if defined(ANDROID) || \
#if defined(__ANDROID__) || \
(defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600)))
// Thread safe (GNU-specific)
const char* str = strerror_r(e, err_str, sizeof(err_str));

View file

@ -9,7 +9,7 @@
#include "common/assert.h"
#include "common/fs/file.h"
#include "common/fs/fs.h"
#ifdef ANDROID
#ifdef __ANDROID__
#include "common/fs/fs_android.h"
#endif
#include "common/logging.h"
@ -259,7 +259,7 @@ void IOFile::Open(const fs::path& path, FileAccessMode mode, FileType type, File
} else {
_wfopen_s(&file, path.c_str(), AccessModeToWStr(mode, type));
}
#elif ANDROID
#elif __ANDROID__
if (Android::IsContentUri(path)) {
ASSERT_MSG(mode == FileAccessMode::Read, "Content URI file access is for read-only!");
const auto fd = Android::OpenContentUri(path, Android::OpenMode::Read);
@ -396,7 +396,7 @@ u64 IOFile::GetSize() const {
// Flush any unwritten buffered data into the file prior to retrieving the file size.
std::fflush(file);
#if ANDROID
#ifdef __ANDROID__
u64 file_size = 0;
if (Android::IsContentUri(file_path)) {
file_size = Android::GetSize(file_path);

View file

@ -6,7 +6,7 @@
#include "common/fs/file.h"
#include "common/fs/fs.h"
#ifdef ANDROID
#ifdef __ANDROID__
#include "common/fs/fs_android.h"
#endif
#include "common/fs/path_util.h"
@ -532,7 +532,7 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path,
bool Exists(const fs::path& path) {
std::error_code ec;
#ifdef ANDROID
#ifdef __ANDROID__
if (Android::IsContentUri(path)) {
return Android::Exists(path);
} else {
@ -545,7 +545,7 @@ bool Exists(const fs::path& path) {
bool IsFile(const fs::path& path) {
std::error_code ec;
#ifdef ANDROID
#ifdef __ANDROID__
if (Android::IsContentUri(path)) {
return !Android::IsDirectory(path);
} else {
@ -558,7 +558,7 @@ bool IsFile(const fs::path& path) {
bool IsDir(const fs::path& path) {
std::error_code ec;
#ifdef ANDROID
#ifdef __ANDROID__
if (Android::IsContentUri(path)) {
return Android::IsDirectory(path);
} else {
@ -611,7 +611,7 @@ fs::file_type GetEntryType(const fs::path& path) {
}
u64 GetSize(const fs::path& path) {
#ifdef ANDROID
#ifdef __ANDROID__
if (Android::IsContentUri(path)) {
return Android::GetSize(path);
}

View file

@ -11,7 +11,7 @@
#include "common/assert.h"
#include "common/fs/fs.h"
#ifdef ANDROID
#ifdef __ANDROID__
#include "common/fs/fs_android.h"
#endif
#include "common/fs/fs_paths.h"
@ -126,7 +126,7 @@ public:
LEGACY_PATH(Yuzu, YUZU)
LEGACY_PATH(Suyu, SUYU)
#undef LEGACY_PATH
#elif ANDROID
#elif __ANDROID__
ASSERT(!eden_path.empty());
eden_path_cache = eden_path / CACHE_DIR;
eden_path_config = eden_path / CONFIG_DIR;
@ -447,11 +447,11 @@ std::vector<std::string> SplitPathComponentsCopy(std::string_view filename) {
std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) {
std::string path(path_);
#ifdef ANDROID
#ifdef __ANDROID__
if (Android::IsContentUri(path)) {
return path;
}
#endif // ANDROID
#endif // __ANDROID__
char type1 = directory_separator == DirectorySeparator::BackwardSlash ? '/' : '\\';
char type2 = directory_separator == DirectorySeparator::BackwardSlash ? '\\' : '/';
@ -482,7 +482,7 @@ std::string GetParentPath(std::string_view path) {
return std::string(path);
}
#ifdef ANDROID
#ifdef __ANDROID__
if (path[0] != '/') {
std::string path_string{path};
return FS::Android::GetParentDirectory(path_string);

View file

@ -320,7 +320,7 @@ struct DebuggerBackend final : public Backend {
void Flush() noexcept override {}
};
#endif
#ifdef ANDROID
#ifdef __ANDROID__
/// @brief Backend that writes to the Android logcat
struct LogcatBackend : public Backend {
explicit LogcatBackend() noexcept = default;
@ -359,7 +359,7 @@ struct Impl {
#ifdef _WIN32
lambda(static_cast<Backend&>(debugger_backend));
#endif
#ifdef ANDROID
#ifdef __ANDROID__
lambda(static_cast<Backend&>(lc_backend));
#endif
}
@ -372,7 +372,7 @@ struct Impl {
#ifdef _WIN32
DebuggerBackend debugger_backend{};
#endif
#ifdef ANDROID
#ifdef __ANDROID__
LogcatBackend lc_backend{};
#endif
std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};

View file

@ -74,7 +74,8 @@ std::vector<Asset> Release::GetPlatformAssets() const {
#endif // ARCHITECTURE_arm64
#elif defined(__APPLE__)
#ifdef ARCHITECTURE_arm64
find_asset("Standard", {".dmg", ".tar.gz"});
find_asset("Standard", {"standard.dmg", "standard.tar.gz", ".dmg", ".tar.gz"});
find_asset("PGO", {"pgo.dmg", "pgo.tar.gz"});
#endif // ARCHITECTURE_arm64
#elif defined(__ANDROID__)
#ifdef ARCHITECTURE_x86_64

View file

@ -359,7 +359,7 @@ struct Values {
true,
true};
SwitchableSetting<int, true> fsr_sharpening_slider{linkage,
#ifdef ANDROID
#ifdef __ANDROID__
0,
#else
25,
@ -417,7 +417,7 @@ struct Values {
linkage, 0, "bg_blue", Category::Renderer, Specialization::Default, true, true};
SwitchableSetting<GpuAccuracy, true> gpu_accuracy{linkage,
#ifdef ANDROID
#ifdef __ANDROID__
GpuAccuracy::Low,
#else
GpuAccuracy::Medium,
@ -447,7 +447,7 @@ struct Values {
"nvdec_emulation", Category::RendererAdvanced};
SwitchableSetting<AnisotropyMode, true> max_anisotropy{linkage,
#ifdef ANDROID
#ifdef __ANDROID__
AnisotropyMode::Default,
#else
AnisotropyMode::Automatic,
@ -500,7 +500,7 @@ struct Values {
Category::RendererAdvanced};
SwitchableSetting<bool> use_reactive_flushing{linkage,
#ifdef ANDROID
#ifdef __ANDROID__
false,
#else
true,
@ -519,7 +519,7 @@ struct Values {
true,
true};
#ifdef ANDROID
#ifdef __ANDROID__
SwitchableSetting<bool> use_optimized_vertex_buffers{linkage,
false,
"use_optimized_vertex_buffers",
@ -553,7 +553,7 @@ struct Values {
true,
true};
SwitchableSetting<bool> async_presentation{linkage,
#ifdef ANDROID
#ifdef __ANDROID__
false,
#else
false,
@ -599,7 +599,7 @@ struct Values {
Category::RendererHacks};
SwitchableSetting<ExtendedDynamicState> dyna_state{linkage,
#if defined(ANDROID)
#if defined(__ANDROID__)
ExtendedDynamicState::Disabled,
#elif defined(__APPLE__)
ExtendedDynamicState::Disabled,
@ -618,7 +618,7 @@ struct Values {
Specialization::Scalar};
SwitchableSetting<bool> vertex_input_dynamic_state{linkage,
#if defined (ANDROID)
#ifdef __ANDROID__
false,
#else
true,
@ -634,7 +634,7 @@ struct Values {
linkage, false, "disable_shader_loop_safety_checks", Category::RendererDebug};
Setting<bool> enable_renderdoc_hotkey{linkage, false, "renderdoc_hotkey",
Category::RendererDebug};
#if defined(ANDROID) && defined(ARCHITECTURE_arm64)
#if defined(__ANDROID__) && defined(ARCHITECTURE_arm64)
// Debug override for automatic BCn patching detection
Setting<bool> patch_old_qcom_drivers{linkage, false, "patch_old_qcom_drivers",
Category::RendererDebug};
@ -679,7 +679,7 @@ struct Values {
Setting<s32> current_user{linkage, 0, "current_user", Category::System};
SwitchableSetting<ConsoleMode> use_docked_mode{linkage,
#ifdef ANDROID
#ifdef __ANDROID__
ConsoleMode::Handheld,
#else
ConsoleMode::Docked,

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
@ -18,7 +18,7 @@
#include <windows.h>
#endif
#ifdef ANDROID
#ifdef __ANDROID__
#include <common/fs/fs_android.h>
#endif
@ -45,7 +45,7 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
if (full_path.empty())
return false;
#ifdef ANDROID
#ifdef __ANDROID__
if (full_path[0] != '/') {
*_pPath = Common::FS::Android::GetParentDirectory(full_path);
*_pFilename = Common::FS::Android::GetFilename(full_path);

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2012 PPSSPP Project
@ -10,12 +10,10 @@
#pragma once
#if defined(_MSC_VER)
#include <cstdlib>
#endif
#include <bit>
#include <cstring>
#include <type_traits>
#include <bit>
#include "common/common_types.h"
namespace Common {

View file

@ -262,20 +262,16 @@ enum {
CMP_ORD = 7,
};
constexpr bool IsWithin2G(uintptr_t ref, uintptr_t target) {
const u64 distance = target - (ref + 5);
return !(distance >= 0x8000'0000ULL && distance <= ~0x8000'0000ULL);
}
inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) {
return IsWithin2G(reinterpret_cast<uintptr_t>(code.getCurr()), target);
constexpr bool IsWithin2G(uintptr_t ref, uintptr_t target) noexcept {
u64 const distance = target - (ref + 5);
return (distance & 0xffff'ffff) == distance;
}
template <typename T>
inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) {
static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer.");
size_t addr = reinterpret_cast<size_t>(f);
if (IsWithin2G(code, addr)) {
uintptr_t addr = uintptr_t(f);
if (IsWithin2G(uintptr_t(code.getCurr()), addr)) {
code.call(f);
} else {
// ABI_RETURN is a safe temp register to use before a call

View file

@ -1215,6 +1215,7 @@ else()
endif()
target_link_libraries(core PRIVATE
OpenSSL::SSL
fmt::fmt
nlohmann_json::nlohmann_json
RenderDoc::API

View file

@ -81,7 +81,7 @@ void CpuManager::MultiCoreRunGuestThread() {
while (true) {
auto* physical_core = &kernel.CurrentPhysicalCore();
while (!physical_core->IsInterrupted()) {
physical_core->RunThread(thread);
physical_core->RunThread(kernel, thread);
physical_core = &kernel.CurrentPhysicalCore();
}
@ -119,7 +119,7 @@ void CpuManager::SingleCoreRunGuestThread() {
while (true) {
auto* physical_core = &kernel.CurrentPhysicalCore();
if (!physical_core->IsInterrupted()) {
physical_core->RunThread(thread);
physical_core->RunThread(kernel, thread);
physical_core = &kernel.CurrentPhysicalCore();
}

View file

@ -9,7 +9,7 @@
#include <boost/asio.hpp>
#include <boost/version.hpp>
#if BOOST_VERSION > 108400 && (!defined(_WINDOWS) && !defined(ANDROID)) || defined(YUZU_BOOST_v1)
#if BOOST_VERSION > 108400 && (!defined(_WINDOWS) && !defined(__ANDROID__)) || defined(YUZU_BOOST_v1)
#define USE_BOOST_v1
#endif
@ -247,17 +247,19 @@ private:
case DebuggerAction::Continue:
MarkResumed([&] { ResumeEmulation(); });
break;
case DebuggerAction::StepThreadUnlocked:
MarkResumed([&] {
state->active_thread->SetStepState(Kernel::StepState::StepPending);
state->active_thread->Resume(Kernel::SuspendType::Debug);
ResumeEmulation(state->active_thread.GetPointerUnsafe());
case DebuggerAction::ContinueThreads: {
auto* gdb = static_cast<GDBStub*>(frontend.get());
MarkResumed([this, threads = std::move(gdb->resume_threads)] {
ResumeThreads(threads);
});
break;
case DebuggerAction::StepThreadLocked: {
MarkResumed([&] {
}
case DebuggerAction::StepThread: {
auto* gdb = static_cast<GDBStub*>(frontend.get());
MarkResumed([this, threads = std::move(gdb->resume_threads)] {
state->active_thread->SetStepState(Kernel::StepState::StepPending);
state->active_thread->Resume(Kernel::SuspendType::Debug);
ResumeThreads(threads, state->active_thread.GetPointerUnsafe());
});
break;
}
@ -298,6 +300,22 @@ private:
}
}
void ResumeThreads(const std::vector<Kernel::KThread*>& threads,
Kernel::KThread* except = nullptr) {
Kernel::KScopedLightLock ll{debug_process->GetListLock()};
Kernel::KScopedSchedulerLock sl{system.Kernel()};
// Wake up only the specified threads.
for (auto* thread : threads) {
if (!thread || thread == except) {
continue;
}
thread->SetStepState(Kernel::StepState::NotStepping);
thread->Resume(Kernel::SuspendType::Debug);
}
}
template <typename Callback>
void MarkResumed(Callback&& cb) {
stopped = false;

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
@ -20,11 +20,11 @@ struct DebugWatchpoint;
namespace Core {
enum class DebuggerAction {
Interrupt, ///< Stop emulation as soon as possible.
Continue, ///< Resume emulation.
StepThreadLocked, ///< Step the currently-active thread without resuming others.
StepThreadUnlocked, ///< Step the currently-active thread and resume others.
ShutdownEmulation, ///< Shut down the emulator.
Interrupt, ///< Stop emulation as soon as possible.
Continue, ///< Resume emulation.
ContinueThreads, ///< Resume only specific threads (listed in frontend).
StepThread, ///< Step the active thread and resume only threads listed in frontend.
ShutdownEmulation, ///< Shut down the emulator.
};
class DebuggerBackend {

View file

@ -4,15 +4,15 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <atomic>
#include <cctype>
#include <codecvt>
#include <locale>
#include <numeric>
#include <optional>
#include <thread>
#include <boost/algorithm/string.hpp>
#include "common/hex_util.h"
#include "common/logging.h"
#include "common/scope_exit.h"
@ -273,10 +273,12 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
break;
}
case 's':
actions.push_back(DebuggerAction::StepThreadLocked);
resume_threads.clear();
actions.push_back(DebuggerAction::StepThread);
break;
case 'C':
case 'c':
resume_threads.clear();
actions.push_back(DebuggerAction::Continue);
break;
case 'Z':
@ -467,29 +469,142 @@ void GDBStub::HandleQuery(std::string_view sv) {
}
void GDBStub::HandleVCont(std::string_view sv, std::vector<DebuggerAction>& actions) {
// Continuing and stepping are supported (signal is ignored, but required for GDB to use vCont)
// Continuing and stepping are supported (signal is ignored, but required for GDB to use vCont).
// Reference: https://sourceware.org/gdb/current/onlinedocs/gdb.html/Packets.html#vCont-packet
if (sv == "?") {
SendReply("vCont;c;C;s;S");
} else {
Kernel::KThread* stepped_thread = nullptr;
bool lock_execution = true;
std::vector<std::string> entries;
boost::split(entries, sv.substr(1), boost::is_any_of(";"));
for (auto const& thread_action : entries) {
std::vector<std::string> parts;
boost::split(parts, thread_action, boost::is_any_of(":"));
if (parts.size() == 1 && (parts[0] == "c" || parts[0].starts_with("C")))
lock_execution = false;
if (parts.size() == 2 && (parts[0] == "s" || parts[0].starts_with("S")))
stepped_thread = GetThreadByID(strtoll(parts[1].data(), nullptr, 16));
return;
}
if (sv.empty() || sv.front() != ';') {
SendReply(GDB_STUB_REPLY_ERR);
return;
}
enum class VContAction {
Continue,
Step,
};
struct VContDirective {
VContAction action;
Kernel::KThread* thread{};
bool all_threads{};
bool Matches(Kernel::KThread* candidate) const {
return all_threads || thread == candidate;
}
};
const auto is_hex_byte = [](std::string_view value) {
return value.size() == 2 && std::isxdigit(static_cast<unsigned char>(value[0])) &&
std::isxdigit(static_cast<unsigned char>(value[1]));
};
const auto is_hex_string = [](std::string_view value) {
return std::ranges::all_of(value, [](auto const c) { return std::isxdigit(int(c)); });
};
resume_threads.clear();
std::vector<VContDirective> directives;
std::string_view remaining = sv.substr(1);
while (!remaining.empty()) {
const auto entry_end = remaining.find(';');
const auto entry = remaining.substr(0, entry_end);
remaining = entry_end == std::string_view::npos ? std::string_view{} : remaining.substr(entry_end + 1);
if (entry.empty()) {
SendReply(GDB_STUB_REPLY_ERR);
return;
}
if (stepped_thread) {
backend.SetActiveThread(stepped_thread);
actions.push_back(lock_execution ? DebuggerAction::StepThreadLocked : DebuggerAction::StepThreadUnlocked);
} else {
actions.push_back(DebuggerAction::Continue);
const auto thread_sep = entry.find(':');
const auto action_token = entry.substr(0, thread_sep);
const auto thread_token = thread_sep == std::string_view::npos ? std::string_view{} : entry.substr(thread_sep + 1);
if (action_token.empty()) {
SendReply(GDB_STUB_REPLY_ERR);
return;
}
VContDirective directive;
if (action_token == "c") {
directive.action = VContAction::Continue;
} else if (action_token.front() == 'C' && is_hex_byte(action_token.substr(1))) {
directive.action = VContAction::Continue;
} else if (action_token == "s") {
directive.action = VContAction::Step;
} else if (action_token.front() == 'S' && is_hex_byte(action_token.substr(1))) {
directive.action = VContAction::Step;
} else {
SendReply(GDB_STUB_REPLY_ERR);
return;
}
if (thread_sep == std::string_view::npos || thread_token == "-1") {
directive.all_threads = true;
} else if (thread_token == "0") {
// A thread-id of 0 selects an arbitrary thread. While stopped, use the
// current active thread as that arbitrary choice.
directive.thread = backend.GetActiveThread();
} else if (thread_token.starts_with('p')) {
// We do not currently support multiprocess thread selectors.
SendReply(GDB_STUB_REPLY_ERR);
return;
} else if (is_hex_string(thread_token)) {
directive.thread = GetThreadByID(strtoull(std::string(thread_token).c_str(), nullptr, 16));
} else {
SendReply(GDB_STUB_REPLY_ERR);
return;
}
directives.push_back(directive);
}
if (directives.empty()) {
SendReply(GDB_STUB_REPLY_ERR);
return;
}
// Resolve the packet exactly as specified by the protocol: for each thread,
// the leftmost action with a matching thread-id wins.
Kernel::KThread* stepped_thread = nullptr;
std::vector<Kernel::KThread*> continue_threads;
auto& thread_list = debug_process->GetThreadList();
for (auto& thread : thread_list) {
const auto directive = std::find_if(directives.begin(), directives.end(),
[&](const VContDirective& candidate) {
return candidate.Matches(std::addressof(thread));
});
if (directive == directives.end()) {
continue;
}
switch (directive->action) {
case VContAction::Continue:
continue_threads.push_back(std::addressof(thread));
break;
case VContAction::Step:
if (stepped_thread) {
// The core can step at most one thread at a time.
SendReply(GDB_STUB_REPLY_ERR);
return;
}
stepped_thread = std::addressof(thread);
break;
}
}
if (stepped_thread) {
backend.SetActiveThread(stepped_thread);
resume_threads = std::move(continue_threads);
actions.push_back(DebuggerAction::StepThread);
} else if (continue_threads.size() == thread_list.size()) {
actions.push_back(DebuggerAction::Continue);
} else if (!continue_threads.empty()) {
resume_threads = std::move(continue_threads);
actions.push_back(DebuggerAction::ContinueThreads);
} else {
// A resume packet that leaves all threads stopped is not useful to execute.
SendReply(GDB_STUB_REPLY_ERR);
}
}

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
@ -52,6 +52,7 @@ struct GDBStub : public DebuggerFrontend {
std::unique_ptr<GDBStubArch> arch;
std::vector<char> current_command;
std::map<VAddr, u32> replaced_instructions;
std::vector<Kernel::KThread*> resume_threads;
bool no_ack{};
};

View file

@ -23,7 +23,7 @@
#define stat _stat64
#endif
#ifdef ANDROID
#ifdef __ANDROID__
#include "common/fs/fs_android.h"
#endif
@ -288,7 +288,7 @@ RealVfsFile::~RealVfsFile() {
}
std::string RealVfsFile::GetName() const {
#ifdef ANDROID
#ifdef __ANDROID__
if (path[0] != '/') {
return FS::Android::GetFilename(path);
}

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
@ -17,7 +17,8 @@
namespace Kernel {
GlobalSchedulerContext::GlobalSchedulerContext(KernelCore& kernel)
: m_kernel{kernel}, m_scheduler_lock{kernel} {}
: m_scheduler_lock{kernel}
{}
GlobalSchedulerContext::~GlobalSchedulerContext() = default;
@ -37,7 +38,7 @@ void GlobalSchedulerContext::RemoveThread(KThread* thread) noexcept {
/// and then does some core rebalancing. Preemption priorities can be found
/// in the array 'preemption_priorities'.
/// @note This operation happens every 10ms.
void GlobalSchedulerContext::PreemptThreads() noexcept {
void GlobalSchedulerContext::PreemptThreads(KernelCore& kernel) noexcept {
// The priority levels at which the global scheduler preempts threads every 10 ms. They are
// ordered from Core 0 to Core 3.
static constexpr std::array<u32, Core::Hardware::NUM_CPU_CORES> per_core{
@ -46,9 +47,9 @@ void GlobalSchedulerContext::PreemptThreads() noexcept {
59,
63,
};
ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel));
ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel));
for (u32 core_id = 0; core_id < per_core.size(); core_id++)
KScheduler::RotateScheduledQueue(m_kernel, core_id, per_core[core_id]);
KScheduler::RotateScheduledQueue(kernel, core_id, per_core[core_id]);
}
/// @brief Returns true if the global scheduler lock is acquired

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
@ -50,7 +50,7 @@ public:
}
void AddThread(KThread* thread) noexcept;
void RemoveThread(KThread* thread) noexcept;
void PreemptThreads() noexcept;
void PreemptThreads(KernelCore& kernel) noexcept;
bool IsLocked() const noexcept;
void UnregisterDummyThreadForWakeup(KThread* thread) noexcept;
void RegisterDummyThreadForWakeup(KThread* thread) noexcept;
@ -60,7 +60,6 @@ private:
friend class KScopedSchedulerLock;
friend class KScopedSchedulerLockAndSleep;
KernelCore& m_kernel;
std::atomic_bool m_scheduler_update_needed{};
KSchedulerPriorityQueue m_priority_queue;
LockType m_scheduler_lock;

View file

@ -1,3 +1,6 @@
// 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
@ -57,7 +60,7 @@ void KCodeMemory::Finalize() {
}
// Close the page group.
m_page_group->Close();
m_page_group->Close(m_kernel);
m_page_group->Finalize();
// Close our reference to our owner.

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@ -106,7 +106,8 @@ public:
} // namespace
KConditionVariable::KConditionVariable(Core::System& system)
: m_system{system}, m_kernel{system.Kernel()} {}
: m_system{system}
{}
KConditionVariable::~KConditionVariable() = default;
@ -173,9 +174,9 @@ Result KConditionVariable::WaitForAddress(KernelCore& kernel, Handle handle, KPr
// Get the lock owner thread.
owner_thread = GetCurrentProcess(kernel)
.GetHandleTable()
.GetObjectWithoutPseudoHandle<KThread>(handle)
.ReleasePointerUnsafe();
.GetHandleTable()
.GetObjectWithoutPseudoHandle<KThread>(kernel, handle)
.ReleasePointerUnsafe();
R_UNLESS(owner_thread != nullptr, ResultInvalidHandle);
// Update the lock.
@ -194,9 +195,9 @@ Result KConditionVariable::WaitForAddress(KernelCore& kernel, Handle handle, KPr
R_RETURN(cur_thread->GetWaitResult());
}
void KConditionVariable::SignalImpl(KThread* thread) {
void KConditionVariable::SignalImpl(KernelCore& kernel, KThread* thread) {
// Check pre-conditions.
ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel));
ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel));
// Update the tag.
KProcessAddress address = thread->GetAddressKey();
@ -211,8 +212,7 @@ void KConditionVariable::SignalImpl(KThread* thread) {
// TODO(bunnei): We should call CanAccessAtomic(..) here.
can_access = true;
if (can_access) {
UpdateLockAtomic(m_kernel, std::addressof(prev_tag), address, own_tag,
Svc::HandleWaitMask);
UpdateLockAtomic(kernel, std::addressof(prev_tag), address, own_tag, Svc::HandleWaitMask);
}
}
@ -222,11 +222,10 @@ void KConditionVariable::SignalImpl(KThread* thread) {
thread->EndWait(ResultSuccess);
} else {
// Get the previous owner.
KThread* owner_thread = GetCurrentProcess(m_kernel)
.GetHandleTable()
.GetObjectWithoutPseudoHandle<KThread>(
static_cast<Handle>(prev_tag & ~Svc::HandleWaitMask))
.ReleasePointerUnsafe();
KThread* owner_thread = GetCurrentProcess(kernel)
.GetHandleTable()
.GetObjectWithoutPseudoHandle<KThread>(kernel, Handle(prev_tag & ~Svc::HandleWaitMask))
.ReleasePointerUnsafe();
if (owner_thread) {
// Add the thread as a waiter on the owner.
@ -247,17 +246,16 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
// Perform signaling.
s32 num_waiters{};
{
KScopedSchedulerLock sl(m_kernel);
KScopedSchedulerLock sl(m_system.Kernel());
auto it = m_tree.nfind_key({cv_key, -1});
while ((it != m_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetConditionVariableKey() == cv_key)) {
while ((it != m_tree.end()) && (count <= 0 || num_waiters < count) && (it->GetConditionVariableKey() == cv_key)) {
KThread* target_thread = std::addressof(*it);
it = m_tree.erase(it);
target_thread->ClearConditionVariable();
this->SignalImpl(target_thread);
this->SignalImpl(m_system.Kernel(), target_thread);
++num_waiters;
}
@ -265,20 +263,20 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
// If we have no waiters, clear the has waiter flag.
if (it == m_tree.end() || it->GetConditionVariableKey() != cv_key) {
constexpr u32 HasNoWaiterFlag = 0;
WriteToUser(m_kernel, cv_key, HasNoWaiterFlag);
WriteToUser(m_system.Kernel(), cv_key, HasNoWaiterFlag);
}
}
}
Result KConditionVariable::Wait(KProcessAddress addr, u64 key, u32 value, s64 timeout) {
// Prepare to wait.
KThread* cur_thread = GetCurrentThreadPointer(m_kernel);
KThread* cur_thread = GetCurrentThreadPointer(m_system.Kernel());
KHardwareTimer* timer{};
ThreadQueueImplForKConditionVariableWaitConditionVariable wait_queue(m_kernel,
ThreadQueueImplForKConditionVariableWaitConditionVariable wait_queue(m_system.Kernel(),
std::addressof(m_tree));
{
KScopedSchedulerLockAndSleep slp(m_kernel, std::addressof(timer), cur_thread, timeout);
KScopedSchedulerLockAndSleep slp(m_system.Kernel(), std::addressof(timer), cur_thread, timeout);
// Check that the thread isn't terminating.
if (cur_thread->IsTerminationRequested()) {
@ -309,12 +307,12 @@ Result KConditionVariable::Wait(KProcessAddress addr, u64 key, u32 value, s64 ti
// Write to the cv key.
{
constexpr u32 HasWaiterFlag = 1;
WriteToUser(m_kernel, key, HasWaiterFlag);
WriteToUser(m_system.Kernel(), key, HasWaiterFlag);
std::atomic_thread_fence(std::memory_order_seq_cst);
}
// Write the value to userspace.
if (!WriteToUser(m_kernel, addr, next_value)) {
if (!WriteToUser(m_system.Kernel(), addr, next_value)) {
slp.CancelSleep();
R_THROW(ResultInvalidCurrentMemory);
}

View file

@ -1,3 +1,6 @@
// 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
@ -26,19 +29,17 @@ public:
// Arbitration.
static Result SignalToAddress(KernelCore& kernel, KProcessAddress addr);
static Result WaitForAddress(KernelCore& kernel, Handle handle, KProcessAddress addr,
u32 value);
static Result WaitForAddress(KernelCore& kernel, Handle handle, KProcessAddress addr, u32 value);
// Condition variable.
void Signal(u64 cv_key, s32 count);
Result Wait(KProcessAddress addr, u64 key, u32 value, s64 timeout);
private:
void SignalImpl(KThread* thread);
void SignalImpl(KernelCore& kernel, KThread* thread);
private:
Core::System& m_system;
KernelCore& m_kernel;
ThreadTree m_tree{};
};

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@ -6,14 +6,15 @@
#include "core/hle/kernel/k_handle_table.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/kernel.h"
namespace Kernel {
void KHandleTable::Finalize() {
void KHandleTable::Finalize(KernelCore& kernel) {
// Get the table and clear our record of it.
u16 saved_table_size = 0;
{
KScopedDisableDispatch dd{m_kernel};
KScopedDisableDispatch dd{kernel};
KScopedSpinLock lk(m_lock);
std::swap(m_table_size, saved_table_size);
@ -27,7 +28,7 @@ void KHandleTable::Finalize() {
}
}
bool KHandleTable::Remove(Handle handle) {
bool KHandleTable::Remove(KernelCore& kernel, Handle handle) {
// Don't allow removal of a pseudo-handle.
if (Svc::IsPseudoHandle(handle)) [[unlikely]] {
return false;
@ -42,7 +43,7 @@ bool KHandleTable::Remove(Handle handle) {
// Find the object and free the entry.
KAutoObject* obj = nullptr;
{
KScopedDisableDispatch dd{m_kernel};
KScopedDisableDispatch dd{kernel};
KScopedSpinLock lk(m_lock);
if (this->IsValidHandle(handle)) [[likely]] {
@ -56,13 +57,13 @@ bool KHandleTable::Remove(Handle handle) {
}
// Close the object.
m_kernel.UnregisterInUseObject(obj);
kernel.UnregisterInUseObject(obj);
obj->Close();
return true;
}
Result KHandleTable::Add(Handle* out_handle, KAutoObject* obj) {
KScopedDisableDispatch dd{m_kernel};
Result KHandleTable::Add(KernelCore& kernel, Handle* out_handle, KAutoObject* obj) {
KScopedDisableDispatch dd{kernel};
KScopedSpinLock lk(m_lock);
// Never exceed our capacity.
@ -84,8 +85,7 @@ Result KHandleTable::Add(Handle* out_handle, KAutoObject* obj) {
R_SUCCEED();
}
KScopedAutoObject<KAutoObject> KHandleTable::GetObjectForIpc(Handle handle,
KThread* cur_thread) const {
KScopedAutoObject<KAutoObject> KHandleTable::GetObjectForIpc(KernelCore& kernel, Handle handle, KThread* cur_thread) const {
// Handle pseudo-handles.
ASSERT(cur_thread != nullptr);
if (handle == Svc::PseudoHandle::CurrentProcess) {
@ -96,12 +96,11 @@ KScopedAutoObject<KAutoObject> KHandleTable::GetObjectForIpc(Handle handle,
if (handle == Svc::PseudoHandle::CurrentThread) {
return cur_thread;
}
return GetObjectForIpcWithoutPseudoHandle(handle);
return GetObjectForIpcWithoutPseudoHandle(kernel, handle);
}
Result KHandleTable::Reserve(Handle* out_handle) {
KScopedDisableDispatch dd{m_kernel};
Result KHandleTable::Reserve(KernelCore& kernel, Handle* out_handle) {
KScopedDisableDispatch dd{kernel};
KScopedSpinLock lk(m_lock);
// Never exceed our capacity.
@ -111,8 +110,8 @@ Result KHandleTable::Reserve(Handle* out_handle) {
R_SUCCEED();
}
void KHandleTable::Unreserve(Handle handle) {
KScopedDisableDispatch dd{m_kernel};
void KHandleTable::Unreserve(KernelCore& kernel, Handle handle) {
KScopedDisableDispatch dd{kernel};
KScopedSpinLock lk(m_lock);
// Unpack the handle.
@ -130,8 +129,8 @@ void KHandleTable::Unreserve(Handle handle) {
}
}
void KHandleTable::Register(Handle handle, KAutoObject* obj) {
KScopedDisableDispatch dd{m_kernel};
void KHandleTable::Register(KernelCore& kernel, Handle handle, KAutoObject* obj) {
KScopedDisableDispatch dd{kernel};
KScopedSpinLock lk(m_lock);
// Unpack the handle.

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@ -31,14 +31,14 @@ public:
static constexpr size_t MaxTableSize = 1024;
public:
explicit KHandleTable(KernelCore& kernel) : m_kernel(kernel) {}
explicit KHandleTable(KernelCore& kernel) {}
Result Initialize(s32 size) {
Result Initialize(KernelCore& kernel, s32 size) {
// Check that the table size is valid.
R_UNLESS(size <= static_cast<s32>(MaxTableSize), ResultOutOfMemory);
// Lock.
KScopedDisableDispatch dd{m_kernel};
KScopedDisableDispatch dd{kernel};
KScopedSpinLock lk(m_lock);
// Initialize all fields.
@ -68,13 +68,13 @@ public:
return m_max_count;
}
void Finalize();
bool Remove(Handle handle);
void Finalize(KernelCore& kernel);
bool Remove(KernelCore& kernel, Handle handle);
template <typename T = KAutoObject>
KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const {
KScopedAutoObject<T> GetObjectWithoutPseudoHandle(KernelCore& kernel, Handle handle) const {
// Lock and look up in table.
KScopedDisableDispatch dd{m_kernel};
KScopedDisableDispatch dd{kernel};
KScopedSpinLock lk(m_lock);
if constexpr (std::is_same_v<T, KAutoObject>) {
@ -89,55 +89,52 @@ public:
}
template <typename T = KAutoObject>
KScopedAutoObject<T> GetObject(Handle handle) const {
KScopedAutoObject<T> GetObject(KernelCore& kernel, Handle handle) const {
// Handle pseudo-handles.
if constexpr (std::derived_from<KProcess, T>) {
if (handle == Svc::PseudoHandle::CurrentProcess) {
auto* const cur_process = GetCurrentProcessPointer(m_kernel);
auto* const cur_process = GetCurrentProcessPointer(kernel);
ASSERT(cur_process != nullptr);
return cur_process;
}
} else if constexpr (std::derived_from<KThread, T>) {
if (handle == Svc::PseudoHandle::CurrentThread) {
auto* const cur_thread = GetCurrentThreadPointer(m_kernel);
auto* const cur_thread = GetCurrentThreadPointer(kernel);
ASSERT(cur_thread != nullptr);
return cur_thread;
}
}
return this->template GetObjectWithoutPseudoHandle<T>(handle);
return this->template GetObjectWithoutPseudoHandle<T>(kernel, handle);
}
KScopedAutoObject<KAutoObject> GetObjectForIpcWithoutPseudoHandle(Handle handle) const {
KScopedAutoObject<KAutoObject> GetObjectForIpcWithoutPseudoHandle(KernelCore& kernel, Handle handle) const {
// Lock and look up in table.
KScopedDisableDispatch dd{m_kernel};
KScopedDisableDispatch dd{kernel};
KScopedSpinLock lk(m_lock);
return this->GetObjectImpl(handle);
}
KScopedAutoObject<KAutoObject> GetObjectForIpc(Handle handle, KThread* cur_thread) const;
KScopedAutoObject<KAutoObject> GetObjectByIndex(Handle* out_handle, size_t index) const {
KScopedDisableDispatch dd{m_kernel};
KScopedAutoObject<KAutoObject> GetObjectForIpc(KernelCore& kernel, Handle handle, KThread* cur_thread) const;
KScopedAutoObject<KAutoObject> GetObjectByIndex(KernelCore& kernel, Handle* out_handle, size_t index) const {
KScopedDisableDispatch dd{kernel};
KScopedSpinLock lk(m_lock);
return this->GetObjectByIndexImpl(out_handle, index);
}
Result Reserve(Handle* out_handle);
void Unreserve(Handle handle);
Result Reserve(KernelCore& kernel, Handle* out_handle);
void Unreserve(KernelCore& kernel, Handle handle);
Result Add(Handle* out_handle, KAutoObject* obj);
void Register(Handle handle, KAutoObject* obj);
Result Add(KernelCore& kernel, Handle* out_handle, KAutoObject* obj);
void Register(KernelCore& kernel, Handle handle, KAutoObject* obj);
template <typename T>
bool GetMultipleObjects(T** out, const Handle* handles, size_t num_handles) const {
bool GetMultipleObjects(KernelCore& kernel, T** out, const Handle* handles, size_t num_handles) const {
// Try to convert and open all the handles.
size_t num_opened;
{
// Lock the table.
KScopedDisableDispatch dd{m_kernel};
KScopedDisableDispatch dd{kernel};
KScopedSpinLock lk(m_lock);
for (num_opened = 0; num_opened < num_handles; num_opened++) {
// Get the current handle.
@ -177,13 +174,9 @@ public:
private:
s32 AllocateEntry() {
ASSERT(m_count < m_table_size);
const auto index = m_free_head_index;
m_free_head_index = m_entry_infos[index].GetNextFreeIndex();
m_max_count = (std::max)(m_max_count, ++m_count);
return index;
}
@ -302,7 +295,6 @@ private:
};
private:
KernelCore& m_kernel;
std::array<EntryInfo, MaxTableSize> m_entry_infos{};
std::array<KAutoObject*, MaxTableSize> m_objects{};
mutable KSpinLock m_lock;

View file

@ -1,3 +1,6 @@
// 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
@ -21,8 +24,8 @@ void KPageGroup::Finalize() {
m_last_block = nullptr;
}
void KPageGroup::CloseAndReset() {
auto& mm = m_kernel.MemoryManager();
void KPageGroup::CloseAndReset(KernelCore& kernel) {
auto& mm = kernel.MemoryManager();
KBlockInfo* cur = m_first_block;
while (cur != nullptr) {
@ -76,28 +79,22 @@ Result KPageGroup::AddBlock(KPhysicalAddress addr, size_t num_pages) {
R_SUCCEED();
}
void KPageGroup::Open() const {
auto& mm = m_kernel.MemoryManager();
for (const auto& it : *this) {
void KPageGroup::Open(KernelCore& kernel) const {
auto& mm = kernel.MemoryManager();
for (const auto& it : *this)
mm.Open(it.GetAddress(), it.GetNumPages());
}
}
void KPageGroup::OpenFirst() const {
auto& mm = m_kernel.MemoryManager();
for (const auto& it : *this) {
void KPageGroup::OpenFirst(KernelCore& kernel) const {
auto& mm = kernel.MemoryManager();
for (const auto& it : *this)
mm.OpenFirst(it.GetAddress(), it.GetNumPages());
}
}
void KPageGroup::Close() const {
auto& mm = m_kernel.MemoryManager();
for (const auto& it : *this) {
void KPageGroup::Close(KernelCore& kernel) const {
auto& mm = kernel.MemoryManager();
for (const auto& it : *this)
mm.Close(it.GetAddress(), it.GetNumPages());
}
}
bool KPageGroup::IsEquivalentTo(const KPageGroup& rhs) const {

View file

@ -1,3 +1,6 @@
// 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
@ -139,12 +142,12 @@ public:
};
explicit KPageGroup(KernelCore& kernel, KBlockInfoManager* m)
: m_kernel{kernel}, m_manager{m} {}
: m_manager{m} {}
~KPageGroup() {
this->Finalize();
}
void CloseAndReset();
void CloseAndReset(KernelCore& kernel);
void Finalize();
Iterator begin() const {
@ -158,9 +161,9 @@ public:
}
Result AddBlock(KPhysicalAddress addr, size_t num_pages);
void Open() const;
void OpenFirst() const;
void Close() const;
void Open(KernelCore& kernel) const;
void OpenFirst(KernelCore& kernel) const;
void Close(KernelCore& kernel) const;
size_t GetNumPages() const;
@ -175,7 +178,6 @@ public:
}
private:
KernelCore& m_kernel;
KBlockInfo* m_first_block{};
KBlockInfo* m_last_block{};
KBlockInfoManager* m_manager{};
@ -183,21 +185,24 @@ private:
class KScopedPageGroup {
public:
explicit KScopedPageGroup(const KPageGroup* gp, bool not_first = true) : m_pg(gp) {
explicit KScopedPageGroup(KernelCore& kernel, const KPageGroup* gp, bool not_first = true)
: m_kernel{kernel}
, m_pg{gp}
{
if (m_pg) {
if (not_first) {
m_pg->Open();
m_pg->Open(kernel);
} else {
m_pg->OpenFirst();
m_pg->OpenFirst(kernel);
}
}
}
explicit KScopedPageGroup(const KPageGroup& gp, bool not_first = true)
: KScopedPageGroup(std::addressof(gp), not_first) {}
explicit KScopedPageGroup(KernelCore& kernel, const KPageGroup& gp, bool not_first = true)
: KScopedPageGroup(kernel, std::addressof(gp), not_first) {}
~KScopedPageGroup() {
if (m_pg) {
m_pg->Close();
}
if (m_pg)
m_pg->Close(m_kernel);
}
void CancelClose() {
@ -205,6 +210,7 @@ public:
}
private:
KernelCore& m_kernel;
const KPageGroup* m_pg{};
};

View file

@ -127,22 +127,22 @@ constexpr Common::MemoryPermission ConvertToMemoryPermission(KMemoryPermission p
} // namespace
void KPageTableBase::MemoryRange::Open() {
void KPageTableBase::MemoryRange::Open(KernelCore& kernel) {
// If the range contains heap pages, open them.
if (this->IsHeap()) {
m_kernel.MemoryManager().Open(this->GetAddress(), this->GetSize() / PageSize);
kernel.MemoryManager().Open(this->GetAddress(), this->GetSize() / PageSize);
}
}
void KPageTableBase::MemoryRange::Close() {
void KPageTableBase::MemoryRange::Close(KernelCore& kernel) {
// If the range contains heap pages, close them.
if (this->IsHeap()) {
m_kernel.MemoryManager().Close(this->GetAddress(), this->GetSize() / PageSize);
kernel.MemoryManager().Close(this->GetAddress(), this->GetSize() / PageSize);
}
}
KPageTableBase::KPageTableBase(KernelCore& kernel)
: m_kernel(kernel), m_system(kernel.System()), m_general_lock(kernel),
: m_system(kernel.System()), m_general_lock(kernel),
m_map_physical_memory_lock(kernel), m_device_map_lock(kernel) {}
KPageTableBase::~KPageTableBase() = default;
@ -177,9 +177,9 @@ Result KPageTableBase::InitializeForKernel(bool is_64_bit, KVirtualAddress start
m_mapped_ipc_server_memory = 0;
m_memory_block_slab_manager =
m_kernel.GetSystemSystemResource().GetMemoryBlockSlabManagerPointer();
m_block_info_manager = m_kernel.GetSystemSystemResource().GetBlockInfoManagerPointer();
m_resource_limit = m_kernel.GetSystemResourceLimit();
m_system.Kernel().GetSystemSystemResource().GetMemoryBlockSlabManagerPointer();
m_block_info_manager = m_system.Kernel().GetSystemSystemResource().GetBlockInfoManagerPointer();
m_resource_limit = m_system.Kernel().GetSystemResourceLimit();
m_allocate_option = KMemoryManager::EncodeOption(KMemoryManager::Pool::System,
KMemoryManager::Direction::FromFront);
@ -469,11 +469,11 @@ void KPageTableBase::Finalize() {
}
// Get physical pages.
KPageGroup pg(m_kernel, m_block_info_manager);
KPageGroup pg(m_system.Kernel(), m_block_info_manager);
this->MakePageGroup(pg, addr, size / PageSize);
// Free the pages.
pg.CloseAndReset();
pg.CloseAndReset(m_system.Kernel());
};
// Finalize memory blocks.
@ -490,7 +490,7 @@ void KPageTableBase::Finalize() {
// Release any insecure mapped memory.
if (m_mapped_insecure_memory) {
if (auto* const insecure_resource_limit =
KSystemControl::GetInsecureMemoryResourceLimit(m_kernel);
KSystemControl::GetInsecureMemoryResourceLimit(m_system.Kernel());
insecure_resource_limit != nullptr) {
insecure_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax,
m_mapped_insecure_memory);
@ -833,7 +833,7 @@ Result KPageTableBase::LockMemoryAndOpen(KPageGroup* out_pg, KPhysicalAddress* o
// If we have an output group, open.
if (out_pg) {
out_pg->Open();
out_pg->Open(m_system.Kernel());
}
R_SUCCEED();
@ -1041,7 +1041,7 @@ Result KPageTableBase::MapMemory(KProcessAddress dst_address, KProcessAddress sr
const size_t num_pages = size / PageSize;
// Create page groups for the memory being unmapped.
KPageGroup pg(m_kernel, m_block_info_manager);
KPageGroup pg(m_system.Kernel(), m_block_info_manager);
// Create the page group representing the source.
R_TRY(this->MakePageGroup(pg, src_address, num_pages));
@ -1129,7 +1129,7 @@ Result KPageTableBase::UnmapMemory(KProcessAddress dst_address, KProcessAddress
const size_t num_pages = size / PageSize;
// Create page groups for the memory being unmapped.
KPageGroup pg(m_kernel, m_block_info_manager);
KPageGroup pg(m_system.Kernel(), m_block_info_manager);
// Create the page group representing the destination.
R_TRY(this->MakePageGroup(pg, dst_address, num_pages));
@ -1217,7 +1217,7 @@ Result KPageTableBase::MapCodeMemory(KProcessAddress dst_address, KProcessAddres
const size_t num_pages = size / PageSize;
// Create page groups for the memory being unmapped.
KPageGroup pg(m_kernel, m_block_info_manager);
KPageGroup pg(m_system.Kernel(), m_block_info_manager);
// Create the page group representing the source.
R_TRY(this->MakePageGroup(pg, src_address, num_pages));
@ -1312,7 +1312,7 @@ Result KPageTableBase::UnmapCodeMemory(KProcessAddress dst_address, KProcessAddr
bool reprotected_pages = false;
SCOPE_EXIT {
if (reprotected_pages && any_code_pages) {
InvalidateInstructionCache(m_kernel, this, dst_address, size);
InvalidateInstructionCache(m_system.Kernel(), this, dst_address, size);
}
};
@ -1322,7 +1322,7 @@ Result KPageTableBase::UnmapCodeMemory(KProcessAddress dst_address, KProcessAddr
const size_t num_pages = size / PageSize;
// Create page groups for the memory being unmapped.
KPageGroup pg(m_kernel, m_block_info_manager);
KPageGroup pg(m_system.Kernel(), m_block_info_manager);
// Create the page group representing the destination.
R_TRY(this->MakePageGroup(pg, dst_address, num_pages));
@ -1383,7 +1383,7 @@ Result KPageTableBase::UnmapCodeMemory(KProcessAddress dst_address, KProcessAddr
Result KPageTableBase::MapInsecureMemory(KProcessAddress address, size_t size) {
// Get the insecure memory resource limit and pool.
auto* const insecure_resource_limit = KSystemControl::GetInsecureMemoryResourceLimit(m_kernel);
auto* const insecure_resource_limit = KSystemControl::GetInsecureMemoryResourceLimit(m_system.Kernel());
const auto insecure_pool =
static_cast<KMemoryManager::Pool>(KSystemControl::GetInsecureMemoryPool());
@ -1394,8 +1394,8 @@ Result KPageTableBase::MapInsecureMemory(KProcessAddress address, size_t size) {
R_UNLESS(memory_reservation.Succeeded(), ResultOutOfMemory);
// Allocate pages for the insecure memory.
KPageGroup pg(m_kernel, m_block_info_manager);
R_TRY(m_kernel.MemoryManager().AllocateAndOpen(
KPageGroup pg(m_system.Kernel(), m_block_info_manager);
R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen(
std::addressof(pg), size / PageSize,
KMemoryManager::EncodeOption(insecure_pool, KMemoryManager::Direction::FromFront)));
@ -1403,7 +1403,7 @@ Result KPageTableBase::MapInsecureMemory(KProcessAddress address, size_t size) {
// If the mapping succeeds, each page will gain an extra reference, otherwise they will be freed
// automatically.
SCOPE_EXIT {
pg.Close();
pg.Close(m_system.Kernel());
};
// Clear all the newly allocated pages.
@ -1491,7 +1491,7 @@ Result KPageTableBase::UnmapInsecureMemory(KProcessAddress address, size_t size)
// Release the insecure memory from the insecure limit.
if (auto* const insecure_resource_limit =
KSystemControl::GetInsecureMemoryResourceLimit(m_kernel);
KSystemControl::GetInsecureMemoryResourceLimit(m_system.Kernel());
insecure_resource_limit != nullptr) {
insecure_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax, size);
}
@ -1603,15 +1603,15 @@ Result KPageTableBase::AllocateAndMapPagesImpl(PageLinkedList* page_list, KProce
ASSERT(this->IsLockedByCurrentThread());
// Create a page group to hold the pages we allocate.
KPageGroup pg(m_kernel, m_block_info_manager);
KPageGroup pg(m_system.Kernel(), m_block_info_manager);
// Allocate the pages.
R_TRY(
m_kernel.MemoryManager().AllocateAndOpen(std::addressof(pg), num_pages, m_allocate_option));
m_system.Kernel().MemoryManager().AllocateAndOpen(std::addressof(pg), num_pages, m_allocate_option));
// Ensure that the page group is closed when we're done working with it.
SCOPE_EXIT {
pg.Close();
pg.Close(m_system.Kernel());
};
// Clear all pages.
@ -1992,7 +1992,7 @@ Result KPageTableBase::SetProcessMemoryPermission(KProcessAddress addr, size_t s
KMemoryAttribute::All, KMemoryAttribute::None));
// Make a new page group for the region.
KPageGroup pg(m_kernel, m_block_info_manager);
KPageGroup pg(m_system.Kernel(), m_block_info_manager);
// Determine new perm/state.
const KMemoryPermission new_perm = ConvertToKMemoryPermission(svc_perm);
@ -2048,9 +2048,9 @@ Result KPageTableBase::SetProcessMemoryPermission(KProcessAddress addr, size_t s
// Ensure cache coherency, if we're setting pages as executable.
if (is_x) {
for (const auto& block : pg) {
StoreDataCache(GetHeapVirtualPointer(m_kernel, block.GetAddress()), block.GetSize());
StoreDataCache(GetHeapVirtualPointer(m_system.Kernel(), block.GetAddress()), block.GetSize());
}
InvalidateInstructionCache(m_kernel, this, addr, size);
InvalidateInstructionCache(m_system.Kernel(), this, addr, size);
}
R_SUCCEED();
@ -2193,15 +2193,15 @@ Result KPageTableBase::SetHeapSize(KProcessAddress* out, size_t size) {
R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
// Allocate pages for the heap extension.
KPageGroup pg(m_kernel, m_block_info_manager);
R_TRY(m_kernel.MemoryManager().AllocateAndOpen(std::addressof(pg), allocation_size / PageSize,
KPageGroup pg(m_system.Kernel(), m_block_info_manager);
R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen(std::addressof(pg), allocation_size / PageSize,
m_allocate_option));
// Close the opened pages when we're done with them.
// If the mapping succeeds, each page will gain an extra reference, otherwise they will be freed
// automatically.
SCOPE_EXIT {
pg.Close();
pg.Close(m_system.Kernel());
};
// Clear all the newly allocated pages.
@ -2406,7 +2406,7 @@ Result KPageTableBase::MapIoImpl(KProcessAddress* out, PageLinkedList* page_list
ASSERT(this->CanContain(region_start, region_size, state));
// Locate the memory region.
const KMemoryRegion* region = KMemoryLayout::Find(m_kernel.MemoryLayout(), phys_addr);
const KMemoryRegion* region = KMemoryLayout::Find(m_system.Kernel().MemoryLayout(), phys_addr);
R_UNLESS(region != nullptr, ResultInvalidAddress);
ASSERT(region->Contains(GetInteger(phys_addr)));
@ -2640,7 +2640,7 @@ Result KPageTableBase::MapStatic(KPhysicalAddress phys_addr, size_t size, KMemor
const size_t region_num_pages = region_size / PageSize;
// Locate the memory region.
const KMemoryRegion* region = KMemoryLayout::Find(m_kernel.MemoryLayout(), phys_addr);
const KMemoryRegion* region = KMemoryLayout::Find(m_system.Kernel().MemoryLayout(), phys_addr);
R_UNLESS(region != nullptr, ResultInvalidAddress);
ASSERT(region->Contains(GetInteger(phys_addr)));
@ -2707,7 +2707,7 @@ Result KPageTableBase::MapStatic(KPhysicalAddress phys_addr, size_t size, KMemor
Result KPageTableBase::MapRegion(KMemoryRegionType region_type, KMemoryPermission perm) {
// Get the memory region.
const KMemoryRegion* region =
m_kernel.MemoryLayout().GetPhysicalMemoryRegionTree().FindFirstDerived(region_type);
m_system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree().FindFirstDerived(region_type);
R_UNLESS(region != nullptr, ResultOutOfRange);
// Check that the region is valid.
@ -3004,7 +3004,7 @@ Result KPageTableBase::MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress add
R_TRY(this->MakePageGroup(*out, address, num_pages));
// Open a new reference to the pages in the group.
out->Open();
out->Open(m_system.Kernel());
R_SUCCEED();
}
@ -3051,7 +3051,7 @@ Result KPageTableBase::InvalidateProcessDataCache(KProcessAddress address, size_
// Invalidate the block.
if (cur_size > 0) {
// NOTE: Nintendo does not check the result of invalidation.
InvalidateDataCache(GetLinearMappedVirtualPointer(m_kernel, cur_addr), cur_size);
InvalidateDataCache(GetLinearMappedVirtualPointer(m_system.Kernel(), cur_addr), cur_size);
}
// Advance.
@ -3075,7 +3075,7 @@ Result KPageTableBase::InvalidateProcessDataCache(KProcessAddress address, size_
// Invalidate the last block.
if (cur_size > 0) {
// NOTE: Nintendo does not check the result of invalidation.
InvalidateDataCache(GetLinearMappedVirtualPointer(m_kernel, cur_addr), cur_size);
InvalidateDataCache(GetLinearMappedVirtualPointer(m_system.Kernel(), cur_addr), cur_size);
}
R_SUCCEED();
@ -3083,7 +3083,7 @@ Result KPageTableBase::InvalidateProcessDataCache(KProcessAddress address, size_
Result KPageTableBase::InvalidateCurrentProcessDataCache(KProcessAddress address, size_t size) {
// Check pre-condition: this is being called on the current process.
ASSERT(this == std::addressof(GetCurrentProcess(m_kernel).GetPageTable().GetBasePageTable()));
ASSERT(this == std::addressof(GetCurrentProcess(m_system.Kernel()).GetPageTable().GetBasePageTable()));
// Check that the region is in range.
R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
@ -3144,7 +3144,7 @@ Result KPageTableBase::ReadDebugMemory(KProcessAddress dst_address, KProcessAddr
// Copy as much aligned data as we can.
if (cur_size >= sizeof(u32)) {
const size_t copy_size = Common::AlignDown(cur_size, sizeof(u32));
const void* copy_src = GetLinearMappedVirtualPointer(m_kernel, cur_addr);
const void* copy_src = GetLinearMappedVirtualPointer(m_system.Kernel(), cur_addr);
FlushDataCache(copy_src, copy_size);
R_UNLESS(dst_memory.WriteBlock(dst_address, copy_src, copy_size), ResultInvalidPointer);
@ -3155,7 +3155,7 @@ Result KPageTableBase::ReadDebugMemory(KProcessAddress dst_address, KProcessAddr
// Copy remaining data.
if (cur_size > 0) {
const void* copy_src = GetLinearMappedVirtualPointer(m_kernel, cur_addr);
const void* copy_src = GetLinearMappedVirtualPointer(m_system.Kernel(), cur_addr);
FlushDataCache(copy_src, cur_size);
R_UNLESS(dst_memory.WriteBlock(dst_address, copy_src, cur_size), ResultInvalidPointer);
}
@ -3240,11 +3240,11 @@ Result KPageTableBase::WriteDebugMemory(KProcessAddress dst_address, KProcessAdd
// Copy as much aligned data as we can.
if (cur_size >= sizeof(u32)) {
const size_t copy_size = Common::AlignDown(cur_size, sizeof(u32));
void* copy_dst = GetLinearMappedVirtualPointer(m_kernel, cur_addr);
void* copy_dst = GetLinearMappedVirtualPointer(m_system.Kernel(), cur_addr);
R_UNLESS(src_memory.ReadBlock(src_address, copy_dst, copy_size),
ResultInvalidCurrentMemory);
StoreDataCache(GetLinearMappedVirtualPointer(m_kernel, cur_addr), copy_size);
StoreDataCache(GetLinearMappedVirtualPointer(m_system.Kernel(), cur_addr), copy_size);
src_address += copy_size;
cur_addr += copy_size;
@ -3253,11 +3253,11 @@ Result KPageTableBase::WriteDebugMemory(KProcessAddress dst_address, KProcessAdd
// Copy remaining data.
if (cur_size > 0) {
void* copy_dst = GetLinearMappedVirtualPointer(m_kernel, cur_addr);
void* copy_dst = GetLinearMappedVirtualPointer(m_system.Kernel(), cur_addr);
R_UNLESS(src_memory.ReadBlock(src_address, copy_dst, cur_size),
ResultInvalidCurrentMemory);
StoreDataCache(GetLinearMappedVirtualPointer(m_kernel, cur_addr), cur_size);
StoreDataCache(GetLinearMappedVirtualPointer(m_system.Kernel(), cur_addr), cur_size);
}
R_SUCCEED();
@ -3295,7 +3295,7 @@ Result KPageTableBase::WriteDebugMemory(KProcessAddress dst_address, KProcessAdd
R_TRY(PerformCopy());
// Invalidate the instruction cache, as this svc allows modifying executable pages.
InvalidateInstructionCache(m_kernel, this, dst_address, size);
InvalidateInstructionCache(m_system.Kernel(), this, dst_address, size);
R_SUCCEED();
}
@ -3311,7 +3311,7 @@ Result KPageTableBase::ReadIoMemoryImpl(KProcessAddress dst_addr, KPhysicalAddre
const size_t map_size = map_end - map_start;
// Get the memory reference to write into.
auto& dst_memory = GetCurrentMemory(m_kernel);
auto& dst_memory = GetCurrentMemory(m_system.Kernel());
// We're going to perform an update, so create a helper.
KScopedPageTableUpdater updater(this);
@ -3347,7 +3347,7 @@ Result KPageTableBase::WriteIoMemoryImpl(KPhysicalAddress phys_addr, KProcessAdd
const size_t map_size = map_end - map_start;
// Get the memory reference to read from.
auto& src_memory = GetCurrentMemory(m_kernel);
auto& src_memory = GetCurrentMemory(m_system.Kernel());
// We're going to perform an update, so create a helper.
KScopedPageTableUpdater updater(this);
@ -3379,7 +3379,7 @@ Result KPageTableBase::ReadDebugIoMemory(KProcessAddress dst_address, KProcessAd
// We need to lock both this table, and the current process's table, so set up some aliases.
KPageTableBase& src_page_table = *this;
KPageTableBase& dst_page_table = GetCurrentProcess(m_kernel).GetPageTable().GetBasePageTable();
KPageTableBase& dst_page_table = GetCurrentProcess(m_system.Kernel()).GetPageTable().GetBasePageTable();
// Acquire the table locks.
KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock);
@ -3421,7 +3421,7 @@ Result KPageTableBase::WriteDebugIoMemory(KProcessAddress dst_address, KProcessA
// We need to lock both this table, and the current process's table, so set up some aliases.
KPageTableBase& src_page_table = *this;
KPageTableBase& dst_page_table = GetCurrentProcess(m_kernel).GetPageTable().GetBasePageTable();
KPageTableBase& dst_page_table = GetCurrentProcess(m_system.Kernel()).GetPageTable().GetBasePageTable();
// Acquire the table locks.
KScopedLightLockPair lk(src_page_table.m_general_lock, dst_page_table.m_general_lock);
@ -3606,7 +3606,7 @@ Result KPageTableBase::OpenMemoryRangeForMapDeviceAddressSpace(KPageTableBase::M
KMemoryAttribute::IpcLocked | KMemoryAttribute::Locked, KMemoryAttribute::None));
// We got the range, so open it.
out->Open();
out->Open(m_system.Kernel());
R_SUCCEED();
}
@ -3624,7 +3624,7 @@ Result KPageTableBase::OpenMemoryRangeForUnmapDeviceAddressSpace(MemoryRange* ou
KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked, KMemoryAttribute::DeviceShared));
// We got the range, so open it.
out->Open();
out->Open(m_system.Kernel());
R_SUCCEED();
}
@ -3697,7 +3697,7 @@ Result KPageTableBase::OpenMemoryRangeForProcessCacheOperation(MemoryRange* out,
KMemoryAttribute::None));
// We got the range, so open it.
out->Open();
out->Open(m_system.Kernel());
R_SUCCEED();
}
@ -3710,7 +3710,7 @@ Result KPageTableBase::CopyMemoryFromLinearToUser(
R_UNLESS(this->Contains(src_addr, size), ResultInvalidCurrentMemory);
// Get the destination memory reference.
auto& dst_memory = GetCurrentMemory(m_kernel);
auto& dst_memory = GetCurrentMemory(m_system.Kernel());
// Copy the memory.
{
@ -3745,7 +3745,7 @@ Result KPageTableBase::CopyMemoryFromLinearToUser(
if (cur_size >= sizeof(u32)) {
const size_t copy_size = Common::AlignDown(cur_size, sizeof(u32));
R_UNLESS(dst_memory.WriteBlock(dst_addr,
GetLinearMappedVirtualPointer(m_kernel, cur_addr),
GetLinearMappedVirtualPointer(m_system.Kernel(), cur_addr),
copy_size),
ResultInvalidCurrentMemory);
@ -3757,7 +3757,7 @@ Result KPageTableBase::CopyMemoryFromLinearToUser(
// Copy remaining data.
if (cur_size > 0) {
R_UNLESS(dst_memory.WriteBlock(
dst_addr, GetLinearMappedVirtualPointer(m_kernel, cur_addr), cur_size),
dst_addr, GetLinearMappedVirtualPointer(m_system.Kernel(), cur_addr), cur_size),
ResultInvalidCurrentMemory);
}
@ -3836,7 +3836,7 @@ Result KPageTableBase::CopyMemoryFromLinearToKernel(
R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), ResultInvalidCurrentMemory);
// Copy the data.
std::memcpy(buffer, GetLinearMappedVirtualPointer(m_kernel, cur_addr), cur_size);
std::memcpy(buffer, GetLinearMappedVirtualPointer(m_system.Kernel(), cur_addr), cur_size);
R_SUCCEED();
};
@ -3884,7 +3884,7 @@ Result KPageTableBase::CopyMemoryFromUserToLinear(
R_UNLESS(this->Contains(dst_addr, size), ResultInvalidCurrentMemory);
// Get the source memory reference.
auto& src_memory = GetCurrentMemory(m_kernel);
auto& src_memory = GetCurrentMemory(m_system.Kernel());
// Copy the memory.
{
@ -3919,7 +3919,7 @@ Result KPageTableBase::CopyMemoryFromUserToLinear(
if (cur_size >= sizeof(u32)) {
const size_t copy_size = Common::AlignDown(cur_size, sizeof(u32));
R_UNLESS(src_memory.ReadBlock(src_addr,
GetLinearMappedVirtualPointer(m_kernel, cur_addr),
GetLinearMappedVirtualPointer(m_system.Kernel(), cur_addr),
copy_size),
ResultInvalidCurrentMemory);
src_addr += copy_size;
@ -3930,7 +3930,7 @@ Result KPageTableBase::CopyMemoryFromUserToLinear(
// Copy remaining data.
if (cur_size > 0) {
R_UNLESS(src_memory.ReadBlock(
src_addr, GetLinearMappedVirtualPointer(m_kernel, cur_addr), cur_size),
src_addr, GetLinearMappedVirtualPointer(m_system.Kernel(), cur_addr), cur_size),
ResultInvalidCurrentMemory);
}
@ -4011,7 +4011,7 @@ Result KPageTableBase::CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, si
R_UNLESS(IsLinearMappedPhysicalAddress(cur_addr), ResultInvalidCurrentMemory);
// Copy the data.
std::memcpy(GetLinearMappedVirtualPointer(m_kernel, cur_addr), buffer, cur_size);
std::memcpy(GetLinearMappedVirtualPointer(m_system.Kernel(), cur_addr), buffer, cur_size);
R_SUCCEED();
};
@ -4162,8 +4162,8 @@ Result KPageTableBase::CopyMemoryFromHeapToHeap(
R_UNLESS(IsHeapPhysicalAddress(cur_dst_addr), ResultInvalidCurrentMemory);
// Copy the data.
std::memcpy(GetHeapVirtualPointer(m_kernel, cur_dst_addr),
GetHeapVirtualPointer(m_kernel, cur_src_addr), cur_copy_size);
std::memcpy(GetHeapVirtualPointer(m_system.Kernel(), cur_dst_addr),
GetHeapVirtualPointer(m_system.Kernel(), cur_src_addr), cur_copy_size);
// Update.
cur_src_block_addr = src_next_entry.phys_addr;
@ -4296,8 +4296,8 @@ Result KPageTableBase::CopyMemoryFromHeapToHeapWithoutCheckDestination(
R_UNLESS(IsHeapPhysicalAddress(cur_dst_addr), ResultInvalidCurrentMemory);
// Copy the data.
std::memcpy(GetHeapVirtualPointer(m_kernel, cur_dst_addr),
GetHeapVirtualPointer(m_kernel, cur_src_addr), cur_copy_size);
std::memcpy(GetHeapVirtualPointer(m_system.Kernel(), cur_dst_addr),
GetHeapVirtualPointer(m_system.Kernel(), cur_src_addr), cur_copy_size);
// Update.
cur_src_block_addr = src_next_entry.phys_addr;
@ -4506,10 +4506,10 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
// free on scope exit.
SCOPE_EXIT {
if (start_partial_page != 0) {
m_kernel.MemoryManager().Close(start_partial_page, 1);
m_system.Kernel().MemoryManager().Close(start_partial_page, 1);
}
if (end_partial_page != 0) {
m_kernel.MemoryManager().Close(end_partial_page, 1);
m_system.Kernel().MemoryManager().Close(end_partial_page, 1);
}
};
@ -4526,7 +4526,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
// Allocate the start page as needed.
if (aligned_src_start < mapping_src_start) {
start_partial_page =
m_kernel.MemoryManager().AllocateAndOpenContinuous(1, 1, m_allocate_option);
m_system.Kernel().MemoryManager().AllocateAndOpenContinuous(1, 1, m_allocate_option);
R_UNLESS(start_partial_page != 0, ResultOutOfMemory);
}
@ -4534,7 +4534,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
if (mapping_src_end < aligned_src_end &&
(aligned_src_start < mapping_src_end || aligned_src_start == mapping_src_start)) {
end_partial_page =
m_kernel.MemoryManager().AllocateAndOpenContinuous(1, 1, m_allocate_option);
m_system.Kernel().MemoryManager().AllocateAndOpenContinuous(1, 1, m_allocate_option);
R_UNLESS(end_partial_page != 0, ResultOutOfMemory);
}
@ -4560,7 +4560,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
// Map the start page, if we have one.
if (start_partial_page != 0) {
// Ensure the page holds correct data.
u8* const start_partial_virt = GetHeapVirtualPointer(m_kernel, start_partial_page);
u8* const start_partial_virt = GetHeapVirtualPointer(m_system.Kernel(), start_partial_page);
if (send) {
const size_t partial_offset = src_start - aligned_src_start;
size_t copy_size, clear_size;
@ -4574,7 +4574,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
std::memset(start_partial_virt, fill_val, partial_offset);
std::memcpy(start_partial_virt + partial_offset,
GetHeapVirtualPointer(m_kernel, cur_block_addr) + partial_offset,
GetHeapVirtualPointer(m_system.Kernel(), cur_block_addr) + partial_offset,
copy_size);
if (clear_size > 0) {
std::memset(start_partial_virt + partial_offset + copy_size, fill_val, clear_size);
@ -4663,10 +4663,10 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
// Map the end page, if we have one.
if (end_partial_page != 0) {
// Ensure the page holds correct data.
u8* const end_partial_virt = GetHeapVirtualPointer(m_kernel, end_partial_page);
u8* const end_partial_virt = GetHeapVirtualPointer(m_system.Kernel(), end_partial_page);
if (send) {
const size_t copy_size = src_end - mapping_src_end;
std::memcpy(end_partial_virt, GetHeapVirtualPointer(m_kernel, cur_block_addr),
std::memcpy(end_partial_virt, GetHeapVirtualPointer(m_system.Kernel(), cur_block_addr),
copy_size);
std::memset(end_partial_virt + copy_size, fill_val, PageSize - copy_size);
} else {
@ -5173,15 +5173,15 @@ Result KPageTableBase::MapPhysicalMemory(KProcessAddress address, size_t size) {
R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
// Allocate pages for the new memory.
KPageGroup pg(m_kernel, m_block_info_manager);
R_TRY(m_kernel.MemoryManager().AllocateForProcess(
KPageGroup pg(m_system.Kernel(), m_block_info_manager);
R_TRY(m_system.Kernel().MemoryManager().AllocateForProcess(
std::addressof(pg), (size - mapped_size) / PageSize, m_allocate_option,
GetCurrentProcess(m_kernel).GetId(), m_heap_fill_value));
GetCurrentProcess(m_system.Kernel()).GetId(), m_heap_fill_value));
// If we fail in the next bit (or retry), we need to cleanup the pages.
auto pg_guard = SCOPE_GUARD {
pg.OpenFirst();
pg.Close();
pg.OpenFirst(m_system.Kernel());
pg.Close(m_system.Kernel());
};
// Map the memory.
@ -5304,12 +5304,12 @@ Result KPageTableBase::MapPhysicalMemory(KProcessAddress address, size_t size) {
}
// Release any remaining unmapped memory.
m_kernel.MemoryManager().OpenFirst(pg_phys_addr, pg_pages);
m_kernel.MemoryManager().Close(pg_phys_addr, pg_pages);
m_system.Kernel().MemoryManager().OpenFirst(pg_phys_addr, pg_pages);
m_system.Kernel().MemoryManager().Close(pg_phys_addr, pg_pages);
for (++pg_it; pg_it != pg.end(); ++pg_it) {
m_kernel.MemoryManager().OpenFirst(pg_it->GetAddress(),
m_system.Kernel().MemoryManager().OpenFirst(pg_it->GetAddress(),
pg_it->GetNumPages());
m_kernel.MemoryManager().Close(pg_it->GetAddress(), pg_it->GetNumPages());
m_system.Kernel().MemoryManager().Close(pg_it->GetAddress(), pg_it->GetNumPages());
}
};
@ -5337,11 +5337,11 @@ Result KPageTableBase::MapPhysicalMemory(KProcessAddress address, size_t size) {
// While we have pages to map, map them.
{
// Create a page group for the current mapping range.
KPageGroup cur_pg(m_kernel, m_block_info_manager);
KPageGroup cur_pg(m_system.Kernel(), m_block_info_manager);
{
ON_RESULT_FAILURE_2 {
cur_pg.OpenFirst();
cur_pg.Close();
cur_pg.OpenFirst(m_system.Kernel());
cur_pg.Close(m_system.Kernel());
};
size_t remain_pages = map_pages;
@ -5706,9 +5706,9 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
const bool separate_heap = operation == OperationType::UnmapPhysical;
// Ensure that any pages we track are closed on exit.
KPageGroup pages_to_close(m_kernel, this->GetBlockInfoManager());
KPageGroup pages_to_close(m_system.Kernel(), this->GetBlockInfoManager());
SCOPE_EXIT {
pages_to_close.CloseAndReset();
pages_to_close.CloseAndReset(m_system.Kernel());
};
// Make a page group representing the region to unmap.
@ -5727,7 +5727,7 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
// Open references to pages, if we should.
if (this->IsHeapPhysicalAddress(phys_addr)) {
m_kernel.MemoryManager().Open(phys_addr, num_pages);
m_system.Kernel().MemoryManager().Open(phys_addr, num_pages);
}
R_SUCCEED();
@ -5767,7 +5767,7 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
const bool separate_heap = operation == OperationType::MapFirstGroupPhysical;
// We want to maintain a new reference to every page in the group.
KScopedPageGroup spg(page_group, operation == OperationType::MapGroup);
KScopedPageGroup spg(m_system.Kernel(), page_group, operation == OperationType::MapGroup);
for (const auto& node : page_group) {
const size_t size{node.GetNumPages() * PageSize};

View file

@ -61,14 +61,12 @@ public:
class MemoryRange {
private:
KernelCore& m_kernel;
KPhysicalAddress m_address;
size_t m_size;
bool m_heap;
KPhysicalAddress m_address = 0;
size_t m_size = 0;
bool m_heap = false;
public:
explicit MemoryRange(KernelCore& kernel)
: m_kernel(kernel), m_address(0), m_size(0), m_heap(false) {}
explicit MemoryRange() : m_address(0), m_size(0), m_heap(false) {}
void Set(KPhysicalAddress address, size_t size, bool heap) {
m_address = address;
@ -86,8 +84,8 @@ public:
return m_heap;
}
void Open();
void Close();
void Open(KernelCore& kernel);
void Close(KernelCore& kernel);
};
protected:
@ -189,7 +187,6 @@ private:
};
private:
KernelCore& m_kernel;
Core::System& m_system;
KProcessAddress m_address_space_start{};
KProcessAddress m_address_space_end{};
@ -329,35 +326,35 @@ protected:
bool IsLinearMappedPhysicalAddress(KPhysicalAddress phys_addr) {
ASSERT(this->IsLockedByCurrentThread());
return m_kernel.MemoryLayout().IsLinearMappedPhysicalAddress(
return m_system.Kernel().MemoryLayout().IsLinearMappedPhysicalAddress(
m_cached_physical_linear_region, phys_addr);
}
bool IsLinearMappedPhysicalAddress(KPhysicalAddress phys_addr, size_t size) {
ASSERT(this->IsLockedByCurrentThread());
return m_kernel.MemoryLayout().IsLinearMappedPhysicalAddress(
return m_system.Kernel().MemoryLayout().IsLinearMappedPhysicalAddress(
m_cached_physical_linear_region, phys_addr, size);
}
bool IsHeapPhysicalAddress(KPhysicalAddress phys_addr) {
ASSERT(this->IsLockedByCurrentThread());
return m_kernel.MemoryLayout().IsHeapPhysicalAddress(m_cached_physical_heap_region,
return m_system.Kernel().MemoryLayout().IsHeapPhysicalAddress(m_cached_physical_heap_region,
phys_addr);
}
bool IsHeapPhysicalAddress(KPhysicalAddress phys_addr, size_t size) {
ASSERT(this->IsLockedByCurrentThread());
return m_kernel.MemoryLayout().IsHeapPhysicalAddress(m_cached_physical_heap_region,
return m_system.Kernel().MemoryLayout().IsHeapPhysicalAddress(m_cached_physical_heap_region,
phys_addr, size);
}
bool IsHeapPhysicalAddressForFinalize(KPhysicalAddress phys_addr) {
ASSERT(!this->IsLockedByCurrentThread());
return m_kernel.MemoryLayout().IsHeapPhysicalAddress(m_cached_physical_heap_region,
return m_system.Kernel().MemoryLayout().IsHeapPhysicalAddress(m_cached_physical_heap_region,
phys_addr);
}
@ -744,15 +741,15 @@ public:
// Member heap
u8* GetHeapVirtualPointer(KPhysicalAddress addr) {
return GetHeapVirtualPointer(m_kernel, addr);
return GetHeapVirtualPointer(m_system.Kernel(), addr);
}
KPhysicalAddress GetHeapPhysicalAddress(KVirtualAddress addr) {
return GetHeapPhysicalAddress(m_kernel, addr);
return GetHeapPhysicalAddress(m_system.Kernel(), addr);
}
KVirtualAddress GetHeapVirtualAddress(KPhysicalAddress addr) {
return GetHeapVirtualAddress(m_kernel, addr);
return GetHeapVirtualAddress(m_system.Kernel(), addr);
}
// TODO: GetPageTableVirtualAddress

View file

@ -993,7 +993,7 @@ Result KProcess::Run(s32 priority, size_t stack_size) {
// Add the thread to our handle table.
Handle thread_handle;
R_TRY(m_handle_table.Add(std::addressof(thread_handle), main_thread));
R_TRY(m_handle_table.Add(m_kernel, std::addressof(thread_handle), main_thread));
// Set the thread arguments.
main_thread->GetContext().r[0] = 0;

View file

@ -564,7 +564,7 @@ private:
Result InitializeHandleTable(s32 size) {
// Try to initialize the handle table.
R_TRY(m_handle_table.Initialize(size));
R_TRY(m_handle_table.Initialize(m_kernel, size));
// We succeeded, so note that we did.
m_is_handle_table_initialized = true;
@ -573,7 +573,7 @@ private:
void FinalizeHandleTable() {
// Finalize the table.
m_handle_table.Finalize();
m_handle_table.Finalize(m_kernel);
// Note that the table is finalized.
m_is_handle_table_initialized = false;

View file

@ -159,7 +159,7 @@ private:
};
template <bool MoveHandleAllowed>
Result ProcessMessageSpecialData(s32& offset, KProcess& dst_process, KProcess& src_process,
Result ProcessMessageSpecialData(KernelCore& kernel, s32& offset, KProcess& dst_process, KProcess& src_process,
KThread& src_thread, const MessageBuffer& dst_msg,
const MessageBuffer& src_msg,
const MessageBuffer::SpecialHeader& src_special_header) {
@ -185,10 +185,9 @@ Result ProcessMessageSpecialData(s32& offset, KProcess& dst_process, KProcess& s
// If we're in a success state, try to move the handle to the new table.
if (R_SUCCEEDED(result) && src_handle != Svc::InvalidHandle) {
KScopedAutoObject obj =
src_handle_table.GetObjectForIpc(src_handle, std::addressof(src_thread));
src_handle_table.GetObjectForIpc(kernel, src_handle, std::addressof(src_thread));
if (obj.IsNotNull()) {
Result add_result =
dst_handle_table.Add(std::addressof(dst_handle), obj.GetPointerUnsafe());
Result add_result = dst_handle_table.Add(kernel, std::addressof(dst_handle), obj.GetPointerUnsafe());
if (R_FAILED(add_result)) {
result = add_result;
dst_handle = Svc::InvalidHandle;
@ -213,12 +212,10 @@ Result ProcessMessageSpecialData(s32& offset, KProcess& dst_process, KProcess& s
if (src_handle != Svc::InvalidHandle) {
if (R_SUCCEEDED(result)) {
KScopedAutoObject obj =
src_handle_table.GetObjectForIpcWithoutPseudoHandle(src_handle);
src_handle_table.GetObjectForIpcWithoutPseudoHandle(kernel, src_handle);
if (obj.IsNotNull()) {
Result add_result = dst_handle_table.Add(std::addressof(dst_handle),
obj.GetPointerUnsafe());
src_handle_table.Remove(src_handle);
Result add_result = dst_handle_table.Add(kernel, std::addressof(dst_handle), obj.GetPointerUnsafe());
src_handle_table.Remove(kernel, src_handle);
if (R_FAILED(add_result)) {
result = add_result;
@ -228,7 +225,7 @@ Result ProcessMessageSpecialData(s32& offset, KProcess& dst_process, KProcess& s
result = ResultInvalidHandle;
}
} else {
src_handle_table.Remove(src_handle);
src_handle_table.Remove(kernel, src_handle);
}
}
@ -336,7 +333,7 @@ constexpr Result GetMapAliasTestStateAndAttributeMask(KMemoryState& out_state,
R_SUCCEED();
}
void CleanupSpecialData(KProcess& dst_process, u32* dst_msg_ptr, size_t dst_buffer_size) {
void CleanupSpecialData(KernelCore& kernel, KProcess& dst_process, u32* dst_msg_ptr, size_t dst_buffer_size) {
// Parse the message.
const MessageBuffer dst_msg(dst_msg_ptr, dst_buffer_size);
const MessageBuffer::MessageHeader dst_header(dst_msg);
@ -363,15 +360,14 @@ void CleanupSpecialData(KProcess& dst_process, u32* dst_msg_ptr, size_t dst_buff
const Handle handle = dst_msg.GetHandle(offset);
if (handle != Svc::InvalidHandle) {
dst_handle_table.Remove(handle);
dst_handle_table.Remove(kernel, handle);
}
offset = dst_msg.SetHandle(offset, Svc::InvalidHandle);
}
}
Result CleanupServerHandles(KernelCore& kernel, uint64_t message, size_t buffer_size,
KPhysicalAddress message_paddr) {
Result CleanupServerHandles(KernelCore& kernel, uint64_t message, size_t buffer_size, KPhysicalAddress message_paddr) {
// Server is assumed to be current thread.
KThread& thread = GetCurrentThread(kernel);
@ -410,7 +406,7 @@ Result CleanupServerHandles(KernelCore& kernel, uint64_t message, size_t buffer_
// Close the handles.
for (auto i = 0; i < special_header.GetMoveHandleCount(); ++i) {
handle_table.Remove(msg.GetHandle(offset));
handle_table.Remove(kernel, msg.GetHandle(offset));
offset += static_cast<int>(sizeof(Svc::Handle) / sizeof(u32));
}
}
@ -639,7 +635,7 @@ Result ReceiveMessage(KernelCore& kernel, bool& recv_list_broken, uint64_t dst_m
// Cleanup special data.
if (src_header.GetHasSpecialHeader()) {
CleanupSpecialData(dst_process, dst_msg_ptr, dst_buffer_size);
CleanupSpecialData(kernel, dst_process, dst_msg_ptr, dst_buffer_size);
}
// Cleanup the header if the receive list isn't broken.
@ -661,7 +657,7 @@ Result ReceiveMessage(KernelCore& kernel, bool& recv_list_broken, uint64_t dst_m
};
// Process special data.
R_TRY(ProcessMessageSpecialData<false>(offset, dst_process, src_process, src_thread,
R_TRY(ProcessMessageSpecialData<false>(kernel, offset, dst_process, src_process, src_thread,
dst_msg, src_msg, src_special_header));
}
@ -922,7 +918,7 @@ Result SendMessage(KernelCore& kernel, uint64_t src_message_buffer, size_t src_b
// Cleanup special data.
if (processed_special_data) {
if (src_header.GetHasSpecialHeader()) {
CleanupSpecialData(dst_process, dst_msg_ptr, dst_buffer_size);
CleanupSpecialData(kernel, dst_process, dst_msg_ptr, dst_buffer_size);
}
} else {
CleanupServerHandles(kernel, src_user ? src_message_buffer : 0, src_buffer_size,
@ -987,7 +983,7 @@ Result SendMessage(KernelCore& kernel, uint64_t src_message_buffer, size_t src_b
ASSERT(GetCurrentThreadPointer(kernel) == std::addressof(src_thread));
processed_special_data = true;
if (src_header.GetHasSpecialHeader()) {
R_TRY(ProcessMessageSpecialData<true>(offset, dst_process, src_process, src_thread,
R_TRY(ProcessMessageSpecialData<true>(kernel, offset, dst_process, src_process, src_thread,
dst_msg, src_msg, src_special_header));
}

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -68,7 +71,7 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory, KProcess* ow
void KSharedMemory::Finalize() {
// Close and finalize the page group.
m_page_group->Close();
m_page_group->Close(m_kernel);
m_page_group->Finalize();
// Release the memory reservation.

View file

@ -1,3 +1,6 @@
// 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
@ -53,7 +56,7 @@ void KTransferMemory::Finalize() {
}
// Close the page group.
m_page_group->Close();
m_page_group->Close(m_kernel);
m_page_group->Finalize();
}

View file

@ -259,7 +259,7 @@ struct KernelCore::Impl {
preemption_event = Core::Timing::CreateEvent("PreemptionCallback", [this, &kernel](s64 time, std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
{
KScopedSchedulerLock lock(kernel);
global_scheduler_context->PreemptThreads();
global_scheduler_context->PreemptThreads(kernel);
}
return std::nullopt;
});

View file

@ -17,14 +17,15 @@
namespace Kernel {
PhysicalCore::PhysicalCore(KernelCore& kernel, std::size_t core_index)
: m_kernel{kernel}, m_core_index{core_index} {
: m_core_index{core_index}
{
m_is_single_core = !kernel.IsMulticore();
}
PhysicalCore::~PhysicalCore() = default;
void PhysicalCore::RunThread(Kernel::KThread* thread) {
void PhysicalCore::RunThread(KernelCore& kernel, Kernel::KThread* thread) {
auto* process = thread->GetOwnerProcess();
auto& system = m_kernel.System();
auto& system = kernel.System();
auto* interface = process->GetArmInterface(m_core_index);
interface->Initialize();
@ -208,8 +209,8 @@ void PhysicalCore::CloneFpuStatus(KThread* dst) const {
dst->GetContext().fpsr = ctx.fpsr;
}
void PhysicalCore::LogBacktrace() {
auto* process = GetCurrentProcessPointer(m_kernel);
void PhysicalCore::LogBacktrace(KernelCore& kernel) {
auto* process = GetCurrentProcessPointer(kernel);
if (!process) {
return;
}

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -30,7 +33,7 @@ public:
YUZU_NON_MOVEABLE(PhysicalCore);
// Execute guest code running on the given thread.
void RunThread(KThread* thread);
void RunThread(KernelCore& kernel, KThread* thread);
// Copy context from thread to current core.
void LoadContext(const KThread* thread);
@ -44,7 +47,7 @@ public:
void CloneFpuStatus(KThread* dst) const;
// Log backtrace of current processor state.
void LogBacktrace();
void LogBacktrace(KernelCore& kernel);
// Wait for an interrupt.
void Idle();
@ -63,9 +66,7 @@ public:
}
private:
KernelCore& m_kernel;
const std::size_t m_core_index;
std::mutex m_guard;
std::condition_variable m_on_interrupt;
Core::ArmInterface* m_arm_interface{};

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -22,13 +25,11 @@ Result SetThreadActivity(Core::System& system, Handle thread_handle,
R_UNLESS(IsValidThreadActivity(thread_activity), ResultInvalidEnumValue);
// Get the thread from its handle.
KScopedAutoObject thread =
GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(thread_handle);
KScopedAutoObject thread = GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(system.Kernel(), thread_handle);
R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
// Check that the activity is being set on a non-current thread for the current process.
R_UNLESS(thread->GetOwnerProcess() == GetCurrentProcessPointer(system.Kernel()),
ResultInvalidHandle);
R_UNLESS(thread->GetOwnerProcess() == GetCurrentProcessPointer(system.Kernel()), ResultInvalidHandle);
R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(system.Kernel()), ResultBusy);
// Set the activity.

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -37,8 +40,7 @@ Result FlushProcessDataCache(Core::System& system, Handle process_handle, u64 ad
R_UNLESS(size == static_cast<uint64_t>(size), ResultInvalidCurrentMemory);
// Get the process from its handle.
KScopedAutoObject process =
GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KProcess>(process_handle);
KScopedAutoObject process = GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KProcess>(system.Kernel(), process_handle);
R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
// Verify the region is within range.

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@ -63,7 +63,7 @@ Result CreateCodeMemory(Core::System& system, Handle* out, u64 address, uint64_t
KCodeMemory::Register(kernel, code_mem);
// Add the code memory to the handle table.
R_TRY(GetCurrentProcess(system.Kernel()).GetHandleTable().Add(out, code_mem));
R_TRY(GetCurrentProcess(system.Kernel()).GetHandleTable().Add(system.Kernel(), out, code_mem));
R_SUCCEED();
}
@ -85,8 +85,8 @@ Result ControlCodeMemory(Core::System& system, Handle code_memory_handle,
// Get the code memory from its handle.
KScopedAutoObject code_mem = GetCurrentProcess(system.Kernel())
.GetHandleTable()
.GetObject<KCodeMemory>(code_memory_handle);
.GetHandleTable()
.GetObject<KCodeMemory>(system.Kernel(), code_memory_handle);
R_UNLESS(code_mem.IsNotNull(), ResultInvalidHandle);
// NOTE: Here, Atmosphere extends the SVC to allow code memory operations on one's own process.

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -39,7 +42,7 @@ Result CreateDeviceAddressSpace(Core::System& system, Handle* out, uint64_t das_
KDeviceAddressSpace::Register(system.Kernel(), das);
// Add to the handle table.
R_TRY(GetCurrentProcess(system.Kernel()).GetHandleTable().Add(out, das));
R_TRY(GetCurrentProcess(system.Kernel()).GetHandleTable().Add(system.Kernel(), out, das));
R_SUCCEED();
}
@ -47,8 +50,8 @@ Result CreateDeviceAddressSpace(Core::System& system, Handle* out, uint64_t das_
Result AttachDeviceAddressSpace(Core::System& system, DeviceName device_name, Handle das_handle) {
// Get the device address space.
KScopedAutoObject das = GetCurrentProcess(system.Kernel())
.GetHandleTable()
.GetObject<KDeviceAddressSpace>(das_handle);
.GetHandleTable()
.GetObject<KDeviceAddressSpace>(system.Kernel(), das_handle);
R_UNLESS(das.IsNotNull(), ResultInvalidHandle);
// Attach.
@ -58,8 +61,8 @@ Result AttachDeviceAddressSpace(Core::System& system, DeviceName device_name, Ha
Result DetachDeviceAddressSpace(Core::System& system, DeviceName device_name, Handle das_handle) {
// Get the device address space.
KScopedAutoObject das = GetCurrentProcess(system.Kernel())
.GetHandleTable()
.GetObject<KDeviceAddressSpace>(das_handle);
.GetHandleTable()
.GetObject<KDeviceAddressSpace>(system.Kernel(), das_handle);
R_UNLESS(das.IsNotNull(), ResultInvalidHandle);
// Detach.
@ -99,13 +102,13 @@ Result MapDeviceAddressSpaceByForce(Core::System& system, Handle das_handle, Han
// Get the device address space.
KScopedAutoObject das = GetCurrentProcess(system.Kernel())
.GetHandleTable()
.GetObject<KDeviceAddressSpace>(das_handle);
.GetHandleTable()
.GetObject<KDeviceAddressSpace>(system.Kernel(), das_handle);
R_UNLESS(das.IsNotNull(), ResultInvalidHandle);
// Get the process.
KScopedAutoObject process =
GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KProcess>(process_handle);
GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KProcess>(system.Kernel(), process_handle);
R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
// Validate that the process address is within range.
@ -140,13 +143,13 @@ Result MapDeviceAddressSpaceAligned(Core::System& system, Handle das_handle, Han
// Get the device address space.
KScopedAutoObject das = GetCurrentProcess(system.Kernel())
.GetHandleTable()
.GetObject<KDeviceAddressSpace>(das_handle);
.GetHandleTable()
.GetObject<KDeviceAddressSpace>(system.Kernel(), das_handle);
R_UNLESS(das.IsNotNull(), ResultInvalidHandle);
// Get the process.
KScopedAutoObject process =
GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KProcess>(process_handle);
GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KProcess>(system.Kernel(), process_handle);
R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
// Validate that the process address is within range.
@ -172,13 +175,12 @@ Result UnmapDeviceAddressSpace(Core::System& system, Handle das_handle, Handle p
// Get the device address space.
KScopedAutoObject das = GetCurrentProcess(system.Kernel())
.GetHandleTable()
.GetObject<KDeviceAddressSpace>(das_handle);
.GetHandleTable()
.GetObject<KDeviceAddressSpace>(system.Kernel(), das_handle);
R_UNLESS(das.IsNotNull(), ResultInvalidHandle);
// Get the process.
KScopedAutoObject process =
GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KProcess>(process_handle);
KScopedAutoObject process = GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KProcess>(system.Kernel(), process_handle);
R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
// Validate that the process address is within range.

View file

@ -22,7 +22,7 @@ Result SignalEvent(Core::System& system, Handle event_handle) {
// Fail-safe for system applets
const auto program_id = GetCurrentProcess(system.Kernel()).GetProgramId();
if ((program_id & 0xFFFFFFFFFFFFFF00ull) == 0x0100000000001000ull) {
KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle);
KScopedAutoObject event = handle_table.GetObject<KEvent>(system.Kernel(), event_handle);
if (event.IsNotNull()) {
event->Signal();
} else {
@ -34,7 +34,7 @@ Result SignalEvent(Core::System& system, Handle event_handle) {
// Get the event.
KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle);
KScopedAutoObject event = handle_table.GetObject<KEvent>(system.Kernel(), event_handle);
R_UNLESS(event.IsNotNull(), ResultInvalidHandle);
R_RETURN(event->Signal());
@ -48,7 +48,7 @@ Result ClearEvent(Core::System& system, Handle event_handle) {
// Try to clear the writable event.
{
KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle);
KScopedAutoObject event = handle_table.GetObject<KEvent>(system.Kernel(), event_handle);
if (event.IsNotNull()) {
event->Clear();
R_SUCCEED();
@ -57,7 +57,7 @@ Result ClearEvent(Core::System& system, Handle event_handle) {
// Try to clear the readable event.
{
KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(event_handle);
KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(system.Kernel(), event_handle);
if (readable_event.IsNotNull()) {
readable_event->Clear();
R_SUCCEED();
@ -99,15 +99,15 @@ Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_read) {
KEvent::Register(kernel, event);
// Add the event to the handle table.
R_TRY(handle_table.Add(out_write, event));
R_TRY(handle_table.Add(system.Kernel(), out_write, event));
// Ensure that we maintain a clean handle state on exit.
ON_RESULT_FAILURE {
handle_table.Remove(*out_write);
handle_table.Remove(system.Kernel(), *out_write);
};
// Add the readable event to the handle table.
R_RETURN(handle_table.Add(out_read, std::addressof(event->GetReadableEvent())));
R_RETURN(handle_table.Add(system.Kernel(), out_read, std::addressof(event->GetReadableEvent())));
}
Result SignalEvent64(Core::System& system, Handle event_handle) {

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -103,7 +106,7 @@ void Break(Core::System& system, BreakReason reason, u64 info1, u64 info2) {
handle_debug_buffer(info1, info2);
system.CurrentPhysicalCore().LogBacktrace();
system.CurrentPhysicalCore().LogBacktrace(system.Kernel());
}
const bool is_hbl = GetCurrentProcess(system.Kernel()).IsHbl();

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@ -46,7 +46,7 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
R_UNLESS(info_sub_id == 0, ResultInvalidEnumValue);
const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
KScopedAutoObject process = handle_table.GetObject<KProcess>(handle);
KScopedAutoObject process = handle_table.GetObject<KProcess>(system.Kernel(), handle);
R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
switch (info_id_type) {
@ -175,7 +175,7 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
}
Handle resource_handle{};
R_TRY(handle_table.Add(std::addressof(resource_handle), resource_limit));
R_TRY(handle_table.Add(system.Kernel(), std::addressof(resource_handle), resource_limit));
*result = resource_handle;
R_SUCCEED();
@ -203,8 +203,8 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
}
KScopedAutoObject thread = GetCurrentProcess(system.Kernel())
.GetHandleTable()
.GetObject<KThread>(static_cast<Handle>(handle));
.GetHandleTable()
.GetObject<KThread>(system.Kernel(), Handle(handle));
if (thread.IsNull()) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}",
static_cast<Handle>(handle));
@ -256,7 +256,7 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
// Get a new handle for the current process.
Handle tmp;
R_TRY(handle_table.Add(std::addressof(tmp), current_process));
R_TRY(handle_table.Add(system.Kernel(), std::addressof(tmp), current_process));
// Set the output.
*result = tmp;

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@ -20,11 +20,9 @@ namespace Kernel::Svc {
namespace {
Result SendSyncRequestImpl(KernelCore& kernel, uintptr_t message, size_t buffer_size,
Handle session_handle) {
Result SendSyncRequestImpl(KernelCore& kernel, uintptr_t message, size_t buffer_size, Handle session_handle) {
// Get the client session.
KScopedAutoObject session =
GetCurrentProcess(kernel).GetHandleTable().GetObject<KClientSession>(session_handle);
KScopedAutoObject session = GetCurrentProcess(kernel).GetHandleTable().GetObject<KClientSession>(kernel, session_handle);
R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
// Get the parent, and persist a reference to it until we're done.
@ -41,8 +39,7 @@ Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t mes
int64_t timeout_ns) {
// Reply to the target, if one is specified.
if (reply_target != InvalidHandle) {
KScopedAutoObject session =
GetCurrentProcess(kernel).GetHandleTable().GetObject<KServerSession>(reply_target);
KScopedAutoObject session = GetCurrentProcess(kernel).GetHandleTable().GetObject<KServerSession>(kernel, reply_target);
R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
// If we fail to reply, we want to set the output index to -1.
@ -127,7 +124,7 @@ Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t mes
// Convert the handles to objects.
R_UNLESS(
handle_table.GetMultipleObjects<KSynchronizationObject>(objs, handles, num_handles),
handle_table.GetMultipleObjects<KSynchronizationObject>(kernel, objs, handles, num_handles),
ResultInvalidHandle);
}
@ -193,7 +190,7 @@ Result SendAsyncRequestWithUserBuffer(Core::System& system, Handle* out_event_ha
R_UNLESS(event_reservation.Succeeded(), ResultLimitReached);
// Get the client session.
KScopedAutoObject session = process.GetHandleTable().GetObject<KClientSession>(session_handle);
KScopedAutoObject session = process.GetHandleTable().GetObject<KClientSession>(system.Kernel(), session_handle);
R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
// Get the parent, and persist a reference to it until we're done.
@ -220,11 +217,11 @@ Result SendAsyncRequestWithUserBuffer(Core::System& system, Handle* out_event_ha
KEvent::Register(system.Kernel(), event);
// Add the readable event to the handle table.
R_TRY(handle_table.Add(out_event_handle, std::addressof(event->GetReadableEvent())));
R_TRY(handle_table.Add(system.Kernel(), out_event_handle, std::addressof(event->GetReadableEvent())));
// Ensure that if we fail to send the request, we close the readable handle.
ON_RESULT_FAILURE {
handle_table.Remove(*out_event_handle);
handle_table.Remove(system.Kernel(), *out_event_handle);
};
// Send the async request.

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -14,8 +17,8 @@ namespace Kernel::Svc {
Result SendSyncRequestLight(Core::System& system, Handle session_handle, u32* args) {
// Get the light client session from its handle.
KScopedAutoObject session = GetCurrentProcess(system.Kernel())
.GetHandleTable()
.GetObject<KLightClientSession>(session_handle);
.GetHandleTable()
.GetObject<KLightClientSession>(system.Kernel(), session_handle);
R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
// Send the request.
@ -27,8 +30,8 @@ Result SendSyncRequestLight(Core::System& system, Handle session_handle, u32* ar
Result ReplyAndReceiveLight(Core::System& system, Handle session_handle, u32* args) {
// Get the light server session from its handle.
KScopedAutoObject session = GetCurrentProcess(system.Kernel())
.GetHandleTable()
.GetObject<KLightServerSession>(session_handle);
.GetHandleTable()
.GetObject<KLightServerSession>(system.Kernel(), session_handle);
R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
// Handle the request.

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -33,9 +36,9 @@ Result ConnectToNamedPort(Core::System& system, Handle* out, u64 user_name) {
// Reserve a handle for the port.
// NOTE: Nintendo really does write directly to the output handle here.
R_TRY(handle_table.Reserve(out));
R_TRY(handle_table.Reserve(system.Kernel(), out));
ON_RESULT_FAILURE {
handle_table.Unreserve(*out);
handle_table.Unreserve(system.Kernel(), *out);
};
// Create a session.
@ -43,7 +46,7 @@ Result ConnectToNamedPort(Core::System& system, Handle* out, u64 user_name) {
R_TRY(port->CreateSession(std::addressof(session)));
// Register the session in the table, close the extra reference.
handle_table.Register(*out, session);
handle_table.Register(system.Kernel(), *out, session);
session->Close();
// We succeeded.
@ -77,15 +80,15 @@ Result CreatePort(Core::System& system, Handle* out_server, Handle* out_client,
KPort::Register(kernel, port);
// Add the client to the handle table.
R_TRY(handle_table.Add(out_client, std::addressof(port->GetClientPort())));
R_TRY(handle_table.Add(system.Kernel(), out_client, std::addressof(port->GetClientPort())));
// Ensure that we maintain a clean handle state on exit.
ON_RESULT_FAILURE {
handle_table.Remove(*out_client);
handle_table.Remove(system.Kernel(), *out_client);
};
// Add the server to the handle table.
R_RETURN(handle_table.Add(out_server, std::addressof(port->GetServerPort())));
R_RETURN(handle_table.Add(system.Kernel(), out_server, std::addressof(port->GetServerPort())));
}
Result ConnectToPort(Core::System& system, Handle* out, Handle port) {
@ -93,14 +96,14 @@ Result ConnectToPort(Core::System& system, Handle* out, Handle port) {
auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
// Get the client port.
KScopedAutoObject client_port = handle_table.GetObject<KClientPort>(port);
KScopedAutoObject client_port = handle_table.GetObject<KClientPort>(system.Kernel(), port);
R_UNLESS(client_port.IsNotNull(), ResultInvalidHandle);
// Reserve a handle for the port.
// NOTE: Nintendo really does write directly to the output handle here.
R_TRY(handle_table.Reserve(out));
R_TRY(handle_table.Reserve(system.Kernel(), out));
ON_RESULT_FAILURE {
handle_table.Unreserve(*out);
handle_table.Unreserve(system.Kernel(), *out);
};
// Create the session.
@ -114,15 +117,14 @@ Result ConnectToPort(Core::System& system, Handle* out, Handle port) {
}
// Register the session.
handle_table.Register(*out, session);
handle_table.Register(system.Kernel(), *out, session);
session->Close();
// We succeeded.
R_SUCCEED();
}
Result ManageNamedPort(Core::System& system, Handle* out_server_handle, uint64_t user_name,
int32_t max_sessions) {
Result ManageNamedPort(Core::System& system, Handle* out_server_handle, uint64_t user_name, int32_t max_sessions) {
// Copy the provided name from user memory to kernel memory.
auto string_name =
GetCurrentMemory(system.Kernel()).ReadCString(user_name, KObjectName::NameLengthMax);
@ -156,9 +158,9 @@ Result ManageNamedPort(Core::System& system, Handle* out_server_handle, uint64_t
};
// Register the handle in the table.
R_TRY(handle_table.Add(out_server_handle, std::addressof(port->GetServerPort())));
R_TRY(handle_table.Add(system.Kernel(), out_server_handle, std::addressof(port->GetServerPort())));
ON_RESULT_FAILURE {
handle_table.Remove(*out_server_handle);
handle_table.Remove(system.Kernel(), *out_server_handle);
};
// Create a new object name.

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@ -27,8 +27,8 @@ Result GetProcessId(Core::System& system, u64* out_process_id, Handle handle) {
// Get the object from the handle table.
KScopedAutoObject obj = GetCurrentProcess(system.Kernel())
.GetHandleTable()
.GetObject<KAutoObject>(static_cast<Handle>(handle));
.GetHandleTable()
.GetObject<KAutoObject>(system.Kernel(), Handle(handle));
R_UNLESS(obj.IsNotNull(), ResultInvalidHandle);
// Get the process from the object.
@ -98,7 +98,7 @@ Result GetProcessInfo(Core::System& system, s64* out, Handle process_handle,
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type={:#X}", process_handle, info_type);
const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
KScopedAutoObject process = handle_table.GetObject<KProcess>(system.Kernel(), process_handle);
if (process.IsNull()) {
LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
process_handle);

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@ -48,7 +48,7 @@ Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, u
// Get the process from its handle.
KScopedAutoObject process =
GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KProcess>(process_handle);
GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KProcess>(system.Kernel(), process_handle);
R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
// Validate that the address is in range.
@ -76,7 +76,7 @@ Result MapProcessMemory(Core::System& system, u64 dst_address, Handle process_ha
// Get the processes.
KProcess* dst_process = GetCurrentProcessPointer(system.Kernel());
KScopedAutoObject src_process =
dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(system.Kernel(), process_handle);
R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
// Get the page tables.
@ -117,7 +117,7 @@ Result UnmapProcessMemory(Core::System& system, u64 dst_address, Handle process_
// Get the processes.
KProcess* dst_process = GetCurrentProcessPointer(system.Kernel());
KScopedAutoObject src_process =
dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(system.Kernel(), process_handle);
R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
// Get the page tables.
@ -174,7 +174,7 @@ Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst
}
const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
KScopedAutoObject process = handle_table.GetObject<KProcess>(system.Kernel(), process_handle);
if (process.IsNull()) {
LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
process_handle);
@ -234,7 +234,7 @@ Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 d
}
const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
KScopedAutoObject process = handle_table.GetObject<KProcess>(system.Kernel(), process_handle);
if (process.IsNull()) {
LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
process_handle);

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -23,7 +26,7 @@ Result QueryProcessMemory(Core::System& system, uint64_t out_memory_info, PageIn
Handle process_handle, uint64_t address) {
LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address);
const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
KScopedAutoObject process = handle_table.GetObject<KProcess>(system.Kernel(), process_handle);
if (process.IsNull()) {
LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
process_handle);

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -29,7 +32,7 @@ Result CreateResourceLimit(Core::System& system, Handle* out_handle) {
KResourceLimit::Register(kernel, resource_limit);
// Add the limit to the handle table.
R_RETURN(GetCurrentProcess(kernel).GetHandleTable().Add(out_handle, resource_limit));
R_RETURN(GetCurrentProcess(kernel).GetHandleTable().Add(system.Kernel(), out_handle, resource_limit));
}
Result GetResourceLimitLimitValue(Core::System& system, s64* out_limit_value,
@ -42,8 +45,8 @@ Result GetResourceLimitLimitValue(Core::System& system, s64* out_limit_value,
// Get the resource limit.
KScopedAutoObject resource_limit = GetCurrentProcess(system.Kernel())
.GetHandleTable()
.GetObject<KResourceLimit>(resource_limit_handle);
.GetHandleTable()
.GetObject<KResourceLimit>(system.Kernel(), resource_limit_handle);
R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle);
// Get the limit value.
@ -62,8 +65,8 @@ Result GetResourceLimitCurrentValue(Core::System& system, s64* out_current_value
// Get the resource limit.
KScopedAutoObject resource_limit = GetCurrentProcess(system.Kernel())
.GetHandleTable()
.GetObject<KResourceLimit>(resource_limit_handle);
.GetHandleTable()
.GetObject<KResourceLimit>(system.Kernel(), resource_limit_handle);
R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle);
// Get the current value.
@ -83,7 +86,7 @@ Result SetResourceLimitLimitValue(Core::System& system, Handle resource_limit_ha
// Get the resource limit.
KScopedAutoObject resource_limit = GetCurrentProcess(system.Kernel())
.GetHandleTable()
.GetObject<KResourceLimit>(resource_limit_handle);
.GetObject<KResourceLimit>(system.Kernel(), resource_limit_handle);
R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle);
// Set the limit value.

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -78,15 +81,15 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien
T::Register(system.Kernel(), session);
// Add the server session to the handle table.
R_TRY(handle_table.Add(out_server, std::addressof(session->GetServerSession())));
R_TRY(handle_table.Add(system.Kernel(), out_server, std::addressof(session->GetServerSession())));
// Ensure that we maintain a clean handle state on exit.
ON_RESULT_FAILURE {
handle_table.Remove(*out_server);
handle_table.Remove(system.Kernel(), *out_server);
};
// Add the client session to the handle table.
R_RETURN(handle_table.Add(out_client, std::addressof(session->GetClientSession())));
R_RETURN(handle_table.Add(system.Kernel(), out_client, std::addressof(session->GetClientSession())));
}
} // namespace
@ -105,13 +108,13 @@ Result AcceptSession(Core::System& system, Handle* out, Handle port_handle) {
auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
// Get the server port.
KScopedAutoObject port = handle_table.GetObject<KServerPort>(port_handle);
KScopedAutoObject port = handle_table.GetObject<KServerPort>(system.Kernel(), port_handle);
R_UNLESS(port.IsNotNull(), ResultInvalidHandle);
// Reserve an entry for the new session.
R_TRY(handle_table.Reserve(out));
R_TRY(handle_table.Reserve(system.Kernel(), out));
ON_RESULT_FAILURE {
handle_table.Unreserve(*out);
handle_table.Unreserve(system.Kernel(), *out);
};
// Accept the session.
@ -126,7 +129,7 @@ Result AcceptSession(Core::System& system, Handle* out, Handle port_handle) {
R_UNLESS(session != nullptr, ResultNotFound);
// Register the session.
handle_table.Register(*out, session);
handle_table.Register(system.Kernel(), *out, session);
session->Close();
R_SUCCEED();

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@ -49,7 +49,7 @@ Result MapSharedMemory(Core::System& system, Handle shmem_handle, u64 address, u
auto& page_table = process.GetPageTable();
// Get the shared memory.
KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle);
KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(system.Kernel(), shmem_handle);
R_UNLESS(shmem.IsNotNull(), ResultInvalidHandle);
// Verify that the mapping is in range.
@ -79,7 +79,7 @@ Result UnmapSharedMemory(Core::System& system, Handle shmem_handle, u64 address,
auto& page_table = process.GetPageTable();
// Get the shared memory.
KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle);
KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(system.Kernel(), shmem_handle);
R_UNLESS(shmem.IsNotNull(), ResultInvalidHandle);
// Verify that the mapping is in range.

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@ -20,7 +20,7 @@ Result CloseHandle(Core::System& system, Handle handle) {
LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);
// Remove the handle.
R_UNLESS(GetCurrentProcess(system.Kernel()).GetHandleTable().Remove(handle),
R_UNLESS(GetCurrentProcess(system.Kernel()).GetHandleTable().Remove(system.Kernel(), handle),
ResultInvalidHandle);
R_SUCCEED();
@ -35,7 +35,7 @@ Result ResetSignal(Core::System& system, Handle handle) {
// Try to reset as readable event.
{
KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(handle);
KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(system.Kernel(), handle);
if (readable_event.IsNotNull()) {
R_RETURN(readable_event->Reset());
}
@ -43,7 +43,7 @@ Result ResetSignal(Core::System& system, Handle handle) {
// Try to reset as process.
{
KScopedAutoObject process = handle_table.GetObject<KProcess>(handle);
KScopedAutoObject process = handle_table.GetObject<KProcess>(system.Kernel(), handle);
if (process.IsNotNull()) {
R_RETURN(process->Reset());
}
@ -75,9 +75,7 @@ Result WaitSynchronization(Core::System& system, int32_t* out_index, u64 user_ha
ResultInvalidPointer);
// Convert the handles to objects.
R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(
objs.data(), handles.data(), num_handles),
ResultInvalidHandle);
R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(system.Kernel(), objs.data(), handles.data(), num_handles), ResultInvalidHandle);
}
// Ensure handles are closed when we're done.
@ -112,7 +110,7 @@ Result CancelSynchronization(Core::System& system, Handle handle) {
// Get the thread from its handle.
KScopedAutoObject thread =
GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(handle);
GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(system.Kernel(), handle);
R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
// Cancel the thread's wait.

View file

@ -75,7 +75,7 @@ Result CreateThread(Core::System& system, Handle* out_handle, u64 entry_point, u
KThread::Register(kernel, thread);
// Add the thread to the handle table.
R_TRY(process.GetHandleTable().Add(out_handle, thread));
R_TRY(process.GetHandleTable().Add(system.Kernel(), out_handle, thread));
// Pass the thread handle to the thread local region.
process.GetMemory().Write32(GetInteger(thread->GetTlsAddress()) + 0x110, *out_handle);
@ -88,8 +88,7 @@ Result StartThread(Core::System& system, Handle thread_handle) {
LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
// Get the thread from its handle.
KScopedAutoObject thread =
GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(thread_handle);
KScopedAutoObject thread = GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(system.Kernel(), thread_handle);
R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
// Try to start the thread.
@ -150,8 +149,7 @@ Result GetThreadContext3(Core::System& system, u64 out_context, Handle thread_ha
auto& kernel = system.Kernel();
// Get the thread from its handle.
KScopedAutoObject thread =
GetCurrentProcess(kernel).GetHandleTable().GetObject<KThread>(thread_handle);
KScopedAutoObject thread = GetCurrentProcess(kernel).GetHandleTable().GetObject<KThread>(system.Kernel(), thread_handle);
R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
// Require the handle be to a non-current thread in the current process.
@ -175,8 +173,7 @@ Result GetThreadPriority(Core::System& system, s32* out_priority, Handle handle)
LOG_TRACE(Kernel_SVC, "called");
// Get the thread from its handle.
KScopedAutoObject thread =
GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(handle);
KScopedAutoObject thread = GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(system.Kernel(), handle);
R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
// Get the thread's priority.
@ -195,7 +192,7 @@ Result SetThreadPriority(Core::System& system, Handle thread_handle, s32 priorit
R_UNLESS(process.CheckThreadPriority(priority), ResultInvalidPriority);
// Get the thread from its handle.
KScopedAutoObject thread = process.GetHandleTable().GetObject<KThread>(thread_handle);
KScopedAutoObject thread = process.GetHandleTable().GetObject<KThread>(system.Kernel(), thread_handle);
R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
// Set the thread priority.
@ -248,8 +245,7 @@ Result GetThreadCoreMask(Core::System& system, s32* out_core_id, u64* out_affini
LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
// Get the thread from its handle.
KScopedAutoObject thread =
GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(thread_handle);
KScopedAutoObject thread = GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(system.Kernel(), thread_handle);
R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
// Get the core mask.
@ -279,7 +275,7 @@ Result SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id
// Get the thread from its handle.
KScopedAutoObject thread =
GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(thread_handle);
GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(system.Kernel(), thread_handle);
R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
// Set the core mask.
@ -289,8 +285,7 @@ Result SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id
/// Get the ID for the specified thread.
Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) {
// Get the thread from its handle.
KScopedAutoObject thread =
GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(thread_handle);
KScopedAutoObject thread = GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(system.Kernel(), thread_handle);
R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
// Get the thread's id.

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -69,7 +72,7 @@ Result CreateTransferMemory(Core::System& system, Handle* out, u64 address, u64
KTransferMemory::Register(kernel, trmem);
// Add the transfer memory to the handle table.
R_RETURN(handle_table.Add(out, trmem));
R_RETURN(handle_table.Add(system.Kernel(), out, trmem));
}
Result MapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t address, uint64_t size,
@ -86,7 +89,7 @@ Result MapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t add
// Get the transfer memory.
KScopedAutoObject trmem = GetCurrentProcess(system.Kernel())
.GetHandleTable()
.GetObject<KTransferMemory>(trmem_handle);
.GetObject<KTransferMemory>(system.Kernel(), trmem_handle);
R_UNLESS(trmem.IsNotNull(), ResultInvalidHandle);
// Verify that the mapping is in range.
@ -113,7 +116,7 @@ Result UnmapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t a
// Get the transfer memory.
KScopedAutoObject trmem = GetCurrentProcess(system.Kernel())
.GetHandleTable()
.GetObject<KTransferMemory>(trmem_handle);
.GetObject<KTransferMemory>(system.Kernel(), trmem_handle);
R_UNLESS(trmem.IsNotNull(), ResultInvalidHandle);
// Verify that the mapping is in range.

View file

@ -275,14 +275,14 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer() {
for (auto& object : outgoing_copy_objects) {
Handle handle{};
if (object) {
R_TRY(handle_table.Add(&handle, object));
R_TRY(handle_table.Add(kernel, &handle, object));
}
cmd_buf[current_offset++] = handle;
}
for (auto& object : outgoing_move_objects) {
Handle handle{};
if (object) {
R_TRY(handle_table.Add(&handle, object));
R_TRY(handle_table.Add(kernel, &handle, object));
// Close our reference to the object, as it is being moved to the caller.
object->Close();

View file

@ -373,7 +373,7 @@ public:
template <typename T>
Kernel::KScopedAutoObject<T> GetObjectFromHandle(u32 handle) {
auto obj = client_handle_table->GetObjectForIpc(handle, thread);
auto obj = client_handle_table->GetObjectForIpc(kernel, handle, thread);
if (obj.IsNotNull()) {
return obj->DynamicCast<T*>();
}

View file

@ -31,7 +31,7 @@ RenderdocAPI::RenderdocAPI() {
#elif defined(__HAIKU__)
// no rtld on haiku
#else
#ifdef ANDROID
#ifdef __ANDROID__
static constexpr const char RENDERDOC_LIB[] = "libVkLayer_GLES_RenderDoc.so";
#else
static constexpr const char RENDERDOC_LIB[] = "librenderdoc.so";

View file

@ -299,10 +299,24 @@ if ("loongarch64" IN_LIST ARCHITECTURE)
target_link_libraries(dynarmic PRIVATE lagoon::lagoon)
target_sources(dynarmic PRIVATE
backend/loongarch64/exclusive_monitor.cpp
backend/loongarch64/abi.h
backend/loongarch64/a32_jitstate.cpp
backend/loongarch64/a32_jitstate.h
backend/loongarch64/code_block.h
backend/loongarch64/emit_context.h
backend/loongarch64/emit_loongarch64.cpp
backend/loongarch64/emit_loongarch64.h
backend/loongarch64/emit_loongarch64_a32.cpp
backend/loongarch64/emit_loongarch64_data_processing.cpp
backend/loongarch64/a32_address_space.cpp
backend/loongarch64/a32_address_space.h
backend/loongarch64/a32_interface.cpp
backend/loongarch64/a64_interface.cpp
backend/loongarch64/code_block.h
backend/loongarch64/exclusive_monitor.cpp
backend/loongarch64/reg_alloc.cpp
backend/loongarch64/reg_alloc.h
backend/loongarch64/stack_layout.h
backend/loongarch64/lagoon_cpp.h
common/spin_lock_loongarch64.cpp
)

View file

@ -0,0 +1,123 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "dynarmic/backend/loongarch64/a32_address_space.h"
#include "common/assert.h"
#include "dynarmic/backend/loongarch64/a32_jitstate.h"
#include "dynarmic/backend/loongarch64/abi.h"
#include "dynarmic/backend/loongarch64/emit_loongarch64.h"
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
#include "dynarmic/frontend/A32/translate/a32_translate.h"
#include "dynarmic/ir/opt_passes.h"
namespace Dynarmic::Backend::LoongArch64 {
A32AddressSpace::A32AddressSpace(const A32::UserConfig& conf)
: conf(conf)
, cb(conf.code_cache_size) {
EmitPrelude();
}
void A32AddressSpace::GenerateIR(IR::Block& ir_block, IR::LocationDescriptor descriptor) const {
A32::Translate(ir_block, A32::LocationDescriptor{descriptor}, conf.callbacks, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions});
Optimization::Optimize(ir_block, conf, {.sha256 = true});
}
CodePtr A32AddressSpace::Get(IR::LocationDescriptor descriptor) {
if (const auto iter = block_entries.find(descriptor.Value()); iter != block_entries.end()) {
return iter->second;
}
return nullptr;
}
CodePtr A32AddressSpace::GetOrEmit(IR::LocationDescriptor descriptor) {
if (CodePtr block_entry = Get(descriptor)) {
return block_entry;
}
IR::Block ir_block{descriptor};
GenerateIR(ir_block, descriptor);
const EmittedBlockInfo block_info = Emit(std::move(ir_block));
block_infos.insert_or_assign(descriptor.Value(), block_info);
block_entries.insert_or_assign(descriptor.Value(), block_info.entry_point);
return block_info.entry_point;
}
void A32AddressSpace::ClearCache() {
block_entries.clear();
block_infos.clear();
SetCursorPtr(prelude_info.end_of_prelude);
}
void A32AddressSpace::EmitPrelude() {
prelude_info.run_code = GetCursorPtr<PreludeInfo::RunCodeFuncType>();
// Save all GPRs except sp (r3) and tp (r2)
la_addi_d(&cb.as, LA_SP, LA_SP, -64 * 8);
for (u32 i = 1; i < 32; i++) {
if (i == LA_SP || i == LA_TP)
continue;
la_st_d(&cb.as, static_cast<la_gpr_t>(i), LA_SP, static_cast<int32_t>(i * 8));
}
// Set up reserved registers and jump to block entry
la_move(&cb.as, Xstate, LA_A1); // Xstate = state ptr
la_move(&cb.as, Xhalt, LA_A2); // Xhalt = halt reason ptr
la_jr(&cb.as, LA_A0); // jump to block_entry
prelude_info.return_from_run_code = GetCursorPtr<CodePtr>();
// Restore all GPRs except sp and tp
for (u32 i = 1; i < 32; i++) {
if (i == LA_SP || i == LA_TP)
continue;
la_ld_d(&cb.as, static_cast<la_gpr_t>(i), LA_SP, static_cast<int32_t>(i * 8));
}
la_addi_d(&cb.as, LA_SP, LA_SP, 64 * 8);
la_ret(&cb.as);
prelude_info.end_of_prelude = GetCursorPtr<CodePtr>();
}
void A32AddressSpace::SetCursorPtr(CodePtr ptr) {
cb.as.cursor = reinterpret_cast<uint8_t*>(ptr);
}
size_t A32AddressSpace::GetRemainingSize() {
return la_get_remaining_buffer_size(&cb.as);
}
EmittedBlockInfo A32AddressSpace::Emit(IR::Block block) {
if (GetRemainingSize() < 1024 * 1024) {
ClearCache();
}
EmitConfig emit_conf{};
EmittedBlockInfo block_info = EmitLoongArch64(cb.as, std::move(block), emit_conf);
Link(block_info);
return block_info;
}
void A32AddressSpace::Link(EmittedBlockInfo& block_info) {
for (const auto& reloc : block_info.relocations) {
uint8_t* patch_location = reinterpret_cast<uint8_t*>(block_info.entry_point) + reloc.code_offset;
switch (reloc.target) {
case LinkTarget::ReturnFromRunCode: {
std::ptrdiff_t off = reinterpret_cast<uint8_t*>(prelude_info.return_from_run_code) - patch_location;
lagoon_assembler_t patch_as;
la_init_assembler(&patch_as, patch_location, 4);
la_b(&patch_as, static_cast<int32_t>(off));
break;
}
default:
ASSERT(false && "Invalid relocation target");
}
}
}
} // namespace Dynarmic::Backend::LoongArch64

View file

@ -0,0 +1,68 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "dynarmic/backend/loongarch64/lagoon_cpp.h"
#include <ankerl/unordered_dense.h>
#include "dynarmic/backend/loongarch64/code_block.h"
#include "dynarmic/backend/loongarch64/emit_loongarch64.h"
#include "dynarmic/interface/A32/config.h"
#include "dynarmic/interface/halt_reason.h"
#include "dynarmic/ir/basic_block.h"
#include "dynarmic/ir/location_descriptor.h"
namespace Dynarmic::Backend::LoongArch64 {
struct A32JitState;
class A32AddressSpace final {
public:
explicit A32AddressSpace(const A32::UserConfig& conf);
void GenerateIR(IR::Block& ir_block, IR::LocationDescriptor descriptor) const;
CodePtr Get(IR::LocationDescriptor descriptor);
CodePtr GetOrEmit(IR::LocationDescriptor descriptor);
void ClearCache();
private:
void EmitPrelude();
template<typename T>
T GetCursorPtr() {
return reinterpret_cast<T>(cb.as.cursor);
}
template<typename T>
T GetCursorPtr() const {
return reinterpret_cast<T>(cb.as.cursor);
}
template<typename T>
T GetMemPtr() const {
return cb.ptr<T>();
}
void SetCursorPtr(CodePtr ptr);
size_t GetRemainingSize();
EmittedBlockInfo Emit(IR::Block block);
void Link(EmittedBlockInfo& block_info);
const A32::UserConfig conf;
CodeBlock cb;
ankerl::unordered_dense::map<u64, CodePtr> block_entries;
ankerl::unordered_dense::map<u64, EmittedBlockInfo> block_infos;
public:
struct PreludeInfo {
CodePtr end_of_prelude;
using RunCodeFuncType = HaltReason (*)(CodePtr entry_point, A32JitState* context, volatile u32* halt_reason);
RunCodeFuncType run_code;
CodePtr return_from_run_code;
} prelude_info;
};
} // namespace Dynarmic::Backend::LoongArch64

View file

@ -1,101 +1,134 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <array>
#include <memory>
#include <string>
#include <utility>
#include <mutex>
#include <boost/icl/interval_set.hpp>
#include "common/assert.h"
#include "common/common_types.h"
#include "dynarmic/backend/loongarch64/a32_address_space.h"
#include "dynarmic/backend/loongarch64/a32_jitstate.h"
#include "dynarmic/common/atomic.h"
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
#include "dynarmic/interface/A32/a32.h"
namespace Dynarmic::A32 {
using namespace Backend::LoongArch64;
struct Jit::Impl final {
explicit Impl(UserConfig conf_) : conf(std::move(conf_)) {}
Impl(Jit* jit_interface, A32::UserConfig conf)
: jit_interface(jit_interface)
, conf(conf)
, current_address_space(conf) {}
HaltReason Run() {
UNIMPLEMENTED();
return halt_reason;
ASSERT(!jit_interface->is_executing);
jit_interface->is_executing = true;
const auto location_descriptor = current_state.GetLocationDescriptor();
const auto entry_point = current_address_space.GetOrEmit(location_descriptor);
current_address_space.prelude_info.run_code(entry_point, &current_state, &halt_reason);
HaltReason hr = static_cast<HaltReason>(Atomic::Exchange(&halt_reason, 0));
jit_interface->is_executing = false;
return hr;
}
HaltReason Step() {
UNIMPLEMENTED();
return halt_reason | HaltReason::Step;
ASSERT(!jit_interface->is_executing);
jit_interface->is_executing = true;
const auto location_descriptor = A32::LocationDescriptor{current_state.GetLocationDescriptor()}.SetSingleStepping(true);
const auto entry_point = current_address_space.GetOrEmit(location_descriptor);
current_address_space.prelude_info.run_code(entry_point, &current_state, &halt_reason);
HaltReason hr = static_cast<HaltReason>(Atomic::Exchange(&halt_reason, 0));
jit_interface->is_executing = false;
return hr;
}
void ClearCache() {
std::unique_lock lock{invalidation_mutex};
invalidate_entire_cache = true;
HaltExecution(HaltReason::CacheInvalidation);
}
void InvalidateCacheRange(u32, std::size_t) {
void InvalidateCacheRange(u32 start_address, size_t length) {
std::unique_lock lock{invalidation_mutex};
invalid_cache_ranges.add(boost::icl::discrete_interval<u32>::closed(start_address, static_cast<u32>(start_address + length - 1)));
HaltExecution(HaltReason::CacheInvalidation);
}
void Reset() {
regs = {};
ext_regs = {};
cpsr = 0;
fpscr = 0;
halt_reason = {};
current_state = {};
}
void HaltExecution(HaltReason hr) {
halt_reason |= hr;
Atomic::Or(&halt_reason, static_cast<u32>(hr));
}
void ClearHalt(HaltReason hr) {
halt_reason &= ~hr;
Atomic::And(&halt_reason, ~static_cast<u32>(hr));
}
std::array<u32, 16>& Regs() {
return regs;
return current_state.regs;
}
const std::array<u32, 16>& Regs() const {
return regs;
return current_state.regs;
}
std::array<u32, 64>& ExtRegs() {
return ext_regs;
return current_state.ext_regs;
}
const std::array<u32, 64>& ExtRegs() const {
return ext_regs;
return current_state.ext_regs;
}
u32 Cpsr() const {
return cpsr;
return current_state.Cpsr();
}
void SetCpsr(u32 value) {
cpsr = value;
current_state.SetCpsr(value);
}
u32 Fpscr() const {
return fpscr;
return current_state.Fpscr();
}
void SetFpscr(u32 value) {
fpscr = value;
current_state.SetFpscr(value);
}
void ClearExclusiveState() {}
void ClearExclusiveState() {
current_state.exclusive_state = false;
}
std::string Disassemble() const {
return {};
}
UserConfig conf;
std::array<u32, 16> regs{};
std::array<u32, 64> ext_regs{};
u32 cpsr = 0;
u32 fpscr = 0;
HaltReason halt_reason{};
private:
Jit* jit_interface;
A32::UserConfig conf;
A32JitState current_state{};
A32AddressSpace current_address_space;
volatile u32 halt_reason = 0;
std::mutex invalidation_mutex;
boost::icl::interval_set<u32> invalid_cache_ranges;
bool invalidate_entire_cache = false;
};
Jit::Jit(UserConfig conf) : impl(std::make_unique<Impl>(std::move(conf))) {}
Jit::Jit(UserConfig conf)
: impl(std::make_unique<Impl>(this, conf)) {}
Jit::~Jit() = default;
@ -111,7 +144,7 @@ void Jit::ClearCache() {
impl->ClearCache();
}
void Jit::InvalidateCacheRange(u32 start_address, std::size_t length) {
void Jit::InvalidateCacheRange(u32 start_address, size_t length) {
impl->InvalidateCacheRange(start_address, length);
}

View file

@ -0,0 +1,57 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "dynarmic/backend/loongarch64/a32_jitstate.h"
#include "dynarmic/mcl/bit.hpp"
#include "common/common_types.h"
namespace Dynarmic::Backend::LoongArch64 {
u32 A32JitState::Cpsr() const {
u32 cpsr = 0;
cpsr |= cpsr_nzcv;
cpsr |= cpsr_q;
cpsr |= mcl::bit::get_bit<31>(cpsr_ge) ? 1 << 19 : 0;
cpsr |= mcl::bit::get_bit<23>(cpsr_ge) ? 1 << 18 : 0;
cpsr |= mcl::bit::get_bit<15>(cpsr_ge) ? 1 << 17 : 0;
cpsr |= mcl::bit::get_bit<7>(cpsr_ge) ? 1 << 16 : 0;
cpsr |= mcl::bit::get_bit<1>(upper_location_descriptor) ? 1 << 9 : 0;
cpsr |= mcl::bit::get_bit<0>(upper_location_descriptor) ? 1 << 5 : 0;
cpsr |= static_cast<u32>(upper_location_descriptor & 0b11111100'00000000);
cpsr |= static_cast<u32>(upper_location_descriptor & 0b00000011'00000000) << 17;
cpsr |= cpsr_jaifm;
return cpsr;
}
void A32JitState::SetCpsr(u32 cpsr) {
cpsr_nzcv = cpsr & 0xF0000000;
cpsr_q = cpsr & (1 << 27);
cpsr_ge = 0;
cpsr_ge |= mcl::bit::get_bit<19>(cpsr) ? 0xFF000000 : 0;
cpsr_ge |= mcl::bit::get_bit<18>(cpsr) ? 0x00FF0000 : 0;
cpsr_ge |= mcl::bit::get_bit<17>(cpsr) ? 0x0000FF00 : 0;
cpsr_ge |= mcl::bit::get_bit<16>(cpsr) ? 0x000000FF : 0;
upper_location_descriptor &= 0xFFFF0000;
upper_location_descriptor |= mcl::bit::get_bit<9>(cpsr) ? 2 : 0;
upper_location_descriptor |= mcl::bit::get_bit<5>(cpsr) ? 1 : 0;
upper_location_descriptor |= (cpsr >> 0) & 0b11111100'00000000;
upper_location_descriptor |= (cpsr >> 17) & 0b00000011'00000000;
cpsr_jaifm = cpsr & 0x010001DF;
}
constexpr u32 FPCR_MASK = A32::LocationDescriptor::FPSCR_MODE_MASK;
constexpr u32 FPSR_MASK = 0x0800009F;
u32 A32JitState::Fpscr() const {
return (upper_location_descriptor & FPCR_MASK) | fpsr | fpsr_nzcv;
}
void A32JitState::SetFpscr(u32 fpscr) {
fpsr = fpscr & FPSR_MASK;
fpsr_nzcv = fpscr & 0xF0000000;
upper_location_descriptor = (upper_location_descriptor & 0x0000ffff) | (fpscr & FPCR_MASK);
}
} // namespace Dynarmic::Backend::LoongArch64

View file

@ -0,0 +1,43 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <array>
#include "common/common_types.h"
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
#include "dynarmic/ir/location_descriptor.h"
namespace Dynarmic::Backend::LoongArch64 {
struct A32JitState {
u32 cpsr_nzcv = 0;
u32 cpsr_q = 0;
u32 cpsr_jaifm = 0;
u32 cpsr_ge = 0;
u32 fpsr = 0;
u32 fpsr_nzcv = 0;
std::array<u32, 16> regs{};
u32 upper_location_descriptor;
alignas(16) std::array<u32, 64> ext_regs{};
u32 exclusive_state = 0;
u32 Cpsr() const;
void SetCpsr(u32 cpsr);
u32 Fpscr() const;
void SetFpscr(u32 fpscr);
IR::LocationDescriptor GetLocationDescriptor() const {
return IR::LocationDescriptor{regs[15] | (static_cast<u64>(upper_location_descriptor) << 32)};
}
};
} // namespace Dynarmic::Backend::LoongArch64

View file

@ -0,0 +1,39 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <initializer_list>
#include "dynarmic/backend/loongarch64/lagoon_cpp.h"
#include "common/common_types.h"
namespace Dynarmic::Backend::LoongArch64 {
// Reserved registers used by the dynarmic JIT
// Xstate (s0/r23): pointer to JIT CPU state, callee-saved
// Xhalt (s1/r24): halt reason pointer, callee-saved
// Xscratch0 (t7/r19): scratch register (caller-saved)
// Xscratch1 (t8/r20): scratch register (caller-saved)
constexpr la_gpr_t Xstate = LA_S0; // r23
constexpr la_gpr_t Xhalt = LA_S1; // r24
constexpr la_gpr_t Xscratch0 = LA_T7; // r19
constexpr la_gpr_t Xscratch1 = LA_T8; // r20
// GPR allocation order: callee-saved (s2-s8) first, then temps, then args
constexpr std::initializer_list<u32> GPR_ORDER{
25, 26, 27, 28, 29, 30, 31, // s2-s8 (r25-r31)
12, 13, 14, 15, 16, 17, 18, // t0-t6 (r12-r18)
4, 5, 6, 7, 8, 9, 10, 11 // a0-a7 (r4-r11)
};
// FPR allocation order: callee-saved (fs0-fs7) first, then ft, then fa
constexpr std::initializer_list<u32> FPR_ORDER{
24, 25, 26, 27, 28, 29, 30, 31, // fs0-fs7 (f24-f31)
8, 9, 10, 11, 12, 13, 14, 15, // ft0-ft7 (f8-f15)
16, 17, 18, 19, 20, 21, 22, 23, // ft8-ft15 (f16-f23)
0, 1, 2, 3, 4, 5, 6, 7 // fa0-fa7 (f0-f7)
};
} // namespace Dynarmic::Backend::LoongArch64

View file

@ -4,20 +4,44 @@
#pragma once
#include <cstdint>
#include <new>
#include <type_traits>
#include <sys/mman.h>
#include "dynarmic/backend/loongarch64/lagoon_cpp.h"
#include "common/assert.h"
#include "common/common_types.h"
namespace Dynarmic::Backend::LoongArch64 {
class CodeBlock {
public:
explicit CodeBlock(std::size_t size) noexcept
: memsize(size) {
mem = static_cast<u8*>(mmap(nullptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0));
ASSERT(mem != MAP_FAILED);
la_init_assembler(&as, mem, size);
}
~CodeBlock() noexcept {
if (mem == nullptr) {
return;
}
munmap(mem, memsize);
}
template<typename T>
T ptr() const noexcept {
static_assert(std::is_pointer_v<T> || std::is_same_v<T, std::uintptr_t> || std::is_same_v<T, std::intptr_t>);
return reinterpret_cast<T>(mem);
}
lagoon_assembler_t as{};
private:
u8* mem = nullptr;
size_t memsize = 0;
};
} // namespace Dynarmic::Backend::LoongArch64

View file

@ -0,0 +1,24 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "dynarmic/backend/loongarch64/emit_loongarch64.h"
#include "dynarmic/backend/loongarch64/reg_alloc.h"
namespace Dynarmic::IR {
class Block;
} // namespace Dynarmic::IR
namespace Dynarmic::Backend::LoongArch64 {
struct EmitConfig;
struct EmitContext {
IR::Block& block;
RegAlloc& reg_alloc;
const EmitConfig& emit_conf;
EmittedBlockInfo& ebi;
};
} // namespace Dynarmic::Backend::LoongArch64

View file

@ -0,0 +1,113 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "dynarmic/backend/loongarch64/emit_loongarch64.h"
#include "dynarmic/backend/loongarch64/a32_jitstate.h"
#include "dynarmic/backend/loongarch64/abi.h"
#include "dynarmic/backend/loongarch64/emit_context.h"
#include "dynarmic/backend/loongarch64/reg_alloc.h"
#include "dynarmic/ir/basic_block.h"
#include "dynarmic/ir/microinstruction.h"
#include "dynarmic/ir/opcodes.h"
namespace Dynarmic::Backend::LoongArch64 {
template<IR::Opcode op>
void EmitIR(lagoon_assembler_t&, EmitContext&, IR::Inst*) {
ASSERT(false && "Unimplemented opcode");
}
template<>
void EmitIR<IR::Opcode::Void>(lagoon_assembler_t&, EmitContext&, IR::Inst*) {}
template<>
void EmitIR<IR::Opcode::A32GetRegister>(lagoon_assembler_t&, EmitContext& ctx, IR::Inst* inst);
template<>
void EmitIR<IR::Opcode::A32SetRegister>(lagoon_assembler_t&, EmitContext& ctx, IR::Inst* inst);
template<>
void EmitIR<IR::Opcode::A32SetCpsrNZC>(lagoon_assembler_t&, EmitContext& ctx, IR::Inst* inst);
template<>
void EmitIR<IR::Opcode::LogicalShiftLeft32>(lagoon_assembler_t&, EmitContext& ctx, IR::Inst* inst);
template<>
void EmitIR<IR::Opcode::GetCarryFromOp>(lagoon_assembler_t&, EmitContext& ctx, IR::Inst* inst) {
ASSERT(ctx.reg_alloc.IsValueLive(inst));
}
template<>
void EmitIR<IR::Opcode::GetNZFromOp>(lagoon_assembler_t& as, EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
auto Xvalue = ctx.reg_alloc.ReadX(args[0]);
auto Xnz = ctx.reg_alloc.WriteX(inst);
RegAlloc::Realize(Xvalue, Xnz);
// Z flag (bit 30): set if value == 0
la_sltui(&as, Xnz->index, Xvalue->index, 1);
la_slli_d(&as, Xnz->index, Xnz->index, 30);
// N flag (bit 31): set if value < 0 (signed)
la_slt(&as, Xscratch0, Xvalue->index, LA_ZERO);
la_slli_d(&as, Xscratch0, Xscratch0, 31);
la_or(&as, Xnz->index, Xnz->index, Xscratch0);
}
EmittedBlockInfo EmitLoongArch64(lagoon_assembler_t& as, IR::Block block, const EmitConfig& emit_conf) {
EmittedBlockInfo ebi;
RegAlloc reg_alloc{as, std::vector<u32>(GPR_ORDER), std::vector<u32>(FPR_ORDER)};
EmitContext ctx{block, reg_alloc, emit_conf, ebi};
ebi.entry_point = reinterpret_cast<CodePtr>(as.cursor);
for (auto iter = block.instructions.begin(); iter != block.instructions.end(); ++iter) {
IR::Inst* inst = &*iter;
switch (inst->GetOpcode()) {
#define OPCODE(name, type, ...) \
case IR::Opcode::name: \
EmitIR<IR::Opcode::name>(as, ctx, inst); \
break;
#define A32OPC(name, type, ...) \
case IR::Opcode::A32##name: \
EmitIR<IR::Opcode::A32##name>(as, ctx, inst); \
break;
#define A64OPC(name, type, ...) \
case IR::Opcode::A64##name: \
EmitIR<IR::Opcode::A64##name>(as, ctx, inst); \
break;
#include "dynarmic/ir/opcodes.inc"
#undef OPCODE
#undef A32OPC
#undef A64OPC
default:
UNREACHABLE();
}
reg_alloc.UpdateAllUses();
}
reg_alloc.UpdateAllUses();
reg_alloc.AssertNoMoreUses();
// TODO: Emit Terminal
const auto term = block.GetTerminal();
const IR::Term::LinkBlock* link_block_term = boost::get<IR::Term::LinkBlock>(&term);
ASSERT(link_block_term);
la_load_immediate64(&as, Xscratch0, link_block_term->next.Value());
la_st_w(&as, Xscratch0, Xstate, static_cast<int32_t>(offsetof(A32JitState, regs) + sizeof(u32) * 15));
ebi.relocations.push_back(Relocation{
reinterpret_cast<CodePtr>(as.cursor) - ebi.entry_point,
LinkTarget::ReturnFromRunCode
});
la_nop(&as);
ebi.size = reinterpret_cast<CodePtr>(as.cursor) - ebi.entry_point;
return ebi;
}
} // namespace Dynarmic::Backend::LoongArch64

View file

@ -0,0 +1,55 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <array>
#include <cstddef>
#include <vector>
#include "dynarmic/backend/loongarch64/lagoon_cpp.h"
#include "common/common_types.h"
namespace Dynarmic::IR {
class Block;
class Inst;
class LocationDescriptor;
enum class Cond;
enum class Opcode;
} // namespace Dynarmic::IR
namespace Dynarmic::A32 {
class Coprocessor;
} // namespace Dynarmic::A32
namespace Dynarmic::Backend::LoongArch64 {
using CodePtr = std::byte*;
enum class LinkTarget {
ReturnFromRunCode,
};
struct Relocation {
std::ptrdiff_t code_offset;
LinkTarget target;
};
struct EmittedBlockInfo {
std::vector<Relocation> relocations;
CodePtr entry_point;
size_t size;
size_t cycle_count;
};
struct EmitConfig {};
struct EmitContext;
template<IR::Opcode op>
void EmitIR(lagoon_assembler_t& as, EmitContext& ctx, IR::Inst* inst);
EmittedBlockInfo EmitLoongArch64(lagoon_assembler_t& as, IR::Block block, const EmitConfig& emit_conf);
} // namespace Dynarmic::Backend::LoongArch64

View file

@ -0,0 +1,59 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "dynarmic/backend/loongarch64/lagoon_cpp.h"
#include "dynarmic/backend/loongarch64/a32_jitstate.h"
#include "dynarmic/backend/loongarch64/abi.h"
#include "dynarmic/backend/loongarch64/emit_context.h"
#include "dynarmic/backend/loongarch64/emit_loongarch64.h"
#include "dynarmic/backend/loongarch64/reg_alloc.h"
#include "dynarmic/ir/basic_block.h"
#include "dynarmic/ir/microinstruction.h"
#include "dynarmic/ir/opcodes.h"
namespace Dynarmic::Backend::LoongArch64 {
template<>
void EmitIR<IR::Opcode::A32GetRegister>(lagoon_assembler_t& as, EmitContext& ctx, IR::Inst* inst) {
const A32::Reg reg = inst->GetArg(0).GetA32RegRef();
auto Xresult = ctx.reg_alloc.WriteX(inst);
RegAlloc::Realize(Xresult);
la_ld_wu(&as, Xresult->index, Xstate,
static_cast<int32_t>(offsetof(A32JitState, regs) + sizeof(u32) * static_cast<size_t>(reg)));
}
template<>
void EmitIR<IR::Opcode::A32SetRegister>(lagoon_assembler_t& as, EmitContext& ctx, IR::Inst* inst) {
const A32::Reg reg = inst->GetArg(0).GetA32RegRef();
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
auto Xvalue = ctx.reg_alloc.ReadX(args[1]);
RegAlloc::Realize(Xvalue);
la_st_w(&as, Xvalue->index, Xstate,
static_cast<int32_t>(offsetof(A32JitState, regs) + sizeof(u32) * static_cast<size_t>(reg)));
}
template<>
void EmitIR<IR::Opcode::A32SetCpsrNZC>(lagoon_assembler_t& as, EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
ASSERT(!args[0].IsImmediate() && !args[1].IsImmediate());
auto Xnz = ctx.reg_alloc.ReadX(args[0]);
auto Xc = ctx.reg_alloc.ReadX(args[1]);
RegAlloc::Realize(Xnz, Xc);
la_ld_wu(&as, Xscratch0, Xstate, static_cast<int32_t>(offsetof(A32JitState, cpsr_nzcv)));
la_load_immediate64(&as, Xscratch1, 0x10000000);
la_and(&as, Xscratch0, Xscratch0, Xscratch1);
la_or(&as, Xscratch0, Xscratch0, Xnz->index);
la_slli_w(&as, Xscratch1, Xc->index, 29);
la_or(&as, Xscratch0, Xscratch0, Xscratch1);
la_st_w(&as, Xscratch0, Xstate, static_cast<int32_t>(offsetof(A32JitState, cpsr_nzcv)));
}
} // namespace Dynarmic::Backend::LoongArch64

View file

@ -0,0 +1,52 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "dynarmic/backend/loongarch64/lagoon_cpp.h"
#include "dynarmic/backend/loongarch64/abi.h"
#include "dynarmic/backend/loongarch64/emit_context.h"
#include "dynarmic/backend/loongarch64/emit_loongarch64.h"
#include "dynarmic/backend/loongarch64/reg_alloc.h"
#include "dynarmic/ir/basic_block.h"
#include "dynarmic/ir/microinstruction.h"
#include "dynarmic/ir/opcodes.h"
namespace Dynarmic::Backend::LoongArch64 {
template<>
void EmitIR<IR::Opcode::LogicalShiftLeft32>(lagoon_assembler_t& as, EmitContext& ctx, IR::Inst* inst) {
const auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp);
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
auto& operand_arg = args[0];
auto& shift_arg = args[1];
auto& carry_arg = args[2];
ASSERT(carry_inst != nullptr);
ASSERT(shift_arg.IsImmediate());
auto Xresult = ctx.reg_alloc.WriteX(inst);
auto Xcarry_out = ctx.reg_alloc.WriteX(carry_inst);
auto Xoperand = ctx.reg_alloc.ReadX(operand_arg);
auto Xcarry_in = ctx.reg_alloc.ReadX(carry_arg);
RegAlloc::Realize(Xresult, Xcarry_out, Xoperand, Xcarry_in);
const u8 shift = shift_arg.GetImmediateU8();
if (shift == 0) {
la_addi_w(&as, Xresult->index, Xoperand->index, 0);
la_addi_w(&as, Xcarry_out->index, Xcarry_in->index, 0);
} else if (shift < 32) {
la_srli_w(&as, Xcarry_out->index, Xoperand->index, 32 - shift);
la_andi(&as, Xcarry_out->index, Xcarry_out->index, 1);
la_slli_w(&as, Xresult->index, Xoperand->index, shift);
} else if (shift > 32) {
la_move(&as, Xresult->index, LA_ZERO);
la_move(&as, Xcarry_out->index, LA_ZERO);
} else {
la_andi(&as, Xcarry_out->index, Xoperand->index, 1);
la_move(&as, Xresult->index, LA_ZERO);
}
}
} // namespace Dynarmic::Backend::LoongArch64

View file

@ -0,0 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
extern "C" {
#include <lagoon.h>
}

View file

@ -0,0 +1,364 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "dynarmic/backend/loongarch64/reg_alloc.h"
#include <algorithm>
#include <array>
#include <limits>
#include "dynarmic/backend/loongarch64/lagoon_cpp.h"
#include "common/assert.h"
#include "common/common_types.h"
#include "dynarmic/common/always_false.h"
namespace Dynarmic::Backend::LoongArch64 {
constexpr size_t spill_offset = offsetof(StackLayout, spill);
constexpr size_t spill_slot_size = sizeof(decltype(StackLayout::spill)::value_type);
static bool IsValuelessType(IR::Type type) {
switch (type) {
case IR::Type::Table:
return true;
default:
return false;
}
}
IR::Type Argument::GetType() const {
return value.GetType();
}
bool Argument::IsImmediate() const {
return value.IsImmediate();
}
bool Argument::GetImmediateU1() const {
return value.GetU1();
}
u8 Argument::GetImmediateU8() const {
const u64 imm = value.GetImmediateAsU64();
ASSERT(imm < 0x100);
return u8(imm);
}
u16 Argument::GetImmediateU16() const {
const u64 imm = value.GetImmediateAsU64();
ASSERT(imm < 0x10000);
return u16(imm);
}
u32 Argument::GetImmediateU32() const {
const u64 imm = value.GetImmediateAsU64();
ASSERT(imm < 0x100000000);
return u32(imm);
}
u64 Argument::GetImmediateU64() const {
return value.GetImmediateAsU64();
}
IR::Cond Argument::GetImmediateCond() const {
ASSERT(IsImmediate() && GetType() == IR::Type::Cond);
return value.GetCond();
}
IR::AccType Argument::GetImmediateAccType() const {
ASSERT(IsImmediate() && GetType() == IR::Type::AccType);
return value.GetAccType();
}
bool HostLocInfo::Contains(const IR::Inst* value) const {
return std::find(values.begin(), values.end(), value) != values.end();
}
void HostLocInfo::SetupScratchLocation() {
ASSERT(IsCompletelyEmpty());
locked = 1;
realized = true;
}
bool HostLocInfo::IsCompletelyEmpty() const {
return values.empty() && !locked && !accumulated_uses && !expected_uses && !uses_this_inst && !realized;
}
void HostLocInfo::UpdateUses() {
accumulated_uses += uses_this_inst;
uses_this_inst = 0;
if (accumulated_uses == expected_uses) {
values.clear();
accumulated_uses = 0;
expected_uses = 0;
}
}
RegAlloc::ArgumentInfo RegAlloc::GetArgumentInfo(IR::Inst* inst) {
ArgumentInfo ret = {Argument{}, Argument{}, Argument{}, Argument{}};
for (size_t i = 0; i < inst->NumArgs(); i++) {
const IR::Value arg = inst->GetArg(i);
ret[i].value = arg;
if (!arg.IsImmediate() && !IsValuelessType(arg.GetType())) {
ASSERT(ValueLocation(arg.GetInst()) && "argument must already been defined");
ValueInfo(arg.GetInst()).uses_this_inst++;
}
}
return ret;
}
bool RegAlloc::IsValueLive(IR::Inst* inst) const {
return !!ValueLocation(inst);
}
void RegAlloc::UpdateAllUses() {
for (auto& info : hostloc_info) {
info.UpdateUses();
}
}
void RegAlloc::DefineAsExisting(IR::Inst* inst, Argument& arg) {
ASSERT(!ValueLocation(inst));
if (arg.value.IsImmediate()) {
inst->ReplaceUsesWith(arg.value);
return;
}
auto& info = ValueInfo(arg.value.GetInst());
info.values.emplace_back(inst);
info.expected_uses += inst->UseCount();
}
void RegAlloc::AssertNoMoreUses() const {
// TODO: Re-enable this assert once all register allocation issues are fixed
// const auto is_empty = [](const auto& i) { return i.IsCompletelyEmpty(); };
// ASSERT(std::all_of(hostloc_info.begin(), hostloc_info.end(), is_empty));
}
template<HostLoc::Kind kind>
u32 RegAlloc::GenerateImmediate(const IR::Value& value) {
if constexpr (kind == HostLoc::Kind::Gpr) {
const u32 new_location_index = AllocateRegister(gpr_order, GprOffset);
SpillGpr(new_location_index);
hostloc_info[GprOffset + new_location_index].SetupScratchLocation();
la_load_immediate64(&as, static_cast<la_gpr_t>(new_location_index),
static_cast<int64_t>(value.GetImmediateAsU64()));
return new_location_index;
} else if constexpr (kind == HostLoc::Kind::Fpr) {
ASSERT(false && "Unimplemented instruction");
} else {
UNREACHABLE();
}
return 0;
}
template<HostLoc::Kind required_kind>
u32 RegAlloc::RealizeReadImpl(const IR::Value& value) {
if (value.IsImmediate()) {
return GenerateImmediate<required_kind>(value);
}
const auto current_location = ValueLocation(value.GetInst());
ASSERT(current_location);
if (current_location->kind == required_kind) {
ValueInfo(*current_location).realized = true;
return current_location->index;
}
ASSERT(!ValueInfo(*current_location).realized);
ASSERT(!ValueInfo(*current_location).locked);
if constexpr (required_kind == HostLoc::Kind::Gpr) {
const u32 new_location_index = AllocateRegister(gpr_order, GprOffset);
SpillGpr(new_location_index);
switch (current_location->kind) {
case HostLoc::Kind::Gpr:
UNREACHABLE();
case HostLoc::Kind::Fpr:
la_movfr2gr_d(&as, static_cast<la_gpr_t>(new_location_index),
static_cast<la_fpr_t>(current_location->index));
break;
case HostLoc::Kind::Spill:
la_ld_d(&as, static_cast<la_gpr_t>(new_location_index), LA_SP,
static_cast<int32_t>(spill_offset + current_location->index * spill_slot_size));
break;
}
hostloc_info[GprOffset + new_location_index] = std::exchange(ValueInfo(*current_location), {});
hostloc_info[GprOffset + new_location_index].realized = true;
return new_location_index;
} else if constexpr (required_kind == HostLoc::Kind::Fpr) {
const u32 new_location_index = AllocateRegister(fpr_order, FprOffset);
SpillFpr(new_location_index);
switch (current_location->kind) {
case HostLoc::Kind::Gpr:
la_movgr2fr_d(&as, static_cast<la_fpr_t>(new_location_index),
static_cast<la_gpr_t>(current_location->index));
break;
case HostLoc::Kind::Fpr:
UNREACHABLE();
case HostLoc::Kind::Spill:
la_fld_d(&as, static_cast<la_fpr_t>(new_location_index), LA_SP,
static_cast<int32_t>(spill_offset + current_location->index * spill_slot_size));
break;
}
hostloc_info[FprOffset + new_location_index] = std::exchange(ValueInfo(*current_location), {});
hostloc_info[FprOffset + new_location_index].realized = true;
return new_location_index;
} else {
UNREACHABLE();
}
}
u32 RegAlloc::RealizeWriteImpl(const IR::Inst* value, HostLoc::Kind required_kind) {
if (value == nullptr) {
// Scratch register allocation
if (required_kind == HostLoc::Kind::Gpr) {
const u32 idx = AllocateRegister(gpr_order, GprOffset);
SpillGpr(idx);
hostloc_info[GprOffset + idx].SetupScratchLocation();
return idx;
} else if (required_kind == HostLoc::Kind::Fpr) {
const u32 idx = AllocateRegister(fpr_order, FprOffset);
SpillFpr(idx);
hostloc_info[FprOffset + idx].SetupScratchLocation();
return idx;
}
}
ASSERT(!ValueLocation(value));
const auto setup_location = [&](HostLocInfo& info) {
info = {};
info.values.emplace_back(value);
info.locked = true;
info.realized = true;
info.expected_uses = value->UseCount();
};
if (required_kind == HostLoc::Kind::Gpr) {
const u32 new_location_index = AllocateRegister(gpr_order, GprOffset);
SpillGpr(new_location_index);
setup_location(hostloc_info[GprOffset + new_location_index]);
return new_location_index;
} else if (required_kind == HostLoc::Kind::Fpr) {
const u32 new_location_index = AllocateRegister(fpr_order, FprOffset);
SpillFpr(new_location_index);
setup_location(hostloc_info[FprOffset + new_location_index]);
return new_location_index;
} else {
UNREACHABLE();
}
}
template u32 RegAlloc::RealizeReadImpl<HostLoc::Kind::Gpr>(const IR::Value& value);
template u32 RegAlloc::RealizeReadImpl<HostLoc::Kind::Fpr>(const IR::Value& value);
u32 RegAlloc::AllocateRegister(const std::vector<u32>& order, size_t base_offset) {
const auto empty = std::find_if(order.begin(), order.end(), [&](u32 i) {
auto& info = hostloc_info[base_offset + i];
return info.values.empty() && !info.locked;
});
if (empty != order.end()) {
return *empty;
}
std::vector<u32> candidates;
std::copy_if(order.begin(), order.end(), std::back_inserter(candidates), [&](u32 i) {
return !hostloc_info[base_offset + i].locked;
});
ASSERT(!candidates.empty());
u32 best = candidates[0];
size_t min_lru = hostloc_info[base_offset + best].lru_counter;
for (size_t i = 1; i < candidates.size(); ++i) {
auto& info = hostloc_info[base_offset + candidates[i]];
if (info.lru_counter < min_lru) {
min_lru = info.lru_counter;
best = candidates[i];
}
}
hostloc_info[base_offset + best].lru_counter++;
return best;
}
void RegAlloc::SpillGpr(u32 index) {
auto& gpr_info = hostloc_info[GprOffset + index];
ASSERT(!gpr_info.locked && !gpr_info.realized);
if (gpr_info.values.empty()) {
return;
}
const u32 new_location_index = FindFreeSpill();
la_st_d(&as, static_cast<la_gpr_t>(index), LA_SP,
static_cast<int32_t>(spill_offset + new_location_index * spill_slot_size));
hostloc_info[SpillOffset + new_location_index] = std::exchange(gpr_info, {});
}
void RegAlloc::SpillFpr(u32 index) {
auto& fpr_info = hostloc_info[FprOffset + index];
ASSERT(!fpr_info.locked && !fpr_info.realized);
if (fpr_info.values.empty()) {
return;
}
const u32 new_location_index = FindFreeSpill();
la_fst_d(&as, static_cast<la_fpr_t>(index), LA_SP,
static_cast<int32_t>(spill_offset + new_location_index * spill_slot_size));
hostloc_info[SpillOffset + new_location_index] = std::exchange(fpr_info, {});
}
u32 RegAlloc::FindFreeSpill() const {
for (size_t i = 0; i < SpillCount; ++i) {
if (hostloc_info[SpillOffset + i].values.empty()) {
return static_cast<u32>(i);
}
}
UNREACHABLE();
}
std::optional<HostLoc> RegAlloc::ValueLocation(const IR::Inst* value) const {
for (size_t i = 0; i < hostloc_info.size(); ++i) {
if (hostloc_info[i].Contains(value)) {
if (i < GprCount) {
return HostLoc{HostLoc::Kind::Gpr, static_cast<u32>(i)};
} else if (i < GprCount + FprCount) {
return HostLoc{HostLoc::Kind::Fpr, static_cast<u32>(i - GprCount)};
} else {
return HostLoc{HostLoc::Kind::Spill, static_cast<u32>(i - GprCount - FprCount)};
}
}
}
return std::nullopt;
}
HostLocInfo& RegAlloc::ValueInfo(HostLoc host_loc) {
switch (host_loc.kind) {
case HostLoc::Kind::Gpr:
return hostloc_info[GprOffset + host_loc.index];
case HostLoc::Kind::Fpr:
return hostloc_info[FprOffset + host_loc.index];
case HostLoc::Kind::Spill:
return hostloc_info[SpillOffset + host_loc.index];
}
UNREACHABLE();
}
HostLocInfo& RegAlloc::ValueInfo(const IR::Inst* value) {
for (auto& info : hostloc_info) {
if (info.Contains(value)) {
return info;
}
}
UNREACHABLE();
}
} // namespace Dynarmic::Backend::LoongArch64

View file

@ -0,0 +1,232 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <array>
#include <optional>
#include <utility>
#include <vector>
#include "dynarmic/backend/loongarch64/lagoon_cpp.h"
#include <ankerl/unordered_dense.h>
#include "common/assert.h"
#include "common/common_types.h"
#include "dynarmic/mcl/is_instance_of_template.hpp"
#include "dynarmic/backend/loongarch64/stack_layout.h"
#include "dynarmic/ir/cond.h"
#include "dynarmic/ir/microinstruction.h"
#include "dynarmic/ir/value.h"
namespace Dynarmic::Backend::LoongArch64 {
class RegAlloc;
// Wrapper types for LoongArch GPR/FPR (replacing biscuit's register types)
struct GPR {
la_gpr_t index = LA_ZERO;
GPR() = default;
explicit GPR(u32 i) : index{static_cast<la_gpr_t>(i)} {}
uint32_t Index() const { return static_cast<uint32_t>(index); }
};
struct FPR {
la_fpr_t index = LA_F0;
FPR() = default;
explicit FPR(u32 i) : index{static_cast<la_fpr_t>(i)} {}
uint32_t Index() const { return static_cast<uint32_t>(index); }
};
struct VPR {
la_vpr_t index;
VPR() = default;
explicit VPR(u32 i) : index{static_cast<la_vpr_t>(i)} {}
uint32_t Index() const { return static_cast<uint32_t>(index); }
};
struct HostLoc {
enum class Kind {
Gpr,
Fpr,
Spill,
} kind;
u32 index;
};
struct Argument {
public:
using copyable_reference = std::reference_wrapper<Argument>;
IR::Type GetType() const;
bool IsImmediate() const;
bool GetImmediateU1() const;
u8 GetImmediateU8() const;
u16 GetImmediateU16() const;
u32 GetImmediateU32() const;
u64 GetImmediateU64() const;
IR::Cond GetImmediateCond() const;
IR::AccType GetImmediateAccType() const;
private:
friend class RegAlloc;
explicit Argument() {}
IR::Value value;
bool allocated = false;
};
template<typename T>
struct RAReg {
public:
static constexpr HostLoc::Kind kind = std::is_same_v<T, FPR> || std::is_same_v<T, VPR>
? HostLoc::Kind::Fpr
: HostLoc::Kind::Gpr;
operator T() const { return *reg; }
T operator*() const { return *reg; }
const T* operator->() const { return &*reg; }
~RAReg();
private:
friend class RegAlloc;
explicit RAReg(RegAlloc& reg_alloc, bool write, const IR::Value& value);
void Realize();
RegAlloc& reg_alloc;
bool write;
const IR::Value value;
std::optional<T> reg;
};
struct HostLocInfo final {
std::vector<const IR::Inst*> values;
size_t locked = 0;
size_t uses_this_inst = 0;
size_t accumulated_uses = 0;
size_t expected_uses = 0;
bool realized = false;
size_t lru_counter = 0;
bool Contains(const IR::Inst*) const;
void SetupScratchLocation();
bool IsCompletelyEmpty() const;
void UpdateUses();
};
class RegAlloc {
public:
using ArgumentInfo = std::array<Argument, IR::max_arg_count>;
explicit RegAlloc(lagoon_assembler_t& as, std::vector<u32> gpr_order, std::vector<u32> fpr_order)
: as{as}, gpr_order{std::move(gpr_order)}, fpr_order{std::move(fpr_order)} {}
ArgumentInfo GetArgumentInfo(IR::Inst* inst);
bool IsValueLive(IR::Inst* inst) const;
auto ReadX(Argument& arg) { return RAReg<GPR>{*this, false, arg.value}; }
auto ReadD(Argument& arg) { return RAReg<FPR>{*this, false, arg.value}; }
auto ReadV(Argument& arg) { return RAReg<VPR>{*this, false, arg.value}; }
auto WriteX(IR::Inst* inst) { return RAReg<GPR>{*this, true, inst ? IR::Value{inst} : IR::Value{}}; }
auto WriteD(IR::Inst* inst) { return RAReg<FPR>{*this, true, inst ? IR::Value{inst} : IR::Value{}}; }
auto WriteV(IR::Inst* inst) { return RAReg<VPR>{*this, true, inst ? IR::Value{inst} : IR::Value{}}; }
auto ScratchGpr() { return RAReg<GPR>{*this, true, IR::Value{}}; }
auto ScratchFpr() { return RAReg<FPR>{*this, true, IR::Value{}}; }
auto ScratchVec() { return RAReg<VPR>{*this, true, IR::Value{}}; }
void DefineAsExisting(IR::Inst* inst, Argument& arg);
template<typename... Ts>
static void Realize(Ts&... rs) {
static_assert((mcl::is_instance_of_template<RAReg, Ts>() && ...));
(rs.Realize(), ...);
}
void UpdateAllUses();
void AssertNoMoreUses() const;
private:
template<typename>
friend struct RAReg;
template<HostLoc::Kind kind>
u32 GenerateImmediate(const IR::Value& value);
template<HostLoc::Kind kind>
u32 RealizeReadImpl(const IR::Value& value);
u32 RealizeWriteImpl(const IR::Inst* value, HostLoc::Kind required_kind);
u32 AllocateRegister(const std::vector<u32>& order, size_t base_offset);
void SpillGpr(u32 index);
void SpillFpr(u32 index);
u32 FindFreeSpill() const;
std::optional<HostLoc> ValueLocation(const IR::Inst* value) const;
HostLocInfo& ValueInfo(HostLoc host_loc);
HostLocInfo& ValueInfo(const IR::Inst* value);
lagoon_assembler_t& as;
std::vector<u32> gpr_order;
std::vector<u32> fpr_order;
static constexpr size_t GprCount = 32;
static constexpr size_t FprCount = 32;
static constexpr size_t GprOffset = 0;
static constexpr size_t FprOffset = GprCount;
static constexpr size_t SpillOffset = GprCount + FprCount;
std::array<HostLocInfo, GprCount + FprCount + SpillCount> hostloc_info;
};
template<typename T>
RAReg<T>::RAReg(RegAlloc& reg_alloc, bool write, const IR::Value& value)
: reg_alloc{reg_alloc}, write{write}, value{value} {
if (!write && !value.IsImmediate()) {
reg_alloc.ValueInfo(value.GetInst()).locked++;
}
}
template<typename T>
RAReg<T>::~RAReg() {
if (value.IsEmpty()) {
if (reg) {
HostLocInfo& info = reg_alloc.ValueInfo(HostLoc{kind, reg->Index()});
info.locked--;
info.realized = false;
}
return;
}
if (value.IsImmediate()) {
if (reg) {
// Immediate was materialized into a scratch register
HostLocInfo& info = reg_alloc.ValueInfo(HostLoc{kind, reg->Index()});
info.locked--;
info.realized = false;
}
} else if (!value.IsEmpty()) {
HostLocInfo& info = reg_alloc.ValueInfo(value.GetInst());
info.locked--;
if (reg) {
info.realized = false;
}
}
}
template<typename T>
void RAReg<T>::Realize() {
if (write && value.IsEmpty()) {
reg = T{reg_alloc.RealizeWriteImpl(nullptr, kind)};
} else {
reg = T{write ? reg_alloc.RealizeWriteImpl(value.GetInst(), kind) : reg_alloc.RealizeReadImpl<kind>(value)};
}
}
} // namespace Dynarmic::Backend::LoongArch64

View file

@ -0,0 +1,28 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <array>
#include "common/common_types.h"
namespace Dynarmic::Backend::LoongArch64 {
constexpr size_t SpillCount = 64;
struct alignas(16) StackLayout {
s64 cycles_remaining;
s64 cycles_to_run;
std::array<u64, SpillCount> spill;
u32 save_host_fpcr;
u32 save_host_fpsr;
bool check_bit;
};
static_assert(sizeof(StackLayout) % 16 == 0);
} // namespace Dynarmic::Backend::LoongArch64

View file

@ -13,8 +13,9 @@
#include <memory>
#include <type_traits>
#include "dynarmic/mcl/bit.hpp"
#include "common/common_types.h"
#include "common/x64/xbyak.h"
#include "dynarmic/mcl/bit.hpp"
#include "dynarmic/backend/x64/xbyak.h"
#include "dynarmic/backend/x64/abi.h"
#include "dynarmic/backend/x64/callback.h"
@ -82,28 +83,17 @@ public:
/// Code emitter: Load required flags for conditional cond from rax into host rflags
void LoadRequiredFlagsForCondFromRax(IR::Cond cond);
/// Code emitter: Calls the function
template<typename FunctionPointer>
void CallFunction(FunctionPointer fn) {
static_assert(std::is_pointer_v<FunctionPointer> && std::is_function_v<std::remove_pointer_t<FunctionPointer>>,
"Supplied type must be a pointer to a function");
const u64 address = reinterpret_cast<u64>(fn);
const u64 distance = address - (getCurr<u64>() + 5);
if (distance >= 0x0000000080000000ULL && distance < 0xFFFFFFFF80000000ULL) {
// Far call
mov(rax, address);
call(rax);
} else {
call(fn);
}
/// @brief Code emitter: Calls the function
template<typename F>
void CallFunction(F fn) {
static_assert(std::is_pointer_v<F> && std::is_function_v<std::remove_pointer_t<F>>, "Supplied type must be a pointer to a function");
::Common::X64::CallFarFunction(*this, fn);
}
/// Code emitter: Calls the lambda. Lambda must not have any captures.
/// @brief Code emitter: Calls the lambda. Lambda must not have any captures.
template<typename Lambda>
void CallLambda(Lambda l) {
CallFunction(Common::FptrCast(l));
::Common::X64::CallFarFunction(*this, Common::FptrCast(l));
}
void ZeroExtendFrom(size_t bitsize, Xbyak::Reg64 reg) {

View file

@ -75,7 +75,7 @@ ArgCallback DevirtualizeItanium(mcl::class_type<decltype(mfp)>* this_) {
template<auto mfp>
ArgCallback Devirtualize(mcl::class_type<decltype(mfp)>* this_) {
#if defined(__APPLE__) || defined(linux) || defined(__linux) || defined(__linux__)
#if defined(__APPLE__) || defined(__linux__)
return DevirtualizeItanium<mfp>(this_);
#elif defined(__MINGW64__)
return DevirtualizeItanium<mfp>(this_);

View file

@ -2103,36 +2103,57 @@ void EmitX64::EmitVectorMaxU64(EmitContext& ctx, IR::Inst* inst) {
} else if (code.HasHostFeature(HostFeature::AVX)) {
auto const x = ctx.reg_alloc.UseScratchXmm(code, args[0]);
auto const y = ctx.reg_alloc.UseXmm(code, args[1]);
auto const tmp = ctx.reg_alloc.ScratchXmm(code);
code.vmovdqa(xmm0, code.Const(xword, 0x8000000000000000, 0x8000000000000000));
code.vpsubq(tmp, y, xmm0);
code.vpsubq(xmm0, x, xmm0);
code.vpcmpgtq(xmm0, tmp, xmm0);
code.pblendvb(x, y);
auto const tmp0 = ctx.reg_alloc.ScratchXmm(code);
auto const tmp1 = ctx.reg_alloc.ScratchXmm(code, HostLoc::XMM0);
code.vmovdqa(tmp1, code.Const(xword, 0x8000000000000000, 0x8000000000000000));
code.vpsubq(tmp0, y, tmp1);
code.vpsubq(tmp1, x, tmp1);
code.vpcmpgtq(tmp1, tmp0, tmp1);
code.pblendvb(x, y); // XMM0 is implicit
ctx.reg_alloc.DefineValue(code, inst, x);
} else if (code.HasHostFeature(HostFeature::SSE41)) {
auto const tmp0 = ctx.reg_alloc.UseScratchXmm(code, args[0]);
auto const tmp1 = ctx.reg_alloc.UseXmm(code, args[1]);
auto const tmp2 = ctx.reg_alloc.ScratchXmm(code);
auto const tmp3 = ctx.reg_alloc.ScratchXmm(code);
auto const tmp4 = ctx.reg_alloc.ScratchXmm(code);
auto const tax = ctx.reg_alloc.ScratchGpr(code);
auto const tdx = ctx.reg_alloc.ScratchGpr(code);
code.movq(tdx, tmp1);
code.movq(tax, tmp0);
code.movhlps(tmp3, tmp0);
code.cmp(tdx, tax);
code.movhlps(tmp2, tmp1);
code.cmovnb(tax, tdx);
code.movq(tdx, tmp2);
code.pinsrq(tmp4, tax, 0);
code.movq(tax, tmp3);
code.cmp(tdx, tax);
code.cmovnb(tax, tdx);
code.pinsrq(tmp4, tax, 1);
ctx.reg_alloc.DefineValue(code, inst, tmp4);
} else {
auto const tmp0 = ctx.reg_alloc.UseScratchXmm(code, args[0]);
auto const tmp1 = ctx.reg_alloc.UseXmm(code, args[1]);
auto const tmp2 = ctx.reg_alloc.ScratchXmm(code);
auto const tmp3 = ctx.reg_alloc.ScratchXmm(code);
auto const tmp4 = ctx.reg_alloc.ScratchXmm(code);
auto const tmp5 = ctx.reg_alloc.ScratchXmm(code);
code.movdqa(tmp2, code.Const(xword, 0x8000000080000000, 0x8000000080000000));
code.movdqa(tmp3, tmp1);
code.pxor(tmp3, tmp2);
code.pxor(tmp2, tmp0);
code.movdqa(tmp4, tmp2);
code.pcmpeqd(tmp2, tmp3);
code.pcmpgtd(tmp4, tmp3);
code.pshufd(tmp2, tmp2, 245);
code.pshufd(tmp5, tmp4, 160);
code.pshufd(tmp3, tmp4, 245);
code.pand(tmp2, tmp5);
code.por(tmp3, tmp2);
code.pand(tmp0, tmp3);
code.pandn(tmp3, tmp1);
code.por(tmp0, tmp3);
ctx.reg_alloc.DefineValue(code, inst, tmp0);
auto const tax = ctx.reg_alloc.ScratchGpr(code);
auto const tdx = ctx.reg_alloc.ScratchGpr(code);
code.movq(tdx, tmp1);
code.movq(tax, tmp0);
code.movhlps(tmp3, tmp0);
code.cmp(tdx, tax);
code.movhlps(tmp2, tmp1);
code.cmovnb(tax, tdx);
code.movq(tdx, tmp2);
code.movq(tmp4, tax);
code.movq(tax, tmp3);
code.cmp(tdx, tax);
code.cmovnb(tax, tdx);
code.movq(tmp3, tax);
code.punpcklqdq(tmp4, tmp3);
ctx.reg_alloc.DefineValue(code, inst, tmp4);
}
}
@ -2247,37 +2268,57 @@ void EmitX64::EmitVectorMinU64(EmitContext& ctx, IR::Inst* inst) {
} else if (code.HasHostFeature(HostFeature::AVX)) {
auto const x = ctx.reg_alloc.UseXmm(code, args[0]);
auto const y = ctx.reg_alloc.UseScratchXmm(code, args[1]);
auto const tmp = ctx.reg_alloc.ScratchXmm(code);
code.vmovdqa(xmm0, code.Const(xword, 0x8000000000000000, 0x8000000000000000));
code.vpsubq(tmp, y, xmm0);
code.vpsubq(xmm0, x, xmm0);
code.vpcmpgtq(xmm0, tmp, xmm0);
auto const tmp0 = ctx.reg_alloc.ScratchXmm(code);
auto const tmp1 = ctx.reg_alloc.ScratchXmm(code, HostLoc::XMM0);
code.vmovdqa(tmp1, code.Const(xword, 0x8000000000000000, 0x8000000000000000));
code.vpsubq(tmp0, y, tmp1);
code.vpsubq(tmp1, x, tmp1);
code.vpcmpgtq(tmp1, tmp0, tmp1);
code.pblendvb(y, x);
ctx.reg_alloc.DefineValue(code, inst, y);
} else if (code.HasHostFeature(HostFeature::SSE41)) {
auto const tmp0 = ctx.reg_alloc.UseScratchXmm(code, args[0]);
auto const tmp1 = ctx.reg_alloc.UseXmm(code, args[1]);
auto const tmp2 = ctx.reg_alloc.ScratchXmm(code);
auto const tmp3 = ctx.reg_alloc.ScratchXmm(code);
auto const tmp4 = ctx.reg_alloc.ScratchXmm(code);
auto const tax = ctx.reg_alloc.ScratchGpr(code);
auto const tdx = ctx.reg_alloc.ScratchGpr(code);
code.movq(tdx, tmp1);
code.movq(tax, tmp0);
code.movhlps(tmp3, tmp0);
code.cmp(tdx, tax);
code.movhlps(tmp2, tmp1);
code.cmovbe(tax, tdx);
code.movq(tdx, tmp2);
code.pinsrq(tmp4, tax, 0);
code.movq(tax, tmp3);
code.cmp(tdx, tax);
code.cmovbe(tax, tdx);
code.pinsrq(tmp4, tax, 1);
ctx.reg_alloc.DefineValue(code, inst, tmp4);
} else {
auto const tmp0 = ctx.reg_alloc.UseScratchXmm(code, args[0]);
auto const tmp1 = ctx.reg_alloc.UseXmm(code, args[1]);
auto const tmp2 = ctx.reg_alloc.ScratchXmm(code);
auto const tmp3 = ctx.reg_alloc.ScratchXmm(code);
auto const tmp4 = ctx.reg_alloc.ScratchXmm(code);
auto const tmp5 = ctx.reg_alloc.ScratchXmm(code);
code.movdqa(tmp2, code.Const(xword, 0x8000000080000000, 0x8000000080000000));
code.movdqa(tmp3, tmp1);
code.pxor(tmp3, tmp2);
code.pxor(tmp2, tmp0);
code.movdqa(tmp4, tmp2);
code.pcmpeqd(tmp2, tmp3);
code.pcmpgtd(tmp4, tmp3);
code.pshufd(tmp3, tmp2, 245);
code.pshufd(tmp5, tmp4, 160);
code.pshufd(tmp2, tmp4, 245);
code.pand(tmp3, tmp5);
code.por(tmp2, tmp3);
code.pand(tmp1, tmp2);
code.pandn(tmp2, tmp0);
code.por(tmp2, tmp1);
//code.movdqa(tmp0, tmp2);
ctx.reg_alloc.DefineValue(code, inst, tmp2);
auto const tax = ctx.reg_alloc.ScratchGpr(code);
auto const tdx = ctx.reg_alloc.ScratchGpr(code);
code.movq(tdx, tmp1);
code.movq(tax, tmp0);
code.movhlps(tmp3, tmp0);
code.cmp(tdx, tax);
code.movhlps(tmp2, tmp1);
code.cmovbe(tax, tdx);
code.movq(tdx, tmp2);
code.movq(tmp4, tax);
code.movq(tax, tmp3);
code.cmp(tdx, tax);
code.cmovbe(tax, tdx);
code.movq(tmp3, tax);
code.punpcklqdq(tmp4, tmp3);
ctx.reg_alloc.DefineValue(code, inst, tmp4);
}
}
@ -2471,13 +2512,11 @@ void EmitX64::EmitVectorNarrow64(EmitContext& ctx, IR::Inst* inst) {
ctx.reg_alloc.DefineValue(code, inst, result);
} else {
auto const a = ctx.reg_alloc.UseScratchXmm(code, args[0]);
auto const zeros = ctx.reg_alloc.ScratchXmm(code);
code.pxor(zeros, zeros);
code.shufps(a, zeros, 0b00001000);
ctx.reg_alloc.DefineValue(code, inst, a);
auto const a = ctx.reg_alloc.UseScratchXmm(code, args[0]);
auto const zeros = ctx.reg_alloc.ScratchXmm(code);
code.pxor(zeros, zeros);
code.shufps(a, zeros, 0b00001000);
ctx.reg_alloc.DefineValue(code, inst, a);
}
}
@ -2490,11 +2529,11 @@ void EmitX64::EmitVectorNot(EmitContext& ctx, IR::Inst* inst) {
code.vpternlogq(result, operand, operand, u8(~Tern::c));
ctx.reg_alloc.DefineValue(code, inst, result);
} else {
auto const xmm_a = ctx.reg_alloc.UseScratchXmm(code, args[0]);
auto const xmm_b = ctx.reg_alloc.ScratchXmm(code);
code.pcmpeqw(xmm_b, xmm_b);
code.pxor(xmm_a, xmm_b);
ctx.reg_alloc.DefineValue(code, inst, xmm_a);
auto const xmm_a = ctx.reg_alloc.UseScratchXmm(code, args[0]);
auto const xmm_b = ctx.reg_alloc.ScratchXmm(code);
code.pcmpeqw(xmm_b, xmm_b);
code.pxor(xmm_a, xmm_b);
ctx.reg_alloc.DefineValue(code, inst, xmm_a);
}
}
@ -2504,11 +2543,9 @@ void EmitX64::EmitVectorOr(EmitContext& ctx, IR::Inst* inst) {
void EmitX64::EmitVectorPairedAddLower8(EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
auto const xmm_a = ctx.reg_alloc.UseScratchXmm(code, args[0]);
auto const xmm_b = ctx.reg_alloc.UseXmm(code, args[1]);
auto const tmp = ctx.reg_alloc.ScratchXmm(code);
code.punpcklqdq(xmm_a, xmm_b);
code.movdqa(tmp, xmm_a);
code.psllw(xmm_a, 8);
@ -2516,7 +2553,6 @@ void EmitX64::EmitVectorPairedAddLower8(EmitContext& ctx, IR::Inst* inst) {
code.pxor(tmp, tmp);
code.psrlw(xmm_a, 8);
code.packuswb(xmm_a, tmp);
ctx.reg_alloc.DefineValue(code, inst, xmm_a);
}

View file

@ -36,6 +36,14 @@ inline void And(volatile u32* ptr, u32 value) {
#endif
}
inline u32 Exchange(volatile u32* ptr, u32 value) {
#ifdef _MSC_VER
return static_cast<u32>(_InterlockedExchange(reinterpret_cast<volatile long*>(ptr), value));
#else
return __atomic_exchange_n(ptr, value, __ATOMIC_SEQ_CST);
#endif
}
inline void Barrier() {
#ifdef _MSC_VER
_ReadWriteBarrier();

View file

@ -7,7 +7,6 @@
*/
#include <string>
#include <fmt/format.h>
#ifdef DYNARMIC_USE_LLVM
@ -16,7 +15,6 @@
#endif
#include "common/assert.h"
#include <bit>
#include "common/common_types.h"
#include "dynarmic/common/llvm_disassemble.h"

View file

@ -2587,11 +2587,11 @@ TEST_CASE("A64: Manual Vector Min/Max U64 (Optimizer Test)", "[a64]") {
// MaxU64 pattern: (a > b) ? a : b
code.CMHI(V2.D2(), V0.D2(), V1.D2()); // V2 = Mask (A > B)
code.BSL(V2.B16(), V0.B16(), V1.B16()); // V2 = Resul
code.BSL(V2.B16(), V0.B16(), V1.B16()); // V2 = Result
// MinU64 pattern: (a > b) ? b : a
code.CMHI(V3.D2(), V0.D2(), V1.D2()); // V3 = Mask (A > B)
code.BSL(V3.B16(), V1.B16(), V0.B16()); // V3 = Resul
code.BSL(V3.B16(), V1.B16(), V0.B16()); // V3 = Result
jit.SetPC(0);
jit.SetVector(0, {100, 20});
@ -2603,3 +2603,32 @@ TEST_CASE("A64: Manual Vector Min/Max U64 (Optimizer Test)", "[a64]") {
CHECK(jit.GetVector(2) == Vector{100, 200});
CHECK(jit.GetVector(3) == Vector{50, 20});
}
TEST_CASE("A64: Rounding", "[a64]") {
A64TestEnv env;
A64::UserConfig jit_user_config{};
jit_user_config.callbacks = &env;
A64::Jit jit{jit_user_config};
oaknut::VectorCodeGenerator code{env.code_mem, nullptr};
code.FRINTN(V1.S4(), V0.S4()); // ToNearest_TieEven
code.FRINTM(V2.S4(), V0.S4()); // TowardsMinusInfinity
code.FRINTP(V3.S4(), V0.S4()); // TowardsPlusInfinity
code.FRINTZ(V4.S4(), V0.S4()); // TowardsZero
code.FRINTA(V5.S4(), V0.S4()); // ToNearest_TieAwayFromZero
code.FRINTX(V6.S4(), V0.S4()); // ToNearest_TieAwayFromZero
jit.SetPC(0);
jit.SetVector(0, {0x4001e17c4001e17c, 0x4001e17c4001e17c});
env.ticks_left = env.code_mem.size();
CheckedRun([&]() { jit.Run(); });
CHECK(jit.GetVector(0) == Vector{0x4001e17c4001e17c, 0x4001e17c4001e17c});
CHECK(jit.GetVector(1) == Vector{0x4000000040000000, 0x4000000040000000});
CHECK(jit.GetVector(2) == Vector{0x4000000040000000, 0x4000000040000000});
CHECK(jit.GetVector(3) == Vector{0x4040000040400000, 0x4040000040400000});
CHECK(jit.GetVector(4) == Vector{0x4000000040000000, 0x4000000040000000});
CHECK(jit.GetVector(5) == Vector{0x4000000040000000, 0x4000000040000000});
CHECK(jit.GetVector(6) == Vector{0x4000000040000000, 0x4000000040000000});
}

View file

@ -17,24 +17,19 @@ using Vector = Dynarmic::A64::Vector;
class A64TestEnv : public Dynarmic::A64::UserCallbacks {
public:
u64 ticks_left = 0;
bool code_mem_modified_by_guest = false;
u64 code_mem_start_address = 0;
std::vector<u32> code_mem;
ankerl::unordered_dense::map<u64, u8> modified_memory;
std::vector<std::string> interrupts;
std::vector<u32> code_mem;
u64 ticks_left = 0;
u64 code_mem_start_address = 0;
bool code_mem_modified_by_guest = false;
bool IsInCodeMem(u64 vaddr) const {
return vaddr >= code_mem_start_address && vaddr < code_mem_start_address + code_mem.size() * 4;
}
std::optional<std::uint32_t> MemoryReadCode(u64 vaddr) override {
if (!IsInCodeMem(vaddr)) {
if (!IsInCodeMem(vaddr))
return 0x14000000; // B .
}
const size_t index = (vaddr - code_mem_start_address) / 4;
return code_mem[index];
}
@ -43,10 +38,9 @@ public:
if (IsInCodeMem(vaddr)) {
return reinterpret_cast<u8*>(code_mem.data())[vaddr - code_mem_start_address];
}
if (auto iter = modified_memory.find(vaddr); iter != modified_memory.end()) {
return iter->second;
}
return static_cast<u8>(vaddr);
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;
@ -68,16 +62,16 @@ public:
modified_memory[vaddr] = value;
}
void MemoryWrite16(u64 vaddr, std::uint16_t value) override {
MemoryWrite8(vaddr, static_cast<u8>(value));
MemoryWrite8(vaddr + 1, static_cast<u8>(value >> 8));
MemoryWrite8(vaddr, u8(value));
MemoryWrite8(vaddr + 1, u8(value >> 8));
}
void MemoryWrite32(u64 vaddr, std::uint32_t value) override {
MemoryWrite16(vaddr, static_cast<u16>(value));
MemoryWrite16(vaddr + 2, static_cast<u16>(value >> 16));
MemoryWrite16(vaddr, u16(value));
MemoryWrite16(vaddr + 2, u16(value >> 16));
}
void MemoryWrite64(u64 vaddr, std::uint64_t value) override {
MemoryWrite32(vaddr, static_cast<u32>(value));
MemoryWrite32(vaddr + 4, static_cast<u32>(value >> 32));
MemoryWrite32(vaddr, u32(value));
MemoryWrite32(vaddr + 4, u32(value >> 32));
}
void MemoryWrite128(u64 vaddr, Vector value) override {
MemoryWrite64(vaddr, value[0]);
@ -139,12 +133,12 @@ public:
template<typename T>
T read(u64 vaddr) {
T value;
memcpy(&value, backing_memory + vaddr, sizeof(T));
std::memcpy(&value, backing_memory + vaddr, sizeof(T));
return value;
}
template<typename T>
void write(u64 vaddr, const T& value) {
memcpy(backing_memory + vaddr, &value, sizeof(T));
std::memcpy(backing_memory + vaddr, &value, sizeof(T));
}
std::optional<std::uint32_t> MemoryReadCode(u64 vaddr) override {

View file

@ -1009,7 +1009,7 @@ std::string Config::AdjustOutputString(const std::string& string) {
// Windows requires that two forward slashes are used at the start of a path for unmapped
// network drives so we have to watch for that here
#ifndef ANDROID
#ifndef __ANDROID__
if (string.substr(0, 2) == "//") {
boost::replace_all(adjusted_string, "//", "/");
adjusted_string.insert(0, "/");

View file

@ -1,8 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#ifndef DATA_MANAGER_H
#define DATA_MANAGER_H
#pragma once
#include "common/common_types.h"
#include <string>
@ -21,5 +20,3 @@ std::string ReadableBytesSize(u64 size) noexcept;
u64 DataDirSize(DataDir dir);
}; // namespace FrontendCommon::DataManager
#endif // DATA_MANAGER_H

View file

@ -13,7 +13,7 @@
#include "core/crypto/key_manager.h"
#include "frontend_common/content_manager.h"
#ifdef ANDROID
#ifdef __ANDROID__
#include <jni.h>
#include <common/android/id_cache.h>
#include <common/android/android_common.h>
@ -25,7 +25,7 @@ FirmwareManager::InstallKeys(std::string location, std::string extension) {
const auto keys_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::KeysDir);
#ifdef ANDROID
#ifdef __ANDROID__
JNIEnv *env = Common::Android::GetEnvForThread();
jstring jsrc = Common::Android::ToJString(env, location);

View file

@ -1,8 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#ifndef FIRMWARE_MANAGER_H
#define FIRMWARE_MANAGER_H
#pragma once
#include "common/common_types.h"
#include "core/core.h"
@ -107,5 +106,3 @@ inline std::pair<Service::Set::FirmwareVersionFormat, Result> GetFirmwareVersion
// TODO(crueter): GET AS STRING
}
#endif

View file

@ -5,6 +5,7 @@
#include <filesystem>
#include <optional>
#include <vector>
#include "common/common_types.h"
namespace FrontendCommon {

View file

@ -179,7 +179,7 @@ void EmulatedController::LoadDevices() {
if (npad_id_type == NpadIdType::Player1 || npad_id_type == NpadIdType::Handheld) {
camera_params[1] = Common::ParamPackage{"engine:camera,camera:1"};
nfc_params[0] = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"};
#ifndef ANDROID
#ifndef __ANDROID__
ring_params[1] = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"};
#else
android_params = Common::ParamPackage{"engine:android,port:100"};

Some files were not shown because too many files have changed in this diff Show more