Compare commits
3 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26c4cb1fdc | ||
|
|
f72b9e2c32 | ||
|
|
03feb29bce |
@@ -1,3 +1,3 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
docker run --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache citraemu/build-environments:linux-clang-format /bin/bash -ex /yuzu/.travis/clang-format/docker.sh
|
||||
docker run -v $(pwd):/yuzu ubuntu:18.04 /bin/bash -ex /yuzu/.travis/clang-format/docker.sh
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh -ex
|
||||
|
||||
docker pull citraemu/build-environments:linux-clang-format
|
||||
docker pull ubuntu:18.04
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
apt-get update
|
||||
apt-get install -y clang-format-6.0
|
||||
|
||||
# Run clang-format
|
||||
cd /yuzu
|
||||
./.travis/clang-format/script.sh
|
||||
|
||||
@@ -57,4 +57,3 @@ done
|
||||
|
||||
pip3 install pefile
|
||||
python3 .travis/linux-mingw/scan_dll.py package/*.exe "package/"
|
||||
python3 .travis/linux-mingw/scan_dll.py package/imageformats/*.dll "package/"
|
||||
|
||||
@@ -23,21 +23,21 @@ option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
|
||||
|
||||
option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
|
||||
|
||||
if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit)
|
||||
if(NOT EXISTS ${CMAKE_SOURCE_DIR}/.git/hooks/pre-commit)
|
||||
message(STATUS "Copying pre-commit hook")
|
||||
file(COPY hooks/pre-commit
|
||||
DESTINATION ${PROJECT_SOURCE_DIR}/.git/hooks)
|
||||
DESTINATION ${CMAKE_SOURCE_DIR}/.git/hooks)
|
||||
endif()
|
||||
|
||||
# Sanity check : Check that all submodules are present
|
||||
# =======================================================================
|
||||
|
||||
function(check_submodules_present)
|
||||
file(READ "${PROJECT_SOURCE_DIR}/.gitmodules" gitmodules)
|
||||
file(READ "${CMAKE_SOURCE_DIR}/.gitmodules" gitmodules)
|
||||
string(REGEX MATCHALL "path *= *[^ \t\r\n]*" gitmodules ${gitmodules})
|
||||
foreach(module ${gitmodules})
|
||||
string(REGEX REPLACE "path *= *" "" module ${module})
|
||||
if (NOT EXISTS "${PROJECT_SOURCE_DIR}/${module}/.git")
|
||||
if (NOT EXISTS "${CMAKE_SOURCE_DIR}/${module}/.git")
|
||||
message(FATAL_ERROR "Git submodule ${module} not found. "
|
||||
"Please run: git submodule update --init --recursive")
|
||||
endif()
|
||||
@@ -45,17 +45,17 @@ function(check_submodules_present)
|
||||
endfunction()
|
||||
check_submodules_present()
|
||||
|
||||
configure_file(${PROJECT_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||
configure_file(${CMAKE_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||
${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||
COPYONLY)
|
||||
if (ENABLE_COMPATIBILITY_LIST_DOWNLOAD AND NOT EXISTS ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
|
||||
if (ENABLE_COMPATIBILITY_LIST_DOWNLOAD AND NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
|
||||
message(STATUS "Downloading compatibility list for yuzu...")
|
||||
file(DOWNLOAD
|
||||
https://api.yuzu-emu.org/gamedb/
|
||||
"${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json" SHOW_PROGRESS)
|
||||
"${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json" SHOW_PROGRESS)
|
||||
endif()
|
||||
if (NOT EXISTS ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
|
||||
file(WRITE ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json "")
|
||||
if (NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
|
||||
file(WRITE ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json "")
|
||||
endif()
|
||||
|
||||
# Detect current compilation architecture and create standard definitions
|
||||
@@ -170,7 +170,7 @@ endif()
|
||||
# On modern Unixes, this is typically already the case. The lone exception is
|
||||
# glibc, which may default to 32 bits. glibc allows this to be configured
|
||||
# by setting _FILE_OFFSET_BITS.
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR MINGW)
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
add_definitions(-D_FILE_OFFSET_BITS=64)
|
||||
endif()
|
||||
|
||||
@@ -178,6 +178,10 @@ endif()
|
||||
set_property(DIRECTORY APPEND PROPERTY
|
||||
COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>)
|
||||
|
||||
|
||||
math(EXPR EMU_ARCH_BITS ${CMAKE_SIZEOF_VOID_P}*8)
|
||||
add_definitions(-DEMU_ARCH_BITS=${EMU_ARCH_BITS})
|
||||
|
||||
# System imported libraries
|
||||
# ======================
|
||||
|
||||
@@ -185,13 +189,13 @@ find_package(Boost 1.63.0 QUIET)
|
||||
if (NOT Boost_FOUND)
|
||||
message(STATUS "Boost 1.63.0 or newer not found, falling back to externals")
|
||||
|
||||
set(BOOST_ROOT "${PROJECT_SOURCE_DIR}/externals/boost")
|
||||
set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/boost")
|
||||
set(Boost_NO_SYSTEM_PATHS OFF)
|
||||
find_package(Boost QUIET REQUIRED)
|
||||
endif()
|
||||
|
||||
# Output binaries to bin/
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
|
||||
# Prefer the -pthread flag on Linux.
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
@@ -260,7 +264,7 @@ if (YUZU_USE_BUNDLED_UNICORN)
|
||||
endif()
|
||||
|
||||
set(UNICORN_FOUND YES)
|
||||
set(UNICORN_PREFIX ${PROJECT_SOURCE_DIR}/externals/unicorn)
|
||||
set(UNICORN_PREFIX ${CMAKE_SOURCE_DIR}/externals/unicorn)
|
||||
set(LIBUNICORN_LIBRARY "${UNICORN_PREFIX}/${UNICORN_LIB_NAME}" CACHE PATH "Path to Unicorn library" FORCE)
|
||||
set(LIBUNICORN_INCLUDE_DIR "${UNICORN_PREFIX}/include" CACHE PATH "Path to Unicorn headers" FORCE)
|
||||
set(UNICORN_DLL_DIR "${UNICORN_PREFIX}/" CACHE PATH "Path to unicorn dynamic library" FORCE)
|
||||
@@ -352,12 +356,12 @@ set(CLANG_FORMAT_POSTFIX "-6.0")
|
||||
find_program(CLANG_FORMAT
|
||||
NAMES clang-format${CLANG_FORMAT_POSTFIX}
|
||||
clang-format
|
||||
PATHS ${PROJECT_BINARY_DIR}/externals)
|
||||
PATHS ${CMAKE_BINARY_DIR}/externals)
|
||||
# if find_program doesn't find it, try to download from externals
|
||||
if (NOT CLANG_FORMAT)
|
||||
if (WIN32)
|
||||
message(STATUS "Clang format not found! Downloading...")
|
||||
set(CLANG_FORMAT "${PROJECT_BINARY_DIR}/externals/clang-format${CLANG_FORMAT_POSTFIX}.exe")
|
||||
set(CLANG_FORMAT "${CMAKE_BINARY_DIR}/externals/clang-format${CLANG_FORMAT_POSTFIX}.exe")
|
||||
file(DOWNLOAD
|
||||
https://github.com/yuzu-emu/ext-windows-bin/raw/master/clang-format${CLANG_FORMAT_POSTFIX}.exe
|
||||
"${CLANG_FORMAT}" SHOW_PROGRESS
|
||||
@@ -373,7 +377,7 @@ if (NOT CLANG_FORMAT)
|
||||
endif()
|
||||
|
||||
if (CLANG_FORMAT)
|
||||
set(SRCS ${PROJECT_SOURCE_DIR}/src)
|
||||
set(SRCS ${CMAKE_SOURCE_DIR}/src)
|
||||
set(CCOMMENT "Running clang format against all the .h and .cpp files in src/")
|
||||
if (WIN32)
|
||||
add_custom_target(clang-format
|
||||
@@ -446,10 +450,10 @@ endif()
|
||||
# http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
|
||||
# http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html
|
||||
if(ENABLE_QT AND UNIX AND NOT APPLE)
|
||||
install(FILES "${PROJECT_SOURCE_DIR}/dist/yuzu.desktop"
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/dist/yuzu.desktop"
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/applications")
|
||||
install(FILES "${PROJECT_SOURCE_DIR}/dist/yuzu.svg"
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/dist/yuzu.svg"
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps")
|
||||
install(FILES "${PROJECT_SOURCE_DIR}/dist/yuzu.xml"
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/dist/yuzu.xml"
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/mime/packages")
|
||||
endif()
|
||||
|
||||
23
appveyor.yml
23
appveyor.yml
@@ -125,6 +125,17 @@ after_build:
|
||||
Copy-Item -path "$CMAKE_SOURCE_DIR/license.txt" -destination $RELEASE_DIST
|
||||
Copy-Item -path "$CMAKE_SOURCE_DIR/README.md" -destination $RELEASE_DIST
|
||||
|
||||
# copy all the dll dependencies to the release folder
|
||||
. "./.appveyor/UtilityFunctions.ps1"
|
||||
$DLLSearchPath = "C:\msys64\mingw64\bin;$env:PATH"
|
||||
$MingwDLLs = RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\yuzu.exe"
|
||||
$MingwDLLs += RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\yuzu_cmd.exe"
|
||||
Write-Host "Detected the following dependencies:"
|
||||
Write-Host $MingwDLLs
|
||||
foreach ($file in $MingwDLLs) {
|
||||
Copy-Item -path "$file" -force -destination "$RELEASE_DIST"
|
||||
}
|
||||
|
||||
# copy the qt windows plugin dll to platforms
|
||||
Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/platforms/qwindows.dll" -force -destination "$RELEASE_DIST/platforms"
|
||||
|
||||
@@ -134,18 +145,6 @@ after_build:
|
||||
# copy the qt jpeg imageformat dll to platforms
|
||||
Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/imageformats/qjpeg.dll" -force -destination "$RELEASE_DIST/imageformats"
|
||||
|
||||
# copy all the dll dependencies to the release folder
|
||||
. "./.appveyor/UtilityFunctions.ps1"
|
||||
$DLLSearchPath = "C:\msys64\mingw64\bin;$env:PATH"
|
||||
$MingwDLLs = RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\yuzu.exe"
|
||||
$MingwDLLs += RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\yuzu_cmd.exe"
|
||||
$MingwDLLs += RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\imageformats\qjpeg.dll"
|
||||
Write-Host "Detected the following dependencies:"
|
||||
Write-Host $MingwDLLs
|
||||
foreach ($file in $MingwDLLs) {
|
||||
Copy-Item -path "$file" -force -destination "$RELEASE_DIST"
|
||||
}
|
||||
|
||||
7z a -tzip $MINGW_BUILD_ZIP $RELEASE_DIST\*
|
||||
7z a $MINGW_SEVENZIP $RELEASE_DIST
|
||||
}
|
||||
|
||||
@@ -33,10 +33,6 @@ else()
|
||||
endif()
|
||||
|
||||
if(NOT HEAD_HASH)
|
||||
if(EXISTS "@GIT_DATA@/head-ref")
|
||||
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
|
||||
string(STRIP "${HEAD_HASH}" HEAD_HASH)
|
||||
else()
|
||||
set(HEAD_HASH "Unknown")
|
||||
endif()
|
||||
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
|
||||
string(STRIP "${HEAD_HASH}" HEAD_HASH)
|
||||
endif()
|
||||
|
||||
@@ -143,7 +143,7 @@ struct AuxInfo {
|
||||
std::array<u8, 24> output_mix_buffers;
|
||||
u32_le mix_buffer_count;
|
||||
u32_le sample_rate; // Stored in the aux buffer currently
|
||||
u32_le sample_count;
|
||||
u32_le sampe_count;
|
||||
u64_le send_buffer_info;
|
||||
u64_le send_buffer_base;
|
||||
|
||||
|
||||
@@ -121,8 +121,7 @@ CubebSink::CubebSink(std::string target_device_name) {
|
||||
const auto collection_end{collection.device + collection.count};
|
||||
const auto device{
|
||||
std::find_if(collection.device, collection_end, [&](const cubeb_device_info& info) {
|
||||
return info.friendly_name != nullptr &&
|
||||
target_device_name == info.friendly_name;
|
||||
return target_device_name == info.friendly_name;
|
||||
})};
|
||||
if (device != collection_end) {
|
||||
output_device = device->devid;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "audio_core/stream.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/core_timing_util.h"
|
||||
#include "core/settings.h"
|
||||
@@ -103,7 +104,10 @@ void Stream::PlayNextBuffer() {
|
||||
CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(AudioOutput, "Audio", "ReleaseActiveBuffer", MP_RGB(100, 100, 255));
|
||||
|
||||
void Stream::ReleaseActiveBuffer() {
|
||||
MICROPROFILE_SCOPE(AudioOutput);
|
||||
ASSERT(active_buffer);
|
||||
released_buffers.push(std::move(active_buffer));
|
||||
release_callback();
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count) : m_sample_rate{sample_rate} {
|
||||
TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count)
|
||||
: m_sample_rate(sample_rate), m_channel_count(channel_count) {
|
||||
m_sound_touch.setChannels(channel_count);
|
||||
m_sound_touch.setSampleRate(sample_rate);
|
||||
m_sound_touch.setPitch(1.0);
|
||||
@@ -32,10 +33,10 @@ std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
|
||||
// We were given actual_samples number of samples, and num_samples were requested from us.
|
||||
double current_ratio = static_cast<double>(num_in) / static_cast<double>(num_out);
|
||||
|
||||
const double max_latency = 0.25; // seconds
|
||||
const double max_latency = 1.0; // seconds
|
||||
const double max_backlog = m_sample_rate * max_latency;
|
||||
const double backlog_fullness = m_sound_touch.numSamples() / max_backlog;
|
||||
if (backlog_fullness > 4.0) {
|
||||
if (backlog_fullness > 5.0) {
|
||||
// Too many samples in backlog: Don't push anymore on
|
||||
num_in = 0;
|
||||
}
|
||||
@@ -49,7 +50,7 @@ std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
|
||||
|
||||
// This low-pass filter smoothes out variance in the calculated stretch ratio.
|
||||
// The time-scale determines how responsive this filter is.
|
||||
constexpr double lpf_time_scale = 0.712; // seconds
|
||||
constexpr double lpf_time_scale = 2.0; // seconds
|
||||
const double lpf_gain = 1.0 - std::exp(-time_delta / lpf_time_scale);
|
||||
m_stretch_ratio += lpf_gain * (current_ratio - m_stretch_ratio);
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ public:
|
||||
|
||||
private:
|
||||
u32 m_sample_rate;
|
||||
u32 m_channel_count;
|
||||
soundtouch::SoundTouch m_sound_touch;
|
||||
double m_stretch_ratio = 1.0;
|
||||
};
|
||||
|
||||
@@ -64,6 +64,8 @@ add_library(common STATIC
|
||||
logging/text_formatter.cpp
|
||||
logging/text_formatter.h
|
||||
math_util.h
|
||||
memory_util.cpp
|
||||
memory_util.h
|
||||
microprofile.cpp
|
||||
microprofile.h
|
||||
microprofileui.h
|
||||
|
||||
@@ -117,21 +117,21 @@ private:
|
||||
// We don't delete it because we want BitField to be trivially copyable.
|
||||
constexpr BitField& operator=(const BitField&) = default;
|
||||
|
||||
// UnderlyingType is T for non-enum types and the underlying type of T if
|
||||
// StorageType is T for non-enum types and the underlying type of T if
|
||||
// T is an enumeration. Note that T is wrapped within an enable_if in the
|
||||
// former case to workaround compile errors which arise when using
|
||||
// std::underlying_type<T>::type directly.
|
||||
using UnderlyingType = typename std::conditional_t<std::is_enum_v<T>, std::underlying_type<T>,
|
||||
std::enable_if<true, T>>::type;
|
||||
using StorageType = typename std::conditional_t<std::is_enum<T>::value, std::underlying_type<T>,
|
||||
std::enable_if<true, T>>::type;
|
||||
|
||||
// We store the value as the unsigned type to avoid undefined behaviour on value shifting
|
||||
using StorageType = std::make_unsigned_t<UnderlyingType>;
|
||||
// Unsigned version of StorageType
|
||||
using StorageTypeU = std::make_unsigned_t<StorageType>;
|
||||
|
||||
public:
|
||||
/// Constants to allow limited introspection of fields if needed
|
||||
static constexpr std::size_t position = Position;
|
||||
static constexpr std::size_t bits = Bits;
|
||||
static constexpr StorageType mask = (((StorageType)~0) >> (8 * sizeof(T) - bits)) << position;
|
||||
static constexpr StorageType mask = (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position;
|
||||
|
||||
/**
|
||||
* Formats a value by masking and shifting it according to the field parameters. A value
|
||||
@@ -148,12 +148,11 @@ public:
|
||||
* union in a constexpr context.
|
||||
*/
|
||||
static constexpr FORCE_INLINE T ExtractValue(const StorageType& storage) {
|
||||
if constexpr (std::numeric_limits<UnderlyingType>::is_signed) {
|
||||
if (std::numeric_limits<T>::is_signed) {
|
||||
std::size_t shift = 8 * sizeof(T) - bits;
|
||||
return static_cast<T>(static_cast<UnderlyingType>(storage << (shift - position)) >>
|
||||
shift);
|
||||
return (T)((storage << (shift - position)) >> shift);
|
||||
} else {
|
||||
return static_cast<T>((storage & mask) >> position);
|
||||
return (T)((storage & mask) >> position);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,24 +15,21 @@
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
// windows.h needs to be included before other windows headers
|
||||
#include <direct.h> // getcwd
|
||||
#include <commdlg.h> // for GetSaveFileName
|
||||
#include <direct.h> // getcwd
|
||||
#include <io.h>
|
||||
#include <shellapi.h>
|
||||
#include <shlobj.h> // for SHGetFolderPath
|
||||
#include <tchar.h>
|
||||
#include "common/string_util.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// 64 bit offsets for MSVC
|
||||
// 64 bit offsets for windows
|
||||
#define fseeko _fseeki64
|
||||
#define ftello _ftelli64
|
||||
#define fileno _fileno
|
||||
#endif
|
||||
|
||||
// 64 bit offsets for MSVC and MinGW. MinGW also needs this for using _wstat64
|
||||
#define atoll _atoi64
|
||||
#define stat _stat64
|
||||
#define fstat _fstat64
|
||||
|
||||
#define fileno _fileno
|
||||
#else
|
||||
#ifdef __APPLE__
|
||||
#include <sys/param.h>
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#ifdef _WIN32
|
||||
#include <share.h> // For _SH_DENYWR
|
||||
#include <windows.h> // For OutputDebugStringA
|
||||
#include <share.h> // For _SH_DENYWR
|
||||
#else
|
||||
#define _SH_DENYWR 0
|
||||
#endif
|
||||
@@ -140,18 +139,12 @@ void FileBackend::Write(const Entry& entry) {
|
||||
if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) {
|
||||
return;
|
||||
}
|
||||
bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n'));
|
||||
bytes_written += file.WriteString(FormatLogMessage(entry) + '\n');
|
||||
if (entry.log_level >= Level::Error) {
|
||||
file.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
void DebuggerBackend::Write(const Entry& entry) {
|
||||
#ifdef _WIN32
|
||||
::OutputDebugStringA(FormatLogMessage(entry).append(1, '\n').c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
|
||||
#define ALL_LOG_CLASSES() \
|
||||
CLS(Log) \
|
||||
@@ -203,7 +196,6 @@ void DebuggerBackend::Write(const Entry& entry) {
|
||||
SUB(Service, NFP) \
|
||||
SUB(Service, NIFM) \
|
||||
SUB(Service, NIM) \
|
||||
SUB(Service, NPNS) \
|
||||
SUB(Service, NS) \
|
||||
SUB(Service, NVDRV) \
|
||||
SUB(Service, PCIE) \
|
||||
@@ -212,12 +204,10 @@ void DebuggerBackend::Write(const Entry& entry) {
|
||||
SUB(Service, PM) \
|
||||
SUB(Service, PREPO) \
|
||||
SUB(Service, PSC) \
|
||||
SUB(Service, PSM) \
|
||||
SUB(Service, SET) \
|
||||
SUB(Service, SM) \
|
||||
SUB(Service, SPL) \
|
||||
SUB(Service, SSL) \
|
||||
SUB(Service, TCAP) \
|
||||
SUB(Service, Time) \
|
||||
SUB(Service, USB) \
|
||||
SUB(Service, VI) \
|
||||
|
||||
@@ -103,20 +103,6 @@ private:
|
||||
std::size_t bytes_written;
|
||||
};
|
||||
|
||||
/**
|
||||
* Backend that writes to Visual Studio's output window
|
||||
*/
|
||||
class DebuggerBackend : public Backend {
|
||||
public:
|
||||
static const char* Name() {
|
||||
return "debugger";
|
||||
}
|
||||
const char* GetName() const override {
|
||||
return Name();
|
||||
}
|
||||
void Write(const Entry& entry) override;
|
||||
};
|
||||
|
||||
void AddBackend(std::unique_ptr<Backend> backend);
|
||||
|
||||
void RemoveBackend(std::string_view backend_name);
|
||||
|
||||
@@ -83,7 +83,6 @@ enum class Class : ClassType {
|
||||
Service_NFP, ///< The NFP service
|
||||
Service_NIFM, ///< The NIFM (Network interface) service
|
||||
Service_NIM, ///< The NIM service
|
||||
Service_NPNS, ///< The NPNS service
|
||||
Service_NS, ///< The NS services
|
||||
Service_NVDRV, ///< The NVDRV (Nvidia driver) service
|
||||
Service_PCIE, ///< The PCIe service
|
||||
@@ -92,12 +91,10 @@ enum class Class : ClassType {
|
||||
Service_PM, ///< The PM service
|
||||
Service_PREPO, ///< The PREPO (Play report) service
|
||||
Service_PSC, ///< The PSC service
|
||||
Service_PSM, ///< The PSM service
|
||||
Service_SET, ///< The SET (Settings) service
|
||||
Service_SM, ///< The SM (Service manager) service
|
||||
Service_SPL, ///< The SPL service
|
||||
Service_SSL, ///< The SSL service
|
||||
Service_TCAP, ///< The TCAP service.
|
||||
Service_Time, ///< The time service
|
||||
Service_USB, ///< The USB (Universal Serial Bus) service
|
||||
Service_VI, ///< The VI (Video interface) service
|
||||
|
||||
177
src/common/memory_util.cpp
Normal file
177
src/common/memory_util.cpp
Normal file
@@ -0,0 +1,177 @@
|
||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/memory_util.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
// Windows.h needs to be included before psapi.h
|
||||
#include <psapi.h>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/string_util.h"
|
||||
#else
|
||||
#include <cstdlib>
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
|
||||
#include <unistd.h>
|
||||
#define PAGE_MASK (getpagesize() - 1)
|
||||
#define round_page(x) ((((unsigned long)(x)) + PAGE_MASK) & ~(PAGE_MASK))
|
||||
#endif
|
||||
|
||||
// This is purposely not a full wrapper for virtualalloc/mmap, but it
|
||||
// provides exactly the primitive operations that Dolphin needs.
|
||||
|
||||
void* AllocateExecutableMemory(std::size_t size, bool low) {
|
||||
#if defined(_WIN32)
|
||||
void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||
#else
|
||||
static char* map_hint = nullptr;
|
||||
#if defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
|
||||
// This OS has no flag to enforce allocation below the 4 GB boundary,
|
||||
// but if we hint that we want a low address it is very likely we will
|
||||
// get one.
|
||||
// An older version of this code used MAP_FIXED, but that has the side
|
||||
// effect of discarding already mapped pages that happen to be in the
|
||||
// requested virtual memory range (such as the emulated RAM, sometimes).
|
||||
if (low && (!map_hint))
|
||||
map_hint = (char*)round_page(512 * 1024 * 1024); /* 0.5 GB rounded up to the next page */
|
||||
#endif
|
||||
void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_ANON | MAP_PRIVATE
|
||||
#if defined(ARCHITECTURE_x86_64) && defined(MAP_32BIT)
|
||||
| (low ? MAP_32BIT : 0)
|
||||
#endif
|
||||
,
|
||||
-1, 0);
|
||||
#endif /* defined(_WIN32) */
|
||||
|
||||
#ifdef _WIN32
|
||||
if (ptr == nullptr) {
|
||||
#else
|
||||
if (ptr == MAP_FAILED) {
|
||||
ptr = nullptr;
|
||||
#endif
|
||||
LOG_ERROR(Common_Memory, "Failed to allocate executable memory");
|
||||
}
|
||||
#if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
|
||||
else {
|
||||
if (low) {
|
||||
map_hint += size;
|
||||
map_hint = (char*)round_page(map_hint); /* round up to the next page */
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if EMU_ARCH_BITS == 64
|
||||
if ((u64)ptr >= 0x80000000 && low == true)
|
||||
LOG_ERROR(Common_Memory, "Executable memory ended up above 2GB!");
|
||||
#endif
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* AllocateMemoryPages(std::size_t size) {
|
||||
#ifdef _WIN32
|
||||
void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
#else
|
||||
void* ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
|
||||
if (ptr == MAP_FAILED)
|
||||
ptr = nullptr;
|
||||
#endif
|
||||
|
||||
if (ptr == nullptr)
|
||||
LOG_ERROR(Common_Memory, "Failed to allocate raw memory");
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* AllocateAlignedMemory(std::size_t size, std::size_t alignment) {
|
||||
#ifdef _WIN32
|
||||
void* ptr = _aligned_malloc(size, alignment);
|
||||
#else
|
||||
void* ptr = nullptr;
|
||||
#ifdef ANDROID
|
||||
ptr = memalign(alignment, size);
|
||||
#else
|
||||
if (posix_memalign(&ptr, alignment, size) != 0)
|
||||
LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (ptr == nullptr)
|
||||
LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void FreeMemoryPages(void* ptr, std::size_t size) {
|
||||
if (ptr) {
|
||||
#ifdef _WIN32
|
||||
if (!VirtualFree(ptr, 0, MEM_RELEASE))
|
||||
LOG_ERROR(Common_Memory, "FreeMemoryPages failed!\n{}", GetLastErrorMsg());
|
||||
#else
|
||||
munmap(ptr, size);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void FreeAlignedMemory(void* ptr) {
|
||||
if (ptr) {
|
||||
#ifdef _WIN32
|
||||
_aligned_free(ptr);
|
||||
#else
|
||||
free(ptr);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void WriteProtectMemory(void* ptr, std::size_t size, bool allowExecute) {
|
||||
#ifdef _WIN32
|
||||
DWORD oldValue;
|
||||
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue))
|
||||
LOG_ERROR(Common_Memory, "WriteProtectMemory failed!\n{}", GetLastErrorMsg());
|
||||
#else
|
||||
mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ);
|
||||
#endif
|
||||
}
|
||||
|
||||
void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute) {
|
||||
#ifdef _WIN32
|
||||
DWORD oldValue;
|
||||
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE,
|
||||
&oldValue))
|
||||
LOG_ERROR(Common_Memory, "UnWriteProtectMemory failed!\n{}", GetLastErrorMsg());
|
||||
#else
|
||||
mprotect(ptr, size,
|
||||
allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string MemUsage() {
|
||||
#ifdef _WIN32
|
||||
#pragma comment(lib, "psapi")
|
||||
DWORD processID = GetCurrentProcessId();
|
||||
HANDLE hProcess;
|
||||
PROCESS_MEMORY_COUNTERS pmc;
|
||||
std::string Ret;
|
||||
|
||||
// Print information about the memory usage of the process.
|
||||
|
||||
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID);
|
||||
if (nullptr == hProcess)
|
||||
return "MemUsage Error";
|
||||
|
||||
if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc)))
|
||||
Ret = fmt::format("{} K", Common::ThousandSeparate(pmc.WorkingSetSize / 1024, 7));
|
||||
|
||||
CloseHandle(hProcess);
|
||||
return Ret;
|
||||
#else
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
21
src/common/memory_util.h
Normal file
21
src/common/memory_util.h
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
void* AllocateExecutableMemory(std::size_t size, bool low = true);
|
||||
void* AllocateMemoryPages(std::size_t size);
|
||||
void FreeMemoryPages(void* ptr, std::size_t size);
|
||||
void* AllocateAlignedMemory(std::size_t size, std::size_t alignment);
|
||||
void FreeAlignedMemory(void* ptr);
|
||||
void WriteProtectMemory(void* ptr, std::size_t size, bool executable = false);
|
||||
void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute = false);
|
||||
std::string MemUsage();
|
||||
|
||||
inline int GetPageSize() {
|
||||
return 4096;
|
||||
}
|
||||
@@ -4,10 +4,11 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cerrno>
|
||||
#include <codecvt>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <locale>
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
#include "common/common_paths.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
@@ -32,6 +33,24 @@ std::string ToUpper(std::string str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
// For Debugging. Read out an u8 array.
|
||||
std::string ArrayToString(const u8* data, std::size_t size, int line_len, bool spaces) {
|
||||
std::ostringstream oss;
|
||||
oss << std::setfill('0') << std::hex;
|
||||
|
||||
for (int line = 0; size; ++data, --size) {
|
||||
oss << std::setw(2) << (int)*data;
|
||||
|
||||
if (line_len == ++line) {
|
||||
oss << '\n';
|
||||
line = 0;
|
||||
} else if (spaces)
|
||||
oss << ' ';
|
||||
}
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string StringFromBuffer(const std::vector<u8>& data) {
|
||||
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
|
||||
}
|
||||
@@ -56,6 +75,40 @@ std::string StripQuotes(const std::string& s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
bool TryParse(const std::string& str, u32* const output) {
|
||||
char* endptr = nullptr;
|
||||
|
||||
// Reset errno to a value other than ERANGE
|
||||
errno = 0;
|
||||
|
||||
unsigned long value = strtoul(str.c_str(), &endptr, 0);
|
||||
|
||||
if (!endptr || *endptr)
|
||||
return false;
|
||||
|
||||
if (errno == ERANGE)
|
||||
return false;
|
||||
|
||||
#if ULONG_MAX > UINT_MAX
|
||||
if (value >= 0x100000000ull && value <= 0xFFFFFFFF00000000ull)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
*output = static_cast<u32>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryParse(const std::string& str, bool* const output) {
|
||||
if ("1" == str || "true" == ToLower(str))
|
||||
*output = true;
|
||||
else if ("0" == str || "false" == ToLower(str))
|
||||
*output = false;
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string StringFromBool(bool value) {
|
||||
return value ? "True" : "False";
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
@@ -17,13 +19,44 @@ std::string ToLower(std::string str);
|
||||
/// Make a string uppercase
|
||||
std::string ToUpper(std::string str);
|
||||
|
||||
std::string ArrayToString(const u8* data, std::size_t size, int line_len = 20, bool spaces = true);
|
||||
|
||||
std::string StringFromBuffer(const std::vector<u8>& data);
|
||||
|
||||
std::string StripSpaces(const std::string& s);
|
||||
std::string StripQuotes(const std::string& s);
|
||||
|
||||
// Thousand separator. Turns 12345678 into 12,345,678
|
||||
template <typename I>
|
||||
std::string ThousandSeparate(I value, int spaces = 0) {
|
||||
std::ostringstream oss;
|
||||
|
||||
// std::locale("") seems to be broken on many platforms
|
||||
#if defined _WIN32 || (defined __linux__ && !defined __clang__)
|
||||
oss.imbue(std::locale(""));
|
||||
#endif
|
||||
oss << std::setw(spaces) << value;
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string StringFromBool(bool value);
|
||||
|
||||
bool TryParse(const std::string& str, bool* output);
|
||||
bool TryParse(const std::string& str, u32* output);
|
||||
|
||||
template <typename N>
|
||||
static bool TryParse(const std::string& str, N* const output) {
|
||||
std::istringstream iss(str);
|
||||
|
||||
N tmp = 0;
|
||||
if (iss >> tmp) {
|
||||
*output = tmp;
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string TabsToSpaces(int tab_size, std::string in);
|
||||
|
||||
void SplitString(const std::string& str, char delim, std::vector<std::string>& output);
|
||||
|
||||
@@ -153,7 +153,6 @@ struct VisitorInterface : NonCopyable {
|
||||
|
||||
/// Completion method, called once all fields have been visited
|
||||
virtual void Complete() = 0;
|
||||
virtual bool SubmitTestcase() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -179,9 +178,6 @@ struct NullVisitor : public VisitorInterface {
|
||||
void Visit(const Field<std::chrono::microseconds>& /*field*/) override {}
|
||||
|
||||
void Complete() override {}
|
||||
bool SubmitTestcase() override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/// Appends build-specific information to the given FieldCollection,
|
||||
|
||||
@@ -49,22 +49,6 @@ struct ThreadQueueList {
|
||||
return T();
|
||||
}
|
||||
|
||||
template <typename UnaryPredicate>
|
||||
T get_first_filter(UnaryPredicate filter) const {
|
||||
const Queue* cur = first;
|
||||
while (cur != nullptr) {
|
||||
if (!cur->data.empty()) {
|
||||
for (const auto& item : cur->data) {
|
||||
if (filter(item))
|
||||
return item;
|
||||
}
|
||||
}
|
||||
cur = cur->next_nonempty;
|
||||
}
|
||||
|
||||
return T();
|
||||
}
|
||||
|
||||
T pop_first() {
|
||||
Queue* cur = first;
|
||||
while (cur != nullptr) {
|
||||
|
||||
@@ -156,8 +156,6 @@ add_library(core STATIC
|
||||
hle/service/am/omm.h
|
||||
hle/service/am/spsm.cpp
|
||||
hle/service/am/spsm.h
|
||||
hle/service/am/tcap.cpp
|
||||
hle/service/am/tcap.h
|
||||
hle/service/aoc/aoc_u.cpp
|
||||
hle/service/aoc/aoc_u.h
|
||||
hle/service/apm/apm.cpp
|
||||
@@ -282,8 +280,6 @@ add_library(core STATIC
|
||||
hle/service/nifm/nifm.h
|
||||
hle/service/nim/nim.cpp
|
||||
hle/service/nim/nim.h
|
||||
hle/service/npns/npns.cpp
|
||||
hle/service/npns/npns.h
|
||||
hle/service/ns/ns.cpp
|
||||
hle/service/ns/ns.h
|
||||
hle/service/ns/pl_u.cpp
|
||||
@@ -331,8 +327,6 @@ add_library(core STATIC
|
||||
hle/service/prepo/prepo.h
|
||||
hle/service/psc/psc.cpp
|
||||
hle/service/psc/psc.h
|
||||
hle/service/ptm/psm.cpp
|
||||
hle/service/ptm/psm.h
|
||||
hle/service/service.cpp
|
||||
hle/service/service.h
|
||||
hle/service/set/set.cpp
|
||||
|
||||
@@ -185,7 +185,7 @@ struct System::Impl {
|
||||
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
|
||||
return ResultStatus::ErrorGetLoader;
|
||||
}
|
||||
std::pair<std::optional<u32>, Loader::ResultStatus> system_mode =
|
||||
std::pair<boost::optional<u32>, Loader::ResultStatus> system_mode =
|
||||
app_loader->LoadKernelSystemMode();
|
||||
|
||||
if (system_mode.second != Loader::ResultStatus::Success) {
|
||||
@@ -312,10 +312,6 @@ Cpu& System::CurrentCpuCore() {
|
||||
return impl->CurrentCpuCore();
|
||||
}
|
||||
|
||||
const Cpu& System::CurrentCpuCore() const {
|
||||
return impl->CurrentCpuCore();
|
||||
}
|
||||
|
||||
System::ResultStatus System::RunLoop(bool tight_loop) {
|
||||
return impl->RunLoop(tight_loop);
|
||||
}
|
||||
@@ -346,11 +342,7 @@ PerfStatsResults System::GetAndResetPerfStats() {
|
||||
return impl->GetAndResetPerfStats();
|
||||
}
|
||||
|
||||
TelemetrySession& System::TelemetrySession() {
|
||||
return *impl->telemetry_session;
|
||||
}
|
||||
|
||||
const TelemetrySession& System::TelemetrySession() const {
|
||||
Core::TelemetrySession& System::TelemetrySession() const {
|
||||
return *impl->telemetry_session;
|
||||
}
|
||||
|
||||
@@ -358,11 +350,7 @@ ARM_Interface& System::CurrentArmInterface() {
|
||||
return CurrentCpuCore().ArmInterface();
|
||||
}
|
||||
|
||||
const ARM_Interface& System::CurrentArmInterface() const {
|
||||
return CurrentCpuCore().ArmInterface();
|
||||
}
|
||||
|
||||
std::size_t System::CurrentCoreIndex() const {
|
||||
std::size_t System::CurrentCoreIndex() {
|
||||
return CurrentCpuCore().CoreIndex();
|
||||
}
|
||||
|
||||
@@ -370,10 +358,6 @@ Kernel::Scheduler& System::CurrentScheduler() {
|
||||
return CurrentCpuCore().Scheduler();
|
||||
}
|
||||
|
||||
const Kernel::Scheduler& System::CurrentScheduler() const {
|
||||
return CurrentCpuCore().Scheduler();
|
||||
}
|
||||
|
||||
Kernel::Scheduler& System::Scheduler(std::size_t core_index) {
|
||||
return CpuCore(core_index).Scheduler();
|
||||
}
|
||||
@@ -394,10 +378,6 @@ ARM_Interface& System::ArmInterface(std::size_t core_index) {
|
||||
return CpuCore(core_index).ArmInterface();
|
||||
}
|
||||
|
||||
const ARM_Interface& System::ArmInterface(std::size_t core_index) const {
|
||||
return CpuCore(core_index).ArmInterface();
|
||||
}
|
||||
|
||||
Cpu& System::CpuCore(std::size_t core_index) {
|
||||
ASSERT(core_index < NUM_CPU_CORES);
|
||||
return *impl->cpu_cores[core_index];
|
||||
@@ -412,10 +392,6 @@ ExclusiveMonitor& System::Monitor() {
|
||||
return *impl->cpu_exclusive_monitor;
|
||||
}
|
||||
|
||||
const ExclusiveMonitor& System::Monitor() const {
|
||||
return *impl->cpu_exclusive_monitor;
|
||||
}
|
||||
|
||||
Tegra::GPU& System::GPU() {
|
||||
return *impl->gpu_core;
|
||||
}
|
||||
|
||||
@@ -129,11 +129,11 @@ public:
|
||||
*/
|
||||
bool IsPoweredOn() const;
|
||||
|
||||
/// Gets a reference to the telemetry session for this emulation session.
|
||||
Core::TelemetrySession& TelemetrySession();
|
||||
|
||||
/// Gets a reference to the telemetry session for this emulation session.
|
||||
const Core::TelemetrySession& TelemetrySession() const;
|
||||
/**
|
||||
* Returns a reference to the telemetry session for this emulation session.
|
||||
* @returns Reference to the telemetry session.
|
||||
*/
|
||||
Core::TelemetrySession& TelemetrySession() const;
|
||||
|
||||
/// Prepare the core emulation for a reschedule
|
||||
void PrepareReschedule();
|
||||
@@ -144,36 +144,24 @@ public:
|
||||
/// Gets an ARM interface to the CPU core that is currently running
|
||||
ARM_Interface& CurrentArmInterface();
|
||||
|
||||
/// Gets an ARM interface to the CPU core that is currently running
|
||||
const ARM_Interface& CurrentArmInterface() const;
|
||||
|
||||
/// Gets the index of the currently running CPU core
|
||||
std::size_t CurrentCoreIndex() const;
|
||||
std::size_t CurrentCoreIndex();
|
||||
|
||||
/// Gets the scheduler for the CPU core that is currently running
|
||||
Kernel::Scheduler& CurrentScheduler();
|
||||
|
||||
/// Gets the scheduler for the CPU core that is currently running
|
||||
const Kernel::Scheduler& CurrentScheduler() const;
|
||||
|
||||
/// Gets a reference to an ARM interface for the CPU core with the specified index
|
||||
/// Gets an ARM interface to the CPU core with the specified index
|
||||
ARM_Interface& ArmInterface(std::size_t core_index);
|
||||
|
||||
/// Gets a const reference to an ARM interface from the CPU core with the specified index
|
||||
const ARM_Interface& ArmInterface(std::size_t core_index) const;
|
||||
|
||||
/// Gets a CPU interface to the CPU core with the specified index
|
||||
Cpu& CpuCore(std::size_t core_index);
|
||||
|
||||
/// Gets a CPU interface to the CPU core with the specified index
|
||||
const Cpu& CpuCore(std::size_t core_index) const;
|
||||
|
||||
/// Gets a reference to the exclusive monitor
|
||||
/// Gets the exclusive monitor
|
||||
ExclusiveMonitor& Monitor();
|
||||
|
||||
/// Gets a constant reference to the exclusive monitor
|
||||
const ExclusiveMonitor& Monitor() const;
|
||||
|
||||
/// Gets a mutable reference to the GPU interface
|
||||
Tegra::GPU& GPU();
|
||||
|
||||
@@ -242,9 +230,6 @@ private:
|
||||
/// Returns the currently running CPU core
|
||||
Cpu& CurrentCpuCore();
|
||||
|
||||
/// Returns the currently running CPU core
|
||||
const Cpu& CurrentCpuCore() const;
|
||||
|
||||
/**
|
||||
* Initialize the emulated system.
|
||||
* @param emu_window Reference to the host-system window used for video output and keyboard
|
||||
|
||||
@@ -141,28 +141,28 @@ Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source)
|
||||
return mac_key;
|
||||
}
|
||||
|
||||
std::optional<Key128> DeriveSDSeed() {
|
||||
boost::optional<Key128> DeriveSDSeed() {
|
||||
const FileUtil::IOFile save_43(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
|
||||
"/system/save/8000000000000043",
|
||||
"rb+");
|
||||
if (!save_43.IsOpen())
|
||||
return {};
|
||||
return boost::none;
|
||||
|
||||
const FileUtil::IOFile sd_private(
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+");
|
||||
if (!sd_private.IsOpen())
|
||||
return {};
|
||||
return boost::none;
|
||||
|
||||
std::array<u8, 0x10> private_seed{};
|
||||
if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != private_seed.size()) {
|
||||
return {};
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
std::array<u8, 0x10> buffer{};
|
||||
std::size_t offset = 0;
|
||||
for (; offset + 0x10 < save_43.GetSize(); ++offset) {
|
||||
if (!save_43.Seek(offset, SEEK_SET)) {
|
||||
return {};
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
save_43.ReadBytes(buffer.data(), buffer.size());
|
||||
@@ -172,12 +172,12 @@ std::optional<Key128> DeriveSDSeed() {
|
||||
}
|
||||
|
||||
if (!save_43.Seek(offset + 0x10, SEEK_SET)) {
|
||||
return {};
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
Key128 seed{};
|
||||
if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) {
|
||||
return {};
|
||||
return boost::none;
|
||||
}
|
||||
return seed;
|
||||
}
|
||||
@@ -291,26 +291,26 @@ static std::array<u8, target_size> MGF1(const std::array<u8, in_size>& seed) {
|
||||
}
|
||||
|
||||
template <size_t size>
|
||||
static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
|
||||
static boost::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
|
||||
u64 offset = 0;
|
||||
for (size_t i = 0x20; i < data.size() - 0x10; ++i) {
|
||||
if (data[i] == 0x1) {
|
||||
offset = i + 1;
|
||||
break;
|
||||
} else if (data[i] != 0x0) {
|
||||
return {};
|
||||
return boost::none;
|
||||
}
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
|
||||
const RSAKeyPair<2048>& key) {
|
||||
boost::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
|
||||
const RSAKeyPair<2048>& key) {
|
||||
u32 cert_authority;
|
||||
std::memcpy(&cert_authority, ticket.data() + 0x140, sizeof(cert_authority));
|
||||
if (cert_authority == 0)
|
||||
return {};
|
||||
return boost::none;
|
||||
if (cert_authority != Common::MakeMagic('R', 'o', 'o', 't')) {
|
||||
LOG_INFO(Crypto,
|
||||
"Attempting to parse ticket with non-standard certificate authority {:08X}.",
|
||||
@@ -321,7 +321,7 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
|
||||
std::memcpy(rights_id.data(), ticket.data() + 0x2A0, sizeof(Key128));
|
||||
|
||||
if (rights_id == Key128{})
|
||||
return {};
|
||||
return boost::none;
|
||||
|
||||
Key128 key_temp{};
|
||||
|
||||
@@ -356,17 +356,17 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
|
||||
std::memcpy(m_2.data(), rsa_step.data() + 0x21, m_2.size());
|
||||
|
||||
if (m_0 != 0)
|
||||
return {};
|
||||
return boost::none;
|
||||
|
||||
m_1 = m_1 ^ MGF1<0x20>(m_2);
|
||||
m_2 = m_2 ^ MGF1<0xDF>(m_1);
|
||||
|
||||
const auto offset = FindTicketOffset(m_2);
|
||||
if (!offset)
|
||||
return {};
|
||||
ASSERT(*offset > 0);
|
||||
if (offset == boost::none)
|
||||
return boost::none;
|
||||
ASSERT(offset.get() > 0);
|
||||
|
||||
std::memcpy(key_temp.data(), m_2.data() + *offset, key_temp.size());
|
||||
std::memcpy(key_temp.data(), m_2.data() + offset.get(), key_temp.size());
|
||||
|
||||
return std::make_pair(rights_id, key_temp);
|
||||
}
|
||||
@@ -395,7 +395,7 @@ static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_
|
||||
if (base.size() < begin + length)
|
||||
return false;
|
||||
return std::all_of(base.begin() + begin, base.begin() + begin + length,
|
||||
[](u8 c) { return std::isxdigit(c); });
|
||||
[](u8 c) { return std::isdigit(c); });
|
||||
}
|
||||
|
||||
void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
|
||||
@@ -661,8 +661,8 @@ void KeyManager::DeriveSDSeedLazy() {
|
||||
return;
|
||||
|
||||
const auto res = DeriveSDSeed();
|
||||
if (res)
|
||||
SetKey(S128KeyType::SDSeed, *res);
|
||||
if (res != boost::none)
|
||||
SetKey(S128KeyType::SDSeed, res.get());
|
||||
}
|
||||
|
||||
static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) {
|
||||
@@ -713,6 +713,7 @@ void KeyManager::DeriveBase() {
|
||||
|
||||
const auto sbk = GetKey(S128KeyType::SecureBoot);
|
||||
const auto tsec = GetKey(S128KeyType::TSEC);
|
||||
const auto master_source = GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Master));
|
||||
|
||||
for (size_t i = 0; i < revisions.size(); ++i) {
|
||||
if (!revisions[i])
|
||||
@@ -889,9 +890,9 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
|
||||
|
||||
for (const auto& raw : res) {
|
||||
const auto pair = ParseTicket(raw, rsa_key);
|
||||
if (!pair)
|
||||
if (pair == boost::none)
|
||||
continue;
|
||||
const auto& [rid, key] = *pair;
|
||||
const auto& [rid, key] = pair.value();
|
||||
u128 rights_id;
|
||||
std::memcpy(rights_id.data(), rid.data(), rid.size());
|
||||
SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
|
||||
|
||||
@@ -6,10 +6,9 @@
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include <boost/container/flat_map.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <fmt/format.h>
|
||||
#include "common/common_types.h"
|
||||
#include "core/crypto/partition_data_manager.h"
|
||||
@@ -192,14 +191,14 @@ Key128 DeriveMasterKey(const std::array<u8, 0x90>& keyblob, const Key128& master
|
||||
std::array<u8, 0x90> DecryptKeyblob(const std::array<u8, 0xB0>& encrypted_keyblob,
|
||||
const Key128& key);
|
||||
|
||||
std::optional<Key128> DeriveSDSeed();
|
||||
boost::optional<Key128> DeriveSDSeed();
|
||||
Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys);
|
||||
|
||||
std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save);
|
||||
|
||||
// Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority (offset
|
||||
// 0x140-0x144 is zero)
|
||||
std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
|
||||
const RSAKeyPair<2048>& eticket_extended_key);
|
||||
boost::optional<std::pair<Key128, Key128>> ParseTicket(
|
||||
const TicketRaw& ticket, const RSAKeyPair<2048>& eticket_extended_key);
|
||||
|
||||
} // namespace Core::Crypto
|
||||
|
||||
@@ -516,8 +516,7 @@ void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& packa
|
||||
out.insert(out.end(), rodata.begin(), rodata.end());
|
||||
out.insert(out.end(), data.begin(), data.end());
|
||||
|
||||
offset += sizeof(KIPHeader) + kip.sections[0].size_compressed +
|
||||
kip.sections[1].size_compressed + kip.sections[2].size_compressed;
|
||||
offset += sizeof(KIPHeader) + out.size();
|
||||
|
||||
if (name == "FS")
|
||||
package2_fs[static_cast<size_t>(type)] = std::move(out);
|
||||
|
||||
@@ -8,9 +8,8 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir dump_root_)
|
||||
BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_)
|
||||
: nand_root(std::move(nand_root_)), load_root(std::move(load_root_)),
|
||||
dump_root(std::move(dump_root_)),
|
||||
sysnand_cache(std::make_unique<RegisteredCache>(
|
||||
GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))),
|
||||
usrnand_cache(std::make_unique<RegisteredCache>(
|
||||
@@ -33,10 +32,4 @@ VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const {
|
||||
return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id));
|
||||
}
|
||||
|
||||
VirtualDir BISFactory::GetModificationDumpRoot(u64 title_id) const {
|
||||
if (title_id == 0)
|
||||
return nullptr;
|
||||
return GetOrCreateDirectoryRelative(dump_root, fmt::format("/{:016X}", title_id));
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -17,19 +17,17 @@ class RegisteredCache;
|
||||
/// registered caches.
|
||||
class BISFactory {
|
||||
public:
|
||||
explicit BISFactory(VirtualDir nand_root, VirtualDir load_root, VirtualDir dump_root);
|
||||
explicit BISFactory(VirtualDir nand_root, VirtualDir load_root);
|
||||
~BISFactory();
|
||||
|
||||
RegisteredCache* GetSystemNANDContents() const;
|
||||
RegisteredCache* GetUserNANDContents() const;
|
||||
|
||||
VirtualDir GetModificationLoadRoot(u64 title_id) const;
|
||||
VirtualDir GetModificationDumpRoot(u64 title_id) const;
|
||||
|
||||
private:
|
||||
VirtualDir nand_root;
|
||||
VirtualDir load_root;
|
||||
VirtualDir dump_root;
|
||||
|
||||
std::unique_ptr<RegisteredCache> sysnand_cache;
|
||||
std::unique_ptr<RegisteredCache> usrnand_cache;
|
||||
|
||||
@@ -168,6 +168,10 @@ VirtualDir XCI::GetParentDirectory() const {
|
||||
return file->GetContainingDirectory();
|
||||
}
|
||||
|
||||
bool XCI::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
|
||||
if (partitions[static_cast<std::size_t>(part)] == nullptr) {
|
||||
return Loader::ResultStatus::ErrorXCIMissingPartition;
|
||||
@@ -176,7 +180,7 @@ Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
|
||||
for (const VirtualFile& file : partitions[static_cast<std::size_t>(part)]->GetFiles()) {
|
||||
if (file->GetExtension() != "nca")
|
||||
continue;
|
||||
auto nca = std::make_shared<NCA>(file, nullptr, 0, keys);
|
||||
auto nca = std::make_shared<NCA>(file);
|
||||
// TODO(DarkLordZach): Add proper Rev1+ Support
|
||||
if (nca->IsUpdate())
|
||||
continue;
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/crypto/key_manager.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace Loader {
|
||||
@@ -95,6 +94,9 @@ public:
|
||||
|
||||
VirtualDir GetParentDirectory() const override;
|
||||
|
||||
protected:
|
||||
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
|
||||
|
||||
private:
|
||||
Loader::ResultStatus AddNCAFromPartition(XCIPartition part);
|
||||
|
||||
@@ -108,7 +110,5 @@ private:
|
||||
std::shared_ptr<NSP> secure_partition;
|
||||
std::shared_ptr<NCA> program;
|
||||
std::vector<std::shared_ptr<NCA>> ncas;
|
||||
|
||||
Core::Crypto::KeyManager keys;
|
||||
};
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/crypto/aes_util.h"
|
||||
#include "core/crypto/ctr_encryption_layer.h"
|
||||
@@ -101,9 +102,8 @@ static bool IsValidNCA(const NCAHeader& header) {
|
||||
return header.magic == Common::MakeMagic('N', 'C', 'A', '3');
|
||||
}
|
||||
|
||||
NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset,
|
||||
Core::Crypto::KeyManager keys_)
|
||||
: file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)), keys(std::move(keys_)) {
|
||||
NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
|
||||
: file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)) {
|
||||
if (file == nullptr) {
|
||||
status = Loader::ResultStatus::ErrorNullFile;
|
||||
return;
|
||||
@@ -306,18 +306,18 @@ bool NCA::ReadRomFSSection(const NCASectionHeader& section, const NCASectionTabl
|
||||
subsection_buckets.back().entries.push_back({section.bktr.relocation.offset, {0}, ctr_low});
|
||||
subsection_buckets.back().entries.push_back({size, {0}, 0});
|
||||
|
||||
std::optional<Core::Crypto::Key128> key = {};
|
||||
boost::optional<Core::Crypto::Key128> key = boost::none;
|
||||
if (encrypted) {
|
||||
if (has_rights_id) {
|
||||
status = Loader::ResultStatus::Success;
|
||||
key = GetTitlekey();
|
||||
if (!key) {
|
||||
if (key == boost::none) {
|
||||
status = Loader::ResultStatus::ErrorMissingTitlekey;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
key = GetKeyAreaKey(NCASectionCryptoType::BKTR);
|
||||
if (!key) {
|
||||
if (key == boost::none) {
|
||||
status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
|
||||
return false;
|
||||
}
|
||||
@@ -332,7 +332,7 @@ bool NCA::ReadRomFSSection(const NCASectionHeader& section, const NCASectionTabl
|
||||
auto bktr = std::make_shared<BKTR>(
|
||||
bktr_base_romfs, std::make_shared<OffsetVfsFile>(file, romfs_size, base_offset),
|
||||
relocation_block, relocation_buckets, subsection_block, subsection_buckets, encrypted,
|
||||
encrypted ? *key : Core::Crypto::Key128{}, base_offset, bktr_base_ivfc_offset,
|
||||
encrypted ? key.get() : Core::Crypto::Key128{}, base_offset, bktr_base_ivfc_offset,
|
||||
section.raw.section_ctr);
|
||||
|
||||
// BKTR applies to entire IVFC, so make an offset version to level 6
|
||||
@@ -388,11 +388,11 @@ u8 NCA::GetCryptoRevision() const {
|
||||
return master_key_id;
|
||||
}
|
||||
|
||||
std::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type) const {
|
||||
boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type) const {
|
||||
const auto master_key_id = GetCryptoRevision();
|
||||
|
||||
if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index))
|
||||
return {};
|
||||
return boost::none;
|
||||
|
||||
std::vector<u8> key_area(header.key_area.begin(), header.key_area.end());
|
||||
Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
|
||||
@@ -416,25 +416,25 @@ std::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type
|
||||
return out;
|
||||
}
|
||||
|
||||
std::optional<Core::Crypto::Key128> NCA::GetTitlekey() {
|
||||
boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() {
|
||||
const auto master_key_id = GetCryptoRevision();
|
||||
|
||||
u128 rights_id{};
|
||||
memcpy(rights_id.data(), header.rights_id.data(), 16);
|
||||
if (rights_id == u128{}) {
|
||||
status = Loader::ResultStatus::ErrorInvalidRightsID;
|
||||
return {};
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]);
|
||||
if (titlekey == Core::Crypto::Key128{}) {
|
||||
status = Loader::ResultStatus::ErrorMissingTitlekey;
|
||||
return {};
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) {
|
||||
status = Loader::ResultStatus::ErrorMissingTitlekek;
|
||||
return {};
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
|
||||
@@ -458,25 +458,25 @@ VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 s
|
||||
case NCASectionCryptoType::BKTR:
|
||||
LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
|
||||
{
|
||||
std::optional<Core::Crypto::Key128> key = {};
|
||||
boost::optional<Core::Crypto::Key128> key = boost::none;
|
||||
if (has_rights_id) {
|
||||
status = Loader::ResultStatus::Success;
|
||||
key = GetTitlekey();
|
||||
if (!key) {
|
||||
if (key == boost::none) {
|
||||
if (status == Loader::ResultStatus::Success)
|
||||
status = Loader::ResultStatus::ErrorMissingTitlekey;
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
key = GetKeyAreaKey(NCASectionCryptoType::CTR);
|
||||
if (!key) {
|
||||
if (key == boost::none) {
|
||||
status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>(std::move(in), *key,
|
||||
starting_offset);
|
||||
auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>(
|
||||
std::move(in), key.value(), starting_offset);
|
||||
std::vector<u8> iv(16);
|
||||
for (u8 i = 0; i < 8; ++i)
|
||||
iv[i] = s_header.raw.section_ctr[0x8 - i - 1];
|
||||
@@ -546,4 +546,7 @@ u64 NCA::GetBaseIVFCOffset() const {
|
||||
return ivfc_offset;
|
||||
}
|
||||
|
||||
bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
|
||||
return false;
|
||||
}
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -6,10 +6,9 @@
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
@@ -79,8 +78,7 @@ inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) {
|
||||
class NCA : public ReadOnlyVfsDirectory {
|
||||
public:
|
||||
explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr,
|
||||
u64 bktr_base_ivfc_offset = 0,
|
||||
Core::Crypto::KeyManager keys = Core::Crypto::KeyManager());
|
||||
u64 bktr_base_ivfc_offset = 0);
|
||||
~NCA() override;
|
||||
|
||||
Loader::ResultStatus GetStatus() const;
|
||||
@@ -102,6 +100,9 @@ public:
|
||||
// Returns the base ivfc offset used in BKTR patching.
|
||||
u64 GetBaseIVFCOffset() const;
|
||||
|
||||
protected:
|
||||
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
|
||||
|
||||
private:
|
||||
bool CheckSupportedNCA(const NCAHeader& header);
|
||||
bool HandlePotentialHeaderDecryption();
|
||||
@@ -113,8 +114,8 @@ private:
|
||||
bool ReadPFS0Section(const NCASectionHeader& section, const NCASectionTableEntry& entry);
|
||||
|
||||
u8 GetCryptoRevision() const;
|
||||
std::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const;
|
||||
std::optional<Core::Crypto::Key128> GetTitlekey();
|
||||
boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const;
|
||||
boost::optional<Core::Crypto::Key128> GetTitlekey();
|
||||
VirtualFile Decrypt(const NCASectionHeader& header, VirtualFile in, u64 starting_offset);
|
||||
|
||||
std::vector<VirtualDir> dirs;
|
||||
|
||||
@@ -66,10 +66,4 @@ std::string NACP::GetVersionString() const {
|
||||
return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(),
|
||||
raw->version_string.size());
|
||||
}
|
||||
|
||||
std::vector<u8> NACP::GetRawBytes() const {
|
||||
std::vector<u8> out(sizeof(RawNACP));
|
||||
std::memcpy(out.data(), raw.get(), sizeof(RawNACP));
|
||||
return out;
|
||||
}
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -81,7 +81,6 @@ public:
|
||||
u64 GetTitleId() const;
|
||||
u64 GetDLCBaseTitleId() const;
|
||||
std::string GetVersionString() const;
|
||||
std::vector<u8> GetRawBytes() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<RawNACP> raw;
|
||||
|
||||
@@ -8,10 +8,25 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
constexpr ResultCode ERROR_PATH_NOT_FOUND{ErrorModule::FS, 1};
|
||||
constexpr ResultCode ERROR_ENTITY_NOT_FOUND{ErrorModule::FS, 1002};
|
||||
constexpr ResultCode ERROR_SD_CARD_NOT_FOUND{ErrorModule::FS, 2001};
|
||||
constexpr ResultCode ERROR_INVALID_OFFSET{ErrorModule::FS, 6061};
|
||||
constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::FS, 6062};
|
||||
namespace ErrCodes {
|
||||
enum {
|
||||
NotFound = 1,
|
||||
TitleNotFound = 1002,
|
||||
SdCardNotFound = 2001,
|
||||
RomFSNotFound = 2520,
|
||||
};
|
||||
}
|
||||
|
||||
constexpr ResultCode ERROR_PATH_NOT_FOUND(ErrorModule::FS, ErrCodes::NotFound);
|
||||
|
||||
// TODO(bunnei): Replace these with correct errors for Switch OS
|
||||
constexpr ResultCode ERROR_INVALID_PATH(-1);
|
||||
constexpr ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(-1);
|
||||
constexpr ResultCode ERROR_INVALID_OPEN_FLAGS(-1);
|
||||
constexpr ResultCode ERROR_FILE_NOT_FOUND(-1);
|
||||
constexpr ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(-1);
|
||||
constexpr ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(-1);
|
||||
constexpr ResultCode ERROR_FILE_ALREADY_EXISTS(-1);
|
||||
constexpr ResultCode ERROR_DIRECTORY_NOT_EMPTY(-1);
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <boost/detail/container_fwd.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
|
||||
@@ -99,16 +99,16 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
|
||||
u16 rle_size{};
|
||||
if (ips->ReadObject(&rle_size, offset) != sizeof(u16))
|
||||
return nullptr;
|
||||
rle_size = Common::swap16(rle_size);
|
||||
rle_size = Common::swap16(data_size);
|
||||
offset += sizeof(u16);
|
||||
|
||||
const auto data = ips->ReadByte(offset++);
|
||||
if (!data)
|
||||
if (data == boost::none)
|
||||
return nullptr;
|
||||
|
||||
if (real_offset + rle_size > in_data.size())
|
||||
rle_size = static_cast<u16>(in_data.size() - real_offset);
|
||||
std::memset(in_data.data() + real_offset, *data, rle_size);
|
||||
std::memset(in_data.data() + real_offset, data.get(), rle_size);
|
||||
} else { // Standard Patch
|
||||
auto read = data_size;
|
||||
if (real_offset + read > in_data.size())
|
||||
|
||||
@@ -83,7 +83,7 @@ std::vector<std::shared_ptr<VfsFile>> PartitionFilesystem::GetFiles() const {
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<VfsDirectory>> PartitionFilesystem::GetSubdirectories() const {
|
||||
return {};
|
||||
return pfs_dirs;
|
||||
}
|
||||
|
||||
std::string PartitionFilesystem::GetName() const {
|
||||
@@ -103,4 +103,18 @@ void PartitionFilesystem::PrintDebugInfo() const {
|
||||
pfs_files[i]->GetName(), pfs_files[i]->GetSize());
|
||||
}
|
||||
}
|
||||
|
||||
bool PartitionFilesystem::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
|
||||
const auto iter = std::find(pfs_files.begin(), pfs_files.end(), file);
|
||||
if (iter == pfs_files.end())
|
||||
return false;
|
||||
|
||||
const std::ptrdiff_t offset = std::distance(pfs_files.begin(), iter);
|
||||
pfs_files[offset] = std::move(pfs_files.back());
|
||||
pfs_files.pop_back();
|
||||
|
||||
pfs_dirs.emplace_back(std::move(dir));
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -35,6 +35,9 @@ public:
|
||||
std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
|
||||
void PrintDebugInfo() const;
|
||||
|
||||
protected:
|
||||
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
|
||||
|
||||
private:
|
||||
struct Header {
|
||||
u32_le magic;
|
||||
@@ -81,6 +84,7 @@ private:
|
||||
std::size_t content_offset = 0;
|
||||
|
||||
std::vector<VirtualFile> pfs_files;
|
||||
std::vector<VirtualDir> pfs_dirs;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include "core/file_sys/vfs_vector.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
@@ -62,12 +61,13 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
// Game Updates
|
||||
const auto update_tid = GetUpdateTitleID(title_id);
|
||||
const auto update = installed->GetEntry(update_tid, ContentRecordType::Program);
|
||||
|
||||
if (update != nullptr && update->GetExeFS() != nullptr &&
|
||||
update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
|
||||
LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully",
|
||||
FormatTitleVersion(installed->GetEntryVersion(update_tid).value_or(0)));
|
||||
exefs = update->GetExeFS();
|
||||
if (update != nullptr) {
|
||||
if (update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS &&
|
||||
update->GetExeFS() != nullptr) {
|
||||
LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully",
|
||||
FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0)));
|
||||
exefs = update->GetExeFS();
|
||||
}
|
||||
}
|
||||
|
||||
return exefs;
|
||||
@@ -120,18 +120,6 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
|
||||
const auto build_id_raw = Common::HexArrayToString(header.build_id);
|
||||
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 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", build_id));
|
||||
|
||||
file->Resize(nso.size());
|
||||
file->WriteBytes(nso);
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INFO(Loader, "Patching NSO for build_id={}", build_id);
|
||||
|
||||
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
|
||||
@@ -180,8 +168,7 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
|
||||
|
||||
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
|
||||
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
|
||||
if ((type != ContentRecordType::Program && type != ContentRecordType::Data) ||
|
||||
load_dir == nullptr || load_dir->GetSize() <= 0) {
|
||||
if (type != ContentRecordType::Program || load_dir == nullptr || load_dir->GetSize() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -231,7 +218,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
|
||||
title_id, static_cast<u8>(type))
|
||||
.c_str();
|
||||
|
||||
if (type == ContentRecordType::Program || type == ContentRecordType::Data)
|
||||
if (type == ContentRecordType::Program)
|
||||
LOG_INFO(Loader, log_string);
|
||||
else
|
||||
LOG_DEBUG(Loader, log_string);
|
||||
@@ -249,7 +236,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
|
||||
if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
|
||||
new_nca->GetRomFS() != nullptr) {
|
||||
LOG_INFO(Loader, " RomFS: Update ({}) applied successfully",
|
||||
FormatTitleVersion(installed->GetEntryVersion(update_tid).value_or(0)));
|
||||
FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0)));
|
||||
romfs = new_nca->GetRomFS();
|
||||
}
|
||||
} else if (update_raw != nullptr) {
|
||||
@@ -293,11 +280,12 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
|
||||
} else {
|
||||
if (installed->HasEntry(update_tid, ContentRecordType::Program)) {
|
||||
const auto meta_ver = installed->GetEntryVersion(update_tid);
|
||||
if (meta_ver.value_or(0) == 0) {
|
||||
if (meta_ver == boost::none || meta_ver.get() == 0) {
|
||||
out.insert_or_assign("Update", "");
|
||||
} else {
|
||||
out.insert_or_assign(
|
||||
"Update", FormatTitleVersion(*meta_ver, TitleVersionFormat::ThreeElements));
|
||||
"Update",
|
||||
FormatTitleVersion(meta_ver.get(), TitleVersionFormat::ThreeElements));
|
||||
}
|
||||
} else if (update_raw != nullptr) {
|
||||
out.insert_or_assign("Update", "PACKED");
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <regex>
|
||||
#include <mbedtls/sha256.h>
|
||||
#include "common/assert.h"
|
||||
@@ -31,14 +30,6 @@ 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 RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) {
|
||||
return std::tie(lhs.title_id, lhs.type) == std::tie(rhs.title_id, rhs.type);
|
||||
}
|
||||
|
||||
bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
static bool FollowsTwoDigitDirFormat(std::string_view name) {
|
||||
static const std::regex two_digit_regex("000000[0-9A-F]{2}", std::regex_constants::ECMAScript |
|
||||
std::regex_constants::icase);
|
||||
@@ -106,12 +97,9 @@ static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
|
||||
|
||||
VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
|
||||
std::string_view path) const {
|
||||
const auto file = dir->GetFileRelative(path);
|
||||
if (file != nullptr)
|
||||
return file;
|
||||
|
||||
const auto nca_dir = dir->GetDirectoryRelative(path);
|
||||
if (nca_dir != nullptr) {
|
||||
if (dir->GetFileRelative(path) != nullptr)
|
||||
return dir->GetFileRelative(path);
|
||||
if (dir->GetDirectoryRelative(path) != nullptr) {
|
||||
const auto nca_dir = dir->GetDirectoryRelative(path);
|
||||
VirtualFile file = nullptr;
|
||||
|
||||
@@ -162,28 +150,28 @@ VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
|
||||
return file;
|
||||
}
|
||||
|
||||
static std::optional<NcaID> CheckMapForContentRecord(
|
||||
static boost::optional<NcaID> CheckMapForContentRecord(
|
||||
const boost::container::flat_map<u64, CNMT>& map, u64 title_id, ContentRecordType type) {
|
||||
if (map.find(title_id) == map.end())
|
||||
return {};
|
||||
return boost::none;
|
||||
|
||||
const auto& cnmt = map.at(title_id);
|
||||
|
||||
const auto iter = std::find_if(cnmt.GetContentRecords().begin(), cnmt.GetContentRecords().end(),
|
||||
[type](const ContentRecord& rec) { return rec.type == type; });
|
||||
if (iter == cnmt.GetContentRecords().end())
|
||||
return {};
|
||||
return boost::none;
|
||||
|
||||
return std::make_optional(iter->nca_id);
|
||||
return boost::make_optional(iter->nca_id);
|
||||
}
|
||||
|
||||
std::optional<NcaID> RegisteredCache::GetNcaIDFromMetadata(u64 title_id,
|
||||
ContentRecordType type) const {
|
||||
boost::optional<NcaID> RegisteredCache::GetNcaIDFromMetadata(u64 title_id,
|
||||
ContentRecordType type) const {
|
||||
if (type == ContentRecordType::Meta && meta_id.find(title_id) != meta_id.end())
|
||||
return meta_id.at(title_id);
|
||||
|
||||
const auto res1 = CheckMapForContentRecord(yuzu_meta, title_id, type);
|
||||
if (res1)
|
||||
if (res1 != boost::none)
|
||||
return res1;
|
||||
return CheckMapForContentRecord(meta, title_id, type);
|
||||
}
|
||||
@@ -228,7 +216,7 @@ void RegisteredCache::ProcessFiles(const std::vector<NcaID>& ids) {
|
||||
|
||||
if (file == nullptr)
|
||||
continue;
|
||||
const auto nca = std::make_shared<NCA>(parser(file, id), nullptr, 0, keys);
|
||||
const auto nca = std::make_shared<NCA>(parser(file, id));
|
||||
if (nca->GetStatus() != Loader::ResultStatus::Success ||
|
||||
nca->GetType() != NCAContentType::Meta) {
|
||||
continue;
|
||||
@@ -286,14 +274,17 @@ bool RegisteredCache::HasEntry(RegisteredCacheEntry entry) const {
|
||||
|
||||
VirtualFile RegisteredCache::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
|
||||
const auto id = GetNcaIDFromMetadata(title_id, type);
|
||||
return id ? GetFileAtID(*id) : nullptr;
|
||||
if (id == boost::none)
|
||||
return nullptr;
|
||||
|
||||
return GetFileAtID(id.get());
|
||||
}
|
||||
|
||||
VirtualFile RegisteredCache::GetEntryUnparsed(RegisteredCacheEntry entry) const {
|
||||
return GetEntryUnparsed(entry.title_id, entry.type);
|
||||
}
|
||||
|
||||
std::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const {
|
||||
boost::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const {
|
||||
const auto meta_iter = meta.find(title_id);
|
||||
if (meta_iter != meta.end())
|
||||
return meta_iter->second.GetTitleVersion();
|
||||
@@ -302,12 +293,15 @@ std::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const {
|
||||
if (yuzu_meta_iter != yuzu_meta.end())
|
||||
return yuzu_meta_iter->second.GetTitleVersion();
|
||||
|
||||
return {};
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) const {
|
||||
const auto id = GetNcaIDFromMetadata(title_id, type);
|
||||
return id ? parser(GetFileAtID(*id), *id) : nullptr;
|
||||
if (id == boost::none)
|
||||
return nullptr;
|
||||
|
||||
return parser(GetFileAtID(id.get()), id.get());
|
||||
}
|
||||
|
||||
VirtualFile RegisteredCache::GetEntryRaw(RegisteredCacheEntry entry) const {
|
||||
@@ -318,7 +312,7 @@ std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType t
|
||||
const auto raw = GetEntryRaw(title_id, type);
|
||||
if (raw == nullptr)
|
||||
return nullptr;
|
||||
return std::make_unique<NCA>(raw, nullptr, 0, keys);
|
||||
return std::make_unique<NCA>(raw);
|
||||
}
|
||||
|
||||
std::unique_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const {
|
||||
@@ -361,8 +355,8 @@ std::vector<RegisteredCacheEntry> RegisteredCache::ListEntries() const {
|
||||
}
|
||||
|
||||
std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter(
|
||||
std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
|
||||
std::optional<u64> title_id) const {
|
||||
boost::optional<TitleType> title_type, boost::optional<ContentRecordType> record_type,
|
||||
boost::optional<u64> title_id) const {
|
||||
std::vector<RegisteredCacheEntry> out;
|
||||
IterateAllMetadata<RegisteredCacheEntry>(
|
||||
out,
|
||||
@@ -370,11 +364,11 @@ std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter(
|
||||
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())
|
||||
if (title_type != boost::none && title_type.get() != c.GetType())
|
||||
return false;
|
||||
if (record_type && *record_type != r.type)
|
||||
if (record_type != boost::none && record_type.get() != r.type)
|
||||
return false;
|
||||
if (title_id && *title_id != c.GetTitleID())
|
||||
if (title_id != boost::none && title_id.get() != c.GetTitleID())
|
||||
return false;
|
||||
return true;
|
||||
});
|
||||
@@ -456,7 +450,7 @@ InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NCA> nca, TitleType
|
||||
|
||||
InstallResult RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy,
|
||||
bool overwrite_if_exists,
|
||||
std::optional<NcaID> override_id) {
|
||||
boost::optional<NcaID> override_id) {
|
||||
const auto in = nca->GetBaseFile();
|
||||
Core::Crypto::SHA256Hash hash{};
|
||||
|
||||
@@ -465,12 +459,12 @@ InstallResult RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const Vfs
|
||||
// game is massive), we're going to cheat and only hash the first MB of the NCA.
|
||||
// Also, for XCIs the NcaID matters, so if the override id isn't none, use that.
|
||||
NcaID id{};
|
||||
if (override_id) {
|
||||
id = *override_id;
|
||||
} else {
|
||||
if (override_id == boost::none) {
|
||||
const auto& data = in->ReadBytes(0x100000);
|
||||
mbedtls_sha256(data.data(), data.size(), hash.data(), 0);
|
||||
memcpy(id.data(), hash.data(), 16);
|
||||
} else {
|
||||
id = override_id.get();
|
||||
}
|
||||
|
||||
std::string path = GetRelativePathFromNcaID(id, false, true);
|
||||
@@ -540,14 +534,14 @@ bool RegisteredCacheUnion::HasEntry(RegisteredCacheEntry entry) const {
|
||||
return HasEntry(entry.title_id, entry.type);
|
||||
}
|
||||
|
||||
std::optional<u32> RegisteredCacheUnion::GetEntryVersion(u64 title_id) const {
|
||||
boost::optional<u32> RegisteredCacheUnion::GetEntryVersion(u64 title_id) const {
|
||||
for (const auto& c : caches) {
|
||||
const auto res = c->GetEntryVersion(title_id);
|
||||
if (res)
|
||||
if (res != boost::none)
|
||||
return res;
|
||||
}
|
||||
|
||||
return {};
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
VirtualFile RegisteredCacheUnion::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
|
||||
@@ -599,15 +593,12 @@ std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntries() const {
|
||||
},
|
||||
[](const CNMT& c, const ContentRecord& r) { return true; });
|
||||
}
|
||||
|
||||
std::sort(out.begin(), out.end());
|
||||
out.erase(std::unique(out.begin(), out.end()), out.end());
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntriesFilter(
|
||||
std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
|
||||
std::optional<u64> title_id) const {
|
||||
boost::optional<TitleType> title_type, boost::optional<ContentRecordType> record_type,
|
||||
boost::optional<u64> title_id) const {
|
||||
std::vector<RegisteredCacheEntry> out;
|
||||
for (const auto& c : caches) {
|
||||
c->IterateAllMetadata<RegisteredCacheEntry>(
|
||||
@@ -616,18 +607,15 @@ std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntriesFilter(
|
||||
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())
|
||||
if (title_type != boost::none && title_type.get() != c.GetType())
|
||||
return false;
|
||||
if (record_type && *record_type != r.type)
|
||||
if (record_type != boost::none && record_type.get() != r.type)
|
||||
return false;
|
||||
if (title_id && *title_id != c.GetTitleID())
|
||||
if (title_id != boost::none && title_id.get() != c.GetTitleID())
|
||||
return false;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
std::sort(out.begin(), out.end());
|
||||
out.erase(std::unique(out.begin(), out.end()), out.end());
|
||||
return out;
|
||||
}
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <vector>
|
||||
#include <boost/container/flat_map.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/crypto/key_manager.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace FileSys {
|
||||
@@ -51,10 +50,6 @@ constexpr u64 GetUpdateTitleID(u64 base_title_id) {
|
||||
// boost flat_map requires operator< for O(log(n)) lookups.
|
||||
bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
|
||||
|
||||
// std unique requires operator== to identify duplicates.
|
||||
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.
|
||||
* Nintendo's registered format follows this structure:
|
||||
@@ -65,8 +60,8 @@ bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs
|
||||
* | 00
|
||||
* | 01 <- Actual content split along 4GB boundaries. (optional)
|
||||
*
|
||||
* (This impl also supports substituting the nca dir for an nca file, as that's more convenient
|
||||
* when 4GB splitting can be ignored.)
|
||||
* (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 {
|
||||
friend class RegisteredCacheUnion;
|
||||
@@ -85,7 +80,7 @@ public:
|
||||
bool HasEntry(u64 title_id, ContentRecordType type) const;
|
||||
bool HasEntry(RegisteredCacheEntry entry) const;
|
||||
|
||||
std::optional<u32> GetEntryVersion(u64 title_id) const;
|
||||
boost::optional<u32> GetEntryVersion(u64 title_id) const;
|
||||
|
||||
VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const;
|
||||
VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
|
||||
@@ -97,10 +92,11 @@ public:
|
||||
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.
|
||||
// If a parameter is not boost::none, 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;
|
||||
boost::optional<TitleType> title_type = boost::none,
|
||||
boost::optional<ContentRecordType> record_type = boost::none,
|
||||
boost::optional<u64> title_id = boost::none) 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.
|
||||
@@ -125,17 +121,16 @@ private:
|
||||
std::vector<NcaID> AccumulateFiles() const;
|
||||
void ProcessFiles(const std::vector<NcaID>& ids);
|
||||
void AccumulateYuzuMeta();
|
||||
std::optional<NcaID> GetNcaIDFromMetadata(u64 title_id, ContentRecordType type) const;
|
||||
boost::optional<NcaID> GetNcaIDFromMetadata(u64 title_id, ContentRecordType type) const;
|
||||
VirtualFile GetFileAtID(NcaID id) const;
|
||||
VirtualFile OpenFileOrDirectoryConcat(const VirtualDir& dir, std::string_view path) const;
|
||||
InstallResult RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy,
|
||||
bool overwrite_if_exists, std::optional<NcaID> override_id = {});
|
||||
bool overwrite_if_exists,
|
||||
boost::optional<NcaID> override_id = boost::none);
|
||||
bool RawInstallYuzuMeta(const CNMT& cnmt);
|
||||
|
||||
VirtualDir dir;
|
||||
RegisteredCacheParsingFunction parser;
|
||||
Core::Crypto::KeyManager keys;
|
||||
|
||||
// maps tid -> NcaID of meta
|
||||
boost::container::flat_map<u64, NcaID> meta_id;
|
||||
// maps tid -> meta
|
||||
@@ -154,7 +149,7 @@ public:
|
||||
bool HasEntry(u64 title_id, ContentRecordType type) const;
|
||||
bool HasEntry(RegisteredCacheEntry entry) const;
|
||||
|
||||
std::optional<u32> GetEntryVersion(u64 title_id) const;
|
||||
boost::optional<u32> GetEntryVersion(u64 title_id) const;
|
||||
|
||||
VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const;
|
||||
VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
|
||||
@@ -166,10 +161,11 @@ public:
|
||||
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.
|
||||
// If a parameter is not boost::none, 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;
|
||||
boost::optional<TitleType> title_type = boost::none,
|
||||
boost::optional<ContentRecordType> record_type = boost::none,
|
||||
boost::optional<u64> title_id = boost::none) const;
|
||||
|
||||
private:
|
||||
std::vector<RegisteredCache*> caches;
|
||||
|
||||
@@ -83,24 +83,6 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescr
|
||||
return MakeResult<VirtualDir>(std::move(out));
|
||||
}
|
||||
|
||||
VirtualDir SaveDataFactory::GetSaveDataSpaceDirectory(SaveDataSpaceId space) const {
|
||||
return dir->GetDirectoryRelative(GetSaveDataSpaceIdPath(space));
|
||||
}
|
||||
|
||||
std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) {
|
||||
switch (space) {
|
||||
case SaveDataSpaceId::NandSystem:
|
||||
return "/system/";
|
||||
case SaveDataSpaceId::NandUser:
|
||||
return "/user/";
|
||||
case SaveDataSpaceId::TemporaryStorage:
|
||||
return "/temp/";
|
||||
default:
|
||||
ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
|
||||
return "/unrecognized/"; ///< To prevent corruption when ignoring asserts.
|
||||
}
|
||||
}
|
||||
|
||||
std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id,
|
||||
u128 user_id, u64 save_id) {
|
||||
// According to switchbrew, if a save is of type SaveData and the title id field is 0, it should
|
||||
@@ -108,7 +90,21 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
|
||||
if (type == SaveDataType::SaveData && title_id == 0)
|
||||
title_id = Core::CurrentProcess()->GetTitleID();
|
||||
|
||||
std::string out = GetSaveDataSpaceIdPath(space);
|
||||
std::string out;
|
||||
|
||||
switch (space) {
|
||||
case SaveDataSpaceId::NandSystem:
|
||||
out = "/system/";
|
||||
break;
|
||||
case SaveDataSpaceId::NandUser:
|
||||
out = "/user/";
|
||||
break;
|
||||
case SaveDataSpaceId::TemporaryStorage:
|
||||
out = "/temp/";
|
||||
break;
|
||||
default:
|
||||
ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case SaveDataType::SystemSaveData:
|
||||
|
||||
@@ -52,9 +52,6 @@ public:
|
||||
|
||||
ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta);
|
||||
|
||||
VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;
|
||||
|
||||
static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space);
|
||||
static std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id,
|
||||
u128 user_id, u64 save_id);
|
||||
|
||||
|
||||
@@ -205,6 +205,10 @@ VirtualDir NSP::GetParentDirectory() const {
|
||||
return file->GetContainingDirectory();
|
||||
}
|
||||
|
||||
bool NSP::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void NSP::InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files) {
|
||||
exefs = pfs;
|
||||
|
||||
@@ -252,7 +256,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto next_nca = std::make_shared<NCA>(next_file, nullptr, 0, keys);
|
||||
auto next_nca = std::make_shared<NCA>(next_file);
|
||||
if (next_nca->GetType() == NCAContentType::Program)
|
||||
program_status[cnmt.GetTitleID()] = next_nca->GetStatus();
|
||||
if (next_nca->GetStatus() == Loader::ResultStatus::Success ||
|
||||
|
||||
@@ -55,6 +55,9 @@ public:
|
||||
|
||||
VirtualDir GetParentDirectory() const override;
|
||||
|
||||
protected:
|
||||
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
|
||||
|
||||
private:
|
||||
void InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files);
|
||||
void ReadNCAs(const std::vector<VirtualFile>& files);
|
||||
@@ -70,8 +73,6 @@ private:
|
||||
std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> ncas;
|
||||
std::vector<VirtualFile> ticket_files;
|
||||
|
||||
Core::Crypto::KeyManager keys;
|
||||
|
||||
VirtualFile romfs;
|
||||
VirtualDir exefs;
|
||||
};
|
||||
|
||||
@@ -167,13 +167,13 @@ std::string VfsFile::GetExtension() const {
|
||||
|
||||
VfsDirectory::~VfsDirectory() = default;
|
||||
|
||||
std::optional<u8> VfsFile::ReadByte(std::size_t offset) const {
|
||||
boost::optional<u8> VfsFile::ReadByte(std::size_t offset) const {
|
||||
u8 out{};
|
||||
std::size_t size = Read(&out, 1, offset);
|
||||
if (size == 1)
|
||||
return out;
|
||||
|
||||
return {};
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
std::vector<u8> VfsFile::ReadBytes(std::size_t size, std::size_t offset) const {
|
||||
@@ -472,14 +472,10 @@ bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t blo
|
||||
std::vector<u8> temp(std::min(block_size, src->GetSize()));
|
||||
for (std::size_t i = 0; i < src->GetSize(); i += block_size) {
|
||||
const auto read = std::min(block_size, src->GetSize() - i);
|
||||
const auto block = src->Read(temp.data(), read, i);
|
||||
|
||||
if (src->Read(temp.data(), read, i) != read) {
|
||||
if (dest->Write(temp.data(), read, i) != read)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dest->Write(temp.data(), read, i) != read) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -4,15 +4,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
|
||||
@@ -105,8 +103,8 @@ public:
|
||||
// into file. Returns number of bytes successfully written.
|
||||
virtual std::size_t Write(const u8* data, std::size_t length, std::size_t offset = 0) = 0;
|
||||
|
||||
// Reads exactly one byte at the offset provided, returning std::nullopt on error.
|
||||
virtual std::optional<u8> ReadByte(std::size_t offset = 0) const;
|
||||
// Reads exactly one byte at the offset provided, returning boost::none on error.
|
||||
virtual boost::optional<u8> ReadByte(std::size_t offset = 0) const;
|
||||
// Reads size bytes starting at offset in file into a vector.
|
||||
virtual std::vector<u8> ReadBytes(std::size_t size, std::size_t offset = 0) const;
|
||||
// Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(),
|
||||
@@ -264,8 +262,36 @@ public:
|
||||
// item name -> type.
|
||||
virtual std::map<std::string, VfsEntryType, std::less<>> GetEntries() const;
|
||||
|
||||
// Interprets the file with name file instead as a directory of type directory.
|
||||
// The directory must have a constructor that takes a single argument of type
|
||||
// std::shared_ptr<VfsFile>. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a
|
||||
// subdirectory in one call.
|
||||
template <typename Directory>
|
||||
bool InterpretAsDirectory(std::string_view file) {
|
||||
auto file_p = GetFile(file);
|
||||
|
||||
if (file_p == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ReplaceFileWithSubdirectory(file_p, std::make_shared<Directory>(file_p));
|
||||
}
|
||||
|
||||
bool InterpretAsDirectory(const std::function<VirtualDir(VirtualFile)>& function,
|
||||
const std::string& file) {
|
||||
auto file_p = GetFile(file);
|
||||
if (file_p == nullptr)
|
||||
return false;
|
||||
return ReplaceFileWithSubdirectory(file_p, function(file_p));
|
||||
}
|
||||
|
||||
// Returns the full path of this directory as a string, recursively
|
||||
virtual std::string GetFullPath() const;
|
||||
|
||||
protected:
|
||||
// Backend for InterpretAsDirectory.
|
||||
// Removes all references to file and adds a reference to dir in the directory's implementation.
|
||||
virtual bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) = 0;
|
||||
};
|
||||
|
||||
// A convenience partial-implementation of VfsDirectory that stubs out methods that should only work
|
||||
|
||||
@@ -126,4 +126,7 @@ bool LayeredVfsDirectory::Rename(std::string_view name_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LayeredVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
|
||||
return false;
|
||||
}
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -39,6 +39,9 @@ public:
|
||||
bool DeleteFile(std::string_view name) override;
|
||||
bool Rename(std::string_view name) override;
|
||||
|
||||
protected:
|
||||
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
|
||||
|
||||
private:
|
||||
std::vector<VirtualDir> dirs;
|
||||
std::string name;
|
||||
|
||||
@@ -57,11 +57,11 @@ std::size_t OffsetVfsFile::Write(const u8* data, std::size_t length, std::size_t
|
||||
return file->Write(data, TrimToFit(length, r_offset), offset + r_offset);
|
||||
}
|
||||
|
||||
std::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const {
|
||||
boost::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const {
|
||||
if (r_offset < size)
|
||||
return file->ReadByte(offset + r_offset);
|
||||
|
||||
return {};
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
std::vector<u8> OffsetVfsFile::ReadBytes(std::size_t r_size, std::size_t r_offset) const {
|
||||
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
bool IsReadable() const override;
|
||||
std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
|
||||
std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override;
|
||||
std::optional<u8> ReadByte(std::size_t offset) const override;
|
||||
boost::optional<u8> ReadByte(std::size_t offset) const override;
|
||||
std::vector<u8> ReadBytes(std::size_t size, std::size_t offset) const override;
|
||||
std::vector<u8> ReadAllBytes() const override;
|
||||
bool WriteByte(u8 data, std::size_t offset) override;
|
||||
|
||||
@@ -430,4 +430,7 @@ std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries()
|
||||
return out;
|
||||
}
|
||||
|
||||
bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
|
||||
return false;
|
||||
}
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -100,6 +100,9 @@ public:
|
||||
std::string GetFullPath() const override;
|
||||
std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override;
|
||||
|
||||
protected:
|
||||
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
|
||||
|
||||
private:
|
||||
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read);
|
||||
|
||||
|
||||
@@ -53,10 +53,10 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::optional<u8> ReadByte(std::size_t offset) const override {
|
||||
boost::optional<u8> ReadByte(std::size_t offset) const override {
|
||||
if (offset < size)
|
||||
return value;
|
||||
return {};
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override {
|
||||
|
||||
@@ -132,4 +132,11 @@ void VectorVfsDirectory::AddFile(VirtualFile file) {
|
||||
void VectorVfsDirectory::AddDirectory(VirtualDir dir) {
|
||||
dirs.push_back(std::move(dir));
|
||||
}
|
||||
|
||||
bool VectorVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
|
||||
if (!DeleteFile(file->GetName()))
|
||||
return false;
|
||||
dirs.emplace_back(std::move(dir));
|
||||
return true;
|
||||
}
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -57,6 +57,9 @@ public:
|
||||
virtual void AddFile(VirtualFile file);
|
||||
virtual void AddDirectory(VirtualDir dir);
|
||||
|
||||
protected:
|
||||
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
|
||||
|
||||
private:
|
||||
std::vector<VirtualFile> files;
|
||||
std::vector<VirtualDir> dirs;
|
||||
|
||||
@@ -163,4 +163,7 @@ std::shared_ptr<VfsDirectory> NAX::GetParentDirectory() const {
|
||||
return file->GetContainingDirectory();
|
||||
}
|
||||
|
||||
bool NAX::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
|
||||
return false;
|
||||
}
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -51,6 +51,9 @@ public:
|
||||
|
||||
std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
|
||||
|
||||
protected:
|
||||
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
|
||||
|
||||
private:
|
||||
Loader::ResultStatus Parse(std::string_view path);
|
||||
|
||||
|
||||
@@ -14,6 +14,11 @@ namespace IPC {
|
||||
/// Size of the command buffer area, in 32-bit words.
|
||||
constexpr std::size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32);
|
||||
|
||||
// These errors are commonly returned by invalid IPC translations, so alias them here for
|
||||
// convenience.
|
||||
// TODO(yuriks): These will probably go away once translation is implemented inside the kernel.
|
||||
constexpr auto ERR_INVALID_HANDLE = Kernel::ERR_INVALID_HANDLE_OS;
|
||||
|
||||
enum class ControlCommand : u32 {
|
||||
ConvertSessionToDomain = 0,
|
||||
ConvertDomainToSession = 1,
|
||||
|
||||
@@ -117,7 +117,8 @@ public:
|
||||
|
||||
AlignWithPadding();
|
||||
|
||||
if (context.Session()->IsDomain() && context.HasDomainMessageHeader()) {
|
||||
const bool request_has_domain_header{context.GetDomainMessageHeader() != nullptr};
|
||||
if (context.Session()->IsDomain() && request_has_domain_header) {
|
||||
IPC::DomainMessageHeader domain_header{};
|
||||
domain_header.num_objects = num_domain_objects;
|
||||
PushRaw(domain_header);
|
||||
|
||||
@@ -8,28 +8,71 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
// Confirmed Switch kernel error codes
|
||||
namespace ErrCodes {
|
||||
enum {
|
||||
// TODO(Subv): Remove these 3DS OS error codes.
|
||||
SessionClosedByRemote = 26,
|
||||
NoPendingSessions = 35,
|
||||
InvalidBufferDescriptor = 48,
|
||||
|
||||
constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7};
|
||||
constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101};
|
||||
constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102};
|
||||
constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105};
|
||||
constexpr ResultCode ERR_INVALID_ADDRESS_STATE{ErrorModule::Kernel, 106};
|
||||
constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS{ErrorModule::Kernel, 108};
|
||||
constexpr ResultCode ERR_INVALID_MEMORY_RANGE{ErrorModule::Kernel, 110};
|
||||
constexpr ResultCode ERR_INVALID_PROCESSOR_ID{ErrorModule::Kernel, 113};
|
||||
constexpr ResultCode ERR_INVALID_THREAD_PRIORITY{ErrorModule::Kernel, 112};
|
||||
constexpr ResultCode ERR_INVALID_HANDLE{ErrorModule::Kernel, 114};
|
||||
constexpr ResultCode ERR_INVALID_POINTER{ErrorModule::Kernel, 115};
|
||||
constexpr ResultCode ERR_INVALID_COMBINATION{ErrorModule::Kernel, 116};
|
||||
constexpr ResultCode RESULT_TIMEOUT{ErrorModule::Kernel, 117};
|
||||
constexpr ResultCode ERR_SYNCHRONIZATION_CANCELED{ErrorModule::Kernel, 118};
|
||||
constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119};
|
||||
constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120};
|
||||
constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121};
|
||||
constexpr ResultCode ERR_ALREADY_REGISTERED{ErrorModule::Kernel, 122};
|
||||
constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123};
|
||||
constexpr ResultCode ERR_INVALID_STATE{ErrorModule::Kernel, 125};
|
||||
constexpr ResultCode ERR_RESOURCE_LIMIT_EXCEEDED{ErrorModule::Kernel, 132};
|
||||
// Confirmed Switch OS error codes
|
||||
MaxConnectionsReached = 7,
|
||||
InvalidSize = 101,
|
||||
InvalidAddress = 102,
|
||||
HandleTableFull = 105,
|
||||
InvalidMemoryState = 106,
|
||||
InvalidMemoryPermissions = 108,
|
||||
InvalidMemoryRange = 110,
|
||||
InvalidThreadPriority = 112,
|
||||
InvalidProcessorId = 113,
|
||||
InvalidHandle = 114,
|
||||
InvalidCombination = 116,
|
||||
Timeout = 117,
|
||||
SynchronizationCanceled = 118,
|
||||
TooLarge = 119,
|
||||
InvalidEnumValue = 120,
|
||||
NoSuchEntry = 121,
|
||||
AlreadyRegistered = 122,
|
||||
InvalidState = 125,
|
||||
ResourceLimitExceeded = 132,
|
||||
};
|
||||
}
|
||||
|
||||
// WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always
|
||||
// double check that the code matches before re-using the constant.
|
||||
|
||||
// TODO(bunnei): Replace -1 with correct errors for Switch OS
|
||||
constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull);
|
||||
constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(-1);
|
||||
constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge);
|
||||
constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrorModule::Kernel,
|
||||
ErrCodes::MaxConnectionsReached);
|
||||
constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue);
|
||||
constexpr ResultCode ERR_INVALID_ENUM_VALUE_FND(-1);
|
||||
constexpr ResultCode ERR_INVALID_COMBINATION(-1);
|
||||
constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorModule::Kernel,
|
||||
ErrCodes::InvalidCombination);
|
||||
constexpr ResultCode ERR_OUT_OF_MEMORY(-1);
|
||||
constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress);
|
||||
constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState);
|
||||
constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel,
|
||||
ErrCodes::InvalidMemoryPermissions);
|
||||
constexpr ResultCode ERR_INVALID_MEMORY_RANGE(ErrorModule::Kernel, ErrCodes::InvalidMemoryRange);
|
||||
constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle);
|
||||
constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId);
|
||||
constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize);
|
||||
constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::Kernel, ErrCodes::AlreadyRegistered);
|
||||
constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState);
|
||||
constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel,
|
||||
ErrCodes::InvalidThreadPriority);
|
||||
constexpr ResultCode ERR_INVALID_POINTER(-1);
|
||||
constexpr ResultCode ERR_INVALID_OBJECT_ADDR(-1);
|
||||
constexpr ResultCode ERR_NOT_AUTHORIZED(-1);
|
||||
/// Alternate code returned instead of ERR_INVALID_HANDLE in some code paths.
|
||||
constexpr ResultCode ERR_INVALID_HANDLE_OS(-1);
|
||||
constexpr ResultCode ERR_NOT_FOUND(ErrorModule::Kernel, ErrCodes::NoSuchEntry);
|
||||
constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout);
|
||||
/// Returned when Accept() is called on a port with no sessions to be accepted.
|
||||
constexpr ResultCode ERR_NO_PENDING_SESSIONS(-1);
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -77,8 +77,7 @@ HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_ses
|
||||
|
||||
HLERequestContext::~HLERequestContext() = default;
|
||||
|
||||
void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf,
|
||||
bool incoming) {
|
||||
void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
|
||||
IPC::RequestParser rp(src_cmdbuf);
|
||||
command_header = std::make_shared<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>());
|
||||
|
||||
@@ -95,6 +94,8 @@ void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_
|
||||
rp.Skip(2, false);
|
||||
}
|
||||
if (incoming) {
|
||||
auto& handle_table = Core::System::GetInstance().Kernel().HandleTable();
|
||||
|
||||
// Populate the object lists with the data in the IPC request.
|
||||
for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) {
|
||||
copy_objects.push_back(handle_table.GetGeneric(rp.Pop<Handle>()));
|
||||
@@ -188,9 +189,10 @@ void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_
|
||||
rp.Skip(1, false); // The command is actually an u64, but we don't use the high part.
|
||||
}
|
||||
|
||||
ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const HandleTable& handle_table,
|
||||
u32_le* src_cmdbuf) {
|
||||
ParseCommandBuffer(handle_table, src_cmdbuf, true);
|
||||
ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf,
|
||||
Process& src_process,
|
||||
HandleTable& src_table) {
|
||||
ParseCommandBuffer(src_cmdbuf, true);
|
||||
if (command_header->type == IPC::CommandType::Close) {
|
||||
// Close does not populate the rest of the IPC header
|
||||
return RESULT_SUCCESS;
|
||||
@@ -205,17 +207,14 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const HandleTabl
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
|
||||
auto& owner_process = *thread.GetOwnerProcess();
|
||||
auto& handle_table = owner_process.GetHandleTable();
|
||||
|
||||
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) {
|
||||
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf;
|
||||
Memory::ReadBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
|
||||
Memory::ReadBlock(*thread.GetOwnerProcess(), thread.GetTLSAddress(), dst_cmdbuf.data(),
|
||||
dst_cmdbuf.size() * sizeof(u32));
|
||||
|
||||
// The header was already built in the internal command buffer. Attempt to parse it to verify
|
||||
// the integrity and then copy it over to the target command buffer.
|
||||
ParseCommandBuffer(handle_table, cmd_buf.data(), false);
|
||||
ParseCommandBuffer(cmd_buf.data(), false);
|
||||
|
||||
// The data_size already includes the payload header, the padding and the domain header.
|
||||
std::size_t size = data_payload_offset + command_header->data_size -
|
||||
@@ -237,6 +236,8 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
|
||||
ASSERT(copy_objects.size() == handle_descriptor_header->num_handles_to_copy);
|
||||
ASSERT(move_objects.size() == handle_descriptor_header->num_handles_to_move);
|
||||
|
||||
auto& handle_table = Core::System::GetInstance().Kernel().HandleTable();
|
||||
|
||||
// We don't make a distinction between copy and move handles when translating since HLE
|
||||
// services don't deal with handles directly. However, the guest applications might check
|
||||
// for specific values in each of these descriptors.
|
||||
@@ -267,7 +268,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
|
||||
}
|
||||
|
||||
// Copy the translated command buffer back into the thread's command buffer area.
|
||||
Memory::WriteBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
|
||||
Memory::WriteBlock(*thread.GetOwnerProcess(), thread.GetTLSAddress(), dst_cmdbuf.data(),
|
||||
dst_cmdbuf.size() * sizeof(u32));
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
@@ -24,10 +24,10 @@ class ServiceFrameworkBase;
|
||||
namespace Kernel {
|
||||
|
||||
class Domain;
|
||||
class Event;
|
||||
class HandleTable;
|
||||
class HLERequestContext;
|
||||
class Process;
|
||||
class Event;
|
||||
|
||||
/**
|
||||
* Interface implemented by HLE Session handlers.
|
||||
@@ -126,12 +126,13 @@ public:
|
||||
u64 timeout, WakeupCallback&& callback,
|
||||
Kernel::SharedPtr<Kernel::Event> event = nullptr);
|
||||
|
||||
/// Populates this context with data from the requesting process/thread.
|
||||
ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table,
|
||||
u32_le* src_cmdbuf);
|
||||
void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming);
|
||||
|
||||
/// Populates this context with data from the requesting process/thread.
|
||||
ResultCode PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, Process& src_process,
|
||||
HandleTable& src_table);
|
||||
/// Writes data from this context back to the requesting process/thread.
|
||||
ResultCode WriteToOutgoingCommandBuffer(Thread& thread);
|
||||
ResultCode WriteToOutgoingCommandBuffer(const Thread& thread);
|
||||
|
||||
u32_le GetCommand() const {
|
||||
return command;
|
||||
@@ -161,12 +162,8 @@ public:
|
||||
return buffer_c_desciptors;
|
||||
}
|
||||
|
||||
const IPC::DomainMessageHeader* GetDomainMessageHeader() const {
|
||||
return domain_message_header.get();
|
||||
}
|
||||
|
||||
bool HasDomainMessageHeader() const {
|
||||
return domain_message_header != nullptr;
|
||||
const std::shared_ptr<IPC::DomainMessageHeader>& GetDomainMessageHeader() const {
|
||||
return domain_message_header;
|
||||
}
|
||||
|
||||
/// Helper function to read a buffer using the appropriate buffer descriptor
|
||||
@@ -258,8 +255,6 @@ public:
|
||||
std::string Description() const;
|
||||
|
||||
private:
|
||||
void ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf, bool incoming);
|
||||
|
||||
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
|
||||
SharedPtr<Kernel::ServerSession> server_session;
|
||||
// TODO(yuriks): Check common usage of this and optimize size accordingly
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Kernel {
|
||||
*/
|
||||
static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_late) {
|
||||
const auto proper_handle = static_cast<Handle>(thread_handle);
|
||||
const auto& system = Core::System::GetInstance();
|
||||
auto& system = Core::System::GetInstance();
|
||||
|
||||
// Lock the global kernel mutex when we enter the kernel HLE.
|
||||
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
|
||||
@@ -90,7 +90,7 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_
|
||||
/// The timer callback event, called when a timer is fired
|
||||
static void TimerCallback(u64 timer_handle, int cycles_late) {
|
||||
const auto proper_handle = static_cast<Handle>(timer_handle);
|
||||
const auto& system = Core::System::GetInstance();
|
||||
auto& system = Core::System::GetInstance();
|
||||
SharedPtr<Timer> timer = system.Kernel().RetrieveTimerFromCallbackHandleTable(proper_handle);
|
||||
|
||||
if (timer == nullptr) {
|
||||
@@ -118,6 +118,7 @@ struct KernelCore::Impl {
|
||||
process_list.clear();
|
||||
current_process = nullptr;
|
||||
|
||||
handle_table.Clear();
|
||||
resource_limits.fill(nullptr);
|
||||
|
||||
thread_wakeup_callback_handle_table.Clear();
|
||||
@@ -208,6 +209,7 @@ struct KernelCore::Impl {
|
||||
std::vector<SharedPtr<Process>> process_list;
|
||||
Process* current_process = nullptr;
|
||||
|
||||
Kernel::HandleTable handle_table;
|
||||
std::array<SharedPtr<ResourceLimit>, 4> resource_limits;
|
||||
|
||||
/// The event type of the generic timer callback event
|
||||
@@ -239,6 +241,14 @@ void KernelCore::Shutdown() {
|
||||
impl->Shutdown();
|
||||
}
|
||||
|
||||
Kernel::HandleTable& KernelCore::HandleTable() {
|
||||
return impl->handle_table;
|
||||
}
|
||||
|
||||
const Kernel::HandleTable& KernelCore::HandleTable() const {
|
||||
return impl->handle_table;
|
||||
}
|
||||
|
||||
SharedPtr<ResourceLimit> KernelCore::ResourceLimitForCategory(
|
||||
ResourceLimitCategory category) const {
|
||||
return impl->resource_limits.at(static_cast<std::size_t>(category));
|
||||
|
||||
@@ -47,6 +47,12 @@ public:
|
||||
/// Clears all resources in use by the kernel instance.
|
||||
void Shutdown();
|
||||
|
||||
/// Provides a reference to the handle table.
|
||||
Kernel::HandleTable& HandleTable();
|
||||
|
||||
/// Provides a const reference to the handle table.
|
||||
const Kernel::HandleTable& HandleTable() const;
|
||||
|
||||
/// Retrieves a shared pointer to a ResourceLimit identified by the given category.
|
||||
SharedPtr<ResourceLimit> ResourceLimitForCategory(ResourceLimitCategory category) const;
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/range/algorithm_ext/erase.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
|
||||
@@ -5,9 +5,11 @@
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include "common/assert.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/program_metadata.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
@@ -15,7 +17,6 @@
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/vm_manager.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -34,11 +35,6 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
|
||||
process->process_id = kernel.CreateNewProcessID();
|
||||
process->svc_access_mask.set();
|
||||
|
||||
std::mt19937 rng(Settings::values.rng_seed.value_or(0));
|
||||
std::uniform_int_distribution<u64> distribution;
|
||||
std::generate(process->random_entropy.begin(), process->random_entropy.end(),
|
||||
[&] { return distribution(rng); });
|
||||
|
||||
kernel.AppendNewProcess(process);
|
||||
return process;
|
||||
}
|
||||
@@ -236,24 +232,86 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
|
||||
MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic);
|
||||
MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable);
|
||||
MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable);
|
||||
|
||||
// Clear instruction cache in CPU JIT
|
||||
Core::System::GetInstance().ArmInterface(0).ClearInstructionCache();
|
||||
Core::System::GetInstance().ArmInterface(1).ClearInstructionCache();
|
||||
Core::System::GetInstance().ArmInterface(2).ClearInstructionCache();
|
||||
Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
|
||||
}
|
||||
|
||||
ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
|
||||
return vm_manager.HeapAllocate(target, size, perms);
|
||||
if (target < vm_manager.GetHeapRegionBaseAddress() ||
|
||||
target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) {
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
if (heap_memory == nullptr) {
|
||||
// Initialize heap
|
||||
heap_memory = std::make_shared<std::vector<u8>>();
|
||||
heap_start = heap_end = target;
|
||||
} else {
|
||||
vm_manager.UnmapRange(heap_start, heap_end - heap_start);
|
||||
}
|
||||
|
||||
// If necessary, expand backing vector to cover new heap extents.
|
||||
if (target < heap_start) {
|
||||
heap_memory->insert(begin(*heap_memory), heap_start - target, 0);
|
||||
heap_start = target;
|
||||
vm_manager.RefreshMemoryBlockMappings(heap_memory.get());
|
||||
}
|
||||
if (target + size > heap_end) {
|
||||
heap_memory->insert(end(*heap_memory), (target + size) - heap_end, 0);
|
||||
heap_end = target + size;
|
||||
vm_manager.RefreshMemoryBlockMappings(heap_memory.get());
|
||||
}
|
||||
ASSERT(heap_end - heap_start == heap_memory->size());
|
||||
|
||||
CASCADE_RESULT(auto vma, vm_manager.MapMemoryBlock(target, heap_memory, target - heap_start,
|
||||
size, MemoryState::Heap));
|
||||
vm_manager.Reprotect(vma, perms);
|
||||
|
||||
heap_used = size;
|
||||
|
||||
return MakeResult<VAddr>(heap_end - size);
|
||||
}
|
||||
|
||||
ResultCode Process::HeapFree(VAddr target, u32 size) {
|
||||
return vm_manager.HeapFree(target, size);
|
||||
if (target < vm_manager.GetHeapRegionBaseAddress() ||
|
||||
target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) {
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode result = vm_manager.UnmapRange(target, size);
|
||||
if (result.IsError())
|
||||
return result;
|
||||
|
||||
heap_used -= size;
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) {
|
||||
return vm_manager.MirrorMemory(dst_addr, src_addr, size, state);
|
||||
ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
|
||||
auto vma = vm_manager.FindVMA(src_addr);
|
||||
|
||||
ASSERT_MSG(vma != vm_manager.vma_map.end(), "Invalid memory address");
|
||||
ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address");
|
||||
|
||||
// The returned VMA might be a bigger one encompassing the desired address.
|
||||
auto vma_offset = src_addr - vma->first;
|
||||
ASSERT_MSG(vma_offset + size <= vma->second.size,
|
||||
"Shared memory exceeds bounds of mapped block");
|
||||
|
||||
const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block;
|
||||
std::size_t backing_block_offset = vma->second.offset + vma_offset;
|
||||
|
||||
CASCADE_RESULT(auto new_vma,
|
||||
vm_manager.MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size,
|
||||
MemoryState::Mapped));
|
||||
// Protect mirror with permissions from old region
|
||||
vm_manager.Reprotect(new_vma, vma->second.permissions);
|
||||
// Remove permissions from old region
|
||||
vm_manager.Reprotect(vma, VMAPermission::None);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode Process::UnmapMemory(VAddr dst_addr, VAddr /*src_addr*/, u64 size) {
|
||||
|
||||
@@ -8,13 +8,11 @@
|
||||
#include <bitset>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/container/static_vector.hpp>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/vm_manager.h"
|
||||
@@ -120,8 +118,6 @@ struct CodeSet final {
|
||||
|
||||
class Process final : public Object {
|
||||
public:
|
||||
static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
|
||||
|
||||
static SharedPtr<Process> Create(KernelCore& kernel, std::string&& name);
|
||||
|
||||
std::string GetTypeName() const override {
|
||||
@@ -146,16 +142,6 @@ public:
|
||||
return vm_manager;
|
||||
}
|
||||
|
||||
/// Gets a reference to the process' handle table.
|
||||
HandleTable& GetHandleTable() {
|
||||
return handle_table;
|
||||
}
|
||||
|
||||
/// Gets a const reference to the process' handle table.
|
||||
const HandleTable& GetHandleTable() const {
|
||||
return handle_table;
|
||||
}
|
||||
|
||||
/// Gets the current status of the process
|
||||
ProcessStatus GetStatus() const {
|
||||
return status;
|
||||
@@ -205,21 +191,6 @@ public:
|
||||
return is_64bit_process;
|
||||
}
|
||||
|
||||
/// Gets the total running time of the process instance in ticks.
|
||||
u64 GetCPUTimeTicks() const {
|
||||
return total_process_running_time_ticks;
|
||||
}
|
||||
|
||||
/// Updates the total running time, adding the given ticks to it.
|
||||
void UpdateCPUTimeTicks(u64 ticks) {
|
||||
total_process_running_time_ticks += ticks;
|
||||
}
|
||||
|
||||
/// Gets 8 bytes of random data for svcGetInfo RandomEntropy
|
||||
u64 GetRandomEntropy(std::size_t index) const {
|
||||
return random_entropy.at(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads process-specifics configuration info with metadata provided
|
||||
* by an executable.
|
||||
@@ -259,8 +230,7 @@ public:
|
||||
ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
|
||||
ResultCode HeapFree(VAddr target, u32 size);
|
||||
|
||||
ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size,
|
||||
MemoryState state = MemoryState::Mapped);
|
||||
ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size);
|
||||
|
||||
ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);
|
||||
|
||||
@@ -301,6 +271,17 @@ private:
|
||||
u32 allowed_thread_priority_mask = 0xFFFFFFFF;
|
||||
u32 is_virtual_address_memory_enabled = 0;
|
||||
|
||||
// Memory used to back the allocations in the regular heap. A single vector is used to cover
|
||||
// the entire virtual address space extents that bound the allocations, including any holes.
|
||||
// This makes deallocation and reallocation of holes fast and keeps process memory contiguous
|
||||
// in the emulator address space, allowing Memory::GetPointer to be reasonably safe.
|
||||
std::shared_ptr<std::vector<u8>> heap_memory;
|
||||
|
||||
// The left/right bounds of the address space covered by heap_memory.
|
||||
VAddr heap_start = 0;
|
||||
VAddr heap_end = 0;
|
||||
u64 heap_used = 0;
|
||||
|
||||
/// The Thread Local Storage area is allocated as processes create threads,
|
||||
/// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part
|
||||
/// holds the TLS for a specific thread. This vector contains which parts are in use for each
|
||||
@@ -313,15 +294,6 @@ private:
|
||||
/// specified by metadata provided to the process during loading.
|
||||
bool is_64bit_process = true;
|
||||
|
||||
/// Total running time for the process in ticks.
|
||||
u64 total_process_running_time_ticks = 0;
|
||||
|
||||
/// Per-process handle table for storing created object handles in.
|
||||
HandleTable handle_table;
|
||||
|
||||
/// Random values for svcGetInfo RandomEntropy
|
||||
std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy;
|
||||
|
||||
std::string name;
|
||||
};
|
||||
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_cpu.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
@@ -36,10 +34,6 @@ Thread* Scheduler::GetCurrentThread() const {
|
||||
return current_thread.get();
|
||||
}
|
||||
|
||||
u64 Scheduler::GetLastContextSwitchTicks() const {
|
||||
return last_context_switch_time;
|
||||
}
|
||||
|
||||
Thread* Scheduler::PopNextReadyThread() {
|
||||
Thread* next = nullptr;
|
||||
Thread* thread = GetCurrentThread();
|
||||
@@ -60,10 +54,7 @@ Thread* Scheduler::PopNextReadyThread() {
|
||||
}
|
||||
|
||||
void Scheduler::SwitchContext(Thread* new_thread) {
|
||||
Thread* const previous_thread = GetCurrentThread();
|
||||
Process* const previous_process = Core::CurrentProcess();
|
||||
|
||||
UpdateLastContextSwitchTime(previous_thread, previous_process);
|
||||
Thread* previous_thread = GetCurrentThread();
|
||||
|
||||
// Save context for previous thread
|
||||
if (previous_thread) {
|
||||
@@ -87,6 +78,8 @@ void Scheduler::SwitchContext(Thread* new_thread) {
|
||||
// Cancel any outstanding wakeup events for this thread
|
||||
new_thread->CancelWakeupTimer();
|
||||
|
||||
auto* const previous_process = Core::CurrentProcess();
|
||||
|
||||
current_thread = new_thread;
|
||||
|
||||
ready_queue.remove(new_thread->GetPriority(), new_thread);
|
||||
@@ -109,22 +102,6 @@ void Scheduler::SwitchContext(Thread* new_thread) {
|
||||
}
|
||||
}
|
||||
|
||||
void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
|
||||
const u64 prev_switch_ticks = last_context_switch_time;
|
||||
const u64 most_recent_switch_ticks = CoreTiming::GetTicks();
|
||||
const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
|
||||
|
||||
if (thread != nullptr) {
|
||||
thread->UpdateCPUTimeTicks(update_ticks);
|
||||
}
|
||||
|
||||
if (process != nullptr) {
|
||||
process->UpdateCPUTimeTicks(update_ticks);
|
||||
}
|
||||
|
||||
last_context_switch_time = most_recent_switch_ticks;
|
||||
}
|
||||
|
||||
void Scheduler::Reschedule() {
|
||||
std::lock_guard<std::mutex> lock(scheduler_mutex);
|
||||
|
||||
@@ -180,69 +157,4 @@ void Scheduler::SetThreadPriority(Thread* thread, u32 priority) {
|
||||
ready_queue.prepare(priority);
|
||||
}
|
||||
|
||||
Thread* Scheduler::GetNextSuggestedThread(u32 core, u32 maximum_priority) const {
|
||||
std::lock_guard<std::mutex> lock(scheduler_mutex);
|
||||
|
||||
const u32 mask = 1U << core;
|
||||
return ready_queue.get_first_filter([mask, maximum_priority](Thread const* thread) {
|
||||
return (thread->GetAffinityMask() & mask) != 0 && thread->GetPriority() < maximum_priority;
|
||||
});
|
||||
}
|
||||
|
||||
void Scheduler::YieldWithoutLoadBalancing(Thread* thread) {
|
||||
ASSERT(thread != nullptr);
|
||||
// Avoid yielding if the thread isn't even running.
|
||||
ASSERT(thread->GetStatus() == ThreadStatus::Running);
|
||||
|
||||
// Sanity check that the priority is valid
|
||||
ASSERT(thread->GetPriority() < THREADPRIO_COUNT);
|
||||
|
||||
// Yield this thread -- sleep for zero time and force reschedule to different thread
|
||||
WaitCurrentThread_Sleep();
|
||||
GetCurrentThread()->WakeAfterDelay(0);
|
||||
}
|
||||
|
||||
void Scheduler::YieldWithLoadBalancing(Thread* thread) {
|
||||
ASSERT(thread != nullptr);
|
||||
const auto priority = thread->GetPriority();
|
||||
const auto core = static_cast<u32>(thread->GetProcessorID());
|
||||
|
||||
// Avoid yielding if the thread isn't even running.
|
||||
ASSERT(thread->GetStatus() == ThreadStatus::Running);
|
||||
|
||||
// Sanity check that the priority is valid
|
||||
ASSERT(priority < THREADPRIO_COUNT);
|
||||
|
||||
// Sleep for zero time to be able to force reschedule to different thread
|
||||
WaitCurrentThread_Sleep();
|
||||
GetCurrentThread()->WakeAfterDelay(0);
|
||||
|
||||
Thread* suggested_thread = nullptr;
|
||||
|
||||
// Search through all of the cpu cores (except this one) for a suggested thread.
|
||||
// Take the first non-nullptr one
|
||||
for (unsigned cur_core = 0; cur_core < Core::NUM_CPU_CORES; ++cur_core) {
|
||||
const auto res =
|
||||
Core::System::GetInstance().CpuCore(cur_core).Scheduler().GetNextSuggestedThread(
|
||||
core, priority);
|
||||
|
||||
// If scheduler provides a suggested thread
|
||||
if (res != nullptr) {
|
||||
// And its better than the current suggested thread (or is the first valid one)
|
||||
if (suggested_thread == nullptr ||
|
||||
suggested_thread->GetPriority() > res->GetPriority()) {
|
||||
suggested_thread = res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If a suggested thread was found, queue that for this core
|
||||
if (suggested_thread != nullptr)
|
||||
suggested_thread->ChangeCore(core, suggested_thread->GetAffinityMask());
|
||||
}
|
||||
|
||||
void Scheduler::YieldAndWaitForLoadBalancing(Thread* thread) {
|
||||
UNIMPLEMENTED_MSG("Wait for load balancing thread yield type is not implemented!");
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -17,8 +17,6 @@ class ARM_Interface;
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class Process;
|
||||
|
||||
class Scheduler final {
|
||||
public:
|
||||
explicit Scheduler(Core::ARM_Interface& cpu_core);
|
||||
@@ -33,9 +31,6 @@ public:
|
||||
/// Gets the current running thread
|
||||
Thread* GetCurrentThread() const;
|
||||
|
||||
/// Gets the timestamp for the last context switch in ticks.
|
||||
u64 GetLastContextSwitchTicks() const;
|
||||
|
||||
/// Adds a new thread to the scheduler
|
||||
void AddThread(SharedPtr<Thread> thread, u32 priority);
|
||||
|
||||
@@ -51,75 +46,6 @@ public:
|
||||
/// Sets the priority of a thread in the scheduler
|
||||
void SetThreadPriority(Thread* thread, u32 priority);
|
||||
|
||||
/// Gets the next suggested thread for load balancing
|
||||
Thread* GetNextSuggestedThread(u32 core, u32 minimum_priority) const;
|
||||
|
||||
/**
|
||||
* YieldWithoutLoadBalancing -- analogous to normal yield on a system
|
||||
* Moves the thread to the end of the ready queue for its priority, and then reschedules the
|
||||
* system to the new head of the queue.
|
||||
*
|
||||
* Example (Single Core -- but can be extrapolated to multi):
|
||||
* ready_queue[prio=0]: ThreadA, ThreadB, ThreadC (->exec order->)
|
||||
* Currently Running: ThreadR
|
||||
*
|
||||
* ThreadR calls YieldWithoutLoadBalancing
|
||||
*
|
||||
* ThreadR is moved to the end of ready_queue[prio=0]:
|
||||
* ready_queue[prio=0]: ThreadA, ThreadB, ThreadC, ThreadR (->exec order->)
|
||||
* Currently Running: Nothing
|
||||
*
|
||||
* System is rescheduled (ThreadA is popped off of queue):
|
||||
* ready_queue[prio=0]: ThreadB, ThreadC, ThreadR (->exec order->)
|
||||
* Currently Running: ThreadA
|
||||
*
|
||||
* If the queue is empty at time of call, no yielding occurs. This does not cross between cores
|
||||
* or priorities at all.
|
||||
*/
|
||||
void YieldWithoutLoadBalancing(Thread* thread);
|
||||
|
||||
/**
|
||||
* YieldWithLoadBalancing -- yield but with better selection of the new running thread
|
||||
* Moves the current thread to the end of the ready queue for its priority, then selects a
|
||||
* 'suggested thread' (a thread on a different core that could run on this core) from the
|
||||
* scheduler, changes its core, and reschedules the current core to that thread.
|
||||
*
|
||||
* Example (Dual Core -- can be extrapolated to Quad Core, this is just normal yield if it were
|
||||
* single core):
|
||||
* ready_queue[core=0][prio=0]: ThreadA, ThreadB (affinities not pictured as irrelevant
|
||||
* ready_queue[core=1][prio=0]: ThreadC[affinity=both], ThreadD[affinity=core1only]
|
||||
* Currently Running: ThreadQ on Core 0 || ThreadP on Core 1
|
||||
*
|
||||
* ThreadQ calls YieldWithLoadBalancing
|
||||
*
|
||||
* ThreadQ is moved to the end of ready_queue[core=0][prio=0]:
|
||||
* ready_queue[core=0][prio=0]: ThreadA, ThreadB
|
||||
* ready_queue[core=1][prio=0]: ThreadC[affinity=both], ThreadD[affinity=core1only]
|
||||
* Currently Running: ThreadQ on Core 0 || ThreadP on Core 1
|
||||
*
|
||||
* A list of suggested threads for each core is compiled
|
||||
* Suggested Threads: {ThreadC on Core 1}
|
||||
* If this were quad core (as the switch is), there could be between 0 and 3 threads in this
|
||||
* list. If there are more than one, the thread is selected by highest prio.
|
||||
*
|
||||
* ThreadC is core changed to Core 0:
|
||||
* ready_queue[core=0][prio=0]: ThreadC, ThreadA, ThreadB, ThreadQ
|
||||
* ready_queue[core=1][prio=0]: ThreadD
|
||||
* Currently Running: None on Core 0 || ThreadP on Core 1
|
||||
*
|
||||
* System is rescheduled (ThreadC is popped off of queue):
|
||||
* ready_queue[core=0][prio=0]: ThreadA, ThreadB, ThreadQ
|
||||
* ready_queue[core=1][prio=0]: ThreadD
|
||||
* Currently Running: ThreadC on Core 0 || ThreadP on Core 1
|
||||
*
|
||||
* If no suggested threads can be found this will behave just as normal yield. If there are
|
||||
* multiple candidates for the suggested thread on a core, the highest prio is taken.
|
||||
*/
|
||||
void YieldWithLoadBalancing(Thread* thread);
|
||||
|
||||
/// Currently unknown -- asserts as unimplemented on call
|
||||
void YieldAndWaitForLoadBalancing(Thread* thread);
|
||||
|
||||
/// Returns a list of all threads managed by the scheduler
|
||||
const std::vector<SharedPtr<Thread>>& GetThreadList() const {
|
||||
return thread_list;
|
||||
@@ -138,19 +64,6 @@ private:
|
||||
*/
|
||||
void SwitchContext(Thread* new_thread);
|
||||
|
||||
/**
|
||||
* Called on every context switch to update the internal timestamp
|
||||
* This also updates the running time ticks for the given thread and
|
||||
* process using the following difference:
|
||||
*
|
||||
* ticks += most_recent_ticks - last_context_switch_ticks
|
||||
*
|
||||
* The internal tick timestamp for the scheduler is simply the
|
||||
* most recent tick count retrieved. No special arithmetic is
|
||||
* applied to it.
|
||||
*/
|
||||
void UpdateLastContextSwitchTime(Thread* thread, Process* process);
|
||||
|
||||
/// Lists all thread ids that aren't deleted/etc.
|
||||
std::vector<SharedPtr<Thread>> thread_list;
|
||||
|
||||
@@ -160,7 +73,6 @@ private:
|
||||
SharedPtr<Thread> current_thread = nullptr;
|
||||
|
||||
Core::ARM_Interface& cpu_core;
|
||||
u64 last_context_switch_time = 0;
|
||||
|
||||
static std::mutex scheduler_mutex;
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@ ServerPort::~ServerPort() = default;
|
||||
|
||||
ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() {
|
||||
if (pending_sessions.empty()) {
|
||||
return ERR_NOT_FOUND;
|
||||
return ERR_NO_PENDING_SESSIONS;
|
||||
}
|
||||
|
||||
auto session = std::move(pending_sessions.back());
|
||||
@@ -28,7 +28,7 @@ ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() {
|
||||
|
||||
bool ServerPort::ShouldWait(Thread* thread) const {
|
||||
// If there are no pending sessions, we wait until a new one is added.
|
||||
return pending_sessions.empty();
|
||||
return pending_sessions.size() == 0;
|
||||
}
|
||||
|
||||
void ServerPort::Acquire(Thread* thread) {
|
||||
|
||||
@@ -63,7 +63,7 @@ void ServerSession::Acquire(Thread* thread) {
|
||||
}
|
||||
|
||||
ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) {
|
||||
auto* const domain_message_header = context.GetDomainMessageHeader();
|
||||
auto& domain_message_header = context.GetDomainMessageHeader();
|
||||
if (domain_message_header) {
|
||||
// Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs
|
||||
context.SetDomainRequestHandlers(domain_request_handlers);
|
||||
@@ -107,11 +107,12 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
|
||||
// similar.
|
||||
Kernel::HLERequestContext context(this);
|
||||
u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress());
|
||||
context.PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
|
||||
context.PopulateFromIncomingCommandBuffer(cmd_buf, *Core::CurrentProcess(),
|
||||
kernel.HandleTable());
|
||||
|
||||
ResultCode result = RESULT_SUCCESS;
|
||||
// If the session has been converted to a domain, handle the domain request
|
||||
if (IsDomain() && context.HasDomainMessageHeader()) {
|
||||
if (IsDomain() && context.GetDomainMessageHeader()) {
|
||||
result = HandleDomainSyncRequest(context);
|
||||
// If there is no domain header, the regular session handler is used
|
||||
} else if (hle_handler != nullptr) {
|
||||
|
||||
@@ -80,19 +80,20 @@ SharedPtr<SharedMemory> SharedMemory::CreateForApplet(
|
||||
|
||||
ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions,
|
||||
MemoryPermission other_permissions) {
|
||||
const MemoryPermission own_other_permissions =
|
||||
|
||||
MemoryPermission own_other_permissions =
|
||||
target_process == owner_process ? this->permissions : this->other_permissions;
|
||||
|
||||
// Automatically allocated memory blocks can only be mapped with other_permissions = DontCare
|
||||
if (base_address == 0 && other_permissions != MemoryPermission::DontCare) {
|
||||
return ERR_INVALID_MEMORY_PERMISSIONS;
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
// Error out if the requested permissions don't match what the creator process allows.
|
||||
if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) {
|
||||
LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match",
|
||||
GetObjectId(), address, name);
|
||||
return ERR_INVALID_MEMORY_PERMISSIONS;
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
// Error out if the provided permissions are not compatible with what the creator process needs.
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Kernel {
|
||||
namespace {
|
||||
@@ -123,48 +122,6 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) {
|
||||
LOG_TRACE(Kernel_SVC, "called, addr=0x{:X}, size=0x{:X}, prot=0x{:X}", addr, size, prot);
|
||||
|
||||
if (!Common::Is4KBAligned(addr)) {
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
if (size == 0 || !Common::Is4KBAligned(size)) {
|
||||
return ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
if (!IsValidAddressRange(addr, size)) {
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
const auto permission = static_cast<MemoryPermission>(prot);
|
||||
if (permission != MemoryPermission::None && permission != MemoryPermission::Read &&
|
||||
permission != MemoryPermission::ReadWrite) {
|
||||
return ERR_INVALID_MEMORY_PERMISSIONS;
|
||||
}
|
||||
|
||||
auto* const current_process = Core::CurrentProcess();
|
||||
auto& vm_manager = current_process->VMManager();
|
||||
|
||||
if (!IsInsideAddressSpace(vm_manager, addr, size)) {
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
const VMManager::VMAHandle iter = vm_manager.FindVMA(addr);
|
||||
if (iter == vm_manager.vma_map.end()) {
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
LOG_WARNING(Kernel_SVC, "Uniformity check on protected memory is not implemented.");
|
||||
// TODO: Performs a uniformity check to make sure only protected memory is changed (it doesn't
|
||||
// make sense to allow changing permissions on kernel memory itself, etc).
|
||||
|
||||
const auto converted_permissions = SharedMemory::ConvertPermissions(permission);
|
||||
|
||||
return vm_manager.ReprotectRange(addr, size, converted_permissions);
|
||||
}
|
||||
|
||||
static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state1) {
|
||||
LOG_WARNING(Kernel_SVC,
|
||||
"(STUBBED) called, addr=0x{:X}, size=0x{:X}, state0=0x{:X}, state1=0x{:X}", addr,
|
||||
@@ -214,7 +171,7 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address
|
||||
// Read 1 char beyond the max allowed port name to detect names that are too long.
|
||||
std::string port_name = Memory::ReadCString(port_name_address, PortNameMaxLength + 1);
|
||||
if (port_name.size() > PortNameMaxLength) {
|
||||
return ERR_OUT_OF_RANGE;
|
||||
return ERR_PORT_NAME_TOO_LONG;
|
||||
}
|
||||
|
||||
LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
|
||||
@@ -232,15 +189,14 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address
|
||||
CASCADE_RESULT(client_session, client_port->Connect());
|
||||
|
||||
// Return the client session
|
||||
auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
CASCADE_RESULT(*out_handle, handle_table.Create(client_session));
|
||||
CASCADE_RESULT(*out_handle, kernel.HandleTable().Create(client_session));
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// Makes a blocking IPC call to an OS service.
|
||||
static ResultCode SendSyncRequest(Handle handle) {
|
||||
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
SharedPtr<ClientSession> session = handle_table.Get<ClientSession>(handle);
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
SharedPtr<ClientSession> session = kernel.HandleTable().Get<ClientSession>(handle);
|
||||
if (!session) {
|
||||
LOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle);
|
||||
return ERR_INVALID_HANDLE;
|
||||
@@ -259,8 +215,8 @@ static ResultCode SendSyncRequest(Handle handle) {
|
||||
static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) {
|
||||
LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
|
||||
|
||||
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle);
|
||||
if (!thread) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
@@ -273,8 +229,8 @@ static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) {
|
||||
static ResultCode GetProcessId(u32* process_id, Handle process_handle) {
|
||||
LOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle);
|
||||
|
||||
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
const SharedPtr<Process> process = handle_table.Get<Process>(process_handle);
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
const SharedPtr<Process> process = kernel.HandleTable().Get<Process>(process_handle);
|
||||
if (!process) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
@@ -310,19 +266,18 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
|
||||
|
||||
static constexpr u64 MaxHandles = 0x40;
|
||||
|
||||
if (handle_count > MaxHandles) {
|
||||
return ERR_OUT_OF_RANGE;
|
||||
}
|
||||
if (handle_count > MaxHandles)
|
||||
return ResultCode(ErrorModule::Kernel, ErrCodes::TooLarge);
|
||||
|
||||
auto* const thread = GetCurrentThread();
|
||||
|
||||
using ObjectPtr = Thread::ThreadWaitObjects::value_type;
|
||||
Thread::ThreadWaitObjects objects(handle_count);
|
||||
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
|
||||
for (u64 i = 0; i < handle_count; ++i) {
|
||||
const Handle handle = Memory::Read32(handles_address + i * sizeof(Handle));
|
||||
const auto object = handle_table.Get<WaitObject>(handle);
|
||||
const auto object = kernel.HandleTable().Get<WaitObject>(handle);
|
||||
|
||||
if (object == nullptr) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
@@ -370,14 +325,15 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
|
||||
static ResultCode CancelSynchronization(Handle thread_handle) {
|
||||
LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle);
|
||||
|
||||
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle);
|
||||
if (!thread) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny);
|
||||
thread->SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED);
|
||||
thread->SetWaitSynchronizationResult(
|
||||
ResultCode(ErrorModule::Kernel, ErrCodes::SynchronizationCanceled));
|
||||
thread->ResumeFromWait();
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
@@ -398,7 +354,7 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr,
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
auto& handle_table = Core::System::GetInstance().Kernel().HandleTable();
|
||||
return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle,
|
||||
requesting_thread_handle);
|
||||
}
|
||||
@@ -418,19 +374,9 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
|
||||
return Mutex::Release(mutex_addr);
|
||||
}
|
||||
|
||||
enum class BreakType : u32 {
|
||||
Panic = 0,
|
||||
AssertionFailed = 1,
|
||||
PreNROLoad = 3,
|
||||
PostNROLoad = 4,
|
||||
PreNROUnload = 5,
|
||||
PostNROUnload = 6,
|
||||
};
|
||||
|
||||
struct BreakReason {
|
||||
union {
|
||||
u32 raw;
|
||||
BitField<0, 30, BreakType> break_type;
|
||||
BitField<31, 1, u32> signal_debugger;
|
||||
};
|
||||
};
|
||||
@@ -438,80 +384,16 @@ struct BreakReason {
|
||||
/// Break program execution
|
||||
static void Break(u32 reason, u64 info1, u64 info2) {
|
||||
BreakReason break_reason{reason};
|
||||
bool has_dumped_buffer{};
|
||||
|
||||
const auto handle_debug_buffer = [&](VAddr addr, u64 sz) {
|
||||
if (sz == 0 || addr == 0 || has_dumped_buffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This typically is an error code so we're going to assume this is the case
|
||||
if (sz == sizeof(u32)) {
|
||||
LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", Memory::Read32(addr));
|
||||
} else {
|
||||
// We don't know what's in here so we'll hexdump it
|
||||
std::vector<u8> debug_buffer(sz);
|
||||
Memory::ReadBlock(addr, debug_buffer.data(), sz);
|
||||
std::string hexdump;
|
||||
for (std::size_t i = 0; i < debug_buffer.size(); i++) {
|
||||
hexdump += fmt::format("{:02X} ", debug_buffer[i]);
|
||||
if (i != 0 && i % 16 == 0) {
|
||||
hexdump += '\n';
|
||||
}
|
||||
}
|
||||
LOG_CRITICAL(Debug_Emulated, "debug_buffer=\n{}", hexdump);
|
||||
}
|
||||
has_dumped_buffer = true;
|
||||
};
|
||||
switch (break_reason.break_type) {
|
||||
case BreakType::Panic:
|
||||
LOG_CRITICAL(Debug_Emulated, "Signalling debugger, PANIC! info1=0x{:016X}, info2=0x{:016X}",
|
||||
info1, info2);
|
||||
handle_debug_buffer(info1, info2);
|
||||
break;
|
||||
case BreakType::AssertionFailed:
|
||||
LOG_CRITICAL(Debug_Emulated,
|
||||
"Signalling debugger, Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
|
||||
info1, info2);
|
||||
handle_debug_buffer(info1, info2);
|
||||
break;
|
||||
case BreakType::PreNROLoad:
|
||||
LOG_WARNING(
|
||||
if (break_reason.signal_debugger) {
|
||||
LOG_ERROR(
|
||||
Debug_Emulated,
|
||||
"Signalling debugger, Attempting to load an NRO at 0x{:016X} with size 0x{:016X}",
|
||||
info1, info2);
|
||||
break;
|
||||
case BreakType::PostNROLoad:
|
||||
LOG_WARNING(Debug_Emulated,
|
||||
"Signalling debugger, Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
|
||||
info2);
|
||||
break;
|
||||
case BreakType::PreNROUnload:
|
||||
LOG_WARNING(
|
||||
Debug_Emulated,
|
||||
"Signalling debugger, Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}",
|
||||
info1, info2);
|
||||
break;
|
||||
case BreakType::PostNROUnload:
|
||||
LOG_WARNING(Debug_Emulated,
|
||||
"Signalling debugger, Unloaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
|
||||
info2);
|
||||
break;
|
||||
default:
|
||||
LOG_WARNING(
|
||||
Debug_Emulated,
|
||||
"Signalling debugger, Unknown break reason {}, info1=0x{:016X}, info2=0x{:016X}",
|
||||
static_cast<u32>(break_reason.break_type.Value()), info1, info2);
|
||||
handle_debug_buffer(info1, info2);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!break_reason.signal_debugger) {
|
||||
"Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
|
||||
reason, info1, info2);
|
||||
} else {
|
||||
LOG_CRITICAL(
|
||||
Debug_Emulated,
|
||||
"Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
|
||||
reason, info1, info2);
|
||||
handle_debug_buffer(info1, info2);
|
||||
ASSERT(false);
|
||||
|
||||
Core::CurrentProcess()->PrepareForTermination();
|
||||
@@ -538,37 +420,6 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
|
||||
info_sub_id, handle);
|
||||
|
||||
enum class GetInfoType : u64 {
|
||||
// 1.0.0+
|
||||
AllowedCpuIdBitmask = 0,
|
||||
AllowedThreadPrioBitmask = 1,
|
||||
MapRegionBaseAddr = 2,
|
||||
MapRegionSize = 3,
|
||||
HeapRegionBaseAddr = 4,
|
||||
HeapRegionSize = 5,
|
||||
TotalMemoryUsage = 6,
|
||||
TotalHeapUsage = 7,
|
||||
IsCurrentProcessBeingDebugged = 8,
|
||||
ResourceHandleLimit = 9,
|
||||
IdleTickCount = 10,
|
||||
RandomEntropy = 11,
|
||||
PerformanceCounter = 0xF0000002,
|
||||
// 2.0.0+
|
||||
ASLRRegionBaseAddr = 12,
|
||||
ASLRRegionSize = 13,
|
||||
NewMapRegionBaseAddr = 14,
|
||||
NewMapRegionSize = 15,
|
||||
// 3.0.0+
|
||||
IsVirtualAddressMemoryEnabled = 16,
|
||||
PersonalMmHeapUsage = 17,
|
||||
TitleId = 18,
|
||||
// 4.0.0+
|
||||
PrivilegedProcessId = 19,
|
||||
// 5.0.0+
|
||||
UserExceptionContextAddr = 20,
|
||||
ThreadTickCount = 0xF0000002,
|
||||
};
|
||||
|
||||
const auto* current_process = Core::CurrentProcess();
|
||||
const auto& vm_manager = current_process->VMManager();
|
||||
|
||||
@@ -601,16 +452,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
*result = 0;
|
||||
break;
|
||||
case GetInfoType::RandomEntropy:
|
||||
if (handle != 0) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (info_sub_id >= Process::RANDOM_ENTROPY_SIZE) {
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
*result = current_process->GetRandomEntropy(info_sub_id);
|
||||
return RESULT_SUCCESS;
|
||||
*result = 0;
|
||||
break;
|
||||
case GetInfoType::ASLRRegionBaseAddr:
|
||||
*result = vm_manager.GetASLRRegionBaseAddress();
|
||||
@@ -640,36 +482,6 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
"(STUBBED) Attempted to query user exception context address, returned 0");
|
||||
*result = 0;
|
||||
break;
|
||||
case GetInfoType::ThreadTickCount: {
|
||||
constexpr u64 num_cpus = 4;
|
||||
if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
const auto thread =
|
||||
current_process->GetHandleTable().Get<Thread>(static_cast<Handle>(handle));
|
||||
if (!thread) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
const auto& system = Core::System::GetInstance();
|
||||
const auto& scheduler = system.CurrentScheduler();
|
||||
const auto* const current_thread = scheduler.GetCurrentThread();
|
||||
const bool same_thread = current_thread == thread;
|
||||
|
||||
const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTicks();
|
||||
u64 out_ticks = 0;
|
||||
if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
|
||||
const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks();
|
||||
|
||||
out_ticks = thread_ticks + (CoreTiming::GetTicks() - prev_ctx_ticks);
|
||||
} else if (same_thread && info_sub_id == system.CurrentCoreIndex()) {
|
||||
out_ticks = CoreTiming::GetTicks() - prev_ctx_ticks;
|
||||
}
|
||||
|
||||
*result = out_ticks;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
@@ -687,12 +499,13 @@ static ResultCode SetThreadActivity(Handle handle, u32 unknown) {
|
||||
static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
|
||||
LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle);
|
||||
|
||||
const auto* current_process = Core::CurrentProcess();
|
||||
const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle);
|
||||
if (!thread) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
const auto* current_process = Core::CurrentProcess();
|
||||
if (thread->GetOwnerProcess() != current_process) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
@@ -718,11 +531,10 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
|
||||
|
||||
/// Gets the priority for the specified thread
|
||||
static ResultCode GetThreadPriority(u32* priority, Handle handle) {
|
||||
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
const SharedPtr<Thread> thread = handle_table.Get<Thread>(handle);
|
||||
if (!thread) {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle);
|
||||
if (!thread)
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
*priority = thread->GetPriority();
|
||||
return RESULT_SUCCESS;
|
||||
@@ -734,18 +546,16 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
|
||||
return ERR_INVALID_THREAD_PRIORITY;
|
||||
}
|
||||
|
||||
const auto* const current_process = Core::CurrentProcess();
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle);
|
||||
if (!thread)
|
||||
return ERR_INVALID_HANDLE;
|
||||
|
||||
// Note: The kernel uses the current process's resource limit instead of
|
||||
// the one from the thread owner's resource limit.
|
||||
const ResourceLimit& resource_limit = current_process->GetResourceLimit();
|
||||
const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit();
|
||||
if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
|
||||
return ERR_INVALID_THREAD_PRIORITY;
|
||||
}
|
||||
|
||||
SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
|
||||
if (!thread) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
return ERR_NOT_AUTHORIZED;
|
||||
}
|
||||
|
||||
thread->SetPriority(priority);
|
||||
@@ -774,10 +584,6 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
|
||||
return ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
if (!IsValidAddressRange(addr, size)) {
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
const auto permissions_type = static_cast<MemoryPermission>(permissions);
|
||||
if (permissions_type != MemoryPermission::Read &&
|
||||
permissions_type != MemoryPermission::ReadWrite) {
|
||||
@@ -785,18 +591,14 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
|
||||
return ERR_INVALID_MEMORY_PERMISSIONS;
|
||||
}
|
||||
|
||||
auto* const current_process = Core::CurrentProcess();
|
||||
auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle);
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle);
|
||||
if (!shared_memory) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
const auto& vm_manager = current_process->VMManager();
|
||||
if (!vm_manager.IsWithinASLRRegion(addr, size)) {
|
||||
return ERR_INVALID_MEMORY_RANGE;
|
||||
}
|
||||
|
||||
return shared_memory->Map(current_process, addr, permissions_type, MemoryPermission::DontCare);
|
||||
return shared_memory->Map(Core::CurrentProcess(), addr, permissions_type,
|
||||
MemoryPermission::DontCare);
|
||||
}
|
||||
|
||||
static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) {
|
||||
@@ -811,35 +613,24 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
|
||||
return ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
if (!IsValidAddressRange(addr, size)) {
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle);
|
||||
|
||||
auto* const current_process = Core::CurrentProcess();
|
||||
auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle);
|
||||
if (!shared_memory) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
const auto& vm_manager = current_process->VMManager();
|
||||
if (!vm_manager.IsWithinASLRRegion(addr, size)) {
|
||||
return ERR_INVALID_MEMORY_RANGE;
|
||||
}
|
||||
|
||||
return shared_memory->Unmap(current_process, addr);
|
||||
return shared_memory->Unmap(Core::CurrentProcess(), addr);
|
||||
}
|
||||
|
||||
/// Query process memory
|
||||
static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/,
|
||||
Handle process_handle, u64 addr) {
|
||||
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
SharedPtr<Process> process = handle_table.Get<Process>(process_handle);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
SharedPtr<Process> process = kernel.HandleTable().Get<Process>(process_handle);
|
||||
if (!process) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
auto vma = process->VMManager().FindVMA(addr);
|
||||
memory_info->attributes = 0;
|
||||
if (vma == process->VMManager().vma_map.end()) {
|
||||
if (vma == Core::CurrentProcess()->VMManager().vma_map.end()) {
|
||||
memory_info->base_address = 0;
|
||||
memory_info->permission = static_cast<u32>(VMAPermission::None);
|
||||
memory_info->size = 0;
|
||||
@@ -880,19 +671,20 @@ static void ExitProcess() {
|
||||
/// Creates a new thread
|
||||
static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top,
|
||||
u32 priority, s32 processor_id) {
|
||||
std::string name = fmt::format("thread-{:X}", entry_point);
|
||||
|
||||
if (priority > THREADPRIO_LOWEST) {
|
||||
return ERR_INVALID_THREAD_PRIORITY;
|
||||
}
|
||||
|
||||
auto* const current_process = Core::CurrentProcess();
|
||||
const ResourceLimit& resource_limit = current_process->GetResourceLimit();
|
||||
const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit();
|
||||
if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
|
||||
return ERR_INVALID_THREAD_PRIORITY;
|
||||
return ERR_NOT_AUTHORIZED;
|
||||
}
|
||||
|
||||
if (processor_id == THREADPROCESSORID_DEFAULT) {
|
||||
// Set the target CPU to the one specified in the process' exheader.
|
||||
processor_id = current_process->GetDefaultProcessorID();
|
||||
processor_id = Core::CurrentProcess()->GetDefaultProcessorID();
|
||||
ASSERT(processor_id != THREADPROCESSORID_DEFAULT);
|
||||
}
|
||||
|
||||
@@ -907,13 +699,11 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
|
||||
return ERR_INVALID_PROCESSOR_ID;
|
||||
}
|
||||
|
||||
const std::string name = fmt::format("thread-{:X}", entry_point);
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
CASCADE_RESULT(SharedPtr<Thread> thread,
|
||||
Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top,
|
||||
*current_process));
|
||||
|
||||
const auto new_guest_handle = current_process->GetHandleTable().Create(thread);
|
||||
*Core::CurrentProcess()));
|
||||
const auto new_guest_handle = kernel.HandleTable().Create(thread);
|
||||
if (new_guest_handle.Failed()) {
|
||||
return new_guest_handle.Code();
|
||||
}
|
||||
@@ -934,8 +724,8 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
|
||||
static ResultCode StartThread(Handle thread_handle) {
|
||||
LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
|
||||
|
||||
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle);
|
||||
if (!thread) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
@@ -960,38 +750,18 @@ static void ExitThread() {
|
||||
static void SleepThread(s64 nanoseconds) {
|
||||
LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds);
|
||||
|
||||
enum class SleepType : s64 {
|
||||
YieldWithoutLoadBalancing = 0,
|
||||
YieldWithLoadBalancing = -1,
|
||||
YieldAndWaitForLoadBalancing = -2,
|
||||
};
|
||||
// Don't attempt to yield execution if there are no available threads to run,
|
||||
// this way we avoid a useless reschedule to the idle thread.
|
||||
if (nanoseconds == 0 && !Core::System::GetInstance().CurrentScheduler().HaveReadyThreads())
|
||||
return;
|
||||
|
||||
if (nanoseconds <= 0) {
|
||||
auto& scheduler{Core::System::GetInstance().CurrentScheduler()};
|
||||
switch (static_cast<SleepType>(nanoseconds)) {
|
||||
case SleepType::YieldWithoutLoadBalancing:
|
||||
scheduler.YieldWithoutLoadBalancing(GetCurrentThread());
|
||||
break;
|
||||
case SleepType::YieldWithLoadBalancing:
|
||||
scheduler.YieldWithLoadBalancing(GetCurrentThread());
|
||||
break;
|
||||
case SleepType::YieldAndWaitForLoadBalancing:
|
||||
scheduler.YieldAndWaitForLoadBalancing(GetCurrentThread());
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds);
|
||||
}
|
||||
} else {
|
||||
// Sleep current thread and check for next thread to schedule
|
||||
WaitCurrentThread_Sleep();
|
||||
// Sleep current thread and check for next thread to schedule
|
||||
WaitCurrentThread_Sleep();
|
||||
|
||||
// Create an event to wake the thread up after the specified nanosecond delay has passed
|
||||
GetCurrentThread()->WakeAfterDelay(nanoseconds);
|
||||
}
|
||||
// Create an event to wake the thread up after the specified nanosecond delay has passed
|
||||
GetCurrentThread()->WakeAfterDelay(nanoseconds);
|
||||
|
||||
// Reschedule all CPU cores
|
||||
for (std::size_t i = 0; i < Core::NUM_CPU_CORES; ++i)
|
||||
Core::System::GetInstance().CpuCore(i).PrepareReschedule();
|
||||
Core::System::GetInstance().PrepareReschedule();
|
||||
}
|
||||
|
||||
/// Wait process wide key atomic
|
||||
@@ -1002,8 +772,8 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var
|
||||
"called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}",
|
||||
mutex_addr, condition_variable_addr, thread_handle, nano_seconds);
|
||||
|
||||
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle);
|
||||
ASSERT(thread);
|
||||
|
||||
CASCADE_CODE(Mutex::Release(mutex_addr));
|
||||
@@ -1114,9 +884,9 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
|
||||
mutex_val | Mutex::MutexHasWaitersFlag));
|
||||
|
||||
// The mutex is already owned by some other thread, make this thread wait on it.
|
||||
const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
|
||||
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
auto owner = handle_table.Get<Thread>(owner_handle);
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
|
||||
auto owner = kernel.HandleTable().Get<Thread>(owner_handle);
|
||||
ASSERT(owner);
|
||||
ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
|
||||
thread->InvalidateWakeupCallback();
|
||||
@@ -1195,16 +965,16 @@ static u64 GetSystemTick() {
|
||||
static ResultCode CloseHandle(Handle handle) {
|
||||
LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);
|
||||
|
||||
auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
return handle_table.Close(handle);
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
return kernel.HandleTable().Close(handle);
|
||||
}
|
||||
|
||||
/// Reset an event
|
||||
static ResultCode ResetSignal(Handle handle) {
|
||||
LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
|
||||
LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle);
|
||||
|
||||
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
auto event = handle_table.Get<Event>(handle);
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
auto event = kernel.HandleTable().Get<Event>(handle);
|
||||
|
||||
ASSERT(event != nullptr);
|
||||
|
||||
@@ -1223,8 +993,8 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32
|
||||
static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) {
|
||||
LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
|
||||
|
||||
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle);
|
||||
if (!thread) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
@@ -1239,8 +1009,8 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
|
||||
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:16X}, core=0x{:X}", thread_handle,
|
||||
mask, core);
|
||||
|
||||
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle);
|
||||
if (!thread) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
@@ -1256,7 +1026,7 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
|
||||
}
|
||||
|
||||
if (mask == 0) {
|
||||
return ERR_INVALID_COMBINATION;
|
||||
return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination);
|
||||
}
|
||||
|
||||
/// This value is used to only change the affinity mask without changing the current ideal core.
|
||||
@@ -1265,12 +1035,12 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
|
||||
if (core == OnlyChangeMask) {
|
||||
core = thread->GetIdealCore();
|
||||
} else if (core >= Core::NUM_CPU_CORES && core != static_cast<u32>(-1)) {
|
||||
return ERR_INVALID_PROCESSOR_ID;
|
||||
return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidProcessorId);
|
||||
}
|
||||
|
||||
// Error out if the input core isn't enabled in the input mask.
|
||||
if (core < Core::NUM_CPU_CORES && (mask & (1ull << core)) == 0) {
|
||||
return ERR_INVALID_COMBINATION;
|
||||
return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination);
|
||||
}
|
||||
|
||||
thread->ChangeCore(core, mask);
|
||||
@@ -1301,7 +1071,7 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
|
||||
}
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
auto& handle_table = kernel.HandleTable();
|
||||
auto shared_mem_handle =
|
||||
SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size,
|
||||
local_perms, remote_perms);
|
||||
@@ -1313,12 +1083,10 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
|
||||
static ResultCode ClearEvent(Handle handle) {
|
||||
LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle);
|
||||
|
||||
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
SharedPtr<Event> evt = handle_table.Get<Event>(handle);
|
||||
if (evt == nullptr) {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
SharedPtr<Event> evt = kernel.HandleTable().Get<Event>(handle);
|
||||
if (evt == nullptr)
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
evt->Clear();
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
@@ -1331,8 +1099,8 @@ static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) {
|
||||
Status,
|
||||
};
|
||||
|
||||
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
const auto process = handle_table.Get<Process>(process_handle);
|
||||
const auto& kernel = Core::System::GetInstance().Kernel();
|
||||
const auto process = kernel.HandleTable().Get<Process>(process_handle);
|
||||
if (!process) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
@@ -1359,7 +1127,7 @@ struct FunctionDef {
|
||||
static const FunctionDef SVC_Table[] = {
|
||||
{0x00, nullptr, "Unknown"},
|
||||
{0x01, SvcWrap<SetHeapSize>, "SetHeapSize"},
|
||||
{0x02, SvcWrap<SetMemoryPermission>, "SetMemoryPermission"},
|
||||
{0x02, nullptr, "SetMemoryPermission"},
|
||||
{0x03, SvcWrap<SetMemoryAttribute>, "SetMemoryAttribute"},
|
||||
{0x04, SvcWrap<MapMemory>, "MapMemory"},
|
||||
{0x05, SvcWrap<UnmapMemory>, "UnmapMemory"},
|
||||
|
||||
@@ -24,6 +24,37 @@ struct PageInfo {
|
||||
u64 flags;
|
||||
};
|
||||
|
||||
/// Values accepted by svcGetInfo
|
||||
enum class GetInfoType : u64 {
|
||||
// 1.0.0+
|
||||
AllowedCpuIdBitmask = 0,
|
||||
AllowedThreadPrioBitmask = 1,
|
||||
MapRegionBaseAddr = 2,
|
||||
MapRegionSize = 3,
|
||||
HeapRegionBaseAddr = 4,
|
||||
HeapRegionSize = 5,
|
||||
TotalMemoryUsage = 6,
|
||||
TotalHeapUsage = 7,
|
||||
IsCurrentProcessBeingDebugged = 8,
|
||||
ResourceHandleLimit = 9,
|
||||
IdleTickCount = 10,
|
||||
RandomEntropy = 11,
|
||||
PerformanceCounter = 0xF0000002,
|
||||
// 2.0.0+
|
||||
ASLRRegionBaseAddr = 12,
|
||||
ASLRRegionSize = 13,
|
||||
NewMapRegionBaseAddr = 14,
|
||||
NewMapRegionSize = 15,
|
||||
// 3.0.0+
|
||||
IsVirtualAddressMemoryEnabled = 16,
|
||||
PersonalMmHeapUsage = 17,
|
||||
TitleId = 18,
|
||||
// 4.0.0+
|
||||
PrivilegedProcessId = 19,
|
||||
// 5.0.0+
|
||||
UserExceptionContextAddr = 20,
|
||||
};
|
||||
|
||||
void CallSVC(u32 immediate);
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -121,11 +121,6 @@ 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(
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/range/algorithm_ext/erase.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
@@ -94,7 +94,7 @@ void Thread::CancelWakeupTimer() {
|
||||
CoreTiming::UnscheduleEventThreadsafe(kernel.ThreadWakeupCallbackEventType(), callback_handle);
|
||||
}
|
||||
|
||||
static std::optional<s32> GetNextProcessorId(u64 mask) {
|
||||
static boost::optional<s32> GetNextProcessorId(u64 mask) {
|
||||
for (s32 index = 0; index < Core::NUM_CPU_CORES; ++index) {
|
||||
if (mask & (1ULL << index)) {
|
||||
if (!Core::System::GetInstance().Scheduler(index).GetCurrentThread()) {
|
||||
@@ -142,7 +142,36 @@ void Thread::ResumeFromWait() {
|
||||
|
||||
status = ThreadStatus::Ready;
|
||||
|
||||
ChangeScheduler();
|
||||
boost::optional<s32> new_processor_id = GetNextProcessorId(affinity_mask);
|
||||
if (!new_processor_id) {
|
||||
new_processor_id = processor_id;
|
||||
}
|
||||
if (ideal_core != -1 &&
|
||||
Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {
|
||||
new_processor_id = ideal_core;
|
||||
}
|
||||
|
||||
ASSERT(*new_processor_id < 4);
|
||||
|
||||
// Add thread to new core's scheduler
|
||||
auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id);
|
||||
|
||||
if (*new_processor_id != processor_id) {
|
||||
// Remove thread from previous core's scheduler
|
||||
scheduler->RemoveThread(this);
|
||||
next_scheduler->AddThread(this, current_priority);
|
||||
}
|
||||
|
||||
processor_id = *new_processor_id;
|
||||
|
||||
// If the thread was ready, unschedule from the previous core and schedule on the new core
|
||||
scheduler->UnscheduleThread(this, current_priority);
|
||||
next_scheduler->ScheduleThread(this, current_priority);
|
||||
|
||||
// Change thread's scheduler
|
||||
scheduler = next_scheduler;
|
||||
|
||||
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -237,7 +266,7 @@ SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 pri
|
||||
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
|
||||
|
||||
// Register 1 must be a handle to the main thread
|
||||
const Handle guest_handle = owner_process.GetHandleTable().Create(thread).Unwrap();
|
||||
const Handle guest_handle = kernel.HandleTable().Create(thread).Unwrap();
|
||||
thread->SetGuestHandle(guest_handle);
|
||||
thread->GetContext().cpu_registers[1] = guest_handle;
|
||||
|
||||
@@ -335,45 +364,42 @@ void Thread::UpdatePriority() {
|
||||
void Thread::ChangeCore(u32 core, u64 mask) {
|
||||
ideal_core = core;
|
||||
affinity_mask = mask;
|
||||
ChangeScheduler();
|
||||
}
|
||||
|
||||
void Thread::ChangeScheduler() {
|
||||
if (status != ThreadStatus::Ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& system = Core::System::GetInstance();
|
||||
std::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)};
|
||||
boost::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)};
|
||||
|
||||
if (!new_processor_id) {
|
||||
new_processor_id = processor_id;
|
||||
}
|
||||
if (ideal_core != -1 && system.Scheduler(ideal_core).GetCurrentThread() == nullptr) {
|
||||
if (ideal_core != -1 &&
|
||||
Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {
|
||||
new_processor_id = ideal_core;
|
||||
}
|
||||
|
||||
ASSERT(*new_processor_id < 4);
|
||||
|
||||
// Add thread to new core's scheduler
|
||||
auto& next_scheduler = system.Scheduler(*new_processor_id);
|
||||
auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id);
|
||||
|
||||
if (*new_processor_id != processor_id) {
|
||||
// Remove thread from previous core's scheduler
|
||||
scheduler->RemoveThread(this);
|
||||
next_scheduler.AddThread(this, current_priority);
|
||||
next_scheduler->AddThread(this, current_priority);
|
||||
}
|
||||
|
||||
processor_id = *new_processor_id;
|
||||
|
||||
// If the thread was ready, unschedule from the previous core and schedule on the new core
|
||||
scheduler->UnscheduleThread(this, current_priority);
|
||||
next_scheduler.ScheduleThread(this, current_priority);
|
||||
next_scheduler->ScheduleThread(this, current_priority);
|
||||
|
||||
// Change thread's scheduler
|
||||
scheduler = &next_scheduler;
|
||||
scheduler = next_scheduler;
|
||||
|
||||
system.CpuCore(processor_id).PrepareReschedule();
|
||||
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
|
||||
}
|
||||
|
||||
bool Thread::AllWaitObjectsReady() {
|
||||
|
||||
@@ -26,7 +26,6 @@ enum ThreadPriority : u32 {
|
||||
THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps
|
||||
THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps
|
||||
THREADPRIO_LOWEST = 63, ///< Lowest thread priority
|
||||
THREADPRIO_COUNT = 64, ///< Total number of possible thread priorities.
|
||||
};
|
||||
|
||||
enum ThreadProcessorId : s32 {
|
||||
@@ -259,14 +258,6 @@ public:
|
||||
return last_running_ticks;
|
||||
}
|
||||
|
||||
u64 GetTotalCPUTimeTicks() const {
|
||||
return total_cpu_time_ticks;
|
||||
}
|
||||
|
||||
void UpdateCPUTimeTicks(u64 ticks) {
|
||||
total_cpu_time_ticks += ticks;
|
||||
}
|
||||
|
||||
s32 GetProcessorID() const {
|
||||
return processor_id;
|
||||
}
|
||||
@@ -375,8 +366,6 @@ private:
|
||||
explicit Thread(KernelCore& kernel);
|
||||
~Thread() override;
|
||||
|
||||
void ChangeScheduler();
|
||||
|
||||
Core::ARM_Interface::ThreadContext context{};
|
||||
|
||||
u32 thread_id = 0;
|
||||
@@ -389,8 +378,7 @@ private:
|
||||
u32 nominal_priority = 0; ///< Nominal thread priority, as set by the emulated application
|
||||
u32 current_priority = 0; ///< Current thread priority, can be temporarily changed
|
||||
|
||||
u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks.
|
||||
u64 last_running_ticks = 0; ///< CPU tick when thread was last running
|
||||
u64 last_running_ticks = 0; ///< CPU tick when thread was last running
|
||||
|
||||
s32 processor_id = 0;
|
||||
|
||||
|
||||
@@ -143,26 +143,6 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me
|
||||
return MakeResult<VMAHandle>(MergeAdjacent(vma_handle));
|
||||
}
|
||||
|
||||
ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const {
|
||||
// Find the first Free VMA.
|
||||
const VAddr base = GetASLRRegionBaseAddress();
|
||||
const VMAHandle vma_handle = std::find_if(vma_map.begin(), vma_map.end(), [&](const auto& vma) {
|
||||
if (vma.second.type != VMAType::Free)
|
||||
return false;
|
||||
|
||||
const VAddr vma_end = vma.second.base + vma.second.size;
|
||||
return vma_end > base && vma_end >= base + size;
|
||||
});
|
||||
|
||||
if (vma_handle == vma_map.end()) {
|
||||
// TODO(Subv): Find the correct error code here.
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
const VAddr target = std::max(base, vma_handle->second.base);
|
||||
return MakeResult<VAddr>(target);
|
||||
}
|
||||
|
||||
ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size,
|
||||
MemoryState state,
|
||||
Memory::MemoryHookPointer mmio_handler) {
|
||||
@@ -243,85 +223,6 @@ ResultCode VMManager::ReprotectRange(VAddr target, u64 size, VMAPermission new_p
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultVal<VAddr> VMManager::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
|
||||
if (target < GetHeapRegionBaseAddress() || target + size > GetHeapRegionEndAddress() ||
|
||||
target + size < target) {
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
if (heap_memory == nullptr) {
|
||||
// Initialize heap
|
||||
heap_memory = std::make_shared<std::vector<u8>>();
|
||||
heap_start = heap_end = target;
|
||||
} else {
|
||||
UnmapRange(heap_start, heap_end - heap_start);
|
||||
}
|
||||
|
||||
// If necessary, expand backing vector to cover new heap extents.
|
||||
if (target < heap_start) {
|
||||
heap_memory->insert(begin(*heap_memory), heap_start - target, 0);
|
||||
heap_start = target;
|
||||
RefreshMemoryBlockMappings(heap_memory.get());
|
||||
}
|
||||
if (target + size > heap_end) {
|
||||
heap_memory->insert(end(*heap_memory), (target + size) - heap_end, 0);
|
||||
heap_end = target + size;
|
||||
RefreshMemoryBlockMappings(heap_memory.get());
|
||||
}
|
||||
ASSERT(heap_end - heap_start == heap_memory->size());
|
||||
|
||||
CASCADE_RESULT(auto vma, MapMemoryBlock(target, heap_memory, target - heap_start, size,
|
||||
MemoryState::Heap));
|
||||
Reprotect(vma, perms);
|
||||
|
||||
heap_used = size;
|
||||
|
||||
return MakeResult<VAddr>(heap_end - size);
|
||||
}
|
||||
|
||||
ResultCode VMManager::HeapFree(VAddr target, u64 size) {
|
||||
if (target < GetHeapRegionBaseAddress() || target + size > GetHeapRegionEndAddress() ||
|
||||
target + size < target) {
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
const ResultCode result = UnmapRange(target, size);
|
||||
if (result.IsError()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
heap_used -= size;
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) {
|
||||
const auto vma = FindVMA(src_addr);
|
||||
|
||||
ASSERT_MSG(vma != vma_map.end(), "Invalid memory address");
|
||||
ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address");
|
||||
|
||||
// The returned VMA might be a bigger one encompassing the desired address.
|
||||
const auto vma_offset = src_addr - vma->first;
|
||||
ASSERT_MSG(vma_offset + size <= vma->second.size,
|
||||
"Shared memory exceeds bounds of mapped block");
|
||||
|
||||
const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block;
|
||||
const std::size_t backing_block_offset = vma->second.offset + vma_offset;
|
||||
|
||||
CASCADE_RESULT(auto new_vma,
|
||||
MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size, state));
|
||||
// Protect mirror with permissions from old region
|
||||
Reprotect(new_vma, vma->second.permissions);
|
||||
// Remove permissions from old region
|
||||
Reprotect(vma, VMAPermission::None);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
void VMManager::RefreshMemoryBlockMappings(const std::vector<u8>* block) {
|
||||
// If this ever proves to have a noticeable performance impact, allow users of the function to
|
||||
// specify a specific range of addresses to limit the scan to.
|
||||
@@ -574,7 +475,8 @@ u64 VMManager::GetTotalMemoryUsage() const {
|
||||
}
|
||||
|
||||
u64 VMManager::GetTotalHeapUsage() const {
|
||||
return heap_used;
|
||||
LOG_WARNING(Kernel, "(STUBBED) called");
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
VAddr VMManager::GetAddressSpaceBaseAddress() const {
|
||||
@@ -605,26 +507,6 @@ u64 VMManager::GetASLRRegionSize() const {
|
||||
return aslr_region_end - aslr_region_base;
|
||||
}
|
||||
|
||||
bool VMManager::IsWithinASLRRegion(VAddr begin, u64 size) const {
|
||||
const VAddr range_end = begin + size;
|
||||
const VAddr aslr_start = GetASLRRegionBaseAddress();
|
||||
const VAddr aslr_end = GetASLRRegionEndAddress();
|
||||
|
||||
if (aslr_start > begin || begin > range_end || range_end - 1 > aslr_end - 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (range_end > heap_region_base && heap_region_end > begin) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (range_end > map_region_base && map_region_end > begin) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
VAddr VMManager::GetCodeRegionBaseAddress() const {
|
||||
return code_region_base;
|
||||
}
|
||||
|
||||
@@ -157,14 +157,6 @@ public:
|
||||
*/
|
||||
ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state);
|
||||
|
||||
/**
|
||||
* Finds the first free address that can hold a region of the desired size.
|
||||
*
|
||||
* @param size Size of the desired region.
|
||||
* @return The found free address.
|
||||
*/
|
||||
ResultVal<VAddr> FindFreeRegion(u64 size) const;
|
||||
|
||||
/**
|
||||
* Maps a memory-mapped IO region at a given address.
|
||||
*
|
||||
@@ -186,12 +178,6 @@ public:
|
||||
/// Changes the permissions of a range of addresses, splitting VMAs as necessary.
|
||||
ResultCode ReprotectRange(VAddr target, u64 size, VMAPermission new_perms);
|
||||
|
||||
ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
|
||||
ResultCode HeapFree(VAddr target, u64 size);
|
||||
|
||||
ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size,
|
||||
MemoryState state = MemoryState::Mapped);
|
||||
|
||||
/**
|
||||
* Scans all VMAs and updates the page table range of any that use the given vector as backing
|
||||
* memory. This should be called after any operation that causes reallocation of the vector.
|
||||
@@ -225,9 +211,6 @@ public:
|
||||
/// Gets the end address of the ASLR region.
|
||||
VAddr GetASLRRegionEndAddress() const;
|
||||
|
||||
/// Determines whether or not the specified address range is within the ASLR region.
|
||||
bool IsWithinASLRRegion(VAddr address, u64 size) const;
|
||||
|
||||
/// Gets the size of the ASLR region
|
||||
u64 GetASLRRegionSize() const;
|
||||
|
||||
@@ -349,15 +332,5 @@ private:
|
||||
|
||||
VAddr tls_io_region_base = 0;
|
||||
VAddr tls_io_region_end = 0;
|
||||
|
||||
// Memory used to back the allocations in the regular heap. A single vector is used to cover
|
||||
// the entire virtual address space extents that bound the allocations, including any holes.
|
||||
// This makes deallocation and reallocation of holes fast and keeps process memory contiguous
|
||||
// in the emulator address space, allowing Memory::GetPointer to be reasonably safe.
|
||||
std::shared_ptr<std::vector<u8>> heap_memory;
|
||||
// The left/right bounds of the address space covered by heap_memory.
|
||||
VAddr heap_start = 0;
|
||||
VAddr heap_end = 0;
|
||||
u64 heap_used = 0;
|
||||
};
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
enum class ErrorDescription : u32 {
|
||||
Success = 0,
|
||||
RemoteProcessDead = 301,
|
||||
InvalidOffset = 6061,
|
||||
InvalidLength = 6062,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,13 +2,9 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include "common/common_paths.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
@@ -20,7 +16,6 @@
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
|
||||
namespace Service::Account {
|
||||
|
||||
// TODO: RE this structure
|
||||
struct UserData {
|
||||
INSERT_PADDING_WORDS(1);
|
||||
@@ -32,29 +27,6 @@ struct UserData {
|
||||
};
|
||||
static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size");
|
||||
|
||||
// Smallest JPEG https://github.com/mathiasbynens/small/blob/master/jpeg.jpg
|
||||
// used as a backup should the one on disk not exist
|
||||
constexpr u32 backup_jpeg_size = 107;
|
||||
constexpr std::array<u8, backup_jpeg_size> backup_jpeg{{
|
||||
0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02,
|
||||
0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05,
|
||||
0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e,
|
||||
0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13,
|
||||
0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01,
|
||||
0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08,
|
||||
0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
|
||||
}};
|
||||
|
||||
static std::string GetImagePath(UUID uuid) {
|
||||
return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
|
||||
"/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
|
||||
}
|
||||
|
||||
static constexpr u32 SanitizeJPEGSize(std::size_t size) {
|
||||
constexpr std::size_t max_jpeg_image_size = 0x20000;
|
||||
return static_cast<u32>(std::min(size, max_jpeg_image_size));
|
||||
}
|
||||
|
||||
class IProfile final : public ServiceFramework<IProfile> {
|
||||
public:
|
||||
explicit IProfile(UUID user_id, ProfileManager& profile_manager)
|
||||
@@ -101,42 +73,32 @@ private:
|
||||
}
|
||||
|
||||
void LoadImage(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
// smallest jpeg https://github.com/mathiasbynens/small/blob/master/jpeg.jpg
|
||||
// TODO(mailwl): load actual profile image from disk, width 256px, max size 0x20000
|
||||
constexpr u32 jpeg_size = 107;
|
||||
static constexpr std::array<u8, jpeg_size> jpeg{
|
||||
0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03,
|
||||
0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x08, 0x06, 0x06, 0x05, 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a,
|
||||
0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f,
|
||||
0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10,
|
||||
0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x11, 0x00,
|
||||
0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, 0x01, 0x01,
|
||||
0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
|
||||
};
|
||||
ctx.WriteBuffer(jpeg);
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
const FileUtil::IOFile image(GetImagePath(user_id), "rb");
|
||||
if (!image.IsOpen()) {
|
||||
LOG_WARNING(Service_ACC,
|
||||
"Failed to load user provided image! Falling back to built-in backup...");
|
||||
ctx.WriteBuffer(backup_jpeg);
|
||||
rb.Push<u32>(backup_jpeg_size);
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 size = SanitizeJPEGSize(image.GetSize());
|
||||
std::vector<u8> buffer(size);
|
||||
image.ReadBytes(buffer.data(), buffer.size());
|
||||
|
||||
ctx.WriteBuffer(buffer.data(), buffer.size());
|
||||
rb.Push<u32>(size);
|
||||
rb.Push<u32>(jpeg_size);
|
||||
}
|
||||
|
||||
void GetImageSize(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
constexpr u32 jpeg_size = 107;
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
const FileUtil::IOFile image(GetImagePath(user_id), "rb");
|
||||
|
||||
if (!image.IsOpen()) {
|
||||
LOG_WARNING(Service_ACC,
|
||||
"Failed to load user provided image! Falling back to built-in backup...");
|
||||
rb.Push<u32>(backup_jpeg_size);
|
||||
} else {
|
||||
rb.Push<u32>(SanitizeJPEGSize(image.GetSize()));
|
||||
}
|
||||
rb.Push<u32>(jpeg_size);
|
||||
}
|
||||
|
||||
const ProfileManager& profile_manager;
|
||||
@@ -242,30 +204,6 @@ void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestCo
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
}
|
||||
|
||||
void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
// A u8 is passed into this function which we can safely ignore. It's to determine if we have
|
||||
// access to use the network or not by the looks of it
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
if (profile_manager->GetUserCount() != 1) {
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<u128>(INVALID_UUID);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto user_list = profile_manager->GetAllUsers();
|
||||
if (std::all_of(user_list.begin(), user_list.end(),
|
||||
[](const auto& user) { return user.uuid == INVALID_UUID; })) {
|
||||
rb.Push(ResultCode(-1)); // TODO(ogniK): Find the correct error code
|
||||
rb.PushRaw<u128>(INVALID_UUID);
|
||||
return;
|
||||
}
|
||||
|
||||
// Select the first user we have
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<u128>(profile_manager->GetUser(0)->uuid);
|
||||
}
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module,
|
||||
std::shared_ptr<ProfileManager> profile_manager, const char* name)
|
||||
: ServiceFramework(name), module(std::move(module)),
|
||||
|
||||
@@ -27,7 +27,6 @@ public:
|
||||
void InitializeApplicationInfo(Kernel::HLERequestContext& ctx);
|
||||
void GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx);
|
||||
void IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx);
|
||||
void TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Module> module;
|
||||
|
||||
@@ -17,7 +17,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
|
||||
{5, &ACC_SU::GetProfile, "GetProfile"},
|
||||
{6, nullptr, "GetProfileDigest"},
|
||||
{50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
|
||||
{51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
|
||||
{51, nullptr, "TrySelectUserWithoutInteraction"},
|
||||
{60, nullptr, "ListOpenContextStoredUsers"},
|
||||
{100, nullptr, "GetUserRegistrationNotifier"},
|
||||
{101, nullptr, "GetUserStateChangeNotifier"},
|
||||
|
||||
@@ -17,7 +17,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
|
||||
{5, &ACC_U0::GetProfile, "GetProfile"},
|
||||
{6, nullptr, "GetProfileDigest"},
|
||||
{50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
|
||||
{51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
|
||||
{51, nullptr, "TrySelectUserWithoutInteraction"},
|
||||
{60, nullptr, "ListOpenContextStoredUsers"},
|
||||
{100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"},
|
||||
{101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"},
|
||||
|
||||
@@ -17,7 +17,7 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
|
||||
{5, &ACC_U1::GetProfile, "GetProfile"},
|
||||
{6, nullptr, "GetProfileDigest"},
|
||||
{50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
|
||||
{51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
|
||||
{51, nullptr, "TrySelectUserWithoutInteraction"},
|
||||
{60, nullptr, "ListOpenContextStoredUsers"},
|
||||
{100, nullptr, "GetUserRegistrationNotifier"},
|
||||
{101, nullptr, "GetUserStateChangeNotifier"},
|
||||
|
||||
@@ -2,83 +2,42 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include <random>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/file_util.h"
|
||||
#include <boost/optional.hpp>
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Service::Account {
|
||||
|
||||
struct UserRaw {
|
||||
UUID uuid;
|
||||
UUID uuid2;
|
||||
u64 timestamp;
|
||||
ProfileUsername username;
|
||||
INSERT_PADDING_BYTES(0x80);
|
||||
};
|
||||
static_assert(sizeof(UserRaw) == 0xC8, "UserRaw has incorrect size.");
|
||||
|
||||
struct ProfileDataRaw {
|
||||
INSERT_PADDING_BYTES(0x10);
|
||||
std::array<UserRaw, MAX_USERS> users;
|
||||
};
|
||||
static_assert(sizeof(ProfileDataRaw) == 0x650, "ProfileDataRaw has incorrect size.");
|
||||
|
||||
// TODO(ogniK): Get actual error codes
|
||||
constexpr ResultCode ERROR_TOO_MANY_USERS(ErrorModule::Account, -1);
|
||||
constexpr ResultCode ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, -2);
|
||||
constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20);
|
||||
|
||||
constexpr char ACC_SAVE_AVATORS_BASE_PATH[] = "/system/save/8000000000000010/su/avators/";
|
||||
|
||||
UUID UUID::Generate() {
|
||||
const UUID& UUID::Generate() {
|
||||
std::random_device device;
|
||||
std::mt19937 gen(device());
|
||||
std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
|
||||
return UUID{distribution(gen), distribution(gen)};
|
||||
}
|
||||
|
||||
std::string UUID::Format() const {
|
||||
return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
|
||||
}
|
||||
|
||||
std::string UUID::FormatSwitch() const {
|
||||
std::array<u8, 16> s{};
|
||||
std::memcpy(s.data(), uuid.data(), sizeof(u128));
|
||||
return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{"
|
||||
":02x}{:02x}{:02x}{:02x}{:02x}",
|
||||
s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
|
||||
s[12], s[13], s[14], s[15]);
|
||||
uuid[0] = distribution(gen);
|
||||
uuid[1] = distribution(gen);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ProfileManager::ProfileManager() {
|
||||
ParseUserSaveFile();
|
||||
|
||||
if (user_count == 0)
|
||||
CreateNewUser(UUID::Generate(), "yuzu");
|
||||
|
||||
auto current = std::clamp<int>(Settings::values.current_user, 0, MAX_USERS - 1);
|
||||
if (UserExistsIndex(current))
|
||||
current = 0;
|
||||
|
||||
OpenUser(*GetUser(current));
|
||||
// TODO(ogniK): Create the default user we have for now until loading/saving users is added
|
||||
auto user_uuid = UUID{1, 0};
|
||||
ASSERT(CreateNewUser(user_uuid, Settings::values.username).IsSuccess());
|
||||
OpenUser(user_uuid);
|
||||
}
|
||||
|
||||
ProfileManager::~ProfileManager() {
|
||||
WriteUserSaveFile();
|
||||
}
|
||||
ProfileManager::~ProfileManager() = default;
|
||||
|
||||
/// After a users creation it needs to be "registered" to the system. AddToProfiles handles the
|
||||
/// internal management of the users profiles
|
||||
std::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& profile) {
|
||||
boost::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& user) {
|
||||
if (user_count >= MAX_USERS) {
|
||||
return {};
|
||||
return boost::none;
|
||||
}
|
||||
profiles[user_count] = profile;
|
||||
profiles[user_count] = user;
|
||||
return user_count++;
|
||||
}
|
||||
|
||||
@@ -97,7 +56,7 @@ bool ProfileManager::RemoveProfileAtIndex(std::size_t index) {
|
||||
|
||||
/// Helper function to register a user to the system
|
||||
ResultCode ProfileManager::AddUser(const ProfileInfo& user) {
|
||||
if (!AddToProfiles(user)) {
|
||||
if (AddToProfiles(user) == boost::none) {
|
||||
return ERROR_TOO_MANY_USERS;
|
||||
}
|
||||
return RESULT_SUCCESS;
|
||||
@@ -142,40 +101,31 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username)
|
||||
return CreateNewUser(uuid, username_output);
|
||||
}
|
||||
|
||||
std::optional<UUID> ProfileManager::GetUser(std::size_t index) const {
|
||||
if (index >= MAX_USERS) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return profiles[index].user_uuid;
|
||||
}
|
||||
|
||||
/// Returns a users profile index based on their user id.
|
||||
std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const {
|
||||
boost::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const {
|
||||
if (!uuid) {
|
||||
return {};
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
const auto iter = std::find_if(profiles.begin(), profiles.end(),
|
||||
[&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; });
|
||||
auto iter = std::find_if(profiles.begin(), profiles.end(),
|
||||
[&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; });
|
||||
if (iter == profiles.end()) {
|
||||
return {};
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
return static_cast<std::size_t>(std::distance(profiles.begin(), iter));
|
||||
}
|
||||
|
||||
/// Returns a users profile index based on their profile
|
||||
std::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const {
|
||||
boost::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const {
|
||||
return GetUserIndex(user.user_uuid);
|
||||
}
|
||||
|
||||
/// Returns the data structure used by the switch when GetProfileBase is called on acc:*
|
||||
bool ProfileManager::GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const {
|
||||
if (!index || index >= MAX_USERS) {
|
||||
bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index,
|
||||
ProfileBase& profile) const {
|
||||
if (index == boost::none || index >= MAX_USERS) {
|
||||
return false;
|
||||
}
|
||||
const auto& prof_info = profiles[*index];
|
||||
const auto& prof_info = profiles[index.get()];
|
||||
profile.user_uuid = prof_info.user_uuid;
|
||||
profile.username = prof_info.username;
|
||||
profile.timestamp = prof_info.creation_time;
|
||||
@@ -184,7 +134,7 @@ bool ProfileManager::GetProfileBase(std::optional<std::size_t> index, ProfileBas
|
||||
|
||||
/// Returns the data structure used by the switch when GetProfileBase is called on acc:*
|
||||
bool ProfileManager::GetProfileBase(UUID uuid, ProfileBase& profile) const {
|
||||
const auto idx = GetUserIndex(uuid);
|
||||
auto idx = GetUserIndex(uuid);
|
||||
return GetProfileBase(idx, profile);
|
||||
}
|
||||
|
||||
@@ -211,34 +161,26 @@ std::size_t ProfileManager::GetOpenUserCount() const {
|
||||
|
||||
/// Checks if a user id exists in our profile manager
|
||||
bool ProfileManager::UserExists(UUID uuid) const {
|
||||
return GetUserIndex(uuid).has_value();
|
||||
}
|
||||
|
||||
bool ProfileManager::UserExistsIndex(std::size_t index) const {
|
||||
if (index >= MAX_USERS)
|
||||
return false;
|
||||
return profiles[index].user_uuid.uuid != INVALID_UUID;
|
||||
return (GetUserIndex(uuid) != boost::none);
|
||||
}
|
||||
|
||||
/// Opens a specific user
|
||||
void ProfileManager::OpenUser(UUID uuid) {
|
||||
const auto idx = GetUserIndex(uuid);
|
||||
if (!idx) {
|
||||
auto idx = GetUserIndex(uuid);
|
||||
if (idx == boost::none) {
|
||||
return;
|
||||
}
|
||||
|
||||
profiles[*idx].is_open = true;
|
||||
profiles[idx.get()].is_open = true;
|
||||
last_opened_user = uuid;
|
||||
}
|
||||
|
||||
/// Closes a specific user
|
||||
void ProfileManager::CloseUser(UUID uuid) {
|
||||
const auto idx = GetUserIndex(uuid);
|
||||
if (!idx) {
|
||||
auto idx = GetUserIndex(uuid);
|
||||
if (idx == boost::none) {
|
||||
return;
|
||||
}
|
||||
|
||||
profiles[*idx].is_open = false;
|
||||
profiles[idx.get()].is_open = false;
|
||||
}
|
||||
|
||||
/// Gets all valid user ids on the system
|
||||
@@ -268,10 +210,10 @@ UUID ProfileManager::GetLastOpenedUser() const {
|
||||
}
|
||||
|
||||
/// Return the users profile base and the unknown arbitary data.
|
||||
bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile,
|
||||
bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile,
|
||||
ProfileData& data) const {
|
||||
if (GetProfileBase(index, profile)) {
|
||||
data = profiles[*index].data;
|
||||
data = profiles[index.get()].data;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -280,7 +222,7 @@ bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, Pro
|
||||
/// Return the users profile base and the unknown arbitary data.
|
||||
bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile,
|
||||
ProfileData& data) const {
|
||||
const auto idx = GetUserIndex(uuid);
|
||||
auto idx = GetUserIndex(uuid);
|
||||
return GetProfileBaseAndData(idx, profile, data);
|
||||
}
|
||||
|
||||
@@ -297,97 +239,4 @@ bool ProfileManager::CanSystemRegisterUser() const {
|
||||
// emulate qlaunch. Update this to dynamically change.
|
||||
}
|
||||
|
||||
bool ProfileManager::RemoveUser(UUID uuid) {
|
||||
const auto index = GetUserIndex(uuid);
|
||||
if (!index) {
|
||||
return false;
|
||||
}
|
||||
|
||||
profiles[*index] = ProfileInfo{};
|
||||
std::stable_partition(profiles.begin(), profiles.end(),
|
||||
[](const ProfileInfo& profile) { return profile.user_uuid; });
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) {
|
||||
const auto index = GetUserIndex(uuid);
|
||||
if (!index || profile_new.user_uuid == UUID(INVALID_UUID)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& profile = profiles[*index];
|
||||
profile.user_uuid = profile_new.user_uuid;
|
||||
profile.username = profile_new.username;
|
||||
profile.creation_time = profile_new.timestamp;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ProfileManager::ParseUserSaveFile() {
|
||||
FileUtil::IOFile save(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
|
||||
ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat",
|
||||
"rb");
|
||||
|
||||
if (!save.IsOpen()) {
|
||||
LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new "
|
||||
"user 'yuzu' with random UUID.");
|
||||
return;
|
||||
}
|
||||
|
||||
ProfileDataRaw data;
|
||||
if (save.ReadBytes(&data, sizeof(ProfileDataRaw)) != sizeof(ProfileDataRaw)) {
|
||||
LOG_WARNING(Service_ACC, "profiles.dat is smaller than expected... Generating new user "
|
||||
"'yuzu' with random UUID.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& user : data.users) {
|
||||
if (user.uuid == UUID(INVALID_UUID)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
AddUser({user.uuid, user.username, user.timestamp, {}, false});
|
||||
}
|
||||
|
||||
std::stable_partition(profiles.begin(), profiles.end(),
|
||||
[](const ProfileInfo& profile) { return profile.user_uuid; });
|
||||
}
|
||||
|
||||
void ProfileManager::WriteUserSaveFile() {
|
||||
ProfileDataRaw raw{};
|
||||
|
||||
for (std::size_t i = 0; i < MAX_USERS; ++i) {
|
||||
raw.users[i].username = profiles[i].username;
|
||||
raw.users[i].uuid2 = profiles[i].user_uuid;
|
||||
raw.users[i].uuid = profiles[i].user_uuid;
|
||||
raw.users[i].timestamp = profiles[i].creation_time;
|
||||
}
|
||||
|
||||
const auto raw_path =
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010";
|
||||
if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path))
|
||||
FileUtil::Delete(raw_path);
|
||||
|
||||
const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
|
||||
ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat";
|
||||
|
||||
if (!FileUtil::CreateFullPath(path)) {
|
||||
LOG_WARNING(Service_ACC, "Failed to create full path of profiles.dat. Create the directory "
|
||||
"nand/system/save/8000000000000010/su/avators to mitigate this "
|
||||
"issue.");
|
||||
return;
|
||||
}
|
||||
|
||||
FileUtil::IOFile save(path, "wb");
|
||||
|
||||
if (!save.IsOpen()) {
|
||||
LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data "
|
||||
"made in current session will be saved.");
|
||||
return;
|
||||
}
|
||||
|
||||
save.Resize(sizeof(ProfileDataRaw));
|
||||
save.WriteBytes(&raw, sizeof(ProfileDataRaw));
|
||||
}
|
||||
|
||||
}; // namespace Service::Account
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
|
||||
#include "boost/optional.hpp"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/result.h"
|
||||
@@ -36,20 +36,19 @@ struct UUID {
|
||||
}
|
||||
|
||||
// TODO(ogniK): Properly generate uuids based on RFC-4122
|
||||
static UUID Generate();
|
||||
const UUID& Generate();
|
||||
|
||||
// Set the UUID to {0,0} to be considered an invalid user
|
||||
void Invalidate() {
|
||||
uuid = INVALID_UUID;
|
||||
}
|
||||
|
||||
std::string Format() const;
|
||||
std::string FormatSwitch() const;
|
||||
std::string Format() const {
|
||||
return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
|
||||
|
||||
constexpr std::size_t profile_username_size = 32;
|
||||
using ProfileUsername = std::array<u8, profile_username_size>;
|
||||
using ProfileUsername = std::array<u8, 0x20>;
|
||||
using ProfileData = std::array<u8, MAX_DATA>;
|
||||
using UserIDArray = std::array<UUID, MAX_USERS>;
|
||||
|
||||
@@ -82,19 +81,18 @@ static_assert(sizeof(ProfileBase) == 0x38, "ProfileBase is an invalid size");
|
||||
/// objects
|
||||
class ProfileManager {
|
||||
public:
|
||||
ProfileManager();
|
||||
ProfileManager(); // TODO(ogniK): Load from system save
|
||||
~ProfileManager();
|
||||
|
||||
ResultCode AddUser(const ProfileInfo& user);
|
||||
ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username);
|
||||
ResultCode CreateNewUser(UUID uuid, const std::string& username);
|
||||
std::optional<UUID> GetUser(std::size_t index) const;
|
||||
std::optional<std::size_t> GetUserIndex(const UUID& uuid) const;
|
||||
std::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const;
|
||||
bool GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const;
|
||||
boost::optional<std::size_t> GetUserIndex(const UUID& uuid) const;
|
||||
boost::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const;
|
||||
bool GetProfileBase(boost::optional<std::size_t> index, ProfileBase& profile) const;
|
||||
bool GetProfileBase(UUID uuid, ProfileBase& profile) const;
|
||||
bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const;
|
||||
bool GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile,
|
||||
bool GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile,
|
||||
ProfileData& data) const;
|
||||
bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const;
|
||||
bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile,
|
||||
@@ -102,7 +100,6 @@ public:
|
||||
std::size_t GetUserCount() const;
|
||||
std::size_t GetOpenUserCount() const;
|
||||
bool UserExists(UUID uuid) const;
|
||||
bool UserExistsIndex(std::size_t index) const;
|
||||
void OpenUser(UUID uuid);
|
||||
void CloseUser(UUID uuid);
|
||||
UserIDArray GetOpenUsers() const;
|
||||
@@ -111,17 +108,11 @@ public:
|
||||
|
||||
bool CanSystemRegisterUser() const;
|
||||
|
||||
bool RemoveUser(UUID uuid);
|
||||
bool SetProfileBase(UUID uuid, const ProfileBase& profile_new);
|
||||
|
||||
private:
|
||||
void ParseUserSaveFile();
|
||||
void WriteUserSaveFile();
|
||||
std::optional<std::size_t> AddToProfiles(const ProfileInfo& profile);
|
||||
bool RemoveProfileAtIndex(std::size_t index);
|
||||
|
||||
std::array<ProfileInfo, MAX_USERS> profiles{};
|
||||
std::size_t user_count = 0;
|
||||
boost::optional<std::size_t> AddToProfiles(const ProfileInfo& profile);
|
||||
bool RemoveProfileAtIndex(std::size_t index);
|
||||
UUID last_opened_user{INVALID_UUID};
|
||||
};
|
||||
|
||||
|
||||
@@ -4,20 +4,17 @@
|
||||
|
||||
#include <array>
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
#include <stack>
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applet_ae.h"
|
||||
#include "core/hle/service/am/applet_oe.h"
|
||||
#include "core/hle/service/am/idle.h"
|
||||
#include "core/hle/service/am/omm.h"
|
||||
#include "core/hle/service/am/spsm.h"
|
||||
#include "core/hle/service/am/tcap.h"
|
||||
#include "core/hle/service/apm/apm.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/nvflinger/nvflinger.h"
|
||||
@@ -28,29 +25,14 @@
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA;
|
||||
|
||||
struct LaunchParameters {
|
||||
u32_le magic;
|
||||
u32_le is_account_selected;
|
||||
u128 current_user;
|
||||
INSERT_PADDING_BYTES(0x70);
|
||||
};
|
||||
static_assert(sizeof(LaunchParameters) == 0x88);
|
||||
|
||||
IWindowController::IWindowController() : ServiceFramework("IWindowController") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "CreateWindow"},
|
||||
{1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"},
|
||||
{10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"},
|
||||
{11, nullptr, "ReleaseForegroundRights"},
|
||||
{12, nullptr, "RejectToChangeIntoBackground"},
|
||||
{20, nullptr, "SetAppletWindowVisibility"},
|
||||
{21, nullptr, "SetAppletGpuTimeSlice"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -105,7 +87,6 @@ void IAudioController::GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestCo
|
||||
}
|
||||
|
||||
IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetLastForegroundCaptureImage"},
|
||||
{1, nullptr, "UpdateLastForegroundCaptureImage"},
|
||||
@@ -136,11 +117,7 @@ IDisplayController::IDisplayController() : ServiceFramework("IDisplayController"
|
||||
{25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"},
|
||||
{26, nullptr, "AcquireCallerAppletCaptureSharedBuffer"},
|
||||
{27, nullptr, "ReleaseCallerAppletCaptureSharedBuffer"},
|
||||
// 6.0.0+
|
||||
{28, nullptr, "TakeScreenShotOfOwnLayerEx"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -151,7 +128,6 @@ IDebugFunctions::~IDebugFunctions() = default;
|
||||
|
||||
ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
: ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger)) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Exit"},
|
||||
{1, &ISelfController::LockExit, "LockExit"},
|
||||
@@ -160,8 +136,10 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
|
||||
{4, nullptr, "LeaveFatalSection"},
|
||||
{9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"},
|
||||
{10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"},
|
||||
{11, &ISelfController::SetOperationModeChangedNotification, "SetOperationModeChangedNotification"},
|
||||
{12, &ISelfController::SetPerformanceModeChangedNotification, "SetPerformanceModeChangedNotification"},
|
||||
{11, &ISelfController::SetOperationModeChangedNotification,
|
||||
"SetOperationModeChangedNotification"},
|
||||
{12, &ISelfController::SetPerformanceModeChangedNotification,
|
||||
"SetPerformanceModeChangedNotification"},
|
||||
{13, &ISelfController::SetFocusHandlingMode, "SetFocusHandlingMode"},
|
||||
{14, &ISelfController::SetRestartMessageEnabled, "SetRestartMessageEnabled"},
|
||||
{15, nullptr, "SetScreenShotAppletIdentityInfo"},
|
||||
@@ -187,12 +165,7 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
|
||||
{69, nullptr, "IsAutoSleepDisabled"},
|
||||
{70, nullptr, "ReportMultimediaError"},
|
||||
{80, nullptr, "SetWirelessPriorityMode"},
|
||||
{90, nullptr, "GetAccumulatedSuspendedTickValue"},
|
||||
{91, nullptr, "GetAccumulatedSuspendedTickChangedEvent"},
|
||||
{1000, nullptr, "GetDebugStorageChannel"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
@@ -203,8 +176,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
|
||||
ISelfController::~ISelfController() = default;
|
||||
|
||||
void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) {
|
||||
// Takes 3 input u8s with each field located immediately after the previous
|
||||
// u8, these are bool flags. No output.
|
||||
// Takes 3 input u8s with each field located immediately after the previous u8, these are
|
||||
// bool flags. No output.
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
@@ -258,8 +231,8 @@ void ISelfController::SetOperationModeChangedNotification(Kernel::HLERequestCont
|
||||
}
|
||||
|
||||
void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) {
|
||||
// Takes 3 input u8s with each field located immediately after the previous
|
||||
// u8, these are bool flags. No output.
|
||||
// Takes 3 input u8s with each field located immediately after the previous u8, these are
|
||||
// bool flags. No output.
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
bool enabled = rp.Pop<bool>();
|
||||
@@ -302,8 +275,8 @@ void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& c
|
||||
}
|
||||
|
||||
void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) {
|
||||
// TODO(Subv): Find out how AM determines the display to use, for now just
|
||||
// create the layer in the Default display.
|
||||
// TODO(Subv): Find out how AM determines the display to use, for now just create the layer
|
||||
// in the Default display.
|
||||
u64 display_id = nvflinger->OpenDisplay("Default");
|
||||
u64 layer_id = nvflinger->CreateLayer(display_id);
|
||||
|
||||
@@ -338,55 +311,7 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
AppletMessageQueue::AppletMessageQueue() {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
on_new_message = Kernel::Event::Create(kernel, Kernel::ResetType::Sticky,
|
||||
"AMMessageQueue:OnMessageRecieved");
|
||||
on_operation_mode_changed = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
|
||||
"AMMessageQueue:OperationModeChanged");
|
||||
}
|
||||
|
||||
AppletMessageQueue::~AppletMessageQueue() = default;
|
||||
|
||||
const Kernel::SharedPtr<Kernel::Event>& AppletMessageQueue::GetMesssageRecieveEvent() const {
|
||||
return on_new_message;
|
||||
}
|
||||
|
||||
const Kernel::SharedPtr<Kernel::Event>& AppletMessageQueue::GetOperationModeChangedEvent() const {
|
||||
return on_operation_mode_changed;
|
||||
}
|
||||
|
||||
void AppletMessageQueue::PushMessage(AppletMessage msg) {
|
||||
messages.push(msg);
|
||||
on_new_message->Signal();
|
||||
}
|
||||
|
||||
AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() {
|
||||
if (messages.empty()) {
|
||||
on_new_message->Clear();
|
||||
return AppletMessage::NoMessage;
|
||||
}
|
||||
auto msg = messages.front();
|
||||
messages.pop();
|
||||
if (messages.empty()) {
|
||||
on_new_message->Clear();
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
std::size_t AppletMessageQueue::GetMessageCount() const {
|
||||
return messages.size();
|
||||
}
|
||||
|
||||
void AppletMessageQueue::OperationModeChanged() {
|
||||
PushMessage(AppletMessage::OperationModeChanged);
|
||||
PushMessage(AppletMessage::PerformanceModeChanged);
|
||||
on_operation_mode_changed->Signal();
|
||||
}
|
||||
|
||||
ICommonStateGetter::ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_queue)
|
||||
: ServiceFramework("ICommonStateGetter"), msg_queue(std::move(msg_queue)) {
|
||||
// clang-format off
|
||||
ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"},
|
||||
{1, &ICommonStateGetter::ReceiveMessage, "ReceiveMessage"},
|
||||
@@ -411,12 +336,11 @@ ICommonStateGetter::ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_q
|
||||
{52, nullptr, "SwitchLcdBacklight"},
|
||||
{55, nullptr, "IsInControllerFirmwareUpdateSection"},
|
||||
{60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"},
|
||||
{61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"},
|
||||
{61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent,
|
||||
"GetDefaultDisplayResolutionChangeEvent"},
|
||||
{62, nullptr, "GetHdcpAuthenticationState"},
|
||||
{63, nullptr, "GetHdcpAuthenticationStateChangeEvent"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
@@ -435,19 +359,21 @@ void ICommonStateGetter::GetBootMode(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void ICommonStateGetter::GetEventHandle(Kernel::HLERequestContext& ctx) {
|
||||
event->Signal();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(msg_queue->GetMesssageRecieveEvent());
|
||||
rb.PushCopyObjects(event);
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushEnum<AppletMessageQueue::AppletMessage>(msg_queue->PopMessage());
|
||||
rb.Push<u32>(15);
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
|
||||
@@ -459,11 +385,13 @@ void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(Kernel::HLERequestContext& ctx) {
|
||||
event->Signal();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(msg_queue->GetOperationModeChangedEvent());
|
||||
rb.PushCopyObjects(event);
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx) {
|
||||
@@ -487,7 +415,7 @@ void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(static_cast<u8>(use_docked_mode ? OperationMode::Docked : OperationMode::Handheld));
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
|
||||
@@ -497,21 +425,18 @@ void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(static_cast<u32>(use_docked_mode ? APM::PerformanceMode::Docked
|
||||
: APM::PerformanceMode::Handheld));
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
|
||||
public:
|
||||
explicit IStorageAccessor(std::vector<u8> buffer)
|
||||
: ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IStorageAccessor::GetSize, "GetSize"},
|
||||
{10, &IStorageAccessor::Write, "Write"},
|
||||
{11, &IStorageAccessor::Read, "Read"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -564,13 +489,10 @@ class IStorage final : public ServiceFramework<IStorage> {
|
||||
public:
|
||||
explicit IStorage(std::vector<u8> buffer)
|
||||
: ServiceFramework("IStorage"), buffer(std::move(buffer)) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IStorage::Open, "Open"},
|
||||
{1, nullptr, "OpenTransferStorage"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -590,7 +512,6 @@ private:
|
||||
class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
|
||||
public:
|
||||
explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"},
|
||||
{1, nullptr, "IsCompleted"},
|
||||
@@ -611,8 +532,6 @@ public:
|
||||
{150, nullptr, "RequestForAppletToGetForeground"},
|
||||
{160, nullptr, "GetIndirectLayerConsumerHandle"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
@@ -705,13 +624,13 @@ void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"},
|
||||
{10, nullptr, "CreateApplicationAndPushAndRequestToStart"},
|
||||
{11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"},
|
||||
{12, nullptr, "CreateApplicationAndRequestToStart"},
|
||||
{13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, "CreateApplicationAndRequestToStartForQuest"},
|
||||
{13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest,
|
||||
"CreateApplicationAndRequestToStartForQuest"},
|
||||
{20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"},
|
||||
{21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"},
|
||||
{22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"},
|
||||
@@ -719,10 +638,10 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
|
||||
{24, nullptr, "GetLaunchStorageInfoForDebug"},
|
||||
{25, nullptr, "ExtendSaveData"},
|
||||
{26, nullptr, "GetSaveDataSize"},
|
||||
{30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"},
|
||||
{31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"},
|
||||
{32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"},
|
||||
{33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"},
|
||||
{30, nullptr, "BeginBlockingHomeButtonShortAndLongPressed"},
|
||||
{31, nullptr, "EndBlockingHomeButtonShortAndLongPressed"},
|
||||
{32, nullptr, "BeginBlockingHomeButton"},
|
||||
{33, nullptr, "EndBlockingHomeButton"},
|
||||
{40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"},
|
||||
{50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"},
|
||||
{60, nullptr, "SetMediaPlaybackStateForApplication"},
|
||||
@@ -733,7 +652,7 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
|
||||
{70, nullptr, "RequestToShutdown"},
|
||||
{71, nullptr, "RequestToReboot"},
|
||||
{80, nullptr, "ExitAndRequestToShowThanksMessage"},
|
||||
{90, &IApplicationFunctions::EnableApplicationCrashReport, "EnableApplicationCrashReport"},
|
||||
{90, nullptr, "EnableApplicationCrashReport"},
|
||||
{100, nullptr, "InitializeApplicationCopyrightFrameBuffer"},
|
||||
{101, nullptr, "SetApplicationCopyrightImage"},
|
||||
{102, nullptr, "SetApplicationCopyrightVisibility"},
|
||||
@@ -745,63 +664,26 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
|
||||
{1000, nullptr, "CreateMovieMaker"},
|
||||
{1001, nullptr, "PrepareForJit"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IApplicationFunctions::~IApplicationFunctions() = default;
|
||||
|
||||
void IApplicationFunctions::EnableApplicationCrashReport(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(
|
||||
Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed(
|
||||
Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void IApplicationFunctions::BeginBlockingHomeButton(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void IApplicationFunctions::EndBlockingHomeButton(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
|
||||
LaunchParameters params{};
|
||||
constexpr std::array<u8, 0x88> data{{
|
||||
0xca, 0x97, 0x94, 0xc7, // Magic
|
||||
1, 0, 0, 0, // IsAccountSelected (bool)
|
||||
1, 0, 0, 0, // User Id (word 0)
|
||||
0, 0, 0, 0, // User Id (word 1)
|
||||
0, 0, 0, 0, // User Id (word 2)
|
||||
0, 0, 0, 0 // User Id (word 3)
|
||||
}};
|
||||
|
||||
params.magic = POP_LAUNCH_PARAMETER_MAGIC;
|
||||
params.is_account_selected = 1;
|
||||
|
||||
Account::ProfileManager profile_manager{};
|
||||
const auto uuid = profile_manager.GetUser(Settings::values.current_user);
|
||||
ASSERT(uuid);
|
||||
params.current_user = uuid->uuid;
|
||||
std::vector<u8> buffer(data.begin(), data.end());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
std::vector<u8> buffer(sizeof(LaunchParameters));
|
||||
std::memcpy(buffer.data(), ¶ms, buffer.size());
|
||||
|
||||
rb.PushIpcInterface<AM::IStorage>(buffer);
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
@@ -827,8 +709,7 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) {
|
||||
// Takes an input u32 Result, no output.
|
||||
// For example, in some cases official apps use this with error 0x2A2 then
|
||||
// uses svcBreak.
|
||||
// For example, in some cases official apps use this with error 0x2A2 then uses svcBreak.
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 result = rp.Pop<u32>();
|
||||
@@ -890,20 +771,14 @@ void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger) {
|
||||
auto message_queue = std::make_shared<AppletMessageQueue>();
|
||||
message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); // Needed on
|
||||
// game boot
|
||||
|
||||
std::make_shared<AppletAE>(nvflinger, message_queue)->InstallAsService(service_manager);
|
||||
std::make_shared<AppletOE>(nvflinger, message_queue)->InstallAsService(service_manager);
|
||||
std::make_shared<AppletAE>(nvflinger)->InstallAsService(service_manager);
|
||||
std::make_shared<AppletOE>(nvflinger)->InstallAsService(service_manager);
|
||||
std::make_shared<IdleSys>()->InstallAsService(service_manager);
|
||||
std::make_shared<OMM>()->InstallAsService(service_manager);
|
||||
std::make_shared<SPSM>()->InstallAsService(service_manager);
|
||||
std::make_shared<TCAP>()->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{10, &IHomeMenuFunctions::RequestToGetForeground, "RequestToGetForeground"},
|
||||
{11, nullptr, "LockForeground"},
|
||||
@@ -912,10 +787,7 @@ IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions"
|
||||
{21, nullptr, "GetPopFromGeneralChannelEvent"},
|
||||
{30, nullptr, "GetHomeButtonWriterLockAccessor"},
|
||||
{31, nullptr, "GetWriterLockAccessorEx"},
|
||||
{100, nullptr, "PopRequestLaunchApplicationForDebug"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -928,7 +800,6 @@ void IHomeMenuFunctions::RequestToGetForeground(Kernel::HLERequestContext& ctx)
|
||||
}
|
||||
|
||||
IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStateController") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "RequestToEnterSleep"},
|
||||
{1, nullptr, "EnterSleep"},
|
||||
@@ -942,23 +813,18 @@ IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStat
|
||||
{14, nullptr, "ShouldSleepOnBoot"},
|
||||
{15, nullptr, "GetHdcpAuthenticationFailedEvent"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IGlobalStateController::~IGlobalStateController() = default;
|
||||
|
||||
IApplicationCreator::IApplicationCreator() : ServiceFramework("IApplicationCreator") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "CreateApplication"},
|
||||
{1, nullptr, "PopLaunchRequestedApplication"},
|
||||
{10, nullptr, "CreateSystemApplication"},
|
||||
{100, nullptr, "PopFloatingApplicationForDevelopment"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -966,7 +832,6 @@ IApplicationCreator::~IApplicationCreator() = default;
|
||||
|
||||
IProcessWindingController::IProcessWindingController()
|
||||
: ServiceFramework("IProcessWindingController") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetLaunchReason"},
|
||||
{11, nullptr, "OpenCallingLibraryApplet"},
|
||||
@@ -977,8 +842,6 @@ IProcessWindingController::IProcessWindingController()
|
||||
{40, nullptr, "ReserveToStartAndWaitAndUnwindThis"},
|
||||
{41, nullptr, "ReserveToStartAndWait"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Kernel {
|
||||
@@ -40,31 +39,6 @@ enum SystemLanguage {
|
||||
TraditionalChinese = 16,
|
||||
};
|
||||
|
||||
class AppletMessageQueue {
|
||||
public:
|
||||
enum class AppletMessage : u32 {
|
||||
NoMessage = 0,
|
||||
FocusStateChanged = 15,
|
||||
OperationModeChanged = 30,
|
||||
PerformanceModeChanged = 31,
|
||||
};
|
||||
|
||||
AppletMessageQueue();
|
||||
~AppletMessageQueue();
|
||||
|
||||
const Kernel::SharedPtr<Kernel::Event>& GetMesssageRecieveEvent() const;
|
||||
const Kernel::SharedPtr<Kernel::Event>& GetOperationModeChangedEvent() const;
|
||||
void PushMessage(AppletMessage msg);
|
||||
AppletMessage PopMessage();
|
||||
std::size_t GetMessageCount() const;
|
||||
void OperationModeChanged();
|
||||
|
||||
private:
|
||||
std::queue<AppletMessage> messages;
|
||||
Kernel::SharedPtr<Kernel::Event> on_new_message;
|
||||
Kernel::SharedPtr<Kernel::Event> on_operation_mode_changed;
|
||||
};
|
||||
|
||||
class IWindowController final : public ServiceFramework<IWindowController> {
|
||||
public:
|
||||
IWindowController();
|
||||
@@ -128,7 +102,7 @@ private:
|
||||
|
||||
class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
|
||||
public:
|
||||
explicit ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_queue);
|
||||
ICommonStateGetter();
|
||||
~ICommonStateGetter() override;
|
||||
|
||||
private:
|
||||
@@ -152,7 +126,6 @@ private:
|
||||
void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx);
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> event;
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
};
|
||||
|
||||
class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
|
||||
@@ -181,11 +154,6 @@ private:
|
||||
void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx);
|
||||
void NotifyRunning(Kernel::HLERequestContext& ctx);
|
||||
void GetPseudoDeviceId(Kernel::HLERequestContext& ctx);
|
||||
void BeginBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
|
||||
void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
|
||||
void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx);
|
||||
void EndBlockingHomeButton(Kernel::HLERequestContext& ctx);
|
||||
void EnableApplicationCrashReport(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
|
||||
|
||||
@@ -12,10 +12,8 @@ namespace Service::AM {
|
||||
|
||||
class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> {
|
||||
public:
|
||||
explicit ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue)
|
||||
: ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)),
|
||||
msg_queue(std::move(msg_queue)) {
|
||||
explicit ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
: ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
|
||||
{1, &ILibraryAppletProxy::GetSelfController, "GetSelfController"},
|
||||
@@ -34,7 +32,7 @@ private:
|
||||
void GetCommonStateGetter(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ICommonStateGetter>(msg_queue);
|
||||
rb.PushIpcInterface<ICommonStateGetter>();
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
@@ -95,15 +93,12 @@ private:
|
||||
}
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
};
|
||||
|
||||
class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> {
|
||||
public:
|
||||
explicit ISystemAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue)
|
||||
: ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)),
|
||||
msg_queue(std::move(msg_queue)) {
|
||||
explicit ISystemAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
: ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
|
||||
{1, &ISystemAppletProxy::GetSelfController, "GetSelfController"},
|
||||
@@ -124,7 +119,7 @@ private:
|
||||
void GetCommonStateGetter(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ICommonStateGetter>(msg_queue);
|
||||
rb.PushIpcInterface<ICommonStateGetter>();
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
@@ -191,35 +186,31 @@ private:
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
};
|
||||
|
||||
void AppletAE::OpenSystemAppletProxy(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ISystemAppletProxy>(nvflinger, msg_queue);
|
||||
rb.PushIpcInterface<ISystemAppletProxy>(nvflinger);
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void AppletAE::OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue);
|
||||
rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger);
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue);
|
||||
rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger);
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue)
|
||||
: ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)),
|
||||
msg_queue(std::move(msg_queue)) {
|
||||
// clang-format off
|
||||
AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
: ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"},
|
||||
{200, &AppletAE::OpenLibraryAppletProxyOld, "OpenLibraryAppletProxyOld"},
|
||||
@@ -227,17 +218,10 @@ AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
{300, nullptr, "OpenOverlayAppletProxy"},
|
||||
{350, nullptr, "OpenSystemApplicationProxy"},
|
||||
{400, nullptr, "CreateSelfLibraryAppletCreatorForDevelop"},
|
||||
{401, nullptr, "GetSystemAppletControllerForDebug"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
AppletAE::~AppletAE() = default;
|
||||
|
||||
const std::shared_ptr<AppletMessageQueue>& AppletAE::GetMessageQueue() const {
|
||||
return msg_queue;
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -17,19 +17,15 @@ namespace AM {
|
||||
|
||||
class AppletAE final : public ServiceFramework<AppletAE> {
|
||||
public:
|
||||
explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue);
|
||||
explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
|
||||
~AppletAE() override;
|
||||
|
||||
const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const;
|
||||
|
||||
private:
|
||||
void OpenSystemAppletProxy(Kernel::HLERequestContext& ctx);
|
||||
void OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx);
|
||||
void OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
};
|
||||
|
||||
} // namespace AM
|
||||
|
||||
@@ -12,11 +12,8 @@ namespace Service::AM {
|
||||
|
||||
class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
|
||||
public:
|
||||
explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue)
|
||||
: ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)),
|
||||
msg_queue(std::move(msg_queue)) {
|
||||
// clang-format off
|
||||
explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
: ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"},
|
||||
{1, &IApplicationProxy::GetSelfController, "GetSelfController"},
|
||||
@@ -28,8 +25,6 @@ public:
|
||||
{20, &IApplicationProxy::GetApplicationFunctions, "GetApplicationFunctions"},
|
||||
{1000, &IApplicationProxy::GetDebugFunctions, "GetDebugFunctions"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -72,7 +67,7 @@ private:
|
||||
void GetCommonStateGetter(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<ICommonStateGetter>(msg_queue);
|
||||
rb.PushIpcInterface<ICommonStateGetter>();
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
@@ -91,20 +86,17 @@ private:
|
||||
}
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
};
|
||||
|
||||
void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IApplicationProxy>(nvflinger, msg_queue);
|
||||
rb.PushIpcInterface<IApplicationProxy>(nvflinger);
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue)
|
||||
: ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)),
|
||||
msg_queue(std::move(msg_queue)) {
|
||||
AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
|
||||
: ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"},
|
||||
};
|
||||
@@ -113,8 +105,4 @@ AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
|
||||
AppletOE::~AppletOE() = default;
|
||||
|
||||
const std::shared_ptr<AppletMessageQueue>& AppletOE::GetMessageQueue() const {
|
||||
return msg_queue;
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -17,17 +17,13 @@ namespace AM {
|
||||
|
||||
class AppletOE final : public ServiceFramework<AppletOE> {
|
||||
public:
|
||||
explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue);
|
||||
explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
|
||||
~AppletOE() override;
|
||||
|
||||
const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const;
|
||||
|
||||
private:
|
||||
void OpenApplicationProxy(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
};
|
||||
|
||||
} // namespace AM
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user