Compare commits

...

6 Commits

Author SHA1 Message Date
yuzubot
4f8698e4a8 "Merge Tagged PR 11689" 2023-10-27 12:02:35 +00:00
yuzubot
6b9301490b "Merge Tagged PR 11827" 2023-10-27 12:02:34 +00:00
yuzubot
9b300d52a2 "Merge Tagged PR 11843" 2023-10-27 12:02:34 +00:00
yuzubot
88fef4aa00 "Merge Tagged PR 11852" 2023-10-27 12:02:33 +00:00
yuzubot
468bd77c0a "Merge Tagged PR 11859" 2023-10-27 12:02:33 +00:00
yuzubot
12e8494dc3 "Merge Tagged PR 11862" 2023-10-27 12:02:32 +00:00
66 changed files with 2271 additions and 1510 deletions

View File

@@ -19,6 +19,7 @@ cmake .. \
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
-DENABLE_QT_TRANSLATION=ON \
-DUSE_DISCORD_PRESENCE=ON \
-DYUZU_CRASH_DUMPS=ON \
-DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \
-DYUZU_USE_BUNDLED_FFMPEG=ON \
-GNinja

View File

@@ -23,6 +23,7 @@ cmake .. \
-DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \
-DYUZU_USE_BUNDLED_FFMPEG=ON \
-DYUZU_ENABLE_LTO=ON \
-DYUZU_CRASH_DUMPS=ON \
-GNinja
ninja

View File

@@ -17,7 +17,6 @@ cmake .. \
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
-DENABLE_QT_TRANSLATION=ON \
-DUSE_CCACHE=ON \
-DYUZU_CRASH_DUMPS=ON \
-DYUZU_USE_BUNDLED_SDL2=OFF \
-DYUZU_USE_EXTERNAL_SDL2=OFF \
-DYUZU_TESTS=OFF \

3
.gitmodules vendored
View File

@@ -58,3 +58,6 @@
[submodule "VulkanMemoryAllocator"]
path = externals/VulkanMemoryAllocator
url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git
[submodule "breakpad"]
path = externals/breakpad
url = https://github.com/yuzu-emu/breakpad.git

View File

@@ -52,7 +52,7 @@ option(YUZU_DOWNLOAD_ANDROID_VVL "Download validation layer binary for android"
CMAKE_DEPENDENT_OPTION(YUZU_ROOM "Compile LDN room server" ON "NOT ANDROID" OFF)
CMAKE_DEPENDENT_OPTION(YUZU_CRASH_DUMPS "Compile Windows crash dump (Minidump) support" OFF "WIN32" OFF)
CMAKE_DEPENDENT_OPTION(YUZU_CRASH_DUMPS "Compile crash dump (Minidump) support" OFF "WIN32 OR LINUX" OFF)
option(YUZU_USE_BUNDLED_VCPKG "Use vcpkg for yuzu dependencies" "${MSVC}")
@@ -139,9 +139,6 @@ if (YUZU_USE_BUNDLED_VCPKG)
if (YUZU_TESTS)
list(APPEND VCPKG_MANIFEST_FEATURES "yuzu-tests")
endif()
if (YUZU_CRASH_DUMPS)
list(APPEND VCPKG_MANIFEST_FEATURES "dbghelp")
endif()
if (ENABLE_WEB_SERVICE)
list(APPEND VCPKG_MANIFEST_FEATURES "web-service")
endif()
@@ -551,6 +548,18 @@ if (NOT YUZU_USE_BUNDLED_FFMPEG)
find_package(FFmpeg 4.3 REQUIRED QUIET COMPONENTS ${FFmpeg_COMPONENTS})
endif()
if (WIN32 AND YUZU_CRASH_DUMPS)
set(BREAKPAD_VER "breakpad-c89f9dd")
download_bundled_external("breakpad/" ${BREAKPAD_VER} BREAKPAD_PREFIX)
set(BREAKPAD_CLIENT_INCLUDE_DIR "${BREAKPAD_PREFIX}/include")
set(BREAKPAD_CLIENT_LIBRARY "${BREAKPAD_PREFIX}/lib/libbreakpad_client.lib")
add_library(libbreakpad_client INTERFACE IMPORTED)
target_link_libraries(libbreakpad_client INTERFACE "${BREAKPAD_CLIENT_LIBRARY}")
target_include_directories(libbreakpad_client INTERFACE "${BREAKPAD_CLIENT_INCLUDE_DIR}")
endif()
# Prefer the -pthread flag on Linux.
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
@@ -570,13 +579,6 @@ elseif (WIN32)
# PSAPI is the Process Status API
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version)
endif()
if (YUZU_CRASH_DUMPS)
find_library(DBGHELP_LIBRARY dbghelp)
if ("${DBGHELP_LIBRARY}" STREQUAL "DBGHELP_LIBRARY-NOTFOUND")
message(FATAL_ERROR "YUZU_CRASH_DUMPS enabled but dbghelp library not found")
endif()
endif()
elseif (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU|SunOS)$")
set(PLATFORM_LIBRARIES rt)
endif()

View File

@@ -189,3 +189,105 @@ if (ANDROID)
add_subdirectory(libadrenotools)
endif()
endif()
# Breakpad
# https://github.com/microsoft/vcpkg/blob/master/ports/breakpad/CMakeLists.txt
if (YUZU_CRASH_DUMPS AND NOT TARGET libbreakpad_client)
set(BREAKPAD_WIN32_DEFINES
NOMINMAX
UNICODE
WIN32_LEAN_AND_MEAN
_CRT_SECURE_NO_WARNINGS
_CRT_SECURE_NO_DEPRECATE
_CRT_NONSTDC_NO_DEPRECATE
)
# libbreakpad
add_library(libbreakpad STATIC)
file(GLOB_RECURSE LIBBREAKPAD_SOURCES breakpad/src/processor/*.cc)
file(GLOB_RECURSE LIBDISASM_SOURCES breakpad/src/third_party/libdisasm/*.c)
list(FILTER LIBBREAKPAD_SOURCES EXCLUDE REGEX "_unittest|_selftest|synth_minidump|/tests|/testdata|/solaris|microdump_stackwalk|minidump_dump|minidump_stackwalk")
if (WIN32)
list(FILTER LIBBREAKPAD_SOURCES EXCLUDE REGEX "/linux|/mac|/android")
target_compile_definitions(libbreakpad PRIVATE ${BREAKPAD_WIN32_DEFINES})
target_include_directories(libbreakpad PRIVATE "${CMAKE_GENERATOR_INSTANCE}/DIA SDK/include")
elseif (APPLE)
list(FILTER LIBBREAKPAD_SOURCES EXCLUDE REGEX "/linux|/windows|/android")
else()
list(FILTER LIBBREAKPAD_SOURCES EXCLUDE REGEX "/mac|/windows|/android")
endif()
target_sources(libbreakpad PRIVATE ${LIBBREAKPAD_SOURCES} ${LIBDISASM_SOURCES})
target_include_directories(libbreakpad
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/breakpad/src
${CMAKE_CURRENT_SOURCE_DIR}/breakpad/src/third_party/libdisasm
)
# libbreakpad_client
add_library(libbreakpad_client STATIC)
file(GLOB LIBBREAKPAD_COMMON_SOURCES breakpad/src/common/*.cc breakpad/src/common/*.c breakpad/src/client/*.cc)
if (WIN32)
file(GLOB_RECURSE LIBBREAKPAD_CLIENT_SOURCES breakpad/src/client/windows/*.cc breakpad/src/common/windows/*.cc)
list(FILTER LIBBREAKPAD_COMMON_SOURCES EXCLUDE REGEX "language.cc|path_helper.cc|stabs_to_module.cc|stabs_reader.cc|minidump_file_writer.cc")
target_include_directories(libbreakpad_client PRIVATE "${CMAKE_GENERATOR_INSTANCE}/DIA SDK/include")
target_compile_definitions(libbreakpad_client PRIVATE ${BREAKPAD_WIN32_DEFINES})
elseif (APPLE)
target_compile_definitions(libbreakpad_client PRIVATE HAVE_MACH_O_NLIST_H)
file(GLOB_RECURSE LIBBREAKPAD_CLIENT_SOURCES breakpad/src/client/mac/*.cc breakpad/src/common/mac/*.cc)
list(APPEND LIBBREAKPAD_CLIENT_SOURCES breakpad/src/common/mac/MachIPC.mm)
else()
target_compile_definitions(libbreakpad_client PUBLIC -DHAVE_A_OUT_H)
file(GLOB_RECURSE LIBBREAKPAD_CLIENT_SOURCES breakpad/src/client/linux/*.cc breakpad/src/common/linux/*.cc)
endif()
list(APPEND LIBBREAKPAD_CLIENT_SOURCES ${LIBBREAKPAD_COMMON_SOURCES})
list(FILTER LIBBREAKPAD_CLIENT_SOURCES EXCLUDE REGEX "/sender|/tests|/unittests|/testcases|_unittest|_test")
target_sources(libbreakpad_client PRIVATE ${LIBBREAKPAD_CLIENT_SOURCES})
target_include_directories(libbreakpad_client PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/breakpad/src)
if (WIN32)
target_link_libraries(libbreakpad_client PRIVATE wininet.lib)
elseif (APPLE)
find_library(CoreFoundation_FRAMEWORK CoreFoundation)
target_link_libraries(libbreakpad_client PRIVATE ${CoreFoundation_FRAMEWORK})
else()
find_library(PTHREAD_LIBRARIES pthread)
target_compile_definitions(libbreakpad_client PRIVATE HAVE_GETCONTEXT=1)
if (PTHREAD_LIBRARIES)
target_link_libraries(libbreakpad_client PRIVATE ${PTHREAD_LIBRARIES})
endif()
endif()
# Host tools for symbol processing
if (LINUX)
find_package(ZLIB REQUIRED)
add_executable(minidump_stackwalk breakpad/src/processor/minidump_stackwalk.cc)
target_link_libraries(minidump_stackwalk PRIVATE libbreakpad libbreakpad_client)
add_executable(dump_syms
breakpad/src/common/dwarf_cfi_to_module.cc
breakpad/src/common/dwarf_cu_to_module.cc
breakpad/src/common/dwarf_line_to_module.cc
breakpad/src/common/dwarf_range_list_handler.cc
breakpad/src/common/language.cc
breakpad/src/common/module.cc
breakpad/src/common/path_helper.cc
breakpad/src/common/stabs_reader.cc
breakpad/src/common/stabs_to_module.cc
breakpad/src/common/dwarf/bytereader.cc
breakpad/src/common/dwarf/dwarf2diehandler.cc
breakpad/src/common/dwarf/dwarf2reader.cc
breakpad/src/common/dwarf/elf_reader.cc
breakpad/src/common/linux/crc32.cc
breakpad/src/common/linux/dump_symbols.cc
breakpad/src/common/linux/elf_symbols_to_module.cc
breakpad/src/common/linux/elfutils.cc
breakpad/src/common/linux/file_id.cc
breakpad/src/common/linux/linux_libc_support.cc
breakpad/src/common/linux/memory_mapped_file.cc
breakpad/src/common/linux/safe_readlink.cc
breakpad/src/tools/linux/dump_syms/dump_syms.cc)
target_link_libraries(dump_syms PRIVATE libbreakpad_client ZLIB::ZLIB)
endif()
endif()

1
externals/breakpad vendored Submodule

Submodule externals/breakpad added at c89f9dddc7

View File

@@ -13,6 +13,7 @@
#define AMIIBO_DIR "amiibo"
#define CACHE_DIR "cache"
#define CONFIG_DIR "config"
#define CRASH_DUMPS_DIR "crash_dumps"
#define DUMP_DIR "dump"
#define KEYS_DIR "keys"
#define LOAD_DIR "load"

View File

@@ -119,6 +119,7 @@ public:
GenerateYuzuPath(YuzuPath::AmiiboDir, yuzu_path / AMIIBO_DIR);
GenerateYuzuPath(YuzuPath::CacheDir, yuzu_path_cache);
GenerateYuzuPath(YuzuPath::ConfigDir, yuzu_path_config);
GenerateYuzuPath(YuzuPath::CrashDumpsDir, yuzu_path / CRASH_DUMPS_DIR);
GenerateYuzuPath(YuzuPath::DumpDir, yuzu_path / DUMP_DIR);
GenerateYuzuPath(YuzuPath::KeysDir, yuzu_path / KEYS_DIR);
GenerateYuzuPath(YuzuPath::LoadDir, yuzu_path / LOAD_DIR);

View File

@@ -15,6 +15,7 @@ enum class YuzuPath {
AmiiboDir, // Where Amiibo backups are stored.
CacheDir, // Where cached filesystem data is stored.
ConfigDir, // Where config files are stored.
CrashDumpsDir, // Where crash dumps are stored.
DumpDir, // Where dumped data is stored.
KeysDir, // Where key files are stored.
LoadDir, // Where cheat/mod files are stored.

View File

@@ -505,7 +505,6 @@ struct Values {
linkage, false, "use_auto_stub", Category::Debugging, Specialization::Default, false};
Setting<bool> enable_all_controllers{linkage, false, "enable_all_controllers",
Category::Debugging};
Setting<bool> create_crash_dumps{linkage, false, "create_crash_dumps", Category::Debugging};
Setting<bool> perform_vulkan_check{linkage, true, "perform_vulkan_check", Category::Debugging};
// Miscellaneous

View File

@@ -86,9 +86,9 @@ void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector<Backt
std::map<std::string, Symbols::Symbols> symbols;
for (const auto& module : modules) {
symbols.insert_or_assign(
module.second, Symbols::GetSymbols(module.first, system.ApplicationMemory(),
system.ApplicationProcess()->Is64BitProcess()));
symbols.insert_or_assign(module.second,
Symbols::GetSymbols(module.first, system.ApplicationMemory(),
system.ApplicationProcess()->Is64Bit()));
}
for (auto& entry : out) {

View File

@@ -309,17 +309,10 @@ struct System::Impl {
telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider);
// Create a resource limit for the process.
const auto physical_memory_size =
kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::Application);
auto* resource_limit = Kernel::CreateResourceLimitForProcess(system, physical_memory_size);
// Create the process.
auto main_process = Kernel::KProcess::Create(system.Kernel());
ASSERT(Kernel::KProcess::Initialize(main_process, system, "main",
Kernel::KProcess::ProcessType::Userland, resource_limit)
.IsSuccess());
Kernel::KProcess::Register(system.Kernel(), main_process);
kernel.AppendNewProcess(main_process);
kernel.MakeApplicationProcess(main_process);
const auto [load_result, load_parameters] = app_loader->Load(*main_process, system);
if (load_result != Loader::ResultStatus::Success) {
@@ -418,6 +411,7 @@ struct System::Impl {
services->KillNVNFlinger();
}
kernel.CloseServices();
kernel.ShutdownCores();
services.reset();
service_manager.reset();
cheat_engine.reset();
@@ -429,7 +423,6 @@ struct System::Impl {
gpu_core.reset();
host1x_core.reset();
perf_stats.reset();
kernel.ShutdownCores();
cpu_manager.Shutdown();
debugger.reset();
kernel.Shutdown();

View File

@@ -258,20 +258,20 @@ private:
Kernel::KScopedSchedulerLock sl{system.Kernel()};
// Put all threads to sleep on next scheduler round.
for (auto* thread : ThreadList()) {
thread->RequestSuspend(Kernel::SuspendType::Debug);
for (auto& thread : ThreadList()) {
thread.RequestSuspend(Kernel::SuspendType::Debug);
}
}
void ResumeEmulation(Kernel::KThread* except = nullptr) {
// Wake up all threads.
for (auto* thread : ThreadList()) {
if (thread == except) {
for (auto& thread : ThreadList()) {
if (std::addressof(thread) == except) {
continue;
}
thread->SetStepState(Kernel::StepState::NotStepping);
thread->Resume(Kernel::SuspendType::Debug);
thread.SetStepState(Kernel::StepState::NotStepping);
thread.Resume(Kernel::SuspendType::Debug);
}
}
@@ -283,13 +283,17 @@ private:
}
void UpdateActiveThread() {
const auto& threads{ThreadList()};
if (std::find(threads.begin(), threads.end(), state->active_thread) == threads.end()) {
state->active_thread = threads.front();
auto& threads{ThreadList()};
for (auto& thread : threads) {
if (std::addressof(thread) == state->active_thread) {
// Thread is still alive, no need to update.
return;
}
}
state->active_thread = std::addressof(threads.front());
}
const std::list<Kernel::KThread*>& ThreadList() {
Kernel::KProcess::ThreadList& ThreadList() {
return system.ApplicationProcess()->GetThreadList();
}

View File

@@ -109,7 +109,7 @@ static std::string EscapeXML(std::string_view data) {
GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_)
: DebuggerFrontend(backend_), system{system_} {
if (system.ApplicationProcess()->Is64BitProcess()) {
if (system.ApplicationProcess()->Is64Bit()) {
arch = std::make_unique<GDBStubA64>();
} else {
arch = std::make_unique<GDBStubA32>();
@@ -446,10 +446,10 @@ void GDBStub::HandleBreakpointRemove(std::string_view command) {
// See osdbg_thread_local_region.os.horizon.hpp and osdbg_thread_type.os.horizon.hpp
static std::optional<std::string> GetNameFromThreadType32(Core::Memory::Memory& memory,
const Kernel::KThread* thread) {
const Kernel::KThread& thread) {
// Read thread type from TLS
const VAddr tls_thread_type{memory.Read32(thread->GetTlsAddress() + 0x1fc)};
const VAddr argument_thread_type{thread->GetArgument()};
const VAddr tls_thread_type{memory.Read32(thread.GetTlsAddress() + 0x1fc)};
const VAddr argument_thread_type{thread.GetArgument()};
if (argument_thread_type && tls_thread_type != argument_thread_type) {
// Probably not created by nnsdk, no name available.
@@ -477,10 +477,10 @@ static std::optional<std::string> GetNameFromThreadType32(Core::Memory::Memory&
}
static std::optional<std::string> GetNameFromThreadType64(Core::Memory::Memory& memory,
const Kernel::KThread* thread) {
const Kernel::KThread& thread) {
// Read thread type from TLS
const VAddr tls_thread_type{memory.Read64(thread->GetTlsAddress() + 0x1f8)};
const VAddr argument_thread_type{thread->GetArgument()};
const VAddr tls_thread_type{memory.Read64(thread.GetTlsAddress() + 0x1f8)};
const VAddr argument_thread_type{thread.GetArgument()};
if (argument_thread_type && tls_thread_type != argument_thread_type) {
// Probably not created by nnsdk, no name available.
@@ -508,16 +508,16 @@ static std::optional<std::string> GetNameFromThreadType64(Core::Memory::Memory&
}
static std::optional<std::string> GetThreadName(Core::System& system,
const Kernel::KThread* thread) {
if (system.ApplicationProcess()->Is64BitProcess()) {
const Kernel::KThread& thread) {
if (system.ApplicationProcess()->Is64Bit()) {
return GetNameFromThreadType64(system.ApplicationMemory(), thread);
} else {
return GetNameFromThreadType32(system.ApplicationMemory(), thread);
}
}
static std::string_view GetThreadWaitReason(const Kernel::KThread* thread) {
switch (thread->GetWaitReasonForDebugging()) {
static std::string_view GetThreadWaitReason(const Kernel::KThread& thread) {
switch (thread.GetWaitReasonForDebugging()) {
case Kernel::ThreadWaitReasonForDebugging::Sleep:
return "Sleep";
case Kernel::ThreadWaitReasonForDebugging::IPC:
@@ -535,8 +535,8 @@ static std::string_view GetThreadWaitReason(const Kernel::KThread* thread) {
}
}
static std::string GetThreadState(const Kernel::KThread* thread) {
switch (thread->GetState()) {
static std::string GetThreadState(const Kernel::KThread& thread) {
switch (thread.GetState()) {
case Kernel::ThreadState::Initialized:
return "Initialized";
case Kernel::ThreadState::Waiting:
@@ -604,7 +604,7 @@ void GDBStub::HandleQuery(std::string_view command) {
const auto& threads = system.ApplicationProcess()->GetThreadList();
std::vector<std::string> thread_ids;
for (const auto& thread : threads) {
thread_ids.push_back(fmt::format("{:x}", thread->GetThreadId()));
thread_ids.push_back(fmt::format("{:x}", thread.GetThreadId()));
}
SendReply(fmt::format("m{}", fmt::join(thread_ids, ",")));
} else if (command.starts_with("sThreadInfo")) {
@@ -616,14 +616,14 @@ void GDBStub::HandleQuery(std::string_view command) {
buffer += "<threads>";
const auto& threads = system.ApplicationProcess()->GetThreadList();
for (const auto* thread : threads) {
for (const auto& thread : threads) {
auto thread_name{GetThreadName(system, thread)};
if (!thread_name) {
thread_name = fmt::format("Thread {:d}", thread->GetThreadId());
thread_name = fmt::format("Thread {:d}", thread.GetThreadId());
}
buffer += fmt::format(R"(<thread id="{:x}" core="{:d}" name="{}">{}</thread>)",
thread->GetThreadId(), thread->GetActiveCore(),
thread.GetThreadId(), thread.GetActiveCore(),
EscapeXML(*thread_name), GetThreadState(thread));
}
@@ -850,10 +850,10 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
}
Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
const auto& threads{system.ApplicationProcess()->GetThreadList()};
for (auto* thread : threads) {
if (thread->GetThreadId() == thread_id) {
return thread;
auto& threads{system.ApplicationProcess()->GetThreadList()};
for (auto& thread : threads) {
if (thread.GetThreadId() == thread_id) {
return std::addressof(thread);
}
}

View File

@@ -104,16 +104,16 @@ Loader::ResultStatus ProgramMetadata::Reload(VirtualFile file) {
}
/*static*/ ProgramMetadata ProgramMetadata::GetDefault() {
// Allow use of cores 0~3 and thread priorities 1~63.
constexpr u32 default_thread_info_capability = 0x30007F7;
// Allow use of cores 0~3 and thread priorities 16~63.
constexpr u32 default_thread_info_capability = 0x30043F7;
ProgramMetadata result;
result.LoadManual(
true /*is_64_bit*/, FileSys::ProgramAddressSpaceType::Is39Bit /*address_space*/,
0x2c /*main_thread_prio*/, 0 /*main_thread_core*/, 0x00100000 /*main_thread_stack_size*/,
0 /*title_id*/, 0xFFFFFFFFFFFFFFFF /*filesystem_permissions*/,
0x1FE00000 /*system_resource_size*/, {default_thread_info_capability} /*capabilities*/);
0x2c /*main_thread_prio*/, 0 /*main_thread_core*/, 0x100000 /*main_thread_stack_size*/,
0 /*title_id*/, 0xFFFFFFFFFFFFFFFF /*filesystem_permissions*/, 0 /*system_resource_size*/,
{default_thread_info_capability} /*capabilities*/);
return result;
}

View File

@@ -73,6 +73,9 @@ public:
u64 GetFilesystemPermissions() const;
u32 GetSystemResourceSize() const;
const KernelCapabilityDescriptors& GetKernelCapabilities() const;
const std::array<u8, 0x10>& GetName() const {
return npdm_header.application_name;
}
void Print() const;
@@ -164,14 +167,14 @@ private:
u32_le unk_size_2;
};
Header npdm_header;
AciHeader aci_header;
AcidHeader acid_header;
Header npdm_header{};
AciHeader aci_header{};
AcidHeader acid_header{};
FileAccessControl acid_file_access;
FileAccessHeader aci_file_access;
FileAccessControl acid_file_access{};
FileAccessHeader aci_file_access{};
KernelCapabilityDescriptors aci_kernel_capabilities;
KernelCapabilityDescriptors aci_kernel_capabilities{};
};
} // namespace FileSys

View File

@@ -8,7 +8,11 @@
#include "core/hle/kernel/board/nintendo/nx/k_system_control.h"
#include "core/hle/kernel/board/nintendo/nx/secure_monitor.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_trace.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel::Board::Nintendo::Nx {
@@ -30,6 +34,8 @@ constexpr const std::size_t RequiredNonSecureSystemMemorySize =
constexpr const std::size_t RequiredNonSecureSystemMemorySizeWithFatal =
RequiredNonSecureSystemMemorySize + impl::RequiredNonSecureSystemMemorySizeViFatal;
constexpr const std::size_t SecureAlignment = 128_KiB;
namespace {
using namespace Common::Literals;
@@ -183,4 +189,57 @@ u64 KSystemControl::GenerateRandomRange(u64 min, u64 max) {
return GenerateUniformRange(min, max, GenerateRandomU64);
}
size_t KSystemControl::CalculateRequiredSecureMemorySize(size_t size, u32 pool) {
if (pool == static_cast<u32>(KMemoryManager::Pool::Applet)) {
return 0;
} else {
// return KSystemControlBase::CalculateRequiredSecureMemorySize(size, pool);
return size;
}
}
Result KSystemControl::AllocateSecureMemory(KernelCore& kernel, KVirtualAddress* out, size_t size,
u32 pool) {
// Applet secure memory is handled separately.
UNIMPLEMENTED_IF(pool == static_cast<u32>(KMemoryManager::Pool::Applet));
// Ensure the size is aligned.
const size_t alignment =
(pool == static_cast<u32>(KMemoryManager::Pool::System) ? PageSize : SecureAlignment);
R_UNLESS(Common::IsAligned(size, alignment), ResultInvalidSize);
// Allocate the memory.
const size_t num_pages = size / PageSize;
const KPhysicalAddress paddr = kernel.MemoryManager().AllocateAndOpenContinuous(
num_pages, alignment / PageSize,
KMemoryManager::EncodeOption(static_cast<KMemoryManager::Pool>(pool),
KMemoryManager::Direction::FromFront));
R_UNLESS(paddr != 0, ResultOutOfMemory);
// Ensure we don't leak references to the memory on error.
ON_RESULT_FAILURE {
kernel.MemoryManager().Close(paddr, num_pages);
};
// We succeeded.
*out = KPageTable::GetHeapVirtualAddress(kernel.MemoryLayout(), paddr);
R_SUCCEED();
}
void KSystemControl::FreeSecureMemory(KernelCore& kernel, KVirtualAddress address, size_t size,
u32 pool) {
// Applet secure memory is handled separately.
UNIMPLEMENTED_IF(pool == static_cast<u32>(KMemoryManager::Pool::Applet));
// Ensure the size is aligned.
const size_t alignment =
(pool == static_cast<u32>(KMemoryManager::Pool::System) ? PageSize : SecureAlignment);
ASSERT(Common::IsAligned(GetInteger(address), alignment));
ASSERT(Common::IsAligned(size, alignment));
// Close the secure region's pages.
kernel.MemoryManager().Close(KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), address),
size / PageSize);
}
} // namespace Kernel::Board::Nintendo::Nx

View File

@@ -4,6 +4,11 @@
#pragma once
#include "core/hle/kernel/k_typed_address.h"
#include "core/hle/result.h"
namespace Kernel {
class KernelCore;
}
namespace Kernel::Board::Nintendo::Nx {
@@ -25,8 +30,16 @@ public:
static std::size_t GetMinimumNonSecureSystemPoolSize();
};
// Randomness.
static u64 GenerateRandomRange(u64 min, u64 max);
static u64 GenerateRandomU64();
// Secure Memory.
static size_t CalculateRequiredSecureMemorySize(size_t size, u32 pool);
static Result AllocateSecureMemory(KernelCore& kernel, KVirtualAddress* out, size_t size,
u32 pool);
static void FreeSecureMemory(KernelCore& kernel, KVirtualAddress address, size_t size,
u32 pool);
};
} // namespace Kernel::Board::Nintendo::Nx

View File

@@ -200,8 +200,8 @@ private:
RawCapabilityValue raw;
BitField<0, 15, CapabilityType> id;
BitField<15, 4, u32> major_version;
BitField<19, 13, u32> minor_version;
BitField<15, 4, u32> minor_version;
BitField<19, 13, u32> major_version;
};
union HandleTable {

View File

@@ -107,12 +107,12 @@ KConditionVariable::KConditionVariable(Core::System& system)
KConditionVariable::~KConditionVariable() = default;
Result KConditionVariable::SignalToAddress(KProcessAddress addr) {
KThread* owner_thread = GetCurrentThreadPointer(m_kernel);
Result KConditionVariable::SignalToAddress(KernelCore& kernel, KProcessAddress addr) {
KThread* owner_thread = GetCurrentThreadPointer(kernel);
// Signal the address.
{
KScopedSchedulerLock sl(m_kernel);
KScopedSchedulerLock sl(kernel);
// Remove waiter thread.
bool has_waiters{};
@@ -133,7 +133,7 @@ Result KConditionVariable::SignalToAddress(KProcessAddress addr) {
// Write the value to userspace.
Result result{ResultSuccess};
if (WriteToUser(m_kernel, addr, std::addressof(next_value))) [[likely]] {
if (WriteToUser(kernel, addr, std::addressof(next_value))) [[likely]] {
result = ResultSuccess;
} else {
result = ResultInvalidCurrentMemory;
@@ -148,28 +148,28 @@ Result KConditionVariable::SignalToAddress(KProcessAddress addr) {
}
}
Result KConditionVariable::WaitForAddress(Handle handle, KProcessAddress addr, u32 value) {
KThread* cur_thread = GetCurrentThreadPointer(m_kernel);
ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(m_kernel);
Result KConditionVariable::WaitForAddress(KernelCore& kernel, Handle handle, KProcessAddress addr,
u32 value) {
KThread* cur_thread = GetCurrentThreadPointer(kernel);
ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(kernel);
// Wait for the address.
KThread* owner_thread{};
{
KScopedSchedulerLock sl(m_kernel);
KScopedSchedulerLock sl(kernel);
// Check if the thread should terminate.
R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested);
// Read the tag from userspace.
u32 test_tag{};
R_UNLESS(ReadFromUser(m_kernel, std::addressof(test_tag), addr),
ResultInvalidCurrentMemory);
R_UNLESS(ReadFromUser(kernel, std::addressof(test_tag), addr), ResultInvalidCurrentMemory);
// If the tag isn't the handle (with wait mask), we're done.
R_SUCCEED_IF(test_tag != (handle | Svc::HandleWaitMask));
// Get the lock owner thread.
owner_thread = GetCurrentProcess(m_kernel)
owner_thread = GetCurrentProcess(kernel)
.GetHandleTable()
.GetObjectWithoutPseudoHandle<KThread>(handle)
.ReleasePointerUnsafe();

View File

@@ -24,11 +24,12 @@ public:
explicit KConditionVariable(Core::System& system);
~KConditionVariable();
// Arbitration
Result SignalToAddress(KProcessAddress addr);
Result WaitForAddress(Handle handle, KProcessAddress addr, u32 value);
// Arbitration.
static Result SignalToAddress(KernelCore& kernel, KProcessAddress addr);
static Result WaitForAddress(KernelCore& kernel, Handle handle, KProcessAddress addr,
u32 value);
// Condition variable
// Condition variable.
void Signal(u64 cv_key, s32 count);
Result Wait(KProcessAddress addr, u64 key, u32 value, s64 timeout);

View File

@@ -22,7 +22,7 @@ void HandleInterrupt(KernelCore& kernel, s32 core_id) {
KScopedSchedulerLock sl{kernel};
// Pin the current thread.
process->PinCurrentThread(core_id);
process->PinCurrentThread();
// Set the interrupt flag for the thread.
GetCurrentThread(kernel).SetInterruptFlag();

View File

@@ -11,6 +11,7 @@
#include "core/hle/kernel/initial_process.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_page_group.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
@@ -168,11 +169,37 @@ void KMemoryManager::Initialize(KVirtualAddress management_region, size_t manage
}
Result KMemoryManager::InitializeOptimizedMemory(u64 process_id, Pool pool) {
UNREACHABLE();
const u32 pool_index = static_cast<u32>(pool);
// Lock the pool.
KScopedLightLock lk(m_pool_locks[pool_index]);
// Check that we don't already have an optimized process.
R_UNLESS(!m_has_optimized_process[pool_index], ResultBusy);
// Set the optimized process id.
m_optimized_process_ids[pool_index] = process_id;
m_has_optimized_process[pool_index] = true;
// Clear the management area for the optimized process.
for (auto* manager = this->GetFirstManager(pool, Direction::FromFront); manager != nullptr;
manager = this->GetNextManager(manager, Direction::FromFront)) {
manager->InitializeOptimizedMemory(m_system.Kernel());
}
R_SUCCEED();
}
void KMemoryManager::FinalizeOptimizedMemory(u64 process_id, Pool pool) {
UNREACHABLE();
const u32 pool_index = static_cast<u32>(pool);
// Lock the pool.
KScopedLightLock lk(m_pool_locks[pool_index]);
// If the process was optimized, clear it.
if (m_has_optimized_process[pool_index] && m_optimized_process_ids[pool_index] == process_id) {
m_has_optimized_process[pool_index] = false;
}
}
KPhysicalAddress KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_pages,
@@ -207,7 +234,7 @@ KPhysicalAddress KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, siz
// Maintain the optimized memory bitmap, if we should.
if (m_has_optimized_process[static_cast<size_t>(pool)]) {
UNIMPLEMENTED();
chosen_manager->TrackUnoptimizedAllocation(m_system.Kernel(), allocated_block, num_pages);
}
// Open the first reference to the pages.
@@ -255,7 +282,8 @@ Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages,
// Maintain the optimized memory bitmap, if we should.
if (unoptimized) {
UNIMPLEMENTED();
cur_manager->TrackUnoptimizedAllocation(m_system.Kernel(), allocated_block,
pages_per_alloc);
}
num_pages -= pages_per_alloc;
@@ -358,8 +386,8 @@ Result KMemoryManager::AllocateForProcess(KPageGroup* out, size_t num_pages, u32
// Process part or all of the block.
const size_t cur_pages =
std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address));
any_new =
manager.ProcessOptimizedAllocation(cur_address, cur_pages, fill_pattern);
any_new = manager.ProcessOptimizedAllocation(m_system.Kernel(), cur_address,
cur_pages, fill_pattern);
// Advance.
cur_address += cur_pages * PageSize;
@@ -382,7 +410,7 @@ Result KMemoryManager::AllocateForProcess(KPageGroup* out, size_t num_pages, u32
// Track some or all of the current pages.
const size_t cur_pages =
std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address));
manager.TrackOptimizedAllocation(cur_address, cur_pages);
manager.TrackOptimizedAllocation(m_system.Kernel(), cur_address, cur_pages);
// Advance.
cur_address += cur_pages * PageSize;
@@ -427,17 +455,86 @@ size_t KMemoryManager::Impl::Initialize(KPhysicalAddress address, size_t size,
return total_management_size;
}
void KMemoryManager::Impl::TrackUnoptimizedAllocation(KPhysicalAddress block, size_t num_pages) {
UNREACHABLE();
void KMemoryManager::Impl::InitializeOptimizedMemory(KernelCore& kernel) {
auto optimize_pa =
KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa);
std::memset(optimize_map, 0, CalculateOptimizedProcessOverheadSize(m_heap.GetSize()));
}
void KMemoryManager::Impl::TrackOptimizedAllocation(KPhysicalAddress block, size_t num_pages) {
UNREACHABLE();
void KMemoryManager::Impl::TrackUnoptimizedAllocation(KernelCore& kernel, KPhysicalAddress block,
size_t num_pages) {
auto optimize_pa =
KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa);
// Get the range we're tracking.
size_t offset = this->GetPageOffset(block);
const size_t last = offset + num_pages - 1;
// Track.
while (offset <= last) {
// Mark the page as not being optimized-allocated.
optimize_map[offset / Common::BitSize<u64>()] &=
~(u64(1) << (offset % Common::BitSize<u64>()));
offset++;
}
}
bool KMemoryManager::Impl::ProcessOptimizedAllocation(KPhysicalAddress block, size_t num_pages,
u8 fill_pattern) {
UNREACHABLE();
void KMemoryManager::Impl::TrackOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block,
size_t num_pages) {
auto optimize_pa =
KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
auto* optimize_map = kernel.System().DeviceMemory().GetPointer<u64>(optimize_pa);
// Get the range we're tracking.
size_t offset = this->GetPageOffset(block);
const size_t last = offset + num_pages - 1;
// Track.
while (offset <= last) {
// Mark the page as being optimized-allocated.
optimize_map[offset / Common::BitSize<u64>()] |=
(u64(1) << (offset % Common::BitSize<u64>()));
offset++;
}
}
bool KMemoryManager::Impl::ProcessOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block,
size_t num_pages, u8 fill_pattern) {
auto& device_memory = kernel.System().DeviceMemory();
auto optimize_pa =
KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), m_management_region);
auto* optimize_map = device_memory.GetPointer<u64>(optimize_pa);
// We want to return whether any pages were newly allocated.
bool any_new = false;
// Get the range we're processing.
size_t offset = this->GetPageOffset(block);
const size_t last = offset + num_pages - 1;
// Process.
while (offset <= last) {
// Check if the page has been optimized-allocated before.
if ((optimize_map[offset / Common::BitSize<u64>()] &
(u64(1) << (offset % Common::BitSize<u64>()))) == 0) {
// If not, it's new.
any_new = true;
// Fill the page.
auto* ptr = device_memory.GetPointer<u8>(m_heap.GetAddress());
std::memset(ptr + offset * PageSize, fill_pattern, PageSize);
}
offset++;
}
// Return the number of pages we processed.
return any_new;
}
size_t KMemoryManager::Impl::CalculateManagementOverheadSize(size_t region_size) {

View File

@@ -216,14 +216,14 @@ private:
m_heap.SetInitialUsedSize(reserved_size);
}
void InitializeOptimizedMemory() {
UNIMPLEMENTED();
}
void InitializeOptimizedMemory(KernelCore& kernel);
void TrackUnoptimizedAllocation(KPhysicalAddress block, size_t num_pages);
void TrackOptimizedAllocation(KPhysicalAddress block, size_t num_pages);
void TrackUnoptimizedAllocation(KernelCore& kernel, KPhysicalAddress block,
size_t num_pages);
void TrackOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block, size_t num_pages);
bool ProcessOptimizedAllocation(KPhysicalAddress block, size_t num_pages, u8 fill_pattern);
bool ProcessOptimizedAllocation(KernelCore& kernel, KPhysicalAddress block,
size_t num_pages, u8 fill_pattern);
constexpr Pool GetPool() const {
return m_pool;

View File

@@ -82,14 +82,14 @@ public:
using namespace Common::Literals;
constexpr size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceType as_type) {
constexpr size_t GetAddressSpaceWidthFromType(Svc::CreateProcessFlag as_type) {
switch (as_type) {
case FileSys::ProgramAddressSpaceType::Is32Bit:
case FileSys::ProgramAddressSpaceType::Is32BitNoMap:
case Svc::CreateProcessFlag::AddressSpace32Bit:
case Svc::CreateProcessFlag::AddressSpace32BitWithoutAlias:
return 32;
case FileSys::ProgramAddressSpaceType::Is36Bit:
case Svc::CreateProcessFlag::AddressSpace64BitDeprecated:
return 36;
case FileSys::ProgramAddressSpaceType::Is39Bit:
case Svc::CreateProcessFlag::AddressSpace64Bit:
return 39;
default:
ASSERT(false);
@@ -105,7 +105,7 @@ KPageTable::KPageTable(Core::System& system_)
KPageTable::~KPageTable() = default;
Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr,
Result KPageTable::InitializeForProcess(Svc::CreateProcessFlag as_type, bool enable_aslr,
bool enable_das_merge, bool from_back,
KMemoryManager::Pool pool, KProcessAddress code_addr,
size_t code_size, KSystemResource* system_resource,
@@ -133,7 +133,7 @@ Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type
ASSERT(code_addr + code_size - 1 <= end - 1);
// Adjust heap/alias size if we don't have an alias region
if (as_type == FileSys::ProgramAddressSpaceType::Is32BitNoMap) {
if (as_type == Svc::CreateProcessFlag::AddressSpace32BitWithoutAlias) {
heap_region_size += alias_region_size;
alias_region_size = 0;
}

View File

@@ -63,7 +63,7 @@ public:
explicit KPageTable(Core::System& system_);
~KPageTable();
Result InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr,
Result InitializeForProcess(Svc::CreateProcessFlag as_type, bool enable_aslr,
bool enable_das_merge, bool from_back, KMemoryManager::Pool pool,
KProcessAddress code_addr, size_t code_size,
KSystemResource* system_resource, KResourceLimit* resource_limit,
@@ -400,7 +400,7 @@ public:
constexpr size_t GetAliasCodeRegionSize() const {
return m_alias_code_region_end - m_alias_code_region_start;
}
size_t GetNormalMemorySize() {
size_t GetNormalMemorySize() const {
KScopedLightLock lk(m_general_lock);
return GetHeapSize() + m_mapped_physical_memory_size;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,59 +1,23 @@
// SPDX-FileCopyrightText: 2015 Citra Emulator Project
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <cstddef>
#include <list>
#include <map>
#include <string>
#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/k_address_arbiter.h"
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_capabilities.h"
#include "core/hle/kernel/k_condition_variable.h"
#include "core/hle/kernel/k_handle_table.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/k_page_table_manager.h"
#include "core/hle/kernel/k_system_resource.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_local_page.h"
#include "core/hle/kernel/k_typed_address.h"
#include "core/hle/kernel/k_worker_task.h"
#include "core/hle/kernel/process_capability.h"
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/result.h"
namespace Core {
namespace Memory {
class Memory;
};
class System;
} // namespace Core
namespace FileSys {
class ProgramMetadata;
}
namespace Kernel {
class KernelCore;
class KResourceLimit;
class KThread;
class KSharedMemoryInfo;
class TLSPage;
struct CodeSet;
enum class MemoryRegion : u16 {
APPLICATION = 1,
SYSTEM = 2,
BASE = 3,
};
enum class ProcessActivity : u32 {
Runnable,
Paused,
};
enum class DebugWatchpointType : u8 {
None = 0,
Read = 1 << 0,
@@ -72,9 +36,6 @@ class KProcess final : public KAutoObjectWithSlabHeapAndContainer<KProcess, KWor
KERNEL_AUTOOBJECT_TRAITS(KProcess, KSynchronizationObject);
public:
explicit KProcess(KernelCore& kernel);
~KProcess() override;
enum class State {
Created = static_cast<u32>(Svc::ProcessState::Created),
CreatedAttached = static_cast<u32>(Svc::ProcessState::CreatedAttached),
@@ -86,337 +47,83 @@ public:
DebugBreak = static_cast<u32>(Svc::ProcessState::DebugBreak),
};
enum : u64 {
/// Lowest allowed process ID for a kernel initial process.
InitialKIPIDMin = 1,
/// Highest allowed process ID for a kernel initial process.
InitialKIPIDMax = 80,
using ThreadList = Common::IntrusiveListMemberTraits<&KThread::m_process_list_node>::ListType;
/// Lowest allowed process ID for a userland process.
ProcessIDMin = 81,
/// Highest allowed process ID for a userland process.
ProcessIDMax = 0xFFFFFFFFFFFFFFFF,
};
static constexpr size_t AslrAlignment = 2_MiB;
// Used to determine how process IDs are assigned.
enum class ProcessType {
KernelInternal,
Userland,
};
public:
static constexpr u64 InitialProcessIdMin = 1;
static constexpr u64 InitialProcessIdMax = 0x50;
static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
static Result Initialize(KProcess* process, Core::System& system, std::string process_name,
ProcessType type, KResourceLimit* res_limit);
/// Gets a reference to the process' page table.
KPageTable& GetPageTable() {
return m_page_table;
}
/// Gets const a reference to the process' page table.
const KPageTable& GetPageTable() const {
return m_page_table;
}
/// Gets a reference to the process' handle table.
KHandleTable& GetHandleTable() {
return m_handle_table;
}
/// Gets a const reference to the process' handle table.
const KHandleTable& GetHandleTable() const {
return m_handle_table;
}
/// Gets a reference to process's memory.
Core::Memory::Memory& GetMemory() const;
Result SignalToAddress(KProcessAddress address) {
return m_condition_var.SignalToAddress(address);
}
Result WaitForAddress(Handle handle, KProcessAddress address, u32 tag) {
return m_condition_var.WaitForAddress(handle, address, tag);
}
void SignalConditionVariable(u64 cv_key, int32_t count) {
return m_condition_var.Signal(cv_key, count);
}
Result WaitConditionVariable(KProcessAddress address, u64 cv_key, u32 tag, s64 ns) {
R_RETURN(m_condition_var.Wait(address, cv_key, tag, ns));
}
Result SignalAddressArbiter(uint64_t address, Svc::SignalType signal_type, s32 value,
s32 count) {
R_RETURN(m_address_arbiter.SignalToAddress(address, signal_type, value, count));
}
Result WaitAddressArbiter(uint64_t address, Svc::ArbitrationType arb_type, s32 value,
s64 timeout) {
R_RETURN(m_address_arbiter.WaitForAddress(address, arb_type, value, timeout));
}
KProcessAddress GetProcessLocalRegionAddress() const {
return m_plr_address;
}
/// Gets the current status of the process
State GetState() const {
return m_state;
}
/// Gets the unique ID that identifies this particular process.
u64 GetProcessId() const {
return m_process_id;
}
/// Gets the program ID corresponding to this process.
u64 GetProgramId() const {
return m_program_id;
}
KProcessAddress GetEntryPoint() const {
return m_code_address;
}
/// Gets the resource limit descriptor for this process
KResourceLimit* GetResourceLimit() const;
/// Gets the ideal CPU core ID for this process
u8 GetIdealCoreId() const {
return m_ideal_core;
}
/// Checks if the specified thread priority is valid.
bool CheckThreadPriority(s32 prio) const {
return ((1ULL << prio) & GetPriorityMask()) != 0;
}
/// Gets the bitmask of allowed cores that this process' threads can run on.
u64 GetCoreMask() const {
return m_capabilities.GetCoreMask();
}
/// Gets the bitmask of allowed thread priorities.
u64 GetPriorityMask() const {
return m_capabilities.GetPriorityMask();
}
/// Gets the amount of secure memory to allocate for memory management.
u32 GetSystemResourceSize() const {
return m_system_resource_size;
}
/// Gets the amount of secure memory currently in use for memory management.
u32 GetSystemResourceUsage() const {
// On hardware, this returns the amount of system resource memory that has
// been used by the kernel. This is problematic for Yuzu to emulate, because
// system resource memory is used for page tables -- and yuzu doesn't really
// have a way to calculate how much memory is required for page tables for
// the current process at any given time.
// TODO: Is this even worth implementing? Games may retrieve this value via
// an SDK function that gets used + available system resource size for debug
// or diagnostic purposes. However, it seems unlikely that a game would make
// decisions based on how much system memory is dedicated to its page tables.
// Is returning a value other than zero wise?
return 0;
}
/// Whether this process is an AArch64 or AArch32 process.
bool Is64BitProcess() const {
return m_is_64bit_process;
}
bool IsSuspended() const {
return m_is_suspended;
}
void SetSuspended(bool suspended) {
m_is_suspended = suspended;
}
/// Gets the total running time of the process instance in ticks.
u64 GetCPUTimeTicks() const {
return m_total_process_running_time_ticks;
}
/// Updates the total running time, adding the given ticks to it.
void UpdateCPUTimeTicks(u64 ticks) {
m_total_process_running_time_ticks += ticks;
}
/// Gets the process schedule count, used for thread yielding
s64 GetScheduledCount() const {
return m_schedule_count;
}
/// Increments the process schedule count, used for thread yielding.
void IncrementScheduledCount() {
++m_schedule_count;
}
void IncrementRunningThreadCount();
void DecrementRunningThreadCount();
void SetRunningThread(s32 core, KThread* thread, u64 idle_count) {
m_running_threads[core] = thread;
m_running_thread_idle_counts[core] = idle_count;
}
void ClearRunningThread(KThread* thread) {
for (size_t i = 0; i < m_running_threads.size(); ++i) {
if (m_running_threads[i] == thread) {
m_running_threads[i] = nullptr;
}
}
}
[[nodiscard]] KThread* GetRunningThread(s32 core) const {
return m_running_threads[core];
}
bool ReleaseUserException(KThread* thread);
[[nodiscard]] KThread* GetPinnedThread(s32 core_id) const {
ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
return m_pinned_threads[core_id];
}
/// Gets 8 bytes of random data for svcGetInfo RandomEntropy
u64 GetRandomEntropy(std::size_t index) const {
return m_random_entropy.at(index);
}
/// Retrieves the total physical memory available to this process in bytes.
u64 GetTotalPhysicalMemoryAvailable();
/// Retrieves the total physical memory available to this process in bytes,
/// without the size of the personal system resource heap added to it.
u64 GetTotalPhysicalMemoryAvailableWithoutSystemResource();
/// Retrieves the total physical memory used by this process in bytes.
u64 GetTotalPhysicalMemoryUsed();
/// Retrieves the total physical memory used by this process in bytes,
/// without the size of the personal system resource heap added to it.
u64 GetTotalPhysicalMemoryUsedWithoutSystemResource();
/// Gets the list of all threads created with this process as their owner.
std::list<KThread*>& GetThreadList() {
return m_thread_list;
}
/// Registers a thread as being created under this process,
/// adding it to this process' thread list.
void RegisterThread(KThread* thread);
/// Unregisters a thread from this process, removing it
/// from this process' thread list.
void UnregisterThread(KThread* thread);
/// Retrieves the number of available threads for this process.
u64 GetFreeThreadCount() const;
/// Clears the signaled state of the process if and only if it's signaled.
///
/// @pre The process must not be already terminated. If this is called on a
/// terminated process, then ResultInvalidState will be returned.
///
/// @pre The process must be in a signaled state. If this is called on a
/// process instance that is not signaled, ResultInvalidState will be
/// returned.
Result Reset();
/**
* Loads process-specifics configuration info with metadata provided
* by an executable.
*
* @param metadata The provided metadata to load process specific info from.
*
* @returns ResultSuccess if all relevant metadata was able to be
* loaded and parsed. Otherwise, an error code is returned.
*/
Result LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size,
bool is_hbl);
/**
* Starts the main application thread for this process.
*
* @param main_thread_priority The priority for the main thread.
* @param stack_size The stack size for the main thread in bytes.
*/
void Run(s32 main_thread_priority, u64 stack_size);
/**
* Prepares a process for termination by stopping all of its threads
* and clearing any other resources.
*/
void PrepareForTermination();
void LoadModule(CodeSet code_set, KProcessAddress base_addr);
bool IsInitialized() const override {
return m_is_initialized;
}
static void PostDestroy(uintptr_t arg) {}
void Finalize() override;
u64 GetId() const override {
return GetProcessId();
}
bool IsHbl() const {
return m_is_hbl;
}
bool IsSignaled() const override;
void DoWorkerTaskImpl();
Result SetActivity(ProcessActivity activity);
void PinCurrentThread(s32 core_id);
void UnpinCurrentThread(s32 core_id);
void UnpinThread(KThread* thread);
KLightLock& GetStateLock() {
return m_state_lock;
}
Result AddSharedMemory(KSharedMemory* shmem, KProcessAddress address, size_t size);
void RemoveSharedMemory(KSharedMemory* shmem, KProcessAddress address, size_t size);
///////////////////////////////////////////////////////////////////////////////////////////////
// Thread-local storage management
// Marks the next available region as used and returns the address of the slot.
[[nodiscard]] Result CreateThreadLocalRegion(KProcessAddress* out);
// Frees a used TLS slot identified by the given address
Result DeleteThreadLocalRegion(KProcessAddress addr);
///////////////////////////////////////////////////////////////////////////////////////////////
// Debug watchpoint management
// Attempts to insert a watchpoint into a free slot. Returns false if none are available.
bool InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type);
// Attempts to remove the watchpoint specified by the given parameters.
bool RemoveWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type);
const std::array<DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>& GetWatchpoints() const {
return m_watchpoints;
}
const std::string& GetName() {
return name;
}
static constexpr u64 ProcessIdMin = InitialProcessIdMax + 1;
static constexpr u64 ProcessIdMax = std::numeric_limits<u64>::max();
private:
using SharedMemoryInfoList = Common::IntrusiveListBaseTraits<KSharedMemoryInfo>::ListType;
using TLPTree =
Common::IntrusiveRedBlackTreeBaseTraits<KThreadLocalPage>::TreeType<KThreadLocalPage>;
using TLPIterator = TLPTree::iterator;
private:
KPageTable m_page_table;
std::atomic<size_t> m_used_kernel_memory_size{};
TLPTree m_fully_used_tlp_tree{};
TLPTree m_partially_used_tlp_tree{};
s32 m_ideal_core_id{};
KResourceLimit* m_resource_limit{};
KSystemResource* m_system_resource{};
size_t m_memory_release_hint{};
State m_state{};
KLightLock m_state_lock;
KLightLock m_list_lock;
KConditionVariable m_cond_var;
KAddressArbiter m_address_arbiter;
std::array<u64, 4> m_entropy{};
bool m_is_signaled{};
bool m_is_initialized{};
bool m_is_application{};
bool m_is_default_application_system_resource{};
bool m_is_hbl{};
std::array<char, 13> m_name{};
std::atomic<u16> m_num_running_threads{};
Svc::CreateProcessFlag m_flags{};
KMemoryManager::Pool m_memory_pool{};
s64 m_schedule_count{};
KCapabilities m_capabilities{};
u64 m_program_id{};
u64 m_process_id{};
KProcessAddress m_code_address{};
size_t m_code_size{};
size_t m_main_thread_stack_size{};
size_t m_max_process_memory{};
u32 m_version{};
KHandleTable m_handle_table;
KProcessAddress m_plr_address{};
KThread* m_exception_thread{};
ThreadList m_thread_list{};
SharedMemoryInfoList m_shared_memory_list{};
bool m_is_suspended{};
bool m_is_immortal{};
bool m_is_handle_table_initialized{};
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> m_running_threads{};
std::array<u64, Core::Hardware::NUM_CPU_CORES> m_running_thread_idle_counts{};
std::array<u64, Core::Hardware::NUM_CPU_CORES> m_running_thread_switch_counts{};
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> m_pinned_threads{};
std::array<DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS> m_watchpoints{};
std::map<KProcessAddress, u64> m_debug_page_refcounts{};
std::atomic<s64> m_cpu_time{};
std::atomic<s64> m_num_process_switches{};
std::atomic<s64> m_num_thread_switches{};
std::atomic<s64> m_num_fpu_switches{};
std::atomic<s64> m_num_supervisor_calls{};
std::atomic<s64> m_num_ipc_messages{};
std::atomic<s64> m_num_ipc_replies{};
std::atomic<s64> m_num_ipc_receives{};
private:
Result StartTermination();
void FinishTermination();
void PinThread(s32 core_id, KThread* thread) {
ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
ASSERT(thread != nullptr);
@@ -431,6 +138,395 @@ private:
m_pinned_threads[core_id] = nullptr;
}
public:
explicit KProcess(KernelCore& kernel);
~KProcess() override;
Result Initialize(const Svc::CreateProcessParameter& params, KResourceLimit* res_limit,
bool is_real);
Result Initialize(const Svc::CreateProcessParameter& params, const KPageGroup& pg,
std::span<const u32> caps, KResourceLimit* res_limit,
KMemoryManager::Pool pool, bool immortal);
Result Initialize(const Svc::CreateProcessParameter& params, std::span<const u32> user_caps,
KResourceLimit* res_limit, KMemoryManager::Pool pool);
void Exit();
const char* GetName() const {
return m_name.data();
}
u64 GetProgramId() const {
return m_program_id;
}
u64 GetProcessId() const {
return m_process_id;
}
State GetState() const {
return m_state;
}
u64 GetCoreMask() const {
return m_capabilities.GetCoreMask();
}
u64 GetPhysicalCoreMask() const {
return m_capabilities.GetPhysicalCoreMask();
}
u64 GetPriorityMask() const {
return m_capabilities.GetPriorityMask();
}
s32 GetIdealCoreId() const {
return m_ideal_core_id;
}
void SetIdealCoreId(s32 core_id) {
m_ideal_core_id = core_id;
}
bool CheckThreadPriority(s32 prio) const {
return ((1ULL << prio) & this->GetPriorityMask()) != 0;
}
u32 GetCreateProcessFlags() const {
return static_cast<u32>(m_flags);
}
bool Is64Bit() const {
return True(m_flags & Svc::CreateProcessFlag::Is64Bit);
}
KProcessAddress GetEntryPoint() const {
return m_code_address;
}
size_t GetMainStackSize() const {
return m_main_thread_stack_size;
}
KMemoryManager::Pool GetMemoryPool() const {
return m_memory_pool;
}
u64 GetRandomEntropy(size_t i) const {
return m_entropy[i];
}
bool IsApplication() const {
return m_is_application;
}
bool IsDefaultApplicationSystemResource() const {
return m_is_default_application_system_resource;
}
bool IsSuspended() const {
return m_is_suspended;
}
void SetSuspended(bool suspended) {
m_is_suspended = suspended;
}
Result Terminate();
bool IsTerminated() const {
return m_state == State::Terminated;
}
bool IsPermittedSvc(u32 svc_id) const {
return m_capabilities.IsPermittedSvc(svc_id);
}
bool IsPermittedInterrupt(s32 interrupt_id) const {
return m_capabilities.IsPermittedInterrupt(interrupt_id);
}
bool IsPermittedDebug() const {
return m_capabilities.IsPermittedDebug();
}
bool CanForceDebug() const {
return m_capabilities.CanForceDebug();
}
bool IsHbl() const {
return m_is_hbl;
}
Kernel::KMemoryManager::Direction GetAllocateOption() const {
// TODO: property of the KPageTableBase
return KMemoryManager::Direction::FromFront;
}
ThreadList& GetThreadList() {
return m_thread_list;
}
const ThreadList& GetThreadList() const {
return m_thread_list;
}
bool EnterUserException();
bool LeaveUserException();
bool ReleaseUserException(KThread* thread);
KThread* GetPinnedThread(s32 core_id) const {
ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
return m_pinned_threads[core_id];
}
const Svc::SvcAccessFlagSet& GetSvcPermissions() const {
return m_capabilities.GetSvcPermissions();
}
KResourceLimit* GetResourceLimit() const {
return m_resource_limit;
}
bool ReserveResource(Svc::LimitableResource which, s64 value);
bool ReserveResource(Svc::LimitableResource which, s64 value, s64 timeout);
void ReleaseResource(Svc::LimitableResource which, s64 value);
void ReleaseResource(Svc::LimitableResource which, s64 value, s64 hint);
KLightLock& GetStateLock() {
return m_state_lock;
}
KLightLock& GetListLock() {
return m_list_lock;
}
KPageTable& GetPageTable() {
return m_page_table;
}
const KPageTable& GetPageTable() const {
return m_page_table;
}
KHandleTable& GetHandleTable() {
return m_handle_table;
}
const KHandleTable& GetHandleTable() const {
return m_handle_table;
}
size_t GetUsedUserPhysicalMemorySize() const;
size_t GetTotalUserPhysicalMemorySize() const;
size_t GetUsedNonSystemUserPhysicalMemorySize() const;
size_t GetTotalNonSystemUserPhysicalMemorySize() const;
Result AddSharedMemory(KSharedMemory* shmem, KProcessAddress address, size_t size);
void RemoveSharedMemory(KSharedMemory* shmem, KProcessAddress address, size_t size);
Result CreateThreadLocalRegion(KProcessAddress* out);
Result DeleteThreadLocalRegion(KProcessAddress addr);
KProcessAddress GetProcessLocalRegionAddress() const {
return m_plr_address;
}
KThread* GetExceptionThread() const {
return m_exception_thread;
}
void AddCpuTime(s64 diff) {
m_cpu_time += diff;
}
s64 GetCpuTime() {
return m_cpu_time.load();
}
s64 GetScheduledCount() const {
return m_schedule_count;
}
void IncrementScheduledCount() {
++m_schedule_count;
}
void IncrementRunningThreadCount();
void DecrementRunningThreadCount();
size_t GetRequiredSecureMemorySizeNonDefault() const {
if (!this->IsDefaultApplicationSystemResource() && m_system_resource->IsSecureResource()) {
auto* secure_system_resource = static_cast<KSecureSystemResource*>(m_system_resource);
return secure_system_resource->CalculateRequiredSecureMemorySize();
}
return 0;
}
size_t GetRequiredSecureMemorySize() const {
if (m_system_resource->IsSecureResource()) {
auto* secure_system_resource = static_cast<KSecureSystemResource*>(m_system_resource);
return secure_system_resource->CalculateRequiredSecureMemorySize();
}
return 0;
}
size_t GetTotalSystemResourceSize() const {
if (!this->IsDefaultApplicationSystemResource() && m_system_resource->IsSecureResource()) {
auto* secure_system_resource = static_cast<KSecureSystemResource*>(m_system_resource);
return secure_system_resource->GetSize();
}
return 0;
}
size_t GetUsedSystemResourceSize() const {
if (!this->IsDefaultApplicationSystemResource() && m_system_resource->IsSecureResource()) {
auto* secure_system_resource = static_cast<KSecureSystemResource*>(m_system_resource);
return secure_system_resource->GetUsedSize();
}
return 0;
}
void SetRunningThread(s32 core, KThread* thread, u64 idle_count, u64 switch_count) {
m_running_threads[core] = thread;
m_running_thread_idle_counts[core] = idle_count;
m_running_thread_switch_counts[core] = switch_count;
}
void ClearRunningThread(KThread* thread) {
for (size_t i = 0; i < m_running_threads.size(); ++i) {
if (m_running_threads[i] == thread) {
m_running_threads[i] = nullptr;
}
}
}
const KSystemResource& GetSystemResource() const {
return *m_system_resource;
}
const KMemoryBlockSlabManager& GetMemoryBlockSlabManager() const {
return m_system_resource->GetMemoryBlockSlabManager();
}
const KBlockInfoManager& GetBlockInfoManager() const {
return m_system_resource->GetBlockInfoManager();
}
const KPageTableManager& GetPageTableManager() const {
return m_system_resource->GetPageTableManager();
}
KThread* GetRunningThread(s32 core) const {
return m_running_threads[core];
}
u64 GetRunningThreadIdleCount(s32 core) const {
return m_running_thread_idle_counts[core];
}
u64 GetRunningThreadSwitchCount(s32 core) const {
return m_running_thread_switch_counts[core];
}
void RegisterThread(KThread* thread);
void UnregisterThread(KThread* thread);
Result Run(s32 priority, size_t stack_size);
Result Reset();
void SetDebugBreak() {
if (m_state == State::RunningAttached) {
this->ChangeState(State::DebugBreak);
}
}
void SetAttached() {
if (m_state == State::DebugBreak) {
this->ChangeState(State::RunningAttached);
}
}
Result SetActivity(Svc::ProcessActivity activity);
void PinCurrentThread();
void UnpinCurrentThread();
void UnpinThread(KThread* thread);
void SignalConditionVariable(uintptr_t cv_key, int32_t count) {
return m_cond_var.Signal(cv_key, count);
}
Result WaitConditionVariable(KProcessAddress address, uintptr_t cv_key, u32 tag, s64 ns) {
R_RETURN(m_cond_var.Wait(address, cv_key, tag, ns));
}
Result SignalAddressArbiter(uintptr_t address, Svc::SignalType signal_type, s32 value,
s32 count) {
R_RETURN(m_address_arbiter.SignalToAddress(address, signal_type, value, count));
}
Result WaitAddressArbiter(uintptr_t address, Svc::ArbitrationType arb_type, s32 value,
s64 timeout) {
R_RETURN(m_address_arbiter.WaitForAddress(address, arb_type, value, timeout));
}
Result GetThreadList(s32* out_num_threads, KProcessAddress out_thread_ids, s32 max_out_count);
static void Switch(KProcess* cur_process, KProcess* next_process);
public:
// Attempts to insert a watchpoint into a free slot. Returns false if none are available.
bool InsertWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type);
// Attempts to remove the watchpoint specified by the given parameters.
bool RemoveWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointType type);
const std::array<DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>& GetWatchpoints() const {
return m_watchpoints;
}
public:
Result LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size,
bool is_hbl);
void LoadModule(CodeSet code_set, KProcessAddress base_addr);
Core::Memory::Memory& GetMemory() const;
public:
// Overridden parent functions.
bool IsInitialized() const override {
return m_is_initialized;
}
static void PostDestroy(uintptr_t arg) {}
void Finalize() override;
u64 GetIdImpl() const {
return this->GetProcessId();
}
u64 GetId() const override {
return this->GetIdImpl();
}
virtual bool IsSignaled() const override {
ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel));
return m_is_signaled;
}
void DoWorkerTaskImpl();
private:
void ChangeState(State new_state) {
if (m_state != new_state) {
m_state = new_state;
m_is_signaled = true;
this->NotifyAvailable();
}
}
Result InitializeHandleTable(s32 size) {
// Try to initialize the handle table.
R_TRY(m_handle_table.Initialize(size));
// We succeeded, so note that we did.
m_is_handle_table_initialized = true;
R_SUCCEED();
}
void FinalizeHandleTable() {
// Finalize the table.
m_handle_table.Finalize();
@@ -438,118 +534,6 @@ private:
// Note that the table is finalized.
m_is_handle_table_initialized = false;
}
void ChangeState(State new_state);
/// Allocates the main thread stack for the process, given the stack size in bytes.
Result AllocateMainThreadStack(std::size_t stack_size);
/// Memory manager for this process
KPageTable m_page_table;
/// Current status of the process
State m_state{};
/// The ID of this process
u64 m_process_id = 0;
/// Title ID corresponding to the process
u64 m_program_id = 0;
/// Specifies additional memory to be reserved for the process's memory management by the
/// system. When this is non-zero, secure memory is allocated and used for page table allocation
/// instead of using the normal global page tables/memory block management.
u32 m_system_resource_size = 0;
/// Resource limit descriptor for this process
KResourceLimit* m_resource_limit{};
KVirtualAddress m_system_resource_address{};
/// The ideal CPU core for this process, threads are scheduled on this core by default.
u8 m_ideal_core = 0;
/// Contains the parsed process capability descriptors.
ProcessCapabilities m_capabilities;
/// Whether or not this process is AArch64, or AArch32.
/// By default, we currently assume this is true, unless otherwise
/// specified by metadata provided to the process during loading.
bool m_is_64bit_process = true;
/// Total running time for the process in ticks.
std::atomic<u64> m_total_process_running_time_ticks = 0;
/// Per-process handle table for storing created object handles in.
KHandleTable m_handle_table;
/// Per-process address arbiter.
KAddressArbiter m_address_arbiter;
/// The per-process mutex lock instance used for handling various
/// forms of services, such as lock arbitration, and condition
/// variable related facilities.
KConditionVariable m_condition_var;
/// Address indicating the location of the process' dedicated TLS region.
KProcessAddress m_plr_address = 0;
/// Address indicating the location of the process's entry point.
KProcessAddress m_code_address = 0;
/// Random values for svcGetInfo RandomEntropy
std::array<u64, RANDOM_ENTROPY_SIZE> m_random_entropy{};
/// List of threads that are running with this process as their owner.
std::list<KThread*> m_thread_list;
/// List of shared memory that are running with this process as their owner.
std::list<KSharedMemoryInfo*> m_shared_memory_list;
/// Address of the top of the main thread's stack
KProcessAddress m_main_thread_stack_top{};
/// Size of the main thread's stack
std::size_t m_main_thread_stack_size{};
/// Memory usage capacity for the process
std::size_t m_memory_usage_capacity{};
/// Process total image size
std::size_t m_image_size{};
/// Schedule count of this process
s64 m_schedule_count{};
size_t m_memory_release_hint{};
std::string name{};
bool m_is_signaled{};
bool m_is_suspended{};
bool m_is_immortal{};
bool m_is_handle_table_initialized{};
bool m_is_initialized{};
bool m_is_hbl{};
std::atomic<u16> m_num_running_threads{};
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> m_running_threads{};
std::array<u64, Core::Hardware::NUM_CPU_CORES> m_running_thread_idle_counts{};
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> m_pinned_threads{};
std::array<DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS> m_watchpoints{};
std::map<KProcessAddress, u64> m_debug_page_refcounts;
KThread* m_exception_thread{};
KLightLock m_state_lock;
KLightLock m_list_lock;
using TLPTree =
Common::IntrusiveRedBlackTreeBaseTraits<KThreadLocalPage>::TreeType<KThreadLocalPage>;
using TLPIterator = TLPTree::iterator;
TLPTree m_fully_used_tlp_tree;
TLPTree m_partially_used_tlp_tree;
};
} // namespace Kernel

View File

@@ -190,7 +190,7 @@ u64 KScheduler::UpdateHighestPriorityThread(KThread* highest_thread) {
if (m_state.should_count_idle) {
if (highest_thread != nullptr) [[likely]] {
if (KProcess* process = highest_thread->GetOwnerProcess(); process != nullptr) {
process->SetRunningThread(m_core_id, highest_thread, m_state.idle_count);
process->SetRunningThread(m_core_id, highest_thread, m_state.idle_count, 0);
}
} else {
m_state.idle_count++;
@@ -356,7 +356,7 @@ void KScheduler::SwitchThread(KThread* next_thread) {
const s64 tick_diff = cur_tick - prev_tick;
cur_thread->AddCpuTime(m_core_id, tick_diff);
if (cur_process != nullptr) {
cur_process->UpdateCPUTimeTicks(tick_diff);
cur_process->AddCpuTime(tick_diff);
}
m_last_context_switch_time = cur_tick;

View File

@@ -1,25 +1,100 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_system_resource.h"
namespace Kernel {
Result KSecureSystemResource::Initialize(size_t size, KResourceLimit* resource_limit,
KMemoryManager::Pool pool) {
// Unimplemented
UNREACHABLE();
// Set members.
m_resource_limit = resource_limit;
m_resource_size = size;
m_resource_pool = pool;
// Determine required size for our secure resource.
const size_t secure_size = this->CalculateRequiredSecureMemorySize();
// Reserve memory for our secure resource.
KScopedResourceReservation memory_reservation(
m_resource_limit, Svc::LimitableResource::PhysicalMemoryMax, secure_size);
R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
// Allocate secure memory.
R_TRY(KSystemControl::AllocateSecureMemory(m_kernel, std::addressof(m_resource_address),
m_resource_size, static_cast<u32>(m_resource_pool)));
ASSERT(m_resource_address != 0);
// Ensure we clean up the secure memory, if we fail past this point.
ON_RESULT_FAILURE {
KSystemControl::FreeSecureMemory(m_kernel, m_resource_address, m_resource_size,
static_cast<u32>(m_resource_pool));
};
// Check that our allocation is bigger than the reference counts needed for it.
const size_t rc_size =
Common::AlignUp(KPageTableSlabHeap::CalculateReferenceCountSize(m_resource_size), PageSize);
R_UNLESS(m_resource_size > rc_size, ResultOutOfMemory);
// Get resource pointer.
KPhysicalAddress resource_paddr =
KPageTable::GetHeapPhysicalAddress(m_kernel.MemoryLayout(), m_resource_address);
auto* resource =
m_kernel.System().DeviceMemory().GetPointer<KPageTableManager::RefCount>(resource_paddr);
// Initialize slab heaps.
m_dynamic_page_manager.Initialize(m_resource_address + rc_size, m_resource_size - rc_size,
PageSize);
m_page_table_heap.Initialize(std::addressof(m_dynamic_page_manager), 0, resource);
m_memory_block_heap.Initialize(std::addressof(m_dynamic_page_manager), 0);
m_block_info_heap.Initialize(std::addressof(m_dynamic_page_manager), 0);
// Initialize managers.
m_page_table_manager.Initialize(std::addressof(m_dynamic_page_manager),
std::addressof(m_page_table_heap));
m_memory_block_slab_manager.Initialize(std::addressof(m_dynamic_page_manager),
std::addressof(m_memory_block_heap));
m_block_info_manager.Initialize(std::addressof(m_dynamic_page_manager),
std::addressof(m_block_info_heap));
// Set our managers.
this->SetManagers(m_memory_block_slab_manager, m_block_info_manager, m_page_table_manager);
// Commit the memory reservation.
memory_reservation.Commit();
// Open reference to our resource limit.
m_resource_limit->Open();
// Set ourselves as initialized.
m_is_initialized = true;
R_SUCCEED();
}
void KSecureSystemResource::Finalize() {
// Unimplemented
UNREACHABLE();
// Check that we have no outstanding allocations.
ASSERT(m_memory_block_slab_manager.GetUsed() == 0);
ASSERT(m_block_info_manager.GetUsed() == 0);
ASSERT(m_page_table_manager.GetUsed() == 0);
// Free our secure memory.
KSystemControl::FreeSecureMemory(m_kernel, m_resource_address, m_resource_size,
static_cast<u32>(m_resource_pool));
// Release the memory reservation.
m_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax,
this->CalculateRequiredSecureMemorySize());
// Close reference to our resource limit.
m_resource_limit->Close();
}
size_t KSecureSystemResource::CalculateRequiredSecureMemorySize(size_t size,
KMemoryManager::Pool pool) {
// Unimplemented
UNREACHABLE();
return KSystemControl::CalculateRequiredSecureMemorySize(size, static_cast<u32>(pool));
}
} // namespace Kernel

View File

@@ -122,16 +122,15 @@ Result KThread::Initialize(KThreadFunction func, uintptr_t arg, KProcessAddress
case ThreadType::Main:
ASSERT(arg == 0);
[[fallthrough]];
case ThreadType::HighPriority:
[[fallthrough]];
case ThreadType::Dummy:
[[fallthrough]];
case ThreadType::User:
ASSERT(((owner == nullptr) ||
(owner->GetCoreMask() | (1ULL << virt_core)) == owner->GetCoreMask()));
ASSERT(((owner == nullptr) || (prio > Svc::LowestThreadPriority) ||
(owner->GetPriorityMask() | (1ULL << prio)) == owner->GetPriorityMask()));
break;
case ThreadType::HighPriority:
case ThreadType::Dummy:
break;
case ThreadType::Kernel:
UNIMPLEMENTED();
break;
@@ -216,6 +215,7 @@ Result KThread::Initialize(KThreadFunction func, uintptr_t arg, KProcessAddress
// Setup the TLS, if needed.
if (type == ThreadType::User) {
R_TRY(owner->CreateThreadLocalRegion(std::addressof(m_tls_address)));
owner->GetMemory().ZeroBlock(m_tls_address, Svc::ThreadLocalRegionSize);
}
m_parent = owner;
@@ -403,7 +403,7 @@ void KThread::StartTermination() {
if (m_parent != nullptr) {
m_parent->ReleaseUserException(this);
if (m_parent->GetPinnedThread(GetCurrentCoreId(m_kernel)) == this) {
m_parent->UnpinCurrentThread(m_core_id);
m_parent->UnpinCurrentThread();
}
}
@@ -415,10 +415,6 @@ void KThread::StartTermination() {
m_parent->ClearRunningThread(this);
}
// Signal.
m_signaled = true;
KSynchronizationObject::NotifyAvailable();
// Clear previous thread in KScheduler.
KScheduler::ClearPreviousThread(m_kernel, this);
@@ -437,6 +433,13 @@ void KThread::FinishTermination() {
}
}
// Acquire the scheduler lock.
KScopedSchedulerLock sl{m_kernel};
// Signal.
m_signaled = true;
KSynchronizationObject::NotifyAvailable();
// Close the thread.
this->Close();
}
@@ -820,7 +823,7 @@ void KThread::CloneFpuStatus() {
ASSERT(this->GetOwnerProcess() != nullptr);
ASSERT(this->GetOwnerProcess() == GetCurrentProcessPointer(m_kernel));
if (this->GetOwnerProcess()->Is64BitProcess()) {
if (this->GetOwnerProcess()->Is64Bit()) {
// Clone FPSR and FPCR.
ThreadContext64 cur_ctx{};
m_kernel.System().CurrentArmInterface().SaveContext(cur_ctx);
@@ -923,7 +926,7 @@ Result KThread::GetThreadContext3(Common::ScratchBuffer<u8>& out) {
// If we're not terminating, get the thread's user context.
if (!this->IsTerminationRequested()) {
if (m_parent->Is64BitProcess()) {
if (m_parent->Is64Bit()) {
// Mask away mode bits, interrupt bits, IL bit, and other reserved bits.
auto context = GetContext64();
context.pstate &= 0xFF0FFE20;
@@ -1174,6 +1177,9 @@ Result KThread::Run() {
owner->IncrementRunningThreadCount();
}
// Open a reference, now that we're running.
this->Open();
// Set our state and finish.
this->SetState(ThreadState::Runnable);

View File

@@ -721,6 +721,7 @@ private:
// For core KThread implementation
ThreadContext32 m_thread_context_32{};
ThreadContext64 m_thread_context_64{};
Common::IntrusiveListNode m_process_list_node;
Common::IntrusiveRedBlackTreeNode m_condvar_arbiter_tree_node{};
s32 m_priority{};
using ConditionVariableThreadTreeTraits =

View File

@@ -101,35 +101,31 @@ struct KernelCore::Impl {
void InitializeCores() {
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
cores[core_id]->Initialize((*application_process).Is64BitProcess());
cores[core_id]->Initialize((*application_process).Is64Bit());
system.ApplicationMemory().SetCurrentPageTable(*application_process, core_id);
}
}
void CloseApplicationProcess() {
KProcess* old_process = application_process.exchange(nullptr);
if (old_process == nullptr) {
return;
}
// old_process->Close();
// TODO: The process should be destroyed based on accurate ref counting after
// calling Close(). Adding a manual Destroy() call instead to avoid a memory leak.
old_process->Finalize();
old_process->Destroy();
void TerminateApplicationProcess() {
application_process.load()->Terminate();
}
void Shutdown() {
is_shutting_down.store(true, std::memory_order_relaxed);
SCOPE_EXIT({ is_shutting_down.store(false, std::memory_order_relaxed); });
process_list.clear();
CloseServices();
auto* old_process = application_process.exchange(nullptr);
if (old_process) {
old_process->Close();
}
process_list.clear();
next_object_id = 0;
next_kernel_process_id = KProcess::InitialKIPIDMin;
next_user_process_id = KProcess::ProcessIDMin;
next_kernel_process_id = KProcess::InitialProcessIdMin;
next_user_process_id = KProcess::ProcessIdMin;
next_thread_id = 1;
global_handle_table->Finalize();
@@ -176,8 +172,6 @@ struct KernelCore::Impl {
}
}
CloseApplicationProcess();
// Track kernel objects that were not freed on shutdown
{
std::scoped_lock lk{registered_objects_lock};
@@ -344,6 +338,8 @@ struct KernelCore::Impl {
// Create the system page table managers.
app_system_resource = std::make_unique<KSystemResource>(kernel);
sys_system_resource = std::make_unique<KSystemResource>(kernel);
KAutoObject::Create(std::addressof(*app_system_resource));
KAutoObject::Create(std::addressof(*sys_system_resource));
// Set the managers for the system resources.
app_system_resource->SetManagers(*app_memory_block_manager, *app_block_info_manager,
@@ -792,8 +788,8 @@ struct KernelCore::Impl {
std::mutex registered_in_use_objects_lock;
std::atomic<u32> next_object_id{0};
std::atomic<u64> next_kernel_process_id{KProcess::InitialKIPIDMin};
std::atomic<u64> next_user_process_id{KProcess::ProcessIDMin};
std::atomic<u64> next_kernel_process_id{KProcess::InitialProcessIdMin};
std::atomic<u64> next_user_process_id{KProcess::ProcessIdMin};
std::atomic<u64> next_thread_id{1};
// Lists all processes that exist in the current session.
@@ -924,10 +920,6 @@ const KProcess* KernelCore::ApplicationProcess() const {
return impl->application_process;
}
void KernelCore::CloseApplicationProcess() {
impl->CloseApplicationProcess();
}
const std::vector<KProcess*>& KernelCore::GetProcessList() const {
return impl->process_list;
}
@@ -1128,8 +1120,8 @@ std::jthread KernelCore::RunOnHostCoreProcess(std::string&& process_name,
std::function<void()> func) {
// Make a new process.
KProcess* process = KProcess::Create(*this);
ASSERT(R_SUCCEEDED(KProcess::Initialize(process, System(), "", KProcess::ProcessType::Userland,
GetSystemResourceLimit())));
ASSERT(R_SUCCEEDED(
process->Initialize(Svc::CreateProcessParameter{}, GetSystemResourceLimit(), false)));
// Ensure that we don't hold onto any extra references.
SCOPE_EXIT({ process->Close(); });
@@ -1156,8 +1148,8 @@ void KernelCore::RunOnGuestCoreProcess(std::string&& process_name, std::function
// Make a new process.
KProcess* process = KProcess::Create(*this);
ASSERT(R_SUCCEEDED(KProcess::Initialize(process, System(), "", KProcess::ProcessType::Userland,
GetSystemResourceLimit())));
ASSERT(R_SUCCEEDED(
process->Initialize(Svc::CreateProcessParameter{}, GetSystemResourceLimit(), false)));
// Ensure that we don't hold onto any extra references.
SCOPE_EXIT({ process->Close(); });
@@ -1266,7 +1258,8 @@ const Kernel::KSharedMemory& KernelCore::GetHidBusSharedMem() const {
void KernelCore::SuspendApplication(bool suspended) {
const bool should_suspend{exception_exited || suspended};
const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable;
const auto activity =
should_suspend ? Svc::ProcessActivity::Paused : Svc::ProcessActivity::Runnable;
// Get the application process.
KScopedAutoObject<KProcess> process = ApplicationProcess();
@@ -1300,6 +1293,8 @@ void KernelCore::SuspendApplication(bool suspended) {
}
void KernelCore::ShutdownCores() {
impl->TerminateApplicationProcess();
KScopedSchedulerLock lk{*this};
for (auto* thread : impl->shutdown_threads) {

View File

@@ -134,9 +134,6 @@ public:
/// Retrieves a const pointer to the application process.
const KProcess* ApplicationProcess() const;
/// Closes the application process.
void CloseApplicationProcess();
/// Retrieves the list of processes.
const std::vector<KProcess*>& GetProcessList() const;

View File

@@ -4426,7 +4426,7 @@ void Call(Core::System& system, u32 imm) {
auto& kernel = system.Kernel();
kernel.EnterSVCProfile();
if (GetCurrentProcess(system.Kernel()).Is64BitProcess()) {
if (GetCurrentProcess(system.Kernel()).Is64Bit()) {
Call64(system, imm);
} else {
Call32(system, imm);

View File

@@ -86,20 +86,19 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
R_SUCCEED();
case InfoType::TotalMemorySize:
*result = process->GetTotalPhysicalMemoryAvailable();
*result = process->GetTotalUserPhysicalMemorySize();
R_SUCCEED();
case InfoType::UsedMemorySize:
*result = process->GetTotalPhysicalMemoryUsed();
*result = process->GetUsedUserPhysicalMemorySize();
R_SUCCEED();
case InfoType::SystemResourceSizeTotal:
*result = process->GetSystemResourceSize();
*result = process->GetTotalSystemResourceSize();
R_SUCCEED();
case InfoType::SystemResourceSizeUsed:
LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage");
*result = process->GetSystemResourceUsage();
*result = process->GetUsedSystemResourceSize();
R_SUCCEED();
case InfoType::ProgramId:
@@ -111,20 +110,29 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
R_SUCCEED();
case InfoType::TotalNonSystemMemorySize:
*result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource();
*result = process->GetTotalNonSystemUserPhysicalMemorySize();
R_SUCCEED();
case InfoType::UsedNonSystemMemorySize:
*result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource();
*result = process->GetUsedNonSystemUserPhysicalMemorySize();
R_SUCCEED();
case InfoType::IsApplication:
LOG_WARNING(Kernel_SVC, "(STUBBED) Assuming process is application");
*result = true;
*result = process->IsApplication();
R_SUCCEED();
case InfoType::FreeThreadCount:
*result = process->GetFreeThreadCount();
if (KResourceLimit* resource_limit = process->GetResourceLimit();
resource_limit != nullptr) {
const auto current_value =
resource_limit->GetCurrentValue(Svc::LimitableResource::ThreadCountMax);
const auto limit_value =
resource_limit->GetLimitValue(Svc::LimitableResource::ThreadCountMax);
*result = limit_value - current_value;
} else {
*result = 0;
}
R_SUCCEED();
default:
@@ -161,7 +169,7 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
case InfoType::RandomEntropy:
R_UNLESS(handle == 0, ResultInvalidHandle);
R_UNLESS(info_sub_id < KProcess::RANDOM_ENTROPY_SIZE, ResultInvalidCombination);
R_UNLESS(info_sub_id < 4, ResultInvalidCombination);
*result = GetCurrentProcess(system.Kernel()).GetRandomEntropy(info_sub_id);
R_SUCCEED();

View File

@@ -17,7 +17,7 @@ Result ArbitrateLock(Core::System& system, Handle thread_handle, u64 address, u3
R_UNLESS(!IsKernelAddress(address), ResultInvalidCurrentMemory);
R_UNLESS(Common::IsAligned(address, sizeof(u32)), ResultInvalidAddress);
R_RETURN(GetCurrentProcess(system.Kernel()).WaitForAddress(thread_handle, address, tag));
R_RETURN(KConditionVariable::WaitForAddress(system.Kernel(), thread_handle, address, tag));
}
/// Unlock a mutex
@@ -28,7 +28,7 @@ Result ArbitrateUnlock(Core::System& system, u64 address) {
R_UNLESS(!IsKernelAddress(address), ResultInvalidCurrentMemory);
R_UNLESS(Common::IsAligned(address, sizeof(u32)), ResultInvalidAddress);
R_RETURN(GetCurrentProcess(system.Kernel()).SignalToAddress(address));
R_RETURN(KConditionVariable::SignalToAddress(system.Kernel(), address));
}
Result ArbitrateLock64(Core::System& system, Handle thread_handle, uint64_t address, uint32_t tag) {

View File

@@ -46,7 +46,7 @@ Result MapPhysicalMemory(Core::System& system, u64 addr, u64 size) {
KProcess* const current_process{GetCurrentProcessPointer(system.Kernel())};
auto& page_table{current_process->GetPageTable()};
if (current_process->GetSystemResourceSize() == 0) {
if (current_process->GetTotalSystemResourceSize() == 0) {
LOG_ERROR(Kernel_SVC, "System Resource Size is zero");
R_THROW(ResultInvalidState);
}
@@ -95,7 +95,7 @@ Result UnmapPhysicalMemory(Core::System& system, u64 addr, u64 size) {
KProcess* const current_process{GetCurrentProcessPointer(system.Kernel())};
auto& page_table{current_process->GetPageTable()};
if (current_process->GetSystemResourceSize() == 0) {
if (current_process->GetTotalSystemResourceSize() == 0) {
LOG_ERROR(Kernel_SVC, "System Resource Size is zero");
R_THROW(ResultInvalidState);
}

View File

@@ -132,7 +132,7 @@ void SynchronizePreemptionState(Core::System& system) {
GetCurrentThread(kernel).ClearInterruptFlag();
// Unpin the current thread.
cur_process->UnpinCurrentThread(core_id);
cur_process->UnpinCurrentThread();
}
}

View File

@@ -85,10 +85,6 @@ Result StartThread(Core::System& system, Handle thread_handle) {
// Try to start the thread.
R_TRY(thread->Run());
// If we succeeded, persist a reference to the thread.
thread->Open();
system.Kernel().RegisterInUseObject(thread.GetPointerUnsafe());
R_SUCCEED();
}
@@ -99,7 +95,6 @@ void ExitThread(Core::System& system) {
auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
system.GlobalSchedulerContext().RemoveThread(current_thread);
current_thread->Exit();
system.Kernel().UnregisterInUseObject(current_thread);
}
/// Sleep the current thread
@@ -260,7 +255,7 @@ Result GetThreadList(Core::System& system, s32* out_num_threads, u64 out_thread_
auto list_iter = thread_list.cbegin();
for (std::size_t i = 0; i < copy_amount; ++i, ++list_iter) {
memory.Write64(out_thread_ids, (*list_iter)->GetThreadId());
memory.Write64(out_thread_ids, list_iter->GetThreadId());
out_thread_ids += sizeof(u64);
}

View File

@@ -592,7 +592,7 @@ void Call(Core::System& system, u32 imm) {
auto& kernel = system.Kernel();
kernel.EnterSVCProfile();
if (GetCurrentProcess(system.Kernel()).Is64BitProcess()) {
if (GetCurrentProcess(system.Kernel()).Is64Bit()) {
Call64(system, imm);
} else {
Call32(system, imm);

View File

@@ -604,13 +604,57 @@ enum class ProcessActivity : u32 {
Paused,
};
enum class CreateProcessFlag : u32 {
// Is 64 bit?
Is64Bit = (1 << 0),
// What kind of address space?
AddressSpaceShift = 1,
AddressSpaceMask = (7 << AddressSpaceShift),
AddressSpace32Bit = (0 << AddressSpaceShift),
AddressSpace64BitDeprecated = (1 << AddressSpaceShift),
AddressSpace32BitWithoutAlias = (2 << AddressSpaceShift),
AddressSpace64Bit = (3 << AddressSpaceShift),
// Should JIT debug be done on crash?
EnableDebug = (1 << 4),
// Should ASLR be enabled for the process?
EnableAslr = (1 << 5),
// Is the process an application?
IsApplication = (1 << 6),
// 4.x deprecated: Should use secure memory?
DeprecatedUseSecureMemory = (1 << 7),
// 5.x+ Pool partition type.
PoolPartitionShift = 7,
PoolPartitionMask = (0xF << PoolPartitionShift),
PoolPartitionApplication = (0 << PoolPartitionShift),
PoolPartitionApplet = (1 << PoolPartitionShift),
PoolPartitionSystem = (2 << PoolPartitionShift),
PoolPartitionSystemNonSecure = (3 << PoolPartitionShift),
// 7.x+ Should memory allocation be optimized? This requires IsApplication.
OptimizeMemoryAllocation = (1 << 11),
// 11.x+ DisableDeviceAddressSpaceMerge.
DisableDeviceAddressSpaceMerge = (1 << 12),
// Mask of all flags.
All = Is64Bit | AddressSpaceMask | EnableDebug | EnableAslr | IsApplication |
PoolPartitionMask | OptimizeMemoryAllocation | DisableDeviceAddressSpaceMerge,
};
DECLARE_ENUM_FLAG_OPERATORS(CreateProcessFlag);
struct CreateProcessParameter {
std::array<char, 12> name;
u32 version;
u64 program_id;
u64 code_address;
s32 code_num_pages;
u32 flags;
CreateProcessFlag flags;
Handle reslimit;
s32 system_resource_num_pages;
};

View File

@@ -21,10 +21,8 @@ ServiceContext::ServiceContext(Core::System& system_, std::string name_)
// Create the process.
process = Kernel::KProcess::Create(kernel);
ASSERT(Kernel::KProcess::Initialize(process, system_, std::move(name_),
Kernel::KProcess::ProcessType::KernelInternal,
kernel.GetSystemResourceLimit())
.IsSuccess());
ASSERT(R_SUCCEEDED(process->Initialize(Kernel::Svc::CreateProcessParameter{},
kernel.GetSystemResourceLimit(), false)));
// Register the process.
Kernel::KProcess::Register(kernel, process);

View File

@@ -41,7 +41,7 @@ bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock<std::mutex>& lk)
s32 BufferQueueCore::GetMinUndequeuedBufferCountLocked(bool async) const {
// If DequeueBuffer is allowed to error out, we don't have to add an extra buffer.
if (!use_async_buffer) {
return max_acquired_buffer_count;
return 0;
}
if (dequeue_buffer_cannot_block || async) {
@@ -52,7 +52,7 @@ s32 BufferQueueCore::GetMinUndequeuedBufferCountLocked(bool async) const {
}
s32 BufferQueueCore::GetMinMaxBufferCountLocked(bool async) const {
return GetMinUndequeuedBufferCountLocked(async) + 1;
return GetMinUndequeuedBufferCountLocked(async);
}
s32 BufferQueueCore::GetMaxBufferCountLocked(bool async) const {
@@ -61,7 +61,7 @@ s32 BufferQueueCore::GetMaxBufferCountLocked(bool async) const {
if (override_max_buffer_count != 0) {
ASSERT(override_max_buffer_count >= min_buffer_count);
max_buffer_count = override_max_buffer_count;
return override_max_buffer_count;
}
// Any buffers that are dequeued by the producer or sitting in the queue waiting to be consumed

View File

@@ -134,7 +134,7 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, St
const s32 max_buffer_count = core->GetMaxBufferCountLocked(async);
if (async && core->override_max_buffer_count) {
if (core->override_max_buffer_count < max_buffer_count) {
LOG_ERROR(Service_Nvnflinger, "async mode is invalid with buffer count override");
*found = BufferQueueCore::INVALID_BUFFER_SLOT;
return Status::BadValue;
}
}
@@ -142,7 +142,8 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, St
// Free up any buffers that are in slots beyond the max buffer count
for (s32 s = max_buffer_count; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
ASSERT(slots[s].buffer_state == BufferState::Free);
if (slots[s].graphic_buffer != nullptr) {
if (slots[s].graphic_buffer != nullptr && slots[s].buffer_state == BufferState::Free &&
!slots[s].is_preallocated) {
core->FreeBufferLocked(s);
*return_flags |= Status::ReleaseAllBuffers;
}

View File

@@ -66,7 +66,6 @@ Nvnflinger::Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_
"ScreenComposition",
[this](std::uintptr_t, s64 time,
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
{ const auto lock_guard = Lock(); }
vsync_signal.Set();
return std::chrono::nanoseconds(GetNextTicks());
});
@@ -99,6 +98,7 @@ Nvnflinger::~Nvnflinger() {
}
ShutdownLayers();
vsync_thread = {};
if (nvdrv) {
nvdrv->Close(disp_fd);
@@ -106,6 +106,7 @@ Nvnflinger::~Nvnflinger() {
}
void Nvnflinger::ShutdownLayers() {
const auto lock_guard = Lock();
for (auto& display : displays) {
for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) {
display.GetLayer(layer).Core().NotifyShutdown();
@@ -229,16 +230,6 @@ VI::Layer* Nvnflinger::FindLayer(u64 display_id, u64 layer_id) {
return display->FindLayer(layer_id);
}
const VI::Layer* Nvnflinger::FindLayer(u64 display_id, u64 layer_id) const {
const auto* const display = FindDisplay(display_id);
if (display == nullptr) {
return nullptr;
}
return display->FindLayer(layer_id);
}
VI::Layer* Nvnflinger::FindOrCreateLayer(u64 display_id, u64 layer_id) {
auto* const display = FindDisplay(display_id);
@@ -288,7 +279,6 @@ void Nvnflinger::Compose() {
auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd);
ASSERT(nvdisp);
guard->unlock();
Common::Rectangle<int> crop_rect{
static_cast<int>(buffer.crop.Left()), static_cast<int>(buffer.crop.Top()),
static_cast<int>(buffer.crop.Right()), static_cast<int>(buffer.crop.Bottom())};
@@ -299,7 +289,6 @@ void Nvnflinger::Compose() {
buffer.fence.fences, buffer.fence.num_fences);
MicroProfileFlip();
guard->lock();
swap_interval = buffer.swap_interval;

View File

@@ -117,9 +117,6 @@ private:
/// Finds the layer identified by the specified ID in the desired display.
[[nodiscard]] VI::Layer* FindLayer(u64 display_id, u64 layer_id);
/// Finds the layer identified by the specified ID in the desired display.
[[nodiscard]] const VI::Layer* FindLayer(u64 display_id, u64 layer_id) const;
/// Finds the layer identified by the specified ID in the desired display,
/// or creates the layer if it is not found.
/// To be used when the system expects the specified ID to already exist.

View File

@@ -37,7 +37,7 @@ std::optional<Kernel::KProcess*> SearchProcessList(
void GetApplicationPidGeneric(HLERequestContext& ctx,
const std::vector<Kernel::KProcess*>& process_list) {
const auto process = SearchProcessList(process_list, [](const auto& proc) {
return proc->GetProcessId() == Kernel::KProcess::ProcessIDMin;
return proc->GetProcessId() == Kernel::KProcess::ProcessIdMin;
});
IPC::ResponseBuilder rb{ctx, 4};

View File

@@ -116,7 +116,7 @@ json GetProcessorStateDataAuto(Core::System& system) {
Core::ARM_Interface::ThreadContext64 context{};
arm.SaveContext(context);
return GetProcessorStateData(process->Is64BitProcess() ? "AArch64" : "AArch32",
return GetProcessorStateData(process->Is64Bit() ? "AArch64" : "AArch32",
GetInteger(process->GetEntryPoint()), context.sp, context.pc,
context.pstate, context.cpu_registers);
}

View File

@@ -139,7 +139,7 @@ void JoyconDriver::InputThread(std::stop_token stop_token) {
input_thread_running = true;
// Max update rate is 5ms, ensure we are always able to read a bit faster
constexpr int ThreadDelay = 2;
constexpr int ThreadDelay = 3;
std::vector<u8> buffer(MaxBufferSize);
while (!stop_token.stop_requested()) {
@@ -163,6 +163,17 @@ void JoyconDriver::InputThread(std::stop_token stop_token) {
OnNewData(buffer);
}
if (!vibration_queue.Empty()) {
VibrationValue vibration_value;
vibration_queue.Pop(vibration_value);
last_vibration_result = rumble_protocol->SendVibration(vibration_value);
}
// We can't keep up with vibrations. Start skipping.
while (vibration_queue.Size() > 6) {
vibration_queue.Pop();
}
std::this_thread::yield();
}
@@ -402,7 +413,8 @@ Common::Input::DriverResult JoyconDriver::SetVibration(const VibrationValue& vib
if (disable_input_thread) {
return Common::Input::DriverResult::HandleInUse;
}
return rumble_protocol->SendVibration(vibration);
vibration_queue.Push(vibration);
return last_vibration_result;
}
Common::Input::DriverResult JoyconDriver::SetLedConfig(u8 led_pattern) {

View File

@@ -9,6 +9,7 @@
#include <span>
#include <thread>
#include "common/threadsafe_queue.h"
#include "input_common/helpers/joycon_protocol/joycon_types.h"
namespace Common::Input {
@@ -152,6 +153,10 @@ private:
SerialNumber handle_serial_number{}; // Serial number type reported by hidapi
SupportedFeatures supported_features{};
/// Queue of vibration request to controllers
Common::Input::DriverResult last_vibration_result{Common::Input::DriverResult::Success};
Common::SPSCQueue<VibrationValue> vibration_queue;
// Thread related
mutable std::mutex mutex;
std::jthread input_thread;

View File

@@ -1067,8 +1067,7 @@ void BufferCache<P>::BindHostComputeTextureBuffers() {
template <class P>
void BufferCache<P>::DoUpdateGraphicsBuffers(bool is_indexed) {
do {
channel_state->has_deleted_buffers = false;
BufferOperations([&]() {
if (is_indexed) {
UpdateIndexBuffer();
}
@@ -1082,14 +1081,16 @@ void BufferCache<P>::DoUpdateGraphicsBuffers(bool is_indexed) {
if (current_draw_indirect) {
UpdateDrawIndirect();
}
} while (channel_state->has_deleted_buffers);
});
}
template <class P>
void BufferCache<P>::DoUpdateComputeBuffers() {
UpdateComputeUniformBuffers();
UpdateComputeStorageBuffers();
UpdateComputeTextureBuffers();
BufferOperations([&]() {
UpdateComputeUniformBuffers();
UpdateComputeStorageBuffers();
UpdateComputeTextureBuffers();
});
}
template <class P>

View File

@@ -358,7 +358,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
.has_broken_spirv_subgroup_mask_vector_extract_dynamic =
driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY,
.has_broken_robust =
device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Maxwell,
device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal,
};
host_info = Shader::HostTranslateInfo{

View File

@@ -13,6 +13,7 @@
#include "common/microprofile.h"
#include "common/scope_exit.h"
#include "common/settings.h"
#include "video_core/buffer_cache/buffer_cache.h"
#include "video_core/control/channel_state.h"
#include "video_core/engines/draw_manager.h"
#include "video_core/engines/kepler_compute.h"
@@ -285,6 +286,7 @@ void RasterizerVulkan::DrawTexture() {
query_cache.NotifySegment(true);
std::scoped_lock l{texture_cache.mutex};
texture_cache.SynchronizeGraphicsDescriptors();
texture_cache.UpdateRenderTargets(false);

View File

@@ -227,14 +227,14 @@ add_executable(yuzu
yuzu.rc
)
if (WIN32 AND YUZU_CRASH_DUMPS)
if (YUZU_CRASH_DUMPS)
target_sources(yuzu PRIVATE
mini_dump.cpp
mini_dump.h
breakpad.cpp
breakpad.h
)
target_link_libraries(yuzu PRIVATE ${DBGHELP_LIBRARY})
target_compile_definitions(yuzu PRIVATE -DYUZU_DBGHELP)
target_link_libraries(yuzu PRIVATE libbreakpad_client)
target_compile_definitions(yuzu PRIVATE YUZU_CRASH_DUMPS)
endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")

77
src/yuzu/breakpad.cpp Normal file
View File

@@ -0,0 +1,77 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <ranges>
#if defined(_WIN32)
#include <client/windows/handler/exception_handler.h>
#elif defined(__linux__)
#include <client/linux/handler/exception_handler.h>
#else
#error Minidump creation not supported on this platform
#endif
#include "common/fs/fs_paths.h"
#include "common/fs/path_util.h"
#include "yuzu/breakpad.h"
namespace Breakpad {
static void PruneDumpDirectory(const std::filesystem::path& dump_path) {
// Code in this function should be exception-safe.
struct Entry {
std::filesystem::path path;
std::filesystem::file_time_type last_write_time;
};
std::vector<Entry> existing_dumps;
// Get existing entries.
std::error_code ec;
std::filesystem::directory_iterator dir(dump_path, ec);
for (auto& entry : dir) {
if (entry.is_regular_file()) {
existing_dumps.push_back(Entry{
.path = entry.path(),
.last_write_time = entry.last_write_time(ec),
});
}
}
// Sort descending by creation date.
std::ranges::stable_sort(existing_dumps, [](const auto& a, const auto& b) {
return a.last_write_time > b.last_write_time;
});
// Delete older dumps.
for (size_t i = 5; i < existing_dumps.size(); i++) {
std::filesystem::remove(existing_dumps[i].path, ec);
}
}
#if defined(__linux__)
[[noreturn]] bool DumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, void* context,
bool succeeded) {
// Prevent time- and space-consuming core dumps from being generated, as we have
// already generated a minidump and a core file will not be useful anyway.
_exit(1);
}
#endif
void InstallCrashHandler() {
// Write crash dumps to profile directory.
const auto dump_path = GetYuzuPath(Common::FS::YuzuPath::CrashDumpsDir);
PruneDumpDirectory(dump_path);
#if defined(_WIN32)
// TODO: If we switch to MinGW builds for Windows, this needs to be wrapped in a C API.
static google_breakpad::ExceptionHandler eh{dump_path, nullptr, nullptr, nullptr,
google_breakpad::ExceptionHandler::HANDLER_ALL};
#elif defined(__linux__)
static google_breakpad::MinidumpDescriptor descriptor{dump_path};
static google_breakpad::ExceptionHandler eh{descriptor, nullptr, DumpCallback,
nullptr, true, -1};
#endif
}
} // namespace Breakpad

10
src/yuzu/breakpad.h Normal file
View File

@@ -0,0 +1,10 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
namespace Breakpad {
void InstallCrashHandler();
}

View File

@@ -27,16 +27,6 @@ ConfigureDebug::ConfigureDebug(const Core::System& system_, QWidget* parent)
connect(ui->toggle_gdbstub, &QCheckBox::toggled,
[&]() { ui->gdbport_spinbox->setEnabled(ui->toggle_gdbstub->isChecked()); });
connect(ui->create_crash_dumps, &QCheckBox::stateChanged, [&](int) {
if (crash_dump_warning_shown) {
return;
}
QMessageBox::warning(this, tr("Restart Required"),
tr("yuzu is required to restart in order to apply this setting."),
QMessageBox::Ok, QMessageBox::Ok);
crash_dump_warning_shown = true;
});
}
ConfigureDebug::~ConfigureDebug() = default;
@@ -89,13 +79,6 @@ void ConfigureDebug::SetConfiguration() {
ui->disable_web_applet->setEnabled(false);
ui->disable_web_applet->setText(tr("Web applet not compiled"));
#endif
#ifdef YUZU_DBGHELP
ui->create_crash_dumps->setChecked(Settings::values.create_crash_dumps.GetValue());
#else
ui->create_crash_dumps->setEnabled(false);
ui->create_crash_dumps->setText(tr("MiniDump creation not compiled"));
#endif
}
void ConfigureDebug::ApplyConfiguration() {
@@ -107,7 +90,6 @@ void ConfigureDebug::ApplyConfiguration() {
Settings::values.enable_fs_access_log = ui->fs_access_log->isChecked();
Settings::values.reporting_services = ui->reporting_services->isChecked();
Settings::values.dump_audio_commands = ui->dump_audio_commands->isChecked();
Settings::values.create_crash_dumps = ui->create_crash_dumps->isChecked();
Settings::values.quest_flag = ui->quest_flag->isChecked();
Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked();
Settings::values.use_auto_stub = ui->use_auto_stub->isChecked();

View File

@@ -471,13 +471,6 @@
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="create_crash_dumps">
<property name="text">
<string>Create Minidump After Crash</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="dump_audio_commands">
<property name="toolTip">

View File

@@ -127,7 +127,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() cons
return list;
}
if (thread.GetOwnerProcess() == nullptr || !thread.GetOwnerProcess()->Is64BitProcess()) {
if (thread.GetOwnerProcess() == nullptr || !thread.GetOwnerProcess()->Is64Bit()) {
return list;
}

View File

@@ -159,8 +159,8 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include "yuzu/util/clickable_label.h"
#include "yuzu/vk_device_info.h"
#ifdef YUZU_DBGHELP
#include "yuzu/mini_dump.h"
#ifdef YUZU_CRASH_DUMPS
#include "yuzu/breakpad.h"
#endif
using namespace Common::Literals;
@@ -2019,7 +2019,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
std::filesystem::path{Common::U16StringFromBuffer(filename.utf16(), filename.size())}
.filename());
}
const bool is_64bit = system->Kernel().ApplicationProcess()->Is64BitProcess();
const bool is_64bit = system->Kernel().ApplicationProcess()->Is64Bit();
const auto instruction_set_suffix = is_64bit ? tr("(64-bit)") : tr("(32-bit)");
title_name = tr("%1 %2", "%1 is the title name. %2 indicates if the title is 64-bit or 32-bit")
.arg(QString::fromStdString(title_name), instruction_set_suffix)
@@ -5187,22 +5187,15 @@ int main(int argc, char* argv[]) {
return 0;
}
#ifdef YUZU_DBGHELP
PROCESS_INFORMATION pi;
if (!is_child && Settings::values.create_crash_dumps.GetValue() &&
MiniDump::SpawnDebuggee(argv[0], pi)) {
// Delete the config object so that it doesn't save when the program exits
config.reset(nullptr);
MiniDump::DebugDebuggee(pi);
return 0;
}
#endif
if (StartupChecks(argv[0], &has_broken_vulkan,
Settings::values.perform_vulkan_check.GetValue())) {
return 0;
}
#ifdef YUZU_CRASH_DUMPS
Breakpad::InstallCrashHandler();
#endif
Common::DetachedTasks detached_tasks;
MicroProfileOnThreadCreate("Frontend");
SCOPE_EXIT({ MicroProfileShutdown(); });

View File

@@ -1,202 +0,0 @@
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstdio>
#include <cstring>
#include <ctime>
#include <filesystem>
#include <fmt/format.h>
#include <windows.h>
#include "yuzu/mini_dump.h"
#include "yuzu/startup_checks.h"
// dbghelp.h must be included after windows.h
#include <dbghelp.h>
namespace MiniDump {
void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info,
EXCEPTION_POINTERS* pep) {
char file_name[255];
const std::time_t the_time = std::time(nullptr);
std::strftime(file_name, 255, "yuzu-crash-%Y%m%d%H%M%S.dmp", std::localtime(&the_time));
// Open the file
HANDLE file_handle = CreateFileA(file_name, GENERIC_READ | GENERIC_WRITE, 0, nullptr,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (file_handle == nullptr || file_handle == INVALID_HANDLE_VALUE) {
fmt::print(stderr, "CreateFileA failed. Error: {}", GetLastError());
return;
}
// Create the minidump
const MINIDUMP_TYPE dump_type = MiniDumpNormal;
const bool write_dump_status = MiniDumpWriteDump(process_handle, process_id, file_handle,
dump_type, (pep != 0) ? info : 0, 0, 0);
if (write_dump_status) {
fmt::print(stderr, "MiniDump created: {}", file_name);
} else {
fmt::print(stderr, "MiniDumpWriteDump failed. Error: {}", GetLastError());
}
// Close the file
CloseHandle(file_handle);
}
void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi) {
EXCEPTION_RECORD& record = deb_ev.u.Exception.ExceptionRecord;
HANDLE thread_handle = OpenThread(THREAD_GET_CONTEXT, false, deb_ev.dwThreadId);
if (thread_handle == nullptr) {
fmt::print(stderr, "OpenThread failed ({})", GetLastError());
return;
}
// Get child process context
CONTEXT context = {};
context.ContextFlags = CONTEXT_ALL;
if (!GetThreadContext(thread_handle, &context)) {
fmt::print(stderr, "GetThreadContext failed ({})", GetLastError());
return;
}
// Create exception pointers for minidump
EXCEPTION_POINTERS ep;
ep.ExceptionRecord = &record;
ep.ContextRecord = &context;
MINIDUMP_EXCEPTION_INFORMATION info;
info.ThreadId = deb_ev.dwThreadId;
info.ExceptionPointers = &ep;
info.ClientPointers = false;
CreateMiniDump(pi.hProcess, pi.dwProcessId, &info, &ep);
if (CloseHandle(thread_handle) == 0) {
fmt::print(stderr, "error: CloseHandle(thread_handle) failed ({})", GetLastError());
}
}
bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi) {
std::memset(&pi, 0, sizeof(pi));
// Don't debug if we are already being debugged
if (IsDebuggerPresent()) {
return false;
}
if (!SpawnChild(arg0, &pi, 0)) {
fmt::print(stderr, "warning: continuing without crash dumps");
return false;
}
const bool can_debug = DebugActiveProcess(pi.dwProcessId);
if (!can_debug) {
fmt::print(stderr,
"warning: DebugActiveProcess failed ({}), continuing without crash dumps",
GetLastError());
return false;
}
return true;
}
static const char* ExceptionName(DWORD exception) {
switch (exception) {
case EXCEPTION_ACCESS_VIOLATION:
return "EXCEPTION_ACCESS_VIOLATION";
case EXCEPTION_DATATYPE_MISALIGNMENT:
return "EXCEPTION_DATATYPE_MISALIGNMENT";
case EXCEPTION_BREAKPOINT:
return "EXCEPTION_BREAKPOINT";
case EXCEPTION_SINGLE_STEP:
return "EXCEPTION_SINGLE_STEP";
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
case EXCEPTION_FLT_DENORMAL_OPERAND:
return "EXCEPTION_FLT_DENORMAL_OPERAND";
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
return "EXCEPTION_FLT_DIVIDE_BY_ZERO";
case EXCEPTION_FLT_INEXACT_RESULT:
return "EXCEPTION_FLT_INEXACT_RESULT";
case EXCEPTION_FLT_INVALID_OPERATION:
return "EXCEPTION_FLT_INVALID_OPERATION";
case EXCEPTION_FLT_OVERFLOW:
return "EXCEPTION_FLT_OVERFLOW";
case EXCEPTION_FLT_STACK_CHECK:
return "EXCEPTION_FLT_STACK_CHECK";
case EXCEPTION_FLT_UNDERFLOW:
return "EXCEPTION_FLT_UNDERFLOW";
case EXCEPTION_INT_DIVIDE_BY_ZERO:
return "EXCEPTION_INT_DIVIDE_BY_ZERO";
case EXCEPTION_INT_OVERFLOW:
return "EXCEPTION_INT_OVERFLOW";
case EXCEPTION_PRIV_INSTRUCTION:
return "EXCEPTION_PRIV_INSTRUCTION";
case EXCEPTION_IN_PAGE_ERROR:
return "EXCEPTION_IN_PAGE_ERROR";
case EXCEPTION_ILLEGAL_INSTRUCTION:
return "EXCEPTION_ILLEGAL_INSTRUCTION";
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
return "EXCEPTION_NONCONTINUABLE_EXCEPTION";
case EXCEPTION_STACK_OVERFLOW:
return "EXCEPTION_STACK_OVERFLOW";
case EXCEPTION_INVALID_DISPOSITION:
return "EXCEPTION_INVALID_DISPOSITION";
case EXCEPTION_GUARD_PAGE:
return "EXCEPTION_GUARD_PAGE";
case EXCEPTION_INVALID_HANDLE:
return "EXCEPTION_INVALID_HANDLE";
default:
return "unknown exception type";
}
}
void DebugDebuggee(PROCESS_INFORMATION& pi) {
DEBUG_EVENT deb_ev = {};
while (deb_ev.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT) {
const bool wait_success = WaitForDebugEvent(&deb_ev, INFINITE);
if (!wait_success) {
fmt::print(stderr, "error: WaitForDebugEvent failed ({})", GetLastError());
return;
}
switch (deb_ev.dwDebugEventCode) {
case OUTPUT_DEBUG_STRING_EVENT:
case CREATE_PROCESS_DEBUG_EVENT:
case CREATE_THREAD_DEBUG_EVENT:
case EXIT_PROCESS_DEBUG_EVENT:
case EXIT_THREAD_DEBUG_EVENT:
case LOAD_DLL_DEBUG_EVENT:
case RIP_EVENT:
case UNLOAD_DLL_DEBUG_EVENT:
// Continue on all other debug events
ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_CONTINUE);
break;
case EXCEPTION_DEBUG_EVENT:
EXCEPTION_RECORD& record = deb_ev.u.Exception.ExceptionRecord;
// We want to generate a crash dump if we are seeing the same exception again.
if (!deb_ev.u.Exception.dwFirstChance) {
fmt::print(stderr, "Creating MiniDump on ExceptionCode: 0x{:08x} {}\n",
record.ExceptionCode, ExceptionName(record.ExceptionCode));
DumpFromDebugEvent(deb_ev, pi);
}
// Continue without handling the exception.
// Lets the debuggee use its own exception handler.
// - If one does not exist, we will see the exception once more where we make a minidump
// for. Then when it reaches here again, yuzu will probably crash.
// - DBG_CONTINUE on an exception that the debuggee does not handle can set us up for an
// infinite loop of exceptions.
ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_EXCEPTION_NOT_HANDLED);
break;
}
}
}
} // namespace MiniDump

View File

@@ -1,19 +0,0 @@
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <windows.h>
#include <dbghelp.h>
namespace MiniDump {
void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info,
EXCEPTION_POINTERS* pep);
void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi);
bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi);
void DebugDebuggee(PROCESS_INFORMATION& pi);
} // namespace MiniDump

View File

@@ -33,10 +33,6 @@
"description": "Compile tests",
"dependencies": [ "catch2" ]
},
"dbghelp": {
"description": "Compile Windows crash dump (Minidump) support",
"dependencies": [ "dbghelp" ]
},
"web-service": {
"description": "Enable web services (telemetry, etc.)",
"dependencies": [