Compare commits
15 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3ae1e5f5a | ||
|
|
dab73605e3 | ||
|
|
98fc73af53 | ||
|
|
68cb3b2133 | ||
|
|
867d19f37a | ||
|
|
fef8802b05 | ||
|
|
e53891d2fa | ||
|
|
9077da0378 | ||
|
|
0a2ad100cb | ||
|
|
5563c01cbf | ||
|
|
6dd6361494 | ||
|
|
d8c18a54e8 | ||
|
|
ba53a504a2 | ||
|
|
60a475c4ee | ||
|
|
46eef11d16 |
@@ -1,16 +1,12 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
# Copy documentation
|
||||
cp license.txt "$DIR_NAME"
|
||||
cp README.md "$DIR_NAME"
|
||||
cp license.txt "$REV_NAME"
|
||||
cp README.md "$REV_NAME"
|
||||
|
||||
tar -cJvf "${REV_NAME}-source.tar.xz" src externals CMakeLists.txt README.md license.txt
|
||||
cp "${REV_NAME}-source.tar.xz" "$DIR_NAME"
|
||||
tar $COMPRESSION_FLAGS "$ARCHIVE_NAME" "$REV_NAME"
|
||||
|
||||
tar $COMPRESSION_FLAGS "$ARCHIVE_NAME" "$DIR_NAME"
|
||||
|
||||
mv "$DIR_NAME" $RELEASE_NAME
|
||||
mv "${REV_NAME}-source.tar.xz" $RELEASE_NAME
|
||||
mv "$REV_NAME" $RELEASE_NAME
|
||||
|
||||
7z a "$REV_NAME.7z" $RELEASE_NAME
|
||||
|
||||
|
||||
@@ -11,4 +11,5 @@ ninja
|
||||
|
||||
ccache -s
|
||||
|
||||
ctest -VV -C Release
|
||||
# Ignore zlib's tests, since they aren't gated behind a CMake option.
|
||||
ctest -VV -E "(example|example64)" -C Release
|
||||
|
||||
@@ -6,15 +6,9 @@ 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 "$REV_NAME"
|
||||
|
||||
mkdir "$DIR_NAME"
|
||||
|
||||
cp build/bin/yuzu-cmd "$DIR_NAME"
|
||||
cp build/bin/yuzu "$DIR_NAME"
|
||||
cp build/bin/yuzu-cmd "$REV_NAME"
|
||||
cp build/bin/yuzu "$REV_NAME"
|
||||
|
||||
. .ci/scripts/common/post-upload.sh
|
||||
|
||||
@@ -1,23 +1,12 @@
|
||||
param($BUILD_NAME)
|
||||
|
||||
$GITDATE = $(git show -s --date=short --format='%ad') -replace "-", ""
|
||||
$GITDATE = $(git show -s --date=short --format='%ad') -replace "-",""
|
||||
$GITREV = $(git show -s --format='%h')
|
||||
|
||||
if ("$BUILD_NAME" -eq "mainline") {
|
||||
$RELEASE_DIST = "yuzu-windows-msvc"
|
||||
}
|
||||
else {
|
||||
$RELEASE_DIST = "yuzu-windows-msvc-$BUILD_NAME"
|
||||
}
|
||||
$RELEASE_DIST = "yuzu-windows-msvc"
|
||||
|
||||
$MSVC_BUILD_ZIP = "yuzu-windows-msvc-$GITDATE-$GITREV.zip" -replace " ", ""
|
||||
$MSVC_BUILD_PDB = "yuzu-windows-msvc-$GITDATE-$GITREV-debugsymbols.zip" -replace " ", ""
|
||||
$MSVC_SEVENZIP = "yuzu-windows-msvc-$GITDATE-$GITREV.7z" -replace " ", ""
|
||||
$MSVC_TAR = "yuzu-windows-msvc-$GITDATE-$GITREV.tar" -replace " ", ""
|
||||
$MSVC_TARXZ = "yuzu-windows-msvc-$GITDATE-$GITREV.tar.xz" -replace " ", ""
|
||||
$MSVC_SOURCE = "yuzu-windows-msvc-source-$GITDATE-$GITREV" -replace " ", ""
|
||||
$MSVC_SOURCE_TAR = "$MSVC_SOURCE.tar"
|
||||
$MSVC_SOURCE_TARXZ = "$MSVC_SOURCE_TAR.xz"
|
||||
|
||||
$env:BUILD_ZIP = $MSVC_BUILD_ZIP
|
||||
$env:BUILD_SYMBOLS = $MSVC_BUILD_PDB
|
||||
@@ -25,34 +14,19 @@ $env:BUILD_UPDATE = $MSVC_SEVENZIP
|
||||
|
||||
$BUILD_DIR = ".\build\bin\Release"
|
||||
|
||||
# Upload debugging symbols
|
||||
mkdir pdb
|
||||
Get-ChildItem "$BUILD_DIR\" -Recurse -Filter "*.pdb" | Copy-Item -destination .\pdb
|
||||
7z a -tzip $MSVC_BUILD_PDB .\pdb\*.pdb
|
||||
rm "$BUILD_DIR\*.pdb"
|
||||
|
||||
# Create artifact directories
|
||||
mkdir $RELEASE_DIST
|
||||
mkdir $MSVC_SOURCE
|
||||
mkdir "artifacts"
|
||||
|
||||
# Build a tar.xz for the source of the release
|
||||
Copy-Item .\license.txt -Destination $MSVC_SOURCE
|
||||
Copy-Item .\README.md -Destination $MSVC_SOURCE
|
||||
Copy-Item .\CMakeLists.txt -Destination $MSVC_SOURCE
|
||||
Copy-Item .\src -Recurse -Destination $MSVC_SOURCE
|
||||
Copy-Item .\externals -Recurse -Destination $MSVC_SOURCE
|
||||
Copy-Item .\dist -Recurse -Destination $MSVC_SOURCE
|
||||
Copy-Item .\CMakeModules -Recurse -Destination $MSVC_SOURCE
|
||||
7z a -r -ttar $MSVC_SOURCE_TAR $MSVC_SOURCE
|
||||
7z a -r -txz $MSVC_SOURCE_TARXZ $MSVC_SOURCE_TAR
|
||||
|
||||
# Build the final release artifacts
|
||||
Copy-Item $MSVC_SOURCE_TARXZ -Destination $RELEASE_DIST
|
||||
Copy-Item "$BUILD_DIR\*" -Destination $RELEASE_DIST -Recurse
|
||||
rm "$RELEASE_DIST\*.exe"
|
||||
Get-ChildItem "$BUILD_DIR" -Recurse -Filter "yuzu*.exe" | Copy-Item -destination $RELEASE_DIST
|
||||
Get-ChildItem "$BUILD_DIR" -Recurse -Filter "QtWebEngineProcess*.exe" | Copy-Item -destination $RELEASE_DIST
|
||||
Copy-Item .\license.txt -Destination $RELEASE_DIST
|
||||
Copy-Item .\README.md -Destination $RELEASE_DIST
|
||||
7z a -tzip $MSVC_BUILD_ZIP $RELEASE_DIST\*
|
||||
7z a $MSVC_SEVENZIP $RELEASE_DIST
|
||||
|
||||
@@ -61,4 +35,4 @@ Get-ChildItem "$BUILD_DIR" -Recurse -Filter "QtWebEngineProcess*.exe" | Copy-Ite
|
||||
|
||||
Get-ChildItem . -Filter "*.zip" | Copy-Item -destination "artifacts"
|
||||
Get-ChildItem . -Filter "*.7z" | Copy-Item -destination "artifacts"
|
||||
Get-ChildItem . -Filter "*.tar.xz" | Copy-Item -destination "artifacts"
|
||||
Get-ChildItem . -Filter "*.tar.xz" | Copy-Item -destination "artifacts"
|
||||
@@ -6,14 +6,8 @@ REV_NAME="yuzu-windows-mingw-${GITDATE}-${GITREV}"
|
||||
ARCHIVE_NAME="${REV_NAME}.tar.gz"
|
||||
COMPRESSION_FLAGS="-czvf"
|
||||
|
||||
if [ "${RELEASE_NAME}" = "mainline" ]; then
|
||||
DIR_NAME="${REV_NAME}"
|
||||
else
|
||||
DIR_NAME="${REV_NAME}_${RELEASE_NAME}"
|
||||
fi
|
||||
|
||||
mkdir "$DIR_NAME"
|
||||
mkdir "$REV_NAME"
|
||||
# get around the permission issues
|
||||
cp -r package/* "$DIR_NAME"
|
||||
cp -r package/* "$REV_NAME"
|
||||
|
||||
. .ci/scripts/common/post-upload.sh
|
||||
|
||||
@@ -17,7 +17,6 @@ steps:
|
||||
inputs:
|
||||
targetType: 'filePath'
|
||||
filePath: './.ci/scripts/windows/upload.ps1'
|
||||
arguments: '$(BuildName)'
|
||||
- publish: artifacts
|
||||
artifact: 'yuzu-$(BuildName)-windows-msvc'
|
||||
displayName: 'Upload Artifacts'
|
||||
|
||||
@@ -10,7 +10,6 @@ stages:
|
||||
jobs:
|
||||
- job: format
|
||||
displayName: 'clang'
|
||||
continueOnError: true
|
||||
pool:
|
||||
vmImage: ubuntu-latest
|
||||
steps:
|
||||
|
||||
8
.gitmodules
vendored
8
.gitmodules
vendored
@@ -47,8 +47,8 @@
|
||||
path = externals/sirit
|
||||
url = https://github.com/ReinUsesLisp/sirit
|
||||
[submodule "libzip"]
|
||||
path = externals/libzip/libzip
|
||||
url = https://github.com/nih-at/libzip.git
|
||||
path = externals/libzip
|
||||
url = https://github.com/DarkLordZach/libzip
|
||||
[submodule "zlib"]
|
||||
path = externals/zlib/zlib
|
||||
url = https://github.com/madler/zlib.git
|
||||
path = externals/zlib
|
||||
url = https://github.com/madler/zlib
|
||||
|
||||
@@ -29,7 +29,7 @@ option(ENABLE_VULKAN "Enables Vulkan backend" ON)
|
||||
|
||||
option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
|
||||
|
||||
if(EXISTS ${PROJECT_SOURCE_DIR}/hooks/pre-commit AND NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit)
|
||||
if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit)
|
||||
message(STATUS "Copying pre-commit hook")
|
||||
file(COPY hooks/pre-commit
|
||||
DESTINATION ${PROJECT_SOURCE_DIR}/.git/hooks)
|
||||
@@ -49,10 +49,7 @@ function(check_submodules_present)
|
||||
endif()
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
if(EXISTS ${PROJECT_SOURCE_DIR}/.gitmodules)
|
||||
check_submodules_present()
|
||||
endif()
|
||||
check_submodules_present()
|
||||
|
||||
configure_file(${PROJECT_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||
|
||||
@@ -1 +1 @@
|
||||
**The Contributor's Guide has moved to [the yuzu wiki](https://github.com/yuzu-emu/yuzu/wiki/Contributing).**
|
||||
**The Contributor's Guide has moved to [the Yuzu wiki](https://github.com/yuzu-emu/yuzu/wiki/Contributing).**
|
||||
|
||||
1
externals/CMakeLists.txt
vendored
1
externals/CMakeLists.txt
vendored
@@ -76,7 +76,6 @@ endif()
|
||||
|
||||
# zlib
|
||||
add_subdirectory(zlib EXCLUDE_FROM_ALL)
|
||||
set(ZLIB_LIBRARIES z)
|
||||
|
||||
# libzip
|
||||
add_subdirectory(libzip EXCLUDE_FROM_ALL)
|
||||
|
||||
2
externals/Vulkan-Headers
vendored
2
externals/Vulkan-Headers
vendored
Submodule externals/Vulkan-Headers updated: d42d0747ee...fd568d51ed
2
externals/boost
vendored
2
externals/boost
vendored
Submodule externals/boost updated: 5e8300b76a...0b920df1c9
5105
externals/httplib/httplib.h
vendored
5105
externals/httplib/httplib.h
vendored
File diff suppressed because it is too large
Load Diff
1
externals/libzip
vendored
Submodule
1
externals/libzip
vendored
Submodule
Submodule externals/libzip added at bd7a8103e9
564
externals/libzip/CMakeLists.txt
vendored
564
externals/libzip/CMakeLists.txt
vendored
@@ -1,564 +0,0 @@
|
||||
# TODO:
|
||||
# create usable libtool .la file
|
||||
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 3.0.2)
|
||||
|
||||
LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/libzip")
|
||||
|
||||
PROJECT(libzip C)
|
||||
|
||||
OPTION(ENABLE_COMMONCRYPTO "Enable use of CommonCrypto" ON)
|
||||
OPTION(ENABLE_GNUTLS "Enable use of GnuTLS" ON)
|
||||
OPTION(ENABLE_MBEDTLS "Enable use of mbed TLS" ON)
|
||||
OPTION(ENABLE_OPENSSL "Enable use of OpenSSL" ON)
|
||||
OPTION(ENABLE_WINDOWS_CRYPTO "Enable use of Windows cryptography libraries" ON)
|
||||
|
||||
OPTION(ENABLE_BZIP2 "Enable use of BZip2" OFF)
|
||||
OPTION(ENABLE_LZMA "Enable use of LZMA" OFF)
|
||||
|
||||
INCLUDE(CheckFunctionExists)
|
||||
INCLUDE(CheckIncludeFiles)
|
||||
INCLUDE(CheckSymbolExists)
|
||||
INCLUDE(CheckTypeSize)
|
||||
INCLUDE(CheckCSourceRuns)
|
||||
INCLUDE(CheckCSourceCompiles)
|
||||
INCLUDE(CheckStructHasMember)
|
||||
INCLUDE(TestBigEndian)
|
||||
INCLUDE(GNUInstallDirs)
|
||||
IF(ENABLE_COMMONCRYPTO)
|
||||
CHECK_INCLUDE_FILES(CommonCrypto/CommonCrypto.h COMMONCRYPTO_FOUND)
|
||||
ELSE()
|
||||
SET(COMMONCRYPTO_FOUND FALSE)
|
||||
ENDIF()
|
||||
IF(ENABLE_GNUTLS)
|
||||
INCLUDE(FindNettle)
|
||||
INCLUDE(FindGnuTLS)
|
||||
ELSE()
|
||||
SET(GNUTLS_FOUND FALSE)
|
||||
ENDIF()
|
||||
IF(ENABLE_MBEDTLS)
|
||||
FIND_PATH(MBEDTLS_INCLUDE_DIR mbedtls/aes.h)
|
||||
FIND_LIBRARY(MBEDTLS_LIBRARIES NAMES mbedcrypto)
|
||||
ELSE()
|
||||
SET(MBEDTLS_LIBRARIES FALSE)
|
||||
ENDIF()
|
||||
IF(ENABLE_OPENSSL)
|
||||
INCLUDE(FindOpenSSL)
|
||||
ELSE()
|
||||
SET(OPENSSL_FOUND FALSE)
|
||||
ENDIF()
|
||||
IF(WIN32)
|
||||
IF(ENABLE_WINDOWS_CRYPTO)
|
||||
SET(WINDOWS_CRYPTO_FOUND TRUE)
|
||||
ENDIF()
|
||||
ELSE()
|
||||
SET(WINDOWS_CRYPTO_FOUND FALSE)
|
||||
ENDIF()
|
||||
|
||||
OPTION(BUILD_SHARED_LIBS "Build shared libraries" ON)
|
||||
OPTION(SHARED_LIB_VERSIONNING "Add SO version in .so build" ON)
|
||||
|
||||
SET(PACKAGE "libzip")
|
||||
SET(PACKAGE_NAME ${PACKAGE})
|
||||
SET(PACKAGE_VERSION_MAJOR "1")
|
||||
SET(PACKAGE_VERSION_MINOR "5")
|
||||
SET(PACKAGE_VERSION_MICRO "2a")
|
||||
#SET(VERSION "${PACKAGE_VERSION_MAJOR}.${PACKAGE_VERSION_MINOR}")
|
||||
SET(VERSION "${PACKAGE_VERSION_MAJOR}.${PACKAGE_VERSION_MINOR}.${PACKAGE_VERSION_MICRO}")
|
||||
SET(PACKAGE_VERSION ${VERSION})
|
||||
SET(LIBZIP_VERSION ${PACKAGE_VERSION})
|
||||
SET(LIBZIP_VERSION_MAJOR ${PACKAGE_VERSION_MAJOR})
|
||||
SET(LIBZIP_VERSION_MINOR ${PACKAGE_VERSION_MINOR})
|
||||
SET(LIBZIP_VERSION_MICRO ${PACKAGE_VERSION_MICRO})
|
||||
SET(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
|
||||
|
||||
SET(ARCHIVE_NAME ${PACKAGE_NAME}-${PACKAGE_VERSION})
|
||||
IF(NOT TARGET dist)
|
||||
ADD_CUSTOM_TARGET(dist
|
||||
COMMAND git config tar.tar.xz.command "xz -c"
|
||||
COMMAND git archive --prefix=${ARCHIVE_NAME}/ -o ${ARCHIVE_NAME}.tar.gz HEAD
|
||||
COMMAND git archive --prefix=${ARCHIVE_NAME}/ -o ${ARCHIVE_NAME}.tar.xz HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
)
|
||||
ADD_CUSTOM_TARGET(distcheck
|
||||
COMMAND chmod -R u+w ${ARCHIVE_NAME} ${ARCHIVE_NAME}-build ${ARCHIVE_NAME}-dest 2>/dev/null || true
|
||||
COMMAND rm -rf ${ARCHIVE_NAME} ${ARCHIVE_NAME}-build ${ARCHIVE_NAME}-dest
|
||||
COMMAND cmake -E tar xf ${ARCHIVE_NAME}.tar.gz
|
||||
COMMAND chmod -R u-w ${ARCHIVE_NAME}
|
||||
COMMAND mkdir ${ARCHIVE_NAME}-build
|
||||
COMMAND mkdir ${ARCHIVE_NAME}-dest
|
||||
COMMAND cd ${ARCHIVE_NAME}-build && cmake -DCMAKE_INSTALL_PREFIX=../${ARCHIVE_NAME}-dest ../${ARCHIVE_NAME}
|
||||
COMMAND cd ${ARCHIVE_NAME}-build && make -j4
|
||||
COMMAND cd ${ARCHIVE_NAME}-build && make test
|
||||
COMMAND cd ${ARCHIVE_NAME}-build && make install
|
||||
# COMMAND cd ${ARCHIVE_NAME}-build && make uninstall
|
||||
# COMMAND if [ `find ${ARCHIVE_NAME}-dest ! -type d | wc -l` -ne 0 ]; then echo leftover files in ${ARCHIVE_NAME}-dest; false; fi
|
||||
COMMAND cd ${ARCHIVE_NAME}-build && make clean
|
||||
COMMAND chmod -R u+w ${ARCHIVE_NAME} ${ARCHIVE_NAME}-build ${ARCHIVE_NAME}-dest
|
||||
COMMAND rm -rf ${ARCHIVE_NAME} ${ARCHIVE_NAME}-build ${ARCHIVE_NAME}-dest
|
||||
COMMAND echo "${ARCHIVE_NAME}.tar.gz is ready for distribution."
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
)
|
||||
ADD_DEPENDENCIES(distcheck dist)
|
||||
ENDIF(NOT TARGET dist)
|
||||
|
||||
IF(BUILD_SHARED_LIBS)
|
||||
SET(HAVE_SHARED TRUE)
|
||||
ELSE()
|
||||
SET(ZIP_STATIC TRUE)
|
||||
ENDIF()
|
||||
|
||||
# Checks
|
||||
|
||||
CHECK_FUNCTION_EXISTS(_chmod HAVE__CHMOD)
|
||||
CHECK_FUNCTION_EXISTS(_close HAVE__CLOSE)
|
||||
CHECK_FUNCTION_EXISTS(_dup HAVE__DUP)
|
||||
CHECK_FUNCTION_EXISTS(_fdopen HAVE__FDOPEN)
|
||||
CHECK_FUNCTION_EXISTS(_fileno HAVE__FILENO)
|
||||
CHECK_FUNCTION_EXISTS(_open HAVE__OPEN)
|
||||
CHECK_FUNCTION_EXISTS(_setmode HAVE__SETMODE)
|
||||
CHECK_FUNCTION_EXISTS(_snprintf HAVE__SNPRINTF)
|
||||
CHECK_FUNCTION_EXISTS(_strdup HAVE__STRDUP)
|
||||
CHECK_FUNCTION_EXISTS(_stricmp HAVE__STRICMP)
|
||||
CHECK_FUNCTION_EXISTS(_strtoi64 HAVE__STRTOI64)
|
||||
CHECK_FUNCTION_EXISTS(_strtoui64 HAVE__STRTOUI64)
|
||||
CHECK_FUNCTION_EXISTS(_unlink HAVE__UNLINK)
|
||||
CHECK_FUNCTION_EXISTS(arc4random HAVE_ARC4RANDOM)
|
||||
CHECK_FUNCTION_EXISTS(clonefile HAVE_CLONEFILE)
|
||||
CHECK_FUNCTION_EXISTS(explicit_bzero HAVE_EXPLICIT_BZERO)
|
||||
CHECK_FUNCTION_EXISTS(explicit_memset HAVE_EXPLICIT_MEMSET)
|
||||
CHECK_FUNCTION_EXISTS(fileno HAVE_FILENO)
|
||||
CHECK_FUNCTION_EXISTS(fseeko HAVE_FSEEKO)
|
||||
CHECK_FUNCTION_EXISTS(ftello HAVE_FTELLO)
|
||||
CHECK_FUNCTION_EXISTS(getprogname HAVE_GETPROGNAME)
|
||||
CHECK_FUNCTION_EXISTS(localtime_r HAVE_LOCALTIME_R)
|
||||
CHECK_FUNCTION_EXISTS(open HAVE_OPEN)
|
||||
CHECK_FUNCTION_EXISTS(setmode HAVE_SETMODE)
|
||||
CHECK_FUNCTION_EXISTS(snprintf HAVE_SNPRINTF)
|
||||
CHECK_FUNCTION_EXISTS(strcasecmp HAVE_STRCASECMP)
|
||||
CHECK_FUNCTION_EXISTS(strdup HAVE_STRDUP)
|
||||
CHECK_FUNCTION_EXISTS(stricmp HAVE_STRICMP)
|
||||
CHECK_FUNCTION_EXISTS(strtoll HAVE_STRTOLL)
|
||||
CHECK_FUNCTION_EXISTS(strtoull HAVE_STRTOULL)
|
||||
|
||||
CHECK_INCLUDE_FILES("sys/types.h;sys/stat.h;fts.h" HAVE_FTS_H)
|
||||
CHECK_INCLUDE_FILES(stdbool.h HAVE_STDBOOL_H)
|
||||
CHECK_INCLUDE_FILES(strings.h HAVE_STRINGS_H)
|
||||
CHECK_INCLUDE_FILES(unistd.h HAVE_UNISTD_H)
|
||||
|
||||
CHECK_INCLUDE_FILES(inttypes.h HAVE_INTTYPES_H_LIBZIP)
|
||||
CHECK_INCLUDE_FILES(stdint.h HAVE_STDINT_H_LIBZIP)
|
||||
CHECK_INCLUDE_FILES(sys/types.h HAVE_SYS_TYPES_H_LIBZIP)
|
||||
|
||||
# TODO: fix test
|
||||
# this test does not find __progname even when it exists
|
||||
#CHECK_SYMBOL_EXISTS(__progname stdlib.h HAVE___PROGNAME)
|
||||
|
||||
CHECK_TYPE_SIZE(__int8 __INT8_LIBZIP)
|
||||
CHECK_TYPE_SIZE(int8_t INT8_T_LIBZIP)
|
||||
CHECK_TYPE_SIZE(uint8_t UINT8_T_LIBZIP)
|
||||
CHECK_TYPE_SIZE(__int16 __INT16_LIBZIP)
|
||||
CHECK_TYPE_SIZE(int16_t INT16_T_LIBZIP)
|
||||
CHECK_TYPE_SIZE(uint16_t UINT16_T_LIBZIP)
|
||||
CHECK_TYPE_SIZE(__int32 __INT32_LIBZIP)
|
||||
CHECK_TYPE_SIZE(int32_t INT32_T_LIBZIP)
|
||||
CHECK_TYPE_SIZE(uint32_t UINT32_T_LIBZIP)
|
||||
CHECK_TYPE_SIZE(__int64 __INT64_LIBZIP)
|
||||
CHECK_TYPE_SIZE(int64_t INT64_T_LIBZIP)
|
||||
CHECK_TYPE_SIZE(uint64_t UINT64_T_LIBZIP)
|
||||
CHECK_TYPE_SIZE("short" SHORT_LIBZIP)
|
||||
CHECK_TYPE_SIZE("int" INT_LIBZIP)
|
||||
CHECK_TYPE_SIZE("long" LONG_LIBZIP)
|
||||
CHECK_TYPE_SIZE("long long" LONG_LONG_LIBZIP)
|
||||
CHECK_TYPE_SIZE("off_t" SIZEOF_OFF_T)
|
||||
CHECK_TYPE_SIZE("size_t" SIZE_T_LIBZIP)
|
||||
CHECK_TYPE_SIZE("ssize_t" SSIZE_T_LIBZIP)
|
||||
|
||||
CHECK_C_SOURCE_COMPILES("#include <sys/ioctl.h>
|
||||
#include <linux/fs.h>
|
||||
int main(int argc, char *argv[]) { unsigned long x = FICLONERANGE; }" HAVE_FICLONERANGE)
|
||||
|
||||
CHECK_C_SOURCE_COMPILES("
|
||||
int foo(char * _Nullable bar);
|
||||
int main(int argc, char *argv[]) { }" HAVE_NULLABLE)
|
||||
|
||||
TEST_BIG_ENDIAN(WORDS_BIGENDIAN)
|
||||
|
||||
#FIND_PACKAGE(ZLIB 1.1.2 REQUIRED)
|
||||
INCLUDE_DIRECTORIES(../zlib/zlib)
|
||||
SET(CMAKE_REQUIRED_INCLUDES ../zlib/zlib)
|
||||
|
||||
IF(ENABLE_BZIP2)
|
||||
FIND_PACKAGE(BZip2)
|
||||
IF(BZIP2_FOUND)
|
||||
SET (HAVE_LIBBZ2 1)
|
||||
|
||||
INCLUDE_DIRECTORIES(${BZIP2_INCLUDE_DIR})
|
||||
SET (OPTIONAL_LIBRARY ${OPTIONAL_LIBRARY} ${BZIP2_LIBRARIES})
|
||||
ELSE()
|
||||
MESSAGE(WARNING "-- bzip2 library not found; bzip2 support disabled")
|
||||
ENDIF(BZIP2_FOUND)
|
||||
ENDIF(ENABLE_BZIP2)
|
||||
|
||||
IF(ENABLE_LZMA)
|
||||
FIND_PACKAGE(LibLZMA)
|
||||
IF(LIBLZMA_FOUND)
|
||||
SET (HAVE_LIBLZMA 1)
|
||||
|
||||
INCLUDE_DIRECTORIES(${LIBLZMA_INCLUDE_DIR})
|
||||
SET (OPTIONAL_LIBRARY ${OPTIONAL_LIBRARY} ${LIBLZMA_LIBRARY})
|
||||
ELSE()
|
||||
MESSAGE(WARNING "-- lzma library not found; lzma support disabled")
|
||||
ENDIF(LIBLZMA_FOUND)
|
||||
ENDIF(ENABLE_LZMA)
|
||||
|
||||
|
||||
IF (COMMONCRYPTO_FOUND)
|
||||
SET (HAVE_CRYPTO 1)
|
||||
SET (HAVE_COMMONCRYPTO 1)
|
||||
ELSEIF (WINDOWS_CRYPTO_FOUND)
|
||||
SET (HAVE_CRYPTO 1)
|
||||
SET (HAVE_WINDOWS_CRYPTO 1)
|
||||
ELSEIF (GNUTLS_FOUND AND NETTLE_FOUND)
|
||||
SET (HAVE_CRYPTO 1)
|
||||
SET (HAVE_GNUTLS 1)
|
||||
INCLUDE_DIRECTORIES(${GNUTLS_INCLUDE_DIR} ${NETTLE_INCLUDE_DIR})
|
||||
SET (OPTIONAL_LIBRARY ${OPTIONAL_LIBRARY} ${GNUTLS_LIBRARY} ${NETTLE_LIBRARY})
|
||||
ELSEIF (OPENSSL_FOUND)
|
||||
SET (HAVE_CRYPTO 1)
|
||||
SET (HAVE_OPENSSL 1)
|
||||
INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
|
||||
SET (OPTIONAL_LIBRARY ${OPTIONAL_LIBRARY} ${OPENSSL_LIBRARIES})
|
||||
ELSEIF (MBEDTLS_LIBRARIES)
|
||||
SET (HAVE_CRYPTO 1)
|
||||
SET (HAVE_MBEDTLS 1)
|
||||
INCLUDE_DIRECTORIES(${MBEDTLS_INCLUDE_DIR})
|
||||
SET (OPTIONAL_LIBRARY ${OPTIONAL_LIBRARY} ${MBEDTLS_LIBRARIES})
|
||||
ENDIF()
|
||||
|
||||
IF (NOT HAVE_CRYPTO)
|
||||
MESSAGE(WARNING "-- neither Common Crypto, GnuTLS, mbed TLS, OpenSSL, nor Windows Cryptography found; AES support disabled")
|
||||
ENDIF()
|
||||
|
||||
IF(MSVC)
|
||||
ADD_DEFINITIONS("-D_CRT_SECURE_NO_WARNINGS")
|
||||
ADD_DEFINITIONS("-D_CRT_NONSTDC_NO_DEPRECATE")
|
||||
ENDIF(MSVC)
|
||||
|
||||
if(WIN32)
|
||||
if(HAVE_WINDOWS_CRYPTO)
|
||||
SET (OPTIONAL_LIBRARY ${OPTIONAL_LIBRARY} bcrypt)
|
||||
endif()
|
||||
if(CMAKE_SYSTEM_NAME MATCHES WindowsPhone OR CMAKE_SYSTEM_NAME MATCHES WindowsStore)
|
||||
ADD_DEFINITIONS(-DMS_UWP)
|
||||
else(CMAKE_SYSTEM_NAME MATCHES WindowsPhone OR CMAKE_SYSTEM_NAME MATCHES WindowsStore)
|
||||
SET (OPTIONAL_LIBRARY ${OPTIONAL_LIBRARY} advapi32)
|
||||
endif(CMAKE_SYSTEM_NAME MATCHES WindowsPhone OR CMAKE_SYSTEM_NAME MATCHES WindowsStore)
|
||||
endif(WIN32)
|
||||
|
||||
ADD_DEFINITIONS("-DHAVE_CONFIG_H")
|
||||
|
||||
# rpath handling: use rpath in installed binaries
|
||||
IF(NOT CMAKE_SYSTEM_NAME MATCHES Linux)
|
||||
SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
|
||||
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||
ENDIF()
|
||||
|
||||
# fixed size integral types
|
||||
|
||||
IF(HAVE_INTTYPES_H_LIBZIP)
|
||||
SET(LIBZIP_TYPES_INCLUDE "#define __STDC_FORMAT_MACROS 1
|
||||
#include <inttypes.h>")
|
||||
ELSEIF(HAVE_STDINT_H_LIBZIP)
|
||||
SET(LIBZIP_TYPES_INCLUDE "#include <stdint.h>")
|
||||
ELSEIF(HAVE_SYS_TYPES_H_LIBZIP)
|
||||
SET(LIBZIP_TYPES_INCLUDE "#include <sys/types.h>")
|
||||
ENDIF()
|
||||
|
||||
IF(HAVE_INT8_T_LIBZIP)
|
||||
SET(ZIP_INT8_T int8_t)
|
||||
ELSEIF(HAVE___INT8_LIBZIP)
|
||||
SET(ZIP_INT8_T __int8)
|
||||
ELSE()
|
||||
SET(ZIP_INT8_T "signed char")
|
||||
ENDIF()
|
||||
|
||||
IF(HAVE_UINT8_T_LIBZIP)
|
||||
SET(ZIP_UINT8_T uint8_t)
|
||||
ELSEIF(HAVE___INT8_LIBZIP)
|
||||
SET(ZIP_UINT8_T "unsigned __int8")
|
||||
ELSE()
|
||||
SET(ZIP_UINT8_T "unsigned char")
|
||||
ENDIF()
|
||||
|
||||
IF(HAVE_INT16_T_LIBZIP)
|
||||
SET(ZIP_INT16_T int16_t)
|
||||
ELSEIF(HAVE___INT16_LIBZIP)
|
||||
SET(INT16_T_LIBZIP __int16)
|
||||
ELSEIF(SHORT_LIBZIP EQUAL 2)
|
||||
SET(INT16_T_LIBZIP short)
|
||||
ENDIF()
|
||||
|
||||
IF(HAVE_UINT16_T_LIBZIP)
|
||||
SET(ZIP_UINT16_T uint16_t)
|
||||
ELSEIF(HAVE___INT16_LIBZIP)
|
||||
SET(UINT16_T_LIBZIP "unsigned __int16")
|
||||
ELSEIF(SHORT_LIBZIP EQUAL 2)
|
||||
SET(UINT16_T_LIBZIP "unsigned short")
|
||||
ENDIF()
|
||||
|
||||
IF(HAVE_INT32_T_LIBZIP)
|
||||
SET(ZIP_INT32_T int32_t)
|
||||
ELSEIF(HAVE___INT32_LIBZIP)
|
||||
SET(ZIP_INT32_T __int32)
|
||||
ELSEIF(INT_LIBZIP EQUAL 4)
|
||||
SET(ZIP_INT32_T int)
|
||||
ELSEIF(LONG_LIBZIP EQUAL 4)
|
||||
SET(ZIP_INT32_T long)
|
||||
ENDIF()
|
||||
|
||||
IF(HAVE_UINT32_T_LIBZIP)
|
||||
SET(ZIP_UINT32_T uint32_t)
|
||||
ELSEIF(HAVE___INT32_LIBZIP)
|
||||
SET(ZIP_UINT32_T "unsigned __int32")
|
||||
ELSEIF(INT_LIBZIP EQUAL 4)
|
||||
SET(ZIP_UINT32_T "unsigned int")
|
||||
ELSEIF(LONG_LIBZIP EQUAL 4)
|
||||
SET(ZIP_UINT32_T "unsigned long")
|
||||
ENDIF()
|
||||
|
||||
IF(HAVE_INT64_T_LIBZIP)
|
||||
SET(ZIP_INT64_T int64_t)
|
||||
ELSEIF(HAVE___INT64_LIBZIP)
|
||||
SET(ZIP_INT64_T __int64)
|
||||
ELSEIF(LONG_LIBZIP EQUAL 8)
|
||||
SET(ZIP_INT64_T long)
|
||||
ELSEIF(LONG_LONG_LIBZIP EQUAL 8)
|
||||
SET(ZIP_INT64_T "long long")
|
||||
ENDIF()
|
||||
|
||||
IF(HAVE_UINT64_T_LIBZIP)
|
||||
SET(ZIP_UINT64_T uint64_t)
|
||||
ELSEIF(HAVE___INT64_LIBZIP)
|
||||
SET(ZIP_UINT64_T "unsigned __int64")
|
||||
ELSEIF(LONG_LIBZIP EQUAL 8)
|
||||
SET(ZIP_UINT64_T "unsigned long")
|
||||
ELSEIF(LONG_LONG_LIBZIP EQUAL 8)
|
||||
SET(ZIP_UINT64_T "unsigned long long")
|
||||
ENDIF()
|
||||
|
||||
IF(HAVE_NULLABLE)
|
||||
SET(ZIP_NULLABLE_DEFINES)
|
||||
ELSE()
|
||||
SET(ZIP_NULLABLE_DEFINES "#define _Nullable
|
||||
#define _Nonnull")
|
||||
ENDIF()
|
||||
|
||||
# write out config file
|
||||
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/libzip/cmake-config.h.in ${CMAKE_CURRENT_BINARY_DIR}/libzip/config.h)
|
||||
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/libzip/cmake-zipconf.h.in ${CMAKE_CURRENT_BINARY_DIR}/libzip/zipconf.h)
|
||||
|
||||
# installation
|
||||
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/libzip/zipconf.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
INSTALL(FILES libzip/lib/zip.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
|
||||
SET(CMAKE_C_VISIBILITY_PRESET hidden)
|
||||
|
||||
ADD_LIBRARY(zip
|
||||
libzip/lib/zip_add.c
|
||||
libzip/lib/zip_add_dir.c
|
||||
libzip/lib/zip_add_entry.c
|
||||
libzip/lib/zip_algorithm_deflate.c
|
||||
libzip/lib/zip_buffer.c
|
||||
libzip/lib/zip_close.c
|
||||
libzip/lib/zip_delete.c
|
||||
libzip/lib/zip_dir_add.c
|
||||
libzip/lib/zip_dirent.c
|
||||
libzip/lib/zip_discard.c
|
||||
libzip/lib/zip_entry.c
|
||||
libzip/lib/zip_err_str.c
|
||||
libzip/lib/zip_error.c
|
||||
libzip/lib/zip_error_clear.c
|
||||
libzip/lib/zip_error_get.c
|
||||
libzip/lib/zip_error_get_sys_type.c
|
||||
libzip/lib/zip_error_strerror.c
|
||||
libzip/lib/zip_error_to_str.c
|
||||
libzip/lib/zip_extra_field.c
|
||||
libzip/lib/zip_extra_field_api.c
|
||||
libzip/lib/zip_fclose.c
|
||||
libzip/lib/zip_fdopen.c
|
||||
libzip/lib/zip_file_add.c
|
||||
libzip/lib/zip_file_error_clear.c
|
||||
libzip/lib/zip_file_error_get.c
|
||||
libzip/lib/zip_file_get_comment.c
|
||||
libzip/lib/zip_file_get_external_attributes.c
|
||||
libzip/lib/zip_file_get_offset.c
|
||||
libzip/lib/zip_file_rename.c
|
||||
libzip/lib/zip_file_replace.c
|
||||
libzip/lib/zip_file_set_comment.c
|
||||
libzip/lib/zip_file_set_encryption.c
|
||||
libzip/lib/zip_file_set_external_attributes.c
|
||||
libzip/lib/zip_file_set_mtime.c
|
||||
libzip/lib/zip_file_strerror.c
|
||||
libzip/lib/zip_filerange_crc.c
|
||||
libzip/lib/zip_fopen.c
|
||||
libzip/lib/zip_fopen_encrypted.c
|
||||
libzip/lib/zip_fopen_index.c
|
||||
libzip/lib/zip_fopen_index_encrypted.c
|
||||
libzip/lib/zip_fread.c
|
||||
libzip/lib/zip_fseek.c
|
||||
libzip/lib/zip_ftell.c
|
||||
libzip/lib/zip_get_archive_comment.c
|
||||
libzip/lib/zip_get_archive_flag.c
|
||||
libzip/lib/zip_get_encryption_implementation.c
|
||||
libzip/lib/zip_get_file_comment.c
|
||||
libzip/lib/zip_get_name.c
|
||||
libzip/lib/zip_get_num_entries.c
|
||||
libzip/lib/zip_get_num_files.c
|
||||
libzip/lib/zip_hash.c
|
||||
libzip/lib/zip_io_util.c
|
||||
libzip/lib/zip_libzip_version.c
|
||||
libzip/lib/zip_memdup.c
|
||||
libzip/lib/zip_name_locate.c
|
||||
libzip/lib/zip_new.c
|
||||
libzip/lib/zip_open.c
|
||||
libzip/lib/zip_progress.c
|
||||
libzip/lib/zip_rename.c
|
||||
libzip/lib/zip_replace.c
|
||||
libzip/lib/zip_set_archive_comment.c
|
||||
libzip/lib/zip_set_archive_flag.c
|
||||
libzip/lib/zip_set_default_password.c
|
||||
libzip/lib/zip_set_file_comment.c
|
||||
libzip/lib/zip_set_file_compression.c
|
||||
libzip/lib/zip_set_name.c
|
||||
libzip/lib/zip_source_accept_empty.c
|
||||
libzip/lib/zip_source_begin_write.c
|
||||
libzip/lib/zip_source_begin_write_cloning.c
|
||||
libzip/lib/zip_source_buffer.c
|
||||
libzip/lib/zip_source_call.c
|
||||
libzip/lib/zip_source_close.c
|
||||
libzip/lib/zip_source_commit_write.c
|
||||
libzip/lib/zip_source_compress.c
|
||||
libzip/lib/zip_source_crc.c
|
||||
libzip/lib/zip_source_error.c
|
||||
libzip/lib/zip_source_filep.c
|
||||
libzip/lib/zip_source_free.c
|
||||
libzip/lib/zip_source_function.c
|
||||
libzip/lib/zip_source_get_compression_flags.c
|
||||
libzip/lib/zip_source_is_deleted.c
|
||||
libzip/lib/zip_source_layered.c
|
||||
libzip/lib/zip_source_open.c
|
||||
libzip/lib/zip_source_pkware.c
|
||||
libzip/lib/zip_source_read.c
|
||||
libzip/lib/zip_source_remove.c
|
||||
libzip/lib/zip_source_rollback_write.c
|
||||
libzip/lib/zip_source_seek.c
|
||||
libzip/lib/zip_source_seek_write.c
|
||||
libzip/lib/zip_source_stat.c
|
||||
libzip/lib/zip_source_supports.c
|
||||
libzip/lib/zip_source_tell.c
|
||||
libzip/lib/zip_source_tell_write.c
|
||||
libzip/lib/zip_source_window.c
|
||||
libzip/lib/zip_source_write.c
|
||||
libzip/lib/zip_source_zip.c
|
||||
libzip/lib/zip_source_zip_new.c
|
||||
libzip/lib/zip_stat.c
|
||||
libzip/lib/zip_stat_index.c
|
||||
libzip/lib/zip_stat_init.c
|
||||
libzip/lib/zip_strerror.c
|
||||
libzip/lib/zip_string.c
|
||||
libzip/lib/zip_unchange.c
|
||||
libzip/lib/zip_unchange_all.c
|
||||
libzip/lib/zip_unchange_archive.c
|
||||
libzip/lib/zip_unchange_data.c
|
||||
libzip/lib/zip_utf-8.c
|
||||
)
|
||||
|
||||
IF(WIN32)
|
||||
target_sources(zip PRIVATE
|
||||
libzip/lib/zip_source_win32handle.c
|
||||
libzip/lib/zip_source_win32utf8.c
|
||||
libzip/lib/zip_source_win32w.c
|
||||
)
|
||||
IF(CMAKE_SYSTEM_NAME MATCHES WindowsPhone OR CMAKE_SYSTEM_NAME MATCHES WindowsStore)
|
||||
ELSE()
|
||||
target_sources(zip PRIVATE libzip/lib/zip_source_win32a.c)
|
||||
ENDIF()
|
||||
ELSE()
|
||||
target_sources(zip PRIVATE
|
||||
libzip/lib/zip_mkstempm.c
|
||||
libzip/lib/zip_source_file.c
|
||||
libzip/lib/zip_random_unix.c
|
||||
)
|
||||
ENDIF()
|
||||
|
||||
IF(HAVE_LIBBZ2)
|
||||
target_sources(zip PRIVATE libzip/lib/zip_algorithm_bzip2.c)
|
||||
ENDIF()
|
||||
|
||||
IF(HAVE_LIBLZMA)
|
||||
target_sources(zip PRIVATE libzip/lib/zip_algorithm_xz.c)
|
||||
ENDIF()
|
||||
|
||||
IF(HAVE_COMMONCRYPTO)
|
||||
target_sources(zip PRIVATE libzip/lib/zip_crypto_commoncrypto.c)
|
||||
ELSEIF(HAVE_WINDOWS_CRYPTO)
|
||||
target_sources(zip PRIVATE libzip/lib/zip_crypto_win.c)
|
||||
ELSEIF(HAVE_GNUTLS)
|
||||
target_sources(zip PRIVATE libzip/lib/zip_crypto_gnutls.c)
|
||||
ELSEIF(HAVE_OPENSSL)
|
||||
target_sources(zip PRIVATE libzip/lib/zip_crypto_openssl.c)
|
||||
ELSEIF(HAVE_MBEDTLS)
|
||||
target_sources(zip PRIVATE libzip/lib/zip_crypto_mbedtls.c)
|
||||
ENDIF()
|
||||
|
||||
IF(HAVE_CRYPTO)
|
||||
target_sources(zip PRIVATE
|
||||
libzip/lib/zip_winzip_aes.c
|
||||
libzip/lib/zip_source_winzip_aes_decode.c
|
||||
libzip/lib/zip_source_winzip_aes_encode.c
|
||||
)
|
||||
ENDIF()
|
||||
|
||||
target_include_directories(zip
|
||||
PUBLIC
|
||||
libzip/lib
|
||||
${CMAKE_CURRENT_BINARY_DIR}/libzip
|
||||
)
|
||||
|
||||
# pkgconfig file
|
||||
SET(prefix ${CMAKE_INSTALL_PREFIX})
|
||||
SET(exec_prefix \${prefix})
|
||||
SET(bindir \${exec_prefix}/${CMAKE_INSTALL_BINDIR})
|
||||
SET(libdir \${exec_prefix}/${CMAKE_INSTALL_LIBDIR})
|
||||
SET(includedir \${prefix}/${CMAKE_INSTALL_INCLUDEDIR})
|
||||
IF(CMAKE_SYSTEM_NAME MATCHES BSD)
|
||||
SET(PKG_CONFIG_RPATH "-Wl,-R\${libdir}")
|
||||
ENDIF(CMAKE_SYSTEM_NAME MATCHES BSD)
|
||||
get_target_property(LIBS_PRIVATE zip LINK_LIBRARIES)
|
||||
foreach(LIB ${LIBS_PRIVATE})
|
||||
if(LIB MATCHES "^/")
|
||||
get_filename_component(LIB ${LIB} NAME_WE)
|
||||
string(REGEX REPLACE "^lib" "" LIB ${LIB})
|
||||
endif()
|
||||
set(LIBS "${LIBS} -l${LIB}")
|
||||
endforeach()
|
||||
CONFIGURE_FILE(libzip/libzip.pc.in libzip/libzip.pc @ONLY)
|
||||
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/libzip.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
|
||||
ADD_CUSTOM_TARGET(update_zip_err_str
|
||||
COMMAND sh ${CMAKE_CURRENT_SOURCE_DIR}/libzip/lib/make_zip_err_str.sh ${CMAKE_CURRENT_SOURCE_DIR}/libzip/lib/zip.h ${CMAKE_CURRENT_SOURCE_DIR}/libzip/lib/zip_err_str.c
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libzip/lib/zip.h ${CMAKE_CURRENT_SOURCE_DIR}/libzip/lib/make_zip_err_str.sh
|
||||
)
|
||||
|
||||
IF(SHARED_LIB_VERSIONNING)
|
||||
SET_TARGET_PROPERTIES(zip PROPERTIES VERSION 5.0 SOVERSION 5)
|
||||
ENDIF()
|
||||
|
||||
TARGET_LINK_LIBRARIES(zip ${ZLIB_LIBRARIES} ${OPTIONAL_LIBRARY})
|
||||
INSTALL(TARGETS zip
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
|
||||
1
externals/libzip/libzip
vendored
1
externals/libzip/libzip
vendored
Submodule externals/libzip/libzip deleted from 89bd6d63bd
6
externals/microprofile/microprofile.h
vendored
6
externals/microprofile/microprofile.h
vendored
@@ -814,7 +814,7 @@ struct MicroProfile
|
||||
|
||||
inline int MicroProfileLogType(MicroProfileLogEntry Index)
|
||||
{
|
||||
return (int)(((MP_LOG_BEGIN_MASK & Index)>>62) & 0x3ULL);
|
||||
return ((MP_LOG_BEGIN_MASK & Index)>>62) & 0x3;
|
||||
}
|
||||
|
||||
inline uint64_t MicroProfileLogTimerIndex(MicroProfileLogEntry Index)
|
||||
@@ -861,12 +861,12 @@ T MicroProfileMax(T a, T b)
|
||||
|
||||
inline int64_t MicroProfileMsToTick(float fMs, int64_t nTicksPerSecond)
|
||||
{
|
||||
return (int64_t)(fMs*0.001f*(float)nTicksPerSecond);
|
||||
return (int64_t)(fMs*0.001f*nTicksPerSecond);
|
||||
}
|
||||
|
||||
inline float MicroProfileTickToMsMultiplier(int64_t nTicksPerSecond)
|
||||
{
|
||||
return 1000.f / (float)nTicksPerSecond;
|
||||
return 1000.f / nTicksPerSecond;
|
||||
}
|
||||
|
||||
inline uint16_t MicroProfileGetGroupIndex(MicroProfileToken t)
|
||||
|
||||
2
externals/sirit
vendored
2
externals/sirit
vendored
Submodule externals/sirit updated: 12f40a8032...f7c4b07a7e
0
externals/zlib/zlib → externals/zlib
vendored
0
externals/zlib/zlib → externals/zlib
vendored
81
externals/zlib/CMakeLists.txt
vendored
81
externals/zlib/CMakeLists.txt
vendored
@@ -1,81 +0,0 @@
|
||||
project(zlib C)
|
||||
|
||||
include(CheckTypeSize)
|
||||
include(CheckFunctionExists)
|
||||
include(CheckIncludeFile)
|
||||
|
||||
check_include_file(sys/types.h HAVE_SYS_TYPES_H)
|
||||
check_include_file(stdint.h HAVE_STDINT_H)
|
||||
check_include_file(stddef.h HAVE_STDDEF_H)
|
||||
|
||||
# Check to see if we have large file support
|
||||
set(CMAKE_REQUIRED_DEFINITIONS -D_LARGEFILE64_SOURCE=1)
|
||||
# We add these other definitions here because CheckTypeSize.cmake
|
||||
# in CMake 2.4.x does not automatically do so and we want
|
||||
# compatibility with CMake 2.4.x.
|
||||
if(HAVE_SYS_TYPES_H)
|
||||
list(APPEND CMAKE_REQUIRED_DEFINITIONS -DHAVE_SYS_TYPES_H)
|
||||
endif()
|
||||
if(HAVE_STDINT_H)
|
||||
list(APPEND CMAKE_REQUIRED_DEFINITIONS -DHAVE_STDINT_H)
|
||||
endif()
|
||||
if(HAVE_STDDEF_H)
|
||||
list(APPEND CMAKE_REQUIRED_DEFINITIONS -DHAVE_STDDEF_H)
|
||||
endif()
|
||||
check_type_size(off64_t OFF64_T)
|
||||
if(HAVE_OFF64_T)
|
||||
add_definitions(-D_LARGEFILE64_SOURCE=1)
|
||||
endif()
|
||||
set(CMAKE_REQUIRED_DEFINITIONS) # clear variable
|
||||
|
||||
# Check for fseeko
|
||||
check_function_exists(fseeko HAVE_FSEEKO)
|
||||
if(NOT HAVE_FSEEKO)
|
||||
add_definitions(-DNO_FSEEKO)
|
||||
endif()
|
||||
|
||||
# Check for unistd.h
|
||||
check_include_file(unistd.h HAVE_UNISTD_H)
|
||||
if(HAVE_UNISTD_H)
|
||||
add_definitions(-DHAVE_UNISTD_H)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
add_definitions(-D_CRT_SECURE_NO_DEPRECATE)
|
||||
add_definitions(-D_CRT_NONSTDC_NO_DEPRECATE)
|
||||
endif()
|
||||
|
||||
add_library(z STATIC
|
||||
zlib/adler32.c
|
||||
zlib/compress.c
|
||||
zlib/crc32.c
|
||||
zlib/crc32.h
|
||||
zlib/deflate.c
|
||||
zlib/deflate.h
|
||||
zlib/gzclose.c
|
||||
zlib/gzguts.h
|
||||
zlib/gzlib.c
|
||||
zlib/gzread.c
|
||||
zlib/gzwrite.c
|
||||
zlib/inffast.h
|
||||
zlib/inffixed.h
|
||||
zlib/inflate.c
|
||||
zlib/inflate.h
|
||||
zlib/infback.c
|
||||
zlib/inftrees.c
|
||||
zlib/inftrees.h
|
||||
zlib/inffast.c
|
||||
zlib/trees.c
|
||||
zlib/trees.h
|
||||
zlib/uncompr.c
|
||||
zlib/zconf.h
|
||||
zlib/zlib.h
|
||||
zlib/zutil.c
|
||||
zlib/zutil.h
|
||||
)
|
||||
add_library(ZLIB::ZLIB ALIAS z)
|
||||
|
||||
target_include_directories(z
|
||||
PUBLIC
|
||||
zlib/
|
||||
)
|
||||
@@ -36,9 +36,9 @@ public:
|
||||
}
|
||||
|
||||
void SetWaveIndex(std::size_t index);
|
||||
std::vector<s16> DequeueSamples(std::size_t sample_count, Memory::Memory& memory);
|
||||
std::vector<s16> DequeueSamples(std::size_t sample_count);
|
||||
void UpdateState();
|
||||
void RefreshBuffer(Memory::Memory& memory);
|
||||
void RefreshBuffer();
|
||||
|
||||
private:
|
||||
bool is_in_use{};
|
||||
@@ -66,18 +66,17 @@ public:
|
||||
return info;
|
||||
}
|
||||
|
||||
void UpdateState(Memory::Memory& memory);
|
||||
void UpdateState();
|
||||
|
||||
private:
|
||||
EffectOutStatus out_status{};
|
||||
EffectInStatus info{};
|
||||
};
|
||||
AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Memory::Memory& memory_,
|
||||
AudioRendererParameter params,
|
||||
std::shared_ptr<Kernel::WritableEvent> buffer_event,
|
||||
AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, AudioRendererParameter params,
|
||||
Kernel::SharedPtr<Kernel::WritableEvent> buffer_event,
|
||||
std::size_t instance_number)
|
||||
: worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count),
|
||||
effects(params.effect_count), memory{memory_} {
|
||||
effects(params.effect_count) {
|
||||
|
||||
audio_out = std::make_unique<AudioCore::AudioOut>();
|
||||
stream = audio_out->OpenStream(core_timing, STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS,
|
||||
@@ -163,7 +162,7 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
|
||||
}
|
||||
|
||||
for (auto& effect : effects) {
|
||||
effect.UpdateState(memory);
|
||||
effect.UpdateState();
|
||||
}
|
||||
|
||||
// Release previous buffers and queue next ones for playback
|
||||
@@ -207,14 +206,13 @@ void AudioRenderer::VoiceState::SetWaveIndex(std::size_t index) {
|
||||
is_refresh_pending = true;
|
||||
}
|
||||
|
||||
std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(std::size_t sample_count,
|
||||
Memory::Memory& memory) {
|
||||
std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(std::size_t sample_count) {
|
||||
if (!IsPlaying()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (is_refresh_pending) {
|
||||
RefreshBuffer(memory);
|
||||
RefreshBuffer();
|
||||
}
|
||||
|
||||
const std::size_t max_size{samples.size() - offset};
|
||||
@@ -258,11 +256,10 @@ void AudioRenderer::VoiceState::UpdateState() {
|
||||
is_in_use = info.is_in_use;
|
||||
}
|
||||
|
||||
void AudioRenderer::VoiceState::RefreshBuffer(Memory::Memory& memory) {
|
||||
const auto wave_buffer_address = info.wave_buffer[wave_index].buffer_addr;
|
||||
const auto wave_buffer_size = info.wave_buffer[wave_index].buffer_sz;
|
||||
std::vector<s16> new_samples(wave_buffer_size / sizeof(s16));
|
||||
memory.ReadBlock(wave_buffer_address, new_samples.data(), wave_buffer_size);
|
||||
void AudioRenderer::VoiceState::RefreshBuffer() {
|
||||
std::vector<s16> new_samples(info.wave_buffer[wave_index].buffer_sz / sizeof(s16));
|
||||
Memory::ReadBlock(info.wave_buffer[wave_index].buffer_addr, new_samples.data(),
|
||||
info.wave_buffer[wave_index].buffer_sz);
|
||||
|
||||
switch (static_cast<Codec::PcmFormat>(info.sample_format)) {
|
||||
case Codec::PcmFormat::Int16: {
|
||||
@@ -272,7 +269,7 @@ void AudioRenderer::VoiceState::RefreshBuffer(Memory::Memory& memory) {
|
||||
case Codec::PcmFormat::Adpcm: {
|
||||
// Decode ADPCM to PCM16
|
||||
Codec::ADPCM_Coeff coeffs;
|
||||
memory.ReadBlock(info.additional_params_addr, coeffs.data(), sizeof(Codec::ADPCM_Coeff));
|
||||
Memory::ReadBlock(info.additional_params_addr, coeffs.data(), sizeof(Codec::ADPCM_Coeff));
|
||||
new_samples = Codec::DecodeADPCM(reinterpret_cast<u8*>(new_samples.data()),
|
||||
new_samples.size() * sizeof(s16), coeffs, adpcm_state);
|
||||
break;
|
||||
@@ -310,18 +307,18 @@ void AudioRenderer::VoiceState::RefreshBuffer(Memory::Memory& memory) {
|
||||
is_refresh_pending = false;
|
||||
}
|
||||
|
||||
void AudioRenderer::EffectState::UpdateState(Memory::Memory& memory) {
|
||||
void AudioRenderer::EffectState::UpdateState() {
|
||||
if (info.is_new) {
|
||||
out_status.state = EffectStatus::New;
|
||||
} else {
|
||||
if (info.type == Effect::Aux) {
|
||||
ASSERT_MSG(memory.Read32(info.aux_info.return_buffer_info) == 0,
|
||||
ASSERT_MSG(Memory::Read32(info.aux_info.return_buffer_info) == 0,
|
||||
"Aux buffers tried to update");
|
||||
ASSERT_MSG(memory.Read32(info.aux_info.send_buffer_info) == 0,
|
||||
ASSERT_MSG(Memory::Read32(info.aux_info.send_buffer_info) == 0,
|
||||
"Aux buffers tried to update");
|
||||
ASSERT_MSG(memory.Read32(info.aux_info.return_buffer_base) == 0,
|
||||
ASSERT_MSG(Memory::Read32(info.aux_info.return_buffer_base) == 0,
|
||||
"Aux buffers tried to update");
|
||||
ASSERT_MSG(memory.Read32(info.aux_info.send_buffer_base) == 0,
|
||||
ASSERT_MSG(Memory::Read32(info.aux_info.send_buffer_base) == 0,
|
||||
"Aux buffers tried to update");
|
||||
}
|
||||
}
|
||||
@@ -343,7 +340,7 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
|
||||
std::size_t offset{};
|
||||
s64 samples_remaining{BUFFER_SIZE};
|
||||
while (samples_remaining > 0) {
|
||||
const std::vector<s16> samples{voice.DequeueSamples(samples_remaining, memory)};
|
||||
const std::vector<s16> samples{voice.DequeueSamples(samples_remaining)};
|
||||
|
||||
if (samples.empty()) {
|
||||
break;
|
||||
|
||||
@@ -22,10 +22,6 @@ namespace Kernel {
|
||||
class WritableEvent;
|
||||
}
|
||||
|
||||
namespace Memory {
|
||||
class Memory;
|
||||
}
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
class AudioOut;
|
||||
@@ -221,9 +217,9 @@ static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size
|
||||
|
||||
class AudioRenderer {
|
||||
public:
|
||||
AudioRenderer(Core::Timing::CoreTiming& core_timing, Memory::Memory& memory_,
|
||||
AudioRendererParameter params,
|
||||
std::shared_ptr<Kernel::WritableEvent> buffer_event, std::size_t instance_number);
|
||||
AudioRenderer(Core::Timing::CoreTiming& core_timing, AudioRendererParameter params,
|
||||
Kernel::SharedPtr<Kernel::WritableEvent> buffer_event,
|
||||
std::size_t instance_number);
|
||||
~AudioRenderer();
|
||||
|
||||
std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params);
|
||||
@@ -239,12 +235,11 @@ private:
|
||||
class VoiceState;
|
||||
|
||||
AudioRendererParameter worker_params;
|
||||
std::shared_ptr<Kernel::WritableEvent> buffer_event;
|
||||
Kernel::SharedPtr<Kernel::WritableEvent> buffer_event;
|
||||
std::vector<VoiceState> voices;
|
||||
std::vector<EffectState> effects;
|
||||
std::unique_ptr<AudioOut> audio_out;
|
||||
StreamPtr stream;
|
||||
Memory::Memory& memory;
|
||||
AudioCore::StreamPtr stream;
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
||||
|
||||
@@ -37,7 +37,7 @@ Stream::Stream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, Format fo
|
||||
: sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)},
|
||||
sink_stream{sink_stream}, core_timing{core_timing}, name{std::move(name_)} {
|
||||
|
||||
release_event = Core::Timing::CreateEvent(
|
||||
release_event = core_timing.RegisterEvent(
|
||||
name, [this](u64 userdata, s64 cycles_late) { ReleaseActiveBuffer(); });
|
||||
}
|
||||
|
||||
|
||||
@@ -98,19 +98,18 @@ private:
|
||||
/// Gets the number of core cycles when the specified buffer will be released
|
||||
s64 GetBufferReleaseCycles(const Buffer& buffer) const;
|
||||
|
||||
u32 sample_rate; ///< Sample rate of the stream
|
||||
Format format; ///< Format of the stream
|
||||
float game_volume = 1.0f; ///< The volume the game currently has set
|
||||
ReleaseCallback release_callback; ///< Buffer release callback for the stream
|
||||
State state{State::Stopped}; ///< Playback state of the stream
|
||||
std::shared_ptr<Core::Timing::EventType>
|
||||
release_event; ///< Core timing release event for the stream
|
||||
BufferPtr active_buffer; ///< Actively playing buffer in the stream
|
||||
std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream
|
||||
std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
|
||||
SinkStream& sink_stream; ///< Output sink for the stream
|
||||
Core::Timing::CoreTiming& core_timing; ///< Core timing instance.
|
||||
std::string name; ///< Name of the stream, must be unique
|
||||
u32 sample_rate; ///< Sample rate of the stream
|
||||
Format format; ///< Format of the stream
|
||||
float game_volume = 1.0f; ///< The volume the game currently has set
|
||||
ReleaseCallback release_callback; ///< Buffer release callback for the stream
|
||||
State state{State::Stopped}; ///< Playback state of the stream
|
||||
Core::Timing::EventType* release_event{}; ///< Core timing release event for the stream
|
||||
BufferPtr active_buffer; ///< Actively playing buffer in the stream
|
||||
std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream
|
||||
std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
|
||||
SinkStream& sink_stream; ///< Output sink for the stream
|
||||
Core::Timing::CoreTiming& core_timing; ///< Core timing instance.
|
||||
std::string name; ///< Name of the stream, must be unique
|
||||
};
|
||||
|
||||
using StreamPtr = std::shared_ptr<Stream>;
|
||||
|
||||
@@ -3,8 +3,17 @@
|
||||
# could affect the result, but much more unlikely than the following files. Keeping a list of files
|
||||
# like this allows for much better caching since it doesn't force the user to recompile binary shaders every update
|
||||
set(VIDEO_CORE "${CMAKE_SOURCE_DIR}/src/video_core")
|
||||
if (DEFINED ENV{AZURECIREPO})
|
||||
set(BUILD_REPOSITORY $ENV{AZURECIREPO})
|
||||
if (DEFINED ENV{CI})
|
||||
if (DEFINED ENV{TRAVIS})
|
||||
set(BUILD_REPOSITORY $ENV{TRAVIS_REPO_SLUG})
|
||||
set(BUILD_TAG $ENV{TRAVIS_TAG})
|
||||
elseif(DEFINED ENV{APPVEYOR})
|
||||
set(BUILD_REPOSITORY $ENV{APPVEYOR_REPO_NAME})
|
||||
set(BUILD_TAG $ENV{APPVEYOR_REPO_TAG_NAME})
|
||||
elseif(DEFINED ENV{AZURE})
|
||||
set(BUILD_REPOSITORY $ENV{AZURE_REPO_NAME})
|
||||
set(BUILD_TAG $ENV{AZURE_REPO_TAG})
|
||||
endif()
|
||||
endif()
|
||||
if (DEFINED ENV{TITLEBARFORMATIDLE})
|
||||
set(TITLE_BAR_FORMAT_IDLE $ENV{TITLEBARFORMATIDLE})
|
||||
|
||||
@@ -28,22 +28,17 @@ __declspec(noinline, noreturn)
|
||||
}
|
||||
|
||||
#define ASSERT(_a_) \
|
||||
do \
|
||||
if (!(_a_)) { \
|
||||
assert_noinline_call([] { LOG_CRITICAL(Debug, "Assertion Failed!"); }); \
|
||||
} \
|
||||
while (0)
|
||||
if (!(_a_)) { \
|
||||
LOG_CRITICAL(Debug, "Assertion Failed!"); \
|
||||
}
|
||||
|
||||
#define ASSERT_MSG(_a_, ...) \
|
||||
do \
|
||||
if (!(_a_)) { \
|
||||
assert_noinline_call([&] { LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \
|
||||
} \
|
||||
while (0)
|
||||
if (!(_a_)) { \
|
||||
LOG_CRITICAL(Debug, "Assertion Failed! " __VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define UNREACHABLE() assert_noinline_call([] { LOG_CRITICAL(Debug, "Unreachable code!"); })
|
||||
#define UNREACHABLE_MSG(...) \
|
||||
assert_noinline_call([&] { LOG_CRITICAL(Debug, "Unreachable code!\n" __VA_ARGS__); })
|
||||
#define UNREACHABLE() ASSERT_MSG(false, "Unreachable code!")
|
||||
#define UNREACHABLE_MSG(...) ASSERT_MSG(false, __VA_ARGS__)
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define DEBUG_ASSERT(_a_) ASSERT(_a_)
|
||||
|
||||
@@ -36,13 +36,6 @@
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/swap.h"
|
||||
|
||||
// Inlining
|
||||
#ifdef _WIN32
|
||||
#define FORCE_INLINE __forceinline
|
||||
#else
|
||||
#define FORCE_INLINE inline __attribute__((always_inline))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Abstract bitfield class
|
||||
*
|
||||
@@ -175,11 +168,11 @@ public:
|
||||
constexpr BitField(BitField&&) noexcept = default;
|
||||
constexpr BitField& operator=(BitField&&) noexcept = default;
|
||||
|
||||
constexpr operator T() const {
|
||||
constexpr FORCE_INLINE operator T() const {
|
||||
return Value();
|
||||
}
|
||||
|
||||
constexpr void Assign(const T& value) {
|
||||
constexpr FORCE_INLINE void Assign(const T& value) {
|
||||
storage = (static_cast<StorageType>(storage) & ~mask) | FormatValue(value);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
||||
#if !defined(ARCHITECTURE_x86_64)
|
||||
@@ -17,17 +16,18 @@
|
||||
#define CONCAT2(x, y) DO_CONCAT2(x, y)
|
||||
#define DO_CONCAT2(x, y) x##y
|
||||
|
||||
/// Helper macros to insert unused bytes or words to properly align structs. These values will be
|
||||
/// zero-initialized.
|
||||
#define INSERT_PADDING_BYTES(num_bytes) \
|
||||
std::array<u8, num_bytes> CONCAT2(pad, __LINE__) {}
|
||||
#define INSERT_PADDING_WORDS(num_words) \
|
||||
std::array<u32, num_words> CONCAT2(pad, __LINE__) {}
|
||||
// helper macro to properly align structure members.
|
||||
// Calling INSERT_PADDING_BYTES will add a new member variable with a name like "pad121",
|
||||
// depending on the current source line to make sure variable names are unique.
|
||||
#define INSERT_PADDING_BYTES(num_bytes) u8 CONCAT2(pad, __LINE__)[(num_bytes)]
|
||||
#define INSERT_PADDING_WORDS(num_words) u32 CONCAT2(pad, __LINE__)[(num_words)]
|
||||
|
||||
/// These are similar to the INSERT_PADDING_* macros, but are needed for padding unions. This is
|
||||
/// because unions can only be initialized by one member.
|
||||
#define INSERT_UNION_PADDING_BYTES(num_bytes) std::array<u8, num_bytes> CONCAT2(pad, __LINE__)
|
||||
#define INSERT_UNION_PADDING_WORDS(num_words) std::array<u32, num_words> CONCAT2(pad, __LINE__)
|
||||
// Inlining
|
||||
#ifdef _WIN32
|
||||
#define FORCE_INLINE __forceinline
|
||||
#else
|
||||
#define FORCE_INLINE inline __attribute__((always_inline))
|
||||
#endif
|
||||
|
||||
#ifndef _MSC_VER
|
||||
|
||||
@@ -58,7 +58,7 @@ std::string GetLastErrorMsg();
|
||||
namespace Common {
|
||||
|
||||
constexpr u32 MakeMagic(char a, char b, char c, char d) {
|
||||
return u32(a) | u32(b) << 8 | u32(c) << 16 | u32(d) << 24;
|
||||
return a | b << 8 | c << 16 | d << 24;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -35,6 +35,41 @@ static inline u64 ComputeStructHash64(const T& data) {
|
||||
return ComputeHash64(&data, sizeof(data));
|
||||
}
|
||||
|
||||
/// A helper template that ensures the padding in a struct is initialized by memsetting to 0.
|
||||
template <typename T>
|
||||
struct HashableStruct {
|
||||
// In addition to being trivially copyable, T must also have a trivial default constructor,
|
||||
// because any member initialization would be overridden by memset
|
||||
static_assert(std::is_trivial_v<T>, "Type passed to HashableStruct must be trivial");
|
||||
/*
|
||||
* We use a union because "implicitly-defined copy/move constructor for a union X copies the
|
||||
* object representation of X." and "implicitly-defined copy assignment operator for a union X
|
||||
* copies the object representation (3.9) of X." = Bytewise copy instead of memberwise copy.
|
||||
* This is important because the padding bytes are included in the hash and comparison between
|
||||
* objects.
|
||||
*/
|
||||
union {
|
||||
T state;
|
||||
};
|
||||
|
||||
HashableStruct() {
|
||||
// Memset structure to zero padding bits, so that they will be deterministic when hashing
|
||||
std::memset(&state, 0, sizeof(T));
|
||||
}
|
||||
|
||||
bool operator==(const HashableStruct<T>& o) const {
|
||||
return std::memcmp(&state, &o.state, sizeof(T)) == 0;
|
||||
};
|
||||
|
||||
bool operator!=(const HashableStruct<T>& o) const {
|
||||
return !(*this == o);
|
||||
};
|
||||
|
||||
std::size_t Hash() const {
|
||||
return Common::ComputeStructHash64(state);
|
||||
}
|
||||
};
|
||||
|
||||
struct PairHash {
|
||||
template <class T1, class T2>
|
||||
std::size_t operator()(const std::pair<T1, T2>& pair) const noexcept {
|
||||
|
||||
@@ -272,10 +272,8 @@ const char* GetLogClassName(Class log_class) {
|
||||
#undef CLS
|
||||
#undef SUB
|
||||
case Class::Count:
|
||||
break;
|
||||
UNREACHABLE();
|
||||
}
|
||||
UNREACHABLE();
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
const char* GetLevelName(Level log_level) {
|
||||
@@ -290,11 +288,9 @@ const char* GetLevelName(Level log_level) {
|
||||
LVL(Error);
|
||||
LVL(Critical);
|
||||
case Level::Count:
|
||||
break;
|
||||
UNREACHABLE();
|
||||
}
|
||||
#undef LVL
|
||||
UNREACHABLE();
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
void SetGlobalFilter(const Filter& filter) {
|
||||
|
||||
@@ -46,16 +46,9 @@ public:
|
||||
ElementPtr* new_ptr = new ElementPtr();
|
||||
write_ptr->next.store(new_ptr, std::memory_order_release);
|
||||
write_ptr = new_ptr;
|
||||
|
||||
const size_t previous_size{size++};
|
||||
|
||||
// Acquire the mutex and then immediately release it as a fence.
|
||||
// TODO(bunnei): This can be replaced with C++20 waitable atomics when properly supported.
|
||||
// See discussion on https://github.com/yuzu-emu/yuzu/pull/3173 for details.
|
||||
if (previous_size == 0) {
|
||||
std::lock_guard lock{cv_mutex};
|
||||
}
|
||||
cv.notify_one();
|
||||
|
||||
++size;
|
||||
}
|
||||
|
||||
void Pop() {
|
||||
|
||||
@@ -108,6 +108,8 @@ add_library(core STATIC
|
||||
file_sys/vfs_offset.h
|
||||
file_sys/vfs_real.cpp
|
||||
file_sys/vfs_real.h
|
||||
file_sys/vfs_ro_layer.cpp
|
||||
file_sys/vfs_ro_layer.h
|
||||
file_sys/vfs_static.h
|
||||
file_sys/vfs_types.h
|
||||
file_sys/vfs_vector.cpp
|
||||
@@ -170,7 +172,6 @@ add_library(core STATIC
|
||||
hle/kernel/server_port.h
|
||||
hle/kernel/server_session.cpp
|
||||
hle/kernel/server_session.h
|
||||
hle/kernel/session.cpp
|
||||
hle/kernel/session.h
|
||||
hle/kernel/shared_memory.cpp
|
||||
hle/kernel/shared_memory.h
|
||||
@@ -510,6 +511,7 @@ add_library(core STATIC
|
||||
memory/dmnt_cheat_vm.h
|
||||
memory.cpp
|
||||
memory.h
|
||||
memory_setup.h
|
||||
perf_stats.cpp
|
||||
perf_stats.h
|
||||
reporter.cpp
|
||||
@@ -522,23 +524,6 @@ add_library(core STATIC
|
||||
tools/freezer.h
|
||||
)
|
||||
|
||||
if (MSVC)
|
||||
target_compile_options(core PRIVATE
|
||||
# 'expression' : signed/unsigned mismatch
|
||||
/we4018
|
||||
# 'argument' : conversion from 'type1' to 'type2', possible loss of data (floating-point)
|
||||
/we4244
|
||||
# 'conversion' : conversion from 'type1' to 'type2', signed/unsigned mismatch
|
||||
/we4245
|
||||
# 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
|
||||
/we4254
|
||||
# 'var' : conversion from 'size_t' to 'type', possible loss of data
|
||||
/we4267
|
||||
# 'context' : truncation from 'type1' to 'type2'
|
||||
/we4305
|
||||
)
|
||||
endif()
|
||||
|
||||
create_target_directory_groups(core)
|
||||
|
||||
target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
|
||||
@@ -60,15 +61,15 @@ static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size.");
|
||||
|
||||
using Symbols = std::vector<std::pair<ELFSymbol, std::string>>;
|
||||
|
||||
Symbols GetSymbols(VAddr text_offset, Memory::Memory& memory) {
|
||||
const auto mod_offset = text_offset + memory.Read32(text_offset + 4);
|
||||
Symbols GetSymbols(VAddr text_offset) {
|
||||
const auto mod_offset = text_offset + Memory::Read32(text_offset + 4);
|
||||
|
||||
if (mod_offset < text_offset || (mod_offset & 0b11) != 0 ||
|
||||
memory.Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
|
||||
Memory::Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto dynamic_offset = memory.Read32(mod_offset + 0x4) + mod_offset;
|
||||
const auto dynamic_offset = Memory::Read32(mod_offset + 0x4) + mod_offset;
|
||||
|
||||
VAddr string_table_offset{};
|
||||
VAddr symbol_table_offset{};
|
||||
@@ -76,8 +77,8 @@ Symbols GetSymbols(VAddr text_offset, Memory::Memory& memory) {
|
||||
|
||||
VAddr dynamic_index = dynamic_offset;
|
||||
while (true) {
|
||||
const u64 tag = memory.Read64(dynamic_index);
|
||||
const u64 value = memory.Read64(dynamic_index + 0x8);
|
||||
const auto tag = Memory::Read64(dynamic_index);
|
||||
const auto value = Memory::Read64(dynamic_index + 0x8);
|
||||
dynamic_index += 0x10;
|
||||
|
||||
if (tag == ELF_DYNAMIC_TAG_NULL) {
|
||||
@@ -105,11 +106,11 @@ Symbols GetSymbols(VAddr text_offset, Memory::Memory& memory) {
|
||||
VAddr symbol_index = symbol_table_address;
|
||||
while (symbol_index < string_table_address) {
|
||||
ELFSymbol symbol{};
|
||||
memory.ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol));
|
||||
Memory::ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol));
|
||||
|
||||
VAddr string_offset = string_table_address + symbol.name_index;
|
||||
std::string name;
|
||||
for (u8 c = memory.Read8(string_offset); c != 0; c = memory.Read8(++string_offset)) {
|
||||
for (u8 c = Memory::Read8(string_offset); c != 0; c = Memory::Read8(++string_offset)) {
|
||||
name += static_cast<char>(c);
|
||||
}
|
||||
|
||||
@@ -141,28 +142,28 @@ constexpr u64 SEGMENT_BASE = 0x7100000000ull;
|
||||
|
||||
std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
|
||||
std::vector<BacktraceEntry> out;
|
||||
auto& memory = system.Memory();
|
||||
|
||||
auto fp = GetReg(29);
|
||||
auto lr = GetReg(30);
|
||||
|
||||
while (true) {
|
||||
out.push_back({"", 0, lr, 0});
|
||||
if (!fp) {
|
||||
break;
|
||||
}
|
||||
lr = memory.Read64(fp + 8) - 4;
|
||||
fp = memory.Read64(fp);
|
||||
lr = Memory::Read64(fp + 8) - 4;
|
||||
fp = Memory::Read64(fp);
|
||||
}
|
||||
|
||||
std::map<VAddr, std::string> modules;
|
||||
auto& loader{system.GetAppLoader()};
|
||||
auto& loader{System::GetInstance().GetAppLoader()};
|
||||
if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::map<std::string, Symbols> symbols;
|
||||
for (const auto& module : modules) {
|
||||
symbols.insert_or_assign(module.second, GetSymbols(module.first, memory));
|
||||
symbols.insert_or_assign(module.second, GetSymbols(module.first));
|
||||
}
|
||||
|
||||
for (auto& entry : out) {
|
||||
|
||||
@@ -17,13 +17,11 @@ enum class VMAPermission : u8;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
|
||||
/// Generic ARMv8 CPU interface
|
||||
class ARM_Interface : NonCopyable {
|
||||
public:
|
||||
explicit ARM_Interface(System& system_) : system{system_} {}
|
||||
virtual ~ARM_Interface() = default;
|
||||
virtual ~ARM_Interface() {}
|
||||
|
||||
struct ThreadContext {
|
||||
std::array<u64, 31> cpu_registers;
|
||||
@@ -165,10 +163,6 @@ public:
|
||||
/// fp+0 : pointer to previous frame record
|
||||
/// fp+8 : value of lr for frame
|
||||
void LogBacktrace() const;
|
||||
|
||||
protected:
|
||||
/// System context that this ARM interface is running under.
|
||||
System& system;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -28,38 +28,36 @@ public:
|
||||
explicit ARM_Dynarmic_Callbacks(ARM_Dynarmic& parent) : parent(parent) {}
|
||||
|
||||
u8 MemoryRead8(u64 vaddr) override {
|
||||
return parent.system.Memory().Read8(vaddr);
|
||||
return Memory::Read8(vaddr);
|
||||
}
|
||||
u16 MemoryRead16(u64 vaddr) override {
|
||||
return parent.system.Memory().Read16(vaddr);
|
||||
return Memory::Read16(vaddr);
|
||||
}
|
||||
u32 MemoryRead32(u64 vaddr) override {
|
||||
return parent.system.Memory().Read32(vaddr);
|
||||
return Memory::Read32(vaddr);
|
||||
}
|
||||
u64 MemoryRead64(u64 vaddr) override {
|
||||
return parent.system.Memory().Read64(vaddr);
|
||||
return Memory::Read64(vaddr);
|
||||
}
|
||||
Vector MemoryRead128(u64 vaddr) override {
|
||||
auto& memory = parent.system.Memory();
|
||||
return {memory.Read64(vaddr), memory.Read64(vaddr + 8)};
|
||||
return {Memory::Read64(vaddr), Memory::Read64(vaddr + 8)};
|
||||
}
|
||||
|
||||
void MemoryWrite8(u64 vaddr, u8 value) override {
|
||||
parent.system.Memory().Write8(vaddr, value);
|
||||
Memory::Write8(vaddr, value);
|
||||
}
|
||||
void MemoryWrite16(u64 vaddr, u16 value) override {
|
||||
parent.system.Memory().Write16(vaddr, value);
|
||||
Memory::Write16(vaddr, value);
|
||||
}
|
||||
void MemoryWrite32(u64 vaddr, u32 value) override {
|
||||
parent.system.Memory().Write32(vaddr, value);
|
||||
Memory::Write32(vaddr, value);
|
||||
}
|
||||
void MemoryWrite64(u64 vaddr, u64 value) override {
|
||||
parent.system.Memory().Write64(vaddr, value);
|
||||
Memory::Write64(vaddr, value);
|
||||
}
|
||||
void MemoryWrite128(u64 vaddr, Vector value) override {
|
||||
auto& memory = parent.system.Memory();
|
||||
memory.Write64(vaddr, value[0]);
|
||||
memory.Write64(vaddr + 8, value[1]);
|
||||
Memory::Write64(vaddr, value[0]);
|
||||
Memory::Write64(vaddr + 8, value[1]);
|
||||
}
|
||||
|
||||
void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
|
||||
@@ -69,7 +67,7 @@ public:
|
||||
ARM_Interface::ThreadContext ctx;
|
||||
parent.SaveContext(ctx);
|
||||
parent.inner_unicorn.LoadContext(ctx);
|
||||
parent.inner_unicorn.ExecuteInstructions(num_instructions);
|
||||
parent.inner_unicorn.ExecuteInstructions(static_cast<int>(num_instructions));
|
||||
parent.inner_unicorn.SaveContext(ctx);
|
||||
parent.LoadContext(ctx);
|
||||
num_interpreted_instructions += num_instructions;
|
||||
@@ -173,10 +171,9 @@ void ARM_Dynarmic::Step() {
|
||||
|
||||
ARM_Dynarmic::ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor,
|
||||
std::size_t core_index)
|
||||
: ARM_Interface{system},
|
||||
cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{system},
|
||||
core_index{core_index}, exclusive_monitor{
|
||||
dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
|
||||
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{system},
|
||||
core_index{core_index}, system{system},
|
||||
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
|
||||
|
||||
ARM_Dynarmic::~ARM_Dynarmic() = default;
|
||||
|
||||
@@ -267,9 +264,7 @@ void ARM_Dynarmic::PageTableChanged(Common::PageTable& page_table,
|
||||
jit = MakeJit(page_table, new_address_space_size_in_bits);
|
||||
}
|
||||
|
||||
DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(Memory::Memory& memory_, std::size_t core_count)
|
||||
: monitor(core_count), memory{memory_} {}
|
||||
|
||||
DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(std::size_t core_count) : monitor(core_count) {}
|
||||
DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default;
|
||||
|
||||
void DynarmicExclusiveMonitor::SetExclusive(std::size_t core_index, VAddr addr) {
|
||||
@@ -282,28 +277,29 @@ void DynarmicExclusiveMonitor::ClearExclusive() {
|
||||
}
|
||||
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) {
|
||||
return monitor.DoExclusiveOperation(core_index, vaddr, 1, [&] { memory.Write8(vaddr, value); });
|
||||
return monitor.DoExclusiveOperation(core_index, vaddr, 1,
|
||||
[&] { Memory::Write8(vaddr, value); });
|
||||
}
|
||||
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) {
|
||||
return monitor.DoExclusiveOperation(core_index, vaddr, 2,
|
||||
[&] { memory.Write16(vaddr, value); });
|
||||
[&] { Memory::Write16(vaddr, value); });
|
||||
}
|
||||
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) {
|
||||
return monitor.DoExclusiveOperation(core_index, vaddr, 4,
|
||||
[&] { memory.Write32(vaddr, value); });
|
||||
[&] { Memory::Write32(vaddr, value); });
|
||||
}
|
||||
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) {
|
||||
return monitor.DoExclusiveOperation(core_index, vaddr, 8,
|
||||
[&] { memory.Write64(vaddr, value); });
|
||||
[&] { Memory::Write64(vaddr, value); });
|
||||
}
|
||||
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) {
|
||||
return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&] {
|
||||
memory.Write64(vaddr + 0, value[0]);
|
||||
memory.Write64(vaddr + 8, value[1]);
|
||||
Memory::Write64(vaddr + 0, value[0]);
|
||||
Memory::Write64(vaddr + 8, value[1]);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -12,10 +12,6 @@
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
#include "core/arm/unicorn/arm_unicorn.h"
|
||||
|
||||
namespace Memory {
|
||||
class Memory;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
|
||||
class ARM_Dynarmic_Callbacks;
|
||||
@@ -62,12 +58,13 @@ private:
|
||||
ARM_Unicorn inner_unicorn;
|
||||
|
||||
std::size_t core_index;
|
||||
System& system;
|
||||
DynarmicExclusiveMonitor& exclusive_monitor;
|
||||
};
|
||||
|
||||
class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
|
||||
public:
|
||||
explicit DynarmicExclusiveMonitor(Memory::Memory& memory_, std::size_t core_count);
|
||||
explicit DynarmicExclusiveMonitor(std::size_t core_count);
|
||||
~DynarmicExclusiveMonitor() override;
|
||||
|
||||
void SetExclusive(std::size_t core_index, VAddr addr) override;
|
||||
@@ -82,7 +79,6 @@ public:
|
||||
private:
|
||||
friend class ARM_Dynarmic;
|
||||
Dynarmic::A64::ExclusiveMonitor monitor;
|
||||
Memory::Memory& memory;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -60,18 +60,17 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si
|
||||
return false;
|
||||
}
|
||||
|
||||
ARM_Unicorn::ARM_Unicorn(System& system) : ARM_Interface{system} {
|
||||
ARM_Unicorn::ARM_Unicorn(System& system) : system{system} {
|
||||
CHECKED(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc));
|
||||
|
||||
auto fpv = 3 << 20;
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_CPACR_EL1, &fpv));
|
||||
|
||||
uc_hook hook{};
|
||||
CHECKED(uc_hook_add(uc, &hook, UC_HOOK_INTR, (void*)InterruptHook, this, 0, UINT64_MAX));
|
||||
CHECKED(uc_hook_add(uc, &hook, UC_HOOK_MEM_INVALID, (void*)UnmappedMemoryHook, &system, 0,
|
||||
UINT64_MAX));
|
||||
CHECKED(uc_hook_add(uc, &hook, UC_HOOK_INTR, (void*)InterruptHook, this, 0, -1));
|
||||
CHECKED(uc_hook_add(uc, &hook, UC_HOOK_MEM_INVALID, (void*)UnmappedMemoryHook, &system, 0, -1));
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
CHECKED(uc_hook_add(uc, &hook, UC_HOOK_CODE, (void*)CodeHook, this, 0, UINT64_MAX));
|
||||
CHECKED(uc_hook_add(uc, &hook, UC_HOOK_CODE, (void*)CodeHook, this, 0, -1));
|
||||
last_bkpt_hit = false;
|
||||
}
|
||||
}
|
||||
@@ -155,10 +154,9 @@ void ARM_Unicorn::SetTPIDR_EL0(u64 value) {
|
||||
|
||||
void ARM_Unicorn::Run() {
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
ExecuteInstructions(std::max(4000000U, 0U));
|
||||
ExecuteInstructions(std::max(4000000, 0));
|
||||
} else {
|
||||
ExecuteInstructions(
|
||||
std::max(std::size_t(system.CoreTiming().GetDowncount()), std::size_t{0}));
|
||||
ExecuteInstructions(std::max(system.CoreTiming().GetDowncount(), s64{0}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,7 +166,7 @@ void ARM_Unicorn::Step() {
|
||||
|
||||
MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64));
|
||||
|
||||
void ARM_Unicorn::ExecuteInstructions(std::size_t num_instructions) {
|
||||
void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
|
||||
MICROPROFILE_SCOPE(ARM_Jit_Unicorn);
|
||||
CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions));
|
||||
system.CoreTiming().AddTicks(num_instructions);
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
void LoadContext(const ThreadContext& ctx) override;
|
||||
void PrepareReschedule() override;
|
||||
void ClearExclusiveState() override;
|
||||
void ExecuteInstructions(std::size_t num_instructions);
|
||||
void ExecuteInstructions(int num_instructions);
|
||||
void Run() override;
|
||||
void Step() override;
|
||||
void ClearInstructionCache() override;
|
||||
@@ -45,6 +45,7 @@ private:
|
||||
static void InterruptHook(uc_engine* uc, u32 int_no, void* user_data);
|
||||
|
||||
uc_engine* uc{};
|
||||
System& system;
|
||||
GDBStub::BreakpointAddress last_bkpt{};
|
||||
bool last_bkpt_hit = false;
|
||||
};
|
||||
|
||||
@@ -39,7 +39,6 @@
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/memory/cheat_engine.h"
|
||||
#include "core/perf_stats.h"
|
||||
#include "core/reporter.h"
|
||||
@@ -113,8 +112,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
||||
}
|
||||
struct System::Impl {
|
||||
explicit Impl(System& system)
|
||||
: kernel{system}, fs_controller{system}, memory{system},
|
||||
cpu_core_manager{system}, reporter{system}, applet_manager{system} {}
|
||||
: kernel{system}, fs_controller{system}, cpu_core_manager{system}, reporter{system},
|
||||
applet_manager{system} {}
|
||||
|
||||
Cpu& CurrentCpuCore() {
|
||||
return cpu_core_manager.GetCurrentCore();
|
||||
@@ -342,8 +341,7 @@ struct System::Impl {
|
||||
std::unique_ptr<VideoCore::RendererBase> renderer;
|
||||
std::unique_ptr<Tegra::GPU> gpu_core;
|
||||
std::shared_ptr<Tegra::DebugContext> debug_context;
|
||||
std::unique_ptr<Hardware::InterruptManager> interrupt_manager;
|
||||
Memory::Memory memory;
|
||||
std::unique_ptr<Core::Hardware::InterruptManager> interrupt_manager;
|
||||
CpuCoreManager cpu_core_manager;
|
||||
bool is_powered_on = false;
|
||||
bool exit_lock = false;
|
||||
@@ -500,14 +498,6 @@ const ExclusiveMonitor& System::Monitor() const {
|
||||
return impl->cpu_core_manager.GetExclusiveMonitor();
|
||||
}
|
||||
|
||||
Memory::Memory& System::Memory() {
|
||||
return impl->memory;
|
||||
}
|
||||
|
||||
const Memory::Memory& System::Memory() const {
|
||||
return impl->memory;
|
||||
}
|
||||
|
||||
Tegra::GPU& System::GPU() {
|
||||
return *impl->gpu_core;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
@@ -86,10 +85,6 @@ namespace Core::Hardware {
|
||||
class InterruptManager;
|
||||
}
|
||||
|
||||
namespace Memory {
|
||||
class Memory;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
|
||||
class ARM_Interface;
|
||||
@@ -229,12 +224,6 @@ public:
|
||||
/// Gets a constant reference to the exclusive monitor
|
||||
const ExclusiveMonitor& Monitor() const;
|
||||
|
||||
/// Gets a mutable reference to the system memory instance.
|
||||
Memory::Memory& Memory();
|
||||
|
||||
/// Gets a constant reference to the system memory instance.
|
||||
const Memory::Memory& Memory() const;
|
||||
|
||||
/// Gets a mutable reference to the GPU interface
|
||||
Tegra::GPU& GPU();
|
||||
|
||||
|
||||
@@ -66,10 +66,9 @@ Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_ba
|
||||
|
||||
Cpu::~Cpu() = default;
|
||||
|
||||
std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(
|
||||
[[maybe_unused]] Memory::Memory& memory, [[maybe_unused]] std::size_t num_cores) {
|
||||
std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
return std::make_unique<DynarmicExclusiveMonitor>(memory, num_cores);
|
||||
return std::make_unique<DynarmicExclusiveMonitor>(num_cores);
|
||||
#else
|
||||
// TODO(merry): Passthrough exclusive monitor
|
||||
return nullptr;
|
||||
@@ -96,8 +95,6 @@ void Cpu::RunLoop(bool tight_loop) {
|
||||
} else {
|
||||
arm_interface->Step();
|
||||
}
|
||||
// We are stopping a run, exclusive state must be cleared
|
||||
arm_interface->ClearExclusiveState();
|
||||
}
|
||||
core_timing.Advance();
|
||||
|
||||
|
||||
@@ -24,10 +24,6 @@ namespace Core::Timing {
|
||||
class CoreTiming;
|
||||
}
|
||||
|
||||
namespace Memory {
|
||||
class Memory;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
|
||||
class ARM_Interface;
|
||||
@@ -90,19 +86,7 @@ public:
|
||||
|
||||
void Shutdown();
|
||||
|
||||
/**
|
||||
* Creates an exclusive monitor to handle exclusive reads/writes.
|
||||
*
|
||||
* @param memory The current memory subsystem that the monitor may wish
|
||||
* to keep track of.
|
||||
*
|
||||
* @param num_cores The number of cores to assume about the CPU.
|
||||
*
|
||||
* @returns The constructed exclusive monitor instance, or nullptr if the current
|
||||
* CPU backend is unable to use an exclusive monitor.
|
||||
*/
|
||||
static std::unique_ptr<ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory,
|
||||
std::size_t num_cores);
|
||||
static std::unique_ptr<ExclusiveMonitor> MakeExclusiveMonitor(std::size_t num_cores);
|
||||
|
||||
private:
|
||||
void Reschedule();
|
||||
|
||||
@@ -17,15 +17,11 @@ namespace Core::Timing {
|
||||
|
||||
constexpr int MAX_SLICE_LENGTH = 10000;
|
||||
|
||||
std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) {
|
||||
return std::make_shared<EventType>(std::move(callback), std::move(name));
|
||||
}
|
||||
|
||||
struct CoreTiming::Event {
|
||||
s64 time;
|
||||
u64 fifo_order;
|
||||
u64 userdata;
|
||||
std::weak_ptr<EventType> type;
|
||||
const EventType* type;
|
||||
|
||||
// Sort by time, unless the times are the same, in which case sort by
|
||||
// the order added to the queue
|
||||
@@ -58,15 +54,36 @@ void CoreTiming::Initialize() {
|
||||
event_fifo_id = 0;
|
||||
|
||||
const auto empty_timed_callback = [](u64, s64) {};
|
||||
ev_lost = CreateEvent("_lost_event", empty_timed_callback);
|
||||
ev_lost = RegisterEvent("_lost_event", empty_timed_callback);
|
||||
}
|
||||
|
||||
void CoreTiming::Shutdown() {
|
||||
ClearPendingEvents();
|
||||
UnregisterAllEvents();
|
||||
}
|
||||
|
||||
void CoreTiming::ScheduleEvent(s64 cycles_into_future, const std::shared_ptr<EventType>& event_type,
|
||||
u64 userdata) {
|
||||
EventType* CoreTiming::RegisterEvent(const std::string& name, TimedCallback callback) {
|
||||
std::lock_guard guard{inner_mutex};
|
||||
// check for existing type with same name.
|
||||
// we want event type names to remain unique so that we can use them for serialization.
|
||||
ASSERT_MSG(event_types.find(name) == event_types.end(),
|
||||
"CoreTiming Event \"{}\" is already registered. Events should only be registered "
|
||||
"during Init to avoid breaking save states.",
|
||||
name.c_str());
|
||||
|
||||
auto info = event_types.emplace(name, EventType{callback, nullptr});
|
||||
EventType* event_type = &info.first->second;
|
||||
event_type->name = &info.first->first;
|
||||
return event_type;
|
||||
}
|
||||
|
||||
void CoreTiming::UnregisterAllEvents() {
|
||||
ASSERT_MSG(event_queue.empty(), "Cannot unregister events with events pending");
|
||||
event_types.clear();
|
||||
}
|
||||
|
||||
void CoreTiming::ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) {
|
||||
ASSERT(event_type != nullptr);
|
||||
std::lock_guard guard{inner_mutex};
|
||||
const s64 timeout = GetTicks() + cycles_into_future;
|
||||
|
||||
@@ -76,15 +93,13 @@ void CoreTiming::ScheduleEvent(s64 cycles_into_future, const std::shared_ptr<Eve
|
||||
}
|
||||
|
||||
event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type});
|
||||
|
||||
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
|
||||
}
|
||||
|
||||
void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata) {
|
||||
void CoreTiming::UnscheduleEvent(const EventType* event_type, u64 userdata) {
|
||||
std::lock_guard guard{inner_mutex};
|
||||
|
||||
const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
|
||||
return e.type.lock().get() == event_type.get() && e.userdata == userdata;
|
||||
return e.type == event_type && e.userdata == userdata;
|
||||
});
|
||||
|
||||
// Removing random items breaks the invariant so we have to re-establish it.
|
||||
@@ -115,12 +130,10 @@ void CoreTiming::ClearPendingEvents() {
|
||||
event_queue.clear();
|
||||
}
|
||||
|
||||
void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
|
||||
void CoreTiming::RemoveEvent(const EventType* event_type) {
|
||||
std::lock_guard guard{inner_mutex};
|
||||
|
||||
const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
|
||||
return e.type.lock().get() == event_type.get();
|
||||
});
|
||||
const auto itr = std::remove_if(event_queue.begin(), event_queue.end(),
|
||||
[&](const Event& e) { return e.type == event_type; });
|
||||
|
||||
// Removing random items breaks the invariant so we have to re-establish it.
|
||||
if (itr != event_queue.end()) {
|
||||
@@ -168,11 +181,7 @@ void CoreTiming::Advance() {
|
||||
std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
|
||||
event_queue.pop_back();
|
||||
inner_mutex.unlock();
|
||||
|
||||
if (auto event_type{evt.type.lock()}) {
|
||||
event_type->callback(evt.userdata, global_timer - evt.time);
|
||||
}
|
||||
|
||||
evt.type->callback(evt.userdata, global_timer - evt.time);
|
||||
inner_mutex.lock();
|
||||
}
|
||||
|
||||
|
||||
@@ -6,12 +6,11 @@
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
|
||||
@@ -22,13 +21,10 @@ using TimedCallback = std::function<void(u64 userdata, s64 cycles_late)>;
|
||||
|
||||
/// Contains the characteristics of a particular event.
|
||||
struct EventType {
|
||||
EventType(TimedCallback&& callback, std::string&& name)
|
||||
: callback{std::move(callback)}, name{std::move(name)} {}
|
||||
|
||||
/// The event's callback function.
|
||||
TimedCallback callback;
|
||||
/// A pointer to the name of the event.
|
||||
const std::string name;
|
||||
const std::string* name;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -61,17 +57,31 @@ public:
|
||||
/// Tears down all timing related functionality.
|
||||
void Shutdown();
|
||||
|
||||
/// Registers a core timing event with the given name and callback.
|
||||
///
|
||||
/// @param name The name of the core timing event to register.
|
||||
/// @param callback The callback to execute for the event.
|
||||
///
|
||||
/// @returns An EventType instance representing the registered event.
|
||||
///
|
||||
/// @pre The name of the event being registered must be unique among all
|
||||
/// registered events.
|
||||
///
|
||||
EventType* RegisterEvent(const std::string& name, TimedCallback callback);
|
||||
|
||||
/// Unregisters all registered events thus far. Note: not thread unsafe
|
||||
void UnregisterAllEvents();
|
||||
|
||||
/// After the first Advance, the slice lengths and the downcount will be reduced whenever an
|
||||
/// event is scheduled earlier than the current values.
|
||||
///
|
||||
/// Scheduling from a callback will not update the downcount until the Advance() completes.
|
||||
void ScheduleEvent(s64 cycles_into_future, const std::shared_ptr<EventType>& event_type,
|
||||
u64 userdata = 0);
|
||||
void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata = 0);
|
||||
|
||||
void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata);
|
||||
void UnscheduleEvent(const EventType* event_type, u64 userdata);
|
||||
|
||||
/// We only permit one event of each type in the queue at a time.
|
||||
void RemoveEvent(const std::shared_ptr<EventType>& event_type);
|
||||
void RemoveEvent(const EventType* event_type);
|
||||
|
||||
void ForceExceptionCheck(s64 cycles);
|
||||
|
||||
@@ -138,18 +148,13 @@ private:
|
||||
std::vector<Event> event_queue;
|
||||
u64 event_fifo_id = 0;
|
||||
|
||||
std::shared_ptr<EventType> ev_lost;
|
||||
// Stores each element separately as a linked list node so pointers to elements
|
||||
// remain stable regardless of rehashes/resizing.
|
||||
std::unordered_map<std::string, EventType> event_types;
|
||||
|
||||
EventType* ev_lost = nullptr;
|
||||
|
||||
std::mutex inner_mutex;
|
||||
};
|
||||
|
||||
/// Creates a core timing event with the given name and callback.
|
||||
///
|
||||
/// @param name The name of the core timing event to create.
|
||||
/// @param callback The callback to execute for the event.
|
||||
///
|
||||
/// @returns An EventType instance representing the created event.
|
||||
///
|
||||
std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback);
|
||||
|
||||
} // namespace Core::Timing
|
||||
|
||||
@@ -25,7 +25,7 @@ CpuCoreManager::~CpuCoreManager() = default;
|
||||
|
||||
void CpuCoreManager::Initialize() {
|
||||
barrier = std::make_unique<CpuBarrier>();
|
||||
exclusive_monitor = Cpu::MakeExclusiveMonitor(system.Memory(), cores.size());
|
||||
exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size());
|
||||
|
||||
for (std::size_t index = 0; index < cores.size(); ++index) {
|
||||
cores[index] = std::make_unique<Cpu>(system, *exclusive_monitor, *barrier, index);
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include "common/file_util.h"
|
||||
#include "common/hex_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/crypto/aes_util.h"
|
||||
#include "core/crypto/key_manager.h"
|
||||
@@ -379,9 +378,8 @@ std::vector<Ticket> GetTicketblob(const FileUtil::IOFile& ticket_save) {
|
||||
template <size_t size>
|
||||
static std::array<u8, size> operator^(const std::array<u8, size>& lhs,
|
||||
const std::array<u8, size>& rhs) {
|
||||
std::array<u8, size> out;
|
||||
std::transform(lhs.begin(), lhs.end(), rhs.begin(), out.begin(),
|
||||
[](u8 lhs, u8 rhs) { return u8(lhs ^ rhs); });
|
||||
std::array<u8, size> out{};
|
||||
std::transform(lhs.begin(), lhs.end(), rhs.begin(), out.begin(), std::bit_xor<>());
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -398,7 +396,7 @@ static std::array<u8, target_size> MGF1(const std::array<u8, in_size>& seed) {
|
||||
while (out.size() < target_size) {
|
||||
out.resize(out.size() + 0x20);
|
||||
seed_exp[in_size + 3] = static_cast<u8>(i);
|
||||
mbedtls_sha256_ret(seed_exp.data(), seed_exp.size(), out.data() + out.size() - 0x20, 0);
|
||||
mbedtls_sha256(seed_exp.data(), seed_exp.size(), out.data() + out.size() - 0x20, 0);
|
||||
++i;
|
||||
}
|
||||
|
||||
@@ -540,7 +538,7 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
|
||||
Key128 key = Common::HexStringToArray<16>(out[1]);
|
||||
s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key;
|
||||
} else {
|
||||
out[0] = Common::ToLower(out[0]);
|
||||
std::transform(out[0].begin(), out[0].end(), out[0].begin(), ::tolower);
|
||||
if (s128_file_id.find(out[0]) != s128_file_id.end()) {
|
||||
const auto index = s128_file_id.at(out[0]);
|
||||
Key128 key = Common::HexStringToArray<16>(out[1]);
|
||||
@@ -670,27 +668,23 @@ void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
|
||||
const std::array<u8, Size>& key) {
|
||||
const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir);
|
||||
std::string filename = "title.keys_autogenerated";
|
||||
if (category == KeyCategory::Standard) {
|
||||
if (category == KeyCategory::Standard)
|
||||
filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated";
|
||||
} else if (category == KeyCategory::Console) {
|
||||
else if (category == KeyCategory::Console)
|
||||
filename = "console.keys_autogenerated";
|
||||
}
|
||||
|
||||
const auto path = yuzu_keys_dir + DIR_SEP + filename;
|
||||
const auto add_info_text = !FileUtil::Exists(path);
|
||||
FileUtil::CreateFullPath(path);
|
||||
FileUtil::IOFile file{path, "a"};
|
||||
if (!file.IsOpen()) {
|
||||
const auto add_info_text = !FileUtil::Exists(yuzu_keys_dir + DIR_SEP + filename);
|
||||
FileUtil::CreateFullPath(yuzu_keys_dir + DIR_SEP + filename);
|
||||
std::ofstream file(yuzu_keys_dir + DIR_SEP + filename, std::ios::app);
|
||||
if (!file.is_open())
|
||||
return;
|
||||
}
|
||||
if (add_info_text) {
|
||||
file.WriteString(
|
||||
"# This file is autogenerated by Yuzu\n"
|
||||
"# It serves to store keys that were automatically generated from the normal keys\n"
|
||||
"# If you are experiencing issues involving keys, it may help to delete this file\n");
|
||||
file
|
||||
<< "# This file is autogenerated by Yuzu\n"
|
||||
<< "# It serves to store keys that were automatically generated from the normal keys\n"
|
||||
<< "# If you are experiencing issues involving keys, it may help to delete this file\n";
|
||||
}
|
||||
|
||||
file.WriteString(fmt::format("\n{} = {}", keyname, Common::HexToString(key)));
|
||||
file << fmt::format("\n{} = {}", keyname, Common::HexToString(key));
|
||||
AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, filename, category == KeyCategory::Title);
|
||||
}
|
||||
|
||||
@@ -950,10 +944,12 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Key128 rsa_oaep_kek = seed3 ^ mask0;
|
||||
if (rsa_oaep_kek == Key128{}) {
|
||||
Key128 rsa_oaep_kek{};
|
||||
std::transform(seed3.begin(), seed3.end(), mask0.begin(), rsa_oaep_kek.begin(),
|
||||
std::bit_xor<>());
|
||||
|
||||
if (rsa_oaep_kek == Key128{})
|
||||
return;
|
||||
}
|
||||
|
||||
SetKey(S128KeyType::Source, rsa_oaep_kek,
|
||||
static_cast<u64>(SourceKeyType::RSAOaepKekGeneration));
|
||||
|
||||
@@ -161,7 +161,7 @@ std::array<u8, key_size> FindKeyFromHex(const std::vector<u8>& binary,
|
||||
|
||||
std::array<u8, 0x20> temp{};
|
||||
for (size_t i = 0; i < binary.size() - key_size; ++i) {
|
||||
mbedtls_sha256_ret(binary.data() + i, key_size, temp.data(), 0);
|
||||
mbedtls_sha256(binary.data() + i, key_size, temp.data(), 0);
|
||||
|
||||
if (temp != hash)
|
||||
continue;
|
||||
@@ -189,7 +189,7 @@ static std::array<Key128, 0x20> FindEncryptedMasterKeyFromHex(const std::vector<
|
||||
AESCipher<Key128> cipher(key, Mode::ECB);
|
||||
for (size_t i = 0; i < binary.size() - 0x10; ++i) {
|
||||
cipher.Transcode(binary.data() + i, dec_temp.size(), dec_temp.data(), Op::Decrypt);
|
||||
mbedtls_sha256_ret(dec_temp.data(), dec_temp.size(), temp.data(), 0);
|
||||
mbedtls_sha256(dec_temp.data(), dec_temp.size(), temp.data(), 0);
|
||||
|
||||
for (size_t k = 0; k < out.size(); ++k) {
|
||||
if (temp == master_key_hashes[k]) {
|
||||
@@ -204,12 +204,11 @@ static std::array<Key128, 0x20> FindEncryptedMasterKeyFromHex(const std::vector<
|
||||
|
||||
FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir,
|
||||
const std::string& name) {
|
||||
const auto upper = Common::ToUpper(name);
|
||||
|
||||
auto upper = name;
|
||||
std::transform(upper.begin(), upper.end(), upper.begin(), [](u8 c) { return std::toupper(c); });
|
||||
for (const auto& fname : {name, name + ".bin", upper, upper + ".BIN"}) {
|
||||
if (dir->GetFile(fname) != nullptr) {
|
||||
if (dir->GetFile(fname) != nullptr)
|
||||
return dir->GetFile(fname);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
||||
@@ -32,28 +32,11 @@ enum class NCASectionFilesystemType : u8 {
|
||||
ROMFS = 0x3,
|
||||
};
|
||||
|
||||
struct IVFCLevel {
|
||||
u64_le offset;
|
||||
u64_le size;
|
||||
u32_le block_size;
|
||||
u32_le reserved;
|
||||
};
|
||||
static_assert(sizeof(IVFCLevel) == 0x18, "IVFCLevel has incorrect size.");
|
||||
|
||||
struct IVFCHeader {
|
||||
u32_le magic;
|
||||
u32_le magic_number;
|
||||
INSERT_UNION_PADDING_BYTES(8);
|
||||
std::array<IVFCLevel, 6> levels;
|
||||
INSERT_UNION_PADDING_BYTES(64);
|
||||
};
|
||||
static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size.");
|
||||
|
||||
struct NCASectionHeaderBlock {
|
||||
INSERT_UNION_PADDING_BYTES(3);
|
||||
INSERT_PADDING_BYTES(3);
|
||||
NCASectionFilesystemType filesystem_type;
|
||||
NCASectionCryptoType crypto_type;
|
||||
INSERT_UNION_PADDING_BYTES(3);
|
||||
INSERT_PADDING_BYTES(3);
|
||||
};
|
||||
static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size.");
|
||||
|
||||
@@ -61,7 +44,7 @@ struct NCASectionRaw {
|
||||
NCASectionHeaderBlock header;
|
||||
std::array<u8, 0x138> block_data;
|
||||
std::array<u8, 0x8> section_ctr;
|
||||
INSERT_UNION_PADDING_BYTES(0xB8);
|
||||
INSERT_PADDING_BYTES(0xB8);
|
||||
};
|
||||
static_assert(sizeof(NCASectionRaw) == 0x200, "NCASectionRaw has incorrect size.");
|
||||
|
||||
@@ -69,19 +52,19 @@ struct PFS0Superblock {
|
||||
NCASectionHeaderBlock header_block;
|
||||
std::array<u8, 0x20> hash;
|
||||
u32_le size;
|
||||
INSERT_UNION_PADDING_BYTES(4);
|
||||
INSERT_PADDING_BYTES(4);
|
||||
u64_le hash_table_offset;
|
||||
u64_le hash_table_size;
|
||||
u64_le pfs0_header_offset;
|
||||
u64_le pfs0_size;
|
||||
INSERT_UNION_PADDING_BYTES(0x1B0);
|
||||
INSERT_PADDING_BYTES(0x1B0);
|
||||
};
|
||||
static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size.");
|
||||
|
||||
struct RomFSSuperblock {
|
||||
NCASectionHeaderBlock header_block;
|
||||
IVFCHeader ivfc;
|
||||
INSERT_UNION_PADDING_BYTES(0x118);
|
||||
INSERT_PADDING_BYTES(0x118);
|
||||
};
|
||||
static_assert(sizeof(RomFSSuperblock) == 0x200, "RomFSSuperblock has incorrect size.");
|
||||
|
||||
@@ -89,24 +72,24 @@ struct BKTRHeader {
|
||||
u64_le offset;
|
||||
u64_le size;
|
||||
u32_le magic;
|
||||
INSERT_UNION_PADDING_BYTES(0x4);
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
u32_le number_entries;
|
||||
INSERT_UNION_PADDING_BYTES(0x4);
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
};
|
||||
static_assert(sizeof(BKTRHeader) == 0x20, "BKTRHeader has incorrect size.");
|
||||
|
||||
struct BKTRSuperblock {
|
||||
NCASectionHeaderBlock header_block;
|
||||
IVFCHeader ivfc;
|
||||
INSERT_UNION_PADDING_BYTES(0x18);
|
||||
INSERT_PADDING_BYTES(0x18);
|
||||
BKTRHeader relocation;
|
||||
BKTRHeader subsection;
|
||||
INSERT_UNION_PADDING_BYTES(0xC0);
|
||||
INSERT_PADDING_BYTES(0xC0);
|
||||
};
|
||||
static_assert(sizeof(BKTRSuperblock) == 0x200, "BKTRSuperblock has incorrect size.");
|
||||
|
||||
union NCASectionHeader {
|
||||
NCASectionRaw raw{};
|
||||
NCASectionRaw raw;
|
||||
PFS0Superblock pfs0;
|
||||
RomFSSuperblock romfs;
|
||||
BKTRSuperblock bktr;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
enum class EntryType : u8 {
|
||||
enum EntryType : u8 {
|
||||
Directory = 0,
|
||||
File = 1,
|
||||
};
|
||||
|
||||
@@ -147,7 +147,7 @@ std::vector<u32> KIP::GetKernelCapabilities() const {
|
||||
}
|
||||
|
||||
s32 KIP::GetMainThreadPriority() const {
|
||||
return static_cast<s32>(header.main_thread_priority);
|
||||
return header.main_thread_priority;
|
||||
}
|
||||
|
||||
u32 KIP::GetMainThreadStackSize() const {
|
||||
|
||||
@@ -76,7 +76,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
const auto update_disabled =
|
||||
std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
|
||||
std::find(disabled.begin(), disabled.end(), "Update") != disabled.end();
|
||||
|
||||
// Game Updates
|
||||
const auto update_tid = GetUpdateTitleID(title_id);
|
||||
@@ -127,7 +127,7 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
|
||||
std::vector<VirtualFile> out;
|
||||
out.reserve(patch_dirs.size());
|
||||
for (const auto& subdir : patch_dirs) {
|
||||
if (std::find(disabled.cbegin(), disabled.cend(), subdir->GetName()) != disabled.cend())
|
||||
if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end())
|
||||
continue;
|
||||
|
||||
auto exefs_dir = subdir->GetSubdirectory("exefs");
|
||||
@@ -284,17 +284,12 @@ std::vector<Memory::CheatEntry> PatchManager::CreateCheatList(
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
auto patch_dirs = load_dir->GetSubdirectories();
|
||||
std::sort(patch_dirs.begin(), patch_dirs.end(),
|
||||
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
|
||||
|
||||
std::vector<Memory::CheatEntry> out;
|
||||
for (const auto& subdir : patch_dirs) {
|
||||
if (std::find(disabled.cbegin(), disabled.cend(), subdir->GetName()) != disabled.cend()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto cheats_dir = subdir->GetSubdirectory("cheats");
|
||||
if (cheats_dir != nullptr) {
|
||||
auto res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, true);
|
||||
@@ -336,9 +331,8 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
|
||||
layers.reserve(patch_dirs.size() + 1);
|
||||
layers_ext.reserve(patch_dirs.size() + 1);
|
||||
for (const auto& subdir : patch_dirs) {
|
||||
if (std::find(disabled.cbegin(), disabled.cend(), subdir->GetName()) != disabled.cend()) {
|
||||
if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end())
|
||||
continue;
|
||||
}
|
||||
|
||||
auto romfs_dir = subdir->GetSubdirectory("romfs");
|
||||
if (romfs_dir != nullptr)
|
||||
@@ -387,7 +381,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
|
||||
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
const auto update_disabled =
|
||||
std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
|
||||
std::find(disabled.begin(), disabled.end(), "Update") != disabled.end();
|
||||
|
||||
if (!update_disabled && update != nullptr) {
|
||||
const auto new_nca = std::make_shared<NCA>(update, romfs, ivfc_offset);
|
||||
@@ -437,7 +431,7 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
|
||||
auto [nacp, discard_icon_file] = update.GetControlMetadata();
|
||||
|
||||
const auto update_disabled =
|
||||
std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
|
||||
std::find(disabled.begin(), disabled.end(), "Update") != disabled.end();
|
||||
const auto update_label = update_disabled ? "[D] Update" : "Update";
|
||||
|
||||
if (nacp != nullptr) {
|
||||
|
||||
@@ -52,14 +52,14 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
|
||||
}
|
||||
|
||||
void ProgramMetadata::LoadManual(bool is_64_bit, ProgramAddressSpaceType address_space,
|
||||
s32 main_thread_prio, u32 main_thread_core,
|
||||
u8 main_thread_prio, u8 main_thread_core,
|
||||
u32 main_thread_stack_size, u64 title_id,
|
||||
u64 filesystem_permissions,
|
||||
KernelCapabilityDescriptors capabilities) {
|
||||
npdm_header.has_64_bit_instructions.Assign(is_64_bit);
|
||||
npdm_header.address_space_type.Assign(address_space);
|
||||
npdm_header.main_thread_priority = static_cast<u8>(main_thread_prio);
|
||||
npdm_header.main_thread_cpu = static_cast<u8>(main_thread_core);
|
||||
npdm_header.main_thread_priority = main_thread_prio;
|
||||
npdm_header.main_thread_cpu = main_thread_core;
|
||||
npdm_header.main_stack_size = main_thread_stack_size;
|
||||
aci_header.title_id = title_id;
|
||||
aci_file_access.permissions = filesystem_permissions;
|
||||
|
||||
@@ -47,8 +47,8 @@ public:
|
||||
Loader::ResultStatus Load(VirtualFile file);
|
||||
|
||||
// Load from parameters instead of NPDM file, used for KIP
|
||||
void LoadManual(bool is_64_bit, ProgramAddressSpaceType address_space, s32 main_thread_prio,
|
||||
u32 main_thread_core, u32 main_thread_stack_size, u64 title_id,
|
||||
void LoadManual(bool is_64_bit, ProgramAddressSpaceType address_space, u8 main_thread_prio,
|
||||
u8 main_thread_core, u32 main_thread_stack_size, u64 title_id,
|
||||
u64 filesystem_permissions, KernelCapabilityDescriptors capabilities);
|
||||
|
||||
bool Is64BitProgram() const;
|
||||
|
||||
@@ -62,7 +62,7 @@ static std::string GetRelativePathFromNcaID(const std::array<u8, 16>& nca_id, bo
|
||||
Common::HexToString(nca_id, second_hex_upper));
|
||||
|
||||
Core::Crypto::SHA256Hash hash{};
|
||||
mbedtls_sha256_ret(nca_id.data(), nca_id.size(), hash.data(), 0);
|
||||
mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0);
|
||||
return fmt::format(cnmt_suffix ? "/000000{:02X}/{}.cnmt.nca" : "/000000{:02X}/{}.nca", hash[0],
|
||||
Common::HexToString(nca_id, second_hex_upper));
|
||||
}
|
||||
@@ -141,7 +141,7 @@ bool PlaceholderCache::Create(const NcaID& id, u64 size) const {
|
||||
}
|
||||
|
||||
Core::Crypto::SHA256Hash hash{};
|
||||
mbedtls_sha256_ret(id.data(), id.size(), hash.data(), 0);
|
||||
mbedtls_sha256(id.data(), id.size(), hash.data(), 0);
|
||||
const auto dirname = fmt::format("000000{:02X}", hash[0]);
|
||||
|
||||
const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
|
||||
@@ -165,7 +165,7 @@ bool PlaceholderCache::Delete(const NcaID& id) const {
|
||||
}
|
||||
|
||||
Core::Crypto::SHA256Hash hash{};
|
||||
mbedtls_sha256_ret(id.data(), id.size(), hash.data(), 0);
|
||||
mbedtls_sha256(id.data(), id.size(), hash.data(), 0);
|
||||
const auto dirname = fmt::format("000000{:02X}", hash[0]);
|
||||
|
||||
const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
|
||||
@@ -603,7 +603,7 @@ InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type,
|
||||
OptionalHeader opt_header{0, 0};
|
||||
ContentRecord c_rec{{}, {}, {}, GetCRTypeFromNCAType(nca.GetType()), {}};
|
||||
const auto& data = nca.GetBaseFile()->ReadBytes(0x100000);
|
||||
mbedtls_sha256_ret(data.data(), data.size(), c_rec.hash.data(), 0);
|
||||
mbedtls_sha256(data.data(), data.size(), c_rec.hash.data(), 0);
|
||||
memcpy(&c_rec.nca_id, &c_rec.hash, 16);
|
||||
const CNMT new_cnmt(header, opt_header, {c_rec}, {});
|
||||
if (!RawInstallYuzuMeta(new_cnmt))
|
||||
@@ -626,7 +626,7 @@ InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFuncti
|
||||
id = *override_id;
|
||||
} else {
|
||||
const auto& data = in->ReadBytes(0x100000);
|
||||
mbedtls_sha256_ret(data.data(), data.size(), hash.data(), 0);
|
||||
mbedtls_sha256(data.data(), data.size(), hash.data(), 0);
|
||||
memcpy(id.data(), hash.data(), 16);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/fsmitm_romfsbuild.h"
|
||||
@@ -14,7 +12,7 @@
|
||||
#include "core/file_sys/vfs_vector.h"
|
||||
|
||||
namespace FileSys {
|
||||
namespace {
|
||||
|
||||
constexpr u32 ROMFS_ENTRY_EMPTY = 0xFFFFFFFF;
|
||||
|
||||
struct TableLocation {
|
||||
@@ -53,7 +51,7 @@ struct FileEntry {
|
||||
static_assert(sizeof(FileEntry) == 0x20, "FileEntry has incorrect size.");
|
||||
|
||||
template <typename Entry>
|
||||
std::pair<Entry, std::string> GetEntry(const VirtualFile& file, std::size_t offset) {
|
||||
static std::pair<Entry, std::string> GetEntry(const VirtualFile& file, std::size_t offset) {
|
||||
Entry entry{};
|
||||
if (file->ReadObject(&entry, offset) != sizeof(Entry))
|
||||
return {};
|
||||
@@ -101,7 +99,6 @@ void ProcessDirectory(VirtualFile file, std::size_t dir_offset, std::size_t file
|
||||
this_dir_offset = entry.first.sibling;
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
|
||||
RomFSHeader header{};
|
||||
|
||||
@@ -5,10 +5,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
struct RomFSHeader;
|
||||
|
||||
struct IVFCLevel {
|
||||
u64_le offset;
|
||||
u64_le size;
|
||||
u32_le block_size;
|
||||
u32_le reserved;
|
||||
};
|
||||
static_assert(sizeof(IVFCLevel) == 0x18, "IVFCLevel has incorrect size.");
|
||||
|
||||
struct IVFCHeader {
|
||||
u32_le magic;
|
||||
u32_le magic_number;
|
||||
INSERT_PADDING_BYTES(8);
|
||||
std::array<IVFCLevel, 6> levels;
|
||||
INSERT_PADDING_BYTES(64);
|
||||
};
|
||||
static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size.");
|
||||
|
||||
enum class RomFSExtractionType {
|
||||
Full, // Includes data directory
|
||||
Truncated, // Traverses into data directory
|
||||
|
||||
@@ -71,12 +71,12 @@ ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage,
|
||||
|
||||
if (res == nullptr) {
|
||||
// TODO(DarkLordZach): Find the right error code to use here
|
||||
return RESULT_UNKNOWN;
|
||||
return ResultCode(-1);
|
||||
}
|
||||
const auto romfs = res->GetRomFS();
|
||||
if (romfs == nullptr) {
|
||||
// TODO(DarkLordZach): Find the right error code to use here
|
||||
return RESULT_UNKNOWN;
|
||||
return ResultCode(-1);
|
||||
}
|
||||
return MakeResult<VirtualFile>(romfs);
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space,
|
||||
// Return an error if the save data doesn't actually exist.
|
||||
if (out == nullptr) {
|
||||
// TODO(DarkLordZach): Find out correct error code.
|
||||
return RESULT_UNKNOWN;
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
return MakeResult<VirtualDir>(std::move(out));
|
||||
@@ -111,7 +111,7 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space,
|
||||
// Return an error if the save data doesn't actually exist.
|
||||
if (out == nullptr) {
|
||||
// TODO(Subv): Find out correct error code.
|
||||
return RESULT_UNKNOWN;
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
return MakeResult<VirtualDir>(std::move(out));
|
||||
|
||||
@@ -27,7 +27,7 @@ VirtualDir ExtractZIP(VirtualFile file) {
|
||||
|
||||
std::shared_ptr<VectorVfsDirectory> out = std::make_shared<VectorVfsDirectory>();
|
||||
|
||||
const auto num_entries = static_cast<std::size_t>(zip_get_num_entries(zip.get(), 0));
|
||||
const auto num_entries = zip_get_num_entries(zip.get(), 0);
|
||||
|
||||
zip_stat_t stat{};
|
||||
zip_stat_init(&stat);
|
||||
|
||||
202
src/core/file_sys/vfs_ro_layer.cpp
Normal file
202
src/core/file_sys/vfs_ro_layer.cpp
Normal file
@@ -0,0 +1,202 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/file_sys/vfs_ro_layer.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
ReadOnlyVfsFileLayer::ReadOnlyVfsFileLayer(VirtualFile base) : base(std::move(base)) {}
|
||||
|
||||
ReadOnlyVfsFileLayer::~ReadOnlyVfsFileLayer() = default;
|
||||
|
||||
std::string ReadOnlyVfsFileLayer::GetName() const {
|
||||
return base->GetName();
|
||||
}
|
||||
|
||||
std::size_t ReadOnlyVfsFileLayer::GetSize() const {
|
||||
return base->GetSize();
|
||||
}
|
||||
|
||||
bool ReadOnlyVfsFileLayer::Resize(std::size_t new_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsDirectory> ReadOnlyVfsFileLayer::GetContainingDirectory() const {
|
||||
// Make containing read-only to prevent escaping the layer by getting containing and then
|
||||
// getting this file again.
|
||||
return std::make_shared<ReadOnlyVfsDirectoryLayer>(base->GetContainingDirectory());
|
||||
}
|
||||
|
||||
bool ReadOnlyVfsFileLayer::IsWritable() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ReadOnlyVfsFileLayer::IsReadable() const {
|
||||
return base->IsReadable();
|
||||
}
|
||||
|
||||
std::size_t ReadOnlyVfsFileLayer::Read(u8* data, std::size_t length, std::size_t offset) const {
|
||||
return base->Read(data, length, offset);
|
||||
}
|
||||
|
||||
std::size_t ReadOnlyVfsFileLayer::Write(const u8* data, std::size_t length, std::size_t offset) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ReadOnlyVfsFileLayer::Rename(std::string_view name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string ReadOnlyVfsFileLayer::GetFullPath() const {
|
||||
return base->GetFullPath();
|
||||
}
|
||||
|
||||
ReadOnlyVfsDirectoryLayer::ReadOnlyVfsDirectoryLayer(VirtualDir base) : base(std::move(base)) {}
|
||||
|
||||
ReadOnlyVfsDirectoryLayer::~ReadOnlyVfsDirectoryLayer() = default;
|
||||
|
||||
std::vector<std::shared_ptr<VfsFile>> ReadOnlyVfsDirectoryLayer::GetFiles() const {
|
||||
std::vector<VirtualFile> out;
|
||||
const auto in = base->GetFiles();
|
||||
std::transform(in.begin(), in.end(), std::back_inserter(out),
|
||||
[](const VirtualFile& i) { return std::make_shared<ReadOnlyVfsFileLayer>(i); });
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<VfsDirectory>> ReadOnlyVfsDirectoryLayer::GetSubdirectories() const {
|
||||
std::vector<VirtualDir> out;
|
||||
const auto in = base->GetSubdirectories();
|
||||
std::transform(in.begin(), in.end(), std::back_inserter(out), [](const VirtualDir& i) {
|
||||
return std::make_shared<ReadOnlyVfsDirectoryLayer>(i);
|
||||
});
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string ReadOnlyVfsDirectoryLayer::GetName() const {
|
||||
return base->GetName();
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsDirectory> ReadOnlyVfsDirectoryLayer::GetParentDirectory() const {
|
||||
auto out = base->GetParentDirectory();
|
||||
if (out == nullptr)
|
||||
return nullptr;
|
||||
return std::make_shared<ReadOnlyVfsDirectoryLayer>(out);
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsFile> ReadOnlyVfsDirectoryLayer::GetFileRelative(std::string_view path) const {
|
||||
auto out = base->GetFileRelative(path);
|
||||
if (out == nullptr)
|
||||
return nullptr;
|
||||
return std::make_shared<ReadOnlyVfsFileLayer>(out);
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsFile> ReadOnlyVfsDirectoryLayer::GetFileAbsolute(std::string_view path) const {
|
||||
auto out = base->GetFileAbsolute(path);
|
||||
if (out == nullptr)
|
||||
return nullptr;
|
||||
return std::make_shared<ReadOnlyVfsFileLayer>(out);
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsDirectory> ReadOnlyVfsDirectoryLayer::GetDirectoryRelative(
|
||||
std::string_view path) const {
|
||||
auto out = base->GetDirectoryRelative(path);
|
||||
if (out == nullptr)
|
||||
return nullptr;
|
||||
return std::make_shared<ReadOnlyVfsDirectoryLayer>(out);
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsDirectory> ReadOnlyVfsDirectoryLayer::GetDirectoryAbsolute(
|
||||
std::string_view path) const {
|
||||
auto out = base->GetDirectoryAbsolute(path);
|
||||
if (out == nullptr)
|
||||
return nullptr;
|
||||
return std::make_shared<ReadOnlyVfsDirectoryLayer>(out);
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsFile> ReadOnlyVfsDirectoryLayer::GetFile(std::string_view name) const {
|
||||
auto out = base->GetFile(name);
|
||||
if (out == nullptr)
|
||||
return nullptr;
|
||||
return std::make_shared<ReadOnlyVfsFileLayer>(out);
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsDirectory> ReadOnlyVfsDirectoryLayer::GetSubdirectory(
|
||||
std::string_view name) const {
|
||||
auto out = base->GetSubdirectory(name);
|
||||
if (out == nullptr)
|
||||
return nullptr;
|
||||
return std::make_shared<ReadOnlyVfsDirectoryLayer>(out);
|
||||
}
|
||||
|
||||
bool ReadOnlyVfsDirectoryLayer::IsRoot() const {
|
||||
return base->IsRoot();
|
||||
}
|
||||
|
||||
std::size_t ReadOnlyVfsDirectoryLayer::GetSize() const {
|
||||
return base->GetSize();
|
||||
}
|
||||
|
||||
bool ReadOnlyVfsDirectoryLayer::Copy(std::string_view src, std::string_view dest) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string ReadOnlyVfsDirectoryLayer::GetFullPath() const {
|
||||
return base->GetFullPath();
|
||||
}
|
||||
|
||||
bool ReadOnlyVfsDirectoryLayer::IsWritable() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ReadOnlyVfsDirectoryLayer::IsReadable() const {
|
||||
return base->IsReadable();
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsDirectory> ReadOnlyVfsDirectoryLayer::CreateSubdirectory(std::string_view name) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsFile> ReadOnlyVfsDirectoryLayer::CreateFile(std::string_view name) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsFile> ReadOnlyVfsDirectoryLayer::CreateFileAbsolute(std::string_view path) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsFile> ReadOnlyVfsDirectoryLayer::CreateFileRelative(std::string_view path) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsDirectory> ReadOnlyVfsDirectoryLayer::CreateDirectoryAbsolute(
|
||||
std::string_view path) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsDirectory> ReadOnlyVfsDirectoryLayer::CreateDirectoryRelative(
|
||||
std::string_view path) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ReadOnlyVfsDirectoryLayer::DeleteSubdirectory(std::string_view name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ReadOnlyVfsDirectoryLayer::DeleteSubdirectoryRecursive(std::string_view name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ReadOnlyVfsDirectoryLayer::CleanSubdirectoryRecursive(std::string_view name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ReadOnlyVfsDirectoryLayer::DeleteFile(std::string_view name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ReadOnlyVfsDirectoryLayer::Rename(std::string_view name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
73
src/core/file_sys/vfs_ro_layer.h
Normal file
73
src/core/file_sys/vfs_ro_layer.h
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
// Class that wraps a VfsFile making it read-only
|
||||
class ReadOnlyVfsFileLayer : public VfsFile {
|
||||
public:
|
||||
explicit ReadOnlyVfsFileLayer(VirtualFile base);
|
||||
~ReadOnlyVfsFileLayer() override;
|
||||
|
||||
std::string GetName() const override;
|
||||
std::size_t GetSize() const override;
|
||||
bool Resize(std::size_t new_size) override;
|
||||
std::shared_ptr<VfsDirectory> GetContainingDirectory() const override;
|
||||
bool IsWritable() const override;
|
||||
bool IsReadable() const override;
|
||||
std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
|
||||
std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override;
|
||||
bool Rename(std::string_view name) override;
|
||||
|
||||
std::string GetFullPath() const override;
|
||||
|
||||
private:
|
||||
VirtualFile base;
|
||||
};
|
||||
|
||||
// Class that wraps a VfsDirectory making it and its children read only.
|
||||
class ReadOnlyVfsDirectoryLayer : public ReadOnlyVfsDirectory {
|
||||
public:
|
||||
explicit ReadOnlyVfsDirectoryLayer(VirtualDir base);
|
||||
~ReadOnlyVfsDirectoryLayer() override;
|
||||
|
||||
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
|
||||
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
|
||||
std::string GetName() const override;
|
||||
std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
|
||||
|
||||
std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const override;
|
||||
std::shared_ptr<VfsFile> GetFileAbsolute(std::string_view path) const override;
|
||||
std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const override;
|
||||
std::shared_ptr<VfsDirectory> GetDirectoryAbsolute(std::string_view path) const override;
|
||||
std::shared_ptr<VfsFile> GetFile(std::string_view name) const override;
|
||||
std::shared_ptr<VfsDirectory> GetSubdirectory(std::string_view name) const override;
|
||||
bool IsRoot() const override;
|
||||
std::size_t GetSize() const override;
|
||||
bool Copy(std::string_view src, std::string_view dest) override;
|
||||
std::string GetFullPath() const override;
|
||||
bool IsWritable() const override;
|
||||
bool IsReadable() const override;
|
||||
std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) override;
|
||||
std::shared_ptr<VfsFile> CreateFile(std::string_view name) override;
|
||||
std::shared_ptr<VfsFile> CreateFileAbsolute(std::string_view path) override;
|
||||
std::shared_ptr<VfsFile> CreateFileRelative(std::string_view path) override;
|
||||
std::shared_ptr<VfsDirectory> CreateDirectoryAbsolute(std::string_view path) override;
|
||||
std::shared_ptr<VfsDirectory> CreateDirectoryRelative(std::string_view path) override;
|
||||
bool DeleteSubdirectory(std::string_view name) override;
|
||||
bool DeleteSubdirectoryRecursive(std::string_view name) override;
|
||||
bool CleanSubdirectoryRecursive(std::string_view name) override;
|
||||
bool DeleteFile(std::string_view name) override;
|
||||
bool Rename(std::string_view name) override;
|
||||
|
||||
private:
|
||||
VirtualDir base;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
@@ -7,13 +7,12 @@
|
||||
#include <cstring>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
#include <mbedtls/md.h>
|
||||
#include <mbedtls/sha256.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/hex_util.h"
|
||||
#include "common/string_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/crypto/aes_util.h"
|
||||
#include "core/crypto/xts_encryption_layer.h"
|
||||
#include "core/file_sys/partition_filesystem.h"
|
||||
@@ -54,15 +53,18 @@ NAX::NAX(VirtualFile file_) : header(std::make_unique<NAXHeader>()), file(std::m
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string two_dir = Common::ToUpper(match[1]);
|
||||
const std::string nca_id = Common::ToLower(match[2]);
|
||||
std::string two_dir = match[1];
|
||||
std::string nca_id = match[2];
|
||||
std::transform(two_dir.begin(), two_dir.end(), two_dir.begin(), ::toupper);
|
||||
std::transform(nca_id.begin(), nca_id.end(), nca_id.begin(), ::tolower);
|
||||
|
||||
status = Parse(fmt::format("/registered/{}/{}.nca", two_dir, nca_id));
|
||||
}
|
||||
|
||||
NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id)
|
||||
: header(std::make_unique<NAXHeader>()), file(std::move(file_)) {
|
||||
Core::Crypto::SHA256Hash hash{};
|
||||
mbedtls_sha256_ret(nca_id.data(), nca_id.size(), hash.data(), 0);
|
||||
mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0);
|
||||
status = Parse(fmt::format("/registered/000000{:02X}/{}.nca", hash[0],
|
||||
Common::HexToString(nca_id, false)));
|
||||
}
|
||||
@@ -91,7 +93,8 @@ Loader::ResultStatus NAX::Parse(std::string_view path) {
|
||||
std::size_t i = 0;
|
||||
for (; i < sd_keys.size(); ++i) {
|
||||
std::array<Core::Crypto::Key128, 2> nax_keys{};
|
||||
if (!CalculateHMAC256(nax_keys.data(), sd_keys[i].data(), 0x10, path.data(), path.size())) {
|
||||
if (!CalculateHMAC256(nax_keys.data(), sd_keys[i].data(), 0x10, std::string(path).c_str(),
|
||||
path.size())) {
|
||||
return Loader::ResultStatus::ErrorNAXKeyHMACFailed;
|
||||
}
|
||||
|
||||
|
||||
@@ -468,8 +468,7 @@ static u8 ReadByte() {
|
||||
|
||||
/// Calculate the checksum of the current command buffer.
|
||||
static u8 CalculateChecksum(const u8* buffer, std::size_t length) {
|
||||
return static_cast<u8>(std::accumulate(buffer, buffer + length, u8{0},
|
||||
[](u8 lhs, u8 rhs) { return u8(lhs + rhs); }));
|
||||
return static_cast<u8>(std::accumulate(buffer, buffer + length, 0, std::plus<u8>()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -508,9 +507,8 @@ static void RemoveBreakpoint(BreakpointType type, VAddr addr) {
|
||||
bp->second.len, bp->second.addr, static_cast<int>(type));
|
||||
|
||||
if (type == BreakpointType::Execute) {
|
||||
auto& system = Core::System::GetInstance();
|
||||
system.Memory().WriteBlock(bp->second.addr, bp->second.inst.data(), bp->second.inst.size());
|
||||
system.InvalidateCpuInstructionCaches();
|
||||
Memory::WriteBlock(bp->second.addr, bp->second.inst.data(), bp->second.inst.size());
|
||||
Core::System::GetInstance().InvalidateCpuInstructionCaches();
|
||||
}
|
||||
p.erase(addr);
|
||||
}
|
||||
@@ -970,13 +968,12 @@ static void ReadMemory() {
|
||||
SendReply("E01");
|
||||
}
|
||||
|
||||
auto& memory = Core::System::GetInstance().Memory();
|
||||
if (!memory.IsValidVirtualAddress(addr)) {
|
||||
if (!Memory::IsValidVirtualAddress(addr)) {
|
||||
return SendReply("E00");
|
||||
}
|
||||
|
||||
std::vector<u8> data(len);
|
||||
memory.ReadBlock(addr, data.data(), len);
|
||||
Memory::ReadBlock(addr, data.data(), len);
|
||||
|
||||
MemToGdbHex(reply, data.data(), len);
|
||||
reply[len * 2] = '\0';
|
||||
@@ -986,23 +983,22 @@ static void ReadMemory() {
|
||||
/// Modify location in memory with data received from the gdb client.
|
||||
static void WriteMemory() {
|
||||
auto start_offset = command_buffer + 1;
|
||||
const auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
|
||||
const VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
|
||||
auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
|
||||
VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
|
||||
|
||||
start_offset = addr_pos + 1;
|
||||
const auto len_pos = std::find(start_offset, command_buffer + command_length, ':');
|
||||
const u64 len = HexToLong(start_offset, static_cast<u64>(len_pos - start_offset));
|
||||
auto len_pos = std::find(start_offset, command_buffer + command_length, ':');
|
||||
u64 len = HexToLong(start_offset, static_cast<u64>(len_pos - start_offset));
|
||||
|
||||
auto& system = Core::System::GetInstance();
|
||||
auto& memory = system.Memory();
|
||||
if (!memory.IsValidVirtualAddress(addr)) {
|
||||
if (!Memory::IsValidVirtualAddress(addr)) {
|
||||
return SendReply("E00");
|
||||
}
|
||||
|
||||
std::vector<u8> data(len);
|
||||
|
||||
GdbHexToMem(data.data(), len_pos + 1, len);
|
||||
memory.WriteBlock(addr, data.data(), len);
|
||||
system.InvalidateCpuInstructionCaches();
|
||||
Memory::WriteBlock(addr, data.data(), len);
|
||||
Core::System::GetInstance().InvalidateCpuInstructionCaches();
|
||||
SendReply("OK");
|
||||
}
|
||||
|
||||
@@ -1058,15 +1054,12 @@ static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) {
|
||||
breakpoint.active = true;
|
||||
breakpoint.addr = addr;
|
||||
breakpoint.len = len;
|
||||
|
||||
auto& system = Core::System::GetInstance();
|
||||
auto& memory = system.Memory();
|
||||
memory.ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size());
|
||||
Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size());
|
||||
|
||||
static constexpr std::array<u8, 4> btrap{0x00, 0x7d, 0x20, 0xd4};
|
||||
if (type == BreakpointType::Execute) {
|
||||
memory.WriteBlock(addr, btrap.data(), btrap.size());
|
||||
system.InvalidateCpuInstructionCaches();
|
||||
Memory::WriteBlock(addr, btrap.data(), btrap.size());
|
||||
Core::System::GetInstance().InvalidateCpuInstructionCaches();
|
||||
}
|
||||
p.insert({addr, breakpoint});
|
||||
|
||||
|
||||
@@ -11,12 +11,13 @@
|
||||
namespace Core::Hardware {
|
||||
|
||||
InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) {
|
||||
gpu_interrupt_event = Core::Timing::CreateEvent("GPUInterrupt", [this](u64 message, s64) {
|
||||
auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv");
|
||||
const u32 syncpt = static_cast<u32>(message >> 32);
|
||||
const u32 value = static_cast<u32>(message);
|
||||
nvdrv->SignalGPUInterruptSyncpt(syncpt, value);
|
||||
});
|
||||
gpu_interrupt_event =
|
||||
system.CoreTiming().RegisterEvent("GPUInterrupt", [this](u64 message, s64) {
|
||||
auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv");
|
||||
const u32 syncpt = static_cast<u32>(message >> 32);
|
||||
const u32 value = static_cast<u32>(message);
|
||||
nvdrv->SignalGPUInterruptSyncpt(syncpt, value);
|
||||
});
|
||||
}
|
||||
|
||||
InterruptManager::~InterruptManager() = default;
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core {
|
||||
@@ -27,7 +25,7 @@ public:
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
std::shared_ptr<Core::Timing::EventType> gpu_interrupt_event;
|
||||
Core::Timing::EventType* gpu_interrupt_event{};
|
||||
};
|
||||
|
||||
} // namespace Core::Hardware
|
||||
|
||||
@@ -160,7 +160,7 @@ struct DomainMessageHeader {
|
||||
// Used when responding to an IPC request, Server -> Client.
|
||||
struct {
|
||||
u32_le num_objects;
|
||||
INSERT_UNION_PADDING_WORDS(3);
|
||||
INSERT_PADDING_WORDS(3);
|
||||
};
|
||||
|
||||
// Used when performing an IPC request, Client -> Server.
|
||||
@@ -171,10 +171,8 @@ struct DomainMessageHeader {
|
||||
BitField<16, 16, u32> size;
|
||||
};
|
||||
u32_le object_id;
|
||||
INSERT_UNION_PADDING_WORDS(2);
|
||||
INSERT_PADDING_WORDS(2);
|
||||
};
|
||||
|
||||
std::array<u32, 4> raw{};
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(DomainMessageHeader) == 16, "DomainMessageHeader size is incorrect");
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/server_session.h"
|
||||
#include "core/hle/kernel/session.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace IPC {
|
||||
@@ -140,9 +139,10 @@ public:
|
||||
context->AddDomainObject(std::move(iface));
|
||||
} else {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
auto [client, server] = Kernel::Session::Create(kernel, iface->GetServiceName());
|
||||
auto [server, client] =
|
||||
Kernel::ServerSession::CreateSessionPair(kernel, iface->GetServiceName());
|
||||
iface->ClientConnected(server);
|
||||
context->AddMoveObject(std::move(client));
|
||||
iface->ClientConnected(std::move(server));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,10 +203,10 @@ public:
|
||||
void PushRaw(const T& value);
|
||||
|
||||
template <typename... O>
|
||||
void PushMoveObjects(std::shared_ptr<O>... pointers);
|
||||
void PushMoveObjects(Kernel::SharedPtr<O>... pointers);
|
||||
|
||||
template <typename... O>
|
||||
void PushCopyObjects(std::shared_ptr<O>... pointers);
|
||||
void PushCopyObjects(Kernel::SharedPtr<O>... pointers);
|
||||
|
||||
private:
|
||||
u32 normal_params_size{};
|
||||
@@ -298,7 +298,7 @@ void ResponseBuilder::Push(const First& first_value, const Other&... other_value
|
||||
}
|
||||
|
||||
template <typename... O>
|
||||
inline void ResponseBuilder::PushCopyObjects(std::shared_ptr<O>... pointers) {
|
||||
inline void ResponseBuilder::PushCopyObjects(Kernel::SharedPtr<O>... pointers) {
|
||||
auto objects = {pointers...};
|
||||
for (auto& object : objects) {
|
||||
context->AddCopyObject(std::move(object));
|
||||
@@ -306,7 +306,7 @@ inline void ResponseBuilder::PushCopyObjects(std::shared_ptr<O>... pointers) {
|
||||
}
|
||||
|
||||
template <typename... O>
|
||||
inline void ResponseBuilder::PushMoveObjects(std::shared_ptr<O>... pointers) {
|
||||
inline void ResponseBuilder::PushMoveObjects(Kernel::SharedPtr<O>... pointers) {
|
||||
auto objects = {pointers...};
|
||||
for (auto& object : objects) {
|
||||
context->AddMoveObject(std::move(object));
|
||||
@@ -357,10 +357,10 @@ public:
|
||||
T PopRaw();
|
||||
|
||||
template <typename T>
|
||||
std::shared_ptr<T> GetMoveObject(std::size_t index);
|
||||
Kernel::SharedPtr<T> GetMoveObject(std::size_t index);
|
||||
|
||||
template <typename T>
|
||||
std::shared_ptr<T> GetCopyObject(std::size_t index);
|
||||
Kernel::SharedPtr<T> GetCopyObject(std::size_t index);
|
||||
|
||||
template <class T>
|
||||
std::shared_ptr<T> PopIpcInterface() {
|
||||
@@ -465,12 +465,12 @@ void RequestParser::Pop(First& first_value, Other&... other_values) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::shared_ptr<T> RequestParser::GetMoveObject(std::size_t index) {
|
||||
Kernel::SharedPtr<T> RequestParser::GetMoveObject(std::size_t index) {
|
||||
return context->GetMoveObject<T>(index);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::shared_ptr<T> RequestParser::GetCopyObject(std::size_t index) {
|
||||
Kernel::SharedPtr<T> RequestParser::GetCopyObject(std::size_t index) {
|
||||
return context->GetCopyObject<T>(index);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,16 +11,18 @@
|
||||
#include "core/core_cpu.h"
|
||||
#include "core/hle/kernel/address_arbiter.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
namespace {
|
||||
// Wake up num_to_wake (or all) threads in a vector.
|
||||
void AddressArbiter::WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads,
|
||||
s32 num_to_wake) {
|
||||
void WakeThreads(const std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_to_wake) {
|
||||
auto& system = Core::System::GetInstance();
|
||||
// Only process up to 'target' threads, unless 'target' is <= 0, in which case process
|
||||
// them all.
|
||||
std::size_t last = waiting_threads.size();
|
||||
@@ -32,12 +34,12 @@ void AddressArbiter::WakeThreads(const std::vector<std::shared_ptr<Thread>>& wai
|
||||
for (std::size_t i = 0; i < last; i++) {
|
||||
ASSERT(waiting_threads[i]->GetStatus() == ThreadStatus::WaitArb);
|
||||
waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS);
|
||||
RemoveThread(waiting_threads[i]);
|
||||
waiting_threads[i]->SetArbiterWaitAddress(0);
|
||||
waiting_threads[i]->ResumeFromWait();
|
||||
system.PrepareReschedule(waiting_threads[i]->GetProcessorID());
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
AddressArbiter::AddressArbiter(Core::System& system) : system{system} {}
|
||||
AddressArbiter::~AddressArbiter() = default;
|
||||
@@ -57,41 +59,35 @@ ResultCode AddressArbiter::SignalToAddress(VAddr address, SignalType type, s32 v
|
||||
}
|
||||
|
||||
ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) {
|
||||
const std::vector<std::shared_ptr<Thread>> waiting_threads =
|
||||
GetThreadsWaitingOnAddress(address);
|
||||
const std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
|
||||
WakeThreads(waiting_threads, num_to_wake);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value,
|
||||
s32 num_to_wake) {
|
||||
auto& memory = system.Memory();
|
||||
|
||||
// Ensure that we can write to the address.
|
||||
if (!memory.IsValidVirtualAddress(address)) {
|
||||
if (!Memory::IsValidVirtualAddress(address)) {
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
if (static_cast<s32>(memory.Read32(address)) != value) {
|
||||
if (static_cast<s32>(Memory::Read32(address)) != value) {
|
||||
return ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
memory.Write32(address, static_cast<u32>(value + 1));
|
||||
Memory::Write32(address, static_cast<u32>(value + 1));
|
||||
return SignalToAddressOnly(address, num_to_wake);
|
||||
}
|
||||
|
||||
ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
|
||||
s32 num_to_wake) {
|
||||
auto& memory = system.Memory();
|
||||
|
||||
// Ensure that we can write to the address.
|
||||
if (!memory.IsValidVirtualAddress(address)) {
|
||||
if (!Memory::IsValidVirtualAddress(address)) {
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
// Get threads waiting on the address.
|
||||
const std::vector<std::shared_ptr<Thread>> waiting_threads =
|
||||
GetThreadsWaitingOnAddress(address);
|
||||
const std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
|
||||
|
||||
// Determine the modified value depending on the waiting count.
|
||||
s32 updated_value;
|
||||
@@ -111,11 +107,11 @@ ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr a
|
||||
}
|
||||
}
|
||||
|
||||
if (static_cast<s32>(memory.Read32(address)) != value) {
|
||||
if (static_cast<s32>(Memory::Read32(address)) != value) {
|
||||
return ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
memory.Write32(address, static_cast<u32>(updated_value));
|
||||
Memory::Write32(address, static_cast<u32>(updated_value));
|
||||
WakeThreads(waiting_threads, num_to_wake);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
@@ -136,20 +132,18 @@ ResultCode AddressArbiter::WaitForAddress(VAddr address, ArbitrationType type, s
|
||||
|
||||
ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout,
|
||||
bool should_decrement) {
|
||||
auto& memory = system.Memory();
|
||||
|
||||
// Ensure that we can read the address.
|
||||
if (!memory.IsValidVirtualAddress(address)) {
|
||||
if (!Memory::IsValidVirtualAddress(address)) {
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
const s32 cur_value = static_cast<s32>(memory.Read32(address));
|
||||
const s32 cur_value = static_cast<s32>(Memory::Read32(address));
|
||||
if (cur_value >= value) {
|
||||
return ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (should_decrement) {
|
||||
memory.Write32(address, static_cast<u32>(cur_value - 1));
|
||||
Memory::Write32(address, static_cast<u32>(cur_value - 1));
|
||||
}
|
||||
|
||||
// Short-circuit without rescheduling, if timeout is zero.
|
||||
@@ -161,19 +155,15 @@ ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s6
|
||||
}
|
||||
|
||||
ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
|
||||
auto& memory = system.Memory();
|
||||
|
||||
// Ensure that we can read the address.
|
||||
if (!memory.IsValidVirtualAddress(address)) {
|
||||
if (!Memory::IsValidVirtualAddress(address)) {
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
// Only wait for the address if equal.
|
||||
if (static_cast<s32>(memory.Read32(address)) != value) {
|
||||
if (static_cast<s32>(Memory::Read32(address)) != value) {
|
||||
return ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
// Short-circuit without rescheduling if timeout is zero.
|
||||
// Short-circuit without rescheduling, if timeout is zero.
|
||||
if (timeout == 0) {
|
||||
return RESULT_TIMEOUT;
|
||||
}
|
||||
@@ -182,62 +172,36 @@ ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 t
|
||||
}
|
||||
|
||||
ResultCode AddressArbiter::WaitForAddressImpl(VAddr address, s64 timeout) {
|
||||
Thread* current_thread = system.CurrentScheduler().GetCurrentThread();
|
||||
SharedPtr<Thread> current_thread = system.CurrentScheduler().GetCurrentThread();
|
||||
current_thread->SetArbiterWaitAddress(address);
|
||||
InsertThread(SharedFrom(current_thread));
|
||||
current_thread->SetStatus(ThreadStatus::WaitArb);
|
||||
current_thread->InvalidateWakeupCallback();
|
||||
|
||||
current_thread->WakeAfterDelay(timeout);
|
||||
|
||||
system.PrepareReschedule(current_thread->GetProcessorID());
|
||||
return RESULT_TIMEOUT;
|
||||
}
|
||||
|
||||
void AddressArbiter::HandleWakeupThread(std::shared_ptr<Thread> thread) {
|
||||
ASSERT(thread->GetStatus() == ThreadStatus::WaitArb);
|
||||
RemoveThread(thread);
|
||||
thread->SetArbiterWaitAddress(0);
|
||||
}
|
||||
std::vector<SharedPtr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress(VAddr address) const {
|
||||
|
||||
void AddressArbiter::InsertThread(std::shared_ptr<Thread> thread) {
|
||||
const VAddr arb_addr = thread->GetArbiterWaitAddress();
|
||||
std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[arb_addr];
|
||||
auto it = thread_list.begin();
|
||||
while (it != thread_list.end()) {
|
||||
const std::shared_ptr<Thread>& current_thread = *it;
|
||||
if (current_thread->GetPriority() >= thread->GetPriority()) {
|
||||
thread_list.insert(it, thread);
|
||||
return;
|
||||
// Retrieve all threads that are waiting for this address.
|
||||
std::vector<SharedPtr<Thread>> threads;
|
||||
const auto& scheduler = system.GlobalScheduler();
|
||||
const auto& thread_list = scheduler.GetThreadList();
|
||||
|
||||
for (const auto& thread : thread_list) {
|
||||
if (thread->GetArbiterWaitAddress() == address) {
|
||||
threads.push_back(thread);
|
||||
}
|
||||
++it;
|
||||
}
|
||||
thread_list.push_back(std::move(thread));
|
||||
}
|
||||
|
||||
void AddressArbiter::RemoveThread(std::shared_ptr<Thread> thread) {
|
||||
const VAddr arb_addr = thread->GetArbiterWaitAddress();
|
||||
std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[arb_addr];
|
||||
auto it = thread_list.begin();
|
||||
while (it != thread_list.end()) {
|
||||
const std::shared_ptr<Thread>& current_thread = *it;
|
||||
if (current_thread.get() == thread.get()) {
|
||||
thread_list.erase(it);
|
||||
return;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
// Sort them by priority, such that the highest priority ones come first.
|
||||
std::sort(threads.begin(), threads.end(),
|
||||
[](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
|
||||
return lhs->GetPriority() < rhs->GetPriority();
|
||||
});
|
||||
|
||||
std::vector<std::shared_ptr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress(VAddr address) {
|
||||
std::vector<std::shared_ptr<Thread>> result;
|
||||
std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[address];
|
||||
auto it = thread_list.begin();
|
||||
while (it != thread_list.end()) {
|
||||
std::shared_ptr<Thread> current_thread = *it;
|
||||
result.push_back(std::move(current_thread));
|
||||
++it;
|
||||
}
|
||||
return result;
|
||||
return threads;
|
||||
}
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -4,12 +4,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
union ResultCode;
|
||||
|
||||
@@ -50,9 +48,6 @@ public:
|
||||
/// Waits on an address with a particular arbitration type.
|
||||
ResultCode WaitForAddress(VAddr address, ArbitrationType type, s32 value, s64 timeout_ns);
|
||||
|
||||
/// Removes a thread from the container and resets its address arbiter adress to 0
|
||||
void HandleWakeupThread(std::shared_ptr<Thread> thread);
|
||||
|
||||
private:
|
||||
/// Signals an address being waited on.
|
||||
ResultCode SignalToAddressOnly(VAddr address, s32 num_to_wake);
|
||||
@@ -76,20 +71,8 @@ private:
|
||||
// Waits on the given address with a timeout in nanoseconds
|
||||
ResultCode WaitForAddressImpl(VAddr address, s64 timeout);
|
||||
|
||||
/// Wake up num_to_wake (or all) threads in a vector.
|
||||
void WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads, s32 num_to_wake);
|
||||
|
||||
/// Insert a thread into the address arbiter container
|
||||
void InsertThread(std::shared_ptr<Thread> thread);
|
||||
|
||||
/// Removes a thread from the address arbiter container
|
||||
void RemoveThread(std::shared_ptr<Thread> thread);
|
||||
|
||||
// Gets the threads waiting on an address.
|
||||
std::vector<std::shared_ptr<Thread>> GetThreadsWaitingOnAddress(VAddr address);
|
||||
|
||||
/// List of threads waiting for a address arbiter
|
||||
std::unordered_map<VAddr, std::list<std::shared_ptr<Thread>>> arb_threads;
|
||||
std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) const;
|
||||
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
@@ -8,35 +8,39 @@
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/server_port.h"
|
||||
#include "core/hle/kernel/session.h"
|
||||
#include "core/hle/kernel/server_session.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
ClientPort::ClientPort(KernelCore& kernel) : Object{kernel} {}
|
||||
ClientPort::~ClientPort() = default;
|
||||
|
||||
std::shared_ptr<ServerPort> ClientPort::GetServerPort() const {
|
||||
SharedPtr<ServerPort> ClientPort::GetServerPort() const {
|
||||
return server_port;
|
||||
}
|
||||
|
||||
ResultVal<std::shared_ptr<ClientSession>> ClientPort::Connect() {
|
||||
ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
|
||||
// Note: Threads do not wait for the server endpoint to call
|
||||
// AcceptSession before returning from this call.
|
||||
|
||||
if (active_sessions >= max_sessions) {
|
||||
return ERR_MAX_CONNECTIONS_REACHED;
|
||||
}
|
||||
active_sessions++;
|
||||
|
||||
auto [client, server] = Kernel::Session::Create(kernel, name);
|
||||
// Create a new session pair, let the created sessions inherit the parent port's HLE handler.
|
||||
auto [server, client] = ServerSession::CreateSessionPair(kernel, server_port->GetName(), this);
|
||||
|
||||
if (server_port->HasHLEHandler()) {
|
||||
server_port->GetHLEHandler()->ClientConnected(std::move(server));
|
||||
server_port->GetHLEHandler()->ClientConnected(server);
|
||||
} else {
|
||||
server_port->AppendPendingSession(std::move(server));
|
||||
server_port->AppendPendingSession(server);
|
||||
}
|
||||
|
||||
// Wake the threads waiting on the ServerPort
|
||||
server_port->WakeupAllWaitingThreads();
|
||||
|
||||
return MakeResult(std::move(client));
|
||||
return MakeResult(client);
|
||||
}
|
||||
|
||||
void ClientPort::ConnectionClosed() {
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/result.h"
|
||||
@@ -19,9 +17,6 @@ class ServerPort;
|
||||
|
||||
class ClientPort final : public Object {
|
||||
public:
|
||||
explicit ClientPort(KernelCore& kernel);
|
||||
~ClientPort() override;
|
||||
|
||||
friend class ServerPort;
|
||||
std::string GetTypeName() const override {
|
||||
return "ClientPort";
|
||||
@@ -35,7 +30,7 @@ public:
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
std::shared_ptr<ServerPort> GetServerPort() const;
|
||||
SharedPtr<ServerPort> GetServerPort() const;
|
||||
|
||||
/**
|
||||
* Creates a new Session pair, adds the created ServerSession to the associated ServerPort's
|
||||
@@ -43,7 +38,7 @@ public:
|
||||
* waiting on it to awake.
|
||||
* @returns ClientSession The client endpoint of the created Session pair, or error code.
|
||||
*/
|
||||
ResultVal<std::shared_ptr<ClientSession>> Connect();
|
||||
ResultVal<SharedPtr<ClientSession>> Connect();
|
||||
|
||||
/**
|
||||
* Signifies that a previously active connection has been closed,
|
||||
@@ -52,7 +47,10 @@ public:
|
||||
void ConnectionClosed();
|
||||
|
||||
private:
|
||||
std::shared_ptr<ServerPort> server_port; ///< ServerPort associated with this client port.
|
||||
explicit ClientPort(KernelCore& kernel);
|
||||
~ClientPort() override;
|
||||
|
||||
SharedPtr<ServerPort> server_port; ///< ServerPort associated with this client port.
|
||||
u32 max_sessions = 0; ///< Maximum number of simultaneous sessions the port can have
|
||||
u32 active_sessions = 0; ///< Number of currently open sessions to this port
|
||||
std::string name; ///< Name of client port (optional)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@@ -12,44 +12,29 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
ClientSession::ClientSession(KernelCore& kernel) : WaitObject{kernel} {}
|
||||
|
||||
ClientSession::ClientSession(KernelCore& kernel) : Object{kernel} {}
|
||||
ClientSession::~ClientSession() {
|
||||
// This destructor will be called automatically when the last ClientSession handle is closed by
|
||||
// the emulated application.
|
||||
if (parent->Server()) {
|
||||
parent->Server()->ClientDisconnected();
|
||||
|
||||
// A local reference to the ServerSession is necessary to guarantee it
|
||||
// will be kept alive until after ClientDisconnected() returns.
|
||||
SharedPtr<ServerSession> server = parent->server;
|
||||
if (server) {
|
||||
server->ClientDisconnected();
|
||||
}
|
||||
|
||||
parent->client = nullptr;
|
||||
}
|
||||
|
||||
bool ClientSession::ShouldWait(const Thread* thread) const {
|
||||
UNIMPLEMENTED();
|
||||
return {};
|
||||
}
|
||||
|
||||
void ClientSession::Acquire(Thread* thread) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
ResultVal<std::shared_ptr<ClientSession>> ClientSession::Create(KernelCore& kernel,
|
||||
std::shared_ptr<Session> parent,
|
||||
std::string name) {
|
||||
std::shared_ptr<ClientSession> client_session{std::make_shared<ClientSession>(kernel)};
|
||||
|
||||
client_session->name = std::move(name);
|
||||
client_session->parent = std::move(parent);
|
||||
|
||||
return MakeResult(std::move(client_session));
|
||||
}
|
||||
|
||||
ResultCode ClientSession::SendSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory) {
|
||||
ResultCode ClientSession::SendSyncRequest(SharedPtr<Thread> thread) {
|
||||
// Keep ServerSession alive until we're done working with it.
|
||||
if (!parent->Server()) {
|
||||
SharedPtr<ServerSession> server = parent->server;
|
||||
if (server == nullptr)
|
||||
return ERR_SESSION_CLOSED_BY_REMOTE;
|
||||
}
|
||||
|
||||
// Signal the server session that new data is available
|
||||
return parent->Server()->HandleSyncRequest(std::move(thread), memory);
|
||||
return server->HandleSyncRequest(std::move(thread));
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@@ -6,28 +6,20 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "core/hle/kernel/wait_object.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
union ResultCode;
|
||||
|
||||
namespace Memory {
|
||||
class Memory;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KernelCore;
|
||||
class Session;
|
||||
class ServerSession;
|
||||
class Thread;
|
||||
|
||||
class ClientSession final : public WaitObject {
|
||||
class ClientSession final : public Object {
|
||||
public:
|
||||
explicit ClientSession(KernelCore& kernel);
|
||||
~ClientSession() override;
|
||||
|
||||
friend class Session;
|
||||
friend class ServerSession;
|
||||
|
||||
std::string GetTypeName() const override {
|
||||
return "ClientSession";
|
||||
@@ -42,16 +34,11 @@ public:
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory);
|
||||
|
||||
bool ShouldWait(const Thread* thread) const override;
|
||||
|
||||
void Acquire(Thread* thread) override;
|
||||
ResultCode SendSyncRequest(SharedPtr<Thread> thread);
|
||||
|
||||
private:
|
||||
static ResultVal<std::shared_ptr<ClientSession>> Create(KernelCore& kernel,
|
||||
std::shared_ptr<Session> parent,
|
||||
std::string name = "Unknown");
|
||||
explicit ClientSession(KernelCore& kernel);
|
||||
~ClientSession() override;
|
||||
|
||||
/// The parent session, which links to the server endpoint.
|
||||
std::shared_ptr<Session> parent;
|
||||
|
||||
@@ -44,7 +44,7 @@ ResultCode HandleTable::SetSize(s32 handle_table_size) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultVal<Handle> HandleTable::Create(std::shared_ptr<Object> obj) {
|
||||
ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) {
|
||||
DEBUG_ASSERT(obj != nullptr);
|
||||
|
||||
const u16 slot = next_free_slot;
|
||||
@@ -70,7 +70,7 @@ ResultVal<Handle> HandleTable::Create(std::shared_ptr<Object> obj) {
|
||||
}
|
||||
|
||||
ResultVal<Handle> HandleTable::Duplicate(Handle handle) {
|
||||
std::shared_ptr<Object> object = GetGeneric(handle);
|
||||
SharedPtr<Object> object = GetGeneric(handle);
|
||||
if (object == nullptr) {
|
||||
LOG_ERROR(Kernel, "Tried to duplicate invalid handle: {:08X}", handle);
|
||||
return ERR_INVALID_HANDLE;
|
||||
@@ -99,11 +99,11 @@ bool HandleTable::IsValid(Handle handle) const {
|
||||
return slot < table_size && objects[slot] != nullptr && generations[slot] == generation;
|
||||
}
|
||||
|
||||
std::shared_ptr<Object> HandleTable::GetGeneric(Handle handle) const {
|
||||
SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const {
|
||||
if (handle == CurrentThread) {
|
||||
return SharedFrom(GetCurrentThread());
|
||||
return GetCurrentThread();
|
||||
} else if (handle == CurrentProcess) {
|
||||
return SharedFrom(Core::System::GetInstance().CurrentProcess());
|
||||
return Core::System::GetInstance().CurrentProcess();
|
||||
}
|
||||
|
||||
if (!IsValid(handle)) {
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/result.h"
|
||||
@@ -70,7 +68,7 @@ public:
|
||||
* @return The created Handle or one of the following errors:
|
||||
* - `ERR_HANDLE_TABLE_FULL`: the maximum number of handles has been exceeded.
|
||||
*/
|
||||
ResultVal<Handle> Create(std::shared_ptr<Object> obj);
|
||||
ResultVal<Handle> Create(SharedPtr<Object> obj);
|
||||
|
||||
/**
|
||||
* Returns a new handle that points to the same object as the passed in handle.
|
||||
@@ -94,7 +92,7 @@ public:
|
||||
* Looks up a handle.
|
||||
* @return Pointer to the looked-up object, or `nullptr` if the handle is not valid.
|
||||
*/
|
||||
std::shared_ptr<Object> GetGeneric(Handle handle) const;
|
||||
SharedPtr<Object> GetGeneric(Handle handle) const;
|
||||
|
||||
/**
|
||||
* Looks up a handle while verifying its type.
|
||||
@@ -102,7 +100,7 @@ public:
|
||||
* type differs from the requested one.
|
||||
*/
|
||||
template <class T>
|
||||
std::shared_ptr<T> Get(Handle handle) const {
|
||||
SharedPtr<T> Get(Handle handle) const {
|
||||
return DynamicObjectCast<T>(GetGeneric(handle));
|
||||
}
|
||||
|
||||
@@ -111,7 +109,7 @@ public:
|
||||
|
||||
private:
|
||||
/// Stores the Object referenced by the handle or null if the slot is empty.
|
||||
std::array<std::shared_ptr<Object>, MAX_COUNT> objects;
|
||||
std::array<SharedPtr<Object>, MAX_COUNT> objects;
|
||||
|
||||
/**
|
||||
* The value of `next_generation` when the handle was created, used to check for validity. For
|
||||
|
||||
@@ -32,25 +32,23 @@ SessionRequestHandler::SessionRequestHandler() = default;
|
||||
|
||||
SessionRequestHandler::~SessionRequestHandler() = default;
|
||||
|
||||
void SessionRequestHandler::ClientConnected(std::shared_ptr<ServerSession> server_session) {
|
||||
void SessionRequestHandler::ClientConnected(SharedPtr<ServerSession> server_session) {
|
||||
server_session->SetHleHandler(shared_from_this());
|
||||
connected_sessions.push_back(std::move(server_session));
|
||||
}
|
||||
|
||||
void SessionRequestHandler::ClientDisconnected(
|
||||
const std::shared_ptr<ServerSession>& server_session) {
|
||||
void SessionRequestHandler::ClientDisconnected(const SharedPtr<ServerSession>& server_session) {
|
||||
server_session->SetHleHandler(nullptr);
|
||||
boost::range::remove_erase(connected_sessions, server_session);
|
||||
}
|
||||
|
||||
std::shared_ptr<WritableEvent> HLERequestContext::SleepClientThread(
|
||||
SharedPtr<WritableEvent> HLERequestContext::SleepClientThread(
|
||||
const std::string& reason, u64 timeout, WakeupCallback&& callback,
|
||||
std::shared_ptr<WritableEvent> writable_event) {
|
||||
SharedPtr<WritableEvent> writable_event) {
|
||||
// Put the client thread to sleep until the wait event is signaled or the timeout expires.
|
||||
thread->SetWakeupCallback([context = *this, callback](ThreadWakeupReason reason,
|
||||
std::shared_ptr<Thread> thread,
|
||||
std::shared_ptr<WaitObject> object,
|
||||
std::size_t index) mutable -> bool {
|
||||
thread->SetWakeupCallback([context = *this, callback](
|
||||
ThreadWakeupReason reason, SharedPtr<Thread> thread,
|
||||
SharedPtr<WaitObject> object, std::size_t index) mutable -> bool {
|
||||
ASSERT(thread->GetStatus() == ThreadStatus::WaitHLEEvent);
|
||||
callback(thread, context, reason);
|
||||
context.WriteToOutgoingCommandBuffer(*thread);
|
||||
@@ -60,7 +58,8 @@ std::shared_ptr<WritableEvent> HLERequestContext::SleepClientThread(
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
if (!writable_event) {
|
||||
// Create event if not provided
|
||||
const auto pair = WritableEvent::CreateEventPair(kernel, "HLE Pause Event: " + reason);
|
||||
const auto pair = WritableEvent::CreateEventPair(kernel, ResetType::Automatic,
|
||||
"HLE Pause Event: " + reason);
|
||||
writable_event = pair.writable;
|
||||
}
|
||||
|
||||
@@ -74,13 +73,11 @@ std::shared_ptr<WritableEvent> HLERequestContext::SleepClientThread(
|
||||
thread->WakeAfterDelay(timeout);
|
||||
}
|
||||
|
||||
is_thread_waiting = true;
|
||||
|
||||
return writable_event;
|
||||
}
|
||||
|
||||
HLERequestContext::HLERequestContext(std::shared_ptr<Kernel::ServerSession> server_session,
|
||||
std::shared_ptr<Thread> thread)
|
||||
HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_session,
|
||||
SharedPtr<Thread> thread)
|
||||
: server_session(std::move(server_session)), thread(std::move(thread)) {
|
||||
cmd_buf[0] = 0;
|
||||
}
|
||||
@@ -216,11 +213,10 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const HandleTabl
|
||||
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
|
||||
auto& owner_process = *thread.GetOwnerProcess();
|
||||
auto& handle_table = owner_process.GetHandleTable();
|
||||
auto& memory = Core::System::GetInstance().Memory();
|
||||
|
||||
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf;
|
||||
memory.ReadBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
|
||||
dst_cmdbuf.size() * sizeof(u32));
|
||||
Memory::ReadBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
|
||||
dst_cmdbuf.size() * sizeof(u32));
|
||||
|
||||
// The header was already built in the internal command buffer. Attempt to parse it to verify
|
||||
// the integrity and then copy it over to the target command buffer.
|
||||
@@ -276,8 +272,8 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
|
||||
}
|
||||
|
||||
// Copy the translated command buffer back into the thread's command buffer area.
|
||||
memory.WriteBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
|
||||
dst_cmdbuf.size() * sizeof(u32));
|
||||
Memory::WriteBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
|
||||
dst_cmdbuf.size() * sizeof(u32));
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
@@ -285,14 +281,15 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
|
||||
std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const {
|
||||
std::vector<u8> buffer;
|
||||
const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()};
|
||||
auto& memory = Core::System::GetInstance().Memory();
|
||||
|
||||
if (is_buffer_a) {
|
||||
buffer.resize(BufferDescriptorA()[buffer_index].Size());
|
||||
memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size());
|
||||
Memory::ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(),
|
||||
buffer.size());
|
||||
} else {
|
||||
buffer.resize(BufferDescriptorX()[buffer_index].Size());
|
||||
memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size());
|
||||
Memory::ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(),
|
||||
buffer.size());
|
||||
}
|
||||
|
||||
return buffer;
|
||||
@@ -313,11 +310,10 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
|
||||
size = buffer_size; // TODO(bunnei): This needs to be HW tested
|
||||
}
|
||||
|
||||
auto& memory = Core::System::GetInstance().Memory();
|
||||
if (is_buffer_b) {
|
||||
memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
|
||||
Memory::WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
|
||||
} else {
|
||||
memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);
|
||||
Memory::WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);
|
||||
}
|
||||
|
||||
return size;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
@@ -61,20 +60,20 @@ public:
|
||||
* associated ServerSession alive for the duration of the connection.
|
||||
* @param server_session Owning pointer to the ServerSession associated with the connection.
|
||||
*/
|
||||
void ClientConnected(std::shared_ptr<ServerSession> server_session);
|
||||
void ClientConnected(SharedPtr<ServerSession> server_session);
|
||||
|
||||
/**
|
||||
* Signals that a client has just disconnected from this HLE handler and releases the
|
||||
* associated ServerSession.
|
||||
* @param server_session ServerSession associated with the connection.
|
||||
*/
|
||||
void ClientDisconnected(const std::shared_ptr<ServerSession>& server_session);
|
||||
void ClientDisconnected(const SharedPtr<ServerSession>& server_session);
|
||||
|
||||
protected:
|
||||
/// List of sessions that are connected to this handler.
|
||||
/// A ServerSession whose server endpoint is an HLE implementation is kept alive by this list
|
||||
/// for the duration of the connection.
|
||||
std::vector<std::shared_ptr<ServerSession>> connected_sessions;
|
||||
std::vector<SharedPtr<ServerSession>> connected_sessions;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -98,8 +97,7 @@ protected:
|
||||
*/
|
||||
class HLERequestContext {
|
||||
public:
|
||||
explicit HLERequestContext(std::shared_ptr<ServerSession> session,
|
||||
std::shared_ptr<Thread> thread);
|
||||
explicit HLERequestContext(SharedPtr<ServerSession> session, SharedPtr<Thread> thread);
|
||||
~HLERequestContext();
|
||||
|
||||
/// Returns a pointer to the IPC command buffer for this request.
|
||||
@@ -111,12 +109,12 @@ public:
|
||||
* Returns the session through which this request was made. This can be used as a map key to
|
||||
* access per-client data on services.
|
||||
*/
|
||||
const std::shared_ptr<Kernel::ServerSession>& Session() const {
|
||||
const SharedPtr<Kernel::ServerSession>& Session() const {
|
||||
return server_session;
|
||||
}
|
||||
|
||||
using WakeupCallback = std::function<void(
|
||||
std::shared_ptr<Thread> thread, HLERequestContext& context, ThreadWakeupReason reason)>;
|
||||
using WakeupCallback = std::function<void(SharedPtr<Thread> thread, HLERequestContext& context,
|
||||
ThreadWakeupReason reason)>;
|
||||
|
||||
/**
|
||||
* Puts the specified guest thread to sleep until the returned event is signaled or until the
|
||||
@@ -131,9 +129,9 @@ public:
|
||||
* created.
|
||||
* @returns Event that when signaled will resume the thread and call the callback function.
|
||||
*/
|
||||
std::shared_ptr<WritableEvent> SleepClientThread(
|
||||
const std::string& reason, u64 timeout, WakeupCallback&& callback,
|
||||
std::shared_ptr<WritableEvent> writable_event = nullptr);
|
||||
SharedPtr<WritableEvent> SleepClientThread(const std::string& reason, u64 timeout,
|
||||
WakeupCallback&& callback,
|
||||
SharedPtr<WritableEvent> writable_event = nullptr);
|
||||
|
||||
/// Populates this context with data from the requesting process/thread.
|
||||
ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table,
|
||||
@@ -211,20 +209,20 @@ public:
|
||||
std::size_t GetWriteBufferSize(int buffer_index = 0) const;
|
||||
|
||||
template <typename T>
|
||||
std::shared_ptr<T> GetCopyObject(std::size_t index) {
|
||||
SharedPtr<T> GetCopyObject(std::size_t index) {
|
||||
return DynamicObjectCast<T>(copy_objects.at(index));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::shared_ptr<T> GetMoveObject(std::size_t index) {
|
||||
SharedPtr<T> GetMoveObject(std::size_t index) {
|
||||
return DynamicObjectCast<T>(move_objects.at(index));
|
||||
}
|
||||
|
||||
void AddMoveObject(std::shared_ptr<Object> object) {
|
||||
void AddMoveObject(SharedPtr<Object> object) {
|
||||
move_objects.emplace_back(std::move(object));
|
||||
}
|
||||
|
||||
void AddCopyObject(std::shared_ptr<Object> object) {
|
||||
void AddCopyObject(SharedPtr<Object> object) {
|
||||
copy_objects.emplace_back(std::move(object));
|
||||
}
|
||||
|
||||
@@ -264,27 +262,15 @@ public:
|
||||
|
||||
std::string Description() const;
|
||||
|
||||
Thread& GetThread() {
|
||||
return *thread;
|
||||
}
|
||||
|
||||
const Thread& GetThread() const {
|
||||
return *thread;
|
||||
}
|
||||
|
||||
bool IsThreadWaiting() const {
|
||||
return is_thread_waiting;
|
||||
}
|
||||
|
||||
private:
|
||||
void ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf, bool incoming);
|
||||
|
||||
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
|
||||
std::shared_ptr<Kernel::ServerSession> server_session;
|
||||
std::shared_ptr<Thread> thread;
|
||||
SharedPtr<Kernel::ServerSession> server_session;
|
||||
SharedPtr<Thread> thread;
|
||||
// TODO(yuriks): Check common usage of this and optimize size accordingly
|
||||
boost::container::small_vector<std::shared_ptr<Object>, 8> move_objects;
|
||||
boost::container::small_vector<std::shared_ptr<Object>, 8> copy_objects;
|
||||
boost::container::small_vector<SharedPtr<Object>, 8> move_objects;
|
||||
boost::container::small_vector<SharedPtr<Object>, 8> copy_objects;
|
||||
boost::container::small_vector<std::shared_ptr<SessionRequestHandler>, 8> domain_objects;
|
||||
|
||||
std::optional<IPC::CommandHeader> command_header;
|
||||
@@ -302,7 +288,6 @@ private:
|
||||
u32_le command{};
|
||||
|
||||
std::vector<std::shared_ptr<SessionRequestHandler>> domain_request_handlers;
|
||||
bool is_thread_waiting{};
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/core_timing_util.h"
|
||||
#include "core/hle/kernel/address_arbiter.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
@@ -39,7 +40,7 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_
|
||||
// Lock the global kernel mutex when we enter the kernel HLE.
|
||||
std::lock_guard lock{HLE::g_hle_lock};
|
||||
|
||||
std::shared_ptr<Thread> thread =
|
||||
SharedPtr<Thread> thread =
|
||||
system.Kernel().RetrieveThreadFromWakeupCallbackHandleTable(proper_handle);
|
||||
if (thread == nullptr) {
|
||||
LOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", proper_handle);
|
||||
@@ -52,7 +53,7 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_
|
||||
thread->GetStatus() == ThreadStatus::WaitHLEEvent) {
|
||||
// Remove the thread from each of its waiting objects' waitlists
|
||||
for (const auto& object : thread->GetWaitObjects()) {
|
||||
object->RemoveWaitingThread(thread);
|
||||
object->RemoveWaitingThread(thread.get());
|
||||
}
|
||||
thread->ClearWaitObjects();
|
||||
|
||||
@@ -63,11 +64,8 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_
|
||||
} else if (thread->GetStatus() == ThreadStatus::WaitMutex ||
|
||||
thread->GetStatus() == ThreadStatus::WaitCondVar) {
|
||||
thread->SetMutexWaitAddress(0);
|
||||
thread->SetCondVarWaitAddress(0);
|
||||
thread->SetWaitHandle(0);
|
||||
if (thread->GetStatus() == ThreadStatus::WaitCondVar) {
|
||||
thread->GetOwnerProcess()->RemoveConditionVariableThread(thread);
|
||||
thread->SetCondVarWaitAddress(0);
|
||||
}
|
||||
|
||||
auto* const lock_owner = thread->GetLockOwner();
|
||||
// Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance
|
||||
@@ -78,9 +76,9 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_
|
||||
}
|
||||
}
|
||||
|
||||
if (thread->GetStatus() == ThreadStatus::WaitArb) {
|
||||
auto& address_arbiter = thread->GetOwnerProcess()->GetAddressArbiter();
|
||||
address_arbiter.HandleWakeupThread(thread);
|
||||
if (thread->GetArbiterWaitAddress() != 0) {
|
||||
ASSERT(thread->GetStatus() == ThreadStatus::WaitArb);
|
||||
thread->SetArbiterWaitAddress(0);
|
||||
}
|
||||
|
||||
if (resume) {
|
||||
@@ -138,12 +136,12 @@ struct KernelCore::Impl {
|
||||
|
||||
void InitializeThreads() {
|
||||
thread_wakeup_event_type =
|
||||
Core::Timing::CreateEvent("ThreadWakeupCallback", ThreadWakeupCallback);
|
||||
system.CoreTiming().RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
|
||||
}
|
||||
|
||||
void InitializePreemption() {
|
||||
preemption_event =
|
||||
Core::Timing::CreateEvent("PreemptionCallback", [this](u64 userdata, s64 cycles_late) {
|
||||
preemption_event = system.CoreTiming().RegisterEvent(
|
||||
"PreemptionCallback", [this](u64 userdata, s64 cycles_late) {
|
||||
global_scheduler.PreemptThreads();
|
||||
s64 time_interval = Core::Timing::msToCycles(std::chrono::milliseconds(10));
|
||||
system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
|
||||
@@ -153,31 +151,20 @@ struct KernelCore::Impl {
|
||||
system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
|
||||
}
|
||||
|
||||
void MakeCurrentProcess(Process* process) {
|
||||
current_process = process;
|
||||
|
||||
if (process == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
system.Memory().SetCurrentPageTable(*process);
|
||||
}
|
||||
|
||||
std::atomic<u32> next_object_id{0};
|
||||
std::atomic<u64> next_kernel_process_id{Process::InitialKIPIDMin};
|
||||
std::atomic<u64> next_user_process_id{Process::ProcessIDMin};
|
||||
std::atomic<u64> next_thread_id{1};
|
||||
|
||||
// Lists all processes that exist in the current session.
|
||||
std::vector<std::shared_ptr<Process>> process_list;
|
||||
std::vector<SharedPtr<Process>> process_list;
|
||||
Process* current_process = nullptr;
|
||||
Kernel::GlobalScheduler global_scheduler;
|
||||
|
||||
std::shared_ptr<ResourceLimit> system_resource_limit;
|
||||
|
||||
std::shared_ptr<Core::Timing::EventType> thread_wakeup_event_type;
|
||||
std::shared_ptr<Core::Timing::EventType> preemption_event;
|
||||
SharedPtr<ResourceLimit> system_resource_limit;
|
||||
|
||||
Core::Timing::EventType* thread_wakeup_event_type = nullptr;
|
||||
Core::Timing::EventType* preemption_event = nullptr;
|
||||
// TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future,
|
||||
// allowing us to simply use a pool index or similar.
|
||||
Kernel::HandleTable thread_wakeup_callback_handle_table;
|
||||
@@ -203,21 +190,26 @@ void KernelCore::Shutdown() {
|
||||
impl->Shutdown();
|
||||
}
|
||||
|
||||
std::shared_ptr<ResourceLimit> KernelCore::GetSystemResourceLimit() const {
|
||||
SharedPtr<ResourceLimit> KernelCore::GetSystemResourceLimit() const {
|
||||
return impl->system_resource_limit;
|
||||
}
|
||||
|
||||
std::shared_ptr<Thread> KernelCore::RetrieveThreadFromWakeupCallbackHandleTable(
|
||||
Handle handle) const {
|
||||
SharedPtr<Thread> KernelCore::RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const {
|
||||
return impl->thread_wakeup_callback_handle_table.Get<Thread>(handle);
|
||||
}
|
||||
|
||||
void KernelCore::AppendNewProcess(std::shared_ptr<Process> process) {
|
||||
void KernelCore::AppendNewProcess(SharedPtr<Process> process) {
|
||||
impl->process_list.push_back(std::move(process));
|
||||
}
|
||||
|
||||
void KernelCore::MakeCurrentProcess(Process* process) {
|
||||
impl->MakeCurrentProcess(process);
|
||||
impl->current_process = process;
|
||||
|
||||
if (process == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
Memory::SetCurrentPageTable(*process);
|
||||
}
|
||||
|
||||
Process* KernelCore::CurrentProcess() {
|
||||
@@ -228,7 +220,7 @@ const Process* KernelCore::CurrentProcess() const {
|
||||
return impl->current_process;
|
||||
}
|
||||
|
||||
const std::vector<std::shared_ptr<Process>>& KernelCore::GetProcessList() const {
|
||||
const std::vector<SharedPtr<Process>>& KernelCore::GetProcessList() const {
|
||||
return impl->process_list;
|
||||
}
|
||||
|
||||
@@ -240,7 +232,7 @@ const Kernel::GlobalScheduler& KernelCore::GlobalScheduler() const {
|
||||
return impl->global_scheduler;
|
||||
}
|
||||
|
||||
void KernelCore::AddNamedPort(std::string name, std::shared_ptr<ClientPort> port) {
|
||||
void KernelCore::AddNamedPort(std::string name, SharedPtr<ClientPort> port) {
|
||||
impl->named_ports.emplace(std::move(name), std::move(port));
|
||||
}
|
||||
|
||||
@@ -273,7 +265,7 @@ u64 KernelCore::CreateNewUserProcessID() {
|
||||
return impl->next_user_process_id++;
|
||||
}
|
||||
|
||||
const std::shared_ptr<Core::Timing::EventType>& KernelCore::ThreadWakeupCallbackEventType() const {
|
||||
Core::Timing::EventType* KernelCore::ThreadWakeupCallbackEventType() const {
|
||||
return impl->thread_wakeup_event_type;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
namespace Core {
|
||||
@@ -32,7 +30,7 @@ class Thread;
|
||||
/// Represents a single instance of the kernel.
|
||||
class KernelCore {
|
||||
private:
|
||||
using NamedPortTable = std::unordered_map<std::string, std::shared_ptr<ClientPort>>;
|
||||
using NamedPortTable = std::unordered_map<std::string, SharedPtr<ClientPort>>;
|
||||
|
||||
public:
|
||||
/// Constructs an instance of the kernel using the given System
|
||||
@@ -58,13 +56,13 @@ public:
|
||||
void Shutdown();
|
||||
|
||||
/// Retrieves a shared pointer to the system resource limit instance.
|
||||
std::shared_ptr<ResourceLimit> GetSystemResourceLimit() const;
|
||||
SharedPtr<ResourceLimit> GetSystemResourceLimit() const;
|
||||
|
||||
/// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table.
|
||||
std::shared_ptr<Thread> RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const;
|
||||
SharedPtr<Thread> RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const;
|
||||
|
||||
/// Adds the given shared pointer to an internal list of active processes.
|
||||
void AppendNewProcess(std::shared_ptr<Process> process);
|
||||
void AppendNewProcess(SharedPtr<Process> process);
|
||||
|
||||
/// Makes the given process the new current process.
|
||||
void MakeCurrentProcess(Process* process);
|
||||
@@ -76,7 +74,7 @@ public:
|
||||
const Process* CurrentProcess() const;
|
||||
|
||||
/// Retrieves the list of processes.
|
||||
const std::vector<std::shared_ptr<Process>>& GetProcessList() const;
|
||||
const std::vector<SharedPtr<Process>>& GetProcessList() const;
|
||||
|
||||
/// Gets the sole instance of the global scheduler
|
||||
Kernel::GlobalScheduler& GlobalScheduler();
|
||||
@@ -85,7 +83,7 @@ public:
|
||||
const Kernel::GlobalScheduler& GlobalScheduler() const;
|
||||
|
||||
/// Adds a port to the named port table
|
||||
void AddNamedPort(std::string name, std::shared_ptr<ClientPort> port);
|
||||
void AddNamedPort(std::string name, SharedPtr<ClientPort> port);
|
||||
|
||||
/// Finds a port within the named port table with the given name.
|
||||
NamedPortTable::iterator FindNamedPort(const std::string& name);
|
||||
@@ -114,7 +112,7 @@ private:
|
||||
u64 CreateNewThreadID();
|
||||
|
||||
/// Retrieves the event type used for thread wakeup callbacks.
|
||||
const std::shared_ptr<Core::Timing::EventType>& ThreadWakeupCallbackEventType() const;
|
||||
Core::Timing::EventType* ThreadWakeupCallbackEventType() const;
|
||||
|
||||
/// Provides a reference to the thread wakeup callback handle table.
|
||||
Kernel::HandleTable& ThreadWakeupCallbackHandleTable();
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_cpu.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
@@ -23,10 +23,10 @@ namespace Kernel {
|
||||
|
||||
/// Returns the number of threads that are waiting for a mutex, and the highest priority one among
|
||||
/// those.
|
||||
static std::pair<std::shared_ptr<Thread>, u32> GetHighestPriorityMutexWaitingThread(
|
||||
const std::shared_ptr<Thread>& current_thread, VAddr mutex_addr) {
|
||||
static std::pair<SharedPtr<Thread>, u32> GetHighestPriorityMutexWaitingThread(
|
||||
const SharedPtr<Thread>& current_thread, VAddr mutex_addr) {
|
||||
|
||||
std::shared_ptr<Thread> highest_priority_thread;
|
||||
SharedPtr<Thread> highest_priority_thread;
|
||||
u32 num_waiters = 0;
|
||||
|
||||
for (const auto& thread : current_thread->GetMutexWaitingThreads()) {
|
||||
@@ -46,14 +46,14 @@ static std::pair<std::shared_ptr<Thread>, u32> GetHighestPriorityMutexWaitingThr
|
||||
}
|
||||
|
||||
/// Update the mutex owner field of all threads waiting on the mutex to point to the new owner.
|
||||
static void TransferMutexOwnership(VAddr mutex_addr, std::shared_ptr<Thread> current_thread,
|
||||
std::shared_ptr<Thread> new_owner) {
|
||||
static void TransferMutexOwnership(VAddr mutex_addr, SharedPtr<Thread> current_thread,
|
||||
SharedPtr<Thread> new_owner) {
|
||||
const auto threads = current_thread->GetMutexWaitingThreads();
|
||||
for (const auto& thread : threads) {
|
||||
if (thread->GetMutexWaitAddress() != mutex_addr)
|
||||
continue;
|
||||
|
||||
ASSERT(thread->GetLockOwner() == current_thread.get());
|
||||
ASSERT(thread->GetLockOwner() == current_thread);
|
||||
current_thread->RemoveMutexWaiter(thread);
|
||||
if (new_owner != thread)
|
||||
new_owner->AddMutexWaiter(thread);
|
||||
@@ -71,16 +71,15 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
|
||||
}
|
||||
|
||||
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
|
||||
std::shared_ptr<Thread> current_thread =
|
||||
SharedFrom(system.CurrentScheduler().GetCurrentThread());
|
||||
std::shared_ptr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle);
|
||||
std::shared_ptr<Thread> requesting_thread = handle_table.Get<Thread>(requesting_thread_handle);
|
||||
Thread* const current_thread = system.CurrentScheduler().GetCurrentThread();
|
||||
SharedPtr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle);
|
||||
SharedPtr<Thread> requesting_thread = handle_table.Get<Thread>(requesting_thread_handle);
|
||||
|
||||
// TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of another
|
||||
// thread.
|
||||
ASSERT(requesting_thread == current_thread);
|
||||
|
||||
const u32 addr_value = system.Memory().Read32(address);
|
||||
u32 addr_value = Memory::Read32(address);
|
||||
|
||||
// If the mutex isn't being held, just return success.
|
||||
if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) {
|
||||
@@ -91,6 +90,20 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
// This a workaround where an unknown bug writes the mutex value to give ownership to a cond var
|
||||
// waiting thread.
|
||||
if (holding_thread->GetStatus() == ThreadStatus::WaitCondVar) {
|
||||
if (holding_thread->GetMutexWaitAddress() == address) {
|
||||
Release(address, holding_thread.get());
|
||||
addr_value = Memory::Read32(address);
|
||||
if (addr_value == 0)
|
||||
return RESULT_SUCCESS;
|
||||
else {
|
||||
holding_thread = handle_table.Get<Thread>(addr_value & Mutex::MutexOwnerMask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wait until the mutex is released
|
||||
current_thread->SetMutexWaitAddress(address);
|
||||
current_thread->SetWaitHandle(requesting_thread_handle);
|
||||
@@ -106,24 +119,22 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode Mutex::Release(VAddr address) {
|
||||
ResultCode Mutex::Release(VAddr address, Thread* holding_thread) {
|
||||
// The mutex address must be 4-byte aligned
|
||||
if ((address % sizeof(u32)) != 0) {
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
std::shared_ptr<Thread> current_thread =
|
||||
SharedFrom(system.CurrentScheduler().GetCurrentThread());
|
||||
auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(current_thread, address);
|
||||
auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(holding_thread, address);
|
||||
|
||||
// There are no more threads waiting for the mutex, release it completely.
|
||||
if (thread == nullptr) {
|
||||
system.Memory().Write32(address, 0);
|
||||
Memory::Write32(address, 0);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
// Transfer the ownership of the mutex from the previous owner to the new one.
|
||||
TransferMutexOwnership(address, current_thread, thread);
|
||||
TransferMutexOwnership(address, holding_thread, thread);
|
||||
|
||||
u32 mutex_value = thread->GetWaitHandle();
|
||||
|
||||
@@ -133,7 +144,7 @@ ResultCode Mutex::Release(VAddr address) {
|
||||
}
|
||||
|
||||
// Grant the mutex to the next waiting thread and resume it.
|
||||
system.Memory().Write32(address, mutex_value);
|
||||
Memory::Write32(address, mutex_value);
|
||||
|
||||
ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
|
||||
thread->ResumeFromWait();
|
||||
@@ -144,7 +155,10 @@ ResultCode Mutex::Release(VAddr address) {
|
||||
thread->SetWaitHandle(0);
|
||||
thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
|
||||
|
||||
system.PrepareReschedule();
|
||||
if (thread->GetProcessorID() >= 0)
|
||||
system.CpuCore(thread->GetProcessorID()).PrepareReschedule();
|
||||
if (holding_thread->GetProcessorID() >= 0)
|
||||
system.CpuCore(holding_thread->GetProcessorID()).PrepareReschedule();
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
Handle requesting_thread_handle);
|
||||
|
||||
/// Releases the mutex at the specified address.
|
||||
ResultCode Release(VAddr address);
|
||||
ResultCode Release(VAddr address, Thread* holding_thread);
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
|
||||
@@ -27,7 +27,6 @@ bool Object::IsWaitable() const {
|
||||
case HandleType::ResourceLimit:
|
||||
case HandleType::ClientPort:
|
||||
case HandleType::ClientSession:
|
||||
case HandleType::Session:
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <boost/smart_ptr/intrusive_ptr.hpp>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Kernel {
|
||||
@@ -29,10 +30,14 @@ enum class HandleType : u32 {
|
||||
ServerPort,
|
||||
ClientSession,
|
||||
ServerSession,
|
||||
Session,
|
||||
};
|
||||
|
||||
class Object : NonCopyable, public std::enable_shared_from_this<Object> {
|
||||
enum class ResetType {
|
||||
Automatic, ///< Reset automatically on object acquisition
|
||||
Manual, ///< Never reset automatically
|
||||
};
|
||||
|
||||
class Object : NonCopyable {
|
||||
public:
|
||||
explicit Object(KernelCore& kernel);
|
||||
virtual ~Object();
|
||||
@@ -61,24 +66,35 @@ protected:
|
||||
KernelCore& kernel;
|
||||
|
||||
private:
|
||||
friend void intrusive_ptr_add_ref(Object*);
|
||||
friend void intrusive_ptr_release(Object*);
|
||||
|
||||
std::atomic<u32> ref_count{0};
|
||||
std::atomic<u32> object_id{0};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
std::shared_ptr<T> SharedFrom(T* raw) {
|
||||
if (raw == nullptr)
|
||||
return nullptr;
|
||||
return std::static_pointer_cast<T>(raw->shared_from_this());
|
||||
// Special functions used by boost::instrusive_ptr to do automatic ref-counting
|
||||
inline void intrusive_ptr_add_ref(Object* object) {
|
||||
object->ref_count.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
inline void intrusive_ptr_release(Object* object) {
|
||||
if (object->ref_count.fetch_sub(1, std::memory_order_acq_rel) == 1) {
|
||||
delete object;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
using SharedPtr = boost::intrusive_ptr<T>;
|
||||
|
||||
/**
|
||||
* Attempts to downcast the given Object pointer to a pointer to T.
|
||||
* @return Derived pointer to the object, or `nullptr` if `object` isn't of type T.
|
||||
*/
|
||||
template <typename T>
|
||||
inline std::shared_ptr<T> DynamicObjectCast(std::shared_ptr<Object> object) {
|
||||
inline SharedPtr<T> DynamicObjectCast(SharedPtr<Object> object) {
|
||||
if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) {
|
||||
return std::static_pointer_cast<T>(object);
|
||||
return boost::static_pointer_cast<T>(object);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority) {
|
||||
auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0,
|
||||
owner_process.GetIdealCore(), stack_top, owner_process);
|
||||
|
||||
std::shared_ptr<Thread> thread = std::move(thread_res).Unwrap();
|
||||
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
|
||||
|
||||
// Register 1 must be a handle to the main thread
|
||||
const Handle thread_handle = owner_process.GetHandleTable().Create(thread).Unwrap();
|
||||
@@ -100,10 +100,10 @@ private:
|
||||
std::bitset<num_slot_entries> is_slot_used;
|
||||
};
|
||||
|
||||
std::shared_ptr<Process> Process::Create(Core::System& system, std::string name, ProcessType type) {
|
||||
SharedPtr<Process> Process::Create(Core::System& system, std::string name, ProcessType type) {
|
||||
auto& kernel = system.Kernel();
|
||||
|
||||
std::shared_ptr<Process> process = std::make_shared<Process>(system);
|
||||
SharedPtr<Process> process(new Process(system));
|
||||
process->name = std::move(name);
|
||||
process->resource_limit = kernel.GetSystemResourceLimit();
|
||||
process->status = ProcessStatus::Created;
|
||||
@@ -121,7 +121,7 @@ std::shared_ptr<Process> Process::Create(Core::System& system, std::string name,
|
||||
return process;
|
||||
}
|
||||
|
||||
std::shared_ptr<ResourceLimit> Process::GetResourceLimit() const {
|
||||
SharedPtr<ResourceLimit> Process::GetResourceLimit() const {
|
||||
return resource_limit;
|
||||
}
|
||||
|
||||
@@ -142,49 +142,6 @@ u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const {
|
||||
return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage();
|
||||
}
|
||||
|
||||
void Process::InsertConditionVariableThread(std::shared_ptr<Thread> thread) {
|
||||
VAddr cond_var_addr = thread->GetCondVarWaitAddress();
|
||||
std::list<std::shared_ptr<Thread>>& thread_list = cond_var_threads[cond_var_addr];
|
||||
auto it = thread_list.begin();
|
||||
while (it != thread_list.end()) {
|
||||
const std::shared_ptr<Thread> current_thread = *it;
|
||||
if (current_thread->GetPriority() > thread->GetPriority()) {
|
||||
thread_list.insert(it, thread);
|
||||
return;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
thread_list.push_back(thread);
|
||||
}
|
||||
|
||||
void Process::RemoveConditionVariableThread(std::shared_ptr<Thread> thread) {
|
||||
VAddr cond_var_addr = thread->GetCondVarWaitAddress();
|
||||
std::list<std::shared_ptr<Thread>>& thread_list = cond_var_threads[cond_var_addr];
|
||||
auto it = thread_list.begin();
|
||||
while (it != thread_list.end()) {
|
||||
const std::shared_ptr<Thread> current_thread = *it;
|
||||
if (current_thread.get() == thread.get()) {
|
||||
thread_list.erase(it);
|
||||
return;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<Thread>> Process::GetConditionVariableThreads(
|
||||
const VAddr cond_var_addr) {
|
||||
std::vector<std::shared_ptr<Thread>> result{};
|
||||
std::list<std::shared_ptr<Thread>>& thread_list = cond_var_threads[cond_var_addr];
|
||||
auto it = thread_list.begin();
|
||||
while (it != thread_list.end()) {
|
||||
std::shared_ptr<Thread> current_thread = *it;
|
||||
result.push_back(current_thread);
|
||||
++it;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Process::RegisterThread(const Thread* thread) {
|
||||
thread_list.push_back(thread);
|
||||
}
|
||||
@@ -240,12 +197,12 @@ void Process::Run(s32 main_thread_priority, u64 stack_size) {
|
||||
void Process::PrepareForTermination() {
|
||||
ChangeStatus(ProcessStatus::Exiting);
|
||||
|
||||
const auto stop_threads = [this](const std::vector<std::shared_ptr<Thread>>& thread_list) {
|
||||
const auto stop_threads = [this](const std::vector<SharedPtr<Thread>>& thread_list) {
|
||||
for (auto& thread : thread_list) {
|
||||
if (thread->GetOwnerProcess() != this)
|
||||
continue;
|
||||
|
||||
if (thread.get() == system.CurrentScheduler().GetCurrentThread())
|
||||
if (thread == system.CurrentScheduler().GetCurrentThread())
|
||||
continue;
|
||||
|
||||
// TODO(Subv): When are the other running/ready threads terminated?
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <cstddef>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/address_arbiter.h"
|
||||
@@ -62,9 +61,6 @@ enum class ProcessStatus {
|
||||
|
||||
class Process final : public WaitObject {
|
||||
public:
|
||||
explicit Process(Core::System& system);
|
||||
~Process() override;
|
||||
|
||||
enum : u64 {
|
||||
/// Lowest allowed process ID for a kernel initial process.
|
||||
InitialKIPIDMin = 1,
|
||||
@@ -85,8 +81,7 @@ public:
|
||||
|
||||
static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
|
||||
|
||||
static std::shared_ptr<Process> Create(Core::System& system, std::string name,
|
||||
ProcessType type);
|
||||
static SharedPtr<Process> Create(Core::System& system, std::string name, ProcessType type);
|
||||
|
||||
std::string GetTypeName() const override {
|
||||
return "Process";
|
||||
@@ -161,7 +156,7 @@ public:
|
||||
}
|
||||
|
||||
/// Gets the resource limit descriptor for this process
|
||||
std::shared_ptr<ResourceLimit> GetResourceLimit() const;
|
||||
SharedPtr<ResourceLimit> GetResourceLimit() const;
|
||||
|
||||
/// Gets the ideal CPU core ID for this process
|
||||
u8 GetIdealCore() const {
|
||||
@@ -237,15 +232,6 @@ public:
|
||||
return thread_list;
|
||||
}
|
||||
|
||||
/// Insert a thread into the condition variable wait container
|
||||
void InsertConditionVariableThread(std::shared_ptr<Thread> thread);
|
||||
|
||||
/// Remove a thread from the condition variable wait container
|
||||
void RemoveConditionVariableThread(std::shared_ptr<Thread> thread);
|
||||
|
||||
/// Obtain all condition variable threads waiting for some address
|
||||
std::vector<std::shared_ptr<Thread>> GetConditionVariableThreads(VAddr cond_var_addr);
|
||||
|
||||
/// Registers a thread as being created under this process,
|
||||
/// adding it to this process' thread list.
|
||||
void RegisterThread(const Thread* thread);
|
||||
@@ -301,6 +287,9 @@ public:
|
||||
void FreeTLSRegion(VAddr tls_address);
|
||||
|
||||
private:
|
||||
explicit Process(Core::System& system);
|
||||
~Process() override;
|
||||
|
||||
/// Checks if the specified thread should wait until this process is available.
|
||||
bool ShouldWait(const Thread* thread) const override;
|
||||
|
||||
@@ -339,7 +328,7 @@ private:
|
||||
u32 system_resource_size = 0;
|
||||
|
||||
/// Resource limit descriptor for this process
|
||||
std::shared_ptr<ResourceLimit> resource_limit;
|
||||
SharedPtr<ResourceLimit> resource_limit;
|
||||
|
||||
/// The ideal CPU core for this process, threads are scheduled on this core by default.
|
||||
u8 ideal_core = 0;
|
||||
@@ -386,9 +375,6 @@ private:
|
||||
/// List of threads that are running with this process as their owner.
|
||||
std::list<const Thread*> thread_list;
|
||||
|
||||
/// List of threads waiting for a condition variable
|
||||
std::unordered_map<VAddr, std::list<std::shared_ptr<Thread>>> cond_var_threads;
|
||||
|
||||
/// System context
|
||||
Core::System& system;
|
||||
|
||||
|
||||
@@ -20,13 +20,15 @@ bool ReadableEvent::ShouldWait(const Thread* thread) const {
|
||||
|
||||
void ReadableEvent::Acquire(Thread* thread) {
|
||||
ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
|
||||
|
||||
if (reset_type == ResetType::Automatic) {
|
||||
signaled = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ReadableEvent::Signal() {
|
||||
if (!signaled) {
|
||||
signaled = true;
|
||||
WakeupAllWaitingThreads();
|
||||
};
|
||||
signaled = true;
|
||||
WakeupAllWaitingThreads();
|
||||
}
|
||||
|
||||
void ReadableEvent::Clear() {
|
||||
|
||||
@@ -27,6 +27,10 @@ public:
|
||||
return name;
|
||||
}
|
||||
|
||||
ResetType GetResetType() const {
|
||||
return reset_type;
|
||||
}
|
||||
|
||||
static constexpr HandleType HANDLE_TYPE = HandleType::ReadableEvent;
|
||||
HandleType GetHandleType() const override {
|
||||
return HANDLE_TYPE;
|
||||
@@ -51,7 +55,8 @@ private:
|
||||
|
||||
void Signal();
|
||||
|
||||
bool signaled{};
|
||||
ResetType reset_type;
|
||||
bool signaled;
|
||||
|
||||
std::string name; ///< Name of event (optional)
|
||||
};
|
||||
|
||||
@@ -16,8 +16,8 @@ constexpr std::size_t ResourceTypeToIndex(ResourceType type) {
|
||||
ResourceLimit::ResourceLimit(KernelCore& kernel) : Object{kernel} {}
|
||||
ResourceLimit::~ResourceLimit() = default;
|
||||
|
||||
std::shared_ptr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel) {
|
||||
return std::make_shared<ResourceLimit>(kernel);
|
||||
SharedPtr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel) {
|
||||
return new ResourceLimit(kernel);
|
||||
}
|
||||
|
||||
s64 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const {
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
@@ -33,11 +31,8 @@ constexpr bool IsValidResourceType(ResourceType type) {
|
||||
|
||||
class ResourceLimit final : public Object {
|
||||
public:
|
||||
explicit ResourceLimit(KernelCore& kernel);
|
||||
~ResourceLimit() override;
|
||||
|
||||
/// Creates a resource limit object.
|
||||
static std::shared_ptr<ResourceLimit> Create(KernelCore& kernel);
|
||||
static SharedPtr<ResourceLimit> Create(KernelCore& kernel);
|
||||
|
||||
std::string GetTypeName() const override {
|
||||
return "ResourceLimit";
|
||||
@@ -81,6 +76,9 @@ public:
|
||||
ResultCode SetLimitValue(ResourceType resource, s64 value);
|
||||
|
||||
private:
|
||||
explicit ResourceLimit(KernelCore& kernel);
|
||||
~ResourceLimit() override;
|
||||
|
||||
// TODO(Subv): Increment resource limit current values in their respective Kernel::T::Create
|
||||
// functions
|
||||
//
|
||||
|
||||
@@ -22,31 +22,44 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
GlobalScheduler::GlobalScheduler(Core::System& system) : system{system} {}
|
||||
GlobalScheduler::GlobalScheduler(Core::System& system) : system{system} {
|
||||
is_reselection_pending = false;
|
||||
}
|
||||
|
||||
GlobalScheduler::~GlobalScheduler() = default;
|
||||
|
||||
void GlobalScheduler::AddThread(std::shared_ptr<Thread> thread) {
|
||||
void GlobalScheduler::AddThread(SharedPtr<Thread> thread) {
|
||||
thread_list.push_back(std::move(thread));
|
||||
}
|
||||
|
||||
void GlobalScheduler::RemoveThread(std::shared_ptr<Thread> thread) {
|
||||
void GlobalScheduler::RemoveThread(const Thread* thread) {
|
||||
thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
|
||||
thread_list.end());
|
||||
}
|
||||
|
||||
void GlobalScheduler::UnloadThread(std::size_t core) {
|
||||
/*
|
||||
* UnloadThread selects a core and forces it to unload its current thread's context
|
||||
*/
|
||||
void GlobalScheduler::UnloadThread(s32 core) {
|
||||
Scheduler& sched = system.Scheduler(core);
|
||||
sched.UnloadThread();
|
||||
}
|
||||
|
||||
void GlobalScheduler::SelectThread(std::size_t core) {
|
||||
/*
|
||||
* SelectThread takes care of selecting the new scheduled thread.
|
||||
* It does it in 3 steps:
|
||||
* - First a thread is selected from the top of the priority queue. If no thread
|
||||
* is obtained then we move to step two, else we are done.
|
||||
* - Second we try to get a suggested thread that's not assigned to any core or
|
||||
* that is not the top thread in that core.
|
||||
* - Third is no suggested thread is found, we do a second pass and pick a running
|
||||
* thread in another core and swap it with its current thread.
|
||||
*/
|
||||
void GlobalScheduler::SelectThread(u32 core) {
|
||||
const auto update_thread = [](Thread* thread, Scheduler& sched) {
|
||||
if (thread != sched.selected_thread.get()) {
|
||||
if (thread != sched.selected_thread) {
|
||||
if (thread == nullptr) {
|
||||
++sched.idle_selection_count;
|
||||
}
|
||||
sched.selected_thread = SharedFrom(thread);
|
||||
sched.selected_thread = thread;
|
||||
}
|
||||
sched.is_context_switch_pending = sched.selected_thread != sched.current_thread;
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
@@ -77,9 +90,9 @@ void GlobalScheduler::SelectThread(std::size_t core) {
|
||||
// if we got a suggested thread, select it, else do a second pass.
|
||||
if (winner && winner->GetPriority() > 2) {
|
||||
if (winner->IsRunning()) {
|
||||
UnloadThread(static_cast<u32>(winner->GetProcessorID()));
|
||||
UnloadThread(winner->GetProcessorID());
|
||||
}
|
||||
TransferToCore(winner->GetPriority(), static_cast<s32>(core), winner);
|
||||
TransferToCore(winner->GetPriority(), core, winner);
|
||||
update_thread(winner, sched);
|
||||
return;
|
||||
}
|
||||
@@ -91,9 +104,9 @@ void GlobalScheduler::SelectThread(std::size_t core) {
|
||||
Thread* thread_on_core = scheduled_queue[src_core].front();
|
||||
Thread* to_change = *it;
|
||||
if (thread_on_core->IsRunning() || to_change->IsRunning()) {
|
||||
UnloadThread(static_cast<u32>(src_core));
|
||||
UnloadThread(src_core);
|
||||
}
|
||||
TransferToCore(thread_on_core->GetPriority(), static_cast<s32>(core), thread_on_core);
|
||||
TransferToCore(thread_on_core->GetPriority(), core, thread_on_core);
|
||||
current_thread = thread_on_core;
|
||||
break;
|
||||
}
|
||||
@@ -101,19 +114,30 @@ void GlobalScheduler::SelectThread(std::size_t core) {
|
||||
update_thread(current_thread, sched);
|
||||
}
|
||||
|
||||
/*
|
||||
* YieldThread takes a thread and moves it to the back of the it's priority list
|
||||
* This operation can be redundant and no scheduling is changed if marked as so.
|
||||
*/
|
||||
bool GlobalScheduler::YieldThread(Thread* yielding_thread) {
|
||||
// Note: caller should use critical section, etc.
|
||||
const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID());
|
||||
const u32 priority = yielding_thread->GetPriority();
|
||||
|
||||
// Yield the thread
|
||||
const Thread* const winner = scheduled_queue[core_id].front(priority);
|
||||
ASSERT_MSG(yielding_thread == winner, "Thread yielding without being in front");
|
||||
ASSERT_MSG(yielding_thread == scheduled_queue[core_id].front(priority),
|
||||
"Thread yielding without being in front");
|
||||
scheduled_queue[core_id].yield(priority);
|
||||
|
||||
Thread* winner = scheduled_queue[core_id].front(priority);
|
||||
return AskForReselectionOrMarkRedundant(yielding_thread, winner);
|
||||
}
|
||||
|
||||
/*
|
||||
* YieldThreadAndBalanceLoad takes a thread and moves it to the back of the it's priority list.
|
||||
* Afterwards, tries to pick a suggested thread from the suggested queue that has worse time or
|
||||
* a better priority than the next thread in the core.
|
||||
* This operation can be redundant and no scheduling is changed if marked as so.
|
||||
*/
|
||||
bool GlobalScheduler::YieldThreadAndBalanceLoad(Thread* yielding_thread) {
|
||||
// Note: caller should check if !thread.IsSchedulerOperationRedundant and use critical section,
|
||||
// etc.
|
||||
@@ -154,9 +178,9 @@ bool GlobalScheduler::YieldThreadAndBalanceLoad(Thread* yielding_thread) {
|
||||
if (winner != nullptr) {
|
||||
if (winner != yielding_thread) {
|
||||
if (winner->IsRunning()) {
|
||||
UnloadThread(static_cast<u32>(winner->GetProcessorID()));
|
||||
UnloadThread(winner->GetProcessorID());
|
||||
}
|
||||
TransferToCore(winner->GetPriority(), s32(core_id), winner);
|
||||
TransferToCore(winner->GetPriority(), core_id, winner);
|
||||
}
|
||||
} else {
|
||||
winner = next_thread;
|
||||
@@ -165,6 +189,12 @@ bool GlobalScheduler::YieldThreadAndBalanceLoad(Thread* yielding_thread) {
|
||||
return AskForReselectionOrMarkRedundant(yielding_thread, winner);
|
||||
}
|
||||
|
||||
/*
|
||||
* YieldThreadAndWaitForLoadBalancing takes a thread and moves it out of the scheduling queue
|
||||
* and into the suggested queue. If no thread can be squeduled afterwards in that core,
|
||||
* a suggested thread is obtained instead.
|
||||
* This operation can be redundant and no scheduling is changed if marked as so.
|
||||
*/
|
||||
bool GlobalScheduler::YieldThreadAndWaitForLoadBalancing(Thread* yielding_thread) {
|
||||
// Note: caller should check if !thread.IsSchedulerOperationRedundant and use critical section,
|
||||
// etc.
|
||||
@@ -196,9 +226,9 @@ bool GlobalScheduler::YieldThreadAndWaitForLoadBalancing(Thread* yielding_thread
|
||||
if (winner != nullptr) {
|
||||
if (winner != yielding_thread) {
|
||||
if (winner->IsRunning()) {
|
||||
UnloadThread(static_cast<u32>(winner->GetProcessorID()));
|
||||
UnloadThread(winner->GetProcessorID());
|
||||
}
|
||||
TransferToCore(winner->GetPriority(), static_cast<s32>(core_id), winner);
|
||||
TransferToCore(winner->GetPriority(), core_id, winner);
|
||||
}
|
||||
} else {
|
||||
winner = yielding_thread;
|
||||
@@ -248,9 +278,9 @@ void GlobalScheduler::PreemptThreads() {
|
||||
|
||||
if (winner != nullptr) {
|
||||
if (winner->IsRunning()) {
|
||||
UnloadThread(static_cast<u32>(winner->GetProcessorID()));
|
||||
UnloadThread(winner->GetProcessorID());
|
||||
}
|
||||
TransferToCore(winner->GetPriority(), s32(core_id), winner);
|
||||
TransferToCore(winner->GetPriority(), core_id, winner);
|
||||
current_thread =
|
||||
winner->GetPriority() <= current_thread->GetPriority() ? winner : current_thread;
|
||||
}
|
||||
@@ -281,9 +311,9 @@ void GlobalScheduler::PreemptThreads() {
|
||||
|
||||
if (winner != nullptr) {
|
||||
if (winner->IsRunning()) {
|
||||
UnloadThread(static_cast<u32>(winner->GetProcessorID()));
|
||||
UnloadThread(winner->GetProcessorID());
|
||||
}
|
||||
TransferToCore(winner->GetPriority(), s32(core_id), winner);
|
||||
TransferToCore(winner->GetPriority(), core_id, winner);
|
||||
current_thread = winner;
|
||||
}
|
||||
}
|
||||
@@ -292,30 +322,30 @@ void GlobalScheduler::PreemptThreads() {
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalScheduler::Suggest(u32 priority, std::size_t core, Thread* thread) {
|
||||
void GlobalScheduler::Suggest(u32 priority, u32 core, Thread* thread) {
|
||||
suggested_queue[core].add(thread, priority);
|
||||
}
|
||||
|
||||
void GlobalScheduler::Unsuggest(u32 priority, std::size_t core, Thread* thread) {
|
||||
void GlobalScheduler::Unsuggest(u32 priority, u32 core, Thread* thread) {
|
||||
suggested_queue[core].remove(thread, priority);
|
||||
}
|
||||
|
||||
void GlobalScheduler::Schedule(u32 priority, std::size_t core, Thread* thread) {
|
||||
ASSERT_MSG(thread->GetProcessorID() == s32(core), "Thread must be assigned to this core.");
|
||||
void GlobalScheduler::Schedule(u32 priority, u32 core, Thread* thread) {
|
||||
ASSERT_MSG(thread->GetProcessorID() == core, "Thread must be assigned to this core.");
|
||||
scheduled_queue[core].add(thread, priority);
|
||||
}
|
||||
|
||||
void GlobalScheduler::SchedulePrepend(u32 priority, std::size_t core, Thread* thread) {
|
||||
ASSERT_MSG(thread->GetProcessorID() == s32(core), "Thread must be assigned to this core.");
|
||||
void GlobalScheduler::SchedulePrepend(u32 priority, u32 core, Thread* thread) {
|
||||
ASSERT_MSG(thread->GetProcessorID() == core, "Thread must be assigned to this core.");
|
||||
scheduled_queue[core].add(thread, priority, false);
|
||||
}
|
||||
|
||||
void GlobalScheduler::Reschedule(u32 priority, std::size_t core, Thread* thread) {
|
||||
void GlobalScheduler::Reschedule(u32 priority, u32 core, Thread* thread) {
|
||||
scheduled_queue[core].remove(thread, priority);
|
||||
scheduled_queue[core].add(thread, priority);
|
||||
}
|
||||
|
||||
void GlobalScheduler::Unschedule(u32 priority, std::size_t core, Thread* thread) {
|
||||
void GlobalScheduler::Unschedule(u32 priority, u32 core, Thread* thread) {
|
||||
scheduled_queue[core].remove(thread, priority);
|
||||
}
|
||||
|
||||
@@ -327,19 +357,18 @@ void GlobalScheduler::TransferToCore(u32 priority, s32 destination_core, Thread*
|
||||
}
|
||||
thread->SetProcessorID(destination_core);
|
||||
if (source_core >= 0) {
|
||||
Unschedule(priority, static_cast<u32>(source_core), thread);
|
||||
Unschedule(priority, source_core, thread);
|
||||
}
|
||||
if (destination_core >= 0) {
|
||||
Unsuggest(priority, static_cast<u32>(destination_core), thread);
|
||||
Schedule(priority, static_cast<u32>(destination_core), thread);
|
||||
Unsuggest(priority, destination_core, thread);
|
||||
Schedule(priority, destination_core, thread);
|
||||
}
|
||||
if (source_core >= 0) {
|
||||
Suggest(priority, static_cast<u32>(source_core), thread);
|
||||
Suggest(priority, source_core, thread);
|
||||
}
|
||||
}
|
||||
|
||||
bool GlobalScheduler::AskForReselectionOrMarkRedundant(Thread* current_thread,
|
||||
const Thread* winner) {
|
||||
bool GlobalScheduler::AskForReselectionOrMarkRedundant(Thread* current_thread, Thread* winner) {
|
||||
if (current_thread == winner) {
|
||||
current_thread->IncrementYieldCount();
|
||||
return true;
|
||||
@@ -357,7 +386,9 @@ void GlobalScheduler::Shutdown() {
|
||||
thread_list.clear();
|
||||
}
|
||||
|
||||
Scheduler::Scheduler(Core::System& system, Core::ARM_Interface& cpu_core, std::size_t core_id)
|
||||
GlobalScheduler::~GlobalScheduler() = default;
|
||||
|
||||
Scheduler::Scheduler(Core::System& system, Core::ARM_Interface& cpu_core, u32 core_id)
|
||||
: system(system), cpu_core(cpu_core), core_id(core_id) {}
|
||||
|
||||
Scheduler::~Scheduler() = default;
|
||||
@@ -439,14 +470,14 @@ void Scheduler::SwitchContext() {
|
||||
|
||||
// Load context of new thread
|
||||
if (new_thread) {
|
||||
ASSERT_MSG(new_thread->GetProcessorID() == s32(this->core_id),
|
||||
ASSERT_MSG(new_thread->GetProcessorID() == this->core_id,
|
||||
"Thread must be assigned to this core.");
|
||||
ASSERT_MSG(new_thread->GetStatus() == ThreadStatus::Ready,
|
||||
"Thread must be ready to become running.");
|
||||
|
||||
// Cancel any outstanding wakeup events for this thread
|
||||
new_thread->CancelWakeupTimer();
|
||||
current_thread = SharedFrom(new_thread);
|
||||
current_thread = new_thread;
|
||||
new_thread->SetStatus(ThreadStatus::Running);
|
||||
new_thread->SetIsRunning(true);
|
||||
|
||||
@@ -458,6 +489,7 @@ void Scheduler::SwitchContext() {
|
||||
cpu_core.LoadContext(new_thread->GetContext());
|
||||
cpu_core.SetTlsAddress(new_thread->GetTLSAddress());
|
||||
cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0());
|
||||
cpu_core.ClearExclusiveState();
|
||||
} else {
|
||||
current_thread = nullptr;
|
||||
// Note: We do not reset the current process and current page table when idling because
|
||||
|
||||
@@ -4,12 +4,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/multi_level_queue.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
namespace Core {
|
||||
@@ -27,100 +26,92 @@ public:
|
||||
|
||||
explicit GlobalScheduler(Core::System& system);
|
||||
~GlobalScheduler();
|
||||
|
||||
/// Adds a new thread to the scheduler
|
||||
void AddThread(std::shared_ptr<Thread> thread);
|
||||
void AddThread(SharedPtr<Thread> thread);
|
||||
|
||||
/// Removes a thread from the scheduler
|
||||
void RemoveThread(std::shared_ptr<Thread> thread);
|
||||
void RemoveThread(const Thread* thread);
|
||||
|
||||
/// Returns a list of all threads managed by the scheduler
|
||||
const std::vector<std::shared_ptr<Thread>>& GetThreadList() const {
|
||||
const std::vector<SharedPtr<Thread>>& GetThreadList() const {
|
||||
return thread_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a thread to the suggested queue of a cpu core. Suggested threads may be
|
||||
* picked if no thread is scheduled to run on the core.
|
||||
// Add a thread to the suggested queue of a cpu core. Suggested threads may be
|
||||
// picked if no thread is scheduled to run on the core.
|
||||
void Suggest(u32 priority, u32 core, Thread* thread);
|
||||
|
||||
// Remove a thread to the suggested queue of a cpu core. Suggested threads may be
|
||||
// picked if no thread is scheduled to run on the core.
|
||||
void Unsuggest(u32 priority, u32 core, Thread* thread);
|
||||
|
||||
// Add a thread to the scheduling queue of a cpu core. The thread is added at the
|
||||
// back the queue in its priority level
|
||||
void Schedule(u32 priority, u32 core, Thread* thread);
|
||||
|
||||
// Add a thread to the scheduling queue of a cpu core. The thread is added at the
|
||||
// front the queue in its priority level
|
||||
void SchedulePrepend(u32 priority, u32 core, Thread* thread);
|
||||
|
||||
// Reschedule an already scheduled thread based on a new priority
|
||||
void Reschedule(u32 priority, u32 core, Thread* thread);
|
||||
|
||||
// Unschedule a thread.
|
||||
void Unschedule(u32 priority, u32 core, Thread* thread);
|
||||
|
||||
// Transfers a thread into an specific core. If the destination_core is -1
|
||||
// it will be unscheduled from its source code and added into its suggested
|
||||
// queue.
|
||||
void TransferToCore(u32 priority, s32 destination_core, Thread* thread);
|
||||
|
||||
/*
|
||||
* UnloadThread selects a core and forces it to unload its current thread's context
|
||||
*/
|
||||
void Suggest(u32 priority, std::size_t core, Thread* thread);
|
||||
void UnloadThread(s32 core);
|
||||
|
||||
/**
|
||||
* Remove a thread to the suggested queue of a cpu core. Suggested threads may be
|
||||
* picked if no thread is scheduled to run on the core.
|
||||
/*
|
||||
* SelectThread takes care of selecting the new scheduled thread.
|
||||
* It does it in 3 steps:
|
||||
* - First a thread is selected from the top of the priority queue. If no thread
|
||||
* is obtained then we move to step two, else we are done.
|
||||
* - Second we try to get a suggested thread that's not assigned to any core or
|
||||
* that is not the top thread in that core.
|
||||
* - Third is no suggested thread is found, we do a second pass and pick a running
|
||||
* thread in another core and swap it with its current thread.
|
||||
*/
|
||||
void Unsuggest(u32 priority, std::size_t core, Thread* thread);
|
||||
void SelectThread(u32 core);
|
||||
|
||||
/**
|
||||
* Add a thread to the scheduling queue of a cpu core. The thread is added at the
|
||||
* back the queue in its priority level.
|
||||
*/
|
||||
void Schedule(u32 priority, std::size_t core, Thread* thread);
|
||||
|
||||
/**
|
||||
* Add a thread to the scheduling queue of a cpu core. The thread is added at the
|
||||
* front the queue in its priority level.
|
||||
*/
|
||||
void SchedulePrepend(u32 priority, std::size_t core, Thread* thread);
|
||||
|
||||
/// Reschedule an already scheduled thread based on a new priority
|
||||
void Reschedule(u32 priority, std::size_t core, Thread* thread);
|
||||
|
||||
/// Unschedules a thread.
|
||||
void Unschedule(u32 priority, std::size_t core, Thread* thread);
|
||||
|
||||
/// Selects a core and forces it to unload its current thread's context
|
||||
void UnloadThread(std::size_t core);
|
||||
|
||||
/**
|
||||
* Takes care of selecting the new scheduled thread in three steps:
|
||||
*
|
||||
* 1. First a thread is selected from the top of the priority queue. If no thread
|
||||
* is obtained then we move to step two, else we are done.
|
||||
*
|
||||
* 2. Second we try to get a suggested thread that's not assigned to any core or
|
||||
* that is not the top thread in that core.
|
||||
*
|
||||
* 3. Third is no suggested thread is found, we do a second pass and pick a running
|
||||
* thread in another core and swap it with its current thread.
|
||||
*/
|
||||
void SelectThread(std::size_t core);
|
||||
|
||||
bool HaveReadyThreads(std::size_t core_id) const {
|
||||
bool HaveReadyThreads(u32 core_id) const {
|
||||
return !scheduled_queue[core_id].empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a thread and moves it to the back of the it's priority list.
|
||||
*
|
||||
* @note This operation can be redundant and no scheduling is changed if marked as so.
|
||||
/*
|
||||
* YieldThread takes a thread and moves it to the back of the it's priority list
|
||||
* This operation can be redundant and no scheduling is changed if marked as so.
|
||||
*/
|
||||
bool YieldThread(Thread* thread);
|
||||
|
||||
/**
|
||||
* Takes a thread and moves it to the back of the it's priority list.
|
||||
/*
|
||||
* YieldThreadAndBalanceLoad takes a thread and moves it to the back of the it's priority list.
|
||||
* Afterwards, tries to pick a suggested thread from the suggested queue that has worse time or
|
||||
* a better priority than the next thread in the core.
|
||||
*
|
||||
* @note This operation can be redundant and no scheduling is changed if marked as so.
|
||||
* This operation can be redundant and no scheduling is changed if marked as so.
|
||||
*/
|
||||
bool YieldThreadAndBalanceLoad(Thread* thread);
|
||||
|
||||
/**
|
||||
* Takes a thread and moves it out of the scheduling queue.
|
||||
* and into the suggested queue. If no thread can be scheduled afterwards in that core,
|
||||
/*
|
||||
* YieldThreadAndWaitForLoadBalancing takes a thread and moves it out of the scheduling queue
|
||||
* and into the suggested queue. If no thread can be squeduled afterwards in that core,
|
||||
* a suggested thread is obtained instead.
|
||||
*
|
||||
* @note This operation can be redundant and no scheduling is changed if marked as so.
|
||||
* This operation can be redundant and no scheduling is changed if marked as so.
|
||||
*/
|
||||
bool YieldThreadAndWaitForLoadBalancing(Thread* thread);
|
||||
|
||||
/**
|
||||
* Rotates the scheduling queues of threads at a preemption priority and then does
|
||||
* some core rebalancing. Preemption priorities can be found in the array
|
||||
* 'preemption_priorities'.
|
||||
*
|
||||
* @note This operation happens every 10ms.
|
||||
/*
|
||||
* PreemptThreads this operation rotates the scheduling queues of threads at
|
||||
* a preemption priority and then does some core rebalancing. Preemption priorities
|
||||
* can be found in the array 'preemption_priorities'. This operation happens
|
||||
* every 10ms.
|
||||
*/
|
||||
void PreemptThreads();
|
||||
|
||||
@@ -139,32 +130,25 @@ public:
|
||||
void Shutdown();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Transfers a thread into an specific core. If the destination_core is -1
|
||||
* it will be unscheduled from its source code and added into its suggested
|
||||
* queue.
|
||||
*/
|
||||
void TransferToCore(u32 priority, s32 destination_core, Thread* thread);
|
||||
|
||||
bool AskForReselectionOrMarkRedundant(Thread* current_thread, const Thread* winner);
|
||||
bool AskForReselectionOrMarkRedundant(Thread* current_thread, Thread* winner);
|
||||
|
||||
static constexpr u32 min_regular_priority = 2;
|
||||
std::array<Common::MultiLevelQueue<Thread*, THREADPRIO_COUNT>, NUM_CPU_CORES> scheduled_queue;
|
||||
std::array<Common::MultiLevelQueue<Thread*, THREADPRIO_COUNT>, NUM_CPU_CORES> suggested_queue;
|
||||
std::atomic<bool> is_reselection_pending{false};
|
||||
std::atomic<bool> is_reselection_pending;
|
||||
|
||||
// The priority levels at which the global scheduler preempts threads every 10 ms. They are
|
||||
// ordered from Core 0 to Core 3.
|
||||
// `preemption_priorities` are the priority levels at which the global scheduler
|
||||
// preempts threads every 10 ms. They are ordered from Core 0 to Core 3
|
||||
std::array<u32, NUM_CPU_CORES> preemption_priorities = {59, 59, 59, 62};
|
||||
|
||||
/// Lists all thread ids that aren't deleted/etc.
|
||||
std::vector<std::shared_ptr<Thread>> thread_list;
|
||||
std::vector<SharedPtr<Thread>> thread_list;
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
class Scheduler final {
|
||||
public:
|
||||
explicit Scheduler(Core::System& system, Core::ARM_Interface& cpu_core, std::size_t core_id);
|
||||
explicit Scheduler(Core::System& system, Core::ARM_Interface& cpu_core, u32 core_id);
|
||||
~Scheduler();
|
||||
|
||||
/// Returns whether there are any threads that are ready to run.
|
||||
@@ -197,8 +181,10 @@ public:
|
||||
|
||||
private:
|
||||
friend class GlobalScheduler;
|
||||
|
||||
/// Switches the CPU's active thread context to that of the specified thread
|
||||
/**
|
||||
* Switches the CPU's active thread context to that of the specified thread
|
||||
* @param new_thread The thread to switch to
|
||||
*/
|
||||
void SwitchContext();
|
||||
|
||||
/**
|
||||
@@ -214,14 +200,14 @@ private:
|
||||
*/
|
||||
void UpdateLastContextSwitchTime(Thread* thread, Process* process);
|
||||
|
||||
std::shared_ptr<Thread> current_thread = nullptr;
|
||||
std::shared_ptr<Thread> selected_thread = nullptr;
|
||||
SharedPtr<Thread> current_thread = nullptr;
|
||||
SharedPtr<Thread> selected_thread = nullptr;
|
||||
|
||||
Core::System& system;
|
||||
Core::ARM_Interface& cpu_core;
|
||||
u64 last_context_switch_time = 0;
|
||||
u64 idle_selection_count = 0;
|
||||
const std::size_t core_id;
|
||||
const u32 core_id;
|
||||
|
||||
bool is_context_switch_pending = false;
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Kernel {
|
||||
ServerPort::ServerPort(KernelCore& kernel) : WaitObject{kernel} {}
|
||||
ServerPort::~ServerPort() = default;
|
||||
|
||||
ResultVal<std::shared_ptr<ServerSession>> ServerPort::Accept() {
|
||||
ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() {
|
||||
if (pending_sessions.empty()) {
|
||||
return ERR_NOT_FOUND;
|
||||
}
|
||||
@@ -26,7 +26,7 @@ ResultVal<std::shared_ptr<ServerSession>> ServerPort::Accept() {
|
||||
return MakeResult(std::move(session));
|
||||
}
|
||||
|
||||
void ServerPort::AppendPendingSession(std::shared_ptr<ServerSession> pending_session) {
|
||||
void ServerPort::AppendPendingSession(SharedPtr<ServerSession> pending_session) {
|
||||
pending_sessions.push_back(std::move(pending_session));
|
||||
}
|
||||
|
||||
@@ -41,8 +41,8 @@ void ServerPort::Acquire(Thread* thread) {
|
||||
|
||||
ServerPort::PortPair ServerPort::CreatePortPair(KernelCore& kernel, u32 max_sessions,
|
||||
std::string name) {
|
||||
std::shared_ptr<ServerPort> server_port = std::make_shared<ServerPort>(kernel);
|
||||
std::shared_ptr<ClientPort> client_port = std::make_shared<ClientPort>(kernel);
|
||||
SharedPtr<ServerPort> server_port(new ServerPort(kernel));
|
||||
SharedPtr<ClientPort> client_port(new ClientPort(kernel));
|
||||
|
||||
server_port->name = name + "_Server";
|
||||
client_port->name = name + "_Client";
|
||||
|
||||
@@ -22,11 +22,8 @@ class SessionRequestHandler;
|
||||
|
||||
class ServerPort final : public WaitObject {
|
||||
public:
|
||||
explicit ServerPort(KernelCore& kernel);
|
||||
~ServerPort() override;
|
||||
|
||||
using HLEHandler = std::shared_ptr<SessionRequestHandler>;
|
||||
using PortPair = std::pair<std::shared_ptr<ServerPort>, std::shared_ptr<ClientPort>>;
|
||||
using PortPair = std::pair<SharedPtr<ServerPort>, SharedPtr<ClientPort>>;
|
||||
|
||||
/**
|
||||
* Creates a pair of ServerPort and an associated ClientPort.
|
||||
@@ -55,7 +52,7 @@ public:
|
||||
* Accepts a pending incoming connection on this port. If there are no pending sessions, will
|
||||
* return ERR_NO_PENDING_SESSIONS.
|
||||
*/
|
||||
ResultVal<std::shared_ptr<ServerSession>> Accept();
|
||||
ResultVal<SharedPtr<ServerSession>> Accept();
|
||||
|
||||
/// Whether or not this server port has an HLE handler available.
|
||||
bool HasHLEHandler() const {
|
||||
@@ -77,14 +74,17 @@ public:
|
||||
|
||||
/// Appends a ServerSession to the collection of ServerSessions
|
||||
/// waiting to be accepted by this port.
|
||||
void AppendPendingSession(std::shared_ptr<ServerSession> pending_session);
|
||||
void AppendPendingSession(SharedPtr<ServerSession> pending_session);
|
||||
|
||||
bool ShouldWait(const Thread* thread) const override;
|
||||
void Acquire(Thread* thread) override;
|
||||
|
||||
private:
|
||||
explicit ServerPort(KernelCore& kernel);
|
||||
~ServerPort() override;
|
||||
|
||||
/// ServerSessions waiting to be accepted by the port
|
||||
std::vector<std::shared_ptr<ServerSession>> pending_sessions;
|
||||
std::vector<SharedPtr<ServerSession>> pending_sessions;
|
||||
|
||||
/// This session's HLE request handler template (optional)
|
||||
/// ServerSessions created from this port inherit a reference to this handler.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/client_session.h"
|
||||
@@ -20,32 +19,35 @@
|
||||
#include "core/hle/kernel/server_session.h"
|
||||
#include "core/hle/kernel/session.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
ServerSession::ServerSession(KernelCore& kernel) : WaitObject{kernel} {}
|
||||
ServerSession::~ServerSession() = default;
|
||||
ServerSession::~ServerSession() {
|
||||
// This destructor will be called automatically when the last ServerSession handle is closed by
|
||||
// the emulated application.
|
||||
|
||||
ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kernel,
|
||||
std::shared_ptr<Session> parent,
|
||||
std::string name) {
|
||||
std::shared_ptr<ServerSession> session{std::make_shared<ServerSession>(kernel)};
|
||||
// Decrease the port's connection count.
|
||||
if (parent->port) {
|
||||
parent->port->ConnectionClosed();
|
||||
}
|
||||
|
||||
session->request_event = Core::Timing::CreateEvent(
|
||||
name, [session](u64 userdata, s64 cycles_late) { session->CompleteSyncRequest(); });
|
||||
session->name = std::move(name);
|
||||
session->parent = std::move(parent);
|
||||
parent->server = nullptr;
|
||||
}
|
||||
|
||||
return MakeResult(std::move(session));
|
||||
ResultVal<SharedPtr<ServerSession>> ServerSession::Create(KernelCore& kernel, std::string name) {
|
||||
SharedPtr<ServerSession> server_session(new ServerSession(kernel));
|
||||
|
||||
server_session->name = std::move(name);
|
||||
server_session->parent = nullptr;
|
||||
|
||||
return MakeResult(std::move(server_session));
|
||||
}
|
||||
|
||||
bool ServerSession::ShouldWait(const Thread* thread) const {
|
||||
// Closed sessions should never wait, an error will be returned from svcReplyAndReceive.
|
||||
if (!parent->Client()) {
|
||||
if (parent->client == nullptr)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait if we have no pending requests, or if we're currently handling a request.
|
||||
return pending_requesting_threads.empty() || currently_handling != nullptr;
|
||||
}
|
||||
@@ -67,7 +69,7 @@ void ServerSession::ClientDisconnected() {
|
||||
if (handler) {
|
||||
// Note that after this returns, this server session's hle_handler is
|
||||
// invalidated (set to null).
|
||||
handler->ClientDisconnected(SharedFrom(this));
|
||||
handler->ClientDisconnected(this);
|
||||
}
|
||||
|
||||
// Clean up the list of client threads with pending requests, they are unneeded now that the
|
||||
@@ -124,21 +126,13 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode ServerSession::QueueSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory) {
|
||||
u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(thread->GetTLSAddress()))};
|
||||
std::shared_ptr<Kernel::HLERequestContext> context{
|
||||
std::make_shared<Kernel::HLERequestContext>(SharedFrom(this), std::move(thread))};
|
||||
|
||||
context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
|
||||
request_queue.Push(std::move(context));
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode ServerSession::CompleteSyncRequest() {
|
||||
ASSERT(!request_queue.Empty());
|
||||
|
||||
auto& context = *request_queue.Front();
|
||||
ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
|
||||
// The ServerSession received a sync request, this means that there's new data available
|
||||
// from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or
|
||||
// similar.
|
||||
Kernel::HLERequestContext context(this, thread);
|
||||
u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress());
|
||||
context.PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
|
||||
|
||||
ResultCode result = RESULT_SUCCESS;
|
||||
// If the session has been converted to a domain, handle the domain request
|
||||
@@ -150,27 +144,61 @@ ResultCode ServerSession::CompleteSyncRequest() {
|
||||
result = hle_handler->HandleSyncRequest(context);
|
||||
}
|
||||
|
||||
if (thread->GetStatus() == ThreadStatus::Running) {
|
||||
// Put the thread to sleep until the server replies, it will be awoken in
|
||||
// svcReplyAndReceive for LLE servers.
|
||||
thread->SetStatus(ThreadStatus::WaitIPC);
|
||||
|
||||
if (hle_handler != nullptr) {
|
||||
// For HLE services, we put the request threads to sleep for a short duration to
|
||||
// simulate IPC overhead, but only if the HLE handler didn't put the thread to sleep for
|
||||
// other reasons like an async callback. The IPC overhead is needed to prevent
|
||||
// starvation when a thread only does sync requests to HLE services while a
|
||||
// lower-priority thread is waiting to run.
|
||||
|
||||
// This delay was approximated in a homebrew application by measuring the average time
|
||||
// it takes for svcSendSyncRequest to return when performing the SetLcdForceBlack IPC
|
||||
// request to the GSP:GPU service in a n3DS with firmware 11.6. The measured values have
|
||||
// a high variance and vary between models.
|
||||
static constexpr u64 IPCDelayNanoseconds = 39000;
|
||||
thread->WakeAfterDelay(IPCDelayNanoseconds);
|
||||
} else {
|
||||
// Add the thread to the list of threads that have issued a sync request with this
|
||||
// server.
|
||||
pending_requesting_threads.push_back(std::move(thread));
|
||||
}
|
||||
}
|
||||
|
||||
// If this ServerSession does not have an HLE implementation, just wake up the threads waiting
|
||||
// on it.
|
||||
WakeupAllWaitingThreads();
|
||||
|
||||
// Handle scenario when ConvertToDomain command was issued, as we must do the conversion at the
|
||||
// end of the command such that only commands following this one are handled as domains
|
||||
if (convert_to_domain) {
|
||||
ASSERT_MSG(IsSession(), "ServerSession is already a domain instance.");
|
||||
domain_request_handlers = {hle_handler};
|
||||
convert_to_domain = false;
|
||||
}
|
||||
|
||||
// Some service requests require the thread to block
|
||||
if (!context.IsThreadWaiting()) {
|
||||
context.GetThread().ResumeFromWait();
|
||||
context.GetThread().SetWaitSynchronizationResult(result);
|
||||
}
|
||||
|
||||
request_queue.Pop();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread,
|
||||
Memory::Memory& memory) {
|
||||
Core::System::GetInstance().CoreTiming().ScheduleEvent(20000, request_event, {});
|
||||
return QueueSyncRequest(std::move(thread), memory);
|
||||
}
|
||||
ServerSession::SessionPair ServerSession::CreateSessionPair(KernelCore& kernel,
|
||||
const std::string& name,
|
||||
SharedPtr<ClientPort> port) {
|
||||
auto server_session = ServerSession::Create(kernel, name + "_Server").Unwrap();
|
||||
SharedPtr<ClientSession> client_session(new ClientSession(kernel));
|
||||
client_session->name = name + "_Client";
|
||||
|
||||
std::shared_ptr<Session> parent(new Session);
|
||||
parent->client = client_session.get();
|
||||
parent->server = server_session.get();
|
||||
parent->port = std::move(port);
|
||||
|
||||
client_session->parent = parent;
|
||||
server_session->parent = parent;
|
||||
|
||||
return std::make_pair(std::move(server_session), std::move(client_session));
|
||||
}
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@@ -9,22 +9,17 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/wait_object.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Memory {
|
||||
class Memory;
|
||||
}
|
||||
|
||||
namespace Core::Timing {
|
||||
struct EventType;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class ClientPort;
|
||||
class ClientSession;
|
||||
class HLERequestContext;
|
||||
class KernelCore;
|
||||
class ServerSession;
|
||||
class Session;
|
||||
class SessionRequestHandler;
|
||||
class Thread;
|
||||
@@ -43,15 +38,6 @@ class Thread;
|
||||
*/
|
||||
class ServerSession final : public WaitObject {
|
||||
public:
|
||||
explicit ServerSession(KernelCore& kernel);
|
||||
~ServerSession() override;
|
||||
|
||||
friend class Session;
|
||||
|
||||
static ResultVal<std::shared_ptr<ServerSession>> Create(KernelCore& kernel,
|
||||
std::shared_ptr<Session> parent,
|
||||
std::string name = "Unknown");
|
||||
|
||||
std::string GetTypeName() const override {
|
||||
return "ServerSession";
|
||||
}
|
||||
@@ -73,6 +59,18 @@ public:
|
||||
return parent.get();
|
||||
}
|
||||
|
||||
using SessionPair = std::pair<SharedPtr<ServerSession>, SharedPtr<ClientSession>>;
|
||||
|
||||
/**
|
||||
* Creates a pair of ServerSession and an associated ClientSession.
|
||||
* @param kernel The kernal instance to create the session pair under.
|
||||
* @param name Optional name of the ports.
|
||||
* @param client_port Optional The ClientPort that spawned this session.
|
||||
* @return The created session tuple
|
||||
*/
|
||||
static SessionPair CreateSessionPair(KernelCore& kernel, const std::string& name = "Unknown",
|
||||
SharedPtr<ClientPort> client_port = nullptr);
|
||||
|
||||
/**
|
||||
* Sets the HLE handler for the session. This handler will be called to service IPC requests
|
||||
* instead of the regular IPC machinery. (The regular IPC machinery is currently not
|
||||
@@ -84,13 +82,10 @@ public:
|
||||
|
||||
/**
|
||||
* Handle a sync request from the emulated application.
|
||||
*
|
||||
* @param thread Thread that initiated the request.
|
||||
* @param memory Memory context to handle the sync request under.
|
||||
*
|
||||
* @returns ResultCode from the operation.
|
||||
*/
|
||||
ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory);
|
||||
ResultCode HandleSyncRequest(SharedPtr<Thread> thread);
|
||||
|
||||
bool ShouldWait(const Thread* thread) const override;
|
||||
|
||||
@@ -123,11 +118,18 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
/// Queues a sync request from the emulated application.
|
||||
ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory);
|
||||
explicit ServerSession(KernelCore& kernel);
|
||||
~ServerSession() override;
|
||||
|
||||
/// Completes a sync request from the emulated application.
|
||||
ResultCode CompleteSyncRequest();
|
||||
/**
|
||||
* Creates a server session. The server session can have an optional HLE handler,
|
||||
* which will be invoked to handle the IPC requests that this session receives.
|
||||
* @param kernel The kernel instance to create this server session under.
|
||||
* @param name Optional name of the server session.
|
||||
* @return The created server session
|
||||
*/
|
||||
static ResultVal<SharedPtr<ServerSession>> Create(KernelCore& kernel,
|
||||
std::string name = "Unknown");
|
||||
|
||||
/// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an
|
||||
/// object handle.
|
||||
@@ -145,24 +147,18 @@ private:
|
||||
/// List of threads that are pending a response after a sync request. This list is processed in
|
||||
/// a LIFO manner, thus, the last request will be dispatched first.
|
||||
/// TODO(Subv): Verify if this is indeed processed in LIFO using a hardware test.
|
||||
std::vector<std::shared_ptr<Thread>> pending_requesting_threads;
|
||||
std::vector<SharedPtr<Thread>> pending_requesting_threads;
|
||||
|
||||
/// Thread whose request is currently being handled. A request is considered "handled" when a
|
||||
/// response is sent via svcReplyAndReceive.
|
||||
/// TODO(Subv): Find a better name for this.
|
||||
std::shared_ptr<Thread> currently_handling;
|
||||
SharedPtr<Thread> currently_handling;
|
||||
|
||||
/// When set to True, converts the session to a domain at the end of the command
|
||||
bool convert_to_domain{};
|
||||
|
||||
/// The name of this session (optional)
|
||||
std::string name;
|
||||
|
||||
/// Core timing event used to schedule the service request at some point in the future
|
||||
std::shared_ptr<Core::Timing::EventType> request_event;
|
||||
|
||||
/// Queue of scheduled service requests
|
||||
Common::MPSCQueue<std::shared_ptr<Kernel::HLERequestContext>> request_queue;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -1,36 +1,12 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Copyright 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/hle/kernel/client_session.h"
|
||||
#include "core/hle/kernel/server_session.h"
|
||||
#include "core/hle/kernel/session.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
Session::Session(KernelCore& kernel) : WaitObject{kernel} {}
|
||||
Session::~Session() = default;
|
||||
|
||||
Session::SessionPair Session::Create(KernelCore& kernel, std::string name) {
|
||||
auto session{std::make_shared<Session>(kernel)};
|
||||
auto client_session{Kernel::ClientSession::Create(kernel, session, name + "_Client").Unwrap()};
|
||||
auto server_session{Kernel::ServerSession::Create(kernel, session, name + "_Server").Unwrap()};
|
||||
|
||||
session->name = std::move(name);
|
||||
session->client = client_session;
|
||||
session->server = server_session;
|
||||
|
||||
return std::make_pair(std::move(client_session), std::move(server_session));
|
||||
}
|
||||
|
||||
bool Session::ShouldWait(const Thread* thread) const {
|
||||
UNIMPLEMENTED();
|
||||
return {};
|
||||
}
|
||||
|
||||
void Session::Acquire(Thread* thread) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
Session::Session() {}
|
||||
Session::~Session() {}
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -1,64 +1,27 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "core/hle/kernel/wait_object.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class ClientSession;
|
||||
class ClientPort;
|
||||
class ServerSession;
|
||||
|
||||
/**
|
||||
* Parent structure to link the client and server endpoints of a session with their associated
|
||||
* client port.
|
||||
* client port. The client port need not exist, as is the case for portless sessions like the
|
||||
* FS File and Directory sessions. When one of the endpoints of a session is destroyed, its
|
||||
* corresponding field in this structure will be set to nullptr.
|
||||
*/
|
||||
class Session final : public WaitObject {
|
||||
class Session final {
|
||||
public:
|
||||
explicit Session(KernelCore& kernel);
|
||||
~Session() override;
|
||||
|
||||
using SessionPair = std::pair<std::shared_ptr<ClientSession>, std::shared_ptr<ServerSession>>;
|
||||
|
||||
static SessionPair Create(KernelCore& kernel, std::string name = "Unknown");
|
||||
|
||||
std::string GetName() const override {
|
||||
return name;
|
||||
}
|
||||
|
||||
static constexpr HandleType HANDLE_TYPE = HandleType::Session;
|
||||
HandleType GetHandleType() const override {
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
bool ShouldWait(const Thread* thread) const override;
|
||||
|
||||
void Acquire(Thread* thread) override;
|
||||
|
||||
std::shared_ptr<ClientSession> Client() {
|
||||
if (auto result{client.lock()}) {
|
||||
return result;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::shared_ptr<ServerSession> Server() {
|
||||
if (auto result{server.lock()}) {
|
||||
return result;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
std::weak_ptr<ClientSession> client;
|
||||
std::weak_ptr<ServerSession> server;
|
||||
ClientSession* client = nullptr; ///< The client endpoint of the session.
|
||||
ServerSession* server = nullptr; ///< The server endpoint of the session.
|
||||
SharedPtr<ClientPort> port; ///< The port that this session is associated with (optional).
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -15,12 +15,11 @@ namespace Kernel {
|
||||
SharedMemory::SharedMemory(KernelCore& kernel) : Object{kernel} {}
|
||||
SharedMemory::~SharedMemory() = default;
|
||||
|
||||
std::shared_ptr<SharedMemory> SharedMemory::Create(KernelCore& kernel, Process* owner_process,
|
||||
u64 size, MemoryPermission permissions,
|
||||
MemoryPermission other_permissions,
|
||||
VAddr address, MemoryRegion region,
|
||||
std::string name) {
|
||||
std::shared_ptr<SharedMemory> shared_memory = std::make_shared<SharedMemory>(kernel);
|
||||
SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, Process* owner_process, u64 size,
|
||||
MemoryPermission permissions,
|
||||
MemoryPermission other_permissions, VAddr address,
|
||||
MemoryRegion region, std::string name) {
|
||||
SharedPtr<SharedMemory> shared_memory(new SharedMemory(kernel));
|
||||
|
||||
shared_memory->owner_process = owner_process;
|
||||
shared_memory->name = std::move(name);
|
||||
@@ -59,10 +58,10 @@ std::shared_ptr<SharedMemory> SharedMemory::Create(KernelCore& kernel, Process*
|
||||
return shared_memory;
|
||||
}
|
||||
|
||||
std::shared_ptr<SharedMemory> SharedMemory::CreateForApplet(
|
||||
SharedPtr<SharedMemory> SharedMemory::CreateForApplet(
|
||||
KernelCore& kernel, std::shared_ptr<Kernel::PhysicalMemory> heap_block, std::size_t offset,
|
||||
u64 size, MemoryPermission permissions, MemoryPermission other_permissions, std::string name) {
|
||||
std::shared_ptr<SharedMemory> shared_memory = std::make_shared<SharedMemory>(kernel);
|
||||
SharedPtr<SharedMemory> shared_memory(new SharedMemory(kernel));
|
||||
|
||||
shared_memory->owner_process = nullptr;
|
||||
shared_memory->name = std::move(name);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
@@ -32,9 +33,6 @@ enum class MemoryPermission : u32 {
|
||||
|
||||
class SharedMemory final : public Object {
|
||||
public:
|
||||
explicit SharedMemory(KernelCore& kernel);
|
||||
~SharedMemory() override;
|
||||
|
||||
/**
|
||||
* Creates a shared memory object.
|
||||
* @param kernel The kernel instance to create a shared memory instance under.
|
||||
@@ -48,12 +46,11 @@ public:
|
||||
* linear heap.
|
||||
* @param name Optional object name, used for debugging purposes.
|
||||
*/
|
||||
static std::shared_ptr<SharedMemory> Create(KernelCore& kernel, Process* owner_process,
|
||||
u64 size, MemoryPermission permissions,
|
||||
MemoryPermission other_permissions,
|
||||
VAddr address = 0,
|
||||
MemoryRegion region = MemoryRegion::BASE,
|
||||
std::string name = "Unknown");
|
||||
static SharedPtr<SharedMemory> Create(KernelCore& kernel, Process* owner_process, u64 size,
|
||||
MemoryPermission permissions,
|
||||
MemoryPermission other_permissions, VAddr address = 0,
|
||||
MemoryRegion region = MemoryRegion::BASE,
|
||||
std::string name = "Unknown");
|
||||
|
||||
/**
|
||||
* Creates a shared memory object from a block of memory managed by an HLE applet.
|
||||
@@ -66,7 +63,7 @@ public:
|
||||
* block.
|
||||
* @param name Optional object name, used for debugging purposes.
|
||||
*/
|
||||
static std::shared_ptr<SharedMemory> CreateForApplet(
|
||||
static SharedPtr<SharedMemory> CreateForApplet(
|
||||
KernelCore& kernel, std::shared_ptr<Kernel::PhysicalMemory> heap_block, std::size_t offset,
|
||||
u64 size, MemoryPermission permissions, MemoryPermission other_permissions,
|
||||
std::string name = "Unknown Applet");
|
||||
@@ -133,6 +130,9 @@ public:
|
||||
const u8* GetPointer(std::size_t offset = 0) const;
|
||||
|
||||
private:
|
||||
explicit SharedMemory(KernelCore& kernel);
|
||||
~SharedMemory() override;
|
||||
|
||||
/// Backing memory for this shared memory block.
|
||||
std::shared_ptr<PhysicalMemory> backing_block;
|
||||
/// Offset into the backing block for this shared memory.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user