Compare commits

..

1 Commits

Author SHA1 Message Date
Fernando Sahmkow
82045d1f7c Optimize CallMethodMaxwell3D 2019-04-02 22:10:21 -04:00
357 changed files with 6253 additions and 12420 deletions

6
.gitmodules vendored
View File

@@ -40,9 +40,3 @@
[submodule "Vulkan-Headers"]
path = externals/Vulkan-Headers
url = https://github.com/KhronosGroup/Vulkan-Headers.git
[submodule "externals/zstd"]
path = externals/zstd
url = https://github.com/facebook/zstd
[submodule "sirit"]
path = externals/sirit
url = https://github.com/ReinUsesLisp/sirit

View File

@@ -24,7 +24,7 @@ matrix:
- os: osx
env: NAME="macos build"
sudo: false
osx_image: xcode10.2
osx_image: xcode10.1
install: "./.travis/macos/deps.sh"
script: "./.travis/macos/build.sh"
after_success: "./.travis/macos/upload.sh"

View File

@@ -1,3 +1,3 @@
#!/bin/bash -ex
mkdir "$HOME/.ccache" || true
docker run --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache yuzuemu/build-environments:linux-mingw /bin/bash -ex /yuzu/.travis/linux-mingw/docker.sh
docker run --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache ubuntu:18.04 /bin/bash -ex /yuzu/.travis/linux-mingw/docker.sh

View File

@@ -1,3 +1,3 @@
#!/bin/sh -ex
docker pull yuzuemu/build-environments:linux-mingw
docker pull ubuntu:18.04

View File

@@ -1,6 +1,16 @@
#!/bin/bash -ex
cd /yuzu
MINGW_PACKAGES="sdl2-mingw-w64 qt5base-mingw-w64 qt5tools-mingw-w64 libsamplerate-mingw-w64 qt5multimedia-mingw-w64"
apt-get update
apt-get install -y gpg wget git python3-pip python ccache g++-mingw-w64-x86-64 gcc-mingw-w64-x86-64 mingw-w64-tools cmake
echo 'deb http://ppa.launchpad.net/tobydox/mingw-w64/ubuntu bionic main ' > /etc/apt/sources.list.d/extras.list
apt-key adv --keyserver keyserver.ubuntu.com --recv '72931B477E22FEFD47F8DECE02FE5F12ADDE29B2'
apt-get update
apt-get install -y ${MINGW_PACKAGES}
# fix a problem in current MinGW headers
wget -q https://raw.githubusercontent.com/Alexpux/mingw-w64/d0d7f784833bbb0b2d279310ddc6afb52fe47a46/mingw-w64-headers/crt/errno.h -O /usr/x86_64-w64-mingw32/include/errno.h
# override Travis CI unreasonable ccache size
echo 'max_size = 3.0G' > "$HOME/.ccache/ccache.conf"
@@ -13,8 +23,8 @@ echo '' >> /bin/cmd
chmod +x /bin/cmd
mkdir build && cd build
cmake .. -G Ninja -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release
ninja
cmake .. -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release
make -j4
# Clean up the dirty hacks
rm /bin/uname && mv /bin/uname1 /bin/uname

View File

@@ -1,4 +1,4 @@
#!/bin/bash -ex
mkdir -p "$HOME/.ccache"
docker run -e ENABLE_COMPATIBILITY_REPORTING --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache yuzuemu/build-environments:linux-fresh /bin/bash /yuzu/.travis/linux/docker.sh
docker run -e ENABLE_COMPATIBILITY_REPORTING --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache ubuntu:18.04 /bin/bash /yuzu/.travis/linux/docker.sh

View File

@@ -1,3 +1,3 @@
#!/bin/sh -ex
docker pull yuzuemu/build-environments:linux-fresh
docker pull ubuntu:18.04

View File

@@ -1,9 +1,12 @@
#!/bin/bash -ex
apt-get update
apt-get install --no-install-recommends -y build-essential git libqt5opengl5-dev libsdl2-dev libssl-dev python qtbase5-dev qtwebengine5-dev wget cmake ninja-build ccache
cd /yuzu
mkdir build && cd build
cmake .. -G Ninja -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -G Ninja
ninja
ccache -s

View File

@@ -7,7 +7,6 @@ export Qt5_DIR=$(brew --prefix)/opt/qt5
export UNICORNDIR=$(pwd)/externals/unicorn
export PATH="/usr/local/opt/ccache/libexec:$PATH"
# TODO: Build using ninja instead of make
mkdir build && cd build
cmake --version
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DUSE_DISCORD_PRESENCE=ON

View File

@@ -1,6 +1,5 @@
#!/bin/sh -ex
brew update
brew install p7zip qt5 sdl2 ccache
brew install dylibbundler p7zip qt5 sdl2 ccache
brew outdated cmake || brew upgrade cmake
pip3 install macpack

View File

@@ -11,18 +11,100 @@ mkdir "$REV_NAME"
cp build/bin/yuzu-cmd "$REV_NAME"
cp -r build/bin/yuzu.app "$REV_NAME"
# move libs into folder for deployment
macpack "${REV_NAME}/yuzu.app/Contents/MacOS/yuzu" -d "../Frameworks"
# move qt frameworks into app bundle for deployment
$(brew --prefix)/opt/qt5/bin/macdeployqt "${REV_NAME}/yuzu.app" -executable="${REV_NAME}/yuzu.app/Contents/MacOS/yuzu"
# move qt libs into app bundle for deployment
$(brew --prefix)/opt/qt5/bin/macdeployqt "${REV_NAME}/yuzu.app"
# move libs into folder for deployment
macpack "${REV_NAME}/yuzu-cmd" -d "libs"
# move SDL2 libs into folder for deployment
dylibbundler -b -x "${REV_NAME}/yuzu-cmd" -cd -d "${REV_NAME}/libs" -p "@executable_path/libs/"
# Make the changes to make the yuzu app standalone (i.e. not dependent on the current brew installation).
# To do this, the absolute references to each and every QT framework must be re-written to point to the local frameworks
# (in the Contents/Frameworks folder).
# The "install_name_tool" is used to do so.
# Coreutils is a hack to coerce Homebrew to point to the absolute Cellar path (symlink dereferenced). i.e:
# ls -l /usr/local/opt/qt5:: /usr/local/opt/qt5 -> ../Cellar/qt5/5.6.1-1
# grealpath ../Cellar/qt5/5.6.1-1:: /usr/local/Cellar/qt5/5.6.1-1
brew install coreutils || brew upgrade coreutils || true
REV_NAME_ALT=$REV_NAME/
# grealpath is located in coreutils, there is no "realpath" for OS X :(
QT_BREWS_PATH=$(grealpath "$(brew --prefix qt5)")
BREW_PATH=$(brew --prefix)
QT_VERSION_NUM=5
$BREW_PATH/opt/qt5/bin/macdeployqt "${REV_NAME_ALT}yuzu.app" \
-executable="${REV_NAME_ALT}yuzu.app/Contents/MacOS/yuzu"
# These are the files that macdeployqt packed into Contents/Frameworks/ - we don't want those, so we replace them.
declare -a macos_libs=("QtCore" "QtWidgets" "QtGui" "QtOpenGL" "QtPrintSupport")
for macos_lib in "${macos_libs[@]}"
do
SC_FRAMEWORK_PART=$macos_lib.framework/Versions/$QT_VERSION_NUM/$macos_lib
# Replace macdeployqt versions of the Frameworks with our own (from /usr/local/opt/qt5/lib/)
cp "$BREW_PATH/opt/qt5/lib/$SC_FRAMEWORK_PART" "${REV_NAME_ALT}yuzu.app/Contents/Frameworks/$SC_FRAMEWORK_PART"
# Replace references within the embedded Framework files with "internal" versions.
for macos_lib2 in "${macos_libs[@]}"
do
# Since brew references both the non-symlinked and symlink paths of QT5, it needs to be duplicated.
# /usr/local/Cellar/qt5/5.6.1-1/lib and /usr/local/opt/qt5/lib both resolve to the same files.
# So the two lines below are effectively duplicates when resolved as a path, but as strings, they aren't.
RM_FRAMEWORK_PART=$macos_lib2.framework/Versions/$QT_VERSION_NUM/$macos_lib2
install_name_tool -change \
$QT_BREWS_PATH/lib/$RM_FRAMEWORK_PART \
@executable_path/../Frameworks/$RM_FRAMEWORK_PART \
"${REV_NAME_ALT}yuzu.app/Contents/Frameworks/$SC_FRAMEWORK_PART"
install_name_tool -change \
"$BREW_PATH/opt/qt5/lib/$RM_FRAMEWORK_PART" \
@executable_path/../Frameworks/$RM_FRAMEWORK_PART \
"${REV_NAME_ALT}yuzu.app/Contents/Frameworks/$SC_FRAMEWORK_PART"
done
done
# Handles `This application failed to start because it could not find or load the Qt platform plugin "cocoa"`
# Which manifests itself as:
# "Exception Type: EXC_CRASH (SIGABRT) | Exception Codes: 0x0000000000000000, 0x0000000000000000 | Exception Note: EXC_CORPSE_NOTIFY"
# There may be more dylibs needed to be fixed...
declare -a macos_plugins=("Plugins/platforms/libqcocoa.dylib")
for macos_lib in "${macos_plugins[@]}"
do
install_name_tool -id @executable_path/../$macos_lib "${REV_NAME_ALT}yuzu.app/Contents/$macos_lib"
for macos_lib2 in "${macos_libs[@]}"
do
RM_FRAMEWORK_PART=$macos_lib2.framework/Versions/$QT_VERSION_NUM/$macos_lib2
install_name_tool -change \
$QT_BREWS_PATH/lib/$RM_FRAMEWORK_PART \
@executable_path/../Frameworks/$RM_FRAMEWORK_PART \
"${REV_NAME_ALT}yuzu.app/Contents/$macos_lib"
install_name_tool -change \
"$BREW_PATH/opt/qt5/lib/$RM_FRAMEWORK_PART" \
@executable_path/../Frameworks/$RM_FRAMEWORK_PART \
"${REV_NAME_ALT}yuzu.app/Contents/$macos_lib"
done
done
for macos_lib in "${macos_libs[@]}"
do
# Debugging info for Travis-CI
otool -L "${REV_NAME_ALT}yuzu.app/Contents/Frameworks/$macos_lib.framework/Versions/$QT_VERSION_NUM/$macos_lib"
done
# Make the yuzu.app application launch a debugging terminal.
# Store away the actual binary
mv ${REV_NAME_ALT}yuzu.app/Contents/MacOS/yuzu ${REV_NAME_ALT}yuzu.app/Contents/MacOS/yuzu-bin
cat > ${REV_NAME_ALT}yuzu.app/Contents/MacOS/yuzu <<EOL
#!/usr/bin/env bash
cd "\`dirname "\$0"\`"
chmod +x yuzu-bin
open yuzu-bin --args "\$@"
EOL
# Content that will serve as the launching script for yuzu (within the .app folder)
# Make the launching script executable
chmod +x ${REV_NAME}/yuzu.app/Contents/MacOS/yuzu
# Verify loader instructions
find "$REV_NAME" -exec otool -L {} \;
chmod +x ${REV_NAME_ALT}yuzu.app/Contents/MacOS/yuzu
. .travis/common/post-upload.sh

View File

@@ -132,7 +132,7 @@ find_package(Threads REQUIRED)
if (ENABLE_SDL2)
if (YUZU_USE_BUNDLED_SDL2)
# Detect toolchain and platform
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1930) AND ARCHITECTURE_x86_64)
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1920) AND ARCHITECTURE_x86_64)
set(SDL2_VER "SDL2-2.0.8")
else()
message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable YUZU_USE_BUNDLED_SDL2 and provide your own.")
@@ -165,7 +165,7 @@ if (YUZU_USE_BUNDLED_UNICORN)
if (MSVC)
message(STATUS "unicorn not found, falling back to bundled")
# Detect toolchain and platform
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1930) AND ARCHITECTURE_x86_64)
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1920) AND ARCHITECTURE_x86_64)
set(UNICORN_VER "unicorn-yuzu")
else()
message(FATAL_ERROR "No bundled Unicorn binaries for your toolchain. Disable YUZU_USE_BUNDLED_UNICORN and provide your own.")
@@ -233,7 +233,7 @@ endif()
if (ENABLE_QT)
if (YUZU_USE_BUNDLED_QT)
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1930) AND ARCHITECTURE_x86_64)
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1920) AND ARCHITECTURE_x86_64)
set(QT_VER qt-5.12.0-msvc2017_64)
else()
message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable YUZU_USE_BUNDLED_QT and provide your own.")
@@ -309,7 +309,7 @@ if (CLANG_FORMAT)
set(CCOMMENT "Running clang format against all the .h and .cpp files in src/")
if (WIN32)
add_custom_target(clang-format
COMMAND powershell.exe -Command "Get-ChildItem '${SRCS}/*' -Include *.cpp,*.h -Recurse | Foreach {&'${CLANG_FORMAT}' -i $_.fullname}"
COMMAND powershell.exe -Command "Get-ChildItem ${SRCS}/* -Include *.cpp,*.h -Recurse | Foreach {${CLANG_FORMAT} -i $_.fullname}"
COMMENT ${CCOMMENT})
elseif(MINGW)
add_custom_target(clang-format

View File

@@ -19,7 +19,7 @@ set(BUILD_VERSION "0")
if (BUILD_REPOSITORY)
# regex capture the string nightly or canary into CMAKE_MATCH_1
string(REGEX MATCH "yuzu-emu/yuzu-?(.*)" OUTVAR ${BUILD_REPOSITORY})
if ("${CMAKE_MATCH_COUNT}" GREATER 0)
if (${CMAKE_MATCH_COUNT} GREATER 0)
# capitalize the first letter of each word in the repo name.
string(REPLACE "-" ";" REPO_NAME_LIST ${CMAKE_MATCH_1})
foreach(WORD ${REPO_NAME_LIST})

View File

@@ -7,10 +7,6 @@ include(DownloadExternals)
add_library(catch-single-include INTERFACE)
target_include_directories(catch-single-include INTERFACE catch/single_include)
# libfmt
add_subdirectory(fmt)
add_library(fmt::fmt ALIAS fmt)
# Dynarmic
if (ARCHITECTURE_x86_64)
set(DYNARMIC_TESTS OFF)
@@ -18,6 +14,10 @@ if (ARCHITECTURE_x86_64)
add_subdirectory(dynarmic)
endif()
# libfmt
add_subdirectory(fmt)
add_library(fmt::fmt ALIAS fmt)
# getopt
if (MSVC)
add_subdirectory(getopt)
@@ -49,10 +49,6 @@ add_subdirectory(open_source_archives EXCLUDE_FROM_ALL)
add_library(unicorn-headers INTERFACE)
target_include_directories(unicorn-headers INTERFACE ./unicorn/include)
# Zstandard
add_subdirectory(zstd/build/cmake EXCLUDE_FROM_ALL)
target_include_directories(libzstd_static INTERFACE ./zstd/lib)
# SoundTouch
add_subdirectory(soundtouch)
@@ -72,11 +68,6 @@ if (USE_DISCORD_PRESENCE)
target_include_directories(discord-rpc INTERFACE ./discord-rpc/include)
endif()
# Sirit
if (ENABLE_VULKAN)
add_subdirectory(sirit)
endif()
if (ENABLE_WEB_SERVICE)
# LibreSSL
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")

1
externals/sirit vendored

Submodule externals/sirit deleted from f7c4b07a7e

1
externals/zstd vendored

Submodule externals/zstd deleted from 470344d33e

View File

@@ -18,32 +18,15 @@ if (MSVC)
# Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors.
add_definitions(-DWIN32_LEAN_AND_MEAN)
# Ensure that projects build with Unicode support.
add_definitions(-DUNICODE -D_UNICODE)
# /W3 - Level 3 warnings
# /MP - Multi-threaded compilation
# /Zi - Output debugging information
# /Zo - Enhanced debug info for optimized builds
# /permissive- - Enables stricter C++ standards conformance checks
# /EHsc - C++-only exception handling semantics
# /volatile:iso - Use strict standards-compliant volatile semantics.
# /Zc:externConstexpr - Allow extern constexpr variables to have external linkage, like the standard mandates
# /Zc:inline - Let codegen omit inline functions in object files
# /Zc:throwingNew - Let codegen assume `operator new` (without std::nothrow) will never return null
add_compile_options(
/W3
/MP
/Zi
/Zo
/permissive-
/EHsc
/std:c++latest
/volatile:iso
/Zc:externConstexpr
/Zc:inline
/Zc:throwingNew
)
# /W3 - Level 3 warnings
# /MP - Multi-threaded compilation
# /Zi - Output debugging information
# /Zo - enhanced debug info for optimized builds
# /permissive- - enables stricter C++ standards conformance checks
# /EHsc - C++-only exception handling semantics
# /Zc:throwingNew - let codegen assume `operator new` will never return null
# /Zc:inline - let codegen omit inline functions in object files
add_compile_options(/W3 /MP /Zi /Zo /permissive- /EHsc /std:c++latest /Zc:throwingNew,inline)
# /GS- - No stack buffer overflow checks
add_compile_options("$<$<CONFIG:Release>:/GS->")
@@ -51,10 +34,7 @@ if (MSVC)
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "/DEBUG /MANIFEST:NO" CACHE STRING "" FORCE)
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE)
else()
add_compile_options(
-Wall
-Wno-attributes
)
add_compile_options("-Wno-attributes")
if (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang)
add_compile_options("-stdlib=libc++")

View File

@@ -91,8 +91,6 @@ add_library(common STATIC
logging/log.h
logging/text_formatter.cpp
logging/text_formatter.h
lz4_compression.cpp
lz4_compression.h
math_util.h
memory_hook.cpp
memory_hook.h
@@ -125,8 +123,6 @@ add_library(common STATIC
uint128.h
vector_math.h
web_result.h
zstd_compression.cpp
zstd_compression.h
)
if(ARCHITECTURE_x86_64)
@@ -140,4 +136,3 @@ endif()
create_target_directory_groups(common)
target_link_libraries(common PUBLIC Boost::boost fmt microprofile)
target_link_libraries(common PRIVATE lz4_static libzstd_static)

View File

@@ -57,21 +57,3 @@ __declspec(noinline, noreturn)
#define UNIMPLEMENTED_IF(cond) ASSERT_MSG(!(cond), "Unimplemented code!")
#define UNIMPLEMENTED_IF_MSG(cond, ...) ASSERT_MSG(!(cond), __VA_ARGS__)
// If the assert is ignored, execute _b_
#define ASSERT_OR_EXECUTE(_a_, _b_) \
do { \
ASSERT(_a_); \
if (!(_a_)) { \
_b_ \
} \
} while (0)
// If the assert is ignored, execute _b_
#define ASSERT_OR_EXECUTE_MSG(_a_, _b_, ...) \
do { \
ASSERT_MSG(_a_, __VA_ARGS__); \
if (!(_a_)) { \
_b_ \
} \
} while (0)

View File

@@ -32,7 +32,7 @@ inline u32 CountLeadingZeroes32(u32 value) {
return 32;
}
inline u32 CountLeadingZeroes64(u64 value) {
inline u64 CountLeadingZeroes64(u64 value) {
unsigned long leading_zero = 0;
if (_BitScanReverse64(&leading_zero, value) != 0) {
@@ -47,15 +47,15 @@ inline u32 CountLeadingZeroes32(u32 value) {
return 32;
}
return static_cast<u32>(__builtin_clz(value));
return __builtin_clz(value);
}
inline u32 CountLeadingZeroes64(u64 value) {
inline u64 CountLeadingZeroes64(u64 value) {
if (value == 0) {
return 64;
}
return static_cast<u32>(__builtin_clzll(value));
return __builtin_clzll(value);
}
#endif
@@ -70,7 +70,7 @@ inline u32 CountTrailingZeroes32(u32 value) {
return 32;
}
inline u32 CountTrailingZeroes64(u64 value) {
inline u64 CountTrailingZeroes64(u64 value) {
unsigned long trailing_zero = 0;
if (_BitScanForward64(&trailing_zero, value) != 0) {
@@ -85,15 +85,15 @@ inline u32 CountTrailingZeroes32(u32 value) {
return 32;
}
return static_cast<u32>(__builtin_ctz(value));
return __builtin_ctz(value);
}
inline u32 CountTrailingZeroes64(u64 value) {
inline u64 CountTrailingZeroes64(u64 value) {
if (value == 0) {
return 64;
}
return static_cast<u32>(__builtin_ctzll(value));
return __builtin_ctzll(value);
}
#endif

View File

@@ -16,22 +16,22 @@ DetachedTasks::DetachedTasks() {
}
void DetachedTasks::WaitForAllTasks() {
std::unique_lock lock{mutex};
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock, [this]() { return count == 0; });
}
DetachedTasks::~DetachedTasks() {
std::unique_lock lock{mutex};
std::unique_lock<std::mutex> lock(mutex);
ASSERT(count == 0);
instance = nullptr;
}
void DetachedTasks::AddTask(std::function<void()> task) {
std::unique_lock lock{instance->mutex};
std::unique_lock<std::mutex> lock(instance->mutex);
++instance->count;
std::thread([task{std::move(task)}]() {
task();
std::unique_lock lock{instance->mutex};
std::unique_lock<std::mutex> lock(instance->mutex);
--instance->count;
std::notify_all_at_thread_exit(instance->cv, std::move(lock));
})

View File

@@ -46,12 +46,12 @@ public:
}
void AddBackend(std::unique_ptr<Backend> backend) {
std::lock_guard lock{writing_mutex};
std::lock_guard<std::mutex> lock(writing_mutex);
backends.push_back(std::move(backend));
}
void RemoveBackend(std::string_view backend_name) {
std::lock_guard lock{writing_mutex};
std::lock_guard<std::mutex> lock(writing_mutex);
const auto it =
std::remove_if(backends.begin(), backends.end(),
[&backend_name](const auto& i) { return backend_name == i->GetName(); });
@@ -80,7 +80,7 @@ private:
backend_thread = std::thread([&] {
Entry entry;
auto write_logs = [&](Entry& e) {
std::lock_guard lock{writing_mutex};
std::lock_guard<std::mutex> lock(writing_mutex);
for (const auto& backend : backends) {
backend->Write(e);
}

View File

@@ -1,76 +0,0 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <lz4hc.h>
#include "common/assert.h"
#include "common/lz4_compression.h"
namespace Common::Compression {
std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size) {
ASSERT_MSG(source_size <= LZ4_MAX_INPUT_SIZE, "Source size exceeds LZ4 maximum input size");
const auto source_size_int = static_cast<int>(source_size);
const int max_compressed_size = LZ4_compressBound(source_size_int);
std::vector<u8> compressed(max_compressed_size);
const int compressed_size = LZ4_compress_default(reinterpret_cast<const char*>(source),
reinterpret_cast<char*>(compressed.data()),
source_size_int, max_compressed_size);
if (compressed_size <= 0) {
// Compression failed
return {};
}
compressed.resize(compressed_size);
return compressed;
}
std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size,
s32 compression_level) {
ASSERT_MSG(source_size <= LZ4_MAX_INPUT_SIZE, "Source size exceeds LZ4 maximum input size");
compression_level = std::clamp(compression_level, LZ4HC_CLEVEL_MIN, LZ4HC_CLEVEL_MAX);
const auto source_size_int = static_cast<int>(source_size);
const int max_compressed_size = LZ4_compressBound(source_size_int);
std::vector<u8> compressed(max_compressed_size);
const int compressed_size = LZ4_compress_HC(
reinterpret_cast<const char*>(source), reinterpret_cast<char*>(compressed.data()),
source_size_int, max_compressed_size, compression_level);
if (compressed_size <= 0) {
// Compression failed
return {};
}
compressed.resize(compressed_size);
return compressed;
}
std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size) {
return CompressDataLZ4HC(source, source_size, LZ4HC_CLEVEL_MAX);
}
std::vector<u8> DecompressDataLZ4(const std::vector<u8>& compressed,
std::size_t uncompressed_size) {
std::vector<u8> uncompressed(uncompressed_size);
const int size_check = LZ4_decompress_safe(reinterpret_cast<const char*>(compressed.data()),
reinterpret_cast<char*>(uncompressed.data()),
static_cast<int>(compressed.size()),
static_cast<int>(uncompressed.size()));
if (static_cast<int>(uncompressed_size) != size_check) {
// Decompression failed
return {};
}
return uncompressed;
}
} // namespace Common::Compression

View File

@@ -1,57 +0,0 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <vector>
#include "common/common_types.h"
namespace Common::Compression {
/**
* Compresses a source memory region with LZ4 and returns the compressed data in a vector.
*
* @param source the uncompressed source memory region.
* @param source_size the size in bytes of the uncompressed source memory region.
*
* @return the compressed data.
*/
std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size);
/**
* Utilizes the LZ4 subalgorithm LZ4HC with the specified compression level. Higher compression
* levels result in a smaller compressed size, but require more CPU time for compression. The
* compression level has almost no impact on decompression speed. Data compressed with LZ4HC can
* also be decompressed with the default LZ4 decompression.
*
* @param source the uncompressed source memory region.
* @param source_size the size in bytes of the uncompressed source memory region.
* @param compression_level the used compression level. Should be between 3 and 12.
*
* @return the compressed data.
*/
std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size, s32 compression_level);
/**
* Utilizes the LZ4 subalgorithm LZ4HC with the highest possible compression level.
*
* @param source the uncompressed source memory region.
* @param source_size the size in bytes of the uncompressed source memory region.
*
* @return the compressed data.
*/
std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size);
/**
* Decompresses a source memory region with LZ4 and returns the uncompressed data in a vector.
*
* @param compressed the compressed source memory region.
* @param uncompressed_size the size in bytes of the uncompressed data.
*
* @return the decompressed data.
*/
std::vector<u8> DecompressDataLZ4(const std::vector<u8>& compressed, std::size_t uncompressed_size);
} // namespace Common::Compression

View File

@@ -72,7 +72,7 @@ public:
u64 prios = mlq.used_priorities;
prios &= ~((1ULL << (current_priority + 1)) - 1);
if (prios == 0) {
current_priority = static_cast<u32>(mlq.depth());
current_priority = mlq.depth();
} else {
current_priority = CountTrailingZeroes64(prios);
it = GetBeginItForPrio();

View File

@@ -20,7 +20,7 @@ struct ScopeExitHelper {
template <typename Func>
ScopeExitHelper<Func> ScopeExit(Func&& func) {
return ScopeExitHelper<Func>(std::forward<Func>(func));
return ScopeExitHelper<Func>(std::move(func));
}
} // namespace detail

View File

@@ -21,6 +21,11 @@
#if defined(_MSC_VER)
#include <cstdlib>
#elif defined(__linux__)
#include <byteswap.h>
#elif defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || \
defined(__NetBSD__) || defined(__OpenBSD__)
#include <sys/endian.h>
#endif
#include <cstring>
#include "common/common_types.h"
@@ -57,49 +62,86 @@
namespace Common {
#ifdef _MSC_VER
[[nodiscard]] inline u16 swap16(u16 data) noexcept {
return _byteswap_ushort(data);
inline u16 swap16(u16 _data) {
return _byteswap_ushort(_data);
}
[[nodiscard]] inline u32 swap32(u32 data) noexcept {
return _byteswap_ulong(data);
inline u32 swap32(u32 _data) {
return _byteswap_ulong(_data);
}
[[nodiscard]] inline u64 swap64(u64 data) noexcept {
return _byteswap_uint64(data);
inline u64 swap64(u64 _data) {
return _byteswap_uint64(_data);
}
#elif defined(__clang__) || defined(__GNUC__)
#if defined(__Bitrig__) || defined(__OpenBSD__)
#elif defined(ARCHITECTURE_ARM) && (__ARM_ARCH >= 6)
inline u16 swap16(u16 _data) {
u32 data = _data;
__asm__("rev16 %0, %1\n" : "=l"(data) : "l"(data));
return (u16)data;
}
inline u32 swap32(u32 _data) {
__asm__("rev %0, %1\n" : "=l"(_data) : "l"(_data));
return _data;
}
inline u64 swap64(u64 _data) {
return ((u64)swap32(_data) << 32) | swap32(_data >> 32);
}
#elif __linux__
inline u16 swap16(u16 _data) {
return bswap_16(_data);
}
inline u32 swap32(u32 _data) {
return bswap_32(_data);
}
inline u64 swap64(u64 _data) {
return bswap_64(_data);
}
#elif __APPLE__
inline __attribute__((always_inline)) u16 swap16(u16 _data) {
return (_data >> 8) | (_data << 8);
}
inline __attribute__((always_inline)) u32 swap32(u32 _data) {
return __builtin_bswap32(_data);
}
inline __attribute__((always_inline)) u64 swap64(u64 _data) {
return __builtin_bswap64(_data);
}
#elif defined(__Bitrig__) || defined(__OpenBSD__)
// redefine swap16, swap32, swap64 as inline functions
#undef swap16
#undef swap32
#undef swap64
#endif
[[nodiscard]] inline u16 swap16(u16 data) noexcept {
return __builtin_bswap16(data);
inline u16 swap16(u16 _data) {
return __swap16(_data);
}
[[nodiscard]] inline u32 swap32(u32 data) noexcept {
return __builtin_bswap32(data);
inline u32 swap32(u32 _data) {
return __swap32(_data);
}
[[nodiscard]] inline u64 swap64(u64 data) noexcept {
return __builtin_bswap64(data);
inline u64 swap64(u64 _data) {
return __swap64(_data);
}
#elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__)
inline u16 swap16(u16 _data) {
return bswap16(_data);
}
inline u32 swap32(u32 _data) {
return bswap32(_data);
}
inline u64 swap64(u64 _data) {
return bswap64(_data);
}
#else
// Generic implementation.
[[nodiscard]] inline u16 swap16(u16 data) noexcept {
// Slow generic implementation.
inline u16 swap16(u16 data) {
return (data >> 8) | (data << 8);
}
[[nodiscard]] inline u32 swap32(u32 data) noexcept {
return ((data & 0xFF000000U) >> 24) | ((data & 0x00FF0000U) >> 8) |
((data & 0x0000FF00U) << 8) | ((data & 0x000000FFU) << 24);
inline u32 swap32(u32 data) {
return (swap16(data) << 16) | swap16(data >> 16);
}
[[nodiscard]] inline u64 swap64(u64 data) noexcept {
return ((data & 0xFF00000000000000ULL) >> 56) | ((data & 0x00FF000000000000ULL) >> 40) |
((data & 0x0000FF0000000000ULL) >> 24) | ((data & 0x000000FF00000000ULL) >> 8) |
((data & 0x00000000FF000000ULL) << 8) | ((data & 0x0000000000FF0000ULL) << 24) |
((data & 0x000000000000FF00ULL) << 40) | ((data & 0x00000000000000FFULL) << 56);
inline u64 swap64(u64 data) {
return ((u64)swap32(data) << 32) | swap32(data >> 32);
}
#endif
[[nodiscard]] inline float swapf(float f) noexcept {
inline float swapf(float f) {
static_assert(sizeof(u32) == sizeof(float), "float must be the same size as uint32_t.");
u32 value;
@@ -111,7 +153,7 @@ namespace Common {
return f;
}
[[nodiscard]] inline double swapd(double f) noexcept {
inline double swapd(double f) {
static_assert(sizeof(u64) == sizeof(double), "double must be the same size as uint64_t.");
u64 value;

View File

@@ -27,6 +27,18 @@ namespace Common {
#ifdef _MSC_VER
void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) {
SetThreadAffinityMask(thread, mask);
}
void SetCurrentThreadAffinity(u32 mask) {
SetThreadAffinityMask(GetCurrentThread(), mask);
}
void SwitchCurrentThread() {
SwitchToThread();
}
// Sets the debugger-visible name of the current thread.
// Uses undocumented (actually, it is now documented) trick.
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/vxtsksettingthreadname.asp
@@ -58,6 +70,31 @@ void SetCurrentThreadName(const char* name) {
#else // !MSVC_VER, so must be POSIX threads
void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) {
#ifdef __APPLE__
thread_policy_set(pthread_mach_thread_np(thread), THREAD_AFFINITY_POLICY, (integer_t*)&mask, 1);
#elif (defined __linux__ || defined __FreeBSD__) && !(defined ANDROID)
cpu_set_t cpu_set;
CPU_ZERO(&cpu_set);
for (int i = 0; i != sizeof(mask) * 8; ++i)
if ((mask >> i) & 1)
CPU_SET(i, &cpu_set);
pthread_setaffinity_np(thread, sizeof(cpu_set), &cpu_set);
#endif
}
void SetCurrentThreadAffinity(u32 mask) {
SetThreadAffinity(pthread_self(), mask);
}
#ifndef _WIN32
void SwitchCurrentThread() {
usleep(1000 * 1);
}
#endif
// MinGW with the POSIX threading model does not support pthread_setname_np
#if !defined(_WIN32) || defined(_MSC_VER)
void SetCurrentThreadName(const char* name) {

View File

@@ -9,13 +9,14 @@
#include <cstddef>
#include <mutex>
#include <thread>
#include "common/common_types.h"
namespace Common {
class Event {
public:
void Set() {
std::lock_guard lk{mutex};
std::lock_guard<std::mutex> lk(mutex);
if (!is_set) {
is_set = true;
condvar.notify_one();
@@ -23,14 +24,14 @@ public:
}
void Wait() {
std::unique_lock lk{mutex};
std::unique_lock<std::mutex> lk(mutex);
condvar.wait(lk, [&] { return is_set; });
is_set = false;
}
template <class Clock, class Duration>
bool WaitUntil(const std::chrono::time_point<Clock, Duration>& time) {
std::unique_lock lk{mutex};
std::unique_lock<std::mutex> lk(mutex);
if (!condvar.wait_until(lk, time, [this] { return is_set; }))
return false;
is_set = false;
@@ -38,7 +39,7 @@ public:
}
void Reset() {
std::unique_lock lk{mutex};
std::unique_lock<std::mutex> lk(mutex);
// no other action required, since wait loops on the predicate and any lingering signal will
// get cleared on the first iteration
is_set = false;
@@ -56,7 +57,7 @@ public:
/// Blocks until all "count" threads have called Sync()
void Sync() {
std::unique_lock lk{mutex};
std::unique_lock<std::mutex> lk(mutex);
const std::size_t current_generation = generation;
if (++waiting == count) {
@@ -77,6 +78,9 @@ private:
std::size_t generation = 0; // Incremented once each time the barrier is used
};
void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask);
void SetCurrentThreadAffinity(u32 mask);
void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms
void SetCurrentThreadName(const char* name);
} // namespace Common

View File

@@ -78,7 +78,7 @@ public:
T PopWait() {
if (Empty()) {
std::unique_lock lock{cv_mutex};
std::unique_lock<std::mutex> lock(cv_mutex);
cv.wait(lock, [this]() { return !Empty(); });
}
T t;
@@ -137,7 +137,7 @@ public:
template <typename Arg>
void Push(Arg&& t) {
std::lock_guard lock{write_lock};
std::lock_guard<std::mutex> lock(write_lock);
spsc_queue.Push(t);
}

View File

@@ -1,51 +0,0 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <zstd.h>
#include "common/assert.h"
#include "common/zstd_compression.h"
namespace Common::Compression {
std::vector<u8> CompressDataZSTD(const u8* source, std::size_t source_size, s32 compression_level) {
compression_level = std::clamp(compression_level, 1, ZSTD_maxCLevel());
const std::size_t max_compressed_size = ZSTD_compressBound(source_size);
std::vector<u8> compressed(max_compressed_size);
const std::size_t compressed_size =
ZSTD_compress(compressed.data(), compressed.size(), source, source_size, compression_level);
if (ZSTD_isError(compressed_size)) {
// Compression failed
return {};
}
compressed.resize(compressed_size);
return compressed;
}
std::vector<u8> CompressDataZSTDDefault(const u8* source, std::size_t source_size) {
return CompressDataZSTD(source, source_size, ZSTD_CLEVEL_DEFAULT);
}
std::vector<u8> DecompressDataZSTD(const std::vector<u8>& compressed) {
const std::size_t decompressed_size =
ZSTD_getDecompressedSize(compressed.data(), compressed.size());
std::vector<u8> decompressed(decompressed_size);
const std::size_t uncompressed_result_size = ZSTD_decompress(
decompressed.data(), decompressed.size(), compressed.data(), compressed.size());
if (decompressed_size != uncompressed_result_size || ZSTD_isError(uncompressed_result_size)) {
// Decompression failed
return {};
}
return decompressed;
}
} // namespace Common::Compression

View File

@@ -1,44 +0,0 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <vector>
#include "common/common_types.h"
namespace Common::Compression {
/**
* Compresses a source memory region with Zstandard and returns the compressed data in a vector.
*
* @param source the uncompressed source memory region.
* @param source_size the size in bytes of the uncompressed source memory region.
* @param compression_level the used compression level. Should be between 1 and 22.
*
* @return the compressed data.
*/
std::vector<u8> CompressDataZSTD(const u8* source, std::size_t source_size, s32 compression_level);
/**
* Compresses a source memory region with Zstandard with the default compression level and returns
* the compressed data in a vector.
*
* @param source the uncompressed source memory region.
* @param source_size the size in bytes of the uncompressed source memory region.
*
* @return the compressed data.
*/
std::vector<u8> CompressDataZSTDDefault(const u8* source, std::size_t source_size);
/**
* Decompresses a source memory region with Zstandard and returns the uncompressed data in a vector.
*
* @param compressed the compressed source memory region.
*
* @return the decompressed data.
*/
std::vector<u8> DecompressDataZSTD(const std::vector<u8>& compressed);
} // namespace Common::Compression

View File

@@ -88,10 +88,6 @@ add_library(core STATIC
file_sys/vfs_vector.h
file_sys/xts_archive.cpp
file_sys/xts_archive.h
frontend/applets/error.cpp
frontend/applets/error.h
frontend/applets/general_frontend.cpp
frontend/applets/general_frontend.h
frontend/applets/profile_select.cpp
frontend/applets/profile_select.h
frontend/applets/software_keyboard.cpp
@@ -181,14 +177,12 @@ add_library(core STATIC
hle/service/am/applet_oe.h
hle/service/am/applets/applets.cpp
hle/service/am/applets/applets.h
hle/service/am/applets/error.cpp
hle/service/am/applets/error.h
hle/service/am/applets/general_backend.cpp
hle/service/am/applets/general_backend.h
hle/service/am/applets/profile_select.cpp
hle/service/am/applets/profile_select.h
hle/service/am/applets/software_keyboard.cpp
hle/service/am/applets/software_keyboard.h
hle/service/am/applets/stub_applet.cpp
hle/service/am/applets/stub_applet.h
hle/service/am/applets/web_browser.cpp
hle/service/am/applets/web_browser.h
hle/service/am/idle.cpp
@@ -464,7 +458,7 @@ add_library(core STATIC
create_target_directory_groups(core)
target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt mbedtls opus unicorn open_source_archives)
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn open_source_archives)
if (ENABLE_WEB_SERVICE)
target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
target_link_libraries(core PRIVATE web_service)

View File

@@ -7,10 +7,6 @@
#include <array>
#include "common/common_types.h"
namespace Common {
struct PageTable;
}
namespace Kernel {
enum class VMAPermission : u8;
}
@@ -53,14 +49,8 @@ public:
/// Clear all instruction cache
virtual void ClearInstructionCache() = 0;
/// Notifies CPU emulation that the current page table has changed.
///
/// @param new_page_table The new page table.
/// @param new_address_space_size_in_bits The new usable size of the address space in bits.
/// This can be either 32, 36, or 39 on official software.
///
virtual void PageTableChanged(Common::PageTable& new_page_table,
std::size_t new_address_space_size_in_bits) = 0;
/// Notify CPU emulation that page tables have changed
virtual void PageTableChanged() = 0;
/**
* Set the Program Counter to an address

View File

@@ -26,6 +26,7 @@ using Vector = Dynarmic::A64::Vector;
class ARM_Dynarmic_Callbacks : public Dynarmic::A64::UserCallbacks {
public:
explicit ARM_Dynarmic_Callbacks(ARM_Dynarmic& parent) : parent(parent) {}
~ARM_Dynarmic_Callbacks() = default;
u8 MemoryRead8(u64 vaddr) override {
return Memory::Read8(vaddr);
@@ -99,7 +100,7 @@ public:
}
void CallSVC(u32 swi) override {
Kernel::CallSVC(parent.system, swi);
Kernel::CallSVC(swi);
}
void AddTicks(u64 ticks) override {
@@ -112,14 +113,14 @@ public:
// Always execute at least one tick.
amortized_ticks = std::max<u64>(amortized_ticks, 1);
parent.system.CoreTiming().AddTicks(amortized_ticks);
parent.core_timing.AddTicks(amortized_ticks);
num_interpreted_instructions = 0;
}
u64 GetTicksRemaining() override {
return std::max(parent.system.CoreTiming().GetDowncount(), 0);
return std::max(parent.core_timing.GetDowncount(), 0);
}
u64 GetCNTPCT() override {
return Timing::CpuCyclesToClockCycles(parent.system.CoreTiming().GetTicks());
return Timing::CpuCyclesToClockCycles(parent.core_timing.GetTicks());
}
ARM_Dynarmic& parent;
@@ -128,16 +129,18 @@ public:
u64 tpidr_el0 = 0;
};
std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit(Common::PageTable& page_table,
std::size_t address_space_bits) const {
std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
auto* current_process = Core::CurrentProcess();
auto** const page_table = current_process->VMManager().page_table.pointers.data();
Dynarmic::A64::UserConfig config;
// Callbacks
config.callbacks = cb.get();
// Memory
config.page_table = reinterpret_cast<void**>(page_table.pointers.data());
config.page_table_address_space_bits = address_space_bits;
config.page_table = reinterpret_cast<void**>(page_table);
config.page_table_address_space_bits = current_process->VMManager().GetAddressSpaceWidth();
config.silently_mirror_page_table = false;
// Multi-process state
@@ -161,6 +164,7 @@ MICROPROFILE_DEFINE(ARM_Jit_Dynarmic, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64)
void ARM_Dynarmic::Run() {
MICROPROFILE_SCOPE(ARM_Jit_Dynarmic);
ASSERT(Memory::GetCurrentPageTable() == current_page_table);
jit->Run();
}
@@ -169,11 +173,16 @@ void ARM_Dynarmic::Step() {
cb->InterpreterFallback(jit->GetPC(), 1);
}
ARM_Dynarmic::ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor,
ARM_Dynarmic::ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor,
std::size_t core_index)
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{system},
core_index{core_index}, system{system},
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{core_timing},
core_index{core_index}, core_timing{core_timing},
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {
ThreadContext ctx{};
inner_unicorn.SaveContext(ctx);
PageTableChanged();
LoadContext(ctx);
}
ARM_Dynarmic::~ARM_Dynarmic() = default;
@@ -268,9 +277,9 @@ void ARM_Dynarmic::ClearExclusiveState() {
jit->ClearExclusiveState();
}
void ARM_Dynarmic::PageTableChanged(Common::PageTable& page_table,
std::size_t new_address_space_size_in_bits) {
jit = MakeJit(page_table, new_address_space_size_in_bits);
void ARM_Dynarmic::PageTableChanged() {
jit = MakeJit();
current_page_table = Memory::GetCurrentPageTable();
}
DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(std::size_t core_count) : monitor(core_count) {}

View File

@@ -12,16 +12,24 @@
#include "core/arm/exclusive_monitor.h"
#include "core/arm/unicorn/arm_unicorn.h"
namespace Common {
struct PageTable;
}
namespace Core::Timing {
class CoreTiming;
}
namespace Core {
class ARM_Dynarmic_Callbacks;
class DynarmicExclusiveMonitor;
class System;
class ARM_Dynarmic final : public ARM_Interface {
public:
ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index);
~ARM_Dynarmic() override;
ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor,
std::size_t core_index);
~ARM_Dynarmic();
void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
Kernel::VMAPermission perms) override;
@@ -48,12 +56,10 @@ public:
void ClearExclusiveState() override;
void ClearInstructionCache() override;
void PageTableChanged(Common::PageTable& new_page_table,
std::size_t new_address_space_size_in_bits) override;
void PageTableChanged() override;
private:
std::unique_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable& page_table,
std::size_t address_space_bits) const;
std::unique_ptr<Dynarmic::A64::Jit> MakeJit() const;
friend class ARM_Dynarmic_Callbacks;
std::unique_ptr<ARM_Dynarmic_Callbacks> cb;
@@ -61,14 +67,16 @@ private:
ARM_Unicorn inner_unicorn;
std::size_t core_index;
System& system;
Timing::CoreTiming& core_timing;
DynarmicExclusiveMonitor& exclusive_monitor;
Common::PageTable* current_page_table = nullptr;
};
class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
public:
explicit DynarmicExclusiveMonitor(std::size_t core_count);
~DynarmicExclusiveMonitor() override;
~DynarmicExclusiveMonitor();
void SetExclusive(std::size_t core_index, VAddr addr) override;
void ClearExclusive() override;

View File

@@ -10,6 +10,7 @@
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/svc.h"
#include "core/memory.h"
namespace Core {
@@ -48,6 +49,20 @@ static void CodeHook(uc_engine* uc, uint64_t address, uint32_t size, void* user_
}
}
static void InterruptHook(uc_engine* uc, u32 intNo, void* user_data) {
u32 esr{};
CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr));
auto ec = esr >> 26;
auto iss = esr & 0xFFFFFF;
switch (ec) {
case 0x15: // SVC
Kernel::CallSVC(iss);
break;
}
}
static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int size, u64 value,
void* user_data) {
ARM_Interface::ThreadContext ctx{};
@@ -57,7 +72,7 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si
return {};
}
ARM_Unicorn::ARM_Unicorn(System& system) : system{system} {
ARM_Unicorn::ARM_Unicorn(Timing::CoreTiming& core_timing) : core_timing{core_timing} {
CHECKED(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc));
auto fpv = 3 << 20;
@@ -162,7 +177,7 @@ void ARM_Unicorn::Run() {
if (GDBStub::IsServerEnabled()) {
ExecuteInstructions(std::max(4000000, 0));
} else {
ExecuteInstructions(std::max(system.CoreTiming().GetDowncount(), 0));
ExecuteInstructions(std::max(core_timing.GetDowncount(), 0));
}
}
@@ -175,15 +190,14 @@ MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64));
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);
core_timing.AddTicks(num_instructions);
if (GDBStub::IsServerEnabled()) {
if (last_bkpt_hit && last_bkpt.type == GDBStub::BreakpointType::Execute) {
if (last_bkpt_hit) {
uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address);
}
Kernel::Thread* thread = Kernel::GetCurrentThread();
SaveContext(thread->GetContext());
if (last_bkpt_hit || GDBStub::IsMemoryBreak() || GDBStub::GetCpuStepFlag()) {
if (last_bkpt_hit || GDBStub::GetCpuStepFlag()) {
last_bkpt_hit = false;
GDBStub::Break();
GDBStub::SendTrap(thread, 5);
@@ -258,20 +272,4 @@ void ARM_Unicorn::RecordBreak(GDBStub::BreakpointAddress bkpt) {
last_bkpt_hit = true;
}
void ARM_Unicorn::InterruptHook(uc_engine* uc, u32 int_no, void* user_data) {
u32 esr{};
CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr));
const auto ec = esr >> 26;
const auto iss = esr & 0xFFFFFF;
auto* const arm_instance = static_cast<ARM_Unicorn*>(user_data);
switch (ec) {
case 0x15: // SVC
Kernel::CallSVC(arm_instance->system, iss);
break;
}
}
} // namespace Core

View File

@@ -9,14 +9,16 @@
#include "core/arm/arm_interface.h"
#include "core/gdbstub/gdbstub.h"
namespace Core {
namespace Core::Timing {
class CoreTiming;
}
class System;
namespace Core {
class ARM_Unicorn final : public ARM_Interface {
public:
explicit ARM_Unicorn(System& system);
~ARM_Unicorn() override;
explicit ARM_Unicorn(Timing::CoreTiming& core_timing);
~ARM_Unicorn();
void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
Kernel::VMAPermission perms) override;
@@ -41,16 +43,14 @@ public:
void Run() override;
void Step() override;
void ClearInstructionCache() override;
void PageTableChanged(Common::PageTable&, std::size_t) override {}
void PageTableChanged() override{};
void RecordBreak(GDBStub::BreakpointAddress bkpt);
private:
static void InterruptHook(uc_engine* uc, u32 int_no, void* user_data);
uc_engine* uc{};
System& system;
Timing::CoreTiming& core_timing;
GDBStub::BreakpointAddress last_bkpt{};
bool last_bkpt_hit = false;
bool last_bkpt_hit;
};
} // namespace Core

View File

@@ -3,7 +3,9 @@
// Refer to the license.txt file included.
#include <array>
#include <map>
#include <memory>
#include <thread>
#include <utility>
#include "common/file_util.h"
@@ -15,21 +17,15 @@
#include "core/core_timing.h"
#include "core/cpu_core_manager.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_real.h"
#include "core/frontend/applets/error.h"
#include "core/frontend/applets/general_frontend.h"
#include "core/frontend/applets/profile_select.h"
#include "core/frontend/applets/software_keyboard.h"
#include "core/frontend/applets/web_browser.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/service/am/applets/applets.h"
#include "core/hle/service/am/applets/software_keyboard.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
#include "core/loader/loader.h"
@@ -41,6 +37,8 @@
#include "frontend/applets/software_keyboard.h"
#include "frontend/applets/web_browser.h"
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/gpu_asynch.h"
#include "video_core/gpu_synch.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
@@ -82,7 +80,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
return vfs->OpenFile(path, FileSys::Mode::Read);
}
struct System::Impl {
explicit Impl(System& system) : kernel{system}, cpu_core_manager{system} {}
explicit Impl(System& system) : kernel{system} {}
Cpu& CurrentCpuCore() {
return cpu_core_manager.GetCurrentCore();
@@ -100,7 +98,6 @@ struct System::Impl {
LOG_DEBUG(HW_Memory, "initialized OK");
core_timing.Initialize();
cpu_core_manager.Initialize();
kernel.Initialize();
const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
@@ -111,11 +108,17 @@ struct System::Impl {
// Create a default fs if one doesn't already exist.
if (virtual_filesystem == nullptr)
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
if (content_provider == nullptr)
content_provider = std::make_unique<FileSys::ContentProviderUnion>();
/// Create default implementations of applets if one is not provided.
applet_manager.SetDefaultAppletsIfMissing();
if (profile_selector == nullptr)
profile_selector = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>();
if (software_keyboard == nullptr)
software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>();
if (web_browser == nullptr)
web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
auto main_process = Kernel::Process::Create(system, "main");
kernel.MakeCurrentProcess(main_process.get());
telemetry_session = std::make_unique<Core::TelemetrySession>();
service_manager = std::make_shared<Service::SM::ServiceManager>();
@@ -128,10 +131,16 @@ struct System::Impl {
return ResultStatus::ErrorVideoCore;
}
gpu_core = VideoCore::CreateGPU(system);
is_powered_on = true;
if (Settings::values.use_asynchronous_gpu_emulation) {
gpu_core = std::make_unique<VideoCommon::GPUAsynch>(system, *renderer);
} else {
gpu_core = std::make_unique<VideoCommon::GPUSynch>(system, *renderer);
}
cpu_core_manager.Initialize(system);
LOG_DEBUG(Core, "Initialized OK");
// Reset counters and set time origin to current frame
@@ -167,8 +176,7 @@ struct System::Impl {
return init_result;
}
auto main_process = Kernel::Process::Create(system, "main");
const auto [load_result, load_parameters] = app_loader->Load(*main_process);
const Loader::ResultStatus load_result{app_loader->Load(*kernel.CurrentProcess())};
if (load_result != Loader::ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
Shutdown();
@@ -176,16 +184,6 @@ struct System::Impl {
return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) +
static_cast<u32>(load_result));
}
kernel.MakeCurrentProcess(main_process.get());
// Main process has been loaded and been made current.
// Begin GPU and CPU execution.
gpu_core->Start();
cpu_core_manager.StartThreads();
// All threads are started, begin main process execution, now that we're in the clear.
main_process->Run(load_parameters->main_thread_priority,
load_parameters->main_thread_stack_size);
status = ResultStatus::Success;
return status;
@@ -223,7 +221,9 @@ struct System::Impl {
app_loader.reset();
// Clear all applets
applet_manager.ClearAll();
profile_selector.reset();
software_keyboard.reset();
web_browser.reset();
LOG_DEBUG(Core, "Shutdown OK");
}
@@ -249,8 +249,6 @@ struct System::Impl {
Kernel::KernelCore kernel;
/// RealVfsFilesystem instance
FileSys::VirtualFilesystem virtual_filesystem;
/// ContentProviderUnion instance
std::unique_ptr<FileSys::ContentProviderUnion> content_provider;
/// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader;
std::unique_ptr<VideoCore::RendererBase> renderer;
@@ -262,7 +260,9 @@ struct System::Impl {
std::unique_ptr<FileSys::CheatEngine> cheat_engine;
/// Frontend applets
Service::AM::Applets::AppletManager applet_manager;
std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_selector;
std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
std::unique_ptr<Core::Frontend::WebBrowserApplet> web_browser;
/// Service manager
std::shared_ptr<Service::SM::ServiceManager> service_manager;
@@ -472,41 +472,32 @@ std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
return impl->virtual_filesystem;
}
void System::SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set) {
impl->applet_manager.SetAppletFrontendSet(std::move(set));
void System::SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet) {
impl->profile_selector = std::move(applet);
}
void System::SetDefaultAppletFrontendSet() {
impl->applet_manager.SetDefaultAppletFrontendSet();
const Frontend::ProfileSelectApplet& System::GetProfileSelector() const {
return *impl->profile_selector;
}
Service::AM::Applets::AppletManager& System::GetAppletManager() {
return impl->applet_manager;
void System::SetSoftwareKeyboard(std::unique_ptr<Frontend::SoftwareKeyboardApplet> applet) {
impl->software_keyboard = std::move(applet);
}
const Service::AM::Applets::AppletManager& System::GetAppletManager() const {
return impl->applet_manager;
const Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() const {
return *impl->software_keyboard;
}
void System::SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider) {
impl->content_provider = std::move(provider);
void System::SetWebBrowser(std::unique_ptr<Frontend::WebBrowserApplet> applet) {
impl->web_browser = std::move(applet);
}
FileSys::ContentProvider& System::GetContentProvider() {
return *impl->content_provider;
Frontend::WebBrowserApplet& System::GetWebBrowser() {
return *impl->web_browser;
}
const FileSys::ContentProvider& System::GetContentProvider() const {
return *impl->content_provider;
}
void System::RegisterContentProvider(FileSys::ContentProviderUnionSlot slot,
FileSys::ContentProvider* provider) {
impl->content_provider->SetSlot(slot, provider);
}
void System::ClearContentProvider(FileSys::ContentProviderUnionSlot slot) {
impl->content_provider->ClearSlot(slot);
const Frontend::WebBrowserApplet& System::GetWebBrowser() const {
return *impl->web_browser;
}
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {

View File

@@ -14,13 +14,13 @@
namespace Core::Frontend {
class EmuWindow;
class ProfileSelectApplet;
class SoftwareKeyboardApplet;
class WebBrowserApplet;
} // namespace Core::Frontend
namespace FileSys {
class CheatList;
class ContentProvider;
class ContentProviderUnion;
enum class ContentProviderUnionSlot;
class VfsFilesystem;
} // namespace FileSys
@@ -35,18 +35,9 @@ class AppLoader;
enum class ResultStatus : u16;
} // namespace Loader
namespace Service {
namespace AM::Applets {
struct AppletFrontendSet;
class AppletManager;
} // namespace AM::Applets
namespace SM {
namespace Service::SM {
class ServiceManager;
} // namespace SM
} // namespace Service
} // namespace Service::SM
namespace Tegra {
class DebugContext;
@@ -266,24 +257,18 @@ public:
void RegisterCheatList(const std::vector<FileSys::CheatList>& list, const std::string& build_id,
VAddr code_region_start, VAddr code_region_end);
void SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set);
void SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet);
void SetDefaultAppletFrontendSet();
const Frontend::ProfileSelectApplet& GetProfileSelector() const;
Service::AM::Applets::AppletManager& GetAppletManager();
void SetSoftwareKeyboard(std::unique_ptr<Frontend::SoftwareKeyboardApplet> applet);
const Service::AM::Applets::AppletManager& GetAppletManager() const;
const Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const;
void SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider);
void SetWebBrowser(std::unique_ptr<Frontend::WebBrowserApplet> applet);
FileSys::ContentProvider& GetContentProvider();
const FileSys::ContentProvider& GetContentProvider() const;
void RegisterContentProvider(FileSys::ContentProviderUnionSlot slot,
FileSys::ContentProvider* provider);
void ClearContentProvider(FileSys::ContentProviderUnionSlot slot);
Frontend::WebBrowserApplet& GetWebBrowser();
const Frontend::WebBrowserApplet& GetWebBrowser() const;
private:
System();

View File

@@ -22,7 +22,7 @@
namespace Core {
void CpuBarrier::NotifyEnd() {
std::unique_lock lock{mutex};
std::unique_lock<std::mutex> lock(mutex);
end = true;
condition.notify_all();
}
@@ -34,7 +34,7 @@ bool CpuBarrier::Rendezvous() {
}
if (!end) {
std::unique_lock lock{mutex};
std::unique_lock<std::mutex> lock(mutex);
--cores_waiting;
if (!cores_waiting) {
@@ -55,13 +55,13 @@ Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_ba
: cpu_barrier{cpu_barrier}, core_timing{system.CoreTiming()}, core_index{core_index} {
if (Settings::values.use_cpu_jit) {
#ifdef ARCHITECTURE_x86_64
arm_interface = std::make_unique<ARM_Dynarmic>(system, exclusive_monitor, core_index);
arm_interface = std::make_unique<ARM_Dynarmic>(core_timing, exclusive_monitor, core_index);
#else
arm_interface = std::make_unique<ARM_Unicorn>(system);
arm_interface = std::make_unique<ARM_Unicorn>();
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
#endif
} else {
arm_interface = std::make_unique<ARM_Unicorn>(system);
arm_interface = std::make_unique<ARM_Unicorn>(core_timing);
}
scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface);
@@ -131,7 +131,7 @@ void Cpu::Reschedule() {
reschedule_pending = false;
// Lock the global kernel mutex when we manipulate the HLE state
std::lock_guard lock{HLE::g_hle_lock};
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
scheduler->Reschedule();
}

View File

@@ -19,19 +19,17 @@ void RunCpuCore(const System& system, Cpu& cpu_state) {
}
} // Anonymous namespace
CpuCoreManager::CpuCoreManager(System& system) : system{system} {}
CpuCoreManager::CpuCoreManager() = default;
CpuCoreManager::~CpuCoreManager() = default;
void CpuCoreManager::Initialize() {
void CpuCoreManager::Initialize(System& system) {
barrier = std::make_unique<CpuBarrier>();
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);
}
}
void CpuCoreManager::StartThreads() {
// Create threads for CPU cores 1-3, and build thread_to_cpu map
// CPU core 0 is run on the main thread
thread_to_cpu[std::this_thread::get_id()] = cores[0].get();

View File

@@ -18,7 +18,7 @@ class System;
class CpuCoreManager {
public:
explicit CpuCoreManager(System& system);
CpuCoreManager();
CpuCoreManager(const CpuCoreManager&) = delete;
CpuCoreManager(CpuCoreManager&&) = delete;
@@ -27,8 +27,7 @@ public:
CpuCoreManager& operator=(const CpuCoreManager&) = delete;
CpuCoreManager& operator=(CpuCoreManager&&) = delete;
void Initialize();
void StartThreads();
void Initialize(System& system);
void Shutdown();
Cpu& GetCore(std::size_t index);
@@ -55,8 +54,6 @@ private:
/// Map of guest threads to CPU cores
std::map<std::thread::id, Cpu*> thread_to_cpu;
System& system;
};
} // namespace Core

View File

@@ -22,7 +22,6 @@
#include "common/file_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/crypto/aes_util.h"
#include "core/crypto/key_manager.h"
#include "core/crypto/partition_data_manager.h"
@@ -795,7 +794,7 @@ void KeyManager::DeriveBase() {
void KeyManager::DeriveETicket(PartitionDataManager& data) {
// ETicket keys
const auto es = Core::System::GetInstance().GetContentProvider().GetEntry(
const auto es = Service::FileSystem::GetUnionContents().GetEntry(
0x0100000000000033, FileSys::ContentRecordType::Program);
if (es == nullptr)

View File

@@ -67,7 +67,7 @@ std::string NACP::GetDeveloperName(Language language) const {
}
u64 NACP::GetTitleId() const {
return raw.save_data_owner_id;
return raw.title_id;
}
u64 NACP::GetDLCBaseTitleId() const {
@@ -80,11 +80,11 @@ std::string NACP::GetVersionString() const {
}
u64 NACP::GetDefaultNormalSaveSize() const {
return raw.user_account_save_data_size;
return raw.normal_save_data_size;
}
u64 NACP::GetDefaultJournalSaveSize() const {
return raw.user_account_save_data_journal_size;
return raw.journal_sava_data_size;
}
std::vector<u8> NACP::GetRawBytes() const {

View File

@@ -38,35 +38,23 @@ struct RawNACP {
u8 video_capture_mode;
bool data_loss_confirmation;
INSERT_PADDING_BYTES(1);
u64_le presence_group_id;
u64_le title_id;
std::array<u8, 0x20> rating_age;
std::array<char, 0x10> version_string;
u64_le dlc_base_title_id;
u64_le save_data_owner_id;
u64_le user_account_save_data_size;
u64_le user_account_save_data_journal_size;
u64_le device_save_data_size;
u64_le device_save_data_journal_size;
u64_le bcat_delivery_cache_storage_size;
char application_error_code_category[8];
u64_le title_id_2;
u64_le normal_save_data_size;
u64_le journal_sava_data_size;
INSERT_PADDING_BYTES(0x18);
u64_le product_code;
std::array<u64_le, 0x8> local_communication;
u8 logo_type;
u8 logo_handling;
bool runtime_add_on_content_install;
INSERT_PADDING_BYTES(5);
u64_le seed_for_pseudo_device_id;
std::array<u8, 0x41> bcat_passphrase;
INSERT_PADDING_BYTES(7);
u64_le user_account_save_data_max_size;
u64_le user_account_save_data_max_journal_size;
u64_le device_save_data_max_size;
u64_le device_save_data_max_journal_size;
u64_le temporary_storage_size;
u64_le cache_storage_size;
u64_le cache_storage_journal_size;
u64_le cache_storage_data_and_journal_max_size;
u64_le cache_storage_max_index;
INSERT_PADDING_BYTES(0xE70);
u64_le title_id_update;
std::array<u8, 0x40> bcat_passphrase;
INSERT_PADDING_BYTES(0xEC0);
};
static_assert(sizeof(RawNACP) == 0x4000, "RawNACP has incorrect size.");

View File

@@ -23,7 +23,6 @@
*/
#include <cstring>
#include <string_view>
#include "common/alignment.h"
#include "common/assert.h"
#include "core/file_sys/fsmitm_romfsbuild.h"
@@ -98,8 +97,7 @@ struct RomFSBuildFileContext {
VirtualFile source;
};
static u32 romfs_calc_path_hash(u32 parent, std::string_view path, u32 start,
std::size_t path_len) {
static u32 romfs_calc_path_hash(u32 parent, std::string path, u32 start, std::size_t path_len) {
u32 hash = parent ^ 123456789;
for (u32 i = 0; i < path_len; i++) {
hash = (hash >> 5) | (hash << 27);

View File

@@ -10,6 +10,14 @@
namespace FileSys {
bool operator>=(TitleType lhs, TitleType rhs) {
return static_cast<std::size_t>(lhs) >= static_cast<std::size_t>(rhs);
}
bool operator<=(TitleType lhs, TitleType rhs) {
return static_cast<std::size_t>(lhs) <= static_cast<std::size_t>(rhs);
}
CNMT::CNMT(VirtualFile file) {
if (file->ReadObject(&header) != sizeof(CNMTHeader))
return;

View File

@@ -29,6 +29,9 @@ enum class TitleType : u8 {
DeltaTitle = 0x83,
};
bool operator>=(TitleType lhs, TitleType rhs);
bool operator<=(TitleType lhs, TitleType rhs);
enum class ContentRecordType : u8 {
Meta = 0,
Program = 1,

View File

@@ -10,7 +10,6 @@
#include "common/file_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/ips_layer.h"
@@ -70,7 +69,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
}
}
const auto& installed = Core::System::GetInstance().GetContentProvider();
const auto installed = Service::FileSystem::GetUnionContents();
const auto& disabled = Settings::values.disabled_addons[title_id];
const auto update_disabled =
@@ -156,7 +155,7 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
return out;
}
std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::string& name) const {
std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
if (nso.size() < sizeof(Loader::NSOHeader)) {
return nso;
}
@@ -172,19 +171,18 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
if (Settings::values.dump_nso) {
LOG_INFO(Loader, "Dumping NSO for name={}, build_id={}, title_id={:016X}", name, build_id,
title_id);
LOG_INFO(Loader, "Dumping NSO for build_id={}, title_id={:016X}", build_id, title_id);
const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id);
if (dump_dir != nullptr) {
const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso");
const auto file = nso_dir->CreateFile(fmt::format("{}-{}.nso", name, build_id));
const auto file = nso_dir->CreateFile(fmt::format("{}.nso", build_id));
file->Resize(nso.size());
file->WriteBytes(nso);
}
}
LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
LOG_INFO(Loader, "Patching NSO for build_id={}", build_id);
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
auto patch_dirs = load_dir->GetSubdirectories();
@@ -347,7 +345,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
if (romfs == nullptr)
return romfs;
const auto& installed = Core::System::GetInstance().GetContentProvider();
const auto installed = Service::FileSystem::GetUnionContents();
// Game Updates
const auto update_tid = GetUpdateTitleID(title_id);
@@ -394,7 +392,7 @@ static bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames(
VirtualFile update_raw) const {
std::map<std::string, std::string, std::less<>> out;
const auto& installed = Core::System::GetInstance().GetContentProvider();
const auto installed = Service::FileSystem::GetUnionContents();
const auto& disabled = Settings::values.disabled_addons[title_id];
// Game Updates
@@ -468,10 +466,10 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
// DLC
const auto dlc_entries = installed.ListEntriesFilter(TitleType::AOC, ContentRecordType::Data);
std::vector<ContentProviderEntry> dlc_match;
std::vector<RegisteredCacheEntry> dlc_match;
dlc_match.reserve(dlc_entries.size());
std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match),
[this, &installed](const ContentProviderEntry& entry) {
[this, &installed](const RegisteredCacheEntry& entry) {
return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == title_id &&
installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success;
});
@@ -494,7 +492,7 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
}
std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const {
const auto& installed = Core::System::GetInstance().GetContentProvider();
const auto installed{Service::FileSystem::GetUnionContents()};
const auto base_control_nca = installed.GetEntry(title_id, ContentRecordType::Control);
if (base_control_nca == nullptr)

View File

@@ -44,7 +44,7 @@ public:
// Currently tracked NSO patches:
// - IPS
// - IPSwitch
std::vector<u8> PatchNSO(const std::vector<u8>& nso, const std::string& name) const;
std::vector<u8> PatchNSO(const std::vector<u8>& nso) const;
// Checks to see if PatchNSO() will have any effect given the NSO's build ID.
// Used to prevent expensive copies in NSO loader.

View File

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <cstddef>
#include <cstring>
#include <vector>
#include "common/logging/log.h"
@@ -16,30 +17,28 @@ ProgramMetadata::ProgramMetadata() = default;
ProgramMetadata::~ProgramMetadata() = default;
Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
const std::size_t total_size = file->GetSize();
if (total_size < sizeof(Header)) {
std::size_t total_size = static_cast<std::size_t>(file->GetSize());
if (total_size < sizeof(Header))
return Loader::ResultStatus::ErrorBadNPDMHeader;
}
if (sizeof(Header) != file->ReadObject(&npdm_header)) {
// TODO(DarkLordZach): Use ReadObject when Header/AcidHeader becomes trivially copyable.
std::vector<u8> npdm_header_data = file->ReadBytes(sizeof(Header));
if (sizeof(Header) != npdm_header_data.size())
return Loader::ResultStatus::ErrorBadNPDMHeader;
}
std::memcpy(&npdm_header, npdm_header_data.data(), sizeof(Header));
if (sizeof(AcidHeader) != file->ReadObject(&acid_header, npdm_header.acid_offset)) {
std::vector<u8> acid_header_data = file->ReadBytes(sizeof(AcidHeader), npdm_header.acid_offset);
if (sizeof(AcidHeader) != acid_header_data.size())
return Loader::ResultStatus::ErrorBadACIDHeader;
}
std::memcpy(&acid_header, acid_header_data.data(), sizeof(AcidHeader));
if (sizeof(AciHeader) != file->ReadObject(&aci_header, npdm_header.aci_offset)) {
if (sizeof(AciHeader) != file->ReadObject(&aci_header, npdm_header.aci_offset))
return Loader::ResultStatus::ErrorBadACIHeader;
}
if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset)) {
if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset))
return Loader::ResultStatus::ErrorBadFileAccessControl;
}
if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset)) {
if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset))
return Loader::ResultStatus::ErrorBadFileAccessHeader;
}
aci_kernel_capabilities.resize(aci_header.kac_size / sizeof(u32));
const u64 read_size = aci_header.kac_size;

View File

@@ -58,6 +58,7 @@ public:
void Print() const;
private:
// TODO(DarkLordZach): BitField is not trivially copyable.
struct Header {
std::array<char, 4> magic;
std::array<u8, 8> reserved;
@@ -84,6 +85,7 @@ private:
static_assert(sizeof(Header) == 0x80, "NPDM header structure size is wrong");
// TODO(DarkLordZach): BitField is not trivially copyable.
struct AcidHeader {
std::array<u8, 0x100> signature;
std::array<u8, 0x100> nca_modulus;

View File

@@ -23,19 +23,19 @@ namespace FileSys {
// The size of blocks to use when vfs raw copying into nand.
constexpr size_t VFS_RC_LARGE_COPY_BLOCK = 0x400000;
std::string ContentProviderEntry::DebugInfo() const {
std::string RegisteredCacheEntry::DebugInfo() const {
return fmt::format("title_id={:016X}, content_type={:02X}", title_id, static_cast<u8>(type));
}
bool operator<(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs) {
bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) {
return (lhs.title_id < rhs.title_id) || (lhs.title_id == rhs.title_id && lhs.type < rhs.type);
}
bool operator==(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs) {
bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) {
return std::tie(lhs.title_id, lhs.type) == std::tie(rhs.title_id, rhs.type);
}
bool operator!=(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs) {
bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) {
return !operator==(lhs, rhs);
}
@@ -84,7 +84,7 @@ static std::string GetCNMTName(TitleType type, u64 title_id) {
return fmt::format("{}_{:016x}.cnmt", TITLE_TYPE_NAMES[index], title_id);
}
ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
switch (type) {
case NCAContentType::Program:
// TODO(DarkLordZach): Differentiate between Program and Patch
@@ -104,28 +104,6 @@ ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
}
}
ContentProvider::~ContentProvider() = default;
bool ContentProvider::HasEntry(ContentProviderEntry entry) const {
return HasEntry(entry.title_id, entry.type);
}
VirtualFile ContentProvider::GetEntryUnparsed(ContentProviderEntry entry) const {
return GetEntryUnparsed(entry.title_id, entry.type);
}
VirtualFile ContentProvider::GetEntryRaw(ContentProviderEntry entry) const {
return GetEntryRaw(entry.title_id, entry.type);
}
std::unique_ptr<NCA> ContentProvider::GetEntry(ContentProviderEntry entry) const {
return GetEntry(entry.title_id, entry.type);
}
std::vector<ContentProviderEntry> ContentProvider::ListEntries() const {
return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt);
}
VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
std::string_view path) const {
const auto file = dir->GetFileRelative(path);
@@ -183,8 +161,8 @@ VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
return file;
}
static std::optional<NcaID> CheckMapForContentRecord(const std::map<u64, CNMT>& map, u64 title_id,
ContentRecordType type) {
static std::optional<NcaID> CheckMapForContentRecord(
const boost::container::flat_map<u64, CNMT>& map, u64 title_id, ContentRecordType type) {
if (map.find(title_id) == map.end())
return {};
@@ -290,7 +268,7 @@ void RegisteredCache::Refresh() {
AccumulateYuzuMeta();
}
RegisteredCache::RegisteredCache(VirtualDir dir_, ContentProviderParsingFunction parsing_function)
RegisteredCache::RegisteredCache(VirtualDir dir_, RegisteredCacheParsingFunction parsing_function)
: dir(std::move(dir_)), parser(std::move(parsing_function)) {
Refresh();
}
@@ -301,11 +279,19 @@ bool RegisteredCache::HasEntry(u64 title_id, ContentRecordType type) const {
return GetEntryRaw(title_id, type) != nullptr;
}
bool RegisteredCache::HasEntry(RegisteredCacheEntry entry) const {
return GetEntryRaw(entry) != nullptr;
}
VirtualFile RegisteredCache::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
const auto id = GetNcaIDFromMetadata(title_id, type);
return id ? GetFileAtID(*id) : nullptr;
}
VirtualFile RegisteredCache::GetEntryUnparsed(RegisteredCacheEntry entry) const {
return GetEntryUnparsed(entry.title_id, entry.type);
}
std::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const {
const auto meta_iter = meta.find(title_id);
if (meta_iter != meta.end())
@@ -323,6 +309,10 @@ VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) c
return id ? parser(GetFileAtID(*id), *id) : nullptr;
}
VirtualFile RegisteredCache::GetEntryRaw(RegisteredCacheEntry entry) const {
return GetEntryRaw(entry.title_id, entry.type);
}
std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType type) const {
const auto raw = GetEntryRaw(title_id, type);
if (raw == nullptr)
@@ -330,6 +320,10 @@ std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType t
return std::make_unique<NCA>(raw, nullptr, 0, keys);
}
std::unique_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const {
return GetEntry(entry.title_id, entry.type);
}
template <typename T>
void RegisteredCache::IterateAllMetadata(
std::vector<T>& out, std::function<T(const CNMT&, const ContentRecord&)> proc,
@@ -354,14 +348,25 @@ void RegisteredCache::IterateAllMetadata(
}
}
std::vector<ContentProviderEntry> RegisteredCache::ListEntriesFilter(
std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
std::optional<u64> title_id) const {
std::vector<ContentProviderEntry> out;
IterateAllMetadata<ContentProviderEntry>(
std::vector<RegisteredCacheEntry> RegisteredCache::ListEntries() const {
std::vector<RegisteredCacheEntry> out;
IterateAllMetadata<RegisteredCacheEntry>(
out,
[](const CNMT& c, const ContentRecord& r) {
return ContentProviderEntry{c.GetTitleID(), r.type};
return RegisteredCacheEntry{c.GetTitleID(), r.type};
},
[](const CNMT& c, const ContentRecord& r) { return true; });
return out;
}
std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter(
std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
std::optional<u64> title_id) const {
std::vector<RegisteredCacheEntry> out;
IterateAllMetadata<RegisteredCacheEntry>(
out,
[](const CNMT& c, const ContentRecord& r) {
return RegisteredCacheEntry{c.GetTitleID(), r.type};
},
[&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) {
if (title_type && *title_type != c.GetType())
@@ -516,56 +521,37 @@ bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) {
}) != yuzu_meta.end();
}
ContentProviderUnion::~ContentProviderUnion() = default;
RegisteredCacheUnion::RegisteredCacheUnion(std::vector<RegisteredCache*> caches)
: caches(std::move(caches)) {}
void ContentProviderUnion::SetSlot(ContentProviderUnionSlot slot, ContentProvider* provider) {
providers[slot] = provider;
void RegisteredCacheUnion::Refresh() {
for (const auto& c : caches)
c->Refresh();
}
void ContentProviderUnion::ClearSlot(ContentProviderUnionSlot slot) {
providers[slot] = nullptr;
bool RegisteredCacheUnion::HasEntry(u64 title_id, ContentRecordType type) const {
return std::any_of(caches.begin(), caches.end(), [title_id, type](const auto& cache) {
return cache->HasEntry(title_id, type);
});
}
void ContentProviderUnion::Refresh() {
for (auto& provider : providers) {
if (provider.second == nullptr)
continue;
provider.second->Refresh();
}
bool RegisteredCacheUnion::HasEntry(RegisteredCacheEntry entry) const {
return HasEntry(entry.title_id, entry.type);
}
bool ContentProviderUnion::HasEntry(u64 title_id, ContentRecordType type) const {
for (const auto& provider : providers) {
if (provider.second == nullptr)
continue;
if (provider.second->HasEntry(title_id, type))
return true;
}
return false;
}
std::optional<u32> ContentProviderUnion::GetEntryVersion(u64 title_id) const {
for (const auto& provider : providers) {
if (provider.second == nullptr)
continue;
const auto res = provider.second->GetEntryVersion(title_id);
if (res != std::nullopt)
std::optional<u32> RegisteredCacheUnion::GetEntryVersion(u64 title_id) const {
for (const auto& c : caches) {
const auto res = c->GetEntryVersion(title_id);
if (res)
return res;
}
return std::nullopt;
return {};
}
VirtualFile ContentProviderUnion::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
for (const auto& provider : providers) {
if (provider.second == nullptr)
continue;
const auto res = provider.second->GetEntryUnparsed(title_id, type);
VirtualFile RegisteredCacheUnion::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
for (const auto& c : caches) {
const auto res = c->GetEntryUnparsed(title_id, type);
if (res != nullptr)
return res;
}
@@ -573,12 +559,13 @@ VirtualFile ContentProviderUnion::GetEntryUnparsed(u64 title_id, ContentRecordTy
return nullptr;
}
VirtualFile ContentProviderUnion::GetEntryRaw(u64 title_id, ContentRecordType type) const {
for (const auto& provider : providers) {
if (provider.second == nullptr)
continue;
VirtualFile RegisteredCacheUnion::GetEntryUnparsed(RegisteredCacheEntry entry) const {
return GetEntryUnparsed(entry.title_id, entry.type);
}
const auto res = provider.second->GetEntryRaw(title_id, type);
VirtualFile RegisteredCacheUnion::GetEntryRaw(u64 title_id, ContentRecordType type) const {
for (const auto& c : caches) {
const auto res = c->GetEntryRaw(title_id, type);
if (res != nullptr)
return res;
}
@@ -586,30 +573,30 @@ VirtualFile ContentProviderUnion::GetEntryRaw(u64 title_id, ContentRecordType ty
return nullptr;
}
std::unique_ptr<NCA> ContentProviderUnion::GetEntry(u64 title_id, ContentRecordType type) const {
for (const auto& provider : providers) {
if (provider.second == nullptr)
continue;
auto res = provider.second->GetEntry(title_id, type);
if (res != nullptr)
return res;
}
return nullptr;
VirtualFile RegisteredCacheUnion::GetEntryRaw(RegisteredCacheEntry entry) const {
return GetEntryRaw(entry.title_id, entry.type);
}
std::vector<ContentProviderEntry> ContentProviderUnion::ListEntriesFilter(
std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
std::optional<u64> title_id) const {
std::vector<ContentProviderEntry> out;
std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(u64 title_id, ContentRecordType type) const {
const auto raw = GetEntryRaw(title_id, type);
if (raw == nullptr)
return nullptr;
return std::make_unique<NCA>(raw);
}
for (const auto& provider : providers) {
if (provider.second == nullptr)
continue;
std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(RegisteredCacheEntry entry) const {
return GetEntry(entry.title_id, entry.type);
}
const auto vec = provider.second->ListEntriesFilter(title_type, record_type, title_id);
std::copy(vec.begin(), vec.end(), std::back_inserter(out));
std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntries() const {
std::vector<RegisteredCacheEntry> out;
for (const auto& c : caches) {
c->IterateAllMetadata<RegisteredCacheEntry>(
out,
[](const CNMT& c, const ContentRecord& r) {
return RegisteredCacheEntry{c.GetTitleID(), r.type};
},
[](const CNMT& c, const ContentRecord& r) { return true; });
}
std::sort(out.begin(), out.end());
@@ -617,87 +604,25 @@ std::vector<ContentProviderEntry> ContentProviderUnion::ListEntriesFilter(
return out;
}
std::vector<std::pair<ContentProviderUnionSlot, ContentProviderEntry>>
ContentProviderUnion::ListEntriesFilterOrigin(std::optional<ContentProviderUnionSlot> origin,
std::optional<TitleType> title_type,
std::optional<ContentRecordType> record_type,
std::optional<u64> title_id) const {
std::vector<std::pair<ContentProviderUnionSlot, ContentProviderEntry>> out;
for (const auto& provider : providers) {
if (provider.second == nullptr)
continue;
if (origin.has_value() && *origin != provider.first)
continue;
const auto vec = provider.second->ListEntriesFilter(title_type, record_type, title_id);
std::transform(vec.begin(), vec.end(), std::back_inserter(out),
[&provider](const ContentProviderEntry& entry) {
return std::make_pair(provider.first, entry);
});
}
std::sort(out.begin(), out.end());
out.erase(std::unique(out.begin(), out.end()), out.end());
return out;
}
ManualContentProvider::~ManualContentProvider() = default;
void ManualContentProvider::AddEntry(TitleType title_type, ContentRecordType content_type,
u64 title_id, VirtualFile file) {
entries.insert_or_assign({title_type, content_type, title_id}, file);
}
void ManualContentProvider::ClearAllEntries() {
entries.clear();
}
void ManualContentProvider::Refresh() {}
bool ManualContentProvider::HasEntry(u64 title_id, ContentRecordType type) const {
return GetEntryRaw(title_id, type) != nullptr;
}
std::optional<u32> ManualContentProvider::GetEntryVersion(u64 title_id) const {
return std::nullopt;
}
VirtualFile ManualContentProvider::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
return GetEntryRaw(title_id, type);
}
VirtualFile ManualContentProvider::GetEntryRaw(u64 title_id, ContentRecordType type) const {
const auto iter =
std::find_if(entries.begin(), entries.end(), [title_id, type](const auto& entry) {
const auto [title_type, content_type, e_title_id] = entry.first;
return content_type == type && e_title_id == title_id;
});
if (iter == entries.end())
return nullptr;
return iter->second;
}
std::unique_ptr<NCA> ManualContentProvider::GetEntry(u64 title_id, ContentRecordType type) const {
const auto res = GetEntryRaw(title_id, type);
if (res == nullptr)
return nullptr;
return std::make_unique<NCA>(res, nullptr, 0, keys);
}
std::vector<ContentProviderEntry> ManualContentProvider::ListEntriesFilter(
std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntriesFilter(
std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
std::optional<u64> title_id) const {
std::vector<ContentProviderEntry> out;
for (const auto& entry : entries) {
const auto [e_title_type, e_content_type, e_title_id] = entry.first;
if ((title_type == std::nullopt || e_title_type == *title_type) &&
(record_type == std::nullopt || e_content_type == *record_type) &&
(title_id == std::nullopt || e_title_id == *title_id)) {
out.emplace_back(ContentProviderEntry{e_title_id, e_content_type});
}
std::vector<RegisteredCacheEntry> out;
for (const auto& c : caches) {
c->IterateAllMetadata<RegisteredCacheEntry>(
out,
[](const CNMT& c, const ContentRecord& r) {
return RegisteredCacheEntry{c.GetTitleID(), r.type};
},
[&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) {
if (title_type && *title_type != c.GetType())
return false;
if (record_type && *record_type != r.type)
return false;
if (title_id && *title_id != c.GetTitleID())
return false;
return true;
});
}
std::sort(out.begin(), out.end());

View File

@@ -21,13 +21,12 @@ class NSP;
class XCI;
enum class ContentRecordType : u8;
enum class NCAContentType : u8;
enum class TitleType : u8;
struct ContentRecord;
using NcaID = std::array<u8, 0x10>;
using ContentProviderParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
using VfsCopyFunction = std::function<bool(const VirtualFile&, const VirtualFile&, size_t)>;
enum class InstallResult {
@@ -37,7 +36,7 @@ enum class InstallResult {
ErrorMetaFailed,
};
struct ContentProviderEntry {
struct RegisteredCacheEntry {
u64 title_id;
ContentRecordType type;
@@ -48,46 +47,12 @@ constexpr u64 GetUpdateTitleID(u64 base_title_id) {
return base_title_id | 0x800;
}
ContentRecordType GetCRTypeFromNCAType(NCAContentType type);
// boost flat_map requires operator< for O(log(n)) lookups.
bool operator<(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs);
bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
// std unique requires operator== to identify duplicates.
bool operator==(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs);
bool operator!=(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs);
class ContentProvider {
public:
virtual ~ContentProvider();
virtual void Refresh() = 0;
virtual bool HasEntry(u64 title_id, ContentRecordType type) const = 0;
virtual bool HasEntry(ContentProviderEntry entry) const;
virtual std::optional<u32> GetEntryVersion(u64 title_id) const = 0;
virtual VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const = 0;
virtual VirtualFile GetEntryUnparsed(ContentProviderEntry entry) const;
virtual VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const = 0;
virtual VirtualFile GetEntryRaw(ContentProviderEntry entry) const;
virtual std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const = 0;
virtual std::unique_ptr<NCA> GetEntry(ContentProviderEntry entry) const;
virtual std::vector<ContentProviderEntry> ListEntries() const;
// If a parameter is not std::nullopt, it will be filtered for from all entries.
virtual std::vector<ContentProviderEntry> ListEntriesFilter(
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
std::optional<u64> title_id = {}) const = 0;
protected:
// A single instance of KeyManager to be used by GetEntry()
Core::Crypto::KeyManager keys;
};
bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
/*
* A class that catalogues NCAs in the registered directory structure.
@@ -102,32 +67,39 @@ protected:
* (This impl also supports substituting the nca dir for an nca file, as that's more convenient
* when 4GB splitting can be ignored.)
*/
class RegisteredCache : public ContentProvider {
class RegisteredCache {
friend class RegisteredCacheUnion;
public:
// Parsing function defines the conversion from raw file to NCA. If there are other steps
// besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom
// parsing function.
explicit RegisteredCache(VirtualDir dir,
ContentProviderParsingFunction parsing_function =
RegisteredCacheParsingFunction parsing_function =
[](const VirtualFile& file, const NcaID& id) { return file; });
~RegisteredCache() override;
~RegisteredCache();
void Refresh() override;
void Refresh();
bool HasEntry(u64 title_id, ContentRecordType type) const override;
bool HasEntry(u64 title_id, ContentRecordType type) const;
bool HasEntry(RegisteredCacheEntry entry) const;
std::optional<u32> GetEntryVersion(u64 title_id) const override;
std::optional<u32> GetEntryVersion(u64 title_id) const;
VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const override;
VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const;
VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const override;
VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const override;
std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
std::vector<RegisteredCacheEntry> ListEntries() const;
// If a parameter is not std::nullopt, it will be filtered for from all entries.
std::vector<ContentProviderEntry> ListEntriesFilter(
std::vector<RegisteredCacheEntry> ListEntriesFilter(
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
std::optional<u64> title_id = {}) const override;
std::optional<u64> title_id = {}) const;
// Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure
// there is a meta NCA and all of them are accessible.
@@ -159,70 +131,46 @@ private:
bool RawInstallYuzuMeta(const CNMT& cnmt);
VirtualDir dir;
ContentProviderParsingFunction parser;
RegisteredCacheParsingFunction parser;
Core::Crypto::KeyManager keys;
// maps tid -> NcaID of meta
std::map<u64, NcaID> meta_id;
boost::container::flat_map<u64, NcaID> meta_id;
// maps tid -> meta
std::map<u64, CNMT> meta;
boost::container::flat_map<u64, CNMT> meta;
// maps tid -> meta for CNMT in yuzu_meta
std::map<u64, CNMT> yuzu_meta;
boost::container::flat_map<u64, CNMT> yuzu_meta;
};
enum class ContentProviderUnionSlot {
SysNAND, ///< System NAND
UserNAND, ///< User NAND
SDMC, ///< SD Card
FrontendManual, ///< Frontend-defined game list or similar
};
// Combines multiple ContentProvider(s) (i.e. SysNAND, UserNAND, SDMC) into one interface.
class ContentProviderUnion : public ContentProvider {
// Combines multiple RegisteredCaches (i.e. SysNAND, UserNAND, SDMC) into one interface.
class RegisteredCacheUnion {
public:
~ContentProviderUnion() override;
explicit RegisteredCacheUnion(std::vector<RegisteredCache*> caches);
void SetSlot(ContentProviderUnionSlot slot, ContentProvider* provider);
void ClearSlot(ContentProviderUnionSlot slot);
void Refresh();
void Refresh() override;
bool HasEntry(u64 title_id, ContentRecordType type) const override;
std::optional<u32> GetEntryVersion(u64 title_id) const override;
VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const override;
VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const override;
std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const override;
std::vector<ContentProviderEntry> ListEntriesFilter(
std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
std::optional<u64> title_id) const override;
bool HasEntry(u64 title_id, ContentRecordType type) const;
bool HasEntry(RegisteredCacheEntry entry) const;
std::vector<std::pair<ContentProviderUnionSlot, ContentProviderEntry>> ListEntriesFilterOrigin(
std::optional<ContentProviderUnionSlot> origin = {},
std::optional<u32> GetEntryVersion(u64 title_id) const;
VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const;
VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
std::vector<RegisteredCacheEntry> ListEntries() const;
// If a parameter is not std::nullopt, it will be filtered for from all entries.
std::vector<RegisteredCacheEntry> ListEntriesFilter(
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
std::optional<u64> title_id = {}) const;
private:
std::map<ContentProviderUnionSlot, ContentProvider*> providers;
};
class ManualContentProvider : public ContentProvider {
public:
~ManualContentProvider() override;
void AddEntry(TitleType title_type, ContentRecordType content_type, u64 title_id,
VirtualFile file);
void ClearAllEntries();
void Refresh() override;
bool HasEntry(u64 title_id, ContentRecordType type) const override;
std::optional<u32> GetEntryVersion(u64 title_id) const override;
VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const override;
VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const override;
std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const override;
std::vector<ContentProviderEntry> ListEntriesFilter(
std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
std::optional<u64> title_id) const override;
private:
std::map<std::tuple<TitleType, ContentRecordType, u64>, VirtualFile> entries;
std::vector<RegisteredCache*> caches;
};
} // namespace FileSys

View File

@@ -48,7 +48,7 @@ ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, Conte
switch (storage) {
case StorageId::None:
res = Core::System::GetInstance().GetContentProvider().GetEntry(title_id, type);
res = Service::FileSystem::GetUnionContents().GetEntry(title_id, type);
break;
case StorageId::NandSystem:
res = Service::FileSystem::GetSystemNANDContents()->GetEntry(title_id, type);

View File

@@ -16,10 +16,8 @@ namespace FileSys {
constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size";
std::string SaveDataDescriptor::DebugInfo() const {
return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}, "
"rank={}, index={}]",
static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id,
static_cast<u8>(rank), index);
return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}]",
static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id);
}
SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {
@@ -30,7 +28,7 @@ SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save
SaveDataFactory::~SaveDataFactory() = default;
ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) {
ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescriptor meta) {
if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) {
if (meta.zero_1 != 0) {
LOG_WARNING(Service_FS,

View File

@@ -32,19 +32,12 @@ enum class SaveDataType : u8 {
CacheStorage = 5,
};
enum class SaveDataRank : u8 {
Primary,
Secondary,
};
struct SaveDataDescriptor {
u64_le title_id;
u128 user_id;
u64_le save_id;
SaveDataType type;
SaveDataRank rank;
u16_le index;
INSERT_PADDING_BYTES(4);
INSERT_PADDING_BYTES(7);
u64_le zero_1;
u64_le zero_2;
u64_le zero_3;
@@ -64,7 +57,7 @@ public:
explicit SaveDataFactory(VirtualDir dir);
~SaveDataFactory();
ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta);
ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta);
VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;

View File

@@ -143,12 +143,11 @@ std::multimap<u64, std::shared_ptr<NCA>> NSP::GetNCAsByTitleID() const {
return out;
}
std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>>
NSP::GetNCAs() const {
std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> NSP::GetNCAs() const {
return ncas;
}
std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type, TitleType title_type) const {
std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type) const {
if (extracted)
LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
@@ -156,14 +155,14 @@ std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type, TitleType
if (title_id_iter == ncas.end())
return nullptr;
const auto type_iter = title_id_iter->second.find({title_type, type});
const auto type_iter = title_id_iter->second.find(type);
if (type_iter == title_id_iter->second.end())
return nullptr;
return type_iter->second;
}
VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type, TitleType title_type) const {
VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type) const {
if (extracted)
LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
const auto nca = GetNCA(title_id, type);
@@ -241,7 +240,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
const CNMT cnmt(inner_file);
auto& ncas_title = ncas[cnmt.GetTitleID()];
ncas_title[{cnmt.GetType(), ContentRecordType::Meta}] = nca;
ncas_title[ContentRecordType::Meta] = nca;
for (const auto& rec : cnmt.GetContentRecords()) {
const auto id_string = Common::HexArrayToString(rec.nca_id, false);
const auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string));
@@ -259,7 +258,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
if (next_nca->GetStatus() == Loader::ResultStatus::Success ||
(next_nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS &&
(cnmt.GetTitleID() & 0x800) != 0)) {
ncas_title[{cnmt.GetType(), rec.type}] = std::move(next_nca);
ncas_title[rec.type] = std::move(next_nca);
}
}

View File

@@ -42,12 +42,9 @@ public:
// Type 0 Only (Collection of NCAs + Certificate + Ticket + Meta XML)
std::vector<std::shared_ptr<NCA>> GetNCAsCollapsed() const;
std::multimap<u64, std::shared_ptr<NCA>> GetNCAsByTitleID() const;
std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> GetNCAs()
const;
std::shared_ptr<NCA> GetNCA(u64 title_id, ContentRecordType type,
TitleType title_type = TitleType::Application) const;
VirtualFile GetNCAFile(u64 title_id, ContentRecordType type,
TitleType title_type = TitleType::Application) const;
std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> GetNCAs() const;
std::shared_ptr<NCA> GetNCA(u64 title_id, ContentRecordType type) const;
VirtualFile GetNCAFile(u64 title_id, ContentRecordType type) const;
std::vector<Core::Crypto::Key128> GetTitlekey() const;
std::vector<VirtualFile> GetFiles() const override;
@@ -70,7 +67,7 @@ private:
std::shared_ptr<PartitionFilesystem> pfs;
// Map title id -> {map type -> NCA}
std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> ncas;
std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> ncas;
std::vector<VirtualFile> ticket_files;
Core::Crypto::KeyManager keys;

View File

@@ -1,34 +0,0 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/frontend/applets/error.h"
namespace Core::Frontend {
ErrorApplet::~ErrorApplet() = default;
void DefaultErrorApplet::ShowError(ResultCode error, std::function<void()> finished) const {
LOG_CRITICAL(Service_Fatal, "Application requested error display: {:04}-{:04} (raw={:08X})",
static_cast<u32>(error.module.Value()), error.description.Value(), error.raw);
}
void DefaultErrorApplet::ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
std::function<void()> finished) const {
LOG_CRITICAL(
Service_Fatal,
"Application requested error display: {:04X}-{:04X} (raw={:08X}) with timestamp={:016X}",
static_cast<u32>(error.module.Value()), error.description.Value(), error.raw, time.count());
}
void DefaultErrorApplet::ShowCustomErrorText(ResultCode error, std::string main_text,
std::string detail_text,
std::function<void()> finished) const {
LOG_CRITICAL(Service_Fatal,
"Application requested custom error with error_code={:04X}-{:04X} (raw={:08X})",
static_cast<u32>(error.module.Value()), error.description.Value(), error.raw);
LOG_CRITICAL(Service_Fatal, " Main Text: {}", main_text);
LOG_CRITICAL(Service_Fatal, " Detail Text: {}", detail_text);
}
} // namespace Core::Frontend

View File

@@ -1,37 +0,0 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <chrono>
#include <functional>
#include "core/hle/result.h"
namespace Core::Frontend {
class ErrorApplet {
public:
virtual ~ErrorApplet();
virtual void ShowError(ResultCode error, std::function<void()> finished) const = 0;
virtual void ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
std::function<void()> finished) const = 0;
virtual void ShowCustomErrorText(ResultCode error, std::string dialog_text,
std::string fullscreen_text,
std::function<void()> finished) const = 0;
};
class DefaultErrorApplet final : public ErrorApplet {
public:
void ShowError(ResultCode error, std::function<void()> finished) const override;
void ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
std::function<void()> finished) const override;
void ShowCustomErrorText(ResultCode error, std::string main_text, std::string detail_text,
std::function<void()> finished) const override;
};
} // namespace Core::Frontend

View File

@@ -1,27 +0,0 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/frontend/applets/general_frontend.h"
namespace Core::Frontend {
PhotoViewerApplet::~PhotoViewerApplet() = default;
DefaultPhotoViewerApplet::~DefaultPhotoViewerApplet() {}
void DefaultPhotoViewerApplet::ShowPhotosForApplication(u64 title_id,
std::function<void()> finished) const {
LOG_INFO(Service_AM,
"Application requested frontend to display stored photos for title_id={:016X}",
title_id);
finished();
}
void DefaultPhotoViewerApplet::ShowAllPhotos(std::function<void()> finished) const {
LOG_INFO(Service_AM, "Application requested frontend to display all stored photos.");
finished();
}
} // namespace Core::Frontend

View File

@@ -1,28 +0,0 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <functional>
#include "common/common_types.h"
namespace Core::Frontend {
class PhotoViewerApplet {
public:
virtual ~PhotoViewerApplet();
virtual void ShowPhotosForApplication(u64 title_id, std::function<void()> finished) const = 0;
virtual void ShowAllPhotos(std::function<void()> finished) const = 0;
};
class DefaultPhotoViewerApplet final : public PhotoViewerApplet {
public:
~DefaultPhotoViewerApplet() override;
void ShowPhotosForApplication(u64 title_id, std::function<void()> finished) const override;
void ShowAllPhotos(std::function<void()> finished) const override;
};
} // namespace Core::Frontend

View File

@@ -10,8 +10,6 @@
namespace Core::Frontend {
GraphicsContext::~GraphicsContext() = default;
class EmuWindow::TouchState : public Input::Factory<Input::TouchDevice>,
public std::enable_shared_from_this<TouchState> {
public:
@@ -32,7 +30,7 @@ private:
explicit Device(std::weak_ptr<TouchState>&& touch_state) : touch_state(touch_state) {}
std::tuple<float, float, bool> GetStatus() const override {
if (auto state = touch_state.lock()) {
std::lock_guard guard{state->mutex};
std::lock_guard<std::mutex> guard(state->mutex);
return std::make_tuple(state->touch_x, state->touch_y, state->touch_pressed);
}
return std::make_tuple(0.0f, 0.0f, false);
@@ -83,7 +81,7 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
return;
std::lock_guard guard{touch_state->mutex};
std::lock_guard<std::mutex> guard(touch_state->mutex);
touch_state->touch_x = static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) /
(framebuffer_layout.screen.right - framebuffer_layout.screen.left);
touch_state->touch_y = static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) /
@@ -93,7 +91,7 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
}
void EmuWindow::TouchReleased() {
std::lock_guard guard{touch_state->mutex};
std::lock_guard<std::mutex> guard(touch_state->mutex);
touch_state->touch_pressed = false;
touch_state->touch_x = 0;
touch_state->touch_y = 0;

View File

@@ -12,25 +12,6 @@
namespace Core::Frontend {
/**
* Represents a graphics context that can be used for background computation or drawing. If the
* graphics backend doesn't require the context, then the implementation of these methods can be
* stubs
*/
class GraphicsContext {
public:
virtual ~GraphicsContext();
/// Makes the graphics context current for the caller thread
virtual void MakeCurrent() = 0;
/// Releases (dunno if this is the "right" word) the context from the caller thread
virtual void DoneCurrent() = 0;
/// Swap buffers to display the next frame
virtual void SwapBuffers() = 0;
};
/**
* Abstraction class used to provide an interface between emulation code and the frontend
* (e.g. SDL, QGLWidget, GLFW, etc...).
@@ -49,7 +30,7 @@ public:
* - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please
* re-read the upper points again and think about it if you don't see this.
*/
class EmuWindow : public GraphicsContext {
class EmuWindow {
public:
/// Data structure to store emuwindow configuration
struct WindowConfig {
@@ -59,21 +40,17 @@ public:
std::pair<unsigned, unsigned> min_client_area_size;
};
/// Swap buffers to display the next frame
virtual void SwapBuffers() = 0;
/// Polls window events
virtual void PollEvents() = 0;
/**
* Returns a GraphicsContext that the frontend provides that is shared with the emu window. This
* context can be used from other threads for background graphics computation. If the frontend
* is using a graphics backend that doesn't need anything specific to run on a different thread,
* then it can use a stubbed implemenation for GraphicsContext.
*
* If the return value is null, then the core should assume that the frontend cannot provide a
* Shared Context
*/
virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const {
return nullptr;
}
/// Makes the graphics context current for the caller thread
virtual void MakeCurrent() = 0;
/// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
virtual void DoneCurrent() = 0;
/**
* Signal that a touch pressed event has occurred (e.g. mouse click pressed)

View File

@@ -1030,7 +1030,7 @@ static void Step() {
/// Tell the CPU if we hit a memory breakpoint.
bool IsMemoryBreak() {
if (!IsConnected()) {
if (IsConnected()) {
return false;
}

View File

@@ -139,8 +139,10 @@ public:
context->AddDomainObject(std::move(iface));
} else {
auto& kernel = Core::System::GetInstance().Kernel();
auto [server, client] =
auto sessions =
Kernel::ServerSession::CreateSessionPair(kernel, iface->GetServiceName());
auto server = std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions);
auto client = std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions);
iface->ClientConnected(server);
context->AddMoveObject(std::move(client));
}
@@ -438,7 +440,7 @@ inline float RequestParser::Pop() {
template <>
inline double RequestParser::Pop() {
const u64 value = Pop<u64>();
double real;
float real;
std::memcpy(&real, &value, sizeof(real));
return real;
}

View File

@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <tuple>
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/errors.h"
@@ -29,18 +31,18 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
active_sessions++;
// 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);
auto sessions = ServerSession::CreateSessionPair(kernel, server_port->GetName(), this);
if (server_port->HasHLEHandler()) {
server_port->GetHLEHandler()->ClientConnected(server);
server_port->GetHLEHandler()->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions));
} else {
server_port->AppendPendingSession(server);
server_port->AppendPendingSession(std::get<SharedPtr<ServerSession>>(sessions));
}
// Wake the threads waiting on the ServerPort
server_port->WakeupAllWaitingThreads();
return MakeResult(client);
return MakeResult(std::get<SharedPtr<ClientSession>>(sessions));
}
void ClientPort::ConnectionClosed() {

View File

@@ -25,7 +25,7 @@ public:
return name;
}
static constexpr HandleType HANDLE_TYPE = HandleType::ClientPort;
static const HandleType HANDLE_TYPE = HandleType::ClientPort;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}

View File

@@ -29,7 +29,7 @@ public:
return name;
}
static constexpr HandleType HANDLE_TYPE = HandleType::ClientSession;
static const HandleType HANDLE_TYPE = HandleType::ClientSession;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}

View File

@@ -5,6 +5,7 @@
#pragma once
#include <cstddef>
#include <memory>
#include <vector>
#include "common/common_types.h"
@@ -77,7 +78,7 @@ struct CodeSet final {
}
/// The overall data that backs this code set.
std::vector<u8> memory;
std::shared_ptr<std::vector<u8>> memory;
/// The segments that comprise this code set.
std::array<Segment, 3> segments;

View File

@@ -58,7 +58,7 @@ SharedPtr<WritableEvent> HLERequestContext::SleepClientThread(
auto& kernel = Core::System::GetInstance().Kernel();
if (!writable_event) {
// Create event if not provided
const auto pair = WritableEvent::CreateEventPair(kernel, ResetType::Automatic,
const auto pair = WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
"HLE Pause Event: " + reason);
writable_event = pair.writable;
}

View File

@@ -21,7 +21,6 @@
#include "core/hle/kernel/thread.h"
#include "core/hle/lock.h"
#include "core/hle/result.h"
#include "core/memory.h"
namespace Kernel {
@@ -35,7 +34,7 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_
const auto& system = Core::System::GetInstance();
// Lock the global kernel mutex when we enter the kernel HLE.
std::lock_guard lock{HLE::g_hle_lock};
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
SharedPtr<Thread> thread =
system.Kernel().RetrieveThreadFromWakeupCallbackHandleTable(proper_handle);
@@ -46,7 +45,8 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_
bool resume = true;
if (thread->GetStatus() == ThreadStatus::WaitSynch ||
if (thread->GetStatus() == ThreadStatus::WaitSynchAny ||
thread->GetStatus() == ThreadStatus::WaitSynchAll ||
thread->GetStatus() == ThreadStatus::WaitHLEEvent) {
// Remove the thread from each of its waiting objects' waitlists
for (const auto& object : thread->GetWaitObjects()) {
@@ -115,7 +115,7 @@ struct KernelCore::Impl {
// Creates the default system resource limit
void InitializeSystemResourceLimit(KernelCore& kernel) {
system_resource_limit = ResourceLimit::Create(kernel);
system_resource_limit = ResourceLimit::Create(kernel, "System");
// If setting the default system values fails, then something seriously wrong has occurred.
ASSERT(system_resource_limit->SetLimitValue(ResourceType::PhysicalMemory, 0x200000000)
@@ -181,12 +181,6 @@ void KernelCore::AppendNewProcess(SharedPtr<Process> process) {
void KernelCore::MakeCurrentProcess(Process* process) {
impl->current_process = process;
if (process == nullptr) {
return;
}
Memory::SetCurrentPageTable(*process);
}
Process* KernelCore::CurrentProcess() {
@@ -197,10 +191,6 @@ const Process* KernelCore::CurrentProcess() const {
return impl->current_process;
}
const std::vector<SharedPtr<Process>>& KernelCore::GetProcessList() const {
return impl->process_list;
}
void KernelCore::AddNamedPort(std::string name, SharedPtr<ClientPort> port) {
impl->named_ports.emplace(std::move(name), std::move(port));
}

View File

@@ -72,9 +72,6 @@ public:
/// Retrieves a const pointer to the current process.
const Process* CurrentProcess() const;
/// Retrieves the list of processes.
const std::vector<SharedPtr<Process>>& GetProcessList() const;
/// Adds a port to the named port table
void AddNamedPort(std::string name, SharedPtr<ClientPort> port);

View File

@@ -24,6 +24,7 @@ bool Object::IsWaitable() const {
case HandleType::WritableEvent:
case HandleType::SharedMemory:
case HandleType::TransferMemory:
case HandleType::AddressArbiter:
case HandleType::ResourceLimit:
case HandleType::ClientPort:
case HandleType::ClientSession:

View File

@@ -25,6 +25,7 @@ enum class HandleType : u32 {
TransferMemory,
Thread,
Process,
AddressArbiter,
ResourceLimit,
ClientPort,
ServerPort,
@@ -33,8 +34,8 @@ enum class HandleType : u32 {
};
enum class ResetType {
Automatic, ///< Reset automatically on object acquisition
Manual, ///< Never reset automatically
OneShot, ///< Reset automatically on object acquisition
Sticky, ///< Never reset automatically
};
class Object : NonCopyable {

View File

@@ -5,7 +5,6 @@
#include <algorithm>
#include <memory>
#include <random>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
@@ -28,20 +27,24 @@ namespace {
*
* @param owner_process The parent process for the main thread
* @param kernel The kernel instance to create the main thread under.
* @param entry_point The address at which the thread should start execution
* @param priority The priority to give the main thread
*/
void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority) {
const auto& vm_manager = owner_process.VMManager();
const VAddr entry_point = vm_manager.GetCodeRegionBaseAddress();
const VAddr stack_top = vm_manager.GetTLSIORegionEndAddress();
void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_point, u32 priority) {
// Setup page table so we can write to memory
Memory::SetCurrentPageTable(&owner_process.VMManager().page_table);
// Initialize new "main" thread
const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress();
auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0,
owner_process.GetIdealCore(), stack_top, owner_process);
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();
thread->GetContext().cpu_registers[1] = thread_handle;
const Handle guest_handle = owner_process.GetHandleTable().Create(thread).Unwrap();
thread->SetGuestHandle(guest_handle);
thread->GetContext().cpu_registers[1] = guest_handle;
// Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
thread->ResumeFromWait();
@@ -72,18 +75,6 @@ SharedPtr<ResourceLimit> Process::GetResourceLimit() const {
return resource_limit;
}
u64 Process::GetTotalPhysicalMemoryUsed() const {
return vm_manager.GetCurrentHeapSize() + main_thread_stack_size + code_memory_size;
}
void Process::RegisterThread(const Thread* thread) {
thread_list.push_back(thread);
}
void Process::UnregisterThread(const Thread* thread) {
thread_list.remove(thread);
}
ResultCode Process::ClearSignalState() {
if (status == ProcessStatus::Exited) {
LOG_ERROR(Kernel, "called on a terminated process instance.");
@@ -116,23 +107,20 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
return handle_table.SetSize(capabilities.GetHandleTableSize());
}
void Process::Run(s32 main_thread_priority, u64 stack_size) {
// The kernel always ensures that the given stack size is page aligned.
main_thread_stack_size = Common::AlignUp(stack_size, Memory::PAGE_SIZE);
void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
// Allocate and map the main thread stack
// TODO(bunnei): This is heap area that should be allocated by the kernel and not mapped as part
// of the user address space.
const VAddr mapping_address = vm_manager.GetTLSIORegionEndAddress() - main_thread_stack_size;
vm_manager
.MapMemoryBlock(mapping_address, std::make_shared<std::vector<u8>>(main_thread_stack_size),
0, main_thread_stack_size, MemoryState::Stack)
.MapMemoryBlock(vm_manager.GetTLSIORegionEndAddress() - stack_size,
std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size,
MemoryState::Stack)
.Unwrap();
vm_manager.LogLayout();
ChangeStatus(ProcessStatus::Running);
SetupMainThread(*this, kernel, main_thread_priority);
SetupMainThread(*this, kernel, entry_point, main_thread_priority);
}
void Process::PrepareForTermination() {
@@ -147,7 +135,8 @@ void Process::PrepareForTermination() {
continue;
// TODO(Subv): When are the other running/ready threads terminated?
ASSERT_MSG(thread->GetStatus() == ThreadStatus::WaitSynch,
ASSERT_MSG(thread->GetStatus() == ThreadStatus::WaitSynchAny ||
thread->GetStatus() == ThreadStatus::WaitSynchAll,
"Exiting processes with non-waiting threads is currently unimplemented");
thread->Stop();
@@ -221,13 +210,11 @@ void Process::FreeTLSSlot(VAddr tls_address) {
}
void Process::LoadModule(CodeSet module_, VAddr base_addr) {
const auto memory = std::make_shared<std::vector<u8>>(std::move(module_.memory));
const auto MapSegment = [&](const CodeSet::Segment& segment, VMAPermission permissions,
MemoryState memory_state) {
const auto vma = vm_manager
.MapMemoryBlock(segment.addr + base_addr, memory, segment.offset,
segment.size, memory_state)
.MapMemoryBlock(segment.addr + base_addr, module_.memory,
segment.offset, segment.size, memory_state)
.Unwrap();
vm_manager.Reprotect(vma, permissions);
};
@@ -237,12 +224,12 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeData);
MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeData);
code_memory_size += module_.memory.size();
// Clear instruction cache in CPU JIT
system.InvalidateCpuInstructionCaches();
}
Process::Process(Core::System& system)
: WaitObject{system.Kernel()}, vm_manager{system},
address_arbiter{system}, mutex{system}, system{system} {}
: WaitObject{system.Kernel()}, address_arbiter{system}, mutex{system}, system{system} {}
Process::~Process() = default;
@@ -250,7 +237,7 @@ void Process::Acquire(Thread* thread) {
ASSERT_MSG(!ShouldWait(thread), "Object unavailable!");
}
bool Process::ShouldWait(const Thread* thread) const {
bool Process::ShouldWait(Thread* thread) const {
return !is_signaled;
}

View File

@@ -7,7 +7,6 @@
#include <array>
#include <bitset>
#include <cstddef>
#include <list>
#include <string>
#include <vector>
#include <boost/container/static_vector.hpp>
@@ -85,7 +84,7 @@ public:
return name;
}
static constexpr HandleType HANDLE_TYPE = HandleType::Process;
static const HandleType HANDLE_TYPE = HandleType::Process;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
@@ -187,22 +186,6 @@ public:
return random_entropy.at(index);
}
/// Retrieves the total physical memory used by this process in bytes.
u64 GetTotalPhysicalMemoryUsed() const;
/// Gets the list of all threads created with this process as their owner.
const std::list<const Thread*>& GetThreadList() const {
return thread_list;
}
/// Registers a thread as being created under this process,
/// adding it to this process' thread list.
void RegisterThread(const Thread* thread);
/// Unregisters a thread from this process, removing it
/// from this process' thread list.
void UnregisterThread(const Thread* thread);
/// Clears the signaled state of the process if and only if it's signaled.
///
/// @pre The process must not be already terminated. If this is called on a
@@ -225,12 +208,9 @@ public:
ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata);
/**
* Starts the main application thread for this process.
*
* @param main_thread_priority The priority for the main thread.
* @param stack_size The stack size for the main thread in bytes.
* Applies address space changes and launches the process main thread.
*/
void Run(s32 main_thread_priority, u64 stack_size);
void Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size);
/**
* Prepares a process for termination by stopping all of its threads
@@ -254,7 +234,7 @@ private:
~Process() override;
/// Checks if the specified thread should wait until this process is available.
bool ShouldWait(const Thread* thread) const override;
bool ShouldWait(Thread* thread) const override;
/// Acquires/locks this process for the specified thread if it's available.
void Acquire(Thread* thread) override;
@@ -267,12 +247,6 @@ private:
/// Memory manager for this process.
Kernel::VMManager vm_manager;
/// Size of the main thread's stack in bytes.
u64 main_thread_stack_size = 0;
/// Size of the loaded code memory in bytes.
u64 code_memory_size = 0;
/// Current status of the process
ProcessStatus status;
@@ -325,9 +299,6 @@ private:
/// Random values for svcGetInfo RandomEntropy
std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy;
/// List of threads that are running with this process as their owner.
std::list<const Thread*> thread_list;
/// System context
Core::System& system;

View File

@@ -14,16 +14,15 @@ namespace Kernel {
ReadableEvent::ReadableEvent(KernelCore& kernel) : WaitObject{kernel} {}
ReadableEvent::~ReadableEvent() = default;
bool ReadableEvent::ShouldWait(const Thread* thread) const {
bool ReadableEvent::ShouldWait(Thread* thread) const {
return !signaled;
}
void ReadableEvent::Acquire(Thread* thread) {
ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
if (reset_type == ResetType::Automatic) {
if (reset_type == ResetType::OneShot)
signaled = false;
}
}
void ReadableEvent::Signal() {

View File

@@ -31,12 +31,12 @@ public:
return reset_type;
}
static constexpr HandleType HANDLE_TYPE = HandleType::ReadableEvent;
static const HandleType HANDLE_TYPE = HandleType::ReadableEvent;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
bool ShouldWait(const Thread* thread) const override;
bool ShouldWait(Thread* thread) const override;
void Acquire(Thread* thread) override;
/// Unconditionally clears the readable event's state.

View File

@@ -16,8 +16,11 @@ constexpr std::size_t ResourceTypeToIndex(ResourceType type) {
ResourceLimit::ResourceLimit(KernelCore& kernel) : Object{kernel} {}
ResourceLimit::~ResourceLimit() = default;
SharedPtr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel) {
return new ResourceLimit(kernel);
SharedPtr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel, std::string name) {
SharedPtr<ResourceLimit> resource_limit(new ResourceLimit(kernel));
resource_limit->name = std::move(name);
return resource_limit;
}
s64 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const {

View File

@@ -31,17 +31,19 @@ constexpr bool IsValidResourceType(ResourceType type) {
class ResourceLimit final : public Object {
public:
/// Creates a resource limit object.
static SharedPtr<ResourceLimit> Create(KernelCore& kernel);
/**
* Creates a resource limit object.
*/
static SharedPtr<ResourceLimit> Create(KernelCore& kernel, std::string name = "Unknown");
std::string GetTypeName() const override {
return "ResourceLimit";
}
std::string GetName() const override {
return GetTypeName();
return name;
}
static constexpr HandleType HANDLE_TYPE = HandleType::ResourceLimit;
static const HandleType HANDLE_TYPE = HandleType::ResourceLimit;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
@@ -93,6 +95,9 @@ private:
ResourceArray limits{};
/// Current resource limit values.
ResourceArray values{};
/// Name of resource limit object.
std::string name;
};
} // namespace Kernel

View File

@@ -29,7 +29,7 @@ Scheduler::~Scheduler() {
}
bool Scheduler::HaveReadyThreads() const {
std::lock_guard lock{scheduler_mutex};
std::lock_guard<std::mutex> lock(scheduler_mutex);
return !ready_queue.empty();
}
@@ -101,6 +101,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
auto* const thread_owner_process = current_thread->GetOwnerProcess();
if (previous_process != thread_owner_process) {
system.Kernel().MakeCurrentProcess(thread_owner_process);
Memory::SetCurrentPageTable(&thread_owner_process->VMManager().page_table);
}
cpu_core.LoadContext(new_thread->GetContext());
@@ -131,7 +132,7 @@ void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
}
void Scheduler::Reschedule() {
std::lock_guard lock{scheduler_mutex};
std::lock_guard<std::mutex> lock(scheduler_mutex);
Thread* cur = GetCurrentThread();
Thread* next = PopNextReadyThread();
@@ -147,35 +148,35 @@ void Scheduler::Reschedule() {
SwitchContext(next);
}
void Scheduler::AddThread(SharedPtr<Thread> thread) {
std::lock_guard lock{scheduler_mutex};
void Scheduler::AddThread(SharedPtr<Thread> thread, u32 priority) {
std::lock_guard<std::mutex> lock(scheduler_mutex);
thread_list.push_back(std::move(thread));
}
void Scheduler::RemoveThread(Thread* thread) {
std::lock_guard lock{scheduler_mutex};
std::lock_guard<std::mutex> lock(scheduler_mutex);
thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
thread_list.end());
}
void Scheduler::ScheduleThread(Thread* thread, u32 priority) {
std::lock_guard lock{scheduler_mutex};
std::lock_guard<std::mutex> lock(scheduler_mutex);
ASSERT(thread->GetStatus() == ThreadStatus::Ready);
ready_queue.add(thread, priority);
}
void Scheduler::UnscheduleThread(Thread* thread, u32 priority) {
std::lock_guard lock{scheduler_mutex};
std::lock_guard<std::mutex> lock(scheduler_mutex);
ASSERT(thread->GetStatus() == ThreadStatus::Ready);
ready_queue.remove(thread, priority);
}
void Scheduler::SetThreadPriority(Thread* thread, u32 priority) {
std::lock_guard lock{scheduler_mutex};
std::lock_guard<std::mutex> lock(scheduler_mutex);
if (thread->GetPriority() == priority) {
return;
}
@@ -186,7 +187,7 @@ void Scheduler::SetThreadPriority(Thread* thread, u32 priority) {
}
Thread* Scheduler::GetNextSuggestedThread(u32 core, u32 maximum_priority) const {
std::lock_guard lock{scheduler_mutex};
std::lock_guard<std::mutex> lock(scheduler_mutex);
const u32 mask = 1U << core;
for (auto* thread : ready_queue) {

View File

@@ -38,7 +38,7 @@ public:
u64 GetLastContextSwitchTicks() const;
/// Adds a new thread to the scheduler
void AddThread(SharedPtr<Thread> thread);
void AddThread(SharedPtr<Thread> thread, u32 priority);
/// Removes a thread from the scheduler
void RemoveThread(Thread* thread);

View File

@@ -30,7 +30,7 @@ void ServerPort::AppendPendingSession(SharedPtr<ServerSession> pending_session)
pending_sessions.push_back(std::move(pending_session));
}
bool ServerPort::ShouldWait(const Thread* thread) const {
bool ServerPort::ShouldWait(Thread* thread) const {
// If there are no pending sessions, we wait until a new one is added.
return pending_sessions.empty();
}
@@ -39,8 +39,9 @@ void ServerPort::Acquire(Thread* thread) {
ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
}
ServerPort::PortPair ServerPort::CreatePortPair(KernelCore& kernel, u32 max_sessions,
std::string name) {
std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> ServerPort::CreatePortPair(
KernelCore& kernel, u32 max_sessions, std::string name) {
SharedPtr<ServerPort> server_port(new ServerPort(kernel));
SharedPtr<ClientPort> client_port(new ClientPort(kernel));
@@ -50,7 +51,7 @@ ServerPort::PortPair ServerPort::CreatePortPair(KernelCore& kernel, u32 max_sess
client_port->max_sessions = max_sessions;
client_port->active_sessions = 0;
return std::make_pair(std::move(server_port), std::move(client_port));
return std::make_tuple(std::move(server_port), std::move(client_port));
}
} // namespace Kernel

View File

@@ -6,7 +6,7 @@
#include <memory>
#include <string>
#include <utility>
#include <tuple>
#include <vector>
#include "common/common_types.h"
#include "core/hle/kernel/object.h"
@@ -23,7 +23,6 @@ class SessionRequestHandler;
class ServerPort final : public WaitObject {
public:
using HLEHandler = std::shared_ptr<SessionRequestHandler>;
using PortPair = std::pair<SharedPtr<ServerPort>, SharedPtr<ClientPort>>;
/**
* Creates a pair of ServerPort and an associated ClientPort.
@@ -33,8 +32,8 @@ public:
* @param name Optional name of the ports
* @return The created port tuple
*/
static PortPair CreatePortPair(KernelCore& kernel, u32 max_sessions,
std::string name = "UnknownPort");
static std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> CreatePortPair(
KernelCore& kernel, u32 max_sessions, std::string name = "UnknownPort");
std::string GetTypeName() const override {
return "ServerPort";
@@ -43,7 +42,7 @@ public:
return name;
}
static constexpr HandleType HANDLE_TYPE = HandleType::ServerPort;
static const HandleType HANDLE_TYPE = HandleType::ServerPort;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
@@ -76,7 +75,7 @@ public:
/// waiting to be accepted by this port.
void AppendPendingSession(SharedPtr<ServerSession> pending_session);
bool ShouldWait(const Thread* thread) const override;
bool ShouldWait(Thread* thread) const override;
void Acquire(Thread* thread) override;
private:

View File

@@ -28,9 +28,11 @@ ServerSession::~ServerSession() {
// the emulated application.
// Decrease the port's connection count.
if (parent->port) {
if (parent->port)
parent->port->ConnectionClosed();
}
// TODO(Subv): Wake up all the ClientSession's waiting threads and set
// the SendSyncRequest result to 0xC920181A.
parent->server = nullptr;
}
@@ -44,7 +46,7 @@ ResultVal<SharedPtr<ServerSession>> ServerSession::Create(KernelCore& kernel, st
return MakeResult(std::move(server_session));
}
bool ServerSession::ShouldWait(const Thread* thread) const {
bool ServerSession::ShouldWait(Thread* thread) const {
// Closed sessions should never wait, an error will be returned from svcReplyAndReceive.
if (parent->client == nullptr)
return false;
@@ -72,6 +74,9 @@ void ServerSession::ClientDisconnected() {
handler->ClientDisconnected(this);
}
// TODO(Subv): Force a wake up of all the ServerSession's waiting threads and set
// their WaitSynchronization result to 0xC920181A.
// Clean up the list of client threads with pending requests, they are unneeded now that the
// client endpoint is closed.
pending_requesting_threads.clear();
@@ -199,6 +204,6 @@ ServerSession::SessionPair ServerSession::CreateSessionPair(KernelCore& kernel,
client_session->parent = parent;
server_session->parent = parent;
return std::make_pair(std::move(server_session), std::move(client_session));
return std::make_tuple(std::move(server_session), std::move(client_session));
}
} // namespace Kernel

View File

@@ -6,7 +6,6 @@
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "core/hle/kernel/object.h"
@@ -42,11 +41,7 @@ public:
return "ServerSession";
}
std::string GetName() const override {
return name;
}
static constexpr HandleType HANDLE_TYPE = HandleType::ServerSession;
static const HandleType HANDLE_TYPE = HandleType::ServerSession;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
@@ -59,7 +54,7 @@ public:
return parent.get();
}
using SessionPair = std::pair<SharedPtr<ServerSession>, SharedPtr<ClientSession>>;
using SessionPair = std::tuple<SharedPtr<ServerSession>, SharedPtr<ClientSession>>;
/**
* Creates a pair of ServerSession and an associated ClientSession.
@@ -87,7 +82,7 @@ public:
*/
ResultCode HandleSyncRequest(SharedPtr<Thread> thread);
bool ShouldWait(const Thread* thread) const override;
bool ShouldWait(Thread* thread) const override;
void Acquire(Thread* thread) override;

View File

@@ -9,6 +9,7 @@
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/memory.h"
namespace Kernel {
@@ -118,15 +119,7 @@ ResultCode SharedMemory::Map(Process& target_process, VAddr address, MemoryPermi
ConvertPermissions(permissions));
}
ResultCode SharedMemory::Unmap(Process& target_process, VAddr address, u64 unmap_size) {
if (unmap_size != size) {
LOG_ERROR(Kernel,
"Invalid size passed to Unmap. Size must be equal to the size of the "
"memory managed. Shared memory size=0x{:016X}, Unmap size=0x{:016X}",
size, unmap_size);
return ERR_INVALID_SIZE;
}
ResultCode SharedMemory::Unmap(Process& target_process, VAddr address) {
// TODO(Subv): Verify what happens if the application tries to unmap an address that is not
// mapped to a SharedMemory.
return target_process.VMManager().UnmapRange(address, size);

View File

@@ -76,7 +76,7 @@ public:
return name;
}
static constexpr HandleType HANDLE_TYPE = HandleType::SharedMemory;
static const HandleType HANDLE_TYPE = HandleType::SharedMemory;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
@@ -104,17 +104,11 @@ public:
/**
* Unmaps a shared memory block from the specified address in system memory
*
* @param target_process Process from which to unmap the memory block.
* @param address Address in system memory where the shared memory block is mapped.
* @param unmap_size The amount of bytes to unmap from this shared memory instance.
*
* @param address Address in system memory where the shared memory block is mapped
* @return Result code of the unmap operation
*
* @pre The given size to unmap must be the same size as the amount of memory managed by
* the SharedMemory instance itself, otherwise ERR_INVALID_SIZE will be returned.
*/
ResultCode Unmap(Process& target_process, VAddr address, u64 unmap_size);
ResultCode Unmap(Process& target_process, VAddr address);
/**
* Gets a pointer to the shared memory block

File diff suppressed because it is too large Load Diff

View File

@@ -6,12 +6,8 @@
#include "common/common_types.h"
namespace Core {
class System;
}
namespace Kernel {
void CallSVC(Core::System& system, u32 immediate);
void CallSVC(u32 immediate);
} // namespace Kernel

View File

@@ -11,319 +11,270 @@
namespace Kernel {
static inline u64 Param(const Core::System& system, int n) {
return system.CurrentArmInterface().GetReg(n);
static inline u64 Param(int n) {
return Core::CurrentArmInterface().GetReg(n);
}
/**
* HLE a function return from the current ARM userland process
* @param system System context
* @param result Result to return
* @param res Result to return
*/
static inline void FuncReturn(Core::System& system, u64 result) {
system.CurrentArmInterface().SetReg(0, result);
static inline void FuncReturn(u64 res) {
Core::CurrentArmInterface().SetReg(0, res);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Function wrappers that return type ResultCode
template <ResultCode func(Core::System&, u64)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0)).raw);
template <ResultCode func(u64)>
void SvcWrap() {
FuncReturn(func(Param(0)).raw);
}
template <ResultCode func(Core::System&, u32)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw);
template <ResultCode func(u32)>
void SvcWrap() {
FuncReturn(func(static_cast<u32>(Param(0))).raw);
}
template <ResultCode func(Core::System&, u32, u32)>
void SvcWrap(Core::System& system) {
FuncReturn(
system,
func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw);
template <ResultCode func(u32, u32)>
void SvcWrap() {
FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1))).raw);
}
template <ResultCode func(Core::System&, u32, u64, u64, u64)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
Param(system, 2), Param(system, 3))
.raw);
}
template <ResultCode func(Core::System&, u32*)>
void SvcWrap(Core::System& system) {
template <ResultCode func(u32*)>
void SvcWrap() {
u32 param = 0;
const u32 retval = func(system, &param).raw;
system.CurrentArmInterface().SetReg(1, param);
FuncReturn(system, retval);
const u32 retval = func(&param).raw;
Core::CurrentArmInterface().SetReg(1, param);
FuncReturn(retval);
}
template <ResultCode func(Core::System&, u32*, u32)>
void SvcWrap(Core::System& system) {
template <ResultCode func(u32*, u32)>
void SvcWrap() {
u32 param_1 = 0;
const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1))).raw;
system.CurrentArmInterface().SetReg(1, param_1);
FuncReturn(system, retval);
u32 retval = func(&param_1, static_cast<u32>(Param(1))).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
template <ResultCode func(Core::System&, u32*, u32*)>
void SvcWrap(Core::System& system) {
template <ResultCode func(u32*, u32*)>
void SvcWrap() {
u32 param_1 = 0;
u32 param_2 = 0;
const u32 retval = func(system, &param_1, &param_2).raw;
const u32 retval = func(&param_1, &param_2).raw;
auto& arm_interface = system.CurrentArmInterface();
auto& arm_interface = Core::CurrentArmInterface();
arm_interface.SetReg(1, param_1);
arm_interface.SetReg(2, param_2);
FuncReturn(system, retval);
FuncReturn(retval);
}
template <ResultCode func(Core::System&, u32*, u64)>
void SvcWrap(Core::System& system) {
template <ResultCode func(u32*, u64)>
void SvcWrap() {
u32 param_1 = 0;
const u32 retval = func(system, &param_1, Param(system, 1)).raw;
system.CurrentArmInterface().SetReg(1, param_1);
FuncReturn(system, retval);
const u32 retval = func(&param_1, Param(1)).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
template <ResultCode func(Core::System&, u32*, u64, u32)>
void SvcWrap(Core::System& system) {
u32 param_1 = 0;
const u32 retval =
func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2))).raw;
system.CurrentArmInterface().SetReg(1, param_1);
FuncReturn(system, retval);
}
template <ResultCode func(Core::System&, u64*, u32)>
void SvcWrap(Core::System& system) {
template <ResultCode func(u64*, u32)>
void SvcWrap() {
u64 param_1 = 0;
const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1))).raw;
system.CurrentArmInterface().SetReg(1, param_1);
FuncReturn(system, retval);
const u32 retval = func(&param_1, static_cast<u32>(Param(1))).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
template <ResultCode func(Core::System&, u64, s32)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), static_cast<s32>(Param(system, 1))).raw);
template <ResultCode func(u64, s32)>
void SvcWrap() {
FuncReturn(func(Param(0), static_cast<s32>(Param(1))).raw);
}
template <ResultCode func(Core::System&, u64, u32)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1))).raw);
template <ResultCode func(u64, u32)>
void SvcWrap() {
FuncReturn(func(Param(0), static_cast<u32>(Param(1))).raw);
}
template <ResultCode func(Core::System&, u64*, u64)>
void SvcWrap(Core::System& system) {
template <ResultCode func(u64*, u64)>
void SvcWrap() {
u64 param_1 = 0;
const u32 retval = func(system, &param_1, Param(system, 1)).raw;
system.CurrentArmInterface().SetReg(1, param_1);
FuncReturn(system, retval);
u32 retval = func(&param_1, Param(1)).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
template <ResultCode func(Core::System&, u64*, u32, u32)>
void SvcWrap(Core::System& system) {
template <ResultCode func(u64*, u32, u32)>
void SvcWrap() {
u64 param_1 = 0;
const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1)),
static_cast<u32>(Param(system, 2)))
.raw;
system.CurrentArmInterface().SetReg(1, param_1);
FuncReturn(system, retval);
u32 retval = func(&param_1, static_cast<u32>(Param(1)), static_cast<u32>(Param(2))).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
template <ResultCode func(Core::System&, u32, u64)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1)).raw);
template <ResultCode func(u32, u64)>
void SvcWrap() {
FuncReturn(func(static_cast<u32>(Param(0)), Param(1)).raw);
}
template <ResultCode func(Core::System&, u32, u32, u64)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)),
static_cast<u32>(Param(system, 1)), Param(system, 2))
.raw);
template <ResultCode func(u32, u32, u64)>
void SvcWrap() {
FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1)), Param(2)).raw);
}
template <ResultCode func(Core::System&, u32, u32*, u64*)>
void SvcWrap(Core::System& system) {
template <ResultCode func(u32, u32*, u64*)>
void SvcWrap() {
u32 param_1 = 0;
u64 param_2 = 0;
const ResultCode retval = func(system, static_cast<u32>(Param(system, 2)), &param_1, &param_2);
system.CurrentArmInterface().SetReg(1, param_1);
system.CurrentArmInterface().SetReg(2, param_2);
FuncReturn(system, retval.raw);
ResultCode retval = func(static_cast<u32>(Param(2)), &param_1, &param_2);
Core::CurrentArmInterface().SetReg(1, param_1);
Core::CurrentArmInterface().SetReg(2, param_2);
FuncReturn(retval.raw);
}
template <ResultCode func(Core::System&, u64, u64, u32, u32)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
static_cast<u32>(Param(system, 2)), static_cast<u32>(Param(system, 3)))
.raw);
}
template <ResultCode func(Core::System&, u64, u64, u32, u64)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
static_cast<u32>(Param(system, 2)), Param(system, 3))
.raw);
}
template <ResultCode func(Core::System&, u32, u64, u32)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
static_cast<u32>(Param(system, 2)))
.raw);
}
template <ResultCode func(Core::System&, u64, u64, u64)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), Param(system, 1), Param(system, 2)).raw);
}
template <ResultCode func(Core::System&, u64, u64, u32)>
void SvcWrap(Core::System& system) {
template <ResultCode func(u64, u64, u32, u32)>
void SvcWrap() {
FuncReturn(
system,
func(system, Param(system, 0), Param(system, 1), static_cast<u32>(Param(system, 2))).raw);
func(Param(0), Param(1), static_cast<u32>(Param(2)), static_cast<u32>(Param(3))).raw);
}
template <ResultCode func(Core::System&, u32, u64, u64, u32)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
Param(system, 2), static_cast<u32>(Param(system, 3)))
.raw);
template <ResultCode func(u64, u64, u32, u64)>
void SvcWrap() {
FuncReturn(func(Param(0), Param(1), static_cast<u32>(Param(2)), Param(3)).raw);
}
template <ResultCode func(Core::System&, u32, u64, u64)>
void SvcWrap(Core::System& system) {
template <ResultCode func(u32, u64, u32)>
void SvcWrap() {
FuncReturn(func(static_cast<u32>(Param(0)), Param(1), static_cast<u32>(Param(2))).raw);
}
template <ResultCode func(u64, u64, u64)>
void SvcWrap() {
FuncReturn(func(Param(0), Param(1), Param(2)).raw);
}
template <ResultCode func(u64, u64, u32)>
void SvcWrap() {
FuncReturn(func(Param(0), Param(1), static_cast<u32>(Param(2))).raw);
}
template <ResultCode func(u32, u64, u64, u32)>
void SvcWrap() {
FuncReturn(
system,
func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2)).raw);
func(static_cast<u32>(Param(0)), Param(1), Param(2), static_cast<u32>(Param(3))).raw);
}
template <ResultCode func(Core::System&, u32*, u64, u64, s64)>
void SvcWrap(Core::System& system) {
template <ResultCode func(u32, u64, u64)>
void SvcWrap() {
FuncReturn(func(static_cast<u32>(Param(0)), Param(1), Param(2)).raw);
}
template <ResultCode func(u32*, u64, u64, s64)>
void SvcWrap() {
u32 param_1 = 0;
const u32 retval = func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2)),
static_cast<s64>(Param(system, 3)))
.raw;
system.CurrentArmInterface().SetReg(1, param_1);
FuncReturn(system, retval);
ResultCode retval =
func(&param_1, Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3)));
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval.raw);
}
template <ResultCode func(Core::System&, u64, u64, u32, s64)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
static_cast<u32>(Param(system, 2)), static_cast<s64>(Param(system, 3)))
.raw);
template <ResultCode func(u64, u64, u32, s64)>
void SvcWrap() {
FuncReturn(
func(Param(0), Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3))).raw);
}
template <ResultCode func(Core::System&, u64*, u64, u64, u64)>
void SvcWrap(Core::System& system) {
template <ResultCode func(u64*, u64, u64, u64)>
void SvcWrap() {
u64 param_1 = 0;
const u32 retval =
func(system, &param_1, Param(system, 1), Param(system, 2), Param(system, 3)).raw;
system.CurrentArmInterface().SetReg(1, param_1);
FuncReturn(system, retval);
u32 retval = func(&param_1, Param(1), Param(2), Param(3)).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
template <ResultCode func(Core::System&, u32*, u64, u64, u64, u32, s32)>
void SvcWrap(Core::System& system) {
template <ResultCode func(u32*, u64, u64, u64, u32, s32)>
void SvcWrap() {
u32 param_1 = 0;
const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2), Param(system, 3),
static_cast<u32>(Param(system, 4)), static_cast<s32>(Param(system, 5)))
.raw;
system.CurrentArmInterface().SetReg(1, param_1);
FuncReturn(system, retval);
u32 retval = func(&param_1, Param(1), Param(2), Param(3), static_cast<u32>(Param(4)),
static_cast<s32>(Param(5)))
.raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
template <ResultCode func(Core::System&, u32*, u64, u64, u32)>
void SvcWrap(Core::System& system) {
template <ResultCode func(u32*, u64, u64, u32)>
void SvcWrap() {
u32 param_1 = 0;
const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2),
static_cast<u32>(Param(system, 3)))
.raw;
system.CurrentArmInterface().SetReg(1, param_1);
FuncReturn(system, retval);
u32 retval = func(&param_1, Param(1), Param(2), static_cast<u32>(Param(3))).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
template <ResultCode func(Core::System&, Handle*, u64, u32, u32)>
void SvcWrap(Core::System& system) {
template <ResultCode func(Handle*, u64, u32, u32)>
void SvcWrap() {
u32 param_1 = 0;
const u32 retval = func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2)),
static_cast<u32>(Param(system, 3)))
.raw;
system.CurrentArmInterface().SetReg(1, param_1);
FuncReturn(system, retval);
u32 retval =
func(&param_1, Param(1), static_cast<u32>(Param(2)), static_cast<u32>(Param(3))).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
template <ResultCode func(Core::System&, u64, u32, s32, s64)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)),
static_cast<s32>(Param(system, 2)), static_cast<s64>(Param(system, 3)))
.raw);
template <ResultCode func(u64, u32, s32, s64)>
void SvcWrap() {
FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)),
static_cast<s64>(Param(3)))
.raw);
}
template <ResultCode func(Core::System&, u64, u32, s32, s32)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)),
static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3)))
.raw);
template <ResultCode func(u64, u32, s32, s32)>
void SvcWrap() {
FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)),
static_cast<s32>(Param(3)))
.raw);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Function wrappers that return type u32
template <u32 func(Core::System&)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system));
template <u32 func()>
void SvcWrap() {
FuncReturn(func());
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Function wrappers that return type u64
template <u64 func(Core::System&)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system));
template <u64 func()>
void SvcWrap() {
FuncReturn(func());
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Function wrappers that return type void
template <void func(Core::System&)>
void SvcWrap(Core::System& system) {
func(system);
template <void func()>
void SvcWrap() {
func();
}
template <void func(Core::System&, s64)>
void SvcWrap(Core::System& system) {
func(system, static_cast<s64>(Param(system, 0)));
template <void func(s64)>
void SvcWrap() {
func(static_cast<s64>(Param(0)));
}
template <void func(Core::System&, u64, u64)>
void SvcWrap(Core::System& system) {
func(system, Param(system, 0), Param(system, 1));
template <void func(u64, u64 len)>
void SvcWrap() {
func(Param(0), Param(1));
}
template <void func(Core::System&, u64, u64, u64)>
void SvcWrap(Core::System& system) {
func(system, Param(system, 0), Param(system, 1), Param(system, 2));
template <void func(u64, u64, u64)>
void SvcWrap() {
func(Param(0), Param(1), Param(2));
}
template <void func(Core::System&, u32, u64, u64)>
void SvcWrap(Core::System& system) {
func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2));
template <void func(u32, u64, u64)>
void SvcWrap() {
func(static_cast<u32>(Param(0)), Param(1), Param(2));
}
} // namespace Kernel

View File

@@ -28,7 +28,7 @@
namespace Kernel {
bool Thread::ShouldWait(const Thread* thread) const {
bool Thread::ShouldWait(Thread* thread) const {
return status != ThreadStatus::Dead;
}
@@ -62,8 +62,6 @@ void Thread::Stop() {
}
wait_objects.clear();
owner_process->UnregisterThread(this);
// Mark the TLS slot in the thread's page as free.
owner_process->FreeTLSSlot(tls_address);
}
@@ -101,7 +99,8 @@ void Thread::ResumeFromWait() {
ASSERT_MSG(wait_objects.empty(), "Thread is waking up while waiting for objects");
switch (status) {
case ThreadStatus::WaitSynch:
case ThreadStatus::WaitSynchAll:
case ThreadStatus::WaitSynchAny:
case ThreadStatus::WaitHLEEvent:
case ThreadStatus::WaitSleep:
case ThreadStatus::WaitIPC:
@@ -141,12 +140,6 @@ void Thread::ResumeFromWait() {
ChangeScheduler();
}
void Thread::CancelWait() {
ASSERT(GetStatus() == ThreadStatus::WaitSynch);
SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED);
ResumeFromWait();
}
/**
* Resets a thread context, making it ready to be scheduled and run by the CPU
* @param context Thread context to reset
@@ -206,11 +199,9 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();
thread->owner_process = &owner_process;
thread->scheduler = &system.Scheduler(processor_id);
thread->scheduler->AddThread(thread);
thread->scheduler->AddThread(thread, priority);
thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
thread->owner_process->RegisterThread(thread.get());
// TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
// to initialize the context
ResetThreadContext(thread->context, stack_top, entry_point, arg);
@@ -225,6 +216,11 @@ void Thread::SetPriority(u32 priority) {
UpdatePriority();
}
void Thread::BoostPriority(u32 priority) {
scheduler->SetThreadPriority(this, priority);
current_priority = priority;
}
void Thread::SetWaitSynchronizationResult(ResultCode result) {
context.cpu_registers[0] = result.raw;
}
@@ -233,16 +229,16 @@ void Thread::SetWaitSynchronizationOutput(s32 output) {
context.cpu_registers[1] = output;
}
s32 Thread::GetWaitObjectIndex(const WaitObject* object) const {
s32 Thread::GetWaitObjectIndex(WaitObject* object) const {
ASSERT_MSG(!wait_objects.empty(), "Thread is not waiting for anything");
const auto match = std::find(wait_objects.rbegin(), wait_objects.rend(), object);
auto match = std::find(wait_objects.rbegin(), wait_objects.rend(), object);
return static_cast<s32>(std::distance(match, wait_objects.rend()) - 1);
}
VAddr Thread::GetCommandBufferAddress() const {
// Offset from the start of TLS at which the IPC command buffer begins.
constexpr u64 command_header_offset = 0x80;
return GetTLSAddress() + command_header_offset;
static constexpr int CommandHeaderOffset = 0x80;
return GetTLSAddress() + CommandHeaderOffset;
}
void Thread::SetStatus(ThreadStatus new_status) {
@@ -356,7 +352,7 @@ void Thread::ChangeScheduler() {
if (*new_processor_id != processor_id) {
// Remove thread from previous core's scheduler
scheduler->RemoveThread(this);
next_scheduler.AddThread(this);
next_scheduler.AddThread(this, current_priority);
}
processor_id = *new_processor_id;
@@ -371,7 +367,7 @@ void Thread::ChangeScheduler() {
system.CpuCore(processor_id).PrepareReschedule();
}
bool Thread::AllWaitObjectsReady() const {
bool Thread::AllWaitObjectsReady() {
return std::none_of(
wait_objects.begin(), wait_objects.end(),
[this](const SharedPtr<WaitObject>& object) { return object->ShouldWait(this); });

View File

@@ -30,21 +30,12 @@ enum ThreadPriority : u32 {
};
enum ThreadProcessorId : s32 {
/// Indicates that no particular processor core is preferred.
THREADPROCESSORID_DONT_CARE = -1,
/// Run thread on the ideal core specified by the process.
THREADPROCESSORID_IDEAL = -2,
/// Indicates that the preferred processor ID shouldn't be updated in
/// a core mask setting operation.
THREADPROCESSORID_DONT_UPDATE = -3,
THREADPROCESSORID_0 = 0, ///< Run thread on core 0
THREADPROCESSORID_1 = 1, ///< Run thread on core 1
THREADPROCESSORID_2 = 2, ///< Run thread on core 2
THREADPROCESSORID_3 = 3, ///< Run thread on core 3
THREADPROCESSORID_MAX = 4, ///< Processor ID must be less than this
THREADPROCESSORID_IDEAL = -2, ///< Run thread on the ideal core specified by the process.
THREADPROCESSORID_0 = 0, ///< Run thread on core 0
THREADPROCESSORID_1 = 1, ///< Run thread on core 1
THREADPROCESSORID_2 = 2, ///< Run thread on core 2
THREADPROCESSORID_3 = 3, ///< Run thread on core 3
THREADPROCESSORID_MAX = 4, ///< Processor ID must be less than this
/// Allowed CPU mask
THREADPROCESSORID_DEFAULT_MASK = (1 << THREADPROCESSORID_0) | (1 << THREADPROCESSORID_1) |
@@ -58,7 +49,8 @@ enum class ThreadStatus {
WaitHLEEvent, ///< Waiting for hle event to finish
WaitSleep, ///< Waiting due to a SleepThread SVC
WaitIPC, ///< Waiting for the reply from an IPC request
WaitSynch, ///< Waiting due to WaitSynchronization
WaitSynchAny, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false
WaitSynchAll, ///< Waiting due to WaitSynchronizationN with wait_all = true
WaitMutex, ///< Waiting due to an ArbitrateLock svc
WaitCondVar, ///< Waiting due to an WaitProcessWideKey svc
WaitArb, ///< Waiting due to a SignalToAddress/WaitForAddress svc
@@ -110,21 +102,16 @@ public:
std::string GetName() const override {
return name;
}
void SetName(std::string new_name) {
name = std::move(new_name);
}
std::string GetTypeName() const override {
return "Thread";
}
static constexpr HandleType HANDLE_TYPE = HandleType::Thread;
static const HandleType HANDLE_TYPE = HandleType::Thread;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
bool ShouldWait(const Thread* thread) const override;
bool ShouldWait(Thread* thread) const override;
void Acquire(Thread* thread) override;
/**
@@ -149,6 +136,12 @@ public:
*/
void SetPriority(u32 priority);
/**
* Temporarily boosts the thread's priority until the next time it is scheduled
* @param priority The new priority
*/
void BoostPriority(u32 priority);
/// Adds a thread to the list of threads that are waiting for a lock held by this thread.
void AddMutexWaiter(SharedPtr<Thread> thread);
@@ -177,17 +170,11 @@ public:
return tls_memory;
}
/// Resumes a thread from waiting
/**
* Resumes a thread from waiting
*/
void ResumeFromWait();
/// Cancels a waiting operation that this thread may or may not be within.
///
/// When the thread is within a waiting state, this will set the thread's
/// waiting result to signal a canceled wait. The function will then resume
/// this thread.
///
void CancelWait();
/**
* Schedules an event to wake up the specified thread after the specified delay
* @param nanoseconds The time this thread will be allowed to sleep for
@@ -198,30 +185,27 @@ public:
void CancelWakeupTimer();
/**
* Sets the result after the thread awakens (from svcWaitSynchronization)
* Sets the result after the thread awakens (from either WaitSynchronization SVC)
* @param result Value to set to the returned result
*/
void SetWaitSynchronizationResult(ResultCode result);
/**
* Sets the output parameter value after the thread awakens (from svcWaitSynchronization)
* Sets the output parameter value after the thread awakens (from WaitSynchronizationN SVC only)
* @param output Value to set to the output parameter
*/
void SetWaitSynchronizationOutput(s32 output);
/**
* Retrieves the index that this particular object occupies in the list of objects
* that the thread passed to WaitSynchronization, starting the search from the last element.
*
* It is used to set the output index of WaitSynchronization when the thread is awakened.
*
* that the thread passed to WaitSynchronizationN, starting the search from the last element.
* It is used to set the output value of WaitSynchronizationN when the thread is awakened.
* When a thread wakes up due to an object signal, the kernel will use the index of the last
* matching object in the wait objects list in case of having multiple instances of the same
* object in the list.
*
* @param object Object to query the index of.
*/
s32 GetWaitObjectIndex(const WaitObject* object) const;
s32 GetWaitObjectIndex(WaitObject* object) const;
/**
* Stops a thread, invalidating it from further use
@@ -255,9 +239,13 @@ public:
*/
VAddr GetCommandBufferAddress() const;
/// Returns whether this thread is waiting on objects from a WaitSynchronization call.
bool IsSleepingOnWait() const {
return status == ThreadStatus::WaitSynch;
/**
* Returns whether this thread is waiting for all the objects in
* its wait list to become ready, as a result of a WaitSynchronizationN call
* with wait_all = true.
*/
bool IsSleepingOnWaitAll() const {
return status == ThreadStatus::WaitSynchAll;
}
ThreadContext& GetContext() {
@@ -311,7 +299,7 @@ public:
}
/// Determines whether all the objects this thread is waiting on are ready.
bool AllWaitObjectsReady() const;
bool AllWaitObjectsReady();
const MutexWaitingThreads& GetMutexWaitingThreads() const {
return wait_mutex_threads;
@@ -357,6 +345,10 @@ public:
arb_wait_address = address;
}
void SetGuestHandle(Handle handle) {
guest_handle = handle;
}
bool HasWakeupCallback() const {
return wakeup_callback != nullptr;
}
@@ -431,7 +423,7 @@ private:
Process* owner_process;
/// Objects that the thread is waiting on, in the same order as they were
/// passed to WaitSynchronization.
/// passed to WaitSynchronization1/N.
ThreadWaitObjects wait_objects;
/// List of threads that are waiting for a mutex that is held by this thread.
@@ -450,11 +442,14 @@ private:
/// If waiting for an AddressArbiter, this is the address being waited on.
VAddr arb_wait_address{0};
/// Handle used by guest emulated application to access this thread
Handle guest_handle = 0;
/// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
Handle callback_handle = 0;
/// Callback that will be invoked when the thread is resumed from a waiting state. If the thread
/// was waiting via WaitSynchronization then the object will be the last object that became
/// was waiting via WaitSynchronizationN then the object will be the last object that became
/// available. In case of a timeout, the object will be nullptr.
WakeupCallback wakeup_callback;

View File

@@ -14,8 +14,8 @@ namespace Kernel {
TransferMemory::TransferMemory(KernelCore& kernel) : Object{kernel} {}
TransferMemory::~TransferMemory() = default;
SharedPtr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr base_address, u64 size,
MemoryPermission permissions) {
SharedPtr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr base_address,
size_t size, MemoryPermission permissions) {
SharedPtr<TransferMemory> transfer_memory{new TransferMemory(kernel)};
transfer_memory->base_address = base_address;
@@ -26,15 +26,7 @@ SharedPtr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr base_
return transfer_memory;
}
const u8* TransferMemory::GetPointer() const {
return backing_block.get()->data();
}
u64 TransferMemory::GetSize() const {
return memory_size;
}
ResultCode TransferMemory::MapMemory(VAddr address, u64 size, MemoryPermission permissions) {
ResultCode TransferMemory::MapMemory(VAddr address, size_t size, MemoryPermission permissions) {
if (memory_size != size) {
return ERR_INVALID_SIZE;
}
@@ -47,13 +39,13 @@ ResultCode TransferMemory::MapMemory(VAddr address, u64 size, MemoryPermission p
return ERR_INVALID_STATE;
}
backing_block = std::make_shared<std::vector<u8>>(size);
const auto map_state = owner_permissions == MemoryPermission::None
? MemoryState::TransferMemoryIsolated
: MemoryState::TransferMemory;
auto& vm_manager = owner_process->VMManager();
const auto map_result = vm_manager.MapMemoryBlock(address, backing_block, 0, size, map_state);
const auto map_result = vm_manager.MapMemoryBlock(
address, std::make_shared<std::vector<u8>>(size), 0, size, map_state);
if (map_result.Failed()) {
return map_result.Code();
}
@@ -62,7 +54,7 @@ ResultCode TransferMemory::MapMemory(VAddr address, u64 size, MemoryPermission p
return RESULT_SUCCESS;
}
ResultCode TransferMemory::UnmapMemory(VAddr address, u64 size) {
ResultCode TransferMemory::UnmapMemory(VAddr address, size_t size) {
if (memory_size != size) {
return ERR_INVALID_SIZE;
}

View File

@@ -4,9 +4,6 @@
#pragma once
#include <memory>
#include <vector>
#include "core/hle/kernel/object.h"
union ResultCode;
@@ -28,7 +25,7 @@ class TransferMemory final : public Object {
public:
static constexpr HandleType HANDLE_TYPE = HandleType::TransferMemory;
static SharedPtr<TransferMemory> Create(KernelCore& kernel, VAddr base_address, u64 size,
static SharedPtr<TransferMemory> Create(KernelCore& kernel, VAddr base_address, size_t size,
MemoryPermission permissions);
TransferMemory(const TransferMemory&) = delete;
@@ -49,12 +46,6 @@ public:
return HANDLE_TYPE;
}
/// Gets a pointer to the backing block of this instance.
const u8* GetPointer() const;
/// Gets the size of the memory backing this instance in bytes.
u64 GetSize() const;
/// Attempts to map transfer memory with the given range and memory permissions.
///
/// @param address The base address to being mapping memory at.
@@ -65,7 +56,7 @@ public:
/// the same values that were given when creating the transfer memory
/// instance.
///
ResultCode MapMemory(VAddr address, u64 size, MemoryPermission permissions);
ResultCode MapMemory(VAddr address, size_t size, MemoryPermission permissions);
/// Unmaps the transfer memory with the given range
///
@@ -75,20 +66,17 @@ public:
/// @pre The given address and size must be the same as the ones used
/// to create the transfer memory instance.
///
ResultCode UnmapMemory(VAddr address, u64 size);
ResultCode UnmapMemory(VAddr address, size_t size);
private:
explicit TransferMemory(KernelCore& kernel);
~TransferMemory() override;
/// Memory block backing this instance.
std::shared_ptr<std::vector<u8>> backing_block;
/// The base address for the memory managed by this instance.
VAddr base_address = 0;
/// Size of the memory, in bytes, that this instance manages.
u64 memory_size = 0;
size_t memory_size = 0;
/// The memory permissions that are applied to this instance.
MemoryPermission owner_permissions{};

View File

@@ -62,7 +62,7 @@ bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
return true;
}
VMManager::VMManager(Core::System& system) : system{system} {
VMManager::VMManager() {
// Default to assuming a 39-bit address space. This way we have a sane
// starting point with executables that don't provide metadata.
Reset(FileSys::ProgramAddressSpaceType::Is39Bit);
@@ -111,6 +111,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
VirtualMemoryArea& final_vma = vma_handle->second;
ASSERT(final_vma.size == size);
auto& system = Core::System::GetInstance();
system.ArmInterface(0).MapBackingMemory(target, size, block->data() + offset,
VMAPermission::ReadWriteExecute);
system.ArmInterface(1).MapBackingMemory(target, size, block->data() + offset,
@@ -139,6 +140,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me
VirtualMemoryArea& final_vma = vma_handle->second;
ASSERT(final_vma.size == size);
auto& system = Core::System::GetInstance();
system.ArmInterface(0).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
system.ArmInterface(1).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
system.ArmInterface(2).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
@@ -221,6 +223,7 @@ ResultCode VMManager::UnmapRange(VAddr target, u64 size) {
ASSERT(FindVMA(target)->second.size >= size);
auto& system = Core::System::GetInstance();
system.ArmInterface(0).UnmapMemory(target, size);
system.ArmInterface(1).UnmapMemory(target, size);
system.ArmInterface(2).UnmapMemory(target, size);
@@ -299,86 +302,6 @@ ResultVal<VAddr> VMManager::SetHeapSize(u64 size) {
return MakeResult<VAddr>(heap_region_base);
}
ResultCode VMManager::MapCodeMemory(VAddr dst_address, VAddr src_address, u64 size) {
constexpr auto ignore_attribute = MemoryAttribute::LockedForIPC | MemoryAttribute::DeviceMapped;
const auto src_check_result = CheckRangeState(
src_address, size, MemoryState::All, MemoryState::Heap, VMAPermission::All,
VMAPermission::ReadWrite, MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute);
if (src_check_result.Failed()) {
return src_check_result.Code();
}
const auto mirror_result =
MirrorMemory(dst_address, src_address, size, MemoryState::ModuleCode);
if (mirror_result.IsError()) {
return mirror_result;
}
// Ensure we lock the source memory region.
const auto src_vma_result = CarveVMARange(src_address, size);
if (src_vma_result.Failed()) {
return src_vma_result.Code();
}
auto src_vma_iter = *src_vma_result;
src_vma_iter->second.attribute = MemoryAttribute::Locked;
Reprotect(src_vma_iter, VMAPermission::Read);
// The destination memory region is fine as is, however we need to make it read-only.
return ReprotectRange(dst_address, size, VMAPermission::Read);
}
ResultCode VMManager::UnmapCodeMemory(VAddr dst_address, VAddr src_address, u64 size) {
constexpr auto ignore_attribute = MemoryAttribute::LockedForIPC | MemoryAttribute::DeviceMapped;
const auto src_check_result = CheckRangeState(
src_address, size, MemoryState::All, MemoryState::Heap, VMAPermission::None,
VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::Locked, ignore_attribute);
if (src_check_result.Failed()) {
return src_check_result.Code();
}
// Yes, the kernel only checks the first page of the region.
const auto dst_check_result =
CheckRangeState(dst_address, Memory::PAGE_SIZE, MemoryState::FlagModule,
MemoryState::FlagModule, VMAPermission::None, VMAPermission::None,
MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute);
if (dst_check_result.Failed()) {
return dst_check_result.Code();
}
const auto dst_memory_state = std::get<MemoryState>(*dst_check_result);
const auto dst_contiguous_check_result = CheckRangeState(
dst_address, size, MemoryState::All, dst_memory_state, VMAPermission::None,
VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute);
if (dst_contiguous_check_result.Failed()) {
return dst_contiguous_check_result.Code();
}
const auto unmap_result = UnmapRange(dst_address, size);
if (unmap_result.IsError()) {
return unmap_result;
}
// With the mirrored portion unmapped, restore the original region's traits.
const auto src_vma_result = CarveVMARange(src_address, size);
if (src_vma_result.Failed()) {
return src_vma_result.Code();
}
auto src_vma_iter = *src_vma_result;
src_vma_iter->second.state = MemoryState::Heap;
src_vma_iter->second.attribute = MemoryAttribute::None;
Reprotect(src_vma_iter, VMAPermission::ReadWrite);
if (dst_memory_state == MemoryState::ModuleCode) {
system.InvalidateCpuInstructionCaches();
}
return unmap_result;
}
MemoryInfo VMManager::QueryMemory(VAddr address) const {
const auto vma = FindVMA(address);
MemoryInfo memory_info{};

Some files were not shown because too many files have changed in this diff Show More