Compare commits
152 Commits
android-10
...
android-11
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7684f37daa | ||
|
|
c60204e255 | ||
|
|
5e69769356 | ||
|
|
22cac3a5e3 | ||
|
|
e867768316 | ||
|
|
07276cf62a | ||
|
|
f04bc172ae | ||
|
|
585b6e9d46 | ||
|
|
a9e29a3972 | ||
|
|
1e61c3e1e7 | ||
|
|
79d3cef8db | ||
|
|
3e0da4f698 | ||
|
|
789c16305d | ||
|
|
1836e62d33 | ||
|
|
0bbbe80f75 | ||
|
|
eec3d356b6 | ||
|
|
2c1d850b46 | ||
|
|
2581590023 | ||
|
|
adb0900906 | ||
|
|
2d608cd625 | ||
|
|
29955de767 | ||
|
|
b0f62d8f24 | ||
|
|
ed2d77ddbc | ||
|
|
6e883a26da | ||
|
|
8427b9d49d | ||
|
|
0bb1c7c804 | ||
|
|
a5aa5876b4 | ||
|
|
911d2216be | ||
|
|
4da2105a32 | ||
|
|
1f9684eaf9 | ||
|
|
40c97c0549 | ||
|
|
6aee148b17 | ||
|
|
b5b93e6741 | ||
|
|
18a4529851 | ||
|
|
9e4d606c4c | ||
|
|
64f60f0acb | ||
|
|
21c631b33b | ||
|
|
43be2bfe33 | ||
|
|
79ba5d9c26 | ||
|
|
008d7e8c5f | ||
|
|
19e9bde9e0 | ||
|
|
6eb3a583cb | ||
|
|
e0834ee50b | ||
|
|
79894152a8 | ||
|
|
9274eaecd0 | ||
|
|
c733620024 | ||
|
|
897b411ae7 | ||
|
|
94836ba3b1 | ||
|
|
b1909b0435 | ||
|
|
1cc764988f | ||
|
|
da5c49f22d | ||
|
|
6b93b0b08c | ||
|
|
68f25217b8 | ||
|
|
0604b14263 | ||
|
|
a065dcdcd9 | ||
|
|
3d4a064674 | ||
|
|
e4dfd51337 | ||
|
|
cfe73af6f2 | ||
|
|
d6bd16b2c0 | ||
|
|
a49b146ccc | ||
|
|
fd9e157184 | ||
|
|
6cbd4020e8 | ||
|
|
3558b236cd | ||
|
|
48e82c4138 | ||
|
|
9eb70aea1d | ||
|
|
0460fbacc9 | ||
|
|
c73297e840 | ||
|
|
633d869ff4 | ||
|
|
e03f86cc54 | ||
|
|
a0a3566977 | ||
|
|
77fb9d415b | ||
|
|
bbdaa62175 | ||
|
|
31bffc7299 | ||
|
|
5f8f09d750 | ||
|
|
dcfe674ed4 | ||
|
|
bb195c2c2b | ||
|
|
8c59543ee3 | ||
|
|
db37e583ff | ||
|
|
d28e826e47 | ||
|
|
13beb85514 | ||
|
|
4b06bcc82c | ||
|
|
12ebc8d9d1 | ||
|
|
2b85e9e997 | ||
|
|
59b62c6507 | ||
|
|
2e760a9833 | ||
|
|
bab4a13a41 | ||
|
|
b56c7397ad | ||
|
|
689f346e97 | ||
|
|
249db0a59b | ||
|
|
9526ce95dd | ||
|
|
687158fe00 | ||
|
|
d8507332c1 | ||
|
|
67e983a354 | ||
|
|
f21058a6c0 | ||
|
|
b456af31e6 | ||
|
|
0441853d0f | ||
|
|
60a1c6b95b | ||
|
|
794e6c7a96 | ||
|
|
22afa2c7a3 | ||
|
|
85a89ca3e3 | ||
|
|
26776c0e60 | ||
|
|
e02ee8e59d | ||
|
|
134ecca9b0 | ||
|
|
c5f1ec8040 | ||
|
|
765ea9b79d | ||
|
|
c5bdc0054c | ||
|
|
0b7593d352 | ||
|
|
bd05ace08d | ||
|
|
fa56518f20 | ||
|
|
b577d7a55f | ||
|
|
d9dde7e6f3 | ||
|
|
2244b613cf | ||
|
|
c73bb33ff1 | ||
|
|
bcce184e60 | ||
|
|
326ebbb2fa | ||
|
|
07143ce15c | ||
|
|
dbc73c6c6c | ||
|
|
8becf13e8b | ||
|
|
9e2ebb24df | ||
|
|
90c56f5dc1 | ||
|
|
4b0291172e | ||
|
|
12e4757cf3 | ||
|
|
144c0734f5 | ||
|
|
f40f65f5d2 | ||
|
|
03c3f936cf | ||
|
|
66f41da365 | ||
|
|
7a986d731b | ||
|
|
eae0570a1c | ||
|
|
b57d98f847 | ||
|
|
762ac5aa9f | ||
|
|
1a4abd184f | ||
|
|
9524d7034c | ||
|
|
36d18e457b | ||
|
|
db562bc08d | ||
|
|
18672e6a78 | ||
|
|
32ad99701d | ||
|
|
63c5340cc4 | ||
|
|
a94371f67b | ||
|
|
22e4add562 | ||
|
|
b1a7bbd458 | ||
|
|
27ab2a6e13 | ||
|
|
68ea0a2b72 | ||
|
|
053a16799e | ||
|
|
d9456f0a11 | ||
|
|
48b67fc4a0 | ||
|
|
6c246f2ac5 | ||
|
|
a34565727b | ||
|
|
98cac9410c | ||
|
|
d3997bad9b | ||
|
|
e69eebb14a | ||
|
|
0145c89879 | ||
|
|
cc4736fa58 |
@@ -19,6 +19,7 @@ cmake .. \
|
|||||||
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
|
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
|
||||||
-DENABLE_QT_TRANSLATION=ON \
|
-DENABLE_QT_TRANSLATION=ON \
|
||||||
-DUSE_DISCORD_PRESENCE=ON \
|
-DUSE_DISCORD_PRESENCE=ON \
|
||||||
|
-DYUZU_CRASH_DUMPS=ON \
|
||||||
-DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \
|
-DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \
|
||||||
-DYUZU_USE_BUNDLED_FFMPEG=ON \
|
-DYUZU_USE_BUNDLED_FFMPEG=ON \
|
||||||
-GNinja
|
-GNinja
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ cmake .. \
|
|||||||
-DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \
|
-DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \
|
||||||
-DYUZU_USE_BUNDLED_FFMPEG=ON \
|
-DYUZU_USE_BUNDLED_FFMPEG=ON \
|
||||||
-DYUZU_ENABLE_LTO=ON \
|
-DYUZU_ENABLE_LTO=ON \
|
||||||
|
-DYUZU_CRASH_DUMPS=ON \
|
||||||
-GNinja
|
-GNinja
|
||||||
|
|
||||||
ninja
|
ninja
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ cmake .. \
|
|||||||
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
|
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
|
||||||
-DENABLE_QT_TRANSLATION=ON \
|
-DENABLE_QT_TRANSLATION=ON \
|
||||||
-DUSE_CCACHE=ON \
|
-DUSE_CCACHE=ON \
|
||||||
-DYUZU_CRASH_DUMPS=ON \
|
|
||||||
-DYUZU_USE_BUNDLED_SDL2=OFF \
|
-DYUZU_USE_BUNDLED_SDL2=OFF \
|
||||||
-DYUZU_USE_EXTERNAL_SDL2=OFF \
|
-DYUZU_USE_EXTERNAL_SDL2=OFF \
|
||||||
-DYUZU_TESTS=OFF \
|
-DYUZU_TESTS=OFF \
|
||||||
|
|||||||
@@ -3,4 +3,4 @@
|
|||||||
|
|
||||||
[codespell]
|
[codespell]
|
||||||
skip = ./.git,./build,./dist,./Doxyfile,./externals,./LICENSES,./src/android/app/src/main/res
|
skip = ./.git,./build,./dist,./Doxyfile,./externals,./LICENSES,./src/android/app/src/main/res
|
||||||
ignore-words-list = aci,allright,ba,canonicalizations,deques,froms,hda,inout,lod,masia,nam,nax,nd,optin,pullrequests,pullrequest,te,transfered,unstall,uscaled,zink
|
ignore-words-list = aci,allright,ba,canonicalizations,deques,froms,hda,inout,lod,masia,nam,nax,nd,optin,pullrequests,pullrequest,te,transfered,unstall,uscaled,vas,zink
|
||||||
|
|||||||
5
.gitmodules
vendored
5
.gitmodules
vendored
@@ -32,7 +32,7 @@
|
|||||||
path = externals/xbyak
|
path = externals/xbyak
|
||||||
url = https://github.com/herumi/xbyak.git
|
url = https://github.com/herumi/xbyak.git
|
||||||
[submodule "opus"]
|
[submodule "opus"]
|
||||||
path = externals/opus/opus
|
path = externals/opus
|
||||||
url = https://github.com/xiph/opus.git
|
url = https://github.com/xiph/opus.git
|
||||||
[submodule "SDL"]
|
[submodule "SDL"]
|
||||||
path = externals/SDL
|
path = externals/SDL
|
||||||
@@ -58,3 +58,6 @@
|
|||||||
[submodule "VulkanMemoryAllocator"]
|
[submodule "VulkanMemoryAllocator"]
|
||||||
path = externals/VulkanMemoryAllocator
|
path = externals/VulkanMemoryAllocator
|
||||||
url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git
|
url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git
|
||||||
|
[submodule "breakpad"]
|
||||||
|
path = externals/breakpad
|
||||||
|
url = https://github.com/yuzu-emu/breakpad.git
|
||||||
|
|||||||
@@ -147,3 +147,7 @@ License: GPL-3.0-or-later
|
|||||||
Files: src/android/gradle/wrapper/*
|
Files: src/android/gradle/wrapper/*
|
||||||
Copyright: 2023 yuzu Emulator Project
|
Copyright: 2023 yuzu Emulator Project
|
||||||
License: GPL-3.0-or-later
|
License: GPL-3.0-or-later
|
||||||
|
|
||||||
|
Files: externals/stb/*
|
||||||
|
Copyright: Sean Barrett
|
||||||
|
License: MIT
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ option(YUZU_DOWNLOAD_ANDROID_VVL "Download validation layer binary for android"
|
|||||||
|
|
||||||
CMAKE_DEPENDENT_OPTION(YUZU_ROOM "Compile LDN room server" ON "NOT ANDROID" OFF)
|
CMAKE_DEPENDENT_OPTION(YUZU_ROOM "Compile LDN room server" ON "NOT ANDROID" OFF)
|
||||||
|
|
||||||
CMAKE_DEPENDENT_OPTION(YUZU_CRASH_DUMPS "Compile Windows crash dump (Minidump) support" OFF "WIN32" OFF)
|
CMAKE_DEPENDENT_OPTION(YUZU_CRASH_DUMPS "Compile crash dump (Minidump) support" OFF "WIN32 OR LINUX" OFF)
|
||||||
|
|
||||||
option(YUZU_USE_BUNDLED_VCPKG "Use vcpkg for yuzu dependencies" "${MSVC}")
|
option(YUZU_USE_BUNDLED_VCPKG "Use vcpkg for yuzu dependencies" "${MSVC}")
|
||||||
|
|
||||||
@@ -139,9 +139,6 @@ if (YUZU_USE_BUNDLED_VCPKG)
|
|||||||
if (YUZU_TESTS)
|
if (YUZU_TESTS)
|
||||||
list(APPEND VCPKG_MANIFEST_FEATURES "yuzu-tests")
|
list(APPEND VCPKG_MANIFEST_FEATURES "yuzu-tests")
|
||||||
endif()
|
endif()
|
||||||
if (YUZU_CRASH_DUMPS)
|
|
||||||
list(APPEND VCPKG_MANIFEST_FEATURES "dbghelp")
|
|
||||||
endif()
|
|
||||||
if (ENABLE_WEB_SERVICE)
|
if (ENABLE_WEB_SERVICE)
|
||||||
list(APPEND VCPKG_MANIFEST_FEATURES "web-service")
|
list(APPEND VCPKG_MANIFEST_FEATURES "web-service")
|
||||||
endif()
|
endif()
|
||||||
@@ -294,6 +291,7 @@ find_package(lz4 REQUIRED)
|
|||||||
find_package(nlohmann_json 3.8 REQUIRED)
|
find_package(nlohmann_json 3.8 REQUIRED)
|
||||||
find_package(Opus 1.3 MODULE)
|
find_package(Opus 1.3 MODULE)
|
||||||
find_package(RenderDoc MODULE)
|
find_package(RenderDoc MODULE)
|
||||||
|
find_package(stb MODULE)
|
||||||
find_package(VulkanMemoryAllocator CONFIG)
|
find_package(VulkanMemoryAllocator CONFIG)
|
||||||
find_package(ZLIB 1.2 REQUIRED)
|
find_package(ZLIB 1.2 REQUIRED)
|
||||||
find_package(zstd 1.5 REQUIRED)
|
find_package(zstd 1.5 REQUIRED)
|
||||||
@@ -360,6 +358,9 @@ function(set_yuzu_qt_components)
|
|||||||
if (ENABLE_QT_TRANSLATION)
|
if (ENABLE_QT_TRANSLATION)
|
||||||
list(APPEND YUZU_QT_COMPONENTS2 LinguistTools)
|
list(APPEND YUZU_QT_COMPONENTS2 LinguistTools)
|
||||||
endif()
|
endif()
|
||||||
|
if (USE_DISCORD_PRESENCE)
|
||||||
|
list(APPEND YUZU_QT_COMPONENTS2 Network)
|
||||||
|
endif()
|
||||||
set(YUZU_QT_COMPONENTS ${YUZU_QT_COMPONENTS2} PARENT_SCOPE)
|
set(YUZU_QT_COMPONENTS ${YUZU_QT_COMPONENTS2} PARENT_SCOPE)
|
||||||
endfunction(set_yuzu_qt_components)
|
endfunction(set_yuzu_qt_components)
|
||||||
|
|
||||||
@@ -547,6 +548,18 @@ if (NOT YUZU_USE_BUNDLED_FFMPEG)
|
|||||||
find_package(FFmpeg 4.3 REQUIRED QUIET COMPONENTS ${FFmpeg_COMPONENTS})
|
find_package(FFmpeg 4.3 REQUIRED QUIET COMPONENTS ${FFmpeg_COMPONENTS})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (WIN32 AND YUZU_CRASH_DUMPS)
|
||||||
|
set(BREAKPAD_VER "breakpad-c89f9dd")
|
||||||
|
download_bundled_external("breakpad/" ${BREAKPAD_VER} BREAKPAD_PREFIX)
|
||||||
|
|
||||||
|
set(BREAKPAD_CLIENT_INCLUDE_DIR "${BREAKPAD_PREFIX}/include")
|
||||||
|
set(BREAKPAD_CLIENT_LIBRARY "${BREAKPAD_PREFIX}/lib/libbreakpad_client.lib")
|
||||||
|
|
||||||
|
add_library(libbreakpad_client INTERFACE IMPORTED)
|
||||||
|
target_link_libraries(libbreakpad_client INTERFACE "${BREAKPAD_CLIENT_LIBRARY}")
|
||||||
|
target_include_directories(libbreakpad_client INTERFACE "${BREAKPAD_CLIENT_INCLUDE_DIR}")
|
||||||
|
endif()
|
||||||
|
|
||||||
# Prefer the -pthread flag on Linux.
|
# Prefer the -pthread flag on Linux.
|
||||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
@@ -566,13 +579,6 @@ elseif (WIN32)
|
|||||||
# PSAPI is the Process Status API
|
# PSAPI is the Process Status API
|
||||||
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version)
|
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (YUZU_CRASH_DUMPS)
|
|
||||||
find_library(DBGHELP_LIBRARY dbghelp)
|
|
||||||
if ("${DBGHELP_LIBRARY}" STREQUAL "DBGHELP_LIBRARY-NOTFOUND")
|
|
||||||
message(FATAL_ERROR "YUZU_CRASH_DUMPS enabled but dbghelp library not found")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
elseif (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU|SunOS)$")
|
elseif (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU|SunOS)$")
|
||||||
set(PLATFORM_LIBRARIES rt)
|
set(PLATFORM_LIBRARIES rt)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
31
CMakeModules/Findstb.cmake
Normal file
31
CMakeModules/Findstb.cmake
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2023 Alexandre Bouvier <contact@amb.tf>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
find_path(stb_image_INCLUDE_DIR stb_image.h PATH_SUFFIXES stb)
|
||||||
|
find_path(stb_image_resize_INCLUDE_DIR stb_image_resize.h PATH_SUFFIXES stb)
|
||||||
|
find_path(stb_image_write_INCLUDE_DIR stb_image_write.h PATH_SUFFIXES stb)
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(stb
|
||||||
|
REQUIRED_VARS
|
||||||
|
stb_image_INCLUDE_DIR
|
||||||
|
stb_image_resize_INCLUDE_DIR
|
||||||
|
stb_image_write_INCLUDE_DIR
|
||||||
|
)
|
||||||
|
|
||||||
|
if (stb_FOUND AND NOT TARGET stb::headers)
|
||||||
|
add_library(stb::headers INTERFACE IMPORTED)
|
||||||
|
set_property(TARGET stb::headers PROPERTY
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES
|
||||||
|
"${stb_image_INCLUDE_DIR}"
|
||||||
|
"${stb_image_resize_INCLUDE_DIR}"
|
||||||
|
"${stb_image_write_INCLUDE_DIR}"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
mark_as_advanced(
|
||||||
|
stb_image_INCLUDE_DIR
|
||||||
|
stb_image_resize_INCLUDE_DIR
|
||||||
|
stb_image_write_INCLUDE_DIR
|
||||||
|
)
|
||||||
@@ -1,3 +1,11 @@
|
|||||||
|
| Pull Request | Commit | Title | Author | Merged? |
|
||||||
|
|----|----|----|----|----|
|
||||||
|
|
||||||
|
|
||||||
|
End of merge log. You can find the original README.md below the break.
|
||||||
|
|
||||||
|
-----
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
||||||
SPDX-License-Identifier: GPL-2.0-or-later
|
SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|||||||
1
dist/org.yuzu_emu.yuzu.desktop
vendored
1
dist/org.yuzu_emu.yuzu.desktop
vendored
@@ -13,3 +13,4 @@ Exec=yuzu %f
|
|||||||
Categories=Game;Emulator;Qt;
|
Categories=Game;Emulator;Qt;
|
||||||
MimeType=application/x-nx-nro;application/x-nx-nso;application/x-nx-nsp;application/x-nx-xci;
|
MimeType=application/x-nx-nro;application/x-nx-nso;application/x-nx-nsp;application/x-nx-xci;
|
||||||
Keywords=Nintendo;Switch;
|
Keywords=Nintendo;Switch;
|
||||||
|
StartupWMClass=yuzu
|
||||||
|
|||||||
112
externals/CMakeLists.txt
vendored
112
externals/CMakeLists.txt
vendored
@@ -134,6 +134,10 @@ endif()
|
|||||||
|
|
||||||
# Opus
|
# Opus
|
||||||
if (NOT TARGET Opus::opus)
|
if (NOT TARGET Opus::opus)
|
||||||
|
set(OPUS_BUILD_TESTING OFF)
|
||||||
|
set(OPUS_BUILD_PROGRAMS OFF)
|
||||||
|
set(OPUS_INSTALL_PKG_CONFIG_MODULE OFF)
|
||||||
|
set(OPUS_INSTALL_CMAKE_CONFIG_MODULE OFF)
|
||||||
add_subdirectory(opus)
|
add_subdirectory(opus)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -168,9 +172,13 @@ if (NOT TARGET LLVM::Demangle)
|
|||||||
add_library(LLVM::Demangle ALIAS demangle)
|
add_library(LLVM::Demangle ALIAS demangle)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_library(stb stb/stb_dxt.cpp stb/stb_image.cpp stb/stb_image_resize.cpp)
|
add_library(stb stb/stb_dxt.cpp)
|
||||||
target_include_directories(stb PUBLIC ./stb)
|
target_include_directories(stb PUBLIC ./stb)
|
||||||
|
|
||||||
|
if (NOT TARGET stb::headers)
|
||||||
|
add_library(stb::headers ALIAS stb)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_library(bc_decoder bc_decoder/bc_decoder.cpp)
|
add_library(bc_decoder bc_decoder/bc_decoder.cpp)
|
||||||
target_include_directories(bc_decoder PUBLIC ./bc_decoder)
|
target_include_directories(bc_decoder PUBLIC ./bc_decoder)
|
||||||
|
|
||||||
@@ -185,3 +193,105 @@ if (ANDROID)
|
|||||||
add_subdirectory(libadrenotools)
|
add_subdirectory(libadrenotools)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Breakpad
|
||||||
|
# https://github.com/microsoft/vcpkg/blob/master/ports/breakpad/CMakeLists.txt
|
||||||
|
if (YUZU_CRASH_DUMPS AND NOT TARGET libbreakpad_client)
|
||||||
|
set(BREAKPAD_WIN32_DEFINES
|
||||||
|
NOMINMAX
|
||||||
|
UNICODE
|
||||||
|
WIN32_LEAN_AND_MEAN
|
||||||
|
_CRT_SECURE_NO_WARNINGS
|
||||||
|
_CRT_SECURE_NO_DEPRECATE
|
||||||
|
_CRT_NONSTDC_NO_DEPRECATE
|
||||||
|
)
|
||||||
|
|
||||||
|
# libbreakpad
|
||||||
|
add_library(libbreakpad STATIC)
|
||||||
|
file(GLOB_RECURSE LIBBREAKPAD_SOURCES breakpad/src/processor/*.cc)
|
||||||
|
file(GLOB_RECURSE LIBDISASM_SOURCES breakpad/src/third_party/libdisasm/*.c)
|
||||||
|
list(FILTER LIBBREAKPAD_SOURCES EXCLUDE REGEX "_unittest|_selftest|synth_minidump|/tests|/testdata|/solaris|microdump_stackwalk|minidump_dump|minidump_stackwalk")
|
||||||
|
if (WIN32)
|
||||||
|
list(FILTER LIBBREAKPAD_SOURCES EXCLUDE REGEX "/linux|/mac|/android")
|
||||||
|
target_compile_definitions(libbreakpad PRIVATE ${BREAKPAD_WIN32_DEFINES})
|
||||||
|
target_include_directories(libbreakpad PRIVATE "${CMAKE_GENERATOR_INSTANCE}/DIA SDK/include")
|
||||||
|
elseif (APPLE)
|
||||||
|
list(FILTER LIBBREAKPAD_SOURCES EXCLUDE REGEX "/linux|/windows|/android")
|
||||||
|
else()
|
||||||
|
list(FILTER LIBBREAKPAD_SOURCES EXCLUDE REGEX "/mac|/windows|/android")
|
||||||
|
endif()
|
||||||
|
target_sources(libbreakpad PRIVATE ${LIBBREAKPAD_SOURCES} ${LIBDISASM_SOURCES})
|
||||||
|
target_include_directories(libbreakpad
|
||||||
|
PUBLIC
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/breakpad/src
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/breakpad/src/third_party/libdisasm
|
||||||
|
)
|
||||||
|
|
||||||
|
# libbreakpad_client
|
||||||
|
add_library(libbreakpad_client STATIC)
|
||||||
|
file(GLOB LIBBREAKPAD_COMMON_SOURCES breakpad/src/common/*.cc breakpad/src/common/*.c breakpad/src/client/*.cc)
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
file(GLOB_RECURSE LIBBREAKPAD_CLIENT_SOURCES breakpad/src/client/windows/*.cc breakpad/src/common/windows/*.cc)
|
||||||
|
list(FILTER LIBBREAKPAD_COMMON_SOURCES EXCLUDE REGEX "language.cc|path_helper.cc|stabs_to_module.cc|stabs_reader.cc|minidump_file_writer.cc")
|
||||||
|
target_include_directories(libbreakpad_client PRIVATE "${CMAKE_GENERATOR_INSTANCE}/DIA SDK/include")
|
||||||
|
target_compile_definitions(libbreakpad_client PRIVATE ${BREAKPAD_WIN32_DEFINES})
|
||||||
|
elseif (APPLE)
|
||||||
|
target_compile_definitions(libbreakpad_client PRIVATE HAVE_MACH_O_NLIST_H)
|
||||||
|
file(GLOB_RECURSE LIBBREAKPAD_CLIENT_SOURCES breakpad/src/client/mac/*.cc breakpad/src/common/mac/*.cc)
|
||||||
|
list(APPEND LIBBREAKPAD_CLIENT_SOURCES breakpad/src/common/mac/MachIPC.mm)
|
||||||
|
else()
|
||||||
|
target_compile_definitions(libbreakpad_client PUBLIC -DHAVE_A_OUT_H)
|
||||||
|
file(GLOB_RECURSE LIBBREAKPAD_CLIENT_SOURCES breakpad/src/client/linux/*.cc breakpad/src/common/linux/*.cc)
|
||||||
|
endif()
|
||||||
|
list(APPEND LIBBREAKPAD_CLIENT_SOURCES ${LIBBREAKPAD_COMMON_SOURCES})
|
||||||
|
list(FILTER LIBBREAKPAD_CLIENT_SOURCES EXCLUDE REGEX "/sender|/tests|/unittests|/testcases|_unittest|_test")
|
||||||
|
target_sources(libbreakpad_client PRIVATE ${LIBBREAKPAD_CLIENT_SOURCES})
|
||||||
|
target_include_directories(libbreakpad_client PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/breakpad/src)
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
target_link_libraries(libbreakpad_client PRIVATE wininet.lib)
|
||||||
|
elseif (APPLE)
|
||||||
|
find_library(CoreFoundation_FRAMEWORK CoreFoundation)
|
||||||
|
target_link_libraries(libbreakpad_client PRIVATE ${CoreFoundation_FRAMEWORK})
|
||||||
|
else()
|
||||||
|
find_library(PTHREAD_LIBRARIES pthread)
|
||||||
|
target_compile_definitions(libbreakpad_client PRIVATE HAVE_GETCONTEXT=1)
|
||||||
|
if (PTHREAD_LIBRARIES)
|
||||||
|
target_link_libraries(libbreakpad_client PRIVATE ${PTHREAD_LIBRARIES})
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Host tools for symbol processing
|
||||||
|
if (LINUX)
|
||||||
|
find_package(ZLIB REQUIRED)
|
||||||
|
|
||||||
|
add_executable(minidump_stackwalk breakpad/src/processor/minidump_stackwalk.cc)
|
||||||
|
target_link_libraries(minidump_stackwalk PRIVATE libbreakpad libbreakpad_client)
|
||||||
|
|
||||||
|
add_executable(dump_syms
|
||||||
|
breakpad/src/common/dwarf_cfi_to_module.cc
|
||||||
|
breakpad/src/common/dwarf_cu_to_module.cc
|
||||||
|
breakpad/src/common/dwarf_line_to_module.cc
|
||||||
|
breakpad/src/common/dwarf_range_list_handler.cc
|
||||||
|
breakpad/src/common/language.cc
|
||||||
|
breakpad/src/common/module.cc
|
||||||
|
breakpad/src/common/path_helper.cc
|
||||||
|
breakpad/src/common/stabs_reader.cc
|
||||||
|
breakpad/src/common/stabs_to_module.cc
|
||||||
|
breakpad/src/common/dwarf/bytereader.cc
|
||||||
|
breakpad/src/common/dwarf/dwarf2diehandler.cc
|
||||||
|
breakpad/src/common/dwarf/dwarf2reader.cc
|
||||||
|
breakpad/src/common/dwarf/elf_reader.cc
|
||||||
|
breakpad/src/common/linux/crc32.cc
|
||||||
|
breakpad/src/common/linux/dump_symbols.cc
|
||||||
|
breakpad/src/common/linux/elf_symbols_to_module.cc
|
||||||
|
breakpad/src/common/linux/elfutils.cc
|
||||||
|
breakpad/src/common/linux/file_id.cc
|
||||||
|
breakpad/src/common/linux/linux_libc_support.cc
|
||||||
|
breakpad/src/common/linux/memory_mapped_file.cc
|
||||||
|
breakpad/src/common/linux/safe_readlink.cc
|
||||||
|
breakpad/src/tools/linux/dump_syms/dump_syms.cc)
|
||||||
|
target_link_libraries(dump_syms PRIVATE libbreakpad_client ZLIB::ZLIB)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|||||||
2
externals/SDL
vendored
2
externals/SDL
vendored
Submodule externals/SDL updated: 031912c4b6...cc016b0046
2
externals/Vulkan-Headers
vendored
2
externals/Vulkan-Headers
vendored
Submodule externals/Vulkan-Headers updated: ed857118e2...df60f03168
2
externals/VulkanMemoryAllocator
vendored
2
externals/VulkanMemoryAllocator
vendored
Submodule externals/VulkanMemoryAllocator updated: 9b0fc3e7b0...2f382df218
1
externals/breakpad
vendored
Submodule
1
externals/breakpad
vendored
Submodule
Submodule externals/breakpad added at c89f9dddc7
2
externals/cpp-httplib
vendored
2
externals/cpp-httplib
vendored
Submodule externals/cpp-httplib updated: 6d963fbe8d...a609330e4c
2
externals/cpp-jwt
vendored
2
externals/cpp-jwt
vendored
Submodule externals/cpp-jwt updated: e12ef06218...10ef5735d8
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: 7da378033a...0df09e2f6b
2
externals/ffmpeg/ffmpeg
vendored
2
externals/ffmpeg/ffmpeg
vendored
Submodule externals/ffmpeg/ffmpeg updated: 6b6b9e593d...9c1294eadd
2
externals/inih/inih
vendored
2
externals/inih/inih
vendored
Submodule externals/inih/inih updated: 1e80a47dff...9cecf0643d
7
externals/libusb/CMakeLists.txt
vendored
7
externals/libusb/CMakeLists.txt
vendored
@@ -49,11 +49,6 @@ if (MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux") OR APPLE)
|
|||||||
|
|
||||||
set(LIBUSB_INCLUDE_DIRS "${LIBUSB_SRC_DIR}/libusb" CACHE PATH "libusb headers path" FORCE)
|
set(LIBUSB_INCLUDE_DIRS "${LIBUSB_SRC_DIR}/libusb" CACHE PATH "libusb headers path" FORCE)
|
||||||
|
|
||||||
# MINGW: causes "externals/libusb/libusb/libusb/os/windows_winusb.c:1427:2: error: conversion to non-scalar type requested", so cannot statically link it for now.
|
|
||||||
if (NOT MINGW)
|
|
||||||
set(LIBUSB_CFLAGS "-DGUID_DEVINTERFACE_USB_DEVICE=\\(GUID\\){0xA5DCBF10,0x6530,0x11D2,{0x90,0x1F,0x00,0xC0,0x4F,0xB9,0x51,0xED}}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
make_directory("${LIBUSB_PREFIX}")
|
make_directory("${LIBUSB_PREFIX}")
|
||||||
|
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
@@ -146,8 +141,6 @@ else() # MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
|||||||
target_include_directories(usb BEFORE PRIVATE libusb/msvc)
|
target_include_directories(usb BEFORE PRIVATE libusb/msvc)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Works around other libraries providing their own definition of USB GUIDs (e.g. SDL2)
|
|
||||||
target_compile_definitions(usb PRIVATE "-DGUID_DEVINTERFACE_USB_DEVICE=(GUID){ 0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED}}")
|
|
||||||
else()
|
else()
|
||||||
target_include_directories(usb
|
target_include_directories(usb
|
||||||
# turns out other projects also have "config.h", so make sure the
|
# turns out other projects also have "config.h", so make sure the
|
||||||
|
|||||||
2
externals/libusb/libusb
vendored
2
externals/libusb/libusb
vendored
Submodule externals/libusb/libusb updated: c6a35c5601...c060e9ce30
1
externals/opus
vendored
Submodule
1
externals/opus
vendored
Submodule
Submodule externals/opus added at 101a71e03b
259
externals/opus/CMakeLists.txt
vendored
259
externals/opus/CMakeLists.txt
vendored
@@ -1,259 +0,0 @@
|
|||||||
# SPDX-FileCopyrightText: 2019 yuzu Emulator Project
|
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.8)
|
|
||||||
|
|
||||||
project(opus)
|
|
||||||
|
|
||||||
option(OPUS_STACK_PROTECTOR "Use stack protection" OFF)
|
|
||||||
option(OPUS_USE_ALLOCA "Use alloca for stack arrays (on non-C99 compilers)" OFF)
|
|
||||||
option(OPUS_CUSTOM_MODES "Enable non-Opus modes, e.g. 44.1 kHz & 2^n frames" OFF)
|
|
||||||
option(OPUS_FIXED_POINT "Compile as fixed-point (for machines without a fast enough FPU)" OFF)
|
|
||||||
option(OPUS_ENABLE_FLOAT_API "Compile with the floating point API (for machines with float library" ON)
|
|
||||||
|
|
||||||
include(opus/opus_functions.cmake)
|
|
||||||
|
|
||||||
if(OPUS_STACK_PROTECTOR)
|
|
||||||
if(NOT MSVC) # GC on by default on MSVC
|
|
||||||
check_and_set_flag(STACK_PROTECTION_STRONG -fstack-protector-strong)
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
if(MSVC)
|
|
||||||
check_and_set_flag(BUFFER_SECURITY_CHECK /GS-)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_library(opus
|
|
||||||
# CELT sources
|
|
||||||
opus/celt/bands.c
|
|
||||||
opus/celt/celt.c
|
|
||||||
opus/celt/celt_decoder.c
|
|
||||||
opus/celt/celt_encoder.c
|
|
||||||
opus/celt/celt_lpc.c
|
|
||||||
opus/celt/cwrs.c
|
|
||||||
opus/celt/entcode.c
|
|
||||||
opus/celt/entdec.c
|
|
||||||
opus/celt/entenc.c
|
|
||||||
opus/celt/kiss_fft.c
|
|
||||||
opus/celt/laplace.c
|
|
||||||
opus/celt/mathops.c
|
|
||||||
opus/celt/mdct.c
|
|
||||||
opus/celt/modes.c
|
|
||||||
opus/celt/pitch.c
|
|
||||||
opus/celt/quant_bands.c
|
|
||||||
opus/celt/rate.c
|
|
||||||
opus/celt/vq.c
|
|
||||||
|
|
||||||
# SILK sources
|
|
||||||
opus/silk/A2NLSF.c
|
|
||||||
opus/silk/CNG.c
|
|
||||||
opus/silk/HP_variable_cutoff.c
|
|
||||||
opus/silk/LPC_analysis_filter.c
|
|
||||||
opus/silk/LPC_fit.c
|
|
||||||
opus/silk/LPC_inv_pred_gain.c
|
|
||||||
opus/silk/LP_variable_cutoff.c
|
|
||||||
opus/silk/NLSF2A.c
|
|
||||||
opus/silk/NLSF_VQ.c
|
|
||||||
opus/silk/NLSF_VQ_weights_laroia.c
|
|
||||||
opus/silk/NLSF_decode.c
|
|
||||||
opus/silk/NLSF_del_dec_quant.c
|
|
||||||
opus/silk/NLSF_encode.c
|
|
||||||
opus/silk/NLSF_stabilize.c
|
|
||||||
opus/silk/NLSF_unpack.c
|
|
||||||
opus/silk/NSQ.c
|
|
||||||
opus/silk/NSQ_del_dec.c
|
|
||||||
opus/silk/PLC.c
|
|
||||||
opus/silk/VAD.c
|
|
||||||
opus/silk/VQ_WMat_EC.c
|
|
||||||
opus/silk/ana_filt_bank_1.c
|
|
||||||
opus/silk/biquad_alt.c
|
|
||||||
opus/silk/bwexpander.c
|
|
||||||
opus/silk/bwexpander_32.c
|
|
||||||
opus/silk/check_control_input.c
|
|
||||||
opus/silk/code_signs.c
|
|
||||||
opus/silk/control_SNR.c
|
|
||||||
opus/silk/control_audio_bandwidth.c
|
|
||||||
opus/silk/control_codec.c
|
|
||||||
opus/silk/dec_API.c
|
|
||||||
opus/silk/decode_core.c
|
|
||||||
opus/silk/decode_frame.c
|
|
||||||
opus/silk/decode_indices.c
|
|
||||||
opus/silk/decode_parameters.c
|
|
||||||
opus/silk/decode_pitch.c
|
|
||||||
opus/silk/decode_pulses.c
|
|
||||||
opus/silk/decoder_set_fs.c
|
|
||||||
opus/silk/enc_API.c
|
|
||||||
opus/silk/encode_indices.c
|
|
||||||
opus/silk/encode_pulses.c
|
|
||||||
opus/silk/gain_quant.c
|
|
||||||
opus/silk/init_decoder.c
|
|
||||||
opus/silk/init_encoder.c
|
|
||||||
opus/silk/inner_prod_aligned.c
|
|
||||||
opus/silk/interpolate.c
|
|
||||||
opus/silk/lin2log.c
|
|
||||||
opus/silk/log2lin.c
|
|
||||||
opus/silk/pitch_est_tables.c
|
|
||||||
opus/silk/process_NLSFs.c
|
|
||||||
opus/silk/quant_LTP_gains.c
|
|
||||||
opus/silk/resampler.c
|
|
||||||
opus/silk/resampler_down2.c
|
|
||||||
opus/silk/resampler_down2_3.c
|
|
||||||
opus/silk/resampler_private_AR2.c
|
|
||||||
opus/silk/resampler_private_IIR_FIR.c
|
|
||||||
opus/silk/resampler_private_down_FIR.c
|
|
||||||
opus/silk/resampler_private_up2_HQ.c
|
|
||||||
opus/silk/resampler_rom.c
|
|
||||||
opus/silk/shell_coder.c
|
|
||||||
opus/silk/sigm_Q15.c
|
|
||||||
opus/silk/sort.c
|
|
||||||
opus/silk/stereo_LR_to_MS.c
|
|
||||||
opus/silk/stereo_MS_to_LR.c
|
|
||||||
opus/silk/stereo_decode_pred.c
|
|
||||||
opus/silk/stereo_encode_pred.c
|
|
||||||
opus/silk/stereo_find_predictor.c
|
|
||||||
opus/silk/stereo_quant_pred.c
|
|
||||||
opus/silk/sum_sqr_shift.c
|
|
||||||
opus/silk/table_LSF_cos.c
|
|
||||||
opus/silk/tables_LTP.c
|
|
||||||
opus/silk/tables_NLSF_CB_NB_MB.c
|
|
||||||
opus/silk/tables_NLSF_CB_WB.c
|
|
||||||
opus/silk/tables_gain.c
|
|
||||||
opus/silk/tables_other.c
|
|
||||||
opus/silk/tables_pitch_lag.c
|
|
||||||
opus/silk/tables_pulses_per_block.c
|
|
||||||
|
|
||||||
# Opus sources
|
|
||||||
opus/src/analysis.c
|
|
||||||
opus/src/mapping_matrix.c
|
|
||||||
opus/src/mlp.c
|
|
||||||
opus/src/mlp_data.c
|
|
||||||
opus/src/opus.c
|
|
||||||
opus/src/opus_decoder.c
|
|
||||||
opus/src/opus_encoder.c
|
|
||||||
opus/src/opus_multistream.c
|
|
||||||
opus/src/opus_multistream_decoder.c
|
|
||||||
opus/src/opus_multistream_encoder.c
|
|
||||||
opus/src/opus_projection_decoder.c
|
|
||||||
opus/src/opus_projection_encoder.c
|
|
||||||
opus/src/repacketizer.c
|
|
||||||
)
|
|
||||||
|
|
||||||
if (DEBUG)
|
|
||||||
target_sources(opus PRIVATE opus/silk/debug.c)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (OPUS_FIXED_POINT)
|
|
||||||
target_sources(opus PRIVATE
|
|
||||||
opus/silk/fixed/LTP_analysis_filter_FIX.c
|
|
||||||
opus/silk/fixed/LTP_scale_ctrl_FIX.c
|
|
||||||
opus/silk/fixed/apply_sine_window_FIX.c
|
|
||||||
opus/silk/fixed/autocorr_FIX.c
|
|
||||||
opus/silk/fixed/burg_modified_FIX.c
|
|
||||||
opus/silk/fixed/corrMatrix_FIX.c
|
|
||||||
opus/silk/fixed/encode_frame_FIX.c
|
|
||||||
opus/silk/fixed/find_LPC_FIX.c
|
|
||||||
opus/silk/fixed/find_LTP_FIX.c
|
|
||||||
opus/silk/fixed/find_pitch_lags_FIX.c
|
|
||||||
opus/silk/fixed/find_pred_coefs_FIX.c
|
|
||||||
opus/silk/fixed/k2a_FIX.c
|
|
||||||
opus/silk/fixed/k2a_Q16_FIX.c
|
|
||||||
opus/silk/fixed/noise_shape_analysis_FIX.c
|
|
||||||
opus/silk/fixed/pitch_analysis_core_FIX.c
|
|
||||||
opus/silk/fixed/prefilter_FIX.c
|
|
||||||
opus/silk/fixed/process_gains_FIX.c
|
|
||||||
opus/silk/fixed/regularize_correlations_FIX.c
|
|
||||||
opus/silk/fixed/residual_energy16_FIX.c
|
|
||||||
opus/silk/fixed/residual_energy_FIX.c
|
|
||||||
opus/silk/fixed/schur64_FIX.c
|
|
||||||
opus/silk/fixed/schur_FIX.c
|
|
||||||
opus/silk/fixed/solve_LS_FIX.c
|
|
||||||
opus/silk/fixed/vector_ops_FIX.c
|
|
||||||
opus/silk/fixed/warped_autocorrelation_FIX.c
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
target_sources(opus PRIVATE
|
|
||||||
opus/silk/float/LPC_analysis_filter_FLP.c
|
|
||||||
opus/silk/float/LPC_inv_pred_gain_FLP.c
|
|
||||||
opus/silk/float/LTP_analysis_filter_FLP.c
|
|
||||||
opus/silk/float/LTP_scale_ctrl_FLP.c
|
|
||||||
opus/silk/float/apply_sine_window_FLP.c
|
|
||||||
opus/silk/float/autocorrelation_FLP.c
|
|
||||||
opus/silk/float/burg_modified_FLP.c
|
|
||||||
opus/silk/float/bwexpander_FLP.c
|
|
||||||
opus/silk/float/corrMatrix_FLP.c
|
|
||||||
opus/silk/float/encode_frame_FLP.c
|
|
||||||
opus/silk/float/energy_FLP.c
|
|
||||||
opus/silk/float/find_LPC_FLP.c
|
|
||||||
opus/silk/float/find_LTP_FLP.c
|
|
||||||
opus/silk/float/find_pitch_lags_FLP.c
|
|
||||||
opus/silk/float/find_pred_coefs_FLP.c
|
|
||||||
opus/silk/float/inner_product_FLP.c
|
|
||||||
opus/silk/float/k2a_FLP.c
|
|
||||||
opus/silk/float/noise_shape_analysis_FLP.c
|
|
||||||
opus/silk/float/pitch_analysis_core_FLP.c
|
|
||||||
opus/silk/float/process_gains_FLP.c
|
|
||||||
opus/silk/float/regularize_correlations_FLP.c
|
|
||||||
opus/silk/float/residual_energy_FLP.c
|
|
||||||
opus/silk/float/scale_copy_vector_FLP.c
|
|
||||||
opus/silk/float/scale_vector_FLP.c
|
|
||||||
opus/silk/float/schur_FLP.c
|
|
||||||
opus/silk/float/sort_FLP.c
|
|
||||||
opus/silk/float/warped_autocorrelation_FLP.c
|
|
||||||
opus/silk/float/wrappers_FLP.c
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_compile_definitions(opus PRIVATE OPUS_BUILD ENABLE_HARDENING)
|
|
||||||
|
|
||||||
if(NOT MSVC)
|
|
||||||
if(MINGW)
|
|
||||||
target_compile_definitions(opus PRIVATE _FORTIFY_SOURCE=0)
|
|
||||||
else()
|
|
||||||
target_compile_definitions(opus PRIVATE _FORTIFY_SOURCE=2)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# It is strongly recommended to uncomment one of these VAR_ARRAYS: Use C99
|
|
||||||
# variable-length arrays for stack allocation USE_ALLOCA: Use alloca() for stack
|
|
||||||
# allocation If none is defined, then the fallback is a non-threadsafe global
|
|
||||||
# array
|
|
||||||
if(OPUS_USE_ALLOCA OR MSVC)
|
|
||||||
target_compile_definitions(opus PRIVATE USE_ALLOCA)
|
|
||||||
else()
|
|
||||||
target_compile_definitions(opus PRIVATE VAR_ARRAYS)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(OPUS_CUSTOM_MODES)
|
|
||||||
target_compile_definitions(opus PRIVATE CUSTOM_MODES)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT OPUS_ENABLE_FLOAT_API)
|
|
||||||
target_compile_definitions(opus PRIVATE DISABLE_FLOAT_API)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_compile_definitions(opus
|
|
||||||
PUBLIC
|
|
||||||
-DOPUS_VERSION="\\"1.3.1\\""
|
|
||||||
|
|
||||||
PRIVATE
|
|
||||||
# Use C99 intrinsics to speed up float-to-int conversion
|
|
||||||
HAVE_LRINTF
|
|
||||||
)
|
|
||||||
|
|
||||||
if (FIXED_POINT)
|
|
||||||
target_compile_definitions(opus PRIVATE -DFIXED_POINT=1 -DDISABLE_FLOAT_API)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_include_directories(opus
|
|
||||||
PUBLIC
|
|
||||||
opus/include
|
|
||||||
|
|
||||||
PRIVATE
|
|
||||||
opus/celt
|
|
||||||
opus/silk
|
|
||||||
opus/silk/fixed
|
|
||||||
opus/silk/float
|
|
||||||
opus/src
|
|
||||||
)
|
|
||||||
|
|
||||||
add_library(Opus::opus ALIAS opus)
|
|
||||||
1
externals/opus/opus
vendored
1
externals/opus/opus
vendored
Submodule externals/opus/opus deleted from ad8fe90db7
7529
externals/stb/stb_image.cpp
vendored
7529
externals/stb/stb_image.cpp
vendored
File diff suppressed because it is too large
Load Diff
7221
externals/stb/stb_image.h
vendored
7221
externals/stb/stb_image.h
vendored
File diff suppressed because it is too large
Load Diff
2282
externals/stb/stb_image_resize.cpp
vendored
2282
externals/stb/stb_image_resize.cpp
vendored
File diff suppressed because it is too large
Load Diff
2214
externals/stb/stb_image_resize.h
vendored
2214
externals/stb/stb_image_resize.h
vendored
File diff suppressed because it is too large
Load Diff
1724
externals/stb/stb_image_write.h
vendored
Normal file
1724
externals/stb/stb_image_write.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2
externals/vcpkg
vendored
2
externals/vcpkg
vendored
Submodule externals/vcpkg updated: cbf56573a9...ef2eef1734
@@ -5,6 +5,7 @@ package org.yuzu.yuzu_emu
|
|||||||
|
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.Html
|
import android.text.Html
|
||||||
import android.text.method.LinkMovementMethod
|
import android.text.method.LinkMovementMethod
|
||||||
@@ -16,7 +17,7 @@ import androidx.fragment.app.DialogFragment
|
|||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import org.yuzu.yuzu_emu.activities.EmulationActivity
|
import org.yuzu.yuzu_emu.activities.EmulationActivity
|
||||||
import org.yuzu.yuzu_emu.utils.DocumentsTree.Companion.isNativePath
|
import org.yuzu.yuzu_emu.utils.DocumentsTree
|
||||||
import org.yuzu.yuzu_emu.utils.FileUtil
|
import org.yuzu.yuzu_emu.utils.FileUtil
|
||||||
import org.yuzu.yuzu_emu.utils.Log
|
import org.yuzu.yuzu_emu.utils.Log
|
||||||
import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
|
import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
|
||||||
@@ -68,7 +69,7 @@ object NativeLibrary {
|
|||||||
@Keep
|
@Keep
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun openContentUri(path: String?, openmode: String?): Int {
|
fun openContentUri(path: String?, openmode: String?): Int {
|
||||||
return if (isNativePath(path!!)) {
|
return if (DocumentsTree.isNativePath(path!!)) {
|
||||||
YuzuApplication.documentsTree!!.openContentUri(path, openmode)
|
YuzuApplication.documentsTree!!.openContentUri(path, openmode)
|
||||||
} else {
|
} else {
|
||||||
FileUtil.openContentUri(path, openmode)
|
FileUtil.openContentUri(path, openmode)
|
||||||
@@ -78,7 +79,7 @@ object NativeLibrary {
|
|||||||
@Keep
|
@Keep
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getSize(path: String?): Long {
|
fun getSize(path: String?): Long {
|
||||||
return if (isNativePath(path!!)) {
|
return if (DocumentsTree.isNativePath(path!!)) {
|
||||||
YuzuApplication.documentsTree!!.getFileSize(path)
|
YuzuApplication.documentsTree!!.getFileSize(path)
|
||||||
} else {
|
} else {
|
||||||
FileUtil.getFileSize(path)
|
FileUtil.getFileSize(path)
|
||||||
@@ -88,23 +89,41 @@ object NativeLibrary {
|
|||||||
@Keep
|
@Keep
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun exists(path: String?): Boolean {
|
fun exists(path: String?): Boolean {
|
||||||
return if (isNativePath(path!!)) {
|
return if (DocumentsTree.isNativePath(path!!)) {
|
||||||
YuzuApplication.documentsTree!!.exists(path)
|
YuzuApplication.documentsTree!!.exists(path)
|
||||||
} else {
|
} else {
|
||||||
FileUtil.exists(path)
|
FileUtil.exists(path, suppressLog = true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun isDirectory(path: String?): Boolean {
|
fun isDirectory(path: String?): Boolean {
|
||||||
return if (isNativePath(path!!)) {
|
return if (DocumentsTree.isNativePath(path!!)) {
|
||||||
YuzuApplication.documentsTree!!.isDirectory(path)
|
YuzuApplication.documentsTree!!.isDirectory(path)
|
||||||
} else {
|
} else {
|
||||||
FileUtil.isDirectory(path)
|
FileUtil.isDirectory(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
@JvmStatic
|
||||||
|
fun getParentDirectory(path: String): String =
|
||||||
|
if (DocumentsTree.isNativePath(path)) {
|
||||||
|
YuzuApplication.documentsTree!!.getParentDirectory(path)
|
||||||
|
} else {
|
||||||
|
path
|
||||||
|
}
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
@JvmStatic
|
||||||
|
fun getFilename(path: String): String =
|
||||||
|
if (DocumentsTree.isNativePath(path)) {
|
||||||
|
YuzuApplication.documentsTree!!.getFilename(path)
|
||||||
|
} else {
|
||||||
|
FileUtil.getFilename(Uri.parse(path))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if pro controller isn't available and handheld is
|
* Returns true if pro controller isn't available and handheld is
|
||||||
*/
|
*/
|
||||||
@@ -215,32 +234,6 @@ object NativeLibrary {
|
|||||||
|
|
||||||
external fun initGameIni(gameID: String?)
|
external fun initGameIni(gameID: String?)
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the embedded icon within the given ROM.
|
|
||||||
*
|
|
||||||
* @param filename the file path to the ROM.
|
|
||||||
* @return a byte array containing the JPEG data for the icon.
|
|
||||||
*/
|
|
||||||
external fun getIcon(filename: String): ByteArray
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the embedded title of the given ISO/ROM.
|
|
||||||
*
|
|
||||||
* @param filename The file path to the ISO/ROM.
|
|
||||||
* @return the embedded title of the ISO/ROM.
|
|
||||||
*/
|
|
||||||
external fun getTitle(filename: String): String
|
|
||||||
|
|
||||||
external fun getDescription(filename: String): String
|
|
||||||
|
|
||||||
external fun getGameId(filename: String): String
|
|
||||||
|
|
||||||
external fun getRegions(filename: String): String
|
|
||||||
|
|
||||||
external fun getCompany(filename: String): String
|
|
||||||
|
|
||||||
external fun isHomebrew(filename: String): Boolean
|
|
||||||
|
|
||||||
external fun setAppDirectory(directory: String)
|
external fun setAppDirectory(directory: String)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -293,11 +286,6 @@ object NativeLibrary {
|
|||||||
*/
|
*/
|
||||||
external fun stopEmulation()
|
external fun stopEmulation()
|
||||||
|
|
||||||
/**
|
|
||||||
* Resets the in-memory ROM metadata cache.
|
|
||||||
*/
|
|
||||||
external fun resetRomMetadata()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if emulation is running (or is paused).
|
* Returns true if emulation is running (or is paused).
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ class GameAdapter(private val activity: AppCompatActivity) :
|
|||||||
|
|
||||||
private class DiffCallback : DiffUtil.ItemCallback<Game>() {
|
private class DiffCallback : DiffUtil.ItemCallback<Game>() {
|
||||||
override fun areItemsTheSame(oldItem: Game, newItem: Game): Boolean {
|
override fun areItemsTheSame(oldItem: Game, newItem: Game): Boolean {
|
||||||
return oldItem.gameId == newItem.gameId
|
return oldItem.programId == newItem.programId
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun areContentsTheSame(oldItem: Game, newItem: Game): Boolean {
|
override fun areContentsTheSame(oldItem: Game, newItem: Game): Boolean {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import android.net.Uri
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
|
import android.os.SystemClock
|
||||||
import android.view.*
|
import android.view.*
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
@@ -25,6 +26,7 @@ import androidx.core.graphics.Insets
|
|||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.drawerlayout.widget.DrawerLayout
|
import androidx.drawerlayout.widget.DrawerLayout
|
||||||
|
import androidx.drawerlayout.widget.DrawerLayout.DrawerListener
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
@@ -156,6 +158,32 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
binding.showFpsText.setTextColor(Color.YELLOW)
|
binding.showFpsText.setTextColor(Color.YELLOW)
|
||||||
binding.doneControlConfig.setOnClickListener { stopConfiguringControls() }
|
binding.doneControlConfig.setOnClickListener { stopConfiguringControls() }
|
||||||
|
|
||||||
|
binding.drawerLayout.addDrawerListener(object : DrawerListener {
|
||||||
|
override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
|
||||||
|
binding.surfaceInputOverlay.dispatchTouchEvent(
|
||||||
|
MotionEvent.obtain(
|
||||||
|
SystemClock.uptimeMillis(),
|
||||||
|
SystemClock.uptimeMillis() + 100,
|
||||||
|
MotionEvent.ACTION_UP,
|
||||||
|
0f,
|
||||||
|
0f,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDrawerOpened(drawerView: View) {
|
||||||
|
// No op
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDrawerClosed(drawerView: View) {
|
||||||
|
// No op
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDrawerStateChanged(newState: Int) {
|
||||||
|
// No op
|
||||||
|
}
|
||||||
|
})
|
||||||
binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
|
binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
|
||||||
binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text =
|
binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text =
|
||||||
game.title
|
game.title
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import androidx.fragment.app.Fragment
|
|||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import com.google.android.material.transition.MaterialSharedAxis
|
import com.google.android.material.transition.MaterialSharedAxis
|
||||||
import org.yuzu.yuzu_emu.BuildConfig
|
import org.yuzu.yuzu_emu.BuildConfig
|
||||||
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
||||||
@@ -186,7 +186,8 @@ class HomeSettingsFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
binding.homeSettingsList.apply {
|
binding.homeSettingsList.apply {
|
||||||
layoutManager = LinearLayoutManager(requireContext())
|
layoutManager =
|
||||||
|
GridLayoutManager(requireContext(), resources.getInteger(R.integer.grid_columns))
|
||||||
adapter = HomeSettingAdapter(
|
adapter = HomeSettingAdapter(
|
||||||
requireActivity() as AppCompatActivity,
|
requireActivity() as AppCompatActivity,
|
||||||
viewLifecycleOwner,
|
viewLifecycleOwner,
|
||||||
|
|||||||
@@ -12,15 +12,14 @@ import kotlinx.serialization.Serializable
|
|||||||
@Serializable
|
@Serializable
|
||||||
class Game(
|
class Game(
|
||||||
val title: String,
|
val title: String,
|
||||||
val description: String,
|
|
||||||
val regions: String,
|
|
||||||
val path: String,
|
val path: String,
|
||||||
val gameId: String,
|
val programId: String,
|
||||||
val company: String,
|
val developer: String,
|
||||||
|
val version: String,
|
||||||
val isHomebrew: Boolean
|
val isHomebrew: Boolean
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
val keyAddedToLibraryTime get() = "${gameId}_AddedToLibraryTime"
|
val keyAddedToLibraryTime get() = "${programId}_AddedToLibraryTime"
|
||||||
val keyLastPlayedTime get() = "${gameId}_LastPlayed"
|
val keyLastPlayedTime get() = "${programId}_LastPlayed"
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (other !is Game) {
|
if (other !is Game) {
|
||||||
@@ -32,11 +31,9 @@ class Game(
|
|||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
var result = title.hashCode()
|
var result = title.hashCode()
|
||||||
result = 31 * result + description.hashCode()
|
|
||||||
result = 31 * result + regions.hashCode()
|
|
||||||
result = 31 * result + path.hashCode()
|
result = 31 * result + path.hashCode()
|
||||||
result = 31 * result + gameId.hashCode()
|
result = 31 * result + programId.hashCode()
|
||||||
result = 31 * result + company.hashCode()
|
result = 31 * result + developer.hashCode()
|
||||||
result = 31 * result + isHomebrew.hashCode()
|
result = 31 * result + isHomebrew.hashCode()
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,15 +14,13 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.serialization.ExperimentalSerializationApi
|
|
||||||
import kotlinx.serialization.MissingFieldException
|
|
||||||
import kotlinx.serialization.decodeFromString
|
import kotlinx.serialization.decodeFromString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import org.yuzu.yuzu_emu.NativeLibrary
|
import org.yuzu.yuzu_emu.NativeLibrary
|
||||||
import org.yuzu.yuzu_emu.YuzuApplication
|
import org.yuzu.yuzu_emu.YuzuApplication
|
||||||
import org.yuzu.yuzu_emu.utils.GameHelper
|
import org.yuzu.yuzu_emu.utils.GameHelper
|
||||||
|
import org.yuzu.yuzu_emu.utils.GameMetadata
|
||||||
|
|
||||||
@OptIn(ExperimentalSerializationApi::class)
|
|
||||||
class GamesViewModel : ViewModel() {
|
class GamesViewModel : ViewModel() {
|
||||||
val games: StateFlow<List<Game>> get() = _games
|
val games: StateFlow<List<Game>> get() = _games
|
||||||
private val _games = MutableStateFlow(emptyList<Game>())
|
private val _games = MutableStateFlow(emptyList<Game>())
|
||||||
@@ -49,19 +47,25 @@ class GamesViewModel : ViewModel() {
|
|||||||
// Retrieve list of cached games
|
// Retrieve list of cached games
|
||||||
val storedGames = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
|
val storedGames = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
|
||||||
.getStringSet(GameHelper.KEY_GAMES, emptySet())
|
.getStringSet(GameHelper.KEY_GAMES, emptySet())
|
||||||
|
|
||||||
|
viewModelScope.launch {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
if (storedGames!!.isNotEmpty()) {
|
if (storedGames!!.isNotEmpty()) {
|
||||||
val deserializedGames = mutableSetOf<Game>()
|
val deserializedGames = mutableSetOf<Game>()
|
||||||
storedGames.forEach {
|
storedGames.forEach {
|
||||||
val game: Game
|
val game: Game
|
||||||
try {
|
try {
|
||||||
game = Json.decodeFromString(it)
|
game = Json.decodeFromString(it)
|
||||||
} catch (e: MissingFieldException) {
|
} catch (e: Exception) {
|
||||||
|
// We don't care about any errors related to parsing the game cache
|
||||||
return@forEach
|
return@forEach
|
||||||
}
|
}
|
||||||
|
|
||||||
val gameExists =
|
val gameExists =
|
||||||
DocumentFile.fromSingleUri(YuzuApplication.appContext, Uri.parse(game.path))
|
DocumentFile.fromSingleUri(
|
||||||
?.exists()
|
YuzuApplication.appContext,
|
||||||
|
Uri.parse(game.path)
|
||||||
|
)?.exists()
|
||||||
if (gameExists == true) {
|
if (gameExists == true) {
|
||||||
deserializedGames.add(game)
|
deserializedGames.add(game)
|
||||||
}
|
}
|
||||||
@@ -70,6 +74,8 @@ class GamesViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
reloadGames(false)
|
reloadGames(false)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun setGames(games: List<Game>) {
|
fun setGames(games: List<Game>) {
|
||||||
val sortedList = games.sortedWith(
|
val sortedList = games.sortedWith(
|
||||||
@@ -106,7 +112,7 @@ class GamesViewModel : ViewModel() {
|
|||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
NativeLibrary.resetRomMetadata()
|
GameMetadata.resetMetadata()
|
||||||
setGames(GameHelper.getGames())
|
setGames(GameHelper.getGames())
|
||||||
_isReloading.value = false
|
_isReloading.value = false
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,23 @@ class DocumentsTree {
|
|||||||
return node != null && node.isDirectory
|
return node != null && node.isDirectory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getParentDirectory(filepath: String): String {
|
||||||
|
val node = resolvePath(filepath)!!
|
||||||
|
val parentNode = node.parent
|
||||||
|
if (parentNode != null && parentNode.isDirectory) {
|
||||||
|
return parentNode.uri!!.toString()
|
||||||
|
}
|
||||||
|
return node.uri!!.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFilename(filepath: String): String {
|
||||||
|
val node = resolvePath(filepath)
|
||||||
|
if (node != null) {
|
||||||
|
return node.name!!
|
||||||
|
}
|
||||||
|
return filepath
|
||||||
|
}
|
||||||
|
|
||||||
private fun resolvePath(filepath: String): DocumentsNode? {
|
private fun resolvePath(filepath: String): DocumentsNode? {
|
||||||
val tokens = StringTokenizer(filepath, File.separator, false)
|
val tokens = StringTokenizer(filepath, File.separator, false)
|
||||||
var iterator = root
|
var iterator = root
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ object FileUtil {
|
|||||||
* @param path Native content uri path
|
* @param path Native content uri path
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
fun exists(path: String?): Boolean {
|
fun exists(path: String?, suppressLog: Boolean = false): Boolean {
|
||||||
var c: Cursor? = null
|
var c: Cursor? = null
|
||||||
try {
|
try {
|
||||||
val mUri = Uri.parse(path)
|
val mUri = Uri.parse(path)
|
||||||
@@ -152,7 +152,9 @@ object FileUtil {
|
|||||||
c = context.contentResolver.query(mUri, columns, null, null, null)
|
c = context.contentResolver.query(mUri, columns, null, null, null)
|
||||||
return c!!.count > 0
|
return c!!.count > 0
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
if (!suppressLog) {
|
||||||
Log.info("[FileUtil] Cannot find file from given path, error: " + e.message)
|
Log.info("[FileUtil] Cannot find file from given path, error: " + e.message)
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
closeQuietly(c)
|
closeQuietly(c)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,27 +71,26 @@ object GameHelper {
|
|||||||
|
|
||||||
fun getGame(uri: Uri, addedToLibrary: Boolean): Game {
|
fun getGame(uri: Uri, addedToLibrary: Boolean): Game {
|
||||||
val filePath = uri.toString()
|
val filePath = uri.toString()
|
||||||
var name = NativeLibrary.getTitle(filePath)
|
var name = GameMetadata.getTitle(filePath)
|
||||||
|
|
||||||
// If the game's title field is empty, use the filename.
|
// If the game's title field is empty, use the filename.
|
||||||
if (name.isEmpty()) {
|
if (name.isEmpty()) {
|
||||||
name = FileUtil.getFilename(uri)
|
name = FileUtil.getFilename(uri)
|
||||||
}
|
}
|
||||||
var gameId = NativeLibrary.getGameId(filePath)
|
var programId = GameMetadata.getProgramId(filePath)
|
||||||
|
|
||||||
// If the game's ID field is empty, use the filename without extension.
|
// If the game's ID field is empty, use the filename without extension.
|
||||||
if (gameId.isEmpty()) {
|
if (programId.isEmpty()) {
|
||||||
gameId = name.substring(0, name.lastIndexOf("."))
|
programId = name.substring(0, name.lastIndexOf("."))
|
||||||
}
|
}
|
||||||
|
|
||||||
val newGame = Game(
|
val newGame = Game(
|
||||||
name,
|
name,
|
||||||
NativeLibrary.getDescription(filePath).replace("\n", " "),
|
|
||||||
NativeLibrary.getRegions(filePath),
|
|
||||||
filePath,
|
filePath,
|
||||||
gameId,
|
programId,
|
||||||
NativeLibrary.getCompany(filePath),
|
GameMetadata.getDeveloper(filePath),
|
||||||
NativeLibrary.isHomebrew(filePath)
|
GameMetadata.getVersion(filePath),
|
||||||
|
GameMetadata.getIsHomebrew(filePath)
|
||||||
)
|
)
|
||||||
|
|
||||||
if (addedToLibrary) {
|
if (addedToLibrary) {
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import coil.key.Keyer
|
|||||||
import coil.memory.MemoryCache
|
import coil.memory.MemoryCache
|
||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import coil.request.Options
|
import coil.request.Options
|
||||||
import org.yuzu.yuzu_emu.NativeLibrary
|
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.YuzuApplication
|
import org.yuzu.yuzu_emu.YuzuApplication
|
||||||
import org.yuzu.yuzu_emu.model.Game
|
import org.yuzu.yuzu_emu.model.Game
|
||||||
@@ -36,7 +35,7 @@ class GameIconFetcher(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun decodeGameIcon(uri: String): Bitmap? {
|
private fun decodeGameIcon(uri: String): Bitmap? {
|
||||||
val data = NativeLibrary.getIcon(uri)
|
val data = GameMetadata.getIcon(uri)
|
||||||
return BitmapFactory.decodeByteArray(
|
return BitmapFactory.decodeByteArray(
|
||||||
data,
|
data,
|
||||||
0,
|
0,
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.yuzu.yuzu_emu.utils
|
||||||
|
|
||||||
|
object GameMetadata {
|
||||||
|
external fun getTitle(path: String): String
|
||||||
|
|
||||||
|
external fun getProgramId(path: String): String
|
||||||
|
|
||||||
|
external fun getDeveloper(path: String): String
|
||||||
|
|
||||||
|
external fun getVersion(path: String): String
|
||||||
|
|
||||||
|
external fun getIcon(path: String): ByteArray
|
||||||
|
|
||||||
|
external fun getIsHomebrew(path: String): Boolean
|
||||||
|
|
||||||
|
external fun resetMetadata()
|
||||||
|
}
|
||||||
@@ -14,8 +14,10 @@ add_library(yuzu-android SHARED
|
|||||||
id_cache.cpp
|
id_cache.cpp
|
||||||
id_cache.h
|
id_cache.h
|
||||||
native.cpp
|
native.cpp
|
||||||
|
native.h
|
||||||
native_config.cpp
|
native_config.cpp
|
||||||
uisettings.cpp
|
uisettings.cpp
|
||||||
|
game_metadata.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
|
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
|
||||||
|
|||||||
112
src/android/app/src/main/jni/game_metadata.cpp
Normal file
112
src/android/app/src/main/jni/game_metadata.cpp
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <core/core.h>
|
||||||
|
#include <core/file_sys/patch_manager.h>
|
||||||
|
#include <core/loader/nro.h>
|
||||||
|
#include <jni.h>
|
||||||
|
#include "core/loader/loader.h"
|
||||||
|
#include "jni/android_common/android_common.h"
|
||||||
|
#include "native.h"
|
||||||
|
|
||||||
|
struct RomMetadata {
|
||||||
|
std::string title;
|
||||||
|
u64 programId;
|
||||||
|
std::string developer;
|
||||||
|
std::string version;
|
||||||
|
std::vector<u8> icon;
|
||||||
|
bool isHomebrew;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unordered_map<std::string, RomMetadata> m_rom_metadata_cache;
|
||||||
|
|
||||||
|
RomMetadata CacheRomMetadata(const std::string& path) {
|
||||||
|
const auto file =
|
||||||
|
Core::GetGameFileFromPath(EmulationSession::GetInstance().System().GetFilesystem(), path);
|
||||||
|
auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0);
|
||||||
|
|
||||||
|
RomMetadata entry;
|
||||||
|
loader->ReadTitle(entry.title);
|
||||||
|
loader->ReadProgramId(entry.programId);
|
||||||
|
loader->ReadIcon(entry.icon);
|
||||||
|
|
||||||
|
const FileSys::PatchManager pm{
|
||||||
|
entry.programId, EmulationSession::GetInstance().System().GetFileSystemController(),
|
||||||
|
EmulationSession::GetInstance().System().GetContentProvider()};
|
||||||
|
const auto control = pm.GetControlMetadata();
|
||||||
|
|
||||||
|
if (control.first != nullptr) {
|
||||||
|
entry.developer = control.first->GetDeveloperName();
|
||||||
|
entry.version = control.first->GetVersionString();
|
||||||
|
} else {
|
||||||
|
FileSys::NACP nacp;
|
||||||
|
if (loader->ReadControlData(nacp) == Loader::ResultStatus::Success) {
|
||||||
|
entry.developer = nacp.GetDeveloperName();
|
||||||
|
} else {
|
||||||
|
entry.developer = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.version = "1.0.0";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loader->GetFileType() == Loader::FileType::NRO) {
|
||||||
|
auto loader_nro = reinterpret_cast<Loader::AppLoader_NRO*>(loader.get());
|
||||||
|
entry.isHomebrew = loader_nro->IsHomebrew();
|
||||||
|
} else {
|
||||||
|
entry.isHomebrew = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_rom_metadata_cache[path] = entry;
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
RomMetadata GetRomMetadata(const std::string& path) {
|
||||||
|
if (auto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) {
|
||||||
|
return search->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CacheRomMetadata(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getTitle(JNIEnv* env, jobject obj,
|
||||||
|
jstring jpath) {
|
||||||
|
return ToJString(env, GetRomMetadata(GetJString(env, jpath)).title);
|
||||||
|
}
|
||||||
|
|
||||||
|
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getProgramId(JNIEnv* env, jobject obj,
|
||||||
|
jstring jpath) {
|
||||||
|
return ToJString(env, std::to_string(GetRomMetadata(GetJString(env, jpath)).programId));
|
||||||
|
}
|
||||||
|
|
||||||
|
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getDeveloper(JNIEnv* env, jobject obj,
|
||||||
|
jstring jpath) {
|
||||||
|
return ToJString(env, GetRomMetadata(GetJString(env, jpath)).developer);
|
||||||
|
}
|
||||||
|
|
||||||
|
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getVersion(JNIEnv* env, jobject obj,
|
||||||
|
jstring jpath) {
|
||||||
|
return ToJString(env, GetRomMetadata(GetJString(env, jpath)).version);
|
||||||
|
}
|
||||||
|
|
||||||
|
jbyteArray Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIcon(JNIEnv* env, jobject obj,
|
||||||
|
jstring jpath) {
|
||||||
|
auto icon_data = GetRomMetadata(GetJString(env, jpath)).icon;
|
||||||
|
jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size()));
|
||||||
|
env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon),
|
||||||
|
reinterpret_cast<jbyte*>(icon_data.data()));
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsHomebrew(JNIEnv* env, jobject obj,
|
||||||
|
jstring jpath) {
|
||||||
|
return static_cast<jboolean>(GetRomMetadata(GetJString(env, jpath)).isHomebrew);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Java_org_yuzu_yuzu_1emu_utils_GameMetadata_resetMetadata(JNIEnv* env, jobject obj) {
|
||||||
|
return m_rom_metadata_cache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // extern "C"
|
||||||
@@ -33,7 +33,6 @@
|
|||||||
#include "core/crypto/key_manager.h"
|
#include "core/crypto/key_manager.h"
|
||||||
#include "core/file_sys/card_image.h"
|
#include "core/file_sys/card_image.h"
|
||||||
#include "core/file_sys/content_archive.h"
|
#include "core/file_sys/content_archive.h"
|
||||||
#include "core/file_sys/registered_cache.h"
|
|
||||||
#include "core/file_sys/submission_package.h"
|
#include "core/file_sys/submission_package.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs.h"
|
||||||
#include "core/file_sys/vfs_real.h"
|
#include "core/file_sys/vfs_real.h"
|
||||||
@@ -48,63 +47,55 @@
|
|||||||
#include "core/hid/emulated_controller.h"
|
#include "core/hid/emulated_controller.h"
|
||||||
#include "core/hid/hid_core.h"
|
#include "core/hid/hid_core.h"
|
||||||
#include "core/hid/hid_types.h"
|
#include "core/hid/hid_types.h"
|
||||||
#include "core/hle/service/acc/profile_manager.h"
|
|
||||||
#include "core/hle/service/am/applet_ae.h"
|
#include "core/hle/service/am/applet_ae.h"
|
||||||
#include "core/hle/service/am/applet_oe.h"
|
#include "core/hle/service/am/applet_oe.h"
|
||||||
#include "core/hle/service/am/applets/applets.h"
|
#include "core/hle/service/am/applets/applets.h"
|
||||||
#include "core/hle/service/filesystem/filesystem.h"
|
#include "core/hle/service/filesystem/filesystem.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
#include "core/perf_stats.h"
|
|
||||||
#include "jni/android_common/android_common.h"
|
#include "jni/android_common/android_common.h"
|
||||||
#include "jni/applets/software_keyboard.h"
|
|
||||||
#include "jni/config.h"
|
#include "jni/config.h"
|
||||||
#include "jni/emu_window/emu_window.h"
|
|
||||||
#include "jni/id_cache.h"
|
#include "jni/id_cache.h"
|
||||||
#include "video_core/rasterizer_interface.h"
|
#include "jni/native.h"
|
||||||
#include "video_core/renderer_base.h"
|
#include "video_core/renderer_base.h"
|
||||||
|
|
||||||
#define jconst [[maybe_unused]] const auto
|
#define jconst [[maybe_unused]] const auto
|
||||||
#define jauto [[maybe_unused]] auto
|
#define jauto [[maybe_unused]] auto
|
||||||
|
|
||||||
namespace {
|
static EmulationSession s_instance;
|
||||||
|
|
||||||
class EmulationSession final {
|
EmulationSession::EmulationSession() {
|
||||||
public:
|
|
||||||
EmulationSession() {
|
|
||||||
m_vfs = std::make_shared<FileSys::RealVfsFilesystem>();
|
m_vfs = std::make_shared<FileSys::RealVfsFilesystem>();
|
||||||
}
|
}
|
||||||
|
|
||||||
~EmulationSession() = default;
|
EmulationSession& EmulationSession::GetInstance() {
|
||||||
|
|
||||||
static EmulationSession& GetInstance() {
|
|
||||||
return s_instance;
|
return s_instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Core::System& System() const {
|
const Core::System& EmulationSession::System() const {
|
||||||
return m_system;
|
return m_system;
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::System& System() {
|
Core::System& EmulationSession::System() {
|
||||||
return m_system;
|
return m_system;
|
||||||
}
|
}
|
||||||
|
|
||||||
const EmuWindow_Android& Window() const {
|
const EmuWindow_Android& EmulationSession::Window() const {
|
||||||
return *m_window;
|
return *m_window;
|
||||||
}
|
}
|
||||||
|
|
||||||
EmuWindow_Android& Window() {
|
EmuWindow_Android& EmulationSession::Window() {
|
||||||
return *m_window;
|
return *m_window;
|
||||||
}
|
}
|
||||||
|
|
||||||
ANativeWindow* NativeWindow() const {
|
ANativeWindow* EmulationSession::NativeWindow() const {
|
||||||
return m_native_window;
|
return m_native_window;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetNativeWindow(ANativeWindow* native_window) {
|
void EmulationSession::SetNativeWindow(ANativeWindow* native_window) {
|
||||||
m_native_window = native_window;
|
m_native_window = native_window;
|
||||||
}
|
}
|
||||||
|
|
||||||
int InstallFileToNand(std::string filename, std::string file_extension) {
|
int EmulationSession::InstallFileToNand(std::string filename, std::string file_extension) {
|
||||||
jconst copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
|
jconst copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
|
||||||
std::size_t block_size) {
|
std::size_t block_size) {
|
||||||
if (src == nullptr || dest == nullptr) {
|
if (src == nullptr || dest == nullptr) {
|
||||||
@@ -153,8 +144,8 @@ public:
|
|||||||
return InstallError;
|
return InstallError;
|
||||||
}
|
}
|
||||||
|
|
||||||
jconst res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry(
|
jconst res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry(*nsp, true,
|
||||||
*nsp, true, copy_func);
|
copy_func);
|
||||||
|
|
||||||
switch (res) {
|
switch (res) {
|
||||||
case FileSys::InstallResult::Success:
|
case FileSys::InstallResult::Success:
|
||||||
@@ -168,7 +159,8 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir,
|
void EmulationSession::InitializeGpuDriver(const std::string& hook_lib_dir,
|
||||||
|
const std::string& custom_driver_dir,
|
||||||
const std::string& custom_driver_name,
|
const std::string& custom_driver_name,
|
||||||
const std::string& file_redirect_dir) {
|
const std::string& file_redirect_dir) {
|
||||||
#ifdef ARCHITECTURE_arm64
|
#ifdef ARCHITECTURE_arm64
|
||||||
@@ -191,8 +183,7 @@ public:
|
|||||||
|
|
||||||
// Try to load the system driver.
|
// Try to load the system driver.
|
||||||
if (!handle) {
|
if (!handle) {
|
||||||
handle =
|
handle = adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(),
|
||||||
adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(),
|
|
||||||
nullptr, nullptr, file_redirect_dir_, nullptr);
|
nullptr, nullptr, file_redirect_dir_, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,27 +191,27 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsRunning() const {
|
bool EmulationSession::IsRunning() const {
|
||||||
return m_is_running;
|
return m_is_running;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsPaused() const {
|
bool EmulationSession::IsPaused() const {
|
||||||
return m_is_running && m_is_paused;
|
return m_is_running && m_is_paused;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Core::PerfStatsResults& PerfStats() const {
|
const Core::PerfStatsResults& EmulationSession::PerfStats() const {
|
||||||
std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex);
|
std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex);
|
||||||
return m_perf_stats;
|
return m_perf_stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SurfaceChanged() {
|
void EmulationSession::SurfaceChanged() {
|
||||||
if (!IsRunning()) {
|
if (!IsRunning()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_window->OnSurfaceChanged(m_native_window);
|
m_window->OnSurfaceChanged(m_native_window);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureFilesystemProvider(const std::string& filepath) {
|
void EmulationSession::ConfigureFilesystemProvider(const std::string& filepath) {
|
||||||
const auto file = m_system.GetFilesystem()->OpenFile(filepath, FileSys::Mode::Read);
|
const auto file = m_system.GetFilesystem()->OpenFile(filepath, FileSys::Mode::Read);
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return;
|
return;
|
||||||
@@ -256,12 +247,12 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::SystemResultStatus InitializeEmulation(const std::string& filepath) {
|
Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string& filepath) {
|
||||||
std::scoped_lock lock(m_mutex);
|
std::scoped_lock lock(m_mutex);
|
||||||
|
|
||||||
// Create the render window.
|
// Create the render window.
|
||||||
m_window = std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window,
|
m_window =
|
||||||
m_vulkan_library);
|
std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window, m_vulkan_library);
|
||||||
|
|
||||||
m_system.SetFilesystem(m_vfs);
|
m_system.SetFilesystem(m_vfs);
|
||||||
m_system.GetUserChannel().clear();
|
m_system.GetUserChannel().clear();
|
||||||
@@ -310,7 +301,7 @@ public:
|
|||||||
return Core::SystemResultStatus::Success;
|
return Core::SystemResultStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShutdownEmulation() {
|
void EmulationSession::ShutdownEmulation() {
|
||||||
std::scoped_lock lock(m_mutex);
|
std::scoped_lock lock(m_mutex);
|
||||||
|
|
||||||
m_is_running = false;
|
m_is_running = false;
|
||||||
@@ -333,25 +324,25 @@ public:
|
|||||||
m_window.reset();
|
m_window.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PauseEmulation() {
|
void EmulationSession::PauseEmulation() {
|
||||||
std::scoped_lock lock(m_mutex);
|
std::scoped_lock lock(m_mutex);
|
||||||
m_system.Pause();
|
m_system.Pause();
|
||||||
m_is_paused = true;
|
m_is_paused = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnPauseEmulation() {
|
void EmulationSession::UnPauseEmulation() {
|
||||||
std::scoped_lock lock(m_mutex);
|
std::scoped_lock lock(m_mutex);
|
||||||
m_system.Run();
|
m_system.Run();
|
||||||
m_is_paused = false;
|
m_is_paused = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HaltEmulation() {
|
void EmulationSession::HaltEmulation() {
|
||||||
std::scoped_lock lock(m_mutex);
|
std::scoped_lock lock(m_mutex);
|
||||||
m_is_running = false;
|
m_is_running = false;
|
||||||
m_cv.notify_one();
|
m_cv.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RunEmulation() {
|
void EmulationSession::RunEmulation() {
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(m_mutex);
|
std::scoped_lock lock(m_mutex);
|
||||||
m_is_running = true;
|
m_is_running = true;
|
||||||
@@ -361,8 +352,7 @@ public:
|
|||||||
if (Settings::values.use_disk_shader_cache.GetValue()) {
|
if (Settings::values.use_disk_shader_cache.GetValue()) {
|
||||||
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
|
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
|
||||||
m_system.Renderer().ReadRasterizer()->LoadDiskResources(
|
m_system.Renderer().ReadRasterizer()->LoadDiskResources(
|
||||||
m_system.GetApplicationProcessProgramID(), std::stop_token{},
|
m_system.GetApplicationProcessProgramID(), std::stop_token{}, LoadDiskCacheProgress);
|
||||||
LoadDiskCacheProgress);
|
|
||||||
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
|
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -391,23 +381,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetRomTitle(const std::string& path) {
|
bool EmulationSession::IsHandheldOnly() {
|
||||||
return GetRomMetadata(path).title;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<u8> GetRomIcon(const std::string& path) {
|
|
||||||
return GetRomMetadata(path).icon;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GetIsHomebrew(const std::string& path) {
|
|
||||||
return GetRomMetadata(path).isHomebrew;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ResetRomMetadata() {
|
|
||||||
m_rom_metadata_cache.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsHandheldOnly() {
|
|
||||||
jconst npad_style_set = m_system.HIDCore().GetSupportedStyleTag();
|
jconst npad_style_set = m_system.HIDCore().GetSupportedStyleTag();
|
||||||
|
|
||||||
if (npad_style_set.fullkey == 1) {
|
if (npad_style_set.fullkey == 1) {
|
||||||
@@ -421,18 +395,17 @@ public:
|
|||||||
return !Settings::IsDockedMode();
|
return !Settings::IsDockedMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetDeviceType([[maybe_unused]] int index, int type) {
|
void EmulationSession::SetDeviceType([[maybe_unused]] int index, int type) {
|
||||||
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
||||||
controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type));
|
controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnGamepadConnectEvent([[maybe_unused]] int index) {
|
void EmulationSession::OnGamepadConnectEvent([[maybe_unused]] int index) {
|
||||||
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
||||||
|
|
||||||
// Ensure that player1 is configured correctly and handheld disconnected
|
// Ensure that player1 is configured correctly and handheld disconnected
|
||||||
if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) {
|
if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) {
|
||||||
jauto handheld =
|
jauto handheld = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
|
||||||
m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
|
|
||||||
|
|
||||||
if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) {
|
if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) {
|
||||||
handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
|
handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
|
||||||
@@ -443,8 +416,7 @@ public:
|
|||||||
|
|
||||||
// Ensure that handheld is configured correctly and player 1 disconnected
|
// Ensure that handheld is configured correctly and player 1 disconnected
|
||||||
if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) {
|
if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) {
|
||||||
jauto player1 =
|
jauto player1 = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
|
||||||
m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
|
|
||||||
|
|
||||||
if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) {
|
if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) {
|
||||||
player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
|
player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
|
||||||
@@ -458,105 +430,34 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnGamepadDisconnectEvent([[maybe_unused]] int index) {
|
void EmulationSession::OnGamepadDisconnectEvent([[maybe_unused]] int index) {
|
||||||
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
||||||
controller->Disconnect();
|
controller->Disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard() {
|
SoftwareKeyboard::AndroidKeyboard* EmulationSession::SoftwareKeyboard() {
|
||||||
return m_software_keyboard;
|
return m_software_keyboard;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
void EmulationSession::LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress,
|
||||||
struct RomMetadata {
|
int max) {
|
||||||
std::string title;
|
|
||||||
std::vector<u8> icon;
|
|
||||||
bool isHomebrew;
|
|
||||||
};
|
|
||||||
|
|
||||||
RomMetadata GetRomMetadata(const std::string& path) {
|
|
||||||
if (jauto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) {
|
|
||||||
return search->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CacheRomMetadata(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
RomMetadata CacheRomMetadata(const std::string& path) {
|
|
||||||
jconst file = Core::GetGameFileFromPath(m_vfs, path);
|
|
||||||
jauto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0);
|
|
||||||
|
|
||||||
RomMetadata entry;
|
|
||||||
loader->ReadTitle(entry.title);
|
|
||||||
loader->ReadIcon(entry.icon);
|
|
||||||
if (loader->GetFileType() == Loader::FileType::NRO) {
|
|
||||||
jauto loader_nro = reinterpret_cast<Loader::AppLoader_NRO*>(loader.get());
|
|
||||||
entry.isHomebrew = loader_nro->IsHomebrew();
|
|
||||||
} else {
|
|
||||||
entry.isHomebrew = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_rom_metadata_cache[path] = entry;
|
|
||||||
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max) {
|
|
||||||
JNIEnv* env = IDCache::GetEnvForThread();
|
JNIEnv* env = IDCache::GetEnvForThread();
|
||||||
env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(),
|
env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(),
|
||||||
IDCache::GetDiskCacheLoadProgress(), static_cast<jint>(stage),
|
IDCache::GetDiskCacheLoadProgress(), static_cast<jint>(stage),
|
||||||
static_cast<jint>(progress), static_cast<jint>(max));
|
static_cast<jint>(progress), static_cast<jint>(max));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void OnEmulationStarted() {
|
void EmulationSession::OnEmulationStarted() {
|
||||||
JNIEnv* env = IDCache::GetEnvForThread();
|
JNIEnv* env = IDCache::GetEnvForThread();
|
||||||
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(),
|
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnEmulationStarted());
|
||||||
IDCache::GetOnEmulationStarted());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void OnEmulationStopped(Core::SystemResultStatus result) {
|
void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) {
|
||||||
JNIEnv* env = IDCache::GetEnvForThread();
|
JNIEnv* env = IDCache::GetEnvForThread();
|
||||||
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(),
|
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnEmulationStopped(),
|
||||||
IDCache::GetOnEmulationStopped(), static_cast<jint>(result));
|
static_cast<jint>(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
static EmulationSession s_instance;
|
|
||||||
|
|
||||||
// Frontend management
|
|
||||||
std::unordered_map<std::string, RomMetadata> m_rom_metadata_cache;
|
|
||||||
|
|
||||||
// Window management
|
|
||||||
std::unique_ptr<EmuWindow_Android> m_window;
|
|
||||||
ANativeWindow* m_native_window{};
|
|
||||||
|
|
||||||
// Core emulation
|
|
||||||
Core::System m_system;
|
|
||||||
InputCommon::InputSubsystem m_input_subsystem;
|
|
||||||
Common::DetachedTasks m_detached_tasks;
|
|
||||||
Core::PerfStatsResults m_perf_stats{};
|
|
||||||
std::shared_ptr<FileSys::VfsFilesystem> m_vfs;
|
|
||||||
Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized};
|
|
||||||
std::atomic<bool> m_is_running = false;
|
|
||||||
std::atomic<bool> m_is_paused = false;
|
|
||||||
SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
|
|
||||||
std::unique_ptr<Service::Account::ProfileManager> m_profile_manager;
|
|
||||||
std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
|
|
||||||
|
|
||||||
// GPU driver parameters
|
|
||||||
std::shared_ptr<Common::DynamicLibrary> m_vulkan_library;
|
|
||||||
|
|
||||||
// Synchronization
|
|
||||||
std::condition_variable_any m_cv;
|
|
||||||
mutable std::mutex m_perf_stats_mutex;
|
|
||||||
mutable std::mutex m_mutex;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*static*/ EmulationSession EmulationSession::s_instance;
|
|
||||||
|
|
||||||
} // Anonymous namespace
|
|
||||||
|
|
||||||
static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
|
static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
|
||||||
Common::Log::Initialize();
|
Common::Log::Initialize();
|
||||||
Common::Log::SetColorConsoleBackendEnabled(true);
|
Common::Log::SetColorConsoleBackendEnabled(true);
|
||||||
@@ -657,10 +558,6 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_stopEmulation(JNIEnv* env, jclass cla
|
|||||||
EmulationSession::GetInstance().HaltEmulation();
|
EmulationSession::GetInstance().HaltEmulation();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_resetRomMetadata(JNIEnv* env, jclass clazz) {
|
|
||||||
EmulationSession::GetInstance().ResetRomMetadata();
|
|
||||||
}
|
|
||||||
|
|
||||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isRunning(JNIEnv* env, jclass clazz) {
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isRunning(JNIEnv* env, jclass clazz) {
|
||||||
return static_cast<jboolean>(EmulationSession::GetInstance().IsRunning());
|
return static_cast<jboolean>(EmulationSession::GetInstance().IsRunning());
|
||||||
}
|
}
|
||||||
@@ -766,46 +663,6 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
jbyteArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getIcon(JNIEnv* env, jclass clazz,
|
|
||||||
jstring j_filename) {
|
|
||||||
jauto icon_data = EmulationSession::GetInstance().GetRomIcon(GetJString(env, j_filename));
|
|
||||||
jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size()));
|
|
||||||
env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon),
|
|
||||||
reinterpret_cast<jbyte*>(icon_data.data()));
|
|
||||||
return icon;
|
|
||||||
}
|
|
||||||
|
|
||||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getTitle(JNIEnv* env, jclass clazz,
|
|
||||||
jstring j_filename) {
|
|
||||||
jauto title = EmulationSession::GetInstance().GetRomTitle(GetJString(env, j_filename));
|
|
||||||
return env->NewStringUTF(title.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDescription(JNIEnv* env, jclass clazz,
|
|
||||||
jstring j_filename) {
|
|
||||||
return j_filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGameId(JNIEnv* env, jclass clazz,
|
|
||||||
jstring j_filename) {
|
|
||||||
return j_filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRegions(JNIEnv* env, jclass clazz,
|
|
||||||
jstring j_filename) {
|
|
||||||
return env->NewStringUTF("");
|
|
||||||
}
|
|
||||||
|
|
||||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany(JNIEnv* env, jclass clazz,
|
|
||||||
jstring j_filename) {
|
|
||||||
return env->NewStringUTF("");
|
|
||||||
}
|
|
||||||
|
|
||||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew(JNIEnv* env, jclass clazz,
|
|
||||||
jstring j_filename) {
|
|
||||||
return EmulationSession::GetInstance().GetIsHomebrew(GetJString(env, j_filename));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation(JNIEnv* env, jclass clazz) {
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation(JNIEnv* env, jclass clazz) {
|
||||||
// Create the default config.ini.
|
// Create the default config.ini.
|
||||||
Config{};
|
Config{};
|
||||||
|
|||||||
84
src/android/app/src/main/jni/native.h
Normal file
84
src/android/app/src/main/jni/native.h
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <android/native_window_jni.h>
|
||||||
|
#include "common/detached_tasks.h"
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/file_sys/registered_cache.h"
|
||||||
|
#include "core/hle/service/acc/profile_manager.h"
|
||||||
|
#include "core/perf_stats.h"
|
||||||
|
#include "jni/applets/software_keyboard.h"
|
||||||
|
#include "jni/emu_window/emu_window.h"
|
||||||
|
#include "video_core/rasterizer_interface.h"
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
class EmulationSession final {
|
||||||
|
public:
|
||||||
|
explicit EmulationSession();
|
||||||
|
~EmulationSession() = default;
|
||||||
|
|
||||||
|
static EmulationSession& GetInstance();
|
||||||
|
const Core::System& System() const;
|
||||||
|
Core::System& System();
|
||||||
|
|
||||||
|
const EmuWindow_Android& Window() const;
|
||||||
|
EmuWindow_Android& Window();
|
||||||
|
ANativeWindow* NativeWindow() const;
|
||||||
|
void SetNativeWindow(ANativeWindow* native_window);
|
||||||
|
void SurfaceChanged();
|
||||||
|
|
||||||
|
int InstallFileToNand(std::string filename, std::string file_extension);
|
||||||
|
void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir,
|
||||||
|
const std::string& custom_driver_name,
|
||||||
|
const std::string& file_redirect_dir);
|
||||||
|
|
||||||
|
bool IsRunning() const;
|
||||||
|
bool IsPaused() const;
|
||||||
|
void PauseEmulation();
|
||||||
|
void UnPauseEmulation();
|
||||||
|
void HaltEmulation();
|
||||||
|
void RunEmulation();
|
||||||
|
void ShutdownEmulation();
|
||||||
|
|
||||||
|
const Core::PerfStatsResults& PerfStats() const;
|
||||||
|
void ConfigureFilesystemProvider(const std::string& filepath);
|
||||||
|
Core::SystemResultStatus InitializeEmulation(const std::string& filepath);
|
||||||
|
|
||||||
|
bool IsHandheldOnly();
|
||||||
|
void SetDeviceType([[maybe_unused]] int index, int type);
|
||||||
|
void OnGamepadConnectEvent([[maybe_unused]] int index);
|
||||||
|
void OnGamepadDisconnectEvent([[maybe_unused]] int index);
|
||||||
|
SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max);
|
||||||
|
static void OnEmulationStarted();
|
||||||
|
static void OnEmulationStopped(Core::SystemResultStatus result);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Window management
|
||||||
|
std::unique_ptr<EmuWindow_Android> m_window;
|
||||||
|
ANativeWindow* m_native_window{};
|
||||||
|
|
||||||
|
// Core emulation
|
||||||
|
Core::System m_system;
|
||||||
|
InputCommon::InputSubsystem m_input_subsystem;
|
||||||
|
Common::DetachedTasks m_detached_tasks;
|
||||||
|
Core::PerfStatsResults m_perf_stats{};
|
||||||
|
std::shared_ptr<FileSys::VfsFilesystem> m_vfs;
|
||||||
|
Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized};
|
||||||
|
std::atomic<bool> m_is_running = false;
|
||||||
|
std::atomic<bool> m_is_paused = false;
|
||||||
|
SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
|
||||||
|
std::unique_ptr<Service::Account::ProfileManager> m_profile_manager;
|
||||||
|
std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
|
||||||
|
|
||||||
|
// GPU driver parameters
|
||||||
|
std::shared_ptr<Common::DynamicLibrary> m_vulkan_library;
|
||||||
|
|
||||||
|
// Synchronization
|
||||||
|
std::condition_variable_any m_cv;
|
||||||
|
mutable std::mutex m_perf_stats_mutex;
|
||||||
|
mutable std::mutex m_mutex;
|
||||||
|
};
|
||||||
@@ -1,63 +1,54 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<FrameLayout
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
style="?attr/materialCardViewElevatedStyle"
|
|
||||||
android:id="@+id/card_game"
|
android:id="@+id/card_game"
|
||||||
|
style="?attr/materialCardViewElevatedStyle"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
android:background="?attr/selectableItemBackground"
|
android:background="?attr/selectableItemBackground"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:transitionName="card_game"
|
android:transitionName="card_game"
|
||||||
android:layout_gravity="center"
|
app:cardCornerRadius="4dp"
|
||||||
app:cardElevation="0dp"
|
app:cardElevation="0dp">
|
||||||
app:cardCornerRadius="12dp">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:padding="6dp">
|
android:padding="6dp">
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
style="?attr/materialCardViewElevatedStyle"
|
android:id="@+id/image_game_screen"
|
||||||
android:id="@+id/card_game_art"
|
|
||||||
android:layout_width="150dp"
|
android:layout_width="150dp"
|
||||||
android:layout_height="150dp"
|
android:layout_height="150dp"
|
||||||
app:cardCornerRadius="4dp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:shapeAppearance="@style/ShapeAppearance.Material3.Corner.ExtraSmall"
|
||||||
<ImageView
|
|
||||||
android:id="@+id/image_game_screen"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
tools:src="@drawable/default_icon" />
|
tools:src="@drawable/default_icon" />
|
||||||
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
<com.google.android.material.textview.MaterialTextView
|
||||||
style="@style/TextAppearance.Material3.TitleMedium"
|
|
||||||
android:id="@+id/text_game_title"
|
android:id="@+id/text_game_title"
|
||||||
|
style="@style/TextAppearance.Material3.TitleMedium"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
|
android:ellipsize="none"
|
||||||
|
android:marqueeRepeatLimit="marquee_forever"
|
||||||
|
android:requiresFadingEdge="horizontal"
|
||||||
|
android:singleLine="true"
|
||||||
android:textAlignment="center"
|
android:textAlignment="center"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
android:singleLine="true"
|
app:layout_constraintEnd_toEndOf="@+id/image_game_screen"
|
||||||
android:marqueeRepeatLimit="marquee_forever"
|
app:layout_constraintStart_toStartOf="@+id/image_game_screen"
|
||||||
android:ellipsize="none"
|
app:layout_constraintTop_toBottomOf="@+id/image_game_screen"
|
||||||
android:requiresFadingEdge="horizontal"
|
|
||||||
app:layout_constraintEnd_toEndOf="@+id/card_game_art"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/card_game_art"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/card_game_art"
|
|
||||||
tools:text="The Legend of Zelda: Skyward Sword" />
|
tools:text="The Legend of Zelda: Skyward Sword" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|||||||
@@ -16,7 +16,8 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/option_layout"
|
android:id="@+id/option_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/option_icon"
|
android:id="@+id/option_icon"
|
||||||
|
|||||||
@@ -204,6 +204,10 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
|
|||||||
// paused and we'll desync, so just play silence.
|
// paused and we'll desync, so just play silence.
|
||||||
if (system.IsPaused() || system.IsShuttingDown()) {
|
if (system.IsPaused() || system.IsShuttingDown()) {
|
||||||
if (system.IsShuttingDown()) {
|
if (system.IsShuttingDown()) {
|
||||||
|
{
|
||||||
|
std::scoped_lock lk{release_mutex};
|
||||||
|
queued_buffers.store(0);
|
||||||
|
}
|
||||||
release_cv.notify_one();
|
release_cv.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -120,6 +120,8 @@ add_library(common STATIC
|
|||||||
socket_types.h
|
socket_types.h
|
||||||
spin_lock.cpp
|
spin_lock.cpp
|
||||||
spin_lock.h
|
spin_lock.h
|
||||||
|
stb.cpp
|
||||||
|
stb.h
|
||||||
steady_clock.cpp
|
steady_clock.cpp
|
||||||
steady_clock.h
|
steady_clock.h
|
||||||
stream.cpp
|
stream.cpp
|
||||||
@@ -208,6 +210,8 @@ if (MSVC)
|
|||||||
/we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
|
/we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
|
||||||
/we4800 # Implicit conversion from 'type' to bool. Possible information loss
|
/we4800 # Implicit conversion from 'type' to bool. Possible information loss
|
||||||
)
|
)
|
||||||
|
else()
|
||||||
|
set_source_files_properties(stb.cpp PROPERTIES COMPILE_OPTIONS "-Wno-implicit-fallthrough;-Wno-missing-declarations;-Wno-missing-field-initializers")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||||
@@ -223,7 +227,7 @@ endif()
|
|||||||
|
|
||||||
create_target_directory_groups(common)
|
create_target_directory_groups(common)
|
||||||
|
|
||||||
target_link_libraries(common PUBLIC Boost::context Boost::headers fmt::fmt microprofile Threads::Threads)
|
target_link_libraries(common PUBLIC Boost::context Boost::headers fmt::fmt microprofile stb::headers Threads::Threads)
|
||||||
target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd LLVM::Demangle)
|
target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd LLVM::Demangle)
|
||||||
|
|
||||||
if (ANDROID)
|
if (ANDROID)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "common/fs/fs_android.h"
|
#include "common/fs/fs_android.h"
|
||||||
|
#include "common/string_util.h"
|
||||||
|
|
||||||
namespace Common::FS::Android {
|
namespace Common::FS::Android {
|
||||||
|
|
||||||
@@ -28,28 +29,35 @@ void RegisterCallbacks(JNIEnv* env, jclass clazz) {
|
|||||||
env->GetJavaVM(&g_jvm);
|
env->GetJavaVM(&g_jvm);
|
||||||
native_library = clazz;
|
native_library = clazz;
|
||||||
|
|
||||||
|
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) \
|
||||||
|
F(JMethodID, JMethodName, Signature)
|
||||||
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \
|
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \
|
||||||
F(JMethodID, JMethodName, Signature)
|
F(JMethodID, JMethodName, Signature)
|
||||||
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \
|
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \
|
||||||
F(JMethodID, JMethodName, Signature)
|
F(JMethodID, JMethodName, Signature)
|
||||||
#define F(JMethodID, JMethodName, Signature) \
|
#define F(JMethodID, JMethodName, Signature) \
|
||||||
JMethodID = env->GetStaticMethodID(native_library, JMethodName, Signature);
|
JMethodID = env->GetStaticMethodID(native_library, JMethodName, Signature);
|
||||||
|
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
|
||||||
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
|
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
|
||||||
ANDROID_STORAGE_FUNCTIONS(FS)
|
ANDROID_STORAGE_FUNCTIONS(FS)
|
||||||
#undef F
|
#undef F
|
||||||
#undef FS
|
#undef FS
|
||||||
#undef FR
|
#undef FR
|
||||||
|
#undef FH
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnRegisterCallbacks() {
|
void UnRegisterCallbacks() {
|
||||||
|
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
|
||||||
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
|
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
|
||||||
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID)
|
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID)
|
||||||
#define F(JMethodID) JMethodID = nullptr;
|
#define F(JMethodID) JMethodID = nullptr;
|
||||||
|
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
|
||||||
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
|
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
|
||||||
ANDROID_STORAGE_FUNCTIONS(FS)
|
ANDROID_STORAGE_FUNCTIONS(FS)
|
||||||
#undef F
|
#undef F
|
||||||
#undef FS
|
#undef FS
|
||||||
#undef FR
|
#undef FR
|
||||||
|
#undef FH
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsContentUri(const std::string& path) {
|
bool IsContentUri(const std::string& path) {
|
||||||
@@ -95,4 +103,29 @@ ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
|
|||||||
#undef F
|
#undef F
|
||||||
#undef FR
|
#undef FR
|
||||||
|
|
||||||
|
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) \
|
||||||
|
F(FunctionName, JMethodID, Caller)
|
||||||
|
#define F(FunctionName, JMethodID, Caller) \
|
||||||
|
std::string FunctionName(const std::string& filepath) { \
|
||||||
|
if (JMethodID == nullptr) { \
|
||||||
|
return 0; \
|
||||||
|
} \
|
||||||
|
auto env = GetEnvForThread(); \
|
||||||
|
jstring j_filepath = env->NewStringUTF(filepath.c_str()); \
|
||||||
|
jstring j_return = \
|
||||||
|
static_cast<jstring>(env->Caller(native_library, JMethodID, j_filepath)); \
|
||||||
|
if (!j_return) { \
|
||||||
|
return {}; \
|
||||||
|
} \
|
||||||
|
const jchar* jchars = env->GetStringChars(j_return, nullptr); \
|
||||||
|
const jsize length = env->GetStringLength(j_return); \
|
||||||
|
const std::u16string_view string_view(reinterpret_cast<const char16_t*>(jchars), length); \
|
||||||
|
const std::string converted_string = Common::UTF16ToUTF8(string_view); \
|
||||||
|
env->ReleaseStringChars(j_return, jchars); \
|
||||||
|
return converted_string; \
|
||||||
|
}
|
||||||
|
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
|
||||||
|
#undef F
|
||||||
|
#undef FH
|
||||||
|
|
||||||
} // namespace Common::FS::Android
|
} // namespace Common::FS::Android
|
||||||
|
|||||||
@@ -17,19 +17,28 @@
|
|||||||
"(Ljava/lang/String;)Z") \
|
"(Ljava/lang/String;)Z") \
|
||||||
V(Exists, bool, file_exists, CallStaticBooleanMethod, "exists", "(Ljava/lang/String;)Z")
|
V(Exists, bool, file_exists, CallStaticBooleanMethod, "exists", "(Ljava/lang/String;)Z")
|
||||||
|
|
||||||
|
#define ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(V) \
|
||||||
|
V(GetParentDirectory, get_parent_directory, CallStaticObjectMethod, "getParentDirectory", \
|
||||||
|
"(Ljava/lang/String;)Ljava/lang/String;") \
|
||||||
|
V(GetFilename, get_filename, CallStaticObjectMethod, "getFilename", \
|
||||||
|
"(Ljava/lang/String;)Ljava/lang/String;")
|
||||||
|
|
||||||
namespace Common::FS::Android {
|
namespace Common::FS::Android {
|
||||||
|
|
||||||
static JavaVM* g_jvm = nullptr;
|
static JavaVM* g_jvm = nullptr;
|
||||||
static jclass native_library = nullptr;
|
static jclass native_library = nullptr;
|
||||||
|
|
||||||
|
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
|
||||||
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
|
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
|
||||||
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID)
|
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID)
|
||||||
#define F(JMethodID) static jmethodID JMethodID = nullptr;
|
#define F(JMethodID) static jmethodID JMethodID = nullptr;
|
||||||
|
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
|
||||||
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
|
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
|
||||||
ANDROID_STORAGE_FUNCTIONS(FS)
|
ANDROID_STORAGE_FUNCTIONS(FS)
|
||||||
#undef F
|
#undef F
|
||||||
#undef FS
|
#undef FS
|
||||||
#undef FR
|
#undef FR
|
||||||
|
#undef FH
|
||||||
|
|
||||||
enum class OpenMode {
|
enum class OpenMode {
|
||||||
Read,
|
Read,
|
||||||
@@ -62,4 +71,10 @@ ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
|
|||||||
#undef F
|
#undef F
|
||||||
#undef FR
|
#undef FR
|
||||||
|
|
||||||
|
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(FunctionName)
|
||||||
|
#define F(FunctionName) std::string FunctionName(const std::string& filepath);
|
||||||
|
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
|
||||||
|
#undef F
|
||||||
|
#undef FH
|
||||||
|
|
||||||
} // namespace Common::FS::Android
|
} // namespace Common::FS::Android
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#define AMIIBO_DIR "amiibo"
|
#define AMIIBO_DIR "amiibo"
|
||||||
#define CACHE_DIR "cache"
|
#define CACHE_DIR "cache"
|
||||||
#define CONFIG_DIR "config"
|
#define CONFIG_DIR "config"
|
||||||
|
#define CRASH_DUMPS_DIR "crash_dumps"
|
||||||
#define DUMP_DIR "dump"
|
#define DUMP_DIR "dump"
|
||||||
#define KEYS_DIR "keys"
|
#define KEYS_DIR "keys"
|
||||||
#define LOAD_DIR "load"
|
#define LOAD_DIR "load"
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ public:
|
|||||||
GenerateYuzuPath(YuzuPath::AmiiboDir, yuzu_path / AMIIBO_DIR);
|
GenerateYuzuPath(YuzuPath::AmiiboDir, yuzu_path / AMIIBO_DIR);
|
||||||
GenerateYuzuPath(YuzuPath::CacheDir, yuzu_path_cache);
|
GenerateYuzuPath(YuzuPath::CacheDir, yuzu_path_cache);
|
||||||
GenerateYuzuPath(YuzuPath::ConfigDir, yuzu_path_config);
|
GenerateYuzuPath(YuzuPath::ConfigDir, yuzu_path_config);
|
||||||
|
GenerateYuzuPath(YuzuPath::CrashDumpsDir, yuzu_path / CRASH_DUMPS_DIR);
|
||||||
GenerateYuzuPath(YuzuPath::DumpDir, yuzu_path / DUMP_DIR);
|
GenerateYuzuPath(YuzuPath::DumpDir, yuzu_path / DUMP_DIR);
|
||||||
GenerateYuzuPath(YuzuPath::KeysDir, yuzu_path / KEYS_DIR);
|
GenerateYuzuPath(YuzuPath::KeysDir, yuzu_path / KEYS_DIR);
|
||||||
GenerateYuzuPath(YuzuPath::LoadDir, yuzu_path / LOAD_DIR);
|
GenerateYuzuPath(YuzuPath::LoadDir, yuzu_path / LOAD_DIR);
|
||||||
@@ -400,6 +401,16 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string_view GetParentPath(std::string_view path) {
|
std::string_view GetParentPath(std::string_view path) {
|
||||||
|
if (path.empty()) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ANDROID
|
||||||
|
if (path[0] != '/') {
|
||||||
|
std::string path_string{path};
|
||||||
|
return FS::Android::GetParentDirectory(path_string);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
const auto name_bck_index = path.rfind('\\');
|
const auto name_bck_index = path.rfind('\\');
|
||||||
const auto name_fwd_index = path.rfind('/');
|
const auto name_fwd_index = path.rfind('/');
|
||||||
std::size_t name_index;
|
std::size_t name_index;
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ enum class YuzuPath {
|
|||||||
AmiiboDir, // Where Amiibo backups are stored.
|
AmiiboDir, // Where Amiibo backups are stored.
|
||||||
CacheDir, // Where cached filesystem data is stored.
|
CacheDir, // Where cached filesystem data is stored.
|
||||||
ConfigDir, // Where config files are stored.
|
ConfigDir, // Where config files are stored.
|
||||||
|
CrashDumpsDir, // Where crash dumps are stored.
|
||||||
DumpDir, // Where dumped data is stored.
|
DumpDir, // Where dumped data is stored.
|
||||||
KeysDir, // Where key files are stored.
|
KeysDir, // Where key files are stored.
|
||||||
LoadDir, // Where cheat/mod files are stored.
|
LoadDir, // Where cheat/mod files are stored.
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ void ConfigureNvidiaEnvironmentFlags() {
|
|||||||
|
|
||||||
void(_putenv(fmt::format("__GL_SHADER_DISK_CACHE_PATH={}", windows_path_string).c_str()));
|
void(_putenv(fmt::format("__GL_SHADER_DISK_CACHE_PATH={}", windows_path_string).c_str()));
|
||||||
void(_putenv("__GL_SHADER_DISK_CACHE_SKIP_CLEANUP=1"));
|
void(_putenv("__GL_SHADER_DISK_CACHE_SKIP_CLEANUP=1"));
|
||||||
|
void(_putenv("__GL_THREADED_OPTIMIZATIONS=1"));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ SWITCHABLE(CpuAccuracy, true);
|
|||||||
SWITCHABLE(FullscreenMode, true);
|
SWITCHABLE(FullscreenMode, true);
|
||||||
SWITCHABLE(GpuAccuracy, true);
|
SWITCHABLE(GpuAccuracy, true);
|
||||||
SWITCHABLE(Language, true);
|
SWITCHABLE(Language, true);
|
||||||
|
SWITCHABLE(MemoryLayout, true);
|
||||||
SWITCHABLE(NvdecEmulation, false);
|
SWITCHABLE(NvdecEmulation, false);
|
||||||
SWITCHABLE(Region, true);
|
SWITCHABLE(Region, true);
|
||||||
SWITCHABLE(RendererBackend, true);
|
SWITCHABLE(RendererBackend, true);
|
||||||
@@ -61,6 +62,10 @@ SWITCHABLE(u32, false);
|
|||||||
SWITCHABLE(u8, false);
|
SWITCHABLE(u8, false);
|
||||||
SWITCHABLE(u8, true);
|
SWITCHABLE(u8, true);
|
||||||
|
|
||||||
|
// Used in UISettings
|
||||||
|
// TODO see if we can move this to uisettings.cpp
|
||||||
|
SWITCHABLE(ConfirmStop, true);
|
||||||
|
|
||||||
#undef SETTING
|
#undef SETTING
|
||||||
#undef SWITCHABLE
|
#undef SWITCHABLE
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ SWITCHABLE(CpuAccuracy, true);
|
|||||||
SWITCHABLE(FullscreenMode, true);
|
SWITCHABLE(FullscreenMode, true);
|
||||||
SWITCHABLE(GpuAccuracy, true);
|
SWITCHABLE(GpuAccuracy, true);
|
||||||
SWITCHABLE(Language, true);
|
SWITCHABLE(Language, true);
|
||||||
|
SWITCHABLE(MemoryLayout, true);
|
||||||
SWITCHABLE(NvdecEmulation, false);
|
SWITCHABLE(NvdecEmulation, false);
|
||||||
SWITCHABLE(Region, true);
|
SWITCHABLE(Region, true);
|
||||||
SWITCHABLE(RendererBackend, true);
|
SWITCHABLE(RendererBackend, true);
|
||||||
@@ -83,6 +84,10 @@ SWITCHABLE(u32, false);
|
|||||||
SWITCHABLE(u8, false);
|
SWITCHABLE(u8, false);
|
||||||
SWITCHABLE(u8, true);
|
SWITCHABLE(u8, true);
|
||||||
|
|
||||||
|
// Used in UISettings
|
||||||
|
// TODO see if we can move this to uisettings.h
|
||||||
|
SWITCHABLE(ConfirmStop, true);
|
||||||
|
|
||||||
#undef SETTING
|
#undef SETTING
|
||||||
#undef SWITCHABLE
|
#undef SWITCHABLE
|
||||||
#endif
|
#endif
|
||||||
@@ -500,7 +505,6 @@ struct Values {
|
|||||||
linkage, false, "use_auto_stub", Category::Debugging, Specialization::Default, false};
|
linkage, false, "use_auto_stub", Category::Debugging, Specialization::Default, false};
|
||||||
Setting<bool> enable_all_controllers{linkage, false, "enable_all_controllers",
|
Setting<bool> enable_all_controllers{linkage, false, "enable_all_controllers",
|
||||||
Category::Debugging};
|
Category::Debugging};
|
||||||
Setting<bool> create_crash_dumps{linkage, false, "create_crash_dumps", Category::Debugging};
|
|
||||||
Setting<bool> perform_vulkan_check{linkage, true, "perform_vulkan_check", Category::Debugging};
|
Setting<bool> perform_vulkan_check{linkage, true, "perform_vulkan_check", Category::Debugging};
|
||||||
|
|
||||||
// Miscellaneous
|
// Miscellaneous
|
||||||
|
|||||||
@@ -133,6 +133,8 @@ ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid);
|
|||||||
|
|
||||||
ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb);
|
ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb);
|
||||||
|
|
||||||
|
ENUM(ConfirmStop, Ask_Always, Ask_Based_On_Game, Ask_Never);
|
||||||
|
|
||||||
ENUM(FullscreenMode, Borderless, Exclusive);
|
ENUM(FullscreenMode, Borderless, Exclusive);
|
||||||
|
|
||||||
ENUM(NvdecEmulation, Off, Cpu, Gpu);
|
ENUM(NvdecEmulation, Off, Cpu, Gpu);
|
||||||
|
|||||||
8
src/common/stb.cpp
Normal file
8
src/common/stb.cpp
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#define STB_IMAGE_RESIZE_IMPLEMENTATION
|
||||||
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
|
|
||||||
|
#include "common/stb.h"
|
||||||
8
src/common/stb.h
Normal file
8
src/common/stb.h
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stb_image.h>
|
||||||
|
#include <stb_image_resize.h>
|
||||||
|
#include <stb_image_write.h>
|
||||||
@@ -14,6 +14,10 @@
|
|||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef ANDROID
|
||||||
|
#include <common/fs/fs_android.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
/// Make a string lowercase
|
/// Make a string lowercase
|
||||||
@@ -63,6 +67,14 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
|
|||||||
if (full_path.empty())
|
if (full_path.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
#ifdef ANDROID
|
||||||
|
if (full_path[0] != '/') {
|
||||||
|
*_pPath = Common::FS::Android::GetParentDirectory(full_path);
|
||||||
|
*_pFilename = Common::FS::Android::GetFilename(full_path);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
std::size_t dir_end = full_path.find_last_of("/"
|
std::size_t dir_end = full_path.find_last_of("/"
|
||||||
// windows needs the : included for something like just "C:" to be considered a directory
|
// windows needs the : included for something like just "C:" to be considered a directory
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include <mach/mach.h>
|
#include <mach/mach.h>
|
||||||
#elif defined(_WIN32)
|
#elif defined(_WIN32)
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#include "common/string_util.h"
|
||||||
#else
|
#else
|
||||||
#if defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
|
#if defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||||
#include <pthread_np.h>
|
#include <pthread_np.h>
|
||||||
@@ -82,29 +83,8 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
|
|||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
|
|
||||||
// Sets the debugger-visible name of the current thread.
|
// Sets the debugger-visible name of the current thread.
|
||||||
// Uses trick documented in:
|
|
||||||
// https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a-thread-name-in-native-code
|
|
||||||
void SetCurrentThreadName(const char* name) {
|
void SetCurrentThreadName(const char* name) {
|
||||||
static const DWORD MS_VC_EXCEPTION = 0x406D1388;
|
SetThreadDescription(GetCurrentThread(), UTF8ToUTF16W(name).data());
|
||||||
|
|
||||||
#pragma pack(push, 8)
|
|
||||||
struct THREADNAME_INFO {
|
|
||||||
DWORD dwType; // must be 0x1000
|
|
||||||
LPCSTR szName; // pointer to name (in user addr space)
|
|
||||||
DWORD dwThreadID; // thread ID (-1=caller thread)
|
|
||||||
DWORD dwFlags; // reserved for future use, must be zero
|
|
||||||
} info;
|
|
||||||
#pragma pack(pop)
|
|
||||||
|
|
||||||
info.dwType = 0x1000;
|
|
||||||
info.szName = name;
|
|
||||||
info.dwThreadID = std::numeric_limits<DWORD>::max();
|
|
||||||
info.dwFlags = 0;
|
|
||||||
|
|
||||||
__try {
|
|
||||||
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
|
|
||||||
} __except (EXCEPTION_CONTINUE_EXECUTION) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // !MSVC_VER, so must be POSIX threads
|
#else // !MSVC_VER, so must be POSIX threads
|
||||||
|
|||||||
@@ -86,9 +86,9 @@ void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector<Backt
|
|||||||
|
|
||||||
std::map<std::string, Symbols::Symbols> symbols;
|
std::map<std::string, Symbols::Symbols> symbols;
|
||||||
for (const auto& module : modules) {
|
for (const auto& module : modules) {
|
||||||
symbols.insert_or_assign(
|
symbols.insert_or_assign(module.second,
|
||||||
module.second, Symbols::GetSymbols(module.first, system.ApplicationMemory(),
|
Symbols::GetSymbols(module.first, system.ApplicationMemory(),
|
||||||
system.ApplicationProcess()->Is64BitProcess()));
|
system.ApplicationProcess()->Is64Bit()));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& entry : out) {
|
for (auto& entry : out) {
|
||||||
|
|||||||
@@ -116,11 +116,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (concat.empty()) {
|
return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(dir->GetName(),
|
||||||
return nullptr;
|
std::move(concat));
|
||||||
}
|
|
||||||
|
|
||||||
return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Common::FS::IsDir(path)) {
|
if (Common::FS::IsDir(path)) {
|
||||||
@@ -312,17 +309,10 @@ struct System::Impl {
|
|||||||
|
|
||||||
telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider);
|
telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider);
|
||||||
|
|
||||||
// Create a resource limit for the process.
|
|
||||||
const auto physical_memory_size =
|
|
||||||
kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::Application);
|
|
||||||
auto* resource_limit = Kernel::CreateResourceLimitForProcess(system, physical_memory_size);
|
|
||||||
|
|
||||||
// Create the process.
|
// Create the process.
|
||||||
auto main_process = Kernel::KProcess::Create(system.Kernel());
|
auto main_process = Kernel::KProcess::Create(system.Kernel());
|
||||||
ASSERT(Kernel::KProcess::Initialize(main_process, system, "main",
|
|
||||||
Kernel::KProcess::ProcessType::Userland, resource_limit)
|
|
||||||
.IsSuccess());
|
|
||||||
Kernel::KProcess::Register(system.Kernel(), main_process);
|
Kernel::KProcess::Register(system.Kernel(), main_process);
|
||||||
|
kernel.AppendNewProcess(main_process);
|
||||||
kernel.MakeApplicationProcess(main_process);
|
kernel.MakeApplicationProcess(main_process);
|
||||||
const auto [load_result, load_parameters] = app_loader->Load(*main_process, system);
|
const auto [load_result, load_parameters] = app_loader->Load(*main_process, system);
|
||||||
if (load_result != Loader::ResultStatus::Success) {
|
if (load_result != Loader::ResultStatus::Success) {
|
||||||
@@ -421,6 +411,7 @@ struct System::Impl {
|
|||||||
services->KillNVNFlinger();
|
services->KillNVNFlinger();
|
||||||
}
|
}
|
||||||
kernel.CloseServices();
|
kernel.CloseServices();
|
||||||
|
kernel.ShutdownCores();
|
||||||
services.reset();
|
services.reset();
|
||||||
service_manager.reset();
|
service_manager.reset();
|
||||||
cheat_engine.reset();
|
cheat_engine.reset();
|
||||||
@@ -432,7 +423,6 @@ struct System::Impl {
|
|||||||
gpu_core.reset();
|
gpu_core.reset();
|
||||||
host1x_core.reset();
|
host1x_core.reset();
|
||||||
perf_stats.reset();
|
perf_stats.reset();
|
||||||
kernel.ShutdownCores();
|
|
||||||
cpu_manager.Shutdown();
|
cpu_manager.Shutdown();
|
||||||
debugger.reset();
|
debugger.reset();
|
||||||
kernel.Shutdown();
|
kernel.Shutdown();
|
||||||
|
|||||||
@@ -258,20 +258,20 @@ private:
|
|||||||
Kernel::KScopedSchedulerLock sl{system.Kernel()};
|
Kernel::KScopedSchedulerLock sl{system.Kernel()};
|
||||||
|
|
||||||
// Put all threads to sleep on next scheduler round.
|
// Put all threads to sleep on next scheduler round.
|
||||||
for (auto* thread : ThreadList()) {
|
for (auto& thread : ThreadList()) {
|
||||||
thread->RequestSuspend(Kernel::SuspendType::Debug);
|
thread.RequestSuspend(Kernel::SuspendType::Debug);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResumeEmulation(Kernel::KThread* except = nullptr) {
|
void ResumeEmulation(Kernel::KThread* except = nullptr) {
|
||||||
// Wake up all threads.
|
// Wake up all threads.
|
||||||
for (auto* thread : ThreadList()) {
|
for (auto& thread : ThreadList()) {
|
||||||
if (thread == except) {
|
if (std::addressof(thread) == except) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
thread->SetStepState(Kernel::StepState::NotStepping);
|
thread.SetStepState(Kernel::StepState::NotStepping);
|
||||||
thread->Resume(Kernel::SuspendType::Debug);
|
thread.Resume(Kernel::SuspendType::Debug);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,13 +283,17 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void UpdateActiveThread() {
|
void UpdateActiveThread() {
|
||||||
const auto& threads{ThreadList()};
|
auto& threads{ThreadList()};
|
||||||
if (std::find(threads.begin(), threads.end(), state->active_thread) == threads.end()) {
|
for (auto& thread : threads) {
|
||||||
state->active_thread = threads.front();
|
if (std::addressof(thread) == state->active_thread) {
|
||||||
|
// Thread is still alive, no need to update.
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
state->active_thread = std::addressof(threads.front());
|
||||||
|
}
|
||||||
|
|
||||||
const std::list<Kernel::KThread*>& ThreadList() {
|
Kernel::KProcess::ThreadList& ThreadList() {
|
||||||
return system.ApplicationProcess()->GetThreadList();
|
return system.ApplicationProcess()->GetThreadList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ static std::string EscapeXML(std::string_view data) {
|
|||||||
|
|
||||||
GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_)
|
GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_)
|
||||||
: DebuggerFrontend(backend_), system{system_} {
|
: DebuggerFrontend(backend_), system{system_} {
|
||||||
if (system.ApplicationProcess()->Is64BitProcess()) {
|
if (system.ApplicationProcess()->Is64Bit()) {
|
||||||
arch = std::make_unique<GDBStubA64>();
|
arch = std::make_unique<GDBStubA64>();
|
||||||
} else {
|
} else {
|
||||||
arch = std::make_unique<GDBStubA32>();
|
arch = std::make_unique<GDBStubA32>();
|
||||||
@@ -446,10 +446,10 @@ void GDBStub::HandleBreakpointRemove(std::string_view command) {
|
|||||||
// See osdbg_thread_local_region.os.horizon.hpp and osdbg_thread_type.os.horizon.hpp
|
// See osdbg_thread_local_region.os.horizon.hpp and osdbg_thread_type.os.horizon.hpp
|
||||||
|
|
||||||
static std::optional<std::string> GetNameFromThreadType32(Core::Memory::Memory& memory,
|
static std::optional<std::string> GetNameFromThreadType32(Core::Memory::Memory& memory,
|
||||||
const Kernel::KThread* thread) {
|
const Kernel::KThread& thread) {
|
||||||
// Read thread type from TLS
|
// Read thread type from TLS
|
||||||
const VAddr tls_thread_type{memory.Read32(thread->GetTlsAddress() + 0x1fc)};
|
const VAddr tls_thread_type{memory.Read32(thread.GetTlsAddress() + 0x1fc)};
|
||||||
const VAddr argument_thread_type{thread->GetArgument()};
|
const VAddr argument_thread_type{thread.GetArgument()};
|
||||||
|
|
||||||
if (argument_thread_type && tls_thread_type != argument_thread_type) {
|
if (argument_thread_type && tls_thread_type != argument_thread_type) {
|
||||||
// Probably not created by nnsdk, no name available.
|
// Probably not created by nnsdk, no name available.
|
||||||
@@ -477,10 +477,10 @@ static std::optional<std::string> GetNameFromThreadType32(Core::Memory::Memory&
|
|||||||
}
|
}
|
||||||
|
|
||||||
static std::optional<std::string> GetNameFromThreadType64(Core::Memory::Memory& memory,
|
static std::optional<std::string> GetNameFromThreadType64(Core::Memory::Memory& memory,
|
||||||
const Kernel::KThread* thread) {
|
const Kernel::KThread& thread) {
|
||||||
// Read thread type from TLS
|
// Read thread type from TLS
|
||||||
const VAddr tls_thread_type{memory.Read64(thread->GetTlsAddress() + 0x1f8)};
|
const VAddr tls_thread_type{memory.Read64(thread.GetTlsAddress() + 0x1f8)};
|
||||||
const VAddr argument_thread_type{thread->GetArgument()};
|
const VAddr argument_thread_type{thread.GetArgument()};
|
||||||
|
|
||||||
if (argument_thread_type && tls_thread_type != argument_thread_type) {
|
if (argument_thread_type && tls_thread_type != argument_thread_type) {
|
||||||
// Probably not created by nnsdk, no name available.
|
// Probably not created by nnsdk, no name available.
|
||||||
@@ -508,16 +508,16 @@ static std::optional<std::string> GetNameFromThreadType64(Core::Memory::Memory&
|
|||||||
}
|
}
|
||||||
|
|
||||||
static std::optional<std::string> GetThreadName(Core::System& system,
|
static std::optional<std::string> GetThreadName(Core::System& system,
|
||||||
const Kernel::KThread* thread) {
|
const Kernel::KThread& thread) {
|
||||||
if (system.ApplicationProcess()->Is64BitProcess()) {
|
if (system.ApplicationProcess()->Is64Bit()) {
|
||||||
return GetNameFromThreadType64(system.ApplicationMemory(), thread);
|
return GetNameFromThreadType64(system.ApplicationMemory(), thread);
|
||||||
} else {
|
} else {
|
||||||
return GetNameFromThreadType32(system.ApplicationMemory(), thread);
|
return GetNameFromThreadType32(system.ApplicationMemory(), thread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string_view GetThreadWaitReason(const Kernel::KThread* thread) {
|
static std::string_view GetThreadWaitReason(const Kernel::KThread& thread) {
|
||||||
switch (thread->GetWaitReasonForDebugging()) {
|
switch (thread.GetWaitReasonForDebugging()) {
|
||||||
case Kernel::ThreadWaitReasonForDebugging::Sleep:
|
case Kernel::ThreadWaitReasonForDebugging::Sleep:
|
||||||
return "Sleep";
|
return "Sleep";
|
||||||
case Kernel::ThreadWaitReasonForDebugging::IPC:
|
case Kernel::ThreadWaitReasonForDebugging::IPC:
|
||||||
@@ -535,8 +535,8 @@ static std::string_view GetThreadWaitReason(const Kernel::KThread* thread) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string GetThreadState(const Kernel::KThread* thread) {
|
static std::string GetThreadState(const Kernel::KThread& thread) {
|
||||||
switch (thread->GetState()) {
|
switch (thread.GetState()) {
|
||||||
case Kernel::ThreadState::Initialized:
|
case Kernel::ThreadState::Initialized:
|
||||||
return "Initialized";
|
return "Initialized";
|
||||||
case Kernel::ThreadState::Waiting:
|
case Kernel::ThreadState::Waiting:
|
||||||
@@ -604,7 +604,7 @@ void GDBStub::HandleQuery(std::string_view command) {
|
|||||||
const auto& threads = system.ApplicationProcess()->GetThreadList();
|
const auto& threads = system.ApplicationProcess()->GetThreadList();
|
||||||
std::vector<std::string> thread_ids;
|
std::vector<std::string> thread_ids;
|
||||||
for (const auto& thread : threads) {
|
for (const auto& thread : threads) {
|
||||||
thread_ids.push_back(fmt::format("{:x}", thread->GetThreadId()));
|
thread_ids.push_back(fmt::format("{:x}", thread.GetThreadId()));
|
||||||
}
|
}
|
||||||
SendReply(fmt::format("m{}", fmt::join(thread_ids, ",")));
|
SendReply(fmt::format("m{}", fmt::join(thread_ids, ",")));
|
||||||
} else if (command.starts_with("sThreadInfo")) {
|
} else if (command.starts_with("sThreadInfo")) {
|
||||||
@@ -616,14 +616,14 @@ void GDBStub::HandleQuery(std::string_view command) {
|
|||||||
buffer += "<threads>";
|
buffer += "<threads>";
|
||||||
|
|
||||||
const auto& threads = system.ApplicationProcess()->GetThreadList();
|
const auto& threads = system.ApplicationProcess()->GetThreadList();
|
||||||
for (const auto* thread : threads) {
|
for (const auto& thread : threads) {
|
||||||
auto thread_name{GetThreadName(system, thread)};
|
auto thread_name{GetThreadName(system, thread)};
|
||||||
if (!thread_name) {
|
if (!thread_name) {
|
||||||
thread_name = fmt::format("Thread {:d}", thread->GetThreadId());
|
thread_name = fmt::format("Thread {:d}", thread.GetThreadId());
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer += fmt::format(R"(<thread id="{:x}" core="{:d}" name="{}">{}</thread>)",
|
buffer += fmt::format(R"(<thread id="{:x}" core="{:d}" name="{}">{}</thread>)",
|
||||||
thread->GetThreadId(), thread->GetActiveCore(),
|
thread.GetThreadId(), thread.GetActiveCore(),
|
||||||
EscapeXML(*thread_name), GetThreadState(thread));
|
EscapeXML(*thread_name), GetThreadState(thread));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -822,11 +822,13 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
|
|||||||
const char i = True(mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-';
|
const char i = True(mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-';
|
||||||
const char d = True(mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-';
|
const char d = True(mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-';
|
||||||
const char u = True(mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-';
|
const char u = True(mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-';
|
||||||
|
const char p =
|
||||||
|
True(mem_info.attribute & MemoryAttribute::PermissionLocked) ? 'P' : '-';
|
||||||
|
|
||||||
reply +=
|
reply += fmt::format(" {:#012x} - {:#012x} {} {} {}{}{}{}{} [{}, {}]\n",
|
||||||
fmt::format(" {:#012x} - {:#012x} {} {} {}{}{}{} [{}, {}]\n",
|
mem_info.base_address,
|
||||||
mem_info.base_address, mem_info.base_address + mem_info.size - 1,
|
mem_info.base_address + mem_info.size - 1, perm, state, l, i,
|
||||||
perm, state, l, i, d, u, mem_info.ipc_count, mem_info.device_count);
|
d, u, p, mem_info.ipc_count, mem_info.device_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
const uintptr_t next_address = mem_info.base_address + mem_info.size;
|
const uintptr_t next_address = mem_info.base_address + mem_info.size;
|
||||||
@@ -848,10 +850,10 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
|
Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
|
||||||
const auto& threads{system.ApplicationProcess()->GetThreadList()};
|
auto& threads{system.ApplicationProcess()->GetThreadList()};
|
||||||
for (auto* thread : threads) {
|
for (auto& thread : threads) {
|
||||||
if (thread->GetThreadId() == thread_id) {
|
if (thread.GetThreadId() == thread_id) {
|
||||||
return thread;
|
return std::addressof(thread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -107,46 +107,25 @@ static u64 romfs_get_hash_table_count(u64 num_entries) {
|
|||||||
|
|
||||||
void RomFSBuildContext::VisitDirectory(VirtualDir romfs_dir, VirtualDir ext_dir,
|
void RomFSBuildContext::VisitDirectory(VirtualDir romfs_dir, VirtualDir ext_dir,
|
||||||
std::shared_ptr<RomFSBuildDirectoryContext> parent) {
|
std::shared_ptr<RomFSBuildDirectoryContext> parent) {
|
||||||
std::vector<std::shared_ptr<RomFSBuildDirectoryContext>> child_dirs;
|
for (auto& child_romfs_file : romfs_dir->GetFiles()) {
|
||||||
|
const auto name = child_romfs_file->GetName();
|
||||||
const auto entries = romfs_dir->GetEntries();
|
|
||||||
|
|
||||||
for (const auto& kv : entries) {
|
|
||||||
if (kv.second == VfsEntryType::Directory) {
|
|
||||||
const auto child = std::make_shared<RomFSBuildDirectoryContext>();
|
|
||||||
// Set child's path.
|
|
||||||
child->cur_path_ofs = parent->path_len + 1;
|
|
||||||
child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
|
|
||||||
child->path = parent->path + "/" + kv.first;
|
|
||||||
|
|
||||||
if (ext_dir != nullptr && ext_dir->GetFile(kv.first + ".stub") != nullptr) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sanity check on path_len
|
|
||||||
ASSERT(child->path_len < FS_MAX_PATH);
|
|
||||||
|
|
||||||
if (AddDirectory(parent, child)) {
|
|
||||||
child_dirs.push_back(child);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const auto child = std::make_shared<RomFSBuildFileContext>();
|
const auto child = std::make_shared<RomFSBuildFileContext>();
|
||||||
// Set child's path.
|
// Set child's path.
|
||||||
child->cur_path_ofs = parent->path_len + 1;
|
child->cur_path_ofs = parent->path_len + 1;
|
||||||
child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
|
child->path_len = child->cur_path_ofs + static_cast<u32>(name.size());
|
||||||
child->path = parent->path + "/" + kv.first;
|
child->path = parent->path + "/" + name;
|
||||||
|
|
||||||
if (ext_dir != nullptr && ext_dir->GetFile(kv.first + ".stub") != nullptr) {
|
if (ext_dir != nullptr && ext_dir->GetFile(name + ".stub") != nullptr) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sanity check on path_len
|
// Sanity check on path_len
|
||||||
ASSERT(child->path_len < FS_MAX_PATH);
|
ASSERT(child->path_len < FS_MAX_PATH);
|
||||||
|
|
||||||
child->source = romfs_dir->GetFile(kv.first);
|
child->source = std::move(child_romfs_file);
|
||||||
|
|
||||||
if (ext_dir != nullptr) {
|
if (ext_dir != nullptr) {
|
||||||
if (const auto ips = ext_dir->GetFile(kv.first + ".ips")) {
|
if (const auto ips = ext_dir->GetFile(name + ".ips")) {
|
||||||
if (auto patched = PatchIPS(child->source, ips)) {
|
if (auto patched = PatchIPS(child->source, ips)) {
|
||||||
child->source = std::move(patched);
|
child->source = std::move(patched);
|
||||||
}
|
}
|
||||||
@@ -157,12 +136,27 @@ void RomFSBuildContext::VisitDirectory(VirtualDir romfs_dir, VirtualDir ext_dir,
|
|||||||
|
|
||||||
AddFile(parent, child);
|
AddFile(parent, child);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto& child_romfs_dir : romfs_dir->GetSubdirectories()) {
|
||||||
|
const auto name = child_romfs_dir->GetName();
|
||||||
|
const auto child = std::make_shared<RomFSBuildDirectoryContext>();
|
||||||
|
// Set child's path.
|
||||||
|
child->cur_path_ofs = parent->path_len + 1;
|
||||||
|
child->path_len = child->cur_path_ofs + static_cast<u32>(name.size());
|
||||||
|
child->path = parent->path + "/" + name;
|
||||||
|
|
||||||
|
if (ext_dir != nullptr && ext_dir->GetFile(name + ".stub") != nullptr) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& child : child_dirs) {
|
// Sanity check on path_len
|
||||||
auto subdir_name = std::string_view(child->path).substr(child->cur_path_ofs);
|
ASSERT(child->path_len < FS_MAX_PATH);
|
||||||
auto child_romfs_dir = romfs_dir->GetSubdirectory(subdir_name);
|
|
||||||
auto child_ext_dir = ext_dir != nullptr ? ext_dir->GetSubdirectory(subdir_name) : nullptr;
|
if (!AddDirectory(parent, child)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto child_ext_dir = ext_dir != nullptr ? ext_dir->GetSubdirectory(name) : nullptr;
|
||||||
this->VisitDirectory(child_romfs_dir, child_ext_dir, child);
|
this->VisitDirectory(child_romfs_dir, child_ext_dir, child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -293,7 +287,7 @@ std::multimap<u64, VirtualFile> RomFSBuildContext::Build() {
|
|||||||
|
|
||||||
cur_entry.name_size = name_size;
|
cur_entry.name_size = name_size;
|
||||||
|
|
||||||
out.emplace(cur_file->offset + ROMFS_FILEPARTITION_OFS, cur_file->source);
|
out.emplace(cur_file->offset + ROMFS_FILEPARTITION_OFS, std::move(cur_file->source));
|
||||||
std::memcpy(file_table.data() + cur_file->entry_offset, &cur_entry, sizeof(RomFSFileEntry));
|
std::memcpy(file_table.data() + cur_file->entry_offset, &cur_entry, sizeof(RomFSFileEntry));
|
||||||
std::memset(file_table.data() + cur_file->entry_offset + sizeof(RomFSFileEntry), 0,
|
std::memset(file_table.data() + cur_file->entry_offset + sizeof(RomFSFileEntry), 0,
|
||||||
Common::AlignUp(cur_entry.name_size, 4));
|
Common::AlignUp(cur_entry.name_size, 4));
|
||||||
|
|||||||
@@ -377,16 +377,16 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
|
|||||||
|
|
||||||
auto romfs_dir = FindSubdirectoryCaseless(subdir, "romfs");
|
auto romfs_dir = FindSubdirectoryCaseless(subdir, "romfs");
|
||||||
if (romfs_dir != nullptr)
|
if (romfs_dir != nullptr)
|
||||||
layers.push_back(std::make_shared<CachedVfsDirectory>(romfs_dir));
|
layers.emplace_back(std::make_shared<CachedVfsDirectory>(std::move(romfs_dir)));
|
||||||
|
|
||||||
auto ext_dir = FindSubdirectoryCaseless(subdir, "romfs_ext");
|
auto ext_dir = FindSubdirectoryCaseless(subdir, "romfs_ext");
|
||||||
if (ext_dir != nullptr)
|
if (ext_dir != nullptr)
|
||||||
layers_ext.push_back(std::make_shared<CachedVfsDirectory>(ext_dir));
|
layers_ext.emplace_back(std::make_shared<CachedVfsDirectory>(std::move(ext_dir)));
|
||||||
|
|
||||||
if (type == ContentRecordType::HtmlDocument) {
|
if (type == ContentRecordType::HtmlDocument) {
|
||||||
auto manual_dir = FindSubdirectoryCaseless(subdir, "manual_html");
|
auto manual_dir = FindSubdirectoryCaseless(subdir, "manual_html");
|
||||||
if (manual_dir != nullptr)
|
if (manual_dir != nullptr)
|
||||||
layers.push_back(std::make_shared<CachedVfsDirectory>(manual_dir));
|
layers.emplace_back(std::make_shared<CachedVfsDirectory>(std::move(manual_dir)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -400,7 +400,7 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
layers.push_back(std::move(extracted));
|
layers.emplace_back(std::move(extracted));
|
||||||
|
|
||||||
auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers));
|
auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers));
|
||||||
if (layered == nullptr) {
|
if (layered == nullptr) {
|
||||||
|
|||||||
@@ -104,16 +104,16 @@ Loader::ResultStatus ProgramMetadata::Reload(VirtualFile file) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*static*/ ProgramMetadata ProgramMetadata::GetDefault() {
|
/*static*/ ProgramMetadata ProgramMetadata::GetDefault() {
|
||||||
// Allow use of cores 0~3 and thread priorities 1~63.
|
// Allow use of cores 0~3 and thread priorities 16~63.
|
||||||
constexpr u32 default_thread_info_capability = 0x30007F7;
|
constexpr u32 default_thread_info_capability = 0x30043F7;
|
||||||
|
|
||||||
ProgramMetadata result;
|
ProgramMetadata result;
|
||||||
|
|
||||||
result.LoadManual(
|
result.LoadManual(
|
||||||
true /*is_64_bit*/, FileSys::ProgramAddressSpaceType::Is39Bit /*address_space*/,
|
true /*is_64_bit*/, FileSys::ProgramAddressSpaceType::Is39Bit /*address_space*/,
|
||||||
0x2c /*main_thread_prio*/, 0 /*main_thread_core*/, 0x00100000 /*main_thread_stack_size*/,
|
0x2c /*main_thread_prio*/, 0 /*main_thread_core*/, 0x100000 /*main_thread_stack_size*/,
|
||||||
0 /*title_id*/, 0xFFFFFFFFFFFFFFFF /*filesystem_permissions*/,
|
0 /*title_id*/, 0xFFFFFFFFFFFFFFFF /*filesystem_permissions*/, 0 /*system_resource_size*/,
|
||||||
0x1FE00000 /*system_resource_size*/, {default_thread_info_capability} /*capabilities*/);
|
{default_thread_info_capability} /*capabilities*/);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,6 +73,9 @@ public:
|
|||||||
u64 GetFilesystemPermissions() const;
|
u64 GetFilesystemPermissions() const;
|
||||||
u32 GetSystemResourceSize() const;
|
u32 GetSystemResourceSize() const;
|
||||||
const KernelCapabilityDescriptors& GetKernelCapabilities() const;
|
const KernelCapabilityDescriptors& GetKernelCapabilities() const;
|
||||||
|
const std::array<u8, 0x10>& GetName() const {
|
||||||
|
return npdm_header.application_name;
|
||||||
|
}
|
||||||
|
|
||||||
void Print() const;
|
void Print() const;
|
||||||
|
|
||||||
@@ -164,14 +167,14 @@ private:
|
|||||||
u32_le unk_size_2;
|
u32_le unk_size_2;
|
||||||
};
|
};
|
||||||
|
|
||||||
Header npdm_header;
|
Header npdm_header{};
|
||||||
AciHeader aci_header;
|
AciHeader aci_header{};
|
||||||
AcidHeader acid_header;
|
AcidHeader acid_header{};
|
||||||
|
|
||||||
FileAccessControl acid_file_access;
|
FileAccessControl acid_file_access{};
|
||||||
FileAccessHeader aci_file_access;
|
FileAccessHeader aci_file_access{};
|
||||||
|
|
||||||
KernelCapabilityDescriptors aci_kernel_capabilities;
|
KernelCapabilityDescriptors aci_kernel_capabilities{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace FileSys
|
} // namespace FileSys
|
||||||
|
|||||||
@@ -322,7 +322,8 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& open_di
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ConcatenatedVfsFile::MakeConcatenatedFile(concat, concat.front()->GetName());
|
auto name = concat.front()->GetName();
|
||||||
|
return ConcatenatedVfsFile::MakeConcatenatedFile(std::move(name), std::move(concat));
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
|
VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
|
|||||||
out = out->GetSubdirectories().front();
|
out = out->GetSubdirectories().front();
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::make_shared<CachedVfsDirectory>(out);
|
return std::make_shared<CachedVfsDirectory>(std::move(out));
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) {
|
VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) {
|
||||||
@@ -141,8 +141,7 @@ VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
RomFSBuildContext ctx{dir, ext};
|
RomFSBuildContext ctx{dir, ext};
|
||||||
auto file_map = ctx.Build();
|
return ConcatenatedVfsFile::MakeConcatenatedFile(0, dir->GetName(), ctx.Build());
|
||||||
return ConcatenatedVfsFile::MakeConcatenatedFile(0, file_map, dir->GetName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace FileSys
|
} // namespace FileSys
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "common/logging/log.h"
|
||||||
#include "core/file_sys/system_archive/system_version.h"
|
#include "core/file_sys/system_archive/system_version.h"
|
||||||
#include "core/file_sys/vfs_vector.h"
|
#include "core/file_sys/vfs_vector.h"
|
||||||
#include "core/hle/api_version.h"
|
#include "core/hle/api_version.h"
|
||||||
@@ -12,6 +13,9 @@ std::string GetLongDisplayVersion() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
VirtualDir SystemVersion() {
|
VirtualDir SystemVersion() {
|
||||||
|
LOG_WARNING(Common_Filesystem, "called - Using hardcoded firmware version '{}'",
|
||||||
|
GetLongDisplayVersion());
|
||||||
|
|
||||||
VirtualFile file = std::make_shared<VectorVfsFile>(std::vector<u8>(0x100), "file");
|
VirtualFile file = std::make_shared<VectorVfsFile>(std::vector<u8>(0x100), "file");
|
||||||
file->WriteObject(HLE::ApiVersion::HOS_VERSION_MAJOR, 0);
|
file->WriteObject(HLE::ApiVersion::HOS_VERSION_MAJOR, 0);
|
||||||
file->WriteObject(HLE::ApiVersion::HOS_VERSION_MINOR, 1);
|
file->WriteObject(HLE::ApiVersion::HOS_VERSION_MINOR, 1);
|
||||||
|
|||||||
@@ -6,13 +6,13 @@
|
|||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
CachedVfsDirectory::CachedVfsDirectory(VirtualDir& source_dir)
|
CachedVfsDirectory::CachedVfsDirectory(VirtualDir&& source_dir)
|
||||||
: name(source_dir->GetName()), parent(source_dir->GetParentDirectory()) {
|
: name(source_dir->GetName()), parent(source_dir->GetParentDirectory()) {
|
||||||
for (auto& dir : source_dir->GetSubdirectories()) {
|
for (auto& dir : source_dir->GetSubdirectories()) {
|
||||||
dirs.emplace(dir->GetName(), std::make_shared<CachedVfsDirectory>(dir));
|
dirs.emplace(dir->GetName(), std::make_shared<CachedVfsDirectory>(std::move(dir)));
|
||||||
}
|
}
|
||||||
for (auto& file : source_dir->GetFiles()) {
|
for (auto& file : source_dir->GetFiles()) {
|
||||||
files.emplace(file->GetName(), file);
|
files.emplace(file->GetName(), std::move(file));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace FileSys {
|
|||||||
|
|
||||||
class CachedVfsDirectory : public ReadOnlyVfsDirectory {
|
class CachedVfsDirectory : public ReadOnlyVfsDirectory {
|
||||||
public:
|
public:
|
||||||
CachedVfsDirectory(VirtualDir& source_directory);
|
CachedVfsDirectory(VirtualDir&& source_directory);
|
||||||
|
|
||||||
~CachedVfsDirectory() override;
|
~CachedVfsDirectory() override;
|
||||||
VirtualFile GetFile(std::string_view file_name) const override;
|
VirtualFile GetFile(std::string_view file_name) const override;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
ConcatenatedVfsFile::ConcatenatedVfsFile(ConcatenationMap&& concatenation_map_, std::string&& name_)
|
ConcatenatedVfsFile::ConcatenatedVfsFile(std::string&& name_, ConcatenationMap&& concatenation_map_)
|
||||||
: concatenation_map(std::move(concatenation_map_)), name(std::move(name_)) {
|
: concatenation_map(std::move(concatenation_map_)), name(std::move(name_)) {
|
||||||
DEBUG_ASSERT(this->VerifyContinuity());
|
DEBUG_ASSERT(this->VerifyContinuity());
|
||||||
}
|
}
|
||||||
@@ -30,8 +30,8 @@ bool ConcatenatedVfsFile::VerifyContinuity() const {
|
|||||||
|
|
||||||
ConcatenatedVfsFile::~ConcatenatedVfsFile() = default;
|
ConcatenatedVfsFile::~ConcatenatedVfsFile() = default;
|
||||||
|
|
||||||
VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(const std::vector<VirtualFile>& files,
|
VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(std::string&& name,
|
||||||
std::string&& name) {
|
std::vector<VirtualFile>&& files) {
|
||||||
// Fold trivial cases.
|
// Fold trivial cases.
|
||||||
if (files.empty()) {
|
if (files.empty()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -46,20 +46,21 @@ VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(const std::vector<VirtualF
|
|||||||
u64 last_offset = 0;
|
u64 last_offset = 0;
|
||||||
|
|
||||||
for (auto& file : files) {
|
for (auto& file : files) {
|
||||||
|
const auto size = file->GetSize();
|
||||||
|
|
||||||
concatenation_map.emplace_back(ConcatenationEntry{
|
concatenation_map.emplace_back(ConcatenationEntry{
|
||||||
.offset = last_offset,
|
.offset = last_offset,
|
||||||
.file = file,
|
.file = std::move(file),
|
||||||
});
|
});
|
||||||
|
|
||||||
last_offset += file->GetSize();
|
last_offset += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
return VirtualFile(new ConcatenatedVfsFile(std::move(concatenation_map), std::move(name)));
|
return VirtualFile(new ConcatenatedVfsFile(std::move(name), std::move(concatenation_map)));
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte,
|
VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte, std::string&& name,
|
||||||
const std::multimap<u64, VirtualFile>& files,
|
std::multimap<u64, VirtualFile>&& files) {
|
||||||
std::string&& name) {
|
|
||||||
// Fold trivial cases.
|
// Fold trivial cases.
|
||||||
if (files.empty()) {
|
if (files.empty()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -76,6 +77,8 @@ VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte,
|
|||||||
|
|
||||||
// Iteration of a multimap is ordered, so offset will be strictly non-decreasing.
|
// Iteration of a multimap is ordered, so offset will be strictly non-decreasing.
|
||||||
for (auto& [offset, file] : files) {
|
for (auto& [offset, file] : files) {
|
||||||
|
const auto size = file->GetSize();
|
||||||
|
|
||||||
if (offset > last_offset) {
|
if (offset > last_offset) {
|
||||||
concatenation_map.emplace_back(ConcatenationEntry{
|
concatenation_map.emplace_back(ConcatenationEntry{
|
||||||
.offset = last_offset,
|
.offset = last_offset,
|
||||||
@@ -85,13 +88,13 @@ VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte,
|
|||||||
|
|
||||||
concatenation_map.emplace_back(ConcatenationEntry{
|
concatenation_map.emplace_back(ConcatenationEntry{
|
||||||
.offset = offset,
|
.offset = offset,
|
||||||
.file = file,
|
.file = std::move(file),
|
||||||
});
|
});
|
||||||
|
|
||||||
last_offset = offset + file->GetSize();
|
last_offset = offset + size;
|
||||||
}
|
}
|
||||||
|
|
||||||
return VirtualFile(new ConcatenatedVfsFile(std::move(concatenation_map), std::move(name)));
|
return VirtualFile(new ConcatenatedVfsFile(std::move(name), std::move(concatenation_map)));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ConcatenatedVfsFile::GetName() const {
|
std::string ConcatenatedVfsFile::GetName() const {
|
||||||
|
|||||||
@@ -24,22 +24,20 @@ private:
|
|||||||
};
|
};
|
||||||
using ConcatenationMap = std::vector<ConcatenationEntry>;
|
using ConcatenationMap = std::vector<ConcatenationEntry>;
|
||||||
|
|
||||||
explicit ConcatenatedVfsFile(std::vector<ConcatenationEntry>&& concatenation_map,
|
explicit ConcatenatedVfsFile(std::string&& name,
|
||||||
std::string&& name);
|
std::vector<ConcatenationEntry>&& concatenation_map);
|
||||||
bool VerifyContinuity() const;
|
bool VerifyContinuity() const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~ConcatenatedVfsFile() override;
|
~ConcatenatedVfsFile() override;
|
||||||
|
|
||||||
/// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases.
|
/// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases.
|
||||||
static VirtualFile MakeConcatenatedFile(const std::vector<VirtualFile>& files,
|
static VirtualFile MakeConcatenatedFile(std::string&& name, std::vector<VirtualFile>&& files);
|
||||||
std::string&& name);
|
|
||||||
|
|
||||||
/// Convenience function that turns a map of offsets to files into a concatenated file, filling
|
/// Convenience function that turns a map of offsets to files into a concatenated file, filling
|
||||||
/// gaps with a given filler byte.
|
/// gaps with a given filler byte.
|
||||||
static VirtualFile MakeConcatenatedFile(u8 filler_byte,
|
static VirtualFile MakeConcatenatedFile(u8 filler_byte, std::string&& name,
|
||||||
const std::multimap<u64, VirtualFile>& files,
|
std::multimap<u64, VirtualFile>&& files);
|
||||||
std::string&& name);
|
|
||||||
|
|
||||||
std::string GetName() const override;
|
std::string GetName() const override;
|
||||||
std::size_t GetSize() const override;
|
std::size_t GetSize() const override;
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ VirtualDir LayeredVfsDirectory::GetDirectoryRelative(std::string_view path) cons
|
|||||||
for (const auto& layer : dirs) {
|
for (const auto& layer : dirs) {
|
||||||
auto dir = layer->GetDirectoryRelative(path);
|
auto dir = layer->GetDirectoryRelative(path);
|
||||||
if (dir != nullptr) {
|
if (dir != nullptr) {
|
||||||
out.push_back(std::move(dir));
|
out.emplace_back(std::move(dir));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,11 +62,11 @@ std::vector<VirtualFile> LayeredVfsDirectory::GetFiles() const {
|
|||||||
std::set<std::string, std::less<>> out_names;
|
std::set<std::string, std::less<>> out_names;
|
||||||
|
|
||||||
for (const auto& layer : dirs) {
|
for (const auto& layer : dirs) {
|
||||||
for (const auto& file : layer->GetFiles()) {
|
for (auto& file : layer->GetFiles()) {
|
||||||
auto file_name = file->GetName();
|
auto file_name = file->GetName();
|
||||||
if (!out_names.contains(file_name)) {
|
if (!out_names.contains(file_name)) {
|
||||||
out_names.emplace(std::move(file_name));
|
out_names.emplace(std::move(file_name));
|
||||||
out.push_back(file);
|
out.emplace_back(std::move(file));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,7 +86,7 @@ std::vector<VirtualDir> LayeredVfsDirectory::GetSubdirectories() const {
|
|||||||
std::vector<VirtualDir> out;
|
std::vector<VirtualDir> out;
|
||||||
out.reserve(names.size());
|
out.reserve(names.size());
|
||||||
for (const auto& subdir : names)
|
for (const auto& subdir : names)
|
||||||
out.push_back(GetSubdirectory(subdir));
|
out.emplace_back(GetSubdirectory(subdir));
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,11 @@
|
|||||||
|
|
||||||
#include "core/hle/kernel/board/nintendo/nx/k_system_control.h"
|
#include "core/hle/kernel/board/nintendo/nx/k_system_control.h"
|
||||||
#include "core/hle/kernel/board/nintendo/nx/secure_monitor.h"
|
#include "core/hle/kernel/board/nintendo/nx/secure_monitor.h"
|
||||||
|
#include "core/hle/kernel/k_memory_manager.h"
|
||||||
|
#include "core/hle/kernel/k_page_table.h"
|
||||||
#include "core/hle/kernel/k_trace.h"
|
#include "core/hle/kernel/k_trace.h"
|
||||||
|
#include "core/hle/kernel/kernel.h"
|
||||||
|
#include "core/hle/kernel/svc_results.h"
|
||||||
|
|
||||||
namespace Kernel::Board::Nintendo::Nx {
|
namespace Kernel::Board::Nintendo::Nx {
|
||||||
|
|
||||||
@@ -30,6 +34,8 @@ constexpr const std::size_t RequiredNonSecureSystemMemorySize =
|
|||||||
constexpr const std::size_t RequiredNonSecureSystemMemorySizeWithFatal =
|
constexpr const std::size_t RequiredNonSecureSystemMemorySizeWithFatal =
|
||||||
RequiredNonSecureSystemMemorySize + impl::RequiredNonSecureSystemMemorySizeViFatal;
|
RequiredNonSecureSystemMemorySize + impl::RequiredNonSecureSystemMemorySizeViFatal;
|
||||||
|
|
||||||
|
constexpr const std::size_t SecureAlignment = 128_KiB;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using namespace Common::Literals;
|
using namespace Common::Literals;
|
||||||
@@ -183,4 +189,57 @@ u64 KSystemControl::GenerateRandomRange(u64 min, u64 max) {
|
|||||||
return GenerateUniformRange(min, max, GenerateRandomU64);
|
return GenerateUniformRange(min, max, GenerateRandomU64);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t KSystemControl::CalculateRequiredSecureMemorySize(size_t size, u32 pool) {
|
||||||
|
if (pool == static_cast<u32>(KMemoryManager::Pool::Applet)) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
// return KSystemControlBase::CalculateRequiredSecureMemorySize(size, pool);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result KSystemControl::AllocateSecureMemory(KernelCore& kernel, KVirtualAddress* out, size_t size,
|
||||||
|
u32 pool) {
|
||||||
|
// Applet secure memory is handled separately.
|
||||||
|
UNIMPLEMENTED_IF(pool == static_cast<u32>(KMemoryManager::Pool::Applet));
|
||||||
|
|
||||||
|
// Ensure the size is aligned.
|
||||||
|
const size_t alignment =
|
||||||
|
(pool == static_cast<u32>(KMemoryManager::Pool::System) ? PageSize : SecureAlignment);
|
||||||
|
R_UNLESS(Common::IsAligned(size, alignment), ResultInvalidSize);
|
||||||
|
|
||||||
|
// Allocate the memory.
|
||||||
|
const size_t num_pages = size / PageSize;
|
||||||
|
const KPhysicalAddress paddr = kernel.MemoryManager().AllocateAndOpenContinuous(
|
||||||
|
num_pages, alignment / PageSize,
|
||||||
|
KMemoryManager::EncodeOption(static_cast<KMemoryManager::Pool>(pool),
|
||||||
|
KMemoryManager::Direction::FromFront));
|
||||||
|
R_UNLESS(paddr != 0, ResultOutOfMemory);
|
||||||
|
|
||||||
|
// Ensure we don't leak references to the memory on error.
|
||||||
|
ON_RESULT_FAILURE {
|
||||||
|
kernel.MemoryManager().Close(paddr, num_pages);
|
||||||
|
};
|
||||||
|
|
||||||
|
// We succeeded.
|
||||||
|
*out = KPageTable::GetHeapVirtualAddress(kernel.MemoryLayout(), paddr);
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
void KSystemControl::FreeSecureMemory(KernelCore& kernel, KVirtualAddress address, size_t size,
|
||||||
|
u32 pool) {
|
||||||
|
// Applet secure memory is handled separately.
|
||||||
|
UNIMPLEMENTED_IF(pool == static_cast<u32>(KMemoryManager::Pool::Applet));
|
||||||
|
|
||||||
|
// Ensure the size is aligned.
|
||||||
|
const size_t alignment =
|
||||||
|
(pool == static_cast<u32>(KMemoryManager::Pool::System) ? PageSize : SecureAlignment);
|
||||||
|
ASSERT(Common::IsAligned(GetInteger(address), alignment));
|
||||||
|
ASSERT(Common::IsAligned(size, alignment));
|
||||||
|
|
||||||
|
// Close the secure region's pages.
|
||||||
|
kernel.MemoryManager().Close(KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), address),
|
||||||
|
size / PageSize);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Kernel::Board::Nintendo::Nx
|
} // namespace Kernel::Board::Nintendo::Nx
|
||||||
|
|||||||
@@ -4,6 +4,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "core/hle/kernel/k_typed_address.h"
|
#include "core/hle/kernel/k_typed_address.h"
|
||||||
|
#include "core/hle/result.h"
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
class KernelCore;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Kernel::Board::Nintendo::Nx {
|
namespace Kernel::Board::Nintendo::Nx {
|
||||||
|
|
||||||
@@ -25,8 +30,16 @@ public:
|
|||||||
static std::size_t GetMinimumNonSecureSystemPoolSize();
|
static std::size_t GetMinimumNonSecureSystemPoolSize();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Randomness.
|
||||||
static u64 GenerateRandomRange(u64 min, u64 max);
|
static u64 GenerateRandomRange(u64 min, u64 max);
|
||||||
static u64 GenerateRandomU64();
|
static u64 GenerateRandomU64();
|
||||||
|
|
||||||
|
// Secure Memory.
|
||||||
|
static size_t CalculateRequiredSecureMemorySize(size_t size, u32 pool);
|
||||||
|
static Result AllocateSecureMemory(KernelCore& kernel, KVirtualAddress* out, size_t size,
|
||||||
|
u32 pool);
|
||||||
|
static void FreeSecureMemory(KernelCore& kernel, KVirtualAddress address, size_t size,
|
||||||
|
u32 pool);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Kernel::Board::Nintendo::Nx
|
} // namespace Kernel::Board::Nintendo::Nx
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ static_assert(KernelPageBufferAdditionalSize ==
|
|||||||
/// memory.
|
/// memory.
|
||||||
static KPhysicalAddress TranslateSlabAddrToPhysical(KMemoryLayout& memory_layout,
|
static KPhysicalAddress TranslateSlabAddrToPhysical(KMemoryLayout& memory_layout,
|
||||||
KVirtualAddress slab_addr) {
|
KVirtualAddress slab_addr) {
|
||||||
slab_addr -= GetInteger(memory_layout.GetSlabRegionAddress());
|
slab_addr -= memory_layout.GetSlabRegion().GetAddress();
|
||||||
return GetInteger(slab_addr) + Core::DramMemoryMap::SlabHeapBase;
|
return GetInteger(slab_addr) + Core::DramMemoryMap::SlabHeapBase;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,7 +196,12 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
|
|||||||
auto& kernel = system.Kernel();
|
auto& kernel = system.Kernel();
|
||||||
|
|
||||||
// Get the start of the slab region, since that's where we'll be working.
|
// Get the start of the slab region, since that's where we'll be working.
|
||||||
KVirtualAddress address = memory_layout.GetSlabRegionAddress();
|
const KMemoryRegion& slab_region = memory_layout.GetSlabRegion();
|
||||||
|
KVirtualAddress address = slab_region.GetAddress();
|
||||||
|
|
||||||
|
// Clear the slab region.
|
||||||
|
// TODO: implement access to kernel VAs.
|
||||||
|
// std::memset(device_ptr, 0, slab_region.GetSize());
|
||||||
|
|
||||||
// Initialize slab type array to be in sorted order.
|
// Initialize slab type array to be in sorted order.
|
||||||
std::array<KSlabType, KSlabType_Count> slab_types;
|
std::array<KSlabType, KSlabType_Count> slab_types;
|
||||||
|
|||||||
@@ -19,4 +19,8 @@ static inline KPhysicalAddress GetInitialProcessBinaryPhysicalAddress() {
|
|||||||
MainMemoryAddress);
|
MainMemoryAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline size_t GetInitialProcessBinarySize() {
|
||||||
|
return InitialProcessBinarySizeMax;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|||||||
@@ -200,8 +200,8 @@ private:
|
|||||||
|
|
||||||
RawCapabilityValue raw;
|
RawCapabilityValue raw;
|
||||||
BitField<0, 15, CapabilityType> id;
|
BitField<0, 15, CapabilityType> id;
|
||||||
BitField<15, 4, u32> major_version;
|
BitField<15, 4, u32> minor_version;
|
||||||
BitField<19, 13, u32> minor_version;
|
BitField<19, 13, u32> major_version;
|
||||||
};
|
};
|
||||||
|
|
||||||
union HandleTable {
|
union HandleTable {
|
||||||
|
|||||||
@@ -107,12 +107,12 @@ KConditionVariable::KConditionVariable(Core::System& system)
|
|||||||
|
|
||||||
KConditionVariable::~KConditionVariable() = default;
|
KConditionVariable::~KConditionVariable() = default;
|
||||||
|
|
||||||
Result KConditionVariable::SignalToAddress(KProcessAddress addr) {
|
Result KConditionVariable::SignalToAddress(KernelCore& kernel, KProcessAddress addr) {
|
||||||
KThread* owner_thread = GetCurrentThreadPointer(m_kernel);
|
KThread* owner_thread = GetCurrentThreadPointer(kernel);
|
||||||
|
|
||||||
// Signal the address.
|
// Signal the address.
|
||||||
{
|
{
|
||||||
KScopedSchedulerLock sl(m_kernel);
|
KScopedSchedulerLock sl(kernel);
|
||||||
|
|
||||||
// Remove waiter thread.
|
// Remove waiter thread.
|
||||||
bool has_waiters{};
|
bool has_waiters{};
|
||||||
@@ -133,7 +133,7 @@ Result KConditionVariable::SignalToAddress(KProcessAddress addr) {
|
|||||||
|
|
||||||
// Write the value to userspace.
|
// Write the value to userspace.
|
||||||
Result result{ResultSuccess};
|
Result result{ResultSuccess};
|
||||||
if (WriteToUser(m_kernel, addr, std::addressof(next_value))) [[likely]] {
|
if (WriteToUser(kernel, addr, std::addressof(next_value))) [[likely]] {
|
||||||
result = ResultSuccess;
|
result = ResultSuccess;
|
||||||
} else {
|
} else {
|
||||||
result = ResultInvalidCurrentMemory;
|
result = ResultInvalidCurrentMemory;
|
||||||
@@ -148,28 +148,28 @@ Result KConditionVariable::SignalToAddress(KProcessAddress addr) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Result KConditionVariable::WaitForAddress(Handle handle, KProcessAddress addr, u32 value) {
|
Result KConditionVariable::WaitForAddress(KernelCore& kernel, Handle handle, KProcessAddress addr,
|
||||||
KThread* cur_thread = GetCurrentThreadPointer(m_kernel);
|
u32 value) {
|
||||||
ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(m_kernel);
|
KThread* cur_thread = GetCurrentThreadPointer(kernel);
|
||||||
|
ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(kernel);
|
||||||
|
|
||||||
// Wait for the address.
|
// Wait for the address.
|
||||||
KThread* owner_thread{};
|
KThread* owner_thread{};
|
||||||
{
|
{
|
||||||
KScopedSchedulerLock sl(m_kernel);
|
KScopedSchedulerLock sl(kernel);
|
||||||
|
|
||||||
// Check if the thread should terminate.
|
// Check if the thread should terminate.
|
||||||
R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested);
|
R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested);
|
||||||
|
|
||||||
// Read the tag from userspace.
|
// Read the tag from userspace.
|
||||||
u32 test_tag{};
|
u32 test_tag{};
|
||||||
R_UNLESS(ReadFromUser(m_kernel, std::addressof(test_tag), addr),
|
R_UNLESS(ReadFromUser(kernel, std::addressof(test_tag), addr), ResultInvalidCurrentMemory);
|
||||||
ResultInvalidCurrentMemory);
|
|
||||||
|
|
||||||
// If the tag isn't the handle (with wait mask), we're done.
|
// If the tag isn't the handle (with wait mask), we're done.
|
||||||
R_SUCCEED_IF(test_tag != (handle | Svc::HandleWaitMask));
|
R_SUCCEED_IF(test_tag != (handle | Svc::HandleWaitMask));
|
||||||
|
|
||||||
// Get the lock owner thread.
|
// Get the lock owner thread.
|
||||||
owner_thread = GetCurrentProcess(m_kernel)
|
owner_thread = GetCurrentProcess(kernel)
|
||||||
.GetHandleTable()
|
.GetHandleTable()
|
||||||
.GetObjectWithoutPseudoHandle<KThread>(handle)
|
.GetObjectWithoutPseudoHandle<KThread>(handle)
|
||||||
.ReleasePointerUnsafe();
|
.ReleasePointerUnsafe();
|
||||||
|
|||||||
@@ -24,11 +24,12 @@ public:
|
|||||||
explicit KConditionVariable(Core::System& system);
|
explicit KConditionVariable(Core::System& system);
|
||||||
~KConditionVariable();
|
~KConditionVariable();
|
||||||
|
|
||||||
// Arbitration
|
// Arbitration.
|
||||||
Result SignalToAddress(KProcessAddress addr);
|
static Result SignalToAddress(KernelCore& kernel, KProcessAddress addr);
|
||||||
Result WaitForAddress(Handle handle, KProcessAddress addr, u32 value);
|
static Result WaitForAddress(KernelCore& kernel, Handle handle, KProcessAddress addr,
|
||||||
|
u32 value);
|
||||||
|
|
||||||
// Condition variable
|
// Condition variable.
|
||||||
void Signal(u64 cv_key, s32 count);
|
void Signal(u64 cv_key, s32 count);
|
||||||
Result Wait(KProcessAddress addr, u64 key, u32 value, s64 timeout);
|
Result Wait(KProcessAddress addr, u64 key, u32 value, s64 timeout);
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ void HandleInterrupt(KernelCore& kernel, s32 core_id) {
|
|||||||
KScopedSchedulerLock sl{kernel};
|
KScopedSchedulerLock sl{kernel};
|
||||||
|
|
||||||
// Pin the current thread.
|
// Pin the current thread.
|
||||||
process->PinCurrentThread(core_id);
|
process->PinCurrentThread();
|
||||||
|
|
||||||
// Set the interrupt flag for the thread.
|
// Set the interrupt flag for the thread.
|
||||||
GetCurrentThread(kernel).SetInterruptFlag();
|
GetCurrentThread(kernel).SetInterruptFlag();
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ enum class KMemoryState : u32 {
|
|||||||
FlagCanChangeAttribute = (1 << 24),
|
FlagCanChangeAttribute = (1 << 24),
|
||||||
FlagCanCodeMemory = (1 << 25),
|
FlagCanCodeMemory = (1 << 25),
|
||||||
FlagLinearMapped = (1 << 26),
|
FlagLinearMapped = (1 << 26),
|
||||||
|
FlagCanPermissionLock = (1 << 27),
|
||||||
|
|
||||||
FlagsData = FlagCanReprotect | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc |
|
FlagsData = FlagCanReprotect | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc |
|
||||||
FlagMapped | FlagCanAlias | FlagCanTransfer | FlagCanQueryPhysical |
|
FlagMapped | FlagCanAlias | FlagCanTransfer | FlagCanQueryPhysical |
|
||||||
@@ -50,12 +51,16 @@ enum class KMemoryState : u32 {
|
|||||||
FlagLinearMapped,
|
FlagLinearMapped,
|
||||||
|
|
||||||
Free = static_cast<u32>(Svc::MemoryState::Free),
|
Free = static_cast<u32>(Svc::MemoryState::Free),
|
||||||
Io = static_cast<u32>(Svc::MemoryState::Io) | FlagMapped | FlagCanDeviceMap |
|
|
||||||
|
IoMemory = static_cast<u32>(Svc::MemoryState::Io) | FlagMapped | FlagCanDeviceMap |
|
||||||
FlagCanAlignedDeviceMap,
|
FlagCanAlignedDeviceMap,
|
||||||
|
IoRegister =
|
||||||
|
static_cast<u32>(Svc::MemoryState::Io) | FlagCanDeviceMap | FlagCanAlignedDeviceMap,
|
||||||
|
|
||||||
Static = static_cast<u32>(Svc::MemoryState::Static) | FlagMapped | FlagCanQueryPhysical,
|
Static = static_cast<u32>(Svc::MemoryState::Static) | FlagMapped | FlagCanQueryPhysical,
|
||||||
Code = static_cast<u32>(Svc::MemoryState::Code) | FlagsCode | FlagCanMapProcess,
|
Code = static_cast<u32>(Svc::MemoryState::Code) | FlagsCode | FlagCanMapProcess,
|
||||||
CodeData = static_cast<u32>(Svc::MemoryState::CodeData) | FlagsData | FlagCanMapProcess |
|
CodeData = static_cast<u32>(Svc::MemoryState::CodeData) | FlagsData | FlagCanMapProcess |
|
||||||
FlagCanCodeMemory,
|
FlagCanCodeMemory | FlagCanPermissionLock,
|
||||||
Normal = static_cast<u32>(Svc::MemoryState::Normal) | FlagsData | FlagCanCodeMemory,
|
Normal = static_cast<u32>(Svc::MemoryState::Normal) | FlagsData | FlagCanCodeMemory,
|
||||||
Shared = static_cast<u32>(Svc::MemoryState::Shared) | FlagMapped | FlagReferenceCounted |
|
Shared = static_cast<u32>(Svc::MemoryState::Shared) | FlagMapped | FlagReferenceCounted |
|
||||||
FlagLinearMapped,
|
FlagLinearMapped,
|
||||||
@@ -65,7 +70,8 @@ enum class KMemoryState : u32 {
|
|||||||
AliasCode = static_cast<u32>(Svc::MemoryState::AliasCode) | FlagsCode | FlagCanMapProcess |
|
AliasCode = static_cast<u32>(Svc::MemoryState::AliasCode) | FlagsCode | FlagCanMapProcess |
|
||||||
FlagCanCodeAlias,
|
FlagCanCodeAlias,
|
||||||
AliasCodeData = static_cast<u32>(Svc::MemoryState::AliasCodeData) | FlagsData |
|
AliasCodeData = static_cast<u32>(Svc::MemoryState::AliasCodeData) | FlagsData |
|
||||||
FlagCanMapProcess | FlagCanCodeAlias | FlagCanCodeMemory,
|
FlagCanMapProcess | FlagCanCodeAlias | FlagCanCodeMemory |
|
||||||
|
FlagCanPermissionLock,
|
||||||
|
|
||||||
Ipc = static_cast<u32>(Svc::MemoryState::Ipc) | FlagsMisc | FlagCanAlignedDeviceMap |
|
Ipc = static_cast<u32>(Svc::MemoryState::Ipc) | FlagsMisc | FlagCanAlignedDeviceMap |
|
||||||
FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
||||||
@@ -73,7 +79,7 @@ enum class KMemoryState : u32 {
|
|||||||
Stack = static_cast<u32>(Svc::MemoryState::Stack) | FlagsMisc | FlagCanAlignedDeviceMap |
|
Stack = static_cast<u32>(Svc::MemoryState::Stack) | FlagsMisc | FlagCanAlignedDeviceMap |
|
||||||
FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
||||||
|
|
||||||
ThreadLocal = static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagLinearMapped,
|
ThreadLocal = static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagLinearMapped,
|
||||||
|
|
||||||
Transfered = static_cast<u32>(Svc::MemoryState::Transfered) | FlagsMisc |
|
Transfered = static_cast<u32>(Svc::MemoryState::Transfered) | FlagsMisc |
|
||||||
FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
|
FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
|
||||||
@@ -94,7 +100,7 @@ enum class KMemoryState : u32 {
|
|||||||
NonDeviceIpc =
|
NonDeviceIpc =
|
||||||
static_cast<u32>(Svc::MemoryState::NonDeviceIpc) | FlagsMisc | FlagCanUseNonDeviceIpc,
|
static_cast<u32>(Svc::MemoryState::NonDeviceIpc) | FlagsMisc | FlagCanUseNonDeviceIpc,
|
||||||
|
|
||||||
Kernel = static_cast<u32>(Svc::MemoryState::Kernel) | FlagMapped,
|
Kernel = static_cast<u32>(Svc::MemoryState::Kernel),
|
||||||
|
|
||||||
GeneratedCode = static_cast<u32>(Svc::MemoryState::GeneratedCode) | FlagMapped |
|
GeneratedCode = static_cast<u32>(Svc::MemoryState::GeneratedCode) | FlagMapped |
|
||||||
FlagReferenceCounted | FlagCanDebug | FlagLinearMapped,
|
FlagReferenceCounted | FlagCanDebug | FlagLinearMapped,
|
||||||
@@ -105,34 +111,36 @@ enum class KMemoryState : u32 {
|
|||||||
|
|
||||||
Insecure = static_cast<u32>(Svc::MemoryState::Insecure) | FlagMapped | FlagReferenceCounted |
|
Insecure = static_cast<u32>(Svc::MemoryState::Insecure) | FlagMapped | FlagReferenceCounted |
|
||||||
FlagLinearMapped | FlagCanChangeAttribute | FlagCanDeviceMap |
|
FlagLinearMapped | FlagCanChangeAttribute | FlagCanDeviceMap |
|
||||||
FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
FlagCanAlignedDeviceMap | FlagCanQueryPhysical | FlagCanUseNonSecureIpc |
|
||||||
|
FlagCanUseNonDeviceIpc,
|
||||||
};
|
};
|
||||||
DECLARE_ENUM_FLAG_OPERATORS(KMemoryState);
|
DECLARE_ENUM_FLAG_OPERATORS(KMemoryState);
|
||||||
|
|
||||||
static_assert(static_cast<u32>(KMemoryState::Free) == 0x00000000);
|
static_assert(static_cast<u32>(KMemoryState::Free) == 0x00000000);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Io) == 0x00182001);
|
static_assert(static_cast<u32>(KMemoryState::IoMemory) == 0x00182001);
|
||||||
|
static_assert(static_cast<u32>(KMemoryState::IoRegister) == 0x00180001);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Static) == 0x00042002);
|
static_assert(static_cast<u32>(KMemoryState::Static) == 0x00042002);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Code) == 0x04DC7E03);
|
static_assert(static_cast<u32>(KMemoryState::Code) == 0x04DC7E03);
|
||||||
static_assert(static_cast<u32>(KMemoryState::CodeData) == 0x07FEBD04);
|
static_assert(static_cast<u32>(KMemoryState::CodeData) == 0x0FFEBD04);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Normal) == 0x077EBD05);
|
static_assert(static_cast<u32>(KMemoryState::Normal) == 0x077EBD05);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Shared) == 0x04402006);
|
static_assert(static_cast<u32>(KMemoryState::Shared) == 0x04402006);
|
||||||
|
|
||||||
static_assert(static_cast<u32>(KMemoryState::AliasCode) == 0x04DD7E08);
|
static_assert(static_cast<u32>(KMemoryState::AliasCode) == 0x04DD7E08);
|
||||||
static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x07FFBD09);
|
static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x0FFFBD09);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x045C3C0A);
|
static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x045C3C0A);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Stack) == 0x045C3C0B);
|
static_assert(static_cast<u32>(KMemoryState::Stack) == 0x045C3C0B);
|
||||||
static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0400200C);
|
static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0400000C);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Transfered) == 0x055C3C0D);
|
static_assert(static_cast<u32>(KMemoryState::Transfered) == 0x055C3C0D);
|
||||||
static_assert(static_cast<u32>(KMemoryState::SharedTransfered) == 0x045C380E);
|
static_assert(static_cast<u32>(KMemoryState::SharedTransfered) == 0x045C380E);
|
||||||
static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0440380F);
|
static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0440380F);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010);
|
static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010);
|
||||||
static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x045C3811);
|
static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x045C3811);
|
||||||
static_assert(static_cast<u32>(KMemoryState::NonDeviceIpc) == 0x044C2812);
|
static_assert(static_cast<u32>(KMemoryState::NonDeviceIpc) == 0x044C2812);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Kernel) == 0x00002013);
|
static_assert(static_cast<u32>(KMemoryState::Kernel) == 0x00000013);
|
||||||
static_assert(static_cast<u32>(KMemoryState::GeneratedCode) == 0x04402214);
|
static_assert(static_cast<u32>(KMemoryState::GeneratedCode) == 0x04402214);
|
||||||
static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x04402015);
|
static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x04402015);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Coverage) == 0x00002016);
|
static_assert(static_cast<u32>(KMemoryState::Coverage) == 0x00002016);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Insecure) == 0x05583817);
|
static_assert(static_cast<u32>(KMemoryState::Insecure) == 0x055C3817);
|
||||||
|
|
||||||
enum class KMemoryPermission : u8 {
|
enum class KMemoryPermission : u8 {
|
||||||
None = 0,
|
None = 0,
|
||||||
@@ -182,8 +190,9 @@ enum class KMemoryAttribute : u8 {
|
|||||||
IpcLocked = static_cast<u8>(Svc::MemoryAttribute::IpcLocked),
|
IpcLocked = static_cast<u8>(Svc::MemoryAttribute::IpcLocked),
|
||||||
DeviceShared = static_cast<u8>(Svc::MemoryAttribute::DeviceShared),
|
DeviceShared = static_cast<u8>(Svc::MemoryAttribute::DeviceShared),
|
||||||
Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached),
|
Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached),
|
||||||
|
PermissionLocked = static_cast<u8>(Svc::MemoryAttribute::PermissionLocked),
|
||||||
|
|
||||||
SetMask = Uncached,
|
SetMask = Uncached | PermissionLocked,
|
||||||
};
|
};
|
||||||
DECLARE_ENUM_FLAG_OPERATORS(KMemoryAttribute);
|
DECLARE_ENUM_FLAG_OPERATORS(KMemoryAttribute);
|
||||||
|
|
||||||
@@ -261,6 +270,10 @@ struct KMemoryInfo {
|
|||||||
return m_state;
|
return m_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr Svc::MemoryState GetSvcState() const {
|
||||||
|
return static_cast<Svc::MemoryState>(m_state & KMemoryState::Mask);
|
||||||
|
}
|
||||||
|
|
||||||
constexpr KMemoryPermission GetPermission() const {
|
constexpr KMemoryPermission GetPermission() const {
|
||||||
return m_permission;
|
return m_permission;
|
||||||
}
|
}
|
||||||
@@ -326,6 +339,10 @@ public:
|
|||||||
return this->GetEndAddress() - 1;
|
return this->GetEndAddress() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr KMemoryState GetState() const {
|
||||||
|
return m_memory_state;
|
||||||
|
}
|
||||||
|
|
||||||
constexpr u16 GetIpcLockCount() const {
|
constexpr u16 GetIpcLockCount() const {
|
||||||
return m_ipc_lock_count;
|
return m_ipc_lock_count;
|
||||||
}
|
}
|
||||||
@@ -443,6 +460,13 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr void UpdateAttribute(KMemoryAttribute mask, KMemoryAttribute attr) {
|
||||||
|
ASSERT(False(mask & KMemoryAttribute::IpcLocked));
|
||||||
|
ASSERT(False(mask & KMemoryAttribute::DeviceShared));
|
||||||
|
|
||||||
|
m_attribute = (m_attribute & ~mask) | attr;
|
||||||
|
}
|
||||||
|
|
||||||
constexpr void Split(KMemoryBlock* block, KProcessAddress addr) {
|
constexpr void Split(KMemoryBlock* block, KProcessAddress addr) {
|
||||||
ASSERT(this->GetAddress() < addr);
|
ASSERT(this->GetAddress() < addr);
|
||||||
ASSERT(this->Contains(addr));
|
ASSERT(this->Contains(addr));
|
||||||
|
|||||||
@@ -160,8 +160,8 @@ void KMemoryBlockManager::Update(KMemoryBlockManagerUpdateAllocator* allocator,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update block state.
|
// Update block state.
|
||||||
it->Update(state, perm, attr, cur_address == address, static_cast<u8>(set_disable_attr),
|
it->Update(state, perm, attr, it->GetAddress() == address,
|
||||||
static_cast<u8>(clear_disable_attr));
|
static_cast<u8>(set_disable_attr), static_cast<u8>(clear_disable_attr));
|
||||||
cur_address += cur_info.GetSize();
|
cur_address += cur_info.GetSize();
|
||||||
remaining_pages -= cur_info.GetNumPages();
|
remaining_pages -= cur_info.GetNumPages();
|
||||||
}
|
}
|
||||||
@@ -175,7 +175,9 @@ void KMemoryBlockManager::UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allo
|
|||||||
KProcessAddress address, size_t num_pages,
|
KProcessAddress address, size_t num_pages,
|
||||||
KMemoryState test_state, KMemoryPermission test_perm,
|
KMemoryState test_state, KMemoryPermission test_perm,
|
||||||
KMemoryAttribute test_attr, KMemoryState state,
|
KMemoryAttribute test_attr, KMemoryState state,
|
||||||
KMemoryPermission perm, KMemoryAttribute attr) {
|
KMemoryPermission perm, KMemoryAttribute attr,
|
||||||
|
KMemoryBlockDisableMergeAttribute set_disable_attr,
|
||||||
|
KMemoryBlockDisableMergeAttribute clear_disable_attr) {
|
||||||
// Ensure for auditing that we never end up with an invalid tree.
|
// Ensure for auditing that we never end up with an invalid tree.
|
||||||
KScopedMemoryBlockManagerAuditor auditor(this);
|
KScopedMemoryBlockManagerAuditor auditor(this);
|
||||||
ASSERT(Common::IsAligned(GetInteger(address), PageSize));
|
ASSERT(Common::IsAligned(GetInteger(address), PageSize));
|
||||||
@@ -214,7 +216,8 @@ void KMemoryBlockManager::UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update block state.
|
// Update block state.
|
||||||
it->Update(state, perm, attr, false, 0, 0);
|
it->Update(state, perm, attr, false, static_cast<u8>(set_disable_attr),
|
||||||
|
static_cast<u8>(clear_disable_attr));
|
||||||
cur_address += cur_info.GetSize();
|
cur_address += cur_info.GetSize();
|
||||||
remaining_pages -= cur_info.GetNumPages();
|
remaining_pages -= cur_info.GetNumPages();
|
||||||
} else {
|
} else {
|
||||||
@@ -284,6 +287,65 @@ void KMemoryBlockManager::UpdateLock(KMemoryBlockManagerUpdateAllocator* allocat
|
|||||||
this->CoalesceForUpdate(allocator, address, num_pages);
|
this->CoalesceForUpdate(allocator, address, num_pages);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void KMemoryBlockManager::UpdateAttribute(KMemoryBlockManagerUpdateAllocator* allocator,
|
||||||
|
KProcessAddress address, size_t num_pages,
|
||||||
|
KMemoryAttribute mask, KMemoryAttribute attr) {
|
||||||
|
// Ensure for auditing that we never end up with an invalid tree.
|
||||||
|
KScopedMemoryBlockManagerAuditor auditor(this);
|
||||||
|
ASSERT(Common::IsAligned(GetInteger(address), PageSize));
|
||||||
|
|
||||||
|
KProcessAddress cur_address = address;
|
||||||
|
size_t remaining_pages = num_pages;
|
||||||
|
iterator it = this->FindIterator(address);
|
||||||
|
|
||||||
|
while (remaining_pages > 0) {
|
||||||
|
const size_t remaining_size = remaining_pages * PageSize;
|
||||||
|
KMemoryInfo cur_info = it->GetMemoryInfo();
|
||||||
|
|
||||||
|
if ((it->GetAttribute() & mask) != attr) {
|
||||||
|
// If we need to, create a new block before and insert it.
|
||||||
|
if (cur_info.GetAddress() != GetInteger(cur_address)) {
|
||||||
|
KMemoryBlock* new_block = allocator->Allocate();
|
||||||
|
|
||||||
|
it->Split(new_block, cur_address);
|
||||||
|
it = m_memory_block_tree.insert(*new_block);
|
||||||
|
it++;
|
||||||
|
|
||||||
|
cur_info = it->GetMemoryInfo();
|
||||||
|
cur_address = cur_info.GetAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we need to, create a new block after and insert it.
|
||||||
|
if (cur_info.GetSize() > remaining_size) {
|
||||||
|
KMemoryBlock* new_block = allocator->Allocate();
|
||||||
|
|
||||||
|
it->Split(new_block, cur_address + remaining_size);
|
||||||
|
it = m_memory_block_tree.insert(*new_block);
|
||||||
|
|
||||||
|
cur_info = it->GetMemoryInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update block state.
|
||||||
|
it->UpdateAttribute(mask, attr);
|
||||||
|
cur_address += cur_info.GetSize();
|
||||||
|
remaining_pages -= cur_info.GetNumPages();
|
||||||
|
} else {
|
||||||
|
// If we already have the right attributes, just advance.
|
||||||
|
if (cur_address + remaining_size < cur_info.GetEndAddress()) {
|
||||||
|
remaining_pages = 0;
|
||||||
|
cur_address += remaining_size;
|
||||||
|
} else {
|
||||||
|
remaining_pages =
|
||||||
|
(cur_address + remaining_size - cur_info.GetEndAddress()) / PageSize;
|
||||||
|
cur_address = cur_info.GetEndAddress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->CoalesceForUpdate(allocator, address, num_pages);
|
||||||
|
}
|
||||||
|
|
||||||
// Debug.
|
// Debug.
|
||||||
bool KMemoryBlockManager::CheckState() const {
|
bool KMemoryBlockManager::CheckState() const {
|
||||||
// Loop over every block, ensuring that we are sorted and coalesced.
|
// Loop over every block, ensuring that we are sorted and coalesced.
|
||||||
|
|||||||
@@ -115,7 +115,11 @@ public:
|
|||||||
void UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allocator, KProcessAddress address,
|
void UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allocator, KProcessAddress address,
|
||||||
size_t num_pages, KMemoryState test_state, KMemoryPermission test_perm,
|
size_t num_pages, KMemoryState test_state, KMemoryPermission test_perm,
|
||||||
KMemoryAttribute test_attr, KMemoryState state, KMemoryPermission perm,
|
KMemoryAttribute test_attr, KMemoryState state, KMemoryPermission perm,
|
||||||
KMemoryAttribute attr);
|
KMemoryAttribute attr, KMemoryBlockDisableMergeAttribute set_disable_attr,
|
||||||
|
KMemoryBlockDisableMergeAttribute clear_disable_attr);
|
||||||
|
|
||||||
|
void UpdateAttribute(KMemoryBlockManagerUpdateAllocator* allocator, KProcessAddress address,
|
||||||
|
size_t num_pages, KMemoryAttribute mask, KMemoryAttribute attr);
|
||||||
|
|
||||||
iterator FindIterator(KProcessAddress address) const {
|
iterator FindIterator(KProcessAddress address) const {
|
||||||
return m_memory_block_tree.find(KMemoryBlock(
|
return m_memory_block_tree.find(KMemoryBlock(
|
||||||
|
|||||||
@@ -137,11 +137,9 @@ public:
|
|||||||
return GetStackTopAddress(core_id, KMemoryRegionType_KernelMiscExceptionStack);
|
return GetStackTopAddress(core_id, KMemoryRegionType_KernelMiscExceptionStack);
|
||||||
}
|
}
|
||||||
|
|
||||||
KVirtualAddress GetSlabRegionAddress() const {
|
const KMemoryRegion& GetSlabRegion() const {
|
||||||
return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_KernelSlab))
|
return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_KernelSlab));
|
||||||
.GetAddress();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const KMemoryRegion& GetDeviceRegion(KMemoryRegionType type) const {
|
const KMemoryRegion& GetDeviceRegion(KMemoryRegionType type) const {
|
||||||
return Dereference(GetPhysicalMemoryRegionTree().FindFirstDerived(type));
|
return Dereference(GetPhysicalMemoryRegionTree().FindFirstDerived(type));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "core/hle/kernel/initial_process.h"
|
#include "core/hle/kernel/initial_process.h"
|
||||||
#include "core/hle/kernel/k_memory_manager.h"
|
#include "core/hle/kernel/k_memory_manager.h"
|
||||||
#include "core/hle/kernel/k_page_group.h"
|
#include "core/hle/kernel/k_page_group.h"
|
||||||
|
#include "core/hle/kernel/k_page_table.h"
|
||||||
#include "core/hle/kernel/kernel.h"
|
#include "core/hle/kernel/kernel.h"
|
||||||
#include "core/hle/kernel/svc_results.h"
|
#include "core/hle/kernel/svc_results.h"
|
||||||
|
|
||||||
@@ -119,7 +120,8 @@ void KMemoryManager::Initialize(KVirtualAddress management_region, size_t manage
|
|||||||
// Free each region to its corresponding heap.
|
// Free each region to its corresponding heap.
|
||||||
size_t reserved_sizes[MaxManagerCount] = {};
|
size_t reserved_sizes[MaxManagerCount] = {};
|
||||||
const KPhysicalAddress ini_start = GetInitialProcessBinaryPhysicalAddress();
|
const KPhysicalAddress ini_start = GetInitialProcessBinaryPhysicalAddress();
|
||||||
const KPhysicalAddress ini_end = ini_start + InitialProcessBinarySizeMax;
|
const size_t ini_size = GetInitialProcessBinarySize();
|
||||||
|
const KPhysicalAddress ini_end = ini_start + ini_size;
|
||||||
const KPhysicalAddress ini_last = ini_end - 1;
|
const KPhysicalAddress ini_last = ini_end - 1;
|
||||||
for (const auto& it : m_system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
|
for (const auto& it : m_system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
|
||||||
if (it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
|
if (it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
|
||||||
@@ -137,13 +139,13 @@ void KMemoryManager::Initialize(KVirtualAddress management_region, size_t manage
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Open/reserve the ini memory.
|
// Open/reserve the ini memory.
|
||||||
manager.OpenFirst(ini_start, InitialProcessBinarySizeMax / PageSize);
|
manager.OpenFirst(ini_start, ini_size / PageSize);
|
||||||
reserved_sizes[it.GetAttributes()] += InitialProcessBinarySizeMax;
|
reserved_sizes[it.GetAttributes()] += ini_size;
|
||||||
|
|
||||||
// Free memory after the ini to the heap.
|
// Free memory after the ini to the heap.
|
||||||
if (ini_last != cur_last) {
|
if (ini_last != cur_last) {
|
||||||
ASSERT(cur_end != 0);
|
ASSERT(cur_end != 0);
|
||||||
manager.Free(ini_end, cur_end - ini_end);
|
manager.Free(ini_end, (cur_end - ini_end) / PageSize);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Ensure there's no partial overlap with the ini image.
|
// Ensure there's no partial overlap with the ini image.
|
||||||
@@ -167,11 +169,37 @@ void KMemoryManager::Initialize(KVirtualAddress management_region, size_t manage
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result KMemoryManager::InitializeOptimizedMemory(u64 process_id, Pool pool) {
|
Result KMemoryManager::InitializeOptimizedMemory(u64 process_id, Pool pool) {
|
||||||
UNREACHABLE();
|
const u32 pool_index = static_cast<u32>(pool);
|
||||||
|
|
||||||
|
// Lock the pool.
|
||||||
|
KScopedLightLock lk(m_pool_locks[pool_index]);
|
||||||
|
|
||||||
|
// Check that we don't already have an optimized process.
|
||||||
|
R_UNLESS(!m_has_optimized_process[pool_index], ResultBusy);
|
||||||
|
|
||||||
|
// Set the optimized process id.
|
||||||
|
m_optimized_process_ids[pool_index] = process_id;
|
||||||
|
m_has_optimized_process[pool_index] = true;
|
||||||
|
|
||||||
|
// Clear the management area for the optimized process.
|
||||||
|
for (auto* manager = this->GetFirstManager(pool, Direction::FromFront); manager != nullptr;
|
||||||
|
manager = this->GetNextManager(manager, Direction::FromFront)) {
|
||||||
|
manager->InitializeOptimizedMemory(m_system.Kernel());
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
void KMemoryManager::FinalizeOptimizedMemory(u64 process_id, Pool pool) {
|
void KMemoryManager::FinalizeOptimizedMemory(u64 process_id, Pool pool) {
|
||||||
UNREACHABLE();
|
const u32 pool_index = static_cast<u32>(pool);
|
||||||
|
|
||||||
|
// Lock the pool.
|
||||||
|
KScopedLightLock lk(m_pool_locks[pool_index]);
|
||||||
|
|
||||||
|
// If the process was optimized, clear it.
|
||||||
|
if (m_has_optimized_process[pool_index] && m_optimized_process_ids[pool_index] == process_id) {
|
||||||
|
m_has_optimized_process[pool_index] = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
KPhysicalAddress KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_pages,
|
KPhysicalAddress KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_pages,
|
||||||
@@ -206,7 +234,7 @@ KPhysicalAddress KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, siz
|
|||||||
|
|
||||||
// Maintain the optimized memory bitmap, if we should.
|
// Maintain the optimized memory bitmap, if we should.
|
||||||
if (m_has_optimized_process[static_cast<size_t>(pool)]) {
|
if (m_has_optimized_process[static_cast<size_t>(pool)]) {
|
||||||
UNIMPLEMENTED();
|
chosen_manager->TrackUnoptimizedAllocation(m_system.Kernel(), allocated_block, num_pages);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open the first reference to the pages.
|
// Open the first reference to the pages.
|
||||||
@@ -254,7 +282,8 @@ Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages,
|
|||||||
|
|
||||||
// Maintain the optimized memory bitmap, if we should.
|
// Maintain the optimized memory bitmap, if we should.
|
||||||
if (unoptimized) {
|
if (unoptimized) {
|
||||||
UNIMPLEMENTED();
|
cur_manager->TrackUnoptimizedAllocation(m_system.Kernel(), allocated_block,
|
||||||
|
pages_per_alloc);
|
||||||
}
|
}
|
||||||
|
|
||||||
num_pages -= pages_per_alloc;
|
num_pages -= pages_per_alloc;
|
||||||
@@ -357,8 +386,8 @@ Result KMemoryManager::AllocateForProcess(KPageGroup* out, size_t num_pages, u32
|
|||||||
// Process part or all of the block.
|
// Process part or all of the block.
|
||||||
const size_t cur_pages =
|
const size_t cur_pages =
|
||||||
std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address));
|
std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address));
|
||||||
any_new =
|
any_new = manager.ProcessOptimizedAllocation(m_system.Kernel(), cur_address,
|
||||||
manager.ProcessOptimizedAllocation(cur_address, cur_pages, fill_pattern);
|
cur_pages, fill_pattern);
|
||||||
|
|
||||||
// Advance.
|
// Advance.
|
||||||
cur_address += cur_pages * PageSize;
|
cur_address += cur_pages * PageSize;
|
||||||
@@ -381,7 +410,7 @@ Result KMemoryManager::AllocateForProcess(KPageGroup* out, size_t num_pages, u32
|
|||||||
// Track some or all of the current pages.
|
// Track some or all of the current pages.
|
||||||
const size_t cur_pages =
|
const size_t cur_pages =
|
||||||
std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address));
|
std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address));
|
||||||
manager.TrackOptimizedAllocation(cur_address, cur_pages);
|
manager.TrackOptimizedAllocation(m_system.Kernel(), cur_address, cur_pages);
|
||||||
|
|
||||||
// Advance.
|
// Advance.
|
||||||
cur_address += cur_pages * PageSize;
|
cur_address += cur_pages * PageSize;
|
||||||
@@ -426,17 +455,86 @@ size_t KMemoryManager::Impl::Initialize(KPhysicalAddress address, size_t size,
|
|||||||
return total_management_size;
|
return total_management_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void KMemoryManager::Impl::TrackUnoptimizedAllocation(KPhysicalAddress block, size_t num_pages) {
|
void KMemoryManager::Impl::InitializeOptimizedMemory(KernelCore& kernel) {
|
||||||
UNREACHABLE();
|
auto optimize_pa =
|
||||||
|
KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
|
||||||
|
auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa);
|
||||||
|
|
||||||
|
std::memset(optimize_map, 0, CalculateOptimizedProcessOverheadSize(m_heap.GetSize()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void KMemoryManager::Impl::TrackOptimizedAllocation(KPhysicalAddress block, size_t num_pages) {
|
void KMemoryManager::Impl::TrackUnoptimizedAllocation(KernelCore& kernel, KPhysicalAddress block,
|
||||||
UNREACHABLE();
|
size_t num_pages) {
|
||||||
|
auto optimize_pa =
|
||||||
|
KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
|
||||||
|
auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa);
|
||||||
|
|
||||||
|
// Get the range we're tracking.
|
||||||
|
size_t offset = this->GetPageOffset(block);
|
||||||
|
const size_t last = offset + num_pages - 1;
|
||||||
|
|
||||||
|
// Track.
|
||||||
|
while (offset <= last) {
|
||||||
|
// Mark the page as not being optimized-allocated.
|
||||||
|
optimize_map[offset / Common::BitSize<u64>()] &=
|
||||||
|
~(u64(1) << (offset % Common::BitSize<u64>()));
|
||||||
|
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool KMemoryManager::Impl::ProcessOptimizedAllocation(KPhysicalAddress block, size_t num_pages,
|
void KMemoryManager::Impl::TrackOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block,
|
||||||
u8 fill_pattern) {
|
size_t num_pages) {
|
||||||
UNREACHABLE();
|
auto optimize_pa =
|
||||||
|
KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
|
||||||
|
auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa);
|
||||||
|
|
||||||
|
// Get the range we're tracking.
|
||||||
|
size_t offset = this->GetPageOffset(block);
|
||||||
|
const size_t last = offset + num_pages - 1;
|
||||||
|
|
||||||
|
// Track.
|
||||||
|
while (offset <= last) {
|
||||||
|
// Mark the page as being optimized-allocated.
|
||||||
|
optimize_map[offset / Common::BitSize<u64>()] |=
|
||||||
|
(u64(1) << (offset % Common::BitSize<u64>()));
|
||||||
|
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KMemoryManager::Impl::ProcessOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block,
|
||||||
|
size_t num_pages, u8 fill_pattern) {
|
||||||
|
auto& device_memory = kernel.System().DeviceMemory();
|
||||||
|
auto optimize_pa =
|
||||||
|
KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
|
||||||
|
auto* optimize_map = device_memory.GetPointer<u64>(optimize_pa);
|
||||||
|
|
||||||
|
// We want to return whether any pages were newly allocated.
|
||||||
|
bool any_new = false;
|
||||||
|
|
||||||
|
// Get the range we're processing.
|
||||||
|
size_t offset = this->GetPageOffset(block);
|
||||||
|
const size_t last = offset + num_pages - 1;
|
||||||
|
|
||||||
|
// Process.
|
||||||
|
while (offset <= last) {
|
||||||
|
// Check if the page has been optimized-allocated before.
|
||||||
|
if ((optimize_map[offset / Common::BitSize<u64>()] &
|
||||||
|
(u64(1) << (offset % Common::BitSize<u64>()))) == 0) {
|
||||||
|
// If not, it's new.
|
||||||
|
any_new = true;
|
||||||
|
|
||||||
|
// Fill the page.
|
||||||
|
auto* ptr = device_memory.GetPointer<u8>(m_heap.GetAddress());
|
||||||
|
std::memset(ptr + offset * PageSize, fill_pattern, PageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the number of pages we processed.
|
||||||
|
return any_new;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t KMemoryManager::Impl::CalculateManagementOverheadSize(size_t region_size) {
|
size_t KMemoryManager::Impl::CalculateManagementOverheadSize(size_t region_size) {
|
||||||
|
|||||||
@@ -216,14 +216,14 @@ private:
|
|||||||
m_heap.SetInitialUsedSize(reserved_size);
|
m_heap.SetInitialUsedSize(reserved_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitializeOptimizedMemory() {
|
void InitializeOptimizedMemory(KernelCore& kernel);
|
||||||
UNIMPLEMENTED();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TrackUnoptimizedAllocation(KPhysicalAddress block, size_t num_pages);
|
void TrackUnoptimizedAllocation(KernelCore& kernel, KPhysicalAddress block,
|
||||||
void TrackOptimizedAllocation(KPhysicalAddress block, size_t num_pages);
|
size_t num_pages);
|
||||||
|
void TrackOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, size_t num_pages);
|
||||||
|
|
||||||
bool ProcessOptimizedAllocation(KPhysicalAddress block, size_t num_pages, u8 fill_pattern);
|
bool ProcessOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block,
|
||||||
|
size_t num_pages, u8 fill_pattern);
|
||||||
|
|
||||||
constexpr Pool GetPool() const {
|
constexpr Pool GetPool() const {
|
||||||
return m_pool;
|
return m_pool;
|
||||||
|
|||||||
@@ -190,9 +190,15 @@ static_assert(KMemoryRegionType_DramKernelInitPt.GetValue() ==
|
|||||||
constexpr inline auto KMemoryRegionType_DramKernelSecureAppletMemory =
|
constexpr inline auto KMemoryRegionType_DramKernelSecureAppletMemory =
|
||||||
KMemoryRegionType_DramKernelBase.DeriveSparse(1, 3, 0).SetAttribute(
|
KMemoryRegionType_DramKernelBase.DeriveSparse(1, 3, 0).SetAttribute(
|
||||||
KMemoryRegionAttr_LinearMapped);
|
KMemoryRegionAttr_LinearMapped);
|
||||||
|
constexpr inline const auto KMemoryRegionType_DramKernelSecureUnknown =
|
||||||
|
KMemoryRegionType_DramKernelBase.DeriveSparse(1, 3, 1).SetAttribute(
|
||||||
|
KMemoryRegionAttr_LinearMapped);
|
||||||
static_assert(KMemoryRegionType_DramKernelSecureAppletMemory.GetValue() ==
|
static_assert(KMemoryRegionType_DramKernelSecureAppletMemory.GetValue() ==
|
||||||
(0x18E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap |
|
(0x18E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap |
|
||||||
KMemoryRegionAttr_LinearMapped));
|
KMemoryRegionAttr_LinearMapped));
|
||||||
|
static_assert(KMemoryRegionType_DramKernelSecureUnknown.GetValue() ==
|
||||||
|
(0x28E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap |
|
||||||
|
KMemoryRegionAttr_LinearMapped));
|
||||||
|
|
||||||
constexpr inline auto KMemoryRegionType_DramReservedEarly =
|
constexpr inline auto KMemoryRegionType_DramReservedEarly =
|
||||||
KMemoryRegionType_DramReservedBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap);
|
KMemoryRegionType_DramReservedBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap);
|
||||||
@@ -217,16 +223,18 @@ constexpr inline auto KMemoryRegionType_DramPoolPartition =
|
|||||||
static_assert(KMemoryRegionType_DramPoolPartition.GetValue() ==
|
static_assert(KMemoryRegionType_DramPoolPartition.GetValue() ==
|
||||||
(0x26 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
(0x26 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
||||||
|
|
||||||
constexpr inline auto KMemoryRegionType_DramPoolManagement =
|
// UNUSED: .Derive(4, 1);
|
||||||
KMemoryRegionType_DramPoolPartition.DeriveTransition(0, 2).DeriveTransition().SetAttribute(
|
// UNUSED: .Derive(4, 2);
|
||||||
|
constexpr inline const auto KMemoryRegionType_DramPoolManagement =
|
||||||
|
KMemoryRegionType_DramPoolPartition.Derive(4, 0).SetAttribute(
|
||||||
KMemoryRegionAttr_CarveoutProtected);
|
KMemoryRegionAttr_CarveoutProtected);
|
||||||
constexpr inline auto KMemoryRegionType_DramUserPool =
|
constexpr inline const auto KMemoryRegionType_DramUserPool =
|
||||||
KMemoryRegionType_DramPoolPartition.DeriveTransition(1, 2).DeriveTransition();
|
KMemoryRegionType_DramPoolPartition.Derive(4, 3);
|
||||||
static_assert(KMemoryRegionType_DramPoolManagement.GetValue() ==
|
static_assert(KMemoryRegionType_DramPoolManagement.GetValue() ==
|
||||||
(0x166 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
|
(0xE6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
|
||||||
KMemoryRegionAttr_CarveoutProtected));
|
KMemoryRegionAttr_CarveoutProtected));
|
||||||
static_assert(KMemoryRegionType_DramUserPool.GetValue() ==
|
static_assert(KMemoryRegionType_DramUserPool.GetValue() ==
|
||||||
(0x1A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
(0x266 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
||||||
|
|
||||||
constexpr inline auto KMemoryRegionType_DramApplicationPool =
|
constexpr inline auto KMemoryRegionType_DramApplicationPool =
|
||||||
KMemoryRegionType_DramUserPool.Derive(4, 0);
|
KMemoryRegionType_DramUserPool.Derive(4, 0);
|
||||||
@@ -237,60 +245,63 @@ constexpr inline auto KMemoryRegionType_DramSystemNonSecurePool =
|
|||||||
constexpr inline auto KMemoryRegionType_DramSystemPool =
|
constexpr inline auto KMemoryRegionType_DramSystemPool =
|
||||||
KMemoryRegionType_DramUserPool.Derive(4, 3).SetAttribute(KMemoryRegionAttr_CarveoutProtected);
|
KMemoryRegionType_DramUserPool.Derive(4, 3).SetAttribute(KMemoryRegionAttr_CarveoutProtected);
|
||||||
static_assert(KMemoryRegionType_DramApplicationPool.GetValue() ==
|
static_assert(KMemoryRegionType_DramApplicationPool.GetValue() ==
|
||||||
(0x7A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
(0xE66 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
||||||
static_assert(KMemoryRegionType_DramAppletPool.GetValue() ==
|
static_assert(KMemoryRegionType_DramAppletPool.GetValue() ==
|
||||||
(0xBA6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
(0x1666 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
||||||
static_assert(KMemoryRegionType_DramSystemNonSecurePool.GetValue() ==
|
static_assert(KMemoryRegionType_DramSystemNonSecurePool.GetValue() ==
|
||||||
(0xDA6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
(0x1A66 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
||||||
static_assert(KMemoryRegionType_DramSystemPool.GetValue() ==
|
static_assert(KMemoryRegionType_DramSystemPool.GetValue() ==
|
||||||
(0x13A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
|
(0x2666 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
|
||||||
KMemoryRegionAttr_CarveoutProtected));
|
KMemoryRegionAttr_CarveoutProtected));
|
||||||
|
|
||||||
constexpr inline auto KMemoryRegionType_VirtualDramHeapBase =
|
constexpr inline auto KMemoryRegionType_VirtualDramHeapBase =
|
||||||
KMemoryRegionType_Dram.DeriveSparse(1, 3, 0);
|
KMemoryRegionType_Dram.DeriveSparse(1, 4, 0);
|
||||||
constexpr inline auto KMemoryRegionType_VirtualDramKernelPtHeap =
|
constexpr inline auto KMemoryRegionType_VirtualDramKernelPtHeap =
|
||||||
KMemoryRegionType_Dram.DeriveSparse(1, 3, 1);
|
KMemoryRegionType_Dram.DeriveSparse(1, 4, 1);
|
||||||
constexpr inline auto KMemoryRegionType_VirtualDramKernelTraceBuffer =
|
constexpr inline auto KMemoryRegionType_VirtualDramKernelTraceBuffer =
|
||||||
KMemoryRegionType_Dram.DeriveSparse(1, 3, 2);
|
KMemoryRegionType_Dram.DeriveSparse(1, 4, 2);
|
||||||
static_assert(KMemoryRegionType_VirtualDramHeapBase.GetValue() == 0x1A);
|
static_assert(KMemoryRegionType_VirtualDramHeapBase.GetValue() == 0x1A);
|
||||||
static_assert(KMemoryRegionType_VirtualDramKernelPtHeap.GetValue() == 0x2A);
|
static_assert(KMemoryRegionType_VirtualDramKernelPtHeap.GetValue() == 0x2A);
|
||||||
static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A);
|
static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A);
|
||||||
|
|
||||||
// UNUSED: .DeriveSparse(2, 2, 0);
|
// UNUSED: .Derive(4, 2);
|
||||||
constexpr inline auto KMemoryRegionType_VirtualDramUnknownDebug =
|
constexpr inline const auto KMemoryRegionType_VirtualDramUnknownDebug =
|
||||||
KMemoryRegionType_Dram.DeriveSparse(2, 2, 1);
|
KMemoryRegionType_Dram.Advance(2).Derive(4, 0);
|
||||||
static_assert(KMemoryRegionType_VirtualDramUnknownDebug.GetValue() == (0x52));
|
constexpr inline const auto KMemoryRegionType_VirtualDramKernelSecureAppletMemory =
|
||||||
|
KMemoryRegionType_Dram.Advance(2).Derive(4, 1);
|
||||||
|
constexpr inline const auto KMemoryRegionType_VirtualDramKernelSecureUnknown =
|
||||||
|
KMemoryRegionType_Dram.Advance(2).Derive(4, 3);
|
||||||
|
static_assert(KMemoryRegionType_VirtualDramUnknownDebug.GetValue() == (0x32));
|
||||||
|
static_assert(KMemoryRegionType_VirtualDramKernelSecureAppletMemory.GetValue() == (0x52));
|
||||||
|
static_assert(KMemoryRegionType_VirtualDramKernelSecureUnknown.GetValue() == (0x92));
|
||||||
|
|
||||||
constexpr inline auto KMemoryRegionType_VirtualDramKernelSecureAppletMemory =
|
// UNUSED: .Derive(4, 3);
|
||||||
KMemoryRegionType_Dram.DeriveSparse(3, 1, 0);
|
constexpr inline const auto KMemoryRegionType_VirtualDramKernelInitPt =
|
||||||
static_assert(KMemoryRegionType_VirtualDramKernelSecureAppletMemory.GetValue() == (0x62));
|
KMemoryRegionType_VirtualDramHeapBase.Derive(4, 0);
|
||||||
|
constexpr inline const auto KMemoryRegionType_VirtualDramPoolManagement =
|
||||||
constexpr inline auto KMemoryRegionType_VirtualDramKernelInitPt =
|
KMemoryRegionType_VirtualDramHeapBase.Derive(4, 1);
|
||||||
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 0);
|
constexpr inline const auto KMemoryRegionType_VirtualDramUserPool =
|
||||||
constexpr inline auto KMemoryRegionType_VirtualDramPoolManagement =
|
KMemoryRegionType_VirtualDramHeapBase.Derive(4, 2);
|
||||||
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 1);
|
static_assert(KMemoryRegionType_VirtualDramKernelInitPt.GetValue() == 0x31A);
|
||||||
constexpr inline auto KMemoryRegionType_VirtualDramUserPool =
|
static_assert(KMemoryRegionType_VirtualDramPoolManagement.GetValue() == 0x51A);
|
||||||
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 2);
|
static_assert(KMemoryRegionType_VirtualDramUserPool.GetValue() == 0x61A);
|
||||||
static_assert(KMemoryRegionType_VirtualDramKernelInitPt.GetValue() == 0x19A);
|
|
||||||
static_assert(KMemoryRegionType_VirtualDramPoolManagement.GetValue() == 0x29A);
|
|
||||||
static_assert(KMemoryRegionType_VirtualDramUserPool.GetValue() == 0x31A);
|
|
||||||
|
|
||||||
// NOTE: For unknown reason, the pools are derived out-of-order here.
|
// NOTE: For unknown reason, the pools are derived out-of-order here.
|
||||||
// It's worth eventually trying to understand why Nintendo made this choice.
|
// It's worth eventually trying to understand why Nintendo made this choice.
|
||||||
// UNUSED: .Derive(6, 0);
|
// UNUSED: .Derive(6, 0);
|
||||||
// UNUSED: .Derive(6, 1);
|
// UNUSED: .Derive(6, 1);
|
||||||
constexpr inline auto KMemoryRegionType_VirtualDramAppletPool =
|
constexpr inline const auto KMemoryRegionType_VirtualDramApplicationPool =
|
||||||
KMemoryRegionType_VirtualDramUserPool.Derive(6, 2);
|
KMemoryRegionType_VirtualDramUserPool.Derive(4, 0);
|
||||||
constexpr inline auto KMemoryRegionType_VirtualDramApplicationPool =
|
constexpr inline const auto KMemoryRegionType_VirtualDramAppletPool =
|
||||||
KMemoryRegionType_VirtualDramUserPool.Derive(6, 3);
|
KMemoryRegionType_VirtualDramUserPool.Derive(4, 1);
|
||||||
constexpr inline auto KMemoryRegionType_VirtualDramSystemNonSecurePool =
|
constexpr inline const auto KMemoryRegionType_VirtualDramSystemNonSecurePool =
|
||||||
KMemoryRegionType_VirtualDramUserPool.Derive(6, 4);
|
KMemoryRegionType_VirtualDramUserPool.Derive(4, 2);
|
||||||
constexpr inline auto KMemoryRegionType_VirtualDramSystemPool =
|
constexpr inline const auto KMemoryRegionType_VirtualDramSystemPool =
|
||||||
KMemoryRegionType_VirtualDramUserPool.Derive(6, 5);
|
KMemoryRegionType_VirtualDramUserPool.Derive(4, 3);
|
||||||
static_assert(KMemoryRegionType_VirtualDramAppletPool.GetValue() == 0x1B1A);
|
static_assert(KMemoryRegionType_VirtualDramApplicationPool.GetValue() == 0x361A);
|
||||||
static_assert(KMemoryRegionType_VirtualDramApplicationPool.GetValue() == 0x271A);
|
static_assert(KMemoryRegionType_VirtualDramAppletPool.GetValue() == 0x561A);
|
||||||
static_assert(KMemoryRegionType_VirtualDramSystemNonSecurePool.GetValue() == 0x2B1A);
|
static_assert(KMemoryRegionType_VirtualDramSystemNonSecurePool.GetValue() == 0x661A);
|
||||||
static_assert(KMemoryRegionType_VirtualDramSystemPool.GetValue() == 0x331A);
|
static_assert(KMemoryRegionType_VirtualDramSystemPool.GetValue() == 0x961A);
|
||||||
|
|
||||||
constexpr inline auto KMemoryRegionType_ArchDeviceBase =
|
constexpr inline auto KMemoryRegionType_ArchDeviceBase =
|
||||||
KMemoryRegionType_Kernel.DeriveTransition(0, 1).SetSparseOnly();
|
KMemoryRegionType_Kernel.DeriveTransition(0, 1).SetSparseOnly();
|
||||||
@@ -354,12 +365,14 @@ constexpr inline auto KMemoryRegionType_KernelTemp =
|
|||||||
static_assert(KMemoryRegionType_KernelTemp.GetValue() == 0x31);
|
static_assert(KMemoryRegionType_KernelTemp.GetValue() == 0x31);
|
||||||
|
|
||||||
constexpr KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) {
|
constexpr KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) {
|
||||||
if (KMemoryRegionType_KernelTraceBuffer.IsAncestorOf(type_id)) {
|
if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) {
|
||||||
return KMemoryRegionType_VirtualDramKernelTraceBuffer;
|
|
||||||
} else if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) {
|
|
||||||
return KMemoryRegionType_VirtualDramKernelPtHeap;
|
return KMemoryRegionType_VirtualDramKernelPtHeap;
|
||||||
} else if (KMemoryRegionType_DramKernelSecureAppletMemory.IsAncestorOf(type_id)) {
|
} else if (KMemoryRegionType_DramKernelSecureAppletMemory.IsAncestorOf(type_id)) {
|
||||||
return KMemoryRegionType_VirtualDramKernelSecureAppletMemory;
|
return KMemoryRegionType_VirtualDramKernelSecureAppletMemory;
|
||||||
|
} else if (KMemoryRegionType_DramKernelSecureUnknown.IsAncestorOf(type_id)) {
|
||||||
|
return KMemoryRegionType_VirtualDramKernelSecureUnknown;
|
||||||
|
} else if (KMemoryRegionType_KernelTraceBuffer.IsAncestorOf(type_id)) {
|
||||||
|
return KMemoryRegionType_VirtualDramKernelTraceBuffer;
|
||||||
} else if ((type_id | KMemoryRegionAttr_ShouldKernelMap) == type_id) {
|
} else if ((type_id | KMemoryRegionAttr_ShouldKernelMap) == type_id) {
|
||||||
return KMemoryRegionType_VirtualDramUnknownDebug;
|
return KMemoryRegionType_VirtualDramUnknownDebug;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -183,12 +183,17 @@ private:
|
|||||||
|
|
||||||
class KScopedPageGroup {
|
class KScopedPageGroup {
|
||||||
public:
|
public:
|
||||||
explicit KScopedPageGroup(const KPageGroup* gp) : m_pg(gp) {
|
explicit KScopedPageGroup(const KPageGroup* gp, bool not_first = true) : m_pg(gp) {
|
||||||
if (m_pg) {
|
if (m_pg) {
|
||||||
|
if (not_first) {
|
||||||
m_pg->Open();
|
m_pg->Open();
|
||||||
|
} else {
|
||||||
|
m_pg->OpenFirst();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
explicit KScopedPageGroup(const KPageGroup& gp) : KScopedPageGroup(std::addressof(gp)) {}
|
}
|
||||||
|
explicit KScopedPageGroup(const KPageGroup& gp, bool not_first = true)
|
||||||
|
: KScopedPageGroup(std::addressof(gp), not_first) {}
|
||||||
~KScopedPageGroup() {
|
~KScopedPageGroup() {
|
||||||
if (m_pg) {
|
if (m_pg) {
|
||||||
m_pg->Close();
|
m_pg->Close();
|
||||||
|
|||||||
@@ -82,14 +82,14 @@ public:
|
|||||||
|
|
||||||
using namespace Common::Literals;
|
using namespace Common::Literals;
|
||||||
|
|
||||||
constexpr size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceType as_type) {
|
constexpr size_t GetAddressSpaceWidthFromType(Svc::CreateProcessFlag as_type) {
|
||||||
switch (as_type) {
|
switch (as_type) {
|
||||||
case FileSys::ProgramAddressSpaceType::Is32Bit:
|
case Svc::CreateProcessFlag::AddressSpace32Bit:
|
||||||
case FileSys::ProgramAddressSpaceType::Is32BitNoMap:
|
case Svc::CreateProcessFlag::AddressSpace32BitWithoutAlias:
|
||||||
return 32;
|
return 32;
|
||||||
case FileSys::ProgramAddressSpaceType::Is36Bit:
|
case Svc::CreateProcessFlag::AddressSpace64BitDeprecated:
|
||||||
return 36;
|
return 36;
|
||||||
case FileSys::ProgramAddressSpaceType::Is39Bit:
|
case Svc::CreateProcessFlag::AddressSpace64Bit:
|
||||||
return 39;
|
return 39;
|
||||||
default:
|
default:
|
||||||
ASSERT(false);
|
ASSERT(false);
|
||||||
@@ -105,7 +105,7 @@ KPageTable::KPageTable(Core::System& system_)
|
|||||||
|
|
||||||
KPageTable::~KPageTable() = default;
|
KPageTable::~KPageTable() = default;
|
||||||
|
|
||||||
Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr,
|
Result KPageTable::InitializeForProcess(Svc::CreateProcessFlag as_type, bool enable_aslr,
|
||||||
bool enable_das_merge, bool from_back,
|
bool enable_das_merge, bool from_back,
|
||||||
KMemoryManager::Pool pool, KProcessAddress code_addr,
|
KMemoryManager::Pool pool, KProcessAddress code_addr,
|
||||||
size_t code_size, KSystemResource* system_resource,
|
size_t code_size, KSystemResource* system_resource,
|
||||||
@@ -133,7 +133,7 @@ Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type
|
|||||||
ASSERT(code_addr + code_size - 1 <= end - 1);
|
ASSERT(code_addr + code_size - 1 <= end - 1);
|
||||||
|
|
||||||
// Adjust heap/alias size if we don't have an alias region
|
// Adjust heap/alias size if we don't have an alias region
|
||||||
if (as_type == FileSys::ProgramAddressSpaceType::Is32BitNoMap) {
|
if (as_type == Svc::CreateProcessFlag::AddressSpace32BitWithoutAlias) {
|
||||||
heap_region_size += alias_region_size;
|
heap_region_size += alias_region_size;
|
||||||
alias_region_size = 0;
|
alias_region_size = 0;
|
||||||
}
|
}
|
||||||
@@ -505,7 +505,7 @@ Result KPageTable::UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress
|
|||||||
R_TRY(this->CheckMemoryStateContiguous(
|
R_TRY(this->CheckMemoryStateContiguous(
|
||||||
std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias,
|
std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias,
|
||||||
KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
|
KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
|
||||||
KMemoryAttribute::All, KMemoryAttribute::None));
|
KMemoryAttribute::All & ~KMemoryAttribute::PermissionLocked, KMemoryAttribute::None));
|
||||||
|
|
||||||
// Determine whether any pages being unmapped are code.
|
// Determine whether any pages being unmapped are code.
|
||||||
bool any_code_pages = false;
|
bool any_code_pages = false;
|
||||||
@@ -1724,7 +1724,17 @@ Result KPageTable::MapPhysicalMemory(KProcessAddress address, size_t size) {
|
|||||||
PageSize;
|
PageSize;
|
||||||
|
|
||||||
// While we have pages to map, map them.
|
// While we have pages to map, map them.
|
||||||
while (map_pages > 0) {
|
{
|
||||||
|
// Create a page group for the current mapping range.
|
||||||
|
KPageGroup cur_pg(m_kernel, m_block_info_manager);
|
||||||
|
{
|
||||||
|
ON_RESULT_FAILURE_2 {
|
||||||
|
cur_pg.OpenFirst();
|
||||||
|
cur_pg.Close();
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t remain_pages = map_pages;
|
||||||
|
while (remain_pages > 0) {
|
||||||
// Check if we're at the end of the physical block.
|
// Check if we're at the end of the physical block.
|
||||||
if (pg_pages == 0) {
|
if (pg_pages == 0) {
|
||||||
// Ensure there are more pages to map.
|
// Ensure there are more pages to map.
|
||||||
@@ -1736,20 +1746,24 @@ Result KPageTable::MapPhysicalMemory(KProcessAddress address, size_t size) {
|
|||||||
pg_pages = pg_it->GetNumPages();
|
pg_pages = pg_it->GetNumPages();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map whatever we can.
|
// Add whatever we can to the current block.
|
||||||
const size_t cur_pages = std::min(pg_pages, map_pages);
|
const size_t cur_pages = std::min(pg_pages, remain_pages);
|
||||||
R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::UserReadWrite,
|
R_TRY(cur_pg.AddBlock(pg_phys_addr +
|
||||||
OperationType::MapFirst, pg_phys_addr));
|
((pg_pages - cur_pages) * PageSize),
|
||||||
|
cur_pages));
|
||||||
|
|
||||||
// Advance.
|
// Advance.
|
||||||
cur_address += cur_pages * PageSize;
|
remain_pages -= cur_pages;
|
||||||
map_pages -= cur_pages;
|
|
||||||
|
|
||||||
pg_phys_addr += cur_pages * PageSize;
|
|
||||||
pg_pages -= cur_pages;
|
pg_pages -= cur_pages;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Map the pages.
|
||||||
|
R_TRY(this->Operate(cur_address, map_pages, cur_pg,
|
||||||
|
OperationType::MapFirstGroup));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check if we're done.
|
// Check if we're done.
|
||||||
if (last_address <= info.GetLastAddress()) {
|
if (last_address <= info.GetLastAddress()) {
|
||||||
break;
|
break;
|
||||||
@@ -1770,7 +1784,11 @@ Result KPageTable::MapPhysicalMemory(KProcessAddress address, size_t size) {
|
|||||||
m_memory_block_manager.UpdateIfMatch(
|
m_memory_block_manager.UpdateIfMatch(
|
||||||
std::addressof(allocator), address, size / PageSize, KMemoryState::Free,
|
std::addressof(allocator), address, size / PageSize, KMemoryState::Free,
|
||||||
KMemoryPermission::None, KMemoryAttribute::None, KMemoryState::Normal,
|
KMemoryPermission::None, KMemoryAttribute::None, KMemoryState::Normal,
|
||||||
KMemoryPermission::UserReadWrite, KMemoryAttribute::None);
|
KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
|
||||||
|
address == this->GetAliasRegionStart()
|
||||||
|
? KMemoryBlockDisableMergeAttribute::Normal
|
||||||
|
: KMemoryBlockDisableMergeAttribute::None,
|
||||||
|
KMemoryBlockDisableMergeAttribute::None);
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
@@ -1868,6 +1886,13 @@ Result KPageTable::UnmapPhysicalMemory(KProcessAddress address, size_t size) {
|
|||||||
|
|
||||||
// Iterate over the memory, unmapping as we go.
|
// Iterate over the memory, unmapping as we go.
|
||||||
auto it = m_memory_block_manager.FindIterator(cur_address);
|
auto it = m_memory_block_manager.FindIterator(cur_address);
|
||||||
|
|
||||||
|
const auto clear_merge_attr =
|
||||||
|
(it->GetState() == KMemoryState::Normal &&
|
||||||
|
it->GetAddress() == this->GetAliasRegionStart() && it->GetAddress() == address)
|
||||||
|
? KMemoryBlockDisableMergeAttribute::Normal
|
||||||
|
: KMemoryBlockDisableMergeAttribute::None;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
// Check that the iterator is valid.
|
// Check that the iterator is valid.
|
||||||
ASSERT(it != m_memory_block_manager.end());
|
ASSERT(it != m_memory_block_manager.end());
|
||||||
@@ -1905,7 +1930,7 @@ Result KPageTable::UnmapPhysicalMemory(KProcessAddress address, size_t size) {
|
|||||||
m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize,
|
m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize,
|
||||||
KMemoryState::Free, KMemoryPermission::None,
|
KMemoryState::Free, KMemoryPermission::None,
|
||||||
KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
|
KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
|
||||||
KMemoryBlockDisableMergeAttribute::None);
|
clear_merge_attr);
|
||||||
|
|
||||||
// We succeeded.
|
// We succeeded.
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
@@ -2379,8 +2404,7 @@ Result KPageTable::MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg,
|
|||||||
KScopedPageTableUpdater updater(this);
|
KScopedPageTableUpdater updater(this);
|
||||||
|
|
||||||
// Perform mapping operation.
|
// Perform mapping operation.
|
||||||
const KPageProperties properties = {perm, state == KMemoryState::Io, false,
|
const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
|
||||||
DisableMergeAttribute::DisableHead};
|
|
||||||
R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
|
R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
|
||||||
|
|
||||||
// Update the blocks.
|
// Update the blocks.
|
||||||
@@ -2422,8 +2446,7 @@ Result KPageTable::MapPageGroup(KProcessAddress addr, const KPageGroup& pg, KMem
|
|||||||
KScopedPageTableUpdater updater(this);
|
KScopedPageTableUpdater updater(this);
|
||||||
|
|
||||||
// Perform mapping operation.
|
// Perform mapping operation.
|
||||||
const KPageProperties properties = {perm, state == KMemoryState::Io, false,
|
const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
|
||||||
DisableMergeAttribute::DisableHead};
|
|
||||||
R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
|
R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
|
||||||
|
|
||||||
// Update the blocks.
|
// Update the blocks.
|
||||||
@@ -2652,10 +2675,17 @@ Result KPageTable::SetMemoryAttribute(KProcessAddress addr, size_t size, u32 mas
|
|||||||
size_t num_allocator_blocks;
|
size_t num_allocator_blocks;
|
||||||
constexpr auto AttributeTestMask =
|
constexpr auto AttributeTestMask =
|
||||||
~(KMemoryAttribute::SetMask | KMemoryAttribute::DeviceShared);
|
~(KMemoryAttribute::SetMask | KMemoryAttribute::DeviceShared);
|
||||||
R_TRY(this->CheckMemoryState(
|
const KMemoryState state_test_mask =
|
||||||
std::addressof(old_state), std::addressof(old_perm), std::addressof(old_attr),
|
static_cast<KMemoryState>(((mask & static_cast<u32>(KMemoryAttribute::Uncached))
|
||||||
std::addressof(num_allocator_blocks), addr, size, KMemoryState::FlagCanChangeAttribute,
|
? static_cast<u32>(KMemoryState::FlagCanChangeAttribute)
|
||||||
KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None,
|
: 0) |
|
||||||
|
((mask & static_cast<u32>(KMemoryAttribute::PermissionLocked))
|
||||||
|
? static_cast<u32>(KMemoryState::FlagCanPermissionLock)
|
||||||
|
: 0));
|
||||||
|
R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
|
||||||
|
std::addressof(old_attr), std::addressof(num_allocator_blocks),
|
||||||
|
addr, size, state_test_mask, state_test_mask,
|
||||||
|
KMemoryPermission::None, KMemoryPermission::None,
|
||||||
AttributeTestMask, KMemoryAttribute::None, ~AttributeTestMask));
|
AttributeTestMask, KMemoryAttribute::None, ~AttributeTestMask));
|
||||||
|
|
||||||
// Create an update allocator.
|
// Create an update allocator.
|
||||||
@@ -2664,18 +2694,17 @@ Result KPageTable::SetMemoryAttribute(KProcessAddress addr, size_t size, u32 mas
|
|||||||
m_memory_block_slab_manager, num_allocator_blocks);
|
m_memory_block_slab_manager, num_allocator_blocks);
|
||||||
R_TRY(allocator_result);
|
R_TRY(allocator_result);
|
||||||
|
|
||||||
// Determine the new attribute.
|
// If we need to, perform a change attribute operation.
|
||||||
const KMemoryAttribute new_attr =
|
if (True(KMemoryAttribute::Uncached & static_cast<KMemoryAttribute>(mask))) {
|
||||||
static_cast<KMemoryAttribute>(((old_attr & static_cast<KMemoryAttribute>(~mask)) |
|
|
||||||
static_cast<KMemoryAttribute>(attr & mask)));
|
|
||||||
|
|
||||||
// Perform operation.
|
// Perform operation.
|
||||||
this->Operate(addr, num_pages, old_perm, OperationType::ChangePermissionsAndRefresh);
|
R_TRY(this->Operate(addr, num_pages, old_perm,
|
||||||
|
OperationType::ChangePermissionsAndRefreshAndFlush, 0));
|
||||||
|
}
|
||||||
|
|
||||||
// Update the blocks.
|
// Update the blocks.
|
||||||
m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, old_perm,
|
m_memory_block_manager.UpdateAttribute(std::addressof(allocator), addr, num_pages,
|
||||||
new_attr, KMemoryBlockDisableMergeAttribute::None,
|
static_cast<KMemoryAttribute>(mask),
|
||||||
KMemoryBlockDisableMergeAttribute::None);
|
static_cast<KMemoryAttribute>(attr));
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
@@ -2863,7 +2892,8 @@ Result KPageTable::LockForMapDeviceAddressSpace(bool* out_is_io, KProcessAddress
|
|||||||
&KMemoryBlock::ShareToDevice, KMemoryPermission::None);
|
&KMemoryBlock::ShareToDevice, KMemoryPermission::None);
|
||||||
|
|
||||||
// Set whether the locked memory was io.
|
// Set whether the locked memory was io.
|
||||||
*out_is_io = old_state == KMemoryState::Io;
|
*out_is_io =
|
||||||
|
static_cast<Svc::MemoryState>(old_state & KMemoryState::Mask) == Svc::MemoryState::Io;
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
@@ -3021,9 +3051,10 @@ Result KPageTable::Operate(KProcessAddress addr, size_t num_pages, const KPageGr
|
|||||||
ASSERT(num_pages == page_group.GetNumPages());
|
ASSERT(num_pages == page_group.GetNumPages());
|
||||||
|
|
||||||
switch (operation) {
|
switch (operation) {
|
||||||
case OperationType::MapGroup: {
|
case OperationType::MapGroup:
|
||||||
|
case OperationType::MapFirstGroup: {
|
||||||
// We want to maintain a new reference to every page in the group.
|
// We want to maintain a new reference to every page in the group.
|
||||||
KScopedPageGroup spg(page_group);
|
KScopedPageGroup spg(page_group, operation != OperationType::MapFirstGroup);
|
||||||
|
|
||||||
for (const auto& node : page_group) {
|
for (const auto& node : page_group) {
|
||||||
const size_t size{node.GetNumPages() * PageSize};
|
const size_t size{node.GetNumPages() * PageSize};
|
||||||
@@ -3065,7 +3096,6 @@ Result KPageTable::Operate(KProcessAddress addr, size_t num_pages, KMemoryPermis
|
|||||||
m_memory->UnmapRegion(*m_page_table_impl, addr, num_pages * PageSize);
|
m_memory->UnmapRegion(*m_page_table_impl, addr, num_pages * PageSize);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OperationType::MapFirst:
|
|
||||||
case OperationType::Map: {
|
case OperationType::Map: {
|
||||||
ASSERT(map_addr);
|
ASSERT(map_addr);
|
||||||
ASSERT(Common::IsAligned(GetInteger(map_addr), PageSize));
|
ASSERT(Common::IsAligned(GetInteger(map_addr), PageSize));
|
||||||
@@ -3073,12 +3103,8 @@ Result KPageTable::Operate(KProcessAddress addr, size_t num_pages, KMemoryPermis
|
|||||||
|
|
||||||
// Open references to pages, if we should.
|
// Open references to pages, if we should.
|
||||||
if (IsHeapPhysicalAddress(m_kernel.MemoryLayout(), map_addr)) {
|
if (IsHeapPhysicalAddress(m_kernel.MemoryLayout(), map_addr)) {
|
||||||
if (operation == OperationType::MapFirst) {
|
|
||||||
m_kernel.MemoryManager().OpenFirst(map_addr, num_pages);
|
|
||||||
} else {
|
|
||||||
m_kernel.MemoryManager().Open(map_addr, num_pages);
|
m_kernel.MemoryManager().Open(map_addr, num_pages);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OperationType::Separate: {
|
case OperationType::Separate: {
|
||||||
@@ -3087,6 +3113,7 @@ Result KPageTable::Operate(KProcessAddress addr, size_t num_pages, KMemoryPermis
|
|||||||
}
|
}
|
||||||
case OperationType::ChangePermissions:
|
case OperationType::ChangePermissions:
|
||||||
case OperationType::ChangePermissionsAndRefresh:
|
case OperationType::ChangePermissionsAndRefresh:
|
||||||
|
case OperationType::ChangePermissionsAndRefreshAndFlush:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ASSERT(false);
|
ASSERT(false);
|
||||||
@@ -3106,79 +3133,79 @@ void KPageTable::FinalizeUpdate(PageLinkedList* page_list) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
KProcessAddress KPageTable::GetRegionAddress(KMemoryState state) const {
|
KProcessAddress KPageTable::GetRegionAddress(Svc::MemoryState state) const {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case KMemoryState::Free:
|
case Svc::MemoryState::Free:
|
||||||
case KMemoryState::Kernel:
|
case Svc::MemoryState::Kernel:
|
||||||
return m_address_space_start;
|
return m_address_space_start;
|
||||||
case KMemoryState::Normal:
|
case Svc::MemoryState::Normal:
|
||||||
return m_heap_region_start;
|
return m_heap_region_start;
|
||||||
case KMemoryState::Ipc:
|
case Svc::MemoryState::Ipc:
|
||||||
case KMemoryState::NonSecureIpc:
|
case Svc::MemoryState::NonSecureIpc:
|
||||||
case KMemoryState::NonDeviceIpc:
|
case Svc::MemoryState::NonDeviceIpc:
|
||||||
return m_alias_region_start;
|
return m_alias_region_start;
|
||||||
case KMemoryState::Stack:
|
case Svc::MemoryState::Stack:
|
||||||
return m_stack_region_start;
|
return m_stack_region_start;
|
||||||
case KMemoryState::Static:
|
case Svc::MemoryState::Static:
|
||||||
case KMemoryState::ThreadLocal:
|
case Svc::MemoryState::ThreadLocal:
|
||||||
return m_kernel_map_region_start;
|
return m_kernel_map_region_start;
|
||||||
case KMemoryState::Io:
|
case Svc::MemoryState::Io:
|
||||||
case KMemoryState::Shared:
|
case Svc::MemoryState::Shared:
|
||||||
case KMemoryState::AliasCode:
|
case Svc::MemoryState::AliasCode:
|
||||||
case KMemoryState::AliasCodeData:
|
case Svc::MemoryState::AliasCodeData:
|
||||||
case KMemoryState::Transfered:
|
case Svc::MemoryState::Transfered:
|
||||||
case KMemoryState::SharedTransfered:
|
case Svc::MemoryState::SharedTransfered:
|
||||||
case KMemoryState::SharedCode:
|
case Svc::MemoryState::SharedCode:
|
||||||
case KMemoryState::GeneratedCode:
|
case Svc::MemoryState::GeneratedCode:
|
||||||
case KMemoryState::CodeOut:
|
case Svc::MemoryState::CodeOut:
|
||||||
case KMemoryState::Coverage:
|
case Svc::MemoryState::Coverage:
|
||||||
case KMemoryState::Insecure:
|
case Svc::MemoryState::Insecure:
|
||||||
return m_alias_code_region_start;
|
return m_alias_code_region_start;
|
||||||
case KMemoryState::Code:
|
case Svc::MemoryState::Code:
|
||||||
case KMemoryState::CodeData:
|
case Svc::MemoryState::CodeData:
|
||||||
return m_code_region_start;
|
return m_code_region_start;
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t KPageTable::GetRegionSize(KMemoryState state) const {
|
size_t KPageTable::GetRegionSize(Svc::MemoryState state) const {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case KMemoryState::Free:
|
case Svc::MemoryState::Free:
|
||||||
case KMemoryState::Kernel:
|
case Svc::MemoryState::Kernel:
|
||||||
return m_address_space_end - m_address_space_start;
|
return m_address_space_end - m_address_space_start;
|
||||||
case KMemoryState::Normal:
|
case Svc::MemoryState::Normal:
|
||||||
return m_heap_region_end - m_heap_region_start;
|
return m_heap_region_end - m_heap_region_start;
|
||||||
case KMemoryState::Ipc:
|
case Svc::MemoryState::Ipc:
|
||||||
case KMemoryState::NonSecureIpc:
|
case Svc::MemoryState::NonSecureIpc:
|
||||||
case KMemoryState::NonDeviceIpc:
|
case Svc::MemoryState::NonDeviceIpc:
|
||||||
return m_alias_region_end - m_alias_region_start;
|
return m_alias_region_end - m_alias_region_start;
|
||||||
case KMemoryState::Stack:
|
case Svc::MemoryState::Stack:
|
||||||
return m_stack_region_end - m_stack_region_start;
|
return m_stack_region_end - m_stack_region_start;
|
||||||
case KMemoryState::Static:
|
case Svc::MemoryState::Static:
|
||||||
case KMemoryState::ThreadLocal:
|
case Svc::MemoryState::ThreadLocal:
|
||||||
return m_kernel_map_region_end - m_kernel_map_region_start;
|
return m_kernel_map_region_end - m_kernel_map_region_start;
|
||||||
case KMemoryState::Io:
|
case Svc::MemoryState::Io:
|
||||||
case KMemoryState::Shared:
|
case Svc::MemoryState::Shared:
|
||||||
case KMemoryState::AliasCode:
|
case Svc::MemoryState::AliasCode:
|
||||||
case KMemoryState::AliasCodeData:
|
case Svc::MemoryState::AliasCodeData:
|
||||||
case KMemoryState::Transfered:
|
case Svc::MemoryState::Transfered:
|
||||||
case KMemoryState::SharedTransfered:
|
case Svc::MemoryState::SharedTransfered:
|
||||||
case KMemoryState::SharedCode:
|
case Svc::MemoryState::SharedCode:
|
||||||
case KMemoryState::GeneratedCode:
|
case Svc::MemoryState::GeneratedCode:
|
||||||
case KMemoryState::CodeOut:
|
case Svc::MemoryState::CodeOut:
|
||||||
case KMemoryState::Coverage:
|
case Svc::MemoryState::Coverage:
|
||||||
case KMemoryState::Insecure:
|
case Svc::MemoryState::Insecure:
|
||||||
return m_alias_code_region_end - m_alias_code_region_start;
|
return m_alias_code_region_end - m_alias_code_region_start;
|
||||||
case KMemoryState::Code:
|
case Svc::MemoryState::Code:
|
||||||
case KMemoryState::CodeData:
|
case Svc::MemoryState::CodeData:
|
||||||
return m_code_region_end - m_code_region_start;
|
return m_code_region_end - m_code_region_start;
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool KPageTable::CanContain(KProcessAddress addr, size_t size, KMemoryState state) const {
|
bool KPageTable::CanContain(KProcessAddress addr, size_t size, Svc::MemoryState state) const {
|
||||||
const KProcessAddress end = addr + size;
|
const KProcessAddress end = addr + size;
|
||||||
const KProcessAddress last = end - 1;
|
const KProcessAddress last = end - 1;
|
||||||
|
|
||||||
@@ -3192,32 +3219,32 @@ bool KPageTable::CanContain(KProcessAddress addr, size_t size, KMemoryState stat
|
|||||||
const bool is_in_alias = !(end <= m_alias_region_start || m_alias_region_end <= addr ||
|
const bool is_in_alias = !(end <= m_alias_region_start || m_alias_region_end <= addr ||
|
||||||
m_alias_region_start == m_alias_region_end);
|
m_alias_region_start == m_alias_region_end);
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case KMemoryState::Free:
|
case Svc::MemoryState::Free:
|
||||||
case KMemoryState::Kernel:
|
case Svc::MemoryState::Kernel:
|
||||||
return is_in_region;
|
return is_in_region;
|
||||||
case KMemoryState::Io:
|
case Svc::MemoryState::Io:
|
||||||
case KMemoryState::Static:
|
case Svc::MemoryState::Static:
|
||||||
case KMemoryState::Code:
|
case Svc::MemoryState::Code:
|
||||||
case KMemoryState::CodeData:
|
case Svc::MemoryState::CodeData:
|
||||||
case KMemoryState::Shared:
|
case Svc::MemoryState::Shared:
|
||||||
case KMemoryState::AliasCode:
|
case Svc::MemoryState::AliasCode:
|
||||||
case KMemoryState::AliasCodeData:
|
case Svc::MemoryState::AliasCodeData:
|
||||||
case KMemoryState::Stack:
|
case Svc::MemoryState::Stack:
|
||||||
case KMemoryState::ThreadLocal:
|
case Svc::MemoryState::ThreadLocal:
|
||||||
case KMemoryState::Transfered:
|
case Svc::MemoryState::Transfered:
|
||||||
case KMemoryState::SharedTransfered:
|
case Svc::MemoryState::SharedTransfered:
|
||||||
case KMemoryState::SharedCode:
|
case Svc::MemoryState::SharedCode:
|
||||||
case KMemoryState::GeneratedCode:
|
case Svc::MemoryState::GeneratedCode:
|
||||||
case KMemoryState::CodeOut:
|
case Svc::MemoryState::CodeOut:
|
||||||
case KMemoryState::Coverage:
|
case Svc::MemoryState::Coverage:
|
||||||
case KMemoryState::Insecure:
|
case Svc::MemoryState::Insecure:
|
||||||
return is_in_region && !is_in_heap && !is_in_alias;
|
return is_in_region && !is_in_heap && !is_in_alias;
|
||||||
case KMemoryState::Normal:
|
case Svc::MemoryState::Normal:
|
||||||
ASSERT(is_in_heap);
|
ASSERT(is_in_heap);
|
||||||
return is_in_region && !is_in_alias;
|
return is_in_region && !is_in_alias;
|
||||||
case KMemoryState::Ipc:
|
case Svc::MemoryState::Ipc:
|
||||||
case KMemoryState::NonSecureIpc:
|
case Svc::MemoryState::NonSecureIpc:
|
||||||
case KMemoryState::NonDeviceIpc:
|
case Svc::MemoryState::NonDeviceIpc:
|
||||||
ASSERT(is_in_alias);
|
ASSERT(is_in_alias);
|
||||||
return is_in_region && !is_in_heap;
|
return is_in_region && !is_in_heap;
|
||||||
default:
|
default:
|
||||||
@@ -3281,21 +3308,16 @@ Result KPageTable::CheckMemoryStateContiguous(size_t* out_blocks_needed, KProces
|
|||||||
|
|
||||||
Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
|
Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
|
||||||
KMemoryAttribute* out_attr, size_t* out_blocks_needed,
|
KMemoryAttribute* out_attr, size_t* out_blocks_needed,
|
||||||
KProcessAddress addr, size_t size, KMemoryState state_mask,
|
KMemoryBlockManager::const_iterator it,
|
||||||
|
KProcessAddress last_addr, KMemoryState state_mask,
|
||||||
KMemoryState state, KMemoryPermission perm_mask,
|
KMemoryState state, KMemoryPermission perm_mask,
|
||||||
KMemoryPermission perm, KMemoryAttribute attr_mask,
|
KMemoryPermission perm, KMemoryAttribute attr_mask,
|
||||||
KMemoryAttribute attr, KMemoryAttribute ignore_attr) const {
|
KMemoryAttribute attr, KMemoryAttribute ignore_attr) const {
|
||||||
ASSERT(this->IsLockedByCurrentThread());
|
ASSERT(this->IsLockedByCurrentThread());
|
||||||
|
|
||||||
// Get information about the first block.
|
// Get information about the first block.
|
||||||
const KProcessAddress last_addr = addr + size - 1;
|
|
||||||
KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr);
|
|
||||||
KMemoryInfo info = it->GetMemoryInfo();
|
KMemoryInfo info = it->GetMemoryInfo();
|
||||||
|
|
||||||
// If the start address isn't aligned, we need a block.
|
|
||||||
const size_t blocks_for_start_align =
|
|
||||||
(Common::AlignDown(GetInteger(addr), PageSize) != info.GetAddress()) ? 1 : 0;
|
|
||||||
|
|
||||||
// Validate all blocks in the range have correct state.
|
// Validate all blocks in the range have correct state.
|
||||||
const KMemoryState first_state = info.m_state;
|
const KMemoryState first_state = info.m_state;
|
||||||
const KMemoryPermission first_perm = info.m_permission;
|
const KMemoryPermission first_perm = info.m_permission;
|
||||||
@@ -3321,10 +3343,6 @@ Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission*
|
|||||||
info = it->GetMemoryInfo();
|
info = it->GetMemoryInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the end address isn't aligned, we need a block.
|
|
||||||
const size_t blocks_for_end_align =
|
|
||||||
(Common::AlignUp(GetInteger(addr) + size, PageSize) != info.GetEndAddress()) ? 1 : 0;
|
|
||||||
|
|
||||||
// Write output state.
|
// Write output state.
|
||||||
if (out_state != nullptr) {
|
if (out_state != nullptr) {
|
||||||
*out_state = first_state;
|
*out_state = first_state;
|
||||||
@@ -3335,9 +3353,39 @@ Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission*
|
|||||||
if (out_attr != nullptr) {
|
if (out_attr != nullptr) {
|
||||||
*out_attr = static_cast<KMemoryAttribute>(first_attr & ~ignore_attr);
|
*out_attr = static_cast<KMemoryAttribute>(first_attr & ~ignore_attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the end address isn't aligned, we need a block.
|
||||||
if (out_blocks_needed != nullptr) {
|
if (out_blocks_needed != nullptr) {
|
||||||
*out_blocks_needed = blocks_for_start_align + blocks_for_end_align;
|
const size_t blocks_for_end_align =
|
||||||
|
(Common::AlignDown(GetInteger(last_addr), PageSize) + PageSize != info.GetEndAddress())
|
||||||
|
? 1
|
||||||
|
: 0;
|
||||||
|
*out_blocks_needed = blocks_for_end_align;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
|
||||||
|
KMemoryAttribute* out_attr, size_t* out_blocks_needed,
|
||||||
|
KProcessAddress addr, size_t size, KMemoryState state_mask,
|
||||||
|
KMemoryState state, KMemoryPermission perm_mask,
|
||||||
|
KMemoryPermission perm, KMemoryAttribute attr_mask,
|
||||||
|
KMemoryAttribute attr, KMemoryAttribute ignore_attr) const {
|
||||||
|
ASSERT(this->IsLockedByCurrentThread());
|
||||||
|
|
||||||
|
// Check memory state.
|
||||||
|
const KProcessAddress last_addr = addr + size - 1;
|
||||||
|
KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr);
|
||||||
|
R_TRY(this->CheckMemoryState(out_state, out_perm, out_attr, out_blocks_needed, it, last_addr,
|
||||||
|
state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr));
|
||||||
|
|
||||||
|
// If the start address isn't aligned, we need a block.
|
||||||
|
if (out_blocks_needed != nullptr &&
|
||||||
|
Common::AlignDown(GetInteger(addr), PageSize) != it->GetAddress()) {
|
||||||
|
++(*out_blocks_needed);
|
||||||
|
}
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ public:
|
|||||||
explicit KPageTable(Core::System& system_);
|
explicit KPageTable(Core::System& system_);
|
||||||
~KPageTable();
|
~KPageTable();
|
||||||
|
|
||||||
Result InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr,
|
Result InitializeForProcess(Svc::CreateProcessFlag as_type, bool enable_aslr,
|
||||||
bool enable_das_merge, bool from_back, KMemoryManager::Pool pool,
|
bool enable_das_merge, bool from_back, KMemoryManager::Pool pool,
|
||||||
KProcessAddress code_addr, size_t code_size,
|
KProcessAddress code_addr, size_t code_size,
|
||||||
KSystemResource* system_resource, KResourceLimit* resource_limit,
|
KSystemResource* system_resource, KResourceLimit* resource_limit,
|
||||||
@@ -126,8 +126,6 @@ public:
|
|||||||
return m_block_info_manager;
|
return m_block_info_manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const;
|
|
||||||
|
|
||||||
Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
|
Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
|
||||||
KPhysicalAddress phys_addr, KProcessAddress region_start,
|
KPhysicalAddress phys_addr, KProcessAddress region_start,
|
||||||
size_t region_num_pages, KMemoryState state, KMemoryPermission perm) {
|
size_t region_num_pages, KMemoryState state, KMemoryPermission perm) {
|
||||||
@@ -162,6 +160,21 @@ public:
|
|||||||
void RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size,
|
void RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size,
|
||||||
const KPageGroup& pg);
|
const KPageGroup& pg);
|
||||||
|
|
||||||
|
KProcessAddress GetRegionAddress(Svc::MemoryState state) const;
|
||||||
|
size_t GetRegionSize(Svc::MemoryState state) const;
|
||||||
|
bool CanContain(KProcessAddress addr, size_t size, Svc::MemoryState state) const;
|
||||||
|
|
||||||
|
KProcessAddress GetRegionAddress(KMemoryState state) const {
|
||||||
|
return this->GetRegionAddress(static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
|
||||||
|
}
|
||||||
|
size_t GetRegionSize(KMemoryState state) const {
|
||||||
|
return this->GetRegionSize(static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
|
||||||
|
}
|
||||||
|
bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const {
|
||||||
|
return this->CanContain(addr, size,
|
||||||
|
static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
struct PageLinkedList {
|
struct PageLinkedList {
|
||||||
private:
|
private:
|
||||||
@@ -204,12 +217,13 @@ protected:
|
|||||||
private:
|
private:
|
||||||
enum class OperationType : u32 {
|
enum class OperationType : u32 {
|
||||||
Map = 0,
|
Map = 0,
|
||||||
MapFirst = 1,
|
MapGroup = 1,
|
||||||
MapGroup = 2,
|
MapFirstGroup = 2,
|
||||||
Unmap = 3,
|
Unmap = 3,
|
||||||
ChangePermissions = 4,
|
ChangePermissions = 4,
|
||||||
ChangePermissionsAndRefresh = 5,
|
ChangePermissionsAndRefresh = 5,
|
||||||
Separate = 6,
|
ChangePermissionsAndRefreshAndFlush = 6,
|
||||||
|
Separate = 7,
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr =
|
static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr =
|
||||||
@@ -228,8 +242,6 @@ private:
|
|||||||
Result Operate(KProcessAddress addr, size_t num_pages, KMemoryPermission perm,
|
Result Operate(KProcessAddress addr, size_t num_pages, KMemoryPermission perm,
|
||||||
OperationType operation, KPhysicalAddress map_addr = 0);
|
OperationType operation, KPhysicalAddress map_addr = 0);
|
||||||
void FinalizeUpdate(PageLinkedList* page_list);
|
void FinalizeUpdate(PageLinkedList* page_list);
|
||||||
KProcessAddress GetRegionAddress(KMemoryState state) const;
|
|
||||||
size_t GetRegionSize(KMemoryState state) const;
|
|
||||||
|
|
||||||
KProcessAddress FindFreeArea(KProcessAddress region_start, size_t region_num_pages,
|
KProcessAddress FindFreeArea(KProcessAddress region_start, size_t region_num_pages,
|
||||||
size_t num_pages, size_t alignment, size_t offset,
|
size_t num_pages, size_t alignment, size_t offset,
|
||||||
@@ -250,6 +262,13 @@ private:
|
|||||||
Result CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, KMemoryState state,
|
Result CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, KMemoryState state,
|
||||||
KMemoryPermission perm_mask, KMemoryPermission perm,
|
KMemoryPermission perm_mask, KMemoryPermission perm,
|
||||||
KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
|
KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
|
||||||
|
Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
|
||||||
|
KMemoryAttribute* out_attr, size_t* out_blocks_needed,
|
||||||
|
KMemoryBlockManager::const_iterator it, KProcessAddress last_addr,
|
||||||
|
KMemoryState state_mask, KMemoryState state,
|
||||||
|
KMemoryPermission perm_mask, KMemoryPermission perm,
|
||||||
|
KMemoryAttribute attr_mask, KMemoryAttribute attr,
|
||||||
|
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
|
||||||
Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
|
Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
|
||||||
KMemoryAttribute* out_attr, size_t* out_blocks_needed,
|
KMemoryAttribute* out_attr, size_t* out_blocks_needed,
|
||||||
KProcessAddress addr, size_t size, KMemoryState state_mask,
|
KProcessAddress addr, size_t size, KMemoryState state_mask,
|
||||||
@@ -381,7 +400,7 @@ public:
|
|||||||
constexpr size_t GetAliasCodeRegionSize() const {
|
constexpr size_t GetAliasCodeRegionSize() const {
|
||||||
return m_alias_code_region_end - m_alias_code_region_start;
|
return m_alias_code_region_end - m_alias_code_region_start;
|
||||||
}
|
}
|
||||||
size_t GetNormalMemorySize() {
|
size_t GetNormalMemorySize() const {
|
||||||
KScopedLightLock lk(m_general_lock);
|
KScopedLightLock lk(m_general_lock);
|
||||||
return GetHeapSize() + m_mapped_physical_memory_size;
|
return GetHeapSize() + m_mapped_physical_memory_size;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,59 +1,23 @@
|
|||||||
// SPDX-FileCopyrightText: 2015 Citra Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <list>
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
|
||||||
|
#include "core/hle/kernel/code_set.h"
|
||||||
#include "core/hle/kernel/k_address_arbiter.h"
|
#include "core/hle/kernel/k_address_arbiter.h"
|
||||||
#include "core/hle/kernel/k_auto_object.h"
|
#include "core/hle/kernel/k_capabilities.h"
|
||||||
#include "core/hle/kernel/k_condition_variable.h"
|
#include "core/hle/kernel/k_condition_variable.h"
|
||||||
#include "core/hle/kernel/k_handle_table.h"
|
#include "core/hle/kernel/k_handle_table.h"
|
||||||
#include "core/hle/kernel/k_page_table.h"
|
#include "core/hle/kernel/k_page_table.h"
|
||||||
#include "core/hle/kernel/k_synchronization_object.h"
|
#include "core/hle/kernel/k_page_table_manager.h"
|
||||||
|
#include "core/hle/kernel/k_system_resource.h"
|
||||||
|
#include "core/hle/kernel/k_thread.h"
|
||||||
#include "core/hle/kernel/k_thread_local_page.h"
|
#include "core/hle/kernel/k_thread_local_page.h"
|
||||||
#include "core/hle/kernel/k_typed_address.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"
|
|
||||||
|
|
||||||
namespace Core {
|
|
||||||
namespace Memory {
|
|
||||||
class Memory;
|
|
||||||
};
|
|
||||||
|
|
||||||
class System;
|
|
||||||
} // namespace Core
|
|
||||||
|
|
||||||
namespace FileSys {
|
|
||||||
class ProgramMetadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
class KernelCore;
|
|
||||||
class KResourceLimit;
|
|
||||||
class KThread;
|
|
||||||
class KSharedMemoryInfo;
|
|
||||||
class TLSPage;
|
|
||||||
|
|
||||||
struct CodeSet;
|
|
||||||
|
|
||||||
enum class MemoryRegion : u16 {
|
|
||||||
APPLICATION = 1,
|
|
||||||
SYSTEM = 2,
|
|
||||||
BASE = 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class ProcessActivity : u32 {
|
|
||||||
Runnable,
|
|
||||||
Paused,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class DebugWatchpointType : u8 {
|
enum class DebugWatchpointType : u8 {
|
||||||
None = 0,
|
None = 0,
|
||||||
Read = 1 << 0,
|
Read = 1 << 0,
|
||||||
@@ -72,9 +36,6 @@ class KProcess final : public KAutoObjectWithSlabHeapAndContainer<KProcess, KWor
|
|||||||
KERNEL_AUTOOBJECT_TRAITS(KProcess, KSynchronizationObject);
|
KERNEL_AUTOOBJECT_TRAITS(KProcess, KSynchronizationObject);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit KProcess(KernelCore& kernel);
|
|
||||||
~KProcess() override;
|
|
||||||
|
|
||||||
enum class State {
|
enum class State {
|
||||||
Created = static_cast<u32>(Svc::ProcessState::Created),
|
Created = static_cast<u32>(Svc::ProcessState::Created),
|
||||||
CreatedAttached = static_cast<u32>(Svc::ProcessState::CreatedAttached),
|
CreatedAttached = static_cast<u32>(Svc::ProcessState::CreatedAttached),
|
||||||
@@ -86,337 +47,83 @@ public:
|
|||||||
DebugBreak = static_cast<u32>(Svc::ProcessState::DebugBreak),
|
DebugBreak = static_cast<u32>(Svc::ProcessState::DebugBreak),
|
||||||
};
|
};
|
||||||
|
|
||||||
enum : u64 {
|
using ThreadList = Common::IntrusiveListMemberTraits<&KThread::m_process_list_node>::ListType;
|
||||||
/// Lowest allowed process ID for a kernel initial process.
|
|
||||||
InitialKIPIDMin = 1,
|
|
||||||
/// Highest allowed process ID for a kernel initial process.
|
|
||||||
InitialKIPIDMax = 80,
|
|
||||||
|
|
||||||
/// Lowest allowed process ID for a userland process.
|
static constexpr size_t AslrAlignment = 2_MiB;
|
||||||
ProcessIDMin = 81,
|
|
||||||
/// Highest allowed process ID for a userland process.
|
|
||||||
ProcessIDMax = 0xFFFFFFFFFFFFFFFF,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Used to determine how process IDs are assigned.
|
public:
|
||||||
enum class ProcessType {
|
static constexpr u64 InitialProcessIdMin = 1;
|
||||||
KernelInternal,
|
static constexpr u64 InitialProcessIdMax = 0x50;
|
||||||
Userland,
|
|
||||||
};
|
|
||||||
|
|
||||||
static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
|
static constexpr u64 ProcessIdMin = InitialProcessIdMax + 1;
|
||||||
|
static constexpr u64 ProcessIdMax = std::numeric_limits<u64>::max();
|
||||||
static Result Initialize(KProcess* process, Core::System& system, std::string process_name,
|
|
||||||
ProcessType type, KResourceLimit* res_limit);
|
|
||||||
|
|
||||||
/// Gets a reference to the process' page table.
|
|
||||||
KPageTable& GetPageTable() {
|
|
||||||
return m_page_table;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets const a reference to the process' page table.
|
|
||||||
const KPageTable& GetPageTable() const {
|
|
||||||
return m_page_table;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets a reference to the process' handle table.
|
|
||||||
KHandleTable& GetHandleTable() {
|
|
||||||
return m_handle_table;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets a const reference to the process' handle table.
|
|
||||||
const KHandleTable& GetHandleTable() const {
|
|
||||||
return m_handle_table;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets a reference to process's memory.
|
|
||||||
Core::Memory::Memory& GetMemory() const;
|
|
||||||
|
|
||||||
Result SignalToAddress(KProcessAddress address) {
|
|
||||||
return m_condition_var.SignalToAddress(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result WaitForAddress(Handle handle, KProcessAddress address, u32 tag) {
|
|
||||||
return m_condition_var.WaitForAddress(handle, address, tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SignalConditionVariable(u64 cv_key, int32_t count) {
|
|
||||||
return m_condition_var.Signal(cv_key, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result WaitConditionVariable(KProcessAddress address, u64 cv_key, u32 tag, s64 ns) {
|
|
||||||
R_RETURN(m_condition_var.Wait(address, cv_key, tag, ns));
|
|
||||||
}
|
|
||||||
|
|
||||||
Result SignalAddressArbiter(uint64_t address, Svc::SignalType signal_type, s32 value,
|
|
||||||
s32 count) {
|
|
||||||
R_RETURN(m_address_arbiter.SignalToAddress(address, signal_type, value, count));
|
|
||||||
}
|
|
||||||
|
|
||||||
Result WaitAddressArbiter(uint64_t address, Svc::ArbitrationType arb_type, s32 value,
|
|
||||||
s64 timeout) {
|
|
||||||
R_RETURN(m_address_arbiter.WaitForAddress(address, arb_type, value, timeout));
|
|
||||||
}
|
|
||||||
|
|
||||||
KProcessAddress GetProcessLocalRegionAddress() const {
|
|
||||||
return m_plr_address;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the current status of the process
|
|
||||||
State GetState() const {
|
|
||||||
return m_state;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the unique ID that identifies this particular process.
|
|
||||||
u64 GetProcessId() const {
|
|
||||||
return m_process_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the program ID corresponding to this process.
|
|
||||||
u64 GetProgramId() const {
|
|
||||||
return m_program_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
KProcessAddress GetEntryPoint() const {
|
|
||||||
return m_code_address;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the resource limit descriptor for this process
|
|
||||||
KResourceLimit* GetResourceLimit() const;
|
|
||||||
|
|
||||||
/// Gets the ideal CPU core ID for this process
|
|
||||||
u8 GetIdealCoreId() const {
|
|
||||||
return m_ideal_core;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if the specified thread priority is valid.
|
|
||||||
bool CheckThreadPriority(s32 prio) const {
|
|
||||||
return ((1ULL << prio) & GetPriorityMask()) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the bitmask of allowed cores that this process' threads can run on.
|
|
||||||
u64 GetCoreMask() const {
|
|
||||||
return m_capabilities.GetCoreMask();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the bitmask of allowed thread priorities.
|
|
||||||
u64 GetPriorityMask() const {
|
|
||||||
return m_capabilities.GetPriorityMask();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the amount of secure memory to allocate for memory management.
|
|
||||||
u32 GetSystemResourceSize() const {
|
|
||||||
return m_system_resource_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the amount of secure memory currently in use for memory management.
|
|
||||||
u32 GetSystemResourceUsage() const {
|
|
||||||
// On hardware, this returns the amount of system resource memory that has
|
|
||||||
// been used by the kernel. This is problematic for Yuzu to emulate, because
|
|
||||||
// system resource memory is used for page tables -- and yuzu doesn't really
|
|
||||||
// have a way to calculate how much memory is required for page tables for
|
|
||||||
// the current process at any given time.
|
|
||||||
// TODO: Is this even worth implementing? Games may retrieve this value via
|
|
||||||
// an SDK function that gets used + available system resource size for debug
|
|
||||||
// or diagnostic purposes. However, it seems unlikely that a game would make
|
|
||||||
// decisions based on how much system memory is dedicated to its page tables.
|
|
||||||
// Is returning a value other than zero wise?
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether this process is an AArch64 or AArch32 process.
|
|
||||||
bool Is64BitProcess() const {
|
|
||||||
return m_is_64bit_process;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsSuspended() const {
|
|
||||||
return m_is_suspended;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetSuspended(bool suspended) {
|
|
||||||
m_is_suspended = suspended;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the total running time of the process instance in ticks.
|
|
||||||
u64 GetCPUTimeTicks() const {
|
|
||||||
return m_total_process_running_time_ticks;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Updates the total running time, adding the given ticks to it.
|
|
||||||
void UpdateCPUTimeTicks(u64 ticks) {
|
|
||||||
m_total_process_running_time_ticks += ticks;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the process schedule count, used for thread yielding
|
|
||||||
s64 GetScheduledCount() const {
|
|
||||||
return m_schedule_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Increments the process schedule count, used for thread yielding.
|
|
||||||
void IncrementScheduledCount() {
|
|
||||||
++m_schedule_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IncrementRunningThreadCount();
|
|
||||||
void DecrementRunningThreadCount();
|
|
||||||
|
|
||||||
void SetRunningThread(s32 core, KThread* thread, u64 idle_count) {
|
|
||||||
m_running_threads[core] = thread;
|
|
||||||
m_running_thread_idle_counts[core] = idle_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClearRunningThread(KThread* thread) {
|
|
||||||
for (size_t i = 0; i < m_running_threads.size(); ++i) {
|
|
||||||
if (m_running_threads[i] == thread) {
|
|
||||||
m_running_threads[i] = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] KThread* GetRunningThread(s32 core) const {
|
|
||||||
return m_running_threads[core];
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ReleaseUserException(KThread* thread);
|
|
||||||
|
|
||||||
[[nodiscard]] KThread* GetPinnedThread(s32 core_id) const {
|
|
||||||
ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
|
|
||||||
return m_pinned_threads[core_id];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets 8 bytes of random data for svcGetInfo RandomEntropy
|
|
||||||
u64 GetRandomEntropy(std::size_t index) const {
|
|
||||||
return m_random_entropy.at(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieves the total physical memory available to this process in bytes.
|
|
||||||
u64 GetTotalPhysicalMemoryAvailable();
|
|
||||||
|
|
||||||
/// Retrieves the total physical memory available to this process in bytes,
|
|
||||||
/// without the size of the personal system resource heap added to it.
|
|
||||||
u64 GetTotalPhysicalMemoryAvailableWithoutSystemResource();
|
|
||||||
|
|
||||||
/// Retrieves the total physical memory used by this process in bytes.
|
|
||||||
u64 GetTotalPhysicalMemoryUsed();
|
|
||||||
|
|
||||||
/// Retrieves the total physical memory used by this process in bytes,
|
|
||||||
/// without the size of the personal system resource heap added to it.
|
|
||||||
u64 GetTotalPhysicalMemoryUsedWithoutSystemResource();
|
|
||||||
|
|
||||||
/// Gets the list of all threads created with this process as their owner.
|
|
||||||
std::list<KThread*>& GetThreadList() {
|
|
||||||
return m_thread_list;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Registers a thread as being created under this process,
|
|
||||||
/// adding it to this process' thread list.
|
|
||||||
void RegisterThread(KThread* thread);
|
|
||||||
|
|
||||||
/// Unregisters a thread from this process, removing it
|
|
||||||
/// from this process' thread list.
|
|
||||||
void UnregisterThread(KThread* thread);
|
|
||||||
|
|
||||||
/// Retrieves the number of available threads for this process.
|
|
||||||
u64 GetFreeThreadCount() const;
|
|
||||||
|
|
||||||
/// Clears the signaled state of the process if and only if it's signaled.
|
|
||||||
///
|
|
||||||
/// @pre The process must not be already terminated. If this is called on a
|
|
||||||
/// terminated process, then ResultInvalidState will be returned.
|
|
||||||
///
|
|
||||||
/// @pre The process must be in a signaled state. If this is called on a
|
|
||||||
/// process instance that is not signaled, ResultInvalidState will be
|
|
||||||
/// returned.
|
|
||||||
Result Reset();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads process-specifics configuration info with metadata provided
|
|
||||||
* by an executable.
|
|
||||||
*
|
|
||||||
* @param metadata The provided metadata to load process specific info from.
|
|
||||||
*
|
|
||||||
* @returns ResultSuccess if all relevant metadata was able to be
|
|
||||||
* loaded and parsed. Otherwise, an error code is returned.
|
|
||||||
*/
|
|
||||||
Result LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size,
|
|
||||||
bool is_hbl);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts the main application thread for this process.
|
|
||||||
*
|
|
||||||
* @param main_thread_priority The priority for the main thread.
|
|
||||||
* @param stack_size The stack size for the main thread in bytes.
|
|
||||||
*/
|
|
||||||
void Run(s32 main_thread_priority, u64 stack_size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prepares a process for termination by stopping all of its threads
|
|
||||||
* and clearing any other resources.
|
|
||||||
*/
|
|
||||||
void PrepareForTermination();
|
|
||||||
|
|
||||||
void LoadModule(CodeSet code_set, KProcessAddress base_addr);
|
|
||||||
|
|
||||||
bool IsInitialized() const override {
|
|
||||||
return m_is_initialized;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void PostDestroy(uintptr_t arg) {}
|
|
||||||
|
|
||||||
void Finalize() override;
|
|
||||||
|
|
||||||
u64 GetId() const override {
|
|
||||||
return GetProcessId();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsHbl() const {
|
|
||||||
return m_is_hbl;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsSignaled() const override;
|
|
||||||
|
|
||||||
void DoWorkerTaskImpl();
|
|
||||||
|
|
||||||
Result SetActivity(ProcessActivity activity);
|
|
||||||
|
|
||||||
void PinCurrentThread(s32 core_id);
|
|
||||||
void UnpinCurrentThread(s32 core_id);
|
|
||||||
void UnpinThread(KThread* thread);
|
|
||||||
|
|
||||||
KLightLock& GetStateLock() {
|
|
||||||
return m_state_lock;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result AddSharedMemory(KSharedMemory* shmem, KProcessAddress address, size_t size);
|
|
||||||
void RemoveSharedMemory(KSharedMemory* shmem, KProcessAddress address, size_t size);
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Thread-local storage management
|
|
||||||
|
|
||||||
// Marks the next available region as used and returns the address of the slot.
|
|
||||||
[[nodiscard]] Result CreateThreadLocalRegion(KProcessAddress* out);
|
|
||||||
|
|
||||||
// Frees a used TLS slot identified by the given address
|
|
||||||
Result DeleteThreadLocalRegion(KProcessAddress addr);
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Debug watchpoint management
|
|
||||||
|
|
||||||
// Attempts to insert a watchpoint into a free slot. Returns false if none are available.
|
|
||||||
bool InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type);
|
|
||||||
|
|
||||||
// Attempts to remove the watchpoint specified by the given parameters.
|
|
||||||
bool RemoveWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type);
|
|
||||||
|
|
||||||
const std::array<DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>& GetWatchpoints() const {
|
|
||||||
return m_watchpoints;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string& GetName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
using SharedMemoryInfoList = Common::IntrusiveListBaseTraits<KSharedMemoryInfo>::ListType;
|
||||||
|
using TLPTree =
|
||||||
|
Common::IntrusiveRedBlackTreeBaseTraits<KThreadLocalPage>::TreeType<KThreadLocalPage>;
|
||||||
|
using TLPIterator = TLPTree::iterator;
|
||||||
|
|
||||||
|
private:
|
||||||
|
KPageTable m_page_table;
|
||||||
|
std::atomic<size_t> m_used_kernel_memory_size{};
|
||||||
|
TLPTree m_fully_used_tlp_tree{};
|
||||||
|
TLPTree m_partially_used_tlp_tree{};
|
||||||
|
s32 m_ideal_core_id{};
|
||||||
|
KResourceLimit* m_resource_limit{};
|
||||||
|
KSystemResource* m_system_resource{};
|
||||||
|
size_t m_memory_release_hint{};
|
||||||
|
State m_state{};
|
||||||
|
KLightLock m_state_lock;
|
||||||
|
KLightLock m_list_lock;
|
||||||
|
KConditionVariable m_cond_var;
|
||||||
|
KAddressArbiter m_address_arbiter;
|
||||||
|
std::array<u64, 4> m_entropy{};
|
||||||
|
bool m_is_signaled{};
|
||||||
|
bool m_is_initialized{};
|
||||||
|
bool m_is_application{};
|
||||||
|
bool m_is_default_application_system_resource{};
|
||||||
|
bool m_is_hbl{};
|
||||||
|
std::array<char, 13> m_name{};
|
||||||
|
std::atomic<u16> m_num_running_threads{};
|
||||||
|
Svc::CreateProcessFlag m_flags{};
|
||||||
|
KMemoryManager::Pool m_memory_pool{};
|
||||||
|
s64 m_schedule_count{};
|
||||||
|
KCapabilities m_capabilities{};
|
||||||
|
u64 m_program_id{};
|
||||||
|
u64 m_process_id{};
|
||||||
|
KProcessAddress m_code_address{};
|
||||||
|
size_t m_code_size{};
|
||||||
|
size_t m_main_thread_stack_size{};
|
||||||
|
size_t m_max_process_memory{};
|
||||||
|
u32 m_version{};
|
||||||
|
KHandleTable m_handle_table;
|
||||||
|
KProcessAddress m_plr_address{};
|
||||||
|
KThread* m_exception_thread{};
|
||||||
|
ThreadList m_thread_list{};
|
||||||
|
SharedMemoryInfoList m_shared_memory_list{};
|
||||||
|
bool m_is_suspended{};
|
||||||
|
bool m_is_immortal{};
|
||||||
|
bool m_is_handle_table_initialized{};
|
||||||
|
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> m_running_threads{};
|
||||||
|
std::array<u64, Core::Hardware::NUM_CPU_CORES> m_running_thread_idle_counts{};
|
||||||
|
std::array<u64, Core::Hardware::NUM_CPU_CORES> m_running_thread_switch_counts{};
|
||||||
|
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> m_pinned_threads{};
|
||||||
|
std::array<DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS> m_watchpoints{};
|
||||||
|
std::map<KProcessAddress, u64> m_debug_page_refcounts{};
|
||||||
|
std::atomic<s64> m_cpu_time{};
|
||||||
|
std::atomic<s64> m_num_process_switches{};
|
||||||
|
std::atomic<s64> m_num_thread_switches{};
|
||||||
|
std::atomic<s64> m_num_fpu_switches{};
|
||||||
|
std::atomic<s64> m_num_supervisor_calls{};
|
||||||
|
std::atomic<s64> m_num_ipc_messages{};
|
||||||
|
std::atomic<s64> m_num_ipc_replies{};
|
||||||
|
std::atomic<s64> m_num_ipc_receives{};
|
||||||
|
|
||||||
|
private:
|
||||||
|
Result StartTermination();
|
||||||
|
void FinishTermination();
|
||||||
|
|
||||||
void PinThread(s32 core_id, KThread* thread) {
|
void PinThread(s32 core_id, KThread* thread) {
|
||||||
ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
|
ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
|
||||||
ASSERT(thread != nullptr);
|
ASSERT(thread != nullptr);
|
||||||
@@ -431,6 +138,395 @@ private:
|
|||||||
m_pinned_threads[core_id] = nullptr;
|
m_pinned_threads[core_id] = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit KProcess(KernelCore& kernel);
|
||||||
|
~KProcess() override;
|
||||||
|
|
||||||
|
Result Initialize(const Svc::CreateProcessParameter& params, KResourceLimit* res_limit,
|
||||||
|
bool is_real);
|
||||||
|
|
||||||
|
Result Initialize(const Svc::CreateProcessParameter& params, const KPageGroup& pg,
|
||||||
|
std::span<const u32> caps, KResourceLimit* res_limit,
|
||||||
|
KMemoryManager::Pool pool, bool immortal);
|
||||||
|
Result Initialize(const Svc::CreateProcessParameter& params, std::span<const u32> user_caps,
|
||||||
|
KResourceLimit* res_limit, KMemoryManager::Pool pool);
|
||||||
|
void Exit();
|
||||||
|
|
||||||
|
const char* GetName() const {
|
||||||
|
return m_name.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 GetProgramId() const {
|
||||||
|
return m_program_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 GetProcessId() const {
|
||||||
|
return m_process_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
State GetState() const {
|
||||||
|
return m_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 GetCoreMask() const {
|
||||||
|
return m_capabilities.GetCoreMask();
|
||||||
|
}
|
||||||
|
u64 GetPhysicalCoreMask() const {
|
||||||
|
return m_capabilities.GetPhysicalCoreMask();
|
||||||
|
}
|
||||||
|
u64 GetPriorityMask() const {
|
||||||
|
return m_capabilities.GetPriorityMask();
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 GetIdealCoreId() const {
|
||||||
|
return m_ideal_core_id;
|
||||||
|
}
|
||||||
|
void SetIdealCoreId(s32 core_id) {
|
||||||
|
m_ideal_core_id = core_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CheckThreadPriority(s32 prio) const {
|
||||||
|
return ((1ULL << prio) & this->GetPriorityMask()) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetCreateProcessFlags() const {
|
||||||
|
return static_cast<u32>(m_flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Is64Bit() const {
|
||||||
|
return True(m_flags & Svc::CreateProcessFlag::Is64Bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
KProcessAddress GetEntryPoint() const {
|
||||||
|
return m_code_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetMainStackSize() const {
|
||||||
|
return m_main_thread_stack_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
KMemoryManager::Pool GetMemoryPool() const {
|
||||||
|
return m_memory_pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 GetRandomEntropy(size_t i) const {
|
||||||
|
return m_entropy[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsApplication() const {
|
||||||
|
return m_is_application;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsDefaultApplicationSystemResource() const {
|
||||||
|
return m_is_default_application_system_resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsSuspended() const {
|
||||||
|
return m_is_suspended;
|
||||||
|
}
|
||||||
|
void SetSuspended(bool suspended) {
|
||||||
|
m_is_suspended = suspended;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Terminate();
|
||||||
|
|
||||||
|
bool IsTerminated() const {
|
||||||
|
return m_state == State::Terminated;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsPermittedSvc(u32 svc_id) const {
|
||||||
|
return m_capabilities.IsPermittedSvc(svc_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsPermittedInterrupt(s32 interrupt_id) const {
|
||||||
|
return m_capabilities.IsPermittedInterrupt(interrupt_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsPermittedDebug() const {
|
||||||
|
return m_capabilities.IsPermittedDebug();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CanForceDebug() const {
|
||||||
|
return m_capabilities.CanForceDebug();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsHbl() const {
|
||||||
|
return m_is_hbl;
|
||||||
|
}
|
||||||
|
|
||||||
|
Kernel::KMemoryManager::Direction GetAllocateOption() const {
|
||||||
|
// TODO: property of the KPageTableBase
|
||||||
|
return KMemoryManager::Direction::FromFront;
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadList& GetThreadList() {
|
||||||
|
return m_thread_list;
|
||||||
|
}
|
||||||
|
const ThreadList& GetThreadList() const {
|
||||||
|
return m_thread_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EnterUserException();
|
||||||
|
bool LeaveUserException();
|
||||||
|
bool ReleaseUserException(KThread* thread);
|
||||||
|
|
||||||
|
KThread* GetPinnedThread(s32 core_id) const {
|
||||||
|
ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
|
||||||
|
return m_pinned_threads[core_id];
|
||||||
|
}
|
||||||
|
|
||||||
|
const Svc::SvcAccessFlagSet& GetSvcPermissions() const {
|
||||||
|
return m_capabilities.GetSvcPermissions();
|
||||||
|
}
|
||||||
|
|
||||||
|
KResourceLimit* GetResourceLimit() const {
|
||||||
|
return m_resource_limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReserveResource(Svc::LimitableResource which, s64 value);
|
||||||
|
bool ReserveResource(Svc::LimitableResource which, s64 value, s64 timeout);
|
||||||
|
void ReleaseResource(Svc::LimitableResource which, s64 value);
|
||||||
|
void ReleaseResource(Svc::LimitableResource which, s64 value, s64 hint);
|
||||||
|
|
||||||
|
KLightLock& GetStateLock() {
|
||||||
|
return m_state_lock;
|
||||||
|
}
|
||||||
|
KLightLock& GetListLock() {
|
||||||
|
return m_list_lock;
|
||||||
|
}
|
||||||
|
|
||||||
|
KPageTable& GetPageTable() {
|
||||||
|
return m_page_table;
|
||||||
|
}
|
||||||
|
const KPageTable& GetPageTable() const {
|
||||||
|
return m_page_table;
|
||||||
|
}
|
||||||
|
|
||||||
|
KHandleTable& GetHandleTable() {
|
||||||
|
return m_handle_table;
|
||||||
|
}
|
||||||
|
const KHandleTable& GetHandleTable() const {
|
||||||
|
return m_handle_table;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetUsedUserPhysicalMemorySize() const;
|
||||||
|
size_t GetTotalUserPhysicalMemorySize() const;
|
||||||
|
size_t GetUsedNonSystemUserPhysicalMemorySize() const;
|
||||||
|
size_t GetTotalNonSystemUserPhysicalMemorySize() const;
|
||||||
|
|
||||||
|
Result AddSharedMemory(KSharedMemory* shmem, KProcessAddress address, size_t size);
|
||||||
|
void RemoveSharedMemory(KSharedMemory* shmem, KProcessAddress address, size_t size);
|
||||||
|
|
||||||
|
Result CreateThreadLocalRegion(KProcessAddress* out);
|
||||||
|
Result DeleteThreadLocalRegion(KProcessAddress addr);
|
||||||
|
|
||||||
|
KProcessAddress GetProcessLocalRegionAddress() const {
|
||||||
|
return m_plr_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
KThread* GetExceptionThread() const {
|
||||||
|
return m_exception_thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddCpuTime(s64 diff) {
|
||||||
|
m_cpu_time += diff;
|
||||||
|
}
|
||||||
|
s64 GetCpuTime() {
|
||||||
|
return m_cpu_time.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
s64 GetScheduledCount() const {
|
||||||
|
return m_schedule_count;
|
||||||
|
}
|
||||||
|
void IncrementScheduledCount() {
|
||||||
|
++m_schedule_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IncrementRunningThreadCount();
|
||||||
|
void DecrementRunningThreadCount();
|
||||||
|
|
||||||
|
size_t GetRequiredSecureMemorySizeNonDefault() const {
|
||||||
|
if (!this->IsDefaultApplicationSystemResource() && m_system_resource->IsSecureResource()) {
|
||||||
|
auto* secure_system_resource = static_cast<KSecureSystemResource*>(m_system_resource);
|
||||||
|
return secure_system_resource->CalculateRequiredSecureMemorySize();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetRequiredSecureMemorySize() const {
|
||||||
|
if (m_system_resource->IsSecureResource()) {
|
||||||
|
auto* secure_system_resource = static_cast<KSecureSystemResource*>(m_system_resource);
|
||||||
|
return secure_system_resource->CalculateRequiredSecureMemorySize();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetTotalSystemResourceSize() const {
|
||||||
|
if (!this->IsDefaultApplicationSystemResource() && m_system_resource->IsSecureResource()) {
|
||||||
|
auto* secure_system_resource = static_cast<KSecureSystemResource*>(m_system_resource);
|
||||||
|
return secure_system_resource->GetSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetUsedSystemResourceSize() const {
|
||||||
|
if (!this->IsDefaultApplicationSystemResource() && m_system_resource->IsSecureResource()) {
|
||||||
|
auto* secure_system_resource = static_cast<KSecureSystemResource*>(m_system_resource);
|
||||||
|
return secure_system_resource->GetUsedSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetRunningThread(s32 core, KThread* thread, u64 idle_count, u64 switch_count) {
|
||||||
|
m_running_threads[core] = thread;
|
||||||
|
m_running_thread_idle_counts[core] = idle_count;
|
||||||
|
m_running_thread_switch_counts[core] = switch_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearRunningThread(KThread* thread) {
|
||||||
|
for (size_t i = 0; i < m_running_threads.size(); ++i) {
|
||||||
|
if (m_running_threads[i] == thread) {
|
||||||
|
m_running_threads[i] = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const KSystemResource& GetSystemResource() const {
|
||||||
|
return *m_system_resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
const KMemoryBlockSlabManager& GetMemoryBlockSlabManager() const {
|
||||||
|
return m_system_resource->GetMemoryBlockSlabManager();
|
||||||
|
}
|
||||||
|
const KBlockInfoManager& GetBlockInfoManager() const {
|
||||||
|
return m_system_resource->GetBlockInfoManager();
|
||||||
|
}
|
||||||
|
const KPageTableManager& GetPageTableManager() const {
|
||||||
|
return m_system_resource->GetPageTableManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
KThread* GetRunningThread(s32 core) const {
|
||||||
|
return m_running_threads[core];
|
||||||
|
}
|
||||||
|
u64 GetRunningThreadIdleCount(s32 core) const {
|
||||||
|
return m_running_thread_idle_counts[core];
|
||||||
|
}
|
||||||
|
u64 GetRunningThreadSwitchCount(s32 core) const {
|
||||||
|
return m_running_thread_switch_counts[core];
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegisterThread(KThread* thread);
|
||||||
|
void UnregisterThread(KThread* thread);
|
||||||
|
|
||||||
|
Result Run(s32 priority, size_t stack_size);
|
||||||
|
|
||||||
|
Result Reset();
|
||||||
|
|
||||||
|
void SetDebugBreak() {
|
||||||
|
if (m_state == State::RunningAttached) {
|
||||||
|
this->ChangeState(State::DebugBreak);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetAttached() {
|
||||||
|
if (m_state == State::DebugBreak) {
|
||||||
|
this->ChangeState(State::RunningAttached);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SetActivity(Svc::ProcessActivity activity);
|
||||||
|
|
||||||
|
void PinCurrentThread();
|
||||||
|
void UnpinCurrentThread();
|
||||||
|
void UnpinThread(KThread* thread);
|
||||||
|
|
||||||
|
void SignalConditionVariable(uintptr_t cv_key, int32_t count) {
|
||||||
|
return m_cond_var.Signal(cv_key, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result WaitConditionVariable(KProcessAddress address, uintptr_t cv_key, u32 tag, s64 ns) {
|
||||||
|
R_RETURN(m_cond_var.Wait(address, cv_key, tag, ns));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SignalAddressArbiter(uintptr_t address, Svc::SignalType signal_type, s32 value,
|
||||||
|
s32 count) {
|
||||||
|
R_RETURN(m_address_arbiter.SignalToAddress(address, signal_type, value, count));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result WaitAddressArbiter(uintptr_t address, Svc::ArbitrationType arb_type, s32 value,
|
||||||
|
s64 timeout) {
|
||||||
|
R_RETURN(m_address_arbiter.WaitForAddress(address, arb_type, value, timeout));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GetThreadList(s32* out_num_threads, KProcessAddress out_thread_ids, s32 max_out_count);
|
||||||
|
|
||||||
|
static void Switch(KProcess* cur_process, KProcess* next_process);
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Attempts to insert a watchpoint into a free slot. Returns false if none are available.
|
||||||
|
bool InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type);
|
||||||
|
|
||||||
|
// Attempts to remove the watchpoint specified by the given parameters.
|
||||||
|
bool RemoveWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type);
|
||||||
|
|
||||||
|
const std::array<DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>& GetWatchpoints() const {
|
||||||
|
return m_watchpoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
Result LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size,
|
||||||
|
bool is_hbl);
|
||||||
|
|
||||||
|
void LoadModule(CodeSet code_set, KProcessAddress base_addr);
|
||||||
|
|
||||||
|
Core::Memory::Memory& GetMemory() const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Overridden parent functions.
|
||||||
|
bool IsInitialized() const override {
|
||||||
|
return m_is_initialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PostDestroy(uintptr_t arg) {}
|
||||||
|
|
||||||
|
void Finalize() override;
|
||||||
|
|
||||||
|
u64 GetIdImpl() const {
|
||||||
|
return this->GetProcessId();
|
||||||
|
}
|
||||||
|
u64 GetId() const override {
|
||||||
|
return this->GetIdImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool IsSignaled() const override {
|
||||||
|
ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel));
|
||||||
|
return m_is_signaled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoWorkerTaskImpl();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void ChangeState(State new_state) {
|
||||||
|
if (m_state != new_state) {
|
||||||
|
m_state = new_state;
|
||||||
|
m_is_signaled = true;
|
||||||
|
this->NotifyAvailable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InitializeHandleTable(s32 size) {
|
||||||
|
// Try to initialize the handle table.
|
||||||
|
R_TRY(m_handle_table.Initialize(size));
|
||||||
|
|
||||||
|
// We succeeded, so note that we did.
|
||||||
|
m_is_handle_table_initialized = true;
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
void FinalizeHandleTable() {
|
void FinalizeHandleTable() {
|
||||||
// Finalize the table.
|
// Finalize the table.
|
||||||
m_handle_table.Finalize();
|
m_handle_table.Finalize();
|
||||||
@@ -438,118 +534,6 @@ private:
|
|||||||
// Note that the table is finalized.
|
// Note that the table is finalized.
|
||||||
m_is_handle_table_initialized = false;
|
m_is_handle_table_initialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChangeState(State new_state);
|
|
||||||
|
|
||||||
/// Allocates the main thread stack for the process, given the stack size in bytes.
|
|
||||||
Result AllocateMainThreadStack(std::size_t stack_size);
|
|
||||||
|
|
||||||
/// Memory manager for this process
|
|
||||||
KPageTable m_page_table;
|
|
||||||
|
|
||||||
/// Current status of the process
|
|
||||||
State m_state{};
|
|
||||||
|
|
||||||
/// The ID of this process
|
|
||||||
u64 m_process_id = 0;
|
|
||||||
|
|
||||||
/// Title ID corresponding to the process
|
|
||||||
u64 m_program_id = 0;
|
|
||||||
|
|
||||||
/// Specifies additional memory to be reserved for the process's memory management by the
|
|
||||||
/// system. When this is non-zero, secure memory is allocated and used for page table allocation
|
|
||||||
/// instead of using the normal global page tables/memory block management.
|
|
||||||
u32 m_system_resource_size = 0;
|
|
||||||
|
|
||||||
/// Resource limit descriptor for this process
|
|
||||||
KResourceLimit* m_resource_limit{};
|
|
||||||
|
|
||||||
KVirtualAddress m_system_resource_address{};
|
|
||||||
|
|
||||||
/// The ideal CPU core for this process, threads are scheduled on this core by default.
|
|
||||||
u8 m_ideal_core = 0;
|
|
||||||
|
|
||||||
/// Contains the parsed process capability descriptors.
|
|
||||||
ProcessCapabilities m_capabilities;
|
|
||||||
|
|
||||||
/// Whether or not this process is AArch64, or AArch32.
|
|
||||||
/// By default, we currently assume this is true, unless otherwise
|
|
||||||
/// specified by metadata provided to the process during loading.
|
|
||||||
bool m_is_64bit_process = true;
|
|
||||||
|
|
||||||
/// Total running time for the process in ticks.
|
|
||||||
std::atomic<u64> m_total_process_running_time_ticks = 0;
|
|
||||||
|
|
||||||
/// Per-process handle table for storing created object handles in.
|
|
||||||
KHandleTable m_handle_table;
|
|
||||||
|
|
||||||
/// Per-process address arbiter.
|
|
||||||
KAddressArbiter m_address_arbiter;
|
|
||||||
|
|
||||||
/// The per-process mutex lock instance used for handling various
|
|
||||||
/// forms of services, such as lock arbitration, and condition
|
|
||||||
/// variable related facilities.
|
|
||||||
KConditionVariable m_condition_var;
|
|
||||||
|
|
||||||
/// Address indicating the location of the process' dedicated TLS region.
|
|
||||||
KProcessAddress m_plr_address = 0;
|
|
||||||
|
|
||||||
/// Address indicating the location of the process's entry point.
|
|
||||||
KProcessAddress m_code_address = 0;
|
|
||||||
|
|
||||||
/// Random values for svcGetInfo RandomEntropy
|
|
||||||
std::array<u64, RANDOM_ENTROPY_SIZE> m_random_entropy{};
|
|
||||||
|
|
||||||
/// List of threads that are running with this process as their owner.
|
|
||||||
std::list<KThread*> m_thread_list;
|
|
||||||
|
|
||||||
/// List of shared memory that are running with this process as their owner.
|
|
||||||
std::list<KSharedMemoryInfo*> m_shared_memory_list;
|
|
||||||
|
|
||||||
/// Address of the top of the main thread's stack
|
|
||||||
KProcessAddress m_main_thread_stack_top{};
|
|
||||||
|
|
||||||
/// Size of the main thread's stack
|
|
||||||
std::size_t m_main_thread_stack_size{};
|
|
||||||
|
|
||||||
/// Memory usage capacity for the process
|
|
||||||
std::size_t m_memory_usage_capacity{};
|
|
||||||
|
|
||||||
/// Process total image size
|
|
||||||
std::size_t m_image_size{};
|
|
||||||
|
|
||||||
/// Schedule count of this process
|
|
||||||
s64 m_schedule_count{};
|
|
||||||
|
|
||||||
size_t m_memory_release_hint{};
|
|
||||||
|
|
||||||
std::string name{};
|
|
||||||
|
|
||||||
bool m_is_signaled{};
|
|
||||||
bool m_is_suspended{};
|
|
||||||
bool m_is_immortal{};
|
|
||||||
bool m_is_handle_table_initialized{};
|
|
||||||
bool m_is_initialized{};
|
|
||||||
bool m_is_hbl{};
|
|
||||||
|
|
||||||
std::atomic<u16> m_num_running_threads{};
|
|
||||||
|
|
||||||
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> m_running_threads{};
|
|
||||||
std::array<u64, Core::Hardware::NUM_CPU_CORES> m_running_thread_idle_counts{};
|
|
||||||
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> m_pinned_threads{};
|
|
||||||
std::array<DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS> m_watchpoints{};
|
|
||||||
std::map<KProcessAddress, u64> m_debug_page_refcounts;
|
|
||||||
|
|
||||||
KThread* m_exception_thread{};
|
|
||||||
|
|
||||||
KLightLock m_state_lock;
|
|
||||||
KLightLock m_list_lock;
|
|
||||||
|
|
||||||
using TLPTree =
|
|
||||||
Common::IntrusiveRedBlackTreeBaseTraits<KThreadLocalPage>::TreeType<KThreadLocalPage>;
|
|
||||||
using TLPIterator = TLPTree::iterator;
|
|
||||||
TLPTree m_fully_used_tlp_tree;
|
|
||||||
TLPTree m_partially_used_tlp_tree;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ u64 KScheduler::UpdateHighestPriorityThread(KThread* highest_thread) {
|
|||||||
if (m_state.should_count_idle) {
|
if (m_state.should_count_idle) {
|
||||||
if (highest_thread != nullptr) [[likely]] {
|
if (highest_thread != nullptr) [[likely]] {
|
||||||
if (KProcess* process = highest_thread->GetOwnerProcess(); process != nullptr) {
|
if (KProcess* process = highest_thread->GetOwnerProcess(); process != nullptr) {
|
||||||
process->SetRunningThread(m_core_id, highest_thread, m_state.idle_count);
|
process->SetRunningThread(m_core_id, highest_thread, m_state.idle_count, 0);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
m_state.idle_count++;
|
m_state.idle_count++;
|
||||||
@@ -356,7 +356,7 @@ void KScheduler::SwitchThread(KThread* next_thread) {
|
|||||||
const s64 tick_diff = cur_tick - prev_tick;
|
const s64 tick_diff = cur_tick - prev_tick;
|
||||||
cur_thread->AddCpuTime(m_core_id, tick_diff);
|
cur_thread->AddCpuTime(m_core_id, tick_diff);
|
||||||
if (cur_process != nullptr) {
|
if (cur_process != nullptr) {
|
||||||
cur_process->UpdateCPUTimeTicks(tick_diff);
|
cur_process->AddCpuTime(tick_diff);
|
||||||
}
|
}
|
||||||
m_last_context_switch_time = cur_tick;
|
m_last_context_switch_time = cur_tick;
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user