Compare commits

..

2 Commits

Author SHA1 Message Date
Laku
e7487bf220 implement invert 2018-08-31 01:55:39 +03:00
Laku
9683918928 Implement POPC 2018-08-30 10:19:46 +03:00
173 changed files with 1968 additions and 4880 deletions

View File

@@ -20,7 +20,6 @@ matrix:
install: "./.travis/linux/deps.sh"
script: "./.travis/linux/build.sh"
after_success: "./.travis/linux/upload.sh"
cache: ccache
- os: osx
env: NAME="macos build"
sudo: false
@@ -28,7 +27,6 @@ matrix:
install: "./.travis/macos/deps.sh"
script: "./.travis/macos/build.sh"
after_success: "./.travis/macos/upload.sh"
cache: ccache
deploy:
provider: releases
@@ -44,3 +42,7 @@ notifications:
webhooks:
urls:
- https://api.yuzu-emu.org/code/travis/notify
cache:
directories:
- $HOME/.ccache

View File

@@ -1,12 +0,0 @@
# List of environment variables to be shared with Docker containers
CI
TRAVIS
CONTINUOUS_INTEGRATION
TRAVIS_BRANCH
TRAVIS_BUILD_ID
TRAVIS_BUILD_NUMBER
TRAVIS_COMMIT
TRAVIS_JOB_ID
TRAVIS_JOB_NUMBER
TRAVIS_REPO_SLUG
TRAVIS_TAG

View File

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

View File

@@ -5,8 +5,14 @@ apt-get install --no-install-recommends -y build-essential git libqt5opengl5-dev
cd /yuzu
export PATH=/usr/lib/ccache:$PATH
ln -sf /usr/bin/ccache /usr/lib/ccache/cc
ln -sf /usr/bin/ccache /usr/lib/ccache/c++
mkdir build && cd build
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -G Ninja
ccache --show-stats > ccache_before
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -G Ninja
ninja
ccache --show-stats > ccache_after
diff -U100 ccache_before ccache_after || true
ctest -VV -C Release

View File

@@ -5,11 +5,14 @@ set -o pipefail
export MACOSX_DEPLOYMENT_TARGET=10.12
export Qt5_DIR=$(brew --prefix)/opt/qt5
export UNICORNDIR=$(pwd)/externals/unicorn
export PATH="/usr/local/opt/ccache/libexec:$PATH"
mkdir build && cd build
export PATH=/usr/local/opt/ccache/libexec:$PATH
ccache --show-stats > ccache_before
cmake --version
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release
make -j4
ccache --show-stats > ccache_after
diff -U100 ccache_before ccache_after || true
ctest -VV -C Release

View File

@@ -41,19 +41,6 @@ function(check_submodules_present)
endfunction()
check_submodules_present()
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 ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
message(STATUS "Downloading compatibility list for yuzu...")
file(DOWNLOAD
https://api.yuzu-emu.org/gamedb/
"${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json" SHOW_PROGRESS)
endif()
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
# =======================================================================

View File

@@ -41,9 +41,9 @@ before_build:
- ps: |
if ($env:BUILD_TYPE -eq 'msvc') {
# redirect stderr and change the exit code to prevent powershell from cancelling the build if cmake prints a warning
cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON .. 2>&1 && exit 0'
cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 .. 2>&1 && exit 0'
} else {
C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DYUZU_BUILD_UNICORN=1 -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON .. 2>&1"
C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DYUZU_BUILD_UNICORN=1 -DCMAKE_BUILD_TYPE=Release .. 2>&1"
}
- cd ..
@@ -162,6 +162,10 @@ artifacts:
- path: $(BUILD_ZIP)
name: build
type: zip
- path: $(BUILD_SYMBOLS)
name: debugsymbols
- path: $(BUILD_UPDATE)
name: update
deploy:
provider: GitHub

View File

@@ -1,5 +0,0 @@
<RCC>
<qresource prefix="compatibility_list">
<file>compatibility_list.json</file>
</qresource>
</RCC>

View File

@@ -10,7 +10,6 @@
#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"
@@ -95,10 +94,7 @@ 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();

View File

@@ -1,16 +1,13 @@
# Generate cpp with Git revision from template
# Also if this is a CI build, add the build name (ie: Nightly, Canary) to the scm_rev file as well
# Also if this is a CI build, add the build name (ie: Nightly, Bleeding Edge) to the scm_rev file as well
set(REPO_NAME "")
set(BUILD_VERSION "0")
if ($ENV{CI})
if ($ENV{TRAVIS})
set(BUILD_REPOSITORY $ENV{TRAVIS_REPO_SLUG})
set(BUILD_TAG $ENV{TRAVIS_TAG})
elseif($ENV{APPVEYOR})
set(BUILD_REPOSITORY $ENV{APPVEYOR_REPO_NAME})
set(BUILD_TAG $ENV{APPVEYOR_REPO_TAG_NAME})
endif()
# regex capture the string nightly or canary into CMAKE_MATCH_1
# regex capture the string nightly or bleeding-edge into CMAKE_MATCH_1
string(REGEX MATCH "yuzu-emu/yuzu-?(.*)" OUTVAR ${BUILD_REPOSITORY})
if (${CMAKE_MATCH_COUNT} GREATER 0)
# capitalize the first letter of each word in the repo name.
@@ -19,21 +16,10 @@ if ($ENV{CI})
string(SUBSTRING ${WORD} 0 1 FIRST_LETTER)
string(SUBSTRING ${WORD} 1 -1 REMAINDER)
string(TOUPPER ${FIRST_LETTER} FIRST_LETTER)
set(REPO_NAME "${REPO_NAME}${FIRST_LETTER}${REMAINDER}")
# this leaves a trailing space on the last word, but we actually want that
# because of how it's styled in the title bar.
set(REPO_NAME "${REPO_NAME}${FIRST_LETTER}${REMAINDER} ")
endforeach()
if (BUILD_TAG)
string(REGEX MATCH "${CMAKE_MATCH_1}-([0-9]+)" OUTVAR ${BUILD_TAG})
if (${CMAKE_MATCH_COUNT} GREATER 0)
set(BUILD_VERSION ${CMAKE_MATCH_1})
endif()
if (BUILD_VERSION)
# This leaves a trailing space on the last word, but we actually want that
# because of how it's styled in the title bar.
set(BUILD_FULLNAME "${REPO_NAME} #${BUILD_VERSION} ")
else()
set(BUILD_FULLNAME "")
endif()
endif()
endif()
endif()
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp" @ONLY)

View File

@@ -19,7 +19,7 @@ namespace Log {
class Filter {
public:
/// Initializes the filter with all classes having `default_level` as the minimum level.
explicit Filter(Level default_level = Level::Info);
Filter(Level default_level = Level::Info);
/// Resets the filter so that all classes have `level` as the minimum displayed level.
void ResetAll(Level level);
@@ -49,6 +49,6 @@ public:
bool IsDebug() const;
private:
std::array<Level, static_cast<size_t>(Class::Count)> class_levels;
std::array<Level, (size_t)Class::Count> class_levels;
};
} // namespace Log

View File

@@ -12,14 +12,14 @@ namespace Log {
/// Specifies the severity or level of detail of the log message.
enum class Level : u8 {
Trace, ///< Extremely detailed and repetitive debugging information that is likely to
///< pollute logs.
/// pollute logs.
Debug, ///< Less detailed debugging information.
Info, ///< Status information from important points during execution.
Warning, ///< Minor or potential problems found during execution of a task.
Error, ///< Major problems found during execution of a task that prevent it from being
///< completed.
Critical, ///< Major problems during execution that threaten the stability of the entire
///< application.
/// completed.
Critical, ///< Major problems during execution that threathen the stability of the entire
/// application.
Count ///< Total number of logging levels
};
@@ -49,7 +49,7 @@ enum class Class : ClassType {
Kernel, ///< The HLE implementation of the CTR kernel
Kernel_SVC, ///< Kernel system calls
Service, ///< HLE implementation of system services. Each major service
///< should have its own subclass.
/// should have its own subclass.
Service_ACC, ///< The ACC (Accounts) service
Service_AM, ///< The AM (Applet manager) service
Service_AOC, ///< The AOC (AddOn Content) service

View File

@@ -15,6 +15,6 @@ struct Entry;
std::string FormatLogMessage(const Entry& entry);
/// Formats and prints a log entry to stderr.
void PrintMessage(const Entry& entry);
/// Prints the same message as `PrintMessage`, but colored according to the severity level.
/// Prints the same message as `PrintMessage`, but colored acoording to the severity level.
void PrintColoredMessage(const Entry& entry);
} // namespace Log

View File

@@ -9,8 +9,6 @@
#define GIT_DESC "@GIT_DESC@"
#define BUILD_NAME "@REPO_NAME@"
#define BUILD_DATE "@BUILD_DATE@"
#define BUILD_FULLNAME "@BUILD_FULLNAME@"
#define BUILD_VERSION "@BUILD_VERSION@"
namespace Common {
@@ -19,8 +17,6 @@ const char g_scm_branch[] = GIT_BRANCH;
const char g_scm_desc[] = GIT_DESC;
const char g_build_name[] = BUILD_NAME;
const char g_build_date[] = BUILD_DATE;
const char g_build_fullname[] = BUILD_FULLNAME;
const char g_build_version[] = BUILD_VERSION;
} // namespace

View File

@@ -11,7 +11,5 @@ extern const char g_scm_branch[];
extern const char g_scm_desc[];
extern const char g_build_name[];
extern const char g_build_date[];
extern const char g_build_fullname[];
extern const char g_build_version[];
} // namespace Common

View File

@@ -35,12 +35,8 @@ add_library(core STATIC
file_sys/mode.h
file_sys/nca_metadata.cpp
file_sys/nca_metadata.h
file_sys/nca_patch.cpp
file_sys/nca_patch.h
file_sys/partition_filesystem.cpp
file_sys/partition_filesystem.h
file_sys/patch_manager.cpp
file_sys/patch_manager.h
file_sys/program_metadata.cpp
file_sys/program_metadata.h
file_sys/registered_cache.cpp
@@ -53,8 +49,6 @@ add_library(core STATIC
file_sys/savedata_factory.h
file_sys/sdmc_factory.cpp
file_sys/sdmc_factory.h
file_sys/submission_package.cpp
file_sys/submission_package.h
file_sys/vfs.cpp
file_sys/vfs.h
file_sys/vfs_concat.cpp
@@ -365,8 +359,6 @@ add_library(core STATIC
loader/nro.h
loader/nso.cpp
loader/nso.h
loader/nsp.cpp
loader/nsp.h
loader/xci.cpp
loader/xci.h
memory.cpp

View File

@@ -7,10 +7,8 @@
#include <dynarmic/A64/a64.h>
#include <dynarmic/A64/config.h>
#include "common/logging/log.h"
#include "common/microprofile.h"
#include "core/arm/dynarmic/arm_dynarmic.h"
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/core_timing.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/svc.h"
@@ -144,10 +142,7 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
return std::make_unique<Dynarmic::A64::Jit>(config);
}
MICROPROFILE_DEFINE(ARM_Jit_Dynarmic, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64));
void ARM_Dynarmic::Run() {
MICROPROFILE_SCOPE(ARM_Jit_Dynarmic);
ASSERT(Memory::GetCurrentPageTable() == current_page_table);
jit->Run();

View File

@@ -193,10 +193,10 @@ void ARM_Unicorn::Step() {
ExecuteInstructions(1);
}
MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64));
MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64));
void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
MICROPROFILE_SCOPE(ARM_Jit_Unicorn);
MICROPROFILE_SCOPE(ARM_Jit);
CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions));
CoreTiming::AddTicks(num_instructions);
if (GDBStub::IsServerEnabled()) {

View File

@@ -2,35 +2,24 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <map>
#include <memory>
#include <thread>
#include <utility>
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/core_timing.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_real.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/controller.h"
#include "core/hle/service/sm/sm.h"
#include "core/loader/loader.h"
#include "core/perf_stats.h"
#include "core/settings.h"
#include "core/telemetry_session.h"
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/gpu.h"
#include "file_sys/vfs_concat.h"
#include "file_sys/vfs_real.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
@@ -38,9 +27,71 @@ namespace Core {
/*static*/ System System::s_instance;
namespace {
FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
const std::string& path) {
System::System() = default;
System::~System() = default;
/// Runs a CPU core while the system is powered on
static void RunCpuCore(std::shared_ptr<Cpu> cpu_state) {
while (Core::System::GetInstance().IsPoweredOn()) {
cpu_state->RunLoop(true);
}
}
Cpu& System::CurrentCpuCore() {
// If multicore is enabled, use host thread to figure out the current CPU core
if (Settings::values.use_multi_core) {
const auto& search = thread_to_cpu.find(std::this_thread::get_id());
ASSERT(search != thread_to_cpu.end());
ASSERT(search->second);
return *search->second;
}
// Otherwise, use single-threaded mode active_core variable
return *cpu_cores[active_core];
}
System::ResultStatus System::RunLoop(bool tight_loop) {
status = ResultStatus::Success;
// Update thread_to_cpu in case Core 0 is run from a different host thread
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
if (GDBStub::IsServerEnabled()) {
GDBStub::HandlePacket();
// If the loop is halted and we want to step, use a tiny (1) number of instructions to
// execute. Otherwise, get out of the loop function.
if (GDBStub::GetCpuHaltFlag()) {
if (GDBStub::GetCpuStepFlag()) {
tight_loop = false;
} else {
return ResultStatus::Success;
}
}
}
for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
cpu_cores[active_core]->RunLoop(tight_loop);
if (Settings::values.use_multi_core) {
// Cores 1-3 are run on other threads in this mode
break;
}
}
if (GDBStub::IsServerEnabled()) {
GDBStub::SetCpuStepFlag(false);
}
return status;
}
System::ResultStatus System::SingleStep() {
return RunLoop(false);
}
static FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
const std::string& path) {
// To account for split 00+01+etc files.
std::string dir_name;
std::string filename;
@@ -70,405 +121,165 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
return vfs->OpenFile(path, FileSys::Mode::Read);
}
/// Runs a CPU core while the system is powered on
void RunCpuCore(std::shared_ptr<Cpu> cpu_state) {
while (Core::System::GetInstance().IsPoweredOn()) {
cpu_state->RunLoop(true);
}
}
} // Anonymous namespace
struct System::Impl {
Cpu& CurrentCpuCore() {
if (Settings::values.use_multi_core) {
const auto& search = thread_to_cpu.find(std::this_thread::get_id());
ASSERT(search != thread_to_cpu.end());
ASSERT(search->second);
return *search->second;
}
// Otherwise, use single-threaded mode active_core variable
return *cpu_cores[active_core];
}
ResultStatus RunLoop(bool tight_loop) {
status = ResultStatus::Success;
// Update thread_to_cpu in case Core 0 is run from a different host thread
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
if (GDBStub::IsServerEnabled()) {
GDBStub::HandlePacket();
// If the loop is halted and we want to step, use a tiny (1) number of instructions to
// execute. Otherwise, get out of the loop function.
if (GDBStub::GetCpuHaltFlag()) {
if (GDBStub::GetCpuStepFlag()) {
tight_loop = false;
} else {
return ResultStatus::Success;
}
}
}
for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
cpu_cores[active_core]->RunLoop(tight_loop);
if (Settings::values.use_multi_core) {
// Cores 1-3 are run on other threads in this mode
break;
}
}
if (GDBStub::IsServerEnabled()) {
GDBStub::SetCpuStepFlag(false);
}
return status;
}
ResultStatus Init(Frontend::EmuWindow& emu_window) {
LOG_DEBUG(HW_Memory, "initialized OK");
CoreTiming::Init();
kernel.Initialize();
// Create a default fs if one doesn't already exist.
if (virtual_filesystem == nullptr)
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
kernel.MakeCurrentProcess(Kernel::Process::Create(kernel, "main"));
cpu_barrier = std::make_shared<CpuBarrier>();
cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
for (size_t index = 0; index < cpu_cores.size(); ++index) {
cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index);
}
telemetry_session = std::make_unique<Core::TelemetrySession>();
service_manager = std::make_shared<Service::SM::ServiceManager>();
Service::Init(service_manager, virtual_filesystem);
GDBStub::Init();
renderer = VideoCore::CreateRenderer(emu_window);
if (!renderer->Init()) {
return ResultStatus::ErrorVideoCore;
}
gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer());
// Create threads for CPU cores 1-3, and build thread_to_cpu map
// CPU core 0 is run on the main thread
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
if (Settings::values.use_multi_core) {
for (size_t index = 0; index < cpu_core_threads.size(); ++index) {
cpu_core_threads[index] =
std::make_unique<std::thread>(RunCpuCore, cpu_cores[index + 1]);
thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1];
}
}
LOG_DEBUG(Core, "Initialized OK");
// Reset counters and set time origin to current frame
GetAndResetPerfStats();
perf_stats.BeginSystemFrame();
return ResultStatus::Success;
}
ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
if (!app_loader) {
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
return ResultStatus::ErrorGetLoader;
}
std::pair<boost::optional<u32>, Loader::ResultStatus> system_mode =
app_loader->LoadKernelSystemMode();
if (system_mode.second != Loader::ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to determine system mode (Error {})!",
static_cast<int>(system_mode.second));
return ResultStatus::ErrorSystemMode;
}
ResultStatus init_result{Init(emu_window)};
if (init_result != ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
static_cast<int>(init_result));
Shutdown();
return init_result;
}
const Loader::ResultStatus load_result{app_loader->Load(kernel.CurrentProcess())};
if (load_result != Loader::ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
Shutdown();
return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) +
static_cast<u32>(load_result));
}
status = ResultStatus::Success;
return status;
}
void Shutdown() {
// Log last frame performance stats
auto perf_results = GetAndResetPerfStats();
Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_EmulationSpeed",
perf_results.emulation_speed * 100.0);
Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate",
perf_results.game_fps);
Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
perf_results.frametime * 1000.0);
// Shutdown emulation session
renderer.reset();
GDBStub::Shutdown();
Service::Shutdown();
service_manager.reset();
telemetry_session.reset();
gpu_core.reset();
// Close all CPU/threading state
cpu_barrier->NotifyEnd();
if (Settings::values.use_multi_core) {
for (auto& thread : cpu_core_threads) {
thread->join();
thread.reset();
}
}
thread_to_cpu.clear();
for (auto& cpu_core : cpu_cores) {
cpu_core.reset();
}
cpu_barrier.reset();
// Shutdown kernel and core timing
kernel.Shutdown();
CoreTiming::Shutdown();
// Close app loader
app_loader.reset();
LOG_DEBUG(Core, "Shutdown OK");
}
Loader::ResultStatus GetGameName(std::string& out) const {
if (app_loader == nullptr)
return Loader::ResultStatus::ErrorNotInitialized;
return app_loader->ReadTitle(out);
}
void SetStatus(ResultStatus new_status, const char* details = nullptr) {
status = new_status;
if (details) {
status_details = details;
}
}
PerfStatsResults GetAndResetPerfStats() {
return perf_stats.GetAndResetStats(CoreTiming::GetGlobalTimeUs());
}
Kernel::KernelCore kernel;
/// RealVfsFilesystem instance
FileSys::VirtualFilesystem virtual_filesystem;
/// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader;
std::unique_ptr<VideoCore::RendererBase> renderer;
std::unique_ptr<Tegra::GPU> gpu_core;
std::shared_ptr<Tegra::DebugContext> debug_context;
std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
std::shared_ptr<CpuBarrier> cpu_barrier;
std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;
size_t active_core{}; ///< Active core, only used in single thread mode
/// Service manager
std::shared_ptr<Service::SM::ServiceManager> service_manager;
/// Telemetry session for this emulation session
std::unique_ptr<Core::TelemetrySession> telemetry_session;
ResultStatus status = ResultStatus::Success;
std::string status_details = "";
/// Map of guest threads to CPU cores
std::map<std::thread::id, std::shared_ptr<Cpu>> thread_to_cpu;
Core::PerfStats perf_stats;
Core::FrameLimiter frame_limiter;
};
System::System() : impl{std::make_unique<Impl>()} {}
System::~System() = default;
Cpu& System::CurrentCpuCore() {
return impl->CurrentCpuCore();
}
System::ResultStatus System::RunLoop(bool tight_loop) {
return impl->RunLoop(tight_loop);
}
System::ResultStatus System::SingleStep() {
return RunLoop(false);
}
void System::InvalidateCpuInstructionCaches() {
for (auto& cpu : impl->cpu_cores) {
cpu->ArmInterface().ClearInstructionCache();
}
}
System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
return impl->Load(emu_window, filepath);
}
app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
bool System::IsPoweredOn() const {
return impl->cpu_barrier && impl->cpu_barrier->IsAlive();
if (!app_loader) {
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
return ResultStatus::ErrorGetLoader;
}
std::pair<boost::optional<u32>, Loader::ResultStatus> system_mode =
app_loader->LoadKernelSystemMode();
if (system_mode.second != Loader::ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to determine system mode (Error {})!",
static_cast<int>(system_mode.second));
return ResultStatus::ErrorSystemMode;
}
ResultStatus init_result{Init(emu_window)};
if (init_result != ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
static_cast<int>(init_result));
System::Shutdown();
return init_result;
}
const Loader::ResultStatus load_result{app_loader->Load(current_process)};
if (load_result != Loader::ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
System::Shutdown();
return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) +
static_cast<u32>(load_result));
}
status = ResultStatus::Success;
return status;
}
void System::PrepareReschedule() {
CurrentCpuCore().PrepareReschedule();
}
PerfStatsResults System::GetAndResetPerfStats() {
return impl->GetAndResetPerfStats();
}
Core::TelemetrySession& System::TelemetrySession() const {
return *impl->telemetry_session;
}
ARM_Interface& System::CurrentArmInterface() {
return CurrentCpuCore().ArmInterface();
}
size_t System::CurrentCoreIndex() {
return CurrentCpuCore().CoreIndex();
}
Kernel::Scheduler& System::CurrentScheduler() {
return *CurrentCpuCore().Scheduler();
PerfStats::Results System::GetAndResetPerfStats() {
return perf_stats.GetAndResetStats(CoreTiming::GetGlobalTimeUs());
}
const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(size_t core_index) {
ASSERT(core_index < NUM_CPU_CORES);
return impl->cpu_cores[core_index]->Scheduler();
}
Kernel::SharedPtr<Kernel::Process>& System::CurrentProcess() {
return impl->kernel.CurrentProcess();
}
const Kernel::SharedPtr<Kernel::Process>& System::CurrentProcess() const {
return impl->kernel.CurrentProcess();
return cpu_cores[core_index]->Scheduler();
}
ARM_Interface& System::ArmInterface(size_t core_index) {
ASSERT(core_index < NUM_CPU_CORES);
return impl->cpu_cores[core_index]->ArmInterface();
return cpu_cores[core_index]->ArmInterface();
}
Cpu& System::CpuCore(size_t core_index) {
ASSERT(core_index < NUM_CPU_CORES);
return *impl->cpu_cores[core_index];
}
ExclusiveMonitor& System::Monitor() {
return *impl->cpu_exclusive_monitor;
}
Tegra::GPU& System::GPU() {
return *impl->gpu_core;
}
const Tegra::GPU& System::GPU() const {
return *impl->gpu_core;
}
VideoCore::RendererBase& System::Renderer() {
return *impl->renderer;
}
const VideoCore::RendererBase& System::Renderer() const {
return *impl->renderer;
}
Kernel::KernelCore& System::Kernel() {
return impl->kernel;
}
const Kernel::KernelCore& System::Kernel() const {
return impl->kernel;
}
Core::PerfStats& System::GetPerfStats() {
return impl->perf_stats;
}
const Core::PerfStats& System::GetPerfStats() const {
return impl->perf_stats;
}
Core::FrameLimiter& System::FrameLimiter() {
return impl->frame_limiter;
}
const Core::FrameLimiter& System::FrameLimiter() const {
return impl->frame_limiter;
}
Loader::ResultStatus System::GetGameName(std::string& out) const {
return impl->GetGameName(out);
}
void System::SetStatus(ResultStatus new_status, const char* details) {
impl->SetStatus(new_status, details);
}
const std::string& System::GetStatusDetails() const {
return impl->status_details;
}
Loader::AppLoader& System::GetAppLoader() const {
return *impl->app_loader;
}
void System::SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context) {
impl->debug_context = std::move(context);
}
Tegra::DebugContext* System::GetGPUDebugContext() const {
return impl->debug_context.get();
}
void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) {
impl->virtual_filesystem = std::move(vfs);
}
std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
return impl->virtual_filesystem;
return *cpu_cores[core_index];
}
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
return impl->Init(emu_window);
LOG_DEBUG(HW_Memory, "initialized OK");
CoreTiming::Init();
// Create a default fs if one doesn't already exist.
if (virtual_filesystem == nullptr)
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
current_process = Kernel::Process::Create("main");
cpu_barrier = std::make_shared<CpuBarrier>();
cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
for (size_t index = 0; index < cpu_cores.size(); ++index) {
cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index);
}
telemetry_session = std::make_unique<Core::TelemetrySession>();
service_manager = std::make_shared<Service::SM::ServiceManager>();
Kernel::Init();
Service::Init(service_manager, virtual_filesystem);
GDBStub::Init();
renderer = VideoCore::CreateRenderer(emu_window);
if (!renderer->Init()) {
return ResultStatus::ErrorVideoCore;
}
gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer());
// Create threads for CPU cores 1-3, and build thread_to_cpu map
// CPU core 0 is run on the main thread
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
if (Settings::values.use_multi_core) {
for (size_t index = 0; index < cpu_core_threads.size(); ++index) {
cpu_core_threads[index] =
std::make_unique<std::thread>(RunCpuCore, cpu_cores[index + 1]);
thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1];
}
}
LOG_DEBUG(Core, "Initialized OK");
// Reset counters and set time origin to current frame
GetAndResetPerfStats();
perf_stats.BeginSystemFrame();
return ResultStatus::Success;
}
void System::Shutdown() {
impl->Shutdown();
// Log last frame performance stats
auto perf_results = GetAndResetPerfStats();
Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_EmulationSpeed",
perf_results.emulation_speed * 100.0);
Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate",
perf_results.game_fps);
Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
perf_results.frametime * 1000.0);
// Shutdown emulation session
renderer.reset();
GDBStub::Shutdown();
Service::Shutdown();
Kernel::Shutdown();
service_manager.reset();
telemetry_session.reset();
gpu_core.reset();
// Close all CPU/threading state
cpu_barrier->NotifyEnd();
if (Settings::values.use_multi_core) {
for (auto& thread : cpu_core_threads) {
thread->join();
thread.reset();
}
}
thread_to_cpu.clear();
for (auto& cpu_core : cpu_cores) {
cpu_core.reset();
}
cpu_barrier.reset();
// Close core timing
CoreTiming::Shutdown();
// Close app loader
app_loader.reset();
LOG_DEBUG(Core, "Shutdown OK");
}
Service::SM::ServiceManager& System::ServiceManager() {
return *impl->service_manager;
return *service_manager;
}
const Service::SM::ServiceManager& System::ServiceManager() const {
return *impl->service_manager;
return *service_manager;
}
} // namespace Core

View File

@@ -4,55 +4,40 @@
#pragma once
#include <cstddef>
#include <array>
#include <map>
#include <memory>
#include <string>
#include <thread>
#include "common/common_types.h"
#include "core/arm/exclusive_monitor.h"
#include "core/core_cpu.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/scheduler.h"
#include "core/loader/loader.h"
#include "core/memory.h"
#include "core/perf_stats.h"
#include "core/telemetry_session.h"
#include "file_sys/vfs_real.h"
#include "hle/service/filesystem/filesystem.h"
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/gpu.h"
namespace Core::Frontend {
class EmuWindow;
} // namespace Core::Frontend
namespace FileSys {
class VfsFilesystem;
} // namespace FileSys
namespace Kernel {
class KernelCore;
class Process;
class Scheduler;
} // namespace Kernel
namespace Loader {
class AppLoader;
enum class ResultStatus : u16;
} // namespace Loader
}
namespace Service::SM {
class ServiceManager;
} // namespace Service::SM
namespace Tegra {
class DebugContext;
class GPU;
} // namespace Tegra
}
namespace VideoCore {
class RendererBase;
} // namespace VideoCore
}
namespace Core {
class ARM_Interface;
class Cpu;
class ExclusiveMonitor;
class FrameLimiter;
class PerfStats;
class TelemetrySession;
struct PerfStatsResults;
class System {
public:
@@ -108,7 +93,11 @@ public:
* This function should only be used by GDB Stub to support breakpoints, memory updates and
* step/continue commands.
*/
void InvalidateCpuInstructionCaches();
void InvalidateCpuInstructionCaches() {
for (auto& cpu : cpu_cores) {
cpu->ArmInterface().ClearInstructionCache();
}
}
/// Shutdown the emulated system.
void Shutdown();
@@ -127,28 +116,33 @@ public:
* application).
* @returns True if the emulated system is powered on, otherwise false.
*/
bool IsPoweredOn() const;
bool IsPoweredOn() const {
return cpu_barrier && cpu_barrier->IsAlive();
}
/**
* Returns a reference to the telemetry session for this emulation session.
* @returns Reference to the telemetry session.
*/
Core::TelemetrySession& TelemetrySession() const;
Core::TelemetrySession& TelemetrySession() const {
return *telemetry_session;
}
/// Prepare the core emulation for a reschedule
void PrepareReschedule();
/// Gets and resets core performance statistics
PerfStatsResults GetAndResetPerfStats();
PerfStats::Results GetAndResetPerfStats();
/// Gets an ARM interface to the CPU core that is currently running
ARM_Interface& CurrentArmInterface();
ARM_Interface& CurrentArmInterface() {
return CurrentCpuCore().ArmInterface();
}
/// Gets the index of the currently running CPU core
size_t CurrentCoreIndex();
/// Gets the scheduler for the CPU core that is currently running
Kernel::Scheduler& CurrentScheduler();
size_t CurrentCoreIndex() {
return CurrentCpuCore().CoreIndex();
}
/// Gets an ARM interface to the CPU core with the specified index
ARM_Interface& ArmInterface(size_t core_index);
@@ -156,67 +150,87 @@ public:
/// Gets a CPU interface to the CPU core with the specified index
Cpu& CpuCore(size_t core_index);
/// Gets the exclusive monitor
ExclusiveMonitor& Monitor();
/// Gets a mutable reference to the GPU interface
Tegra::GPU& GPU();
Tegra::GPU& GPU() {
return *gpu_core;
}
/// Gets an immutable reference to the GPU interface.
const Tegra::GPU& GPU() const;
const Tegra::GPU& GPU() const {
return *gpu_core;
}
/// Gets a mutable reference to the renderer.
VideoCore::RendererBase& Renderer();
VideoCore::RendererBase& Renderer() {
return *renderer;
}
/// Gets an immutable reference to the renderer.
const VideoCore::RendererBase& Renderer() const;
const VideoCore::RendererBase& Renderer() const {
return *renderer;
}
/// Gets the scheduler for the CPU core that is currently running
Kernel::Scheduler& CurrentScheduler() {
return *CurrentCpuCore().Scheduler();
}
/// Gets the exclusive monitor
ExclusiveMonitor& Monitor() {
return *cpu_exclusive_monitor;
}
/// Gets the scheduler for the CPU core with the specified index
const std::shared_ptr<Kernel::Scheduler>& Scheduler(size_t core_index);
/// Provides a reference to the current process
Kernel::SharedPtr<Kernel::Process>& CurrentProcess();
/// Provides a constant reference to the current process.
const Kernel::SharedPtr<Kernel::Process>& CurrentProcess() const;
/// Provides a reference to the kernel instance.
Kernel::KernelCore& Kernel();
/// Provides a constant reference to the kernel instance.
const Kernel::KernelCore& Kernel() const;
/// Provides a reference to the internal PerfStats instance.
Core::PerfStats& GetPerfStats();
/// Provides a constant reference to the internal PerfStats instance.
const Core::PerfStats& GetPerfStats() const;
/// Provides a reference to the frame limiter;
Core::FrameLimiter& FrameLimiter();
/// Provides a constant referent to the frame limiter
const Core::FrameLimiter& FrameLimiter() const;
/// Gets the current process
Kernel::SharedPtr<Kernel::Process>& CurrentProcess() {
return current_process;
}
/// Gets the name of the current game
Loader::ResultStatus GetGameName(std::string& out) const;
Loader::ResultStatus GetGameName(std::string& out) const {
if (app_loader == nullptr)
return Loader::ResultStatus::ErrorNotInitialized;
return app_loader->ReadTitle(out);
}
void SetStatus(ResultStatus new_status, const char* details);
PerfStats perf_stats;
FrameLimiter frame_limiter;
const std::string& GetStatusDetails() const;
void SetStatus(ResultStatus new_status, const char* details = nullptr) {
status = new_status;
if (details) {
status_details = details;
}
}
Loader::AppLoader& GetAppLoader() const;
const std::string& GetStatusDetails() const {
return status_details;
}
Loader::AppLoader& GetAppLoader() const {
return *app_loader;
}
Service::SM::ServiceManager& ServiceManager();
const Service::SM::ServiceManager& ServiceManager() const;
void SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context);
void SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context) {
debug_context = std::move(context);
}
Tegra::DebugContext* GetGPUDebugContext() const;
std::shared_ptr<Tegra::DebugContext> GetGPUDebugContext() const {
return debug_context;
}
void SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs);
void SetFilesystem(FileSys::VirtualFilesystem vfs) {
virtual_filesystem = std::move(vfs);
}
std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
FileSys::VirtualFilesystem GetFilesystem() const {
return virtual_filesystem;
}
private:
System();
@@ -232,10 +246,33 @@ private:
*/
ResultStatus Init(Frontend::EmuWindow& emu_window);
struct Impl;
std::unique_ptr<Impl> impl;
/// RealVfsFilesystem instance
FileSys::VirtualFilesystem virtual_filesystem;
/// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader;
std::unique_ptr<VideoCore::RendererBase> renderer;
std::unique_ptr<Tegra::GPU> gpu_core;
std::shared_ptr<Tegra::DebugContext> debug_context;
Kernel::SharedPtr<Kernel::Process> current_process;
std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
std::shared_ptr<CpuBarrier> cpu_barrier;
std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;
size_t active_core{}; ///< Active core, only used in single thread mode
/// Service manager
std::shared_ptr<Service::SM::ServiceManager> service_manager;
/// Telemetry session for this emulation session
std::unique_ptr<Core::TelemetrySession> telemetry_session;
static System s_instance;
ResultStatus status = ResultStatus::Success;
std::string status_details = "";
/// Map of guest threads to CPU cores
std::map<std::thread::id, std::shared_ptr<Cpu>> thread_to_cpu;
};
inline ARM_Interface& CurrentArmInterface() {

View File

@@ -82,25 +82,11 @@ void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op
}
} else {
const auto block_size = mbedtls_cipher_get_block_size(context);
if (size < block_size) {
std::vector<u8> block(block_size);
std::memcpy(block.data(), src, size);
Transcode(block.data(), block.size(), block.data(), op);
std::memcpy(dest, block.data(), size);
return;
}
for (size_t offset = 0; offset < size; offset += block_size) {
auto length = std::min<size_t>(block_size, size - offset);
mbedtls_cipher_update(context, src + offset, length, dest + offset, &written);
if (written != length) {
if (length < block_size) {
std::vector<u8> block(block_size);
std::memcpy(block.data(), src + offset, length);
Transcode(block.data(), block.size(), block.data(), op);
std::memcpy(dest + offset, block.data(), length);
return;
}
LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.",
length, written);
}

View File

@@ -21,7 +21,7 @@ size_t CTREncryptionLayer::Read(u8* data, size_t length, size_t offset) const {
UpdateIV(base_offset + offset);
std::vector<u8> raw = base->ReadBytes(length, offset);
cipher.Transcode(raw.data(), raw.size(), data, Op::Decrypt);
return length;
return raw.size();
}
// offset does not fall on block boundary (0x10)

View File

@@ -8,15 +8,12 @@
#include <locale>
#include <sstream>
#include <string_view>
#include <tuple>
#include <vector>
#include "common/common_paths.h"
#include "common/file_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "core/crypto/aes_util.h"
#include "core/crypto/key_manager.h"
#include "core/loader/loader.h"
#include "core/settings.h"
namespace Core::Crypto {
@@ -231,28 +228,18 @@ void KeyManager::WriteKeyToFile(bool title_key, std::string_view keyname,
}
void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
if (s128_keys.find({id, field1, field2}) != s128_keys.end())
return;
if (id == S128KeyType::Titlekey) {
Key128 rights_id;
std::memcpy(rights_id.data(), &field2, sizeof(u64));
std::memcpy(rights_id.data() + sizeof(u64), &field1, sizeof(u64));
WriteKeyToFile(true, Common::HexArrayToString(rights_id), key);
}
const auto iter2 = std::find_if(
const auto iter = std::find_if(
s128_file_id.begin(), s128_file_id.end(),
[&id, &field1, &field2](const std::pair<std::string, KeyIndex<S128KeyType>> elem) {
return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
std::tie(id, field1, field2);
});
if (iter2 != s128_file_id.end())
WriteKeyToFile(false, iter2->first, key);
if (iter != s128_file_id.end())
WriteKeyToFile(id == S128KeyType::Titlekey, iter->first, key);
s128_keys[{id, field1, field2}] = key;
}
void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
if (s256_keys.find({id, field1, field2}) != s256_keys.end())
return;
const auto iter = std::find_if(
s256_file_id.begin(), s256_file_id.end(),
[&id, &field1, &field2](const std::pair<std::string, KeyIndex<S256KeyType>> elem) {

View File

@@ -6,19 +6,16 @@
#include <array>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>
#include <boost/container/flat_map.hpp>
#include <boost/optional.hpp>
#include <fmt/format.h>
#include "common/common_types.h"
namespace Loader {
enum class ResultStatus : u16;
}
#include "core/loader/loader.h"
namespace Core::Crypto {
constexpr u64 TICKET_FILE_TITLEKEY_OFFSET = 0x180;
using Key128 = std::array<u8, 0x10>;
using Key256 = std::array<u8, 0x20>;
using SHA256Hash = std::array<u8, 0x20>;

View File

@@ -3,7 +3,6 @@
// Refer to the license.txt file included.
#include "core/file_sys/bis_factory.h"
#include "core/file_sys/registered_cache.h"
namespace FileSys {
@@ -14,8 +13,6 @@ BISFactory::BISFactory(VirtualDir nand_root_)
usrnand_cache(std::make_shared<RegisteredCache>(
GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {}
BISFactory::~BISFactory() = default;
std::shared_ptr<RegisteredCache> BISFactory::GetSystemNANDContents() const {
return sysnand_cache;
}

View File

@@ -5,20 +5,17 @@
#pragma once
#include <memory>
#include "core/file_sys/vfs.h"
#include "core/loader/loader.h"
#include "registered_cache.h"
namespace FileSys {
class RegisteredCache;
/// File system interface to the Built-In Storage
/// This is currently missing accessors to BIS partitions, but seemed like a good place for the NAND
/// registered caches.
class BISFactory {
public:
explicit BISFactory(VirtualDir nand_root);
~BISFactory();
std::shared_ptr<RegisteredCache> GetSystemNANDContents() const;
std::shared_ptr<RegisteredCache> GetUserNANDContents() const;

View File

@@ -9,10 +9,7 @@
#include "common/logging/log.h"
#include "core/file_sys/card_image.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/file_sys/submission_package.h"
#include "core/file_sys/vfs_offset.h"
#include "core/loader/loader.h"
@@ -46,19 +43,15 @@ XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) {
partitions[static_cast<size_t>(partition)] = std::make_shared<PartitionFilesystem>(raw);
}
secure_partition = std::make_shared<NSP>(
main_hfs.GetFile(partition_names[static_cast<size_t>(XCIPartition::Secure)]));
program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA;
const auto secure_ncas = secure_partition->GetNCAsCollapsed();
std::copy(secure_ncas.begin(), secure_ncas.end(), std::back_inserter(ncas));
auto result = AddNCAFromPartition(XCIPartition::Secure);
if (result != Loader::ResultStatus::Success) {
status = result;
return;
}
program =
secure_partition->GetNCA(secure_partition->GetProgramTitleID(), ContentRecordType::Program);
program_nca_status = secure_partition->GetProgramStatus(secure_partition->GetProgramTitleID());
if (program_nca_status == Loader::ResultStatus::ErrorNSPMissingProgramNCA)
program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA;
auto result = AddNCAFromPartition(XCIPartition::Update);
result = AddNCAFromPartition(XCIPartition::Update);
if (result != Loader::ResultStatus::Success) {
status = result;
return;
@@ -81,8 +74,6 @@ XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) {
status = Loader::ResultStatus::Success;
}
XCI::~XCI() = default;
Loader::ResultStatus XCI::GetStatus() const {
return status;
}
@@ -95,10 +86,6 @@ VirtualDir XCI::GetPartition(XCIPartition partition) const {
return partitions[static_cast<size_t>(partition)];
}
std::shared_ptr<NSP> XCI::GetSecurePartitionNSP() const {
return secure_partition;
}
VirtualDir XCI::GetSecurePartition() const {
return GetPartition(XCIPartition::Secure);
}
@@ -115,20 +102,6 @@ VirtualDir XCI::GetLogoPartition() const {
return GetPartition(XCIPartition::Logo);
}
u64 XCI::GetProgramTitleID() const {
return secure_partition->GetProgramTitleID();
}
std::shared_ptr<NCA> XCI::GetProgramNCA() const {
return program;
}
VirtualFile XCI::GetProgramNCAFile() const {
if (GetProgramNCA() == nullptr)
return nullptr;
return GetProgramNCA()->GetBaseFile();
}
const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const {
return ncas;
}

View File

@@ -5,22 +5,15 @@
#pragma once
#include <array>
#include <memory>
#include <vector>
#include "common/common_types.h"
#include "common/swap.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/vfs.h"
namespace Loader {
enum class ResultStatus : u16;
}
#include "core/loader/loader.h"
namespace FileSys {
class NCA;
enum class NCAContentType : u8;
class NSP;
enum class GamecardSize : u8 {
S_1GB = 0xFA,
S_2GB = 0xF8,
@@ -64,7 +57,6 @@ enum class XCIPartition : u8 { Update, Normal, Secure, Logo };
class XCI : public ReadOnlyVfsDirectory {
public:
explicit XCI(VirtualFile file);
~XCI() override;
Loader::ResultStatus GetStatus() const;
Loader::ResultStatus GetProgramNCAStatus() const;
@@ -72,16 +64,11 @@ public:
u8 GetFormatVersion() const;
VirtualDir GetPartition(XCIPartition partition) const;
std::shared_ptr<NSP> GetSecurePartitionNSP() const;
VirtualDir GetSecurePartition() const;
VirtualDir GetNormalPartition() const;
VirtualDir GetUpdatePartition() const;
VirtualDir GetLogoPartition() const;
u64 GetProgramTitleID() const;
std::shared_ptr<NCA> GetProgramNCA() const;
VirtualFile GetProgramNCAFile() const;
const std::vector<std::shared_ptr<NCA>>& GetNCAs() const;
std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const;
VirtualFile GetNCAFileByType(NCAContentType type) const;
@@ -107,8 +94,6 @@ private:
Loader::ResultStatus program_nca_status;
std::vector<VirtualDir> partitions;
std::shared_ptr<NSP> secure_partition;
std::shared_ptr<NCA> program;
std::vector<std::shared_ptr<NCA>> ncas;
};
} // namespace FileSys

View File

@@ -3,17 +3,12 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <cstring>
#include <utility>
#include <boost/optional.hpp>
#include "common/logging/log.h"
#include "core/crypto/aes_util.h"
#include "core/crypto/ctr_encryption_layer.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_patch.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/vfs_offset.h"
#include "core/loader/loader.h"
@@ -69,31 +64,10 @@ struct RomFSSuperblock {
};
static_assert(sizeof(RomFSSuperblock) == 0x200, "RomFSSuperblock has incorrect size.");
struct BKTRHeader {
u64_le offset;
u64_le size;
u32_le magic;
INSERT_PADDING_BYTES(0x4);
u32_le number_entries;
INSERT_PADDING_BYTES(0x4);
};
static_assert(sizeof(BKTRHeader) == 0x20, "BKTRHeader has incorrect size.");
struct BKTRSuperblock {
NCASectionHeaderBlock header_block;
IVFCHeader ivfc;
INSERT_PADDING_BYTES(0x18);
BKTRHeader relocation;
BKTRHeader subsection;
INSERT_PADDING_BYTES(0xC0);
};
static_assert(sizeof(BKTRSuperblock) == 0x200, "BKTRSuperblock has incorrect size.");
union NCASectionHeader {
NCASectionRaw raw;
PFS0Superblock pfs0;
RomFSSuperblock romfs;
BKTRSuperblock bktr;
};
static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size.");
@@ -126,7 +100,7 @@ boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType ty
Core::Crypto::Key128 out;
if (type == NCASectionCryptoType::XTS)
std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin());
else if (type == NCASectionCryptoType::CTR || type == NCASectionCryptoType::BKTR)
else if (type == NCASectionCryptoType::CTR)
std::copy(key_area.begin() + 0x20, key_area.begin() + 0x30, out.begin());
else
LOG_CRITICAL(Crypto, "Called GetKeyAreaKey on invalid NCASectionCryptoType type={:02X}",
@@ -176,9 +150,6 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting
LOG_DEBUG(Crypto, "called with mode=NONE");
return in;
case NCASectionCryptoType::CTR:
// During normal BKTR decryption, this entire function is skipped. This is for the metadata,
// which uses the same CTR as usual.
case NCASectionCryptoType::BKTR:
LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
{
boost::optional<Core::Crypto::Key128> key = boost::none;
@@ -215,9 +186,7 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting
}
}
NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
: file(std::move(file_)),
bktr_base_romfs(bktr_base_romfs_ ? std::move(bktr_base_romfs_) : nullptr) {
NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
status = Loader::ResultStatus::Success;
if (file == nullptr) {
@@ -292,21 +261,22 @@ NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_off
is_update = std::find_if(sections.begin(), sections.end(), [](const NCASectionHeader& header) {
return header.raw.header.crypto_type == NCASectionCryptoType::BKTR;
}) != sections.end();
ivfc_offset = 0;
for (std::ptrdiff_t i = 0; i < number_sections; ++i) {
auto section = sections[i];
if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) {
const size_t base_offset =
header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER;
ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
const size_t romfs_offset = base_offset + ivfc_offset;
const size_t romfs_offset =
header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER +
section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
const size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
auto raw = std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset);
auto dec = Decrypt(section, raw, romfs_offset);
if (dec == nullptr) {
auto dec =
Decrypt(section, std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset),
romfs_offset);
if (dec != nullptr) {
files.push_back(std::move(dec));
romfs = files.back();
} else {
if (status != Loader::ResultStatus::Success)
return;
if (has_rights_id)
@@ -315,117 +285,6 @@ NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_off
status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
return;
}
if (section.raw.header.crypto_type == NCASectionCryptoType::BKTR) {
if (section.bktr.relocation.magic != Common::MakeMagic('B', 'K', 'T', 'R') ||
section.bktr.subsection.magic != Common::MakeMagic('B', 'K', 'T', 'R')) {
status = Loader::ResultStatus::ErrorBadBKTRHeader;
return;
}
if (section.bktr.relocation.offset + section.bktr.relocation.size !=
section.bktr.subsection.offset) {
status = Loader::ResultStatus::ErrorBKTRSubsectionNotAfterRelocation;
return;
}
const u64 size =
MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset -
header.section_tables[i].media_offset);
if (section.bktr.subsection.offset + section.bktr.subsection.size != size) {
status = Loader::ResultStatus::ErrorBKTRSubsectionNotAtEnd;
return;
}
const u64 offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
RelocationBlock relocation_block{};
if (dec->ReadObject(&relocation_block, section.bktr.relocation.offset - offset) !=
sizeof(RelocationBlock)) {
status = Loader::ResultStatus::ErrorBadRelocationBlock;
return;
}
SubsectionBlock subsection_block{};
if (dec->ReadObject(&subsection_block, section.bktr.subsection.offset - offset) !=
sizeof(RelocationBlock)) {
status = Loader::ResultStatus::ErrorBadSubsectionBlock;
return;
}
std::vector<RelocationBucketRaw> relocation_buckets_raw(
(section.bktr.relocation.size - sizeof(RelocationBlock)) /
sizeof(RelocationBucketRaw));
if (dec->ReadBytes(relocation_buckets_raw.data(),
section.bktr.relocation.size - sizeof(RelocationBlock),
section.bktr.relocation.offset + sizeof(RelocationBlock) -
offset) !=
section.bktr.relocation.size - sizeof(RelocationBlock)) {
status = Loader::ResultStatus::ErrorBadRelocationBuckets;
return;
}
std::vector<SubsectionBucketRaw> subsection_buckets_raw(
(section.bktr.subsection.size - sizeof(SubsectionBlock)) /
sizeof(SubsectionBucketRaw));
if (dec->ReadBytes(subsection_buckets_raw.data(),
section.bktr.subsection.size - sizeof(SubsectionBlock),
section.bktr.subsection.offset + sizeof(SubsectionBlock) -
offset) !=
section.bktr.subsection.size - sizeof(SubsectionBlock)) {
status = Loader::ResultStatus::ErrorBadSubsectionBuckets;
return;
}
std::vector<RelocationBucket> relocation_buckets(relocation_buckets_raw.size());
std::transform(relocation_buckets_raw.begin(), relocation_buckets_raw.end(),
relocation_buckets.begin(), &ConvertRelocationBucketRaw);
std::vector<SubsectionBucket> subsection_buckets(subsection_buckets_raw.size());
std::transform(subsection_buckets_raw.begin(), subsection_buckets_raw.end(),
subsection_buckets.begin(), &ConvertSubsectionBucketRaw);
u32 ctr_low;
std::memcpy(&ctr_low, section.raw.section_ctr.data(), sizeof(ctr_low));
subsection_buckets.back().entries.push_back(
{section.bktr.relocation.offset, {0}, ctr_low});
subsection_buckets.back().entries.push_back({size, {0}, 0});
boost::optional<Core::Crypto::Key128> key = boost::none;
if (encrypted) {
if (has_rights_id) {
status = Loader::ResultStatus::Success;
key = GetTitlekey();
if (key == boost::none) {
status = Loader::ResultStatus::ErrorMissingTitlekey;
return;
}
} else {
key = GetKeyAreaKey(NCASectionCryptoType::BKTR);
if (key == boost::none) {
status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
return;
}
}
}
if (bktr_base_romfs == nullptr) {
status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS;
return;
}
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.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
files.push_back(std::make_shared<OffsetVfsFile>(
bktr, romfs_size, section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset));
romfs = files.back();
} else {
files.push_back(std::move(dec));
romfs = files.back();
}
} else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) {
u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) *
MEDIA_OFFSET_MULTIPLIER) +
@@ -441,12 +300,6 @@ NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_off
dirs.push_back(std::move(npfs));
if (IsDirectoryExeFS(dirs.back()))
exefs = dirs.back();
} else {
if (has_rights_id)
status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
else
status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
return;
}
} else {
if (status != Loader::ResultStatus::Success)
@@ -492,15 +345,11 @@ NCAContentType NCA::GetType() const {
}
u64 NCA::GetTitleId() const {
if (is_update || status == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS)
return header.title_id | 0x800;
if (status != Loader::ResultStatus::Success)
return {};
return header.title_id;
}
bool NCA::IsUpdate() const {
return is_update;
}
VirtualFile NCA::GetRomFS() const {
return romfs;
}
@@ -513,8 +362,8 @@ VirtualFile NCA::GetBaseFile() const {
return file;
}
u64 NCA::GetBaseIVFCOffset() const {
return ivfc_offset;
bool NCA::IsUpdate() const {
return is_update;
}
bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {

View File

@@ -12,12 +12,10 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "control_metadata.h"
#include "core/crypto/key_manager.h"
#include "core/file_sys/vfs.h"
namespace Loader {
enum class ResultStatus : u16;
}
#include "core/file_sys/partition_filesystem.h"
#include "core/loader/loader.h"
namespace FileSys {
@@ -79,8 +77,7 @@ bool IsValidNCA(const NCAHeader& header);
// After construction, use GetStatus to determine if the file is valid and ready to be used.
class NCA : public ReadOnlyVfsDirectory {
public:
explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr,
u64 bktr_base_ivfc_offset = 0);
explicit NCA(VirtualFile file);
Loader::ResultStatus GetStatus() const;
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
@@ -90,15 +87,13 @@ public:
NCAContentType GetType() const;
u64 GetTitleId() const;
bool IsUpdate() const;
VirtualFile GetRomFS() const;
VirtualDir GetExeFS() const;
VirtualFile GetBaseFile() const;
// Returns the base ivfc offset used in BKTR patching.
u64 GetBaseIVFCOffset() const;
bool IsUpdate() const;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
@@ -115,16 +110,14 @@ private:
VirtualFile romfs = nullptr;
VirtualDir exefs = nullptr;
VirtualFile file;
VirtualFile bktr_base_romfs;
u64 ivfc_offset;
NCAHeader header{};
bool has_rights_id{};
bool is_update{};
Loader::ResultStatus status{};
bool encrypted;
bool is_update;
Core::Crypto::KeyManager keys;
};

View File

@@ -21,17 +21,7 @@ NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) {
}
const LanguageEntry& NACP::GetLanguageEntry(Language language) const {
if (language != Language::Default) {
return raw->language_entries.at(static_cast<u8>(language));
} else {
for (const auto& language_entry : raw->language_entries) {
if (!language_entry.GetApplicationName().empty())
return language_entry;
}
// Fallback to English
return GetLanguageEntry(Language::AmericanEnglish);
}
return raw->language_entries.at(static_cast<u8>(language));
}
std::string NACP::GetApplicationName(Language language) const {

View File

@@ -8,8 +8,6 @@
#include <memory>
#include <string>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/file_sys/vfs.h"
namespace FileSys {
@@ -62,8 +60,6 @@ enum class Language : u8 {
Korean = 12,
Taiwanese = 13,
Chinese = 14,
Default = 255,
};
static constexpr std::array<const char*, 15> LANGUAGE_NAMES = {
@@ -78,9 +74,9 @@ static constexpr std::array<const char*, 15> LANGUAGE_NAMES = {
class NACP {
public:
explicit NACP(VirtualFile file);
const LanguageEntry& GetLanguageEntry(Language language = Language::Default) const;
std::string GetApplicationName(Language language = Language::Default) const;
std::string GetDeveloperName(Language language = Language::Default) const;
const LanguageEntry& GetLanguageEntry(Language language = Language::AmericanEnglish) const;
std::string GetApplicationName(Language language = Language::AmericanEnglish) const;
std::string GetDeveloperName(Language language = Language::AmericanEnglish) const;
u64 GetTitleId() const;
std::string GetVersionString() const;

View File

@@ -3,9 +3,10 @@
// Refer to the license.txt file included.
#include <cstring>
#include "common/common_types.h"
#include "common/common_funcs.h"
#include "common/logging/log.h"
#include "common/swap.h"
#include "content_archive.h"
#include "core/file_sys/nca_metadata.h"
namespace FileSys {

View File

@@ -4,6 +4,7 @@
#pragma once
#include <cstring>
#include <memory>
#include <vector>
#include "common/common_funcs.h"

View File

@@ -1,210 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cstddef>
#include <cstring>
#include "common/assert.h"
#include "core/crypto/aes_util.h"
#include "core/file_sys/nca_patch.h"
namespace FileSys {
BKTR::BKTR(VirtualFile base_romfs_, VirtualFile bktr_romfs_, RelocationBlock relocation_,
std::vector<RelocationBucket> relocation_buckets_, SubsectionBlock subsection_,
std::vector<SubsectionBucket> subsection_buckets_, bool is_encrypted_,
Core::Crypto::Key128 key_, u64 base_offset_, u64 ivfc_offset_,
std::array<u8, 8> section_ctr_)
: relocation(relocation_), relocation_buckets(std::move(relocation_buckets_)),
subsection(subsection_), subsection_buckets(std::move(subsection_buckets_)),
base_romfs(std::move(base_romfs_)), bktr_romfs(std::move(bktr_romfs_)),
encrypted(is_encrypted_), key(key_), base_offset(base_offset_), ivfc_offset(ivfc_offset_),
section_ctr(section_ctr_) {
for (size_t i = 0; i < relocation.number_buckets - 1; ++i) {
relocation_buckets[i].entries.push_back({relocation.base_offsets[i + 1], 0, 0});
}
for (size_t i = 0; i < subsection.number_buckets - 1; ++i) {
subsection_buckets[i].entries.push_back({subsection_buckets[i + 1].entries[0].address_patch,
{0},
subsection_buckets[i + 1].entries[0].ctr});
}
relocation_buckets.back().entries.push_back({relocation.size, 0, 0});
}
BKTR::~BKTR() = default;
size_t BKTR::Read(u8* data, size_t length, size_t offset) const {
// Read out of bounds.
if (offset >= relocation.size)
return 0;
const auto relocation = GetRelocationEntry(offset);
const auto section_offset = offset - relocation.address_patch + relocation.address_source;
const auto bktr_read = relocation.from_patch;
const auto next_relocation = GetNextRelocationEntry(offset);
if (offset + length > next_relocation.address_patch) {
const u64 partition = next_relocation.address_patch - offset;
return Read(data, partition, offset) +
Read(data + partition, length - partition, offset + partition);
}
if (!bktr_read) {
ASSERT_MSG(section_offset >= ivfc_offset, "Offset calculation negative.");
return base_romfs->Read(data, length, section_offset - ivfc_offset);
}
if (!encrypted) {
return bktr_romfs->Read(data, length, section_offset);
}
const auto subsection = GetSubsectionEntry(section_offset);
Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(key, Core::Crypto::Mode::CTR);
// Calculate AES IV
std::vector<u8> iv(16);
auto subsection_ctr = subsection.ctr;
auto offset_iv = section_offset + base_offset;
for (size_t i = 0; i < section_ctr.size(); ++i)
iv[i] = section_ctr[0x8 - i - 1];
offset_iv >>= 4;
for (size_t i = 0; i < sizeof(u64); ++i) {
iv[0xF - i] = static_cast<u8>(offset_iv & 0xFF);
offset_iv >>= 8;
}
for (size_t i = 0; i < sizeof(u32); ++i) {
iv[0x7 - i] = static_cast<u8>(subsection_ctr & 0xFF);
subsection_ctr >>= 8;
}
cipher.SetIV(iv);
const auto next_subsection = GetNextSubsectionEntry(section_offset);
if (section_offset + length > next_subsection.address_patch) {
const u64 partition = next_subsection.address_patch - section_offset;
return Read(data, partition, offset) +
Read(data + partition, length - partition, offset + partition);
}
const auto block_offset = section_offset & 0xF;
if (block_offset != 0) {
auto block = bktr_romfs->ReadBytes(0x10, section_offset & ~0xF);
cipher.Transcode(block.data(), block.size(), block.data(), Core::Crypto::Op::Decrypt);
if (length + block_offset < 0x10) {
std::memcpy(data, block.data() + block_offset, std::min(length, block.size()));
return std::min(length, block.size());
}
const auto read = 0x10 - block_offset;
std::memcpy(data, block.data() + block_offset, read);
return read + Read(data + read, length - read, offset + read);
}
const auto raw_read = bktr_romfs->Read(data, length, section_offset);
cipher.Transcode(data, raw_read, data, Core::Crypto::Op::Decrypt);
return raw_read;
}
template <bool Subsection, typename BlockType, typename BucketType>
std::pair<size_t, size_t> BKTR::SearchBucketEntry(u64 offset, BlockType block,
BucketType buckets) const {
if constexpr (Subsection) {
const auto last_bucket = buckets[block.number_buckets - 1];
if (offset >= last_bucket.entries[last_bucket.number_entries].address_patch)
return {block.number_buckets - 1, last_bucket.number_entries};
} else {
ASSERT_MSG(offset <= block.size, "Offset is out of bounds in BKTR relocation block.");
}
size_t bucket_id = std::count_if(block.base_offsets.begin() + 1,
block.base_offsets.begin() + block.number_buckets,
[&offset](u64 base_offset) { return base_offset <= offset; });
const auto bucket = buckets[bucket_id];
if (bucket.number_entries == 1)
return {bucket_id, 0};
size_t low = 0;
size_t mid = 0;
size_t high = bucket.number_entries - 1;
while (low <= high) {
mid = (low + high) / 2;
if (bucket.entries[mid].address_patch > offset) {
high = mid - 1;
} else {
if (mid == bucket.number_entries - 1 ||
bucket.entries[mid + 1].address_patch > offset) {
return {bucket_id, mid};
}
low = mid + 1;
}
}
UNREACHABLE_MSG("Offset could not be found in BKTR block.");
}
RelocationEntry BKTR::GetRelocationEntry(u64 offset) const {
const auto res = SearchBucketEntry<false>(offset, relocation, relocation_buckets);
return relocation_buckets[res.first].entries[res.second];
}
RelocationEntry BKTR::GetNextRelocationEntry(u64 offset) const {
const auto res = SearchBucketEntry<false>(offset, relocation, relocation_buckets);
const auto bucket = relocation_buckets[res.first];
if (res.second + 1 < bucket.entries.size())
return bucket.entries[res.second + 1];
return relocation_buckets[res.first + 1].entries[0];
}
SubsectionEntry BKTR::GetSubsectionEntry(u64 offset) const {
const auto res = SearchBucketEntry<true>(offset, subsection, subsection_buckets);
return subsection_buckets[res.first].entries[res.second];
}
SubsectionEntry BKTR::GetNextSubsectionEntry(u64 offset) const {
const auto res = SearchBucketEntry<true>(offset, subsection, subsection_buckets);
const auto bucket = subsection_buckets[res.first];
if (res.second + 1 < bucket.entries.size())
return bucket.entries[res.second + 1];
return subsection_buckets[res.first + 1].entries[0];
}
std::string BKTR::GetName() const {
return base_romfs->GetName();
}
size_t BKTR::GetSize() const {
return relocation.size;
}
bool BKTR::Resize(size_t new_size) {
return false;
}
std::shared_ptr<VfsDirectory> BKTR::GetContainingDirectory() const {
return base_romfs->GetContainingDirectory();
}
bool BKTR::IsWritable() const {
return false;
}
bool BKTR::IsReadable() const {
return true;
}
size_t BKTR::Write(const u8* data, size_t length, size_t offset) {
return 0;
}
bool BKTR::Rename(std::string_view name) {
return base_romfs->Rename(name);
}
} // namespace FileSys

View File

@@ -1,150 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <memory>
#include <vector>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/crypto/key_manager.h"
namespace FileSys {
#pragma pack(push, 1)
struct RelocationEntry {
u64_le address_patch;
u64_le address_source;
u32 from_patch;
};
#pragma pack(pop)
static_assert(sizeof(RelocationEntry) == 0x14, "RelocationEntry has incorrect size.");
struct RelocationBucketRaw {
INSERT_PADDING_BYTES(4);
u32_le number_entries;
u64_le end_offset;
std::array<RelocationEntry, 0x332> relocation_entries;
INSERT_PADDING_BYTES(8);
};
static_assert(sizeof(RelocationBucketRaw) == 0x4000, "RelocationBucketRaw has incorrect size.");
// Vector version of RelocationBucketRaw
struct RelocationBucket {
u32 number_entries;
u64 end_offset;
std::vector<RelocationEntry> entries;
};
struct RelocationBlock {
INSERT_PADDING_BYTES(4);
u32_le number_buckets;
u64_le size;
std::array<u64, 0x7FE> base_offsets;
};
static_assert(sizeof(RelocationBlock) == 0x4000, "RelocationBlock has incorrect size.");
struct SubsectionEntry {
u64_le address_patch;
INSERT_PADDING_BYTES(0x4);
u32_le ctr;
};
static_assert(sizeof(SubsectionEntry) == 0x10, "SubsectionEntry has incorrect size.");
struct SubsectionBucketRaw {
INSERT_PADDING_BYTES(4);
u32_le number_entries;
u64_le end_offset;
std::array<SubsectionEntry, 0x3FF> subsection_entries;
};
static_assert(sizeof(SubsectionBucketRaw) == 0x4000, "SubsectionBucketRaw has incorrect size.");
// Vector version of SubsectionBucketRaw
struct SubsectionBucket {
u32 number_entries;
u64 end_offset;
std::vector<SubsectionEntry> entries;
};
struct SubsectionBlock {
INSERT_PADDING_BYTES(4);
u32_le number_buckets;
u64_le size;
std::array<u64, 0x7FE> base_offsets;
};
static_assert(sizeof(SubsectionBlock) == 0x4000, "SubsectionBlock has incorrect size.");
inline RelocationBucket ConvertRelocationBucketRaw(RelocationBucketRaw raw) {
return {raw.number_entries,
raw.end_offset,
{raw.relocation_entries.begin(), raw.relocation_entries.begin() + raw.number_entries}};
}
inline SubsectionBucket ConvertSubsectionBucketRaw(SubsectionBucketRaw raw) {
return {raw.number_entries,
raw.end_offset,
{raw.subsection_entries.begin(), raw.subsection_entries.begin() + raw.number_entries}};
}
class BKTR : public VfsFile {
public:
BKTR(VirtualFile base_romfs, VirtualFile bktr_romfs, RelocationBlock relocation,
std::vector<RelocationBucket> relocation_buckets, SubsectionBlock subsection,
std::vector<SubsectionBucket> subsection_buckets, bool is_encrypted,
Core::Crypto::Key128 key, u64 base_offset, u64 ivfc_offset, std::array<u8, 8> section_ctr);
~BKTR() override;
size_t Read(u8* data, size_t length, size_t offset) const override;
std::string GetName() const override;
size_t GetSize() const override;
bool Resize(size_t new_size) override;
std::shared_ptr<VfsDirectory> GetContainingDirectory() const override;
bool IsWritable() const override;
bool IsReadable() const override;
size_t Write(const u8* data, size_t length, size_t offset) override;
bool Rename(std::string_view name) override;
private:
template <bool Subsection, typename BlockType, typename BucketType>
std::pair<size_t, size_t> SearchBucketEntry(u64 offset, BlockType block,
BucketType buckets) const;
RelocationEntry GetRelocationEntry(u64 offset) const;
RelocationEntry GetNextRelocationEntry(u64 offset) const;
SubsectionEntry GetSubsectionEntry(u64 offset) const;
SubsectionEntry GetNextSubsectionEntry(u64 offset) const;
RelocationBlock relocation;
std::vector<RelocationBucket> relocation_buckets;
SubsectionBlock subsection;
std::vector<SubsectionBucket> subsection_buckets;
// Should be the raw base romfs, decrypted.
VirtualFile base_romfs;
// Should be the raw BKTR romfs, (located at media_offset with size media_size).
VirtualFile bktr_romfs;
bool encrypted;
Core::Crypto::Key128 key;
// Base offset into NCA, used for IV calculation.
u64 base_offset;
// Distance between IVFC start and RomFS start, used for base reads
u64 ivfc_offset;
std::array<u8, 8> section_ctr;
};
} // namespace FileSys

View File

@@ -1,157 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <cstddef>
#include "common/logging/log.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
namespace FileSys {
constexpr u64 SINGLE_BYTE_MODULUS = 0x100;
std::string FormatTitleVersion(u32 version, TitleVersionFormat format) {
std::array<u8, sizeof(u32)> bytes{};
bytes[0] = version % SINGLE_BYTE_MODULUS;
for (size_t i = 1; i < bytes.size(); ++i) {
version /= SINGLE_BYTE_MODULUS;
bytes[i] = version % SINGLE_BYTE_MODULUS;
}
if (format == TitleVersionFormat::FourElements)
return fmt::format("v{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]);
return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]);
}
constexpr std::array<const char*, 1> PATCH_TYPE_NAMES{
"Update",
};
std::string FormatPatchTypeName(PatchType type) {
return PATCH_TYPE_NAMES.at(static_cast<size_t>(type));
}
PatchManager::PatchManager(u64 title_id) : title_id(title_id) {}
VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
if (exefs == nullptr)
return exefs;
const auto installed = Service::FileSystem::GetUnionContents();
// Game Updates
const auto update_tid = GetUpdateTitleID(title_id);
const auto update = installed->GetEntry(update_tid, ContentRecordType::Program);
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;
}
VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset,
ContentRecordType type) const {
LOG_INFO(Loader, "Patching RomFS for title_id={:016X}, type={:02X}", title_id,
static_cast<u8>(type));
if (romfs == nullptr)
return romfs;
const auto installed = Service::FileSystem::GetUnionContents();
// Game Updates
const auto update_tid = GetUpdateTitleID(title_id);
const auto update = installed->GetEntryRaw(update_tid, type);
if (update != nullptr) {
const auto new_nca = std::make_shared<NCA>(update, romfs, ivfc_offset);
if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
new_nca->GetRomFS() != nullptr) {
LOG_INFO(Loader, " RomFS: Update ({}) applied successfully",
FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0)));
romfs = new_nca->GetRomFS();
}
}
return romfs;
}
std::map<PatchType, std::string> PatchManager::GetPatchVersionNames() const {
std::map<PatchType, std::string> out;
const auto installed = Service::FileSystem::GetUnionContents();
const auto update_tid = GetUpdateTitleID(title_id);
PatchManager update{update_tid};
auto [nacp, discard_icon_file] = update.GetControlMetadata();
if (nacp != nullptr) {
out[PatchType::Update] = nacp->GetVersionString();
} else {
if (installed->HasEntry(update_tid, ContentRecordType::Program)) {
const auto meta_ver = installed->GetEntryVersion(update_tid);
if (meta_ver == boost::none || meta_ver.get() == 0) {
out[PatchType::Update] = "";
} else {
out[PatchType::Update] =
FormatTitleVersion(meta_ver.get(), TitleVersionFormat::ThreeElements);
}
}
}
return out;
}
std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const {
const auto& installed{Service::FileSystem::GetUnionContents()};
const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control);
if (base_control_nca == nullptr)
return {};
return ParseControlNCA(base_control_nca);
}
std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(
const std::shared_ptr<NCA>& nca) const {
const auto base_romfs = nca->GetRomFS();
if (base_romfs == nullptr)
return {};
const auto romfs = PatchRomFS(base_romfs, nca->GetBaseIVFCOffset(), ContentRecordType::Control);
if (romfs == nullptr)
return {};
const auto extracted = ExtractRomFS(romfs);
if (extracted == nullptr)
return {};
auto nacp_file = extracted->GetFile("control.nacp");
if (nacp_file == nullptr)
nacp_file = extracted->GetFile("Control.nacp");
const auto nacp = nacp_file == nullptr ? nullptr : std::make_shared<NACP>(nacp_file);
VirtualFile icon_file;
for (const auto& language : FileSys::LANGUAGE_NAMES) {
icon_file = extracted->GetFile("icon_" + std::string(language) + ".dat");
if (icon_file != nullptr)
break;
}
return {nacp, icon_file};
}
} // namespace FileSys

View File

@@ -1,63 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <map>
#include <memory>
#include <string>
#include "common/common_types.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/vfs.h"
namespace FileSys {
class NCA;
class NACP;
enum class TitleVersionFormat : u8 {
ThreeElements, ///< vX.Y.Z
FourElements, ///< vX.Y.Z.W
};
std::string FormatTitleVersion(u32 version,
TitleVersionFormat format = TitleVersionFormat::ThreeElements);
enum class PatchType {
Update,
};
std::string FormatPatchTypeName(PatchType type);
// A centralized class to manage patches to games.
class PatchManager {
public:
explicit PatchManager(u64 title_id);
// Currently tracked ExeFS patches:
// - Game Updates
VirtualDir PatchExeFS(VirtualDir exefs) const;
// Currently tracked RomFS patches:
// - Game Updates
VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset,
ContentRecordType type = ContentRecordType::Program) const;
// Returns a vector of pairs between patch names and patch versions.
// i.e. Update v80 will return {Update, 80}
std::map<PatchType, std::string> GetPatchVersionNames() const;
// Given title_id of the program, attempts to get the control data of the update and parse it,
// falling back to the base control data.
std::pair<std::shared_ptr<NACP>, VirtualFile> GetControlMetadata() const;
// Version of GetControlMetadata that takes an arbitrary NCA
std::pair<std::shared_ptr<NACP>, VirtualFile> ParseControlNCA(
const std::shared_ptr<NCA>& nca) const;
private:
u64 title_id;
};
} // namespace FileSys

View File

@@ -2,10 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstddef>
#include <cstring>
#include <vector>
#include "common/file_util.h"
#include "common/logging/log.h"
#include "core/file_sys/program_metadata.h"
#include "core/loader/loader.h"

View File

@@ -5,10 +5,12 @@
#pragma once
#include <array>
#include <string>
#include <vector>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/file_sys/vfs.h"
#include "partition_filesystem.h"
namespace Loader {
enum class ResultStatus : u16;

View File

@@ -5,17 +5,13 @@
#include <regex>
#include <mbedtls/sha256.h>
#include "common/assert.h"
#include "common/file_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "core/crypto/key_manager.h"
#include "core/crypto/encryption_layer.h"
#include "core/file_sys/card_image.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/submission_package.h"
#include "core/file_sys/vfs_concat.h"
#include "core/loader/loader.h"
namespace FileSys {
std::string RegisteredCacheEntry::DebugInfo() const {
@@ -280,18 +276,6 @@ VirtualFile RegisteredCache::GetEntryUnparsed(RegisteredCacheEntry entry) const
return GetEntryUnparsed(entry.title_id, entry.type);
}
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();
const auto yuzu_meta_iter = yuzu_meta.find(title_id);
if (yuzu_meta_iter != yuzu_meta.end())
return yuzu_meta_iter->second.GetTitleVersion();
return boost::none;
}
VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) const {
const auto id = GetNcaIDFromMetadata(title_id, type);
if (id == boost::none)
@@ -371,21 +355,17 @@ std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter(
return out;
}
static std::shared_ptr<NCA> GetNCAFromNSPForID(std::shared_ptr<NSP> nsp, const NcaID& id) {
const auto file = nsp->GetFile(fmt::format("{}.nca", Common::HexArrayToString(id, false)));
if (file == nullptr)
return nullptr;
return std::make_shared<NCA>(file);
static std::shared_ptr<NCA> GetNCAFromXCIForID(std::shared_ptr<XCI> xci, const NcaID& id) {
const auto filename = fmt::format("{}.nca", Common::HexArrayToString(id, false));
const auto iter =
std::find_if(xci->GetNCAs().begin(), xci->GetNCAs().end(),
[&filename](std::shared_ptr<NCA> nca) { return nca->GetName() == filename; });
return iter == xci->GetNCAs().end() ? nullptr : *iter;
}
InstallResult RegisteredCache::InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists,
const VfsCopyFunction& copy) {
return InstallEntry(xci->GetSecurePartitionNSP(), overwrite_if_exists, copy);
}
InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NSP> nsp, bool overwrite_if_exists,
const VfsCopyFunction& copy) {
const auto& ncas = nsp->GetNCAsCollapsed();
const auto& ncas = xci->GetNCAs();
const auto& meta_iter = std::find_if(ncas.begin(), ncas.end(), [](std::shared_ptr<NCA> nca) {
return nca->GetType() == NCAContentType::Meta;
});
@@ -409,7 +389,7 @@ InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NSP> nsp, bool overw
const auto cnmt_file = section0->GetFiles()[0];
const CNMT cnmt(cnmt_file);
for (const auto& record : cnmt.GetContentRecords()) {
const auto nca = GetNCAFromNSPForID(nsp, record.nca_id);
const auto nca = GetNCAFromXCIForID(xci, record.nca_id);
if (nca == nullptr)
return InstallResult::ErrorCopyFailed;
const auto res2 = RawInstallNCA(nca, copy, overwrite_if_exists, record.nca_id);
@@ -510,107 +490,4 @@ bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) {
kv.second.GetTitleID() == cnmt.GetTitleID();
}) != yuzu_meta.end();
}
RegisteredCacheUnion::RegisteredCacheUnion(std::vector<std::shared_ptr<RegisteredCache>> caches)
: caches(std::move(caches)) {}
void RegisteredCacheUnion::Refresh() {
for (const auto& c : caches)
c->Refresh();
}
bool RegisteredCacheUnion::HasEntry(u64 title_id, ContentRecordType type) const {
return std::any_of(caches.begin(), caches.end(), [title_id, type](const auto& cache) {
return cache->HasEntry(title_id, type);
});
}
bool RegisteredCacheUnion::HasEntry(RegisteredCacheEntry entry) const {
return HasEntry(entry.title_id, entry.type);
}
boost::optional<u32> RegisteredCacheUnion::GetEntryVersion(u64 title_id) const {
for (const auto& c : caches) {
const auto res = c->GetEntryVersion(title_id);
if (res != boost::none)
return res;
}
return boost::none;
}
VirtualFile RegisteredCacheUnion::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
for (const auto& c : caches) {
const auto res = c->GetEntryUnparsed(title_id, type);
if (res != nullptr)
return res;
}
return nullptr;
}
VirtualFile RegisteredCacheUnion::GetEntryUnparsed(RegisteredCacheEntry entry) const {
return GetEntryUnparsed(entry.title_id, entry.type);
}
VirtualFile RegisteredCacheUnion::GetEntryRaw(u64 title_id, ContentRecordType type) const {
for (const auto& c : caches) {
const auto res = c->GetEntryRaw(title_id, type);
if (res != nullptr)
return res;
}
return nullptr;
}
VirtualFile RegisteredCacheUnion::GetEntryRaw(RegisteredCacheEntry entry) const {
return GetEntryRaw(entry.title_id, entry.type);
}
std::shared_ptr<NCA> RegisteredCacheUnion::GetEntry(u64 title_id, ContentRecordType type) const {
const auto raw = GetEntryRaw(title_id, type);
if (raw == nullptr)
return nullptr;
return std::make_shared<NCA>(raw);
}
std::shared_ptr<NCA> RegisteredCacheUnion::GetEntry(RegisteredCacheEntry entry) const {
return GetEntry(entry.title_id, entry.type);
}
std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntries() const {
std::vector<RegisteredCacheEntry> out;
for (const auto& c : caches) {
c->IterateAllMetadata<RegisteredCacheEntry>(
out,
[](const CNMT& c, const ContentRecord& r) {
return RegisteredCacheEntry{c.GetTitleID(), r.type};
},
[](const CNMT& c, const ContentRecord& r) { return true; });
}
return out;
}
std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntriesFilter(
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>(
out,
[](const CNMT& c, const ContentRecord& r) {
return RegisteredCacheEntry{c.GetTitleID(), r.type};
},
[&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) {
if (title_type != boost::none && title_type.get() != c.GetType())
return false;
if (record_type != boost::none && record_type.get() != r.type)
return false;
if (title_id != boost::none && title_id.get() != c.GetTitleID())
return false;
return true;
});
}
return out;
}
} // namespace FileSys

View File

@@ -11,19 +11,15 @@
#include <string>
#include <vector>
#include <boost/container/flat_map.hpp>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "content_archive.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/vfs.h"
namespace FileSys {
class CNMT;
class NCA;
class NSP;
class XCI;
enum class ContentRecordType : u8;
enum class TitleType : u8;
struct ContentRecord;
class CNMT;
using NcaID = std::array<u8, 0x10>;
using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
@@ -43,10 +39,6 @@ struct RegisteredCacheEntry {
std::string DebugInfo() const;
};
constexpr u64 GetUpdateTitleID(u64 base_title_id) {
return base_title_id | 0x800;
}
// boost flat_map requires operator< for O(log(n)) lookups.
bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
@@ -64,8 +56,6 @@ bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs)
* 4GB splitting can be ignored.)
*/
class RegisteredCache {
friend class RegisteredCacheUnion;
public:
// Parsing function defines the conversion from raw file to NCA. If there are other steps
// besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom
@@ -80,8 +70,6 @@ public:
bool HasEntry(u64 title_id, ContentRecordType type) const;
bool HasEntry(RegisteredCacheEntry entry) const;
boost::optional<u32> GetEntryVersion(u64 title_id) const;
VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const;
VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
@@ -98,12 +86,10 @@ public:
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.
// Raw copies all the ncas from the xci to the csache. Does some quick checks to make sure there
// is a meta NCA and all of them are accessible.
InstallResult InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists = false,
const VfsCopyFunction& copy = &VfsRawCopy);
InstallResult InstallEntry(std::shared_ptr<NSP> nsp, bool overwrite_if_exists = false,
const VfsCopyFunction& copy = &VfsRawCopy);
// Due to the fact that we must use Meta-type NCAs to determine the existance of files, this
// poses quite a challenge. Instead of creating a new meta NCA for this file, yuzu will create a
@@ -139,36 +125,4 @@ private:
boost::container::flat_map<u64, CNMT> yuzu_meta;
};
// Combines multiple RegisteredCaches (i.e. SysNAND, UserNAND, SDMC) into one interface.
class RegisteredCacheUnion {
public:
explicit RegisteredCacheUnion(std::vector<std::shared_ptr<RegisteredCache>> caches);
void Refresh();
bool HasEntry(u64 title_id, ContentRecordType type) const;
bool HasEntry(RegisteredCacheEntry entry) const;
boost::optional<u32> GetEntryVersion(u64 title_id) const;
VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const;
VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
std::shared_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
std::shared_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
std::vector<RegisteredCacheEntry> ListEntries() const;
// If a parameter is not boost::none, it will be filtered for from all entries.
std::vector<RegisteredCacheEntry> ListEntriesFilter(
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<std::shared_ptr<RegisteredCache>> caches;
};
} // namespace FileSys

View File

@@ -6,7 +6,6 @@
#include <array>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/file_sys/vfs.h"

View File

@@ -2,14 +2,11 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <memory>
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs_factory.h"
#include "core/hle/kernel/process.h"
@@ -23,17 +20,10 @@ RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) {
if (app_loader.ReadRomFS(file) != Loader::ResultStatus::Success) {
LOG_ERROR(Service_FS, "Unable to read RomFS!");
}
updatable = app_loader.IsRomFSUpdatable();
ivfc_offset = app_loader.ReadRomFSIVFCOffset();
}
ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() {
if (!updatable)
return MakeResult<VirtualFile>(file);
const PatchManager patch_manager(Core::CurrentProcess()->program_id);
return MakeResult<VirtualFile>(patch_manager.PatchRomFS(file, ivfc_offset));
return MakeResult<VirtualFile>(file);
}
ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, ContentRecordType type) {

View File

@@ -36,8 +36,6 @@ public:
private:
VirtualFile file;
bool updatable;
u64 ivfc_offset;
};
} // namespace FileSys

View File

@@ -3,12 +3,10 @@
// Refer to the license.txt file included.
#include <memory>
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/vfs.h"
#include "core/hle/kernel/process.h"
namespace FileSys {

View File

@@ -6,10 +6,8 @@
#include <memory>
#include <string>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/file_sys/vfs.h"
#include "core/hle/result.h"
namespace FileSys {

View File

@@ -1,245 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cstring>
#include <string_view>
#include <fmt/ostream.h>
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "core/crypto/key_manager.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/file_sys/submission_package.h"
#include "core/loader/loader.h"
namespace FileSys {
NSP::NSP(VirtualFile file_)
: file(std::move(file_)), status{Loader::ResultStatus::Success},
pfs(std::make_shared<PartitionFilesystem>(file)) {
if (pfs->GetStatus() != Loader::ResultStatus::Success) {
status = pfs->GetStatus();
return;
}
if (IsDirectoryExeFS(pfs)) {
extracted = true;
exefs = pfs;
const auto& files = pfs->GetFiles();
const auto romfs_iter =
std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) {
return file->GetName().find(".romfs") != std::string::npos;
});
if (romfs_iter != files.end())
romfs = *romfs_iter;
return;
}
extracted = false;
const auto files = pfs->GetFiles();
Core::Crypto::KeyManager keys;
for (const auto& ticket_file : files) {
if (ticket_file->GetExtension() == "tik") {
if (ticket_file == nullptr ||
ticket_file->GetSize() <
Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) {
continue;
}
Core::Crypto::Key128 key{};
ticket_file->Read(key.data(), key.size(), Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET);
std::string_view name_only(ticket_file->GetName());
name_only.remove_suffix(4);
const auto rights_id_raw = Common::HexStringToArray<16>(name_only);
u128 rights_id;
std::memcpy(rights_id.data(), rights_id_raw.data(), sizeof(u128));
keys.SetKey(Core::Crypto::S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
}
}
for (const auto& outer_file : files) {
if (outer_file->GetName().substr(outer_file->GetName().size() - 9) == ".cnmt.nca") {
const auto nca = std::make_shared<NCA>(outer_file);
if (nca->GetStatus() != Loader::ResultStatus::Success) {
program_status[nca->GetTitleId()] = nca->GetStatus();
continue;
}
const auto section0 = nca->GetSubdirectories()[0];
for (const auto& inner_file : section0->GetFiles()) {
if (inner_file->GetExtension() != "cnmt")
continue;
const CNMT cnmt(inner_file);
auto& ncas_title = ncas[cnmt.GetTitleID()];
ncas_title[ContentRecordType::Meta] = nca;
for (const auto& rec : cnmt.GetContentRecords()) {
const auto id_string = Common::HexArrayToString(rec.nca_id, false);
const auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string));
if (next_file == nullptr) {
LOG_WARNING(Service_FS,
"NCA with ID {}.nca is listed in content metadata, but cannot "
"be found in PFS. NSP appears to be corrupted.",
id_string);
continue;
}
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)
ncas_title[rec.type] = std::move(next_nca);
}
break;
}
}
}
}
NSP::~NSP() = default;
Loader::ResultStatus NSP::GetStatus() const {
return status;
}
Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const {
const auto iter = program_status.find(title_id);
if (iter == program_status.end())
return Loader::ResultStatus::ErrorNSPMissingProgramNCA;
return iter->second;
}
u64 NSP::GetFirstTitleID() const {
if (program_status.empty())
return 0;
return program_status.begin()->first;
}
u64 NSP::GetProgramTitleID() const {
const auto out = GetFirstTitleID();
if ((out & 0x800) == 0)
return out;
const auto ids = GetTitleIDs();
const auto iter =
std::find_if(ids.begin(), ids.end(), [](u64 tid) { return (tid & 0x800) == 0; });
return iter == ids.end() ? out : *iter;
}
std::vector<u64> NSP::GetTitleIDs() const {
std::vector<u64> out;
out.reserve(ncas.size());
for (const auto& kv : ncas)
out.push_back(kv.first);
return out;
}
bool NSP::IsExtractedType() const {
return extracted;
}
VirtualFile NSP::GetRomFS() const {
return romfs;
}
VirtualDir NSP::GetExeFS() const {
return exefs;
}
std::vector<std::shared_ptr<NCA>> NSP::GetNCAsCollapsed() const {
if (extracted)
LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
std::vector<std::shared_ptr<NCA>> out;
for (const auto& map : ncas) {
for (const auto& inner_map : map.second)
out.push_back(inner_map.second);
}
return out;
}
std::multimap<u64, std::shared_ptr<NCA>> NSP::GetNCAsByTitleID() const {
if (extracted)
LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
std::multimap<u64, std::shared_ptr<NCA>> out;
for (const auto& map : ncas) {
for (const auto& inner_map : map.second)
out.emplace(map.first, inner_map.second);
}
return out;
}
std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> NSP::GetNCAs() const {
return ncas;
}
std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type) const {
if (extracted)
LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
const auto title_id_iter = ncas.find(title_id);
if (title_id_iter == ncas.end())
return nullptr;
const auto type_iter = title_id_iter->second.find(type);
if (type_iter == title_id_iter->second.end())
return nullptr;
return type_iter->second;
}
VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type) const {
if (extracted)
LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
const auto nca = GetNCA(title_id, type);
if (nca != nullptr)
return nca->GetBaseFile();
return nullptr;
}
std::vector<Core::Crypto::Key128> NSP::GetTitlekey() const {
if (extracted)
LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
std::vector<Core::Crypto::Key128> out;
for (const auto& ticket_file : ticket_files) {
if (ticket_file == nullptr ||
ticket_file->GetSize() <
Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) {
continue;
}
out.emplace_back();
ticket_file->Read(out.back().data(), out.back().size(),
Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET);
}
return out;
}
std::vector<VirtualFile> NSP::GetFiles() const {
return pfs->GetFiles();
}
std::vector<VirtualDir> NSP::GetSubdirectories() const {
return pfs->GetSubdirectories();
}
std::string NSP::GetName() const {
return file->GetName();
}
VirtualDir NSP::GetParentDirectory() const {
return file->GetContainingDirectory();
}
bool NSP::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}
} // namespace FileSys

View File

@@ -1,76 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <map>
#include <memory>
#include <vector>
#include "common/common_types.h"
#include "core/file_sys/vfs.h"
namespace Loader {
enum class ResultStatus : u16;
}
namespace FileSys {
class NCA;
class PartitionFilesystem;
enum class ContentRecordType : u8;
class NSP : public ReadOnlyVfsDirectory {
public:
explicit NSP(VirtualFile file);
~NSP();
Loader::ResultStatus GetStatus() const;
Loader::ResultStatus GetProgramStatus(u64 title_id) const;
// Should only be used when one title id can be assured.
u64 GetFirstTitleID() const;
u64 GetProgramTitleID() const;
std::vector<u64> GetTitleIDs() const;
bool IsExtractedType() const;
// Common (Can be safely called on both types)
VirtualFile GetRomFS() const;
VirtualDir GetExeFS() const;
// Type 0 Only (Collection of NCAs + Certificate + Ticket + Meta XML)
std::vector<std::shared_ptr<NCA>> GetNCAsCollapsed() const;
std::multimap<u64, std::shared_ptr<NCA>> GetNCAsByTitleID() const;
std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> GetNCAs() const;
std::shared_ptr<NCA> GetNCA(u64 title_id, ContentRecordType type) const;
VirtualFile GetNCAFile(u64 title_id, ContentRecordType type) const;
std::vector<Core::Crypto::Key128> GetTitlekey() const;
std::vector<VirtualFile> GetFiles() const override;
std::vector<VirtualDir> GetSubdirectories() const override;
std::string GetName() const override;
VirtualDir GetParentDirectory() const override;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
VirtualFile file;
bool extracted;
Loader::ResultStatus status;
std::map<u64, Loader::ResultStatus> program_status;
std::shared_ptr<PartitionFilesystem> pfs;
// Map title id -> {map type -> NCA}
std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> ncas;
std::vector<VirtualFile> ticket_files;
VirtualFile romfs;
VirtualDir exefs;
};
} // namespace FileSys

View File

@@ -8,7 +8,6 @@
#include <utility>
#include "common/assert.h"
#include "common/common_paths.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "core/file_sys/vfs_real.h"
@@ -40,7 +39,6 @@ static std::string ModeFlagsToString(Mode mode) {
}
RealVfsFilesystem::RealVfsFilesystem() : VfsFilesystem(nullptr) {}
RealVfsFilesystem::~RealVfsFilesystem() = default;
std::string RealVfsFilesystem::GetName() const {
return "Real";
@@ -221,8 +219,6 @@ RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FileUtil::IOF
parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
perms(perms_) {}
RealVfsFile::~RealVfsFile() = default;
std::string RealVfsFile::GetName() const {
return path_components.back();
}
@@ -316,8 +312,6 @@ RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string&
FileUtil::CreateDir(path);
}
RealVfsDirectory::~RealVfsDirectory() = default;
std::shared_ptr<VfsFile> RealVfsDirectory::GetFileRelative(std::string_view path) const {
const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
if (!FileUtil::Exists(full_path) || FileUtil::IsDirectory(full_path))

View File

@@ -6,19 +6,15 @@
#include <string_view>
#include <boost/container/flat_map.hpp>
#include "common/file_util.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/vfs.h"
namespace FileUtil {
class IOFile;
}
namespace FileSys {
class RealVfsFilesystem : public VfsFilesystem {
public:
RealVfsFilesystem();
~RealVfsFilesystem() override;
std::string GetName() const override;
bool IsReadable() const override;
@@ -44,9 +40,10 @@ class RealVfsFile : public VfsFile {
friend class RealVfsDirectory;
friend class RealVfsFilesystem;
public:
~RealVfsFile() override;
RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<FileUtil::IOFile> backing,
const std::string& path, Mode perms = Mode::Read);
public:
std::string GetName() const override;
size_t GetSize() const override;
bool Resize(size_t new_size) override;
@@ -58,9 +55,6 @@ public:
bool Rename(std::string_view name) override;
private:
RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<FileUtil::IOFile> backing,
const std::string& path, Mode perms = Mode::Read);
bool Close();
RealVfsFilesystem& base;
@@ -76,9 +70,9 @@ private:
class RealVfsDirectory : public VfsDirectory {
friend class RealVfsFilesystem;
public:
~RealVfsDirectory() override;
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read);
public:
std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const override;
std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const override;
std::shared_ptr<VfsFile> GetFile(std::string_view name) const override;
@@ -103,8 +97,6 @@ protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read);
template <typename T, typename R>
std::vector<std::shared_ptr<R>> IterateEntries() const;

View File

@@ -10,7 +10,6 @@
#include <mbedtls/md.h>
#include <mbedtls/sha256.h>
#include "common/assert.h"
#include "common/file_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "core/crypto/aes_util.h"

View File

@@ -12,7 +12,6 @@
#include <utility>
#include "common/assert.h"
#include "common/common_types.h"
#include "core/core.h"
#include "core/hle/ipc.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
@@ -136,9 +135,7 @@ public:
if (context->Session()->IsDomain()) {
context->AddDomainObject(std::move(iface));
} else {
auto& kernel = Core::System::GetInstance().Kernel();
auto sessions =
Kernel::ServerSession::CreateSessionPair(kernel, iface->GetServiceName());
auto sessions = Kernel::ServerSession::CreateSessionPair(iface->GetServiceName());
auto server = std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions);
auto client = std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions);
iface->ClientConnected(server);

View File

@@ -8,11 +8,9 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/result.h"
#include "core/memory.h"

View File

@@ -14,7 +14,7 @@
namespace Kernel {
ClientPort::ClientPort(KernelCore& kernel) : Object{kernel} {}
ClientPort::ClientPort() = default;
ClientPort::~ClientPort() = default;
ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
@@ -27,7 +27,7 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
active_sessions++;
// Create a new session pair, let the created sessions inherit the parent port's HLE handler.
auto sessions = ServerSession::CreateSessionPair(kernel, server_port->GetName(), this);
auto sessions = ServerSession::CreateSessionPair(server_port->GetName(), this);
if (server_port->hle_handler)
server_port->hle_handler->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions));

View File

@@ -11,9 +11,8 @@
namespace Kernel {
class ClientSession;
class KernelCore;
class ServerPort;
class ClientSession;
class ClientPort final : public Object {
public:
@@ -45,7 +44,7 @@ public:
void ConnectionClosed();
private:
explicit ClientPort(KernelCore& kernel);
ClientPort();
~ClientPort() override;
SharedPtr<ServerPort> server_port; ///< ServerPort associated with this client port.

View File

@@ -11,7 +11,7 @@
namespace Kernel {
ClientSession::ClientSession(KernelCore& kernel) : Object{kernel} {}
ClientSession::ClientSession() = default;
ClientSession::~ClientSession() {
// This destructor will be called automatically when the last ClientSession handle is closed by
// the emulated application.

View File

@@ -12,9 +12,8 @@
namespace Kernel {
class KernelCore;
class Session;
class ServerSession;
class Session;
class Thread;
class ClientSession final : public Object {
@@ -42,7 +41,7 @@ public:
std::shared_ptr<Session> parent;
private:
explicit ClientSession(KernelCore& kernel);
ClientSession();
~ClientSession() override;
};

View File

@@ -10,11 +10,11 @@
namespace Kernel {
Event::Event(KernelCore& kernel) : WaitObject{kernel} {}
Event::~Event() = default;
Event::Event() {}
Event::~Event() {}
SharedPtr<Event> Event::Create(KernelCore& kernel, ResetType reset_type, std::string name) {
SharedPtr<Event> evt(new Event(kernel));
SharedPtr<Event> Event::Create(ResetType reset_type, std::string name) {
SharedPtr<Event> evt(new Event);
evt->signaled = false;
evt->reset_type = reset_type;

View File

@@ -10,18 +10,14 @@
namespace Kernel {
class KernelCore;
class Event final : public WaitObject {
public:
/**
* Creates an event
* @param kernel The kernel instance to create this event under.
* @param reset_type ResetType describing how to create event
* @param name Optional name of event
*/
static SharedPtr<Event> Create(KernelCore& kernel, ResetType reset_type,
std::string name = "Unknown");
static SharedPtr<Event> Create(ResetType reset_type, std::string name = "Unknown");
std::string GetTypeName() const override {
return "Event";
@@ -48,7 +44,7 @@ public:
void Clear();
private:
explicit Event(KernelCore& kernel);
Event();
~Event() override;
ResetType reset_type; ///< Current ResetType

View File

@@ -13,6 +13,8 @@
namespace Kernel {
HandleTable g_handle_table;
HandleTable::HandleTable() {
next_generation = 1;
Clear();

View File

@@ -121,4 +121,6 @@ private:
u16 next_free_slot;
};
extern HandleTable g_handle_table;
} // namespace Kernel

View File

@@ -13,12 +13,10 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/server_session.h"
@@ -53,9 +51,7 @@ SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
if (!event) {
// Create event if not provided
auto& kernel = Core::System::GetInstance().Kernel();
event =
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "HLE Pause Event: " + reason);
event = Kernel::Event::Create(Kernel::ResetType::OneShot, "HLE Pause Event: " + reason);
}
event->Clear();
@@ -94,14 +90,12 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
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>()));
copy_objects.push_back(Kernel::g_handle_table.GetGeneric(rp.Pop<Handle>()));
}
for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_move; ++handle) {
move_objects.push_back(handle_table.GetGeneric(rp.Pop<Handle>()));
move_objects.push_back(Kernel::g_handle_table.GetGeneric(rp.Pop<Handle>()));
}
} else {
// For responses we just ignore the handles, they're empty and will be populated when
@@ -236,19 +230,17 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const 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.
for (auto& object : copy_objects) {
ASSERT(object != nullptr);
dst_cmdbuf[current_offset++] = handle_table.Create(object).Unwrap();
dst_cmdbuf[current_offset++] = Kernel::g_handle_table.Create(object).Unwrap();
}
for (auto& object : move_objects) {
ASSERT(object != nullptr);
dst_cmdbuf[current_offset++] = handle_table.Create(object).Unwrap();
dst_cmdbuf[current_offset++] = Kernel::g_handle_table.Create(object).Unwrap();
}
}

View File

@@ -2,329 +2,38 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <atomic>
#include <memory>
#include <mutex>
#include <utility>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/timer.h"
#include "core/hle/lock.h"
#include "core/hle/result.h"
namespace Kernel {
/**
* Callback that will wake up the thread it was scheduled for
* @param thread_handle The handle of the thread that's been awoken
* @param cycles_late The number of CPU cycles that have passed since the desired wakeup time
*/
static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_late) {
const auto proper_handle = static_cast<Handle>(thread_handle);
auto& system = Core::System::GetInstance();
std::atomic<u32> Object::next_object_id{0};
// Lock the global kernel mutex when we enter the kernel HLE.
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
/// Initialize the kernel
void Init() {
Kernel::ResourceLimitsInit();
Kernel::ThreadingInit();
Kernel::TimersInit();
SharedPtr<Thread> thread =
system.Kernel().RetrieveThreadFromWakeupCallbackHandleTable(proper_handle);
if (thread == nullptr) {
LOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", proper_handle);
return;
}
bool resume = true;
if (thread->status == ThreadStatus::WaitSynchAny ||
thread->status == ThreadStatus::WaitSynchAll ||
thread->status == ThreadStatus::WaitHLEEvent) {
// Remove the thread from each of its waiting objects' waitlists
for (auto& object : thread->wait_objects) {
object->RemoveWaitingThread(thread.get());
}
thread->wait_objects.clear();
// Invoke the wakeup callback before clearing the wait objects
if (thread->wakeup_callback) {
resume = thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr, 0);
}
}
if (thread->mutex_wait_address != 0 || thread->condvar_wait_address != 0 ||
thread->wait_handle) {
ASSERT(thread->status == ThreadStatus::WaitMutex);
thread->mutex_wait_address = 0;
thread->condvar_wait_address = 0;
thread->wait_handle = 0;
auto lock_owner = thread->lock_owner;
// Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance
// and don't have a lock owner unless SignalProcessWideKey was called first and the thread
// wasn't awakened due to the mutex already being acquired.
if (lock_owner) {
lock_owner->RemoveMutexWaiter(thread);
}
}
if (thread->arb_wait_address != 0) {
ASSERT(thread->status == ThreadStatus::WaitArb);
thread->arb_wait_address = 0;
}
if (resume) {
thread->ResumeFromWait();
}
}
/// 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);
auto& system = Core::System::GetInstance();
SharedPtr<Timer> timer = system.Kernel().RetrieveTimerFromCallbackHandleTable(proper_handle);
if (timer == nullptr) {
LOG_CRITICAL(Kernel, "Callback fired for invalid timer {:016X}", timer_handle);
return;
}
timer->Signal(cycles_late);
}
struct KernelCore::Impl {
void Initialize(KernelCore& kernel) {
Shutdown();
InitializeResourceLimits(kernel);
InitializeThreads();
InitializeTimers();
}
void Shutdown() {
next_object_id = 0;
next_process_id = 10;
next_thread_id = 1;
process_list.clear();
current_process.reset();
handle_table.Clear();
resource_limits.fill(nullptr);
thread_wakeup_callback_handle_table.Clear();
thread_wakeup_event_type = nullptr;
timer_callback_handle_table.Clear();
timer_callback_event_type = nullptr;
named_ports.clear();
}
void InitializeResourceLimits(KernelCore& kernel) {
// Create the four resource limits that the system uses
// Create the APPLICATION resource limit
SharedPtr<ResourceLimit> resource_limit = ResourceLimit::Create(kernel, "Applications");
resource_limit->max_priority = 0x18;
resource_limit->max_commit = 0x4000000;
resource_limit->max_threads = 0x20;
resource_limit->max_events = 0x20;
resource_limit->max_mutexes = 0x20;
resource_limit->max_semaphores = 0x8;
resource_limit->max_timers = 0x8;
resource_limit->max_shared_mems = 0x10;
resource_limit->max_address_arbiters = 0x2;
resource_limit->max_cpu_time = 0x1E;
resource_limits[static_cast<u8>(ResourceLimitCategory::APPLICATION)] = resource_limit;
// Create the SYS_APPLET resource limit
resource_limit = ResourceLimit::Create(kernel, "System Applets");
resource_limit->max_priority = 0x4;
resource_limit->max_commit = 0x5E00000;
resource_limit->max_threads = 0x1D;
resource_limit->max_events = 0xB;
resource_limit->max_mutexes = 0x8;
resource_limit->max_semaphores = 0x4;
resource_limit->max_timers = 0x4;
resource_limit->max_shared_mems = 0x8;
resource_limit->max_address_arbiters = 0x3;
resource_limit->max_cpu_time = 0x2710;
resource_limits[static_cast<u8>(ResourceLimitCategory::SYS_APPLET)] = resource_limit;
// Create the LIB_APPLET resource limit
resource_limit = ResourceLimit::Create(kernel, "Library Applets");
resource_limit->max_priority = 0x4;
resource_limit->max_commit = 0x600000;
resource_limit->max_threads = 0xE;
resource_limit->max_events = 0x8;
resource_limit->max_mutexes = 0x8;
resource_limit->max_semaphores = 0x4;
resource_limit->max_timers = 0x4;
resource_limit->max_shared_mems = 0x8;
resource_limit->max_address_arbiters = 0x1;
resource_limit->max_cpu_time = 0x2710;
resource_limits[static_cast<u8>(ResourceLimitCategory::LIB_APPLET)] = resource_limit;
// Create the OTHER resource limit
resource_limit = ResourceLimit::Create(kernel, "Others");
resource_limit->max_priority = 0x4;
resource_limit->max_commit = 0x2180000;
resource_limit->max_threads = 0xE1;
resource_limit->max_events = 0x108;
resource_limit->max_mutexes = 0x25;
resource_limit->max_semaphores = 0x43;
resource_limit->max_timers = 0x2C;
resource_limit->max_shared_mems = 0x1F;
resource_limit->max_address_arbiters = 0x2D;
resource_limit->max_cpu_time = 0x3E8;
resource_limits[static_cast<u8>(ResourceLimitCategory::OTHER)] = resource_limit;
}
void InitializeThreads() {
thread_wakeup_event_type =
CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
}
void InitializeTimers() {
timer_callback_handle_table.Clear();
timer_callback_event_type = CoreTiming::RegisterEvent("TimerCallback", TimerCallback);
}
std::atomic<u32> next_object_id{0};
Object::next_object_id = 0;
// TODO(Subv): Start the process ids from 10 for now, as lower PIDs are
// reserved for low-level services
std::atomic<u32> next_process_id{10};
std::atomic<u32> next_thread_id{1};
// Lists all processes that exist in the current session.
std::vector<SharedPtr<Process>> process_list;
SharedPtr<Process> current_process;
Kernel::HandleTable handle_table;
std::array<SharedPtr<ResourceLimit>, 4> resource_limits;
/// The event type of the generic timer callback event
CoreTiming::EventType* timer_callback_event_type = nullptr;
// TODO(yuriks): This can be removed if Timer objects are explicitly pooled in the future,
// allowing us to simply use a pool index or similar.
Kernel::HandleTable timer_callback_handle_table;
CoreTiming::EventType* thread_wakeup_event_type = nullptr;
// TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future,
// allowing us to simply use a pool index or similar.
Kernel::HandleTable thread_wakeup_callback_handle_table;
/// Map of named ports managed by the kernel, which can be retrieved using
/// the ConnectToPort SVC.
NamedPortTable named_ports;
};
KernelCore::KernelCore() : impl{std::make_unique<Impl>()} {}
KernelCore::~KernelCore() {
Shutdown();
Process::next_process_id = 10;
}
void KernelCore::Initialize() {
impl->Initialize(*this);
}
/// Shutdown the kernel
void Shutdown() {
// Free all kernel objects
g_handle_table.Clear();
void KernelCore::Shutdown() {
impl->Shutdown();
}
Kernel::ThreadingShutdown();
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));
}
SharedPtr<Thread> KernelCore::RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const {
return impl->thread_wakeup_callback_handle_table.Get<Thread>(handle);
}
SharedPtr<Timer> KernelCore::RetrieveTimerFromCallbackHandleTable(Handle handle) const {
return impl->timer_callback_handle_table.Get<Timer>(handle);
}
void KernelCore::AppendNewProcess(SharedPtr<Process> process) {
impl->process_list.push_back(std::move(process));
}
void KernelCore::MakeCurrentProcess(SharedPtr<Process> process) {
impl->current_process = std::move(process);
}
SharedPtr<Process>& KernelCore::CurrentProcess() {
return impl->current_process;
}
const SharedPtr<Process>& KernelCore::CurrentProcess() const {
return impl->current_process;
}
void KernelCore::AddNamedPort(std::string name, SharedPtr<ClientPort> port) {
impl->named_ports.emplace(std::move(name), std::move(port));
}
KernelCore::NamedPortTable::iterator KernelCore::FindNamedPort(const std::string& name) {
return impl->named_ports.find(name);
}
KernelCore::NamedPortTable::const_iterator KernelCore::FindNamedPort(
const std::string& name) const {
return impl->named_ports.find(name);
}
bool KernelCore::IsValidNamedPort(NamedPortTable::const_iterator port) const {
return port != impl->named_ports.cend();
}
u32 KernelCore::CreateNewObjectID() {
return impl->next_object_id++;
}
u32 KernelCore::CreateNewThreadID() {
return impl->next_thread_id++;
}
u32 KernelCore::CreateNewProcessID() {
return impl->next_process_id++;
}
ResultVal<Handle> KernelCore::CreateTimerCallbackHandle(const SharedPtr<Timer>& timer) {
return impl->timer_callback_handle_table.Create(timer);
}
CoreTiming::EventType* KernelCore::ThreadWakeupCallbackEventType() const {
return impl->thread_wakeup_event_type;
}
CoreTiming::EventType* KernelCore::TimerCallbackEventType() const {
return impl->timer_callback_event_type;
}
Kernel::HandleTable& KernelCore::ThreadWakeupCallbackHandleTable() {
return impl->thread_wakeup_callback_handle_table;
}
const Kernel::HandleTable& KernelCore::ThreadWakeupCallbackHandleTable() const {
return impl->thread_wakeup_callback_handle_table;
Kernel::TimersShutdown();
Kernel::ResourceLimitsShutdown();
}
} // namespace Kernel

View File

@@ -4,120 +4,14 @@
#pragma once
#include <string>
#include <unordered_map>
#include "core/hle/kernel/object.h"
template <typename T>
class ResultVal;
namespace CoreTiming {
struct EventType;
}
#include "common/common_types.h"
namespace Kernel {
class ClientPort;
class HandleTable;
class Process;
class ResourceLimit;
class Thread;
class Timer;
/// Initialize the kernel with the specified system mode.
void Init();
enum class ResourceLimitCategory : u8;
/// Represents a single instance of the kernel.
class KernelCore {
private:
using NamedPortTable = std::unordered_map<std::string, SharedPtr<ClientPort>>;
public:
KernelCore();
~KernelCore();
KernelCore(const KernelCore&) = delete;
KernelCore& operator=(const KernelCore&) = delete;
KernelCore(KernelCore&&) = delete;
KernelCore& operator=(KernelCore&&) = delete;
/// Resets the kernel to a clean slate for use.
void Initialize();
/// 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;
/// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table.
SharedPtr<Thread> RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const;
/// Retrieves a shared pointer to a Timer instance within the timer callback handle table.
SharedPtr<Timer> RetrieveTimerFromCallbackHandleTable(Handle handle) const;
/// Adds the given shared pointer to an internal list of active processes.
void AppendNewProcess(SharedPtr<Process> process);
/// Makes the given process the new current process.
void MakeCurrentProcess(SharedPtr<Process> process);
/// Retrieves a reference to the current process.
SharedPtr<Process>& CurrentProcess();
/// Retrieves a const reference to the current process.
const SharedPtr<Process>& CurrentProcess() const;
/// Adds a port to the named port table
void AddNamedPort(std::string name, SharedPtr<ClientPort> port);
/// Finds a port within the named port table with the given name.
NamedPortTable::iterator FindNamedPort(const std::string& name);
/// Finds a port within the named port table with the given name.
NamedPortTable::const_iterator FindNamedPort(const std::string& name) const;
/// Determines whether or not the given port is a valid named port.
bool IsValidNamedPort(NamedPortTable::const_iterator port) const;
private:
friend class Object;
friend class Process;
friend class Thread;
friend class Timer;
/// Creates a new object ID, incrementing the internal object ID counter.
u32 CreateNewObjectID();
/// Creates a new process ID, incrementing the internal process ID counter;
u32 CreateNewProcessID();
/// Creates a new thread ID, incrementing the internal thread ID counter.
u32 CreateNewThreadID();
/// Creates a timer callback handle for the given timer.
ResultVal<Handle> CreateTimerCallbackHandle(const SharedPtr<Timer>& timer);
/// Retrieves the event type used for thread wakeup callbacks.
CoreTiming::EventType* ThreadWakeupCallbackEventType() const;
/// Retrieves the event type used for timer callbacks.
CoreTiming::EventType* TimerCallbackEventType() const;
/// Provides a reference to the thread wakeup callback handle table.
Kernel::HandleTable& ThreadWakeupCallbackHandleTable();
/// Provides a const reference to the thread wakeup callback handle table.
const Kernel::HandleTable& ThreadWakeupCallbackHandleTable() const;
struct Impl;
std::unique_ptr<Impl> impl;
};
/// Shutdown the kernel
void Shutdown();
} // namespace Kernel

View File

@@ -58,15 +58,15 @@ static void TransferMutexOwnership(VAddr mutex_addr, SharedPtr<Thread> current_t
}
}
ResultCode Mutex::TryAcquire(HandleTable& handle_table, VAddr address, Handle holding_thread_handle,
ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
Handle requesting_thread_handle) {
// The mutex address must be 4-byte aligned
if ((address % sizeof(u32)) != 0) {
return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidAddress);
}
SharedPtr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle);
SharedPtr<Thread> requesting_thread = handle_table.Get<Thread>(requesting_thread_handle);
SharedPtr<Thread> holding_thread = g_handle_table.Get<Thread>(holding_thread_handle);
SharedPtr<Thread> requesting_thread = g_handle_table.Get<Thread>(requesting_thread_handle);
// TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of another
// thread.

View File

@@ -11,7 +11,6 @@ union ResultCode;
namespace Kernel {
class HandleTable;
class Thread;
class Mutex final {
@@ -22,8 +21,8 @@ public:
static constexpr u32 MutexOwnerMask = 0xBFFFFFFF;
/// Attempts to acquire a mutex at the specified address.
static ResultCode TryAcquire(HandleTable& handle_table, VAddr address,
Handle holding_thread_handle, Handle requesting_thread_handle);
static ResultCode TryAcquire(VAddr address, Handle holding_thread_handle,
Handle requesting_thread_handle);
/// Releases the mutex at the specified address.
static ResultCode Release(VAddr address);

View File

@@ -3,12 +3,10 @@
// Refer to the license.txt file included.
#include "common/assert.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
namespace Kernel {
Object::Object(KernelCore& kernel) : kernel{kernel}, object_id{kernel.CreateNewObjectID()} {}
Object::~Object() = default;
bool Object::IsWaitable() const {

View File

@@ -14,8 +14,6 @@
namespace Kernel {
class KernelCore;
using Handle = u32;
enum class HandleType : u32 {
@@ -42,7 +40,6 @@ enum class ResetType {
class Object : NonCopyable {
public:
explicit Object(KernelCore& kernel);
virtual ~Object();
/// Returns a unique identifier for the object. For debugging purposes only.
@@ -64,16 +61,15 @@ public:
*/
bool IsWaitable() const;
protected:
/// The kernel instance this object was created under.
KernelCore& kernel;
public:
static std::atomic<u32> next_object_id;
private:
friend void intrusive_ptr_add_ref(Object*);
friend void intrusive_ptr_release(Object*);
std::atomic<u32> ref_count{0};
std::atomic<u32> object_id{0};
std::atomic<u32> object_id{next_object_id++};
};
// Special functions used by boost::instrusive_ptr to do automatic ref-counting

View File

@@ -8,7 +8,6 @@
#include "common/common_funcs.h"
#include "common/logging/log.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"
#include "core/hle/kernel/thread.h"
@@ -17,26 +16,30 @@
namespace Kernel {
SharedPtr<CodeSet> CodeSet::Create(KernelCore& kernel, std::string name) {
SharedPtr<CodeSet> codeset(new CodeSet(kernel));
// Lists all processes that exist in the current session.
static std::vector<SharedPtr<Process>> process_list;
SharedPtr<CodeSet> CodeSet::Create(std::string name) {
SharedPtr<CodeSet> codeset(new CodeSet);
codeset->name = std::move(name);
return codeset;
}
CodeSet::CodeSet(KernelCore& kernel) : Object{kernel} {}
CodeSet::~CodeSet() = default;
CodeSet::CodeSet() {}
CodeSet::~CodeSet() {}
SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
SharedPtr<Process> process(new Process(kernel));
u32 Process::next_process_id;
SharedPtr<Process> Process::Create(std::string&& name) {
SharedPtr<Process> process(new Process);
process->name = std::move(name);
process->flags.raw = 0;
process->flags.memory_region.Assign(MemoryRegion::APPLICATION);
process->status = ProcessStatus::Created;
process->program_id = 0;
process->process_id = kernel.CreateNewProcessID();
kernel.AppendNewProcess(process);
process_list.push_back(process);
return process;
}
@@ -125,7 +128,7 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
vm_manager.LogLayout();
status = ProcessStatus::Running;
Kernel::SetupMainThread(kernel, entry_point, main_thread_priority, this);
Kernel::SetupMainThread(entry_point, main_thread_priority, this);
}
void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) {
@@ -228,7 +231,22 @@ ResultCode Process::UnmapMemory(VAddr dst_addr, VAddr /*src_addr*/, u64 size) {
return vm_manager.UnmapRange(dst_addr, size);
}
Kernel::Process::Process(KernelCore& kernel) : Object{kernel} {}
Kernel::Process::Process() {}
Kernel::Process::~Process() {}
void ClearProcessList() {
process_list.clear();
}
SharedPtr<Process> GetProcessById(u32 process_id) {
auto itr = std::find_if(
process_list.begin(), process_list.end(),
[&](const SharedPtr<Process>& process) { return process->process_id == process_id; });
if (itr == process_list.end())
return nullptr;
return *itr;
}
} // namespace Kernel

View File

@@ -19,8 +19,6 @@
namespace Kernel {
class KernelCore;
struct AddressMapping {
// Address and size must be page-aligned
VAddr address;
@@ -64,7 +62,7 @@ struct CodeSet final : public Object {
u32 size = 0;
};
static SharedPtr<CodeSet> Create(KernelCore& kernel, std::string name);
static SharedPtr<CodeSet> Create(std::string name);
std::string GetTypeName() const override {
return "CodeSet";
@@ -111,13 +109,13 @@ struct CodeSet final : public Object {
std::string name;
private:
explicit CodeSet(KernelCore& kernel);
CodeSet();
~CodeSet() override;
};
class Process final : public Object {
public:
static SharedPtr<Process> Create(KernelCore& kernel, std::string&& name);
static SharedPtr<Process> Create(std::string&& name);
std::string GetTypeName() const override {
return "Process";
@@ -131,6 +129,8 @@ public:
return HANDLE_TYPE;
}
static u32 next_process_id;
/// Title ID corresponding to the process
u64 program_id;
@@ -157,8 +157,8 @@ public:
/// Current status of the process
ProcessStatus status;
/// The ID of this process
u32 process_id = 0;
/// The id of this process
u32 process_id = next_process_id++;
/**
* Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them
@@ -206,8 +206,13 @@ public:
ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);
private:
explicit Process(KernelCore& kernel);
Process();
~Process() override;
};
void ClearProcessList();
/// Retrieves a process from the current list of processes.
SharedPtr<Process> GetProcessById(u32 process_id);
} // namespace Kernel

View File

@@ -9,16 +9,31 @@
namespace Kernel {
ResourceLimit::ResourceLimit(KernelCore& kernel) : Object{kernel} {}
ResourceLimit::~ResourceLimit() = default;
static SharedPtr<ResourceLimit> resource_limits[4];
SharedPtr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel, std::string name) {
SharedPtr<ResourceLimit> resource_limit(new ResourceLimit(kernel));
ResourceLimit::ResourceLimit() {}
ResourceLimit::~ResourceLimit() {}
SharedPtr<ResourceLimit> ResourceLimit::Create(std::string name) {
SharedPtr<ResourceLimit> resource_limit(new ResourceLimit);
resource_limit->name = std::move(name);
return resource_limit;
}
SharedPtr<ResourceLimit> ResourceLimit::GetForCategory(ResourceLimitCategory category) {
switch (category) {
case ResourceLimitCategory::APPLICATION:
case ResourceLimitCategory::SYS_APPLET:
case ResourceLimitCategory::LIB_APPLET:
case ResourceLimitCategory::OTHER:
return resource_limits[static_cast<u8>(category)];
default:
LOG_CRITICAL(Kernel, "Unknown resource limit category");
UNREACHABLE();
}
}
s32 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const {
switch (resource) {
case ResourceType::Commit:
@@ -74,4 +89,66 @@ u32 ResourceLimit::GetMaxResourceValue(ResourceType resource) const {
return 0;
}
}
void ResourceLimitsInit() {
// Create the four resource limits that the system uses
// Create the APPLICATION resource limit
SharedPtr<ResourceLimit> resource_limit = ResourceLimit::Create("Applications");
resource_limit->max_priority = 0x18;
resource_limit->max_commit = 0x4000000;
resource_limit->max_threads = 0x20;
resource_limit->max_events = 0x20;
resource_limit->max_mutexes = 0x20;
resource_limit->max_semaphores = 0x8;
resource_limit->max_timers = 0x8;
resource_limit->max_shared_mems = 0x10;
resource_limit->max_address_arbiters = 0x2;
resource_limit->max_cpu_time = 0x1E;
resource_limits[static_cast<u8>(ResourceLimitCategory::APPLICATION)] = resource_limit;
// Create the SYS_APPLET resource limit
resource_limit = ResourceLimit::Create("System Applets");
resource_limit->max_priority = 0x4;
resource_limit->max_commit = 0x5E00000;
resource_limit->max_threads = 0x1D;
resource_limit->max_events = 0xB;
resource_limit->max_mutexes = 0x8;
resource_limit->max_semaphores = 0x4;
resource_limit->max_timers = 0x4;
resource_limit->max_shared_mems = 0x8;
resource_limit->max_address_arbiters = 0x3;
resource_limit->max_cpu_time = 0x2710;
resource_limits[static_cast<u8>(ResourceLimitCategory::SYS_APPLET)] = resource_limit;
// Create the LIB_APPLET resource limit
resource_limit = ResourceLimit::Create("Library Applets");
resource_limit->max_priority = 0x4;
resource_limit->max_commit = 0x600000;
resource_limit->max_threads = 0xE;
resource_limit->max_events = 0x8;
resource_limit->max_mutexes = 0x8;
resource_limit->max_semaphores = 0x4;
resource_limit->max_timers = 0x4;
resource_limit->max_shared_mems = 0x8;
resource_limit->max_address_arbiters = 0x1;
resource_limit->max_cpu_time = 0x2710;
resource_limits[static_cast<u8>(ResourceLimitCategory::LIB_APPLET)] = resource_limit;
// Create the OTHER resource limit
resource_limit = ResourceLimit::Create("Others");
resource_limit->max_priority = 0x4;
resource_limit->max_commit = 0x2180000;
resource_limit->max_threads = 0xE1;
resource_limit->max_events = 0x108;
resource_limit->max_mutexes = 0x25;
resource_limit->max_semaphores = 0x43;
resource_limit->max_timers = 0x2C;
resource_limit->max_shared_mems = 0x1F;
resource_limit->max_address_arbiters = 0x2D;
resource_limit->max_cpu_time = 0x3E8;
resource_limits[static_cast<u8>(ResourceLimitCategory::OTHER)] = resource_limit;
}
void ResourceLimitsShutdown() {}
} // namespace Kernel

View File

@@ -9,8 +9,6 @@
namespace Kernel {
class KernelCore;
enum class ResourceLimitCategory : u8 {
APPLICATION = 0,
SYS_APPLET = 1,
@@ -36,7 +34,14 @@ public:
/**
* Creates a resource limit object.
*/
static SharedPtr<ResourceLimit> Create(KernelCore& kernel, std::string name = "Unknown");
static SharedPtr<ResourceLimit> Create(std::string name = "Unknown");
/**
* Retrieves the resource limit associated with the specified resource limit category.
* @param category The resource limit category
* @returns The resource limit associated with the category
*/
static SharedPtr<ResourceLimit> GetForCategory(ResourceLimitCategory category);
std::string GetTypeName() const override {
return "ResourceLimit";
@@ -108,8 +113,14 @@ public:
s32 current_cpu_time = 0;
private:
explicit ResourceLimit(KernelCore& kernel);
ResourceLimit();
~ResourceLimit() override;
};
/// Initializes the resource limits
void ResourceLimitsInit();
// Destroys the resource limits
void ResourceLimitsShutdown();
} // namespace Kernel

View File

@@ -13,8 +13,8 @@
namespace Kernel {
ServerPort::ServerPort(KernelCore& kernel) : WaitObject{kernel} {}
ServerPort::~ServerPort() = default;
ServerPort::ServerPort() {}
ServerPort::~ServerPort() {}
ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() {
if (pending_sessions.empty()) {
@@ -36,10 +36,10 @@ void ServerPort::Acquire(Thread* thread) {
}
std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> ServerPort::CreatePortPair(
KernelCore& kernel, u32 max_sessions, std::string name) {
u32 max_sessions, std::string name) {
SharedPtr<ServerPort> server_port(new ServerPort(kernel));
SharedPtr<ClientPort> client_port(new ClientPort(kernel));
SharedPtr<ServerPort> server_port(new ServerPort);
SharedPtr<ClientPort> client_port(new ClientPort);
server_port->name = name + "_Server";
client_port->name = name + "_Client";

View File

@@ -15,7 +15,6 @@
namespace Kernel {
class ClientPort;
class KernelCore;
class ServerSession;
class SessionRequestHandler;
@@ -24,13 +23,12 @@ public:
/**
* Creates a pair of ServerPort and an associated ClientPort.
*
* @param kernel The kernel instance to create the port pair under.
* @param max_sessions Maximum number of sessions to the port
* @param name Optional name of the ports
* @return The created port tuple
*/
static std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> CreatePortPair(
KernelCore& kernel, u32 max_sessions, std::string name = "UnknownPort");
u32 max_sessions, std::string name = "UnknownPort");
std::string GetTypeName() const override {
return "ServerPort";
@@ -71,7 +69,7 @@ public:
void Acquire(Thread* thread) override;
private:
explicit ServerPort(KernelCore& kernel);
ServerPort();
~ServerPort() override;
};

View File

@@ -13,7 +13,6 @@
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/session.h"
@@ -21,7 +20,7 @@
namespace Kernel {
ServerSession::ServerSession(KernelCore& kernel) : WaitObject{kernel} {}
ServerSession::ServerSession() = default;
ServerSession::~ServerSession() {
// This destructor will be called automatically when the last ServerSession handle is closed by
// the emulated application.
@@ -36,8 +35,8 @@ ServerSession::~ServerSession() {
parent->server = nullptr;
}
ResultVal<SharedPtr<ServerSession>> ServerSession::Create(KernelCore& kernel, std::string name) {
SharedPtr<ServerSession> server_session(new ServerSession(kernel));
ResultVal<SharedPtr<ServerSession>> ServerSession::Create(std::string name) {
SharedPtr<ServerSession> server_session(new ServerSession);
server_session->name = std::move(name);
server_session->parent = nullptr;
@@ -105,10 +104,11 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
// The ServerSession received a sync request, this means that there's new data available
// from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or
// similar.
Kernel::HLERequestContext context(this);
u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress());
context.PopulateFromIncomingCommandBuffer(cmd_buf, *Core::CurrentProcess(),
kernel.HandleTable());
Kernel::g_handle_table);
ResultCode result = RESULT_SUCCESS;
// If the session has been converted to a domain, handle the domain request
@@ -160,11 +160,10 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
return result;
}
ServerSession::SessionPair ServerSession::CreateSessionPair(KernelCore& kernel,
const std::string& name,
ServerSession::SessionPair ServerSession::CreateSessionPair(const std::string& name,
SharedPtr<ClientPort> port) {
auto server_session = ServerSession::Create(kernel, name + "_Server").Unwrap();
SharedPtr<ClientSession> client_session(new ClientSession(kernel));
auto server_session = ServerSession::Create(name + "_Server").Unwrap();
SharedPtr<ClientSession> client_session(new ClientSession);
client_session->name = name + "_Client";
std::shared_ptr<Session> parent(new Session);

View File

@@ -15,14 +15,13 @@
namespace Kernel {
class ClientPort;
class ClientSession;
class HLERequestContext;
class KernelCore;
class ClientPort;
class ServerSession;
class Session;
class SessionRequestHandler;
class Thread;
class HLERequestContext;
/**
* Kernel object representing the server endpoint of an IPC session. Sessions are the basic CTR-OS
@@ -51,12 +50,11 @@ public:
/**
* Creates a pair of ServerSession and an associated ClientSession.
* @param kernel The kernal instance to create the session pair under.
* @param name Optional name of the ports.
* @param client_port Optional The ClientPort that spawned this session.
* @return The created session tuple
*/
static SessionPair CreateSessionPair(KernelCore& kernel, const std::string& name = "Unknown",
static SessionPair CreateSessionPair(const std::string& name = "Unknown",
SharedPtr<ClientPort> client_port = nullptr);
/**
@@ -113,18 +111,16 @@ public:
}
private:
explicit ServerSession(KernelCore& kernel);
ServerSession();
~ServerSession() override;
/**
* Creates a server session. The server session can have an optional HLE handler,
* which will be invoked to handle the IPC requests that this session receives.
* @param kernel The kernel instance to create this server session under.
* @param name Optional name of the server session.
* @return The created server session
*/
static ResultVal<SharedPtr<ServerSession>> Create(KernelCore& kernel,
std::string name = "Unknown");
static ResultVal<SharedPtr<ServerSession>> Create(std::string name = "Unknown");
/// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an
/// object handle.

View File

@@ -13,14 +13,14 @@
namespace Kernel {
SharedMemory::SharedMemory(KernelCore& kernel) : Object{kernel} {}
SharedMemory::~SharedMemory() = default;
SharedMemory::SharedMemory() {}
SharedMemory::~SharedMemory() {}
SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, SharedPtr<Process> owner_process,
u64 size, MemoryPermission permissions,
SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u64 size,
MemoryPermission permissions,
MemoryPermission other_permissions, VAddr address,
MemoryRegion region, std::string name) {
SharedPtr<SharedMemory> shared_memory(new SharedMemory(kernel));
SharedPtr<SharedMemory> shared_memory(new SharedMemory);
shared_memory->owner_process = std::move(owner_process);
shared_memory->name = std::move(name);
@@ -59,10 +59,12 @@ SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, SharedPtr<Proce
return shared_memory;
}
SharedPtr<SharedMemory> SharedMemory::CreateForApplet(
KernelCore& kernel, std::shared_ptr<std::vector<u8>> heap_block, u32 offset, u32 size,
MemoryPermission permissions, MemoryPermission other_permissions, std::string name) {
SharedPtr<SharedMemory> shared_memory(new SharedMemory(kernel));
SharedPtr<SharedMemory> SharedMemory::CreateForApplet(std::shared_ptr<std::vector<u8>> heap_block,
u32 offset, u32 size,
MemoryPermission permissions,
MemoryPermission other_permissions,
std::string name) {
SharedPtr<SharedMemory> shared_memory(new SharedMemory);
shared_memory->owner_process = nullptr;
shared_memory->name = std::move(name);

View File

@@ -15,8 +15,6 @@
namespace Kernel {
class KernelCore;
/// Permissions for mapped shared memory blocks
enum class MemoryPermission : u32 {
None = 0,
@@ -34,7 +32,6 @@ class SharedMemory final : public Object {
public:
/**
* Creates a shared memory object.
* @param kernel The kernel instance to create a shared memory instance under.
* @param owner_process Process that created this shared memory object.
* @param size Size of the memory block. Must be page-aligned.
* @param permissions Permission restrictions applied to the process which created the block.
@@ -45,15 +42,14 @@ public:
* linear heap.
* @param name Optional object name, used for debugging purposes.
*/
static SharedPtr<SharedMemory> Create(KernelCore& kernel, SharedPtr<Process> owner_process,
u64 size, MemoryPermission permissions,
static SharedPtr<SharedMemory> Create(SharedPtr<Process> owner_process, u64 size,
MemoryPermission permissions,
MemoryPermission other_permissions, VAddr address = 0,
MemoryRegion region = MemoryRegion::BASE,
std::string name = "Unknown");
/**
* Creates a shared memory object from a block of memory managed by an HLE applet.
* @param kernel The kernel instance to create a shared memory instance under.
* @param heap_block Heap block of the HLE applet.
* @param offset The offset into the heap block that the SharedMemory will map.
* @param size Size of the memory block. Must be page-aligned.
@@ -62,8 +58,7 @@ public:
* block.
* @param name Optional object name, used for debugging purposes.
*/
static SharedPtr<SharedMemory> CreateForApplet(KernelCore& kernel,
std::shared_ptr<std::vector<u8>> heap_block,
static SharedPtr<SharedMemory> CreateForApplet(std::shared_ptr<std::vector<u8>> heap_block,
u32 offset, u32 size,
MemoryPermission permissions,
MemoryPermission other_permissions,
@@ -130,7 +125,7 @@ public:
std::string name;
private:
explicit SharedMemory(KernelCore& kernel);
SharedMemory();
~SharedMemory() override;
};

View File

@@ -12,20 +12,16 @@
#include "common/logging/log.h"
#include "common/microprofile.h"
#include "common/string_util.h"
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/core_timing.h"
#include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/kernel/svc_wrap.h"
@@ -68,22 +64,19 @@ static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
/// Connect to an OS service given the port name, returns the handle to the port to out
static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address) {
if (!Memory::IsValidVirtualAddress(port_name_address)) {
if (!Memory::IsValidVirtualAddress(port_name_address))
return ERR_NOT_FOUND;
}
static constexpr std::size_t PortNameMaxLength = 11;
// 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) {
if (port_name.size() > PortNameMaxLength)
return ERR_PORT_NAME_TOO_LONG;
}
LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
auto& kernel = Core::System::GetInstance().Kernel();
auto it = kernel.FindNamedPort(port_name);
if (!kernel.IsValidNamedPort(it)) {
auto it = Service::g_kernel_named_ports.find(port_name);
if (it == Service::g_kernel_named_ports.end()) {
LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name);
return ERR_NOT_FOUND;
}
@@ -94,14 +87,13 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address
CASCADE_RESULT(client_session, client_port->Connect());
// Return the client session
CASCADE_RESULT(*out_handle, kernel.HandleTable().Create(client_session));
CASCADE_RESULT(*out_handle, g_handle_table.Create(client_session));
return RESULT_SUCCESS;
}
/// Makes a blocking IPC call to an OS service.
static ResultCode SendSyncRequest(Handle handle) {
auto& kernel = Core::System::GetInstance().Kernel();
SharedPtr<ClientSession> session = kernel.HandleTable().Get<ClientSession>(handle);
SharedPtr<ClientSession> session = g_handle_table.Get<ClientSession>(handle);
if (!session) {
LOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle);
return ERR_INVALID_HANDLE;
@@ -120,8 +112,7 @@ static ResultCode SendSyncRequest(Handle handle) {
static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) {
LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
auto& kernel = Core::System::GetInstance().Kernel();
const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle);
const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle);
if (!thread) {
return ERR_INVALID_HANDLE;
}
@@ -134,8 +125,7 @@ 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);
auto& kernel = Core::System::GetInstance().Kernel();
const SharedPtr<Process> process = kernel.HandleTable().Get<Process>(process_handle);
const SharedPtr<Process> process = g_handle_table.Get<Process>(process_handle);
if (!process) {
return ERR_INVALID_HANDLE;
}
@@ -178,11 +168,10 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
using ObjectPtr = SharedPtr<WaitObject>;
std::vector<ObjectPtr> objects(handle_count);
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 = kernel.HandleTable().Get<WaitObject>(handle);
const auto object = g_handle_table.Get<WaitObject>(handle);
if (object == nullptr) {
return ERR_INVALID_HANDLE;
@@ -230,8 +219,7 @@ 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);
auto& kernel = Core::System::GetInstance().Kernel();
const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle);
const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle);
if (!thread) {
return ERR_INVALID_HANDLE;
}
@@ -251,9 +239,7 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr,
"requesting_current_thread_handle=0x{:08X}",
holding_thread_handle, mutex_addr, requesting_thread_handle);
auto& handle_table = Core::System::GetInstance().Kernel().HandleTable();
return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle,
requesting_thread_handle);
return Mutex::TryAcquire(mutex_addr, holding_thread_handle, requesting_thread_handle);
}
/// Unlock a mutex
@@ -366,8 +352,7 @@ static ResultCode GetThreadContext(Handle handle, VAddr addr) {
/// Gets the priority for the specified thread
static ResultCode GetThreadPriority(u32* priority, Handle handle) {
auto& kernel = Core::System::GetInstance().Kernel();
const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle);
const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(handle);
if (!thread)
return ERR_INVALID_HANDLE;
@@ -381,8 +366,7 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
return ERR_OUT_OF_RANGE;
}
auto& kernel = Core::System::GetInstance().Kernel();
SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle);
SharedPtr<Thread> thread = g_handle_table.Get<Thread>(handle);
if (!thread)
return ERR_INVALID_HANDLE;
@@ -411,8 +395,7 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
"called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
shared_memory_handle, addr, size, permissions);
auto& kernel = Core::System::GetInstance().Kernel();
auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle);
SharedPtr<SharedMemory> shared_memory = g_handle_table.Get<SharedMemory>(shared_memory_handle);
if (!shared_memory) {
return ERR_INVALID_HANDLE;
}
@@ -440,8 +423,7 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}",
shared_memory_handle, addr, size);
auto& kernel = Core::System::GetInstance().Kernel();
auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle);
SharedPtr<SharedMemory> shared_memory = g_handle_table.Get<SharedMemory>(shared_memory_handle);
return shared_memory->Unmap(Core::CurrentProcess().get(), addr);
}
@@ -449,9 +431,7 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
/// Query process memory
static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/,
Handle process_handle, u64 addr) {
auto& kernel = Core::System::GetInstance().Kernel();
SharedPtr<Process> process = kernel.HandleTable().Get<Process>(process_handle);
SharedPtr<Process> process = g_handle_table.Get<Process>(process_handle);
if (!process) {
return ERR_INVALID_HANDLE;
}
@@ -548,11 +528,10 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
break;
}
auto& kernel = Core::System::GetInstance().Kernel();
CASCADE_RESULT(SharedPtr<Thread> thread,
Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top,
Thread::Create(name, entry_point, priority, arg, processor_id, stack_top,
Core::CurrentProcess()));
CASCADE_RESULT(thread->guest_handle, kernel.HandleTable().Create(thread));
CASCADE_RESULT(thread->guest_handle, g_handle_table.Create(thread));
*out_handle = thread->guest_handle;
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
@@ -569,8 +548,7 @@ 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);
auto& kernel = Core::System::GetInstance().Kernel();
const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle);
const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle);
if (!thread) {
return ERR_INVALID_HANDLE;
}
@@ -617,8 +595,7 @@ 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);
auto& kernel = Core::System::GetInstance().Kernel();
SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle);
SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle);
ASSERT(thread);
CASCADE_CODE(Mutex::Release(mutex_addr));
@@ -727,9 +704,8 @@ 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.
auto& kernel = Core::System::GetInstance().Kernel();
Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
auto owner = kernel.HandleTable().Get<Thread>(owner_handle);
auto owner = g_handle_table.Get<Thread>(owner_handle);
ASSERT(owner);
ASSERT(thread->status == ThreadStatus::WaitMutex);
thread->wakeup_callback = nullptr;
@@ -807,20 +783,14 @@ static u64 GetSystemTick() {
/// Close a handle
static ResultCode CloseHandle(Handle handle) {
LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);
auto& kernel = Core::System::GetInstance().Kernel();
return kernel.HandleTable().Close(handle);
return g_handle_table.Close(handle);
}
/// Reset an event
static ResultCode ResetSignal(Handle handle) {
LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle);
auto& kernel = Core::System::GetInstance().Kernel();
auto event = kernel.HandleTable().Get<Event>(handle);
auto event = g_handle_table.Get<Event>(handle);
ASSERT(event != nullptr);
event->Clear();
return RESULT_SUCCESS;
}
@@ -836,8 +806,7 @@ 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);
auto& kernel = Core::System::GetInstance().Kernel();
const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle);
const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle);
if (!thread) {
return ERR_INVALID_HANDLE;
}
@@ -852,8 +821,7 @@ 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);
auto& kernel = Core::System::GetInstance().Kernel();
const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle);
const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle);
if (!thread) {
return ERR_INVALID_HANDLE;
}
@@ -893,23 +861,19 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
u32 remote_permissions) {
LOG_TRACE(Kernel_SVC, "called, size=0x{:X}, localPerms=0x{:08X}, remotePerms=0x{:08X}", size,
local_permissions, remote_permissions);
auto& kernel = Core::System::GetInstance().Kernel();
auto& handle_table = kernel.HandleTable();
auto shared_mem_handle =
SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size,
auto sharedMemHandle =
SharedMemory::Create(g_handle_table.Get<Process>(KernelHandle::CurrentProcess), size,
static_cast<MemoryPermission>(local_permissions),
static_cast<MemoryPermission>(remote_permissions));
CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle));
CASCADE_RESULT(*handle, g_handle_table.Create(sharedMemHandle));
return RESULT_SUCCESS;
}
static ResultCode ClearEvent(Handle handle) {
LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle);
auto& kernel = Core::System::GetInstance().Kernel();
SharedPtr<Event> evt = kernel.HandleTable().Get<Event>(handle);
SharedPtr<Event> evt = g_handle_table.Get<Event>(handle);
if (evt == nullptr)
return ERR_INVALID_HANDLE;
evt->Clear();

View File

@@ -16,21 +16,22 @@
#include "common/thread_queue_list.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/lock.h"
#include "core/hle/result.h"
#include "core/memory.h"
namespace Kernel {
/// Event type for the thread wake up event
static CoreTiming::EventType* ThreadWakeupEventType = nullptr;
bool Thread::ShouldWait(Thread* thread) const {
return status != ThreadStatus::Dead;
}
@@ -39,17 +40,32 @@ void Thread::Acquire(Thread* thread) {
ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
}
Thread::Thread(KernelCore& kernel) : WaitObject{kernel} {}
Thread::~Thread() = default;
// TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, allowing
// us to simply use a pool index or similar.
static Kernel::HandleTable wakeup_callback_handle_table;
// The first available thread id at startup
static u32 next_thread_id;
/**
* Creates a new thread ID
* @return The new thread ID
*/
inline static u32 const NewThreadId() {
return next_thread_id++;
}
Thread::Thread() {}
Thread::~Thread() {}
void Thread::Stop() {
// Cancel any outstanding wakeup events for this thread
CoreTiming::UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), callback_handle);
kernel.ThreadWakeupCallbackHandleTable().Close(callback_handle);
CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle);
wakeup_callback_handle_table.Close(callback_handle);
callback_handle = 0;
// Clean up thread from ready queue
// This is only needed when the thread is terminated forcefully (SVC TerminateProcess)
// This is only needed when the thread is termintated forcefully (SVC TerminateProcess)
if (status == ThreadStatus::Ready) {
scheduler->UnscheduleThread(this, current_priority);
}
@@ -82,6 +98,63 @@ void ExitCurrentThread() {
Core::System::GetInstance().CurrentScheduler().RemoveThread(thread);
}
/**
* Callback that will wake up the thread it was scheduled for
* @param thread_handle The handle of the thread that's been awoken
* @param cycles_late The number of CPU cycles that have passed since the desired wakeup time
*/
static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
const auto proper_handle = static_cast<Handle>(thread_handle);
// Lock the global kernel mutex when we enter the kernel HLE.
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
SharedPtr<Thread> thread = wakeup_callback_handle_table.Get<Thread>(proper_handle);
if (thread == nullptr) {
LOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", proper_handle);
return;
}
bool resume = true;
if (thread->status == ThreadStatus::WaitSynchAny ||
thread->status == ThreadStatus::WaitSynchAll ||
thread->status == ThreadStatus::WaitHLEEvent) {
// Remove the thread from each of its waiting objects' waitlists
for (auto& object : thread->wait_objects)
object->RemoveWaitingThread(thread.get());
thread->wait_objects.clear();
// Invoke the wakeup callback before clearing the wait objects
if (thread->wakeup_callback)
resume = thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr, 0);
}
if (thread->mutex_wait_address != 0 || thread->condvar_wait_address != 0 ||
thread->wait_handle) {
ASSERT(thread->status == ThreadStatus::WaitMutex);
thread->mutex_wait_address = 0;
thread->condvar_wait_address = 0;
thread->wait_handle = 0;
auto lock_owner = thread->lock_owner;
// Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance
// and don't have a lock owner unless SignalProcessWideKey was called first and the thread
// wasn't awakened due to the mutex already being acquired.
if (lock_owner) {
lock_owner->RemoveMutexWaiter(thread);
}
}
if (thread->arb_wait_address != 0) {
ASSERT(thread->status == ThreadStatus::WaitArb);
thread->arb_wait_address = 0;
}
if (resume)
thread->ResumeFromWait();
}
void Thread::WakeAfterDelay(s64 nanoseconds) {
// Don't schedule a wakeup if the thread wants to wait forever
if (nanoseconds == -1)
@@ -89,12 +162,12 @@ void Thread::WakeAfterDelay(s64 nanoseconds) {
// This function might be called from any thread so we have to be cautious and use the
// thread-safe version of ScheduleEvent.
CoreTiming::ScheduleEventThreadsafe(CoreTiming::nsToCycles(nanoseconds),
kernel.ThreadWakeupCallbackEventType(), callback_handle);
CoreTiming::ScheduleEventThreadsafe(CoreTiming::nsToCycles(nanoseconds), ThreadWakeupEventType,
callback_handle);
}
void Thread::CancelWakeupTimer() {
CoreTiming::UnscheduleEventThreadsafe(kernel.ThreadWakeupCallbackEventType(), callback_handle);
CoreTiming::UnscheduleEventThreadsafe(ThreadWakeupEventType, callback_handle);
}
static boost::optional<s32> GetNextProcessorId(u64 mask) {
@@ -221,9 +294,9 @@ static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, VAdd
context.fpscr = 0;
}
ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name, VAddr entry_point,
u32 priority, u64 arg, s32 processor_id,
VAddr stack_top, SharedPtr<Process> owner_process) {
ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, u32 priority,
u64 arg, s32 processor_id, VAddr stack_top,
SharedPtr<Process> owner_process) {
// Check if priority is in ranged. Lowest priority -> highest priority id.
if (priority > THREADPRIO_LOWEST) {
LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority);
@@ -243,9 +316,9 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
return ResultCode(-1);
}
SharedPtr<Thread> thread(new Thread(kernel));
SharedPtr<Thread> thread(new Thread);
thread->thread_id = kernel.CreateNewThreadID();
thread->thread_id = NewThreadId();
thread->status = ThreadStatus::Dormant;
thread->entry_point = entry_point;
thread->stack_top = stack_top;
@@ -260,7 +333,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
thread->condvar_wait_address = 0;
thread->wait_handle = 0;
thread->name = std::move(name);
thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();
thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap();
thread->owner_process = owner_process;
thread->scheduler = Core::System::GetInstance().Scheduler(processor_id);
thread->scheduler->AddThread(thread, priority);
@@ -310,19 +383,19 @@ void Thread::BoostPriority(u32 priority) {
current_priority = priority;
}
SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority,
SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority,
SharedPtr<Process> owner_process) {
// Setup page table so we can write to memory
SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table);
// Initialize new "main" thread
auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0,
auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0,
Memory::STACK_AREA_VADDR_END, std::move(owner_process));
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
// Register 1 must be a handle to the main thread
thread->guest_handle = kernel.HandleTable().Create(thread).Unwrap();
thread->guest_handle = Kernel::g_handle_table.Create(thread).Unwrap();
thread->context.cpu_registers[1] = thread->guest_handle;
@@ -455,4 +528,13 @@ Thread* GetCurrentThread() {
return Core::System::GetInstance().CurrentScheduler().GetCurrentThread();
}
void ThreadingInit() {
ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
next_thread_id = 1;
}
void ThreadingShutdown() {
Kernel::ClearProcessList();
}
} // namespace Kernel

View File

@@ -56,7 +56,6 @@ enum class ThreadWakeupReason {
namespace Kernel {
class KernelCore;
class Process;
class Scheduler;
@@ -64,7 +63,6 @@ class Thread final : public WaitObject {
public:
/**
* Creates and returns a new thread. The new thread is immediately scheduled
* @param kernel The kernel instance this thread will be created under.
* @param name The friendly name desired for the thread
* @param entry_point The address at which the thread should start execution
* @param priority The thread's priority
@@ -74,9 +72,8 @@ public:
* @param owner_process The parent process for the thread
* @return A shared pointer to the newly created thread
*/
static ResultVal<SharedPtr<Thread>> Create(KernelCore& kernel, std::string name,
VAddr entry_point, u32 priority, u64 arg,
s32 processor_id, VAddr stack_top,
static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, u32 priority,
u64 arg, s32 processor_id, VAddr stack_top,
SharedPtr<Process> owner_process);
std::string GetName() const override {
@@ -266,7 +263,7 @@ public:
u64 affinity_mask{0x1};
private:
explicit Thread(KernelCore& kernel);
Thread();
~Thread() override;
std::shared_ptr<std::vector<u8>> tls_memory = std::make_shared<std::vector<u8>>();
@@ -274,13 +271,12 @@ private:
/**
* Sets up the primary application thread
* @param kernel The kernel instance to create the main thread under.
* @param entry_point The address at which the thread should start execution
* @param priority The priority to give the main thread
* @param owner_process The parent process for the main thread
* @return A shared pointer to the main thread
*/
SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority,
SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority,
SharedPtr<Process> owner_process);
/**
@@ -298,4 +294,14 @@ void WaitCurrentThread_Sleep();
*/
void ExitCurrentThread();
/**
* Initialize threading
*/
void ThreadingInit();
/**
* Shutdown threading
*/
void ThreadingShutdown();
} // namespace Kernel

View File

@@ -2,31 +2,36 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cinttypes>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/timer.h"
namespace Kernel {
Timer::Timer(KernelCore& kernel) : WaitObject{kernel} {}
Timer::~Timer() = default;
/// The event type of the generic timer callback event
static CoreTiming::EventType* timer_callback_event_type = nullptr;
// TODO(yuriks): This can be removed if Timer objects are explicitly pooled in the future, allowing
// us to simply use a pool index or similar.
static Kernel::HandleTable timer_callback_handle_table;
SharedPtr<Timer> Timer::Create(KernelCore& kernel, ResetType reset_type, std::string name) {
SharedPtr<Timer> timer(new Timer(kernel));
Timer::Timer() {}
Timer::~Timer() {}
SharedPtr<Timer> Timer::Create(ResetType reset_type, std::string name) {
SharedPtr<Timer> timer(new Timer);
timer->reset_type = reset_type;
timer->signaled = false;
timer->name = std::move(name);
timer->initial_delay = 0;
timer->interval_delay = 0;
timer->callback_handle = kernel.CreateTimerCallbackHandle(timer).Unwrap();
timer->callback_handle = timer_callback_handle_table.Create(timer).Unwrap();
return timer;
}
@@ -53,13 +58,13 @@ void Timer::Set(s64 initial, s64 interval) {
// Immediately invoke the callback
Signal(0);
} else {
CoreTiming::ScheduleEvent(CoreTiming::nsToCycles(initial), kernel.TimerCallbackEventType(),
CoreTiming::ScheduleEvent(CoreTiming::nsToCycles(initial), timer_callback_event_type,
callback_handle);
}
}
void Timer::Cancel() {
CoreTiming::UnscheduleEvent(kernel.TimerCallbackEventType(), callback_handle);
CoreTiming::UnscheduleEvent(timer_callback_event_type, callback_handle);
}
void Timer::Clear() {
@@ -84,8 +89,28 @@ void Timer::Signal(int cycles_late) {
if (interval_delay != 0) {
// Reschedule the timer with the interval delay
CoreTiming::ScheduleEvent(CoreTiming::nsToCycles(interval_delay) - cycles_late,
kernel.TimerCallbackEventType(), callback_handle);
timer_callback_event_type, callback_handle);
}
}
/// The timer callback event, called when a timer is fired
static void TimerCallback(u64 timer_handle, int cycles_late) {
SharedPtr<Timer> timer =
timer_callback_handle_table.Get<Timer>(static_cast<Handle>(timer_handle));
if (timer == nullptr) {
LOG_CRITICAL(Kernel, "Callback fired for invalid timer {:016X}", timer_handle);
return;
}
timer->Signal(cycles_late);
}
void TimersInit() {
timer_callback_handle_table.Clear();
timer_callback_event_type = CoreTiming::RegisterEvent("TimerCallback", TimerCallback);
}
void TimersShutdown() {}
} // namespace Kernel

View File

@@ -10,19 +10,15 @@
namespace Kernel {
class KernelCore;
class Timer final : public WaitObject {
public:
/**
* Creates a timer
* @param kernel The kernel instance to create the timer callback handle for.
* @param reset_type ResetType describing how to create the timer
* @param name Optional name of timer
* @return The created Timer
*/
static SharedPtr<Timer> Create(KernelCore& kernel, ResetType reset_type,
std::string name = "Unknown");
static SharedPtr<Timer> Create(ResetType reset_type, std::string name = "Unknown");
std::string GetTypeName() const override {
return "Timer";
@@ -72,7 +68,7 @@ public:
void Signal(int cycles_late);
private:
explicit Timer(KernelCore& kernel);
Timer();
~Timer() override;
ResetType reset_type; ///< The ResetType of this timer
@@ -87,4 +83,9 @@ private:
Handle callback_handle;
};
/// Initializes the required variables for timers
void TimersInit();
/// Tears down the timer variables
void TimersShutdown();
} // namespace Kernel

View File

@@ -12,9 +12,6 @@
namespace Kernel {
WaitObject::WaitObject(KernelCore& kernel) : Object{kernel} {}
WaitObject::~WaitObject() = default;
void WaitObject::AddWaitingThread(SharedPtr<Thread> thread) {
auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread);
if (itr == waiting_threads.end())

View File

@@ -11,15 +11,11 @@
namespace Kernel {
class KernelCore;
class Thread;
/// Class that represents a Kernel object that a thread can be waiting on
class WaitObject : public Object {
public:
explicit WaitObject(KernelCore& kernel);
~WaitObject() override;
/**
* Check if the specified thread should wait until the object is available
* @param thread The thread about which we're deciding.

View File

@@ -160,9 +160,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
};
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
launchable_event =
Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "ISelfController:LaunchableEvent");
Kernel::Event::Create(Kernel::ResetType::Sticky, "ISelfController:LaunchableEvent");
}
void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) {
@@ -333,8 +332,7 @@ ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter"
};
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "ICommonStateGetter:Event");
event = Kernel::Event::Create(Kernel::ResetType::OneShot, "ICommonStateGetter:Event");
}
void ICommonStateGetter::GetBootMode(Kernel::HLERequestContext& ctx) {
@@ -507,8 +505,7 @@ public:
};
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
state_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
state_changed_event = Kernel::Event::Create(Kernel::ResetType::OneShot,
"ILibraryAppletAccessor:StateChangedEvent");
}

View File

@@ -47,9 +47,7 @@ public:
RegisterHandlers(functions);
// This is the event handle used to check if the audio buffer was released
auto& kernel = Core::System::GetInstance().Kernel();
buffer_event =
Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "IAudioOutBufferReleased");
buffer_event = Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioOutBufferReleased");
stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count,
"IAudioOut", [=]() { buffer_event->Signal(); });

View File

@@ -35,9 +35,8 @@ public:
};
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
system_event =
Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "IAudioRenderer:SystemEvent");
Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioRenderer:SystemEvent");
renderer = std::make_unique<AudioCore::AudioRenderer>(audren_params, system_event);
}
@@ -122,9 +121,8 @@ public:
};
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
buffer_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
"IAudioOutBufferReleasedEvent");
buffer_event =
Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioOutBufferReleasedEvent");
}
private:

View File

@@ -10,7 +10,6 @@
#include "core/file_sys/bis_factory.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/sdmc_factory.h"
@@ -61,20 +60,17 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64
ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
std::string path(FileUtil::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
if (path.empty()) {
// TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
return RESULT_SUCCESS;
}
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
if (dir->GetFile(FileUtil::GetFilename(path)) == nullptr) {
if (dir->GetFile(FileUtil::GetFilename(path)) == nullptr)
return FileSys::ERROR_PATH_NOT_FOUND;
}
if (!dir->DeleteFile(FileUtil::GetFilename(path))) {
// TODO(DarkLordZach): Find a better error code for this
return ResultCode(-1);
}
return RESULT_SUCCESS;
}
@@ -308,12 +304,6 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() {
return sdmc_factory->Open();
}
std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() {
return std::make_shared<FileSys::RegisteredCacheUnion>(
std::vector<std::shared_ptr<FileSys::RegisteredCache>>{
GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()});
}
std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents() {
LOG_TRACE(Service_FS, "Opening System NAND Contents");

View File

@@ -7,13 +7,11 @@
#include <memory>
#include "common/common_types.h"
#include "core/file_sys/directory.h"
#include "core/file_sys/vfs.h"
#include "core/hle/result.h"
namespace FileSys {
class BISFactory;
class RegisteredCache;
class RegisteredCacheUnion;
class RomFSFactory;
class SaveDataFactory;
class SDMCFactory;
@@ -46,8 +44,6 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
FileSys::SaveDataDescriptor save_struct);
ResultVal<FileSys::VirtualDir> OpenSDMC();
std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents();
std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents();
std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents();
std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents();

View File

@@ -26,17 +26,6 @@
namespace Service::FileSystem {
enum class FileSystemType : u8 {
Invalid0 = 0,
Invalid1 = 1,
Logo = 2,
ContentControl = 3,
ContentManual = 4,
ContentMeta = 5,
ContentData = 6,
ApplicationPackage = 7,
};
class IStorage final : public ServiceFramework<IStorage> {
public:
explicit IStorage(FileSys::VirtualFile backend_)
@@ -431,7 +420,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
{0, nullptr, "MountContent"},
{1, &FSP_SRV::Initialize, "Initialize"},
{2, nullptr, "OpenDataFileSystemByCurrentProcess"},
{7, &FSP_SRV::OpenFileSystemWithPatch, "OpenFileSystemWithPatch"},
{7, nullptr, "OpenFileSystemWithPatch"},
{8, nullptr, "OpenFileSystemWithId"},
{9, nullptr, "OpenDataFileSystemByApplicationId"},
{11, nullptr, "OpenBisFileSystem"},
@@ -455,7 +444,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
{34, nullptr, "GetCacheStorageSize"},
{51, &FSP_SRV::MountSaveData, "MountSaveData"},
{52, nullptr, "OpenSaveDataFileSystemBySystemSaveDataId"},
{53, &FSP_SRV::OpenReadOnlySaveDataFileSystem, "OpenReadOnlySaveDataFileSystem"},
{53, nullptr, "OpenReadOnlySaveDataFileSystem"},
{57, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataSpaceId"},
{58, nullptr, "ReadSaveDataFileSystemExtraData"},
{59, nullptr, "WriteSaveDataFileSystemExtraData"},
@@ -527,16 +516,6 @@ void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS);
}
void FSP_SRV::OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto type = rp.PopRaw<FileSystemType>();
const auto title_id = rp.PopRaw<u64>();
IPC::ResponseBuilder rb{ctx, 2, 0, 0};
rb.Push(ResultCode(-1));
}
void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
@@ -584,11 +563,6 @@ void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
rb.PushIpcInterface<IFileSystem>(std::move(filesystem));
}
void FSP_SRV::OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called, delegating to 51 OpenSaveDataFilesystem");
MountSaveData(ctx);
}
void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");

View File

@@ -20,11 +20,9 @@ public:
private:
void Initialize(Kernel::HLERequestContext& ctx);
void OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx);
void MountSdCard(Kernel::HLERequestContext& ctx);
void CreateSaveData(Kernel::HLERequestContext& ctx);
void MountSaveData(Kernel::HLERequestContext& ctx);
void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx);
void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx);

View File

@@ -4,7 +4,6 @@
#include <atomic>
#include "common/logging/log.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/frontend/emu_window.h"
@@ -36,10 +35,9 @@ public:
};
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
shared_mem = Kernel::SharedMemory::Create(
kernel, nullptr, 0x40000, Kernel::MemoryPermission::ReadWrite,
Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "HID:SharedMemory");
nullptr, 0x40000, Kernel::MemoryPermission::ReadWrite, Kernel::MemoryPermission::Read,
0, Kernel::MemoryRegion::BASE, "HID:SharedMemory");
// Register update callbacks
pad_update_event = CoreTiming::RegisterEvent(
@@ -404,8 +402,7 @@ public:
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "hid:EventHandle");
event = Kernel::Event::Create(Kernel::ResetType::OneShot, "hid:EventHandle");
}
~Hid() = default;

View File

@@ -46,13 +46,11 @@ public:
};
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
activate_event =
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:ActivateEvent");
activate_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "IUser:ActivateEvent");
deactivate_event =
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:DeactivateEvent");
availability_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
"IUser:AvailabilityChangeEvent");
Kernel::Event::Create(Kernel::ResetType::OneShot, "IUser:DeactivateEvent");
availability_change_event =
Kernel::Event::Create(Kernel::ResetType::OneShot, "IUser:AvailabilityChangeEvent");
}
private:

View File

@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/service/nifm/nifm.h"
@@ -55,9 +54,8 @@ public:
};
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
event1 = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IRequest:Event1");
event2 = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IRequest:Event2");
event1 = Kernel::Event::Create(Kernel::ResetType::OneShot, "IRequest:Event1");
event2 = Kernel::Event::Create(Kernel::ResetType::OneShot, "IRequest:Event2");
}
private:

View File

@@ -5,9 +5,7 @@
#include "common/common_paths.h"
#include "common/file_util.h"
#include "core/core.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/bis_factory.h"
#include "core/file_sys/romfs.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/filesystem/filesystem.h"
@@ -268,9 +266,8 @@ void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
SHARED_FONT_MEM_VADDR, shared_font, 0, SHARED_FONT_MEM_SIZE, Kernel::MemoryState::Shared);
// Create shared font memory object
auto& kernel = Core::System::GetInstance().Kernel();
shared_font_mem = Kernel::SharedMemory::Create(
kernel, Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite,
Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite,
Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE,
"PL_U:shared_font_mem");

View File

@@ -7,7 +7,6 @@
#include "core/core.h"
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
#include "core/hle/service/nvdrv/devices/nvmap.h"
#include "core/perf_stats.h"
#include "video_core/gpu.h"
#include "video_core/renderer_base.h"
@@ -32,7 +31,7 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
transform, crop_rect};
auto& instance = Core::System::GetInstance();
instance.GetPerfStats().EndGameFrame();
instance.perf_stats.EndGameFrame();
instance.Renderer().SwapBuffers(framebuffer);
}

View File

@@ -10,7 +10,6 @@
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
#include "video_core/memory_manager.h"
namespace Service::Nvidia::Devices {

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