Compare commits
353 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff6a83e0fe | ||
|
|
8e7734bf40 | ||
|
|
0fb3773924 | ||
|
|
26d60014d0 | ||
|
|
ddbd1387aa | ||
|
|
9f1cf99ea0 | ||
|
|
0c19147e09 | ||
|
|
55b7d8e322 | ||
|
|
a208c7b059 | ||
|
|
0ec6cb942d | ||
|
|
9bd71f4064 | ||
|
|
08337a492d | ||
|
|
bcf58c8210 | ||
|
|
4f60818eae | ||
|
|
290b452ea1 | ||
|
|
1063e0be48 | ||
|
|
b04877dd95 | ||
|
|
31c80b8c6f | ||
|
|
c6d2af16b5 | ||
|
|
9cf8bcc75c | ||
|
|
96d07b4949 | ||
|
|
d6e5e053a6 | ||
|
|
c34249559d | ||
|
|
9c85bcbecc | ||
|
|
467ef54e2a | ||
|
|
8a9e834fc2 | ||
|
|
e0bddf8f07 | ||
|
|
5ce0e127da | ||
|
|
6af7bd02b2 | ||
|
|
7e62452ac8 | ||
|
|
5ec2fdceca | ||
|
|
214ef2dd8a | ||
|
|
c1017efb4a | ||
|
|
09a8e08109 | ||
|
|
ac14ef70b5 | ||
|
|
35abd560c4 | ||
|
|
e7530b341e | ||
|
|
01928b1d17 | ||
|
|
ff1201210c | ||
|
|
f90041a2d4 | ||
|
|
6c81332ca7 | ||
|
|
7133bfdd2c | ||
|
|
556c5df745 | ||
|
|
4530b58eb0 | ||
|
|
53f1560a24 | ||
|
|
c37b8a1028 | ||
|
|
278c38aa4a | ||
|
|
a4e2821530 | ||
|
|
a83f0f7154 | ||
|
|
dbc4c8e314 | ||
|
|
03790771a6 | ||
|
|
0377618288 | ||
|
|
247b1c14d2 | ||
|
|
ec0e644fb0 | ||
|
|
8f1300cd31 | ||
|
|
6219da627b | ||
|
|
5ce97bf0a0 | ||
|
|
b059db74c5 | ||
|
|
c51d9e0b0a | ||
|
|
d7f2078e7b | ||
|
|
b6bbc0d483 | ||
|
|
701b6c2fb8 | ||
|
|
d894818bb3 | ||
|
|
1744fa6ecf | ||
|
|
5096ed5e0a | ||
|
|
dfac2e2d25 | ||
|
|
2283fccc1b | ||
|
|
415dfb6cd3 | ||
|
|
8e545deab9 | ||
|
|
15a07f0282 | ||
|
|
28d3661a5c | ||
|
|
262a70223f | ||
|
|
535e50db1c | ||
|
|
e8bd9aed8b | ||
|
|
e6fb49fa4b | ||
|
|
5145133a60 | ||
|
|
4aec060f6d | ||
|
|
3173a53db9 | ||
|
|
d490f6dcf8 | ||
|
|
6dc35caad1 | ||
|
|
a2d40b8185 | ||
|
|
1ad4c98a5c | ||
|
|
c44db90ad1 | ||
|
|
531e797795 | ||
|
|
ded420cfeb | ||
|
|
63caeca6ba | ||
|
|
4fe05d7b42 | ||
|
|
63ebf2a328 | ||
|
|
d04120169d | ||
|
|
ad0d5818a6 | ||
|
|
49ec5784b0 | ||
|
|
4cdc701fd3 | ||
|
|
95fa89da27 | ||
|
|
530a5a1d09 | ||
|
|
1e964604bb | ||
|
|
c018769016 | ||
|
|
bc30aa8249 | ||
|
|
97e2604575 | ||
|
|
24900674b7 | ||
|
|
068b2ffbcc | ||
|
|
55fc808d64 | ||
|
|
03dda80e2b | ||
|
|
8ce31f1c8e | ||
|
|
01ea0f3c74 | ||
|
|
2cc42e40c5 | ||
|
|
638c892edf | ||
|
|
a60653dcd3 | ||
|
|
7feb490f83 | ||
|
|
9be819faaf | ||
|
|
33ed02a239 | ||
|
|
69b2dbdffd | ||
|
|
17ad323b36 | ||
|
|
c92a211e7c | ||
|
|
c5ca8675c8 | ||
|
|
aca6ab6417 | ||
|
|
0fbd7752c3 | ||
|
|
eaf0c4af11 | ||
|
|
94d77d0e39 | ||
|
|
824adb44fa | ||
|
|
d3e87d70ec | ||
|
|
7ff7e65926 | ||
|
|
d69421b1db | ||
|
|
e44622860a | ||
|
|
bb3dce9363 | ||
|
|
5ee669466f | ||
|
|
eeea426c74 | ||
|
|
ca30190fad | ||
|
|
54c1e0897d | ||
|
|
e6e61424d4 | ||
|
|
b3f68098d5 | ||
|
|
4930242c20 | ||
|
|
f234531f92 | ||
|
|
9b50b23a50 | ||
|
|
fb7dcbf7af | ||
|
|
a63dcb6d56 | ||
|
|
f8561c7a65 | ||
|
|
a527e5f0cd | ||
|
|
9cebde760f | ||
|
|
8a5794c4db | ||
|
|
cb7f2e5616 | ||
|
|
3b98fab850 | ||
|
|
b2fb5c60e1 | ||
|
|
7c26a9aefe | ||
|
|
e9a1f29e93 | ||
|
|
2f83d9a61b | ||
|
|
99fdfa1fcd | ||
|
|
65774084fd | ||
|
|
b83eb4dd18 | ||
|
|
8c016b02e7 | ||
|
|
52dae41d7f | ||
|
|
10d6e9f32b | ||
|
|
fb91647bca | ||
|
|
4eac8703d2 | ||
|
|
9032d21365 | ||
|
|
a32190d0c2 | ||
|
|
ab5995c7ae | ||
|
|
716285fab8 | ||
|
|
dde074eaab | ||
|
|
875183e7c5 | ||
|
|
a50133fc5e | ||
|
|
e274e38205 | ||
|
|
1d78190843 | ||
|
|
3ffbe50e7d | ||
|
|
fc5205fc84 | ||
|
|
a35717b245 | ||
|
|
1996cae9cb | ||
|
|
343eaecd38 | ||
|
|
8d0ba7ee49 | ||
|
|
80688362cf | ||
|
|
edbc505e52 | ||
|
|
10265ad0e4 | ||
|
|
28be8aec9a | ||
|
|
01f04fee32 | ||
|
|
f2e1441567 | ||
|
|
5762517728 | ||
|
|
a439867f2c | ||
|
|
43a29b5803 | ||
|
|
c17beefe3d | ||
|
|
3fb64da452 | ||
|
|
5872561077 | ||
|
|
541b4353e4 | ||
|
|
778e0f8ec1 | ||
|
|
538f097f97 | ||
|
|
afa4bcbb3b | ||
|
|
2e85ee250d | ||
|
|
cb48ed2e1a | ||
|
|
4aa8189328 | ||
|
|
ec514a4d1b | ||
|
|
e1f7938a3b | ||
|
|
ab102787fa | ||
|
|
8441094ba3 | ||
|
|
0687a8370d | ||
|
|
df9899eed6 | ||
|
|
824e53149d | ||
|
|
9761618a8d | ||
|
|
d3a4a192fe | ||
|
|
2f30c10584 | ||
|
|
c7553abe89 | ||
|
|
20eb368e14 | ||
|
|
f6566338eb | ||
|
|
2985e5e94c | ||
|
|
3b85ac2ac4 | ||
|
|
4735d18bb9 | ||
|
|
a9d24b0df3 | ||
|
|
5dae45b958 | ||
|
|
827dcad26e | ||
|
|
4439801c0f | ||
|
|
ad653550eb | ||
|
|
59173ff7a7 | ||
|
|
87cfe5b1da | ||
|
|
2490ffbbce | ||
|
|
daf5c5060b | ||
|
|
d1a7b2eca7 | ||
|
|
9c4c9f1e7d | ||
|
|
69ce5e41eb | ||
|
|
9d77ae39de | ||
|
|
de21c9e330 | ||
|
|
8f7eb194af | ||
|
|
cd7abba1a9 | ||
|
|
41e94b7b99 | ||
|
|
4bcc5bacff | ||
|
|
68ffac250a | ||
|
|
1f228c51ca | ||
|
|
4cf5b860bd | ||
|
|
47af34003b | ||
|
|
97415ad07a | ||
|
|
7b29a8ce4e | ||
|
|
a5ab85ac37 | ||
|
|
9d010be483 | ||
|
|
34a3ee1631 | ||
|
|
96c9e67b1b | ||
|
|
6faabd6d69 | ||
|
|
e7038344aa | ||
|
|
5213f70230 | ||
|
|
0639244d85 | ||
|
|
b8b5891585 | ||
|
|
394475c4e3 | ||
|
|
50ee9c46ab | ||
|
|
6ab839462c | ||
|
|
f8bfec3109 | ||
|
|
94da1e8a7e | ||
|
|
4a45012f35 | ||
|
|
5ad62e7bfc | ||
|
|
925671071c | ||
|
|
cd25817938 | ||
|
|
c7a7e47615 | ||
|
|
9b3af0027b | ||
|
|
ac8b1445ff | ||
|
|
52e9d7fa49 | ||
|
|
2910aa77b2 | ||
|
|
9e9341f4b4 | ||
|
|
ee9ebeeb80 | ||
|
|
e895ab7d6f | ||
|
|
4738e14cb0 | ||
|
|
55f556c53e | ||
|
|
ab65cb499d | ||
|
|
51fb0a6f96 | ||
|
|
09f7c355c6 | ||
|
|
bfa1644464 | ||
|
|
272bc4c3d6 | ||
|
|
1ba578c4aa | ||
|
|
d31dbb1bc1 | ||
|
|
aae399c1a8 | ||
|
|
1841ca4b9b | ||
|
|
71526ecfc7 | ||
|
|
ae876ed047 | ||
|
|
fb0b4c7e27 | ||
|
|
20245e660f | ||
|
|
ec19a85890 | ||
|
|
3de8e7a8f2 | ||
|
|
3d0394681c | ||
|
|
8e4c9c9852 | ||
|
|
2807a98168 | ||
|
|
1a5d4d7840 | ||
|
|
def03d4075 | ||
|
|
3acb265c9e | ||
|
|
728ee181eb | ||
|
|
93e20867b0 | ||
|
|
b1e27890e8 | ||
|
|
93109c870e | ||
|
|
65e0178cc0 | ||
|
|
9e520e8f12 | ||
|
|
1d162f28d1 | ||
|
|
7ed5dd0d62 | ||
|
|
701ef616b2 | ||
|
|
f7a008d77f | ||
|
|
6a19086001 | ||
|
|
a02566136c | ||
|
|
e7c33d1ad6 | ||
|
|
c9235764c7 | ||
|
|
6da91da08e | ||
|
|
24e1e17a8a | ||
|
|
b5b92fd1e5 | ||
|
|
0d62f30b00 | ||
|
|
51e8b2733c | ||
|
|
9cae3e6e90 | ||
|
|
0c24ae300c | ||
|
|
6686468df0 | ||
|
|
1c550ff954 | ||
|
|
c864f2c532 | ||
|
|
90f93a408a | ||
|
|
f3805376f7 | ||
|
|
9ca5e52f07 | ||
|
|
6be0975bf2 | ||
|
|
723e038dba | ||
|
|
aaccb21f81 | ||
|
|
df1a9d09a9 | ||
|
|
24d0cc3ab8 | ||
|
|
86212d4bcd | ||
|
|
f3345e84ad | ||
|
|
592a649918 | ||
|
|
0a91599aec | ||
|
|
cffa6f4e62 | ||
|
|
ed543c4d5c | ||
|
|
b53b50adec | ||
|
|
48cfc47050 | ||
|
|
90610bde9b | ||
|
|
9d8f793969 | ||
|
|
8378b8a61f | ||
|
|
fb54c38631 | ||
|
|
6269cd7f1d | ||
|
|
b0a3915351 | ||
|
|
eae9f2e440 | ||
|
|
d9a8060ce3 | ||
|
|
594973bdd2 | ||
|
|
51c13606d6 | ||
|
|
d25011c92f | ||
|
|
0bbf5e61f1 | ||
|
|
b8ffdbb167 | ||
|
|
21b40de318 | ||
|
|
70353649d7 | ||
|
|
95722823b9 | ||
|
|
c7325c6a4c | ||
|
|
b675c44e49 | ||
|
|
3c37d66c28 | ||
|
|
09722cb4a7 | ||
|
|
77564f987c | ||
|
|
ac265a72ce | ||
|
|
83227ad981 | ||
|
|
710aa22f7c | ||
|
|
6f1ad6aa9f | ||
|
|
06e3d3a658 | ||
|
|
757fd1e917 | ||
|
|
069afcc633 | ||
|
|
7ad63ea542 | ||
|
|
d28b942458 | ||
|
|
4a7fd91857 | ||
|
|
dcc0617cc2 | ||
|
|
c44ab0f8f6 | ||
|
|
bcd4e4f650 | ||
|
|
a994a40467 | ||
|
|
8e77d331be | ||
|
|
e6f9fe1f60 |
18
.ci/scripts/clang/docker.sh
Executable file
18
.ci/scripts/clang/docker.sh
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
# Exit on error, rather than continuing with the rest of the script.
|
||||
set -e
|
||||
|
||||
cd /yuzu
|
||||
|
||||
ccache -s
|
||||
|
||||
mkdir build || true && cd build
|
||||
cmake .. -DDISPLAY_VERSION=$1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/clang -DCMAKE_CXX_COMPILER=/usr/lib/ccache/clang++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DCMAKE_INSTALL_PREFIX="/usr"
|
||||
|
||||
make -j$(nproc)
|
||||
|
||||
ccache -s
|
||||
|
||||
ctest -VV -C Release
|
||||
|
||||
8
.ci/scripts/clang/exec.sh
Normal file
8
.ci/scripts/clang/exec.sh
Normal file
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
mkdir -p "ccache" || true
|
||||
chmod a+x ./.ci/scripts/clang/docker.sh
|
||||
# the UID for the container yuzu user is 1027
|
||||
sudo chown -R 1027 ./
|
||||
docker run -e ENABLE_COMPATIBILITY_REPORTING -e CCACHE_DIR=/yuzu/ccache -v $(pwd):/yuzu yuzuemu/build-environments:linux-fresh /bin/bash /yuzu/.ci/scripts/clang/docker.sh $1
|
||||
sudo chown -R $UID ./
|
||||
20
.ci/scripts/clang/upload.sh
Normal file
20
.ci/scripts/clang/upload.sh
Normal file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
. .ci/scripts/common/pre-upload.sh
|
||||
|
||||
REV_NAME="yuzu-linux-${GITDATE}-${GITREV}"
|
||||
ARCHIVE_NAME="${REV_NAME}.tar.xz"
|
||||
COMPRESSION_FLAGS="-cJvf"
|
||||
|
||||
if [ "${RELEASE_NAME}" = "mainline" ]; then
|
||||
DIR_NAME="${REV_NAME}"
|
||||
else
|
||||
DIR_NAME="${REV_NAME}_${RELEASE_NAME}"
|
||||
fi
|
||||
|
||||
mkdir "$DIR_NAME"
|
||||
|
||||
cp build/bin/yuzu-cmd "$DIR_NAME"
|
||||
cp build/bin/yuzu "$DIR_NAME"
|
||||
|
||||
. .ci/scripts/common/post-upload.sh
|
||||
@@ -8,7 +8,7 @@ steps:
|
||||
displayName: 'Install vulkan-sdk'
|
||||
- script: python -m pip install --upgrade pip conan
|
||||
displayName: 'Install conan'
|
||||
- script: refreshenv && mkdir build && cd build && cmake -G "Visual Studio 16 2019" -A x64 --config Release -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} .. && cd ..
|
||||
- script: refreshenv && mkdir build && cd build && cmake -G "Visual Studio 16 2019" -A x64 -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} .. && cmake --install . --config Release && cd ..
|
||||
displayName: 'Configure CMake'
|
||||
- task: MSBuild@1
|
||||
displayName: 'Build'
|
||||
|
||||
@@ -12,6 +12,9 @@ jobs:
|
||||
windows:
|
||||
BuildSuffix: 'windows-mingw'
|
||||
ScriptFolder: 'windows'
|
||||
clang:
|
||||
BuildSuffix: 'clang'
|
||||
ScriptFolder: 'clang'
|
||||
linux:
|
||||
BuildSuffix: 'linux'
|
||||
ScriptFolder: 'linux'
|
||||
@@ -24,4 +27,4 @@ jobs:
|
||||
parameters:
|
||||
artifactSource: 'false'
|
||||
cache: $(parameters.cache)
|
||||
version: $(parameters.version)
|
||||
version: $(parameters.version)
|
||||
|
||||
4
.gitmodules
vendored
4
.gitmodules
vendored
@@ -27,7 +27,7 @@
|
||||
url = https://github.com/ReinUsesLisp/sirit
|
||||
[submodule "mbedtls"]
|
||||
path = externals/mbedtls
|
||||
url = https://github.com/DarkLordZach/mbedtls
|
||||
url = https://github.com/yuzu-emu/mbedtls
|
||||
[submodule "libzip"]
|
||||
path = externals/libzip/libzip
|
||||
url = https://github.com/nih-at/libzip.git
|
||||
@@ -37,6 +37,6 @@
|
||||
[submodule "opus"]
|
||||
path = externals/opus/opus
|
||||
url = https://github.com/xiph/opus.git
|
||||
[submodule "externals/ffmpeg"]
|
||||
[submodule "ffmpeg"]
|
||||
path = externals/ffmpeg
|
||||
url = https://git.ffmpeg.org/ffmpeg.git
|
||||
|
||||
@@ -172,6 +172,8 @@ macro(yuzu_find_packages)
|
||||
"nlohmann_json 3.8 nlohmann_json/3.8.0"
|
||||
"ZLIB 1.2 zlib/1.2.11"
|
||||
"zstd 1.4 zstd/1.4.8"
|
||||
# can't use opus until AVX check is fixed: https://github.com/yuzu-emu/yuzu/pull/4068
|
||||
#"opus 1.3 opus/1.3.1"
|
||||
)
|
||||
|
||||
foreach(PACKAGE ${REQUIRED_LIBS})
|
||||
@@ -504,7 +506,7 @@ if (YUZU_USE_BUNDLED_FFMPEG)
|
||||
endif()
|
||||
else() # WIN32
|
||||
# Use yuzu FFmpeg binaries
|
||||
set(FFmpeg_EXT_NAME "ffmpeg-4.2.1")
|
||||
set(FFmpeg_EXT_NAME "ffmpeg-4.3.1")
|
||||
set(FFmpeg_PATH "${CMAKE_BINARY_DIR}/externals/${FFmpeg_EXT_NAME}")
|
||||
download_bundled_external("ffmpeg/" ${FFmpeg_EXT_NAME} "")
|
||||
set(FFmpeg_FOUND YES)
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
function(copy_yuzu_FFmpeg_deps target_dir)
|
||||
include(WindowsCopyFiles)
|
||||
set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/")
|
||||
windows_copy_files(${target_dir} ${FFmpeg_DLL_DIR} ${DLL_DEST}
|
||||
avcodec-58.dll
|
||||
avutil-56.dll
|
||||
swresample-3.dll
|
||||
swscale-5.dll
|
||||
)
|
||||
file(READ "${FFmpeg_PATH}/requirements.txt" FFmpeg_REQUIRED_DLLS)
|
||||
windows_copy_files(${target_dir} ${FFmpeg_DLL_DIR} ${DLL_DEST} ${FFmpeg_REQUIRED_DLLS})
|
||||
endfunction(copy_yuzu_FFmpeg_deps)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[Icon Theme]
|
||||
Name=colorful_dark
|
||||
Comment=Colorful theme (Dark style)
|
||||
Inherits=default
|
||||
Inherits=colorful
|
||||
Directories=16x16
|
||||
|
||||
[16x16]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[Icon Theme]
|
||||
Name=colorful_midnight_blue
|
||||
Comment=Colorful theme (Midnight Blue style)
|
||||
Inherits=default
|
||||
Inherits=colorful
|
||||
Directories=16x16
|
||||
|
||||
[16x16]
|
||||
|
||||
@@ -1257,10 +1257,6 @@ QComboBox::item:alternate {
|
||||
background: #19232D;
|
||||
}
|
||||
|
||||
QComboBox::item:checked {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
QComboBox::item:selected {
|
||||
border: 0px solid transparent;
|
||||
}
|
||||
|
||||
BIN
dist/yuzu.bmp
vendored
Normal file
BIN
dist/yuzu.bmp
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 256 KiB |
10
externals/CMakeLists.txt
vendored
10
externals/CMakeLists.txt
vendored
@@ -64,8 +64,8 @@ endif()
|
||||
add_subdirectory(sirit)
|
||||
|
||||
# libzip
|
||||
find_package(Libzip 1.5)
|
||||
if (NOT LIBZIP_FOUND)
|
||||
find_package(libzip 1.5)
|
||||
if (NOT libzip_FOUND)
|
||||
message(STATUS "libzip 1.5 or newer not found, falling back to externals")
|
||||
add_subdirectory(libzip EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
@@ -97,4 +97,8 @@ if (ENABLE_WEB_SERVICE)
|
||||
endif()
|
||||
|
||||
# Opus
|
||||
add_subdirectory(opus)
|
||||
find_package(opus 1.3)
|
||||
if (NOT opus_FOUND)
|
||||
message(STATUS "opus 1.3 or newer not found, falling back to externals")
|
||||
add_subdirectory(opus EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
|
||||
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: 8c09da666a...b2a4da5e65
72
externals/find-modules/FindLibzip.cmake
vendored
72
externals/find-modules/FindLibzip.cmake
vendored
@@ -1,72 +0,0 @@
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
pkg_check_modules(PC_LIBZIP QUIET libzip)
|
||||
|
||||
find_path(LIBZIP_INCLUDE_DIR
|
||||
NAMES zip.h
|
||||
PATHS ${PC_LIBZIP_INCLUDE_DIRS}
|
||||
"$ENV{LIB_DIR}/include"
|
||||
"$ENV{INCLUDE}"
|
||||
/usr/local/include
|
||||
/usr/include
|
||||
)
|
||||
find_path(LIBZIP_INCLUDE_DIR_ZIPCONF
|
||||
NAMES zipconf.h
|
||||
HINTS ${PC_LIBZIP_INCLUDE_DIRS}
|
||||
"$ENV{LIB_DIR}/include"
|
||||
"$ENV{LIB_DIR}/lib/libzip/include"
|
||||
"$ENV{LIB}/lib/libzip/include"
|
||||
/usr/local/lib/libzip/include
|
||||
/usr/lib/libzip/include
|
||||
/usr/local/include
|
||||
/usr/include
|
||||
"$ENV{INCLUDE}"
|
||||
)
|
||||
find_library(LIBZIP_LIBRARY
|
||||
NAMES zip
|
||||
PATHS ${PC_LIBZIP_LIBRARY_DIRS}
|
||||
"$ENV{LIB_DIR}/lib" "$ENV{LIB}" /usr/local/lib /usr/lib
|
||||
)
|
||||
|
||||
if (LIBZIP_INCLUDE_DIR_ZIPCONF)
|
||||
FILE(READ "${LIBZIP_INCLUDE_DIR_ZIPCONF}/zipconf.h" _LIBZIP_VERSION_CONTENTS)
|
||||
if (_LIBZIP_VERSION_CONTENTS)
|
||||
STRING(REGEX REPLACE ".*#define LIBZIP_VERSION \"([0-9.]+)\".*" "\\1" LIBZIP_VERSION "${_LIBZIP_VERSION_CONTENTS}")
|
||||
endif()
|
||||
unset(_LIBZIP_VERSION_CONTENTS)
|
||||
endif()
|
||||
|
||||
set(LIBZIP_VERSION ${LIBZIP_VERSION} CACHE STRING "Version number of libzip")
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Libzip
|
||||
FOUND_VAR LIBZIP_FOUND
|
||||
REQUIRED_VARS
|
||||
LIBZIP_LIBRARY
|
||||
LIBZIP_INCLUDE_DIR
|
||||
LIBZIP_INCLUDE_DIR_ZIPCONF
|
||||
LIBZIP_VERSION
|
||||
VERSION_VAR LIBZIP_VERSION
|
||||
)
|
||||
|
||||
if(LIBZIP_FOUND)
|
||||
set(LIBZIP_LIBRARIES ${LIBZIP_LIBRARY})
|
||||
set(LIBZIP_INCLUDE_DIRS ${LIBZIP_INCLUDE_DIR})
|
||||
set(LIBZIP_DEFINITIONS ${PC_LIBZIP_CFLAGS_OTHER})
|
||||
endif()
|
||||
|
||||
if(LIBZIP_FOUND AND NOT TARGET libzip::libzip)
|
||||
add_library(libzip::libzip UNKNOWN IMPORTED)
|
||||
set_target_properties(libzip::libzip PROPERTIES
|
||||
IMPORTED_LOCATION "${LIBZIP_LIBRARY}"
|
||||
INTERFACE_COMPILE_OPTIONS "${PC_LIBZIP_CFLAGS_OTHER}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${LIBZIP_INCLUDE_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(
|
||||
LIBZIP_INCLUDE_DIR
|
||||
LIBZIP_INCLUDE_DIR_ZIPCONF
|
||||
LIBZIP_LIBRARY
|
||||
LIBZIP_VERSION
|
||||
)
|
||||
72
externals/find-modules/Findlibzip.cmake
vendored
Normal file
72
externals/find-modules/Findlibzip.cmake
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
pkg_check_modules(PC_libzip QUIET libzip)
|
||||
|
||||
find_path(libzip_INCLUDE_DIR
|
||||
NAMES zip.h
|
||||
PATHS ${PC_libzip_INCLUDE_DIRS}
|
||||
"$ENV{LIB_DIR}/include"
|
||||
"$ENV{INCLUDE}"
|
||||
/usr/local/include
|
||||
/usr/include
|
||||
)
|
||||
find_path(libzip_INCLUDE_DIR_ZIPCONF
|
||||
NAMES zipconf.h
|
||||
HINTS ${PC_libzip_INCLUDE_DIRS}
|
||||
"$ENV{LIB_DIR}/include"
|
||||
"$ENV{LIB_DIR}/lib/libzip/include"
|
||||
"$ENV{LIB}/lib/libzip/include"
|
||||
/usr/local/lib/libzip/include
|
||||
/usr/lib/libzip/include
|
||||
/usr/local/include
|
||||
/usr/include
|
||||
"$ENV{INCLUDE}"
|
||||
)
|
||||
find_library(libzip_LIBRARY
|
||||
NAMES zip
|
||||
PATHS ${PC_libzip_LIBRARY_DIRS}
|
||||
"$ENV{LIB_DIR}/lib" "$ENV{LIB}" /usr/local/lib /usr/lib
|
||||
)
|
||||
|
||||
if (libzip_INCLUDE_DIR_ZIPCONF)
|
||||
FILE(READ "${libzip_INCLUDE_DIR_ZIPCONF}/zipconf.h" _libzip_VERSION_CONTENTS)
|
||||
if (_libzip_VERSION_CONTENTS)
|
||||
STRING(REGEX REPLACE ".*#define LIBZIP_VERSION \"([0-9.]+)\".*" "\\1" libzip_VERSION "${_libzip_VERSION_CONTENTS}")
|
||||
endif()
|
||||
unset(_libzip_VERSION_CONTENTS)
|
||||
endif()
|
||||
|
||||
set(libzip_VERSION ${libzip_VERSION} CACHE STRING "Version number of libzip")
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(libzip
|
||||
FOUND_VAR libzip_FOUND
|
||||
REQUIRED_VARS
|
||||
libzip_LIBRARY
|
||||
libzip_INCLUDE_DIR
|
||||
libzip_INCLUDE_DIR_ZIPCONF
|
||||
libzip_VERSION
|
||||
VERSION_VAR libzip_VERSION
|
||||
)
|
||||
|
||||
if(libzip_FOUND)
|
||||
set(libzip_LIBRARIES ${libzip_LIBRARY})
|
||||
set(libzip_INCLUDE_DIRS ${libzip_INCLUDE_DIR})
|
||||
set(libzip_DEFINITIONS ${PC_libzip_CFLAGS_OTHER})
|
||||
endif()
|
||||
|
||||
if(libzip_FOUND AND NOT TARGET libzip::libzip)
|
||||
add_library(libzip::libzip UNKNOWN IMPORTED)
|
||||
set_target_properties(libzip::libzip PROPERTIES
|
||||
IMPORTED_LOCATION "${libzip_LIBRARY}"
|
||||
INTERFACE_COMPILE_OPTIONS "${PC_libzip_CFLAGS_OTHER}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${libzip_INCLUDE_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(
|
||||
libzip_INCLUDE_DIR
|
||||
libzip_INCLUDE_DIR_ZIPCONF
|
||||
libzip_LIBRARY
|
||||
libzip_VERSION
|
||||
)
|
||||
2
externals/find-modules/Findopus.cmake
vendored
2
externals/find-modules/Findopus.cmake
vendored
@@ -28,7 +28,7 @@ if(opus_FOUND)
|
||||
endif()
|
||||
|
||||
if(opus_FOUND AND NOT TARGET Opus::Opus)
|
||||
add_library(Opus::Opus UNKNOWN IMPORTED)
|
||||
add_library(Opus::Opus UNKNOWN IMPORTED GLOBAL)
|
||||
set_target_properties(Opus::Opus PROPERTIES
|
||||
IMPORTED_LOCATION "${opus_LIBRARY}"
|
||||
INTERFACE_COMPILE_OPTIONS "${PC_opus_CFLAGS_OTHER}"
|
||||
|
||||
3
externals/glad/include/glad/glad.h
vendored
3
externals/glad/include/glad/glad.h
vendored
@@ -5156,6 +5156,9 @@ GLAPI PFNGLDEPTHRANGEARRAYVPROC glad_glDepthRangeArrayv;
|
||||
typedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDPROC)(GLuint index, GLdouble n, GLdouble f);
|
||||
GLAPI PFNGLDEPTHRANGEINDEXEDPROC glad_glDepthRangeIndexed;
|
||||
#define glDepthRangeIndexed glad_glDepthRangeIndexed
|
||||
typedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDDNVPROC)(GLuint index, GLdouble n, GLdouble f);
|
||||
GLAPI PFNGLDEPTHRANGEINDEXEDDNVPROC glad_glDepthRangeIndexeddNV;
|
||||
#define glDepthRangeIndexeddNV glad_glDepthRangeIndexeddNV
|
||||
typedef void (APIENTRYP PFNGLGETFLOATI_VPROC)(GLenum target, GLuint index, GLfloat *data);
|
||||
GLAPI PFNGLGETFLOATI_VPROC glad_glGetFloati_v;
|
||||
#define glGetFloati_v glad_glGetFloati_v
|
||||
|
||||
2
externals/glad/src/glad.c
vendored
2
externals/glad/src/glad.c
vendored
@@ -1044,6 +1044,7 @@ PFNGLDEPTHMASKPROC glad_glDepthMask = NULL;
|
||||
PFNGLDEPTHRANGEPROC glad_glDepthRange = NULL;
|
||||
PFNGLDEPTHRANGEARRAYVPROC glad_glDepthRangeArrayv = NULL;
|
||||
PFNGLDEPTHRANGEINDEXEDPROC glad_glDepthRangeIndexed = NULL;
|
||||
PFNGLDEPTHRANGEINDEXEDDNVPROC glad_glDepthRangeIndexeddNV = NULL;
|
||||
PFNGLDEPTHRANGEFPROC glad_glDepthRangef = NULL;
|
||||
PFNGLDETACHSHADERPROC glad_glDetachShader = NULL;
|
||||
PFNGLDISABLEPROC glad_glDisable = NULL;
|
||||
@@ -7971,6 +7972,7 @@ static void load_GL_NV_depth_buffer_float(GLADloadproc load) {
|
||||
glad_glDepthRangedNV = (PFNGLDEPTHRANGEDNVPROC)load("glDepthRangedNV");
|
||||
glad_glClearDepthdNV = (PFNGLCLEARDEPTHDNVPROC)load("glClearDepthdNV");
|
||||
glad_glDepthBoundsdNV = (PFNGLDEPTHBOUNDSDNVPROC)load("glDepthBoundsdNV");
|
||||
glad_glDepthRangeIndexeddNV = (PFNGLDEPTHRANGEINDEXEDDNVPROC)load("glDepthRangeIndexeddNV");
|
||||
}
|
||||
static void load_GL_NV_draw_texture(GLADloadproc load) {
|
||||
if(!GLAD_GL_NV_draw_texture) return;
|
||||
|
||||
5
externals/libusb/CMakeLists.txt
vendored
5
externals/libusb/CMakeLists.txt
vendored
@@ -1,3 +1,8 @@
|
||||
# Ensure libusb compiles with UTF-8 encoding on MSVC
|
||||
if(MSVC)
|
||||
add_compile_options(/utf-8)
|
||||
endif()
|
||||
|
||||
add_library(usb STATIC EXCLUDE_FROM_ALL
|
||||
libusb/libusb/core.c
|
||||
libusb/libusb/core.c
|
||||
|
||||
2
externals/mbedtls
vendored
2
externals/mbedtls
vendored
Submodule externals/mbedtls updated: a280e602f3...eac2416b8f
2
externals/opus/CMakeLists.txt
vendored
2
externals/opus/CMakeLists.txt
vendored
@@ -252,3 +252,5 @@ PRIVATE
|
||||
opus/silk/float
|
||||
opus/src
|
||||
)
|
||||
|
||||
add_library(Opus::Opus ALIAS opus)
|
||||
|
||||
@@ -27,6 +27,7 @@ if (MSVC)
|
||||
# /Zo - Enhanced debug info for optimized builds
|
||||
# /permissive- - Enables stricter C++ standards conformance checks
|
||||
# /EHsc - C++-only exception handling semantics
|
||||
# /utf-8 - Set source and execution character sets to UTF-8
|
||||
# /volatile:iso - Use strict standards-compliant volatile semantics.
|
||||
# /Zc:externConstexpr - Allow extern constexpr variables to have external linkage, like the standard mandates
|
||||
# /Zc:inline - Let codegen omit inline functions in object files
|
||||
@@ -38,6 +39,7 @@ if (MSVC)
|
||||
/permissive-
|
||||
/EHsc
|
||||
/std:c++latest
|
||||
/utf-8
|
||||
/volatile:iso
|
||||
/Zc:externConstexpr
|
||||
/Zc:inline
|
||||
|
||||
@@ -15,6 +15,8 @@ add_library(audio_core STATIC
|
||||
command_generator.cpp
|
||||
command_generator.h
|
||||
common.h
|
||||
delay_line.cpp
|
||||
delay_line.h
|
||||
effect_context.cpp
|
||||
effect_context.h
|
||||
info_updater.cpp
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cmath>
|
||||
#include <numbers>
|
||||
#include "audio_core/algorithm/interpolate.h"
|
||||
#include "audio_core/command_generator.h"
|
||||
#include "audio_core/effect_context.h"
|
||||
@@ -13,6 +15,20 @@ namespace AudioCore {
|
||||
namespace {
|
||||
constexpr std::size_t MIX_BUFFER_SIZE = 0x3f00;
|
||||
constexpr std::size_t SCALED_MIX_BUFFER_SIZE = MIX_BUFFER_SIZE << 15ULL;
|
||||
using DelayLineTimes = std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT>;
|
||||
|
||||
constexpr DelayLineTimes FDN_MIN_DELAY_LINE_TIMES{5.0f, 6.0f, 13.0f, 14.0f};
|
||||
constexpr DelayLineTimes FDN_MAX_DELAY_LINE_TIMES{45.704f, 82.782f, 149.94f, 271.58f};
|
||||
constexpr DelayLineTimes DECAY0_MAX_DELAY_LINE_TIMES{17.0f, 13.0f, 9.0f, 7.0f};
|
||||
constexpr DelayLineTimes DECAY1_MAX_DELAY_LINE_TIMES{19.0f, 11.0f, 10.0f, 6.0f};
|
||||
constexpr std::array<f32, AudioCommon::I3DL2REVERB_TAPS> EARLY_TAP_TIMES{
|
||||
0.017136f, 0.059154f, 0.161733f, 0.390186f, 0.425262f, 0.455411f, 0.689737f,
|
||||
0.745910f, 0.833844f, 0.859502f, 0.000000f, 0.075024f, 0.168788f, 0.299901f,
|
||||
0.337443f, 0.371903f, 0.599011f, 0.716741f, 0.817859f, 0.851664f};
|
||||
constexpr std::array<f32, AudioCommon::I3DL2REVERB_TAPS> EARLY_GAIN{
|
||||
0.67096f, 0.61027f, 1.0f, 0.35680f, 0.68361f, 0.65978f, 0.51939f,
|
||||
0.24712f, 0.45945f, 0.45021f, 0.64196f, 0.54879f, 0.92925f, 0.38270f,
|
||||
0.72867f, 0.69794f, 0.5464f, 0.24563f, 0.45214f, 0.44042f};
|
||||
|
||||
template <std::size_t N>
|
||||
void ApplyMix(s32* output, const s32* input, s32 gain, s32 sample_count) {
|
||||
@@ -65,6 +81,154 @@ s32 ApplyMixDepop(s32* output, s32 first_sample, s32 delta, s32 sample_count) {
|
||||
}
|
||||
}
|
||||
|
||||
float Pow10(float x) {
|
||||
if (x >= 0.0f) {
|
||||
return 1.0f;
|
||||
} else if (x <= -5.3f) {
|
||||
return 0.0f;
|
||||
}
|
||||
return std::pow(10.0f, x);
|
||||
}
|
||||
|
||||
float SinD(float degrees) {
|
||||
return std::sin(degrees * std::numbers::pi_v<float> / 180.0f);
|
||||
}
|
||||
|
||||
float CosD(float degrees) {
|
||||
return std::cos(degrees * std::numbers::pi_v<float> / 180.0f);
|
||||
}
|
||||
|
||||
float ToFloat(s32 sample) {
|
||||
return static_cast<float>(sample) / 65536.f;
|
||||
}
|
||||
|
||||
s32 ToS32(float sample) {
|
||||
constexpr auto min = -8388608.0f;
|
||||
constexpr auto max = 8388607.f;
|
||||
float rescaled_sample = sample * 65536.0f;
|
||||
if (rescaled_sample < min) {
|
||||
rescaled_sample = min;
|
||||
}
|
||||
if (rescaled_sample > max) {
|
||||
rescaled_sample = max;
|
||||
}
|
||||
return static_cast<s32>(rescaled_sample);
|
||||
}
|
||||
|
||||
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_1CH{0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_2CH{0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
|
||||
1, 1, 1, 0, 0, 0, 0, 1, 1, 1};
|
||||
|
||||
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_4CH{0, 0, 0, 1, 1, 1, 1, 2, 2, 2,
|
||||
1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
|
||||
|
||||
constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_6CH{4, 0, 0, 1, 1, 1, 1, 2, 2, 2,
|
||||
1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
|
||||
|
||||
template <std::size_t CHANNEL_COUNT>
|
||||
void ApplyReverbGeneric(I3dl2ReverbState& state,
|
||||
const std::array<const s32*, AudioCommon::MAX_CHANNEL_COUNT>& input,
|
||||
const std::array<s32*, AudioCommon::MAX_CHANNEL_COUNT>& output,
|
||||
s32 sample_count) {
|
||||
|
||||
auto GetTapLookup = []() {
|
||||
if constexpr (CHANNEL_COUNT == 1) {
|
||||
return REVERB_TAP_INDEX_1CH;
|
||||
} else if constexpr (CHANNEL_COUNT == 2) {
|
||||
return REVERB_TAP_INDEX_2CH;
|
||||
} else if constexpr (CHANNEL_COUNT == 4) {
|
||||
return REVERB_TAP_INDEX_4CH;
|
||||
} else if constexpr (CHANNEL_COUNT == 6) {
|
||||
return REVERB_TAP_INDEX_6CH;
|
||||
}
|
||||
};
|
||||
|
||||
const auto& tap_index_lut = GetTapLookup();
|
||||
for (s32 sample = 0; sample < sample_count; sample++) {
|
||||
std::array<f32, CHANNEL_COUNT> out_samples{};
|
||||
std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> fsamp{};
|
||||
std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> mixed{};
|
||||
std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> osamp{};
|
||||
|
||||
// Mix everything into a single sample
|
||||
s32 temp_mixed_sample = 0;
|
||||
for (std::size_t i = 0; i < CHANNEL_COUNT; i++) {
|
||||
temp_mixed_sample += input[i][sample];
|
||||
}
|
||||
const auto current_sample = ToFloat(temp_mixed_sample);
|
||||
const auto early_tap = state.early_delay_line.TapOut(state.early_to_late_taps);
|
||||
|
||||
for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_TAPS; i++) {
|
||||
const auto tapped_samp =
|
||||
state.early_delay_line.TapOut(state.early_tap_steps[i]) * EARLY_GAIN[i];
|
||||
out_samples[tap_index_lut[i]] += tapped_samp;
|
||||
|
||||
if constexpr (CHANNEL_COUNT == 6) {
|
||||
// handle lfe
|
||||
out_samples[5] += tapped_samp;
|
||||
}
|
||||
}
|
||||
|
||||
state.lowpass_0 = current_sample * state.lowpass_2 + state.lowpass_0 * state.lowpass_1;
|
||||
state.early_delay_line.Tick(state.lowpass_0);
|
||||
|
||||
for (std::size_t i = 0; i < CHANNEL_COUNT; i++) {
|
||||
out_samples[i] *= state.early_gain;
|
||||
}
|
||||
|
||||
// Two channel seems to apply a latet gain, we require to save this
|
||||
f32 filter{};
|
||||
for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
|
||||
filter = state.fdn_delay_line[i].GetOutputSample();
|
||||
const auto computed = filter * state.lpf_coefficients[0][i] + state.shelf_filter[i];
|
||||
state.shelf_filter[i] =
|
||||
filter * state.lpf_coefficients[1][i] + computed * state.lpf_coefficients[2][i];
|
||||
fsamp[i] = computed;
|
||||
}
|
||||
|
||||
// Mixing matrix
|
||||
mixed[0] = fsamp[1] + fsamp[2];
|
||||
mixed[1] = -fsamp[0] - fsamp[3];
|
||||
mixed[2] = fsamp[0] - fsamp[3];
|
||||
mixed[3] = fsamp[1] - fsamp[2];
|
||||
|
||||
if constexpr (CHANNEL_COUNT == 2) {
|
||||
for (auto& mix : mixed) {
|
||||
mix *= (filter * state.late_gain);
|
||||
}
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
|
||||
const auto late = early_tap * state.late_gain;
|
||||
osamp[i] = state.decay_delay_line0[i].Tick(late + mixed[i]);
|
||||
osamp[i] = state.decay_delay_line1[i].Tick(osamp[i]);
|
||||
state.fdn_delay_line[i].Tick(osamp[i]);
|
||||
}
|
||||
|
||||
if constexpr (CHANNEL_COUNT == 1) {
|
||||
output[0][sample] = ToS32(state.dry_gain * ToFloat(input[0][sample]) +
|
||||
(out_samples[0] + osamp[0] + osamp[1]));
|
||||
} else if constexpr (CHANNEL_COUNT == 2 || CHANNEL_COUNT == 4) {
|
||||
for (std::size_t i = 0; i < CHANNEL_COUNT; i++) {
|
||||
output[i][sample] =
|
||||
ToS32(state.dry_gain * ToFloat(input[i][sample]) + (out_samples[i] + osamp[i]));
|
||||
}
|
||||
} else if constexpr (CHANNEL_COUNT == 6) {
|
||||
const auto temp_center = state.center_delay_line.Tick(0.5f * (osamp[2] - osamp[3]));
|
||||
for (std::size_t i = 0; i < 4; i++) {
|
||||
output[i][sample] =
|
||||
ToS32(state.dry_gain * ToFloat(input[i][sample]) + (out_samples[i] + osamp[i]));
|
||||
}
|
||||
output[4][sample] =
|
||||
ToS32(state.dry_gain * ToFloat(input[4][sample]) + (out_samples[4] + temp_center));
|
||||
output[5][sample] =
|
||||
ToS32(state.dry_gain * ToFloat(input[5][sample]) + (out_samples[5] + osamp[3]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
CommandGenerator::CommandGenerator(AudioCommon::AudioRendererParameter& worker_params_,
|
||||
@@ -271,11 +435,10 @@ void CommandGenerator::GenerateBiquadFilterCommandForVoice(ServerVoiceInfo& voic
|
||||
}
|
||||
|
||||
// Generate biquad filter
|
||||
// GenerateBiquadFilterCommand(mix_buffer_count, biquad_filter,
|
||||
// dsp_state.biquad_filter_state,
|
||||
// mix_buffer_count + channel, mix_buffer_count +
|
||||
// channel, worker_params.sample_count,
|
||||
// voice_info.GetInParams().node_id);
|
||||
// GenerateBiquadFilterCommand(mix_buffer_count, biquad_filter,
|
||||
// dsp_state.biquad_filter_state,
|
||||
// mix_buffer_count + channel, mix_buffer_count + channel,
|
||||
// worker_params.sample_count, voice_info.GetInParams().node_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,21 +539,54 @@ void CommandGenerator::GenerateEffectCommand(ServerMixInfo& mix_info) {
|
||||
|
||||
void CommandGenerator::GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info,
|
||||
bool enabled) {
|
||||
if (!enabled) {
|
||||
auto* reverb = dynamic_cast<EffectI3dl2Reverb*>(info);
|
||||
const auto& params = reverb->GetParams();
|
||||
auto& state = reverb->GetState();
|
||||
const auto channel_count = params.channel_count;
|
||||
|
||||
if (channel_count != 1 && channel_count != 2 && channel_count != 4 && channel_count != 6) {
|
||||
return;
|
||||
}
|
||||
const auto& params = dynamic_cast<EffectI3dl2Reverb*>(info)->GetParams();
|
||||
const auto channel_count = params.channel_count;
|
||||
|
||||
std::array<const s32*, AudioCommon::MAX_CHANNEL_COUNT> input{};
|
||||
std::array<s32*, AudioCommon::MAX_CHANNEL_COUNT> output{};
|
||||
|
||||
const auto status = params.status;
|
||||
for (s32 i = 0; i < channel_count; i++) {
|
||||
// TODO(ogniK): Actually implement reverb
|
||||
/*
|
||||
if (params.input[i] != params.output[i]) {
|
||||
const auto* input = GetMixBuffer(mix_buffer_offset + params.input[i]);
|
||||
auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]);
|
||||
ApplyMix<1>(output, input, 32768, worker_params.sample_count);
|
||||
}*/
|
||||
auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]);
|
||||
std::memset(output, 0, worker_params.sample_count * sizeof(s32));
|
||||
input[i] = GetMixBuffer(mix_buffer_offset + params.input[i]);
|
||||
output[i] = GetMixBuffer(mix_buffer_offset + params.output[i]);
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
if (status == ParameterStatus::Initialized) {
|
||||
InitializeI3dl2Reverb(reverb->GetParams(), state, info->GetWorkBuffer());
|
||||
} else if (status == ParameterStatus::Updating) {
|
||||
UpdateI3dl2Reverb(reverb->GetParams(), state, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
switch (channel_count) {
|
||||
case 1:
|
||||
ApplyReverbGeneric<1>(state, input, output, worker_params.sample_count);
|
||||
break;
|
||||
case 2:
|
||||
ApplyReverbGeneric<2>(state, input, output, worker_params.sample_count);
|
||||
break;
|
||||
case 4:
|
||||
ApplyReverbGeneric<4>(state, input, output, worker_params.sample_count);
|
||||
break;
|
||||
case 6:
|
||||
ApplyReverbGeneric<6>(state, input, output, worker_params.sample_count);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
for (s32 i = 0; i < channel_count; i++) {
|
||||
// Only copy if the buffer input and output do not match!
|
||||
if ((mix_buffer_offset + params.input[i]) != (mix_buffer_offset + params.output[i])) {
|
||||
std::memcpy(output[i], input[i], worker_params.sample_count * sizeof(s32));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -528,6 +724,133 @@ s32 CommandGenerator::ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u3
|
||||
return sample_count;
|
||||
}
|
||||
|
||||
void CommandGenerator::InitializeI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state,
|
||||
std::vector<u8>& work_buffer) {
|
||||
// Reset state
|
||||
state.lowpass_0 = 0.0f;
|
||||
state.lowpass_1 = 0.0f;
|
||||
state.lowpass_2 = 0.0f;
|
||||
|
||||
state.early_delay_line.Reset();
|
||||
state.early_tap_steps.fill(0);
|
||||
state.early_gain = 0.0f;
|
||||
state.late_gain = 0.0f;
|
||||
state.early_to_late_taps = 0;
|
||||
for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
|
||||
state.fdn_delay_line[i].Reset();
|
||||
state.decay_delay_line0[i].Reset();
|
||||
state.decay_delay_line1[i].Reset();
|
||||
}
|
||||
state.last_reverb_echo = 0.0f;
|
||||
state.center_delay_line.Reset();
|
||||
for (auto& coef : state.lpf_coefficients) {
|
||||
coef.fill(0.0f);
|
||||
}
|
||||
state.shelf_filter.fill(0.0f);
|
||||
state.dry_gain = 0.0f;
|
||||
|
||||
const auto sample_rate = info.sample_rate / 1000;
|
||||
f32* work_buffer_ptr = reinterpret_cast<f32*>(work_buffer.data());
|
||||
|
||||
s32 delay_samples{};
|
||||
for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
|
||||
delay_samples =
|
||||
AudioCommon::CalculateDelaySamples(sample_rate, FDN_MAX_DELAY_LINE_TIMES[i]);
|
||||
state.fdn_delay_line[i].Initialize(delay_samples, work_buffer_ptr);
|
||||
work_buffer_ptr += delay_samples + 1;
|
||||
|
||||
delay_samples =
|
||||
AudioCommon::CalculateDelaySamples(sample_rate, DECAY0_MAX_DELAY_LINE_TIMES[i]);
|
||||
state.decay_delay_line0[i].Initialize(delay_samples, 0.0f, work_buffer_ptr);
|
||||
work_buffer_ptr += delay_samples + 1;
|
||||
|
||||
delay_samples =
|
||||
AudioCommon::CalculateDelaySamples(sample_rate, DECAY1_MAX_DELAY_LINE_TIMES[i]);
|
||||
state.decay_delay_line1[i].Initialize(delay_samples, 0.0f, work_buffer_ptr);
|
||||
work_buffer_ptr += delay_samples + 1;
|
||||
}
|
||||
delay_samples = AudioCommon::CalculateDelaySamples(sample_rate, 5.0f);
|
||||
state.center_delay_line.Initialize(delay_samples, work_buffer_ptr);
|
||||
work_buffer_ptr += delay_samples + 1;
|
||||
|
||||
delay_samples = AudioCommon::CalculateDelaySamples(sample_rate, 400.0f);
|
||||
state.early_delay_line.Initialize(delay_samples, work_buffer_ptr);
|
||||
|
||||
UpdateI3dl2Reverb(info, state, true);
|
||||
}
|
||||
|
||||
void CommandGenerator::UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state,
|
||||
bool should_clear) {
|
||||
|
||||
state.dry_gain = info.dry_gain;
|
||||
state.shelf_filter.fill(0.0f);
|
||||
state.lowpass_0 = 0.0f;
|
||||
state.early_gain = Pow10(std::min(info.room + info.reflection, 5000.0f) / 2000.0f);
|
||||
state.late_gain = Pow10(std::min(info.room + info.reverb, 5000.0f) / 2000.0f);
|
||||
|
||||
const auto sample_rate = info.sample_rate / 1000;
|
||||
const f32 hf_gain = Pow10(info.room_hf / 2000.0f);
|
||||
if (hf_gain >= 1.0f) {
|
||||
state.lowpass_2 = 1.0f;
|
||||
state.lowpass_1 = 0.0f;
|
||||
} else {
|
||||
const auto a = 1.0f - hf_gain;
|
||||
const auto b = 2.0f * (1.0f - hf_gain * CosD(256.0f * info.hf_reference /
|
||||
static_cast<f32>(info.sample_rate)));
|
||||
const auto c = std::sqrt(b * b - 4.0f * a * a);
|
||||
|
||||
state.lowpass_1 = (b - c) / (2.0f * a);
|
||||
state.lowpass_2 = 1.0f - state.lowpass_1;
|
||||
}
|
||||
state.early_to_late_taps = AudioCommon::CalculateDelaySamples(
|
||||
sample_rate, 1000.0f * (info.reflection_delay + info.reverb_delay));
|
||||
|
||||
state.last_reverb_echo = 0.6f * info.diffusion * 0.01f;
|
||||
for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
|
||||
const auto length =
|
||||
FDN_MIN_DELAY_LINE_TIMES[i] +
|
||||
(info.density / 100.0f) * (FDN_MAX_DELAY_LINE_TIMES[i] - FDN_MIN_DELAY_LINE_TIMES[i]);
|
||||
state.fdn_delay_line[i].SetDelay(AudioCommon::CalculateDelaySamples(sample_rate, length));
|
||||
|
||||
const auto delay_sample_counts = state.fdn_delay_line[i].GetDelay() +
|
||||
state.decay_delay_line0[i].GetDelay() +
|
||||
state.decay_delay_line1[i].GetDelay();
|
||||
|
||||
float a = (-60.0f * static_cast<f32>(delay_sample_counts)) /
|
||||
(info.decay_time * static_cast<f32>(info.sample_rate));
|
||||
float b = a / info.hf_decay_ratio;
|
||||
float c = CosD(128.0f * 0.5f * info.hf_reference / static_cast<f32>(info.sample_rate)) /
|
||||
SinD(128.0f * 0.5f * info.hf_reference / static_cast<f32>(info.sample_rate));
|
||||
float d = Pow10((b - a) / 40.0f);
|
||||
float e = Pow10((b + a) / 40.0f) * 0.7071f;
|
||||
|
||||
state.lpf_coefficients[0][i] = e * ((d * c) + 1.0f) / (c + d);
|
||||
state.lpf_coefficients[1][i] = e * (1.0f - (d * c)) / (c + d);
|
||||
state.lpf_coefficients[2][i] = (c - d) / (c + d);
|
||||
|
||||
state.decay_delay_line0[i].SetCoefficient(state.last_reverb_echo);
|
||||
state.decay_delay_line1[i].SetCoefficient(-0.9f * state.last_reverb_echo);
|
||||
}
|
||||
|
||||
if (should_clear) {
|
||||
for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
|
||||
state.fdn_delay_line[i].Clear();
|
||||
state.decay_delay_line0[i].Clear();
|
||||
state.decay_delay_line1[i].Clear();
|
||||
}
|
||||
state.early_delay_line.Clear();
|
||||
state.center_delay_line.Clear();
|
||||
}
|
||||
|
||||
const auto max_early_delay = state.early_delay_line.GetMaxDelay();
|
||||
const auto reflection_time = 1000.0f * (0.0098f * info.reverb_delay + 0.02f);
|
||||
for (std::size_t tap = 0; tap < AudioCommon::I3DL2REVERB_TAPS; tap++) {
|
||||
const auto length = AudioCommon::CalculateDelaySamples(
|
||||
sample_rate, 1000.0f * info.reflection_delay + reflection_time * EARLY_TAP_TIMES[tap]);
|
||||
state.early_tap_steps[tap] = std::min(length, max_early_delay);
|
||||
}
|
||||
}
|
||||
|
||||
void CommandGenerator::GenerateVolumeRampCommand(float last_volume, float current_volume,
|
||||
s32 channel, s32 node_id) {
|
||||
const auto last = static_cast<s32>(last_volume * 32768.0f);
|
||||
|
||||
@@ -21,6 +21,8 @@ class ServerMixInfo;
|
||||
class EffectContext;
|
||||
class EffectBase;
|
||||
struct AuxInfoDSP;
|
||||
struct I3dl2ReverbParams;
|
||||
struct I3dl2ReverbState;
|
||||
using MixVolumeBuffer = std::array<float, AudioCommon::MAX_MIX_BUFFERS>;
|
||||
|
||||
class CommandGenerator {
|
||||
@@ -80,6 +82,9 @@ private:
|
||||
s32 ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples, s32* out_data,
|
||||
u32 sample_count, u32 read_offset, u32 read_count);
|
||||
|
||||
void InitializeI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state,
|
||||
std::vector<u8>& work_buffer);
|
||||
void UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state, bool should_clear);
|
||||
// DSP Code
|
||||
s32 DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count,
|
||||
s32 channel, std::size_t mix_offset);
|
||||
|
||||
@@ -33,6 +33,29 @@ constexpr std::size_t TEMP_MIX_BASE_SIZE = 0x3f00; // TODO(ogniK): Work out this
|
||||
// and our const ends up being 0x3f04, the 4 bytes are most
|
||||
// likely the sample history
|
||||
constexpr std::size_t TOTAL_TEMP_MIX_SIZE = TEMP_MIX_BASE_SIZE + AudioCommon::MAX_SAMPLE_HISTORY;
|
||||
constexpr f32 I3DL2REVERB_MAX_LEVEL = 5000.0f;
|
||||
constexpr f32 I3DL2REVERB_MIN_REFLECTION_DURATION = 0.02f;
|
||||
constexpr std::size_t I3DL2REVERB_TAPS = 20;
|
||||
constexpr std::size_t I3DL2REVERB_DELAY_LINE_COUNT = 4;
|
||||
using Fractional = s32;
|
||||
|
||||
template <typename T>
|
||||
constexpr Fractional ToFractional(T x) {
|
||||
return static_cast<Fractional>(x * static_cast<T>(0x4000));
|
||||
}
|
||||
|
||||
constexpr Fractional MultiplyFractional(Fractional lhs, Fractional rhs) {
|
||||
return static_cast<Fractional>(static_cast<s64>(lhs) * rhs >> 14);
|
||||
}
|
||||
|
||||
constexpr s32 FractionalToFixed(Fractional x) {
|
||||
const auto s = x & (1 << 13);
|
||||
return static_cast<s32>(x >> 14) + s;
|
||||
}
|
||||
|
||||
constexpr s32 CalculateDelaySamples(s32 sample_rate_khz, float time) {
|
||||
return FractionalToFixed(MultiplyFractional(ToFractional(sample_rate_khz), ToFractional(time)));
|
||||
}
|
||||
|
||||
static constexpr u32 VersionFromRevision(u32_le rev) {
|
||||
// "REV7" -> 7
|
||||
|
||||
104
src/audio_core/delay_line.cpp
Normal file
104
src/audio_core/delay_line.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
#include <cstring>
|
||||
#include "audio_core/delay_line.h"
|
||||
|
||||
namespace AudioCore {
|
||||
DelayLineBase::DelayLineBase() = default;
|
||||
DelayLineBase::~DelayLineBase() = default;
|
||||
|
||||
void DelayLineBase::Initialize(s32 max_delay_, float* src_buffer) {
|
||||
buffer = src_buffer;
|
||||
buffer_end = buffer + max_delay_;
|
||||
max_delay = max_delay_;
|
||||
output = buffer;
|
||||
SetDelay(max_delay_);
|
||||
Clear();
|
||||
}
|
||||
|
||||
void DelayLineBase::SetDelay(s32 new_delay) {
|
||||
if (max_delay < new_delay) {
|
||||
return;
|
||||
}
|
||||
delay = new_delay;
|
||||
input = (buffer + ((output - buffer) + new_delay) % (max_delay + 1));
|
||||
}
|
||||
|
||||
s32 DelayLineBase::GetDelay() const {
|
||||
return delay;
|
||||
}
|
||||
|
||||
s32 DelayLineBase::GetMaxDelay() const {
|
||||
return max_delay;
|
||||
}
|
||||
|
||||
f32 DelayLineBase::TapOut(s32 last_sample) {
|
||||
const float* ptr = input - (last_sample + 1);
|
||||
if (ptr < buffer) {
|
||||
ptr += (max_delay + 1);
|
||||
}
|
||||
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
f32 DelayLineBase::Tick(f32 sample) {
|
||||
*(input++) = sample;
|
||||
const auto out_sample = *(output++);
|
||||
|
||||
if (buffer_end < input) {
|
||||
input = buffer;
|
||||
}
|
||||
|
||||
if (buffer_end < output) {
|
||||
output = buffer;
|
||||
}
|
||||
|
||||
return out_sample;
|
||||
}
|
||||
|
||||
float* DelayLineBase::GetInput() {
|
||||
return input;
|
||||
}
|
||||
|
||||
const float* DelayLineBase::GetInput() const {
|
||||
return input;
|
||||
}
|
||||
|
||||
f32 DelayLineBase::GetOutputSample() const {
|
||||
return *output;
|
||||
}
|
||||
|
||||
void DelayLineBase::Clear() {
|
||||
std::memset(buffer, 0, sizeof(float) * max_delay);
|
||||
}
|
||||
|
||||
void DelayLineBase::Reset() {
|
||||
buffer = nullptr;
|
||||
buffer_end = nullptr;
|
||||
max_delay = 0;
|
||||
input = nullptr;
|
||||
output = nullptr;
|
||||
delay = 0;
|
||||
}
|
||||
|
||||
DelayLineAllPass::DelayLineAllPass() = default;
|
||||
DelayLineAllPass::~DelayLineAllPass() = default;
|
||||
|
||||
void DelayLineAllPass::Initialize(u32 delay_, float coeffcient_, f32* src_buffer) {
|
||||
DelayLineBase::Initialize(delay_, src_buffer);
|
||||
SetCoefficient(coeffcient_);
|
||||
}
|
||||
|
||||
void DelayLineAllPass::SetCoefficient(float coeffcient_) {
|
||||
coefficient = coeffcient_;
|
||||
}
|
||||
|
||||
f32 DelayLineAllPass::Tick(f32 sample) {
|
||||
const auto temp = sample - coefficient * *output;
|
||||
return coefficient * temp + DelayLineBase::Tick(temp);
|
||||
}
|
||||
|
||||
void DelayLineAllPass::Reset() {
|
||||
coefficient = 0.0f;
|
||||
DelayLineBase::Reset();
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
46
src/audio_core/delay_line.h
Normal file
46
src/audio_core/delay_line.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
class DelayLineBase {
|
||||
public:
|
||||
DelayLineBase();
|
||||
~DelayLineBase();
|
||||
|
||||
void Initialize(s32 max_delay_, float* src_buffer);
|
||||
void SetDelay(s32 new_delay);
|
||||
s32 GetDelay() const;
|
||||
s32 GetMaxDelay() const;
|
||||
f32 TapOut(s32 last_sample);
|
||||
f32 Tick(f32 sample);
|
||||
float* GetInput();
|
||||
const float* GetInput() const;
|
||||
f32 GetOutputSample() const;
|
||||
void Clear();
|
||||
void Reset();
|
||||
|
||||
protected:
|
||||
float* buffer{nullptr};
|
||||
float* buffer_end{nullptr};
|
||||
s32 max_delay{};
|
||||
float* input{nullptr};
|
||||
float* output{nullptr};
|
||||
s32 delay{};
|
||||
};
|
||||
|
||||
class DelayLineAllPass final : public DelayLineBase {
|
||||
public:
|
||||
DelayLineAllPass();
|
||||
~DelayLineAllPass();
|
||||
|
||||
void Initialize(u32 delay, float coeffcient_, f32* src_buffer);
|
||||
void SetCoefficient(float coeffcient_);
|
||||
f32 Tick(f32 sample);
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
float coefficient{};
|
||||
};
|
||||
} // namespace AudioCore
|
||||
@@ -90,6 +90,14 @@ s32 EffectBase::GetProcessingOrder() const {
|
||||
return processing_order;
|
||||
}
|
||||
|
||||
std::vector<u8>& EffectBase::GetWorkBuffer() {
|
||||
return work_buffer;
|
||||
}
|
||||
|
||||
const std::vector<u8>& EffectBase::GetWorkBuffer() const {
|
||||
return work_buffer;
|
||||
}
|
||||
|
||||
EffectI3dl2Reverb::EffectI3dl2Reverb() : EffectGeneric(EffectType::I3dl2Reverb) {}
|
||||
EffectI3dl2Reverb::~EffectI3dl2Reverb() = default;
|
||||
|
||||
@@ -117,6 +125,12 @@ void EffectI3dl2Reverb::Update(EffectInfo::InParams& in_params) {
|
||||
usage = UsageState::Initialized;
|
||||
params.status = ParameterStatus::Initialized;
|
||||
skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
|
||||
if (!skipped) {
|
||||
auto& cur_work_buffer = GetWorkBuffer();
|
||||
// Has two buffers internally
|
||||
cur_work_buffer.resize(in_params.buffer_size * 2);
|
||||
std::fill(cur_work_buffer.begin(), cur_work_buffer.end(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,6 +143,14 @@ void EffectI3dl2Reverb::UpdateForCommandGeneration() {
|
||||
GetParams().status = ParameterStatus::Updated;
|
||||
}
|
||||
|
||||
I3dl2ReverbState& EffectI3dl2Reverb::GetState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
const I3dl2ReverbState& EffectI3dl2Reverb::GetState() const {
|
||||
return state;
|
||||
}
|
||||
|
||||
EffectBiquadFilter::EffectBiquadFilter() : EffectGeneric(EffectType::BiquadFilter) {}
|
||||
EffectBiquadFilter::~EffectBiquadFilter() = default;
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "audio_core/common.h"
|
||||
#include "audio_core/delay_line.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
@@ -194,6 +195,8 @@ public:
|
||||
[[nodiscard]] bool IsEnabled() const;
|
||||
[[nodiscard]] s32 GetMixID() const;
|
||||
[[nodiscard]] s32 GetProcessingOrder() const;
|
||||
[[nodiscard]] std::vector<u8>& GetWorkBuffer();
|
||||
[[nodiscard]] const std::vector<u8>& GetWorkBuffer() const;
|
||||
|
||||
protected:
|
||||
UsageState usage{UsageState::Invalid};
|
||||
@@ -201,6 +204,7 @@ protected:
|
||||
s32 mix_id{};
|
||||
s32 processing_order{};
|
||||
bool enabled = false;
|
||||
std::vector<u8> work_buffer{};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
@@ -212,7 +216,7 @@ public:
|
||||
return internal_params;
|
||||
}
|
||||
|
||||
const I3dl2ReverbParams& GetParams() const {
|
||||
const T& GetParams() const {
|
||||
return internal_params;
|
||||
}
|
||||
|
||||
@@ -229,6 +233,27 @@ public:
|
||||
void UpdateForCommandGeneration() override;
|
||||
};
|
||||
|
||||
struct I3dl2ReverbState {
|
||||
f32 lowpass_0{};
|
||||
f32 lowpass_1{};
|
||||
f32 lowpass_2{};
|
||||
|
||||
DelayLineBase early_delay_line{};
|
||||
std::array<u32, AudioCommon::I3DL2REVERB_TAPS> early_tap_steps{};
|
||||
f32 early_gain{};
|
||||
f32 late_gain{};
|
||||
|
||||
u32 early_to_late_taps{};
|
||||
std::array<DelayLineBase, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> fdn_delay_line{};
|
||||
std::array<DelayLineAllPass, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> decay_delay_line0{};
|
||||
std::array<DelayLineAllPass, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> decay_delay_line1{};
|
||||
f32 last_reverb_echo{};
|
||||
DelayLineBase center_delay_line{};
|
||||
std::array<std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT>, 3> lpf_coefficients{};
|
||||
std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> shelf_filter{};
|
||||
f32 dry_gain{};
|
||||
};
|
||||
|
||||
class EffectI3dl2Reverb : public EffectGeneric<I3dl2ReverbParams> {
|
||||
public:
|
||||
explicit EffectI3dl2Reverb();
|
||||
@@ -237,8 +262,12 @@ public:
|
||||
void Update(EffectInfo::InParams& in_params) override;
|
||||
void UpdateForCommandGeneration() override;
|
||||
|
||||
I3dl2ReverbState& GetState();
|
||||
const I3dl2ReverbState& GetState() const;
|
||||
|
||||
private:
|
||||
bool skipped = false;
|
||||
I3dl2ReverbState state{};
|
||||
};
|
||||
|
||||
class EffectBiquadFilter : public EffectGeneric<BiquadFilterParams> {
|
||||
|
||||
@@ -97,6 +97,7 @@ add_custom_command(OUTPUT scm_rev.cpp
|
||||
add_library(common STATIC
|
||||
algorithm.h
|
||||
alignment.h
|
||||
assert.cpp
|
||||
assert.h
|
||||
atomic_ops.h
|
||||
detached_tasks.cpp
|
||||
@@ -109,6 +110,7 @@ add_library(common STATIC
|
||||
cityhash.h
|
||||
common_funcs.h
|
||||
common_paths.h
|
||||
common_sizes.h
|
||||
common_types.h
|
||||
concepts.h
|
||||
div_ceil.h
|
||||
@@ -167,8 +169,8 @@ add_library(common STATIC
|
||||
threadsafe_queue.h
|
||||
time_zone.cpp
|
||||
time_zone.h
|
||||
tiny_mt.h
|
||||
tree.h
|
||||
uint128.cpp
|
||||
uint128.h
|
||||
uuid.cpp
|
||||
uuid.h
|
||||
|
||||
@@ -42,6 +42,11 @@ requires std::is_integral_v<T>[[nodiscard]] constexpr bool IsAligned(T value, si
|
||||
return (value & mask) == 0;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
requires std::is_integral_v<T>[[nodiscard]] constexpr T DivideUp(T x, U y) {
|
||||
return (x + (y - 1)) / y;
|
||||
}
|
||||
|
||||
template <typename T, size_t Align = 16>
|
||||
class AlignmentAllocator {
|
||||
public:
|
||||
|
||||
11
src/common/assert.cpp
Normal file
11
src/common/assert.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/assert.h"
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
|
||||
void assert_handle_failure() {
|
||||
Crash();
|
||||
}
|
||||
@@ -4,10 +4,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
// Sometimes we want to try to continue even after hitting an assert.
|
||||
// However touching this file yields a global recompilation as this header is included almost
|
||||
// everywhere. So let's just move the handling of the failed assert to a single cpp file.
|
||||
void assert_handle_failure();
|
||||
|
||||
// For asserts we'd like to keep all the junk executed when an assert happens away from the
|
||||
// important code in the function. One way of doing this is to put all the relevant code inside a
|
||||
// lambda and force the compiler to not inline it. Unfortunately, MSVC seems to have no syntax to
|
||||
@@ -17,15 +20,14 @@
|
||||
// enough for our purposes.
|
||||
template <typename Fn>
|
||||
#if defined(_MSC_VER)
|
||||
[[msvc::noinline, noreturn]]
|
||||
[[msvc::noinline]]
|
||||
#elif defined(__GNUC__)
|
||||
[[gnu::cold, gnu::noinline, noreturn]]
|
||||
[[gnu::cold, gnu::noinline]]
|
||||
#endif
|
||||
static void
|
||||
assert_noinline_call(const Fn& fn) {
|
||||
fn();
|
||||
Crash();
|
||||
exit(1); // Keeps GCC's mouth shut about this actually returning
|
||||
assert_handle_failure();
|
||||
}
|
||||
|
||||
#define ASSERT(_a_) \
|
||||
|
||||
@@ -28,8 +28,10 @@
|
||||
// compromising on hash quality.
|
||||
|
||||
#include <algorithm>
|
||||
#include <string.h> // for memcpy and memset
|
||||
#include "cityhash.h"
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
|
||||
#include "common/cityhash.h"
|
||||
#include "common/swap.h"
|
||||
|
||||
// #include "config.h"
|
||||
@@ -42,21 +44,17 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
typedef uint8_t uint8;
|
||||
typedef uint32_t uint32;
|
||||
typedef uint64_t uint64;
|
||||
|
||||
namespace Common {
|
||||
|
||||
static uint64 UNALIGNED_LOAD64(const char* p) {
|
||||
uint64 result;
|
||||
memcpy(&result, p, sizeof(result));
|
||||
static u64 unaligned_load64(const char* p) {
|
||||
u64 result;
|
||||
std::memcpy(&result, p, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint32 UNALIGNED_LOAD32(const char* p) {
|
||||
uint32 result;
|
||||
memcpy(&result, p, sizeof(result));
|
||||
static u32 unaligned_load32(const char* p) {
|
||||
u32 result;
|
||||
std::memcpy(&result, p, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -76,64 +74,64 @@ static uint32 UNALIGNED_LOAD32(const char* p) {
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static uint64 Fetch64(const char* p) {
|
||||
return uint64_in_expected_order(UNALIGNED_LOAD64(p));
|
||||
static u64 Fetch64(const char* p) {
|
||||
return uint64_in_expected_order(unaligned_load64(p));
|
||||
}
|
||||
|
||||
static uint32 Fetch32(const char* p) {
|
||||
return uint32_in_expected_order(UNALIGNED_LOAD32(p));
|
||||
static u32 Fetch32(const char* p) {
|
||||
return uint32_in_expected_order(unaligned_load32(p));
|
||||
}
|
||||
|
||||
// Some primes between 2^63 and 2^64 for various uses.
|
||||
static const uint64 k0 = 0xc3a5c85c97cb3127ULL;
|
||||
static const uint64 k1 = 0xb492b66fbe98f273ULL;
|
||||
static const uint64 k2 = 0x9ae16a3b2f90404fULL;
|
||||
static constexpr u64 k0 = 0xc3a5c85c97cb3127ULL;
|
||||
static constexpr u64 k1 = 0xb492b66fbe98f273ULL;
|
||||
static constexpr u64 k2 = 0x9ae16a3b2f90404fULL;
|
||||
|
||||
// Bitwise right rotate. Normally this will compile to a single
|
||||
// instruction, especially if the shift is a manifest constant.
|
||||
static uint64 Rotate(uint64 val, int shift) {
|
||||
static u64 Rotate(u64 val, int shift) {
|
||||
// Avoid shifting by 64: doing so yields an undefined result.
|
||||
return shift == 0 ? val : ((val >> shift) | (val << (64 - shift)));
|
||||
}
|
||||
|
||||
static uint64 ShiftMix(uint64 val) {
|
||||
static u64 ShiftMix(u64 val) {
|
||||
return val ^ (val >> 47);
|
||||
}
|
||||
|
||||
static uint64 HashLen16(uint64 u, uint64 v) {
|
||||
return Hash128to64(uint128(u, v));
|
||||
static u64 HashLen16(u64 u, u64 v) {
|
||||
return Hash128to64(u128{u, v});
|
||||
}
|
||||
|
||||
static uint64 HashLen16(uint64 u, uint64 v, uint64 mul) {
|
||||
static u64 HashLen16(u64 u, u64 v, u64 mul) {
|
||||
// Murmur-inspired hashing.
|
||||
uint64 a = (u ^ v) * mul;
|
||||
u64 a = (u ^ v) * mul;
|
||||
a ^= (a >> 47);
|
||||
uint64 b = (v ^ a) * mul;
|
||||
u64 b = (v ^ a) * mul;
|
||||
b ^= (b >> 47);
|
||||
b *= mul;
|
||||
return b;
|
||||
}
|
||||
|
||||
static uint64 HashLen0to16(const char* s, std::size_t len) {
|
||||
static u64 HashLen0to16(const char* s, size_t len) {
|
||||
if (len >= 8) {
|
||||
uint64 mul = k2 + len * 2;
|
||||
uint64 a = Fetch64(s) + k2;
|
||||
uint64 b = Fetch64(s + len - 8);
|
||||
uint64 c = Rotate(b, 37) * mul + a;
|
||||
uint64 d = (Rotate(a, 25) + b) * mul;
|
||||
u64 mul = k2 + len * 2;
|
||||
u64 a = Fetch64(s) + k2;
|
||||
u64 b = Fetch64(s + len - 8);
|
||||
u64 c = Rotate(b, 37) * mul + a;
|
||||
u64 d = (Rotate(a, 25) + b) * mul;
|
||||
return HashLen16(c, d, mul);
|
||||
}
|
||||
if (len >= 4) {
|
||||
uint64 mul = k2 + len * 2;
|
||||
uint64 a = Fetch32(s);
|
||||
u64 mul = k2 + len * 2;
|
||||
u64 a = Fetch32(s);
|
||||
return HashLen16(len + (a << 3), Fetch32(s + len - 4), mul);
|
||||
}
|
||||
if (len > 0) {
|
||||
uint8 a = s[0];
|
||||
uint8 b = s[len >> 1];
|
||||
uint8 c = s[len - 1];
|
||||
uint32 y = static_cast<uint32>(a) + (static_cast<uint32>(b) << 8);
|
||||
uint32 z = static_cast<uint32>(len) + (static_cast<uint32>(c) << 2);
|
||||
u8 a = s[0];
|
||||
u8 b = s[len >> 1];
|
||||
u8 c = s[len - 1];
|
||||
u32 y = static_cast<u32>(a) + (static_cast<u32>(b) << 8);
|
||||
u32 z = static_cast<u32>(len) + (static_cast<u32>(c) << 2);
|
||||
return ShiftMix(y * k2 ^ z * k0) * k2;
|
||||
}
|
||||
return k2;
|
||||
@@ -141,22 +139,21 @@ static uint64 HashLen0to16(const char* s, std::size_t len) {
|
||||
|
||||
// This probably works well for 16-byte strings as well, but it may be overkill
|
||||
// in that case.
|
||||
static uint64 HashLen17to32(const char* s, std::size_t len) {
|
||||
uint64 mul = k2 + len * 2;
|
||||
uint64 a = Fetch64(s) * k1;
|
||||
uint64 b = Fetch64(s + 8);
|
||||
uint64 c = Fetch64(s + len - 8) * mul;
|
||||
uint64 d = Fetch64(s + len - 16) * k2;
|
||||
static u64 HashLen17to32(const char* s, size_t len) {
|
||||
u64 mul = k2 + len * 2;
|
||||
u64 a = Fetch64(s) * k1;
|
||||
u64 b = Fetch64(s + 8);
|
||||
u64 c = Fetch64(s + len - 8) * mul;
|
||||
u64 d = Fetch64(s + len - 16) * k2;
|
||||
return HashLen16(Rotate(a + b, 43) + Rotate(c, 30) + d, a + Rotate(b + k2, 18) + c, mul);
|
||||
}
|
||||
|
||||
// Return a 16-byte hash for 48 bytes. Quick and dirty.
|
||||
// Callers do best to use "random-looking" values for a and b.
|
||||
static pair<uint64, uint64> WeakHashLen32WithSeeds(uint64 w, uint64 x, uint64 y, uint64 z, uint64 a,
|
||||
uint64 b) {
|
||||
static pair<u64, u64> WeakHashLen32WithSeeds(u64 w, u64 x, u64 y, u64 z, u64 a, u64 b) {
|
||||
a += w;
|
||||
b = Rotate(b + a + z, 21);
|
||||
uint64 c = a;
|
||||
u64 c = a;
|
||||
a += x;
|
||||
a += y;
|
||||
b += Rotate(a, 44);
|
||||
@@ -164,34 +161,34 @@ static pair<uint64, uint64> WeakHashLen32WithSeeds(uint64 w, uint64 x, uint64 y,
|
||||
}
|
||||
|
||||
// Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty.
|
||||
static pair<uint64, uint64> WeakHashLen32WithSeeds(const char* s, uint64 a, uint64 b) {
|
||||
static pair<u64, u64> WeakHashLen32WithSeeds(const char* s, u64 a, u64 b) {
|
||||
return WeakHashLen32WithSeeds(Fetch64(s), Fetch64(s + 8), Fetch64(s + 16), Fetch64(s + 24), a,
|
||||
b);
|
||||
}
|
||||
|
||||
// Return an 8-byte hash for 33 to 64 bytes.
|
||||
static uint64 HashLen33to64(const char* s, std::size_t len) {
|
||||
uint64 mul = k2 + len * 2;
|
||||
uint64 a = Fetch64(s) * k2;
|
||||
uint64 b = Fetch64(s + 8);
|
||||
uint64 c = Fetch64(s + len - 24);
|
||||
uint64 d = Fetch64(s + len - 32);
|
||||
uint64 e = Fetch64(s + 16) * k2;
|
||||
uint64 f = Fetch64(s + 24) * 9;
|
||||
uint64 g = Fetch64(s + len - 8);
|
||||
uint64 h = Fetch64(s + len - 16) * mul;
|
||||
uint64 u = Rotate(a + g, 43) + (Rotate(b, 30) + c) * 9;
|
||||
uint64 v = ((a + g) ^ d) + f + 1;
|
||||
uint64 w = swap64((u + v) * mul) + h;
|
||||
uint64 x = Rotate(e + f, 42) + c;
|
||||
uint64 y = (swap64((v + w) * mul) + g) * mul;
|
||||
uint64 z = e + f + c;
|
||||
static u64 HashLen33to64(const char* s, size_t len) {
|
||||
u64 mul = k2 + len * 2;
|
||||
u64 a = Fetch64(s) * k2;
|
||||
u64 b = Fetch64(s + 8);
|
||||
u64 c = Fetch64(s + len - 24);
|
||||
u64 d = Fetch64(s + len - 32);
|
||||
u64 e = Fetch64(s + 16) * k2;
|
||||
u64 f = Fetch64(s + 24) * 9;
|
||||
u64 g = Fetch64(s + len - 8);
|
||||
u64 h = Fetch64(s + len - 16) * mul;
|
||||
u64 u = Rotate(a + g, 43) + (Rotate(b, 30) + c) * 9;
|
||||
u64 v = ((a + g) ^ d) + f + 1;
|
||||
u64 w = swap64((u + v) * mul) + h;
|
||||
u64 x = Rotate(e + f, 42) + c;
|
||||
u64 y = (swap64((v + w) * mul) + g) * mul;
|
||||
u64 z = e + f + c;
|
||||
a = swap64((x + z) * mul + y) + b;
|
||||
b = ShiftMix((z + a) * mul + d + h) * mul;
|
||||
return b + x;
|
||||
}
|
||||
|
||||
uint64 CityHash64(const char* s, std::size_t len) {
|
||||
u64 CityHash64(const char* s, size_t len) {
|
||||
if (len <= 32) {
|
||||
if (len <= 16) {
|
||||
return HashLen0to16(s, len);
|
||||
@@ -204,15 +201,15 @@ uint64 CityHash64(const char* s, std::size_t len) {
|
||||
|
||||
// For strings over 64 bytes we hash the end first, and then as we
|
||||
// loop we keep 56 bytes of state: v, w, x, y, and z.
|
||||
uint64 x = Fetch64(s + len - 40);
|
||||
uint64 y = Fetch64(s + len - 16) + Fetch64(s + len - 56);
|
||||
uint64 z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24));
|
||||
pair<uint64, uint64> v = WeakHashLen32WithSeeds(s + len - 64, len, z);
|
||||
pair<uint64, uint64> w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x);
|
||||
u64 x = Fetch64(s + len - 40);
|
||||
u64 y = Fetch64(s + len - 16) + Fetch64(s + len - 56);
|
||||
u64 z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24));
|
||||
pair<u64, u64> v = WeakHashLen32WithSeeds(s + len - 64, len, z);
|
||||
pair<u64, u64> w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x);
|
||||
x = x * k1 + Fetch64(s);
|
||||
|
||||
// Decrease len to the nearest multiple of 64, and operate on 64-byte chunks.
|
||||
len = (len - 1) & ~static_cast<std::size_t>(63);
|
||||
len = (len - 1) & ~static_cast<size_t>(63);
|
||||
do {
|
||||
x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1;
|
||||
y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;
|
||||
@@ -229,21 +226,21 @@ uint64 CityHash64(const char* s, std::size_t len) {
|
||||
HashLen16(v.second, w.second) + x);
|
||||
}
|
||||
|
||||
uint64 CityHash64WithSeed(const char* s, std::size_t len, uint64 seed) {
|
||||
u64 CityHash64WithSeed(const char* s, size_t len, u64 seed) {
|
||||
return CityHash64WithSeeds(s, len, k2, seed);
|
||||
}
|
||||
|
||||
uint64 CityHash64WithSeeds(const char* s, std::size_t len, uint64 seed0, uint64 seed1) {
|
||||
u64 CityHash64WithSeeds(const char* s, size_t len, u64 seed0, u64 seed1) {
|
||||
return HashLen16(CityHash64(s, len) - seed0, seed1);
|
||||
}
|
||||
|
||||
// A subroutine for CityHash128(). Returns a decent 128-bit hash for strings
|
||||
// of any length representable in signed long. Based on City and Murmur.
|
||||
static uint128 CityMurmur(const char* s, std::size_t len, uint128 seed) {
|
||||
uint64 a = Uint128Low64(seed);
|
||||
uint64 b = Uint128High64(seed);
|
||||
uint64 c = 0;
|
||||
uint64 d = 0;
|
||||
static u128 CityMurmur(const char* s, size_t len, u128 seed) {
|
||||
u64 a = seed[0];
|
||||
u64 b = seed[1];
|
||||
u64 c = 0;
|
||||
u64 d = 0;
|
||||
signed long l = static_cast<long>(len) - 16;
|
||||
if (l <= 0) { // len <= 16
|
||||
a = ShiftMix(a * k1) * k1;
|
||||
@@ -266,20 +263,20 @@ static uint128 CityMurmur(const char* s, std::size_t len, uint128 seed) {
|
||||
}
|
||||
a = HashLen16(a, c);
|
||||
b = HashLen16(d, b);
|
||||
return uint128(a ^ b, HashLen16(b, a));
|
||||
return u128{a ^ b, HashLen16(b, a)};
|
||||
}
|
||||
|
||||
uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed) {
|
||||
u128 CityHash128WithSeed(const char* s, size_t len, u128 seed) {
|
||||
if (len < 128) {
|
||||
return CityMurmur(s, len, seed);
|
||||
}
|
||||
|
||||
// We expect len >= 128 to be the common case. Keep 56 bytes of state:
|
||||
// v, w, x, y, and z.
|
||||
pair<uint64, uint64> v, w;
|
||||
uint64 x = Uint128Low64(seed);
|
||||
uint64 y = Uint128High64(seed);
|
||||
uint64 z = len * k1;
|
||||
pair<u64, u64> v, w;
|
||||
u64 x = seed[0];
|
||||
u64 y = seed[1];
|
||||
u64 z = len * k1;
|
||||
v.first = Rotate(y ^ k1, 49) * k1 + Fetch64(s);
|
||||
v.second = Rotate(v.first, 42) * k1 + Fetch64(s + 8);
|
||||
w.first = Rotate(y + z, 35) * k1 + x;
|
||||
@@ -313,7 +310,7 @@ uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed) {
|
||||
w.first *= 9;
|
||||
v.first *= k0;
|
||||
// If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s.
|
||||
for (std::size_t tail_done = 0; tail_done < len;) {
|
||||
for (size_t tail_done = 0; tail_done < len;) {
|
||||
tail_done += 32;
|
||||
y = Rotate(x + y, 42) * k0 + v.second;
|
||||
w.first += Fetch64(s + len - tail_done + 16);
|
||||
@@ -328,13 +325,12 @@ uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed) {
|
||||
// different 56-byte-to-8-byte hashes to get a 16-byte final result.
|
||||
x = HashLen16(x, v.first);
|
||||
y = HashLen16(y + z, w.first);
|
||||
return uint128(HashLen16(x + v.second, w.second) + y, HashLen16(x + w.second, y + v.second));
|
||||
return u128{HashLen16(x + v.second, w.second) + y, HashLen16(x + w.second, y + v.second)};
|
||||
}
|
||||
|
||||
uint128 CityHash128(const char* s, std::size_t len) {
|
||||
return len >= 16
|
||||
? CityHash128WithSeed(s + 16, len - 16, uint128(Fetch64(s), Fetch64(s + 8) + k0))
|
||||
: CityHash128WithSeed(s, len, uint128(k0, k1));
|
||||
u128 CityHash128(const char* s, size_t len) {
|
||||
return len >= 16 ? CityHash128WithSeed(s + 16, len - 16, u128{Fetch64(s), Fetch64(s + 8) + k0})
|
||||
: CityHash128WithSeed(s, len, u128{k0, k1});
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -62,49 +62,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
using uint128 = std::pair<uint64_t, uint64_t>;
|
||||
|
||||
[[nodiscard]] inline uint64_t Uint128Low64(const uint128& x) {
|
||||
return x.first;
|
||||
}
|
||||
[[nodiscard]] inline uint64_t Uint128High64(const uint128& x) {
|
||||
return x.second;
|
||||
}
|
||||
|
||||
// Hash function for a byte array.
|
||||
[[nodiscard]] uint64_t CityHash64(const char* buf, std::size_t len);
|
||||
[[nodiscard]] u64 CityHash64(const char* buf, size_t len);
|
||||
|
||||
// Hash function for a byte array. For convenience, a 64-bit seed is also
|
||||
// hashed into the result.
|
||||
[[nodiscard]] uint64_t CityHash64WithSeed(const char* buf, std::size_t len, uint64_t seed);
|
||||
[[nodiscard]] u64 CityHash64WithSeed(const char* buf, size_t len, u64 seed);
|
||||
|
||||
// Hash function for a byte array. For convenience, two seeds are also
|
||||
// hashed into the result.
|
||||
[[nodiscard]] uint64_t CityHash64WithSeeds(const char* buf, std::size_t len, uint64_t seed0,
|
||||
uint64_t seed1);
|
||||
[[nodiscard]] u64 CityHash64WithSeeds(const char* buf, size_t len, u64 seed0, u64 seed1);
|
||||
|
||||
// Hash function for a byte array.
|
||||
[[nodiscard]] uint128 CityHash128(const char* s, std::size_t len);
|
||||
[[nodiscard]] u128 CityHash128(const char* s, size_t len);
|
||||
|
||||
// Hash function for a byte array. For convenience, a 128-bit seed is also
|
||||
// hashed into the result.
|
||||
[[nodiscard]] uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed);
|
||||
[[nodiscard]] u128 CityHash128WithSeed(const char* s, size_t len, u128 seed);
|
||||
|
||||
// Hash 128 input bits down to 64 bits of output.
|
||||
// This is intended to be a reasonably good hash function.
|
||||
[[nodiscard]] inline uint64_t Hash128to64(const uint128& x) {
|
||||
[[nodiscard]] inline u64 Hash128to64(const u128& x) {
|
||||
// Murmur-inspired hashing.
|
||||
const uint64_t kMul = 0x9ddfea08eb382d69ULL;
|
||||
uint64_t a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul;
|
||||
const u64 mul = 0x9ddfea08eb382d69ULL;
|
||||
u64 a = (x[0] ^ x[1]) * mul;
|
||||
a ^= (a >> 47);
|
||||
uint64_t b = (Uint128High64(x) ^ a) * kMul;
|
||||
u64 b = (x[1] ^ a) * mul;
|
||||
b ^= (b >> 47);
|
||||
b *= kMul;
|
||||
b *= mul;
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
@@ -52,9 +52,13 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
|
||||
// Generic function to get last error message.
|
||||
// Call directly after the command or use the error num.
|
||||
// This function might change the error code.
|
||||
// Defined in Misc.cpp.
|
||||
// Defined in misc.cpp.
|
||||
[[nodiscard]] std::string GetLastErrorMsg();
|
||||
|
||||
// Like GetLastErrorMsg(), but passing an explicit error code.
|
||||
// Defined in misc.cpp.
|
||||
[[nodiscard]] std::string NativeErrorToString(int e);
|
||||
|
||||
#define DECLARE_ENUM_FLAG_OPERATORS(type) \
|
||||
[[nodiscard]] constexpr type operator|(type a, type b) noexcept { \
|
||||
using T = std::underlying_type_t<type>; \
|
||||
|
||||
43
src/common/common_sizes.h
Normal file
43
src/common/common_sizes.h
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
enum : u64 {
|
||||
Size_1_KB = 0x400ULL,
|
||||
Size_64_KB = 64ULL * Size_1_KB,
|
||||
Size_128_KB = 128ULL * Size_1_KB,
|
||||
Size_1_MB = 0x100000ULL,
|
||||
Size_2_MB = 2ULL * Size_1_MB,
|
||||
Size_4_MB = 4ULL * Size_1_MB,
|
||||
Size_5_MB = 5ULL * Size_1_MB,
|
||||
Size_14_MB = 14ULL * Size_1_MB,
|
||||
Size_32_MB = 32ULL * Size_1_MB,
|
||||
Size_33_MB = 33ULL * Size_1_MB,
|
||||
Size_128_MB = 128ULL * Size_1_MB,
|
||||
Size_448_MB = 448ULL * Size_1_MB,
|
||||
Size_507_MB = 507ULL * Size_1_MB,
|
||||
Size_562_MB = 562ULL * Size_1_MB,
|
||||
Size_1554_MB = 1554ULL * Size_1_MB,
|
||||
Size_2048_MB = 2048ULL * Size_1_MB,
|
||||
Size_2193_MB = 2193ULL * Size_1_MB,
|
||||
Size_3285_MB = 3285ULL * Size_1_MB,
|
||||
Size_4916_MB = 4916ULL * Size_1_MB,
|
||||
Size_1_GB = 0x40000000ULL,
|
||||
Size_2_GB = 2ULL * Size_1_GB,
|
||||
Size_4_GB = 4ULL * Size_1_GB,
|
||||
Size_6_GB = 6ULL * Size_1_GB,
|
||||
Size_8_GB = 8ULL * Size_1_GB,
|
||||
Size_64_GB = 64ULL * Size_1_GB,
|
||||
Size_512_GB = 512ULL * Size_1_GB,
|
||||
Size_Invalid = std::numeric_limits<u64>::max(),
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
namespace Common {
|
||||
|
||||
constexpr std::size_t default_stack_size = 256 * 1024;
|
||||
constexpr std::size_t default_stack_size = 512 * 1024;
|
||||
|
||||
struct Fiber::FiberImpl {
|
||||
FiberImpl() : stack{default_stack_size}, rewind_stack{default_stack_size} {}
|
||||
@@ -116,16 +116,19 @@ void Fiber::Rewind() {
|
||||
boost::context::detail::jump_fcontext(impl->rewind_context, this);
|
||||
}
|
||||
|
||||
void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
|
||||
ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
|
||||
ASSERT_MSG(to != nullptr, "Next fiber is null!");
|
||||
to->impl->guard.lock();
|
||||
to->impl->previous_fiber = from;
|
||||
auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get());
|
||||
ASSERT(from->impl->previous_fiber != nullptr);
|
||||
from->impl->previous_fiber->impl->context = transfer.fctx;
|
||||
from->impl->previous_fiber->impl->guard.unlock();
|
||||
from->impl->previous_fiber.reset();
|
||||
void Fiber::YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to) {
|
||||
to.impl->guard.lock();
|
||||
to.impl->previous_fiber = weak_from.lock();
|
||||
|
||||
auto transfer = boost::context::detail::jump_fcontext(to.impl->context, &to);
|
||||
|
||||
// "from" might no longer be valid if the thread was killed
|
||||
if (auto from = weak_from.lock()) {
|
||||
ASSERT(from->impl->previous_fiber != nullptr);
|
||||
from->impl->previous_fiber->impl->context = transfer.fctx;
|
||||
from->impl->previous_fiber->impl->guard.unlock();
|
||||
from->impl->previous_fiber.reset();
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
|
||||
|
||||
@@ -41,7 +41,7 @@ public:
|
||||
|
||||
/// Yields control from Fiber 'from' to Fiber 'to'
|
||||
/// Fiber 'from' must be the currently running fiber.
|
||||
static void YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to);
|
||||
static void YieldTo(std::weak_ptr<Fiber> weak_from, Fiber& to);
|
||||
[[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();
|
||||
|
||||
void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param);
|
||||
|
||||
@@ -212,6 +212,7 @@ void DebuggerBackend::Write(const Entry& entry) {
|
||||
SUB(Service, ARP) \
|
||||
SUB(Service, BCAT) \
|
||||
SUB(Service, BPC) \
|
||||
SUB(Service, BGTC) \
|
||||
SUB(Service, BTDRV) \
|
||||
SUB(Service, BTM) \
|
||||
SUB(Service, Capture) \
|
||||
|
||||
@@ -66,6 +66,7 @@ enum class Class : ClassType {
|
||||
Service_ARP, ///< The ARP service
|
||||
Service_Audio, ///< The Audio (Audio control) service
|
||||
Service_BCAT, ///< The BCAT service
|
||||
Service_BGTC, ///< The BGTC (Background Task Controller) service
|
||||
Service_BPC, ///< The BPC service
|
||||
Service_BTDRV, ///< The Bluetooth driver service
|
||||
Service_BTM, ///< The BTM service
|
||||
|
||||
@@ -12,27 +12,41 @@
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
|
||||
// Generic function to get last error message.
|
||||
// Call directly after the command or use the error num.
|
||||
// This function might change the error code.
|
||||
std::string GetLastErrorMsg() {
|
||||
static constexpr std::size_t buff_size = 255;
|
||||
char err_str[buff_size];
|
||||
|
||||
std::string NativeErrorToString(int e) {
|
||||
#ifdef _WIN32
|
||||
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err_str, buff_size, nullptr);
|
||||
return std::string(err_str, buff_size);
|
||||
#elif defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600))
|
||||
LPSTR err_str;
|
||||
|
||||
DWORD res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
nullptr, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
reinterpret_cast<LPSTR>(&err_str), 1, nullptr);
|
||||
if (!res) {
|
||||
return "(FormatMessageA failed to format error)";
|
||||
}
|
||||
std::string ret(err_str);
|
||||
LocalFree(err_str);
|
||||
return ret;
|
||||
#else
|
||||
char err_str[255];
|
||||
#if defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600))
|
||||
// Thread safe (GNU-specific)
|
||||
const char* str = strerror_r(errno, err_str, buff_size);
|
||||
const char* str = strerror_r(e, err_str, sizeof(err_str));
|
||||
return std::string(str);
|
||||
#else
|
||||
// Thread safe (XSI-compliant)
|
||||
const int success = strerror_r(errno, err_str, buff_size);
|
||||
if (success != 0) {
|
||||
return {};
|
||||
int second_err = strerror_r(e, err_str, sizeof(err_str));
|
||||
if (second_err != 0) {
|
||||
return "(strerror_r failed to format error)";
|
||||
}
|
||||
return std::string(err_str);
|
||||
#endif // GLIBC etc.
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
std::string GetLastErrorMsg() {
|
||||
#ifdef _WIN32
|
||||
return NativeErrorToString(GetLastError());
|
||||
#else
|
||||
return NativeErrorToString(errno);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Common {
|
||||
|
||||
/// Configure platform specific flags for Nvidia's driver
|
||||
|
||||
@@ -83,11 +83,15 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
T PopWait() {
|
||||
void Wait() {
|
||||
if (Empty()) {
|
||||
std::unique_lock lock{cv_mutex};
|
||||
cv.wait(lock, [this]() { return !Empty(); });
|
||||
}
|
||||
}
|
||||
|
||||
T PopWait() {
|
||||
Wait();
|
||||
T t;
|
||||
Pop(t);
|
||||
return t;
|
||||
@@ -156,6 +160,10 @@ public:
|
||||
return spsc_queue.Pop(t);
|
||||
}
|
||||
|
||||
void Wait() {
|
||||
spsc_queue.Wait();
|
||||
}
|
||||
|
||||
T PopWait() {
|
||||
return spsc_queue.PopWait();
|
||||
}
|
||||
|
||||
250
src/common/tiny_mt.h
Normal file
250
src/common/tiny_mt.h
Normal file
@@ -0,0 +1,250 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
// Implementation of TinyMT (mersenne twister RNG).
|
||||
// Like Nintendo, we will use the sample parameters.
|
||||
class TinyMT {
|
||||
public:
|
||||
static constexpr std::size_t NumStateWords = 4;
|
||||
|
||||
struct State {
|
||||
std::array<u32, NumStateWords> data{};
|
||||
};
|
||||
|
||||
private:
|
||||
static constexpr u32 ParamMat1 = 0x8F7011EE;
|
||||
static constexpr u32 ParamMat2 = 0xFC78FF1F;
|
||||
static constexpr u32 ParamTmat = 0x3793FDFF;
|
||||
|
||||
static constexpr u32 ParamMult = 0x6C078965;
|
||||
static constexpr u32 ParamPlus = 0x0019660D;
|
||||
static constexpr u32 ParamXor = 0x5D588B65;
|
||||
|
||||
static constexpr u32 TopBitmask = 0x7FFFFFFF;
|
||||
|
||||
static constexpr int MinimumInitIterations = 8;
|
||||
static constexpr int NumDiscardedInitOutputs = 8;
|
||||
|
||||
static constexpr u32 XorByShifted27(u32 value) {
|
||||
return value ^ (value >> 27);
|
||||
}
|
||||
|
||||
static constexpr u32 XorByShifted30(u32 value) {
|
||||
return value ^ (value >> 30);
|
||||
}
|
||||
|
||||
private:
|
||||
State state{};
|
||||
|
||||
private:
|
||||
// Internal API.
|
||||
void FinalizeInitialization() {
|
||||
const u32 state0 = this->state.data[0] & TopBitmask;
|
||||
const u32 state1 = this->state.data[1];
|
||||
const u32 state2 = this->state.data[2];
|
||||
const u32 state3 = this->state.data[3];
|
||||
|
||||
if (state0 == 0 && state1 == 0 && state2 == 0 && state3 == 0) {
|
||||
this->state.data[0] = 'T';
|
||||
this->state.data[1] = 'I';
|
||||
this->state.data[2] = 'N';
|
||||
this->state.data[3] = 'Y';
|
||||
}
|
||||
|
||||
for (int i = 0; i < NumDiscardedInitOutputs; i++) {
|
||||
this->GenerateRandomU32();
|
||||
}
|
||||
}
|
||||
|
||||
u32 GenerateRandomU24() {
|
||||
return (this->GenerateRandomU32() >> 8);
|
||||
}
|
||||
|
||||
static void GenerateInitialValuePlus(TinyMT::State* state, int index, u32 value) {
|
||||
u32& state0 = state->data[(index + 0) % NumStateWords];
|
||||
u32& state1 = state->data[(index + 1) % NumStateWords];
|
||||
u32& state2 = state->data[(index + 2) % NumStateWords];
|
||||
u32& state3 = state->data[(index + 3) % NumStateWords];
|
||||
|
||||
const u32 x = XorByShifted27(state0 ^ state1 ^ state3) * ParamPlus;
|
||||
const u32 y = x + index + value;
|
||||
|
||||
state0 = y;
|
||||
state1 += x;
|
||||
state2 += y;
|
||||
}
|
||||
|
||||
static void GenerateInitialValueXor(TinyMT::State* state, int index) {
|
||||
u32& state0 = state->data[(index + 0) % NumStateWords];
|
||||
u32& state1 = state->data[(index + 1) % NumStateWords];
|
||||
u32& state2 = state->data[(index + 2) % NumStateWords];
|
||||
u32& state3 = state->data[(index + 3) % NumStateWords];
|
||||
|
||||
const u32 x = XorByShifted27(state0 + state1 + state3) * ParamXor;
|
||||
const u32 y = x - index;
|
||||
|
||||
state0 = y;
|
||||
state1 ^= x;
|
||||
state2 ^= y;
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr TinyMT() = default;
|
||||
|
||||
// Public API.
|
||||
|
||||
// Initialization.
|
||||
void Initialize(u32 seed) {
|
||||
this->state.data[0] = seed;
|
||||
this->state.data[1] = ParamMat1;
|
||||
this->state.data[2] = ParamMat2;
|
||||
this->state.data[3] = ParamTmat;
|
||||
|
||||
for (int i = 1; i < MinimumInitIterations; i++) {
|
||||
const u32 mixed = XorByShifted30(this->state.data[(i - 1) % NumStateWords]);
|
||||
this->state.data[i % NumStateWords] ^= mixed * ParamMult + i;
|
||||
}
|
||||
|
||||
this->FinalizeInitialization();
|
||||
}
|
||||
|
||||
void Initialize(const u32* seed, int seed_count) {
|
||||
this->state.data[0] = 0;
|
||||
this->state.data[1] = ParamMat1;
|
||||
this->state.data[2] = ParamMat2;
|
||||
this->state.data[3] = ParamTmat;
|
||||
|
||||
{
|
||||
const int num_init_iterations = std::max(seed_count + 1, MinimumInitIterations) - 1;
|
||||
|
||||
GenerateInitialValuePlus(&this->state, 0, seed_count);
|
||||
|
||||
for (int i = 0; i < num_init_iterations; i++) {
|
||||
GenerateInitialValuePlus(&this->state, (i + 1) % NumStateWords,
|
||||
(i < seed_count) ? seed[i] : 0);
|
||||
}
|
||||
|
||||
for (int i = 0; i < static_cast<int>(NumStateWords); i++) {
|
||||
GenerateInitialValueXor(&this->state,
|
||||
(i + 1 + num_init_iterations) % NumStateWords);
|
||||
}
|
||||
}
|
||||
|
||||
this->FinalizeInitialization();
|
||||
}
|
||||
|
||||
// State management.
|
||||
void GetState(TinyMT::State& out) const {
|
||||
out.data = this->state.data;
|
||||
}
|
||||
|
||||
void SetState(const TinyMT::State& state_) {
|
||||
this->state.data = state_.data;
|
||||
}
|
||||
|
||||
// Random generation.
|
||||
void GenerateRandomBytes(void* dst, std::size_t size) {
|
||||
const uintptr_t start = reinterpret_cast<uintptr_t>(dst);
|
||||
const uintptr_t end = start + size;
|
||||
const uintptr_t aligned_start = Common::AlignUp(start, 4);
|
||||
const uintptr_t aligned_end = Common::AlignDown(end, 4);
|
||||
|
||||
// Make sure we're aligned.
|
||||
if (start < aligned_start) {
|
||||
const u32 rnd = this->GenerateRandomU32();
|
||||
std::memcpy(dst, &rnd, aligned_start - start);
|
||||
}
|
||||
|
||||
// Write as many aligned u32s as we can.
|
||||
{
|
||||
u32* cur_dst = reinterpret_cast<u32*>(aligned_start);
|
||||
u32* const end_dst = reinterpret_cast<u32*>(aligned_end);
|
||||
|
||||
while (cur_dst < end_dst) {
|
||||
*(cur_dst++) = this->GenerateRandomU32();
|
||||
}
|
||||
}
|
||||
|
||||
// Handle any leftover unaligned data.
|
||||
if (aligned_end < end) {
|
||||
const u32 rnd = this->GenerateRandomU32();
|
||||
std::memcpy(reinterpret_cast<void*>(aligned_end), &rnd, end - aligned_end);
|
||||
}
|
||||
}
|
||||
|
||||
u32 GenerateRandomU32() {
|
||||
// Advance state.
|
||||
const u32 x0 =
|
||||
(this->state.data[0] & TopBitmask) ^ this->state.data[1] ^ this->state.data[2];
|
||||
const u32 y0 = this->state.data[3];
|
||||
const u32 x1 = x0 ^ (x0 << 1);
|
||||
const u32 y1 = y0 ^ (y0 >> 1) ^ x1;
|
||||
|
||||
const u32 state0 = this->state.data[1];
|
||||
u32 state1 = this->state.data[2];
|
||||
u32 state2 = x1 ^ (y1 << 10);
|
||||
const u32 state3 = y1;
|
||||
|
||||
if ((y1 & 1) != 0) {
|
||||
state1 ^= ParamMat1;
|
||||
state2 ^= ParamMat2;
|
||||
}
|
||||
|
||||
this->state.data[0] = state0;
|
||||
this->state.data[1] = state1;
|
||||
this->state.data[2] = state2;
|
||||
this->state.data[3] = state3;
|
||||
|
||||
// Temper.
|
||||
const u32 t1 = state0 + (state2 >> 8);
|
||||
u32 t0 = state3 ^ t1;
|
||||
|
||||
if ((t1 & 1) != 0) {
|
||||
t0 ^= ParamTmat;
|
||||
}
|
||||
|
||||
return t0;
|
||||
}
|
||||
|
||||
u64 GenerateRandomU64() {
|
||||
const u32 lo = this->GenerateRandomU32();
|
||||
const u32 hi = this->GenerateRandomU32();
|
||||
return (u64{hi} << 32) | u64{lo};
|
||||
}
|
||||
|
||||
float GenerateRandomF32() {
|
||||
// Floats have 24 bits of mantissa.
|
||||
constexpr u32 MantissaBits = 24;
|
||||
return static_cast<float>(GenerateRandomU24()) * (1.0f / (1U << MantissaBits));
|
||||
}
|
||||
|
||||
double GenerateRandomF64() {
|
||||
// Doubles have 53 bits of mantissa.
|
||||
// The smart way to generate 53 bits of random would be to use 32 bits
|
||||
// from the first rnd32() call, and then 21 from the second.
|
||||
// Nintendo does not. They use (32 - 5) = 27 bits from the first rnd32()
|
||||
// call, and (32 - 6) bits from the second. We'll do what they do, but
|
||||
// There's not a clear reason why.
|
||||
constexpr u32 MantissaBits = 53;
|
||||
constexpr u32 Shift1st = (64 - MantissaBits) / 2;
|
||||
constexpr u32 Shift2nd = (64 - MantissaBits) - Shift1st;
|
||||
|
||||
const u32 first = (this->GenerateRandomU32() >> Shift1st);
|
||||
const u32 second = (this->GenerateRandomU32() >> Shift2nd);
|
||||
|
||||
return (1.0 * first * (u64{1} << (32 - Shift2nd)) + second) *
|
||||
(1.0 / (u64{1} << MantissaBits));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,71 +0,0 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
|
||||
#pragma intrinsic(_umul128)
|
||||
#pragma intrinsic(_udiv128)
|
||||
#endif
|
||||
#include <cstring>
|
||||
#include "common/uint128.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) {
|
||||
u128 r{};
|
||||
r[0] = _umul128(a, b, &r[1]);
|
||||
u64 remainder;
|
||||
#if _MSC_VER < 1923
|
||||
return udiv128(r[1], r[0], d, &remainder);
|
||||
#else
|
||||
return _udiv128(r[1], r[0], d, &remainder);
|
||||
#endif
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) {
|
||||
const u64 diva = a / d;
|
||||
const u64 moda = a % d;
|
||||
const u64 divb = b / d;
|
||||
const u64 modb = b % d;
|
||||
return diva * b + moda * divb + moda * modb / d;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
u128 Multiply64Into128(u64 a, u64 b) {
|
||||
u128 result;
|
||||
#ifdef _MSC_VER
|
||||
result[0] = _umul128(a, b, &result[1]);
|
||||
#else
|
||||
unsigned __int128 tmp = a;
|
||||
tmp *= b;
|
||||
std::memcpy(&result, &tmp, sizeof(u128));
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor) {
|
||||
u64 remainder = dividend[0] % divisor;
|
||||
u64 accum = dividend[0] / divisor;
|
||||
if (dividend[1] == 0)
|
||||
return {accum, remainder};
|
||||
// We ignore dividend[1] / divisor as that overflows
|
||||
const u64 first_segment = (dividend[1] % divisor) << 32;
|
||||
accum += (first_segment / divisor) << 32;
|
||||
const u64 second_segment = (first_segment % divisor) << 32;
|
||||
accum += (second_segment / divisor);
|
||||
remainder += second_segment % divisor;
|
||||
if (remainder >= divisor) {
|
||||
accum++;
|
||||
remainder -= divisor;
|
||||
}
|
||||
return {accum, remainder};
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@@ -4,19 +4,118 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#pragma intrinsic(__umulh)
|
||||
#pragma intrinsic(_umul128)
|
||||
#pragma intrinsic(_udiv128)
|
||||
#else
|
||||
#include <x86intrin.h>
|
||||
#endif
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
// This function multiplies 2 u64 values and divides it by a u64 value.
|
||||
[[nodiscard]] u64 MultiplyAndDivide64(u64 a, u64 b, u64 d);
|
||||
[[nodiscard]] static inline u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) {
|
||||
#ifdef _MSC_VER
|
||||
u128 r{};
|
||||
r[0] = _umul128(a, b, &r[1]);
|
||||
u64 remainder;
|
||||
#if _MSC_VER < 1923
|
||||
return udiv128(r[1], r[0], d, &remainder);
|
||||
#else
|
||||
return _udiv128(r[1], r[0], d, &remainder);
|
||||
#endif
|
||||
#else
|
||||
const u64 diva = a / d;
|
||||
const u64 moda = a % d;
|
||||
const u64 divb = b / d;
|
||||
const u64 modb = b % d;
|
||||
return diva * b + moda * divb + moda * modb / d;
|
||||
#endif
|
||||
}
|
||||
|
||||
// This function multiplies 2 u64 values and produces a u128 value;
|
||||
[[nodiscard]] u128 Multiply64Into128(u64 a, u64 b);
|
||||
[[nodiscard]] static inline u128 Multiply64Into128(u64 a, u64 b) {
|
||||
u128 result;
|
||||
#ifdef _MSC_VER
|
||||
result[0] = _umul128(a, b, &result[1]);
|
||||
#else
|
||||
unsigned __int128 tmp = a;
|
||||
tmp *= b;
|
||||
std::memcpy(&result, &tmp, sizeof(u128));
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] static inline u64 GetFixedPoint64Factor(u64 numerator, u64 divisor) {
|
||||
#ifdef __SIZEOF_INT128__
|
||||
const auto base = static_cast<unsigned __int128>(numerator) << 64ULL;
|
||||
return static_cast<u64>(base / divisor);
|
||||
#elif defined(_M_X64) || defined(_M_ARM64)
|
||||
std::array<u64, 2> r = {0, numerator};
|
||||
u64 remainder;
|
||||
#if _MSC_VER < 1923
|
||||
return udiv128(r[1], r[0], divisor, &remainder);
|
||||
#else
|
||||
return _udiv128(r[1], r[0], divisor, &remainder);
|
||||
#endif
|
||||
#else
|
||||
// This one is bit more inaccurate.
|
||||
return MultiplyAndDivide64(std::numeric_limits<u64>::max(), numerator, divisor);
|
||||
#endif
|
||||
}
|
||||
|
||||
[[nodiscard]] static inline u64 MultiplyHigh(u64 a, u64 b) {
|
||||
#ifdef __SIZEOF_INT128__
|
||||
return (static_cast<unsigned __int128>(a) * static_cast<unsigned __int128>(b)) >> 64;
|
||||
#elif defined(_M_X64) || defined(_M_ARM64)
|
||||
return __umulh(a, b); // MSVC
|
||||
#else
|
||||
// Generic fallback
|
||||
const u64 a_lo = u32(a);
|
||||
const u64 a_hi = a >> 32;
|
||||
const u64 b_lo = u32(b);
|
||||
const u64 b_hi = b >> 32;
|
||||
|
||||
const u64 a_x_b_hi = a_hi * b_hi;
|
||||
const u64 a_x_b_mid = a_hi * b_lo;
|
||||
const u64 b_x_a_mid = b_hi * a_lo;
|
||||
const u64 a_x_b_lo = a_lo * b_lo;
|
||||
|
||||
const u64 carry_bit = (static_cast<u64>(static_cast<u32>(a_x_b_mid)) +
|
||||
static_cast<u64>(static_cast<u32>(b_x_a_mid)) + (a_x_b_lo >> 32)) >>
|
||||
32;
|
||||
|
||||
const u64 multhi = a_x_b_hi + (a_x_b_mid >> 32) + (b_x_a_mid >> 32) + carry_bit;
|
||||
|
||||
return multhi;
|
||||
#endif
|
||||
}
|
||||
|
||||
// This function divides a u128 by a u32 value and produces two u64 values:
|
||||
// the result of division and the remainder
|
||||
[[nodiscard]] std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor);
|
||||
[[nodiscard]] static inline std::pair<u64, u64> Divide128On32(u128 dividend, u32 divisor) {
|
||||
u64 remainder = dividend[0] % divisor;
|
||||
u64 accum = dividend[0] / divisor;
|
||||
if (dividend[1] == 0)
|
||||
return {accum, remainder};
|
||||
// We ignore dividend[1] / divisor as that overflows
|
||||
const u64 first_segment = (dividend[1] % divisor) << 32;
|
||||
accum += (first_segment / divisor) << 32;
|
||||
const u64 second_segment = (first_segment % divisor) << 32;
|
||||
accum += (second_segment / divisor);
|
||||
remainder += second_segment % divisor;
|
||||
if (remainder >= divisor) {
|
||||
accum++;
|
||||
remainder -= divisor;
|
||||
}
|
||||
return {accum, remainder};
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "common/uint128.h"
|
||||
#include "common/wall_clock.h"
|
||||
|
||||
|
||||
@@ -8,68 +8,10 @@
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
|
||||
#pragma intrinsic(__umulh)
|
||||
#pragma intrinsic(_udiv128)
|
||||
#else
|
||||
#include <x86intrin.h>
|
||||
#endif
|
||||
|
||||
#include "common/atomic_ops.h"
|
||||
#include "common/uint128.h"
|
||||
#include "common/x64/native_clock.h"
|
||||
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] u64 GetFixedPoint64Factor(u64 numerator, u64 divisor) {
|
||||
#ifdef __SIZEOF_INT128__
|
||||
const auto base = static_cast<unsigned __int128>(numerator) << 64ULL;
|
||||
return static_cast<u64>(base / divisor);
|
||||
#elif defined(_M_X64) || defined(_M_ARM64)
|
||||
std::array<u64, 2> r = {0, numerator};
|
||||
u64 remainder;
|
||||
#if _MSC_VER < 1923
|
||||
return udiv128(r[1], r[0], divisor, &remainder);
|
||||
#else
|
||||
return _udiv128(r[1], r[0], divisor, &remainder);
|
||||
#endif
|
||||
#else
|
||||
// This one is bit more inaccurate.
|
||||
return MultiplyAndDivide64(std::numeric_limits<u64>::max(), numerator, divisor);
|
||||
#endif
|
||||
}
|
||||
|
||||
[[nodiscard]] u64 MultiplyHigh(u64 a, u64 b) {
|
||||
#ifdef __SIZEOF_INT128__
|
||||
return (static_cast<unsigned __int128>(a) * static_cast<unsigned __int128>(b)) >> 64;
|
||||
#elif defined(_M_X64) || defined(_M_ARM64)
|
||||
return __umulh(a, b); // MSVC
|
||||
#else
|
||||
// Generic fallback
|
||||
const u64 a_lo = u32(a);
|
||||
const u64 a_hi = a >> 32;
|
||||
const u64 b_lo = u32(b);
|
||||
const u64 b_hi = b >> 32;
|
||||
|
||||
const u64 a_x_b_hi = a_hi * b_hi;
|
||||
const u64 a_x_b_mid = a_hi * b_lo;
|
||||
const u64 b_x_a_mid = b_hi * a_lo;
|
||||
const u64 a_x_b_lo = a_lo * b_lo;
|
||||
|
||||
const u64 carry_bit = (static_cast<u64>(static_cast<u32>(a_x_b_mid)) +
|
||||
static_cast<u64>(static_cast<u32>(b_x_a_mid)) + (a_x_b_lo >> 32)) >>
|
||||
32;
|
||||
|
||||
const u64 multhi = a_x_b_hi + (a_x_b_mid >> 32) + (b_x_a_mid >> 32) + carry_bit;
|
||||
|
||||
return multhi;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Common {
|
||||
|
||||
u64 EstimateRDTSCFrequency() {
|
||||
|
||||
@@ -19,7 +19,6 @@ add_library(core STATIC
|
||||
core.h
|
||||
core_timing.cpp
|
||||
core_timing.h
|
||||
core_timing_util.cpp
|
||||
core_timing_util.h
|
||||
cpu_manager.cpp
|
||||
cpu_manager.h
|
||||
@@ -142,6 +141,9 @@ add_library(core STATIC
|
||||
hardware_interrupt_manager.h
|
||||
hle/ipc.h
|
||||
hle/ipc_helpers.h
|
||||
hle/kernel/board/nintendo/nx/k_system_control.cpp
|
||||
hle/kernel/board/nintendo/nx/k_system_control.h
|
||||
hle/kernel/board/nintendo/nx/secure_monitor.h
|
||||
hle/kernel/client_port.cpp
|
||||
hle/kernel/client_port.h
|
||||
hle/kernel/client_session.cpp
|
||||
@@ -157,6 +159,8 @@ add_library(core STATIC
|
||||
hle/kernel/hle_ipc.h
|
||||
hle/kernel/k_address_arbiter.cpp
|
||||
hle/kernel/k_address_arbiter.h
|
||||
hle/kernel/k_address_space_info.cpp
|
||||
hle/kernel/k_address_space_info.h
|
||||
hle/kernel/k_affinity_mask.h
|
||||
hle/kernel/k_condition_variable.cpp
|
||||
hle/kernel/k_condition_variable.h
|
||||
@@ -165,6 +169,22 @@ add_library(core STATIC
|
||||
hle/kernel/k_light_condition_variable.h
|
||||
hle/kernel/k_light_lock.cpp
|
||||
hle/kernel/k_light_lock.h
|
||||
hle/kernel/k_memory_block.h
|
||||
hle/kernel/k_memory_block_manager.cpp
|
||||
hle/kernel/k_memory_block_manager.h
|
||||
hle/kernel/k_memory_layout.cpp
|
||||
hle/kernel/k_memory_layout.board.nintendo_nx.cpp
|
||||
hle/kernel/k_memory_layout.h
|
||||
hle/kernel/k_memory_manager.cpp
|
||||
hle/kernel/k_memory_manager.h
|
||||
hle/kernel/k_memory_region.h
|
||||
hle/kernel/k_memory_region_type.h
|
||||
hle/kernel/k_page_bitmap.h
|
||||
hle/kernel/k_page_heap.cpp
|
||||
hle/kernel/k_page_heap.h
|
||||
hle/kernel/k_page_linked_list.h
|
||||
hle/kernel/k_page_table.cpp
|
||||
hle/kernel/k_page_table.h
|
||||
hle/kernel/k_priority_queue.h
|
||||
hle/kernel/k_readable_event.cpp
|
||||
hle/kernel/k_readable_event.h
|
||||
@@ -176,32 +196,23 @@ add_library(core STATIC
|
||||
hle/kernel/k_scoped_lock.h
|
||||
hle/kernel/k_scoped_resource_reservation.h
|
||||
hle/kernel/k_scoped_scheduler_lock_and_sleep.h
|
||||
hle/kernel/k_shared_memory.cpp
|
||||
hle/kernel/k_shared_memory.h
|
||||
hle/kernel/k_slab_heap.h
|
||||
hle/kernel/k_spin_lock.cpp
|
||||
hle/kernel/k_spin_lock.h
|
||||
hle/kernel/k_synchronization_object.cpp
|
||||
hle/kernel/k_synchronization_object.h
|
||||
hle/kernel/k_system_control.h
|
||||
hle/kernel/k_thread.cpp
|
||||
hle/kernel/k_thread.h
|
||||
hle/kernel/k_thread_queue.h
|
||||
hle/kernel/k_trace.h
|
||||
hle/kernel/k_writable_event.cpp
|
||||
hle/kernel/k_writable_event.h
|
||||
hle/kernel/kernel.cpp
|
||||
hle/kernel/kernel.h
|
||||
hle/kernel/memory/address_space_info.cpp
|
||||
hle/kernel/memory/address_space_info.h
|
||||
hle/kernel/memory/memory_block.h
|
||||
hle/kernel/memory/memory_block_manager.cpp
|
||||
hle/kernel/memory/memory_block_manager.h
|
||||
hle/kernel/memory/memory_layout.h
|
||||
hle/kernel/memory/memory_manager.cpp
|
||||
hle/kernel/memory/memory_manager.h
|
||||
hle/kernel/memory/memory_types.h
|
||||
hle/kernel/memory/page_linked_list.h
|
||||
hle/kernel/memory/page_heap.cpp
|
||||
hle/kernel/memory/page_heap.h
|
||||
hle/kernel/memory/page_table.cpp
|
||||
hle/kernel/memory/page_table.h
|
||||
hle/kernel/memory/slab_heap.h
|
||||
hle/kernel/memory/system_control.cpp
|
||||
hle/kernel/memory/system_control.h
|
||||
hle/kernel/memory_types.h
|
||||
hle/kernel/object.cpp
|
||||
hle/kernel/object.h
|
||||
hle/kernel/physical_core.cpp
|
||||
@@ -219,8 +230,6 @@ add_library(core STATIC
|
||||
hle/kernel/service_thread.h
|
||||
hle/kernel/session.cpp
|
||||
hle/kernel/session.h
|
||||
hle/kernel/shared_memory.cpp
|
||||
hle/kernel/shared_memory.h
|
||||
hle/kernel/svc.cpp
|
||||
hle/kernel/svc.h
|
||||
hle/kernel/svc_common.h
|
||||
@@ -266,6 +275,7 @@ add_library(core STATIC
|
||||
hle/service/am/applets/software_keyboard.h
|
||||
hle/service/am/applets/web_browser.cpp
|
||||
hle/service/am/applets/web_browser.h
|
||||
hle/service/am/applets/web_types.h
|
||||
hle/service/am/idle.cpp
|
||||
hle/service/am/idle.h
|
||||
hle/service/am/omm.cpp
|
||||
@@ -400,6 +410,7 @@ add_library(core STATIC
|
||||
hle/service/hid/controllers/xpad.h
|
||||
hle/service/lbl/lbl.cpp
|
||||
hle/service/lbl/lbl.h
|
||||
hle/service/ldn/errors.h
|
||||
hle/service/ldn/ldn.cpp
|
||||
hle/service/ldn/ldn.h
|
||||
hle/service/ldr/ldr.cpp
|
||||
@@ -662,7 +673,7 @@ endif()
|
||||
create_target_directory_groups(core)
|
||||
|
||||
target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
|
||||
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls opus zip)
|
||||
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus zip)
|
||||
|
||||
if (YUZU_ENABLE_BOXCAT)
|
||||
target_compile_definitions(core PRIVATE -DYUZU_ENABLE_BOXCAT)
|
||||
|
||||
@@ -114,18 +114,17 @@ public:
|
||||
static constexpr u64 minimum_run_cycles = 1000U;
|
||||
};
|
||||
|
||||
std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable& page_table,
|
||||
std::size_t address_space_bits) const {
|
||||
std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* page_table) const {
|
||||
Dynarmic::A32::UserConfig config;
|
||||
config.callbacks = cb.get();
|
||||
// TODO(bunnei): Implement page table for 32-bit
|
||||
// config.page_table = &page_table.pointers;
|
||||
config.coprocessors[15] = cp15;
|
||||
config.define_unpredictable_behaviour = true;
|
||||
static constexpr std::size_t PAGE_BITS = 12;
|
||||
static constexpr std::size_t NUM_PAGE_TABLE_ENTRIES = 1 << (32 - PAGE_BITS);
|
||||
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>(
|
||||
page_table.pointers.data());
|
||||
if (page_table) {
|
||||
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>(
|
||||
page_table->pointers.data());
|
||||
}
|
||||
config.absolute_offset_page_table = true;
|
||||
config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
|
||||
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
||||
@@ -138,6 +137,10 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&
|
||||
// Timing
|
||||
config.wall_clock_cntpct = uses_wall_clock;
|
||||
|
||||
// Code cache size
|
||||
config.code_cache_size = 512 * 1024 * 1024;
|
||||
config.far_code_offset = 256 * 1024 * 1024;
|
||||
|
||||
// Safe optimizations
|
||||
if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::DebugMode) {
|
||||
if (!Settings::values.cpuopt_page_tables) {
|
||||
@@ -201,7 +204,8 @@ ARM_Dynarmic_32::ARM_Dynarmic_32(System& system, CPUInterrupts& interrupt_handle
|
||||
: ARM_Interface{system, interrupt_handlers, uses_wall_clock},
|
||||
cb(std::make_unique<DynarmicCallbacks32>(*this)),
|
||||
cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index},
|
||||
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
|
||||
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)},
|
||||
jit(MakeJit(nullptr)) {}
|
||||
|
||||
ARM_Dynarmic_32::~ARM_Dynarmic_32() = default;
|
||||
|
||||
@@ -256,9 +260,6 @@ void ARM_Dynarmic_32::ChangeProcessorID(std::size_t new_core_id) {
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) {
|
||||
if (!jit) {
|
||||
return;
|
||||
}
|
||||
Dynarmic::A32::Context context;
|
||||
jit->SaveContext(context);
|
||||
ctx.cpu_registers = context.Regs();
|
||||
@@ -268,9 +269,6 @@ void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) {
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) {
|
||||
if (!jit) {
|
||||
return;
|
||||
}
|
||||
Dynarmic::A32::Context context;
|
||||
context.Regs() = ctx.cpu_registers;
|
||||
context.ExtRegs() = ctx.extension_registers;
|
||||
@@ -284,35 +282,31 @@ void ARM_Dynarmic_32::PrepareReschedule() {
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::ClearInstructionCache() {
|
||||
if (!jit) {
|
||||
return;
|
||||
}
|
||||
jit->ClearCache();
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::InvalidateCacheRange(VAddr addr, std::size_t size) {
|
||||
if (!jit) {
|
||||
return;
|
||||
}
|
||||
jit->InvalidateCacheRange(static_cast<u32>(addr), size);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::ClearExclusiveState() {
|
||||
if (!jit) {
|
||||
return;
|
||||
}
|
||||
jit->ClearExclusiveState();
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table,
|
||||
std::size_t new_address_space_size_in_bits) {
|
||||
ThreadContext32 ctx{};
|
||||
SaveContext(ctx);
|
||||
|
||||
auto key = std::make_pair(&page_table, new_address_space_size_in_bits);
|
||||
auto iter = jit_cache.find(key);
|
||||
if (iter != jit_cache.end()) {
|
||||
jit = iter->second;
|
||||
LoadContext(ctx);
|
||||
return;
|
||||
}
|
||||
jit = MakeJit(page_table, new_address_space_size_in_bits);
|
||||
jit = MakeJit(&page_table);
|
||||
LoadContext(ctx);
|
||||
jit_cache.emplace(key, jit);
|
||||
}
|
||||
|
||||
|
||||
@@ -68,8 +68,7 @@ public:
|
||||
std::size_t new_address_space_size_in_bits) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable& page_table,
|
||||
std::size_t address_space_bits) const;
|
||||
std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const;
|
||||
|
||||
using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
|
||||
using JitCacheType =
|
||||
@@ -80,10 +79,10 @@ private:
|
||||
|
||||
std::unique_ptr<DynarmicCallbacks32> cb;
|
||||
JitCacheType jit_cache;
|
||||
std::shared_ptr<Dynarmic::A32::Jit> jit;
|
||||
std::shared_ptr<DynarmicCP15> cp15;
|
||||
std::size_t core_index;
|
||||
DynarmicExclusiveMonitor& exclusive_monitor;
|
||||
std::shared_ptr<Dynarmic::A32::Jit> jit;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -142,7 +142,7 @@ public:
|
||||
static constexpr u64 minimum_run_cycles = 1000U;
|
||||
};
|
||||
|
||||
std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable& page_table,
|
||||
std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* page_table,
|
||||
std::size_t address_space_bits) const {
|
||||
Dynarmic::A64::UserConfig config;
|
||||
|
||||
@@ -150,13 +150,15 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
|
||||
config.callbacks = cb.get();
|
||||
|
||||
// Memory
|
||||
config.page_table = reinterpret_cast<void**>(page_table.pointers.data());
|
||||
config.page_table_address_space_bits = address_space_bits;
|
||||
config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
|
||||
config.silently_mirror_page_table = false;
|
||||
config.absolute_offset_page_table = true;
|
||||
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
||||
config.only_detect_misalignment_via_page_table_on_page_boundary = true;
|
||||
if (page_table) {
|
||||
config.page_table = reinterpret_cast<void**>(page_table->pointers.data());
|
||||
config.page_table_address_space_bits = address_space_bits;
|
||||
config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
|
||||
config.silently_mirror_page_table = false;
|
||||
config.absolute_offset_page_table = true;
|
||||
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
||||
config.only_detect_misalignment_via_page_table_on_page_boundary = true;
|
||||
}
|
||||
|
||||
// Multi-process state
|
||||
config.processor_id = core_index;
|
||||
@@ -175,6 +177,10 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
|
||||
// Timing
|
||||
config.wall_clock_cntpct = uses_wall_clock;
|
||||
|
||||
// Code cache size
|
||||
config.code_cache_size = 512 * 1024 * 1024;
|
||||
config.far_code_offset = 256 * 1024 * 1024;
|
||||
|
||||
// Safe optimizations
|
||||
if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::DebugMode) {
|
||||
if (!Settings::values.cpuopt_page_tables) {
|
||||
@@ -237,7 +243,8 @@ ARM_Dynarmic_64::ARM_Dynarmic_64(System& system, CPUInterrupts& interrupt_handle
|
||||
std::size_t core_index)
|
||||
: ARM_Interface{system, interrupt_handlers, uses_wall_clock},
|
||||
cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index},
|
||||
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
|
||||
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)},
|
||||
jit(MakeJit(nullptr, 48)) {}
|
||||
|
||||
ARM_Dynarmic_64::~ARM_Dynarmic_64() = default;
|
||||
|
||||
@@ -294,9 +301,6 @@ void ARM_Dynarmic_64::ChangeProcessorID(std::size_t new_core_id) {
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) {
|
||||
if (!jit) {
|
||||
return;
|
||||
}
|
||||
ctx.cpu_registers = jit->GetRegisters();
|
||||
ctx.sp = jit->GetSP();
|
||||
ctx.pc = jit->GetPC();
|
||||
@@ -308,9 +312,6 @@ void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) {
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) {
|
||||
if (!jit) {
|
||||
return;
|
||||
}
|
||||
jit->SetRegisters(ctx.cpu_registers);
|
||||
jit->SetSP(ctx.sp);
|
||||
jit->SetPC(ctx.pc);
|
||||
@@ -326,35 +327,31 @@ void ARM_Dynarmic_64::PrepareReschedule() {
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_64::ClearInstructionCache() {
|
||||
if (!jit) {
|
||||
return;
|
||||
}
|
||||
jit->ClearCache();
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_64::InvalidateCacheRange(VAddr addr, std::size_t size) {
|
||||
if (!jit) {
|
||||
return;
|
||||
}
|
||||
jit->InvalidateCacheRange(addr, size);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_64::ClearExclusiveState() {
|
||||
if (!jit) {
|
||||
return;
|
||||
}
|
||||
jit->ClearExclusiveState();
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table,
|
||||
std::size_t new_address_space_size_in_bits) {
|
||||
ThreadContext64 ctx{};
|
||||
SaveContext(ctx);
|
||||
|
||||
auto key = std::make_pair(&page_table, new_address_space_size_in_bits);
|
||||
auto iter = jit_cache.find(key);
|
||||
if (iter != jit_cache.end()) {
|
||||
jit = iter->second;
|
||||
LoadContext(ctx);
|
||||
return;
|
||||
}
|
||||
jit = MakeJit(page_table, new_address_space_size_in_bits);
|
||||
jit = MakeJit(&page_table, new_address_space_size_in_bits);
|
||||
LoadContext(ctx);
|
||||
jit_cache.emplace(key, jit);
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ public:
|
||||
std::size_t new_address_space_size_in_bits) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable& page_table,
|
||||
std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table,
|
||||
std::size_t address_space_bits) const;
|
||||
|
||||
using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
|
||||
@@ -71,10 +71,11 @@ private:
|
||||
friend class DynarmicCallbacks64;
|
||||
std::unique_ptr<DynarmicCallbacks64> cb;
|
||||
JitCacheType jit_cache;
|
||||
std::shared_ptr<Dynarmic::A64::Jit> jit;
|
||||
|
||||
std::size_t core_index;
|
||||
DynarmicExclusiveMonitor& exclusive_monitor;
|
||||
|
||||
std::shared_ptr<Dynarmic::A64::Jit> jit;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -296,28 +296,20 @@ struct System::Impl {
|
||||
exit_lock = false;
|
||||
|
||||
if (gpu_core) {
|
||||
gpu_core->WaitIdle();
|
||||
gpu_core->ShutDown();
|
||||
}
|
||||
|
||||
// Shutdown emulation session
|
||||
services.reset();
|
||||
service_manager.reset();
|
||||
cheat_engine.reset();
|
||||
telemetry_session.reset();
|
||||
|
||||
// Close all CPU/threading state
|
||||
cpu_manager.Shutdown();
|
||||
|
||||
// Shutdown kernel and core timing
|
||||
time_manager.Shutdown();
|
||||
core_timing.Shutdown();
|
||||
kernel.Shutdown();
|
||||
|
||||
// Close app loader
|
||||
app_loader.reset();
|
||||
gpu_core.reset();
|
||||
perf_stats.reset();
|
||||
|
||||
// Clear all applets
|
||||
kernel.Shutdown();
|
||||
applet_manager.ClearAll();
|
||||
|
||||
LOG_DEBUG(Core, "Shutdown OK");
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
// Copyright 2008 Dolphin Emulator Project / 2017 Citra Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/core_timing_util.h"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <limits>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/uint128.h"
|
||||
#include "core/hardware_properties.h"
|
||||
|
||||
namespace Core::Timing {
|
||||
|
||||
constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits<s64>::max() / Hardware::BASE_CLOCK_RATE;
|
||||
|
||||
s64 msToCycles(std::chrono::milliseconds ms) {
|
||||
if (static_cast<u64>(ms.count() / 1000) > MAX_VALUE_TO_MULTIPLY) {
|
||||
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
|
||||
return std::numeric_limits<s64>::max();
|
||||
}
|
||||
if (static_cast<u64>(ms.count()) > MAX_VALUE_TO_MULTIPLY) {
|
||||
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
|
||||
return Hardware::BASE_CLOCK_RATE * (ms.count() / 1000);
|
||||
}
|
||||
return (Hardware::BASE_CLOCK_RATE * ms.count()) / 1000;
|
||||
}
|
||||
|
||||
s64 usToCycles(std::chrono::microseconds us) {
|
||||
if (static_cast<u64>(us.count() / 1000000) > MAX_VALUE_TO_MULTIPLY) {
|
||||
LOG_ERROR(Core_Timing, "Integer overflow, use max value");
|
||||
return std::numeric_limits<s64>::max();
|
||||
}
|
||||
if (static_cast<u64>(us.count()) > MAX_VALUE_TO_MULTIPLY) {
|
||||
LOG_DEBUG(Core_Timing, "Time very big, do rounding");
|
||||
return Hardware::BASE_CLOCK_RATE * (us.count() / 1000000);
|
||||
}
|
||||
return (Hardware::BASE_CLOCK_RATE * us.count()) / 1000000;
|
||||
}
|
||||
|
||||
s64 nsToCycles(std::chrono::nanoseconds ns) {
|
||||
const u128 temporal = Common::Multiply64Into128(ns.count(), Hardware::BASE_CLOCK_RATE);
|
||||
return Common::Divide128On32(temporal, static_cast<u32>(1000000000)).first;
|
||||
}
|
||||
|
||||
u64 msToClockCycles(std::chrono::milliseconds ns) {
|
||||
const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ);
|
||||
return Common::Divide128On32(temp, 1000).first;
|
||||
}
|
||||
|
||||
u64 usToClockCycles(std::chrono::microseconds ns) {
|
||||
const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ);
|
||||
return Common::Divide128On32(temp, 1000000).first;
|
||||
}
|
||||
|
||||
u64 nsToClockCycles(std::chrono::nanoseconds ns) {
|
||||
const u128 temp = Common::Multiply64Into128(ns.count(), Hardware::CNTFREQ);
|
||||
return Common::Divide128On32(temp, 1000000000).first;
|
||||
}
|
||||
|
||||
u64 CpuCyclesToClockCycles(u64 ticks) {
|
||||
const u128 temporal = Common::Multiply64Into128(ticks, Hardware::CNTFREQ);
|
||||
return Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first;
|
||||
}
|
||||
|
||||
std::chrono::milliseconds CyclesToMs(s64 cycles) {
|
||||
const u128 temporal = Common::Multiply64Into128(cycles, 1000);
|
||||
u64 ms = Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first;
|
||||
return std::chrono::milliseconds(ms);
|
||||
}
|
||||
|
||||
std::chrono::nanoseconds CyclesToNs(s64 cycles) {
|
||||
const u128 temporal = Common::Multiply64Into128(cycles, 1000000000);
|
||||
u64 ns = Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first;
|
||||
return std::chrono::nanoseconds(ns);
|
||||
}
|
||||
|
||||
std::chrono::microseconds CyclesToUs(s64 cycles) {
|
||||
const u128 temporal = Common::Multiply64Into128(cycles, 1000000);
|
||||
u64 us = Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first;
|
||||
return std::chrono::microseconds(us);
|
||||
}
|
||||
|
||||
} // namespace Core::Timing
|
||||
@@ -1,24 +1,59 @@
|
||||
// Copyright 2008 Dolphin Emulator Project / 2017 Citra Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hardware_properties.h"
|
||||
|
||||
namespace Core::Timing {
|
||||
|
||||
s64 msToCycles(std::chrono::milliseconds ms);
|
||||
s64 usToCycles(std::chrono::microseconds us);
|
||||
s64 nsToCycles(std::chrono::nanoseconds ns);
|
||||
u64 msToClockCycles(std::chrono::milliseconds ns);
|
||||
u64 usToClockCycles(std::chrono::microseconds ns);
|
||||
u64 nsToClockCycles(std::chrono::nanoseconds ns);
|
||||
std::chrono::milliseconds CyclesToMs(s64 cycles);
|
||||
std::chrono::nanoseconds CyclesToNs(s64 cycles);
|
||||
std::chrono::microseconds CyclesToUs(s64 cycles);
|
||||
namespace detail {
|
||||
constexpr u64 CNTFREQ_ADJUSTED = Hardware::CNTFREQ / 1000;
|
||||
constexpr u64 BASE_CLOCK_RATE_ADJUSTED = Hardware::BASE_CLOCK_RATE / 1000;
|
||||
} // namespace detail
|
||||
|
||||
u64 CpuCyclesToClockCycles(u64 ticks);
|
||||
[[nodiscard]] constexpr s64 msToCycles(std::chrono::milliseconds ms) {
|
||||
return ms.count() * detail::BASE_CLOCK_RATE_ADJUSTED;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr s64 usToCycles(std::chrono::microseconds us) {
|
||||
return us.count() * detail::BASE_CLOCK_RATE_ADJUSTED / 1000;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr s64 nsToCycles(std::chrono::nanoseconds ns) {
|
||||
return ns.count() * detail::BASE_CLOCK_RATE_ADJUSTED / 1000000;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr u64 msToClockCycles(std::chrono::milliseconds ms) {
|
||||
return static_cast<u64>(ms.count()) * detail::CNTFREQ_ADJUSTED;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr u64 usToClockCycles(std::chrono::microseconds us) {
|
||||
return us.count() * detail::CNTFREQ_ADJUSTED / 1000;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr u64 nsToClockCycles(std::chrono::nanoseconds ns) {
|
||||
return ns.count() * detail::CNTFREQ_ADJUSTED / 1000000;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr u64 CpuCyclesToClockCycles(u64 ticks) {
|
||||
return ticks * detail::CNTFREQ_ADJUSTED / detail::BASE_CLOCK_RATE_ADJUSTED;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::chrono::milliseconds CyclesToMs(s64 cycles) {
|
||||
return std::chrono::milliseconds(cycles / detail::BASE_CLOCK_RATE_ADJUSTED);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::chrono::nanoseconds CyclesToNs(s64 cycles) {
|
||||
return std::chrono::nanoseconds(cycles * 1000000 / detail::BASE_CLOCK_RATE_ADJUSTED);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::chrono::microseconds CyclesToUs(s64 cycles) {
|
||||
return std::chrono::microseconds(cycles * 1000 / detail::BASE_CLOCK_RATE_ADJUSTED);
|
||||
}
|
||||
|
||||
} // namespace Core::Timing
|
||||
|
||||
@@ -148,7 +148,7 @@ void CpuManager::MultiCoreRunSuspendThread() {
|
||||
auto core = kernel.GetCurrentHostThreadID();
|
||||
auto& scheduler = *kernel.CurrentScheduler();
|
||||
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[core].host_context);
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
|
||||
ASSERT(scheduler.ContextSwitchPending());
|
||||
ASSERT(core == kernel.GetCurrentHostThreadID());
|
||||
scheduler.RescheduleCurrentCore();
|
||||
@@ -245,7 +245,7 @@ void CpuManager::SingleCoreRunSuspendThread() {
|
||||
auto core = kernel.GetCurrentHostThreadID();
|
||||
auto& scheduler = *kernel.CurrentScheduler();
|
||||
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[0].host_context);
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[0].host_context);
|
||||
ASSERT(scheduler.ContextSwitchPending());
|
||||
ASSERT(core == kernel.GetCurrentHostThreadID());
|
||||
scheduler.RescheduleCurrentCore();
|
||||
@@ -271,7 +271,7 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
|
||||
scheduler.Unload(scheduler.GetCurrentThread());
|
||||
|
||||
auto& next_scheduler = kernel.Scheduler(current_core);
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), next_scheduler.ControlContext());
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), *next_scheduler.ControlContext());
|
||||
}
|
||||
|
||||
// May have changed scheduler
|
||||
@@ -363,7 +363,7 @@ void CpuManager::RunThread(std::size_t core) {
|
||||
|
||||
auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
|
||||
data.is_running = true;
|
||||
Common::Fiber::YieldTo(data.host_context, current_thread->GetHostContext());
|
||||
Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext());
|
||||
data.is_running = false;
|
||||
data.is_paused = true;
|
||||
data.exit_barrier->Wait();
|
||||
|
||||
@@ -105,8 +105,6 @@ void AESCipher<Key, KeySize>::Transcode(const u8* src, std::size_t size, u8* des
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mbedtls_cipher_finish(context, nullptr, nullptr);
|
||||
}
|
||||
|
||||
template <typename Key, std::size_t KeySize>
|
||||
|
||||
@@ -100,6 +100,14 @@ u64 NACP::GetDeviceSaveDataSize() const {
|
||||
return raw.device_save_data_size;
|
||||
}
|
||||
|
||||
u32 NACP::GetParentalControlFlag() const {
|
||||
return raw.parental_control;
|
||||
}
|
||||
|
||||
const std::array<u8, 0x20>& NACP::GetRatingAge() const {
|
||||
return raw.rating_age;
|
||||
}
|
||||
|
||||
std::vector<u8> NACP::GetRawBytes() const {
|
||||
std::vector<u8> out(sizeof(RawNACP));
|
||||
std::memcpy(out.data(), &raw, sizeof(RawNACP));
|
||||
|
||||
@@ -114,6 +114,8 @@ public:
|
||||
std::vector<u8> GetRawBytes() const;
|
||||
bool GetUserAccountSwitchLock() const;
|
||||
u64 GetDeviceSaveDataSize() const;
|
||||
u32 GetParentalControlFlag() const;
|
||||
const std::array<u8, 0x20>& GetRatingAge() const;
|
||||
|
||||
private:
|
||||
RawNACP raw{};
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace NgWord1Data {
|
||||
constexpr std::size_t NUMBER_WORD_TXT_FILES = 0x10;
|
||||
|
||||
// Should this archive replacement mysteriously not work on a future game, consider updating.
|
||||
constexpr std::array<u8, 4> VERSION_DAT{0x0, 0x0, 0x0, 0x19}; // 5.1.0 System Version
|
||||
constexpr std::array<u8, 4> VERSION_DAT{0x0, 0x0, 0x0, 0x20}; // 11.0.1 System Version
|
||||
|
||||
constexpr std::array<u8, 30> WORD_TXT{
|
||||
0xFE, 0xFF, 0x00, 0x5E, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x79, 0x00, 0x62, 0x00,
|
||||
@@ -43,7 +43,7 @@ namespace NgWord2Data {
|
||||
constexpr std::size_t NUMBER_AC_NX_FILES = 0x10;
|
||||
|
||||
// Should this archive replacement mysteriously not work on a future game, consider updating.
|
||||
constexpr std::array<u8, 4> VERSION_DAT{0x0, 0x0, 0x0, 0x15}; // 5.1.0 System Version
|
||||
constexpr std::array<u8, 4> VERSION_DAT{0x0, 0x0, 0x0, 0x1A}; // 11.0.1 System Version
|
||||
|
||||
constexpr std::array<u8, 0x2C> AC_NX_DATA{
|
||||
0x1F, 0x8B, 0x08, 0x08, 0xD5, 0x2C, 0x09, 0x5C, 0x04, 0x00, 0x61, 0x63, 0x72, 0x61, 0x77,
|
||||
|
||||
@@ -14,15 +14,15 @@ namespace SystemVersionData {
|
||||
|
||||
constexpr u8 VERSION_MAJOR = 11;
|
||||
constexpr u8 VERSION_MINOR = 0;
|
||||
constexpr u8 VERSION_MICRO = 0;
|
||||
constexpr u8 VERSION_MICRO = 1;
|
||||
|
||||
constexpr u8 REVISION_MAJOR = 5;
|
||||
constexpr u8 REVISION_MAJOR = 1;
|
||||
constexpr u8 REVISION_MINOR = 0;
|
||||
|
||||
constexpr char PLATFORM_STRING[] = "NX";
|
||||
constexpr char VERSION_HASH[] = "34197eba8810e2edd5e9dfcfbde7b340882e856d";
|
||||
constexpr char DISPLAY_VERSION[] = "11.0.0";
|
||||
constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 11.0.0-5.0";
|
||||
constexpr char VERSION_HASH[] = "69103fcb2004dace877094c2f8c29e6113be5dbf";
|
||||
constexpr char DISPLAY_VERSION[] = "11.0.1";
|
||||
constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 11.0.1-1.0";
|
||||
|
||||
} // namespace SystemVersionData
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ struct ControllerParameters {
|
||||
bool allow_dual_joycons{};
|
||||
bool allow_left_joycon{};
|
||||
bool allow_right_joycon{};
|
||||
bool allow_gamecube_controller{};
|
||||
};
|
||||
|
||||
class ControllerApplet {
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
// All architectures must define NumArchitectureDeviceRegions.
|
||||
constexpr inline const auto NumArchitectureDeviceRegions = 3;
|
||||
|
||||
constexpr inline const auto KMemoryRegionType_Uart =
|
||||
KMemoryRegionType_ArchDeviceBase.DeriveSparse(0, NumArchitectureDeviceRegions, 0);
|
||||
constexpr inline const auto KMemoryRegionType_InterruptCpuInterface =
|
||||
KMemoryRegionType_ArchDeviceBase.DeriveSparse(0, NumArchitectureDeviceRegions, 1)
|
||||
.SetAttribute(KMemoryRegionAttr_NoUserMap);
|
||||
constexpr inline const auto KMemoryRegionType_InterruptDistributor =
|
||||
KMemoryRegionType_ArchDeviceBase.DeriveSparse(0, NumArchitectureDeviceRegions, 2)
|
||||
.SetAttribute(KMemoryRegionAttr_NoUserMap);
|
||||
static_assert(KMemoryRegionType_Uart.GetValue() == (0x1D));
|
||||
static_assert(KMemoryRegionType_InterruptCpuInterface.GetValue() ==
|
||||
(0x2D | KMemoryRegionAttr_NoUserMap));
|
||||
static_assert(KMemoryRegionType_InterruptDistributor.GetValue() ==
|
||||
(0x4D | KMemoryRegionAttr_NoUserMap));
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
// All architectures must define NumBoardDeviceRegions.
|
||||
constexpr inline const auto NumBoardDeviceRegions = 6;
|
||||
// UNUSED: .Derive(NumBoardDeviceRegions, 0);
|
||||
constexpr inline const auto KMemoryRegionType_MemoryController =
|
||||
KMemoryRegionType_BoardDeviceBase.Derive(NumBoardDeviceRegions, 1)
|
||||
.SetAttribute(KMemoryRegionAttr_NoUserMap);
|
||||
constexpr inline const auto KMemoryRegionType_MemoryController1 =
|
||||
KMemoryRegionType_BoardDeviceBase.Derive(NumBoardDeviceRegions, 2)
|
||||
.SetAttribute(KMemoryRegionAttr_NoUserMap);
|
||||
constexpr inline const auto KMemoryRegionType_MemoryController0 =
|
||||
KMemoryRegionType_BoardDeviceBase.Derive(NumBoardDeviceRegions, 3)
|
||||
.SetAttribute(KMemoryRegionAttr_NoUserMap);
|
||||
constexpr inline const auto KMemoryRegionType_PowerManagementController =
|
||||
KMemoryRegionType_BoardDeviceBase.Derive(NumBoardDeviceRegions, 4).DeriveTransition();
|
||||
constexpr inline const auto KMemoryRegionType_LegacyLpsDevices =
|
||||
KMemoryRegionType_BoardDeviceBase.Derive(NumBoardDeviceRegions, 5);
|
||||
static_assert(KMemoryRegionType_MemoryController.GetValue() ==
|
||||
(0x55 | KMemoryRegionAttr_NoUserMap));
|
||||
static_assert(KMemoryRegionType_MemoryController1.GetValue() ==
|
||||
(0x65 | KMemoryRegionAttr_NoUserMap));
|
||||
static_assert(KMemoryRegionType_MemoryController0.GetValue() ==
|
||||
(0x95 | KMemoryRegionAttr_NoUserMap));
|
||||
static_assert(KMemoryRegionType_PowerManagementController.GetValue() == (0x1A5));
|
||||
|
||||
static_assert(KMemoryRegionType_LegacyLpsDevices.GetValue() == 0xC5);
|
||||
|
||||
constexpr inline const auto NumLegacyLpsDevices = 7;
|
||||
constexpr inline const auto KMemoryRegionType_LegacyLpsExceptionVectors =
|
||||
KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 0);
|
||||
constexpr inline const auto KMemoryRegionType_LegacyLpsIram =
|
||||
KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 1);
|
||||
constexpr inline const auto KMemoryRegionType_LegacyLpsFlowController =
|
||||
KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 2);
|
||||
constexpr inline const auto KMemoryRegionType_LegacyLpsPrimaryICtlr =
|
||||
KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 3);
|
||||
constexpr inline const auto KMemoryRegionType_LegacyLpsSemaphore =
|
||||
KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 4);
|
||||
constexpr inline const auto KMemoryRegionType_LegacyLpsAtomics =
|
||||
KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 5);
|
||||
constexpr inline const auto KMemoryRegionType_LegacyLpsClkRst =
|
||||
KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 6);
|
||||
static_assert(KMemoryRegionType_LegacyLpsExceptionVectors.GetValue() == 0x3C5);
|
||||
static_assert(KMemoryRegionType_LegacyLpsIram.GetValue() == 0x5C5);
|
||||
static_assert(KMemoryRegionType_LegacyLpsFlowController.GetValue() == 0x6C5);
|
||||
static_assert(KMemoryRegionType_LegacyLpsPrimaryICtlr.GetValue() == 0x9C5);
|
||||
static_assert(KMemoryRegionType_LegacyLpsSemaphore.GetValue() == 0xAC5);
|
||||
static_assert(KMemoryRegionType_LegacyLpsAtomics.GetValue() == 0xCC5);
|
||||
static_assert(KMemoryRegionType_LegacyLpsClkRst.GetValue() == 0x11C5);
|
||||
164
src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
Normal file
164
src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
Normal file
@@ -0,0 +1,164 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <random>
|
||||
|
||||
#include "common/common_sizes.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/k_trace.h"
|
||||
|
||||
namespace Kernel::Board::Nintendo::Nx {
|
||||
|
||||
namespace impl {
|
||||
|
||||
constexpr const std::size_t RequiredNonSecureSystemMemorySizeVi = 0x2238 * 4 * 1024;
|
||||
constexpr const std::size_t RequiredNonSecureSystemMemorySizeNvservices = 0x710 * 4 * 1024;
|
||||
constexpr const std::size_t RequiredNonSecureSystemMemorySizeMisc = 0x80 * 4 * 1024;
|
||||
|
||||
} // namespace impl
|
||||
|
||||
constexpr const std::size_t RequiredNonSecureSystemMemorySize =
|
||||
impl::RequiredNonSecureSystemMemorySizeVi + impl::RequiredNonSecureSystemMemorySizeNvservices +
|
||||
impl::RequiredNonSecureSystemMemorySizeMisc;
|
||||
|
||||
namespace {
|
||||
|
||||
u32 GetMemoryModeForInit() {
|
||||
return 0x01;
|
||||
}
|
||||
|
||||
u32 GetMemorySizeForInit() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Smc::MemoryArrangement GetMemoryArrangeForInit() {
|
||||
switch (GetMemoryModeForInit() & 0x3F) {
|
||||
case 0x01:
|
||||
default:
|
||||
return Smc::MemoryArrangement_4GB;
|
||||
case 0x02:
|
||||
return Smc::MemoryArrangement_4GBForAppletDev;
|
||||
case 0x03:
|
||||
return Smc::MemoryArrangement_4GBForSystemDev;
|
||||
case 0x11:
|
||||
return Smc::MemoryArrangement_6GB;
|
||||
case 0x12:
|
||||
return Smc::MemoryArrangement_6GBForAppletDev;
|
||||
case 0x21:
|
||||
return Smc::MemoryArrangement_8GB;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Initialization.
|
||||
size_t KSystemControl::Init::GetIntendedMemorySize() {
|
||||
switch (GetMemorySizeForInit()) {
|
||||
case Smc::MemorySize_4GB:
|
||||
default: // All invalid modes should go to 4GB.
|
||||
return Common::Size_4_GB;
|
||||
case Smc::MemorySize_6GB:
|
||||
return Common::Size_6_GB;
|
||||
case Smc::MemorySize_8GB:
|
||||
return Common::Size_8_GB;
|
||||
}
|
||||
}
|
||||
|
||||
PAddr KSystemControl::Init::GetKernelPhysicalBaseAddress(u64 base_address) {
|
||||
return base_address;
|
||||
}
|
||||
|
||||
bool KSystemControl::Init::ShouldIncreaseThreadResourceLimit() {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::size_t KSystemControl::Init::GetApplicationPoolSize() {
|
||||
// Get the base pool size.
|
||||
const size_t base_pool_size = []() -> size_t {
|
||||
switch (GetMemoryArrangeForInit()) {
|
||||
case Smc::MemoryArrangement_4GB:
|
||||
default:
|
||||
return Common::Size_3285_MB;
|
||||
case Smc::MemoryArrangement_4GBForAppletDev:
|
||||
return Common::Size_2048_MB;
|
||||
case Smc::MemoryArrangement_4GBForSystemDev:
|
||||
return Common::Size_3285_MB;
|
||||
case Smc::MemoryArrangement_6GB:
|
||||
return Common::Size_4916_MB;
|
||||
case Smc::MemoryArrangement_6GBForAppletDev:
|
||||
return Common::Size_3285_MB;
|
||||
case Smc::MemoryArrangement_8GB:
|
||||
return Common::Size_4916_MB;
|
||||
}
|
||||
}();
|
||||
|
||||
// Return (possibly) adjusted size.
|
||||
return base_pool_size;
|
||||
}
|
||||
|
||||
size_t KSystemControl::Init::GetAppletPoolSize() {
|
||||
// Get the base pool size.
|
||||
const size_t base_pool_size = []() -> size_t {
|
||||
switch (GetMemoryArrangeForInit()) {
|
||||
case Smc::MemoryArrangement_4GB:
|
||||
default:
|
||||
return Common::Size_507_MB;
|
||||
case Smc::MemoryArrangement_4GBForAppletDev:
|
||||
return Common::Size_1554_MB;
|
||||
case Smc::MemoryArrangement_4GBForSystemDev:
|
||||
return Common::Size_448_MB;
|
||||
case Smc::MemoryArrangement_6GB:
|
||||
return Common::Size_562_MB;
|
||||
case Smc::MemoryArrangement_6GBForAppletDev:
|
||||
return Common::Size_2193_MB;
|
||||
case Smc::MemoryArrangement_8GB:
|
||||
return Common::Size_2193_MB;
|
||||
}
|
||||
}();
|
||||
|
||||
// Return (possibly) adjusted size.
|
||||
constexpr size_t ExtraSystemMemoryForAtmosphere = Common::Size_33_MB;
|
||||
return base_pool_size - ExtraSystemMemoryForAtmosphere - KTraceBufferSize;
|
||||
}
|
||||
|
||||
size_t KSystemControl::Init::GetMinimumNonSecureSystemPoolSize() {
|
||||
// Verify that our minimum is at least as large as Nintendo's.
|
||||
constexpr size_t MinimumSize = RequiredNonSecureSystemMemorySize;
|
||||
static_assert(MinimumSize >= 0x29C8000);
|
||||
|
||||
return MinimumSize;
|
||||
}
|
||||
|
||||
namespace {
|
||||
template <typename F>
|
||||
u64 GenerateUniformRange(u64 min, u64 max, F f) {
|
||||
// Handle the case where the difference is too large to represent.
|
||||
if (max == std::numeric_limits<u64>::max() && min == std::numeric_limits<u64>::min()) {
|
||||
return f();
|
||||
}
|
||||
|
||||
// Iterate until we get a value in range.
|
||||
const u64 range_size = ((max + 1) - min);
|
||||
const u64 effective_max = (std::numeric_limits<u64>::max() / range_size) * range_size;
|
||||
while (true) {
|
||||
if (const u64 rnd = f(); rnd < effective_max) {
|
||||
return min + (rnd % range_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
u64 KSystemControl::GenerateRandomU64() {
|
||||
static std::random_device device;
|
||||
static std::mt19937 gen(device());
|
||||
static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
|
||||
return distribution(gen);
|
||||
}
|
||||
|
||||
u64 KSystemControl::GenerateRandomRange(u64 min, u64 max) {
|
||||
return GenerateUniformRange(min, max, GenerateRandomU64);
|
||||
}
|
||||
|
||||
} // namespace Kernel::Board::Nintendo::Nx
|
||||
28
src/core/hle/kernel/board/nintendo/nx/k_system_control.h
Normal file
28
src/core/hle/kernel/board/nintendo/nx/k_system_control.h
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Kernel::Board::Nintendo::Nx {
|
||||
|
||||
class KSystemControl {
|
||||
public:
|
||||
class Init {
|
||||
public:
|
||||
// Initialization.
|
||||
static std::size_t GetIntendedMemorySize();
|
||||
static PAddr GetKernelPhysicalBaseAddress(u64 base_address);
|
||||
static bool ShouldIncreaseThreadResourceLimit();
|
||||
static std::size_t GetApplicationPoolSize();
|
||||
static std::size_t GetAppletPoolSize();
|
||||
static std::size_t GetMinimumNonSecureSystemPoolSize();
|
||||
};
|
||||
|
||||
static u64 GenerateRandomRange(u64 min, u64 max);
|
||||
static u64 GenerateRandomU64();
|
||||
};
|
||||
|
||||
} // namespace Kernel::Board::Nintendo::Nx
|
||||
26
src/core/hle/kernel/board/nintendo/nx/secure_monitor.h
Normal file
26
src/core/hle/kernel/board/nintendo/nx/secure_monitor.h
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Kernel::Board::Nintendo::Nx::Smc {
|
||||
|
||||
enum MemorySize {
|
||||
MemorySize_4GB = 0,
|
||||
MemorySize_6GB = 1,
|
||||
MemorySize_8GB = 2,
|
||||
};
|
||||
|
||||
enum MemoryArrangement {
|
||||
MemoryArrangement_4GB = 0,
|
||||
MemoryArrangement_4GBForAppletDev = 1,
|
||||
MemoryArrangement_4GBForSystemDev = 2,
|
||||
MemoryArrangement_6GB = 3,
|
||||
MemoryArrangement_6GBForAppletDev = 4,
|
||||
MemoryArrangement_8GB = 5,
|
||||
};
|
||||
|
||||
} // namespace Kernel::Board::Nintendo::Nx::Smc
|
||||
106
src/core/hle/kernel/k_address_space_info.cpp
Normal file
106
src/core/hle/kernel/k_address_space_info.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_sizes.h"
|
||||
#include "core/hle/kernel/k_address_space_info.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
namespace {
|
||||
|
||||
// clang-format off
|
||||
constexpr std::array<KAddressSpaceInfo, 13> AddressSpaceInfos{{
|
||||
{ .bit_width = 32, .address = Common::Size_2_MB , .size = Common::Size_1_GB - Common::Size_2_MB , .type = KAddressSpaceInfo::Type::MapSmall, },
|
||||
{ .bit_width = 32, .address = Common::Size_1_GB , .size = Common::Size_4_GB - Common::Size_1_GB , .type = KAddressSpaceInfo::Type::MapLarge, },
|
||||
{ .bit_width = 32, .address = Common::Size_Invalid, .size = Common::Size_1_GB , .type = KAddressSpaceInfo::Type::Alias, },
|
||||
{ .bit_width = 32, .address = Common::Size_Invalid, .size = Common::Size_1_GB , .type = KAddressSpaceInfo::Type::Heap, },
|
||||
{ .bit_width = 36, .address = Common::Size_128_MB , .size = Common::Size_2_GB - Common::Size_128_MB, .type = KAddressSpaceInfo::Type::MapSmall, },
|
||||
{ .bit_width = 36, .address = Common::Size_2_GB , .size = Common::Size_64_GB - Common::Size_2_GB , .type = KAddressSpaceInfo::Type::MapLarge, },
|
||||
{ .bit_width = 36, .address = Common::Size_Invalid, .size = Common::Size_6_GB , .type = KAddressSpaceInfo::Type::Heap, },
|
||||
{ .bit_width = 36, .address = Common::Size_Invalid, .size = Common::Size_6_GB , .type = KAddressSpaceInfo::Type::Alias, },
|
||||
{ .bit_width = 39, .address = Common::Size_128_MB , .size = Common::Size_512_GB - Common::Size_128_MB, .type = KAddressSpaceInfo::Type::Map39Bit, },
|
||||
{ .bit_width = 39, .address = Common::Size_Invalid, .size = Common::Size_64_GB , .type = KAddressSpaceInfo::Type::MapSmall },
|
||||
{ .bit_width = 39, .address = Common::Size_Invalid, .size = Common::Size_6_GB , .type = KAddressSpaceInfo::Type::Heap, },
|
||||
{ .bit_width = 39, .address = Common::Size_Invalid, .size = Common::Size_64_GB , .type = KAddressSpaceInfo::Type::Alias, },
|
||||
{ .bit_width = 39, .address = Common::Size_Invalid, .size = Common::Size_2_GB , .type = KAddressSpaceInfo::Type::Stack, },
|
||||
}};
|
||||
// clang-format on
|
||||
|
||||
constexpr bool IsAllowedIndexForAddress(std::size_t index) {
|
||||
return index < AddressSpaceInfos.size() &&
|
||||
AddressSpaceInfos[index].address != Common::Size_Invalid;
|
||||
}
|
||||
|
||||
using IndexArray =
|
||||
std::array<std::size_t, static_cast<std::size_t>(KAddressSpaceInfo::Type::Count)>;
|
||||
|
||||
constexpr IndexArray AddressSpaceIndices32Bit{
|
||||
0, 1, 0, 2, 0, 3,
|
||||
};
|
||||
|
||||
constexpr IndexArray AddressSpaceIndices36Bit{
|
||||
4, 5, 4, 6, 4, 7,
|
||||
};
|
||||
|
||||
constexpr IndexArray AddressSpaceIndices39Bit{
|
||||
9, 8, 8, 10, 12, 11,
|
||||
};
|
||||
|
||||
constexpr bool IsAllowed32BitType(KAddressSpaceInfo::Type type) {
|
||||
return type < KAddressSpaceInfo::Type::Count && type != KAddressSpaceInfo::Type::Map39Bit &&
|
||||
type != KAddressSpaceInfo::Type::Stack;
|
||||
}
|
||||
|
||||
constexpr bool IsAllowed36BitType(KAddressSpaceInfo::Type type) {
|
||||
return type < KAddressSpaceInfo::Type::Count && type != KAddressSpaceInfo::Type::Map39Bit &&
|
||||
type != KAddressSpaceInfo::Type::Stack;
|
||||
}
|
||||
|
||||
constexpr bool IsAllowed39BitType(KAddressSpaceInfo::Type type) {
|
||||
return type < KAddressSpaceInfo::Type::Count && type != KAddressSpaceInfo::Type::MapLarge;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
u64 KAddressSpaceInfo::GetAddressSpaceStart(std::size_t width, Type type) {
|
||||
const std::size_t index{static_cast<std::size_t>(type)};
|
||||
switch (width) {
|
||||
case 32:
|
||||
ASSERT(IsAllowed32BitType(type));
|
||||
ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices32Bit[index]));
|
||||
return AddressSpaceInfos[AddressSpaceIndices32Bit[index]].address;
|
||||
case 36:
|
||||
ASSERT(IsAllowed36BitType(type));
|
||||
ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices36Bit[index]));
|
||||
return AddressSpaceInfos[AddressSpaceIndices36Bit[index]].address;
|
||||
case 39:
|
||||
ASSERT(IsAllowed39BitType(type));
|
||||
ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices39Bit[index]));
|
||||
return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].address;
|
||||
}
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::size_t KAddressSpaceInfo::GetAddressSpaceSize(std::size_t width, Type type) {
|
||||
const std::size_t index{static_cast<std::size_t>(type)};
|
||||
switch (width) {
|
||||
case 32:
|
||||
ASSERT(IsAllowed32BitType(type));
|
||||
return AddressSpaceInfos[AddressSpaceIndices32Bit[index]].size;
|
||||
case 36:
|
||||
ASSERT(IsAllowed36BitType(type));
|
||||
return AddressSpaceInfos[AddressSpaceIndices36Bit[index]].size;
|
||||
case 39:
|
||||
ASSERT(IsAllowed39BitType(type));
|
||||
return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].size;
|
||||
}
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -2,20 +2,17 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
// This file references various implementation details from Atmosphere, an open-source firmware for
|
||||
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Kernel::Memory {
|
||||
namespace Kernel {
|
||||
|
||||
struct AddressSpaceInfo final {
|
||||
struct KAddressSpaceInfo final {
|
||||
enum class Type : u32 {
|
||||
Is32Bit = 0,
|
||||
Small64Bit = 1,
|
||||
Large64Bit = 2,
|
||||
MapSmall = 0,
|
||||
MapLarge = 1,
|
||||
Map39Bit = 2,
|
||||
Heap = 3,
|
||||
Stack = 4,
|
||||
Alias = 5,
|
||||
@@ -31,4 +28,4 @@ struct AddressSpaceInfo final {
|
||||
const Type type{};
|
||||
};
|
||||
|
||||
} // namespace Kernel::Memory
|
||||
} // namespace Kernel
|
||||
@@ -2,20 +2,17 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
// This file references various implementation details from Atmosphere, an open-source firmware for
|
||||
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/memory/memory_types.h"
|
||||
#include "core/hle/kernel/memory_types.h"
|
||||
#include "core/hle/kernel/svc_types.h"
|
||||
|
||||
namespace Kernel::Memory {
|
||||
namespace Kernel {
|
||||
|
||||
enum class MemoryState : u32 {
|
||||
enum class KMemoryState : u32 {
|
||||
None = 0,
|
||||
Mask = 0xFF,
|
||||
All = ~None,
|
||||
@@ -97,31 +94,31 @@ enum class MemoryState : u32 {
|
||||
FlagReferenceCounted | FlagCanDebug,
|
||||
CodeOut = static_cast<u32>(Svc::MemoryState::CodeOut) | FlagMapped | FlagReferenceCounted,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(MemoryState);
|
||||
DECLARE_ENUM_FLAG_OPERATORS(KMemoryState);
|
||||
|
||||
static_assert(static_cast<u32>(MemoryState::Free) == 0x00000000);
|
||||
static_assert(static_cast<u32>(MemoryState::Io) == 0x00002001);
|
||||
static_assert(static_cast<u32>(MemoryState::Static) == 0x00042002);
|
||||
static_assert(static_cast<u32>(MemoryState::Code) == 0x00DC7E03);
|
||||
static_assert(static_cast<u32>(MemoryState::CodeData) == 0x03FEBD04);
|
||||
static_assert(static_cast<u32>(MemoryState::Normal) == 0x037EBD05);
|
||||
static_assert(static_cast<u32>(MemoryState::Shared) == 0x00402006);
|
||||
static_assert(static_cast<u32>(MemoryState::AliasCode) == 0x00DD7E08);
|
||||
static_assert(static_cast<u32>(MemoryState::AliasCodeData) == 0x03FFBD09);
|
||||
static_assert(static_cast<u32>(MemoryState::Ipc) == 0x005C3C0A);
|
||||
static_assert(static_cast<u32>(MemoryState::Stack) == 0x005C3C0B);
|
||||
static_assert(static_cast<u32>(MemoryState::ThreadLocal) == 0x0040200C);
|
||||
static_assert(static_cast<u32>(MemoryState::Transferred) == 0x015C3C0D);
|
||||
static_assert(static_cast<u32>(MemoryState::SharedTransferred) == 0x005C380E);
|
||||
static_assert(static_cast<u32>(MemoryState::SharedCode) == 0x0040380F);
|
||||
static_assert(static_cast<u32>(MemoryState::Inaccessible) == 0x00000010);
|
||||
static_assert(static_cast<u32>(MemoryState::NonSecureIpc) == 0x005C3811);
|
||||
static_assert(static_cast<u32>(MemoryState::NonDeviceIpc) == 0x004C2812);
|
||||
static_assert(static_cast<u32>(MemoryState::Kernel) == 0x00002013);
|
||||
static_assert(static_cast<u32>(MemoryState::GeneratedCode) == 0x00402214);
|
||||
static_assert(static_cast<u32>(MemoryState::CodeOut) == 0x00402015);
|
||||
static_assert(static_cast<u32>(KMemoryState::Free) == 0x00000000);
|
||||
static_assert(static_cast<u32>(KMemoryState::Io) == 0x00002001);
|
||||
static_assert(static_cast<u32>(KMemoryState::Static) == 0x00042002);
|
||||
static_assert(static_cast<u32>(KMemoryState::Code) == 0x00DC7E03);
|
||||
static_assert(static_cast<u32>(KMemoryState::CodeData) == 0x03FEBD04);
|
||||
static_assert(static_cast<u32>(KMemoryState::Normal) == 0x037EBD05);
|
||||
static_assert(static_cast<u32>(KMemoryState::Shared) == 0x00402006);
|
||||
static_assert(static_cast<u32>(KMemoryState::AliasCode) == 0x00DD7E08);
|
||||
static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x03FFBD09);
|
||||
static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x005C3C0A);
|
||||
static_assert(static_cast<u32>(KMemoryState::Stack) == 0x005C3C0B);
|
||||
static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0040200C);
|
||||
static_assert(static_cast<u32>(KMemoryState::Transferred) == 0x015C3C0D);
|
||||
static_assert(static_cast<u32>(KMemoryState::SharedTransferred) == 0x005C380E);
|
||||
static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0040380F);
|
||||
static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010);
|
||||
static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x005C3811);
|
||||
static_assert(static_cast<u32>(KMemoryState::NonDeviceIpc) == 0x004C2812);
|
||||
static_assert(static_cast<u32>(KMemoryState::Kernel) == 0x00002013);
|
||||
static_assert(static_cast<u32>(KMemoryState::GeneratedCode) == 0x00402214);
|
||||
static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x00402015);
|
||||
|
||||
enum class MemoryPermission : u8 {
|
||||
enum class KMemoryPermission : u8 {
|
||||
None = 0,
|
||||
Mask = static_cast<u8>(~None),
|
||||
|
||||
@@ -135,9 +132,9 @@ enum class MemoryPermission : u8 {
|
||||
UserMask = static_cast<u8>(Svc::MemoryPermission::Read | Svc::MemoryPermission::Write |
|
||||
Svc::MemoryPermission::Execute),
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission);
|
||||
DECLARE_ENUM_FLAG_OPERATORS(KMemoryPermission);
|
||||
|
||||
enum class MemoryAttribute : u8 {
|
||||
enum class KMemoryAttribute : u8 {
|
||||
None = 0x00,
|
||||
Mask = 0x7F,
|
||||
All = Mask,
|
||||
@@ -152,18 +149,18 @@ enum class MemoryAttribute : u8 {
|
||||
LockedAndIpcLocked = Locked | IpcLocked,
|
||||
DeviceSharedAndUncached = DeviceShared | Uncached
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(MemoryAttribute);
|
||||
DECLARE_ENUM_FLAG_OPERATORS(KMemoryAttribute);
|
||||
|
||||
static_assert((static_cast<u8>(MemoryAttribute::Mask) &
|
||||
static_cast<u8>(MemoryAttribute::DontCareMask)) == 0);
|
||||
static_assert((static_cast<u8>(KMemoryAttribute::Mask) &
|
||||
static_cast<u8>(KMemoryAttribute::DontCareMask)) == 0);
|
||||
|
||||
struct MemoryInfo {
|
||||
struct KMemoryInfo {
|
||||
VAddr addr{};
|
||||
std::size_t size{};
|
||||
MemoryState state{};
|
||||
MemoryPermission perm{};
|
||||
MemoryAttribute attribute{};
|
||||
MemoryPermission original_perm{};
|
||||
KMemoryState state{};
|
||||
KMemoryPermission perm{};
|
||||
KMemoryAttribute attribute{};
|
||||
KMemoryPermission original_perm{};
|
||||
u16 ipc_lock_count{};
|
||||
u16 device_use_count{};
|
||||
|
||||
@@ -171,9 +168,9 @@ struct MemoryInfo {
|
||||
return {
|
||||
addr,
|
||||
size,
|
||||
static_cast<Svc::MemoryState>(state & MemoryState::Mask),
|
||||
static_cast<Svc::MemoryAttribute>(attribute & MemoryAttribute::Mask),
|
||||
static_cast<Svc::MemoryPermission>(perm & MemoryPermission::UserMask),
|
||||
static_cast<Svc::MemoryState>(state & KMemoryState::Mask),
|
||||
static_cast<Svc::MemoryAttribute>(attribute & KMemoryAttribute::Mask),
|
||||
static_cast<Svc::MemoryPermission>(perm & KMemoryPermission::UserMask),
|
||||
ipc_lock_count,
|
||||
device_use_count,
|
||||
};
|
||||
@@ -196,21 +193,21 @@ struct MemoryInfo {
|
||||
}
|
||||
};
|
||||
|
||||
class MemoryBlock final {
|
||||
friend class MemoryBlockManager;
|
||||
class KMemoryBlock final {
|
||||
friend class KMemoryBlockManager;
|
||||
|
||||
private:
|
||||
VAddr addr{};
|
||||
std::size_t num_pages{};
|
||||
MemoryState state{MemoryState::None};
|
||||
KMemoryState state{KMemoryState::None};
|
||||
u16 ipc_lock_count{};
|
||||
u16 device_use_count{};
|
||||
MemoryPermission perm{MemoryPermission::None};
|
||||
MemoryPermission original_perm{MemoryPermission::None};
|
||||
MemoryAttribute attribute{MemoryAttribute::None};
|
||||
KMemoryPermission perm{KMemoryPermission::None};
|
||||
KMemoryPermission original_perm{KMemoryPermission::None};
|
||||
KMemoryAttribute attribute{KMemoryAttribute::None};
|
||||
|
||||
public:
|
||||
static constexpr int Compare(const MemoryBlock& lhs, const MemoryBlock& rhs) {
|
||||
static constexpr int Compare(const KMemoryBlock& lhs, const KMemoryBlock& rhs) {
|
||||
if (lhs.GetAddress() < rhs.GetAddress()) {
|
||||
return -1;
|
||||
} else if (lhs.GetAddress() <= rhs.GetLastAddress()) {
|
||||
@@ -221,9 +218,9 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr MemoryBlock() = default;
|
||||
constexpr MemoryBlock(VAddr addr_, std::size_t num_pages_, MemoryState state_,
|
||||
MemoryPermission perm_, MemoryAttribute attribute_)
|
||||
constexpr KMemoryBlock() = default;
|
||||
constexpr KMemoryBlock(VAddr addr_, std::size_t num_pages_, KMemoryState state_,
|
||||
KMemoryPermission perm_, KMemoryAttribute attribute_)
|
||||
: addr{addr_}, num_pages(num_pages_), state{state_}, perm{perm_}, attribute{attribute_} {}
|
||||
|
||||
constexpr VAddr GetAddress() const {
|
||||
@@ -246,40 +243,40 @@ public:
|
||||
return GetEndAddress() - 1;
|
||||
}
|
||||
|
||||
constexpr MemoryInfo GetMemoryInfo() const {
|
||||
constexpr KMemoryInfo GetMemoryInfo() const {
|
||||
return {
|
||||
GetAddress(), GetSize(), state, perm,
|
||||
attribute, original_perm, ipc_lock_count, device_use_count,
|
||||
};
|
||||
}
|
||||
|
||||
void ShareToDevice(MemoryPermission /*new_perm*/) {
|
||||
ASSERT((attribute & MemoryAttribute::DeviceShared) == MemoryAttribute::DeviceShared ||
|
||||
void ShareToDevice(KMemoryPermission /*new_perm*/) {
|
||||
ASSERT((attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared ||
|
||||
device_use_count == 0);
|
||||
attribute |= MemoryAttribute::DeviceShared;
|
||||
attribute |= KMemoryAttribute::DeviceShared;
|
||||
const u16 new_use_count{++device_use_count};
|
||||
ASSERT(new_use_count > 0);
|
||||
}
|
||||
|
||||
void UnshareToDevice(MemoryPermission /*new_perm*/) {
|
||||
ASSERT((attribute & MemoryAttribute::DeviceShared) == MemoryAttribute::DeviceShared);
|
||||
void UnshareToDevice(KMemoryPermission /*new_perm*/) {
|
||||
ASSERT((attribute & KMemoryAttribute::DeviceShared) == KMemoryAttribute::DeviceShared);
|
||||
const u16 prev_use_count{device_use_count--};
|
||||
ASSERT(prev_use_count > 0);
|
||||
if (prev_use_count == 1) {
|
||||
attribute &= ~MemoryAttribute::DeviceShared;
|
||||
attribute &= ~KMemoryAttribute::DeviceShared;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr bool HasProperties(MemoryState s, MemoryPermission p, MemoryAttribute a) const {
|
||||
constexpr MemoryAttribute AttributeIgnoreMask{MemoryAttribute::DontCareMask |
|
||||
MemoryAttribute::IpcLocked |
|
||||
MemoryAttribute::DeviceShared};
|
||||
constexpr bool HasProperties(KMemoryState s, KMemoryPermission p, KMemoryAttribute a) const {
|
||||
constexpr KMemoryAttribute AttributeIgnoreMask{KMemoryAttribute::DontCareMask |
|
||||
KMemoryAttribute::IpcLocked |
|
||||
KMemoryAttribute::DeviceShared};
|
||||
return state == s && perm == p &&
|
||||
(attribute | AttributeIgnoreMask) == (a | AttributeIgnoreMask);
|
||||
}
|
||||
|
||||
constexpr bool HasSameProperties(const MemoryBlock& rhs) const {
|
||||
constexpr bool HasSameProperties(const KMemoryBlock& rhs) const {
|
||||
return state == rhs.state && perm == rhs.perm && original_perm == rhs.original_perm &&
|
||||
attribute == rhs.attribute && ipc_lock_count == rhs.ipc_lock_count &&
|
||||
device_use_count == rhs.device_use_count;
|
||||
@@ -296,25 +293,25 @@ private:
|
||||
num_pages += count;
|
||||
}
|
||||
|
||||
constexpr void Update(MemoryState new_state, MemoryPermission new_perm,
|
||||
MemoryAttribute new_attribute) {
|
||||
ASSERT(original_perm == MemoryPermission::None);
|
||||
ASSERT((attribute & MemoryAttribute::IpcLocked) == MemoryAttribute::None);
|
||||
constexpr void Update(KMemoryState new_state, KMemoryPermission new_perm,
|
||||
KMemoryAttribute new_attribute) {
|
||||
ASSERT(original_perm == KMemoryPermission::None);
|
||||
ASSERT((attribute & KMemoryAttribute::IpcLocked) == KMemoryAttribute::None);
|
||||
|
||||
state = new_state;
|
||||
perm = new_perm;
|
||||
|
||||
attribute = static_cast<MemoryAttribute>(
|
||||
attribute = static_cast<KMemoryAttribute>(
|
||||
new_attribute |
|
||||
(attribute & (MemoryAttribute::IpcLocked | MemoryAttribute::DeviceShared)));
|
||||
(attribute & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)));
|
||||
}
|
||||
|
||||
constexpr MemoryBlock Split(VAddr split_addr) {
|
||||
constexpr KMemoryBlock Split(VAddr split_addr) {
|
||||
ASSERT(GetAddress() < split_addr);
|
||||
ASSERT(Contains(split_addr));
|
||||
ASSERT(Common::IsAligned(split_addr, PageSize));
|
||||
|
||||
MemoryBlock block;
|
||||
KMemoryBlock block;
|
||||
block.addr = addr;
|
||||
block.num_pages = (split_addr - GetAddress()) / PageSize;
|
||||
block.state = state;
|
||||
@@ -330,6 +327,6 @@ private:
|
||||
return block;
|
||||
}
|
||||
};
|
||||
static_assert(std::is_trivially_destructible<MemoryBlock>::value);
|
||||
static_assert(std::is_trivially_destructible<KMemoryBlock>::value);
|
||||
|
||||
} // namespace Kernel::Memory
|
||||
} // namespace Kernel
|
||||
@@ -2,19 +2,19 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/kernel/memory/memory_block_manager.h"
|
||||
#include "core/hle/kernel/memory/memory_types.h"
|
||||
#include "core/hle/kernel/k_memory_block_manager.h"
|
||||
#include "core/hle/kernel/memory_types.h"
|
||||
|
||||
namespace Kernel::Memory {
|
||||
namespace Kernel {
|
||||
|
||||
MemoryBlockManager::MemoryBlockManager(VAddr start_addr, VAddr end_addr)
|
||||
KMemoryBlockManager::KMemoryBlockManager(VAddr start_addr, VAddr end_addr)
|
||||
: start_addr{start_addr}, end_addr{end_addr} {
|
||||
const u64 num_pages{(end_addr - start_addr) / PageSize};
|
||||
memory_block_tree.emplace_back(start_addr, num_pages, MemoryState::Free, MemoryPermission::None,
|
||||
MemoryAttribute::None);
|
||||
memory_block_tree.emplace_back(start_addr, num_pages, KMemoryState::Free,
|
||||
KMemoryPermission::None, KMemoryAttribute::None);
|
||||
}
|
||||
|
||||
MemoryBlockManager::iterator MemoryBlockManager::FindIterator(VAddr addr) {
|
||||
KMemoryBlockManager::iterator KMemoryBlockManager::FindIterator(VAddr addr) {
|
||||
auto node{memory_block_tree.begin()};
|
||||
while (node != end()) {
|
||||
const VAddr end_addr{node->GetNumPages() * PageSize + node->GetAddress()};
|
||||
@@ -26,9 +26,9 @@ MemoryBlockManager::iterator MemoryBlockManager::FindIterator(VAddr addr) {
|
||||
return end();
|
||||
}
|
||||
|
||||
VAddr MemoryBlockManager::FindFreeArea(VAddr region_start, std::size_t region_num_pages,
|
||||
std::size_t num_pages, std::size_t align, std::size_t offset,
|
||||
std::size_t guard_pages) {
|
||||
VAddr KMemoryBlockManager::FindFreeArea(VAddr region_start, std::size_t region_num_pages,
|
||||
std::size_t num_pages, std::size_t align,
|
||||
std::size_t offset, std::size_t guard_pages) {
|
||||
if (num_pages == 0) {
|
||||
return {};
|
||||
}
|
||||
@@ -41,7 +41,7 @@ VAddr MemoryBlockManager::FindFreeArea(VAddr region_start, std::size_t region_nu
|
||||
break;
|
||||
}
|
||||
|
||||
if (info.state != MemoryState::Free) {
|
||||
if (info.state != KMemoryState::Free) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -63,17 +63,17 @@ VAddr MemoryBlockManager::FindFreeArea(VAddr region_start, std::size_t region_nu
|
||||
return {};
|
||||
}
|
||||
|
||||
void MemoryBlockManager::Update(VAddr addr, std::size_t num_pages, MemoryState prev_state,
|
||||
MemoryPermission prev_perm, MemoryAttribute prev_attribute,
|
||||
MemoryState state, MemoryPermission perm,
|
||||
MemoryAttribute attribute) {
|
||||
void KMemoryBlockManager::Update(VAddr addr, std::size_t num_pages, KMemoryState prev_state,
|
||||
KMemoryPermission prev_perm, KMemoryAttribute prev_attribute,
|
||||
KMemoryState state, KMemoryPermission perm,
|
||||
KMemoryAttribute attribute) {
|
||||
const VAddr end_addr{addr + num_pages * PageSize};
|
||||
iterator node{memory_block_tree.begin()};
|
||||
|
||||
prev_attribute |= MemoryAttribute::IpcAndDeviceMapped;
|
||||
prev_attribute |= KMemoryAttribute::IpcAndDeviceMapped;
|
||||
|
||||
while (node != memory_block_tree.end()) {
|
||||
MemoryBlock* block{&(*node)};
|
||||
KMemoryBlock* block{&(*node)};
|
||||
iterator next_node{std::next(node)};
|
||||
const VAddr cur_addr{block->GetAddress()};
|
||||
const VAddr cur_end_addr{block->GetNumPages() * PageSize + cur_addr};
|
||||
@@ -106,13 +106,13 @@ void MemoryBlockManager::Update(VAddr addr, std::size_t num_pages, MemoryState p
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryBlockManager::Update(VAddr addr, std::size_t num_pages, MemoryState state,
|
||||
MemoryPermission perm, MemoryAttribute attribute) {
|
||||
void KMemoryBlockManager::Update(VAddr addr, std::size_t num_pages, KMemoryState state,
|
||||
KMemoryPermission perm, KMemoryAttribute attribute) {
|
||||
const VAddr end_addr{addr + num_pages * PageSize};
|
||||
iterator node{memory_block_tree.begin()};
|
||||
|
||||
while (node != memory_block_tree.end()) {
|
||||
MemoryBlock* block{&(*node)};
|
||||
KMemoryBlock* block{&(*node)};
|
||||
iterator next_node{std::next(node)};
|
||||
const VAddr cur_addr{block->GetAddress()};
|
||||
const VAddr cur_end_addr{block->GetNumPages() * PageSize + cur_addr};
|
||||
@@ -141,13 +141,13 @@ void MemoryBlockManager::Update(VAddr addr, std::size_t num_pages, MemoryState s
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryBlockManager::UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&& lock_func,
|
||||
MemoryPermission perm) {
|
||||
void KMemoryBlockManager::UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&& lock_func,
|
||||
KMemoryPermission perm) {
|
||||
const VAddr end_addr{addr + num_pages * PageSize};
|
||||
iterator node{memory_block_tree.begin()};
|
||||
|
||||
while (node != memory_block_tree.end()) {
|
||||
MemoryBlock* block{&(*node)};
|
||||
KMemoryBlock* block{&(*node)};
|
||||
iterator next_node{std::next(node)};
|
||||
const VAddr cur_addr{block->GetAddress()};
|
||||
const VAddr cur_end_addr{block->GetNumPages() * PageSize + cur_addr};
|
||||
@@ -176,9 +176,9 @@ void MemoryBlockManager::UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryBlockManager::IterateForRange(VAddr start, VAddr end, IterateFunc&& func) {
|
||||
void KMemoryBlockManager::IterateForRange(VAddr start, VAddr end, IterateFunc&& func) {
|
||||
const_iterator it{FindIterator(start)};
|
||||
MemoryInfo info{};
|
||||
KMemoryInfo info{};
|
||||
do {
|
||||
info = it->GetMemoryInfo();
|
||||
func(info);
|
||||
@@ -186,8 +186,8 @@ void MemoryBlockManager::IterateForRange(VAddr start, VAddr end, IterateFunc&& f
|
||||
} while (info.addr + info.size - 1 < end - 1 && it != cend());
|
||||
}
|
||||
|
||||
void MemoryBlockManager::MergeAdjacent(iterator it, iterator& next_it) {
|
||||
MemoryBlock* block{&(*it)};
|
||||
void KMemoryBlockManager::MergeAdjacent(iterator it, iterator& next_it) {
|
||||
KMemoryBlock* block{&(*it)};
|
||||
|
||||
auto EraseIt = [&](const iterator it_to_erase) {
|
||||
if (next_it == it_to_erase) {
|
||||
@@ -197,7 +197,7 @@ void MemoryBlockManager::MergeAdjacent(iterator it, iterator& next_it) {
|
||||
};
|
||||
|
||||
if (it != memory_block_tree.begin()) {
|
||||
MemoryBlock* prev{&(*std::prev(it))};
|
||||
KMemoryBlock* prev{&(*std::prev(it))};
|
||||
|
||||
if (block->HasSameProperties(*prev)) {
|
||||
const iterator prev_it{std::prev(it)};
|
||||
@@ -211,7 +211,7 @@ void MemoryBlockManager::MergeAdjacent(iterator it, iterator& next_it) {
|
||||
}
|
||||
|
||||
if (it != cend()) {
|
||||
const MemoryBlock* const next{&(*std::next(it))};
|
||||
const KMemoryBlock* const next{&(*std::next(it))};
|
||||
|
||||
if (block->HasSameProperties(*next)) {
|
||||
block->Add(next->GetNumPages());
|
||||
@@ -220,4 +220,4 @@ void MemoryBlockManager::MergeAdjacent(iterator it, iterator& next_it) {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Kernel::Memory
|
||||
} // namespace Kernel
|
||||
@@ -8,18 +8,18 @@
|
||||
#include <list>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/memory/memory_block.h"
|
||||
#include "core/hle/kernel/k_memory_block.h"
|
||||
|
||||
namespace Kernel::Memory {
|
||||
namespace Kernel {
|
||||
|
||||
class MemoryBlockManager final {
|
||||
class KMemoryBlockManager final {
|
||||
public:
|
||||
using MemoryBlockTree = std::list<MemoryBlock>;
|
||||
using MemoryBlockTree = std::list<KMemoryBlock>;
|
||||
using iterator = MemoryBlockTree::iterator;
|
||||
using const_iterator = MemoryBlockTree::const_iterator;
|
||||
|
||||
public:
|
||||
MemoryBlockManager(VAddr start_addr, VAddr end_addr);
|
||||
KMemoryBlockManager(VAddr start_addr, VAddr end_addr);
|
||||
|
||||
iterator end() {
|
||||
return memory_block_tree.end();
|
||||
@@ -36,21 +36,22 @@ public:
|
||||
VAddr FindFreeArea(VAddr region_start, std::size_t region_num_pages, std::size_t num_pages,
|
||||
std::size_t align, std::size_t offset, std::size_t guard_pages);
|
||||
|
||||
void Update(VAddr addr, std::size_t num_pages, MemoryState prev_state,
|
||||
MemoryPermission prev_perm, MemoryAttribute prev_attribute, MemoryState state,
|
||||
MemoryPermission perm, MemoryAttribute attribute);
|
||||
void Update(VAddr addr, std::size_t num_pages, KMemoryState prev_state,
|
||||
KMemoryPermission prev_perm, KMemoryAttribute prev_attribute, KMemoryState state,
|
||||
KMemoryPermission perm, KMemoryAttribute attribute);
|
||||
|
||||
void Update(VAddr addr, std::size_t num_pages, MemoryState state,
|
||||
MemoryPermission perm = MemoryPermission::None,
|
||||
MemoryAttribute attribute = MemoryAttribute::None);
|
||||
void Update(VAddr addr, std::size_t num_pages, KMemoryState state,
|
||||
KMemoryPermission perm = KMemoryPermission::None,
|
||||
KMemoryAttribute attribute = KMemoryAttribute::None);
|
||||
|
||||
using LockFunc = std::function<void(iterator, MemoryPermission)>;
|
||||
void UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&& lock_func, MemoryPermission perm);
|
||||
using LockFunc = std::function<void(iterator, KMemoryPermission)>;
|
||||
void UpdateLock(VAddr addr, std::size_t num_pages, LockFunc&& lock_func,
|
||||
KMemoryPermission perm);
|
||||
|
||||
using IterateFunc = std::function<void(const MemoryInfo&)>;
|
||||
using IterateFunc = std::function<void(const KMemoryInfo&)>;
|
||||
void IterateForRange(VAddr start, VAddr end, IterateFunc&& func);
|
||||
|
||||
MemoryBlock& FindBlock(VAddr addr) {
|
||||
KMemoryBlock& FindBlock(VAddr addr) {
|
||||
return *FindIterator(addr);
|
||||
}
|
||||
|
||||
@@ -63,4 +64,4 @@ private:
|
||||
MemoryBlockTree memory_block_tree;
|
||||
};
|
||||
|
||||
} // namespace Kernel::Memory
|
||||
} // namespace Kernel
|
||||
199
src/core/hle/kernel/k_memory_layout.board.nintendo_nx.cpp
Normal file
199
src/core/hle/kernel/k_memory_layout.board.nintendo_nx.cpp
Normal file
@@ -0,0 +1,199 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "core/hle/kernel/k_memory_layout.h"
|
||||
#include "core/hle/kernel/k_memory_manager.h"
|
||||
#include "core/hle/kernel/k_system_control.h"
|
||||
#include "core/hle/kernel/k_trace.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr size_t CarveoutAlignment = 0x20000;
|
||||
constexpr size_t CarveoutSizeMax = (512ULL * 1024 * 1024) - CarveoutAlignment;
|
||||
|
||||
bool SetupPowerManagementControllerMemoryRegion(KMemoryLayout& memory_layout) {
|
||||
// Above firmware 2.0.0, the PMC is not mappable.
|
||||
return memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||
0x7000E000, 0x400, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap) &&
|
||||
memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||
0x7000E400, 0xC00,
|
||||
KMemoryRegionType_PowerManagementController | KMemoryRegionAttr_NoUserMap);
|
||||
}
|
||||
|
||||
void InsertPoolPartitionRegionIntoBothTrees(KMemoryLayout& memory_layout, size_t start, size_t size,
|
||||
KMemoryRegionType phys_type,
|
||||
KMemoryRegionType virt_type, u32& cur_attr) {
|
||||
const u32 attr = cur_attr++;
|
||||
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(start, size,
|
||||
static_cast<u32>(phys_type), attr));
|
||||
const KMemoryRegion* phys = memory_layout.GetPhysicalMemoryRegionTree().FindByTypeAndAttribute(
|
||||
static_cast<u32>(phys_type), attr);
|
||||
ASSERT(phys != nullptr);
|
||||
ASSERT(phys->GetEndAddress() != 0);
|
||||
ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(phys->GetPairAddress(), size,
|
||||
static_cast<u32>(virt_type), attr));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Init {
|
||||
|
||||
void SetupDevicePhysicalMemoryRegions(KMemoryLayout& memory_layout) {
|
||||
ASSERT(SetupPowerManagementControllerMemoryRegion(memory_layout));
|
||||
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||
0x70019000, 0x1000, KMemoryRegionType_MemoryController | KMemoryRegionAttr_NoUserMap));
|
||||
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||
0x7001C000, 0x1000, KMemoryRegionType_MemoryController0 | KMemoryRegionAttr_NoUserMap));
|
||||
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||
0x7001D000, 0x1000, KMemoryRegionType_MemoryController1 | KMemoryRegionAttr_NoUserMap));
|
||||
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||
0x50040000, 0x1000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap));
|
||||
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||
0x50041000, 0x1000,
|
||||
KMemoryRegionType_InterruptDistributor | KMemoryRegionAttr_ShouldKernelMap));
|
||||
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||
0x50042000, 0x1000,
|
||||
KMemoryRegionType_InterruptCpuInterface | KMemoryRegionAttr_ShouldKernelMap));
|
||||
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||
0x50043000, 0x1D000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap));
|
||||
|
||||
// Map IRAM unconditionally, to support debug-logging-to-iram build config.
|
||||
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||
0x40000000, 0x40000, KMemoryRegionType_LegacyLpsIram | KMemoryRegionAttr_ShouldKernelMap));
|
||||
|
||||
// Above firmware 2.0.0, prevent mapping the bpmp exception vectors or the ipatch region.
|
||||
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||
0x6000F000, 0x1000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap));
|
||||
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||
0x6001DC00, 0x400, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap));
|
||||
}
|
||||
|
||||
void SetupDramPhysicalMemoryRegions(KMemoryLayout& memory_layout) {
|
||||
const size_t intended_memory_size = KSystemControl::Init::GetIntendedMemorySize();
|
||||
const PAddr physical_memory_base_address =
|
||||
KSystemControl::Init::GetKernelPhysicalBaseAddress(DramPhysicalAddress);
|
||||
|
||||
// Insert blocks into the tree.
|
||||
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||
physical_memory_base_address, intended_memory_size, KMemoryRegionType_Dram));
|
||||
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||
physical_memory_base_address, ReservedEarlyDramSize, KMemoryRegionType_DramReservedEarly));
|
||||
|
||||
// Insert the KTrace block at the end of Dram, if KTrace is enabled.
|
||||
static_assert(!IsKTraceEnabled || KTraceBufferSize > 0);
|
||||
if constexpr (IsKTraceEnabled) {
|
||||
const PAddr ktrace_buffer_phys_addr =
|
||||
physical_memory_base_address + intended_memory_size - KTraceBufferSize;
|
||||
ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
|
||||
ktrace_buffer_phys_addr, KTraceBufferSize, KMemoryRegionType_KernelTraceBuffer));
|
||||
}
|
||||
}
|
||||
|
||||
void SetupPoolPartitionMemoryRegions(KMemoryLayout& memory_layout) {
|
||||
// Start by identifying the extents of the DRAM memory region.
|
||||
const auto dram_extents = memory_layout.GetMainMemoryPhysicalExtents();
|
||||
ASSERT(dram_extents.GetEndAddress() != 0);
|
||||
|
||||
// Determine the end of the pool region.
|
||||
const u64 pool_end = dram_extents.GetEndAddress() - KTraceBufferSize;
|
||||
|
||||
// Find the start of the kernel DRAM region.
|
||||
const KMemoryRegion* kernel_dram_region =
|
||||
memory_layout.GetPhysicalMemoryRegionTree().FindFirstDerived(
|
||||
KMemoryRegionType_DramKernelBase);
|
||||
ASSERT(kernel_dram_region != nullptr);
|
||||
|
||||
const u64 kernel_dram_start = kernel_dram_region->GetAddress();
|
||||
ASSERT(Common::IsAligned(kernel_dram_start, CarveoutAlignment));
|
||||
|
||||
// Find the start of the pool partitions region.
|
||||
const KMemoryRegion* pool_partitions_region =
|
||||
memory_layout.GetPhysicalMemoryRegionTree().FindByTypeAndAttribute(
|
||||
KMemoryRegionType_DramPoolPartition, 0);
|
||||
ASSERT(pool_partitions_region != nullptr);
|
||||
const u64 pool_partitions_start = pool_partitions_region->GetAddress();
|
||||
|
||||
// Setup the pool partition layouts.
|
||||
// On 5.0.0+, setup modern 4-pool-partition layout.
|
||||
|
||||
// Get Application and Applet pool sizes.
|
||||
const size_t application_pool_size = KSystemControl::Init::GetApplicationPoolSize();
|
||||
const size_t applet_pool_size = KSystemControl::Init::GetAppletPoolSize();
|
||||
const size_t unsafe_system_pool_min_size =
|
||||
KSystemControl::Init::GetMinimumNonSecureSystemPoolSize();
|
||||
|
||||
// Decide on starting addresses for our pools.
|
||||
const u64 application_pool_start = pool_end - application_pool_size;
|
||||
const u64 applet_pool_start = application_pool_start - applet_pool_size;
|
||||
const u64 unsafe_system_pool_start = std::min(
|
||||
kernel_dram_start + CarveoutSizeMax,
|
||||
Common::AlignDown(applet_pool_start - unsafe_system_pool_min_size, CarveoutAlignment));
|
||||
const size_t unsafe_system_pool_size = applet_pool_start - unsafe_system_pool_start;
|
||||
|
||||
// We want to arrange application pool depending on where the middle of dram is.
|
||||
const u64 dram_midpoint = (dram_extents.GetAddress() + dram_extents.GetEndAddress()) / 2;
|
||||
u32 cur_pool_attr = 0;
|
||||
size_t total_overhead_size = 0;
|
||||
if (dram_extents.GetEndAddress() <= dram_midpoint || dram_midpoint <= application_pool_start) {
|
||||
InsertPoolPartitionRegionIntoBothTrees(
|
||||
memory_layout, application_pool_start, application_pool_size,
|
||||
KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool,
|
||||
cur_pool_attr);
|
||||
total_overhead_size +=
|
||||
KMemoryManager::CalculateManagementOverheadSize(application_pool_size);
|
||||
} else {
|
||||
const size_t first_application_pool_size = dram_midpoint - application_pool_start;
|
||||
const size_t second_application_pool_size =
|
||||
application_pool_start + application_pool_size - dram_midpoint;
|
||||
InsertPoolPartitionRegionIntoBothTrees(
|
||||
memory_layout, application_pool_start, first_application_pool_size,
|
||||
KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool,
|
||||
cur_pool_attr);
|
||||
InsertPoolPartitionRegionIntoBothTrees(
|
||||
memory_layout, dram_midpoint, second_application_pool_size,
|
||||
KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool,
|
||||
cur_pool_attr);
|
||||
total_overhead_size +=
|
||||
KMemoryManager::CalculateManagementOverheadSize(first_application_pool_size);
|
||||
total_overhead_size +=
|
||||
KMemoryManager::CalculateManagementOverheadSize(second_application_pool_size);
|
||||
}
|
||||
|
||||
// Insert the applet pool.
|
||||
InsertPoolPartitionRegionIntoBothTrees(memory_layout, applet_pool_start, applet_pool_size,
|
||||
KMemoryRegionType_DramAppletPool,
|
||||
KMemoryRegionType_VirtualDramAppletPool, cur_pool_attr);
|
||||
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(applet_pool_size);
|
||||
|
||||
// Insert the nonsecure system pool.
|
||||
InsertPoolPartitionRegionIntoBothTrees(
|
||||
memory_layout, unsafe_system_pool_start, unsafe_system_pool_size,
|
||||
KMemoryRegionType_DramSystemNonSecurePool, KMemoryRegionType_VirtualDramSystemNonSecurePool,
|
||||
cur_pool_attr);
|
||||
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(unsafe_system_pool_size);
|
||||
|
||||
// Insert the pool management region.
|
||||
total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(
|
||||
(unsafe_system_pool_start - pool_partitions_start) - total_overhead_size);
|
||||
const u64 pool_management_start = unsafe_system_pool_start - total_overhead_size;
|
||||
const size_t pool_management_size = total_overhead_size;
|
||||
u32 pool_management_attr = 0;
|
||||
InsertPoolPartitionRegionIntoBothTrees(
|
||||
memory_layout, pool_management_start, pool_management_size,
|
||||
KMemoryRegionType_DramPoolManagement, KMemoryRegionType_VirtualDramPoolManagement,
|
||||
pool_management_attr);
|
||||
|
||||
// Insert the system pool.
|
||||
const u64 system_pool_size = pool_management_start - pool_partitions_start;
|
||||
InsertPoolPartitionRegionIntoBothTrees(memory_layout, pool_partitions_start, system_pool_size,
|
||||
KMemoryRegionType_DramSystemPool,
|
||||
KMemoryRegionType_VirtualDramSystemPool, cur_pool_attr);
|
||||
}
|
||||
|
||||
} // namespace Init
|
||||
|
||||
} // namespace Kernel
|
||||
166
src/core/hle/kernel/k_memory_layout.cpp
Normal file
166
src/core/hle/kernel/k_memory_layout.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "core/hle/kernel/k_memory_layout.h"
|
||||
#include "core/hle/kernel/k_system_control.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename... Args>
|
||||
KMemoryRegion* AllocateRegion(KMemoryRegionAllocator& memory_region_allocator, Args&&... args) {
|
||||
return memory_region_allocator.Allocate(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
KMemoryRegionTree::KMemoryRegionTree(KMemoryRegionAllocator& memory_region_allocator_)
|
||||
: memory_region_allocator{memory_region_allocator_} {}
|
||||
|
||||
void KMemoryRegionTree::InsertDirectly(u64 address, u64 last_address, u32 attr, u32 type_id) {
|
||||
this->insert(*AllocateRegion(memory_region_allocator, address, last_address, attr, type_id));
|
||||
}
|
||||
|
||||
bool KMemoryRegionTree::Insert(u64 address, size_t size, u32 type_id, u32 new_attr, u32 old_attr) {
|
||||
// Locate the memory region that contains the address.
|
||||
KMemoryRegion* found = this->FindModifiable(address);
|
||||
|
||||
// We require that the old attr is correct.
|
||||
if (found->GetAttributes() != old_attr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We further require that the region can be split from the old region.
|
||||
const u64 inserted_region_end = address + size;
|
||||
const u64 inserted_region_last = inserted_region_end - 1;
|
||||
if (found->GetLastAddress() < inserted_region_last) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Further, we require that the type id is a valid transformation.
|
||||
if (!found->CanDerive(type_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cache information from the region before we remove it.
|
||||
const u64 old_address = found->GetAddress();
|
||||
const u64 old_last = found->GetLastAddress();
|
||||
const u64 old_pair = found->GetPairAddress();
|
||||
const u32 old_type = found->GetType();
|
||||
|
||||
// Erase the existing region from the tree.
|
||||
this->erase(this->iterator_to(*found));
|
||||
|
||||
// Insert the new region into the tree.
|
||||
if (old_address == address) {
|
||||
// Reuse the old object for the new region, if we can.
|
||||
found->Reset(address, inserted_region_last, old_pair, new_attr, type_id);
|
||||
this->insert(*found);
|
||||
} else {
|
||||
// If we can't re-use, adjust the old region.
|
||||
found->Reset(old_address, address - 1, old_pair, old_attr, old_type);
|
||||
this->insert(*found);
|
||||
|
||||
// Insert a new region for the split.
|
||||
const u64 new_pair = (old_pair != std::numeric_limits<u64>::max())
|
||||
? old_pair + (address - old_address)
|
||||
: old_pair;
|
||||
this->insert(*AllocateRegion(memory_region_allocator, address, inserted_region_last,
|
||||
new_pair, new_attr, type_id));
|
||||
}
|
||||
|
||||
// If we need to insert a region after the region, do so.
|
||||
if (old_last != inserted_region_last) {
|
||||
const u64 after_pair = (old_pair != std::numeric_limits<u64>::max())
|
||||
? old_pair + (inserted_region_end - old_address)
|
||||
: old_pair;
|
||||
this->insert(*AllocateRegion(memory_region_allocator, inserted_region_end, old_last,
|
||||
after_pair, old_attr, old_type));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
VAddr KMemoryRegionTree::GetRandomAlignedRegion(size_t size, size_t alignment, u32 type_id) {
|
||||
// We want to find the total extents of the type id.
|
||||
const auto extents = this->GetDerivedRegionExtents(static_cast<KMemoryRegionType>(type_id));
|
||||
|
||||
// Ensure that our alignment is correct.
|
||||
ASSERT(Common::IsAligned(extents.GetAddress(), alignment));
|
||||
|
||||
const u64 first_address = extents.GetAddress();
|
||||
const u64 last_address = extents.GetLastAddress();
|
||||
|
||||
const u64 first_index = first_address / alignment;
|
||||
const u64 last_index = last_address / alignment;
|
||||
|
||||
while (true) {
|
||||
const u64 candidate =
|
||||
KSystemControl::GenerateRandomRange(first_index, last_index) * alignment;
|
||||
|
||||
// Ensure that the candidate doesn't overflow with the size.
|
||||
if (!(candidate < candidate + size)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const u64 candidate_last = candidate + size - 1;
|
||||
|
||||
// Ensure that the candidate fits within the region.
|
||||
if (candidate_last > last_address) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Locate the candidate region, and ensure it fits and has the correct type id.
|
||||
if (const auto& candidate_region = *this->Find(candidate);
|
||||
!(candidate_last <= candidate_region.GetLastAddress() &&
|
||||
candidate_region.GetType() == type_id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
KMemoryLayout::KMemoryLayout()
|
||||
: virtual_tree{memory_region_allocator}, physical_tree{memory_region_allocator},
|
||||
virtual_linear_tree{memory_region_allocator}, physical_linear_tree{memory_region_allocator} {}
|
||||
|
||||
void KMemoryLayout::InitializeLinearMemoryRegionTrees(PAddr aligned_linear_phys_start,
|
||||
VAddr linear_virtual_start) {
|
||||
// Set static differences.
|
||||
linear_phys_to_virt_diff = linear_virtual_start - aligned_linear_phys_start;
|
||||
linear_virt_to_phys_diff = aligned_linear_phys_start - linear_virtual_start;
|
||||
|
||||
// Initialize linear trees.
|
||||
for (auto& region : GetPhysicalMemoryRegionTree()) {
|
||||
if (region.HasTypeAttribute(KMemoryRegionAttr_LinearMapped)) {
|
||||
GetPhysicalLinearMemoryRegionTree().InsertDirectly(
|
||||
region.GetAddress(), region.GetLastAddress(), region.GetAttributes(),
|
||||
region.GetType());
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& region : GetVirtualMemoryRegionTree()) {
|
||||
if (region.IsDerivedFrom(KMemoryRegionType_Dram)) {
|
||||
GetVirtualLinearMemoryRegionTree().InsertDirectly(
|
||||
region.GetAddress(), region.GetLastAddress(), region.GetAttributes(),
|
||||
region.GetType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t KMemoryLayout::GetResourceRegionSizeForInit() {
|
||||
// Calculate resource region size based on whether we allow extra threads.
|
||||
const bool use_extra_resources = KSystemControl::Init::ShouldIncreaseThreadResourceLimit();
|
||||
size_t resource_region_size =
|
||||
KernelResourceSize + (use_extra_resources ? KernelSlabHeapAdditionalSize : 0);
|
||||
|
||||
return resource_region_size;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
399
src/core/hle/kernel/k_memory_layout.h
Normal file
399
src/core/hle/kernel/k_memory_layout.h
Normal file
@@ -0,0 +1,399 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/common_sizes.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/device_memory.h"
|
||||
#include "core/hle/kernel/k_memory_region.h"
|
||||
#include "core/hle/kernel/k_memory_region_type.h"
|
||||
#include "core/hle/kernel/memory_types.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
constexpr std::size_t L1BlockSize = Common::Size_1_GB;
|
||||
constexpr std::size_t L2BlockSize = Common::Size_2_MB;
|
||||
|
||||
constexpr std::size_t GetMaximumOverheadSize(std::size_t size) {
|
||||
return (Common::DivideUp(size, L1BlockSize) + Common::DivideUp(size, L2BlockSize)) * PageSize;
|
||||
}
|
||||
|
||||
constexpr std::size_t MainMemorySize = Common::Size_4_GB;
|
||||
constexpr std::size_t MainMemorySizeMax = Common::Size_8_GB;
|
||||
|
||||
constexpr std::size_t ReservedEarlyDramSize = 0x60000;
|
||||
constexpr std::size_t DramPhysicalAddress = 0x80000000;
|
||||
|
||||
constexpr std::size_t KernelAslrAlignment = Common::Size_2_MB;
|
||||
constexpr std::size_t KernelVirtualAddressSpaceWidth = 1ULL << 39;
|
||||
constexpr std::size_t KernelPhysicalAddressSpaceWidth = 1ULL << 48;
|
||||
|
||||
constexpr std::size_t KernelVirtualAddressSpaceBase = 0ULL - KernelVirtualAddressSpaceWidth;
|
||||
constexpr std::size_t KernelVirtualAddressSpaceEnd =
|
||||
KernelVirtualAddressSpaceBase + (KernelVirtualAddressSpaceWidth - KernelAslrAlignment);
|
||||
constexpr std::size_t KernelVirtualAddressSpaceLast = KernelVirtualAddressSpaceEnd - 1ULL;
|
||||
constexpr std::size_t KernelVirtualAddressSpaceSize =
|
||||
KernelVirtualAddressSpaceEnd - KernelVirtualAddressSpaceBase;
|
||||
constexpr std::size_t KernelVirtualAddressCodeBase = KernelVirtualAddressSpaceBase;
|
||||
constexpr std::size_t KernelVirtualAddressCodeSize = 0x62000;
|
||||
constexpr std::size_t KernelVirtualAddressCodeEnd =
|
||||
KernelVirtualAddressCodeBase + KernelVirtualAddressCodeSize;
|
||||
|
||||
constexpr std::size_t KernelPhysicalAddressSpaceBase = 0ULL;
|
||||
constexpr std::size_t KernelPhysicalAddressSpaceEnd =
|
||||
KernelPhysicalAddressSpaceBase + KernelPhysicalAddressSpaceWidth;
|
||||
constexpr std::size_t KernelPhysicalAddressSpaceLast = KernelPhysicalAddressSpaceEnd - 1ULL;
|
||||
constexpr std::size_t KernelPhysicalAddressSpaceSize =
|
||||
KernelPhysicalAddressSpaceEnd - KernelPhysicalAddressSpaceBase;
|
||||
constexpr std::size_t KernelPhysicalAddressCodeBase = DramPhysicalAddress + ReservedEarlyDramSize;
|
||||
|
||||
constexpr std::size_t KernelPageTableHeapSize = GetMaximumOverheadSize(MainMemorySizeMax);
|
||||
constexpr std::size_t KernelInitialPageHeapSize = Common::Size_128_KB;
|
||||
|
||||
constexpr std::size_t KernelSlabHeapDataSize = Common::Size_5_MB;
|
||||
constexpr std::size_t KernelSlabHeapGapsSize = Common::Size_2_MB - Common::Size_64_KB;
|
||||
constexpr std::size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSize;
|
||||
|
||||
// NOTE: This is calculated from KThread slab counts, assuming KThread size <= 0x860.
|
||||
constexpr std::size_t KernelSlabHeapAdditionalSize = 0x68000ULL;
|
||||
|
||||
constexpr std::size_t KernelResourceSize =
|
||||
KernelPageTableHeapSize + KernelInitialPageHeapSize + KernelSlabHeapSize;
|
||||
|
||||
constexpr bool IsKernelAddressKey(VAddr key) {
|
||||
return KernelVirtualAddressSpaceBase <= key && key <= KernelVirtualAddressSpaceLast;
|
||||
}
|
||||
|
||||
constexpr bool IsKernelAddress(VAddr address) {
|
||||
return KernelVirtualAddressSpaceBase <= address && address < KernelVirtualAddressSpaceEnd;
|
||||
}
|
||||
|
||||
class KMemoryLayout final {
|
||||
public:
|
||||
KMemoryLayout();
|
||||
|
||||
KMemoryRegionTree& GetVirtualMemoryRegionTree() {
|
||||
return virtual_tree;
|
||||
}
|
||||
const KMemoryRegionTree& GetVirtualMemoryRegionTree() const {
|
||||
return virtual_tree;
|
||||
}
|
||||
KMemoryRegionTree& GetPhysicalMemoryRegionTree() {
|
||||
return physical_tree;
|
||||
}
|
||||
const KMemoryRegionTree& GetPhysicalMemoryRegionTree() const {
|
||||
return physical_tree;
|
||||
}
|
||||
KMemoryRegionTree& GetVirtualLinearMemoryRegionTree() {
|
||||
return virtual_linear_tree;
|
||||
}
|
||||
const KMemoryRegionTree& GetVirtualLinearMemoryRegionTree() const {
|
||||
return virtual_linear_tree;
|
||||
}
|
||||
KMemoryRegionTree& GetPhysicalLinearMemoryRegionTree() {
|
||||
return physical_linear_tree;
|
||||
}
|
||||
const KMemoryRegionTree& GetPhysicalLinearMemoryRegionTree() const {
|
||||
return physical_linear_tree;
|
||||
}
|
||||
|
||||
VAddr GetLinearVirtualAddress(PAddr address) const {
|
||||
return address + linear_phys_to_virt_diff;
|
||||
}
|
||||
PAddr GetLinearPhysicalAddress(VAddr address) const {
|
||||
return address + linear_virt_to_phys_diff;
|
||||
}
|
||||
|
||||
const KMemoryRegion* FindVirtual(VAddr address) const {
|
||||
return Find(address, GetVirtualMemoryRegionTree());
|
||||
}
|
||||
const KMemoryRegion* FindPhysical(PAddr address) const {
|
||||
return Find(address, GetPhysicalMemoryRegionTree());
|
||||
}
|
||||
|
||||
const KMemoryRegion* FindVirtualLinear(VAddr address) const {
|
||||
return Find(address, GetVirtualLinearMemoryRegionTree());
|
||||
}
|
||||
const KMemoryRegion* FindPhysicalLinear(PAddr address) const {
|
||||
return Find(address, GetPhysicalLinearMemoryRegionTree());
|
||||
}
|
||||
|
||||
VAddr GetMainStackTopAddress(s32 core_id) const {
|
||||
return GetStackTopAddress(core_id, KMemoryRegionType_KernelMiscMainStack);
|
||||
}
|
||||
VAddr GetIdleStackTopAddress(s32 core_id) const {
|
||||
return GetStackTopAddress(core_id, KMemoryRegionType_KernelMiscIdleStack);
|
||||
}
|
||||
VAddr GetExceptionStackTopAddress(s32 core_id) const {
|
||||
return GetStackTopAddress(core_id, KMemoryRegionType_KernelMiscExceptionStack);
|
||||
}
|
||||
|
||||
VAddr GetSlabRegionAddress() const {
|
||||
return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_KernelSlab))
|
||||
.GetAddress();
|
||||
}
|
||||
|
||||
const KMemoryRegion& GetDeviceRegion(KMemoryRegionType type) const {
|
||||
return Dereference(GetPhysicalMemoryRegionTree().FindFirstDerived(type));
|
||||
}
|
||||
PAddr GetDevicePhysicalAddress(KMemoryRegionType type) const {
|
||||
return GetDeviceRegion(type).GetAddress();
|
||||
}
|
||||
VAddr GetDeviceVirtualAddress(KMemoryRegionType type) const {
|
||||
return GetDeviceRegion(type).GetPairAddress();
|
||||
}
|
||||
|
||||
const KMemoryRegion& GetPoolManagementRegion() const {
|
||||
return Dereference(
|
||||
GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_VirtualDramPoolManagement));
|
||||
}
|
||||
const KMemoryRegion& GetPageTableHeapRegion() const {
|
||||
return Dereference(
|
||||
GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_VirtualDramKernelPtHeap));
|
||||
}
|
||||
const KMemoryRegion& GetKernelStackRegion() const {
|
||||
return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_KernelStack));
|
||||
}
|
||||
const KMemoryRegion& GetTempRegion() const {
|
||||
return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_KernelTemp));
|
||||
}
|
||||
|
||||
const KMemoryRegion& GetKernelTraceBufferRegion() const {
|
||||
return Dereference(GetVirtualLinearMemoryRegionTree().FindByType(
|
||||
KMemoryRegionType_VirtualDramKernelTraceBuffer));
|
||||
}
|
||||
|
||||
const KMemoryRegion& GetVirtualLinearRegion(VAddr address) const {
|
||||
return Dereference(FindVirtualLinear(address));
|
||||
}
|
||||
|
||||
const KMemoryRegion* GetPhysicalKernelTraceBufferRegion() const {
|
||||
return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_KernelTraceBuffer);
|
||||
}
|
||||
const KMemoryRegion* GetPhysicalOnMemoryBootImageRegion() const {
|
||||
return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_OnMemoryBootImage);
|
||||
}
|
||||
const KMemoryRegion* GetPhysicalDTBRegion() const {
|
||||
return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_DTB);
|
||||
}
|
||||
|
||||
bool IsHeapPhysicalAddress(const KMemoryRegion*& region, PAddr address) const {
|
||||
return IsTypedAddress(region, address, GetPhysicalLinearMemoryRegionTree(),
|
||||
KMemoryRegionType_DramUserPool);
|
||||
}
|
||||
bool IsHeapVirtualAddress(const KMemoryRegion*& region, VAddr address) const {
|
||||
return IsTypedAddress(region, address, GetVirtualLinearMemoryRegionTree(),
|
||||
KMemoryRegionType_VirtualDramUserPool);
|
||||
}
|
||||
|
||||
bool IsHeapPhysicalAddress(const KMemoryRegion*& region, PAddr address, size_t size) const {
|
||||
return IsTypedAddress(region, address, size, GetPhysicalLinearMemoryRegionTree(),
|
||||
KMemoryRegionType_DramUserPool);
|
||||
}
|
||||
bool IsHeapVirtualAddress(const KMemoryRegion*& region, VAddr address, size_t size) const {
|
||||
return IsTypedAddress(region, address, size, GetVirtualLinearMemoryRegionTree(),
|
||||
KMemoryRegionType_VirtualDramUserPool);
|
||||
}
|
||||
|
||||
bool IsLinearMappedPhysicalAddress(const KMemoryRegion*& region, PAddr address) const {
|
||||
return IsTypedAddress(region, address, GetPhysicalLinearMemoryRegionTree(),
|
||||
static_cast<KMemoryRegionType>(KMemoryRegionAttr_LinearMapped));
|
||||
}
|
||||
bool IsLinearMappedPhysicalAddress(const KMemoryRegion*& region, PAddr address,
|
||||
size_t size) const {
|
||||
return IsTypedAddress(region, address, size, GetPhysicalLinearMemoryRegionTree(),
|
||||
static_cast<KMemoryRegionType>(KMemoryRegionAttr_LinearMapped));
|
||||
}
|
||||
|
||||
std::pair<size_t, size_t> GetTotalAndKernelMemorySizes() const {
|
||||
size_t total_size = 0, kernel_size = 0;
|
||||
for (const auto& region : GetPhysicalMemoryRegionTree()) {
|
||||
if (region.IsDerivedFrom(KMemoryRegionType_Dram)) {
|
||||
total_size += region.GetSize();
|
||||
if (!region.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
|
||||
kernel_size += region.GetSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::make_pair(total_size, kernel_size);
|
||||
}
|
||||
|
||||
void InitializeLinearMemoryRegionTrees(PAddr aligned_linear_phys_start,
|
||||
VAddr linear_virtual_start);
|
||||
static size_t GetResourceRegionSizeForInit();
|
||||
|
||||
auto GetKernelRegionExtents() const {
|
||||
return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_Kernel);
|
||||
}
|
||||
auto GetKernelCodeRegionExtents() const {
|
||||
return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelCode);
|
||||
}
|
||||
auto GetKernelStackRegionExtents() const {
|
||||
return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelStack);
|
||||
}
|
||||
auto GetKernelMiscRegionExtents() const {
|
||||
return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelMisc);
|
||||
}
|
||||
auto GetKernelSlabRegionExtents() const {
|
||||
return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelSlab);
|
||||
}
|
||||
|
||||
auto GetLinearRegionPhysicalExtents() const {
|
||||
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||
KMemoryRegionAttr_LinearMapped);
|
||||
}
|
||||
|
||||
auto GetLinearRegionVirtualExtents() const {
|
||||
const auto physical = GetLinearRegionPhysicalExtents();
|
||||
return KMemoryRegion(GetLinearVirtualAddress(physical.GetAddress()),
|
||||
GetLinearVirtualAddress(physical.GetLastAddress()), 0,
|
||||
KMemoryRegionType_None);
|
||||
}
|
||||
|
||||
auto GetMainMemoryPhysicalExtents() const {
|
||||
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_Dram);
|
||||
}
|
||||
auto GetCarveoutRegionExtents() const {
|
||||
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||
KMemoryRegionAttr_CarveoutProtected);
|
||||
}
|
||||
|
||||
auto GetKernelRegionPhysicalExtents() const {
|
||||
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||
KMemoryRegionType_DramKernelBase);
|
||||
}
|
||||
auto GetKernelCodeRegionPhysicalExtents() const {
|
||||
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||
KMemoryRegionType_DramKernelCode);
|
||||
}
|
||||
auto GetKernelSlabRegionPhysicalExtents() const {
|
||||
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||
KMemoryRegionType_DramKernelSlab);
|
||||
}
|
||||
auto GetKernelPageTableHeapRegionPhysicalExtents() const {
|
||||
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||
KMemoryRegionType_DramKernelPtHeap);
|
||||
}
|
||||
auto GetKernelInitPageTableRegionPhysicalExtents() const {
|
||||
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||
KMemoryRegionType_DramKernelInitPt);
|
||||
}
|
||||
|
||||
auto GetKernelPoolManagementRegionPhysicalExtents() const {
|
||||
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||
KMemoryRegionType_DramPoolManagement);
|
||||
}
|
||||
auto GetKernelPoolPartitionRegionPhysicalExtents() const {
|
||||
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||
KMemoryRegionType_DramPoolPartition);
|
||||
}
|
||||
auto GetKernelSystemPoolRegionPhysicalExtents() const {
|
||||
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||
KMemoryRegionType_DramSystemPool);
|
||||
}
|
||||
auto GetKernelSystemNonSecurePoolRegionPhysicalExtents() const {
|
||||
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||
KMemoryRegionType_DramSystemNonSecurePool);
|
||||
}
|
||||
auto GetKernelAppletPoolRegionPhysicalExtents() const {
|
||||
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||
KMemoryRegionType_DramAppletPool);
|
||||
}
|
||||
auto GetKernelApplicationPoolRegionPhysicalExtents() const {
|
||||
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||
KMemoryRegionType_DramApplicationPool);
|
||||
}
|
||||
|
||||
auto GetKernelTraceBufferRegionPhysicalExtents() const {
|
||||
return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
|
||||
KMemoryRegionType_KernelTraceBuffer);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename AddressType>
|
||||
static bool IsTypedAddress(const KMemoryRegion*& region, AddressType address,
|
||||
const KMemoryRegionTree& tree, KMemoryRegionType type) {
|
||||
// Check if the cached region already contains the address.
|
||||
if (region != nullptr && region->Contains(address)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Find the containing region, and update the cache.
|
||||
if (const KMemoryRegion* found = tree.Find(address);
|
||||
found != nullptr && found->IsDerivedFrom(type)) {
|
||||
region = found;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
static bool IsTypedAddress(const KMemoryRegion*& region, AddressType address, size_t size,
|
||||
const KMemoryRegionTree& tree, KMemoryRegionType type) {
|
||||
// Get the end of the checked region.
|
||||
const u64 last_address = address + size - 1;
|
||||
|
||||
// Walk the tree to verify the region is correct.
|
||||
const KMemoryRegion* cur =
|
||||
(region != nullptr && region->Contains(address)) ? region : tree.Find(address);
|
||||
while (cur != nullptr && cur->IsDerivedFrom(type)) {
|
||||
if (last_address <= cur->GetLastAddress()) {
|
||||
region = cur;
|
||||
return true;
|
||||
}
|
||||
|
||||
cur = cur->GetNext();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
static const KMemoryRegion* Find(AddressType address, const KMemoryRegionTree& tree) {
|
||||
return tree.Find(address);
|
||||
}
|
||||
|
||||
static KMemoryRegion& Dereference(KMemoryRegion* region) {
|
||||
ASSERT(region != nullptr);
|
||||
return *region;
|
||||
}
|
||||
|
||||
static const KMemoryRegion& Dereference(const KMemoryRegion* region) {
|
||||
ASSERT(region != nullptr);
|
||||
return *region;
|
||||
}
|
||||
|
||||
VAddr GetStackTopAddress(s32 core_id, KMemoryRegionType type) const {
|
||||
const auto& region = Dereference(
|
||||
GetVirtualMemoryRegionTree().FindByTypeAndAttribute(type, static_cast<u32>(core_id)));
|
||||
ASSERT(region.GetEndAddress() != 0);
|
||||
return region.GetEndAddress();
|
||||
}
|
||||
|
||||
private:
|
||||
u64 linear_phys_to_virt_diff{};
|
||||
u64 linear_virt_to_phys_diff{};
|
||||
KMemoryRegionAllocator memory_region_allocator;
|
||||
KMemoryRegionTree virtual_tree;
|
||||
KMemoryRegionTree physical_tree;
|
||||
KMemoryRegionTree virtual_linear_tree;
|
||||
KMemoryRegionTree physical_linear_tree;
|
||||
};
|
||||
|
||||
namespace Init {
|
||||
|
||||
// These should be generic, regardless of board.
|
||||
void SetupPoolPartitionMemoryRegions(KMemoryLayout& memory_layout);
|
||||
|
||||
// These may be implemented in a board-specific manner.
|
||||
void SetupDevicePhysicalMemoryRegions(KMemoryLayout& memory_layout);
|
||||
void SetupDramPhysicalMemoryRegions(KMemoryLayout& memory_layout);
|
||||
|
||||
} // namespace Init
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -8,20 +8,20 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/hle/kernel/memory/memory_manager.h"
|
||||
#include "core/hle/kernel/memory/page_linked_list.h"
|
||||
#include "core/hle/kernel/k_memory_manager.h"
|
||||
#include "core/hle/kernel/k_page_linked_list.h"
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
|
||||
namespace Kernel::Memory {
|
||||
namespace Kernel {
|
||||
|
||||
std::size_t MemoryManager::Impl::Initialize(Pool new_pool, u64 start_address, u64 end_address) {
|
||||
std::size_t KMemoryManager::Impl::Initialize(Pool new_pool, u64 start_address, u64 end_address) {
|
||||
const auto size{end_address - start_address};
|
||||
|
||||
// Calculate metadata sizes
|
||||
const auto ref_count_size{(size / PageSize) * sizeof(u16)};
|
||||
const auto optimize_map_size{(Common::AlignUp((size / PageSize), 64) / 64) * sizeof(u64)};
|
||||
const auto manager_size{Common::AlignUp(optimize_map_size + ref_count_size, PageSize)};
|
||||
const auto page_heap_size{PageHeap::CalculateMetadataOverheadSize(size)};
|
||||
const auto page_heap_size{KPageHeap::CalculateManagementOverheadSize(size)};
|
||||
const auto total_metadata_size{manager_size + page_heap_size};
|
||||
ASSERT(manager_size <= total_metadata_size);
|
||||
ASSERT(Common::IsAligned(total_metadata_size, PageSize));
|
||||
@@ -41,29 +41,30 @@ std::size_t MemoryManager::Impl::Initialize(Pool new_pool, u64 start_address, u6
|
||||
return total_metadata_size;
|
||||
}
|
||||
|
||||
void MemoryManager::InitializeManager(Pool pool, u64 start_address, u64 end_address) {
|
||||
void KMemoryManager::InitializeManager(Pool pool, u64 start_address, u64 end_address) {
|
||||
ASSERT(pool < Pool::Count);
|
||||
managers[static_cast<std::size_t>(pool)].Initialize(pool, start_address, end_address);
|
||||
}
|
||||
|
||||
VAddr MemoryManager::AllocateContinuous(std::size_t num_pages, std::size_t align_pages, Pool pool,
|
||||
Direction dir) {
|
||||
VAddr KMemoryManager::AllocateAndOpenContinuous(std::size_t num_pages, std::size_t align_pages,
|
||||
u32 option) {
|
||||
// Early return if we're allocating no pages
|
||||
if (num_pages == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Lock the pool that we're allocating from
|
||||
const auto [pool, dir] = DecodeOption(option);
|
||||
const auto pool_index{static_cast<std::size_t>(pool)};
|
||||
std::lock_guard lock{pool_locks[pool_index]};
|
||||
|
||||
// Choose a heap based on our page size request
|
||||
const s32 heap_index{PageHeap::GetAlignedBlockIndex(num_pages, align_pages)};
|
||||
const s32 heap_index{KPageHeap::GetAlignedBlockIndex(num_pages, align_pages)};
|
||||
|
||||
// Loop, trying to iterate from each block
|
||||
// TODO (bunnei): Support multiple managers
|
||||
Impl& chosen_manager{managers[pool_index]};
|
||||
VAddr allocated_block{chosen_manager.AllocateBlock(heap_index)};
|
||||
VAddr allocated_block{chosen_manager.AllocateBlock(heap_index, false)};
|
||||
|
||||
// If we failed to allocate, quit now
|
||||
if (!allocated_block) {
|
||||
@@ -71,7 +72,7 @@ VAddr MemoryManager::AllocateContinuous(std::size_t num_pages, std::size_t align
|
||||
}
|
||||
|
||||
// If we allocated more than we need, free some
|
||||
const auto allocated_pages{PageHeap::GetBlockNumPages(heap_index)};
|
||||
const auto allocated_pages{KPageHeap::GetBlockNumPages(heap_index)};
|
||||
if (allocated_pages > num_pages) {
|
||||
chosen_manager.Free(allocated_block + num_pages * PageSize, allocated_pages - num_pages);
|
||||
}
|
||||
@@ -79,8 +80,8 @@ VAddr MemoryManager::AllocateContinuous(std::size_t num_pages, std::size_t align
|
||||
return allocated_block;
|
||||
}
|
||||
|
||||
ResultCode MemoryManager::Allocate(PageLinkedList& page_list, std::size_t num_pages, Pool pool,
|
||||
Direction dir) {
|
||||
ResultCode KMemoryManager::Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
|
||||
Direction dir) {
|
||||
ASSERT(page_list.GetNumPages() == 0);
|
||||
|
||||
// Early return if we're allocating no pages
|
||||
@@ -93,7 +94,7 @@ ResultCode MemoryManager::Allocate(PageLinkedList& page_list, std::size_t num_pa
|
||||
std::lock_guard lock{pool_locks[pool_index]};
|
||||
|
||||
// Choose a heap based on our page size request
|
||||
const s32 heap_index{PageHeap::GetBlockIndex(num_pages)};
|
||||
const s32 heap_index{KPageHeap::GetBlockIndex(num_pages)};
|
||||
if (heap_index < 0) {
|
||||
return ResultOutOfMemory;
|
||||
}
|
||||
@@ -112,11 +113,11 @@ ResultCode MemoryManager::Allocate(PageLinkedList& page_list, std::size_t num_pa
|
||||
|
||||
// Keep allocating until we've allocated all our pages
|
||||
for (s32 index{heap_index}; index >= 0 && num_pages > 0; index--) {
|
||||
const auto pages_per_alloc{PageHeap::GetBlockNumPages(index)};
|
||||
const auto pages_per_alloc{KPageHeap::GetBlockNumPages(index)};
|
||||
|
||||
while (num_pages >= pages_per_alloc) {
|
||||
// Allocate a block
|
||||
VAddr allocated_block{chosen_manager.AllocateBlock(index)};
|
||||
VAddr allocated_block{chosen_manager.AllocateBlock(index, false)};
|
||||
if (!allocated_block) {
|
||||
break;
|
||||
}
|
||||
@@ -148,8 +149,8 @@ ResultCode MemoryManager::Allocate(PageLinkedList& page_list, std::size_t num_pa
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode MemoryManager::Free(PageLinkedList& page_list, std::size_t num_pages, Pool pool,
|
||||
Direction dir) {
|
||||
ResultCode KMemoryManager::Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
|
||||
Direction dir) {
|
||||
// Early return if we're freeing no pages
|
||||
if (!num_pages) {
|
||||
return RESULT_SUCCESS;
|
||||
@@ -172,4 +173,16 @@ ResultCode MemoryManager::Free(PageLinkedList& page_list, std::size_t num_pages,
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace Kernel::Memory
|
||||
std::size_t KMemoryManager::Impl::CalculateManagementOverheadSize(std::size_t region_size) {
|
||||
const std::size_t ref_count_size = (region_size / PageSize) * sizeof(u16);
|
||||
const std::size_t optimize_map_size =
|
||||
(Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) /
|
||||
Common::BitSize<u64>()) *
|
||||
sizeof(u64);
|
||||
const std::size_t manager_meta_size =
|
||||
Common::AlignUp(optimize_map_size + ref_count_size, PageSize);
|
||||
const std::size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(region_size);
|
||||
return manager_meta_size + page_heap_size;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
137
src/core/hle/kernel/k_memory_manager.h
Normal file
137
src/core/hle/kernel/k_memory_manager.h
Normal file
@@ -0,0 +1,137 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <mutex>
|
||||
#include <tuple>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/k_page_heap.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KPageLinkedList;
|
||||
|
||||
class KMemoryManager final : NonCopyable {
|
||||
public:
|
||||
enum class Pool : u32 {
|
||||
Application = 0,
|
||||
Applet = 1,
|
||||
System = 2,
|
||||
SystemNonSecure = 3,
|
||||
|
||||
Count,
|
||||
|
||||
Shift = 4,
|
||||
Mask = (0xF << Shift),
|
||||
|
||||
// Aliases.
|
||||
Unsafe = Application,
|
||||
Secure = System,
|
||||
};
|
||||
|
||||
enum class Direction : u32 {
|
||||
FromFront = 0,
|
||||
FromBack = 1,
|
||||
|
||||
Shift = 0,
|
||||
Mask = (0xF << Shift),
|
||||
};
|
||||
|
||||
KMemoryManager() = default;
|
||||
|
||||
constexpr std::size_t GetSize(Pool pool) const {
|
||||
return managers[static_cast<std::size_t>(pool)].GetSize();
|
||||
}
|
||||
|
||||
void InitializeManager(Pool pool, u64 start_address, u64 end_address);
|
||||
|
||||
VAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option);
|
||||
ResultCode Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
|
||||
Direction dir = Direction::FromFront);
|
||||
ResultCode Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
|
||||
Direction dir = Direction::FromFront);
|
||||
|
||||
static constexpr std::size_t MaxManagerCount = 10;
|
||||
|
||||
public:
|
||||
static std::size_t CalculateManagementOverheadSize(std::size_t region_size) {
|
||||
return Impl::CalculateManagementOverheadSize(region_size);
|
||||
}
|
||||
|
||||
static constexpr u32 EncodeOption(Pool pool, Direction dir) {
|
||||
return (static_cast<u32>(pool) << static_cast<u32>(Pool::Shift)) |
|
||||
(static_cast<u32>(dir) << static_cast<u32>(Direction::Shift));
|
||||
}
|
||||
|
||||
static constexpr Pool GetPool(u32 option) {
|
||||
return static_cast<Pool>((static_cast<u32>(option) & static_cast<u32>(Pool::Mask)) >>
|
||||
static_cast<u32>(Pool::Shift));
|
||||
}
|
||||
|
||||
static constexpr Direction GetDirection(u32 option) {
|
||||
return static_cast<Direction>(
|
||||
(static_cast<u32>(option) & static_cast<u32>(Direction::Mask)) >>
|
||||
static_cast<u32>(Direction::Shift));
|
||||
}
|
||||
|
||||
static constexpr std::tuple<Pool, Direction> DecodeOption(u32 option) {
|
||||
return std::make_tuple(GetPool(option), GetDirection(option));
|
||||
}
|
||||
|
||||
private:
|
||||
class Impl final : NonCopyable {
|
||||
private:
|
||||
using RefCount = u16;
|
||||
|
||||
private:
|
||||
KPageHeap heap;
|
||||
Pool pool{};
|
||||
|
||||
public:
|
||||
static std::size_t CalculateManagementOverheadSize(std::size_t region_size);
|
||||
|
||||
static constexpr std::size_t CalculateOptimizedProcessOverheadSize(
|
||||
std::size_t region_size) {
|
||||
return (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) /
|
||||
Common::BitSize<u64>()) *
|
||||
sizeof(u64);
|
||||
}
|
||||
|
||||
public:
|
||||
Impl() = default;
|
||||
|
||||
std::size_t Initialize(Pool new_pool, u64 start_address, u64 end_address);
|
||||
|
||||
VAddr AllocateBlock(s32 index, bool random) {
|
||||
return heap.AllocateBlock(index, random);
|
||||
}
|
||||
|
||||
void Free(VAddr addr, std::size_t num_pages) {
|
||||
heap.Free(addr, num_pages);
|
||||
}
|
||||
|
||||
constexpr std::size_t GetSize() const {
|
||||
return heap.GetSize();
|
||||
}
|
||||
|
||||
constexpr VAddr GetAddress() const {
|
||||
return heap.GetAddress();
|
||||
}
|
||||
|
||||
constexpr VAddr GetEndAddress() const {
|
||||
return heap.GetEndAddress();
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
std::array<std::mutex, static_cast<std::size_t>(Pool::Count)> pool_locks;
|
||||
std::array<Impl, MaxManagerCount> managers;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
350
src/core/hle/kernel/k_memory_region.h
Normal file
350
src/core/hle/kernel/k_memory_region.h
Normal file
@@ -0,0 +1,350 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/intrusive_red_black_tree.h"
|
||||
#include "core/hle/kernel/k_memory_region_type.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KMemoryRegionAllocator;
|
||||
|
||||
class KMemoryRegion final : public Common::IntrusiveRedBlackTreeBaseNode<KMemoryRegion>,
|
||||
NonCopyable {
|
||||
friend class KMemoryRegionTree;
|
||||
|
||||
public:
|
||||
constexpr KMemoryRegion() = default;
|
||||
constexpr KMemoryRegion(u64 address_, u64 last_address_)
|
||||
: address{address_}, last_address{last_address_} {}
|
||||
constexpr KMemoryRegion(u64 address_, u64 last_address_, u64 pair_address_, u32 attributes_,
|
||||
u32 type_id_)
|
||||
: address(address_), last_address(last_address_), pair_address(pair_address_),
|
||||
attributes(attributes_), type_id(type_id_) {}
|
||||
constexpr KMemoryRegion(u64 address_, u64 last_address_, u32 attributes_, u32 type_id_)
|
||||
: KMemoryRegion(address_, last_address_, std::numeric_limits<u64>::max(), attributes_,
|
||||
type_id_) {}
|
||||
|
||||
static constexpr int Compare(const KMemoryRegion& lhs, const KMemoryRegion& rhs) {
|
||||
if (lhs.GetAddress() < rhs.GetAddress()) {
|
||||
return -1;
|
||||
} else if (lhs.GetAddress() <= rhs.GetLastAddress()) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr void Reset(u64 a, u64 la, u64 p, u32 r, u32 t) {
|
||||
address = a;
|
||||
pair_address = p;
|
||||
last_address = la;
|
||||
attributes = r;
|
||||
type_id = t;
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr u64 GetAddress() const {
|
||||
return address;
|
||||
}
|
||||
|
||||
constexpr u64 GetPairAddress() const {
|
||||
return pair_address;
|
||||
}
|
||||
|
||||
constexpr u64 GetLastAddress() const {
|
||||
return last_address;
|
||||
}
|
||||
|
||||
constexpr u64 GetEndAddress() const {
|
||||
return this->GetLastAddress() + 1;
|
||||
}
|
||||
|
||||
constexpr size_t GetSize() const {
|
||||
return this->GetEndAddress() - this->GetAddress();
|
||||
}
|
||||
|
||||
constexpr u32 GetAttributes() const {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
constexpr u32 GetType() const {
|
||||
return type_id;
|
||||
}
|
||||
|
||||
constexpr void SetType(u32 type) {
|
||||
ASSERT(this->CanDerive(type));
|
||||
type_id = type;
|
||||
}
|
||||
|
||||
constexpr bool Contains(u64 address) const {
|
||||
ASSERT(this->GetEndAddress() != 0);
|
||||
return this->GetAddress() <= address && address <= this->GetLastAddress();
|
||||
}
|
||||
|
||||
constexpr bool IsDerivedFrom(u32 type) const {
|
||||
return (this->GetType() | type) == this->GetType();
|
||||
}
|
||||
|
||||
constexpr bool HasTypeAttribute(u32 attr) const {
|
||||
return (this->GetType() | attr) == this->GetType();
|
||||
}
|
||||
|
||||
constexpr bool CanDerive(u32 type) const {
|
||||
return (this->GetType() | type) == type;
|
||||
}
|
||||
|
||||
constexpr void SetPairAddress(u64 a) {
|
||||
pair_address = a;
|
||||
}
|
||||
|
||||
constexpr void SetTypeAttribute(u32 attr) {
|
||||
type_id |= attr;
|
||||
}
|
||||
|
||||
private:
|
||||
u64 address{};
|
||||
u64 last_address{};
|
||||
u64 pair_address{};
|
||||
u32 attributes{};
|
||||
u32 type_id{};
|
||||
};
|
||||
|
||||
class KMemoryRegionTree final : NonCopyable {
|
||||
public:
|
||||
struct DerivedRegionExtents {
|
||||
const KMemoryRegion* first_region{};
|
||||
const KMemoryRegion* last_region{};
|
||||
|
||||
constexpr DerivedRegionExtents() = default;
|
||||
|
||||
constexpr u64 GetAddress() const {
|
||||
return this->first_region->GetAddress();
|
||||
}
|
||||
|
||||
constexpr u64 GetLastAddress() const {
|
||||
return this->last_region->GetLastAddress();
|
||||
}
|
||||
|
||||
constexpr u64 GetEndAddress() const {
|
||||
return this->GetLastAddress() + 1;
|
||||
}
|
||||
|
||||
constexpr size_t GetSize() const {
|
||||
return this->GetEndAddress() - this->GetAddress();
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
using TreeType =
|
||||
Common::IntrusiveRedBlackTreeBaseTraits<KMemoryRegion>::TreeType<KMemoryRegion>;
|
||||
|
||||
public:
|
||||
using value_type = TreeType::value_type;
|
||||
using size_type = TreeType::size_type;
|
||||
using difference_type = TreeType::difference_type;
|
||||
using pointer = TreeType::pointer;
|
||||
using const_pointer = TreeType::const_pointer;
|
||||
using reference = TreeType::reference;
|
||||
using const_reference = TreeType::const_reference;
|
||||
using iterator = TreeType::iterator;
|
||||
using const_iterator = TreeType::const_iterator;
|
||||
|
||||
private:
|
||||
TreeType m_tree{};
|
||||
KMemoryRegionAllocator& memory_region_allocator;
|
||||
|
||||
public:
|
||||
explicit KMemoryRegionTree(KMemoryRegionAllocator& memory_region_allocator_);
|
||||
|
||||
public:
|
||||
KMemoryRegion* FindModifiable(u64 address) {
|
||||
if (auto it = this->find(KMemoryRegion(address, address, 0, 0)); it != this->end()) {
|
||||
return std::addressof(*it);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const KMemoryRegion* Find(u64 address) const {
|
||||
if (auto it = this->find(KMemoryRegion(address, address, 0, 0)); it != this->cend()) {
|
||||
return std::addressof(*it);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const KMemoryRegion* FindByType(KMemoryRegionType type_id) const {
|
||||
for (auto it = this->cbegin(); it != this->cend(); ++it) {
|
||||
if (it->GetType() == static_cast<u32>(type_id)) {
|
||||
return std::addressof(*it);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const KMemoryRegion* FindByTypeAndAttribute(u32 type_id, u32 attr) const {
|
||||
for (auto it = this->cbegin(); it != this->cend(); ++it) {
|
||||
if (it->GetType() == type_id && it->GetAttributes() == attr) {
|
||||
return std::addressof(*it);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const KMemoryRegion* FindFirstDerived(KMemoryRegionType type_id) const {
|
||||
for (auto it = this->cbegin(); it != this->cend(); it++) {
|
||||
if (it->IsDerivedFrom(type_id)) {
|
||||
return std::addressof(*it);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const KMemoryRegion* FindLastDerived(KMemoryRegionType type_id) const {
|
||||
const KMemoryRegion* region = nullptr;
|
||||
for (auto it = this->begin(); it != this->end(); it++) {
|
||||
if (it->IsDerivedFrom(type_id)) {
|
||||
region = std::addressof(*it);
|
||||
}
|
||||
}
|
||||
return region;
|
||||
}
|
||||
|
||||
DerivedRegionExtents GetDerivedRegionExtents(KMemoryRegionType type_id) const {
|
||||
DerivedRegionExtents extents;
|
||||
|
||||
ASSERT(extents.first_region == nullptr);
|
||||
ASSERT(extents.last_region == nullptr);
|
||||
|
||||
for (auto it = this->cbegin(); it != this->cend(); it++) {
|
||||
if (it->IsDerivedFrom(type_id)) {
|
||||
if (extents.first_region == nullptr) {
|
||||
extents.first_region = std::addressof(*it);
|
||||
}
|
||||
extents.last_region = std::addressof(*it);
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(extents.first_region != nullptr);
|
||||
ASSERT(extents.last_region != nullptr);
|
||||
|
||||
return extents;
|
||||
}
|
||||
|
||||
DerivedRegionExtents GetDerivedRegionExtents(u32 type_id) const {
|
||||
return GetDerivedRegionExtents(static_cast<KMemoryRegionType>(type_id));
|
||||
}
|
||||
|
||||
public:
|
||||
void InsertDirectly(u64 address, u64 last_address, u32 attr = 0, u32 type_id = 0);
|
||||
bool Insert(u64 address, size_t size, u32 type_id, u32 new_attr = 0, u32 old_attr = 0);
|
||||
|
||||
VAddr GetRandomAlignedRegion(size_t size, size_t alignment, u32 type_id);
|
||||
|
||||
VAddr GetRandomAlignedRegionWithGuard(size_t size, size_t alignment, u32 type_id,
|
||||
size_t guard_size) {
|
||||
return this->GetRandomAlignedRegion(size + 2 * guard_size, alignment, type_id) + guard_size;
|
||||
}
|
||||
|
||||
public:
|
||||
// Iterator accessors.
|
||||
iterator begin() {
|
||||
return m_tree.begin();
|
||||
}
|
||||
|
||||
const_iterator begin() const {
|
||||
return m_tree.begin();
|
||||
}
|
||||
|
||||
iterator end() {
|
||||
return m_tree.end();
|
||||
}
|
||||
|
||||
const_iterator end() const {
|
||||
return m_tree.end();
|
||||
}
|
||||
|
||||
const_iterator cbegin() const {
|
||||
return this->begin();
|
||||
}
|
||||
|
||||
const_iterator cend() const {
|
||||
return this->end();
|
||||
}
|
||||
|
||||
iterator iterator_to(reference ref) {
|
||||
return m_tree.iterator_to(ref);
|
||||
}
|
||||
|
||||
const_iterator iterator_to(const_reference ref) const {
|
||||
return m_tree.iterator_to(ref);
|
||||
}
|
||||
|
||||
// Content management.
|
||||
bool empty() const {
|
||||
return m_tree.empty();
|
||||
}
|
||||
|
||||
reference back() {
|
||||
return m_tree.back();
|
||||
}
|
||||
|
||||
const_reference back() const {
|
||||
return m_tree.back();
|
||||
}
|
||||
|
||||
reference front() {
|
||||
return m_tree.front();
|
||||
}
|
||||
|
||||
const_reference front() const {
|
||||
return m_tree.front();
|
||||
}
|
||||
|
||||
iterator insert(reference ref) {
|
||||
return m_tree.insert(ref);
|
||||
}
|
||||
|
||||
iterator erase(iterator it) {
|
||||
return m_tree.erase(it);
|
||||
}
|
||||
|
||||
iterator find(const_reference ref) const {
|
||||
return m_tree.find(ref);
|
||||
}
|
||||
|
||||
iterator nfind(const_reference ref) const {
|
||||
return m_tree.nfind(ref);
|
||||
}
|
||||
};
|
||||
|
||||
class KMemoryRegionAllocator final : NonCopyable {
|
||||
public:
|
||||
static constexpr size_t MaxMemoryRegions = 200;
|
||||
|
||||
constexpr KMemoryRegionAllocator() = default;
|
||||
|
||||
template <typename... Args>
|
||||
KMemoryRegion* Allocate(Args&&... args) {
|
||||
// Ensure we stay within the bounds of our heap.
|
||||
ASSERT(this->num_regions < MaxMemoryRegions);
|
||||
|
||||
// Create the new region.
|
||||
KMemoryRegion* region = std::addressof(this->region_heap[this->num_regions++]);
|
||||
new (region) KMemoryRegion(std::forward<Args>(args)...);
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<KMemoryRegion, MaxMemoryRegions> region_heap{};
|
||||
size_t num_regions{};
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
338
src/core/hle/kernel/k_memory_region_type.h
Normal file
338
src/core/hle/kernel/k_memory_region_type.h
Normal file
@@ -0,0 +1,338 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/bit_util.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
#define ARCH_ARM64
|
||||
#define BOARD_NINTENDO_NX
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
enum KMemoryRegionType : u32 {
|
||||
KMemoryRegionAttr_CarveoutProtected = 0x04000000,
|
||||
KMemoryRegionAttr_DidKernelMap = 0x08000000,
|
||||
KMemoryRegionAttr_ShouldKernelMap = 0x10000000,
|
||||
KMemoryRegionAttr_UserReadOnly = 0x20000000,
|
||||
KMemoryRegionAttr_NoUserMap = 0x40000000,
|
||||
KMemoryRegionAttr_LinearMapped = 0x80000000,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(KMemoryRegionType);
|
||||
|
||||
namespace impl {
|
||||
|
||||
constexpr size_t BitsForDeriveSparse(size_t n) {
|
||||
return n + 1;
|
||||
}
|
||||
|
||||
constexpr size_t BitsForDeriveDense(size_t n) {
|
||||
size_t low = 0, high = 1;
|
||||
for (size_t i = 0; i < n - 1; ++i) {
|
||||
if ((++low) == high) {
|
||||
++high;
|
||||
low = 0;
|
||||
}
|
||||
}
|
||||
return high + 1;
|
||||
}
|
||||
|
||||
class KMemoryRegionTypeValue {
|
||||
public:
|
||||
using ValueType = std::underlying_type_t<KMemoryRegionType>;
|
||||
|
||||
constexpr KMemoryRegionTypeValue() = default;
|
||||
|
||||
constexpr operator KMemoryRegionType() const {
|
||||
return static_cast<KMemoryRegionType>(m_value);
|
||||
}
|
||||
|
||||
constexpr ValueType GetValue() const {
|
||||
return m_value;
|
||||
}
|
||||
|
||||
constexpr const KMemoryRegionTypeValue& Finalize() {
|
||||
m_finalized = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr const KMemoryRegionTypeValue& SetSparseOnly() {
|
||||
m_sparse_only = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr const KMemoryRegionTypeValue& SetDenseOnly() {
|
||||
m_dense_only = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr KMemoryRegionTypeValue& SetAttribute(u32 attr) {
|
||||
m_value |= attr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr KMemoryRegionTypeValue DeriveInitial(
|
||||
size_t i, size_t next = Common::BitSize<ValueType>()) const {
|
||||
KMemoryRegionTypeValue new_type = *this;
|
||||
new_type.m_value = (ValueType{1} << i);
|
||||
new_type.m_next_bit = next;
|
||||
return new_type;
|
||||
}
|
||||
|
||||
constexpr KMemoryRegionTypeValue DeriveAttribute(u32 attr) const {
|
||||
KMemoryRegionTypeValue new_type = *this;
|
||||
new_type.m_value |= attr;
|
||||
return new_type;
|
||||
}
|
||||
|
||||
constexpr KMemoryRegionTypeValue DeriveTransition(size_t ofs = 0, size_t adv = 1) const {
|
||||
KMemoryRegionTypeValue new_type = *this;
|
||||
new_type.m_value |= (ValueType{1} << (m_next_bit + ofs));
|
||||
new_type.m_next_bit += adv;
|
||||
return new_type;
|
||||
}
|
||||
|
||||
constexpr KMemoryRegionTypeValue DeriveSparse(size_t ofs, size_t n, size_t i) const {
|
||||
KMemoryRegionTypeValue new_type = *this;
|
||||
new_type.m_value |= (ValueType{1} << (m_next_bit + ofs));
|
||||
new_type.m_value |= (ValueType{1} << (m_next_bit + ofs + 1 + i));
|
||||
new_type.m_next_bit += ofs + n + 1;
|
||||
return new_type;
|
||||
}
|
||||
|
||||
constexpr KMemoryRegionTypeValue Derive(size_t n, size_t i) const {
|
||||
size_t low = 0, high = 1;
|
||||
for (size_t j = 0; j < i; ++j) {
|
||||
if ((++low) == high) {
|
||||
++high;
|
||||
low = 0;
|
||||
}
|
||||
}
|
||||
|
||||
KMemoryRegionTypeValue new_type = *this;
|
||||
new_type.m_value |= (ValueType{1} << (m_next_bit + low));
|
||||
new_type.m_value |= (ValueType{1} << (m_next_bit + high));
|
||||
new_type.m_next_bit += BitsForDeriveDense(n);
|
||||
return new_type;
|
||||
}
|
||||
|
||||
constexpr KMemoryRegionTypeValue Advance(size_t n) const {
|
||||
KMemoryRegionTypeValue new_type = *this;
|
||||
new_type.m_next_bit += n;
|
||||
return new_type;
|
||||
}
|
||||
|
||||
constexpr bool IsAncestorOf(ValueType v) const {
|
||||
return (m_value | v) == v;
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr KMemoryRegionTypeValue(ValueType v) : m_value(v) {}
|
||||
|
||||
private:
|
||||
ValueType m_value{};
|
||||
size_t m_next_bit{};
|
||||
bool m_finalized{};
|
||||
bool m_sparse_only{};
|
||||
bool m_dense_only{};
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
|
||||
constexpr auto KMemoryRegionType_None = impl::KMemoryRegionTypeValue();
|
||||
constexpr auto KMemoryRegionType_Kernel = KMemoryRegionType_None.DeriveInitial(0, 2);
|
||||
constexpr auto KMemoryRegionType_Dram = KMemoryRegionType_None.DeriveInitial(1, 2);
|
||||
static_assert(KMemoryRegionType_Kernel.GetValue() == 0x1);
|
||||
static_assert(KMemoryRegionType_Dram.GetValue() == 0x2);
|
||||
|
||||
constexpr auto KMemoryRegionType_DramKernelBase =
|
||||
KMemoryRegionType_Dram.DeriveSparse(0, 3, 0)
|
||||
.SetAttribute(KMemoryRegionAttr_NoUserMap)
|
||||
.SetAttribute(KMemoryRegionAttr_CarveoutProtected);
|
||||
constexpr auto KMemoryRegionType_DramReservedBase = KMemoryRegionType_Dram.DeriveSparse(0, 3, 1);
|
||||
constexpr auto KMemoryRegionType_DramHeapBase =
|
||||
KMemoryRegionType_Dram.DeriveSparse(0, 3, 2).SetAttribute(KMemoryRegionAttr_LinearMapped);
|
||||
static_assert(KMemoryRegionType_DramKernelBase.GetValue() ==
|
||||
(0xE | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap));
|
||||
static_assert(KMemoryRegionType_DramReservedBase.GetValue() == (0x16));
|
||||
static_assert(KMemoryRegionType_DramHeapBase.GetValue() == (0x26 | KMemoryRegionAttr_LinearMapped));
|
||||
|
||||
constexpr auto KMemoryRegionType_DramKernelCode =
|
||||
KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 0);
|
||||
constexpr auto KMemoryRegionType_DramKernelSlab =
|
||||
KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 1);
|
||||
constexpr auto KMemoryRegionType_DramKernelPtHeap =
|
||||
KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 2).SetAttribute(
|
||||
KMemoryRegionAttr_LinearMapped);
|
||||
constexpr auto KMemoryRegionType_DramKernelInitPt =
|
||||
KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 3).SetAttribute(
|
||||
KMemoryRegionAttr_LinearMapped);
|
||||
static_assert(KMemoryRegionType_DramKernelCode.GetValue() ==
|
||||
(0xCE | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap));
|
||||
static_assert(KMemoryRegionType_DramKernelSlab.GetValue() ==
|
||||
(0x14E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap));
|
||||
static_assert(KMemoryRegionType_DramKernelPtHeap.GetValue() ==
|
||||
(0x24E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap |
|
||||
KMemoryRegionAttr_LinearMapped));
|
||||
static_assert(KMemoryRegionType_DramKernelInitPt.GetValue() ==
|
||||
(0x44E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap |
|
||||
KMemoryRegionAttr_LinearMapped));
|
||||
|
||||
constexpr auto KMemoryRegionType_DramReservedEarly =
|
||||
KMemoryRegionType_DramReservedBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap);
|
||||
static_assert(KMemoryRegionType_DramReservedEarly.GetValue() ==
|
||||
(0x16 | KMemoryRegionAttr_NoUserMap));
|
||||
|
||||
constexpr auto KMemoryRegionType_KernelTraceBuffer =
|
||||
KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 0)
|
||||
.SetAttribute(KMemoryRegionAttr_LinearMapped)
|
||||
.SetAttribute(KMemoryRegionAttr_UserReadOnly);
|
||||
constexpr auto KMemoryRegionType_OnMemoryBootImage =
|
||||
KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 1);
|
||||
constexpr auto KMemoryRegionType_DTB = KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 2);
|
||||
static_assert(KMemoryRegionType_KernelTraceBuffer.GetValue() ==
|
||||
(0xD6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_UserReadOnly));
|
||||
static_assert(KMemoryRegionType_OnMemoryBootImage.GetValue() == 0x156);
|
||||
static_assert(KMemoryRegionType_DTB.GetValue() == 0x256);
|
||||
|
||||
constexpr auto KMemoryRegionType_DramPoolPartition =
|
||||
KMemoryRegionType_DramHeapBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap);
|
||||
static_assert(KMemoryRegionType_DramPoolPartition.GetValue() ==
|
||||
(0x26 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
||||
|
||||
constexpr auto KMemoryRegionType_DramPoolManagement =
|
||||
KMemoryRegionType_DramPoolPartition.DeriveTransition(0, 2).DeriveTransition().SetAttribute(
|
||||
KMemoryRegionAttr_CarveoutProtected);
|
||||
constexpr auto KMemoryRegionType_DramUserPool =
|
||||
KMemoryRegionType_DramPoolPartition.DeriveTransition(1, 2).DeriveTransition();
|
||||
static_assert(KMemoryRegionType_DramPoolManagement.GetValue() ==
|
||||
(0x166 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
|
||||
KMemoryRegionAttr_CarveoutProtected));
|
||||
static_assert(KMemoryRegionType_DramUserPool.GetValue() ==
|
||||
(0x1A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
||||
|
||||
constexpr auto KMemoryRegionType_DramApplicationPool = KMemoryRegionType_DramUserPool.Derive(4, 0);
|
||||
constexpr auto KMemoryRegionType_DramAppletPool = KMemoryRegionType_DramUserPool.Derive(4, 1);
|
||||
constexpr auto KMemoryRegionType_DramSystemNonSecurePool =
|
||||
KMemoryRegionType_DramUserPool.Derive(4, 2);
|
||||
constexpr auto KMemoryRegionType_DramSystemPool =
|
||||
KMemoryRegionType_DramUserPool.Derive(4, 3).SetAttribute(KMemoryRegionAttr_CarveoutProtected);
|
||||
static_assert(KMemoryRegionType_DramApplicationPool.GetValue() ==
|
||||
(0x7A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
||||
static_assert(KMemoryRegionType_DramAppletPool.GetValue() ==
|
||||
(0xBA6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
||||
static_assert(KMemoryRegionType_DramSystemNonSecurePool.GetValue() ==
|
||||
(0xDA6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
||||
static_assert(KMemoryRegionType_DramSystemPool.GetValue() ==
|
||||
(0x13A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
|
||||
KMemoryRegionAttr_CarveoutProtected));
|
||||
|
||||
constexpr auto KMemoryRegionType_VirtualDramHeapBase = KMemoryRegionType_Dram.DeriveSparse(1, 3, 0);
|
||||
constexpr auto KMemoryRegionType_VirtualDramKernelPtHeap =
|
||||
KMemoryRegionType_Dram.DeriveSparse(1, 3, 1);
|
||||
constexpr auto KMemoryRegionType_VirtualDramKernelTraceBuffer =
|
||||
KMemoryRegionType_Dram.DeriveSparse(1, 3, 2);
|
||||
static_assert(KMemoryRegionType_VirtualDramHeapBase.GetValue() == 0x1A);
|
||||
static_assert(KMemoryRegionType_VirtualDramKernelPtHeap.GetValue() == 0x2A);
|
||||
static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A);
|
||||
|
||||
constexpr auto KMemoryRegionType_VirtualDramKernelInitPt =
|
||||
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 0);
|
||||
constexpr auto KMemoryRegionType_VirtualDramPoolManagement =
|
||||
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 1);
|
||||
constexpr auto KMemoryRegionType_VirtualDramUserPool =
|
||||
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 2);
|
||||
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. It's worth eventually trying
|
||||
// to understand why Nintendo made this choice.
|
||||
// UNUSED: .Derive(6, 0);
|
||||
// UNUSED: .Derive(6, 1);
|
||||
constexpr auto KMemoryRegionType_VirtualDramAppletPool =
|
||||
KMemoryRegionType_VirtualDramUserPool.Derive(6, 2);
|
||||
constexpr auto KMemoryRegionType_VirtualDramApplicationPool =
|
||||
KMemoryRegionType_VirtualDramUserPool.Derive(6, 3);
|
||||
constexpr auto KMemoryRegionType_VirtualDramSystemNonSecurePool =
|
||||
KMemoryRegionType_VirtualDramUserPool.Derive(6, 4);
|
||||
constexpr auto KMemoryRegionType_VirtualDramSystemPool =
|
||||
KMemoryRegionType_VirtualDramUserPool.Derive(6, 5);
|
||||
static_assert(KMemoryRegionType_VirtualDramAppletPool.GetValue() == 0x1B1A);
|
||||
static_assert(KMemoryRegionType_VirtualDramApplicationPool.GetValue() == 0x271A);
|
||||
static_assert(KMemoryRegionType_VirtualDramSystemNonSecurePool.GetValue() == 0x2B1A);
|
||||
static_assert(KMemoryRegionType_VirtualDramSystemPool.GetValue() == 0x331A);
|
||||
|
||||
constexpr auto KMemoryRegionType_ArchDeviceBase =
|
||||
KMemoryRegionType_Kernel.DeriveTransition(0, 1).SetSparseOnly();
|
||||
constexpr auto KMemoryRegionType_BoardDeviceBase =
|
||||
KMemoryRegionType_Kernel.DeriveTransition(0, 2).SetDenseOnly();
|
||||
static_assert(KMemoryRegionType_ArchDeviceBase.GetValue() == 0x5);
|
||||
static_assert(KMemoryRegionType_BoardDeviceBase.GetValue() == 0x5);
|
||||
|
||||
#if defined(ARCH_ARM64)
|
||||
#include "core/hle/kernel/arch/arm64/k_memory_region_device_types.inc"
|
||||
#elif defined(ARCH_ARM)
|
||||
#error "Unimplemented"
|
||||
#else
|
||||
// Default to no architecture devices.
|
||||
constexpr auto NumArchitectureDeviceRegions = 0;
|
||||
#endif
|
||||
static_assert(NumArchitectureDeviceRegions >= 0);
|
||||
|
||||
#if defined(BOARD_NINTENDO_NX)
|
||||
#include "core/hle/kernel/board/nintendo/nx/k_memory_region_device_types.inc"
|
||||
#else
|
||||
// Default to no board devices.
|
||||
constexpr auto NumBoardDeviceRegions = 0;
|
||||
#endif
|
||||
static_assert(NumBoardDeviceRegions >= 0);
|
||||
|
||||
constexpr auto KMemoryRegionType_KernelCode = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 0);
|
||||
constexpr auto KMemoryRegionType_KernelStack = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 1);
|
||||
constexpr auto KMemoryRegionType_KernelMisc = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 2);
|
||||
constexpr auto KMemoryRegionType_KernelSlab = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 3);
|
||||
static_assert(KMemoryRegionType_KernelCode.GetValue() == 0x19);
|
||||
static_assert(KMemoryRegionType_KernelStack.GetValue() == 0x29);
|
||||
static_assert(KMemoryRegionType_KernelMisc.GetValue() == 0x49);
|
||||
static_assert(KMemoryRegionType_KernelSlab.GetValue() == 0x89);
|
||||
|
||||
constexpr auto KMemoryRegionType_KernelMiscDerivedBase =
|
||||
KMemoryRegionType_KernelMisc.DeriveTransition();
|
||||
static_assert(KMemoryRegionType_KernelMiscDerivedBase.GetValue() == 0x149);
|
||||
|
||||
// UNUSED: .Derive(7, 0);
|
||||
constexpr auto KMemoryRegionType_KernelMiscMainStack =
|
||||
KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 1);
|
||||
constexpr auto KMemoryRegionType_KernelMiscMappedDevice =
|
||||
KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 2);
|
||||
constexpr auto KMemoryRegionType_KernelMiscExceptionStack =
|
||||
KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 3);
|
||||
constexpr auto KMemoryRegionType_KernelMiscUnknownDebug =
|
||||
KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 4);
|
||||
// UNUSED: .Derive(7, 5);
|
||||
constexpr auto KMemoryRegionType_KernelMiscIdleStack =
|
||||
KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 6);
|
||||
static_assert(KMemoryRegionType_KernelMiscMainStack.GetValue() == 0xB49);
|
||||
static_assert(KMemoryRegionType_KernelMiscMappedDevice.GetValue() == 0xD49);
|
||||
static_assert(KMemoryRegionType_KernelMiscExceptionStack.GetValue() == 0x1349);
|
||||
static_assert(KMemoryRegionType_KernelMiscUnknownDebug.GetValue() == 0x1549);
|
||||
static_assert(KMemoryRegionType_KernelMiscIdleStack.GetValue() == 0x2349);
|
||||
|
||||
constexpr auto KMemoryRegionType_KernelTemp = KMemoryRegionType_Kernel.Advance(2).Derive(2, 0);
|
||||
static_assert(KMemoryRegionType_KernelTemp.GetValue() == 0x31);
|
||||
|
||||
constexpr KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) {
|
||||
if (KMemoryRegionType_KernelTraceBuffer.IsAncestorOf(type_id)) {
|
||||
return KMemoryRegionType_VirtualDramKernelTraceBuffer;
|
||||
} else if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) {
|
||||
return KMemoryRegionType_VirtualDramKernelPtHeap;
|
||||
} else {
|
||||
return KMemoryRegionType_Dram;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
279
src/core/hle/kernel/k_page_bitmap.h
Normal file
279
src/core/hle/kernel/k_page_bitmap.h
Normal file
@@ -0,0 +1,279 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <bit>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/bit_util.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/tiny_mt.h"
|
||||
#include "core/hle/kernel/k_system_control.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KPageBitmap {
|
||||
private:
|
||||
class RandomBitGenerator {
|
||||
private:
|
||||
Common::TinyMT rng{};
|
||||
u32 entropy{};
|
||||
u32 bits_available{};
|
||||
|
||||
private:
|
||||
void RefreshEntropy() {
|
||||
entropy = rng.GenerateRandomU32();
|
||||
bits_available = static_cast<u32>(Common::BitSize<decltype(entropy)>());
|
||||
}
|
||||
|
||||
bool GenerateRandomBit() {
|
||||
if (bits_available == 0) {
|
||||
this->RefreshEntropy();
|
||||
}
|
||||
|
||||
const bool rnd_bit = (entropy & 1) != 0;
|
||||
entropy >>= 1;
|
||||
--bits_available;
|
||||
return rnd_bit;
|
||||
}
|
||||
|
||||
public:
|
||||
RandomBitGenerator() {
|
||||
rng.Initialize(static_cast<u32>(KSystemControl::GenerateRandomU64()));
|
||||
}
|
||||
|
||||
std::size_t SelectRandomBit(u64 bitmap) {
|
||||
u64 selected = 0;
|
||||
|
||||
u64 cur_num_bits = Common::BitSize<decltype(bitmap)>() / 2;
|
||||
u64 cur_mask = (1ULL << cur_num_bits) - 1;
|
||||
|
||||
while (cur_num_bits) {
|
||||
const u64 low = (bitmap >> 0) & cur_mask;
|
||||
const u64 high = (bitmap >> cur_num_bits) & cur_mask;
|
||||
|
||||
bool choose_low;
|
||||
if (high == 0) {
|
||||
// If only low val is set, choose low.
|
||||
choose_low = true;
|
||||
} else if (low == 0) {
|
||||
// If only high val is set, choose high.
|
||||
choose_low = false;
|
||||
} else {
|
||||
// If both are set, choose random.
|
||||
choose_low = this->GenerateRandomBit();
|
||||
}
|
||||
|
||||
// If we chose low, proceed with low.
|
||||
if (choose_low) {
|
||||
bitmap = low;
|
||||
selected += 0;
|
||||
} else {
|
||||
bitmap = high;
|
||||
selected += cur_num_bits;
|
||||
}
|
||||
|
||||
// Proceed.
|
||||
cur_num_bits /= 2;
|
||||
cur_mask >>= cur_num_bits;
|
||||
}
|
||||
|
||||
return selected;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
static constexpr std::size_t MaxDepth = 4;
|
||||
|
||||
private:
|
||||
std::array<u64*, MaxDepth> bit_storages{};
|
||||
RandomBitGenerator rng{};
|
||||
std::size_t num_bits{};
|
||||
std::size_t used_depths{};
|
||||
|
||||
public:
|
||||
KPageBitmap() = default;
|
||||
|
||||
constexpr std::size_t GetNumBits() const {
|
||||
return num_bits;
|
||||
}
|
||||
constexpr s32 GetHighestDepthIndex() const {
|
||||
return static_cast<s32>(used_depths) - 1;
|
||||
}
|
||||
|
||||
u64* Initialize(u64* storage, std::size_t size) {
|
||||
// Initially, everything is un-set.
|
||||
num_bits = 0;
|
||||
|
||||
// Calculate the needed bitmap depth.
|
||||
used_depths = static_cast<std::size_t>(GetRequiredDepth(size));
|
||||
ASSERT(used_depths <= MaxDepth);
|
||||
|
||||
// Set the bitmap pointers.
|
||||
for (s32 depth = this->GetHighestDepthIndex(); depth >= 0; depth--) {
|
||||
bit_storages[depth] = storage;
|
||||
size = Common::AlignUp(size, Common::BitSize<u64>()) / Common::BitSize<u64>();
|
||||
storage += size;
|
||||
}
|
||||
|
||||
return storage;
|
||||
}
|
||||
|
||||
s64 FindFreeBlock(bool random) {
|
||||
uintptr_t offset = 0;
|
||||
s32 depth = 0;
|
||||
|
||||
if (random) {
|
||||
do {
|
||||
const u64 v = bit_storages[depth][offset];
|
||||
if (v == 0) {
|
||||
// If depth is bigger than zero, then a previous level indicated a block was
|
||||
// free.
|
||||
ASSERT(depth == 0);
|
||||
return -1;
|
||||
}
|
||||
offset = offset * Common::BitSize<u64>() + rng.SelectRandomBit(v);
|
||||
++depth;
|
||||
} while (depth < static_cast<s32>(used_depths));
|
||||
} else {
|
||||
do {
|
||||
const u64 v = bit_storages[depth][offset];
|
||||
if (v == 0) {
|
||||
// If depth is bigger than zero, then a previous level indicated a block was
|
||||
// free.
|
||||
ASSERT(depth == 0);
|
||||
return -1;
|
||||
}
|
||||
offset = offset * Common::BitSize<u64>() + std::countr_zero(v);
|
||||
++depth;
|
||||
} while (depth < static_cast<s32>(used_depths));
|
||||
}
|
||||
|
||||
return static_cast<s64>(offset);
|
||||
}
|
||||
|
||||
void SetBit(std::size_t offset) {
|
||||
this->SetBit(this->GetHighestDepthIndex(), offset);
|
||||
num_bits++;
|
||||
}
|
||||
|
||||
void ClearBit(std::size_t offset) {
|
||||
this->ClearBit(this->GetHighestDepthIndex(), offset);
|
||||
num_bits--;
|
||||
}
|
||||
|
||||
bool ClearRange(std::size_t offset, std::size_t count) {
|
||||
s32 depth = this->GetHighestDepthIndex();
|
||||
u64* bits = bit_storages[depth];
|
||||
std::size_t bit_ind = offset / Common::BitSize<u64>();
|
||||
if (count < Common::BitSize<u64>()) {
|
||||
const std::size_t shift = offset % Common::BitSize<u64>();
|
||||
ASSERT(shift + count <= Common::BitSize<u64>());
|
||||
// Check that all the bits are set.
|
||||
const u64 mask = ((u64(1) << count) - 1) << shift;
|
||||
u64 v = bits[bit_ind];
|
||||
if ((v & mask) != mask) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clear the bits.
|
||||
v &= ~mask;
|
||||
bits[bit_ind] = v;
|
||||
if (v == 0) {
|
||||
this->ClearBit(depth - 1, bit_ind);
|
||||
}
|
||||
} else {
|
||||
ASSERT(offset % Common::BitSize<u64>() == 0);
|
||||
ASSERT(count % Common::BitSize<u64>() == 0);
|
||||
// Check that all the bits are set.
|
||||
std::size_t remaining = count;
|
||||
std::size_t i = 0;
|
||||
do {
|
||||
if (bits[bit_ind + i++] != ~u64(0)) {
|
||||
return false;
|
||||
}
|
||||
remaining -= Common::BitSize<u64>();
|
||||
} while (remaining > 0);
|
||||
|
||||
// Clear the bits.
|
||||
remaining = count;
|
||||
i = 0;
|
||||
do {
|
||||
bits[bit_ind + i] = 0;
|
||||
this->ClearBit(depth - 1, bit_ind + i);
|
||||
i++;
|
||||
remaining -= Common::BitSize<u64>();
|
||||
} while (remaining > 0);
|
||||
}
|
||||
|
||||
num_bits -= count;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
void SetBit(s32 depth, std::size_t offset) {
|
||||
while (depth >= 0) {
|
||||
std::size_t ind = offset / Common::BitSize<u64>();
|
||||
std::size_t which = offset % Common::BitSize<u64>();
|
||||
const u64 mask = u64(1) << which;
|
||||
|
||||
u64* bit = std::addressof(bit_storages[depth][ind]);
|
||||
u64 v = *bit;
|
||||
ASSERT((v & mask) == 0);
|
||||
*bit = v | mask;
|
||||
if (v) {
|
||||
break;
|
||||
}
|
||||
offset = ind;
|
||||
depth--;
|
||||
}
|
||||
}
|
||||
|
||||
void ClearBit(s32 depth, std::size_t offset) {
|
||||
while (depth >= 0) {
|
||||
std::size_t ind = offset / Common::BitSize<u64>();
|
||||
std::size_t which = offset % Common::BitSize<u64>();
|
||||
const u64 mask = u64(1) << which;
|
||||
|
||||
u64* bit = std::addressof(bit_storages[depth][ind]);
|
||||
u64 v = *bit;
|
||||
ASSERT((v & mask) != 0);
|
||||
v &= ~mask;
|
||||
*bit = v;
|
||||
if (v) {
|
||||
break;
|
||||
}
|
||||
offset = ind;
|
||||
depth--;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr s32 GetRequiredDepth(std::size_t region_size) {
|
||||
s32 depth = 0;
|
||||
while (true) {
|
||||
region_size /= Common::BitSize<u64>();
|
||||
depth++;
|
||||
if (region_size == 0) {
|
||||
return depth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
static constexpr std::size_t CalculateManagementOverheadSize(std::size_t region_size) {
|
||||
std::size_t overhead_bits = 0;
|
||||
for (s32 depth = GetRequiredDepth(region_size) - 1; depth >= 0; depth--) {
|
||||
region_size =
|
||||
Common::AlignUp(region_size, Common::BitSize<u64>()) / Common::BitSize<u64>();
|
||||
overhead_bits += region_size;
|
||||
}
|
||||
return overhead_bits * sizeof(u64);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -2,16 +2,13 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
// This file references various implementation details from Atmosphere, an open-source firmware for
|
||||
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/memory/page_heap.h"
|
||||
#include "core/hle/kernel/k_page_heap.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Kernel::Memory {
|
||||
namespace Kernel {
|
||||
|
||||
void PageHeap::Initialize(VAddr address, std::size_t size, std::size_t metadata_size) {
|
||||
void KPageHeap::Initialize(VAddr address, std::size_t size, std::size_t metadata_size) {
|
||||
// Check our assumptions
|
||||
ASSERT(Common::IsAligned((address), PageSize));
|
||||
ASSERT(Common::IsAligned(size, PageSize));
|
||||
@@ -32,11 +29,11 @@ void PageHeap::Initialize(VAddr address, std::size_t size, std::size_t metadata_
|
||||
}
|
||||
}
|
||||
|
||||
VAddr PageHeap::AllocateBlock(s32 index) {
|
||||
VAddr KPageHeap::AllocateBlock(s32 index, bool random) {
|
||||
const std::size_t needed_size{blocks[index].GetSize()};
|
||||
|
||||
for (s32 i{index}; i < static_cast<s32>(MemoryBlockPageShifts.size()); i++) {
|
||||
if (const VAddr addr{blocks[i].PopBlock()}; addr) {
|
||||
if (const VAddr addr{blocks[i].PopBlock(random)}; addr) {
|
||||
if (const std::size_t allocated_size{blocks[i].GetSize()};
|
||||
allocated_size > needed_size) {
|
||||
Free(addr + needed_size, (allocated_size - needed_size) / PageSize);
|
||||
@@ -48,13 +45,13 @@ VAddr PageHeap::AllocateBlock(s32 index) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PageHeap::FreeBlock(VAddr block, s32 index) {
|
||||
void KPageHeap::FreeBlock(VAddr block, s32 index) {
|
||||
do {
|
||||
block = blocks[index++].PushBlock(block);
|
||||
} while (block != 0);
|
||||
}
|
||||
|
||||
void PageHeap::Free(VAddr addr, std::size_t num_pages) {
|
||||
void KPageHeap::Free(VAddr addr, std::size_t num_pages) {
|
||||
// Freeing no pages is a no-op
|
||||
if (num_pages == 0) {
|
||||
return;
|
||||
@@ -104,16 +101,16 @@ void PageHeap::Free(VAddr addr, std::size_t num_pages) {
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t PageHeap::CalculateMetadataOverheadSize(std::size_t region_size) {
|
||||
std::size_t KPageHeap::CalculateManagementOverheadSize(std::size_t region_size) {
|
||||
std::size_t overhead_size = 0;
|
||||
for (std::size_t i = 0; i < MemoryBlockPageShifts.size(); i++) {
|
||||
const std::size_t cur_block_shift{MemoryBlockPageShifts[i]};
|
||||
const std::size_t next_block_shift{
|
||||
(i != MemoryBlockPageShifts.size() - 1) ? MemoryBlockPageShifts[i + 1] : 0};
|
||||
overhead_size += PageHeap::Block::CalculateMetadataOverheadSize(
|
||||
overhead_size += KPageHeap::Block::CalculateManagementOverheadSize(
|
||||
region_size, cur_block_shift, next_block_shift);
|
||||
}
|
||||
return Common::AlignUp(overhead_size, PageSize);
|
||||
}
|
||||
|
||||
} // namespace Kernel::Memory
|
||||
} // namespace Kernel
|
||||
193
src/core/hle/kernel/k_page_heap.h
Normal file
193
src/core/hle/kernel/k_page_heap.h
Normal file
@@ -0,0 +1,193 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <bit>
|
||||
#include <vector>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/k_page_bitmap.h"
|
||||
#include "core/hle/kernel/memory_types.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KPageHeap final : NonCopyable {
|
||||
public:
|
||||
static constexpr s32 GetAlignedBlockIndex(std::size_t num_pages, std::size_t align_pages) {
|
||||
const auto target_pages{std::max(num_pages, align_pages)};
|
||||
for (std::size_t i = 0; i < NumMemoryBlockPageShifts; i++) {
|
||||
if (target_pages <=
|
||||
(static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) {
|
||||
return static_cast<s32>(i);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static constexpr s32 GetBlockIndex(std::size_t num_pages) {
|
||||
for (s32 i{static_cast<s32>(NumMemoryBlockPageShifts) - 1}; i >= 0; i--) {
|
||||
if (num_pages >= (static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static constexpr std::size_t GetBlockSize(std::size_t index) {
|
||||
return static_cast<std::size_t>(1) << MemoryBlockPageShifts[index];
|
||||
}
|
||||
|
||||
static constexpr std::size_t GetBlockNumPages(std::size_t index) {
|
||||
return GetBlockSize(index) / PageSize;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr std::size_t NumMemoryBlockPageShifts{7};
|
||||
static constexpr std::array<std::size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{
|
||||
0xC, 0x10, 0x15, 0x16, 0x19, 0x1D, 0x1E,
|
||||
};
|
||||
|
||||
class Block final : NonCopyable {
|
||||
private:
|
||||
KPageBitmap bitmap;
|
||||
VAddr heap_address{};
|
||||
uintptr_t end_offset{};
|
||||
std::size_t block_shift{};
|
||||
std::size_t next_block_shift{};
|
||||
|
||||
public:
|
||||
Block() = default;
|
||||
|
||||
constexpr std::size_t GetShift() const {
|
||||
return block_shift;
|
||||
}
|
||||
constexpr std::size_t GetNextShift() const {
|
||||
return next_block_shift;
|
||||
}
|
||||
constexpr std::size_t GetSize() const {
|
||||
return static_cast<std::size_t>(1) << GetShift();
|
||||
}
|
||||
constexpr std::size_t GetNumPages() const {
|
||||
return GetSize() / PageSize;
|
||||
}
|
||||
constexpr std::size_t GetNumFreeBlocks() const {
|
||||
return bitmap.GetNumBits();
|
||||
}
|
||||
constexpr std::size_t GetNumFreePages() const {
|
||||
return GetNumFreeBlocks() * GetNumPages();
|
||||
}
|
||||
|
||||
u64* Initialize(VAddr addr, std::size_t size, std::size_t bs, std::size_t nbs,
|
||||
u64* bit_storage) {
|
||||
// Set shifts
|
||||
block_shift = bs;
|
||||
next_block_shift = nbs;
|
||||
|
||||
// Align up the address
|
||||
VAddr end{addr + size};
|
||||
const auto align{(next_block_shift != 0) ? (1ULL << next_block_shift)
|
||||
: (1ULL << block_shift)};
|
||||
addr = Common::AlignDown((addr), align);
|
||||
end = Common::AlignUp((end), align);
|
||||
|
||||
heap_address = addr;
|
||||
end_offset = (end - addr) / (1ULL << block_shift);
|
||||
return bitmap.Initialize(bit_storage, end_offset);
|
||||
}
|
||||
|
||||
VAddr PushBlock(VAddr address) {
|
||||
// Set the bit for the free block
|
||||
std::size_t offset{(address - heap_address) >> GetShift()};
|
||||
bitmap.SetBit(offset);
|
||||
|
||||
// If we have a next shift, try to clear the blocks below and return the address
|
||||
if (GetNextShift()) {
|
||||
const auto diff{1ULL << (GetNextShift() - GetShift())};
|
||||
offset = Common::AlignDown(offset, diff);
|
||||
if (bitmap.ClearRange(offset, diff)) {
|
||||
return heap_address + (offset << GetShift());
|
||||
}
|
||||
}
|
||||
|
||||
// We couldn't coalesce, or we're already as big as possible
|
||||
return 0;
|
||||
}
|
||||
|
||||
VAddr PopBlock(bool random) {
|
||||
// Find a free block
|
||||
const s64 soffset{bitmap.FindFreeBlock(random)};
|
||||
if (soffset < 0) {
|
||||
return 0;
|
||||
}
|
||||
const auto offset{static_cast<std::size_t>(soffset)};
|
||||
|
||||
// Update our tracking and return it
|
||||
bitmap.ClearBit(offset);
|
||||
return heap_address + (offset << GetShift());
|
||||
}
|
||||
|
||||
public:
|
||||
static constexpr std::size_t CalculateManagementOverheadSize(std::size_t region_size,
|
||||
std::size_t cur_block_shift,
|
||||
std::size_t next_block_shift) {
|
||||
const auto cur_block_size{(1ULL << cur_block_shift)};
|
||||
const auto next_block_size{(1ULL << next_block_shift)};
|
||||
const auto align{(next_block_shift != 0) ? next_block_size : cur_block_size};
|
||||
return KPageBitmap::CalculateManagementOverheadSize(
|
||||
(align * 2 + Common::AlignUp(region_size, align)) / cur_block_size);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
KPageHeap() = default;
|
||||
|
||||
constexpr VAddr GetAddress() const {
|
||||
return heap_address;
|
||||
}
|
||||
constexpr std::size_t GetSize() const {
|
||||
return heap_size;
|
||||
}
|
||||
constexpr VAddr GetEndAddress() const {
|
||||
return GetAddress() + GetSize();
|
||||
}
|
||||
constexpr std::size_t GetPageOffset(VAddr block) const {
|
||||
return (block - GetAddress()) / PageSize;
|
||||
}
|
||||
|
||||
void Initialize(VAddr heap_address, std::size_t heap_size, std::size_t metadata_size);
|
||||
VAddr AllocateBlock(s32 index, bool random);
|
||||
void Free(VAddr addr, std::size_t num_pages);
|
||||
|
||||
void UpdateUsedSize() {
|
||||
used_size = heap_size - (GetNumFreePages() * PageSize);
|
||||
}
|
||||
|
||||
static std::size_t CalculateManagementOverheadSize(std::size_t region_size);
|
||||
|
||||
private:
|
||||
constexpr std::size_t GetNumFreePages() const {
|
||||
std::size_t num_free{};
|
||||
|
||||
for (const auto& block : blocks) {
|
||||
num_free += block.GetNumFreePages();
|
||||
}
|
||||
|
||||
return num_free;
|
||||
}
|
||||
|
||||
void FreeBlock(VAddr block, s32 index);
|
||||
|
||||
VAddr heap_address{};
|
||||
std::size_t heap_size{};
|
||||
std::size_t used_size{};
|
||||
std::array<Block, NumMemoryBlockPageShifts> blocks{};
|
||||
std::vector<u64> metadata;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -8,12 +8,12 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/memory/memory_types.h"
|
||||
#include "core/hle/kernel/memory_types.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Kernel::Memory {
|
||||
namespace Kernel {
|
||||
|
||||
class PageLinkedList final {
|
||||
class KPageLinkedList final {
|
||||
public:
|
||||
class Node final {
|
||||
public:
|
||||
@@ -33,8 +33,8 @@ public:
|
||||
};
|
||||
|
||||
public:
|
||||
PageLinkedList() = default;
|
||||
PageLinkedList(u64 address, u64 num_pages) {
|
||||
KPageLinkedList() = default;
|
||||
KPageLinkedList(u64 address, u64 num_pages) {
|
||||
ASSERT(AddBlock(address, num_pages).IsSuccess());
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ public:
|
||||
return num_pages;
|
||||
}
|
||||
|
||||
bool IsEqual(PageLinkedList& other) const {
|
||||
bool IsEqual(KPageLinkedList& other) const {
|
||||
auto this_node = nodes.begin();
|
||||
auto other_node = other.nodes.begin();
|
||||
while (this_node != nodes.end() && other_node != other.nodes.end()) {
|
||||
@@ -89,4 +89,4 @@ private:
|
||||
std::list<Node> nodes;
|
||||
};
|
||||
|
||||
} // namespace Kernel::Memory
|
||||
} // namespace Kernel
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,27 +10,27 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/page_table.h"
|
||||
#include "core/file_sys/program_metadata.h"
|
||||
#include "core/hle/kernel/memory/memory_block.h"
|
||||
#include "core/hle/kernel/memory/memory_manager.h"
|
||||
#include "core/hle/kernel/k_memory_block.h"
|
||||
#include "core/hle/kernel/k_memory_manager.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Kernel::Memory {
|
||||
namespace Kernel {
|
||||
|
||||
class MemoryBlockManager;
|
||||
class KMemoryBlockManager;
|
||||
|
||||
class PageTable final : NonCopyable {
|
||||
class KPageTable final : NonCopyable {
|
||||
public:
|
||||
explicit PageTable(Core::System& system);
|
||||
explicit KPageTable(Core::System& system);
|
||||
|
||||
ResultCode InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr,
|
||||
VAddr code_addr, std::size_t code_size,
|
||||
Memory::MemoryManager::Pool pool);
|
||||
ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, MemoryState state,
|
||||
MemoryPermission perm);
|
||||
KMemoryManager::Pool pool);
|
||||
ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state,
|
||||
KMemoryPermission perm);
|
||||
ResultCode MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
|
||||
ResultCode UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
|
||||
ResultCode MapPhysicalMemory(VAddr addr, std::size_t size);
|
||||
@@ -38,20 +38,20 @@ public:
|
||||
ResultCode UnmapMemory(VAddr addr, std::size_t size);
|
||||
ResultCode Map(VAddr dst_addr, VAddr src_addr, std::size_t size);
|
||||
ResultCode Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size);
|
||||
ResultCode MapPages(VAddr addr, PageLinkedList& page_linked_list, MemoryState state,
|
||||
MemoryPermission perm);
|
||||
ResultCode SetCodeMemoryPermission(VAddr addr, std::size_t size, MemoryPermission perm);
|
||||
MemoryInfo QueryInfo(VAddr addr);
|
||||
ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, MemoryPermission perm);
|
||||
ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state,
|
||||
KMemoryPermission perm);
|
||||
ResultCode SetCodeMemoryPermission(VAddr addr, std::size_t size, KMemoryPermission perm);
|
||||
KMemoryInfo QueryInfo(VAddr addr);
|
||||
ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm);
|
||||
ResultCode ResetTransferMemory(VAddr addr, std::size_t size);
|
||||
ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, MemoryAttribute mask,
|
||||
MemoryAttribute value);
|
||||
ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, KMemoryAttribute mask,
|
||||
KMemoryAttribute value);
|
||||
ResultCode SetHeapCapacity(std::size_t new_heap_capacity);
|
||||
ResultVal<VAddr> SetHeapSize(std::size_t size);
|
||||
ResultVal<VAddr> AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align,
|
||||
bool is_map_only, VAddr region_start,
|
||||
std::size_t region_num_pages, MemoryState state,
|
||||
MemoryPermission perm, PAddr map_addr = 0);
|
||||
std::size_t region_num_pages, KMemoryState state,
|
||||
KMemoryPermission perm, PAddr map_addr = 0);
|
||||
ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size);
|
||||
ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size);
|
||||
|
||||
@@ -72,47 +72,49 @@ private:
|
||||
ChangePermissionsAndRefresh,
|
||||
};
|
||||
|
||||
static constexpr MemoryAttribute DefaultMemoryIgnoreAttr =
|
||||
MemoryAttribute::DontCareMask | MemoryAttribute::IpcLocked | MemoryAttribute::DeviceShared;
|
||||
static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr = KMemoryAttribute::DontCareMask |
|
||||
KMemoryAttribute::IpcLocked |
|
||||
KMemoryAttribute::DeviceShared;
|
||||
|
||||
ResultCode InitializeMemoryLayout(VAddr start, VAddr end);
|
||||
ResultCode MapPages(VAddr addr, const PageLinkedList& page_linked_list, MemoryPermission perm);
|
||||
void MapPhysicalMemory(PageLinkedList& page_linked_list, VAddr start, VAddr end);
|
||||
ResultCode MapPages(VAddr addr, const KPageLinkedList& page_linked_list,
|
||||
KMemoryPermission perm);
|
||||
void MapPhysicalMemory(KPageLinkedList& page_linked_list, VAddr start, VAddr end);
|
||||
bool IsRegionMapped(VAddr address, u64 size);
|
||||
bool IsRegionContiguous(VAddr addr, u64 size) const;
|
||||
void AddRegionToPages(VAddr start, std::size_t num_pages, PageLinkedList& page_linked_list);
|
||||
MemoryInfo QueryInfoImpl(VAddr addr);
|
||||
void AddRegionToPages(VAddr start, std::size_t num_pages, KPageLinkedList& page_linked_list);
|
||||
KMemoryInfo QueryInfoImpl(VAddr addr);
|
||||
VAddr AllocateVirtualMemory(VAddr start, std::size_t region_num_pages, u64 needed_num_pages,
|
||||
std::size_t align);
|
||||
ResultCode Operate(VAddr addr, std::size_t num_pages, const PageLinkedList& page_group,
|
||||
ResultCode Operate(VAddr addr, std::size_t num_pages, const KPageLinkedList& page_group,
|
||||
OperationType operation);
|
||||
ResultCode Operate(VAddr addr, std::size_t num_pages, MemoryPermission perm,
|
||||
ResultCode Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm,
|
||||
OperationType operation, PAddr map_addr = 0);
|
||||
constexpr VAddr GetRegionAddress(MemoryState state) const;
|
||||
constexpr std::size_t GetRegionSize(MemoryState state) const;
|
||||
constexpr bool CanContain(VAddr addr, std::size_t size, MemoryState state) const;
|
||||
constexpr VAddr GetRegionAddress(KMemoryState state) const;
|
||||
constexpr std::size_t GetRegionSize(KMemoryState state) const;
|
||||
constexpr bool CanContain(VAddr addr, std::size_t size, KMemoryState state) const;
|
||||
|
||||
constexpr ResultCode CheckMemoryState(const MemoryInfo& info, MemoryState state_mask,
|
||||
MemoryState state, MemoryPermission perm_mask,
|
||||
MemoryPermission perm, MemoryAttribute attr_mask,
|
||||
MemoryAttribute attr) const;
|
||||
ResultCode CheckMemoryState(MemoryState* out_state, MemoryPermission* out_perm,
|
||||
MemoryAttribute* out_attr, VAddr addr, std::size_t size,
|
||||
MemoryState state_mask, MemoryState state,
|
||||
MemoryPermission perm_mask, MemoryPermission perm,
|
||||
MemoryAttribute attr_mask, MemoryAttribute attr,
|
||||
MemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr);
|
||||
ResultCode CheckMemoryState(VAddr addr, std::size_t size, MemoryState state_mask,
|
||||
MemoryState state, MemoryPermission perm_mask,
|
||||
MemoryPermission perm, MemoryAttribute attr_mask,
|
||||
MemoryAttribute attr,
|
||||
MemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) {
|
||||
constexpr ResultCode CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask,
|
||||
KMemoryState state, KMemoryPermission perm_mask,
|
||||
KMemoryPermission perm, KMemoryAttribute attr_mask,
|
||||
KMemoryAttribute attr) const;
|
||||
ResultCode CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
|
||||
KMemoryAttribute* out_attr, VAddr addr, std::size_t size,
|
||||
KMemoryState state_mask, KMemoryState state,
|
||||
KMemoryPermission perm_mask, KMemoryPermission perm,
|
||||
KMemoryAttribute attr_mask, KMemoryAttribute attr,
|
||||
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr);
|
||||
ResultCode CheckMemoryState(VAddr addr, std::size_t size, KMemoryState state_mask,
|
||||
KMemoryState state, KMemoryPermission perm_mask,
|
||||
KMemoryPermission perm, KMemoryAttribute attr_mask,
|
||||
KMemoryAttribute attr,
|
||||
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) {
|
||||
return CheckMemoryState(nullptr, nullptr, nullptr, addr, size, state_mask, state, perm_mask,
|
||||
perm, attr_mask, attr, ignore_attr);
|
||||
}
|
||||
|
||||
std::recursive_mutex page_table_lock;
|
||||
std::unique_ptr<MemoryBlockManager> block_manager;
|
||||
std::unique_ptr<KMemoryBlockManager> block_manager;
|
||||
|
||||
public:
|
||||
constexpr VAddr GetAddressSpaceStart() const {
|
||||
@@ -212,7 +214,7 @@ public:
|
||||
return !IsOutsideASLRRegion(address, size);
|
||||
}
|
||||
constexpr PAddr GetPhysicalAddr(VAddr addr) {
|
||||
return page_table_impl.backing_addr[addr >> Memory::PageBits] + addr;
|
||||
return page_table_impl.backing_addr[addr >> PageBits] + addr;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -267,11 +269,11 @@ private:
|
||||
bool is_kernel{};
|
||||
bool is_aslr_enabled{};
|
||||
|
||||
MemoryManager::Pool memory_pool{MemoryManager::Pool::Application};
|
||||
KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application};
|
||||
|
||||
Common::PageTable page_table_impl;
|
||||
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
} // namespace Kernel::Memory
|
||||
} // namespace Kernel
|
||||
@@ -62,7 +62,7 @@ void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedul
|
||||
}
|
||||
|
||||
u64 KScheduler::UpdateHighestPriorityThread(KThread* highest_thread) {
|
||||
std::scoped_lock lock{guard};
|
||||
KScopedSpinLock lk{guard};
|
||||
if (KThread* prev_highest_thread = state.highest_priority_thread;
|
||||
prev_highest_thread != highest_thread) {
|
||||
if (prev_highest_thread != nullptr) {
|
||||
@@ -637,11 +637,11 @@ void KScheduler::RescheduleCurrentCore() {
|
||||
if (phys_core.IsInterrupted()) {
|
||||
phys_core.ClearInterrupt();
|
||||
}
|
||||
guard.lock();
|
||||
guard.Lock();
|
||||
if (state.needs_scheduling.load()) {
|
||||
Schedule();
|
||||
} else {
|
||||
guard.unlock();
|
||||
guard.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -669,7 +669,7 @@ void KScheduler::Unload(KThread* thread) {
|
||||
} else {
|
||||
prev_thread = nullptr;
|
||||
}
|
||||
thread->context_guard.unlock();
|
||||
thread->context_guard.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -713,7 +713,7 @@ void KScheduler::ScheduleImpl() {
|
||||
|
||||
// If we're not actually switching thread, there's nothing to do.
|
||||
if (next_thread == current_thread.load()) {
|
||||
guard.unlock();
|
||||
guard.Unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -732,9 +732,9 @@ void KScheduler::ScheduleImpl() {
|
||||
} else {
|
||||
old_context = &idle_thread->GetHostContext();
|
||||
}
|
||||
guard.unlock();
|
||||
guard.Unlock();
|
||||
|
||||
Common::Fiber::YieldTo(*old_context, switch_fiber);
|
||||
Common::Fiber::YieldTo(*old_context, *switch_fiber);
|
||||
/// When a thread wakes up, the scheduler may have changed to other in another core.
|
||||
auto& next_scheduler = *system.Kernel().CurrentScheduler();
|
||||
next_scheduler.SwitchContextStep2();
|
||||
@@ -748,34 +748,29 @@ void KScheduler::OnSwitch(void* this_scheduler) {
|
||||
void KScheduler::SwitchToCurrent() {
|
||||
while (true) {
|
||||
{
|
||||
std::scoped_lock lock{guard};
|
||||
KScopedSpinLock lk{guard};
|
||||
current_thread.store(state.highest_priority_thread);
|
||||
state.needs_scheduling.store(false);
|
||||
}
|
||||
const auto is_switch_pending = [this] {
|
||||
std::scoped_lock lock{guard};
|
||||
KScopedSpinLock lk{guard};
|
||||
return state.needs_scheduling.load();
|
||||
};
|
||||
do {
|
||||
auto next_thread = current_thread.load();
|
||||
if (next_thread != nullptr) {
|
||||
next_thread->context_guard.lock();
|
||||
next_thread->context_guard.Lock();
|
||||
if (next_thread->GetRawState() != ThreadState::Runnable) {
|
||||
next_thread->context_guard.unlock();
|
||||
next_thread->context_guard.Unlock();
|
||||
break;
|
||||
}
|
||||
if (next_thread->GetActiveCore() != core_id) {
|
||||
next_thread->context_guard.unlock();
|
||||
next_thread->context_guard.Unlock();
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::shared_ptr<Common::Fiber>* next_context;
|
||||
if (next_thread != nullptr) {
|
||||
next_context = &next_thread->GetHostContext();
|
||||
} else {
|
||||
next_context = &idle_thread->GetHostContext();
|
||||
}
|
||||
Common::Fiber::YieldTo(switch_fiber, *next_context);
|
||||
auto thread = next_thread ? next_thread : idle_thread;
|
||||
Common::Fiber::YieldTo(switch_fiber, *thread->GetHostContext());
|
||||
} while (!is_switch_pending());
|
||||
}
|
||||
}
|
||||
@@ -800,9 +795,9 @@ void KScheduler::Initialize() {
|
||||
std::string name = "Idle Thread Id:" + std::to_string(core_id);
|
||||
std::function<void(void*)> init_func = Core::CpuManager::GetIdleThreadStartFunc();
|
||||
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
|
||||
auto thread_res = KThread::Create(system, ThreadType::Main, name, 0,
|
||||
KThread::IdleThreadPriority, 0, static_cast<u32>(core_id), 0,
|
||||
nullptr, std::move(init_func), init_func_parameter);
|
||||
auto thread_res = KThread::CreateThread(
|
||||
system, ThreadType::Main, name, 0, KThread::IdleThreadPriority, 0,
|
||||
static_cast<u32>(core_id), 0, nullptr, std::move(init_func), init_func_parameter);
|
||||
idle_thread = thread_res.Unwrap().get();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,19 +2,16 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
// This file references various implementation details from Atmosphere, an open-source firmware for
|
||||
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/spin_lock.h"
|
||||
#include "core/hle/kernel/global_scheduler_context.h"
|
||||
#include "core/hle/kernel/k_priority_queue.h"
|
||||
#include "core/hle/kernel/k_scheduler_lock.h"
|
||||
#include "core/hle/kernel/k_scoped_lock.h"
|
||||
#include "core/hle/kernel/k_spin_lock.h"
|
||||
|
||||
namespace Common {
|
||||
class Fiber;
|
||||
@@ -195,12 +192,12 @@ private:
|
||||
u64 last_context_switch_time{};
|
||||
const s32 core_id;
|
||||
|
||||
Common::SpinLock guard{};
|
||||
KSpinLock guard{};
|
||||
};
|
||||
|
||||
class KScopedSchedulerLock : KScopedLock<GlobalSchedulerContext::LockType> {
|
||||
class [[nodiscard]] KScopedSchedulerLock : KScopedLock<GlobalSchedulerContext::LockType> {
|
||||
public:
|
||||
explicit KScopedSchedulerLock(KernelCore& kernel);
|
||||
explicit KScopedSchedulerLock(KernelCore & kernel);
|
||||
~KScopedSchedulerLock();
|
||||
};
|
||||
|
||||
|
||||
@@ -2,14 +2,11 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
// This file references various implementation details from Atmosphere, an open-source firmware for
|
||||
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/spin_lock.h"
|
||||
#include "core/hardware_properties.h"
|
||||
#include "core/hle/kernel/k_spin_lock.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
|
||||
@@ -34,7 +31,7 @@ public:
|
||||
} else {
|
||||
// Otherwise, we want to disable scheduling and acquire the spinlock.
|
||||
SchedulerType::DisableScheduling(kernel);
|
||||
spin_lock.lock();
|
||||
spin_lock.Lock();
|
||||
|
||||
// For debug, ensure that our state is valid.
|
||||
ASSERT(lock_count == 0);
|
||||
@@ -58,7 +55,7 @@ public:
|
||||
|
||||
// Note that we no longer hold the lock, and unlock the spinlock.
|
||||
owner_thread = nullptr;
|
||||
spin_lock.unlock();
|
||||
spin_lock.Unlock();
|
||||
|
||||
// Enable scheduling, and perform a rescheduling operation.
|
||||
SchedulerType::EnableScheduling(kernel, cores_needing_scheduling);
|
||||
@@ -67,7 +64,7 @@ public:
|
||||
|
||||
private:
|
||||
KernelCore& kernel;
|
||||
Common::SpinLock spin_lock{};
|
||||
KAlignedSpinLock spin_lock{};
|
||||
s32 lock_count{};
|
||||
KThread* owner_thread{};
|
||||
};
|
||||
|
||||
@@ -20,19 +20,22 @@ concept KLockable = !std::is_reference_v<T> && requires(T & t) {
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
requires KLockable<T> class KScopedLock {
|
||||
requires KLockable<T> class [[nodiscard]] KScopedLock {
|
||||
public:
|
||||
explicit KScopedLock(T* l) : lock_ptr(l) {
|
||||
explicit KScopedLock(T * l) : lock_ptr(l) {
|
||||
this->lock_ptr->Lock();
|
||||
}
|
||||
explicit KScopedLock(T& l) : KScopedLock(std::addressof(l)) { /* ... */
|
||||
}
|
||||
explicit KScopedLock(T & l) : KScopedLock(std::addressof(l)) {}
|
||||
|
||||
~KScopedLock() {
|
||||
this->lock_ptr->Unlock();
|
||||
}
|
||||
|
||||
KScopedLock(const KScopedLock&) = delete;
|
||||
KScopedLock(KScopedLock&&) = delete;
|
||||
KScopedLock& operator=(const KScopedLock&) = delete;
|
||||
|
||||
KScopedLock(KScopedLock &&) = delete;
|
||||
KScopedLock& operator=(KScopedLock&&) = delete;
|
||||
|
||||
private:
|
||||
T* lock_ptr;
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KScopedSchedulerLockAndSleep {
|
||||
class [[nodiscard]] KScopedSchedulerLockAndSleep {
|
||||
public:
|
||||
explicit KScopedSchedulerLockAndSleep(KernelCore& kernel, KThread* t, s64 timeout)
|
||||
explicit KScopedSchedulerLockAndSleep(KernelCore & kernel, KThread * t, s64 timeout)
|
||||
: kernel(kernel), thread(t), timeout_tick(timeout) {
|
||||
// Lock the scheduler.
|
||||
kernel.GlobalSchedulerContext().scheduler_lock.Lock();
|
||||
|
||||
@@ -4,33 +4,32 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_page_table.h"
|
||||
#include "core/hle/kernel/k_scoped_resource_reservation.h"
|
||||
#include "core/hle/kernel/k_shared_memory.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/memory/page_table.h"
|
||||
#include "core/hle/kernel/shared_memory.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
SharedMemory::SharedMemory(KernelCore& kernel, Core::DeviceMemory& device_memory)
|
||||
KSharedMemory::KSharedMemory(KernelCore& kernel, Core::DeviceMemory& device_memory)
|
||||
: Object{kernel}, device_memory{device_memory} {}
|
||||
|
||||
SharedMemory::~SharedMemory() {
|
||||
KSharedMemory::~KSharedMemory() {
|
||||
kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemory, size);
|
||||
}
|
||||
|
||||
std::shared_ptr<SharedMemory> SharedMemory::Create(
|
||||
std::shared_ptr<KSharedMemory> KSharedMemory::Create(
|
||||
KernelCore& kernel, Core::DeviceMemory& device_memory, Process* owner_process,
|
||||
Memory::PageLinkedList&& page_list, Memory::MemoryPermission owner_permission,
|
||||
Memory::MemoryPermission user_permission, PAddr physical_address, std::size_t size,
|
||||
std::string name) {
|
||||
KPageLinkedList&& page_list, KMemoryPermission owner_permission,
|
||||
KMemoryPermission user_permission, PAddr physical_address, std::size_t size, std::string name) {
|
||||
|
||||
const auto resource_limit = kernel.GetSystemResourceLimit();
|
||||
KScopedResourceReservation memory_reservation(resource_limit, LimitableResource::PhysicalMemory,
|
||||
size);
|
||||
ASSERT(memory_reservation.Succeeded());
|
||||
|
||||
std::shared_ptr<SharedMemory> shared_memory{
|
||||
std::make_shared<SharedMemory>(kernel, device_memory)};
|
||||
std::shared_ptr<KSharedMemory> shared_memory{
|
||||
std::make_shared<KSharedMemory>(kernel, device_memory)};
|
||||
|
||||
shared_memory->owner_process = owner_process;
|
||||
shared_memory->page_list = std::move(page_list);
|
||||
@@ -44,22 +43,22 @@ std::shared_ptr<SharedMemory> SharedMemory::Create(
|
||||
return shared_memory;
|
||||
}
|
||||
|
||||
ResultCode SharedMemory::Map(Process& target_process, VAddr address, std::size_t size,
|
||||
Memory::MemoryPermission permissions) {
|
||||
const u64 page_count{(size + Memory::PageSize - 1) / Memory::PageSize};
|
||||
ResultCode KSharedMemory::Map(Process& target_process, VAddr address, std::size_t size,
|
||||
KMemoryPermission permissions) {
|
||||
const u64 page_count{(size + PageSize - 1) / PageSize};
|
||||
|
||||
if (page_list.GetNumPages() != page_count) {
|
||||
UNIMPLEMENTED_MSG("Page count does not match");
|
||||
}
|
||||
|
||||
const Memory::MemoryPermission expected =
|
||||
const KMemoryPermission expected =
|
||||
&target_process == owner_process ? owner_permission : user_permission;
|
||||
|
||||
if (permissions != expected) {
|
||||
UNIMPLEMENTED_MSG("Permission does not match");
|
||||
}
|
||||
|
||||
return target_process.PageTable().MapPages(address, page_list, Memory::MemoryState::Shared,
|
||||
return target_process.PageTable().MapPages(address, page_list, KMemoryState::Shared,
|
||||
permissions);
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/device_memory.h"
|
||||
#include "core/hle/kernel/memory/memory_block.h"
|
||||
#include "core/hle/kernel/memory/page_linked_list.h"
|
||||
#include "core/hle/kernel/k_memory_block.h"
|
||||
#include "core/hle/kernel/k_page_linked_list.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/result.h"
|
||||
@@ -19,15 +19,15 @@ namespace Kernel {
|
||||
|
||||
class KernelCore;
|
||||
|
||||
class SharedMemory final : public Object {
|
||||
class KSharedMemory final : public Object {
|
||||
public:
|
||||
explicit SharedMemory(KernelCore& kernel, Core::DeviceMemory& device_memory);
|
||||
~SharedMemory() override;
|
||||
explicit KSharedMemory(KernelCore& kernel, Core::DeviceMemory& device_memory);
|
||||
~KSharedMemory() override;
|
||||
|
||||
static std::shared_ptr<SharedMemory> Create(
|
||||
static std::shared_ptr<KSharedMemory> Create(
|
||||
KernelCore& kernel, Core::DeviceMemory& device_memory, Process* owner_process,
|
||||
Memory::PageLinkedList&& page_list, Memory::MemoryPermission owner_permission,
|
||||
Memory::MemoryPermission user_permission, PAddr physical_address, std::size_t size,
|
||||
KPageLinkedList&& page_list, KMemoryPermission owner_permission,
|
||||
KMemoryPermission user_permission, PAddr physical_address, std::size_t size,
|
||||
std::string name);
|
||||
|
||||
std::string GetTypeName() const override {
|
||||
@@ -51,7 +51,7 @@ public:
|
||||
* @param permissions Memory block map permissions (specified by SVC field)
|
||||
*/
|
||||
ResultCode Map(Process& target_process, VAddr address, std::size_t size,
|
||||
Memory::MemoryPermission permissions);
|
||||
KMemoryPermission permissions);
|
||||
|
||||
/**
|
||||
* Gets a pointer to the shared memory block
|
||||
@@ -76,9 +76,9 @@ public:
|
||||
private:
|
||||
Core::DeviceMemory& device_memory;
|
||||
Process* owner_process{};
|
||||
Memory::PageLinkedList page_list;
|
||||
Memory::MemoryPermission owner_permission{};
|
||||
Memory::MemoryPermission user_permission{};
|
||||
KPageLinkedList page_list;
|
||||
KMemoryPermission owner_permission{};
|
||||
KMemoryPermission user_permission{};
|
||||
PAddr physical_address{};
|
||||
std::size_t size{};
|
||||
std::string name;
|
||||
@@ -2,9 +2,6 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
// This file references various implementation details from Atmosphere, an open-source firmware for
|
||||
// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
@@ -12,17 +9,17 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Kernel::Memory {
|
||||
namespace Kernel {
|
||||
|
||||
namespace impl {
|
||||
|
||||
class SlabHeapImpl final : NonCopyable {
|
||||
class KSlabHeapImpl final : NonCopyable {
|
||||
public:
|
||||
struct Node {
|
||||
Node* next{};
|
||||
};
|
||||
|
||||
constexpr SlabHeapImpl() = default;
|
||||
constexpr KSlabHeapImpl() = default;
|
||||
|
||||
void Initialize(std::size_t size) {
|
||||
ASSERT(head == nullptr);
|
||||
@@ -65,9 +62,9 @@ private:
|
||||
|
||||
} // namespace impl
|
||||
|
||||
class SlabHeapBase : NonCopyable {
|
||||
class KSlabHeapBase : NonCopyable {
|
||||
public:
|
||||
constexpr SlabHeapBase() = default;
|
||||
constexpr KSlabHeapBase() = default;
|
||||
|
||||
constexpr bool Contains(uintptr_t addr) const {
|
||||
return start <= addr && addr < end;
|
||||
@@ -126,7 +123,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
using Impl = impl::SlabHeapImpl;
|
||||
using Impl = impl::KSlabHeapImpl;
|
||||
|
||||
Impl impl;
|
||||
uintptr_t peak{};
|
||||
@@ -135,9 +132,9 @@ private:
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class SlabHeap final : public SlabHeapBase {
|
||||
class KSlabHeap final : public KSlabHeapBase {
|
||||
public:
|
||||
constexpr SlabHeap() : SlabHeapBase() {}
|
||||
constexpr KSlabHeap() : KSlabHeapBase() {}
|
||||
|
||||
void Initialize(void* memory, std::size_t memory_size) {
|
||||
InitializeImpl(sizeof(T), memory, memory_size);
|
||||
@@ -160,4 +157,4 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Kernel::Memory
|
||||
} // namespace Kernel
|
||||
54
src/core/hle/kernel/k_spin_lock.cpp
Normal file
54
src/core/hle/kernel/k_spin_lock.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/kernel/k_spin_lock.h"
|
||||
|
||||
#if _MSC_VER
|
||||
#include <intrin.h>
|
||||
#if _M_AMD64
|
||||
#define __x86_64__ 1
|
||||
#endif
|
||||
#if _M_ARM64
|
||||
#define __aarch64__ 1
|
||||
#endif
|
||||
#else
|
||||
#if __x86_64__
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
void ThreadPause() {
|
||||
#if __x86_64__
|
||||
_mm_pause();
|
||||
#elif __aarch64__ && _MSC_VER
|
||||
__yield();
|
||||
#elif __aarch64__
|
||||
asm("yield");
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
void KSpinLock::Lock() {
|
||||
while (lck.test_and_set(std::memory_order_acquire)) {
|
||||
ThreadPause();
|
||||
}
|
||||
}
|
||||
|
||||
void KSpinLock::Unlock() {
|
||||
lck.clear(std::memory_order_release);
|
||||
}
|
||||
|
||||
bool KSpinLock::TryLock() {
|
||||
if (lck.test_and_set(std::memory_order_acquire)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
39
src/core/hle/kernel/k_spin_lock.h
Normal file
39
src/core/hle/kernel/k_spin_lock.h
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "core/hle/kernel/k_scoped_lock.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KSpinLock {
|
||||
public:
|
||||
KSpinLock() = default;
|
||||
|
||||
KSpinLock(const KSpinLock&) = delete;
|
||||
KSpinLock& operator=(const KSpinLock&) = delete;
|
||||
|
||||
KSpinLock(KSpinLock&&) = delete;
|
||||
KSpinLock& operator=(KSpinLock&&) = delete;
|
||||
|
||||
void Lock();
|
||||
void Unlock();
|
||||
[[nodiscard]] bool TryLock();
|
||||
|
||||
private:
|
||||
std::atomic_flag lck = ATOMIC_FLAG_INIT;
|
||||
};
|
||||
|
||||
// TODO(bunnei): Alias for now, in case we want to implement these accurately in the future.
|
||||
using KAlignedSpinLock = KSpinLock;
|
||||
using KNotAlignedSpinLock = KSpinLock;
|
||||
|
||||
using KScopedSpinLock = KScopedLock<KSpinLock>;
|
||||
using KScopedAlignedSpinLock = KScopedLock<KAlignedSpinLock>;
|
||||
using KScopedNotAlignedSpinLock = KScopedLock<KNotAlignedSpinLock>;
|
||||
|
||||
} // namespace Kernel
|
||||
23
src/core/hle/kernel/k_system_control.h
Normal file
23
src/core/hle/kernel/k_system_control.h
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#define BOARD_NINTENDO_NX
|
||||
|
||||
#ifdef BOARD_NINTENDO_NX
|
||||
|
||||
#include "core/hle/kernel/board/nintendo/nx/k_system_control.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
using Kernel::Board::Nintendo::Nx::KSystemControl;
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
#else
|
||||
#error "Unknown board for KSystemControl"
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user