Compare commits
274 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e292b2d991 | |||
| 2f45e999d8 | |||
| d44464829b | |||
| efe50d88ec | |||
| 21f5912ec9 | |||
| 8d46c3cc66 | |||
| 7712e46d64 | |||
| 92b2e92620 | |||
| 2984695265 | |||
| c9260a75f6 | |||
| 1a16d055df | |||
| 83a84f1c2d | |||
| 90a4591563 | |||
| 768fdb269e | |||
| c48b9668f0 | |||
| 027ff7847c | |||
| 4514325b9c | |||
| b5fd9c58cd | |||
| 910a0fa58e | |||
| 1e21f5f872 | |||
| e1201abc1e | |||
| c3242abe95 | |||
| d81cdd9114 | |||
| 45bcd7f9b8 | |||
| 4390370a19 | |||
| 54c7af9902 | |||
| b57d61010f | |||
| eaca010ee9 | |||
| c5d22952bf | |||
| 81806603eb | |||
| ca9da569ce | |||
| 6705439cf3 | |||
| 3799c820ca | |||
| 1105614b86 | |||
| 6a1ad03153 | |||
| fc01074f89 | |||
| bcd666b86b | |||
| d0c7c3f64f | |||
| 862dddf8c9 | |||
| d134ca68c6 | |||
| e35c2fd5d0 | |||
| 29f9a454eb | |||
| 41b65d38fa | |||
| fc9abd3c62 | |||
| c001a2af25 | |||
| 3d24eb54ec | |||
| ab93b4c66d | |||
| 49eb78497b | |||
| 6a4ab3e0d2 | |||
| b720009dc0 | |||
| 36910e9020 | |||
| 0456ed6b4e | |||
| ec4d7f71fe | |||
| 25db62ce15 | |||
| dfe11d72e3 | |||
| d94dcaefa0 | |||
| f0340b8d22 | |||
| ee0547e4c4 | |||
| cb30fe50cd | |||
| 3271099fea | |||
| 5cb1c2ad84 | |||
| 928380ebf9 | |||
| 0ec5b9bff2 | |||
| 7ad17ae397 | |||
| edbfbf2f2f | |||
| fbefcf7280 | |||
| 16bf50e610 | |||
| fc8aef7323 | |||
| 8ec32e9755 | |||
| 03186af6a1 | |||
| d68eb751c5 | |||
| 694c078655 | |||
| 9484519ec3 | |||
| 064aa3de11 | |||
| ade44ac2ea | |||
| f785f73e92 | |||
| 09400e4f4e | |||
| 76d83ffbec | |||
| 72add82ee9 | |||
| 995e27e9b7 | |||
| 79e8f4abe8 | |||
| 404a94a199 | |||
| 40d31b8b8a | |||
| b9f06bd599 | |||
| 330b31ae2e | |||
| 50e9ba34b4 | |||
| a28a10bc54 | |||
| cd9345e10c | |||
| 5c4ed30c21 | |||
| e2a86e2c8a | |||
| 2dba59d1ff | |||
| 9ba6bab920 | |||
| f56226e17f | |||
| 2ce0410f2c | |||
| cd4aa50207 | |||
| e5c83b5a3e | |||
| 7432343214 | |||
| 8f32bab4c9 | |||
| 26a9dc3f65 | |||
| 360a97ab54 | |||
| 540ed72e99 | |||
| c81793029d | |||
| 1e66eaf929 | |||
| 7ddccb55d2 | |||
| 34dc1c8bc2 | |||
| 1ed22b4613 | |||
| 8a6e6465a7 | |||
| 35ed051742 | |||
| 5bd0c1517f | |||
| 6267110b69 | |||
| 4e766280c4 | |||
| 3d2d77eb3f | |||
| e394e1ecc4 | |||
| 90a0506d56 | |||
| ad58d7eae7 | |||
| d0a5a48948 | |||
| 4258d515e6 | |||
| f54280dafd | |||
| 11099dda2e | |||
| 64a68ccbb4 | |||
| 4790ba7839 | |||
| 1900abde13 | |||
| 60b5670577 | |||
| b00406c8e4 | |||
| 8dea7fa129 | |||
| 2241d8c971 | |||
| beaf7654bb | |||
| 0dec42431f | |||
| fea05a17ea | |||
| e4c63d432d | |||
| f300a1d54b | |||
| 3a1a3dd0db | |||
| 74e6e3623f | |||
| 8a244dd3d3 | |||
| fd1cef5616 | |||
| adcac857f8 | |||
| f6a049337e | |||
| 40050c1188 | |||
| 9bf7ad97f5 | |||
| 5723145165 | |||
| 84cc22b21b | |||
| c93dd45997 | |||
| a1c4bca908 | |||
| 432f4441b9 | |||
| 306b3491c4 | |||
| 8dbad556ec | |||
| a8a4f37628 | |||
| 81d1a1133d | |||
| cfd9f7d25b | |||
| a05d9405b9 | |||
| 74f80299b0 | |||
| f11eefed56 | |||
| 6b873b72ae | |||
| a3c81745b1 | |||
| d8486a9968 | |||
| 9b38c8ef08 | |||
| e7af84670d | |||
| b46ec4efea | |||
| 4f9f55ec21 | |||
| 3442365127 | |||
| 651358d0b6 | |||
| 187c9d7e33 | |||
| 0849be094e | |||
| 8bb39750a1 | |||
| 12e7d3b254 | |||
| 51dd3da11c | |||
| 87eb3cb083 | |||
| b084a9bf0a | |||
| 2f12caccf9 | |||
| ebf19616f4 | |||
| b998aa5504 | |||
| fdde08bd01 | |||
| 2136ebccd6 | |||
| 59add00d4a | |||
| e791da9791 | |||
| 07add23251 | |||
| ee25e0a40b | |||
| 0cee5e1af8 | |||
| ffcaf5af90 | |||
| 5b57ee66ce | |||
| f68be36159 | |||
| 2935c9d8de | |||
| 264bb5abf7 | |||
| 0137f2e6e1 | |||
| 6d8e498f76 | |||
| b8b1b58f36 | |||
| 8433edacb3 | |||
| 68c8a1b170 | |||
| 7d133fd37e | |||
| cef7649bed | |||
| 615fb40416 | |||
| d990f043bd | |||
| 12bf8ca929 | |||
| 8dd6dc9a29 | |||
| 43039d95ac | |||
| 7ba6f68a8d | |||
| 19c11e2fd2 | |||
| 888bc10c17 | |||
| 54150f5afa | |||
| 3da43ad2a9 | |||
| 0f7337c522 | |||
| 29e3636e0a | |||
| a271cf89cf | |||
| 88d9b55b14 | |||
| 03cf308c16 | |||
| d63d8bf7f9 | |||
| a396473201 | |||
| ef7c50b276 | |||
| f6815086a1 | |||
| 91ff6d4cb3 | |||
| 46a620f9d7 | |||
| 0b37e7cb39 | |||
| 384e24d3e9 | |||
| ad53dc22fd | |||
| 5ffec69dc7 | |||
| 11a380c3da | |||
| f6cbb14dce | |||
| e781f6e767 | |||
| 55ef89a9c7 | |||
| d24a4b79d4 | |||
| dc471700ca | |||
| 490df43f34 | |||
| 8fa0416fee | |||
| d15814d39f | |||
| b5e83bcc7b | |||
| fc3eb72359 | |||
| eceee8e5f4 | |||
| 101d86897b | |||
| 1dbb9e353e | |||
| 5eb0b65590 | |||
| 36144a5690 | |||
| a943600019 | |||
| 45ac3f414e | |||
| b92198b7f8 | |||
| cf4893410b | |||
| 345bcd4cdf | |||
| 84786dde00 | |||
| ca2d904770 | |||
| 3431e0acf5 | |||
| a99f84f4b2 | |||
| 8eddafd9d0 | |||
| 78e233c460 | |||
| 789af19c60 | |||
| c624edceba | |||
| 9eab07f863 | |||
| a5bff8e9b3 | |||
| 850896a52b | |||
| c8b3a12856 | |||
| 92d676d788 | |||
| 419f427a01 | |||
| 480b03b645 | |||
| 5eda606952 | |||
| b54cbc985e | |||
| 7978ea4e8c | |||
| 4064e03568 | |||
| f499c8177e | |||
| d8b3f665db | |||
| 03884b7ea6 | |||
| c905044e1b | |||
| bf32fcc817 | |||
| f6de57c1a5 | |||
| cc112f971e | |||
| 5ce5432830 | |||
| b2d45a4072 | |||
| 07e477f891 | |||
| 2147240e47 | |||
| f1aa7ff893 | |||
| b3bcf0fa88 | |||
| 8185509683 | |||
| d348070226 | |||
| eb7197eb47 | |||
| c8b358dba2 | |||
| d92b5fc435 | |||
| 36da4d1121 |
+7
-1
@@ -35,6 +35,8 @@ option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
|
||||
|
||||
option(YUZU_USE_BUNDLED_OPUS "Compile bundled opus" ON)
|
||||
|
||||
option(YUZU_TESTS "Compile tests" ON)
|
||||
|
||||
# Default to a Release build
|
||||
get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
if (NOT IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE)
|
||||
@@ -168,7 +170,6 @@ macro(yuzu_find_packages)
|
||||
# Capitalization matters here. We need the naming to match the generated paths from Conan
|
||||
set(REQUIRED_LIBS
|
||||
# Cmake Pkg Prefix Version Conan Pkg
|
||||
"Catch2 2.13.7 catch2/2.13.7"
|
||||
"fmt 8.0.1 fmt/8.1.1"
|
||||
"lz4 1.8 lz4/1.9.2"
|
||||
"nlohmann_json 3.8 nlohmann_json/3.8.0"
|
||||
@@ -177,6 +178,11 @@ macro(yuzu_find_packages)
|
||||
# can't use opus until AVX check is fixed: https://github.com/yuzu-emu/yuzu/pull/4068
|
||||
#"opus 1.3 opus/1.3.1"
|
||||
)
|
||||
if (YUZU_TESTS)
|
||||
list(APPEND REQUIRED_LIBS
|
||||
"Catch2 2.13.7 catch2/2.13.7"
|
||||
)
|
||||
endif()
|
||||
|
||||
foreach(PACKAGE ${REQUIRED_LIBS})
|
||||
string(REGEX REPLACE "[ \t\r\n]+" ";" PACKAGE_SPLIT ${PACKAGE})
|
||||
|
||||
Vendored
+461
-315
File diff suppressed because it is too large
Load Diff
Vendored
+492
-346
File diff suppressed because it is too large
Load Diff
Vendored
+506
-360
File diff suppressed because it is too large
Load Diff
Vendored
+489
-343
File diff suppressed because it is too large
Load Diff
Vendored
+456
-310
File diff suppressed because it is too large
Load Diff
Vendored
+6221
File diff suppressed because it is too large
Load Diff
Vendored
+521
-375
File diff suppressed because it is too large
Load Diff
Vendored
+6218
File diff suppressed because it is too large
Load Diff
Vendored
+482
-336
File diff suppressed because it is too large
Load Diff
Vendored
+486
-340
File diff suppressed because it is too large
Load Diff
Vendored
+566
-420
File diff suppressed because it is too large
Load Diff
Vendored
+557
-411
File diff suppressed because it is too large
Load Diff
Vendored
+547
-400
File diff suppressed because it is too large
Load Diff
Vendored
+690
-534
File diff suppressed because it is too large
Load Diff
Vendored
+463
-317
File diff suppressed because it is too large
Load Diff
Vendored
+530
-384
File diff suppressed because it is too large
Load Diff
Vendored
+461
-315
File diff suppressed because it is too large
Load Diff
Vendored
+556
-410
File diff suppressed because it is too large
Load Diff
Vendored
+461
-315
File diff suppressed because it is too large
Load Diff
Vendored
+559
-413
File diff suppressed because it is too large
Load Diff
Vendored
+456
-310
File diff suppressed because it is too large
Load Diff
Vendored
+463
-317
File diff suppressed because it is too large
Load Diff
Vendored
-4
@@ -13,10 +13,6 @@ if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
|
||||
target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES)
|
||||
endif()
|
||||
|
||||
# Catch
|
||||
add_library(catch-single-include INTERFACE)
|
||||
target_include_directories(catch-single-include INTERFACE catch/single_include)
|
||||
|
||||
# Dynarmic
|
||||
if (ARCHITECTURE_x86_64)
|
||||
set(DYNARMIC_TESTS OFF)
|
||||
|
||||
+18
-18
@@ -747,12 +747,12 @@ AF1 sharpness){
|
||||
// Immediate constants for peak range.
|
||||
AF2 peakC=AF2(1.0,-1.0*4.0);
|
||||
// Limiters, these need to be high precision RCPs.
|
||||
AF1 hitMinR=mn4R*ARcpF1(AF1_(4.0)*mx4R);
|
||||
AF1 hitMinG=mn4G*ARcpF1(AF1_(4.0)*mx4G);
|
||||
AF1 hitMinB=mn4B*ARcpF1(AF1_(4.0)*mx4B);
|
||||
AF1 hitMaxR=(peakC.x-mx4R)*ARcpF1(AF1_(4.0)*mn4R+peakC.y);
|
||||
AF1 hitMaxG=(peakC.x-mx4G)*ARcpF1(AF1_(4.0)*mn4G+peakC.y);
|
||||
AF1 hitMaxB=(peakC.x-mx4B)*ARcpF1(AF1_(4.0)*mn4B+peakC.y);
|
||||
AF1 hitMinR=min(mn4R,eR)*ARcpF1(AF1_(4.0)*mx4R);
|
||||
AF1 hitMinG=min(mn4G,eG)*ARcpF1(AF1_(4.0)*mx4G);
|
||||
AF1 hitMinB=min(mn4B,eB)*ARcpF1(AF1_(4.0)*mx4B);
|
||||
AF1 hitMaxR=(peakC.x-max(mx4R,eR))*ARcpF1(AF1_(4.0)*mn4R+peakC.y);
|
||||
AF1 hitMaxG=(peakC.x-max(mx4G,eG))*ARcpF1(AF1_(4.0)*mn4G+peakC.y);
|
||||
AF1 hitMaxB=(peakC.x-max(mx4B,eB))*ARcpF1(AF1_(4.0)*mn4B+peakC.y);
|
||||
AF1 lobeR=max(-hitMinR,hitMaxR);
|
||||
AF1 lobeG=max(-hitMinG,hitMaxG);
|
||||
AF1 lobeB=max(-hitMinB,hitMaxB);
|
||||
@@ -845,12 +845,12 @@ AF1 sharpness){
|
||||
// Immediate constants for peak range.
|
||||
AH2 peakC=AH2(1.0,-1.0*4.0);
|
||||
// Limiters, these need to be high precision RCPs.
|
||||
AH1 hitMinR=mn4R*ARcpH1(AH1_(4.0)*mx4R);
|
||||
AH1 hitMinG=mn4G*ARcpH1(AH1_(4.0)*mx4G);
|
||||
AH1 hitMinB=mn4B*ARcpH1(AH1_(4.0)*mx4B);
|
||||
AH1 hitMaxR=(peakC.x-mx4R)*ARcpH1(AH1_(4.0)*mn4R+peakC.y);
|
||||
AH1 hitMaxG=(peakC.x-mx4G)*ARcpH1(AH1_(4.0)*mn4G+peakC.y);
|
||||
AH1 hitMaxB=(peakC.x-mx4B)*ARcpH1(AH1_(4.0)*mn4B+peakC.y);
|
||||
AH1 hitMinR=min(mn4R,eR)*ARcpH1(AH1_(4.0)*mx4R);
|
||||
AH1 hitMinG=min(mn4G,eG)*ARcpH1(AH1_(4.0)*mx4G);
|
||||
AH1 hitMinB=min(mn4B,eB)*ARcpH1(AH1_(4.0)*mx4B);
|
||||
AH1 hitMaxR=(peakC.x-max(mx4R,eR))*ARcpH1(AH1_(4.0)*mn4R+peakC.y);
|
||||
AH1 hitMaxG=(peakC.x-max(mx4G,eG))*ARcpH1(AH1_(4.0)*mn4G+peakC.y);
|
||||
AH1 hitMaxB=(peakC.x-max(mx4B,eB))*ARcpH1(AH1_(4.0)*mn4B+peakC.y);
|
||||
AH1 lobeR=max(-hitMinR,hitMaxR);
|
||||
AH1 lobeG=max(-hitMinG,hitMaxG);
|
||||
AH1 lobeB=max(-hitMinB,hitMaxB);
|
||||
@@ -963,12 +963,12 @@ AF1 sharpness){
|
||||
// Immediate constants for peak range.
|
||||
AH2 peakC=AH2(1.0,-1.0*4.0);
|
||||
// Limiters, these need to be high precision RCPs.
|
||||
AH2 hitMinR=mn4R*ARcpH2(AH2_(4.0)*mx4R);
|
||||
AH2 hitMinG=mn4G*ARcpH2(AH2_(4.0)*mx4G);
|
||||
AH2 hitMinB=mn4B*ARcpH2(AH2_(4.0)*mx4B);
|
||||
AH2 hitMaxR=(peakC.x-mx4R)*ARcpH2(AH2_(4.0)*mn4R+peakC.y);
|
||||
AH2 hitMaxG=(peakC.x-mx4G)*ARcpH2(AH2_(4.0)*mn4G+peakC.y);
|
||||
AH2 hitMaxB=(peakC.x-mx4B)*ARcpH2(AH2_(4.0)*mn4B+peakC.y);
|
||||
AH2 hitMinR=min(mn4R,eR)*ARcpH2(AH2_(4.0)*mx4R);
|
||||
AH2 hitMinG=min(mn4G,eG)*ARcpH2(AH2_(4.0)*mx4G);
|
||||
AH2 hitMinB=min(mn4B,eB)*ARcpH2(AH2_(4.0)*mx4B);
|
||||
AH2 hitMaxR=(peakC.x-max(mx4R,eR))*ARcpH2(AH2_(4.0)*mn4R+peakC.y);
|
||||
AH2 hitMaxG=(peakC.x-max(mx4G,eG))*ARcpH2(AH2_(4.0)*mn4G+peakC.y);
|
||||
AH2 hitMaxB=(peakC.x-max(mx4B,eB))*ARcpH2(AH2_(4.0)*mn4B+peakC.y);
|
||||
AH2 lobeR=max(-hitMinR,hitMaxR);
|
||||
AH2 lobeG=max(-hitMinG,hitMaxG);
|
||||
AH2 lobeB=max(-hitMinB,hitMaxB);
|
||||
|
||||
Vendored
+1
-1
Submodule externals/dynarmic updated: 1635958d06...a8cbfd9af4
Vendored
+8
-3
@@ -95,7 +95,8 @@ if (NOT WIN32)
|
||||
# ffnvenc could load CUDA libraries at the runtime using dlopen/dlsym or LoadLibrary/GetProcAddress
|
||||
# here we handle the hard-linking senario where CUDA is linked during compilation
|
||||
if (CUDA_FOUND)
|
||||
list(APPEND FFmpeg_HWACCEL_FLAGS --extra-cflags=-I${CUDA_INCLUDE_DIRS})
|
||||
# This line causes build error if CUDA_INCLUDE_DIRS is anything but a single non-empty value
|
||||
#list(APPEND FFmpeg_HWACCEL_FLAGS --extra-cflags=-I${CUDA_INCLUDE_DIRS})
|
||||
list(APPEND FFmpeg_HWACCEL_LIBRARIES ${CUDA_LIBRARIES})
|
||||
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS ${CUDA_INCLUDE_DIRS})
|
||||
list(APPEND FFmpeg_HWACCEL_LDFLAGS ${CUDA_LDFLAGS})
|
||||
@@ -119,6 +120,8 @@ if (NOT WIN32)
|
||||
|
||||
# `configure` parameters builds only exactly what yuzu needs from FFmpeg
|
||||
# `--disable-vdpau` is needed to avoid linking issues
|
||||
set(FFmpeg_CC ${CMAKE_C_COMPILER_LAUNCHER} ${CMAKE_C_COMPILER})
|
||||
set(FFmpeg_CXX ${CMAKE_CXX_COMPILER_LAUNCHER} ${CMAKE_CXX_COMPILER})
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${FFmpeg_MAKEFILE}
|
||||
@@ -137,12 +140,14 @@ if (NOT WIN32)
|
||||
--enable-decoder=h264
|
||||
--enable-decoder=vp8
|
||||
--enable-decoder=vp9
|
||||
--cc="${CMAKE_C_COMPILER}"
|
||||
--cxx="${CMAKE_CXX_COMPILER}"
|
||||
--cc="${FFmpeg_CC}"
|
||||
--cxx="${FFmpeg_CXX}"
|
||||
${FFmpeg_HWACCEL_FLAGS}
|
||||
WORKING_DIRECTORY
|
||||
${FFmpeg_BUILD_DIR}
|
||||
)
|
||||
unset(FFmpeg_CC)
|
||||
unset(FFmpeg_CXX)
|
||||
unset(FFmpeg_HWACCEL_FLAGS)
|
||||
|
||||
# Workaround for Ubuntu 18.04's older version of make not being able to call make as a child
|
||||
|
||||
+4
-1
@@ -151,7 +151,10 @@ add_subdirectory(audio_core)
|
||||
add_subdirectory(video_core)
|
||||
add_subdirectory(input_common)
|
||||
add_subdirectory(shader_recompiler)
|
||||
add_subdirectory(tests)
|
||||
|
||||
if (YUZU_TESTS)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
if (ENABLE_SDL2)
|
||||
add_subdirectory(yuzu_cmd)
|
||||
|
||||
@@ -15,7 +15,9 @@ constexpr ResultCode ERR_INVALID_PARAMETERS{ErrorModule::Audio, 41};
|
||||
constexpr ResultCode ERR_SPLITTER_SORT_FAILED{ErrorModule::Audio, 43};
|
||||
} // namespace Audren
|
||||
|
||||
constexpr u32_le CURRENT_PROCESS_REVISION = Common::MakeMagic('R', 'E', 'V', '9');
|
||||
constexpr u8 BASE_REVISION = '0';
|
||||
constexpr u32_le CURRENT_PROCESS_REVISION =
|
||||
Common::MakeMagic('R', 'E', 'V', static_cast<u8>(BASE_REVISION + 0xA));
|
||||
constexpr std::size_t MAX_MIX_BUFFERS = 24;
|
||||
constexpr std::size_t MAX_BIQUAD_FILTERS = 2;
|
||||
constexpr std::size_t MAX_CHANNEL_COUNT = 6;
|
||||
|
||||
@@ -79,8 +79,8 @@ static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Implementation of a volume slider with a dynamic range of 60 dB
|
||||
const float volume_scale_factor = volume == 0 ? 0 : std::exp(6.90775f * volume) * 0.001f;
|
||||
// Perceived volume is not the same as the volume level
|
||||
const float volume_scale_factor = (0.85f * ((volume * volume) - volume)) + volume;
|
||||
for (auto& sample : samples) {
|
||||
sample = static_cast<s16>(sample * volume_scale_factor);
|
||||
}
|
||||
|
||||
@@ -45,6 +45,12 @@ template <typename T>
|
||||
return static_cast<u32>(log2_f + static_cast<u64>((value ^ (1ULL << log2_f)) != 0ULL));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires std::is_unsigned_v<T>
|
||||
[[nodiscard]] constexpr bool IsPow2(T value) {
|
||||
return std::has_single_bit(value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires std::is_integral_v<T>
|
||||
[[nodiscard]] T NextPow2(T value) {
|
||||
|
||||
@@ -46,13 +46,3 @@ using GPUVAddr = u64; ///< Represents a pointer in the GPU virtual address space
|
||||
|
||||
using u128 = std::array<std::uint64_t, 2>;
|
||||
static_assert(sizeof(u128) == 16, "u128 must be 128 bits wide");
|
||||
|
||||
// An inheritable class to disallow the copy constructor and operator= functions
|
||||
class NonCopyable {
|
||||
protected:
|
||||
constexpr NonCopyable() = default;
|
||||
~NonCopyable() = default;
|
||||
|
||||
NonCopyable(const NonCopyable&) = delete;
|
||||
NonCopyable& operator=(const NonCopyable&) = delete;
|
||||
};
|
||||
|
||||
@@ -124,7 +124,10 @@ void Fiber::YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to) {
|
||||
|
||||
// "from" might no longer be valid if the thread was killed
|
||||
if (auto from = weak_from.lock()) {
|
||||
ASSERT(from->impl->previous_fiber != nullptr);
|
||||
if (from->impl->previous_fiber == nullptr) {
|
||||
ASSERT_MSG(false, "previous_fiber is nullptr!");
|
||||
return;
|
||||
}
|
||||
from->impl->previous_fiber->impl->context = transfer.fctx;
|
||||
from->impl->previous_fiber->impl->guard.unlock();
|
||||
from->impl->previous_fiber.reset();
|
||||
|
||||
@@ -188,9 +188,8 @@ public:
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path>
|
||||
[[nodiscard]] void Open(const Path& path, FileAccessMode mode,
|
||||
FileType type = FileType::BinaryFile,
|
||||
FileShareFlag flag = FileShareFlag::ShareReadOnly) {
|
||||
void Open(const Path& path, FileAccessMode mode, FileType type = FileType::BinaryFile,
|
||||
FileShareFlag flag = FileShareFlag::ShareReadOnly) {
|
||||
using ValueType = typename Path::value_type;
|
||||
if constexpr (IsChar<ValueType>) {
|
||||
Open(ToU8String(path), mode, type, flag);
|
||||
|
||||
@@ -16,6 +16,10 @@ std::u8string BufferToU8String(std::span<const u8> buffer) {
|
||||
return std::u8string{buffer.begin(), std::ranges::find(buffer, u8{0})};
|
||||
}
|
||||
|
||||
std::u8string_view BufferToU8StringView(std::span<const u8> buffer) {
|
||||
return std::u8string_view{reinterpret_cast<const char8_t*>(buffer.data())};
|
||||
}
|
||||
|
||||
std::string ToUTF8String(std::u8string_view u8_string) {
|
||||
return std::string{u8_string.begin(), u8_string.end()};
|
||||
}
|
||||
@@ -24,6 +28,10 @@ std::string BufferToUTF8String(std::span<const u8> buffer) {
|
||||
return std::string{buffer.begin(), std::ranges::find(buffer, u8{0})};
|
||||
}
|
||||
|
||||
std::string_view BufferToUTF8StringView(std::span<const u8> buffer) {
|
||||
return std::string_view{reinterpret_cast<const char*>(buffer.data())};
|
||||
}
|
||||
|
||||
std::string PathToUTF8String(const std::filesystem::path& path) {
|
||||
return ToUTF8String(path.u8string());
|
||||
}
|
||||
|
||||
@@ -37,6 +37,15 @@ concept IsChar = std::same_as<T, char>;
|
||||
*/
|
||||
[[nodiscard]] std::u8string BufferToU8String(std::span<const u8> buffer);
|
||||
|
||||
/**
|
||||
* Same as BufferToU8String, but returns a string view of the buffer.
|
||||
*
|
||||
* @param buffer Buffer of bytes
|
||||
*
|
||||
* @returns UTF-8 encoded std::u8string_view.
|
||||
*/
|
||||
[[nodiscard]] std::u8string_view BufferToU8StringView(std::span<const u8> buffer);
|
||||
|
||||
/**
|
||||
* Converts a std::u8string or std::u8string_view to a UTF-8 encoded std::string.
|
||||
*
|
||||
@@ -57,6 +66,15 @@ concept IsChar = std::same_as<T, char>;
|
||||
*/
|
||||
[[nodiscard]] std::string BufferToUTF8String(std::span<const u8> buffer);
|
||||
|
||||
/**
|
||||
* Same as BufferToUTF8String, but returns a string view of the buffer.
|
||||
*
|
||||
* @param buffer Buffer of bytes
|
||||
*
|
||||
* @returns UTF-8 encoded std::string_view.
|
||||
*/
|
||||
[[nodiscard]] std::string_view BufferToUTF8StringView(std::span<const u8> buffer);
|
||||
|
||||
/**
|
||||
* Converts a filesystem path to a UTF-8 encoded std::string.
|
||||
*
|
||||
|
||||
@@ -209,6 +209,15 @@ enum class ButtonNames {
|
||||
Triangle,
|
||||
Share,
|
||||
Options,
|
||||
Home,
|
||||
Touch,
|
||||
|
||||
// Mouse buttons
|
||||
ButtonMouseWheel,
|
||||
ButtonBackward,
|
||||
ButtonForward,
|
||||
ButtonTask,
|
||||
ButtonExtra,
|
||||
};
|
||||
|
||||
// Callback data consisting of an input type and the equivalent data status
|
||||
|
||||
@@ -108,6 +108,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
||||
SUB(Service, Migration) \
|
||||
SUB(Service, Mii) \
|
||||
SUB(Service, MM) \
|
||||
SUB(Service, MNPP) \
|
||||
SUB(Service, NCM) \
|
||||
SUB(Service, NFC) \
|
||||
SUB(Service, NFP) \
|
||||
|
||||
@@ -76,6 +76,7 @@ enum class Class : u8 {
|
||||
Service_Migration, ///< The migration service
|
||||
Service_Mii, ///< The Mii service
|
||||
Service_MM, ///< The MM (Multimedia) service
|
||||
Service_MNPP, ///< The MNPP service
|
||||
Service_NCM, ///< The NCM service
|
||||
Service_NFC, ///< The NFC (Near-field communication) service
|
||||
Service_NFP, ///< The NFP service
|
||||
|
||||
@@ -10,11 +10,65 @@ PageTable::PageTable() = default;
|
||||
|
||||
PageTable::~PageTable() noexcept = default;
|
||||
|
||||
void PageTable::Resize(size_t address_space_width_in_bits, size_t page_size_in_bits) {
|
||||
const size_t num_page_table_entries{1ULL << (address_space_width_in_bits - page_size_in_bits)};
|
||||
bool PageTable::BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context,
|
||||
u64 address) const {
|
||||
// Setup invalid defaults.
|
||||
out_entry.phys_addr = 0;
|
||||
out_entry.block_size = page_size;
|
||||
out_context.next_page = 0;
|
||||
|
||||
// Validate that we can read the actual entry.
|
||||
const auto page = address / page_size;
|
||||
if (page >= backing_addr.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate that the entry is mapped.
|
||||
const auto phys_addr = backing_addr[page];
|
||||
if (phys_addr == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Populate the results.
|
||||
out_entry.phys_addr = phys_addr + address;
|
||||
out_context.next_page = page + 1;
|
||||
out_context.next_offset = address + page_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PageTable::ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const {
|
||||
// Setup invalid defaults.
|
||||
out_entry.phys_addr = 0;
|
||||
out_entry.block_size = page_size;
|
||||
|
||||
// Validate that we can read the actual entry.
|
||||
const auto page = context.next_page;
|
||||
if (page >= backing_addr.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate that the entry is mapped.
|
||||
const auto phys_addr = backing_addr[page];
|
||||
if (phys_addr == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Populate the results.
|
||||
out_entry.phys_addr = phys_addr + context.next_offset;
|
||||
context.next_page = page + 1;
|
||||
context.next_offset += page_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits) {
|
||||
const std::size_t num_page_table_entries{1ULL
|
||||
<< (address_space_width_in_bits - page_size_in_bits)};
|
||||
pointers.resize(num_page_table_entries);
|
||||
backing_addr.resize(num_page_table_entries);
|
||||
current_address_space_width_in_bits = address_space_width_in_bits;
|
||||
page_size = 1ULL << page_size_in_bits;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
+20
-4
@@ -27,6 +27,16 @@ enum class PageType : u8 {
|
||||
* mimics the way a real CPU page table works.
|
||||
*/
|
||||
struct PageTable {
|
||||
struct TraversalEntry {
|
||||
u64 phys_addr{};
|
||||
std::size_t block_size{};
|
||||
};
|
||||
|
||||
struct TraversalContext {
|
||||
u64 next_page{};
|
||||
u64 next_offset{};
|
||||
};
|
||||
|
||||
/// Number of bits reserved for attribute tagging.
|
||||
/// This can be at most the guaranteed alignment of the pointers in the page table.
|
||||
static constexpr int ATTRIBUTE_BITS = 2;
|
||||
@@ -89,6 +99,10 @@ struct PageTable {
|
||||
PageTable(PageTable&&) noexcept = default;
|
||||
PageTable& operator=(PageTable&&) noexcept = default;
|
||||
|
||||
bool BeginTraversal(TraversalEntry& out_entry, TraversalContext& out_context,
|
||||
u64 address) const;
|
||||
bool ContinueTraversal(TraversalEntry& out_entry, TraversalContext& context) const;
|
||||
|
||||
/**
|
||||
* Resizes the page table to be able to accommodate enough pages within
|
||||
* a given address space.
|
||||
@@ -96,9 +110,9 @@ struct PageTable {
|
||||
* @param address_space_width_in_bits The address size width in bits.
|
||||
* @param page_size_in_bits The page size in bits.
|
||||
*/
|
||||
void Resize(size_t address_space_width_in_bits, size_t page_size_in_bits);
|
||||
void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits);
|
||||
|
||||
size_t GetAddressSpaceBits() const {
|
||||
std::size_t GetAddressSpaceBits() const {
|
||||
return current_address_space_width_in_bits;
|
||||
}
|
||||
|
||||
@@ -110,9 +124,11 @@ struct PageTable {
|
||||
|
||||
VirtualBuffer<u64> backing_addr;
|
||||
|
||||
size_t current_address_space_width_in_bits;
|
||||
std::size_t current_address_space_width_in_bits{};
|
||||
|
||||
u8* fastmem_arena;
|
||||
u8* fastmem_arena{};
|
||||
|
||||
std::size_t page_size{};
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -554,6 +554,7 @@ struct Values {
|
||||
Setting<bool> use_docked_mode{true, "use_docked_mode"};
|
||||
|
||||
BasicSetting<bool> enable_raw_input{false, "enable_raw_input"};
|
||||
BasicSetting<bool> controller_navigation{true, "controller_navigation"};
|
||||
|
||||
Setting<bool> vibration_enabled{true, "vibration_enabled"};
|
||||
Setting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"};
|
||||
|
||||
+20
-9
@@ -8,6 +8,7 @@
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common::Telemetry {
|
||||
@@ -28,7 +29,7 @@ struct VisitorInterface;
|
||||
/**
|
||||
* Interface class for telemetry data fields.
|
||||
*/
|
||||
class FieldInterface : NonCopyable {
|
||||
class FieldInterface {
|
||||
public:
|
||||
virtual ~FieldInterface() = default;
|
||||
|
||||
@@ -52,14 +53,15 @@ public:
|
||||
template <typename T>
|
||||
class Field : public FieldInterface {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(Field);
|
||||
|
||||
Field(FieldType type_, std::string name_, T value_)
|
||||
: name(std::move(name_)), type(type_), value(std::move(value_)) {}
|
||||
|
||||
Field(const Field&) = default;
|
||||
Field& operator=(const Field&) = default;
|
||||
~Field() override = default;
|
||||
|
||||
Field(Field&&) = default;
|
||||
Field& operator=(Field&& other) = default;
|
||||
Field(Field&&) noexcept = default;
|
||||
Field& operator=(Field&& other) noexcept = default;
|
||||
|
||||
void Accept(VisitorInterface& visitor) const override;
|
||||
|
||||
@@ -98,9 +100,15 @@ private:
|
||||
/**
|
||||
* Collection of data fields that have been logged.
|
||||
*/
|
||||
class FieldCollection final : NonCopyable {
|
||||
class FieldCollection final {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(FieldCollection);
|
||||
|
||||
FieldCollection() = default;
|
||||
~FieldCollection() = default;
|
||||
|
||||
FieldCollection(FieldCollection&&) noexcept = default;
|
||||
FieldCollection& operator=(FieldCollection&&) noexcept = default;
|
||||
|
||||
/**
|
||||
* Accept method for the visitor pattern, visits each field in the collection.
|
||||
@@ -133,7 +141,7 @@ private:
|
||||
* Telemetry fields visitor interface class. A backend to log to a web service should implement
|
||||
* this interface.
|
||||
*/
|
||||
struct VisitorInterface : NonCopyable {
|
||||
struct VisitorInterface {
|
||||
virtual ~VisitorInterface() = default;
|
||||
|
||||
virtual void Visit(const Field<bool>& field) = 0;
|
||||
@@ -160,8 +168,11 @@ struct VisitorInterface : NonCopyable {
|
||||
* Empty implementation of VisitorInterface that drops all fields. Used when a functional
|
||||
* backend implementation is not available.
|
||||
*/
|
||||
struct NullVisitor : public VisitorInterface {
|
||||
~NullVisitor() = default;
|
||||
struct NullVisitor final : public VisitorInterface {
|
||||
YUZU_NON_COPYABLE(NullVisitor);
|
||||
|
||||
NullVisitor() = default;
|
||||
~NullVisitor() override = default;
|
||||
|
||||
void Visit(const Field<bool>& /*field*/) override {}
|
||||
void Visit(const Field<double>& /*field*/) override {}
|
||||
|
||||
+173
-47
@@ -1,23 +1,25 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Copyright 2022 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <bit>
|
||||
#include <optional>
|
||||
#include <random>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/tiny_mt.h"
|
||||
#include "common/uuid.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsHexDigit(char c) {
|
||||
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
|
||||
}
|
||||
constexpr size_t RawStringSize = sizeof(UUID) * 2;
|
||||
constexpr size_t FormattedStringSize = RawStringSize + 4;
|
||||
|
||||
u8 HexCharToByte(char c) {
|
||||
std::optional<u8> HexCharToByte(char c) {
|
||||
if (c >= '0' && c <= '9') {
|
||||
return static_cast<u8>(c - '0');
|
||||
}
|
||||
@@ -28,60 +30,184 @@ u8 HexCharToByte(char c) {
|
||||
return static_cast<u8>(c - 'A' + 10);
|
||||
}
|
||||
ASSERT_MSG(false, "{} is not a hexadecimal digit!", c);
|
||||
return u8{0};
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::array<u8, 0x10> ConstructFromRawString(std::string_view raw_string) {
|
||||
std::array<u8, 0x10> uuid;
|
||||
|
||||
for (size_t i = 0; i < RawStringSize; i += 2) {
|
||||
const auto upper = HexCharToByte(raw_string[i]);
|
||||
const auto lower = HexCharToByte(raw_string[i + 1]);
|
||||
if (!upper || !lower) {
|
||||
return {};
|
||||
}
|
||||
uuid[i / 2] = static_cast<u8>((*upper << 4) | *lower);
|
||||
}
|
||||
|
||||
return uuid;
|
||||
}
|
||||
|
||||
std::array<u8, 0x10> ConstructFromFormattedString(std::string_view formatted_string) {
|
||||
std::array<u8, 0x10> uuid;
|
||||
|
||||
size_t i = 0;
|
||||
|
||||
// Process the first 8 characters.
|
||||
const auto* str = formatted_string.data();
|
||||
|
||||
for (; i < 4; ++i) {
|
||||
const auto upper = HexCharToByte(*(str++));
|
||||
const auto lower = HexCharToByte(*(str++));
|
||||
if (!upper || !lower) {
|
||||
return {};
|
||||
}
|
||||
uuid[i] = static_cast<u8>((*upper << 4) | *lower);
|
||||
}
|
||||
|
||||
// Process the next 4 characters.
|
||||
++str;
|
||||
|
||||
for (; i < 6; ++i) {
|
||||
const auto upper = HexCharToByte(*(str++));
|
||||
const auto lower = HexCharToByte(*(str++));
|
||||
if (!upper || !lower) {
|
||||
return {};
|
||||
}
|
||||
uuid[i] = static_cast<u8>((*upper << 4) | *lower);
|
||||
}
|
||||
|
||||
// Process the next 4 characters.
|
||||
++str;
|
||||
|
||||
for (; i < 8; ++i) {
|
||||
const auto upper = HexCharToByte(*(str++));
|
||||
const auto lower = HexCharToByte(*(str++));
|
||||
if (!upper || !lower) {
|
||||
return {};
|
||||
}
|
||||
uuid[i] = static_cast<u8>((*upper << 4) | *lower);
|
||||
}
|
||||
|
||||
// Process the next 4 characters.
|
||||
++str;
|
||||
|
||||
for (; i < 10; ++i) {
|
||||
const auto upper = HexCharToByte(*(str++));
|
||||
const auto lower = HexCharToByte(*(str++));
|
||||
if (!upper || !lower) {
|
||||
return {};
|
||||
}
|
||||
uuid[i] = static_cast<u8>((*upper << 4) | *lower);
|
||||
}
|
||||
|
||||
// Process the last 12 characters.
|
||||
++str;
|
||||
|
||||
for (; i < 16; ++i) {
|
||||
const auto upper = HexCharToByte(*(str++));
|
||||
const auto lower = HexCharToByte(*(str++));
|
||||
if (!upper || !lower) {
|
||||
return {};
|
||||
}
|
||||
uuid[i] = static_cast<u8>((*upper << 4) | *lower);
|
||||
}
|
||||
|
||||
return uuid;
|
||||
}
|
||||
|
||||
std::array<u8, 0x10> ConstructUUID(std::string_view uuid_string) {
|
||||
const auto length = uuid_string.length();
|
||||
|
||||
if (length == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Check if the input string contains 32 hexadecimal characters.
|
||||
if (length == RawStringSize) {
|
||||
return ConstructFromRawString(uuid_string);
|
||||
}
|
||||
|
||||
// Check if the input string has the length of a RFC 4122 formatted UUID string.
|
||||
if (length == FormattedStringSize) {
|
||||
return ConstructFromFormattedString(uuid_string);
|
||||
}
|
||||
|
||||
ASSERT_MSG(false, "UUID string has an invalid length of {} characters!", length);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
u128 HexStringToU128(std::string_view hex_string) {
|
||||
const size_t length = hex_string.length();
|
||||
UUID::UUID(std::string_view uuid_string) : uuid{ConstructUUID(uuid_string)} {}
|
||||
|
||||
// Detect "0x" prefix.
|
||||
const bool has_0x_prefix = length > 2 && hex_string[0] == '0' && hex_string[1] == 'x';
|
||||
const size_t offset = has_0x_prefix ? 2 : 0;
|
||||
|
||||
// Check length.
|
||||
if (length > 32 + offset) {
|
||||
ASSERT_MSG(false, "hex_string has more than 32 hexadecimal characters!");
|
||||
return INVALID_UUID;
|
||||
}
|
||||
|
||||
u64 lo = 0;
|
||||
u64 hi = 0;
|
||||
for (size_t i = 0; i < length - offset; ++i) {
|
||||
const char c = hex_string[length - 1 - i];
|
||||
if (!IsHexDigit(c)) {
|
||||
ASSERT_MSG(false, "{} is not a hexadecimal digit!", c);
|
||||
return INVALID_UUID;
|
||||
}
|
||||
if (i < 16) {
|
||||
lo |= u64{HexCharToByte(c)} << (i * 4);
|
||||
}
|
||||
if (i >= 16) {
|
||||
hi |= u64{HexCharToByte(c)} << ((i - 16) * 4);
|
||||
}
|
||||
}
|
||||
return u128{lo, hi};
|
||||
std::string UUID::RawString() const {
|
||||
return fmt::format("{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}"
|
||||
"{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
|
||||
uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
|
||||
uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14],
|
||||
uuid[15]);
|
||||
}
|
||||
|
||||
UUID UUID::Generate() {
|
||||
std::string UUID::FormattedString() const {
|
||||
return fmt::format("{:02x}{:02x}{:02x}{:02x}"
|
||||
"-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-"
|
||||
"{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
|
||||
uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
|
||||
uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14],
|
||||
uuid[15]);
|
||||
}
|
||||
|
||||
size_t UUID::Hash() const noexcept {
|
||||
u64 upper_hash;
|
||||
u64 lower_hash;
|
||||
|
||||
std::memcpy(&upper_hash, uuid.data(), sizeof(u64));
|
||||
std::memcpy(&lower_hash, uuid.data() + sizeof(u64), sizeof(u64));
|
||||
|
||||
return upper_hash ^ std::rotl(lower_hash, 1);
|
||||
}
|
||||
|
||||
u128 UUID::AsU128() const {
|
||||
u128 uuid_old;
|
||||
std::memcpy(&uuid_old, uuid.data(), sizeof(UUID));
|
||||
return uuid_old;
|
||||
}
|
||||
|
||||
UUID UUID::MakeRandom() {
|
||||
std::random_device device;
|
||||
std::mt19937 gen(device());
|
||||
std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
|
||||
return UUID{distribution(gen), distribution(gen)};
|
||||
|
||||
return MakeRandomWithSeed(device());
|
||||
}
|
||||
|
||||
std::string UUID::Format() const {
|
||||
return fmt::format("{:016x}{:016x}", uuid[1], uuid[0]);
|
||||
UUID UUID::MakeRandomWithSeed(u32 seed) {
|
||||
// Create and initialize our RNG.
|
||||
TinyMT rng;
|
||||
rng.Initialize(seed);
|
||||
|
||||
UUID uuid;
|
||||
|
||||
// Populate the UUID with random bytes.
|
||||
rng.GenerateRandomBytes(uuid.uuid.data(), sizeof(UUID));
|
||||
|
||||
return uuid;
|
||||
}
|
||||
|
||||
std::string UUID::FormatSwitch() const {
|
||||
std::array<u8, 16> s{};
|
||||
std::memcpy(s.data(), uuid.data(), sizeof(u128));
|
||||
return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{"
|
||||
":02x}{:02x}{:02x}{:02x}{:02x}",
|
||||
s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
|
||||
s[12], s[13], s[14], s[15]);
|
||||
UUID UUID::MakeRandomRFC4122V4() {
|
||||
auto uuid = MakeRandom();
|
||||
|
||||
// According to Proposed Standard RFC 4122 Section 4.4, we must:
|
||||
|
||||
// 1. Set the two most significant bits (bits 6 and 7) of the
|
||||
// clock_seq_hi_and_reserved to zero and one, respectively.
|
||||
uuid.uuid[8] = 0x80 | (uuid.uuid[8] & 0x3F);
|
||||
|
||||
// 2. Set the four most significant bits (bits 12 through 15) of the
|
||||
// time_hi_and_version field to the 4-bit version number from Section 4.1.3.
|
||||
uuid.uuid[6] = 0x40 | (uuid.uuid[6] & 0xF);
|
||||
|
||||
return uuid;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
+106
-54
@@ -1,9 +1,11 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Copyright 2022 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
@@ -11,69 +13,119 @@
|
||||
|
||||
namespace Common {
|
||||
|
||||
constexpr u128 INVALID_UUID{{0, 0}};
|
||||
|
||||
/**
|
||||
* Converts a hex string to a 128-bit unsigned integer.
|
||||
*
|
||||
* The hex string can be formatted in lowercase or uppercase, with or without the "0x" prefix.
|
||||
*
|
||||
* This function will assert and return INVALID_UUID under the following conditions:
|
||||
* - If the hex string is more than 32 characters long
|
||||
* - If the hex string contains non-hexadecimal characters
|
||||
*
|
||||
* @param hex_string Hexadecimal string
|
||||
*
|
||||
* @returns A 128-bit unsigned integer if successfully converted, INVALID_UUID otherwise.
|
||||
*/
|
||||
[[nodiscard]] u128 HexStringToU128(std::string_view hex_string);
|
||||
|
||||
struct UUID {
|
||||
// UUIDs which are 0 are considered invalid!
|
||||
u128 uuid;
|
||||
UUID() = default;
|
||||
constexpr explicit UUID(const u128& id) : uuid{id} {}
|
||||
constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}
|
||||
explicit UUID(std::string_view hex_string) {
|
||||
uuid = HexStringToU128(hex_string);
|
||||
std::array<u8, 0x10> uuid{};
|
||||
|
||||
/// Constructs an invalid UUID.
|
||||
constexpr UUID() = default;
|
||||
|
||||
/// Constructs a UUID from a reference to a 128 bit array.
|
||||
constexpr explicit UUID(const std::array<u8, 16>& uuid_) : uuid{uuid_} {}
|
||||
|
||||
/**
|
||||
* Constructs a UUID from either:
|
||||
* 1. A 32 hexadecimal character string representing the bytes of the UUID
|
||||
* 2. A RFC 4122 formatted UUID string, in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
*
|
||||
* The input string may contain uppercase or lowercase characters, but they must:
|
||||
* 1. Contain valid hexadecimal characters (0-9, a-f, A-F)
|
||||
* 2. Not contain the "0x" hexadecimal prefix
|
||||
*
|
||||
* Should the input string not meet the above requirements,
|
||||
* an assert will be triggered and an invalid UUID is set instead.
|
||||
*/
|
||||
explicit UUID(std::string_view uuid_string);
|
||||
|
||||
~UUID() = default;
|
||||
|
||||
constexpr UUID(const UUID&) noexcept = default;
|
||||
constexpr UUID(UUID&&) noexcept = default;
|
||||
|
||||
constexpr UUID& operator=(const UUID&) noexcept = default;
|
||||
constexpr UUID& operator=(UUID&&) noexcept = default;
|
||||
|
||||
/**
|
||||
* Returns whether the stored UUID is valid or not.
|
||||
*
|
||||
* @returns True if the stored UUID is valid, false otherwise.
|
||||
*/
|
||||
constexpr bool IsValid() const {
|
||||
return uuid != std::array<u8, 0x10>{};
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr explicit operator bool() const {
|
||||
return uuid != INVALID_UUID;
|
||||
/**
|
||||
* Returns whether the stored UUID is invalid or not.
|
||||
*
|
||||
* @returns True if the stored UUID is invalid, false otherwise.
|
||||
*/
|
||||
constexpr bool IsInvalid() const {
|
||||
return !IsValid();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool operator==(const UUID& rhs) const {
|
||||
return uuid == rhs.uuid;
|
||||
/**
|
||||
* Returns a 32 hexadecimal character string representing the bytes of the UUID.
|
||||
*
|
||||
* @returns A 32 hexadecimal character string of the UUID.
|
||||
*/
|
||||
std::string RawString() const;
|
||||
|
||||
/**
|
||||
* Returns a RFC 4122 formatted UUID string in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
|
||||
*
|
||||
* @returns A RFC 4122 formatted UUID string.
|
||||
*/
|
||||
std::string FormattedString() const;
|
||||
|
||||
/**
|
||||
* Returns a 64-bit hash of the UUID for use in hash table data structures.
|
||||
*
|
||||
* @returns A 64-bit hash of the UUID.
|
||||
*/
|
||||
size_t Hash() const noexcept;
|
||||
|
||||
/// DO NOT USE. Copies the contents of the UUID into a u128.
|
||||
u128 AsU128() const;
|
||||
|
||||
/**
|
||||
* Creates a default UUID "yuzu Default UID".
|
||||
*
|
||||
* @returns A UUID with its bytes set to the ASCII values of "yuzu Default UID".
|
||||
*/
|
||||
static constexpr UUID MakeDefault() {
|
||||
return UUID{
|
||||
{'y', 'u', 'z', 'u', ' ', 'D', 'e', 'f', 'a', 'u', 'l', 't', ' ', 'U', 'I', 'D'},
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool operator!=(const UUID& rhs) const {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
/**
|
||||
* Creates a random UUID.
|
||||
*
|
||||
* @returns A random UUID.
|
||||
*/
|
||||
static UUID MakeRandom();
|
||||
|
||||
// TODO(ogniK): Properly generate uuids based on RFC-4122
|
||||
[[nodiscard]] static UUID Generate();
|
||||
/**
|
||||
* Creates a random UUID with a seed.
|
||||
*
|
||||
* @param seed A seed to initialize the Mersenne-Twister RNG
|
||||
*
|
||||
* @returns A random UUID.
|
||||
*/
|
||||
static UUID MakeRandomWithSeed(u32 seed);
|
||||
|
||||
// Set the UUID to {0,0} to be considered an invalid user
|
||||
constexpr void Invalidate() {
|
||||
uuid = INVALID_UUID;
|
||||
}
|
||||
/**
|
||||
* Creates a random UUID. The generated UUID is RFC 4122 Version 4 compliant.
|
||||
*
|
||||
* @returns A random UUID that is RFC 4122 Version 4 compliant.
|
||||
*/
|
||||
static UUID MakeRandomRFC4122V4();
|
||||
|
||||
[[nodiscard]] constexpr bool IsInvalid() const {
|
||||
return uuid == INVALID_UUID;
|
||||
}
|
||||
[[nodiscard]] constexpr bool IsValid() const {
|
||||
return !IsInvalid();
|
||||
}
|
||||
|
||||
// TODO(ogniK): Properly generate a Nintendo ID
|
||||
[[nodiscard]] constexpr u64 GetNintendoID() const {
|
||||
return uuid[0];
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string Format() const;
|
||||
[[nodiscard]] std::string FormatSwitch() const;
|
||||
friend constexpr bool operator==(const UUID& lhs, const UUID& rhs) = default;
|
||||
};
|
||||
static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
|
||||
static_assert(sizeof(UUID) == 0x10, "UUID has incorrect size.");
|
||||
|
||||
/// An invalid UUID. This UUID has all its bytes set to 0.
|
||||
constexpr UUID InvalidUUID = {};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -82,7 +134,7 @@ namespace std {
|
||||
template <>
|
||||
struct hash<Common::UUID> {
|
||||
size_t operator()(const Common::UUID& uuid) const noexcept {
|
||||
return uuid.uuid[1] ^ uuid.uuid[0];
|
||||
return uuid.Hash();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -65,14 +65,20 @@ private:
|
||||
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
|
||||
std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency,
|
||||
u32 emulated_clock_frequency) {
|
||||
std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency,
|
||||
u64 emulated_clock_frequency) {
|
||||
const auto& caps = GetCPUCaps();
|
||||
u64 rtsc_frequency = 0;
|
||||
if (caps.invariant_tsc) {
|
||||
rtsc_frequency = EstimateRDTSCFrequency();
|
||||
}
|
||||
if (rtsc_frequency == 0) {
|
||||
|
||||
// Fallback to StandardWallClock if the hardware TSC does not have the precision greater than:
|
||||
// - A nanosecond
|
||||
// - The emulated CPU frequency
|
||||
// - The emulated clock counter frequency (CNTFRQ)
|
||||
if (rtsc_frequency <= WallClock::NS_RATIO || rtsc_frequency <= emulated_cpu_frequency ||
|
||||
rtsc_frequency <= emulated_clock_frequency) {
|
||||
return std::make_unique<StandardWallClock>(emulated_cpu_frequency,
|
||||
emulated_clock_frequency);
|
||||
} else {
|
||||
@@ -83,8 +89,8 @@ std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency,
|
||||
|
||||
#else
|
||||
|
||||
std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency,
|
||||
u32 emulated_clock_frequency) {
|
||||
std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency,
|
||||
u64 emulated_clock_frequency) {
|
||||
return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,10 @@ namespace Common {
|
||||
|
||||
class WallClock {
|
||||
public:
|
||||
static constexpr u64 NS_RATIO = 1'000'000'000;
|
||||
static constexpr u64 US_RATIO = 1'000'000;
|
||||
static constexpr u64 MS_RATIO = 1'000;
|
||||
|
||||
virtual ~WallClock() = default;
|
||||
|
||||
/// Returns current wall time in nanoseconds
|
||||
@@ -49,7 +53,7 @@ private:
|
||||
bool is_native;
|
||||
};
|
||||
|
||||
[[nodiscard]] std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency,
|
||||
u32 emulated_clock_frequency);
|
||||
[[nodiscard]] std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency,
|
||||
u64 emulated_clock_frequency);
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -47,9 +47,9 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen
|
||||
_mm_mfence();
|
||||
time_point.inner.last_measure = __rdtsc();
|
||||
time_point.inner.accumulated_ticks = 0U;
|
||||
ns_rtsc_factor = GetFixedPoint64Factor(1000000000, rtsc_frequency);
|
||||
us_rtsc_factor = GetFixedPoint64Factor(1000000, rtsc_frequency);
|
||||
ms_rtsc_factor = GetFixedPoint64Factor(1000, rtsc_frequency);
|
||||
ns_rtsc_factor = GetFixedPoint64Factor(NS_RATIO, rtsc_frequency);
|
||||
us_rtsc_factor = GetFixedPoint64Factor(US_RATIO, rtsc_frequency);
|
||||
ms_rtsc_factor = GetFixedPoint64Factor(MS_RATIO, rtsc_frequency);
|
||||
clock_rtsc_factor = GetFixedPoint64Factor(emulated_clock_frequency, rtsc_frequency);
|
||||
cpu_rtsc_factor = GetFixedPoint64Factor(emulated_cpu_frequency, rtsc_frequency);
|
||||
}
|
||||
|
||||
@@ -37,12 +37,12 @@ constexpr Xbyak::Reg IndexToReg(size_t reg_index) {
|
||||
}
|
||||
}
|
||||
|
||||
inline std::bitset<32> BuildRegSet(std::initializer_list<Xbyak::Reg> regs) {
|
||||
std::bitset<32> bits;
|
||||
constexpr std::bitset<32> BuildRegSet(std::initializer_list<Xbyak::Reg> regs) {
|
||||
size_t bits = 0;
|
||||
for (const Xbyak::Reg& reg : regs) {
|
||||
bits[RegToIndex(reg)] = true;
|
||||
bits |= size_t{1} << RegToIndex(reg);
|
||||
}
|
||||
return bits;
|
||||
return {bits};
|
||||
}
|
||||
|
||||
constexpr inline std::bitset<32> ABI_ALL_GPRS(0x0000FFFF);
|
||||
@@ -57,7 +57,7 @@ constexpr inline Xbyak::Reg ABI_PARAM2 = Xbyak::util::rdx;
|
||||
constexpr inline Xbyak::Reg ABI_PARAM3 = Xbyak::util::r8;
|
||||
constexpr inline Xbyak::Reg ABI_PARAM4 = Xbyak::util::r9;
|
||||
|
||||
const std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({
|
||||
constexpr inline std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({
|
||||
// GPRs
|
||||
Xbyak::util::rcx,
|
||||
Xbyak::util::rdx,
|
||||
@@ -74,7 +74,7 @@ const std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({
|
||||
Xbyak::util::xmm5,
|
||||
});
|
||||
|
||||
const std::bitset<32> ABI_ALL_CALLEE_SAVED = BuildRegSet({
|
||||
constexpr inline std::bitset<32> ABI_ALL_CALLEE_SAVED = BuildRegSet({
|
||||
// GPRs
|
||||
Xbyak::util::rbx,
|
||||
Xbyak::util::rsi,
|
||||
@@ -108,7 +108,7 @@ constexpr inline Xbyak::Reg ABI_PARAM2 = Xbyak::util::rsi;
|
||||
constexpr inline Xbyak::Reg ABI_PARAM3 = Xbyak::util::rdx;
|
||||
constexpr inline Xbyak::Reg ABI_PARAM4 = Xbyak::util::rcx;
|
||||
|
||||
const std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({
|
||||
constexpr inline std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({
|
||||
// GPRs
|
||||
Xbyak::util::rcx,
|
||||
Xbyak::util::rdx,
|
||||
@@ -137,7 +137,7 @@ const std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({
|
||||
Xbyak::util::xmm15,
|
||||
});
|
||||
|
||||
const std::bitset<32> ABI_ALL_CALLEE_SAVED = BuildRegSet({
|
||||
constexpr inline std::bitset<32> ABI_ALL_CALLEE_SAVED = BuildRegSet({
|
||||
// GPRs
|
||||
Xbyak::util::rbx,
|
||||
Xbyak::util::rbp,
|
||||
|
||||
@@ -247,6 +247,9 @@ add_library(core STATIC
|
||||
hle/kernel/k_trace.h
|
||||
hle/kernel/k_transfer_memory.cpp
|
||||
hle/kernel/k_transfer_memory.h
|
||||
hle/kernel/k_worker_task.h
|
||||
hle/kernel/k_worker_task_manager.cpp
|
||||
hle/kernel/k_worker_task_manager.h
|
||||
hle/kernel/k_writable_event.cpp
|
||||
hle/kernel/k_writable_event.h
|
||||
hle/kernel/kernel.cpp
|
||||
@@ -464,6 +467,8 @@ add_library(core STATIC
|
||||
hle/service/mii/types.h
|
||||
hle/service/mm/mm_u.cpp
|
||||
hle/service/mm/mm_u.h
|
||||
hle/service/mnpp/mnpp_app.cpp
|
||||
hle/service/mnpp/mnpp_app.h
|
||||
hle/service/ncm/ncm.cpp
|
||||
hle/service/ncm/ncm.h
|
||||
hle/service/nfc/nfc.cpp
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hardware_properties.h"
|
||||
|
||||
@@ -24,8 +25,11 @@ class CPUInterruptHandler;
|
||||
using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>;
|
||||
|
||||
/// Generic ARMv8 CPU interface
|
||||
class ARM_Interface : NonCopyable {
|
||||
class ARM_Interface {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(ARM_Interface);
|
||||
YUZU_NON_MOVEABLE(ARM_Interface);
|
||||
|
||||
explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers_,
|
||||
bool uses_wall_clock_)
|
||||
: system{system_}, interrupt_handlers{interrupt_handlers_}, uses_wall_clock{
|
||||
|
||||
@@ -128,15 +128,6 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
if (exefs == nullptr)
|
||||
return exefs;
|
||||
|
||||
if (Settings::values.dump_exefs) {
|
||||
LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
|
||||
const auto dump_dir = fs_controller.GetModificationDumpRoot(title_id);
|
||||
if (dump_dir != nullptr) {
|
||||
const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
|
||||
VfsRawCopyD(exefs, exefs_dir);
|
||||
}
|
||||
}
|
||||
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
const auto update_disabled =
|
||||
std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
|
||||
@@ -179,6 +170,15 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
}
|
||||
}
|
||||
|
||||
if (Settings::values.dump_exefs) {
|
||||
LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
|
||||
const auto dump_dir = fs_controller.GetModificationDumpRoot(title_id);
|
||||
if (dump_dir != nullptr) {
|
||||
const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
|
||||
VfsRawCopyD(exefs, exefs_dir);
|
||||
}
|
||||
}
|
||||
|
||||
return exefs;
|
||||
}
|
||||
|
||||
|
||||
+15
-3
@@ -12,6 +12,7 @@
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
|
||||
@@ -29,8 +30,11 @@ enum class VfsEntryType {
|
||||
// A class representing an abstract filesystem. A default implementation given the root VirtualDir
|
||||
// is provided for convenience, but if the Vfs implementation has any additional state or
|
||||
// functionality, they will need to override.
|
||||
class VfsFilesystem : NonCopyable {
|
||||
class VfsFilesystem {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(VfsFilesystem);
|
||||
YUZU_NON_MOVEABLE(VfsFilesystem);
|
||||
|
||||
explicit VfsFilesystem(VirtualDir root);
|
||||
virtual ~VfsFilesystem();
|
||||
|
||||
@@ -77,8 +81,12 @@ protected:
|
||||
};
|
||||
|
||||
// A class representing a file in an abstract filesystem.
|
||||
class VfsFile : NonCopyable {
|
||||
class VfsFile {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(VfsFile);
|
||||
YUZU_NON_MOVEABLE(VfsFile);
|
||||
|
||||
VfsFile() = default;
|
||||
virtual ~VfsFile();
|
||||
|
||||
// Retrieves the file name.
|
||||
@@ -176,8 +184,12 @@ public:
|
||||
};
|
||||
|
||||
// A class representing a directory in an abstract filesystem.
|
||||
class VfsDirectory : NonCopyable {
|
||||
class VfsDirectory {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(VfsDirectory);
|
||||
YUZU_NON_MOVEABLE(VfsDirectory);
|
||||
|
||||
VfsDirectory() = default;
|
||||
virtual ~VfsDirectory();
|
||||
|
||||
// Retrives the file located at path as if the current directory was root. Returns nullptr if
|
||||
|
||||
@@ -13,8 +13,7 @@ ProfileSelectApplet::~ProfileSelectApplet() = default;
|
||||
void DefaultProfileSelectApplet::SelectProfile(
|
||||
std::function<void(std::optional<Common::UUID>)> callback) const {
|
||||
Service::Account::ProfileManager manager;
|
||||
callback(manager.GetUser(Settings::values.current_user.GetValue())
|
||||
.value_or(Common::UUID{Common::INVALID_UUID}));
|
||||
callback(manager.GetUser(Settings::values.current_user.GetValue()).value_or(Common::UUID{}));
|
||||
LOG_INFO(Service_ACC, "called, selecting current user instead of prompting...");
|
||||
}
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
|
||||
auto& motion = console.motion_state;
|
||||
motion.accel = emulated.GetAcceleration();
|
||||
motion.gyro = emulated.GetGyroscope();
|
||||
motion.rotation = emulated.GetGyroscope();
|
||||
motion.rotation = emulated.GetRotations();
|
||||
motion.orientation = emulated.GetOrientation();
|
||||
motion.quaternion = emulated.GetQuaternion();
|
||||
motion.gyro_bias = emulated.GetGyroBias();
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/input.h"
|
||||
#include "common/param_package.h"
|
||||
|
||||
@@ -145,7 +145,7 @@ void EmulatedController::LoadDevices() {
|
||||
motion_devices.begin(), Common::Input::CreateDevice<Common::Input::InputDevice>);
|
||||
std::transform(trigger_params.begin(), trigger_params.end(), trigger_devices.begin(),
|
||||
Common::Input::CreateDevice<Common::Input::InputDevice>);
|
||||
std::transform(battery_params.begin(), battery_params.begin(), battery_devices.end(),
|
||||
std::transform(battery_params.begin(), battery_params.end(), battery_devices.begin(),
|
||||
Common::Input::CreateDevice<Common::Input::InputDevice>);
|
||||
std::transform(output_params.begin(), output_params.end(), output_devices.begin(),
|
||||
Common::Input::CreateDevice<Common::Input::OutputDevice>);
|
||||
@@ -269,7 +269,8 @@ void EmulatedController::ReloadInput() {
|
||||
}
|
||||
|
||||
// Use a common UUID for TAS
|
||||
const auto tas_uuid = Common::UUID{0x0, 0x7A5};
|
||||
static constexpr Common::UUID TAS_UUID = Common::UUID{
|
||||
{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
|
||||
|
||||
// Register TAS devices. No need to force update
|
||||
for (std::size_t index = 0; index < tas_button_devices.size(); ++index) {
|
||||
@@ -278,8 +279,8 @@ void EmulatedController::ReloadInput() {
|
||||
}
|
||||
tas_button_devices[index]->SetCallback({
|
||||
.on_change =
|
||||
[this, index, tas_uuid](const Common::Input::CallbackStatus& callback) {
|
||||
SetButton(callback, index, tas_uuid);
|
||||
[this, index](const Common::Input::CallbackStatus& callback) {
|
||||
SetButton(callback, index, TAS_UUID);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -290,8 +291,8 @@ void EmulatedController::ReloadInput() {
|
||||
}
|
||||
tas_stick_devices[index]->SetCallback({
|
||||
.on_change =
|
||||
[this, index, tas_uuid](const Common::Input::CallbackStatus& callback) {
|
||||
SetStick(callback, index, tas_uuid);
|
||||
[this, index](const Common::Input::CallbackStatus& callback) {
|
||||
SetStick(callback, index, TAS_UUID);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -749,6 +750,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
|
||||
raw_status.gyro.y.value,
|
||||
raw_status.gyro.z.value,
|
||||
});
|
||||
emulated.SetGyroThreshold(raw_status.gyro.x.properties.threshold);
|
||||
emulated.UpdateRotation(raw_status.delta_timestamp);
|
||||
emulated.UpdateOrientation(raw_status.delta_timestamp);
|
||||
force_update_motion = raw_status.force_update;
|
||||
@@ -883,6 +885,12 @@ bool EmulatedController::TestVibration(std::size_t device_index) {
|
||||
return SetVibration(device_index, DEFAULT_VIBRATION_VALUE);
|
||||
}
|
||||
|
||||
bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) {
|
||||
LOG_INFO(Service_HID, "Set polling mode {}", polling_mode);
|
||||
auto& output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
|
||||
return output_device->SetPollingMode(polling_mode) == Common::Input::PollingError::None;
|
||||
}
|
||||
|
||||
void EmulatedController::SetLedPattern() {
|
||||
for (auto& device : output_devices) {
|
||||
if (!device) {
|
||||
|
||||
@@ -13,8 +13,6 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/input.h"
|
||||
#include "common/param_package.h"
|
||||
#include "common/point.h"
|
||||
#include "common/quaternion.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/vector_math.h"
|
||||
#include "core/hid/hid_types.h"
|
||||
@@ -301,16 +299,23 @@ public:
|
||||
|
||||
/**
|
||||
* Sends a specific vibration to the output device
|
||||
* @return returns true if vibration had no errors
|
||||
* @return true if vibration had no errors
|
||||
*/
|
||||
bool SetVibration(std::size_t device_index, VibrationValue vibration);
|
||||
|
||||
/**
|
||||
* Sends a small vibration to the output device
|
||||
* @return returns true if SetVibration was successfull
|
||||
* @return true if SetVibration was successfull
|
||||
*/
|
||||
bool TestVibration(std::size_t device_index);
|
||||
|
||||
/**
|
||||
* Sets the desired data to be polled from a controller
|
||||
* @param polling_mode type of input desired buttons, gyro, nfc, ir, etc.
|
||||
* @return true if SetPollingMode was successfull
|
||||
*/
|
||||
bool SetPollingMode(Common::Input::PollingMode polling_mode);
|
||||
|
||||
/// Returns the led pattern corresponding to this emulated controller
|
||||
LedPattern GetLedPattern() const;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "core/hid/hid_types.h"
|
||||
|
||||
namespace Core::HID {
|
||||
|
||||
@@ -28,7 +28,7 @@ Common::Input::BatteryStatus TransformToBattery(const Common::Input::CallbackSta
|
||||
if (value > 0.8f) {
|
||||
battery = Common::Input::BatteryLevel::Full;
|
||||
}
|
||||
if (value >= 1.0f) {
|
||||
if (value >= 0.95f) {
|
||||
battery = Common::Input::BatteryLevel::Charging;
|
||||
}
|
||||
break;
|
||||
@@ -114,7 +114,7 @@ Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatu
|
||||
if (TransformToButton(callback).value) {
|
||||
std::random_device device;
|
||||
std::mt19937 gen(device());
|
||||
std::uniform_int_distribution<s16> distribution(-1000, 1000);
|
||||
std::uniform_int_distribution<s16> distribution(-5000, 5000);
|
||||
status.accel.x.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
|
||||
status.accel.y.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
|
||||
status.accel.z.raw_value = static_cast<f32>(distribution(gen)) * 0.001f;
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Core::HID {
|
||||
MotionInput::MotionInput() {
|
||||
// Initialize PID constants with default values
|
||||
SetPID(0.3f, 0.005f, 0.0f);
|
||||
SetGyroThreshold(0.001f);
|
||||
SetGyroThreshold(0.007f);
|
||||
}
|
||||
|
||||
void MotionInput::SetPID(f32 new_kp, f32 new_ki, f32 new_kd) {
|
||||
@@ -31,7 +31,7 @@ void MotionInput::SetGyroscope(const Common::Vec3f& gyroscope) {
|
||||
gyro_bias = (gyro_bias * 0.9999f) + (gyroscope * 0.0001f);
|
||||
}
|
||||
|
||||
if (gyro.Length2() < gyro_threshold) {
|
||||
if (gyro.Length() < gyro_threshold) {
|
||||
gyro = {};
|
||||
} else {
|
||||
only_accelerometer = false;
|
||||
|
||||
@@ -404,6 +404,11 @@ inline s32 RequestParser::Pop() {
|
||||
return static_cast<s32>(Pop<u32>());
|
||||
}
|
||||
|
||||
// Ignore the -Wclass-memaccess warning on memcpy for non-trivially default constructible objects.
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wclass-memaccess"
|
||||
#endif
|
||||
template <typename T>
|
||||
void RequestParser::PopRaw(T& value) {
|
||||
static_assert(std::is_trivially_copyable_v<T>,
|
||||
@@ -411,6 +416,9 @@ void RequestParser::PopRaw(T& value) {
|
||||
std::memcpy(&value, cmdbuf + index, sizeof(T));
|
||||
index += (sizeof(T) + 3) / 4; // round up to word length
|
||||
}
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
T RequestParser::PopRaw() {
|
||||
|
||||
@@ -341,10 +341,6 @@ public:
|
||||
return *thread;
|
||||
}
|
||||
|
||||
bool IsThreadWaiting() const {
|
||||
return is_thread_waiting;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class IPC::ResponseBuilder;
|
||||
|
||||
@@ -379,7 +375,6 @@ private:
|
||||
u32 domain_offset{};
|
||||
|
||||
std::shared_ptr<SessionRequestManager> manager;
|
||||
bool is_thread_waiting{};
|
||||
|
||||
KernelCore& kernel;
|
||||
Core::Memory::Memory& memory;
|
||||
|
||||
@@ -31,8 +31,6 @@ public:
|
||||
}
|
||||
|
||||
constexpr void SetAffinity(s32 core, bool set) {
|
||||
ASSERT(0 <= core && core < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
|
||||
|
||||
if (set) {
|
||||
this->mask |= GetCoreBit(core);
|
||||
} else {
|
||||
|
||||
@@ -20,8 +20,6 @@ class KernelCore;
|
||||
class KProcess;
|
||||
|
||||
#define KERNEL_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS) \
|
||||
YUZU_NON_COPYABLE(CLASS); \
|
||||
YUZU_NON_MOVEABLE(CLASS); \
|
||||
\
|
||||
private: \
|
||||
friend class ::Kernel::KClassTokenGenerator; \
|
||||
@@ -32,6 +30,9 @@ private:
|
||||
} \
|
||||
\
|
||||
public: \
|
||||
YUZU_NON_COPYABLE(CLASS); \
|
||||
YUZU_NON_MOVEABLE(CLASS); \
|
||||
\
|
||||
using BaseClass = BASE_CLASS; \
|
||||
static constexpr TypeObj GetStaticTypeObj() { \
|
||||
constexpr ClassTokenType Token = ClassToken(); \
|
||||
@@ -224,9 +225,9 @@ private:
|
||||
|
||||
template <typename T>
|
||||
class KScopedAutoObject {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(KScopedAutoObject);
|
||||
|
||||
public:
|
||||
constexpr KScopedAutoObject() = default;
|
||||
|
||||
constexpr KScopedAutoObject(T* o) : m_obj(o) {
|
||||
|
||||
@@ -16,13 +16,12 @@ class KernelCore;
|
||||
class KProcess;
|
||||
|
||||
class KAutoObjectWithListContainer {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(KAutoObjectWithListContainer);
|
||||
YUZU_NON_MOVEABLE(KAutoObjectWithListContainer);
|
||||
|
||||
public:
|
||||
using ListType = boost::intrusive::rbtree<KAutoObjectWithList>;
|
||||
|
||||
public:
|
||||
class ListAccessor : public KScopedLightLock {
|
||||
public:
|
||||
explicit ListAccessor(KAutoObjectWithListContainer* container)
|
||||
@@ -48,7 +47,6 @@ public:
|
||||
|
||||
friend class ListAccessor;
|
||||
|
||||
public:
|
||||
KAutoObjectWithListContainer(KernelCore& kernel) : m_lock(kernel), m_object_list() {}
|
||||
|
||||
void Initialize() {}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/device_memory.h"
|
||||
#include "core/hle/kernel/k_auto_object.h"
|
||||
@@ -28,8 +29,7 @@ ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr
|
||||
auto& page_table = m_owner->PageTable();
|
||||
|
||||
// Construct the page group.
|
||||
KMemoryInfo kBlockInfo = page_table.QueryInfo(addr);
|
||||
m_page_group = KPageLinkedList(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages());
|
||||
m_page_group = KPageLinkedList(addr, Common::DivideUp(size, PageSize));
|
||||
|
||||
// Lock the memory.
|
||||
R_TRY(page_table.LockForCodeMemory(addr, size))
|
||||
@@ -143,4 +143,4 @@ ResultCode KCodeMemory::UnmapFromOwner(VAddr address, size_t size) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -22,13 +22,12 @@ namespace Kernel {
|
||||
class KernelCore;
|
||||
|
||||
class KHandleTable {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(KHandleTable);
|
||||
YUZU_NON_MOVEABLE(KHandleTable);
|
||||
|
||||
public:
|
||||
static constexpr size_t MaxTableSize = 1024;
|
||||
|
||||
public:
|
||||
explicit KHandleTable(KernelCore& kernel_);
|
||||
~KHandleTable();
|
||||
|
||||
|
||||
@@ -8,12 +8,16 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
#include "core/device_memory.h"
|
||||
#include "core/hle/kernel/k_memory_manager.h"
|
||||
#include "core/hle/kernel/k_page_linked_list.h"
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
KMemoryManager::KMemoryManager(Core::System& system_) : system{system_} {}
|
||||
|
||||
std::size_t KMemoryManager::Impl::Initialize(Pool new_pool, u64 start_address, u64 end_address) {
|
||||
const auto size{end_address - start_address};
|
||||
|
||||
@@ -81,7 +85,7 @@ VAddr KMemoryManager::AllocateAndOpenContinuous(std::size_t num_pages, std::size
|
||||
}
|
||||
|
||||
ResultCode KMemoryManager::Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
|
||||
Direction dir) {
|
||||
Direction dir, u32 heap_fill_value) {
|
||||
ASSERT(page_list.GetNumPages() == 0);
|
||||
|
||||
// Early return if we're allocating no pages
|
||||
@@ -139,6 +143,12 @@ ResultCode KMemoryManager::Allocate(KPageLinkedList& page_list, std::size_t num_
|
||||
}
|
||||
}
|
||||
|
||||
// Clear allocated memory.
|
||||
for (const auto& it : page_list.Nodes()) {
|
||||
std::memset(system.DeviceMemory().GetPointer(it.GetAddress()), heap_fill_value,
|
||||
it.GetSize());
|
||||
}
|
||||
|
||||
// Only succeed if we allocated as many pages as we wanted
|
||||
if (num_pages) {
|
||||
return ResultOutOfMemory;
|
||||
@@ -146,11 +156,12 @@ ResultCode KMemoryManager::Allocate(KPageLinkedList& page_list, std::size_t num_
|
||||
|
||||
// We succeeded!
|
||||
group_guard.Cancel();
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KMemoryManager::Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
|
||||
Direction dir) {
|
||||
Direction dir, u32 heap_fill_value) {
|
||||
// Early return if we're freeing no pages
|
||||
if (!num_pages) {
|
||||
return ResultSuccess;
|
||||
|
||||
@@ -8,16 +8,24 @@
|
||||
#include <mutex>
|
||||
#include <tuple>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/k_page_heap.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KPageLinkedList;
|
||||
|
||||
class KMemoryManager final : NonCopyable {
|
||||
class KMemoryManager final {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(KMemoryManager);
|
||||
YUZU_NON_MOVEABLE(KMemoryManager);
|
||||
|
||||
enum class Pool : u32 {
|
||||
Application = 0,
|
||||
Applet = 1,
|
||||
@@ -42,7 +50,7 @@ public:
|
||||
Mask = (0xF << Shift),
|
||||
};
|
||||
|
||||
KMemoryManager() = default;
|
||||
explicit KMemoryManager(Core::System& system_);
|
||||
|
||||
constexpr std::size_t GetSize(Pool pool) const {
|
||||
return managers[static_cast<std::size_t>(pool)].GetSize();
|
||||
@@ -51,10 +59,10 @@ public:
|
||||
void InitializeManager(Pool pool, u64 start_address, u64 end_address);
|
||||
|
||||
VAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option);
|
||||
ResultCode Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
|
||||
Direction dir = Direction::FromFront);
|
||||
ResultCode Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
|
||||
Direction dir = Direction::FromFront);
|
||||
ResultCode Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, Direction dir,
|
||||
u32 heap_fill_value = 0);
|
||||
ResultCode Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, Direction dir,
|
||||
u32 heap_fill_value = 0);
|
||||
|
||||
static constexpr std::size_t MaxManagerCount = 10;
|
||||
|
||||
@@ -84,26 +92,13 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
class Impl final : NonCopyable {
|
||||
private:
|
||||
using RefCount = u16;
|
||||
|
||||
private:
|
||||
KPageHeap heap;
|
||||
Pool pool{};
|
||||
|
||||
class Impl final {
|
||||
public:
|
||||
static std::size_t CalculateManagementOverheadSize(std::size_t region_size);
|
||||
YUZU_NON_COPYABLE(Impl);
|
||||
YUZU_NON_MOVEABLE(Impl);
|
||||
|
||||
static constexpr std::size_t CalculateOptimizedProcessOverheadSize(
|
||||
std::size_t region_size) {
|
||||
return (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) /
|
||||
Common::BitSize<u64>()) *
|
||||
sizeof(u64);
|
||||
}
|
||||
|
||||
public:
|
||||
Impl() = default;
|
||||
~Impl() = default;
|
||||
|
||||
std::size_t Initialize(Pool new_pool, u64 start_address, u64 end_address);
|
||||
|
||||
@@ -126,9 +121,25 @@ private:
|
||||
constexpr VAddr GetEndAddress() const {
|
||||
return heap.GetEndAddress();
|
||||
}
|
||||
|
||||
static std::size_t CalculateManagementOverheadSize(std::size_t region_size);
|
||||
|
||||
static constexpr std::size_t CalculateOptimizedProcessOverheadSize(
|
||||
std::size_t region_size) {
|
||||
return (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) /
|
||||
Common::BitSize<u64>()) *
|
||||
sizeof(u64);
|
||||
}
|
||||
|
||||
private:
|
||||
using RefCount = u16;
|
||||
|
||||
KPageHeap heap;
|
||||
Pool pool{};
|
||||
};
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
std::array<std::mutex, static_cast<std::size_t>(Pool::Count)> pool_locks;
|
||||
std::array<Impl, MaxManagerCount> managers;
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/intrusive_red_black_tree.h"
|
||||
#include "core/hle/kernel/k_memory_region_type.h"
|
||||
@@ -13,11 +14,13 @@ namespace Kernel {
|
||||
|
||||
class KMemoryRegionAllocator;
|
||||
|
||||
class KMemoryRegion final : public Common::IntrusiveRedBlackTreeBaseNode<KMemoryRegion>,
|
||||
NonCopyable {
|
||||
class KMemoryRegion final : public Common::IntrusiveRedBlackTreeBaseNode<KMemoryRegion> {
|
||||
friend class KMemoryRegionTree;
|
||||
|
||||
public:
|
||||
YUZU_NON_COPYABLE(KMemoryRegion);
|
||||
YUZU_NON_MOVEABLE(KMemoryRegion);
|
||||
|
||||
constexpr KMemoryRegion() = default;
|
||||
constexpr KMemoryRegion(u64 address_, u64 last_address_)
|
||||
: address{address_}, last_address{last_address_} {}
|
||||
@@ -29,6 +32,8 @@ public:
|
||||
: KMemoryRegion(address_, last_address_, std::numeric_limits<u64>::max(), attributes_,
|
||||
type_id_) {}
|
||||
|
||||
~KMemoryRegion() = default;
|
||||
|
||||
static constexpr int Compare(const KMemoryRegion& lhs, const KMemoryRegion& rhs) {
|
||||
if (lhs.GetAddress() < rhs.GetAddress()) {
|
||||
return -1;
|
||||
@@ -39,16 +44,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr void Reset(u64 a, u64 la, u64 p, u32 r, u32 t) {
|
||||
address = a;
|
||||
pair_address = p;
|
||||
last_address = la;
|
||||
attributes = r;
|
||||
type_id = t;
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr u64 GetAddress() const {
|
||||
return address;
|
||||
}
|
||||
@@ -108,6 +103,14 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr void Reset(u64 a, u64 la, u64 p, u32 r, u32 t) {
|
||||
address = a;
|
||||
pair_address = p;
|
||||
last_address = la;
|
||||
attributes = r;
|
||||
type_id = t;
|
||||
}
|
||||
|
||||
u64 address{};
|
||||
u64 last_address{};
|
||||
u64 pair_address{};
|
||||
@@ -115,8 +118,25 @@ private:
|
||||
u32 type_id{};
|
||||
};
|
||||
|
||||
class KMemoryRegionTree final : NonCopyable {
|
||||
class KMemoryRegionTree final {
|
||||
private:
|
||||
using TreeType =
|
||||
Common::IntrusiveRedBlackTreeBaseTraits<KMemoryRegion>::TreeType<KMemoryRegion>;
|
||||
|
||||
public:
|
||||
YUZU_NON_COPYABLE(KMemoryRegionTree);
|
||||
YUZU_NON_MOVEABLE(KMemoryRegionTree);
|
||||
|
||||
using value_type = TreeType::value_type;
|
||||
using size_type = TreeType::size_type;
|
||||
using difference_type = TreeType::difference_type;
|
||||
using pointer = TreeType::pointer;
|
||||
using const_pointer = TreeType::const_pointer;
|
||||
using reference = TreeType::reference;
|
||||
using const_reference = TreeType::const_reference;
|
||||
using iterator = TreeType::iterator;
|
||||
using const_iterator = TreeType::const_iterator;
|
||||
|
||||
struct DerivedRegionExtents {
|
||||
const KMemoryRegion* first_region{};
|
||||
const KMemoryRegion* last_region{};
|
||||
@@ -140,29 +160,9 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
using TreeType =
|
||||
Common::IntrusiveRedBlackTreeBaseTraits<KMemoryRegion>::TreeType<KMemoryRegion>;
|
||||
|
||||
public:
|
||||
using value_type = TreeType::value_type;
|
||||
using size_type = TreeType::size_type;
|
||||
using difference_type = TreeType::difference_type;
|
||||
using pointer = TreeType::pointer;
|
||||
using const_pointer = TreeType::const_pointer;
|
||||
using reference = TreeType::reference;
|
||||
using const_reference = TreeType::const_reference;
|
||||
using iterator = TreeType::iterator;
|
||||
using const_iterator = TreeType::const_iterator;
|
||||
|
||||
private:
|
||||
TreeType m_tree{};
|
||||
KMemoryRegionAllocator& memory_region_allocator;
|
||||
|
||||
public:
|
||||
explicit KMemoryRegionTree(KMemoryRegionAllocator& memory_region_allocator_);
|
||||
~KMemoryRegionTree() = default;
|
||||
|
||||
public:
|
||||
KMemoryRegion* FindModifiable(u64 address) {
|
||||
if (auto it = this->find(KMemoryRegion(address, address, 0, 0)); it != this->end()) {
|
||||
return std::addressof(*it);
|
||||
@@ -241,7 +241,6 @@ public:
|
||||
return GetDerivedRegionExtents(static_cast<KMemoryRegionType>(type_id));
|
||||
}
|
||||
|
||||
public:
|
||||
void InsertDirectly(u64 address, u64 last_address, u32 attr = 0, u32 type_id = 0);
|
||||
bool Insert(u64 address, size_t size, u32 type_id, u32 new_attr = 0, u32 old_attr = 0);
|
||||
|
||||
@@ -252,7 +251,6 @@ public:
|
||||
return this->GetRandomAlignedRegion(size + 2 * guard_size, alignment, type_id) + guard_size;
|
||||
}
|
||||
|
||||
public:
|
||||
// Iterator accessors.
|
||||
iterator begin() {
|
||||
return m_tree.begin();
|
||||
@@ -322,13 +320,21 @@ public:
|
||||
iterator nfind(const_reference ref) const {
|
||||
return m_tree.nfind(ref);
|
||||
}
|
||||
|
||||
private:
|
||||
TreeType m_tree{};
|
||||
KMemoryRegionAllocator& memory_region_allocator;
|
||||
};
|
||||
|
||||
class KMemoryRegionAllocator final : NonCopyable {
|
||||
class KMemoryRegionAllocator final {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(KMemoryRegionAllocator);
|
||||
YUZU_NON_MOVEABLE(KMemoryRegionAllocator);
|
||||
|
||||
static constexpr size_t MaxMemoryRegions = 200;
|
||||
|
||||
constexpr KMemoryRegionAllocator() = default;
|
||||
constexpr ~KMemoryRegionAllocator() = default;
|
||||
|
||||
template <typename... Args>
|
||||
KMemoryRegion* Allocate(Args&&... args) {
|
||||
|
||||
@@ -8,14 +8,44 @@
|
||||
#include <vector>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/k_page_bitmap.h"
|
||||
#include "core/hle/kernel/memory_types.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KPageHeap final : NonCopyable {
|
||||
class KPageHeap final {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(KPageHeap);
|
||||
YUZU_NON_MOVEABLE(KPageHeap);
|
||||
|
||||
KPageHeap() = default;
|
||||
~KPageHeap() = default;
|
||||
|
||||
constexpr VAddr GetAddress() const {
|
||||
return heap_address;
|
||||
}
|
||||
constexpr std::size_t GetSize() const {
|
||||
return heap_size;
|
||||
}
|
||||
constexpr VAddr GetEndAddress() const {
|
||||
return GetAddress() + GetSize();
|
||||
}
|
||||
constexpr std::size_t GetPageOffset(VAddr block) const {
|
||||
return (block - GetAddress()) / PageSize;
|
||||
}
|
||||
|
||||
void Initialize(VAddr heap_address, std::size_t heap_size, std::size_t metadata_size);
|
||||
VAddr AllocateBlock(s32 index, bool random);
|
||||
void Free(VAddr addr, std::size_t num_pages);
|
||||
|
||||
void UpdateUsedSize() {
|
||||
used_size = heap_size - (GetNumFreePages() * PageSize);
|
||||
}
|
||||
|
||||
static std::size_t CalculateManagementOverheadSize(std::size_t region_size);
|
||||
|
||||
static constexpr s32 GetAlignedBlockIndex(std::size_t num_pages, std::size_t align_pages) {
|
||||
const auto target_pages{std::max(num_pages, align_pages)};
|
||||
for (std::size_t i = 0; i < NumMemoryBlockPageShifts; i++) {
|
||||
@@ -45,21 +75,13 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr std::size_t NumMemoryBlockPageShifts{7};
|
||||
static constexpr std::array<std::size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{
|
||||
0xC, 0x10, 0x15, 0x16, 0x19, 0x1D, 0x1E,
|
||||
};
|
||||
|
||||
class Block final : NonCopyable {
|
||||
private:
|
||||
KPageBitmap bitmap;
|
||||
VAddr heap_address{};
|
||||
uintptr_t end_offset{};
|
||||
std::size_t block_shift{};
|
||||
std::size_t next_block_shift{};
|
||||
|
||||
class Block final {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(Block);
|
||||
YUZU_NON_MOVEABLE(Block);
|
||||
|
||||
Block() = default;
|
||||
~Block() = default;
|
||||
|
||||
constexpr std::size_t GetShift() const {
|
||||
return block_shift;
|
||||
@@ -129,7 +151,6 @@ private:
|
||||
return heap_address + (offset << GetShift());
|
||||
}
|
||||
|
||||
public:
|
||||
static constexpr std::size_t CalculateManagementOverheadSize(std::size_t region_size,
|
||||
std::size_t cur_block_shift,
|
||||
std::size_t next_block_shift) {
|
||||
@@ -139,35 +160,15 @@ private:
|
||||
return KPageBitmap::CalculateManagementOverheadSize(
|
||||
(align * 2 + Common::AlignUp(region_size, align)) / cur_block_size);
|
||||
}
|
||||
|
||||
private:
|
||||
KPageBitmap bitmap;
|
||||
VAddr heap_address{};
|
||||
uintptr_t end_offset{};
|
||||
std::size_t block_shift{};
|
||||
std::size_t next_block_shift{};
|
||||
};
|
||||
|
||||
public:
|
||||
KPageHeap() = default;
|
||||
|
||||
constexpr VAddr GetAddress() const {
|
||||
return heap_address;
|
||||
}
|
||||
constexpr std::size_t GetSize() const {
|
||||
return heap_size;
|
||||
}
|
||||
constexpr VAddr GetEndAddress() const {
|
||||
return GetAddress() + GetSize();
|
||||
}
|
||||
constexpr std::size_t GetPageOffset(VAddr block) const {
|
||||
return (block - GetAddress()) / PageSize;
|
||||
}
|
||||
|
||||
void Initialize(VAddr heap_address, std::size_t heap_size, std::size_t metadata_size);
|
||||
VAddr AllocateBlock(s32 index, bool random);
|
||||
void Free(VAddr addr, std::size_t num_pages);
|
||||
|
||||
void UpdateUsedSize() {
|
||||
used_size = heap_size - (GetNumFreePages() * PageSize);
|
||||
}
|
||||
|
||||
static std::size_t CalculateManagementOverheadSize(std::size_t region_size);
|
||||
|
||||
private:
|
||||
constexpr std::size_t GetNumFreePages() const {
|
||||
std::size_t num_free{};
|
||||
|
||||
@@ -180,6 +181,11 @@ private:
|
||||
|
||||
void FreeBlock(VAddr block, s32 index);
|
||||
|
||||
static constexpr std::size_t NumMemoryBlockPageShifts{7};
|
||||
static constexpr std::array<std::size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{
|
||||
0xC, 0x10, 0x15, 0x16, 0x19, 0x1D, 0x1E,
|
||||
};
|
||||
|
||||
VAddr heap_address{};
|
||||
std::size_t heap_size{};
|
||||
std::size_t used_size{};
|
||||
|
||||
@@ -41,27 +41,12 @@ constexpr std::size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceT
|
||||
}
|
||||
}
|
||||
|
||||
constexpr u64 GetAddressInRange(const KMemoryInfo& info, VAddr addr) {
|
||||
if (info.GetAddress() < addr) {
|
||||
return addr;
|
||||
}
|
||||
return info.GetAddress();
|
||||
}
|
||||
|
||||
constexpr std::size_t GetSizeInRange(const KMemoryInfo& info, VAddr start, VAddr end) {
|
||||
std::size_t size{info.GetSize()};
|
||||
if (info.GetAddress() < start) {
|
||||
size -= start - info.GetAddress();
|
||||
}
|
||||
if (info.GetEndAddress() > end) {
|
||||
size -= info.GetEndAddress() - end;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
KPageTable::KPageTable(Core::System& system_) : system{system_} {}
|
||||
KPageTable::KPageTable(Core::System& system_)
|
||||
: general_lock{system_.Kernel()}, map_physical_memory_lock{system_.Kernel()}, system{system_} {}
|
||||
|
||||
KPageTable::~KPageTable() = default;
|
||||
|
||||
ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type,
|
||||
bool enable_aslr, VAddr code_addr,
|
||||
@@ -276,22 +261,23 @@ ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_
|
||||
|
||||
ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemoryState state,
|
||||
KMemoryPermission perm) {
|
||||
std::lock_guard lock{page_table_lock};
|
||||
|
||||
const u64 size{num_pages * PageSize};
|
||||
|
||||
if (!CanContain(addr, size, state)) {
|
||||
return ResultInvalidCurrentMemory;
|
||||
}
|
||||
// Validate the mapping request.
|
||||
R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory);
|
||||
|
||||
if (IsRegionMapped(addr, size)) {
|
||||
return ResultInvalidCurrentMemory;
|
||||
}
|
||||
// Lock the table.
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
// Verify that the destination memory is unmapped.
|
||||
R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, KMemoryState::Free,
|
||||
KMemoryPermission::None, KMemoryPermission::None,
|
||||
KMemoryAttribute::None, KMemoryAttribute::None));
|
||||
|
||||
KPageLinkedList page_linked_list;
|
||||
CASCADE_CODE(
|
||||
system.Kernel().MemoryManager().Allocate(page_linked_list, num_pages, memory_pool));
|
||||
CASCADE_CODE(Operate(addr, num_pages, page_linked_list, OperationType::MapGroup));
|
||||
R_TRY(system.Kernel().MemoryManager().Allocate(page_linked_list, num_pages, memory_pool,
|
||||
allocation_option));
|
||||
R_TRY(Operate(addr, num_pages, page_linked_list, OperationType::MapGroup));
|
||||
|
||||
block_manager->Update(addr, num_pages, state, perm);
|
||||
|
||||
@@ -299,7 +285,7 @@ ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemory
|
||||
}
|
||||
|
||||
ResultCode KPageTable::MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
|
||||
std::lock_guard lock{page_table_lock};
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
const std::size_t num_pages{size / PageSize};
|
||||
|
||||
@@ -336,7 +322,7 @@ ResultCode KPageTable::MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t
|
||||
}
|
||||
|
||||
ResultCode KPageTable::UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
|
||||
std::lock_guard lock{page_table_lock};
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
if (!size) {
|
||||
return ResultSuccess;
|
||||
@@ -370,7 +356,7 @@ ResultCode KPageTable::UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size
|
||||
|
||||
ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
|
||||
KPageTable& src_page_table, VAddr src_addr) {
|
||||
std::lock_guard lock{page_table_lock};
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
const std::size_t num_pages{size / PageSize};
|
||||
|
||||
@@ -395,161 +381,478 @@ ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
void KPageTable::MapPhysicalMemory(KPageLinkedList& page_linked_list, VAddr start, VAddr end) {
|
||||
auto node{page_linked_list.Nodes().begin()};
|
||||
PAddr map_addr{node->GetAddress()};
|
||||
std::size_t src_num_pages{node->GetNumPages()};
|
||||
|
||||
block_manager->IterateForRange(start, end, [&](const KMemoryInfo& info) {
|
||||
if (info.state != KMemoryState::Free) {
|
||||
return;
|
||||
}
|
||||
ResultCode KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) {
|
||||
// Lock the physical memory lock.
|
||||
KScopedLightLock map_phys_mem_lk(map_physical_memory_lock);
|
||||
|
||||
std::size_t dst_num_pages{GetSizeInRange(info, start, end) / PageSize};
|
||||
VAddr dst_addr{GetAddressInRange(info, start)};
|
||||
// Calculate the last address for convenience.
|
||||
const VAddr last_address = address + size - 1;
|
||||
|
||||
while (dst_num_pages) {
|
||||
if (!src_num_pages) {
|
||||
node = std::next(node);
|
||||
map_addr = node->GetAddress();
|
||||
src_num_pages = node->GetNumPages();
|
||||
// Define iteration variables.
|
||||
VAddr cur_address;
|
||||
std::size_t mapped_size;
|
||||
|
||||
// The entire mapping process can be retried.
|
||||
while (true) {
|
||||
// Check if the memory is already mapped.
|
||||
{
|
||||
// Lock the table.
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
// Iterate over the memory.
|
||||
cur_address = address;
|
||||
mapped_size = 0;
|
||||
|
||||
auto it = block_manager->FindIterator(cur_address);
|
||||
while (true) {
|
||||
// Check that the iterator is valid.
|
||||
ASSERT(it != block_manager->end());
|
||||
|
||||
// Get the memory info.
|
||||
const KMemoryInfo info = it->GetMemoryInfo();
|
||||
|
||||
// Check if we're done.
|
||||
if (last_address <= info.GetLastAddress()) {
|
||||
if (info.GetState() != KMemoryState::Free) {
|
||||
mapped_size += (last_address + 1 - cur_address);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Track the memory if it's mapped.
|
||||
if (info.GetState() != KMemoryState::Free) {
|
||||
mapped_size += VAddr(info.GetEndAddress()) - cur_address;
|
||||
}
|
||||
|
||||
// Advance.
|
||||
cur_address = info.GetEndAddress();
|
||||
++it;
|
||||
}
|
||||
|
||||
const std::size_t num_pages{std::min(src_num_pages, dst_num_pages)};
|
||||
Operate(dst_addr, num_pages, KMemoryPermission::UserReadWrite, OperationType::Map,
|
||||
map_addr);
|
||||
|
||||
dst_addr += num_pages * PageSize;
|
||||
map_addr += num_pages * PageSize;
|
||||
src_num_pages -= num_pages;
|
||||
dst_num_pages -= num_pages;
|
||||
// If the size mapped is the size requested, we've nothing to do.
|
||||
R_SUCCEED_IF(size == mapped_size);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ResultCode KPageTable::MapPhysicalMemory(VAddr addr, std::size_t size) {
|
||||
std::lock_guard lock{page_table_lock};
|
||||
// Allocate and map the memory.
|
||||
{
|
||||
// Reserve the memory from the process resource limit.
|
||||
KScopedResourceReservation memory_reservation(
|
||||
system.Kernel().CurrentProcess()->GetResourceLimit(),
|
||||
LimitableResource::PhysicalMemory, size - mapped_size);
|
||||
R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
|
||||
|
||||
std::size_t mapped_size{};
|
||||
const VAddr end_addr{addr + size};
|
||||
// Allocate pages for the new memory.
|
||||
KPageLinkedList page_linked_list;
|
||||
R_TRY(system.Kernel().MemoryManager().Allocate(
|
||||
page_linked_list, (size - mapped_size) / PageSize, memory_pool, allocation_option));
|
||||
|
||||
block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) {
|
||||
if (info.state != KMemoryState::Free) {
|
||||
mapped_size += GetSizeInRange(info, addr, end_addr);
|
||||
}
|
||||
});
|
||||
// Map the memory.
|
||||
{
|
||||
// Lock the table.
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
if (mapped_size == size) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
size_t num_allocator_blocks = 0;
|
||||
|
||||
const std::size_t remaining_size{size - mapped_size};
|
||||
const std::size_t remaining_pages{remaining_size / PageSize};
|
||||
// Verify that nobody has mapped memory since we first checked.
|
||||
{
|
||||
// Iterate over the memory.
|
||||
size_t checked_mapped_size = 0;
|
||||
cur_address = address;
|
||||
|
||||
// Reserve the memory from the process resource limit.
|
||||
KScopedResourceReservation memory_reservation(
|
||||
system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory,
|
||||
remaining_size);
|
||||
if (!memory_reservation.Succeeded()) {
|
||||
LOG_ERROR(Kernel, "Could not reserve remaining {:X} bytes", remaining_size);
|
||||
return ResultLimitReached;
|
||||
}
|
||||
auto it = block_manager->FindIterator(cur_address);
|
||||
while (true) {
|
||||
// Check that the iterator is valid.
|
||||
ASSERT(it != block_manager->end());
|
||||
|
||||
KPageLinkedList page_linked_list;
|
||||
// Get the memory info.
|
||||
const KMemoryInfo info = it->GetMemoryInfo();
|
||||
|
||||
CASCADE_CODE(
|
||||
system.Kernel().MemoryManager().Allocate(page_linked_list, remaining_pages, memory_pool));
|
||||
const bool is_free = info.GetState() == KMemoryState::Free;
|
||||
if (is_free) {
|
||||
if (info.GetAddress() < address) {
|
||||
++num_allocator_blocks;
|
||||
}
|
||||
if (last_address < info.GetLastAddress()) {
|
||||
++num_allocator_blocks;
|
||||
}
|
||||
}
|
||||
|
||||
// We succeeded, so commit the memory reservation.
|
||||
memory_reservation.Commit();
|
||||
// Check if we're done.
|
||||
if (last_address <= info.GetLastAddress()) {
|
||||
if (!is_free) {
|
||||
checked_mapped_size += (last_address + 1 - cur_address);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
MapPhysicalMemory(page_linked_list, addr, end_addr);
|
||||
// Track the memory if it's mapped.
|
||||
if (!is_free) {
|
||||
checked_mapped_size += VAddr(info.GetEndAddress()) - cur_address;
|
||||
}
|
||||
|
||||
mapped_physical_memory_size += remaining_size;
|
||||
// Advance.
|
||||
cur_address = info.GetEndAddress();
|
||||
++it;
|
||||
}
|
||||
|
||||
const std::size_t num_pages{size / PageSize};
|
||||
block_manager->Update(addr, num_pages, KMemoryState::Free, KMemoryPermission::None,
|
||||
KMemoryAttribute::None, KMemoryState::Normal,
|
||||
KMemoryPermission::UserReadWrite, KMemoryAttribute::None);
|
||||
// If the size now isn't what it was before, somebody mapped or unmapped
|
||||
// concurrently. If this happened, retry.
|
||||
if (mapped_size != checked_mapped_size) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
// Reset the current tracking address, and make sure we clean up on failure.
|
||||
cur_address = address;
|
||||
auto unmap_guard = detail::ScopeExit([&] {
|
||||
if (cur_address > address) {
|
||||
const VAddr last_unmap_address = cur_address - 1;
|
||||
|
||||
ResultCode KPageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) {
|
||||
std::lock_guard lock{page_table_lock};
|
||||
// Iterate, unmapping the pages.
|
||||
cur_address = address;
|
||||
|
||||
const VAddr end_addr{addr + size};
|
||||
ResultCode result{ResultSuccess};
|
||||
std::size_t mapped_size{};
|
||||
auto it = block_manager->FindIterator(cur_address);
|
||||
while (true) {
|
||||
// Check that the iterator is valid.
|
||||
ASSERT(it != block_manager->end());
|
||||
|
||||
// Verify that the region can be unmapped
|
||||
block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) {
|
||||
if (info.state == KMemoryState::Normal) {
|
||||
if (info.attribute != KMemoryAttribute::None) {
|
||||
result = ResultInvalidCurrentMemory;
|
||||
return;
|
||||
// Get the memory info.
|
||||
const KMemoryInfo info = it->GetMemoryInfo();
|
||||
|
||||
// If the memory state is free, we mapped it and need to unmap it.
|
||||
if (info.GetState() == KMemoryState::Free) {
|
||||
// Determine the range to unmap.
|
||||
const size_t cur_pages =
|
||||
std::min(VAddr(info.GetEndAddress()) - cur_address,
|
||||
last_unmap_address + 1 - cur_address) /
|
||||
PageSize;
|
||||
|
||||
// Unmap.
|
||||
ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None,
|
||||
OperationType::Unmap)
|
||||
.IsSuccess());
|
||||
}
|
||||
|
||||
// Check if we're done.
|
||||
if (last_unmap_address <= info.GetLastAddress()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Advance.
|
||||
cur_address = info.GetEndAddress();
|
||||
++it;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Iterate over the memory.
|
||||
auto pg_it = page_linked_list.Nodes().begin();
|
||||
PAddr pg_phys_addr = pg_it->GetAddress();
|
||||
size_t pg_pages = pg_it->GetNumPages();
|
||||
|
||||
auto it = block_manager->FindIterator(cur_address);
|
||||
while (true) {
|
||||
// Check that the iterator is valid.
|
||||
ASSERT(it != block_manager->end());
|
||||
|
||||
// Get the memory info.
|
||||
const KMemoryInfo info = it->GetMemoryInfo();
|
||||
|
||||
// If it's unmapped, we need to map it.
|
||||
if (info.GetState() == KMemoryState::Free) {
|
||||
// Determine the range to map.
|
||||
size_t map_pages = std::min(VAddr(info.GetEndAddress()) - cur_address,
|
||||
last_address + 1 - cur_address) /
|
||||
PageSize;
|
||||
|
||||
// While we have pages to map, map them.
|
||||
while (map_pages > 0) {
|
||||
// Check if we're at the end of the physical block.
|
||||
if (pg_pages == 0) {
|
||||
// Ensure there are more pages to map.
|
||||
ASSERT(pg_it != page_linked_list.Nodes().end());
|
||||
|
||||
// Advance our physical block.
|
||||
++pg_it;
|
||||
pg_phys_addr = pg_it->GetAddress();
|
||||
pg_pages = pg_it->GetNumPages();
|
||||
}
|
||||
|
||||
// Map whatever we can.
|
||||
const size_t cur_pages = std::min(pg_pages, map_pages);
|
||||
R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::UserReadWrite,
|
||||
OperationType::Map, pg_phys_addr));
|
||||
|
||||
// Advance.
|
||||
cur_address += cur_pages * PageSize;
|
||||
map_pages -= cur_pages;
|
||||
|
||||
pg_phys_addr += cur_pages * PageSize;
|
||||
pg_pages -= cur_pages;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we're done.
|
||||
if (last_address <= info.GetLastAddress()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Advance.
|
||||
cur_address = info.GetEndAddress();
|
||||
++it;
|
||||
}
|
||||
|
||||
// We succeeded, so commit the memory reservation.
|
||||
memory_reservation.Commit();
|
||||
|
||||
// Increase our tracked mapped size.
|
||||
mapped_physical_memory_size += (size - mapped_size);
|
||||
|
||||
// Update the relevant memory blocks.
|
||||
block_manager->Update(address, size / PageSize, KMemoryState::Free,
|
||||
KMemoryPermission::None, KMemoryAttribute::None,
|
||||
KMemoryState::Normal, KMemoryPermission::UserReadWrite,
|
||||
KMemoryAttribute::None);
|
||||
|
||||
// Cancel our guard.
|
||||
unmap_guard.Cancel();
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ResultCode KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) {
|
||||
// Lock the physical memory lock.
|
||||
KScopedLightLock map_phys_mem_lk(map_physical_memory_lock);
|
||||
|
||||
// Lock the table.
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
// Calculate the last address for convenience.
|
||||
const VAddr last_address = address + size - 1;
|
||||
|
||||
// Define iteration variables.
|
||||
VAddr cur_address = 0;
|
||||
std::size_t mapped_size = 0;
|
||||
std::size_t num_allocator_blocks = 0;
|
||||
|
||||
// Check if the memory is mapped.
|
||||
{
|
||||
// Iterate over the memory.
|
||||
cur_address = address;
|
||||
mapped_size = 0;
|
||||
|
||||
auto it = block_manager->FindIterator(cur_address);
|
||||
while (true) {
|
||||
// Check that the iterator is valid.
|
||||
ASSERT(it != block_manager->end());
|
||||
|
||||
// Get the memory info.
|
||||
const KMemoryInfo info = it->GetMemoryInfo();
|
||||
|
||||
// Verify the memory's state.
|
||||
const bool is_normal = info.GetState() == KMemoryState::Normal &&
|
||||
info.GetAttribute() == KMemoryAttribute::None;
|
||||
const bool is_free = info.GetState() == KMemoryState::Free;
|
||||
R_UNLESS(is_normal || is_free, ResultInvalidCurrentMemory);
|
||||
|
||||
if (is_normal) {
|
||||
R_UNLESS(info.GetAttribute() == KMemoryAttribute::None, ResultInvalidCurrentMemory);
|
||||
|
||||
if (info.GetAddress() < address) {
|
||||
++num_allocator_blocks;
|
||||
}
|
||||
if (last_address < info.GetLastAddress()) {
|
||||
++num_allocator_blocks;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we're done.
|
||||
if (last_address <= info.GetLastAddress()) {
|
||||
if (is_normal) {
|
||||
mapped_size += (last_address + 1 - cur_address);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Track the memory if it's mapped.
|
||||
if (is_normal) {
|
||||
mapped_size += VAddr(info.GetEndAddress()) - cur_address;
|
||||
}
|
||||
|
||||
// Advance.
|
||||
cur_address = info.GetEndAddress();
|
||||
++it;
|
||||
}
|
||||
|
||||
// If there's nothing mapped, we've nothing to do.
|
||||
R_SUCCEED_IF(mapped_size == 0);
|
||||
}
|
||||
|
||||
// Make a page group for the unmap region.
|
||||
KPageLinkedList pg;
|
||||
{
|
||||
auto& impl = this->PageTableImpl();
|
||||
|
||||
// Begin traversal.
|
||||
Common::PageTable::TraversalContext context;
|
||||
Common::PageTable::TraversalEntry cur_entry = {.phys_addr = 0, .block_size = 0};
|
||||
bool cur_valid = false;
|
||||
Common::PageTable::TraversalEntry next_entry;
|
||||
bool next_valid = false;
|
||||
size_t tot_size = 0;
|
||||
|
||||
cur_address = address;
|
||||
next_valid = impl.BeginTraversal(next_entry, context, cur_address);
|
||||
next_entry.block_size =
|
||||
(next_entry.block_size - (next_entry.phys_addr & (next_entry.block_size - 1)));
|
||||
|
||||
// Iterate, building the group.
|
||||
while (true) {
|
||||
if ((!next_valid && !cur_valid) ||
|
||||
(next_valid && cur_valid &&
|
||||
next_entry.phys_addr == cur_entry.phys_addr + cur_entry.block_size)) {
|
||||
cur_entry.block_size += next_entry.block_size;
|
||||
} else {
|
||||
if (cur_valid) {
|
||||
// ASSERT(IsHeapPhysicalAddress(cur_entry.phys_addr));
|
||||
R_TRY(pg.AddBlock(cur_entry.phys_addr, cur_entry.block_size / PageSize));
|
||||
}
|
||||
|
||||
// Update tracking variables.
|
||||
tot_size += cur_entry.block_size;
|
||||
cur_entry = next_entry;
|
||||
cur_valid = next_valid;
|
||||
}
|
||||
|
||||
if (cur_entry.block_size + tot_size >= size) {
|
||||
break;
|
||||
}
|
||||
|
||||
next_valid = impl.ContinueTraversal(next_entry, context);
|
||||
}
|
||||
|
||||
// Add the last block.
|
||||
if (cur_valid) {
|
||||
// ASSERT(IsHeapPhysicalAddress(cur_entry.phys_addr));
|
||||
R_TRY(pg.AddBlock(cur_entry.phys_addr, (size - tot_size) / PageSize));
|
||||
}
|
||||
}
|
||||
ASSERT(pg.GetNumPages() == mapped_size / PageSize);
|
||||
|
||||
// Reset the current tracking address, and make sure we clean up on failure.
|
||||
cur_address = address;
|
||||
auto remap_guard = detail::ScopeExit([&] {
|
||||
if (cur_address > address) {
|
||||
const VAddr last_map_address = cur_address - 1;
|
||||
cur_address = address;
|
||||
|
||||
// Iterate over the memory we unmapped.
|
||||
auto it = block_manager->FindIterator(cur_address);
|
||||
auto pg_it = pg.Nodes().begin();
|
||||
PAddr pg_phys_addr = pg_it->GetAddress();
|
||||
size_t pg_pages = pg_it->GetNumPages();
|
||||
|
||||
while (true) {
|
||||
// Get the memory info for the pages we unmapped, convert to property.
|
||||
const KMemoryInfo info = it->GetMemoryInfo();
|
||||
|
||||
// If the memory is normal, we unmapped it and need to re-map it.
|
||||
if (info.GetState() == KMemoryState::Normal) {
|
||||
// Determine the range to map.
|
||||
size_t map_pages = std::min(VAddr(info.GetEndAddress()) - cur_address,
|
||||
last_map_address + 1 - cur_address) /
|
||||
PageSize;
|
||||
|
||||
// While we have pages to map, map them.
|
||||
while (map_pages > 0) {
|
||||
// Check if we're at the end of the physical block.
|
||||
if (pg_pages == 0) {
|
||||
// Ensure there are more pages to map.
|
||||
ASSERT(pg_it != pg.Nodes().end());
|
||||
|
||||
// Advance our physical block.
|
||||
++pg_it;
|
||||
pg_phys_addr = pg_it->GetAddress();
|
||||
pg_pages = pg_it->GetNumPages();
|
||||
}
|
||||
|
||||
// Map whatever we can.
|
||||
const size_t cur_pages = std::min(pg_pages, map_pages);
|
||||
ASSERT(this->Operate(cur_address, cur_pages, info.GetPermission(),
|
||||
OperationType::Map, pg_phys_addr) == ResultSuccess);
|
||||
|
||||
// Advance.
|
||||
cur_address += cur_pages * PageSize;
|
||||
map_pages -= cur_pages;
|
||||
|
||||
pg_phys_addr += cur_pages * PageSize;
|
||||
pg_pages -= cur_pages;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we're done.
|
||||
if (last_map_address <= info.GetLastAddress()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Advance.
|
||||
++it;
|
||||
}
|
||||
mapped_size += GetSizeInRange(info, addr, end_addr);
|
||||
} else if (info.state != KMemoryState::Free) {
|
||||
result = ResultInvalidCurrentMemory;
|
||||
}
|
||||
});
|
||||
|
||||
if (result.IsError()) {
|
||||
return result;
|
||||
// Iterate over the memory, unmapping as we go.
|
||||
auto it = block_manager->FindIterator(cur_address);
|
||||
while (true) {
|
||||
// Check that the iterator is valid.
|
||||
ASSERT(it != block_manager->end());
|
||||
|
||||
// Get the memory info.
|
||||
const KMemoryInfo info = it->GetMemoryInfo();
|
||||
|
||||
// If the memory state is normal, we need to unmap it.
|
||||
if (info.GetState() == KMemoryState::Normal) {
|
||||
// Determine the range to unmap.
|
||||
const size_t cur_pages = std::min(VAddr(info.GetEndAddress()) - cur_address,
|
||||
last_address + 1 - cur_address) /
|
||||
PageSize;
|
||||
|
||||
// Unmap.
|
||||
R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::None, OperationType::Unmap));
|
||||
}
|
||||
|
||||
// Check if we're done.
|
||||
if (last_address <= info.GetLastAddress()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Advance.
|
||||
cur_address = info.GetEndAddress();
|
||||
++it;
|
||||
}
|
||||
|
||||
if (!mapped_size) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
CASCADE_CODE(UnmapMemory(addr, size));
|
||||
|
||||
// Release the memory resource.
|
||||
mapped_physical_memory_size -= mapped_size;
|
||||
auto process{system.Kernel().CurrentProcess()};
|
||||
process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size);
|
||||
mapped_physical_memory_size -= mapped_size;
|
||||
|
||||
// Update memory blocks.
|
||||
system.Kernel().MemoryManager().Free(pg, size / PageSize, memory_pool, allocation_option);
|
||||
block_manager->Update(address, size / PageSize, KMemoryState::Free, KMemoryPermission::None,
|
||||
KMemoryAttribute::None);
|
||||
|
||||
// We succeeded.
|
||||
remap_guard.Cancel();
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::UnmapMemory(VAddr addr, std::size_t size) {
|
||||
std::lock_guard lock{page_table_lock};
|
||||
|
||||
const VAddr end_addr{addr + size};
|
||||
ResultCode result{ResultSuccess};
|
||||
KPageLinkedList page_linked_list;
|
||||
|
||||
// Unmap each region within the range
|
||||
block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) {
|
||||
if (info.state == KMemoryState::Normal) {
|
||||
const std::size_t block_size{GetSizeInRange(info, addr, end_addr)};
|
||||
const std::size_t block_num_pages{block_size / PageSize};
|
||||
const VAddr block_addr{GetAddressInRange(info, addr)};
|
||||
|
||||
AddRegionToPages(block_addr, block_size / PageSize, page_linked_list);
|
||||
|
||||
if (result = Operate(block_addr, block_num_pages, KMemoryPermission::None,
|
||||
OperationType::Unmap);
|
||||
result.IsError()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (result.IsError()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const std::size_t num_pages{size / PageSize};
|
||||
system.Kernel().MemoryManager().Free(page_linked_list, num_pages, memory_pool);
|
||||
|
||||
block_manager->Update(addr, num_pages, KMemoryState::Free);
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::Map(VAddr dst_addr, VAddr src_addr, std::size_t size) {
|
||||
std::lock_guard lock{page_table_lock};
|
||||
ResultCode KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
KMemoryState src_state{};
|
||||
CASCADE_CODE(CheckMemoryState(
|
||||
@@ -587,8 +890,8 @@ ResultCode KPageTable::Map(VAddr dst_addr, VAddr src_addr, std::size_t size) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size) {
|
||||
std::lock_guard lock{page_table_lock};
|
||||
ResultCode KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
KMemoryState src_state{};
|
||||
CASCADE_CODE(CheckMemoryState(
|
||||
@@ -631,6 +934,8 @@ ResultCode KPageTable::Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size) {
|
||||
|
||||
ResultCode KPageTable::MapPages(VAddr addr, const KPageLinkedList& page_linked_list,
|
||||
KMemoryPermission perm) {
|
||||
ASSERT(this->IsLockedByCurrentThread());
|
||||
|
||||
VAddr cur_addr{addr};
|
||||
|
||||
for (const auto& node : page_linked_list.Nodes()) {
|
||||
@@ -651,35 +956,38 @@ ResultCode KPageTable::MapPages(VAddr addr, const KPageLinkedList& page_linked_l
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state,
|
||||
KMemoryPermission perm) {
|
||||
std::lock_guard lock{page_table_lock};
|
||||
|
||||
ResultCode KPageTable::MapPages(VAddr address, KPageLinkedList& page_linked_list,
|
||||
KMemoryState state, KMemoryPermission perm) {
|
||||
// Check that the map is in range.
|
||||
const std::size_t num_pages{page_linked_list.GetNumPages()};
|
||||
const std::size_t size{num_pages * PageSize};
|
||||
R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory);
|
||||
|
||||
if (!CanContain(addr, size, state)) {
|
||||
return ResultInvalidCurrentMemory;
|
||||
}
|
||||
// Lock the table.
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
if (IsRegionMapped(addr, num_pages * PageSize)) {
|
||||
return ResultInvalidCurrentMemory;
|
||||
}
|
||||
// Check the memory state.
|
||||
R_TRY(this->CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free,
|
||||
KMemoryPermission::None, KMemoryPermission::None,
|
||||
KMemoryAttribute::None, KMemoryAttribute::None));
|
||||
|
||||
CASCADE_CODE(MapPages(addr, page_linked_list, perm));
|
||||
// Map the pages.
|
||||
R_TRY(MapPages(address, page_linked_list, perm));
|
||||
|
||||
block_manager->Update(addr, num_pages, state, perm);
|
||||
// Update the blocks.
|
||||
block_manager->Update(address, num_pages, state, perm);
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) {
|
||||
ASSERT(this->IsLockedByCurrentThread());
|
||||
|
||||
VAddr cur_addr{addr};
|
||||
|
||||
for (const auto& node : page_linked_list.Nodes()) {
|
||||
const std::size_t num_pages{(addr - cur_addr) / PageSize};
|
||||
if (const auto result{
|
||||
Operate(addr, num_pages, KMemoryPermission::None, OperationType::Unmap)};
|
||||
if (const auto result{Operate(cur_addr, node.GetNumPages(), KMemoryPermission::None,
|
||||
OperationType::Unmap)};
|
||||
result.IsError()) {
|
||||
return result;
|
||||
}
|
||||
@@ -692,21 +1000,23 @@ ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked
|
||||
|
||||
ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list,
|
||||
KMemoryState state) {
|
||||
std::lock_guard lock{page_table_lock};
|
||||
|
||||
// Check that the unmap is in range.
|
||||
const std::size_t num_pages{page_linked_list.GetNumPages()};
|
||||
const std::size_t size{num_pages * PageSize};
|
||||
R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
|
||||
|
||||
if (!CanContain(addr, size, state)) {
|
||||
return ResultInvalidCurrentMemory;
|
||||
}
|
||||
// Lock the table.
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
if (IsRegionMapped(addr, num_pages * PageSize)) {
|
||||
return ResultInvalidCurrentMemory;
|
||||
}
|
||||
// Check the memory state.
|
||||
R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, state, KMemoryPermission::None,
|
||||
KMemoryPermission::None, KMemoryAttribute::All,
|
||||
KMemoryAttribute::None));
|
||||
|
||||
CASCADE_CODE(UnmapPages(addr, page_linked_list));
|
||||
// Perform the unmap.
|
||||
R_TRY(UnmapPages(addr, page_linked_list));
|
||||
|
||||
// Update the blocks.
|
||||
block_manager->Update(addr, num_pages, state, KMemoryPermission::None);
|
||||
|
||||
return ResultSuccess;
|
||||
@@ -717,7 +1027,7 @@ ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
|
||||
const size_t num_pages = size / PageSize;
|
||||
|
||||
// Lock the table.
|
||||
std::lock_guard lock{page_table_lock};
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
// Verify we can change the memory permission.
|
||||
KMemoryState old_state;
|
||||
@@ -764,7 +1074,6 @@ ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
|
||||
|
||||
// Ensure cache coherency, if we're setting pages as executable.
|
||||
if (is_x) {
|
||||
// Memory execution state is changing, invalidate CPU cache range
|
||||
system.InvalidateCpuInstructionCacheRange(addr, size);
|
||||
}
|
||||
|
||||
@@ -772,7 +1081,7 @@ ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
|
||||
}
|
||||
|
||||
KMemoryInfo KPageTable::QueryInfoImpl(VAddr addr) {
|
||||
std::lock_guard lock{page_table_lock};
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
return block_manager->FindBlock(addr).GetMemoryInfo();
|
||||
}
|
||||
@@ -787,17 +1096,17 @@ KMemoryInfo KPageTable::QueryInfo(VAddr addr) {
|
||||
}
|
||||
|
||||
ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm) {
|
||||
std::lock_guard lock{page_table_lock};
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
KMemoryState state{};
|
||||
KMemoryAttribute attribute{};
|
||||
|
||||
CASCADE_CODE(CheckMemoryState(
|
||||
&state, nullptr, &attribute, nullptr, addr, size,
|
||||
KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
|
||||
KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, KMemoryPermission::All,
|
||||
KMemoryPermission::UserReadWrite, KMemoryAttribute::Mask, KMemoryAttribute::None,
|
||||
KMemoryAttribute::IpcAndDeviceMapped));
|
||||
R_TRY(CheckMemoryState(&state, nullptr, &attribute, nullptr, addr, size,
|
||||
KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
|
||||
KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
|
||||
KMemoryPermission::All, KMemoryPermission::UserReadWrite,
|
||||
KMemoryAttribute::Mask, KMemoryAttribute::None,
|
||||
KMemoryAttribute::IpcAndDeviceMapped));
|
||||
|
||||
block_manager->Update(addr, size / PageSize, state, perm, attribute | KMemoryAttribute::Locked);
|
||||
|
||||
@@ -805,16 +1114,15 @@ ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemo
|
||||
}
|
||||
|
||||
ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) {
|
||||
std::lock_guard lock{page_table_lock};
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
KMemoryState state{};
|
||||
|
||||
CASCADE_CODE(
|
||||
CheckMemoryState(&state, nullptr, nullptr, nullptr, addr, size,
|
||||
KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
|
||||
KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
|
||||
KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::Mask,
|
||||
KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
|
||||
R_TRY(CheckMemoryState(&state, nullptr, nullptr, nullptr, addr, size,
|
||||
KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
|
||||
KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted,
|
||||
KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::Mask,
|
||||
KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
|
||||
|
||||
block_manager->Update(addr, size / PageSize, state, KMemoryPermission::UserReadWrite);
|
||||
return ResultSuccess;
|
||||
@@ -825,7 +1133,7 @@ ResultCode KPageTable::SetMemoryPermission(VAddr addr, std::size_t size,
|
||||
const size_t num_pages = size / PageSize;
|
||||
|
||||
// Lock the table.
|
||||
std::lock_guard lock{page_table_lock};
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
// Verify we can change the memory permission.
|
||||
KMemoryState old_state;
|
||||
@@ -854,7 +1162,7 @@ ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask
|
||||
KMemoryAttribute::SetMask);
|
||||
|
||||
// Lock the table.
|
||||
std::lock_guard lock{page_table_lock};
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
// Verify we can change the memory attribute.
|
||||
KMemoryState old_state;
|
||||
@@ -870,8 +1178,9 @@ ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask
|
||||
AttributeTestMask, KMemoryAttribute::None, ~AttributeTestMask));
|
||||
|
||||
// Determine the new attribute.
|
||||
const auto new_attr = ((old_attr & static_cast<KMemoryAttribute>(~mask)) |
|
||||
static_cast<KMemoryAttribute>(attr & mask));
|
||||
const KMemoryAttribute new_attr =
|
||||
static_cast<KMemoryAttribute>(((old_attr & static_cast<KMemoryAttribute>(~mask)) |
|
||||
static_cast<KMemoryAttribute>(attr & mask)));
|
||||
|
||||
// Perform operation.
|
||||
this->Operate(addr, num_pages, old_perm, OperationType::ChangePermissionsAndRefresh);
|
||||
@@ -884,7 +1193,7 @@ ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask
|
||||
|
||||
ResultCode KPageTable::SetMaxHeapSize(std::size_t size) {
|
||||
// Lock the table.
|
||||
std::lock_guard lock{page_table_lock};
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
// Only process page tables are allowed to set heap size.
|
||||
ASSERT(!this->IsKernel());
|
||||
@@ -895,12 +1204,15 @@ ResultCode KPageTable::SetMaxHeapSize(std::size_t size) {
|
||||
}
|
||||
|
||||
ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
|
||||
// Lock the physical memory mutex.
|
||||
KScopedLightLock map_phys_mem_lk(map_physical_memory_lock);
|
||||
|
||||
// Try to perform a reduction in heap, instead of an extension.
|
||||
VAddr cur_address{};
|
||||
std::size_t allocation_size{};
|
||||
{
|
||||
// Lock the table.
|
||||
std::lock_guard lk(page_table_lock);
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
// Validate that setting heap size is possible at all.
|
||||
R_UNLESS(!is_kernel, ResultOutOfMemory);
|
||||
@@ -960,12 +1272,12 @@ ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
|
||||
// Allocate pages for the heap extension.
|
||||
KPageLinkedList page_linked_list;
|
||||
R_TRY(system.Kernel().MemoryManager().Allocate(page_linked_list, allocation_size / PageSize,
|
||||
memory_pool));
|
||||
memory_pool, allocation_option));
|
||||
|
||||
// Map the pages.
|
||||
{
|
||||
// Lock the table.
|
||||
std::lock_guard lk(page_table_lock);
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
// Ensure that the heap hasn't changed since we began executing.
|
||||
ASSERT(cur_address == current_heap_end);
|
||||
@@ -1007,7 +1319,7 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages,
|
||||
bool is_map_only, VAddr region_start,
|
||||
std::size_t region_num_pages, KMemoryState state,
|
||||
KMemoryPermission perm, PAddr map_addr) {
|
||||
std::lock_guard lock{page_table_lock};
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
if (!CanContain(region_start, region_num_pages * PageSize, state)) {
|
||||
return ResultInvalidCurrentMemory;
|
||||
@@ -1024,12 +1336,12 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages,
|
||||
}
|
||||
|
||||
if (is_map_only) {
|
||||
CASCADE_CODE(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr));
|
||||
R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr));
|
||||
} else {
|
||||
KPageLinkedList page_group;
|
||||
CASCADE_CODE(
|
||||
system.Kernel().MemoryManager().Allocate(page_group, needed_num_pages, memory_pool));
|
||||
CASCADE_CODE(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup));
|
||||
R_TRY(system.Kernel().MemoryManager().Allocate(page_group, needed_num_pages, memory_pool,
|
||||
allocation_option));
|
||||
R_TRY(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup));
|
||||
}
|
||||
|
||||
block_manager->Update(addr, needed_num_pages, state, perm);
|
||||
@@ -1038,7 +1350,7 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages,
|
||||
}
|
||||
|
||||
ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
|
||||
std::lock_guard lock{page_table_lock};
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
KMemoryPermission perm{};
|
||||
if (const ResultCode result{CheckMemoryState(
|
||||
@@ -1061,7 +1373,7 @@ ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
|
||||
}
|
||||
|
||||
ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) {
|
||||
std::lock_guard lock{page_table_lock};
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
KMemoryPermission perm{};
|
||||
if (const ResultCode result{CheckMemoryState(
|
||||
@@ -1084,7 +1396,7 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
|
||||
}
|
||||
|
||||
ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
|
||||
std::lock_guard lock{page_table_lock};
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite;
|
||||
|
||||
@@ -1111,7 +1423,7 @@ ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
|
||||
}
|
||||
|
||||
ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) {
|
||||
std::lock_guard lock{page_table_lock};
|
||||
KScopedLightLock lk(general_lock);
|
||||
|
||||
KMemoryPermission new_perm = KMemoryPermission::UserReadWrite;
|
||||
|
||||
@@ -1185,7 +1497,7 @@ VAddr KPageTable::AllocateVirtualMemory(VAddr start, std::size_t region_num_page
|
||||
|
||||
ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageLinkedList& page_group,
|
||||
OperationType operation) {
|
||||
std::lock_guard lock{page_table_lock};
|
||||
ASSERT(this->IsLockedByCurrentThread());
|
||||
|
||||
ASSERT(Common::IsAligned(addr, PageSize));
|
||||
ASSERT(num_pages > 0);
|
||||
@@ -1210,7 +1522,7 @@ ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageLin
|
||||
|
||||
ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm,
|
||||
OperationType operation, PAddr map_addr) {
|
||||
std::lock_guard lock{page_table_lock};
|
||||
ASSERT(this->IsLockedByCurrentThread());
|
||||
|
||||
ASSERT(num_pages > 0);
|
||||
ASSERT(Common::IsAligned(addr, PageSize));
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/page_table.h"
|
||||
#include "core/file_sys/program_metadata.h"
|
||||
#include "core/hle/kernel/k_light_lock.h"
|
||||
#include "core/hle/kernel/k_memory_block.h"
|
||||
#include "core/hle/kernel/k_memory_manager.h"
|
||||
#include "core/hle/result.h"
|
||||
@@ -22,9 +23,13 @@ namespace Kernel {
|
||||
|
||||
class KMemoryBlockManager;
|
||||
|
||||
class KPageTable final : NonCopyable {
|
||||
class KPageTable final {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(KPageTable);
|
||||
YUZU_NON_MOVEABLE(KPageTable);
|
||||
|
||||
explicit KPageTable(Core::System& system_);
|
||||
~KPageTable();
|
||||
|
||||
ResultCode InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr,
|
||||
VAddr code_addr, std::size_t code_size,
|
||||
@@ -37,9 +42,8 @@ public:
|
||||
VAddr src_addr);
|
||||
ResultCode MapPhysicalMemory(VAddr addr, std::size_t size);
|
||||
ResultCode UnmapPhysicalMemory(VAddr addr, std::size_t size);
|
||||
ResultCode UnmapMemory(VAddr addr, std::size_t size);
|
||||
ResultCode Map(VAddr dst_addr, VAddr src_addr, std::size_t size);
|
||||
ResultCode Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size);
|
||||
ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
|
||||
ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
|
||||
ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state,
|
||||
KMemoryPermission perm);
|
||||
ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state);
|
||||
@@ -88,7 +92,6 @@ private:
|
||||
ResultCode MapPages(VAddr addr, const KPageLinkedList& page_linked_list,
|
||||
KMemoryPermission perm);
|
||||
ResultCode UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list);
|
||||
void MapPhysicalMemory(KPageLinkedList& page_linked_list, VAddr start, VAddr end);
|
||||
bool IsRegionMapped(VAddr address, u64 size);
|
||||
bool IsRegionContiguous(VAddr addr, u64 size) const;
|
||||
void AddRegionToPages(VAddr start, std::size_t num_pages, KPageLinkedList& page_linked_list);
|
||||
@@ -144,10 +147,12 @@ private:
|
||||
}
|
||||
|
||||
bool IsLockedByCurrentThread() const {
|
||||
return true;
|
||||
return general_lock.IsLockedByCurrentThread();
|
||||
}
|
||||
|
||||
std::recursive_mutex page_table_lock;
|
||||
mutable KLightLock general_lock;
|
||||
mutable KLightLock map_physical_memory_lock;
|
||||
|
||||
std::unique_ptr<KMemoryBlockManager> block_manager;
|
||||
|
||||
public:
|
||||
@@ -206,7 +211,7 @@ public:
|
||||
return alias_code_region_end - alias_code_region_start;
|
||||
}
|
||||
size_t GetNormalMemorySize() {
|
||||
std::lock_guard lk(page_table_lock);
|
||||
KScopedLightLock lk(general_lock);
|
||||
return GetHeapSize() + mapped_physical_memory_size;
|
||||
}
|
||||
constexpr std::size_t GetAddressSpaceWidth() const {
|
||||
@@ -248,8 +253,12 @@ public:
|
||||
constexpr bool IsInsideASLRRegion(VAddr address, std::size_t size) const {
|
||||
return !IsOutsideASLRRegion(address, size);
|
||||
}
|
||||
constexpr PAddr GetPhysicalAddr(VAddr addr) {
|
||||
return page_table_impl.backing_addr[addr >> PageBits] + addr;
|
||||
|
||||
PAddr GetPhysicalAddr(VAddr addr) {
|
||||
ASSERT(IsLockedByCurrentThread());
|
||||
const auto backing_addr = page_table_impl.backing_addr[addr >> PageBits];
|
||||
ASSERT(backing_addr);
|
||||
return backing_addr + addr;
|
||||
}
|
||||
constexpr bool Contains(VAddr addr) const {
|
||||
return address_space_start <= addr && addr <= address_space_end - 1;
|
||||
@@ -303,6 +312,7 @@ private:
|
||||
bool is_aslr_enabled{};
|
||||
|
||||
KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application};
|
||||
KMemoryManager::Direction allocation_option{KMemoryManager::Direction::FromFront};
|
||||
|
||||
Common::PageTable page_table_impl;
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ concept KPriorityQueueMember = !std::is_reference_v<T> && requires(T & t) {
|
||||
|
||||
{ t.GetActiveCore() } -> Common::ConvertibleTo<s32>;
|
||||
{ t.GetPriority() } -> Common::ConvertibleTo<s32>;
|
||||
{ t.IsDummyThread() } -> Common::ConvertibleTo<bool>;
|
||||
};
|
||||
|
||||
template <typename Member, size_t NumCores_, int LowestPriority, int HighestPriority>
|
||||
@@ -257,7 +258,7 @@ private:
|
||||
|
||||
private:
|
||||
constexpr void ClearAffinityBit(u64& affinity, s32 core) {
|
||||
affinity &= ~(u64(1) << core);
|
||||
affinity &= ~(UINT64_C(1) << core);
|
||||
}
|
||||
|
||||
constexpr s32 GetNextCore(u64& affinity) {
|
||||
@@ -349,24 +350,49 @@ public:
|
||||
|
||||
// Mutators.
|
||||
constexpr void PushBack(Member* member) {
|
||||
// This is for host (dummy) threads that we do not want to enter the priority queue.
|
||||
if (member->IsDummyThread()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->PushBack(member->GetPriority(), member);
|
||||
}
|
||||
|
||||
constexpr void Remove(Member* member) {
|
||||
// This is for host (dummy) threads that we do not want to enter the priority queue.
|
||||
if (member->IsDummyThread()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->Remove(member->GetPriority(), member);
|
||||
}
|
||||
|
||||
constexpr void MoveToScheduledFront(Member* member) {
|
||||
// This is for host (dummy) threads that we do not want to enter the priority queue.
|
||||
if (member->IsDummyThread()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->scheduled_queue.MoveToFront(member->GetPriority(), member->GetActiveCore(), member);
|
||||
}
|
||||
|
||||
constexpr KThread* MoveToScheduledBack(Member* member) {
|
||||
// This is for host (dummy) threads that we do not want to enter the priority queue.
|
||||
if (member->IsDummyThread()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return this->scheduled_queue.MoveToBack(member->GetPriority(), member->GetActiveCore(),
|
||||
member);
|
||||
}
|
||||
|
||||
// First class fancy operations.
|
||||
constexpr void ChangePriority(s32 prev_priority, bool is_running, Member* member) {
|
||||
// This is for host (dummy) threads that we do not want to enter the priority queue.
|
||||
if (member->IsDummyThread()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(IsValidPriority(prev_priority));
|
||||
|
||||
// Remove the member from the queues.
|
||||
@@ -383,6 +409,11 @@ public:
|
||||
|
||||
constexpr void ChangeAffinityMask(s32 prev_core, const AffinityMaskType& prev_affinity,
|
||||
Member* member) {
|
||||
// This is for host (dummy) threads that we do not want to enter the priority queue.
|
||||
if (member->IsDummyThread()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the new information.
|
||||
const s32 priority = member->GetPriority();
|
||||
const AffinityMaskType& new_affinity = member->GetAffinityMask();
|
||||
@@ -412,6 +443,11 @@ public:
|
||||
}
|
||||
|
||||
constexpr void ChangeCore(s32 prev_core, Member* member, bool to_front = false) {
|
||||
// This is for host (dummy) threads that we do not want to enter the priority queue.
|
||||
if (member->IsDummyThread()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the new information.
|
||||
const s32 new_core = member->GetActiveCore();
|
||||
const s32 priority = member->GetPriority();
|
||||
|
||||
@@ -146,27 +146,35 @@ ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::st
|
||||
// Open a reference to the resource limit.
|
||||
process->resource_limit->Open();
|
||||
|
||||
// Clear remaining fields.
|
||||
process->num_running_threads = 0;
|
||||
process->is_signaled = false;
|
||||
process->exception_thread = nullptr;
|
||||
process->is_suspended = false;
|
||||
process->schedule_count = 0;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void KProcess::DoWorkerTaskImpl() {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
KResourceLimit* KProcess::GetResourceLimit() const {
|
||||
return resource_limit;
|
||||
}
|
||||
|
||||
void KProcess::IncrementThreadCount() {
|
||||
ASSERT(num_threads >= 0);
|
||||
num_created_threads++;
|
||||
|
||||
if (const auto count = ++num_threads; count > peak_num_threads) {
|
||||
peak_num_threads = count;
|
||||
}
|
||||
void KProcess::IncrementRunningThreadCount() {
|
||||
ASSERT(num_running_threads.load() >= 0);
|
||||
++num_running_threads;
|
||||
}
|
||||
|
||||
void KProcess::DecrementThreadCount() {
|
||||
ASSERT(num_threads > 0);
|
||||
void KProcess::DecrementRunningThreadCount() {
|
||||
ASSERT(num_running_threads.load() > 0);
|
||||
|
||||
if (const auto count = --num_threads; count == 0) {
|
||||
LOG_WARNING(Kernel, "Process termination is not fully implemented.");
|
||||
if (const auto prev = num_running_threads--; prev == 1) {
|
||||
// TODO(bunnei): Process termination to be implemented when multiprocess is supported.
|
||||
UNIMPLEMENTED_MSG("KProcess termination is not implemennted!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -477,7 +485,7 @@ void KProcess::Finalize() {
|
||||
}
|
||||
|
||||
// Perform inherited finalization.
|
||||
KAutoObjectWithSlabHeapAndContainer<KProcess, KSynchronizationObject>::Finalize();
|
||||
KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask>::Finalize();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "core/hle/kernel/k_condition_variable.h"
|
||||
#include "core/hle/kernel/k_handle_table.h"
|
||||
#include "core/hle/kernel/k_synchronization_object.h"
|
||||
#include "core/hle/kernel/k_worker_task.h"
|
||||
#include "core/hle/kernel/process_capability.h"
|
||||
#include "core/hle/kernel/slab_helpers.h"
|
||||
#include "core/hle/result.h"
|
||||
@@ -62,8 +63,7 @@ enum class ProcessStatus {
|
||||
DebugBreak,
|
||||
};
|
||||
|
||||
class KProcess final
|
||||
: public KAutoObjectWithSlabHeapAndContainer<KProcess, KSynchronizationObject> {
|
||||
class KProcess final : public KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask> {
|
||||
KERNEL_AUTOOBJECT_TRAITS(KProcess, KSynchronizationObject);
|
||||
|
||||
public:
|
||||
@@ -235,8 +235,8 @@ public:
|
||||
++schedule_count;
|
||||
}
|
||||
|
||||
void IncrementThreadCount();
|
||||
void DecrementThreadCount();
|
||||
void IncrementRunningThreadCount();
|
||||
void DecrementRunningThreadCount();
|
||||
|
||||
void SetRunningThread(s32 core, KThread* thread, u64 idle_count) {
|
||||
running_threads[core] = thread;
|
||||
@@ -345,6 +345,8 @@ public:
|
||||
|
||||
bool IsSignaled() const override;
|
||||
|
||||
void DoWorkerTaskImpl();
|
||||
|
||||
void PinCurrentThread(s32 core_id);
|
||||
void UnpinCurrentThread(s32 core_id);
|
||||
void UnpinThread(KThread* thread);
|
||||
@@ -471,9 +473,7 @@ private:
|
||||
bool is_suspended{};
|
||||
bool is_initialized{};
|
||||
|
||||
std::atomic<s32> num_created_threads{};
|
||||
std::atomic<u16> num_threads{};
|
||||
u16 peak_num_threads{};
|
||||
std::atomic<u16> num_running_threads{};
|
||||
|
||||
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> running_threads{};
|
||||
std::array<u64, Core::Hardware::NUM_CPU_CORES> running_thread_idle_counts{};
|
||||
|
||||
@@ -49,8 +49,6 @@ void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedul
|
||||
if (!must_context_switch || core != current_core) {
|
||||
auto& phys_core = kernel.PhysicalCore(core);
|
||||
phys_core.Interrupt();
|
||||
} else {
|
||||
must_context_switch = true;
|
||||
}
|
||||
cores_pending_reschedule &= ~(1ULL << core);
|
||||
}
|
||||
@@ -408,6 +406,9 @@ void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduli
|
||||
} else {
|
||||
RescheduleCores(kernel, cores_needing_scheduling);
|
||||
}
|
||||
|
||||
// Special case to ensure dummy threads that are waiting block.
|
||||
current_thread->IfDummyThreadTryWait();
|
||||
}
|
||||
|
||||
u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) {
|
||||
@@ -709,23 +710,19 @@ void KScheduler::Unload(KThread* thread) {
|
||||
}
|
||||
|
||||
void KScheduler::Reload(KThread* thread) {
|
||||
LOG_TRACE(Kernel, "core {}, reload thread {}", core_id, thread ? thread->GetName() : "nullptr");
|
||||
LOG_TRACE(Kernel, "core {}, reload thread {}", core_id, thread->GetName());
|
||||
|
||||
if (thread) {
|
||||
ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable.");
|
||||
|
||||
Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
|
||||
cpu_core.LoadContext(thread->GetContext32());
|
||||
cpu_core.LoadContext(thread->GetContext64());
|
||||
cpu_core.SetTlsAddress(thread->GetTLSAddress());
|
||||
cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0());
|
||||
cpu_core.ClearExclusiveState();
|
||||
}
|
||||
Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
|
||||
cpu_core.LoadContext(thread->GetContext32());
|
||||
cpu_core.LoadContext(thread->GetContext64());
|
||||
cpu_core.SetTlsAddress(thread->GetTLSAddress());
|
||||
cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0());
|
||||
cpu_core.ClearExclusiveState();
|
||||
}
|
||||
|
||||
void KScheduler::SwitchContextStep2() {
|
||||
// Load context of new thread
|
||||
Reload(current_thread.load());
|
||||
Reload(GetCurrentThread());
|
||||
|
||||
RescheduleCurrentCore();
|
||||
}
|
||||
@@ -734,13 +731,23 @@ void KScheduler::ScheduleImpl() {
|
||||
KThread* previous_thread = GetCurrentThread();
|
||||
KThread* next_thread = state.highest_priority_thread;
|
||||
|
||||
state.needs_scheduling = false;
|
||||
state.needs_scheduling.store(false);
|
||||
|
||||
// We never want to schedule a null thread, so use the idle thread if we don't have a next.
|
||||
if (next_thread == nullptr) {
|
||||
next_thread = idle_thread;
|
||||
}
|
||||
|
||||
if (next_thread->GetCurrentCore() != core_id) {
|
||||
next_thread->SetCurrentCore(core_id);
|
||||
}
|
||||
|
||||
// We never want to schedule a dummy thread, as these are only used by host threads for locking.
|
||||
if (next_thread->GetThreadType() == ThreadType::Dummy) {
|
||||
ASSERT_MSG(false, "Dummy threads should never be scheduled!");
|
||||
next_thread = idle_thread;
|
||||
}
|
||||
|
||||
// If we're not actually switching thread, there's nothing to do.
|
||||
if (next_thread == current_thread.load()) {
|
||||
previous_thread->EnableDispatch();
|
||||
@@ -748,14 +755,8 @@ void KScheduler::ScheduleImpl() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (next_thread->GetCurrentCore() != core_id) {
|
||||
next_thread->SetCurrentCore(core_id);
|
||||
}
|
||||
|
||||
current_thread.store(next_thread);
|
||||
|
||||
// Update the CPU time tracking variables.
|
||||
KProcess* const previous_process = system.Kernel().CurrentProcess();
|
||||
|
||||
UpdateLastContextSwitchTime(previous_thread, previous_process);
|
||||
|
||||
// Save context for previous thread
|
||||
@@ -763,6 +764,10 @@ void KScheduler::ScheduleImpl() {
|
||||
|
||||
std::shared_ptr<Common::Fiber>* old_context;
|
||||
old_context = &previous_thread->GetHostContext();
|
||||
|
||||
// Set the new thread.
|
||||
current_thread.store(next_thread);
|
||||
|
||||
guard.Unlock();
|
||||
|
||||
Common::Fiber::YieldTo(*old_context, *switch_fiber);
|
||||
@@ -790,8 +795,8 @@ void KScheduler::SwitchToCurrent() {
|
||||
do {
|
||||
auto next_thread = current_thread.load();
|
||||
if (next_thread != nullptr) {
|
||||
next_thread->context_guard.Lock();
|
||||
if (next_thread->GetRawState() != ThreadState::Runnable) {
|
||||
const auto locked = next_thread->context_guard.TryLock();
|
||||
if (state.needs_scheduling.load()) {
|
||||
next_thread->context_guard.Unlock();
|
||||
break;
|
||||
}
|
||||
@@ -799,6 +804,9 @@ void KScheduler::SwitchToCurrent() {
|
||||
next_thread->context_guard.Unlock();
|
||||
break;
|
||||
}
|
||||
if (!locked) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
auto thread = next_thread ? next_thread : idle_thread;
|
||||
Common::Fiber::YieldTo(switch_fiber, *thread->GetHostContext());
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
@@ -123,20 +122,10 @@ ResultCode KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memor
|
||||
|
||||
context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
|
||||
|
||||
// In the event that something fails here, stub a result to prevent the game from crashing.
|
||||
// This is a work-around in the event that somehow we process a service request after the
|
||||
// session has been closed by the game. This has been observed to happen rarely in Pokemon
|
||||
// Sword/Shield and is likely a result of us using host threads/scheduling for services.
|
||||
// TODO(bunnei): Find a better solution here.
|
||||
auto error_guard = SCOPE_GUARD({ CompleteSyncRequest(*context); });
|
||||
|
||||
// Ensure we have a session request handler
|
||||
if (manager->HasSessionRequestHandler(*context)) {
|
||||
if (auto strong_ptr = manager->GetServiceThread().lock()) {
|
||||
strong_ptr->QueueSyncRequest(*parent, std::move(context));
|
||||
|
||||
// We succeeded.
|
||||
error_guard.Cancel();
|
||||
} else {
|
||||
ASSERT_MSG(false, "strong_ptr is nullptr!");
|
||||
}
|
||||
@@ -171,13 +160,8 @@ ResultCode KServerSession::CompleteSyncRequest(HLERequestContext& context) {
|
||||
convert_to_domain = false;
|
||||
}
|
||||
|
||||
// Some service requests require the thread to block
|
||||
{
|
||||
KScopedSchedulerLock lock(kernel);
|
||||
if (!context.IsThreadWaiting()) {
|
||||
context.GetThread().EndWait(result);
|
||||
}
|
||||
}
|
||||
// The calling thread is waiting for this request to complete, so wake it up.
|
||||
context.GetThread().EndWait(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <atomic>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Kernel {
|
||||
@@ -15,13 +16,17 @@ class KernelCore;
|
||||
|
||||
namespace impl {
|
||||
|
||||
class KSlabHeapImpl final : NonCopyable {
|
||||
class KSlabHeapImpl final {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(KSlabHeapImpl);
|
||||
YUZU_NON_MOVEABLE(KSlabHeapImpl);
|
||||
|
||||
struct Node {
|
||||
Node* next{};
|
||||
};
|
||||
|
||||
constexpr KSlabHeapImpl() = default;
|
||||
constexpr ~KSlabHeapImpl() = default;
|
||||
|
||||
void Initialize(std::size_t size) {
|
||||
ASSERT(head == nullptr);
|
||||
@@ -64,9 +69,13 @@ private:
|
||||
|
||||
} // namespace impl
|
||||
|
||||
class KSlabHeapBase : NonCopyable {
|
||||
class KSlabHeapBase {
|
||||
public:
|
||||
YUZU_NON_COPYABLE(KSlabHeapBase);
|
||||
YUZU_NON_MOVEABLE(KSlabHeapBase);
|
||||
|
||||
constexpr KSlabHeapBase() = default;
|
||||
constexpr ~KSlabHeapBase() = default;
|
||||
|
||||
constexpr bool Contains(uintptr_t addr) const {
|
||||
return start <= addr && addr < end;
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "core/hle/kernel/k_system_control.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/k_thread_queue.h"
|
||||
#include "core/hle/kernel/k_worker_task_manager.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
#include "core/hle/kernel/time_manager.h"
|
||||
@@ -105,7 +106,7 @@ KThread::~KThread() = default;
|
||||
ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top, s32 prio,
|
||||
s32 virt_core, KProcess* owner, ThreadType type) {
|
||||
// Assert parameters are valid.
|
||||
ASSERT((type == ThreadType::Main) ||
|
||||
ASSERT((type == ThreadType::Main) || (type == ThreadType::Dummy) ||
|
||||
(Svc::HighestThreadPriority <= prio && prio <= Svc::LowestThreadPriority));
|
||||
ASSERT((owner != nullptr) || (type != ThreadType::User));
|
||||
ASSERT(0 <= virt_core && virt_core < static_cast<s32>(Common::BitSize<u64>()));
|
||||
@@ -139,7 +140,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
|
||||
UNREACHABLE_MSG("KThread::Initialize: Unknown ThreadType {}", static_cast<u32>(type));
|
||||
break;
|
||||
}
|
||||
thread_type_for_debugging = type;
|
||||
thread_type = type;
|
||||
|
||||
// Set the ideal core ID and affinity mask.
|
||||
virtual_ideal_core_id = virt_core;
|
||||
@@ -214,7 +215,6 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
|
||||
|
||||
parent = owner;
|
||||
parent->Open();
|
||||
parent->IncrementThreadCount();
|
||||
}
|
||||
|
||||
// Initialize thread context.
|
||||
@@ -261,7 +261,7 @@ ResultCode KThread::InitializeThread(KThread* thread, KThreadFunction func, uint
|
||||
}
|
||||
|
||||
ResultCode KThread::InitializeDummyThread(KThread* thread) {
|
||||
return thread->Initialize({}, {}, {}, DefaultThreadPriority, 3, {}, ThreadType::Dummy);
|
||||
return thread->Initialize({}, {}, {}, DummyThreadPriority, 3, {}, ThreadType::Dummy);
|
||||
}
|
||||
|
||||
ResultCode KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) {
|
||||
@@ -326,13 +326,8 @@ void KThread::Finalize() {
|
||||
}
|
||||
}
|
||||
|
||||
// Decrement the parent process's thread count.
|
||||
if (parent != nullptr) {
|
||||
parent->DecrementThreadCount();
|
||||
}
|
||||
|
||||
// Perform inherited finalization.
|
||||
KAutoObjectWithSlabHeapAndContainer<KThread, KSynchronizationObject>::Finalize();
|
||||
KSynchronizationObject::Finalize();
|
||||
}
|
||||
|
||||
bool KThread::IsSignaled() const {
|
||||
@@ -376,11 +371,28 @@ void KThread::StartTermination() {
|
||||
|
||||
// Register terminated dpc flag.
|
||||
RegisterDpc(DpcFlag::Terminated);
|
||||
}
|
||||
|
||||
void KThread::FinishTermination() {
|
||||
// Ensure that the thread is not executing on any core.
|
||||
if (parent != nullptr) {
|
||||
for (std::size_t i = 0; i < static_cast<std::size_t>(Core::Hardware::NUM_CPU_CORES); ++i) {
|
||||
KThread* core_thread{};
|
||||
do {
|
||||
core_thread = kernel.Scheduler(i).GetCurrentThread();
|
||||
} while (core_thread == this);
|
||||
}
|
||||
}
|
||||
|
||||
// Close the thread.
|
||||
this->Close();
|
||||
}
|
||||
|
||||
void KThread::DoWorkerTaskImpl() {
|
||||
// Finish the termination that was begun by Exit().
|
||||
this->FinishTermination();
|
||||
}
|
||||
|
||||
void KThread::Pin(s32 current_core) {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
|
||||
@@ -417,12 +429,7 @@ void KThread::Pin(s32 current_core) {
|
||||
static_cast<u32>(ThreadState::SuspendShift)));
|
||||
|
||||
// Update our state.
|
||||
const ThreadState old_state = thread_state;
|
||||
thread_state = static_cast<ThreadState>(GetSuspendFlags() |
|
||||
static_cast<u32>(old_state & ThreadState::Mask));
|
||||
if (thread_state != old_state) {
|
||||
KScheduler::OnThreadStateChanged(kernel, this, old_state);
|
||||
}
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
// TODO(bunnei): Update our SVC access permissions.
|
||||
@@ -463,20 +470,13 @@ void KThread::Unpin() {
|
||||
}
|
||||
|
||||
// Allow performing thread suspension (if termination hasn't been requested).
|
||||
{
|
||||
if (!IsTerminationRequested()) {
|
||||
// Update our allow flags.
|
||||
if (!IsTerminationRequested()) {
|
||||
suspend_allowed_flags |= (1 << (static_cast<u32>(SuspendType::Thread) +
|
||||
static_cast<u32>(ThreadState::SuspendShift)));
|
||||
}
|
||||
suspend_allowed_flags |= (1 << (static_cast<u32>(SuspendType::Thread) +
|
||||
static_cast<u32>(ThreadState::SuspendShift)));
|
||||
|
||||
// Update our state.
|
||||
const ThreadState old_state = thread_state;
|
||||
thread_state = static_cast<ThreadState>(GetSuspendFlags() |
|
||||
static_cast<u32>(old_state & ThreadState::Mask));
|
||||
if (thread_state != old_state) {
|
||||
KScheduler::OnThreadStateChanged(kernel, this, old_state);
|
||||
}
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
// TODO(bunnei): Update our SVC access permissions.
|
||||
@@ -689,12 +689,7 @@ void KThread::Resume(SuspendType type) {
|
||||
~(1u << (static_cast<u32>(ThreadState::SuspendShift) + static_cast<u32>(type)));
|
||||
|
||||
// Update our state.
|
||||
const ThreadState old_state = thread_state;
|
||||
thread_state = static_cast<ThreadState>(GetSuspendFlags() |
|
||||
static_cast<u32>(old_state & ThreadState::Mask));
|
||||
if (thread_state != old_state) {
|
||||
KScheduler::OnThreadStateChanged(kernel, this, old_state);
|
||||
}
|
||||
this->UpdateState();
|
||||
}
|
||||
|
||||
void KThread::WaitCancel() {
|
||||
@@ -721,19 +716,22 @@ void KThread::TrySuspend() {
|
||||
ASSERT(GetNumKernelWaiters() == 0);
|
||||
|
||||
// Perform the suspend.
|
||||
Suspend();
|
||||
this->UpdateState();
|
||||
}
|
||||
|
||||
void KThread::Suspend() {
|
||||
void KThread::UpdateState() {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
ASSERT(IsSuspendRequested());
|
||||
|
||||
// Set our suspend flags in state.
|
||||
const auto old_state = thread_state;
|
||||
thread_state = static_cast<ThreadState>(GetSuspendFlags()) | (old_state & ThreadState::Mask);
|
||||
const auto new_state =
|
||||
static_cast<ThreadState>(this->GetSuspendFlags()) | (old_state & ThreadState::Mask);
|
||||
thread_state = new_state;
|
||||
|
||||
// Note the state change in scheduler.
|
||||
KScheduler::OnThreadStateChanged(kernel, this, old_state);
|
||||
if (new_state != old_state) {
|
||||
KScheduler::OnThreadStateChanged(kernel, this, old_state);
|
||||
}
|
||||
}
|
||||
|
||||
void KThread::Continue() {
|
||||
@@ -998,13 +996,16 @@ ResultCode KThread::Run() {
|
||||
|
||||
// If the current thread has been asked to suspend, suspend it and retry.
|
||||
if (GetCurrentThread(kernel).IsSuspended()) {
|
||||
GetCurrentThread(kernel).Suspend();
|
||||
GetCurrentThread(kernel).UpdateState();
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we're not a kernel thread and we've been asked to suspend, suspend ourselves.
|
||||
if (IsUserThread() && IsSuspended()) {
|
||||
Suspend();
|
||||
if (KProcess* owner = this->GetOwnerProcess(); owner != nullptr) {
|
||||
if (IsUserThread() && IsSuspended()) {
|
||||
this->UpdateState();
|
||||
}
|
||||
owner->IncrementRunningThreadCount();
|
||||
}
|
||||
|
||||
// Set our state and finish.
|
||||
@@ -1019,21 +1020,29 @@ ResultCode KThread::Run() {
|
||||
void KThread::Exit() {
|
||||
ASSERT(this == GetCurrentThreadPointer(kernel));
|
||||
|
||||
// Release the thread resource hint from parent.
|
||||
// Release the thread resource hint, running thread count from parent.
|
||||
if (parent != nullptr) {
|
||||
parent->GetResourceLimit()->Release(Kernel::LimitableResource::Threads, 0, 1);
|
||||
resource_limit_release_hint = true;
|
||||
parent->DecrementRunningThreadCount();
|
||||
}
|
||||
|
||||
// Perform termination.
|
||||
{
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
// Disallow all suspension.
|
||||
suspend_allowed_flags = 0;
|
||||
this->UpdateState();
|
||||
|
||||
// Disallow all suspension.
|
||||
suspend_allowed_flags = 0;
|
||||
|
||||
// Start termination.
|
||||
StartTermination();
|
||||
|
||||
// Register the thread as a work task.
|
||||
KWorkerTaskManager::AddTask(kernel, KWorkerTaskManager::WorkerType::Exit, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1061,12 +1070,46 @@ ResultCode KThread::Sleep(s64 timeout) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void KThread::IfDummyThreadTryWait() {
|
||||
if (!IsDummyThread()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetState() != ThreadState::Waiting) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Block until we can grab the lock.
|
||||
KScopedSpinLock lk{dummy_wait_lock};
|
||||
}
|
||||
|
||||
void KThread::IfDummyThreadBeginWait() {
|
||||
if (!IsDummyThread()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure the thread will block when IfDummyThreadTryWait is called.
|
||||
dummy_wait_lock.Lock();
|
||||
}
|
||||
|
||||
void KThread::IfDummyThreadEndWait() {
|
||||
if (!IsDummyThread()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure the thread will no longer block.
|
||||
dummy_wait_lock.Unlock();
|
||||
}
|
||||
|
||||
void KThread::BeginWait(KThreadQueue* queue) {
|
||||
// Set our state as waiting.
|
||||
SetState(ThreadState::Waiting);
|
||||
|
||||
// Set our wait queue.
|
||||
wait_queue = queue;
|
||||
|
||||
// Special case for dummy threads to ensure they block.
|
||||
IfDummyThreadBeginWait();
|
||||
}
|
||||
|
||||
void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_) {
|
||||
@@ -1085,7 +1128,16 @@ void KThread::EndWait(ResultCode wait_result_) {
|
||||
|
||||
// If we're waiting, notify our queue that we're available.
|
||||
if (GetState() == ThreadState::Waiting) {
|
||||
if (wait_queue == nullptr) {
|
||||
// This should never happen, but avoid a hard crash below to get this logged.
|
||||
ASSERT_MSG(false, "wait_queue is nullptr!");
|
||||
return;
|
||||
}
|
||||
|
||||
wait_queue->EndWait(this, wait_result_);
|
||||
|
||||
// Special case for dummy threads to wakeup if necessary.
|
||||
IfDummyThreadEndWait();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "core/hle/kernel/k_light_lock.h"
|
||||
#include "core/hle/kernel/k_spin_lock.h"
|
||||
#include "core/hle/kernel/k_synchronization_object.h"
|
||||
#include "core/hle/kernel/k_worker_task.h"
|
||||
#include "core/hle/kernel/slab_helpers.h"
|
||||
#include "core/hle/kernel/svc_common.h"
|
||||
#include "core/hle/kernel/svc_types.h"
|
||||
@@ -100,7 +101,7 @@ enum class ThreadWaitReasonForDebugging : u32 {
|
||||
[[nodiscard]] KThread& GetCurrentThread(KernelCore& kernel);
|
||||
[[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel);
|
||||
|
||||
class KThread final : public KAutoObjectWithSlabHeapAndContainer<KThread, KSynchronizationObject>,
|
||||
class KThread final : public KAutoObjectWithSlabHeapAndContainer<KThread, KWorkerTask>,
|
||||
public boost::intrusive::list_base_hook<> {
|
||||
KERNEL_AUTOOBJECT_TRAITS(KThread, KSynchronizationObject);
|
||||
|
||||
@@ -111,6 +112,7 @@ private:
|
||||
public:
|
||||
static constexpr s32 DefaultThreadPriority = 44;
|
||||
static constexpr s32 IdleThreadPriority = Svc::LowestThreadPriority + 1;
|
||||
static constexpr s32 DummyThreadPriority = Svc::LowestThreadPriority + 2;
|
||||
|
||||
explicit KThread(KernelCore& kernel_);
|
||||
~KThread() override;
|
||||
@@ -192,9 +194,9 @@ public:
|
||||
|
||||
void TrySuspend();
|
||||
|
||||
void Continue();
|
||||
void UpdateState();
|
||||
|
||||
void Suspend();
|
||||
void Continue();
|
||||
|
||||
constexpr void SetSyncedIndex(s32 index) {
|
||||
synced_index = index;
|
||||
@@ -385,6 +387,8 @@ public:
|
||||
|
||||
void OnTimer();
|
||||
|
||||
void DoWorkerTaskImpl();
|
||||
|
||||
static void PostDestroy(uintptr_t arg);
|
||||
|
||||
[[nodiscard]] static ResultCode InitializeDummyThread(KThread* thread);
|
||||
@@ -550,8 +554,12 @@ public:
|
||||
return wait_reason_for_debugging;
|
||||
}
|
||||
|
||||
[[nodiscard]] ThreadType GetThreadTypeForDebugging() const {
|
||||
return thread_type_for_debugging;
|
||||
[[nodiscard]] ThreadType GetThreadType() const {
|
||||
return thread_type;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsDummyThread() const {
|
||||
return GetThreadType() == ThreadType::Dummy;
|
||||
}
|
||||
|
||||
void SetWaitObjectsForDebugging(const std::span<KSynchronizationObject*>& objects) {
|
||||
@@ -628,6 +636,14 @@ public:
|
||||
return condvar_key;
|
||||
}
|
||||
|
||||
// Dummy threads (used for HLE host threads) cannot wait based on the guest scheduler, and
|
||||
// therefore will not block on guest kernel synchronization primitives. These methods handle
|
||||
// blocking as needed.
|
||||
|
||||
void IfDummyThreadTryWait();
|
||||
void IfDummyThreadBeginWait();
|
||||
void IfDummyThreadEndWait();
|
||||
|
||||
private:
|
||||
static constexpr size_t PriorityInheritanceCountMax = 10;
|
||||
union SyncObjectBuffer {
|
||||
@@ -679,6 +695,8 @@ private:
|
||||
|
||||
void StartTermination();
|
||||
|
||||
void FinishTermination();
|
||||
|
||||
[[nodiscard]] ResultCode Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top,
|
||||
s32 prio, s32 virt_core, KProcess* owner, ThreadType type);
|
||||
|
||||
@@ -744,16 +762,17 @@ private:
|
||||
bool resource_limit_release_hint{};
|
||||
StackParameters stack_parameters{};
|
||||
KSpinLock context_guard{};
|
||||
KSpinLock dummy_wait_lock{};
|
||||
|
||||
// For emulation
|
||||
std::shared_ptr<Common::Fiber> host_context{};
|
||||
bool is_single_core{};
|
||||
ThreadType thread_type{};
|
||||
|
||||
// For debugging
|
||||
std::vector<KSynchronizationObject*> wait_objects_for_debugging;
|
||||
VAddr mutex_wait_address_for_debugging{};
|
||||
ThreadWaitReasonForDebugging wait_reason_for_debugging{};
|
||||
ThreadType thread_type_for_debugging{};
|
||||
|
||||
public:
|
||||
using ConditionVariableThreadTreeType = ConditionVariableThreadTree;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright 2022 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/kernel/k_synchronization_object.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KWorkerTask : public KSynchronizationObject {
|
||||
public:
|
||||
explicit KWorkerTask(KernelCore& kernel_);
|
||||
|
||||
void DoWorkerTask();
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright 2022 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/k_worker_task.h"
|
||||
#include "core/hle/kernel/k_worker_task_manager.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
KWorkerTask::KWorkerTask(KernelCore& kernel_) : KSynchronizationObject{kernel_} {}
|
||||
|
||||
void KWorkerTask::DoWorkerTask() {
|
||||
if (auto* const thread = this->DynamicCast<KThread*>(); thread != nullptr) {
|
||||
return thread->DoWorkerTaskImpl();
|
||||
} else {
|
||||
auto* const process = this->DynamicCast<KProcess*>();
|
||||
ASSERT(process != nullptr);
|
||||
|
||||
return process->DoWorkerTaskImpl();
|
||||
}
|
||||
}
|
||||
|
||||
KWorkerTaskManager::KWorkerTaskManager() : m_waiting_thread(1, "yuzu:KWorkerTaskManager") {}
|
||||
|
||||
void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task) {
|
||||
ASSERT(type <= WorkerType::Count);
|
||||
kernel.WorkerTaskManager().AddTask(kernel, task);
|
||||
}
|
||||
|
||||
void KWorkerTaskManager::AddTask(KernelCore& kernel, KWorkerTask* task) {
|
||||
KScopedSchedulerLock sl(kernel);
|
||||
m_waiting_thread.QueueWork([task]() {
|
||||
// Do the task.
|
||||
task->DoWorkerTask();
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright 2022 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/thread_worker.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KernelCore;
|
||||
class KWorkerTask;
|
||||
|
||||
class KWorkerTaskManager final {
|
||||
public:
|
||||
enum class WorkerType : u32 {
|
||||
Exit,
|
||||
Count,
|
||||
};
|
||||
|
||||
KWorkerTaskManager();
|
||||
|
||||
static void AddTask(KernelCore& kernel_, WorkerType type, KWorkerTask* task);
|
||||
|
||||
private:
|
||||
void AddTask(KernelCore& kernel, KWorkerTask* task);
|
||||
|
||||
private:
|
||||
Common::ThreadWorker m_waiting_thread;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "core/hle/kernel/k_shared_memory.h"
|
||||
#include "core/hle/kernel/k_slab_heap.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/k_worker_task_manager.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/physical_core.h"
|
||||
#include "core/hle/kernel/service_thread.h"
|
||||
@@ -51,7 +52,8 @@ namespace Kernel {
|
||||
|
||||
struct KernelCore::Impl {
|
||||
explicit Impl(Core::System& system_, KernelCore& kernel_)
|
||||
: time_manager{system_}, object_list_container{kernel_}, system{system_} {}
|
||||
: time_manager{system_}, object_list_container{kernel_},
|
||||
service_threads_manager{1, "yuzu:ServiceThreadsManager"}, system{system_} {}
|
||||
|
||||
void SetMulticore(bool is_multi) {
|
||||
is_multicore = is_multi;
|
||||
@@ -121,7 +123,7 @@ struct KernelCore::Impl {
|
||||
object_list_container.Finalize();
|
||||
|
||||
// Ensures all service threads gracefully shutdown.
|
||||
service_threads.clear();
|
||||
ClearServiceThreads();
|
||||
|
||||
next_object_id = 0;
|
||||
next_kernel_process_id = KProcess::InitialKIPIDMin;
|
||||
@@ -299,12 +301,10 @@ struct KernelCore::Impl {
|
||||
// Gets the dummy KThread for the caller, allocating a new one if this is the first time
|
||||
KThread* GetHostDummyThread() {
|
||||
auto make_thread = [this]() {
|
||||
std::lock_guard lk(dummy_thread_lock);
|
||||
auto& thread = dummy_threads.emplace_back(std::make_unique<KThread>(system.Kernel()));
|
||||
KAutoObject::Create(thread.get());
|
||||
ASSERT(KThread::InitializeDummyThread(thread.get()).IsSuccess());
|
||||
KThread* thread = KThread::Create(system.Kernel());
|
||||
ASSERT(KThread::InitializeDummyThread(thread).IsSuccess());
|
||||
thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId()));
|
||||
return thread.get();
|
||||
return thread;
|
||||
};
|
||||
|
||||
thread_local KThread* saved_thread = make_thread();
|
||||
@@ -629,7 +629,7 @@ struct KernelCore::Impl {
|
||||
const auto application_pool = memory_layout.GetKernelApplicationPoolRegionPhysicalExtents();
|
||||
|
||||
// Initialize memory managers
|
||||
memory_manager = std::make_unique<KMemoryManager>();
|
||||
memory_manager = std::make_unique<KMemoryManager>(system);
|
||||
memory_manager->InitializeManager(KMemoryManager::Pool::Application,
|
||||
application_pool.GetAddress(),
|
||||
application_pool.GetEndAddress());
|
||||
@@ -704,11 +704,31 @@ struct KernelCore::Impl {
|
||||
return port;
|
||||
}
|
||||
|
||||
std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel,
|
||||
const std::string& name) {
|
||||
auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, 1, name);
|
||||
|
||||
service_threads_manager.QueueWork(
|
||||
[this, service_thread]() { service_threads.emplace(service_thread); });
|
||||
|
||||
return service_thread;
|
||||
}
|
||||
|
||||
void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
|
||||
if (auto strong_ptr = service_thread.lock()) {
|
||||
service_threads_manager.QueueWork(
|
||||
[this, strong_ptr{std::move(strong_ptr)}]() { service_threads.erase(strong_ptr); });
|
||||
}
|
||||
}
|
||||
|
||||
void ClearServiceThreads() {
|
||||
service_threads_manager.QueueWork([this]() { service_threads.clear(); });
|
||||
}
|
||||
|
||||
std::mutex server_ports_lock;
|
||||
std::mutex server_sessions_lock;
|
||||
std::mutex registered_objects_lock;
|
||||
std::mutex registered_in_use_objects_lock;
|
||||
std::mutex dummy_thread_lock;
|
||||
|
||||
std::atomic<u32> next_object_id{0};
|
||||
std::atomic<u64> next_kernel_process_id{KProcess::InitialKIPIDMin};
|
||||
@@ -759,14 +779,12 @@ struct KernelCore::Impl {
|
||||
|
||||
// Threads used for services
|
||||
std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads;
|
||||
Common::ThreadWorker service_threads_manager;
|
||||
|
||||
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> suspend_threads;
|
||||
std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};
|
||||
std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
|
||||
|
||||
// Specifically tracked to be automatically destroyed with kernel
|
||||
std::vector<std::unique_ptr<KThread>> dummy_threads;
|
||||
|
||||
bool is_multicore{};
|
||||
std::atomic_bool is_shutting_down{};
|
||||
bool is_phantom_mode_for_singlecore{};
|
||||
@@ -774,6 +792,8 @@ struct KernelCore::Impl {
|
||||
|
||||
std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{};
|
||||
|
||||
KWorkerTaskManager worker_task_manager;
|
||||
|
||||
// System context
|
||||
Core::System& system;
|
||||
};
|
||||
@@ -1099,15 +1119,11 @@ void KernelCore::ExitSVCProfile() {
|
||||
}
|
||||
|
||||
std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) {
|
||||
auto service_thread = std::make_shared<Kernel::ServiceThread>(*this, 1, name);
|
||||
impl->service_threads.emplace(service_thread);
|
||||
return service_thread;
|
||||
return impl->CreateServiceThread(*this, name);
|
||||
}
|
||||
|
||||
void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
|
||||
if (auto strong_ptr = service_thread.lock()) {
|
||||
impl->service_threads.erase(strong_ptr);
|
||||
}
|
||||
impl->ReleaseServiceThread(service_thread);
|
||||
}
|
||||
|
||||
Init::KSlabResourceCounts& KernelCore::SlabResourceCounts() {
|
||||
@@ -1118,6 +1134,14 @@ const Init::KSlabResourceCounts& KernelCore::SlabResourceCounts() const {
|
||||
return impl->slab_resource_counts;
|
||||
}
|
||||
|
||||
KWorkerTaskManager& KernelCore::WorkerTaskManager() {
|
||||
return impl->worker_task_manager;
|
||||
}
|
||||
|
||||
const KWorkerTaskManager& KernelCore::WorkerTaskManager() const {
|
||||
return impl->worker_task_manager;
|
||||
}
|
||||
|
||||
bool KernelCore::IsPhantomModeForSingleCore() const {
|
||||
return impl->IsPhantomModeForSingleCore();
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ class KSharedMemory;
|
||||
class KSharedMemoryInfo;
|
||||
class KThread;
|
||||
class KTransferMemory;
|
||||
class KWorkerTaskManager;
|
||||
class KWritableEvent;
|
||||
class KCodeMemory;
|
||||
class PhysicalCore;
|
||||
@@ -343,6 +344,12 @@ public:
|
||||
/// Gets the current slab resource counts.
|
||||
const Init::KSlabResourceCounts& SlabResourceCounts() const;
|
||||
|
||||
/// Gets the current worker task manager, used for dispatching KThread/KProcess tasks.
|
||||
KWorkerTaskManager& WorkerTaskManager();
|
||||
|
||||
/// Gets the current worker task manager, used for dispatching KThread/KProcess tasks.
|
||||
const KWorkerTaskManager& WorkerTaskManager() const;
|
||||
|
||||
private:
|
||||
friend class KProcess;
|
||||
friend class KThread;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/hle/kernel/k_session.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/service_thread.h"
|
||||
|
||||
@@ -50,6 +51,10 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std
|
||||
|
||||
kernel.RegisterHostThread();
|
||||
|
||||
// Ensure the dummy thread allocated for this host thread is closed on exit.
|
||||
auto* dummy_thread = kernel.GetCurrentEmuThread();
|
||||
SCOPE_EXIT({ dummy_thread->Close(); });
|
||||
|
||||
while (true) {
|
||||
std::function<void()> task;
|
||||
|
||||
|
||||
+97
-83
@@ -230,7 +230,7 @@ static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr
|
||||
return result;
|
||||
}
|
||||
|
||||
return page_table.Map(dst_addr, src_addr, size);
|
||||
return page_table.MapMemory(dst_addr, src_addr, size);
|
||||
}
|
||||
|
||||
static ResultCode MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) {
|
||||
@@ -249,7 +249,7 @@ static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_ad
|
||||
return result;
|
||||
}
|
||||
|
||||
return page_table.Unmap(dst_addr, src_addr, size);
|
||||
return page_table.UnmapMemory(dst_addr, src_addr, size);
|
||||
}
|
||||
|
||||
static ResultCode UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) {
|
||||
@@ -396,7 +396,7 @@ static ResultCode GetProcessId(Core::System& system, u64* out_process_id, Handle
|
||||
// Get the process id.
|
||||
*out_process_id = process->GetId();
|
||||
|
||||
return ResultInvalidHandle;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
static ResultCode GetProcessId32(Core::System& system, u32* out_process_id_low,
|
||||
@@ -645,6 +645,10 @@ static void OutputDebugString(Core::System& system, VAddr address, u64 len) {
|
||||
LOG_DEBUG(Debug_Emulated, "{}", str);
|
||||
}
|
||||
|
||||
static void OutputDebugString32(Core::System& system, u32 address, u32 len) {
|
||||
OutputDebugString(system, address, len);
|
||||
}
|
||||
|
||||
/// Gets system/memory information for the current process
|
||||
static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle,
|
||||
u64 info_sub_id) {
|
||||
@@ -1404,7 +1408,7 @@ static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Ha
|
||||
}
|
||||
|
||||
static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
|
||||
LOG_TRACE(Kernel_SVC, "called, handle_out=0x{:X}, address=0x{:X}, size=0x{:X}",
|
||||
LOG_TRACE(Kernel_SVC, "called, handle_out={}, address=0x{:X}, size=0x{:X}",
|
||||
static_cast<void*>(out), address, size);
|
||||
// Get kernel instance.
|
||||
auto& kernel = system.Kernel();
|
||||
@@ -1438,6 +1442,10 @@ static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr addr
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
static ResultCode CreateCodeMemory32(Core::System& system, Handle* out, u32 address, u32 size) {
|
||||
return CreateCodeMemory(system, out, address, size);
|
||||
}
|
||||
|
||||
static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation,
|
||||
VAddr address, size_t size, Svc::MemoryPermission perm) {
|
||||
|
||||
@@ -1517,6 +1525,12 @@ static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_han
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
static ResultCode ControlCodeMemory32(Core::System& system, Handle code_memory_handle,
|
||||
u32 operation, u64 address, u64 size,
|
||||
Svc::MemoryPermission perm) {
|
||||
return ControlCodeMemory(system, code_memory_handle, operation, address, size, perm);
|
||||
}
|
||||
|
||||
static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address,
|
||||
VAddr page_info_address, Handle process_handle,
|
||||
VAddr address) {
|
||||
@@ -2559,9 +2573,9 @@ struct FunctionDef {
|
||||
} // namespace
|
||||
|
||||
static const FunctionDef SVC_Table_32[] = {
|
||||
{0x00, nullptr, "Unknown"},
|
||||
{0x00, nullptr, "Unknown0"},
|
||||
{0x01, SvcWrap32<SetHeapSize32>, "SetHeapSize32"},
|
||||
{0x02, nullptr, "Unknown"},
|
||||
{0x02, nullptr, "SetMemoryPermission32"},
|
||||
{0x03, SvcWrap32<SetMemoryAttribute32>, "SetMemoryAttribute32"},
|
||||
{0x04, SvcWrap32<MapMemory32>, "MapMemory32"},
|
||||
{0x05, SvcWrap32<UnmapMemory32>, "UnmapMemory32"},
|
||||
@@ -2591,97 +2605,97 @@ static const FunctionDef SVC_Table_32[] = {
|
||||
{0x1d, SvcWrap32<SignalProcessWideKey32>, "SignalProcessWideKey32"},
|
||||
{0x1e, SvcWrap32<GetSystemTick32>, "GetSystemTick32"},
|
||||
{0x1f, SvcWrap32<ConnectToNamedPort32>, "ConnectToNamedPort32"},
|
||||
{0x20, nullptr, "Unknown"},
|
||||
{0x20, nullptr, "SendSyncRequestLight32"},
|
||||
{0x21, SvcWrap32<SendSyncRequest32>, "SendSyncRequest32"},
|
||||
{0x22, nullptr, "SendSyncRequestWithUserBuffer32"},
|
||||
{0x23, nullptr, "Unknown"},
|
||||
{0x23, nullptr, "SendAsyncRequestWithUserBuffer32"},
|
||||
{0x24, SvcWrap32<GetProcessId32>, "GetProcessId32"},
|
||||
{0x25, SvcWrap32<GetThreadId32>, "GetThreadId32"},
|
||||
{0x26, SvcWrap32<Break32>, "Break32"},
|
||||
{0x27, nullptr, "OutputDebugString32"},
|
||||
{0x28, nullptr, "Unknown"},
|
||||
{0x27, SvcWrap32<OutputDebugString32>, "OutputDebugString32"},
|
||||
{0x28, nullptr, "ReturnFromException32"},
|
||||
{0x29, SvcWrap32<GetInfo32>, "GetInfo32"},
|
||||
{0x2a, nullptr, "Unknown"},
|
||||
{0x2b, nullptr, "Unknown"},
|
||||
{0x2a, nullptr, "FlushEntireDataCache32"},
|
||||
{0x2b, nullptr, "FlushDataCache32"},
|
||||
{0x2c, SvcWrap32<MapPhysicalMemory32>, "MapPhysicalMemory32"},
|
||||
{0x2d, SvcWrap32<UnmapPhysicalMemory32>, "UnmapPhysicalMemory32"},
|
||||
{0x2e, nullptr, "Unknown"},
|
||||
{0x2f, nullptr, "Unknown"},
|
||||
{0x30, nullptr, "Unknown"},
|
||||
{0x31, nullptr, "Unknown"},
|
||||
{0x2e, nullptr, "GetDebugFutureThreadInfo32"},
|
||||
{0x2f, nullptr, "GetLastThreadInfo32"},
|
||||
{0x30, nullptr, "GetResourceLimitLimitValue32"},
|
||||
{0x31, nullptr, "GetResourceLimitCurrentValue32"},
|
||||
{0x32, SvcWrap32<SetThreadActivity32>, "SetThreadActivity32"},
|
||||
{0x33, SvcWrap32<GetThreadContext32>, "GetThreadContext32"},
|
||||
{0x34, SvcWrap32<WaitForAddress32>, "WaitForAddress32"},
|
||||
{0x35, SvcWrap32<SignalToAddress32>, "SignalToAddress32"},
|
||||
{0x36, nullptr, "Unknown"},
|
||||
{0x37, nullptr, "Unknown"},
|
||||
{0x38, nullptr, "Unknown"},
|
||||
{0x39, nullptr, "Unknown"},
|
||||
{0x3a, nullptr, "Unknown"},
|
||||
{0x3b, nullptr, "Unknown"},
|
||||
{0x3c, nullptr, "Unknown"},
|
||||
{0x3d, nullptr, "Unknown"},
|
||||
{0x3e, nullptr, "Unknown"},
|
||||
{0x3f, nullptr, "Unknown"},
|
||||
{0x36, SvcWrap32<SynchronizePreemptionState>, "SynchronizePreemptionState32"},
|
||||
{0x37, nullptr, "GetResourceLimitPeakValue32"},
|
||||
{0x38, nullptr, "Unknown38"},
|
||||
{0x39, nullptr, "CreateIoPool32"},
|
||||
{0x3a, nullptr, "CreateIoRegion32"},
|
||||
{0x3b, nullptr, "Unknown3b"},
|
||||
{0x3c, nullptr, "KernelDebug32"},
|
||||
{0x3d, nullptr, "ChangeKernelTraceState32"},
|
||||
{0x3e, nullptr, "Unknown3e"},
|
||||
{0x3f, nullptr, "Unknown3f"},
|
||||
{0x40, nullptr, "CreateSession32"},
|
||||
{0x41, nullptr, "AcceptSession32"},
|
||||
{0x42, nullptr, "Unknown"},
|
||||
{0x42, nullptr, "ReplyAndReceiveLight32"},
|
||||
{0x43, nullptr, "ReplyAndReceive32"},
|
||||
{0x44, nullptr, "Unknown"},
|
||||
{0x44, nullptr, "ReplyAndReceiveWithUserBuffer32"},
|
||||
{0x45, SvcWrap32<CreateEvent32>, "CreateEvent32"},
|
||||
{0x46, nullptr, "Unknown"},
|
||||
{0x47, nullptr, "Unknown"},
|
||||
{0x48, nullptr, "Unknown"},
|
||||
{0x49, nullptr, "Unknown"},
|
||||
{0x4a, nullptr, "Unknown"},
|
||||
{0x4b, nullptr, "Unknown"},
|
||||
{0x4c, nullptr, "Unknown"},
|
||||
{0x4d, nullptr, "Unknown"},
|
||||
{0x4e, nullptr, "Unknown"},
|
||||
{0x4f, nullptr, "Unknown"},
|
||||
{0x50, nullptr, "Unknown"},
|
||||
{0x51, nullptr, "Unknown"},
|
||||
{0x52, nullptr, "Unknown"},
|
||||
{0x53, nullptr, "Unknown"},
|
||||
{0x54, nullptr, "Unknown"},
|
||||
{0x55, nullptr, "Unknown"},
|
||||
{0x56, nullptr, "Unknown"},
|
||||
{0x57, nullptr, "Unknown"},
|
||||
{0x58, nullptr, "Unknown"},
|
||||
{0x59, nullptr, "Unknown"},
|
||||
{0x5a, nullptr, "Unknown"},
|
||||
{0x5b, nullptr, "Unknown"},
|
||||
{0x5c, nullptr, "Unknown"},
|
||||
{0x5d, nullptr, "Unknown"},
|
||||
{0x5e, nullptr, "Unknown"},
|
||||
{0x46, nullptr, "MapIoRegion32"},
|
||||
{0x47, nullptr, "UnmapIoRegion32"},
|
||||
{0x48, nullptr, "MapPhysicalMemoryUnsafe32"},
|
||||
{0x49, nullptr, "UnmapPhysicalMemoryUnsafe32"},
|
||||
{0x4a, nullptr, "SetUnsafeLimit32"},
|
||||
{0x4b, SvcWrap32<CreateCodeMemory32>, "CreateCodeMemory32"},
|
||||
{0x4c, SvcWrap32<ControlCodeMemory32>, "ControlCodeMemory32"},
|
||||
{0x4d, nullptr, "SleepSystem32"},
|
||||
{0x4e, nullptr, "ReadWriteRegister32"},
|
||||
{0x4f, nullptr, "SetProcessActivity32"},
|
||||
{0x50, nullptr, "CreateSharedMemory32"},
|
||||
{0x51, nullptr, "MapTransferMemory32"},
|
||||
{0x52, nullptr, "UnmapTransferMemory32"},
|
||||
{0x53, nullptr, "CreateInterruptEvent32"},
|
||||
{0x54, nullptr, "QueryPhysicalAddress32"},
|
||||
{0x55, nullptr, "QueryIoMapping32"},
|
||||
{0x56, nullptr, "CreateDeviceAddressSpace32"},
|
||||
{0x57, nullptr, "AttachDeviceAddressSpace32"},
|
||||
{0x58, nullptr, "DetachDeviceAddressSpace32"},
|
||||
{0x59, nullptr, "MapDeviceAddressSpaceByForce32"},
|
||||
{0x5a, nullptr, "MapDeviceAddressSpaceAligned32"},
|
||||
{0x5b, nullptr, "MapDeviceAddressSpace32"},
|
||||
{0x5c, nullptr, "UnmapDeviceAddressSpace32"},
|
||||
{0x5d, nullptr, "InvalidateProcessDataCache32"},
|
||||
{0x5e, nullptr, "StoreProcessDataCache32"},
|
||||
{0x5F, SvcWrap32<FlushProcessDataCache32>, "FlushProcessDataCache32"},
|
||||
{0x60, nullptr, "Unknown"},
|
||||
{0x61, nullptr, "Unknown"},
|
||||
{0x62, nullptr, "Unknown"},
|
||||
{0x63, nullptr, "Unknown"},
|
||||
{0x64, nullptr, "Unknown"},
|
||||
{0x60, nullptr, "StoreProcessDataCache32"},
|
||||
{0x61, nullptr, "BreakDebugProcess32"},
|
||||
{0x62, nullptr, "TerminateDebugProcess32"},
|
||||
{0x63, nullptr, "GetDebugEvent32"},
|
||||
{0x64, nullptr, "ContinueDebugEvent32"},
|
||||
{0x65, nullptr, "GetProcessList32"},
|
||||
{0x66, nullptr, "Unknown"},
|
||||
{0x67, nullptr, "Unknown"},
|
||||
{0x68, nullptr, "Unknown"},
|
||||
{0x69, nullptr, "Unknown"},
|
||||
{0x6A, nullptr, "Unknown"},
|
||||
{0x6B, nullptr, "Unknown"},
|
||||
{0x6C, nullptr, "Unknown"},
|
||||
{0x6D, nullptr, "Unknown"},
|
||||
{0x6E, nullptr, "Unknown"},
|
||||
{0x66, nullptr, "GetThreadList"},
|
||||
{0x67, nullptr, "GetDebugThreadContext32"},
|
||||
{0x68, nullptr, "SetDebugThreadContext32"},
|
||||
{0x69, nullptr, "QueryDebugProcessMemory32"},
|
||||
{0x6A, nullptr, "ReadDebugProcessMemory32"},
|
||||
{0x6B, nullptr, "WriteDebugProcessMemory32"},
|
||||
{0x6C, nullptr, "SetHardwareBreakPoint32"},
|
||||
{0x6D, nullptr, "GetDebugThreadParam32"},
|
||||
{0x6E, nullptr, "Unknown6E"},
|
||||
{0x6f, nullptr, "GetSystemInfo32"},
|
||||
{0x70, nullptr, "CreatePort32"},
|
||||
{0x71, nullptr, "ManageNamedPort32"},
|
||||
{0x72, nullptr, "ConnectToPort32"},
|
||||
{0x73, nullptr, "SetProcessMemoryPermission32"},
|
||||
{0x74, nullptr, "Unknown"},
|
||||
{0x75, nullptr, "Unknown"},
|
||||
{0x76, nullptr, "Unknown"},
|
||||
{0x74, nullptr, "MapProcessMemory32"},
|
||||
{0x75, nullptr, "UnmapProcessMemory32"},
|
||||
{0x76, nullptr, "QueryProcessMemory32"},
|
||||
{0x77, nullptr, "MapProcessCodeMemory32"},
|
||||
{0x78, nullptr, "UnmapProcessCodeMemory32"},
|
||||
{0x79, nullptr, "Unknown"},
|
||||
{0x7A, nullptr, "Unknown"},
|
||||
{0x79, nullptr, "CreateProcess32"},
|
||||
{0x7A, nullptr, "StartProcess32"},
|
||||
{0x7B, nullptr, "TerminateProcess32"},
|
||||
{0x7C, nullptr, "GetProcessInfo32"},
|
||||
{0x7D, nullptr, "CreateResourceLimit32"},
|
||||
@@ -2754,7 +2768,7 @@ static const FunctionDef SVC_Table_32[] = {
|
||||
};
|
||||
|
||||
static const FunctionDef SVC_Table_64[] = {
|
||||
{0x00, nullptr, "Unknown"},
|
||||
{0x00, nullptr, "Unknown0"},
|
||||
{0x01, SvcWrap64<SetHeapSize>, "SetHeapSize"},
|
||||
{0x02, SvcWrap64<SetMemoryPermission>, "SetMemoryPermission"},
|
||||
{0x03, SvcWrap64<SetMemoryAttribute>, "SetMemoryAttribute"},
|
||||
@@ -2809,23 +2823,23 @@ static const FunctionDef SVC_Table_64[] = {
|
||||
{0x34, SvcWrap64<WaitForAddress>, "WaitForAddress"},
|
||||
{0x35, SvcWrap64<SignalToAddress>, "SignalToAddress"},
|
||||
{0x36, SvcWrap64<SynchronizePreemptionState>, "SynchronizePreemptionState"},
|
||||
{0x37, nullptr, "Unknown"},
|
||||
{0x38, nullptr, "Unknown"},
|
||||
{0x39, nullptr, "Unknown"},
|
||||
{0x3A, nullptr, "Unknown"},
|
||||
{0x3B, nullptr, "Unknown"},
|
||||
{0x37, nullptr, "GetResourceLimitPeakValue"},
|
||||
{0x38, nullptr, "Unknown38"},
|
||||
{0x39, nullptr, "CreateIoPool"},
|
||||
{0x3A, nullptr, "CreateIoRegion"},
|
||||
{0x3B, nullptr, "Unknown3B"},
|
||||
{0x3C, SvcWrap64<KernelDebug>, "KernelDebug"},
|
||||
{0x3D, SvcWrap64<ChangeKernelTraceState>, "ChangeKernelTraceState"},
|
||||
{0x3E, nullptr, "Unknown"},
|
||||
{0x3F, nullptr, "Unknown"},
|
||||
{0x3E, nullptr, "Unknown3e"},
|
||||
{0x3F, nullptr, "Unknown3f"},
|
||||
{0x40, nullptr, "CreateSession"},
|
||||
{0x41, nullptr, "AcceptSession"},
|
||||
{0x42, nullptr, "ReplyAndReceiveLight"},
|
||||
{0x43, nullptr, "ReplyAndReceive"},
|
||||
{0x44, nullptr, "ReplyAndReceiveWithUserBuffer"},
|
||||
{0x45, SvcWrap64<CreateEvent>, "CreateEvent"},
|
||||
{0x46, nullptr, "Unknown"},
|
||||
{0x47, nullptr, "Unknown"},
|
||||
{0x46, nullptr, "MapIoRegion"},
|
||||
{0x47, nullptr, "UnmapIoRegion"},
|
||||
{0x48, nullptr, "MapPhysicalMemoryUnsafe"},
|
||||
{0x49, nullptr, "UnmapPhysicalMemoryUnsafe"},
|
||||
{0x4A, nullptr, "SetUnsafeLimit"},
|
||||
@@ -2864,7 +2878,7 @@ static const FunctionDef SVC_Table_64[] = {
|
||||
{0x6B, nullptr, "WriteDebugProcessMemory"},
|
||||
{0x6C, nullptr, "SetHardwareBreakPoint"},
|
||||
{0x6D, nullptr, "GetDebugThreadParam"},
|
||||
{0x6E, nullptr, "Unknown"},
|
||||
{0x6E, nullptr, "Unknown6E"},
|
||||
{0x6F, nullptr, "GetSystemInfo"},
|
||||
{0x70, nullptr, "CreatePort"},
|
||||
{0x71, nullptr, "ManageNamedPort"},
|
||||
|
||||
@@ -669,4 +669,26 @@ void SvcWrap32(Core::System& system) {
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
// Used by CreateCodeMemory32
|
||||
template <ResultCode func(Core::System&, Handle*, u32, u32)>
|
||||
void SvcWrap32(Core::System& system) {
|
||||
Handle handle = 0;
|
||||
|
||||
const u32 retval = func(system, &handle, Param32(system, 1), Param32(system, 2)).raw;
|
||||
|
||||
system.CurrentArmInterface().SetReg(1, handle);
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
// Used by ControlCodeMemory32
|
||||
template <ResultCode func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)>
|
||||
void SvcWrap32(Core::System& system) {
|
||||
const u32 retval =
|
||||
func(system, Param32(system, 0), Param32(system, 1), Param(system, 2), Param(system, 4),
|
||||
static_cast<Svc::MemoryPermission>(Param32(system, 6)))
|
||||
.raw;
|
||||
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -39,9 +39,9 @@ constexpr ResultCode ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100};
|
||||
// Thumbnails are hard coded to be at least this size
|
||||
constexpr std::size_t THUMBNAIL_SIZE = 0x24000;
|
||||
|
||||
static std::filesystem::path GetImagePath(Common::UUID uuid) {
|
||||
static std::filesystem::path GetImagePath(const Common::UUID& uuid) {
|
||||
return Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) /
|
||||
fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormatSwitch());
|
||||
fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormattedString());
|
||||
}
|
||||
|
||||
static constexpr u32 SanitizeJPEGSize(std::size_t size) {
|
||||
@@ -290,7 +290,7 @@ public:
|
||||
|
||||
protected:
|
||||
void Get(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
|
||||
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString());
|
||||
ProfileBase profile_base{};
|
||||
ProfileData data{};
|
||||
if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) {
|
||||
@@ -300,21 +300,21 @@ protected:
|
||||
rb.PushRaw(profile_base);
|
||||
} else {
|
||||
LOG_ERROR(Service_ACC, "Failed to get profile base and data for user=0x{}",
|
||||
user_id.Format());
|
||||
user_id.RawString());
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code
|
||||
}
|
||||
}
|
||||
|
||||
void GetBase(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
|
||||
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString());
|
||||
ProfileBase profile_base{};
|
||||
if (profile_manager.GetProfileBase(user_id, profile_base)) {
|
||||
IPC::ResponseBuilder rb{ctx, 16};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw(profile_base);
|
||||
} else {
|
||||
LOG_ERROR(Service_ACC, "Failed to get profile base for user=0x{}", user_id.Format());
|
||||
LOG_ERROR(Service_ACC, "Failed to get profile base for user=0x{}", user_id.RawString());
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultUnknown); // TODO(ogniK): Get actual error code
|
||||
}
|
||||
@@ -373,7 +373,7 @@ protected:
|
||||
LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}",
|
||||
Common::StringFromFixedZeroTerminatedBuffer(
|
||||
reinterpret_cast<const char*>(base.username.data()), base.username.size()),
|
||||
base.timestamp, base.user_uuid.Format());
|
||||
base.timestamp, base.user_uuid.RawString());
|
||||
|
||||
if (user_data.size() < sizeof(ProfileData)) {
|
||||
LOG_ERROR(Service_ACC, "ProfileData buffer too small!");
|
||||
@@ -406,7 +406,7 @@ protected:
|
||||
LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}",
|
||||
Common::StringFromFixedZeroTerminatedBuffer(
|
||||
reinterpret_cast<const char*>(base.username.data()), base.username.size()),
|
||||
base.timestamp, base.user_uuid.Format());
|
||||
base.timestamp, base.user_uuid.RawString());
|
||||
|
||||
if (user_data.size() < sizeof(ProfileData)) {
|
||||
LOG_ERROR(Service_ACC, "ProfileData buffer too small!");
|
||||
@@ -435,7 +435,7 @@ protected:
|
||||
}
|
||||
|
||||
ProfileManager& profile_manager;
|
||||
Common::UUID user_id{Common::INVALID_UUID}; ///< The user id this profile refers to.
|
||||
Common::UUID user_id{}; ///< The user id this profile refers to.
|
||||
};
|
||||
|
||||
class IProfile final : public IProfileCommon {
|
||||
@@ -547,7 +547,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw<u64>(user_id.GetNintendoID());
|
||||
rb.PushRaw<u64>(user_id.Hash());
|
||||
}
|
||||
|
||||
void EnsureIdTokenCacheAsync(Kernel::HLERequestContext& ctx) {
|
||||
@@ -577,7 +577,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw<u64>(user_id.GetNintendoID());
|
||||
rb.PushRaw<u64>(user_id.Hash());
|
||||
}
|
||||
|
||||
void StoreOpenContext(Kernel::HLERequestContext& ctx) {
|
||||
@@ -587,7 +587,7 @@ private:
|
||||
}
|
||||
|
||||
std::shared_ptr<EnsureTokenIdCacheAsyncInterface> ensure_token_id{};
|
||||
Common::UUID user_id{Common::INVALID_UUID};
|
||||
Common::UUID user_id{};
|
||||
};
|
||||
|
||||
// 6.0.0+
|
||||
@@ -687,7 +687,7 @@ void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
|
||||
void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
Common::UUID user_id = rp.PopRaw<Common::UUID>();
|
||||
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
|
||||
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
@@ -718,7 +718,7 @@ void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) {
|
||||
void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
Common::UUID user_id = rp.PopRaw<Common::UUID>();
|
||||
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.Format());
|
||||
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
@@ -833,7 +833,7 @@ void Module::Interface::GetProfileEditor(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
Common::UUID user_id = rp.PopRaw<Common::UUID>();
|
||||
|
||||
LOG_DEBUG(Service_ACC, "called, user_id=0x{}", user_id.Format());
|
||||
LOG_DEBUG(Service_ACC, "called, user_id=0x{}", user_id.RawString());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
@@ -875,7 +875,7 @@ void Module::Interface::StoreSaveDataThumbnailApplication(Kernel::HLERequestCont
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto uuid = rp.PopRaw<Common::UUID>();
|
||||
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}", uuid.Format());
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}", uuid.RawString());
|
||||
|
||||
// TODO(ogniK): Check if application ID is zero on acc initialize. As we don't have a reliable
|
||||
// way of confirming things like the TID, we're going to assume a non zero value for the time
|
||||
@@ -889,7 +889,7 @@ void Module::Interface::StoreSaveDataThumbnailSystem(Kernel::HLERequestContext&
|
||||
const auto uuid = rp.PopRaw<Common::UUID>();
|
||||
const auto tid = rp.Pop<u64_le>();
|
||||
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}, tid={:016X}", uuid.Format(), tid);
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called, uuid=0x{}, tid={:016X}", uuid.RawString(), tid);
|
||||
StoreSaveDataThumbnail(ctx, uuid, tid);
|
||||
}
|
||||
|
||||
@@ -903,7 +903,7 @@ void Module::Interface::StoreSaveDataThumbnail(Kernel::HLERequestContext& ctx,
|
||||
return;
|
||||
}
|
||||
|
||||
if (!uuid) {
|
||||
if (uuid.IsInvalid()) {
|
||||
LOG_ERROR(Service_ACC, "User ID is not valid!");
|
||||
rb.Push(ERR_INVALID_USER_ID);
|
||||
return;
|
||||
@@ -927,20 +927,20 @@ void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContex
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
if (profile_manager->GetUserCount() != 1) {
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw<u128>(Common::INVALID_UUID);
|
||||
rb.PushRaw(Common::InvalidUUID);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto user_list = profile_manager->GetAllUsers();
|
||||
if (std::ranges::all_of(user_list, [](const auto& user) { return user.IsInvalid(); })) {
|
||||
rb.Push(ResultUnknown); // TODO(ogniK): Find the correct error code
|
||||
rb.PushRaw<u128>(Common::INVALID_UUID);
|
||||
rb.PushRaw(Common::InvalidUUID);
|
||||
return;
|
||||
}
|
||||
|
||||
// Select the first user we have
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw<u128>(profile_manager->GetUser(0)->uuid);
|
||||
rb.PushRaw(profile_manager->GetUser(0)->uuid);
|
||||
}
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module_,
|
||||
|
||||
@@ -37,8 +37,8 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module_, std::shared_ptr<ProfileManager>
|
||||
{130, nullptr, "ActivateOpenContextRetention"},
|
||||
{140, &ACC_SU::ListQualifiedUsers, "ListQualifiedUsers"},
|
||||
{150, nullptr, "AuthenticateApplicationAsync"},
|
||||
{151, nullptr, "Unknown151"},
|
||||
{152, nullptr, "Unknown152"},
|
||||
{151, nullptr, "EnsureSignedDeviceIdentifierCacheForNintendoAccountAsync"},
|
||||
{152, nullptr, "LoadSignedDeviceIdentifierCacheForNintendoAccount"},
|
||||
{190, nullptr, "GetUserLastOpenedApplication"},
|
||||
{191, nullptr, "ActivateOpenContextHolder"},
|
||||
{200, nullptr, "BeginUserRegistration"},
|
||||
|
||||
@@ -37,8 +37,8 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module_, std::shared_ptr<ProfileManager>
|
||||
{130, nullptr, "ActivateOpenContextRetention"},
|
||||
{140, &ACC_U1::ListQualifiedUsers, "ListQualifiedUsers"},
|
||||
{150, nullptr, "AuthenticateApplicationAsync"},
|
||||
{151, nullptr, "Unknown151"},
|
||||
{152, nullptr, "Unknown152"},
|
||||
{151, nullptr, "EnsureSignedDeviceIdentifierCacheForNintendoAccountAsync"},
|
||||
{152, nullptr, "LoadSignedDeviceIdentifierCacheForNintendoAccount"},
|
||||
{190, nullptr, "GetUserLastOpenedApplication"},
|
||||
{191, nullptr, "ActivateOpenContextHolder"},
|
||||
{997, nullptr, "DebugInvalidateTokenCacheForUser"},
|
||||
|
||||
@@ -19,8 +19,8 @@ namespace FS = Common::FS;
|
||||
using Common::UUID;
|
||||
|
||||
struct UserRaw {
|
||||
UUID uuid{Common::INVALID_UUID};
|
||||
UUID uuid2{Common::INVALID_UUID};
|
||||
UUID uuid{};
|
||||
UUID uuid2{};
|
||||
u64 timestamp{};
|
||||
ProfileUsername username{};
|
||||
ProfileData extra_data{};
|
||||
@@ -45,7 +45,7 @@ ProfileManager::ProfileManager() {
|
||||
|
||||
// Create an user if none are present
|
||||
if (user_count == 0) {
|
||||
CreateNewUser(UUID::Generate(), "yuzu");
|
||||
CreateNewUser(UUID::MakeRandom(), "yuzu");
|
||||
}
|
||||
|
||||
auto current =
|
||||
@@ -101,7 +101,7 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& usern
|
||||
if (user_count == MAX_USERS) {
|
||||
return ERROR_TOO_MANY_USERS;
|
||||
}
|
||||
if (!uuid) {
|
||||
if (uuid.IsInvalid()) {
|
||||
return ERROR_ARGUMENT_IS_NULL;
|
||||
}
|
||||
if (username[0] == 0x0) {
|
||||
@@ -145,7 +145,7 @@ std::optional<UUID> ProfileManager::GetUser(std::size_t index) const {
|
||||
|
||||
/// Returns a users profile index based on their user id.
|
||||
std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const {
|
||||
if (!uuid) {
|
||||
if (uuid.IsInvalid()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -250,9 +250,10 @@ UserIDArray ProfileManager::GetOpenUsers() const {
|
||||
std::ranges::transform(profiles, output.begin(), [](const ProfileInfo& p) {
|
||||
if (p.is_open)
|
||||
return p.user_uuid;
|
||||
return UUID{Common::INVALID_UUID};
|
||||
return Common::InvalidUUID;
|
||||
});
|
||||
std::stable_partition(output.begin(), output.end(), [](const UUID& uuid) { return uuid; });
|
||||
std::stable_partition(output.begin(), output.end(),
|
||||
[](const UUID& uuid) { return uuid.IsValid(); });
|
||||
return output;
|
||||
}
|
||||
|
||||
@@ -299,7 +300,7 @@ bool ProfileManager::RemoveUser(UUID uuid) {
|
||||
|
||||
profiles[*index] = ProfileInfo{};
|
||||
std::stable_partition(profiles.begin(), profiles.end(),
|
||||
[](const ProfileInfo& profile) { return profile.user_uuid; });
|
||||
[](const ProfileInfo& profile) { return profile.user_uuid.IsValid(); });
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -361,7 +362,7 @@ void ProfileManager::ParseUserSaveFile() {
|
||||
}
|
||||
|
||||
std::stable_partition(profiles.begin(), profiles.end(),
|
||||
[](const ProfileInfo& profile) { return profile.user_uuid; });
|
||||
[](const ProfileInfo& profile) { return profile.user_uuid.IsValid(); });
|
||||
}
|
||||
|
||||
void ProfileManager::WriteUserSaveFile() {
|
||||
|
||||
@@ -35,7 +35,7 @@ static_assert(sizeof(ProfileData) == 0x80, "ProfileData structure has incorrect
|
||||
/// This holds general information about a users profile. This is where we store all the information
|
||||
/// based on a specific user
|
||||
struct ProfileInfo {
|
||||
Common::UUID user_uuid{Common::INVALID_UUID};
|
||||
Common::UUID user_uuid{};
|
||||
ProfileUsername username{};
|
||||
u64 creation_time{};
|
||||
ProfileData data{}; // TODO(ognik): Work out what this is
|
||||
@@ -49,7 +49,7 @@ struct ProfileBase {
|
||||
|
||||
// Zero out all the fields to make the profile slot considered "Empty"
|
||||
void Invalidate() {
|
||||
user_uuid.Invalidate();
|
||||
user_uuid = {};
|
||||
timestamp = 0;
|
||||
username.fill(0);
|
||||
}
|
||||
@@ -103,7 +103,7 @@ private:
|
||||
|
||||
std::array<ProfileInfo, MAX_USERS> profiles{};
|
||||
std::size_t user_count{};
|
||||
Common::UUID last_opened_user{Common::INVALID_UUID};
|
||||
Common::UUID last_opened_user{};
|
||||
};
|
||||
|
||||
}; // namespace Service::Account
|
||||
|
||||
@@ -55,7 +55,7 @@ constexpr u32 LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC = 0xC79497CA;
|
||||
struct LaunchParameterAccountPreselectedUser {
|
||||
u32_le magic;
|
||||
u32_le is_account_selected;
|
||||
u128 current_user;
|
||||
Common::UUID current_user;
|
||||
INSERT_PADDING_BYTES(0x70);
|
||||
};
|
||||
static_assert(sizeof(LaunchParameterAccountPreselectedUser) == 0x88);
|
||||
@@ -618,7 +618,7 @@ void AppletMessageQueue::PushMessage(AppletMessage msg) {
|
||||
AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() {
|
||||
if (messages.empty()) {
|
||||
on_new_message->GetWritableEvent().Clear();
|
||||
return AppletMessage::NoMessage;
|
||||
return AppletMessage::None;
|
||||
}
|
||||
auto msg = messages.front();
|
||||
messages.pop();
|
||||
@@ -633,7 +633,7 @@ std::size_t AppletMessageQueue::GetMessageCount() const {
|
||||
}
|
||||
|
||||
void AppletMessageQueue::RequestExit() {
|
||||
PushMessage(AppletMessage::ExitRequested);
|
||||
PushMessage(AppletMessage::Exit);
|
||||
}
|
||||
|
||||
void AppletMessageQueue::FocusStateChanged() {
|
||||
@@ -732,7 +732,7 @@ void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) {
|
||||
const auto message = msg_queue->PopMessage();
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
|
||||
if (message == AppletMessageQueue::AppletMessage::NoMessage) {
|
||||
if (message == AppletMessageQueue::AppletMessage::None) {
|
||||
LOG_ERROR(Service_AM, "Message queue is empty");
|
||||
rb.Push(ERR_NO_MESSAGES);
|
||||
rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
|
||||
@@ -1453,8 +1453,8 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
Account::ProfileManager profile_manager{};
|
||||
const auto uuid = profile_manager.GetUser(static_cast<s32>(Settings::values.current_user));
|
||||
ASSERT(uuid);
|
||||
params.current_user = uuid->uuid;
|
||||
ASSERT(uuid.has_value() && uuid->IsValid());
|
||||
params.current_user = *uuid;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ class NVFlinger;
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
// This is nn::settings::Language
|
||||
enum SystemLanguage {
|
||||
Japanese = 0,
|
||||
English = 1, // en-US
|
||||
@@ -41,16 +42,44 @@ enum SystemLanguage {
|
||||
// 4.0.0+
|
||||
SimplifiedChinese = 15,
|
||||
TraditionalChinese = 16,
|
||||
// 10.1.0+
|
||||
BrazilianPortuguese = 17,
|
||||
};
|
||||
|
||||
class AppletMessageQueue {
|
||||
public:
|
||||
// This is nn::am::AppletMessage
|
||||
enum class AppletMessage : u32 {
|
||||
NoMessage = 0,
|
||||
ExitRequested = 4,
|
||||
None = 0,
|
||||
ChangeIntoForeground = 1,
|
||||
ChangeIntoBackground = 2,
|
||||
Exit = 4,
|
||||
ApplicationExited = 6,
|
||||
FocusStateChanged = 15,
|
||||
Resume = 16,
|
||||
DetectShortPressingHomeButton = 20,
|
||||
DetectLongPressingHomeButton = 21,
|
||||
DetectShortPressingPowerButton = 22,
|
||||
DetectMiddlePressingPowerButton = 23,
|
||||
DetectLongPressingPowerButton = 24,
|
||||
RequestToPrepareSleep = 25,
|
||||
FinishedSleepSequence = 26,
|
||||
SleepRequiredByHighTemperature = 27,
|
||||
SleepRequiredByLowBattery = 28,
|
||||
AutoPowerDown = 29,
|
||||
OperationModeChanged = 30,
|
||||
PerformanceModeChanged = 31,
|
||||
DetectReceivingCecSystemStandby = 32,
|
||||
SdCardRemoved = 33,
|
||||
LaunchApplicationRequested = 50,
|
||||
RequestToDisplay = 51,
|
||||
ShowApplicationLogo = 55,
|
||||
HideApplicationLogo = 56,
|
||||
ForceHideApplicationLogo = 57,
|
||||
FloatingApplicationDetected = 60,
|
||||
DetectShortPressingCaptureButton = 90,
|
||||
AlbumScreenShotTaken = 92,
|
||||
AlbumRecordingSaved = 93,
|
||||
};
|
||||
|
||||
explicit AppletMessageQueue(Core::System& system);
|
||||
@@ -179,11 +208,14 @@ public:
|
||||
~ICommonStateGetter() override;
|
||||
|
||||
private:
|
||||
// This is nn::oe::FocusState
|
||||
enum class FocusState : u8 {
|
||||
InFocus = 1,
|
||||
NotInFocus = 2,
|
||||
Background = 3,
|
||||
};
|
||||
|
||||
// This is nn::oe::OperationMode
|
||||
enum class OperationMode : u8 {
|
||||
Handheld = 0,
|
||||
Docked = 1,
|
||||
|
||||
@@ -62,11 +62,11 @@ void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid) {
|
||||
|
||||
if (uuid.has_value() && uuid->IsValid()) {
|
||||
output.result = 0;
|
||||
output.uuid_selected = uuid->uuid;
|
||||
output.uuid_selected = *uuid;
|
||||
} else {
|
||||
status = ERR_USER_CANCELLED_SELECTION;
|
||||
output.result = ERR_USER_CANCELLED_SELECTION.raw;
|
||||
output.uuid_selected = Common::INVALID_UUID;
|
||||
output.uuid_selected = Common::InvalidUUID;
|
||||
}
|
||||
|
||||
final_data = std::vector<u8>(sizeof(UserSelectionOutput));
|
||||
|
||||
@@ -27,7 +27,7 @@ static_assert(sizeof(UserSelectionConfig) == 0xA0, "UserSelectionConfig has inco
|
||||
|
||||
struct UserSelectionOutput {
|
||||
u64 result;
|
||||
u128 uuid_selected;
|
||||
Common::UUID uuid_selected;
|
||||
};
|
||||
static_assert(sizeof(UserSelectionOutput) == 0x18, "UserSelectionOutput has incorrect size.");
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ OMM::OMM(Core::System& system_) : ServiceFramework{system_, "omm"} {
|
||||
{25, nullptr, "SetApplicationCecSettingsAndNotifyChanged"},
|
||||
{26, nullptr, "GetOperationModeSystemInfo"},
|
||||
{27, nullptr, "GetAppletFullAwakingSystemEvent"},
|
||||
{28, nullptr, "CreateCradleFirmwareUpdater"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user