Compare commits
340 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5f8d6bc504 | |||
| 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 | |||
| 49a0e4330e | |||
| 6ac44f3bdc | |||
| 6123b6ea45 | |||
| 081669c334 | |||
| 599c0763e5 | |||
| c8b358dba2 | |||
| d92b5fc435 | |||
| c65c651b6f | |||
| 36da4d1121 | |||
| b3308830b2 | |||
| 18adea343e | |||
| 09f4f3f23b | |||
| a1054a093c | |||
| 099dd0c0d2 | |||
| acbfb0083a | |||
| af4696657c | |||
| ea089c012e | |||
| b9a313057e | |||
| 42f653ab6f | |||
| 26f4e92c1f | |||
| 873d26b335 | |||
| d05675242a | |||
| 72c8a94a6c | |||
| b94e947793 | |||
| 50a7152941 | |||
| 4b4af29f86 | |||
| a95c49e7d0 | |||
| 2897ddb8f5 | |||
| b590e5572d | |||
| f46e1b8ca2 | |||
| 7116a7d28b | |||
| 37a3b89e7a | |||
| 7c88f4de2d | |||
| 62c13e8318 | |||
| 41bbb31af4 | |||
| 524674aafc | |||
| 6af82fd070 | |||
| afb06b1495 | |||
| 118d5fa3b0 | |||
| c68a0d17fc | |||
| c17938f96b | |||
| f58ee3f15f | |||
| 7652543231 | |||
| e773354477 | |||
| abbbdc2bc0 | |||
| 05b7b177f6 | |||
| b89d096652 | |||
| d866916f42 | |||
| da8e0f6571 | |||
| 3fa9702952 | |||
| ae7da0b12d | |||
| 7a13a515d9 | |||
| 133f497f84 | |||
| a9ad983c13 | |||
| 214b9fc9a7 | |||
| 951c61aeaa | |||
| 285b6dbc39 | |||
| 2428214c4b | |||
| 3a89723d97 | |||
| 8c907c620d | |||
| b84d429c2e | |||
| 9f34be5a61 | |||
| 14ac0c2923 | |||
| 1eec9590c3 | |||
| e18bf4b062 | |||
| 88e1e7f14b | |||
| 640fc1418b | |||
| f9e0681d59 | |||
| 78759d98dc |
+10
-2
@@ -33,6 +33,10 @@ option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
|
||||
|
||||
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)
|
||||
@@ -166,8 +170,7 @@ 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.0.1"
|
||||
"fmt 8.0.1 fmt/8.1.1"
|
||||
"lz4 1.8 lz4/1.9.2"
|
||||
"nlohmann_json 3.8 nlohmann_json/3.8.0"
|
||||
"ZLIB 1.2 zlib/1.2.11"
|
||||
@@ -175,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
+1230
-1010
File diff suppressed because it is too large
Load Diff
Vendored
+603
-416
File diff suppressed because it is too large
Load Diff
Vendored
+611
-424
File diff suppressed because it is too large
Load Diff
Vendored
+600
-413
File diff suppressed because it is too large
Load Diff
Vendored
+601
-414
File diff suppressed because it is too large
Load Diff
Vendored
+6221
File diff suppressed because it is too large
Load Diff
Vendored
+644
-457
File diff suppressed because it is too large
Load Diff
Vendored
+6218
File diff suppressed because it is too large
Load Diff
Vendored
+663
-468
File diff suppressed because it is too large
Load Diff
Vendored
+592
-405
File diff suppressed because it is too large
Load Diff
Vendored
+676
-489
File diff suppressed because it is too large
Load Diff
Vendored
+667
-480
File diff suppressed because it is too large
Load Diff
Vendored
+657
-469
File diff suppressed because it is too large
Load Diff
Vendored
+770
-573
File diff suppressed because it is too large
Load Diff
Vendored
+721
-528
File diff suppressed because it is too large
Load Diff
Vendored
+639
-452
File diff suppressed because it is too large
Load Diff
Vendored
+563
-376
File diff suppressed because it is too large
Load Diff
Vendored
+666
-479
File diff suppressed because it is too large
Load Diff
Vendored
+630
-443
File diff suppressed because it is too large
Load Diff
Vendored
+668
-481
File diff suppressed because it is too large
Load Diff
Vendored
+563
-376
File diff suppressed because it is too large
Load Diff
Vendored
+616
-429
File diff suppressed because it is too large
Load Diff
Vendored
+3
-7
@@ -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)
|
||||
@@ -117,10 +113,10 @@ if (ENABLE_WEB_SERVICE)
|
||||
endif()
|
||||
|
||||
# Opus
|
||||
find_package(opus 1.3)
|
||||
if (NOT opus_FOUND)
|
||||
message(STATUS "opus 1.3 or newer not found, falling back to externals")
|
||||
if (YUZU_USE_BUNDLED_OPUS)
|
||||
add_subdirectory(opus EXCLUDE_FROM_ALL)
|
||||
else()
|
||||
find_package(opus 1.3 REQUIRED)
|
||||
endif()
|
||||
|
||||
# FFMpeg
|
||||
|
||||
+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: 28714ee75a...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);
|
||||
}
|
||||
|
||||
@@ -85,6 +85,7 @@ add_library(common STATIC
|
||||
logging/backend.h
|
||||
logging/filter.cpp
|
||||
logging/filter.h
|
||||
logging/formatter.h
|
||||
logging/log.h
|
||||
logging/log_entry.h
|
||||
logging/text_formatter.cpp
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright 2022 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
// adapted from https://github.com/fmtlib/fmt/issues/2704
|
||||
// a generic formatter for enum classes
|
||||
#if FMT_VERSION >= 80100
|
||||
template <typename T>
|
||||
struct fmt::formatter<T, std::enable_if_t<std::is_enum_v<T>, char>>
|
||||
: formatter<std::underlying_type_t<T>> {
|
||||
template <typename FormatContext>
|
||||
auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||
return fmt::formatter<std::underlying_type_t<T>>::format(
|
||||
static_cast<std::underlying_type_t<T>>(value), ctx);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
@@ -7,8 +7,9 @@
|
||||
#include <algorithm>
|
||||
#include <string_view>
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/logging/formatter.h"
|
||||
#include "common/logging/types.h"
|
||||
|
||||
namespace Common::Log {
|
||||
|
||||
@@ -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"};
|
||||
@@ -597,6 +598,7 @@ struct Values {
|
||||
BasicSetting<std::string> program_args{std::string(), "program_args"};
|
||||
BasicSetting<bool> dump_exefs{false, "dump_exefs"};
|
||||
BasicSetting<bool> dump_nso{false, "dump_nso"};
|
||||
BasicSetting<bool> dump_shaders{false, "dump_shaders"};
|
||||
BasicSetting<bool> enable_fs_access_log{false, "enable_fs_access_log"};
|
||||
BasicSetting<bool> reporting_services{false, "reporting_services"};
|
||||
BasicSetting<bool> quest_flag{false, "quest_flag"};
|
||||
|
||||
+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,
|
||||
|
||||
@@ -187,6 +187,8 @@ add_library(core STATIC
|
||||
hle/kernel/k_event.h
|
||||
hle/kernel/k_handle_table.cpp
|
||||
hle/kernel/k_handle_table.h
|
||||
hle/kernel/k_interrupt_manager.cpp
|
||||
hle/kernel/k_interrupt_manager.h
|
||||
hle/kernel/k_light_condition_variable.cpp
|
||||
hle/kernel/k_light_condition_variable.h
|
||||
hle/kernel/k_light_lock.cpp
|
||||
@@ -245,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
|
||||
@@ -462,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{
|
||||
|
||||
@@ -317,6 +317,8 @@ struct System::Impl {
|
||||
is_powered_on = false;
|
||||
exit_lock = false;
|
||||
|
||||
gpu_core->NotifyShutdown();
|
||||
|
||||
services.reset();
|
||||
service_manager.reset();
|
||||
cheat_engine.reset();
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -351,6 +352,19 @@ void EmulatedController::DisableConfiguration() {
|
||||
}
|
||||
}
|
||||
|
||||
void EmulatedController::EnableSystemButtons() {
|
||||
system_buttons_enabled = true;
|
||||
}
|
||||
|
||||
void EmulatedController::DisableSystemButtons() {
|
||||
system_buttons_enabled = false;
|
||||
}
|
||||
|
||||
void EmulatedController::ResetSystemButtons() {
|
||||
controller.home_button_state.home.Assign(false);
|
||||
controller.capture_button_state.capture.Assign(false);
|
||||
}
|
||||
|
||||
bool EmulatedController::IsConfiguring() const {
|
||||
return is_configuring;
|
||||
}
|
||||
@@ -389,7 +403,8 @@ std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices(
|
||||
devices.begin(), devices.end(), [param](const Common::ParamPackage param_) {
|
||||
return param.Get("engine", "") == param_.Get("engine", "") &&
|
||||
param.Get("guid", "") == param_.Get("guid", "") &&
|
||||
param.Get("port", 0) == param_.Get("port", 0);
|
||||
param.Get("port", 0) == param_.Get("port", 0) &&
|
||||
param.Get("pad", 0) == param_.Get("pad", 0);
|
||||
});
|
||||
if (devices_it != devices.end()) {
|
||||
continue;
|
||||
@@ -398,6 +413,7 @@ std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices(
|
||||
device.Set("engine", param.Get("engine", ""));
|
||||
device.Set("guid", param.Get("guid", ""));
|
||||
device.Set("port", param.Get("port", 0));
|
||||
device.Set("pad", param.Get("pad", 0));
|
||||
devices.push_back(device);
|
||||
}
|
||||
|
||||
@@ -412,7 +428,8 @@ std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices(
|
||||
devices.begin(), devices.end(), [param](const Common::ParamPackage param_) {
|
||||
return param.Get("engine", "") == param_.Get("engine", "") &&
|
||||
param.Get("guid", "") == param_.Get("guid", "") &&
|
||||
param.Get("port", 0) == param_.Get("port", 0);
|
||||
param.Get("port", 0) == param_.Get("port", 0) &&
|
||||
param.Get("pad", 0) == param_.Get("pad", 0);
|
||||
});
|
||||
if (devices_it != devices.end()) {
|
||||
continue;
|
||||
@@ -421,6 +438,7 @@ std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices(
|
||||
device.Set("engine", param.Get("engine", ""));
|
||||
device.Set("guid", param.Get("guid", ""));
|
||||
device.Set("port", param.Get("port", 0));
|
||||
device.Set("pad", param.Get("pad", 0));
|
||||
devices.push_back(device);
|
||||
}
|
||||
return devices;
|
||||
@@ -596,7 +614,16 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
|
||||
controller.npad_button_state.right_sr.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::Home:
|
||||
if (!system_buttons_enabled) {
|
||||
break;
|
||||
}
|
||||
controller.home_button_state.home.Assign(current_status.value);
|
||||
break;
|
||||
case Settings::NativeButton::Screenshot:
|
||||
if (!system_buttons_enabled) {
|
||||
break;
|
||||
}
|
||||
controller.capture_button_state.capture.Assign(current_status.value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -723,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;
|
||||
@@ -857,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) {
|
||||
@@ -879,10 +913,36 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
|
||||
if (!is_connected) {
|
||||
return;
|
||||
}
|
||||
if (!IsControllerSupported()) {
|
||||
LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller",
|
||||
npad_type);
|
||||
Disconnect();
|
||||
if (IsControllerSupported()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Disconnect();
|
||||
|
||||
// Fallback fullkey controllers to Pro controllers
|
||||
if (IsControllerFullkey() && supported_style_tag.fullkey) {
|
||||
LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type);
|
||||
SetNpadStyleIndex(NpadStyleIndex::ProController);
|
||||
Connect();
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller",
|
||||
npad_type);
|
||||
}
|
||||
|
||||
bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
|
||||
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
|
||||
switch (type) {
|
||||
case NpadStyleIndex::ProController:
|
||||
case NpadStyleIndex::GameCube:
|
||||
case NpadStyleIndex::NES:
|
||||
case NpadStyleIndex::SNES:
|
||||
case NpadStyleIndex::N64:
|
||||
case NpadStyleIndex::SegaGenesis:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1051,6 +1111,20 @@ BatteryValues EmulatedController::GetBatteryValues() const {
|
||||
return controller.battery_values;
|
||||
}
|
||||
|
||||
HomeButtonState EmulatedController::GetHomeButtons() const {
|
||||
if (is_configuring) {
|
||||
return {};
|
||||
}
|
||||
return controller.home_button_state;
|
||||
}
|
||||
|
||||
CaptureButtonState EmulatedController::GetCaptureButtons() const {
|
||||
if (is_configuring) {
|
||||
return {};
|
||||
}
|
||||
return controller.capture_button_state;
|
||||
}
|
||||
|
||||
NpadButtonState EmulatedController::GetNpadButtons() const {
|
||||
if (is_configuring) {
|
||||
return {};
|
||||
|
||||
@@ -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"
|
||||
@@ -101,6 +99,8 @@ struct ControllerStatus {
|
||||
VibrationValues vibration_values{};
|
||||
|
||||
// Data for HID serices
|
||||
HomeButtonState home_button_state{};
|
||||
CaptureButtonState capture_button_state{};
|
||||
NpadButtonState npad_button_state{};
|
||||
DebugPadButton debug_pad_button_state{};
|
||||
AnalogSticks analog_stick_state{};
|
||||
@@ -198,6 +198,15 @@ public:
|
||||
/// Returns the emulated controller into normal mode, allowing the modification of the HID state
|
||||
void DisableConfiguration();
|
||||
|
||||
/// Enables Home and Screenshot buttons
|
||||
void EnableSystemButtons();
|
||||
|
||||
/// Disables Home and Screenshot buttons
|
||||
void DisableSystemButtons();
|
||||
|
||||
/// Sets Home and Screenshot buttons to false
|
||||
void ResetSystemButtons();
|
||||
|
||||
/// Returns true if the emulated controller is in configuring mode
|
||||
bool IsConfiguring() const;
|
||||
|
||||
@@ -261,7 +270,13 @@ public:
|
||||
/// Returns the latest battery status from the controller with parameters
|
||||
BatteryValues GetBatteryValues() const;
|
||||
|
||||
/// Returns the latest status of button input for the npad service
|
||||
/// Returns the latest status of button input for the hid::HomeButton service
|
||||
HomeButtonState GetHomeButtons() const;
|
||||
|
||||
/// Returns the latest status of button input for the hid::CaptureButton service
|
||||
CaptureButtonState GetCaptureButtons() const;
|
||||
|
||||
/// Returns the latest status of button input for the hid::Npad service
|
||||
NpadButtonState GetNpadButtons() const;
|
||||
|
||||
/// Returns the latest status of button input for the debug pad service
|
||||
@@ -284,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;
|
||||
|
||||
@@ -320,6 +342,12 @@ private:
|
||||
/// Set the params for TAS devices
|
||||
void LoadTASParams();
|
||||
|
||||
/**
|
||||
* @param use_temporary_value If true tmp_npad_type will be used
|
||||
* @return true if the controller style is fullkey
|
||||
*/
|
||||
bool IsControllerFullkey(bool use_temporary_value = false) const;
|
||||
|
||||
/**
|
||||
* Checks the current controller type against the supported_style_tag
|
||||
* @param use_temporary_value If true tmp_npad_type will be used
|
||||
@@ -377,6 +405,7 @@ private:
|
||||
NpadStyleTag supported_style_tag{NpadStyleSet::All};
|
||||
bool is_connected{false};
|
||||
bool is_configuring{false};
|
||||
bool system_buttons_enabled{true};
|
||||
f32 motion_sensitivity{0.01f};
|
||||
bool force_update_motion{false};
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "core/hid/hid_types.h"
|
||||
|
||||
namespace Core::HID {
|
||||
|
||||
@@ -378,6 +378,26 @@ struct LedPattern {
|
||||
};
|
||||
};
|
||||
|
||||
struct HomeButtonState {
|
||||
union {
|
||||
u64 raw{};
|
||||
|
||||
// Buttons
|
||||
BitField<0, 1, u64> home;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(HomeButtonState) == 0x8, "HomeButtonState has incorrect size.");
|
||||
|
||||
struct CaptureButtonState {
|
||||
union {
|
||||
u64 raw{};
|
||||
|
||||
// Buttons
|
||||
BitField<0, 1, u64> capture;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(CaptureButtonState) == 0x8, "CaptureButtonState has incorrect size.");
|
||||
|
||||
struct NpadButtonState {
|
||||
union {
|
||||
NpadButton raw{};
|
||||
|
||||
@@ -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,6 +10,7 @@ namespace Core::HID {
|
||||
MotionInput::MotionInput() {
|
||||
// Initialize PID constants with default values
|
||||
SetPID(0.3f, 0.005f, 0.0f);
|
||||
SetGyroThreshold(0.007f);
|
||||
}
|
||||
|
||||
void MotionInput::SetPID(f32 new_kp, f32 new_ki, f32 new_kd) {
|
||||
@@ -30,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() {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "core/hle/kernel/global_scheduler_context.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/physical_core.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -42,6 +43,11 @@ void GlobalSchedulerContext::PreemptThreads() {
|
||||
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
|
||||
const u32 priority = preemption_priorities[core_id];
|
||||
kernel.Scheduler(core_id).RotateScheduledQueue(core_id, priority);
|
||||
|
||||
// Signal an interrupt occurred. For core 3, this is a certainty, as preemption will result
|
||||
// in the rotator thread being scheduled. For cores 0-2, this is to simulate or system
|
||||
// interrupts that may have occurred.
|
||||
kernel.PhysicalCore(core_id).Interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/kernel/k_interrupt_manager.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
|
||||
namespace Kernel::KInterruptManager {
|
||||
|
||||
void HandleInterrupt(KernelCore& kernel, s32 core_id) {
|
||||
auto* process = kernel.CurrentProcess();
|
||||
if (!process) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& scheduler = kernel.Scheduler(core_id);
|
||||
auto& current_thread = *scheduler.GetCurrentThread();
|
||||
|
||||
// If the user disable count is set, we may need to pin the current thread.
|
||||
if (current_thread.GetUserDisableCount() && !process->GetPinnedThread(core_id)) {
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
// Pin the current thread.
|
||||
process->PinCurrentThread(core_id);
|
||||
|
||||
// Set the interrupt flag for the thread.
|
||||
scheduler.GetCurrentThread()->SetInterruptFlag();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Kernel::KInterruptManager
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KernelCore;
|
||||
|
||||
namespace KInterruptManager {
|
||||
void HandleInterrupt(KernelCore& kernel, s32 core_id);
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -70,12 +70,12 @@ enum class KMemoryState : u32 {
|
||||
ThreadLocal =
|
||||
static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagReferenceCounted,
|
||||
|
||||
Transferred = static_cast<u32>(Svc::MemoryState::Transferred) | FlagsMisc |
|
||||
FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
|
||||
FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
||||
Transfered = static_cast<u32>(Svc::MemoryState::Transferred) | FlagsMisc |
|
||||
FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
|
||||
FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
||||
|
||||
SharedTransferred = static_cast<u32>(Svc::MemoryState::SharedTransferred) | FlagsMisc |
|
||||
FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
||||
SharedTransfered = static_cast<u32>(Svc::MemoryState::SharedTransferred) | FlagsMisc |
|
||||
FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
||||
|
||||
SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped |
|
||||
FlagReferenceCounted | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
||||
@@ -93,6 +93,8 @@ enum class KMemoryState : u32 {
|
||||
GeneratedCode = static_cast<u32>(Svc::MemoryState::GeneratedCode) | FlagMapped |
|
||||
FlagReferenceCounted | FlagCanDebug,
|
||||
CodeOut = static_cast<u32>(Svc::MemoryState::CodeOut) | FlagMapped | FlagReferenceCounted,
|
||||
|
||||
Coverage = static_cast<u32>(Svc::MemoryState::Coverage) | FlagMapped,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(KMemoryState);
|
||||
|
||||
@@ -108,8 +110,8 @@ static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x03FFBD09);
|
||||
static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x005C3C0A);
|
||||
static_assert(static_cast<u32>(KMemoryState::Stack) == 0x005C3C0B);
|
||||
static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0040200C);
|
||||
static_assert(static_cast<u32>(KMemoryState::Transferred) == 0x015C3C0D);
|
||||
static_assert(static_cast<u32>(KMemoryState::SharedTransferred) == 0x005C380E);
|
||||
static_assert(static_cast<u32>(KMemoryState::Transfered) == 0x015C3C0D);
|
||||
static_assert(static_cast<u32>(KMemoryState::SharedTransfered) == 0x005C380E);
|
||||
static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0040380F);
|
||||
static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010);
|
||||
static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x005C3811);
|
||||
@@ -117,6 +119,7 @@ static_assert(static_cast<u32>(KMemoryState::NonDeviceIpc) == 0x004C2812);
|
||||
static_assert(static_cast<u32>(KMemoryState::Kernel) == 0x00002013);
|
||||
static_assert(static_cast<u32>(KMemoryState::GeneratedCode) == 0x00402214);
|
||||
static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x00402015);
|
||||
static_assert(static_cast<u32>(KMemoryState::Coverage) == 0x00002016);
|
||||
|
||||
enum class KMemoryPermission : u8 {
|
||||
None = 0,
|
||||
@@ -155,7 +158,13 @@ enum class KMemoryPermission : u8 {
|
||||
DECLARE_ENUM_FLAG_OPERATORS(KMemoryPermission);
|
||||
|
||||
constexpr KMemoryPermission ConvertToKMemoryPermission(Svc::MemoryPermission perm) {
|
||||
return static_cast<KMemoryPermission>(perm);
|
||||
return static_cast<KMemoryPermission>(
|
||||
(static_cast<KMemoryPermission>(perm) & KMemoryPermission::UserMask) |
|
||||
KMemoryPermission::KernelRead |
|
||||
((static_cast<KMemoryPermission>(perm) & KMemoryPermission::UserWrite)
|
||||
<< KMemoryPermission::KernelShift) |
|
||||
(perm == Svc::MemoryPermission::None ? KMemoryPermission::NotMapped
|
||||
: KMemoryPermission::None));
|
||||
}
|
||||
|
||||
enum class KMemoryAttribute : u8 {
|
||||
@@ -169,6 +178,8 @@ enum class KMemoryAttribute : u8 {
|
||||
DeviceShared = static_cast<u8>(Svc::MemoryAttribute::DeviceShared),
|
||||
Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached),
|
||||
|
||||
SetMask = Uncached,
|
||||
|
||||
IpcAndDeviceMapped = IpcLocked | DeviceShared,
|
||||
LockedAndIpcLocked = Locked | IpcLocked,
|
||||
DeviceSharedAndUncached = DeviceShared | Uncached
|
||||
@@ -215,6 +226,15 @@ struct KMemoryInfo {
|
||||
constexpr VAddr GetLastAddress() const {
|
||||
return GetEndAddress() - 1;
|
||||
}
|
||||
constexpr KMemoryState GetState() const {
|
||||
return state;
|
||||
}
|
||||
constexpr KMemoryAttribute GetAttribute() const {
|
||||
return attribute;
|
||||
}
|
||||
constexpr KMemoryPermission GetPermission() const {
|
||||
return perm;
|
||||
}
|
||||
};
|
||||
|
||||
class KMemoryBlock final {
|
||||
|
||||
@@ -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{};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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,34 +23,37 @@ 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,
|
||||
KMemoryManager::Pool pool);
|
||||
ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state,
|
||||
KMemoryPermission perm);
|
||||
ResultCode MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
|
||||
ResultCode UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
|
||||
ResultCode MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
|
||||
ResultCode UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
|
||||
ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table,
|
||||
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);
|
||||
ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size, KMemoryPermission perm);
|
||||
ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size,
|
||||
Svc::MemoryPermission svc_perm);
|
||||
KMemoryInfo QueryInfo(VAddr addr);
|
||||
ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm);
|
||||
ResultCode ResetTransferMemory(VAddr addr, std::size_t size);
|
||||
ResultCode SetMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission perm);
|
||||
ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, KMemoryAttribute mask,
|
||||
KMemoryAttribute value);
|
||||
ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr);
|
||||
ResultCode SetMaxHeapSize(std::size_t size);
|
||||
ResultCode SetHeapSize(VAddr* out, std::size_t size);
|
||||
ResultVal<VAddr> AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align,
|
||||
@@ -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);
|
||||
@@ -102,30 +105,54 @@ private:
|
||||
constexpr VAddr GetRegionAddress(KMemoryState state) const;
|
||||
constexpr std::size_t GetRegionSize(KMemoryState state) const;
|
||||
|
||||
constexpr ResultCode CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask,
|
||||
ResultCode CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr,
|
||||
std::size_t size, KMemoryState state_mask,
|
||||
KMemoryState state, KMemoryPermission perm_mask,
|
||||
KMemoryPermission perm, KMemoryAttribute attr_mask,
|
||||
KMemoryAttribute attr) const;
|
||||
ResultCode CheckMemoryStateContiguous(VAddr addr, std::size_t size, KMemoryState state_mask,
|
||||
KMemoryState state, KMemoryPermission perm_mask,
|
||||
KMemoryPermission perm, KMemoryAttribute attr_mask,
|
||||
KMemoryAttribute attr) const {
|
||||
return this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask,
|
||||
perm, attr_mask, attr);
|
||||
}
|
||||
|
||||
ResultCode CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask,
|
||||
KMemoryState state, KMemoryPermission perm_mask,
|
||||
KMemoryPermission perm, KMemoryAttribute attr_mask,
|
||||
KMemoryAttribute attr) const;
|
||||
ResultCode CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
|
||||
KMemoryAttribute* out_attr, VAddr addr, std::size_t size,
|
||||
KMemoryState state_mask, KMemoryState state,
|
||||
KMemoryPermission perm_mask, KMemoryPermission perm,
|
||||
KMemoryAttribute attr_mask, KMemoryAttribute attr,
|
||||
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr);
|
||||
ResultCode CheckMemoryState(VAddr addr, std::size_t size, KMemoryState state_mask,
|
||||
KMemoryAttribute* out_attr, std::size_t* out_blocks_needed,
|
||||
VAddr addr, std::size_t size, KMemoryState state_mask,
|
||||
KMemoryState state, KMemoryPermission perm_mask,
|
||||
KMemoryPermission perm, KMemoryAttribute attr_mask,
|
||||
KMemoryAttribute attr,
|
||||
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) {
|
||||
return CheckMemoryState(nullptr, nullptr, nullptr, addr, size, state_mask, state, perm_mask,
|
||||
perm, attr_mask, attr, ignore_attr);
|
||||
}
|
||||
ResultCode CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size,
|
||||
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
|
||||
ResultCode CheckMemoryState(std::size_t* out_blocks_needed, VAddr addr, std::size_t size,
|
||||
KMemoryState state_mask, KMemoryState state,
|
||||
KMemoryPermission perm_mask, KMemoryPermission perm,
|
||||
KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
|
||||
KMemoryAttribute attr_mask, KMemoryAttribute attr,
|
||||
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
|
||||
return CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size,
|
||||
state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr);
|
||||
}
|
||||
ResultCode CheckMemoryState(VAddr addr, size_t size, KMemoryState state_mask,
|
||||
KMemoryState state, KMemoryPermission perm_mask,
|
||||
KMemoryPermission perm, KMemoryAttribute attr_mask,
|
||||
KMemoryAttribute attr,
|
||||
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
|
||||
return this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm,
|
||||
attr_mask, attr, ignore_attr);
|
||||
}
|
||||
|
||||
bool IsLockedByCurrentThread() const {
|
||||
return general_lock.IsLockedByCurrentThread();
|
||||
}
|
||||
|
||||
mutable KLightLock general_lock;
|
||||
mutable KLightLock map_physical_memory_lock;
|
||||
|
||||
std::recursive_mutex page_table_lock;
|
||||
std::unique_ptr<KMemoryBlockManager> block_manager;
|
||||
|
||||
public:
|
||||
@@ -184,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 {
|
||||
@@ -226,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;
|
||||
@@ -281,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!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,30 +228,28 @@ bool KProcess::ReleaseUserException(KThread* thread) {
|
||||
}
|
||||
}
|
||||
|
||||
void KProcess::PinCurrentThread() {
|
||||
void KProcess::PinCurrentThread(s32 core_id) {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
|
||||
// Get the current thread.
|
||||
const s32 core_id = GetCurrentCoreId(kernel);
|
||||
KThread* cur_thread = GetCurrentThreadPointer(kernel);
|
||||
KThread* cur_thread = kernel.Scheduler(static_cast<std::size_t>(core_id)).GetCurrentThread();
|
||||
|
||||
// If the thread isn't terminated, pin it.
|
||||
if (!cur_thread->IsTerminationRequested()) {
|
||||
// Pin it.
|
||||
PinThread(core_id, cur_thread);
|
||||
cur_thread->Pin();
|
||||
cur_thread->Pin(core_id);
|
||||
|
||||
// An update is needed.
|
||||
KScheduler::SetSchedulerUpdateNeeded(kernel);
|
||||
}
|
||||
}
|
||||
|
||||
void KProcess::UnpinCurrentThread() {
|
||||
void KProcess::UnpinCurrentThread(s32 core_id) {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
|
||||
// Get the current thread.
|
||||
const s32 core_id = GetCurrentCoreId(kernel);
|
||||
KThread* cur_thread = GetCurrentThreadPointer(kernel);
|
||||
KThread* cur_thread = kernel.Scheduler(static_cast<std::size_t>(core_id)).GetCurrentThread();
|
||||
|
||||
// Unpin it.
|
||||
cur_thread->Unpin();
|
||||
@@ -479,7 +485,7 @@ void KProcess::Finalize() {
|
||||
}
|
||||
|
||||
// Perform inherited finalization.
|
||||
KAutoObjectWithSlabHeapAndContainer<KProcess, KSynchronizationObject>::Finalize();
|
||||
KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask>::Finalize();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -511,7 +517,7 @@ VAddr KProcess::CreateTLSRegion() {
|
||||
const VAddr tls_page_addr{page_table
|
||||
->AllocateAndMapMemory(1, PageSize, true, start, size / PageSize,
|
||||
KMemoryState::ThreadLocal,
|
||||
KMemoryPermission::ReadAndWrite,
|
||||
KMemoryPermission::UserReadWrite,
|
||||
tls_map_addr)
|
||||
.ValueOr(0)};
|
||||
|
||||
@@ -543,16 +549,16 @@ void KProcess::FreeTLSRegion(VAddr tls_address) {
|
||||
|
||||
void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) {
|
||||
const auto ReprotectSegment = [&](const CodeSet::Segment& segment,
|
||||
KMemoryPermission permission) {
|
||||
Svc::MemoryPermission permission) {
|
||||
page_table->SetProcessMemoryPermission(segment.addr + base_addr, segment.size, permission);
|
||||
};
|
||||
|
||||
kernel.System().Memory().WriteBlock(*this, base_addr, code_set.memory.data(),
|
||||
code_set.memory.size());
|
||||
|
||||
ReprotectSegment(code_set.CodeSegment(), KMemoryPermission::ReadAndExecute);
|
||||
ReprotectSegment(code_set.RODataSegment(), KMemoryPermission::Read);
|
||||
ReprotectSegment(code_set.DataSegment(), KMemoryPermission::ReadAndWrite);
|
||||
ReprotectSegment(code_set.CodeSegment(), Svc::MemoryPermission::ReadExecute);
|
||||
ReprotectSegment(code_set.RODataSegment(), Svc::MemoryPermission::Read);
|
||||
ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite);
|
||||
}
|
||||
|
||||
bool KProcess::IsSignaled() const {
|
||||
@@ -589,7 +595,7 @@ ResultCode KProcess::AllocateMainThreadStack(std::size_t stack_size) {
|
||||
CASCADE_RESULT(main_thread_stack_top,
|
||||
page_table->AllocateAndMapMemory(
|
||||
main_thread_stack_size / PageSize, PageSize, false, start, size / PageSize,
|
||||
KMemoryState::Stack, KMemoryPermission::ReadAndWrite));
|
||||
KMemoryState::Stack, KMemoryPermission::UserReadWrite));
|
||||
|
||||
main_thread_stack_top += main_thread_stack_size;
|
||||
|
||||
|
||||
@@ -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,8 +345,10 @@ public:
|
||||
|
||||
bool IsSignaled() const override;
|
||||
|
||||
void PinCurrentThread();
|
||||
void UnpinCurrentThread();
|
||||
void DoWorkerTaskImpl();
|
||||
|
||||
void PinCurrentThread(s32 core_id);
|
||||
void UnpinCurrentThread(s32 core_id);
|
||||
void UnpinThread(KThread* thread);
|
||||
|
||||
KLightLock& GetStateLock() {
|
||||
@@ -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{};
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/cpu_manager.h"
|
||||
#include "core/hle/kernel/k_interrupt_manager.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
|
||||
@@ -48,11 +49,16 @@ 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);
|
||||
}
|
||||
|
||||
for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; ++core_id) {
|
||||
if (kernel.PhysicalCore(core_id).IsInterrupted()) {
|
||||
KInterruptManager::HandleInterrupt(kernel, static_cast<s32>(core_id));
|
||||
}
|
||||
}
|
||||
|
||||
if (must_context_switch) {
|
||||
auto core_scheduler = kernel.CurrentScheduler();
|
||||
kernel.ExitSVCProfile();
|
||||
@@ -400,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) {
|
||||
@@ -701,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();
|
||||
}
|
||||
@@ -726,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();
|
||||
@@ -740,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
|
||||
@@ -755,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);
|
||||
@@ -782,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;
|
||||
}
|
||||
@@ -791,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;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cinttypes>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
@@ -29,10 +30,12 @@
|
||||
#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"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#include "core/arm/dynarmic/arm_dynarmic_32.h"
|
||||
@@ -63,6 +66,13 @@ namespace Kernel {
|
||||
|
||||
namespace {
|
||||
|
||||
struct ThreadLocalRegion {
|
||||
static constexpr std::size_t MessageBufferSize = 0x100;
|
||||
std::array<u32, MessageBufferSize / sizeof(u32)> message_buffer;
|
||||
std::atomic_uint16_t disable_count;
|
||||
std::atomic_uint16_t interrupt_flag;
|
||||
};
|
||||
|
||||
class ThreadQueueImplForKThreadSleep final : public KThreadQueueWithoutEndWait {
|
||||
public:
|
||||
explicit ThreadQueueImplForKThreadSleep(KernelCore& kernel_)
|
||||
@@ -96,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>()));
|
||||
@@ -130,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;
|
||||
@@ -205,7 +215,6 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
|
||||
|
||||
parent = owner;
|
||||
parent->Open();
|
||||
parent->IncrementThreadCount();
|
||||
}
|
||||
|
||||
// Initialize thread context.
|
||||
@@ -252,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) {
|
||||
@@ -317,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 {
|
||||
@@ -346,7 +350,7 @@ void KThread::StartTermination() {
|
||||
if (parent != nullptr) {
|
||||
parent->ReleaseUserException(this);
|
||||
if (parent->GetPinnedThread(GetCurrentCoreId(kernel)) == this) {
|
||||
parent->UnpinCurrentThread();
|
||||
parent->UnpinCurrentThread(core_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,12 +371,29 @@ 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::Pin() {
|
||||
void KThread::DoWorkerTaskImpl() {
|
||||
// Finish the termination that was begun by Exit().
|
||||
this->FinishTermination();
|
||||
}
|
||||
|
||||
void KThread::Pin(s32 current_core) {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
|
||||
// Set ourselves as pinned.
|
||||
@@ -389,7 +410,6 @@ void KThread::Pin() {
|
||||
|
||||
// Bind ourselves to this core.
|
||||
const s32 active_core = GetActiveCore();
|
||||
const s32 current_core = GetCurrentCoreId(kernel);
|
||||
|
||||
SetActiveCore(current_core);
|
||||
physical_ideal_core_id = current_core;
|
||||
@@ -409,12 +429,7 @@ void KThread::Pin() {
|
||||
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.
|
||||
@@ -455,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.
|
||||
@@ -482,6 +490,36 @@ void KThread::Unpin() {
|
||||
}
|
||||
}
|
||||
|
||||
u16 KThread::GetUserDisableCount() const {
|
||||
if (!IsUserThread()) {
|
||||
// We only emulate TLS for user threads
|
||||
return {};
|
||||
}
|
||||
|
||||
auto& memory = kernel.System().Memory();
|
||||
return memory.Read16(tls_address + offsetof(ThreadLocalRegion, disable_count));
|
||||
}
|
||||
|
||||
void KThread::SetInterruptFlag() {
|
||||
if (!IsUserThread()) {
|
||||
// We only emulate TLS for user threads
|
||||
return;
|
||||
}
|
||||
|
||||
auto& memory = kernel.System().Memory();
|
||||
memory.Write16(tls_address + offsetof(ThreadLocalRegion, interrupt_flag), 1);
|
||||
}
|
||||
|
||||
void KThread::ClearInterruptFlag() {
|
||||
if (!IsUserThread()) {
|
||||
// We only emulate TLS for user threads
|
||||
return;
|
||||
}
|
||||
|
||||
auto& memory = kernel.System().Memory();
|
||||
memory.Write16(tls_address + offsetof(ThreadLocalRegion, interrupt_flag), 0);
|
||||
}
|
||||
|
||||
ResultCode KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) {
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
@@ -651,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() {
|
||||
@@ -683,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() {
|
||||
@@ -960,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.
|
||||
@@ -981,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1023,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_) {
|
||||
@@ -1047,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;
|
||||
@@ -307,6 +309,10 @@ public:
|
||||
return parent != nullptr;
|
||||
}
|
||||
|
||||
u16 GetUserDisableCount() const;
|
||||
void SetInterruptFlag();
|
||||
void ClearInterruptFlag();
|
||||
|
||||
[[nodiscard]] KThread* GetLockOwner() const {
|
||||
return lock_owner;
|
||||
}
|
||||
@@ -381,6 +387,8 @@ public:
|
||||
|
||||
void OnTimer();
|
||||
|
||||
void DoWorkerTaskImpl();
|
||||
|
||||
static void PostDestroy(uintptr_t arg);
|
||||
|
||||
[[nodiscard]] static ResultCode InitializeDummyThread(KThread* thread);
|
||||
@@ -490,7 +498,7 @@ public:
|
||||
this->GetStackParameters().disable_count--;
|
||||
}
|
||||
|
||||
void Pin();
|
||||
void Pin(s32 current_core);
|
||||
|
||||
void Unpin();
|
||||
|
||||
@@ -546,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) {
|
||||
@@ -624,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 {
|
||||
@@ -675,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);
|
||||
|
||||
@@ -740,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;
|
||||
|
||||
@@ -16,17 +16,25 @@ namespace Kernel {
|
||||
PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_,
|
||||
Core::CPUInterrupts& interrupts_)
|
||||
: core_index{core_index_}, system{system_}, scheduler{scheduler_},
|
||||
interrupts{interrupts_}, guard{std::make_unique<Common::SpinLock>()} {}
|
||||
interrupts{interrupts_}, guard{std::make_unique<Common::SpinLock>()} {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
// TODO(bunnei): Initialization relies on a core being available. We may later replace this with
|
||||
// a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager.
|
||||
auto& kernel = system.Kernel();
|
||||
arm_interface = std::make_unique<Core::ARM_Dynarmic_64>(
|
||||
system, interrupts, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index);
|
||||
#else
|
||||
#error Platform not supported yet.
|
||||
#endif
|
||||
}
|
||||
|
||||
PhysicalCore::~PhysicalCore() = default;
|
||||
|
||||
void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
auto& kernel = system.Kernel();
|
||||
if (is_64_bit) {
|
||||
arm_interface = std::make_unique<Core::ARM_Dynarmic_64>(
|
||||
system, interrupts, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index);
|
||||
} else {
|
||||
if (!is_64_bit) {
|
||||
// We already initialized a 64-bit core, replace with a 32-bit one.
|
||||
arm_interface = std::make_unique<Core::ARM_Dynarmic_32>(
|
||||
system, interrupts, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
+142
-117
@@ -168,6 +168,9 @@ constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) {
|
||||
|
||||
static ResultCode SetMemoryPermission(Core::System& system, VAddr address, u64 size,
|
||||
MemoryPermission perm) {
|
||||
LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size=0x{:X}, perm=0x{:08X", address, size,
|
||||
perm);
|
||||
|
||||
// Validate address / size.
|
||||
R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
|
||||
R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
|
||||
@@ -186,46 +189,33 @@ static ResultCode SetMemoryPermission(Core::System& system, VAddr address, u64 s
|
||||
}
|
||||
|
||||
static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask,
|
||||
u32 attribute) {
|
||||
u32 attr) {
|
||||
LOG_DEBUG(Kernel_SVC,
|
||||
"called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address,
|
||||
size, mask, attribute);
|
||||
size, mask, attr);
|
||||
|
||||
if (!Common::Is4KBAligned(address)) {
|
||||
LOG_ERROR(Kernel_SVC, "Address not page aligned (0x{:016X})", address);
|
||||
return ResultInvalidAddress;
|
||||
}
|
||||
// Validate address / size.
|
||||
R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
|
||||
R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
|
||||
R_UNLESS(size > 0, ResultInvalidSize);
|
||||
R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
|
||||
|
||||
if (size == 0 || !Common::Is4KBAligned(size)) {
|
||||
LOG_ERROR(Kernel_SVC, "Invalid size (0x{:X}). Size must be non-zero and page aligned.",
|
||||
size);
|
||||
return ResultInvalidAddress;
|
||||
}
|
||||
|
||||
if (!IsValidAddressRange(address, size)) {
|
||||
LOG_ERROR(Kernel_SVC, "Address range overflowed (Address: 0x{:016X}, Size: 0x{:016X})",
|
||||
address, size);
|
||||
return ResultInvalidCurrentMemory;
|
||||
}
|
||||
|
||||
const auto attributes{static_cast<MemoryAttribute>(mask | attribute)};
|
||||
if (attributes != static_cast<MemoryAttribute>(mask) ||
|
||||
(attributes | MemoryAttribute::Uncached) != MemoryAttribute::Uncached) {
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"Memory attribute doesn't match the given mask (Attribute: 0x{:X}, Mask: {:X}",
|
||||
attribute, mask);
|
||||
return ResultInvalidCombination;
|
||||
}
|
||||
// Validate the attribute and mask.
|
||||
constexpr u32 SupportedMask = static_cast<u32>(MemoryAttribute::Uncached);
|
||||
R_UNLESS((mask | attr) == mask, ResultInvalidCombination);
|
||||
R_UNLESS((mask | attr | SupportedMask) == SupportedMask, ResultInvalidCombination);
|
||||
|
||||
// Validate that the region is in range for the current process.
|
||||
auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
|
||||
R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
|
||||
|
||||
return page_table.SetMemoryAttribute(address, size, static_cast<KMemoryAttribute>(mask),
|
||||
static_cast<KMemoryAttribute>(attribute));
|
||||
// Set the memory attribute.
|
||||
return page_table.SetMemoryAttribute(address, size, mask, attr);
|
||||
}
|
||||
|
||||
static ResultCode SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask,
|
||||
u32 attribute) {
|
||||
return SetMemoryAttribute(system, address, size, mask, attribute);
|
||||
u32 attr) {
|
||||
return SetMemoryAttribute(system, address, size, mask, attr);
|
||||
}
|
||||
|
||||
/// Maps a memory range into a different range.
|
||||
@@ -240,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) {
|
||||
@@ -259,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) {
|
||||
@@ -406,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,
|
||||
@@ -655,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) {
|
||||
@@ -1319,6 +1313,8 @@ static ResultCode SetProcessMemoryPermission(Core::System& system, Handle proces
|
||||
R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
|
||||
R_UNLESS(size > 0, ResultInvalidSize);
|
||||
R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
|
||||
R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory);
|
||||
R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory);
|
||||
|
||||
// Validate the memory permission.
|
||||
R_UNLESS(IsValidProcessMemoryPermission(perm), ResultInvalidNewMemoryPermission);
|
||||
@@ -1333,7 +1329,7 @@ static ResultCode SetProcessMemoryPermission(Core::System& system, Handle proces
|
||||
R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
|
||||
|
||||
// Set the memory permission.
|
||||
return page_table.SetProcessMemoryPermission(address, size, ConvertToKMemoryPermission(perm));
|
||||
return page_table.SetProcessMemoryPermission(address, size, perm);
|
||||
}
|
||||
|
||||
static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
|
||||
@@ -1412,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();
|
||||
@@ -1446,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) {
|
||||
|
||||
@@ -1525,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) {
|
||||
@@ -1636,7 +1642,7 @@ static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_hand
|
||||
return ResultInvalidMemoryRegion;
|
||||
}
|
||||
|
||||
return page_table.MapProcessCodeMemory(dst_address, src_address, size);
|
||||
return page_table.MapCodeMemory(dst_address, src_address, size);
|
||||
}
|
||||
|
||||
static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle,
|
||||
@@ -1704,7 +1710,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
|
||||
return ResultInvalidMemoryRegion;
|
||||
}
|
||||
|
||||
return page_table.UnmapProcessCodeMemory(dst_address, src_address, size);
|
||||
return page_table.UnmapCodeMemory(dst_address, src_address, size);
|
||||
}
|
||||
|
||||
/// Exits the current process
|
||||
@@ -2027,6 +2033,25 @@ static ResultCode SignalToAddress(Core::System& system, VAddr address, Svc::Sign
|
||||
count);
|
||||
}
|
||||
|
||||
static void SynchronizePreemptionState(Core::System& system) {
|
||||
auto& kernel = system.Kernel();
|
||||
|
||||
// Lock the scheduler.
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
// If the current thread is pinned, unpin it.
|
||||
KProcess* cur_process = system.Kernel().CurrentProcess();
|
||||
const auto core_id = GetCurrentCoreId(kernel);
|
||||
|
||||
if (cur_process->GetPinnedThread(core_id) == GetCurrentThreadPointer(kernel)) {
|
||||
// Clear the current thread's interrupt flag.
|
||||
GetCurrentThread(kernel).ClearInterruptFlag();
|
||||
|
||||
// Unpin the current thread.
|
||||
cur_process->UnpinCurrentThread(core_id);
|
||||
}
|
||||
}
|
||||
|
||||
static ResultCode SignalToAddress32(Core::System& system, u32 address, Svc::SignalType signal_type,
|
||||
s32 value, s32 count) {
|
||||
return SignalToAddress(system, address, signal_type, value, count);
|
||||
@@ -2548,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"},
|
||||
@@ -2580,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"},
|
||||
@@ -2743,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"},
|
||||
@@ -2797,24 +2822,24 @@ static const FunctionDef SVC_Table_64[] = {
|
||||
{0x33, SvcWrap64<GetThreadContext>, "GetThreadContext"},
|
||||
{0x34, SvcWrap64<WaitForAddress>, "WaitForAddress"},
|
||||
{0x35, SvcWrap64<SignalToAddress>, "SignalToAddress"},
|
||||
{0x36, nullptr, "SynchronizePreemptionState"},
|
||||
{0x37, nullptr, "Unknown"},
|
||||
{0x38, nullptr, "Unknown"},
|
||||
{0x39, nullptr, "Unknown"},
|
||||
{0x3A, nullptr, "Unknown"},
|
||||
{0x3B, nullptr, "Unknown"},
|
||||
{0x36, SvcWrap64<SynchronizePreemptionState>, "SynchronizePreemptionState"},
|
||||
{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"},
|
||||
@@ -2853,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"},
|
||||
|
||||
@@ -32,6 +32,7 @@ enum class MemoryState : u32 {
|
||||
Kernel = 0x13,
|
||||
GeneratedCode = 0x14,
|
||||
CodeOut = 0x15,
|
||||
Coverage = 0x16,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(MemoryState);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user