Compare commits
141 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
543e212554 | ||
|
|
8d1afcb90f | ||
|
|
c8fe8247ee | ||
|
|
e24c6dab93 | ||
|
|
10738839ad | ||
|
|
3856564727 | ||
|
|
ff46ef7ea3 | ||
|
|
6ee8340a6b | ||
|
|
055194d2ab | ||
|
|
091e9e8c41 | ||
|
|
6e953f7f02 | ||
|
|
37f74d8741 | ||
|
|
f6b10fad63 | ||
|
|
0a1449e04b | ||
|
|
89a5ae92bd | ||
|
|
ca78f77827 | ||
|
|
cdd14b03e5 | ||
|
|
1470338458 | ||
|
|
1772ebeb1e | ||
|
|
1f99f5473c | ||
|
|
c0f5830323 | ||
|
|
bb966d3e33 | ||
|
|
ff186b2498 | ||
|
|
33b4930280 | ||
|
|
5a4fc4a529 | ||
|
|
97129bc742 | ||
|
|
2fb77adb9f | ||
|
|
4dbf3f4880 | ||
|
|
1e55498110 | ||
|
|
0530292b97 | ||
|
|
4782985013 | ||
|
|
eea346ba8e | ||
|
|
9a4e148f9e | ||
|
|
c0d3aef28c | ||
|
|
df41e78205 | ||
|
|
c21ce728c2 | ||
|
|
16818e952c | ||
|
|
9f6290d207 | ||
|
|
0e125dfd43 | ||
|
|
4921ba05db | ||
|
|
ae6b3bdfbf | ||
|
|
008afa5d59 | ||
|
|
659b5f8088 | ||
|
|
9e88ad8da9 | ||
|
|
45b13c3037 | ||
|
|
ef6cc3aa1d | ||
|
|
28b822fe38 | ||
|
|
d4d39aa4c7 | ||
|
|
fb0fe3b8c3 | ||
|
|
2350b76a91 | ||
|
|
09b6f03592 | ||
|
|
72c1cb85f1 | ||
|
|
64a5548454 | ||
|
|
81a037df9d | ||
|
|
2c57f0fbd5 | ||
|
|
04e9486651 | ||
|
|
2a2ee62cfd | ||
|
|
62766b1326 | ||
|
|
5dc021d15b | ||
|
|
34c3ec2f8c | ||
|
|
45e117b043 | ||
|
|
9dc4a80b17 | ||
|
|
df0d8c45d2 | ||
|
|
b769b1be26 | ||
|
|
44c5ea3639 | ||
|
|
6b00443bc1 | ||
|
|
8959f3521f | ||
|
|
6a0143400f | ||
|
|
748551dafb | ||
|
|
19c14589d3 | ||
|
|
a8245cf2f1 | ||
|
|
2afc1060ef | ||
|
|
5882cc0502 | ||
|
|
04dcada85f | ||
|
|
f81c783b5b | ||
|
|
cc4335a9c6 | ||
|
|
f7ac4e1eb4 | ||
|
|
1b76e7e890 | ||
|
|
80a673a27f | ||
|
|
ad48259d7e | ||
|
|
e9bb95ae16 | ||
|
|
9477d23d70 | ||
|
|
bfd2bcb068 | ||
|
|
5942d206c2 | ||
|
|
822edff5bd | ||
|
|
3b0458a7a5 | ||
|
|
65f821850e | ||
|
|
df42100320 | ||
|
|
966896daad | ||
|
|
625a011888 | ||
|
|
12355cbf02 | ||
|
|
37ef2ee595 | ||
|
|
302a5f00e8 | ||
|
|
981d8e82d2 | ||
|
|
a175ba1089 | ||
|
|
1e9b1d439f | ||
|
|
436457b6e7 | ||
|
|
2c4c7aea8a | ||
|
|
b7febb5625 | ||
|
|
0e9a6759f9 | ||
|
|
dd790abab0 | ||
|
|
46dda01151 | ||
|
|
6ff2db181f | ||
|
|
a1335d3d51 | ||
|
|
ffbde909c8 | ||
|
|
f83ef80ebd | ||
|
|
51512d01d8 | ||
|
|
d98b0f8f48 | ||
|
|
c795207fb2 | ||
|
|
5b8bc56e65 | ||
|
|
dc18a1261c | ||
|
|
dca2e2c8f1 | ||
|
|
83f8c1a25e | ||
|
|
4cd8b2f1f7 | ||
|
|
2d33b2c55a | ||
|
|
2ef4591e58 | ||
|
|
f1b58f0cd9 | ||
|
|
dd0679d710 | ||
|
|
4a67a5b917 | ||
|
|
e7c1d7bf77 | ||
|
|
bf9f737c60 | ||
|
|
fb796843df | ||
|
|
0bd8cecc94 | ||
|
|
e8401964b4 | ||
|
|
132f2006af | ||
|
|
e1ecf64701 | ||
|
|
5f4e7c77bd | ||
|
|
c61b973968 | ||
|
|
c3c7603076 | ||
|
|
5f517e3e16 | ||
|
|
f8650a9580 | ||
|
|
b483f2d010 | ||
|
|
8495e1bd83 | ||
|
|
d8df9a16bd | ||
|
|
390ee10eef | ||
|
|
d583e01f54 | ||
|
|
7a3c884e39 | ||
|
|
bc69cc1511 | ||
|
|
24c1bb3842 | ||
|
|
a19dc3bf00 | ||
|
|
d53b79ff5c |
@@ -261,7 +261,7 @@ if(ENABLE_SDL2)
|
||||
find_package(SDL2)
|
||||
if (NOT SDL2_FOUND)
|
||||
# otherwise add this to the list of libraries to install
|
||||
list(APPEND CONAN_REQUIRED_LIBS "sdl2/2.0.12@bincrafters/stable")
|
||||
list(APPEND CONAN_REQUIRED_LIBS "sdl2/2.0.14@bincrafters/stable")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: 3806284cbe...0f27368fda
@@ -64,8 +64,10 @@ if (MSVC)
|
||||
else()
|
||||
add_compile_options(
|
||||
-Wall
|
||||
-Werror=array-bounds
|
||||
-Werror=implicit-fallthrough
|
||||
-Werror=missing-declarations
|
||||
-Werror=missing-field-initializers
|
||||
-Werror=reorder
|
||||
-Werror=switch
|
||||
-Werror=uninitialized
|
||||
|
||||
@@ -86,28 +86,28 @@ struct BehaviorFlags {
|
||||
static_assert(sizeof(BehaviorFlags) == 0x4, "BehaviorFlags is an invalid size");
|
||||
|
||||
struct ADPCMContext {
|
||||
u16 header{};
|
||||
s16 yn1{};
|
||||
s16 yn2{};
|
||||
u16 header;
|
||||
s16 yn1;
|
||||
s16 yn2;
|
||||
};
|
||||
static_assert(sizeof(ADPCMContext) == 0x6, "ADPCMContext is an invalid size");
|
||||
|
||||
struct VoiceState {
|
||||
s64 played_sample_count{};
|
||||
s32 offset{};
|
||||
s32 wave_buffer_index{};
|
||||
std::array<bool, AudioCommon::MAX_WAVE_BUFFERS> is_wave_buffer_valid{};
|
||||
s32 wave_buffer_consumed{};
|
||||
std::array<s32, AudioCommon::MAX_SAMPLE_HISTORY> sample_history{};
|
||||
s32 fraction{};
|
||||
VAddr context_address{};
|
||||
Codec::ADPCM_Coeff coeff{};
|
||||
ADPCMContext context{};
|
||||
std::array<s64, 2> biquad_filter_state{};
|
||||
std::array<s32, AudioCommon::MAX_MIX_BUFFERS> previous_samples{};
|
||||
u32 external_context_size{};
|
||||
bool is_external_context_used{};
|
||||
bool voice_dropped{};
|
||||
s64 played_sample_count;
|
||||
s32 offset;
|
||||
s32 wave_buffer_index;
|
||||
std::array<bool, AudioCommon::MAX_WAVE_BUFFERS> is_wave_buffer_valid;
|
||||
s32 wave_buffer_consumed;
|
||||
std::array<s32, AudioCommon::MAX_SAMPLE_HISTORY> sample_history;
|
||||
s32 fraction;
|
||||
VAddr context_address;
|
||||
Codec::ADPCM_Coeff coeff;
|
||||
ADPCMContext context;
|
||||
std::array<s64, 2> biquad_filter_state;
|
||||
std::array<s32, AudioCommon::MAX_MIX_BUFFERS> previous_samples;
|
||||
u32 external_context_size;
|
||||
bool is_external_context_used;
|
||||
bool voice_dropped;
|
||||
};
|
||||
|
||||
class VoiceChannelResource {
|
||||
|
||||
@@ -138,6 +138,8 @@ add_library(common STATIC
|
||||
microprofile.h
|
||||
microprofileui.h
|
||||
misc.cpp
|
||||
nvidia_flags.cpp
|
||||
nvidia_flags.h
|
||||
page_table.cpp
|
||||
page_table.h
|
||||
param_package.cpp
|
||||
|
||||
@@ -4,13 +4,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bit>
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
@@ -21,48 +18,30 @@ template <typename T>
|
||||
return sizeof(T) * CHAR_BIT;
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
[[nodiscard]] inline u32 MostSignificantBit32(const u32 value) {
|
||||
unsigned long result;
|
||||
_BitScanReverse(&result, value);
|
||||
return static_cast<u32>(result);
|
||||
[[nodiscard]] constexpr u32 MostSignificantBit32(const u32 value) {
|
||||
return 31U - static_cast<u32>(std::countl_zero(value));
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u32 MostSignificantBit64(const u64 value) {
|
||||
unsigned long result;
|
||||
_BitScanReverse64(&result, value);
|
||||
return static_cast<u32>(result);
|
||||
[[nodiscard]] constexpr u32 MostSignificantBit64(const u64 value) {
|
||||
return 63U - static_cast<u32>(std::countl_zero(value));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
[[nodiscard]] inline u32 MostSignificantBit32(const u32 value) {
|
||||
return 31U - static_cast<u32>(__builtin_clz(value));
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u32 MostSignificantBit64(const u64 value) {
|
||||
return 63U - static_cast<u32>(__builtin_clzll(value));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
[[nodiscard]] inline u32 Log2Floor32(const u32 value) {
|
||||
[[nodiscard]] constexpr u32 Log2Floor32(const u32 value) {
|
||||
return MostSignificantBit32(value);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u32 Log2Ceil32(const u32 value) {
|
||||
const u32 log2_f = Log2Floor32(value);
|
||||
return log2_f + ((value ^ (1U << log2_f)) != 0U);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u32 Log2Floor64(const u64 value) {
|
||||
[[nodiscard]] constexpr u32 Log2Floor64(const u64 value) {
|
||||
return MostSignificantBit64(value);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u32 Log2Ceil64(const u64 value) {
|
||||
const u64 log2_f = static_cast<u64>(Log2Floor64(value));
|
||||
return static_cast<u32>(log2_f + ((value ^ (1ULL << log2_f)) != 0ULL));
|
||||
[[nodiscard]] constexpr u32 Log2Ceil32(const u32 value) {
|
||||
const u32 log2_f = Log2Floor32(value);
|
||||
return log2_f + static_cast<u32>((value ^ (1U << log2_f)) != 0U);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr u32 Log2Ceil64(const u64 value) {
|
||||
const u64 log2_f = Log2Floor64(value);
|
||||
return static_cast<u32>(log2_f + static_cast<u64>((value ^ (1ULL << log2_f)) != 0ULL));
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -97,10 +97,27 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
|
||||
#define R_UNLESS(expr, res) \
|
||||
{ \
|
||||
if (!(expr)) { \
|
||||
if (res.IsError()) { \
|
||||
LOG_ERROR(Kernel, "Failed with result: {}", res.raw); \
|
||||
} \
|
||||
return res; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define R_SUCCEEDED(res) (res.IsSuccess())
|
||||
|
||||
/// Evaluates an expression that returns a result, and returns the result if it would fail.
|
||||
#define R_TRY(res_expr) \
|
||||
{ \
|
||||
const auto _tmp_r_try_rc = (res_expr); \
|
||||
if (_tmp_r_try_rc.IsError()) { \
|
||||
return _tmp_r_try_rc; \
|
||||
} \
|
||||
}
|
||||
|
||||
/// Evaluates a boolean expression, and succeeds if that expression is true.
|
||||
#define R_SUCCEED_IF(expr) R_UNLESS(!(expr), RESULT_SUCCESS)
|
||||
|
||||
namespace Common {
|
||||
|
||||
[[nodiscard]] constexpr u32 MakeMagic(char a, char b, char c, char d) {
|
||||
|
||||
27
src/common/nvidia_flags.cpp
Normal file
27
src/common/nvidia_flags.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <filesystem>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/file_util.h"
|
||||
#include "common/nvidia_flags.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
void ConfigureNvidiaEnvironmentFlags() {
|
||||
#ifdef _WIN32
|
||||
const std::string shader_path = Common::FS::SanitizePath(
|
||||
fmt::format("{}/nvidia/", Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir)));
|
||||
const std::string windows_path =
|
||||
Common::FS::SanitizePath(shader_path, Common::FS::DirectorySeparator::BackwardSlash);
|
||||
void(Common::FS::CreateFullPath(shader_path + '/'));
|
||||
void(_putenv(fmt::format("__GL_SHADER_DISK_CACHE_PATH={}", windows_path).c_str()));
|
||||
void(_putenv("__GL_SHADER_DISK_CACHE_SKIP_CLEANUP=1"));
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
10
src/common/nvidia_flags.h
Normal file
10
src/common/nvidia_flags.h
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
namespace Common {
|
||||
|
||||
/// Configure platform specific flags for Nvidia's driver
|
||||
void ConfigureNvidiaEnvironmentFlags();
|
||||
|
||||
} // namespace Common
|
||||
@@ -14,8 +14,8 @@ constexpr u128 INVALID_UUID{{0, 0}};
|
||||
|
||||
struct UUID {
|
||||
// UUIDs which are 0 are considered invalid!
|
||||
u128 uuid = INVALID_UUID;
|
||||
constexpr UUID() = default;
|
||||
u128 uuid;
|
||||
UUID() = default;
|
||||
constexpr explicit UUID(const u128& id) : uuid{id} {}
|
||||
constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}
|
||||
|
||||
|
||||
@@ -160,6 +160,8 @@ add_library(core STATIC
|
||||
hle/kernel/k_affinity_mask.h
|
||||
hle/kernel/k_condition_variable.cpp
|
||||
hle/kernel/k_condition_variable.h
|
||||
hle/kernel/k_light_lock.cpp
|
||||
hle/kernel/k_light_lock.h
|
||||
hle/kernel/k_priority_queue.h
|
||||
hle/kernel/k_scheduler.cpp
|
||||
hle/kernel/k_scheduler.h
|
||||
@@ -168,6 +170,9 @@ add_library(core STATIC
|
||||
hle/kernel/k_scoped_scheduler_lock_and_sleep.h
|
||||
hle/kernel/k_synchronization_object.cpp
|
||||
hle/kernel/k_synchronization_object.h
|
||||
hle/kernel/k_thread.cpp
|
||||
hle/kernel/k_thread.h
|
||||
hle/kernel/k_thread_queue.h
|
||||
hle/kernel/kernel.cpp
|
||||
hle/kernel/kernel.h
|
||||
hle/kernel/memory/address_space_info.cpp
|
||||
@@ -216,8 +221,6 @@ add_library(core STATIC
|
||||
hle/kernel/svc_results.h
|
||||
hle/kernel/svc_types.h
|
||||
hle/kernel/svc_wrap.h
|
||||
hle/kernel/thread.cpp
|
||||
hle/kernel/thread.h
|
||||
hle/kernel/time_manager.cpp
|
||||
hle/kernel/time_manager.h
|
||||
hle/kernel/transfer_memory.cpp
|
||||
@@ -400,8 +403,6 @@ add_library(core STATIC
|
||||
hle/service/ldr/ldr.h
|
||||
hle/service/lm/lm.cpp
|
||||
hle/service/lm/lm.h
|
||||
hle/service/lm/manager.cpp
|
||||
hle/service/lm/manager.h
|
||||
hle/service/mig/mig.cpp
|
||||
hle/service/mig/mig.h
|
||||
hle/service/mii/manager.cpp
|
||||
@@ -645,6 +646,7 @@ else()
|
||||
-Werror=implicit-fallthrough
|
||||
-Werror=sign-compare
|
||||
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess>
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
|
||||
|
||||
|
||||
@@ -255,6 +255,9 @@ void ARM_Dynarmic_32::ChangeProcessorID(std::size_t new_core_id) {
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) {
|
||||
if (!jit) {
|
||||
return;
|
||||
}
|
||||
Dynarmic::A32::Context context;
|
||||
jit->SaveContext(context);
|
||||
ctx.cpu_registers = context.Regs();
|
||||
@@ -264,6 +267,9 @@ void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) {
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) {
|
||||
if (!jit) {
|
||||
return;
|
||||
}
|
||||
Dynarmic::A32::Context context;
|
||||
context.Regs() = ctx.cpu_registers;
|
||||
context.ExtRegs() = ctx.extension_registers;
|
||||
|
||||
@@ -294,6 +294,9 @@ void ARM_Dynarmic_64::ChangeProcessorID(std::size_t new_core_id) {
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) {
|
||||
if (!jit) {
|
||||
return;
|
||||
}
|
||||
ctx.cpu_registers = jit->GetRegisters();
|
||||
ctx.sp = jit->GetSP();
|
||||
ctx.pc = jit->GetPC();
|
||||
@@ -305,6 +308,9 @@ void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) {
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) {
|
||||
if (!jit) {
|
||||
return;
|
||||
}
|
||||
jit->SetRegisters(ctx.cpu_registers);
|
||||
jit->SetSP(ctx.sp);
|
||||
jit->SetPC(ctx.pc);
|
||||
|
||||
@@ -28,15 +28,14 @@
|
||||
#include "core/hardware_interrupt_manager.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/physical_core.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
#include "core/hle/service/apm/controller.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/glue/manager.h"
|
||||
#include "core/hle/service/lm/manager.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/hle/service/time/time_manager.h"
|
||||
@@ -293,8 +292,6 @@ struct System::Impl {
|
||||
perf_stats->GetMeanFrametime());
|
||||
}
|
||||
|
||||
lm_manager.Flush();
|
||||
|
||||
is_powered_on = false;
|
||||
exit_lock = false;
|
||||
|
||||
@@ -398,7 +395,6 @@ struct System::Impl {
|
||||
|
||||
/// Service State
|
||||
Service::Glue::ARPManager arp_manager;
|
||||
Service::LM::Manager lm_manager{reporter};
|
||||
Service::Time::TimeManager time_manager;
|
||||
|
||||
/// Service manager
|
||||
@@ -720,14 +716,6 @@ const Service::APM::Controller& System::GetAPMController() const {
|
||||
return impl->apm_controller;
|
||||
}
|
||||
|
||||
Service::LM::Manager& System::GetLogManager() {
|
||||
return impl->lm_manager;
|
||||
}
|
||||
|
||||
const Service::LM::Manager& System::GetLogManager() const {
|
||||
return impl->lm_manager;
|
||||
}
|
||||
|
||||
Service::Time::TimeManager& System::GetTimeManager() {
|
||||
return impl->time_manager;
|
||||
}
|
||||
|
||||
@@ -62,10 +62,6 @@ namespace Glue {
|
||||
class ARPManager;
|
||||
}
|
||||
|
||||
namespace LM {
|
||||
class Manager;
|
||||
} // namespace LM
|
||||
|
||||
namespace SM {
|
||||
class ServiceManager;
|
||||
} // namespace SM
|
||||
@@ -351,9 +347,6 @@ public:
|
||||
[[nodiscard]] Service::APM::Controller& GetAPMController();
|
||||
[[nodiscard]] const Service::APM::Controller& GetAPMController() const;
|
||||
|
||||
[[nodiscard]] Service::LM::Manager& GetLogManager();
|
||||
[[nodiscard]] const Service::LM::Manager& GetLogManager() const;
|
||||
|
||||
[[nodiscard]] Service::Time::TimeManager& GetTimeManager();
|
||||
[[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const;
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
#include "core/core_timing.h"
|
||||
#include "core/cpu_manager.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/physical_core.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "video_core/gpu.h"
|
||||
|
||||
namespace Core {
|
||||
@@ -147,7 +147,7 @@ void CpuManager::MultiCoreRunSuspendThread() {
|
||||
while (true) {
|
||||
auto core = kernel.GetCurrentHostThreadID();
|
||||
auto& scheduler = *kernel.CurrentScheduler();
|
||||
Kernel::Thread* current_thread = scheduler.GetCurrentThread();
|
||||
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[core].host_context);
|
||||
ASSERT(scheduler.ContextSwitchPending());
|
||||
ASSERT(core == kernel.GetCurrentHostThreadID());
|
||||
@@ -208,7 +208,6 @@ void CpuManager::SingleCoreRunGuestThread() {
|
||||
|
||||
void CpuManager::SingleCoreRunGuestLoop() {
|
||||
auto& kernel = system.Kernel();
|
||||
auto* thread = kernel.CurrentScheduler()->GetCurrentThread();
|
||||
while (true) {
|
||||
auto* physical_core = &kernel.CurrentPhysicalCore();
|
||||
system.EnterDynarmicProfile();
|
||||
@@ -217,9 +216,9 @@ void CpuManager::SingleCoreRunGuestLoop() {
|
||||
physical_core = &kernel.CurrentPhysicalCore();
|
||||
}
|
||||
system.ExitDynarmicProfile();
|
||||
thread->SetPhantomMode(true);
|
||||
kernel.SetIsPhantomModeForSingleCore(true);
|
||||
system.CoreTiming().Advance();
|
||||
thread->SetPhantomMode(false);
|
||||
kernel.SetIsPhantomModeForSingleCore(false);
|
||||
physical_core->ArmInterface().ClearExclusiveState();
|
||||
PreemptSingleCore();
|
||||
auto& scheduler = kernel.Scheduler(current_core);
|
||||
@@ -245,7 +244,7 @@ void CpuManager::SingleCoreRunSuspendThread() {
|
||||
while (true) {
|
||||
auto core = kernel.GetCurrentHostThreadID();
|
||||
auto& scheduler = *kernel.CurrentScheduler();
|
||||
Kernel::Thread* current_thread = scheduler.GetCurrentThread();
|
||||
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[0].host_context);
|
||||
ASSERT(scheduler.ContextSwitchPending());
|
||||
ASSERT(core == kernel.GetCurrentHostThreadID());
|
||||
@@ -255,22 +254,23 @@ void CpuManager::SingleCoreRunSuspendThread() {
|
||||
|
||||
void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
|
||||
{
|
||||
auto& scheduler = system.Kernel().Scheduler(current_core);
|
||||
Kernel::Thread* current_thread = scheduler.GetCurrentThread();
|
||||
auto& kernel = system.Kernel();
|
||||
auto& scheduler = kernel.Scheduler(current_core);
|
||||
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
|
||||
if (idle_count >= 4 || from_running_enviroment) {
|
||||
if (!from_running_enviroment) {
|
||||
system.CoreTiming().Idle();
|
||||
idle_count = 0;
|
||||
}
|
||||
current_thread->SetPhantomMode(true);
|
||||
kernel.SetIsPhantomModeForSingleCore(true);
|
||||
system.CoreTiming().Advance();
|
||||
current_thread->SetPhantomMode(false);
|
||||
kernel.SetIsPhantomModeForSingleCore(false);
|
||||
}
|
||||
current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES);
|
||||
system.CoreTiming().ResetTicks();
|
||||
scheduler.Unload(scheduler.GetCurrentThread());
|
||||
|
||||
auto& next_scheduler = system.Kernel().Scheduler(current_core);
|
||||
auto& next_scheduler = kernel.Scheduler(current_core);
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), next_scheduler.ControlContext());
|
||||
}
|
||||
|
||||
@@ -278,8 +278,7 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
|
||||
{
|
||||
auto& scheduler = system.Kernel().Scheduler(current_core);
|
||||
scheduler.Reload(scheduler.GetCurrentThread());
|
||||
auto* currrent_thread2 = scheduler.GetCurrentThread();
|
||||
if (!currrent_thread2->IsIdleThread()) {
|
||||
if (!scheduler.IsIdle()) {
|
||||
idle_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ struct SaveDataAttribute {
|
||||
SaveDataType type;
|
||||
SaveDataRank rank;
|
||||
u16 index;
|
||||
INSERT_PADDING_BYTES(4);
|
||||
INSERT_PADDING_BYTES_NOINIT(4);
|
||||
u64 zero_1;
|
||||
u64 zero_2;
|
||||
u64 zero_3;
|
||||
@@ -72,7 +72,7 @@ struct SaveDataExtraData {
|
||||
u64 owner_id;
|
||||
s64 timestamp;
|
||||
SaveDataFlags flags;
|
||||
INSERT_PADDING_BYTES(4);
|
||||
INSERT_PADDING_BYTES_NOINIT(4);
|
||||
s64 available_size;
|
||||
s64 journal_size;
|
||||
s64 commit_id;
|
||||
|
||||
@@ -133,8 +133,11 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
|
||||
}
|
||||
|
||||
cache.erase(old_path);
|
||||
file->Open(new_path, "r+b");
|
||||
cache.insert_or_assign(new_path, std::move(file));
|
||||
if (file->Open(new_path, "r+b")) {
|
||||
cache.insert_or_assign(new_path, std::move(file));
|
||||
} else {
|
||||
LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", new_path);
|
||||
}
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
@@ -214,9 +217,12 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
|
||||
}
|
||||
|
||||
auto file = cached.lock();
|
||||
file->Open(file_new_path, "r+b");
|
||||
cache.erase(file_old_path);
|
||||
cache.insert_or_assign(std::move(file_new_path), std::move(file));
|
||||
if (file->Open(file_new_path, "r+b")) {
|
||||
cache.insert_or_assign(std::move(file_new_path), std::move(file));
|
||||
} else {
|
||||
LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", file_new_path);
|
||||
}
|
||||
}
|
||||
|
||||
return OpenDirectory(new_path, Mode::ReadWrite);
|
||||
|
||||
@@ -21,21 +21,18 @@ public:
|
||||
|
||||
std::mutex mutex;
|
||||
|
||||
bool touch_pressed = false; ///< True if touchpad area is currently pressed, otherwise false
|
||||
|
||||
float touch_x = 0.0f; ///< Touchpad X-position
|
||||
float touch_y = 0.0f; ///< Touchpad Y-position
|
||||
Input::TouchStatus status;
|
||||
|
||||
private:
|
||||
class Device : public Input::TouchDevice {
|
||||
public:
|
||||
explicit Device(std::weak_ptr<TouchState>&& touch_state) : touch_state(touch_state) {}
|
||||
std::tuple<float, float, bool> GetStatus() const override {
|
||||
Input::TouchStatus GetStatus() const override {
|
||||
if (auto state = touch_state.lock()) {
|
||||
std::lock_guard guard{state->mutex};
|
||||
return std::make_tuple(state->touch_x, state->touch_y, state->touch_pressed);
|
||||
return state->status;
|
||||
}
|
||||
return std::make_tuple(0.0f, 0.0f, false);
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -79,36 +76,44 @@ std::tuple<unsigned, unsigned> EmuWindow::ClipToTouchScreen(unsigned new_x, unsi
|
||||
return std::make_tuple(new_x, new_y);
|
||||
}
|
||||
|
||||
void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
|
||||
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
|
||||
void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id) {
|
||||
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) {
|
||||
return;
|
||||
}
|
||||
if (id >= touch_state->status.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard guard{touch_state->mutex};
|
||||
touch_state->touch_x =
|
||||
const float x =
|
||||
static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) /
|
||||
static_cast<float>(framebuffer_layout.screen.right - framebuffer_layout.screen.left);
|
||||
touch_state->touch_y =
|
||||
const float y =
|
||||
static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) /
|
||||
static_cast<float>(framebuffer_layout.screen.bottom - framebuffer_layout.screen.top);
|
||||
|
||||
touch_state->touch_pressed = true;
|
||||
touch_state->status[id] = std::make_tuple(x, y, true);
|
||||
}
|
||||
|
||||
void EmuWindow::TouchReleased() {
|
||||
void EmuWindow::TouchReleased(std::size_t id) {
|
||||
if (id >= touch_state->status.size()) {
|
||||
return;
|
||||
}
|
||||
std::lock_guard guard{touch_state->mutex};
|
||||
touch_state->touch_pressed = false;
|
||||
touch_state->touch_x = 0;
|
||||
touch_state->touch_y = 0;
|
||||
touch_state->status[id] = std::make_tuple(0.0f, 0.0f, false);
|
||||
}
|
||||
|
||||
void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
|
||||
if (!touch_state->touch_pressed)
|
||||
void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id) {
|
||||
if (id >= touch_state->status.size()) {
|
||||
return;
|
||||
}
|
||||
if (!std::get<2>(touch_state->status[id]))
|
||||
return;
|
||||
|
||||
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
|
||||
std::tie(framebuffer_x, framebuffer_y) = ClipToTouchScreen(framebuffer_x, framebuffer_y);
|
||||
|
||||
TouchPressed(framebuffer_x, framebuffer_y);
|
||||
TouchPressed(framebuffer_x, framebuffer_y, id);
|
||||
}
|
||||
|
||||
void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) {
|
||||
|
||||
@@ -117,18 +117,23 @@ public:
|
||||
* Signal that a touch pressed event has occurred (e.g. mouse click pressed)
|
||||
* @param framebuffer_x Framebuffer x-coordinate that was pressed
|
||||
* @param framebuffer_y Framebuffer y-coordinate that was pressed
|
||||
* @param id Touch event ID
|
||||
*/
|
||||
void TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y);
|
||||
void TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id);
|
||||
|
||||
/// Signal that a touch released event has occurred (e.g. mouse click released)
|
||||
void TouchReleased();
|
||||
/**
|
||||
* Signal that a touch released event has occurred (e.g. mouse click released)
|
||||
* @param id Touch event ID
|
||||
*/
|
||||
void TouchReleased(std::size_t id);
|
||||
|
||||
/**
|
||||
* Signal that a touch movement event has occurred (e.g. mouse was moved over the emu window)
|
||||
* @param framebuffer_x Framebuffer x-coordinate
|
||||
* @param framebuffer_y Framebuffer y-coordinate
|
||||
* @param id Touch event ID
|
||||
*/
|
||||
void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y);
|
||||
void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y, std::size_t id);
|
||||
|
||||
/**
|
||||
* Returns currently active configuration.
|
||||
|
||||
@@ -163,10 +163,11 @@ using MotionStatus = std::tuple<Common::Vec3<float>, Common::Vec3<float>, Common
|
||||
using MotionDevice = InputDevice<MotionStatus>;
|
||||
|
||||
/**
|
||||
* A touch status is an object that returns a tuple of two floats and a bool. The floats are
|
||||
* x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is pressed.
|
||||
* A touch status is an object that returns an array of 16 tuple elements of two floats and a bool.
|
||||
* The floats are x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is
|
||||
* pressed.
|
||||
*/
|
||||
using TouchStatus = std::tuple<float, float, bool>;
|
||||
using TouchStatus = std::array<std::tuple<float, float, bool>, 16>;
|
||||
|
||||
/**
|
||||
* A touch device is an input device that returns a touch status object
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <tuple>
|
||||
|
||||
#include "common/bit_util.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core {
|
||||
@@ -18,34 +20,12 @@ constexpr u64 BASE_CLOCK_RATE = 1019215872; // Switch cpu frequency is 1020MHz u
|
||||
constexpr u64 CNTFREQ = 19200000; // Switch's hardware clock speed
|
||||
constexpr u32 NUM_CPU_CORES = 4; // Number of CPU Cores
|
||||
|
||||
} // namespace Hardware
|
||||
|
||||
constexpr u32 INVALID_HOST_THREAD_ID = 0xFFFFFFFF;
|
||||
|
||||
struct EmuThreadHandle {
|
||||
u32 host_handle;
|
||||
u32 guest_handle;
|
||||
|
||||
u64 GetRaw() const {
|
||||
return (static_cast<u64>(host_handle) << 32) | guest_handle;
|
||||
}
|
||||
|
||||
bool operator==(const EmuThreadHandle& rhs) const {
|
||||
return std::tie(host_handle, guest_handle) == std::tie(rhs.host_handle, rhs.guest_handle);
|
||||
}
|
||||
|
||||
bool operator!=(const EmuThreadHandle& rhs) const {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
static constexpr EmuThreadHandle InvalidHandle() {
|
||||
constexpr u32 invalid_handle = 0xFFFFFFFF;
|
||||
return {invalid_handle, invalid_handle};
|
||||
}
|
||||
|
||||
bool IsInvalid() const {
|
||||
return (*this) == InvalidHandle();
|
||||
}
|
||||
// Virtual to Physical core map.
|
||||
constexpr std::array<s32, Common::BitSize<u64>()> VirtualToPhysicalCoreMap{
|
||||
0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
|
||||
};
|
||||
|
||||
} // namespace Hardware
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -146,7 +146,7 @@ static_assert(sizeof(BufferDescriptorC) == 8, "BufferDescriptorC size is incorre
|
||||
|
||||
struct DataPayloadHeader {
|
||||
u32_le magic;
|
||||
INSERT_PADDING_WORDS(1);
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
};
|
||||
static_assert(sizeof(DataPayloadHeader) == 8, "DataPayloadHeader size is incorrect");
|
||||
|
||||
@@ -174,7 +174,7 @@ struct DomainMessageHeader {
|
||||
INSERT_PADDING_WORDS_NOINIT(2);
|
||||
};
|
||||
|
||||
std::array<u32, 4> raw{};
|
||||
std::array<u32, 4> raw;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(DomainMessageHeader) == 16, "DomainMessageHeader size is incorrect");
|
||||
|
||||
@@ -51,6 +51,8 @@ public:
|
||||
*/
|
||||
void ConnectionClosed();
|
||||
|
||||
void Finalize() override {}
|
||||
|
||||
private:
|
||||
std::shared_ptr<ServerPort> server_port; ///< ServerPort associated with this client port.
|
||||
u32 max_sessions = 0; ///< Maximum number of simultaneous sessions the port can have
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
#include "core/hle/kernel/client_session.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/server_session.h"
|
||||
#include "core/hle/kernel/session.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Kernel {
|
||||
@@ -38,7 +38,7 @@ ResultVal<std::shared_ptr<ClientSession>> ClientSession::Create(KernelCore& kern
|
||||
return MakeResult(std::move(client_session));
|
||||
}
|
||||
|
||||
ResultCode ClientSession::SendSyncRequest(std::shared_ptr<Thread> thread,
|
||||
ResultCode ClientSession::SendSyncRequest(std::shared_ptr<KThread> thread,
|
||||
Core::Memory::Memory& memory,
|
||||
Core::Timing::CoreTiming& core_timing) {
|
||||
// Keep ServerSession alive until we're done working with it.
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Kernel {
|
||||
|
||||
class KernelCore;
|
||||
class Session;
|
||||
class Thread;
|
||||
class KThread;
|
||||
|
||||
class ClientSession final : public KSynchronizationObject {
|
||||
public:
|
||||
@@ -46,11 +46,13 @@ public:
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory,
|
||||
ResultCode SendSyncRequest(std::shared_ptr<KThread> thread, Core::Memory::Memory& memory,
|
||||
Core::Timing::CoreTiming& core_timing);
|
||||
|
||||
bool IsSignaled() const override;
|
||||
|
||||
void Finalize() override {}
|
||||
|
||||
private:
|
||||
static ResultVal<std::shared_ptr<ClientSession>> Create(KernelCore& kernel,
|
||||
std::shared_ptr<Session> parent,
|
||||
|
||||
@@ -17,12 +17,12 @@ GlobalSchedulerContext::GlobalSchedulerContext(KernelCore& kernel)
|
||||
|
||||
GlobalSchedulerContext::~GlobalSchedulerContext() = default;
|
||||
|
||||
void GlobalSchedulerContext::AddThread(std::shared_ptr<Thread> thread) {
|
||||
void GlobalSchedulerContext::AddThread(std::shared_ptr<KThread> thread) {
|
||||
std::scoped_lock lock{global_list_guard};
|
||||
thread_list.push_back(std::move(thread));
|
||||
}
|
||||
|
||||
void GlobalSchedulerContext::RemoveThread(std::shared_ptr<Thread> thread) {
|
||||
void GlobalSchedulerContext::RemoveThread(std::shared_ptr<KThread> thread) {
|
||||
std::scoped_lock lock{global_list_guard};
|
||||
thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
|
||||
thread_list.end());
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
#include "core/hardware_properties.h"
|
||||
#include "core/hle/kernel/k_priority_queue.h"
|
||||
#include "core/hle/kernel/k_scheduler_lock.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/svc_types.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -20,8 +21,12 @@ class KernelCore;
|
||||
class SchedulerLock;
|
||||
|
||||
using KSchedulerPriorityQueue =
|
||||
KPriorityQueue<Thread, Core::Hardware::NUM_CPU_CORES, THREADPRIO_LOWEST, THREADPRIO_HIGHEST>;
|
||||
constexpr s32 HighestCoreMigrationAllowedPriority = 2;
|
||||
KPriorityQueue<KThread, Core::Hardware::NUM_CPU_CORES, Svc::LowestThreadPriority,
|
||||
Svc::HighestThreadPriority>;
|
||||
|
||||
static constexpr s32 HighestCoreMigrationAllowedPriority = 2;
|
||||
static_assert(Svc::LowestThreadPriority >= HighestCoreMigrationAllowedPriority);
|
||||
static_assert(Svc::HighestThreadPriority <= HighestCoreMigrationAllowedPriority);
|
||||
|
||||
class GlobalSchedulerContext final {
|
||||
friend class KScheduler;
|
||||
@@ -33,13 +38,13 @@ public:
|
||||
~GlobalSchedulerContext();
|
||||
|
||||
/// Adds a new thread to the scheduler
|
||||
void AddThread(std::shared_ptr<Thread> thread);
|
||||
void AddThread(std::shared_ptr<KThread> thread);
|
||||
|
||||
/// Removes a thread from the scheduler
|
||||
void RemoveThread(std::shared_ptr<Thread> thread);
|
||||
void RemoveThread(std::shared_ptr<KThread> thread);
|
||||
|
||||
/// Returns a list of all threads managed by the scheduler
|
||||
[[nodiscard]] const std::vector<std::shared_ptr<Thread>>& GetThreadList() const {
|
||||
[[nodiscard]] const std::vector<std::shared_ptr<KThread>>& GetThreadList() const {
|
||||
return thread_list;
|
||||
}
|
||||
|
||||
@@ -74,7 +79,7 @@ private:
|
||||
LockType scheduler_lock;
|
||||
|
||||
/// Lists all thread ids that aren't deleted/etc.
|
||||
std::vector<std::shared_ptr<Thread>> thread_list;
|
||||
std::vector<std::shared_ptr<KThread>> thread_list;
|
||||
Common::SpinLock global_list_guard{};
|
||||
};
|
||||
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
namespace Kernel {
|
||||
namespace {
|
||||
@@ -89,6 +89,10 @@ ResultCode HandleTable::Close(Handle handle) {
|
||||
|
||||
const u16 slot = GetSlot(handle);
|
||||
|
||||
if (objects[slot].use_count() == 1) {
|
||||
objects[slot]->Finalize();
|
||||
}
|
||||
|
||||
objects[slot] = nullptr;
|
||||
|
||||
generations[slot] = next_free_slot;
|
||||
|
||||
@@ -19,12 +19,12 @@
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/server_session.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/time_manager.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/memory.h"
|
||||
@@ -48,7 +48,7 @@ void SessionRequestHandler::ClientDisconnected(
|
||||
|
||||
HLERequestContext::HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory,
|
||||
std::shared_ptr<ServerSession> server_session,
|
||||
std::shared_ptr<Thread> thread)
|
||||
std::shared_ptr<KThread> thread)
|
||||
: server_session(std::move(server_session)),
|
||||
thread(std::move(thread)), kernel{kernel}, memory{memory} {
|
||||
cmd_buf[0] = 0;
|
||||
@@ -182,7 +182,7 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const HandleTabl
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
|
||||
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(KThread& thread) {
|
||||
auto& owner_process = *thread.GetOwnerProcess();
|
||||
auto& handle_table = owner_process.GetHandleTable();
|
||||
|
||||
@@ -338,6 +338,28 @@ std::size_t HLERequestContext::GetWriteBufferSize(std::size_t buffer_index) cons
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool HLERequestContext::CanReadBuffer(std::size_t buffer_index) const {
|
||||
const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
|
||||
BufferDescriptorA()[buffer_index].Size()};
|
||||
|
||||
if (is_buffer_a) {
|
||||
return BufferDescriptorA().size() > buffer_index;
|
||||
} else {
|
||||
return BufferDescriptorX().size() > buffer_index;
|
||||
}
|
||||
}
|
||||
|
||||
bool HLERequestContext::CanWriteBuffer(std::size_t buffer_index) const {
|
||||
const bool is_buffer_b{BufferDescriptorB().size() > buffer_index &&
|
||||
BufferDescriptorB()[buffer_index].Size()};
|
||||
|
||||
if (is_buffer_b) {
|
||||
return BufferDescriptorB().size() > buffer_index;
|
||||
} else {
|
||||
return BufferDescriptorC().size() > buffer_index;
|
||||
}
|
||||
}
|
||||
|
||||
std::string HLERequestContext::Description() const {
|
||||
if (!command_header) {
|
||||
return "No command header available";
|
||||
|
||||
@@ -40,7 +40,7 @@ class HLERequestContext;
|
||||
class KernelCore;
|
||||
class Process;
|
||||
class ServerSession;
|
||||
class Thread;
|
||||
class KThread;
|
||||
class ReadableEvent;
|
||||
class WritableEvent;
|
||||
|
||||
@@ -110,7 +110,7 @@ class HLERequestContext {
|
||||
public:
|
||||
explicit HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory,
|
||||
std::shared_ptr<ServerSession> session,
|
||||
std::shared_ptr<Thread> thread);
|
||||
std::shared_ptr<KThread> thread);
|
||||
~HLERequestContext();
|
||||
|
||||
/// Returns a pointer to the IPC command buffer for this request.
|
||||
@@ -126,15 +126,12 @@ public:
|
||||
return server_session;
|
||||
}
|
||||
|
||||
using WakeupCallback = std::function<void(
|
||||
std::shared_ptr<Thread> thread, HLERequestContext& context, ThreadWakeupReason reason)>;
|
||||
|
||||
/// Populates this context with data from the requesting process/thread.
|
||||
ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table,
|
||||
u32_le* src_cmdbuf);
|
||||
|
||||
/// Writes data from this context back to the requesting process/thread.
|
||||
ResultCode WriteToOutgoingCommandBuffer(Thread& thread);
|
||||
ResultCode WriteToOutgoingCommandBuffer(KThread& thread);
|
||||
|
||||
u32_le GetCommand() const {
|
||||
return command;
|
||||
@@ -207,6 +204,12 @@ public:
|
||||
/// Helper function to get the size of the output buffer
|
||||
std::size_t GetWriteBufferSize(std::size_t buffer_index = 0) const;
|
||||
|
||||
/// Helper function to test whether the input buffer at buffer_index can be read
|
||||
bool CanReadBuffer(std::size_t buffer_index = 0) const;
|
||||
|
||||
/// Helper function to test whether the output buffer at buffer_index can be written
|
||||
bool CanWriteBuffer(std::size_t buffer_index = 0) const;
|
||||
|
||||
template <typename T>
|
||||
std::shared_ptr<T> GetCopyObject(std::size_t index) {
|
||||
return DynamicObjectCast<T>(copy_objects.at(index));
|
||||
@@ -261,11 +264,11 @@ public:
|
||||
|
||||
std::string Description() const;
|
||||
|
||||
Thread& GetThread() {
|
||||
KThread& GetThread() {
|
||||
return *thread;
|
||||
}
|
||||
|
||||
const Thread& GetThread() const {
|
||||
const KThread& GetThread() const {
|
||||
return *thread;
|
||||
}
|
||||
|
||||
@@ -280,7 +283,7 @@ private:
|
||||
|
||||
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
|
||||
std::shared_ptr<Kernel::ServerSession> server_session;
|
||||
std::shared_ptr<Thread> thread;
|
||||
std::shared_ptr<KThread> thread;
|
||||
// TODO(yuriks): Check common usage of this and optimize size accordingly
|
||||
boost::container::small_vector<std::shared_ptr<Object>, 8> move_objects;
|
||||
boost::container::small_vector<std::shared_ptr<Object>, 8> copy_objects;
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
#include "core/hle/kernel/k_address_arbiter.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/time_manager.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
@@ -96,7 +96,7 @@ ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
|
||||
auto it = thread_tree.nfind_light({addr, -1});
|
||||
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
|
||||
(it->GetAddressArbiterKey() == addr)) {
|
||||
Thread* target_thread = std::addressof(*it);
|
||||
KThread* target_thread = std::addressof(*it);
|
||||
target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
|
||||
|
||||
ASSERT(target_thread->IsWaitingForAddressArbiter());
|
||||
@@ -125,7 +125,7 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32
|
||||
auto it = thread_tree.nfind_light({addr, -1});
|
||||
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
|
||||
(it->GetAddressArbiterKey() == addr)) {
|
||||
Thread* target_thread = std::addressof(*it);
|
||||
KThread* target_thread = std::addressof(*it);
|
||||
target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
|
||||
|
||||
ASSERT(target_thread->IsWaitingForAddressArbiter());
|
||||
@@ -215,7 +215,7 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
|
||||
|
||||
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
|
||||
(it->GetAddressArbiterKey() == addr)) {
|
||||
Thread* target_thread = std::addressof(*it);
|
||||
KThread* target_thread = std::addressof(*it);
|
||||
target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
|
||||
|
||||
ASSERT(target_thread->IsWaitingForAddressArbiter());
|
||||
@@ -231,11 +231,10 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
|
||||
|
||||
ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) {
|
||||
// Prepare to wait.
|
||||
Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
|
||||
Handle timer = InvalidHandle;
|
||||
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
|
||||
|
||||
{
|
||||
KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout);
|
||||
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
|
||||
|
||||
// Check that the thread isn't terminating.
|
||||
if (cur_thread->IsTerminationRequested()) {
|
||||
@@ -280,10 +279,7 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement
|
||||
}
|
||||
|
||||
// Cancel the timer wait.
|
||||
if (timer != InvalidHandle) {
|
||||
auto& time_manager = kernel.TimeManager();
|
||||
time_manager.UnscheduleTimeEvent(timer);
|
||||
}
|
||||
kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
|
||||
|
||||
// Remove from the address arbiter.
|
||||
{
|
||||
@@ -302,11 +298,10 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement
|
||||
|
||||
ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
|
||||
// Prepare to wait.
|
||||
Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
|
||||
Handle timer = InvalidHandle;
|
||||
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
|
||||
|
||||
{
|
||||
KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout);
|
||||
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
|
||||
|
||||
// Check that the thread isn't terminating.
|
||||
if (cur_thread->IsTerminationRequested()) {
|
||||
@@ -344,10 +339,7 @@ ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
|
||||
}
|
||||
|
||||
// Cancel the timer wait.
|
||||
if (timer != InvalidHandle) {
|
||||
auto& time_manager = kernel.TimeManager();
|
||||
time_manager.UnscheduleTimeEvent(timer);
|
||||
}
|
||||
kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
|
||||
|
||||
// Remove from the address arbiter.
|
||||
{
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
|
||||
#include "core/hle/kernel/k_synchronization_object.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/svc_common.h"
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Kernel {
|
||||
@@ -66,7 +66,7 @@ KConditionVariable::KConditionVariable(Core::System& system_)
|
||||
KConditionVariable::~KConditionVariable() = default;
|
||||
|
||||
ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
|
||||
Thread* owner_thread = kernel.CurrentScheduler()->GetCurrentThread();
|
||||
KThread* owner_thread = kernel.CurrentScheduler()->GetCurrentThread();
|
||||
|
||||
// Signal the address.
|
||||
{
|
||||
@@ -74,7 +74,7 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
|
||||
|
||||
// Remove waiter thread.
|
||||
s32 num_waiters{};
|
||||
Thread* next_owner_thread =
|
||||
KThread* next_owner_thread =
|
||||
owner_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr);
|
||||
|
||||
// Determine the next tag.
|
||||
@@ -103,11 +103,11 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
|
||||
}
|
||||
|
||||
ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) {
|
||||
Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
|
||||
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
|
||||
|
||||
// Wait for the address.
|
||||
{
|
||||
std::shared_ptr<Thread> owner_thread;
|
||||
std::shared_ptr<KThread> owner_thread;
|
||||
ASSERT(!owner_thread);
|
||||
{
|
||||
KScopedSchedulerLock sl(kernel);
|
||||
@@ -126,7 +126,7 @@ ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 val
|
||||
R_UNLESS(test_tag == (handle | Svc::HandleWaitMask), RESULT_SUCCESS);
|
||||
|
||||
// Get the lock owner thread.
|
||||
owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<Thread>(handle);
|
||||
owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<KThread>(handle);
|
||||
R_UNLESS(owner_thread, Svc::ResultInvalidHandle);
|
||||
|
||||
// Update the lock.
|
||||
@@ -143,7 +143,7 @@ ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 val
|
||||
// Remove the thread as a waiter from the lock owner.
|
||||
{
|
||||
KScopedSchedulerLock sl(kernel);
|
||||
Thread* owner_thread = cur_thread->GetLockOwner();
|
||||
KThread* owner_thread = cur_thread->GetLockOwner();
|
||||
if (owner_thread != nullptr) {
|
||||
owner_thread->RemoveWaiter(cur_thread);
|
||||
}
|
||||
@@ -154,7 +154,7 @@ ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 val
|
||||
return cur_thread->GetWaitResult(std::addressof(dummy));
|
||||
}
|
||||
|
||||
Thread* KConditionVariable::SignalImpl(Thread* thread) {
|
||||
KThread* KConditionVariable::SignalImpl(KThread* thread) {
|
||||
// Check pre-conditions.
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
|
||||
@@ -174,7 +174,7 @@ Thread* KConditionVariable::SignalImpl(Thread* thread) {
|
||||
}
|
||||
}
|
||||
|
||||
Thread* thread_to_close = nullptr;
|
||||
KThread* thread_to_close = nullptr;
|
||||
if (can_access) {
|
||||
if (prev_tag == InvalidHandle) {
|
||||
// If nobody held the lock previously, we're all good.
|
||||
@@ -182,7 +182,7 @@ Thread* KConditionVariable::SignalImpl(Thread* thread) {
|
||||
thread->Wakeup();
|
||||
} else {
|
||||
// Get the previous owner.
|
||||
auto owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<Thread>(
|
||||
auto owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<KThread>(
|
||||
prev_tag & ~Svc::HandleWaitMask);
|
||||
|
||||
if (owner_thread) {
|
||||
@@ -210,8 +210,8 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
|
||||
|
||||
// TODO(bunnei): This should just be Thread once we implement KAutoObject instead of using
|
||||
// std::shared_ptr.
|
||||
std::vector<std::shared_ptr<Thread>> thread_list;
|
||||
std::array<Thread*, MaxThreads> thread_array;
|
||||
std::vector<std::shared_ptr<KThread>> thread_list;
|
||||
std::array<KThread*, MaxThreads> thread_array;
|
||||
s32 num_to_close{};
|
||||
|
||||
// Perform signaling.
|
||||
@@ -222,9 +222,9 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
|
||||
auto it = thread_tree.nfind_light({cv_key, -1});
|
||||
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
|
||||
(it->GetConditionVariableKey() == cv_key)) {
|
||||
Thread* target_thread = std::addressof(*it);
|
||||
KThread* target_thread = std::addressof(*it);
|
||||
|
||||
if (Thread* thread = SignalImpl(target_thread); thread != nullptr) {
|
||||
if (KThread* thread = SignalImpl(target_thread); thread != nullptr) {
|
||||
if (num_to_close < MaxThreads) {
|
||||
thread_array[num_to_close++] = thread;
|
||||
} else {
|
||||
@@ -257,11 +257,10 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
|
||||
|
||||
ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) {
|
||||
// Prepare to wait.
|
||||
Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
|
||||
Handle timer = InvalidHandle;
|
||||
KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
|
||||
|
||||
{
|
||||
KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout);
|
||||
KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
|
||||
|
||||
// Set the synced object.
|
||||
cur_thread->SetSyncedObject(nullptr, Svc::ResultTimedOut);
|
||||
@@ -276,7 +275,7 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout)
|
||||
{
|
||||
// Remove waiter thread.
|
||||
s32 num_waiters{};
|
||||
Thread* next_owner_thread =
|
||||
KThread* next_owner_thread =
|
||||
cur_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr);
|
||||
|
||||
// Update for the next owner thread.
|
||||
@@ -322,16 +321,13 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout)
|
||||
}
|
||||
|
||||
// Cancel the timer wait.
|
||||
if (timer != InvalidHandle) {
|
||||
auto& time_manager = kernel.TimeManager();
|
||||
time_manager.UnscheduleTimeEvent(timer);
|
||||
}
|
||||
kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
|
||||
|
||||
// Remove from the condition variable.
|
||||
{
|
||||
KScopedSchedulerLock sl(kernel);
|
||||
|
||||
if (Thread* owner = cur_thread->GetLockOwner(); owner != nullptr) {
|
||||
if (KThread* owner = cur_thread->GetLockOwner(); owner != nullptr) {
|
||||
owner->RemoveWaiter(cur_thread);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Core {
|
||||
@@ -20,7 +20,7 @@ namespace Kernel {
|
||||
|
||||
class KConditionVariable {
|
||||
public:
|
||||
using ThreadTree = typename Thread::ConditionVariableThreadTreeType;
|
||||
using ThreadTree = typename KThread::ConditionVariableThreadTreeType;
|
||||
|
||||
explicit KConditionVariable(Core::System& system_);
|
||||
~KConditionVariable();
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
[[nodiscard]] ResultCode Wait(VAddr addr, u64 key, u32 value, s64 timeout);
|
||||
|
||||
private:
|
||||
[[nodiscard]] Thread* SignalImpl(Thread* thread);
|
||||
[[nodiscard]] KThread* SignalImpl(KThread* thread);
|
||||
|
||||
ThreadTree thread_tree;
|
||||
|
||||
@@ -43,14 +43,14 @@ private:
|
||||
};
|
||||
|
||||
inline void BeforeUpdatePriority(const KernelCore& kernel, KConditionVariable::ThreadTree* tree,
|
||||
Thread* thread) {
|
||||
KThread* thread) {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
|
||||
tree->erase(tree->iterator_to(*thread));
|
||||
}
|
||||
|
||||
inline void AfterUpdatePriority(const KernelCore& kernel, KConditionVariable::ThreadTree* tree,
|
||||
Thread* thread) {
|
||||
KThread* thread) {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
|
||||
tree->insert(*thread);
|
||||
|
||||
130
src/core/hle/kernel/k_light_lock.cpp
Normal file
130
src/core/hle/kernel/k_light_lock.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/kernel/k_light_lock.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
void KLightLock::Lock() {
|
||||
const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
|
||||
const uintptr_t cur_thread_tag = (cur_thread | 1);
|
||||
|
||||
while (true) {
|
||||
uintptr_t old_tag = tag.load(std::memory_order_relaxed);
|
||||
|
||||
while (!tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : old_tag | 1,
|
||||
std::memory_order_acquire)) {
|
||||
if ((old_tag | 1) == cur_thread_tag) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ((old_tag == 0) || ((old_tag | 1) == cur_thread_tag)) {
|
||||
break;
|
||||
}
|
||||
|
||||
LockSlowPath(old_tag | 1, cur_thread);
|
||||
}
|
||||
}
|
||||
|
||||
void KLightLock::Unlock() {
|
||||
const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
|
||||
uintptr_t expected = cur_thread;
|
||||
do {
|
||||
if (expected != cur_thread) {
|
||||
return UnlockSlowPath(cur_thread);
|
||||
}
|
||||
} while (!tag.compare_exchange_weak(expected, 0, std::memory_order_release));
|
||||
}
|
||||
|
||||
void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
|
||||
KThread* cur_thread = reinterpret_cast<KThread*>(_cur_thread);
|
||||
|
||||
// Pend the current thread waiting on the owner thread.
|
||||
{
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
// Ensure we actually have locking to do.
|
||||
if (tag.load(std::memory_order_relaxed) != _owner) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add the current thread as a waiter on the owner.
|
||||
KThread* owner_thread = reinterpret_cast<KThread*>(_owner & ~1ULL);
|
||||
cur_thread->SetAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag)));
|
||||
owner_thread->AddWaiter(cur_thread);
|
||||
|
||||
// Set thread states.
|
||||
if (cur_thread->GetState() == ThreadState::Runnable) {
|
||||
cur_thread->SetState(ThreadState::Waiting);
|
||||
} else {
|
||||
KScheduler::SetSchedulerUpdateNeeded(kernel);
|
||||
}
|
||||
|
||||
if (owner_thread->IsSuspended()) {
|
||||
owner_thread->ContinueIfHasKernelWaiters();
|
||||
}
|
||||
}
|
||||
|
||||
// We're no longer waiting on the lock owner.
|
||||
{
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
KThread* owner_thread = cur_thread->GetLockOwner();
|
||||
if (owner_thread) {
|
||||
owner_thread->RemoveWaiter(cur_thread);
|
||||
KScheduler::SetSchedulerUpdateNeeded(kernel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
|
||||
KThread* owner_thread = reinterpret_cast<KThread*>(_cur_thread);
|
||||
|
||||
// Unlock.
|
||||
{
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
// Get the next owner.
|
||||
s32 num_waiters = 0;
|
||||
KThread* next_owner = owner_thread->RemoveWaiterByKey(
|
||||
std::addressof(num_waiters), reinterpret_cast<uintptr_t>(std::addressof(tag)));
|
||||
|
||||
// Pass the lock to the next owner.
|
||||
uintptr_t next_tag = 0;
|
||||
if (next_owner) {
|
||||
next_tag = reinterpret_cast<uintptr_t>(next_owner);
|
||||
if (num_waiters > 1) {
|
||||
next_tag |= 0x1;
|
||||
}
|
||||
|
||||
if (next_owner->GetState() == ThreadState::Waiting) {
|
||||
next_owner->SetState(ThreadState::Runnable);
|
||||
} else {
|
||||
KScheduler::SetSchedulerUpdateNeeded(kernel);
|
||||
}
|
||||
|
||||
if (next_owner->IsSuspended()) {
|
||||
next_owner->ContinueIfHasKernelWaiters();
|
||||
}
|
||||
}
|
||||
|
||||
// We may have unsuspended in the process of acquiring the lock, so we'll re-suspend now if
|
||||
// so.
|
||||
if (owner_thread->IsSuspended()) {
|
||||
owner_thread->TrySuspend();
|
||||
}
|
||||
|
||||
// Write the new tag value.
|
||||
tag.store(next_tag);
|
||||
}
|
||||
}
|
||||
|
||||
bool KLightLock::IsLockedByCurrentThread() const {
|
||||
return (tag | 1ULL) == (reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel)) | 1ULL);
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
41
src/core/hle/kernel/k_light_lock.h
Normal file
41
src/core/hle/kernel/k_light_lock.h
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/k_scoped_lock.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KernelCore;
|
||||
|
||||
class KLightLock {
|
||||
public:
|
||||
explicit KLightLock(KernelCore& kernel_) : kernel{kernel_} {}
|
||||
|
||||
void Lock();
|
||||
|
||||
void Unlock();
|
||||
|
||||
void LockSlowPath(uintptr_t owner, uintptr_t cur_thread);
|
||||
|
||||
void UnlockSlowPath(uintptr_t cur_thread);
|
||||
|
||||
bool IsLocked() const {
|
||||
return tag != 0;
|
||||
}
|
||||
|
||||
bool IsLockedByCurrentThread() const;
|
||||
|
||||
private:
|
||||
std::atomic<uintptr_t> tag{};
|
||||
KernelCore& kernel;
|
||||
};
|
||||
|
||||
using KScopedLightLock = KScopedLock<KLightLock>;
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class Thread;
|
||||
class KThread;
|
||||
|
||||
template <typename T>
|
||||
concept KPriorityQueueAffinityMask = !std::is_reference_v<T> && requires(T & t) {
|
||||
@@ -367,7 +367,7 @@ public:
|
||||
this->scheduled_queue.MoveToFront(member->GetPriority(), member->GetActiveCore(), member);
|
||||
}
|
||||
|
||||
constexpr Thread* MoveToScheduledBack(Member* member) {
|
||||
constexpr KThread* MoveToScheduledBack(Member* member) {
|
||||
return this->scheduled_queue.MoveToBack(member->GetPriority(), member->GetActiveCore(),
|
||||
member);
|
||||
}
|
||||
|
||||
@@ -17,25 +17,30 @@
|
||||
#include "core/cpu_manager.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/physical_core.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/time_manager.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
static void IncrementScheduledCount(Kernel::Thread* thread) {
|
||||
static void IncrementScheduledCount(Kernel::KThread* thread) {
|
||||
if (auto process = thread->GetOwnerProcess(); process) {
|
||||
process->IncrementScheduledCount();
|
||||
}
|
||||
}
|
||||
|
||||
void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule,
|
||||
Core::EmuThreadHandle global_thread) {
|
||||
const u32 current_core = global_thread.host_handle;
|
||||
bool must_context_switch = global_thread.guest_handle != InvalidHandle &&
|
||||
(current_core < Core::Hardware::NUM_CPU_CORES);
|
||||
void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule) {
|
||||
auto scheduler = kernel.CurrentScheduler();
|
||||
|
||||
u32 current_core{0xF};
|
||||
bool must_context_switch{};
|
||||
if (scheduler) {
|
||||
current_core = scheduler->core_id;
|
||||
// TODO(bunnei): Should be set to true when we deprecate single core
|
||||
must_context_switch = !kernel.IsPhantomModeForSingleCore();
|
||||
}
|
||||
|
||||
while (cores_pending_reschedule != 0) {
|
||||
const auto core = static_cast<u32>(std::countr_zero(cores_pending_reschedule));
|
||||
@@ -56,28 +61,27 @@ void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedul
|
||||
}
|
||||
}
|
||||
|
||||
u64 KScheduler::UpdateHighestPriorityThread(Thread* highest_thread) {
|
||||
u64 KScheduler::UpdateHighestPriorityThread(KThread* highest_thread) {
|
||||
std::scoped_lock lock{guard};
|
||||
if (Thread* prev_highest_thread = this->state.highest_priority_thread;
|
||||
if (KThread* prev_highest_thread = state.highest_priority_thread;
|
||||
prev_highest_thread != highest_thread) {
|
||||
if (prev_highest_thread != nullptr) {
|
||||
IncrementScheduledCount(prev_highest_thread);
|
||||
prev_highest_thread->SetLastScheduledTick(system.CoreTiming().GetCPUTicks());
|
||||
}
|
||||
if (this->state.should_count_idle) {
|
||||
if (state.should_count_idle) {
|
||||
if (highest_thread != nullptr) {
|
||||
// if (Process* process = highest_thread->GetOwnerProcess(); process != nullptr) {
|
||||
// process->SetRunningThread(this->core_id, highest_thread,
|
||||
// this->state.idle_count);
|
||||
//}
|
||||
if (Process* process = highest_thread->GetOwnerProcess(); process != nullptr) {
|
||||
process->SetRunningThread(core_id, highest_thread, state.idle_count);
|
||||
}
|
||||
} else {
|
||||
this->state.idle_count++;
|
||||
state.idle_count++;
|
||||
}
|
||||
}
|
||||
|
||||
this->state.highest_priority_thread = highest_thread;
|
||||
this->state.needs_scheduling = true;
|
||||
return (1ULL << this->core_id);
|
||||
state.highest_priority_thread = highest_thread;
|
||||
state.needs_scheduling.store(true);
|
||||
return (1ULL << core_id);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
@@ -90,16 +94,29 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
|
||||
ClearSchedulerUpdateNeeded(kernel);
|
||||
|
||||
u64 cores_needing_scheduling = 0, idle_cores = 0;
|
||||
Thread* top_threads[Core::Hardware::NUM_CPU_CORES];
|
||||
KThread* top_threads[Core::Hardware::NUM_CPU_CORES];
|
||||
auto& priority_queue = GetPriorityQueue(kernel);
|
||||
|
||||
/// We want to go over all cores, finding the highest priority thread and determining if
|
||||
/// scheduling is needed for that core.
|
||||
for (size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
|
||||
Thread* top_thread = priority_queue.GetScheduledFront(static_cast<s32>(core_id));
|
||||
KThread* top_thread = priority_queue.GetScheduledFront(static_cast<s32>(core_id));
|
||||
if (top_thread != nullptr) {
|
||||
// If the thread has no waiters, we need to check if the process has a thread pinned.
|
||||
// TODO(bunnei): Implement thread pinning
|
||||
if (top_thread->GetNumKernelWaiters() == 0) {
|
||||
if (Process* parent = top_thread->GetOwnerProcess(); parent != nullptr) {
|
||||
if (KThread* pinned = parent->GetPinnedThread(static_cast<s32>(core_id));
|
||||
pinned != nullptr && pinned != top_thread) {
|
||||
// We prefer our parent's pinned thread if possible. However, we also don't
|
||||
// want to schedule un-runnable threads.
|
||||
if (pinned->GetRawState() == ThreadState::Runnable) {
|
||||
top_thread = pinned;
|
||||
} else {
|
||||
top_thread = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
idle_cores |= (1ULL << core_id);
|
||||
}
|
||||
@@ -112,7 +129,7 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
|
||||
// Idle cores are bad. We're going to try to migrate threads to each idle core in turn.
|
||||
while (idle_cores != 0) {
|
||||
const auto core_id = static_cast<u32>(std::countr_zero(idle_cores));
|
||||
if (Thread* suggested = priority_queue.GetSuggestedFront(core_id); suggested != nullptr) {
|
||||
if (KThread* suggested = priority_queue.GetSuggestedFront(core_id); suggested != nullptr) {
|
||||
s32 migration_candidates[Core::Hardware::NUM_CPU_CORES];
|
||||
size_t num_candidates = 0;
|
||||
|
||||
@@ -120,7 +137,7 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
|
||||
while (suggested != nullptr) {
|
||||
// Check if the suggested thread is the top thread on its core.
|
||||
const s32 suggested_core = suggested->GetActiveCore();
|
||||
if (Thread* top_thread =
|
||||
if (KThread* top_thread =
|
||||
(suggested_core >= 0) ? top_threads[suggested_core] : nullptr;
|
||||
top_thread != suggested) {
|
||||
// Make sure we're not dealing with threads too high priority for migration.
|
||||
@@ -152,7 +169,7 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
|
||||
// Check if there's some other thread that can run on the candidate core.
|
||||
const s32 candidate_core = migration_candidates[i];
|
||||
suggested = top_threads[candidate_core];
|
||||
if (Thread* next_on_candidate_core =
|
||||
if (KThread* next_on_candidate_core =
|
||||
priority_queue.GetScheduledNext(candidate_core, suggested);
|
||||
next_on_candidate_core != nullptr) {
|
||||
// The candidate core can run some other thread! We'll migrate its current
|
||||
@@ -182,7 +199,20 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
|
||||
return cores_needing_scheduling;
|
||||
}
|
||||
|
||||
void KScheduler::OnThreadStateChanged(KernelCore& kernel, Thread* thread, ThreadState old_state) {
|
||||
void KScheduler::ClearPreviousThread(KernelCore& kernel, KThread* thread) {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; ++i) {
|
||||
// Get an atomic reference to the core scheduler's previous thread.
|
||||
std::atomic_ref<KThread*> prev_thread(kernel.Scheduler(static_cast<s32>(i)).prev_thread);
|
||||
static_assert(std::atomic_ref<KThread*>::is_always_lock_free);
|
||||
|
||||
// Atomically clear the previous thread if it's our target.
|
||||
KThread* compare = thread;
|
||||
prev_thread.compare_exchange_strong(compare, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void KScheduler::OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state) {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
|
||||
// Check if the state has changed, because if it hasn't there's nothing to do.
|
||||
@@ -205,7 +235,7 @@ void KScheduler::OnThreadStateChanged(KernelCore& kernel, Thread* thread, Thread
|
||||
}
|
||||
}
|
||||
|
||||
void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, s32 old_priority) {
|
||||
void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s32 old_priority) {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
|
||||
// If the thread is runnable, we want to change its priority in the queue.
|
||||
@@ -217,7 +247,7 @@ void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, s32
|
||||
}
|
||||
}
|
||||
|
||||
void KScheduler::OnThreadAffinityMaskChanged(KernelCore& kernel, Thread* thread,
|
||||
void KScheduler::OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread,
|
||||
const KAffinityMask& old_affinity, s32 old_core) {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
|
||||
@@ -237,8 +267,8 @@ void KScheduler::RotateScheduledQueue(s32 core_id, s32 priority) {
|
||||
auto& priority_queue = GetPriorityQueue(kernel);
|
||||
|
||||
// Rotate the front of the queue to the end.
|
||||
Thread* top_thread = priority_queue.GetScheduledFront(core_id, priority);
|
||||
Thread* next_thread = nullptr;
|
||||
KThread* top_thread = priority_queue.GetScheduledFront(core_id, priority);
|
||||
KThread* next_thread = nullptr;
|
||||
if (top_thread != nullptr) {
|
||||
next_thread = priority_queue.MoveToScheduledBack(top_thread);
|
||||
if (next_thread != top_thread) {
|
||||
@@ -249,11 +279,11 @@ void KScheduler::RotateScheduledQueue(s32 core_id, s32 priority) {
|
||||
|
||||
// While we have a suggested thread, try to migrate it!
|
||||
{
|
||||
Thread* suggested = priority_queue.GetSuggestedFront(core_id, priority);
|
||||
KThread* suggested = priority_queue.GetSuggestedFront(core_id, priority);
|
||||
while (suggested != nullptr) {
|
||||
// Check if the suggested thread is the top thread on its core.
|
||||
const s32 suggested_core = suggested->GetActiveCore();
|
||||
if (Thread* top_on_suggested_core =
|
||||
if (KThread* top_on_suggested_core =
|
||||
(suggested_core >= 0) ? priority_queue.GetScheduledFront(suggested_core)
|
||||
: nullptr;
|
||||
top_on_suggested_core != suggested) {
|
||||
@@ -285,7 +315,7 @@ void KScheduler::RotateScheduledQueue(s32 core_id, s32 priority) {
|
||||
// Now that we might have migrated a thread with the same priority, check if we can do better.
|
||||
|
||||
{
|
||||
Thread* best_thread = priority_queue.GetScheduledFront(core_id);
|
||||
KThread* best_thread = priority_queue.GetScheduledFront(core_id);
|
||||
if (best_thread == GetCurrentThread()) {
|
||||
best_thread = priority_queue.GetScheduledNext(core_id, best_thread);
|
||||
}
|
||||
@@ -293,7 +323,7 @@ void KScheduler::RotateScheduledQueue(s32 core_id, s32 priority) {
|
||||
// If the best thread we can choose has a priority the same or worse than ours, try to
|
||||
// migrate a higher priority thread.
|
||||
if (best_thread != nullptr && best_thread->GetPriority() >= priority) {
|
||||
Thread* suggested = priority_queue.GetSuggestedFront(core_id);
|
||||
KThread* suggested = priority_queue.GetSuggestedFront(core_id);
|
||||
while (suggested != nullptr) {
|
||||
// If the suggestion's priority is the same as ours, don't bother.
|
||||
if (suggested->GetPriority() >= best_thread->GetPriority()) {
|
||||
@@ -302,7 +332,7 @@ void KScheduler::RotateScheduledQueue(s32 core_id, s32 priority) {
|
||||
|
||||
// Check if the suggested thread is the top thread on its core.
|
||||
const s32 suggested_core = suggested->GetActiveCore();
|
||||
if (Thread* top_on_suggested_core =
|
||||
if (KThread* top_on_suggested_core =
|
||||
(suggested_core >= 0) ? priority_queue.GetScheduledFront(suggested_core)
|
||||
: nullptr;
|
||||
top_on_suggested_core != suggested) {
|
||||
@@ -352,12 +382,14 @@ void KScheduler::DisableScheduling(KernelCore& kernel) {
|
||||
}
|
||||
}
|
||||
|
||||
void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling,
|
||||
Core::EmuThreadHandle global_thread) {
|
||||
void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) {
|
||||
if (auto* scheduler = kernel.CurrentScheduler(); scheduler) {
|
||||
scheduler->GetCurrentThread()->EnableDispatch();
|
||||
ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1);
|
||||
if (scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1) {
|
||||
scheduler->GetCurrentThread()->EnableDispatch();
|
||||
}
|
||||
}
|
||||
RescheduleCores(kernel, cores_needing_scheduling, global_thread);
|
||||
RescheduleCores(kernel, cores_needing_scheduling);
|
||||
}
|
||||
|
||||
u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) {
|
||||
@@ -372,15 +404,13 @@ KSchedulerPriorityQueue& KScheduler::GetPriorityQueue(KernelCore& kernel) {
|
||||
return kernel.GlobalSchedulerContext().priority_queue;
|
||||
}
|
||||
|
||||
void KScheduler::YieldWithoutCoreMigration() {
|
||||
auto& kernel = system.Kernel();
|
||||
|
||||
void KScheduler::YieldWithoutCoreMigration(KernelCore& kernel) {
|
||||
// Validate preconditions.
|
||||
ASSERT(CanSchedule(kernel));
|
||||
ASSERT(kernel.CurrentProcess() != nullptr);
|
||||
|
||||
// Get the current thread and process.
|
||||
Thread& cur_thread = *GetCurrentThread();
|
||||
KThread& cur_thread = Kernel::GetCurrentThread(kernel);
|
||||
Process& cur_process = *kernel.CurrentProcess();
|
||||
|
||||
// If the thread's yield count matches, there's nothing for us to do.
|
||||
@@ -398,7 +428,7 @@ void KScheduler::YieldWithoutCoreMigration() {
|
||||
const auto cur_state = cur_thread.GetRawState();
|
||||
if (cur_state == ThreadState::Runnable) {
|
||||
// Put the current thread at the back of the queue.
|
||||
Thread* next_thread = priority_queue.MoveToScheduledBack(std::addressof(cur_thread));
|
||||
KThread* next_thread = priority_queue.MoveToScheduledBack(std::addressof(cur_thread));
|
||||
IncrementScheduledCount(std::addressof(cur_thread));
|
||||
|
||||
// If the next thread is different, we have an update to perform.
|
||||
@@ -413,15 +443,13 @@ void KScheduler::YieldWithoutCoreMigration() {
|
||||
}
|
||||
}
|
||||
|
||||
void KScheduler::YieldWithCoreMigration() {
|
||||
auto& kernel = system.Kernel();
|
||||
|
||||
void KScheduler::YieldWithCoreMigration(KernelCore& kernel) {
|
||||
// Validate preconditions.
|
||||
ASSERT(CanSchedule(kernel));
|
||||
ASSERT(kernel.CurrentProcess() != nullptr);
|
||||
|
||||
// Get the current thread and process.
|
||||
Thread& cur_thread = *GetCurrentThread();
|
||||
KThread& cur_thread = Kernel::GetCurrentThread(kernel);
|
||||
Process& cur_process = *kernel.CurrentProcess();
|
||||
|
||||
// If the thread's yield count matches, there's nothing for us to do.
|
||||
@@ -442,17 +470,17 @@ void KScheduler::YieldWithCoreMigration() {
|
||||
const s32 core_id = cur_thread.GetActiveCore();
|
||||
|
||||
// Put the current thread at the back of the queue.
|
||||
Thread* next_thread = priority_queue.MoveToScheduledBack(std::addressof(cur_thread));
|
||||
KThread* next_thread = priority_queue.MoveToScheduledBack(std::addressof(cur_thread));
|
||||
IncrementScheduledCount(std::addressof(cur_thread));
|
||||
|
||||
// While we have a suggested thread, try to migrate it!
|
||||
bool recheck = false;
|
||||
Thread* suggested = priority_queue.GetSuggestedFront(core_id);
|
||||
KThread* suggested = priority_queue.GetSuggestedFront(core_id);
|
||||
while (suggested != nullptr) {
|
||||
// Check if the suggested thread is the thread running on its core.
|
||||
const s32 suggested_core = suggested->GetActiveCore();
|
||||
|
||||
if (Thread* running_on_suggested_core =
|
||||
if (KThread* running_on_suggested_core =
|
||||
(suggested_core >= 0)
|
||||
? kernel.Scheduler(suggested_core).state.highest_priority_thread
|
||||
: nullptr;
|
||||
@@ -503,15 +531,13 @@ void KScheduler::YieldWithCoreMigration() {
|
||||
}
|
||||
}
|
||||
|
||||
void KScheduler::YieldToAnyThread() {
|
||||
auto& kernel = system.Kernel();
|
||||
|
||||
void KScheduler::YieldToAnyThread(KernelCore& kernel) {
|
||||
// Validate preconditions.
|
||||
ASSERT(CanSchedule(kernel));
|
||||
ASSERT(kernel.CurrentProcess() != nullptr);
|
||||
|
||||
// Get the current thread and process.
|
||||
Thread& cur_thread = *GetCurrentThread();
|
||||
KThread& cur_thread = Kernel::GetCurrentThread(kernel);
|
||||
Process& cur_process = *kernel.CurrentProcess();
|
||||
|
||||
// If the thread's yield count matches, there's nothing for us to do.
|
||||
@@ -539,11 +565,11 @@ void KScheduler::YieldToAnyThread() {
|
||||
// If there's nothing scheduled, we can try to perform a migration.
|
||||
if (priority_queue.GetScheduledFront(core_id) == nullptr) {
|
||||
// While we have a suggested thread, try to migrate it!
|
||||
Thread* suggested = priority_queue.GetSuggestedFront(core_id);
|
||||
KThread* suggested = priority_queue.GetSuggestedFront(core_id);
|
||||
while (suggested != nullptr) {
|
||||
// Check if the suggested thread is the top thread on its core.
|
||||
const s32 suggested_core = suggested->GetActiveCore();
|
||||
if (Thread* top_on_suggested_core =
|
||||
if (KThread* top_on_suggested_core =
|
||||
(suggested_core >= 0) ? priority_queue.GetScheduledFront(suggested_core)
|
||||
: nullptr;
|
||||
top_on_suggested_core != suggested) {
|
||||
@@ -581,22 +607,21 @@ void KScheduler::YieldToAnyThread() {
|
||||
}
|
||||
}
|
||||
|
||||
KScheduler::KScheduler(Core::System& system, std::size_t core_id)
|
||||
: system(system), core_id(core_id) {
|
||||
KScheduler::KScheduler(Core::System& system, s32 core_id) : system(system), core_id(core_id) {
|
||||
switch_fiber = std::make_shared<Common::Fiber>(OnSwitch, this);
|
||||
this->state.needs_scheduling = true;
|
||||
this->state.interrupt_task_thread_runnable = false;
|
||||
this->state.should_count_idle = false;
|
||||
this->state.idle_count = 0;
|
||||
this->state.idle_thread_stack = nullptr;
|
||||
this->state.highest_priority_thread = nullptr;
|
||||
state.needs_scheduling.store(true);
|
||||
state.interrupt_task_thread_runnable = false;
|
||||
state.should_count_idle = false;
|
||||
state.idle_count = 0;
|
||||
state.idle_thread_stack = nullptr;
|
||||
state.highest_priority_thread = nullptr;
|
||||
}
|
||||
|
||||
KScheduler::~KScheduler() = default;
|
||||
|
||||
Thread* KScheduler::GetCurrentThread() const {
|
||||
if (current_thread) {
|
||||
return current_thread;
|
||||
KThread* KScheduler::GetCurrentThread() const {
|
||||
if (auto result = current_thread.load(); result) {
|
||||
return result;
|
||||
}
|
||||
return idle_thread;
|
||||
}
|
||||
@@ -613,7 +638,7 @@ void KScheduler::RescheduleCurrentCore() {
|
||||
phys_core.ClearInterrupt();
|
||||
}
|
||||
guard.lock();
|
||||
if (this->state.needs_scheduling) {
|
||||
if (state.needs_scheduling.load()) {
|
||||
Schedule();
|
||||
} else {
|
||||
guard.unlock();
|
||||
@@ -624,66 +649,76 @@ void KScheduler::OnThreadStart() {
|
||||
SwitchContextStep2();
|
||||
}
|
||||
|
||||
void KScheduler::Unload(Thread* thread) {
|
||||
void KScheduler::Unload(KThread* thread) {
|
||||
LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr");
|
||||
|
||||
if (thread) {
|
||||
thread->SetIsRunning(false);
|
||||
if (thread->IsContinuousOnSVC() && !thread->IsHLEThread()) {
|
||||
if (thread->IsCallingSvc()) {
|
||||
system.ArmInterface(core_id).ExceptionalExit();
|
||||
thread->SetContinuousOnSVC(false);
|
||||
thread->ClearIsCallingSvc();
|
||||
}
|
||||
if (!thread->IsHLEThread() && !thread->HasExited()) {
|
||||
if (!thread->IsTerminationRequested()) {
|
||||
prev_thread = thread;
|
||||
|
||||
Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
|
||||
cpu_core.SaveContext(thread->GetContext32());
|
||||
cpu_core.SaveContext(thread->GetContext64());
|
||||
// Save the TPIDR_EL0 system register in case it was modified.
|
||||
thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
|
||||
cpu_core.ClearExclusiveState();
|
||||
} else {
|
||||
prev_thread = nullptr;
|
||||
}
|
||||
thread->context_guard.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void KScheduler::Reload(Thread* thread) {
|
||||
void KScheduler::Reload(KThread* thread) {
|
||||
LOG_TRACE(Kernel, "core {}, reload thread {}", core_id, thread ? thread->GetName() : "nullptr");
|
||||
|
||||
if (thread) {
|
||||
ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable.");
|
||||
|
||||
// Cancel any outstanding wakeup events for this thread
|
||||
thread->SetIsRunning(true);
|
||||
thread->SetWasRunning(false);
|
||||
|
||||
auto* const thread_owner_process = thread->GetOwnerProcess();
|
||||
if (thread_owner_process != nullptr) {
|
||||
system.Kernel().MakeCurrentProcess(thread_owner_process);
|
||||
}
|
||||
if (!thread->IsHLEThread()) {
|
||||
Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
|
||||
cpu_core.LoadContext(thread->GetContext32());
|
||||
cpu_core.LoadContext(thread->GetContext64());
|
||||
cpu_core.SetTlsAddress(thread->GetTLSAddress());
|
||||
cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0());
|
||||
cpu_core.ClearExclusiveState();
|
||||
}
|
||||
|
||||
Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
|
||||
cpu_core.LoadContext(thread->GetContext32());
|
||||
cpu_core.LoadContext(thread->GetContext64());
|
||||
cpu_core.SetTlsAddress(thread->GetTLSAddress());
|
||||
cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0());
|
||||
cpu_core.ClearExclusiveState();
|
||||
}
|
||||
}
|
||||
|
||||
void KScheduler::SwitchContextStep2() {
|
||||
// Load context of new thread
|
||||
Reload(current_thread);
|
||||
Reload(current_thread.load());
|
||||
|
||||
RescheduleCurrentCore();
|
||||
}
|
||||
|
||||
void KScheduler::ScheduleImpl() {
|
||||
Thread* previous_thread = current_thread;
|
||||
current_thread = state.highest_priority_thread;
|
||||
KThread* previous_thread = current_thread.load();
|
||||
KThread* next_thread = state.highest_priority_thread;
|
||||
|
||||
this->state.needs_scheduling = false;
|
||||
state.needs_scheduling = false;
|
||||
|
||||
if (current_thread == previous_thread) {
|
||||
// We never want to schedule a null thread, so use the idle thread if we don't have a next.
|
||||
if (next_thread == nullptr) {
|
||||
next_thread = idle_thread;
|
||||
}
|
||||
|
||||
// If we're not actually switching thread, there's nothing to do.
|
||||
if (next_thread == current_thread.load()) {
|
||||
guard.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
current_thread.store(next_thread);
|
||||
|
||||
Process* const previous_process = system.Kernel().CurrentProcess();
|
||||
|
||||
UpdateLastContextSwitchTime(previous_thread, previous_process);
|
||||
@@ -714,28 +749,29 @@ void KScheduler::SwitchToCurrent() {
|
||||
while (true) {
|
||||
{
|
||||
std::scoped_lock lock{guard};
|
||||
current_thread = state.highest_priority_thread;
|
||||
this->state.needs_scheduling = false;
|
||||
current_thread.store(state.highest_priority_thread);
|
||||
state.needs_scheduling.store(false);
|
||||
}
|
||||
const auto is_switch_pending = [this] {
|
||||
std::scoped_lock lock{guard};
|
||||
return state.needs_scheduling.load(std::memory_order_relaxed);
|
||||
return state.needs_scheduling.load();
|
||||
};
|
||||
do {
|
||||
if (current_thread != nullptr && !current_thread->IsHLEThread()) {
|
||||
current_thread->context_guard.lock();
|
||||
if (current_thread->GetRawState() != ThreadState::Runnable) {
|
||||
current_thread->context_guard.unlock();
|
||||
auto next_thread = current_thread.load();
|
||||
if (next_thread != nullptr) {
|
||||
next_thread->context_guard.lock();
|
||||
if (next_thread->GetRawState() != ThreadState::Runnable) {
|
||||
next_thread->context_guard.unlock();
|
||||
break;
|
||||
}
|
||||
if (static_cast<u32>(current_thread->GetProcessorID()) != core_id) {
|
||||
current_thread->context_guard.unlock();
|
||||
if (next_thread->GetActiveCore() != core_id) {
|
||||
next_thread->context_guard.unlock();
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::shared_ptr<Common::Fiber>* next_context;
|
||||
if (current_thread != nullptr) {
|
||||
next_context = ¤t_thread->GetHostContext();
|
||||
if (next_thread != nullptr) {
|
||||
next_context = &next_thread->GetHostContext();
|
||||
} else {
|
||||
next_context = &idle_thread->GetHostContext();
|
||||
}
|
||||
@@ -744,13 +780,13 @@ void KScheduler::SwitchToCurrent() {
|
||||
}
|
||||
}
|
||||
|
||||
void KScheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
|
||||
void KScheduler::UpdateLastContextSwitchTime(KThread* thread, Process* process) {
|
||||
const u64 prev_switch_ticks = last_context_switch_time;
|
||||
const u64 most_recent_switch_ticks = system.CoreTiming().GetCPUTicks();
|
||||
const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
|
||||
|
||||
if (thread != nullptr) {
|
||||
thread->UpdateCPUTimeTicks(update_ticks);
|
||||
thread->AddCpuTime(core_id, update_ticks);
|
||||
}
|
||||
|
||||
if (process != nullptr) {
|
||||
@@ -764,15 +800,10 @@ void KScheduler::Initialize() {
|
||||
std::string name = "Idle Thread Id:" + std::to_string(core_id);
|
||||
std::function<void(void*)> init_func = Core::CpuManager::GetIdleThreadStartFunc();
|
||||
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
|
||||
ThreadType type = static_cast<ThreadType>(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_IDLE);
|
||||
auto thread_res = Thread::Create(system, type, name, 0, 64, 0, static_cast<u32>(core_id), 0,
|
||||
nullptr, std::move(init_func), init_func_parameter);
|
||||
auto thread_res = KThread::Create(system, ThreadType::Main, name, 0,
|
||||
KThread::IdleThreadPriority, 0, static_cast<u32>(core_id), 0,
|
||||
nullptr, std::move(init_func), init_func_parameter);
|
||||
idle_thread = thread_res.Unwrap().get();
|
||||
|
||||
{
|
||||
KScopedSchedulerLock lock{system.Kernel()};
|
||||
idle_thread->SetState(ThreadState::Runnable);
|
||||
}
|
||||
}
|
||||
|
||||
KScopedSchedulerLock::KScopedSchedulerLock(KernelCore& kernel)
|
||||
|
||||
@@ -29,29 +29,33 @@ namespace Kernel {
|
||||
class KernelCore;
|
||||
class Process;
|
||||
class SchedulerLock;
|
||||
class Thread;
|
||||
class KThread;
|
||||
|
||||
class KScheduler final {
|
||||
public:
|
||||
explicit KScheduler(Core::System& system, std::size_t core_id);
|
||||
explicit KScheduler(Core::System& system, s32 core_id);
|
||||
~KScheduler();
|
||||
|
||||
/// Reschedules to the next available thread (call after current thread is suspended)
|
||||
void RescheduleCurrentCore();
|
||||
|
||||
/// Reschedules cores pending reschedule, to be called on EnableScheduling.
|
||||
static void RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule,
|
||||
Core::EmuThreadHandle global_thread);
|
||||
static void RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule);
|
||||
|
||||
/// The next two are for SingleCore Only.
|
||||
/// Unload current thread before preempting core.
|
||||
void Unload(Thread* thread);
|
||||
void Unload(KThread* thread);
|
||||
|
||||
/// Reload current thread after core preemption.
|
||||
void Reload(Thread* thread);
|
||||
void Reload(KThread* thread);
|
||||
|
||||
/// Gets the current running thread
|
||||
[[nodiscard]] Thread* GetCurrentThread() const;
|
||||
[[nodiscard]] KThread* GetCurrentThread() const;
|
||||
|
||||
/// Returns true if the scheduler is idle
|
||||
[[nodiscard]] bool IsIdle() const {
|
||||
return GetCurrentThread() == idle_thread;
|
||||
}
|
||||
|
||||
/// Gets the timestamp for the last context switch in ticks.
|
||||
[[nodiscard]] u64 GetLastContextSwitchTicks() const;
|
||||
@@ -72,14 +76,14 @@ public:
|
||||
return switch_fiber;
|
||||
}
|
||||
|
||||
[[nodiscard]] u64 UpdateHighestPriorityThread(Thread* highest_thread);
|
||||
[[nodiscard]] u64 UpdateHighestPriorityThread(KThread* highest_thread);
|
||||
|
||||
/**
|
||||
* Takes a thread and moves it to the back of the it's priority list.
|
||||
*
|
||||
* @note This operation can be redundant and no scheduling is changed if marked as so.
|
||||
*/
|
||||
void YieldWithoutCoreMigration();
|
||||
static void YieldWithoutCoreMigration(KernelCore& kernel);
|
||||
|
||||
/**
|
||||
* Takes a thread and moves it to the back of the it's priority list.
|
||||
@@ -88,7 +92,7 @@ public:
|
||||
*
|
||||
* @note This operation can be redundant and no scheduling is changed if marked as so.
|
||||
*/
|
||||
void YieldWithCoreMigration();
|
||||
static void YieldWithCoreMigration(KernelCore& kernel);
|
||||
|
||||
/**
|
||||
* Takes a thread and moves it out of the scheduling queue.
|
||||
@@ -97,16 +101,18 @@ public:
|
||||
*
|
||||
* @note This operation can be redundant and no scheduling is changed if marked as so.
|
||||
*/
|
||||
void YieldToAnyThread();
|
||||
static void YieldToAnyThread(KernelCore& kernel);
|
||||
|
||||
static void ClearPreviousThread(KernelCore& kernel, KThread* thread);
|
||||
|
||||
/// Notify the scheduler a thread's status has changed.
|
||||
static void OnThreadStateChanged(KernelCore& kernel, Thread* thread, ThreadState old_state);
|
||||
static void OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state);
|
||||
|
||||
/// Notify the scheduler a thread's priority has changed.
|
||||
static void OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, s32 old_priority);
|
||||
static void OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s32 old_priority);
|
||||
|
||||
/// Notify the scheduler a thread's core and/or affinity mask has changed.
|
||||
static void OnThreadAffinityMaskChanged(KernelCore& kernel, Thread* thread,
|
||||
static void OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread,
|
||||
const KAffinityMask& old_affinity, s32 old_core);
|
||||
|
||||
static bool CanSchedule(KernelCore& kernel);
|
||||
@@ -114,8 +120,7 @@ public:
|
||||
static void SetSchedulerUpdateNeeded(KernelCore& kernel);
|
||||
static void ClearSchedulerUpdateNeeded(KernelCore& kernel);
|
||||
static void DisableScheduling(KernelCore& kernel);
|
||||
static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling,
|
||||
Core::EmuThreadHandle global_thread);
|
||||
static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling);
|
||||
[[nodiscard]] static u64 UpdateHighestPriorityThreads(KernelCore& kernel);
|
||||
|
||||
private:
|
||||
@@ -163,13 +168,15 @@ private:
|
||||
* most recent tick count retrieved. No special arithmetic is
|
||||
* applied to it.
|
||||
*/
|
||||
void UpdateLastContextSwitchTime(Thread* thread, Process* process);
|
||||
void UpdateLastContextSwitchTime(KThread* thread, Process* process);
|
||||
|
||||
static void OnSwitch(void* this_scheduler);
|
||||
void SwitchToCurrent();
|
||||
|
||||
Thread* current_thread{};
|
||||
Thread* idle_thread{};
|
||||
KThread* prev_thread{};
|
||||
std::atomic<KThread*> current_thread{};
|
||||
|
||||
KThread* idle_thread;
|
||||
|
||||
std::shared_ptr<Common::Fiber> switch_fiber{};
|
||||
|
||||
@@ -178,7 +185,7 @@ private:
|
||||
bool interrupt_task_thread_runnable{};
|
||||
bool should_count_idle{};
|
||||
u64 idle_count{};
|
||||
Thread* highest_priority_thread{};
|
||||
KThread* highest_priority_thread{};
|
||||
void* idle_thread_stack{};
|
||||
};
|
||||
|
||||
@@ -186,7 +193,7 @@ private:
|
||||
|
||||
Core::System& system;
|
||||
u64 last_context_switch_time{};
|
||||
const std::size_t core_id;
|
||||
const s32 core_id;
|
||||
|
||||
Common::SpinLock guard{};
|
||||
};
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/spin_lock.h"
|
||||
#include "core/hardware_properties.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
|
||||
namespace Kernel {
|
||||
@@ -22,46 +23,45 @@ public:
|
||||
explicit KAbstractSchedulerLock(KernelCore& kernel_) : kernel{kernel_} {}
|
||||
|
||||
bool IsLockedByCurrentThread() const {
|
||||
return this->owner_thread == kernel.GetCurrentEmuThreadID();
|
||||
return owner_thread == GetCurrentThreadPointer(kernel);
|
||||
}
|
||||
|
||||
void Lock() {
|
||||
if (this->IsLockedByCurrentThread()) {
|
||||
if (IsLockedByCurrentThread()) {
|
||||
// If we already own the lock, we can just increment the count.
|
||||
ASSERT(this->lock_count > 0);
|
||||
this->lock_count++;
|
||||
ASSERT(lock_count > 0);
|
||||
lock_count++;
|
||||
} else {
|
||||
// Otherwise, we want to disable scheduling and acquire the spinlock.
|
||||
SchedulerType::DisableScheduling(kernel);
|
||||
this->spin_lock.lock();
|
||||
spin_lock.lock();
|
||||
|
||||
// For debug, ensure that our state is valid.
|
||||
ASSERT(this->lock_count == 0);
|
||||
ASSERT(this->owner_thread == Core::EmuThreadHandle::InvalidHandle());
|
||||
ASSERT(lock_count == 0);
|
||||
ASSERT(owner_thread == nullptr);
|
||||
|
||||
// Increment count, take ownership.
|
||||
this->lock_count = 1;
|
||||
this->owner_thread = kernel.GetCurrentEmuThreadID();
|
||||
lock_count = 1;
|
||||
owner_thread = GetCurrentThreadPointer(kernel);
|
||||
}
|
||||
}
|
||||
|
||||
void Unlock() {
|
||||
ASSERT(this->IsLockedByCurrentThread());
|
||||
ASSERT(this->lock_count > 0);
|
||||
ASSERT(IsLockedByCurrentThread());
|
||||
ASSERT(lock_count > 0);
|
||||
|
||||
// Release an instance of the lock.
|
||||
if ((--this->lock_count) == 0) {
|
||||
if ((--lock_count) == 0) {
|
||||
// We're no longer going to hold the lock. Take note of what cores need scheduling.
|
||||
const u64 cores_needing_scheduling =
|
||||
SchedulerType::UpdateHighestPriorityThreads(kernel);
|
||||
Core::EmuThreadHandle leaving_thread = owner_thread;
|
||||
|
||||
// Note that we no longer hold the lock, and unlock the spinlock.
|
||||
this->owner_thread = Core::EmuThreadHandle::InvalidHandle();
|
||||
this->spin_lock.unlock();
|
||||
owner_thread = nullptr;
|
||||
spin_lock.unlock();
|
||||
|
||||
// Enable scheduling, and perform a rescheduling operation.
|
||||
SchedulerType::EnableScheduling(kernel, cores_needing_scheduling, leaving_thread);
|
||||
SchedulerType::EnableScheduling(kernel, cores_needing_scheduling);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ private:
|
||||
KernelCore& kernel;
|
||||
Common::SpinLock spin_lock{};
|
||||
s32 lock_count{};
|
||||
Core::EmuThreadHandle owner_thread{Core::EmuThreadHandle::InvalidHandle()};
|
||||
KThread* owner_thread{};
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -9,27 +9,24 @@
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/time_manager.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KScopedSchedulerLockAndSleep {
|
||||
public:
|
||||
explicit KScopedSchedulerLockAndSleep(KernelCore& kernel, Handle& event_handle, Thread* t,
|
||||
s64 timeout)
|
||||
: kernel(kernel), event_handle(event_handle), thread(t), timeout_tick(timeout) {
|
||||
event_handle = InvalidHandle;
|
||||
|
||||
explicit KScopedSchedulerLockAndSleep(KernelCore& kernel, KThread* t, s64 timeout)
|
||||
: kernel(kernel), thread(t), timeout_tick(timeout) {
|
||||
// Lock the scheduler.
|
||||
kernel.GlobalSchedulerContext().scheduler_lock.Lock();
|
||||
}
|
||||
|
||||
~KScopedSchedulerLockAndSleep() {
|
||||
// Register the sleep.
|
||||
if (this->timeout_tick > 0) {
|
||||
kernel.TimeManager().ScheduleTimeEvent(event_handle, this->thread, this->timeout_tick);
|
||||
if (timeout_tick > 0) {
|
||||
kernel.TimeManager().ScheduleTimeEvent(thread, timeout_tick);
|
||||
}
|
||||
|
||||
// Unlock the scheduler.
|
||||
@@ -37,13 +34,12 @@ public:
|
||||
}
|
||||
|
||||
void CancelSleep() {
|
||||
this->timeout_tick = 0;
|
||||
timeout_tick = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
KernelCore& kernel;
|
||||
Handle& event_handle;
|
||||
Thread* thread{};
|
||||
KThread* thread{};
|
||||
s64 timeout_tick{};
|
||||
};
|
||||
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
|
||||
#include "core/hle/kernel/k_synchronization_object.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -20,12 +20,11 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index,
|
||||
std::vector<ThreadListNode> thread_nodes(num_objects);
|
||||
|
||||
// Prepare for wait.
|
||||
Thread* thread = kernel.CurrentScheduler()->GetCurrentThread();
|
||||
Handle timer = InvalidHandle;
|
||||
KThread* thread = kernel.CurrentScheduler()->GetCurrentThread();
|
||||
|
||||
{
|
||||
// Setup the scheduling lock and sleep.
|
||||
KScopedSchedulerLockAndSleep slp(kernel, timer, thread, timeout);
|
||||
KScopedSchedulerLockAndSleep slp{kernel, thread, timeout};
|
||||
|
||||
// Check if any of the objects are already signaled.
|
||||
for (auto i = 0; i < num_objects; ++i) {
|
||||
@@ -90,10 +89,7 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index,
|
||||
thread->SetWaitObjectsForDebugging({});
|
||||
|
||||
// Cancel the timer as needed.
|
||||
if (timer != InvalidHandle) {
|
||||
auto& time_manager = kernel.TimeManager();
|
||||
time_manager.UnscheduleTimeEvent(timer);
|
||||
}
|
||||
kernel.TimeManager().UnscheduleTimeEvent(thread);
|
||||
|
||||
// Get the wait result.
|
||||
ResultCode wait_result{RESULT_SUCCESS};
|
||||
@@ -136,7 +132,7 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index,
|
||||
|
||||
KSynchronizationObject::KSynchronizationObject(KernelCore& kernel) : Object{kernel} {}
|
||||
|
||||
KSynchronizationObject ::~KSynchronizationObject() = default;
|
||||
KSynchronizationObject::~KSynchronizationObject() = default;
|
||||
|
||||
void KSynchronizationObject::NotifyAvailable(ResultCode result) {
|
||||
KScopedSchedulerLock lock(kernel);
|
||||
@@ -148,7 +144,7 @@ void KSynchronizationObject::NotifyAvailable(ResultCode result) {
|
||||
|
||||
// Iterate over each thread.
|
||||
for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) {
|
||||
Thread* thread = cur_node->thread;
|
||||
KThread* thread = cur_node->thread;
|
||||
if (thread->GetState() == ThreadState::Waiting) {
|
||||
thread->SetSyncedObject(this, result);
|
||||
thread->SetState(ThreadState::Runnable);
|
||||
@@ -156,8 +152,8 @@ void KSynchronizationObject::NotifyAvailable(ResultCode result) {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Thread*> KSynchronizationObject::GetWaitingThreadsForDebugging() const {
|
||||
std::vector<Thread*> threads;
|
||||
std::vector<KThread*> KSynchronizationObject::GetWaitingThreadsForDebugging() const {
|
||||
std::vector<KThread*> threads;
|
||||
|
||||
// If debugging, dump the list of waiters.
|
||||
{
|
||||
|
||||
@@ -13,14 +13,14 @@ namespace Kernel {
|
||||
|
||||
class KernelCore;
|
||||
class Synchronization;
|
||||
class Thread;
|
||||
class KThread;
|
||||
|
||||
/// Class that represents a Kernel object that a thread can be waiting on
|
||||
class KSynchronizationObject : public Object {
|
||||
public:
|
||||
struct ThreadListNode {
|
||||
ThreadListNode* next{};
|
||||
Thread* thread{};
|
||||
KThread* thread{};
|
||||
};
|
||||
|
||||
[[nodiscard]] static ResultCode Wait(KernelCore& kernel, s32* out_index,
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
|
||||
[[nodiscard]] virtual bool IsSignaled() const = 0;
|
||||
|
||||
[[nodiscard]] std::vector<Thread*> GetWaitingThreadsForDebugging() const;
|
||||
[[nodiscard]] std::vector<KThread*> GetWaitingThreadsForDebugging() const;
|
||||
|
||||
protected:
|
||||
explicit KSynchronizationObject(KernelCore& kernel);
|
||||
|
||||
1050
src/core/hle/kernel/k_thread.cpp
Normal file
1050
src/core/hle/kernel/k_thread.cpp
Normal file
File diff suppressed because it is too large
Load Diff
768
src/core/hle/kernel/k_thread.h
Normal file
768
src/core/hle/kernel/k_thread.h
Normal file
@@ -0,0 +1,768 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/intrusive/list.hpp>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/intrusive_red_black_tree.h"
|
||||
#include "common/spin_lock.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/hle/kernel/k_affinity_mask.h"
|
||||
#include "core/hle/kernel/k_light_lock.h"
|
||||
#include "core/hle/kernel/k_synchronization_object.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/svc_common.h"
|
||||
#include "core/hle/kernel/svc_types.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Common {
|
||||
class Fiber;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class ARM_Interface;
|
||||
class System;
|
||||
} // namespace Core
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class GlobalSchedulerContext;
|
||||
class KernelCore;
|
||||
class Process;
|
||||
class KScheduler;
|
||||
class KThreadQueue;
|
||||
|
||||
using KThreadFunction = VAddr;
|
||||
|
||||
enum class ThreadType : u32 {
|
||||
Main = 0,
|
||||
Kernel = 1,
|
||||
HighPriority = 2,
|
||||
User = 3,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(ThreadType);
|
||||
|
||||
enum class SuspendType : u32 {
|
||||
Process = 0,
|
||||
Thread = 1,
|
||||
Debug = 2,
|
||||
Backtrace = 3,
|
||||
Init = 4,
|
||||
|
||||
Count,
|
||||
};
|
||||
|
||||
enum class ThreadState : u16 {
|
||||
Initialized = 0,
|
||||
Waiting = 1,
|
||||
Runnable = 2,
|
||||
Terminated = 3,
|
||||
|
||||
SuspendShift = 4,
|
||||
Mask = (1 << SuspendShift) - 1,
|
||||
|
||||
ProcessSuspended = (1 << (0 + SuspendShift)),
|
||||
ThreadSuspended = (1 << (1 + SuspendShift)),
|
||||
DebugSuspended = (1 << (2 + SuspendShift)),
|
||||
BacktraceSuspended = (1 << (3 + SuspendShift)),
|
||||
InitSuspended = (1 << (4 + SuspendShift)),
|
||||
|
||||
SuspendFlagMask = ((1 << 5) - 1) << SuspendShift,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(ThreadState);
|
||||
|
||||
enum class DpcFlag : u32 {
|
||||
Terminating = (1 << 0),
|
||||
Terminated = (1 << 1),
|
||||
};
|
||||
|
||||
enum class ThreadWaitReasonForDebugging : u32 {
|
||||
None, ///< Thread is not waiting
|
||||
Sleep, ///< Thread is waiting due to a SleepThread SVC
|
||||
IPC, ///< Thread is waiting for the reply from an IPC request
|
||||
Synchronization, ///< Thread is waiting due to a WaitSynchronization SVC
|
||||
ConditionVar, ///< Thread is waiting due to a WaitProcessWideKey SVC
|
||||
Arbitration, ///< Thread is waiting due to a SignalToAddress/WaitForAddress SVC
|
||||
Suspended, ///< Thread is waiting due to process suspension
|
||||
};
|
||||
|
||||
[[nodiscard]] KThread* GetCurrentThreadPointer(KernelCore& kernel);
|
||||
[[nodiscard]] KThread& GetCurrentThread(KernelCore& kernel);
|
||||
[[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel);
|
||||
|
||||
class KThread final : public KSynchronizationObject, public boost::intrusive::list_base_hook<> {
|
||||
friend class KScheduler;
|
||||
friend class Process;
|
||||
|
||||
public:
|
||||
static constexpr s32 DefaultThreadPriority = 44;
|
||||
static constexpr s32 IdleThreadPriority = Svc::LowestThreadPriority + 1;
|
||||
|
||||
explicit KThread(KernelCore& kernel);
|
||||
~KThread() override;
|
||||
|
||||
public:
|
||||
using ThreadContext32 = Core::ARM_Interface::ThreadContext32;
|
||||
using ThreadContext64 = Core::ARM_Interface::ThreadContext64;
|
||||
using WaiterList = boost::intrusive::list<KThread>;
|
||||
|
||||
/**
|
||||
* Creates and returns a new thread. The new thread is immediately scheduled
|
||||
* @param system The instance of the whole system
|
||||
* @param name The friendly name desired for the thread
|
||||
* @param entry_point The address at which the thread should start execution
|
||||
* @param priority The thread's priority
|
||||
* @param arg User data to pass to the thread
|
||||
* @param processor_id The ID(s) of the processors on which the thread is desired to be run
|
||||
* @param stack_top The address of the thread's stack top
|
||||
* @param owner_process The parent process for the thread, if null, it's a kernel thread
|
||||
* @return A shared pointer to the newly created thread
|
||||
*/
|
||||
[[nodiscard]] static ResultVal<std::shared_ptr<KThread>> Create(
|
||||
Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point,
|
||||
u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process);
|
||||
|
||||
/**
|
||||
* Creates and returns a new thread. The new thread is immediately scheduled
|
||||
* @param system The instance of the whole system
|
||||
* @param name The friendly name desired for the thread
|
||||
* @param entry_point The address at which the thread should start execution
|
||||
* @param priority The thread's priority
|
||||
* @param arg User data to pass to the thread
|
||||
* @param processor_id The ID(s) of the processors on which the thread is desired to be run
|
||||
* @param stack_top The address of the thread's stack top
|
||||
* @param owner_process The parent process for the thread, if null, it's a kernel thread
|
||||
* @param thread_start_func The function where the host context will start.
|
||||
* @param thread_start_parameter The parameter which will passed to host context on init
|
||||
* @return A shared pointer to the newly created thread
|
||||
*/
|
||||
[[nodiscard]] static ResultVal<std::shared_ptr<KThread>> Create(
|
||||
Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point,
|
||||
u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process,
|
||||
std::function<void(void*)>&& thread_start_func, void* thread_start_parameter);
|
||||
|
||||
[[nodiscard]] std::string GetName() const override {
|
||||
return name;
|
||||
}
|
||||
|
||||
void SetName(std::string new_name) {
|
||||
name = std::move(new_name);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string GetTypeName() const override {
|
||||
return "Thread";
|
||||
}
|
||||
|
||||
static constexpr HandleType HANDLE_TYPE = HandleType::Thread;
|
||||
[[nodiscard]] HandleType GetHandleType() const override {
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the thread's current priority
|
||||
* @return The current thread's priority
|
||||
*/
|
||||
[[nodiscard]] s32 GetPriority() const {
|
||||
return priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the thread's current priority.
|
||||
* @param priority The new priority.
|
||||
*/
|
||||
void SetPriority(s32 value) {
|
||||
priority = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the thread's nominal priority.
|
||||
* @return The current thread's nominal priority.
|
||||
*/
|
||||
[[nodiscard]] s32 GetBasePriority() const {
|
||||
return base_priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the thread's thread ID
|
||||
* @return The thread's ID
|
||||
*/
|
||||
[[nodiscard]] u64 GetThreadID() const {
|
||||
return thread_id;
|
||||
}
|
||||
|
||||
void ContinueIfHasKernelWaiters() {
|
||||
if (GetNumKernelWaiters() > 0) {
|
||||
Continue();
|
||||
}
|
||||
}
|
||||
|
||||
void Wakeup();
|
||||
|
||||
void SetBasePriority(s32 value);
|
||||
|
||||
[[nodiscard]] ResultCode Run();
|
||||
|
||||
void Exit();
|
||||
|
||||
[[nodiscard]] u32 GetSuspendFlags() const {
|
||||
return suspend_allowed_flags & suspend_request_flags;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsSuspended() const {
|
||||
return GetSuspendFlags() != 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsSuspendRequested(SuspendType type) const {
|
||||
return (suspend_request_flags &
|
||||
(1u << (static_cast<u32>(ThreadState::SuspendShift) + static_cast<u32>(type)))) !=
|
||||
0;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsSuspendRequested() const {
|
||||
return suspend_request_flags != 0;
|
||||
}
|
||||
|
||||
void RequestSuspend(SuspendType type);
|
||||
|
||||
void Resume(SuspendType type);
|
||||
|
||||
void TrySuspend();
|
||||
|
||||
void Continue();
|
||||
|
||||
void Suspend();
|
||||
|
||||
void Finalize() override;
|
||||
|
||||
bool IsSignaled() const override;
|
||||
|
||||
void SetSyncedObject(KSynchronizationObject* obj, ResultCode wait_res) {
|
||||
synced_object = obj;
|
||||
wait_result = wait_res;
|
||||
}
|
||||
|
||||
[[nodiscard]] ResultCode GetWaitResult(KSynchronizationObject** out) const {
|
||||
*out = synced_object;
|
||||
return wait_result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the Thread Local Storage address of the current thread
|
||||
* @returns VAddr of the thread's TLS
|
||||
*/
|
||||
[[nodiscard]] VAddr GetTLSAddress() const {
|
||||
return tls_address;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the value of the TPIDR_EL0 Read/Write system register for this thread.
|
||||
* @returns The value of the TPIDR_EL0 register.
|
||||
*/
|
||||
[[nodiscard]] u64 GetTPIDR_EL0() const {
|
||||
return thread_context_64.tpidr;
|
||||
}
|
||||
|
||||
/// Sets the value of the TPIDR_EL0 Read/Write system register for this thread.
|
||||
void SetTPIDR_EL0(u64 value) {
|
||||
thread_context_64.tpidr = value;
|
||||
thread_context_32.tpidr = static_cast<u32>(value);
|
||||
}
|
||||
|
||||
[[nodiscard]] ThreadContext32& GetContext32() {
|
||||
return thread_context_32;
|
||||
}
|
||||
|
||||
[[nodiscard]] const ThreadContext32& GetContext32() const {
|
||||
return thread_context_32;
|
||||
}
|
||||
|
||||
[[nodiscard]] ThreadContext64& GetContext64() {
|
||||
return thread_context_64;
|
||||
}
|
||||
|
||||
[[nodiscard]] const ThreadContext64& GetContext64() const {
|
||||
return thread_context_64;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::shared_ptr<Common::Fiber>& GetHostContext();
|
||||
|
||||
[[nodiscard]] ThreadState GetState() const {
|
||||
return thread_state & ThreadState::Mask;
|
||||
}
|
||||
|
||||
[[nodiscard]] ThreadState GetRawState() const {
|
||||
return thread_state;
|
||||
}
|
||||
|
||||
void SetState(ThreadState state);
|
||||
|
||||
[[nodiscard]] s64 GetLastScheduledTick() const {
|
||||
return last_scheduled_tick;
|
||||
}
|
||||
|
||||
void SetLastScheduledTick(s64 tick) {
|
||||
last_scheduled_tick = tick;
|
||||
}
|
||||
|
||||
void AddCpuTime([[maybe_unused]] s32 core_id_, s64 amount) {
|
||||
cpu_time += amount;
|
||||
// TODO(bunnei): Debug kernels track per-core tick counts. Should we?
|
||||
}
|
||||
|
||||
[[nodiscard]] s64 GetCpuTime() const {
|
||||
return cpu_time;
|
||||
}
|
||||
|
||||
[[nodiscard]] s32 GetActiveCore() const {
|
||||
return core_id;
|
||||
}
|
||||
|
||||
void SetActiveCore(s32 core) {
|
||||
core_id = core;
|
||||
}
|
||||
|
||||
[[nodiscard]] s32 GetCurrentCore() const {
|
||||
return current_core_id;
|
||||
}
|
||||
|
||||
void SetCurrentCore(s32 core) {
|
||||
current_core_id = core;
|
||||
}
|
||||
|
||||
[[nodiscard]] Process* GetOwnerProcess() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
[[nodiscard]] const Process* GetOwnerProcess() const {
|
||||
return parent;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsUserThread() const {
|
||||
return parent != nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] KThread* GetLockOwner() const {
|
||||
return lock_owner;
|
||||
}
|
||||
|
||||
void SetLockOwner(KThread* owner) {
|
||||
lock_owner = owner;
|
||||
}
|
||||
|
||||
[[nodiscard]] const KAffinityMask& GetAffinityMask() const {
|
||||
return physical_affinity_mask;
|
||||
}
|
||||
|
||||
[[nodiscard]] ResultCode GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask);
|
||||
|
||||
[[nodiscard]] ResultCode GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask);
|
||||
|
||||
[[nodiscard]] ResultCode SetCoreMask(s32 core_id, u64 v_affinity_mask);
|
||||
|
||||
[[nodiscard]] ResultCode SetActivity(Svc::ThreadActivity activity);
|
||||
|
||||
[[nodiscard]] ResultCode Sleep(s64 timeout);
|
||||
|
||||
[[nodiscard]] s64 GetYieldScheduleCount() const {
|
||||
return schedule_count;
|
||||
}
|
||||
|
||||
void SetYieldScheduleCount(s64 count) {
|
||||
schedule_count = count;
|
||||
}
|
||||
|
||||
void WaitCancel();
|
||||
|
||||
[[nodiscard]] bool IsWaitCancelled() const {
|
||||
return wait_cancelled;
|
||||
}
|
||||
|
||||
[[nodiscard]] void ClearWaitCancelled() {
|
||||
wait_cancelled = false;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsCancellable() const {
|
||||
return cancellable;
|
||||
}
|
||||
|
||||
void SetCancellable() {
|
||||
cancellable = true;
|
||||
}
|
||||
|
||||
void ClearCancellable() {
|
||||
cancellable = false;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsTerminationRequested() const {
|
||||
return termination_requested || GetRawState() == ThreadState::Terminated;
|
||||
}
|
||||
|
||||
struct StackParameters {
|
||||
u8 svc_permission[0x10];
|
||||
std::atomic<u8> dpc_flags;
|
||||
u8 current_svc_id;
|
||||
bool is_calling_svc;
|
||||
bool is_in_exception_handler;
|
||||
bool is_pinned;
|
||||
s32 disable_count;
|
||||
KThread* cur_thread;
|
||||
};
|
||||
|
||||
[[nodiscard]] StackParameters& GetStackParameters() {
|
||||
return stack_parameters;
|
||||
}
|
||||
|
||||
[[nodiscard]] const StackParameters& GetStackParameters() const {
|
||||
return stack_parameters;
|
||||
}
|
||||
|
||||
class QueueEntry {
|
||||
public:
|
||||
constexpr QueueEntry() = default;
|
||||
|
||||
constexpr void Initialize() {
|
||||
prev = nullptr;
|
||||
next = nullptr;
|
||||
}
|
||||
|
||||
constexpr KThread* GetPrev() const {
|
||||
return prev;
|
||||
}
|
||||
constexpr KThread* GetNext() const {
|
||||
return next;
|
||||
}
|
||||
constexpr void SetPrev(KThread* thread) {
|
||||
prev = thread;
|
||||
}
|
||||
constexpr void SetNext(KThread* thread) {
|
||||
next = thread;
|
||||
}
|
||||
|
||||
private:
|
||||
KThread* prev{};
|
||||
KThread* next{};
|
||||
};
|
||||
|
||||
[[nodiscard]] QueueEntry& GetPriorityQueueEntry(s32 core) {
|
||||
return per_core_priority_queue_entry[core];
|
||||
}
|
||||
|
||||
[[nodiscard]] const QueueEntry& GetPriorityQueueEntry(s32 core) const {
|
||||
return per_core_priority_queue_entry[core];
|
||||
}
|
||||
|
||||
void SetSleepingQueue(KThreadQueue* q) {
|
||||
sleeping_queue = q;
|
||||
}
|
||||
|
||||
[[nodiscard]] s32 GetDisableDispatchCount() const {
|
||||
return this->GetStackParameters().disable_count;
|
||||
}
|
||||
|
||||
void DisableDispatch() {
|
||||
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0);
|
||||
this->GetStackParameters().disable_count++;
|
||||
}
|
||||
|
||||
void EnableDispatch() {
|
||||
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0);
|
||||
this->GetStackParameters().disable_count--;
|
||||
}
|
||||
|
||||
void Pin();
|
||||
|
||||
void Unpin();
|
||||
|
||||
void SetInExceptionHandler() {
|
||||
this->GetStackParameters().is_in_exception_handler = true;
|
||||
}
|
||||
|
||||
void ClearInExceptionHandler() {
|
||||
this->GetStackParameters().is_in_exception_handler = false;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsInExceptionHandler() const {
|
||||
return this->GetStackParameters().is_in_exception_handler;
|
||||
}
|
||||
|
||||
void SetIsCallingSvc() {
|
||||
this->GetStackParameters().is_calling_svc = true;
|
||||
}
|
||||
|
||||
void ClearIsCallingSvc() {
|
||||
this->GetStackParameters().is_calling_svc = false;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsCallingSvc() const {
|
||||
return this->GetStackParameters().is_calling_svc;
|
||||
}
|
||||
|
||||
[[nodiscard]] u8 GetSvcId() const {
|
||||
return this->GetStackParameters().current_svc_id;
|
||||
}
|
||||
|
||||
void RegisterDpc(DpcFlag flag) {
|
||||
this->GetStackParameters().dpc_flags |= static_cast<u8>(flag);
|
||||
}
|
||||
|
||||
void ClearDpc(DpcFlag flag) {
|
||||
this->GetStackParameters().dpc_flags &= ~static_cast<u8>(flag);
|
||||
}
|
||||
|
||||
[[nodiscard]] u8 GetDpc() const {
|
||||
return this->GetStackParameters().dpc_flags;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool HasDpc() const {
|
||||
return this->GetDpc() != 0;
|
||||
}
|
||||
|
||||
void SetWaitReasonForDebugging(ThreadWaitReasonForDebugging reason) {
|
||||
wait_reason_for_debugging = reason;
|
||||
}
|
||||
|
||||
[[nodiscard]] ThreadWaitReasonForDebugging GetWaitReasonForDebugging() const {
|
||||
return wait_reason_for_debugging;
|
||||
}
|
||||
|
||||
[[nodiscard]] ThreadType GetThreadTypeForDebugging() const {
|
||||
return thread_type_for_debugging;
|
||||
}
|
||||
|
||||
void SetWaitObjectsForDebugging(const std::span<KSynchronizationObject*>& objects) {
|
||||
wait_objects_for_debugging.clear();
|
||||
wait_objects_for_debugging.reserve(objects.size());
|
||||
for (const auto& object : objects) {
|
||||
wait_objects_for_debugging.emplace_back(object);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<KSynchronizationObject*>& GetWaitObjectsForDebugging() const {
|
||||
return wait_objects_for_debugging;
|
||||
}
|
||||
|
||||
void SetMutexWaitAddressForDebugging(VAddr address) {
|
||||
mutex_wait_address_for_debugging = address;
|
||||
}
|
||||
|
||||
[[nodiscard]] VAddr GetMutexWaitAddressForDebugging() const {
|
||||
return mutex_wait_address_for_debugging;
|
||||
}
|
||||
|
||||
[[nodiscard]] s32 GetIdealCoreForDebugging() const {
|
||||
return virtual_ideal_core_id;
|
||||
}
|
||||
|
||||
void AddWaiter(KThread* thread);
|
||||
|
||||
void RemoveWaiter(KThread* thread);
|
||||
|
||||
[[nodiscard]] ResultCode GetThreadContext3(std::vector<u8>& out);
|
||||
|
||||
[[nodiscard]] KThread* RemoveWaiterByKey(s32* out_num_waiters, VAddr key);
|
||||
|
||||
[[nodiscard]] VAddr GetAddressKey() const {
|
||||
return address_key;
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 GetAddressKeyValue() const {
|
||||
return address_key_value;
|
||||
}
|
||||
|
||||
void SetAddressKey(VAddr key) {
|
||||
address_key = key;
|
||||
}
|
||||
|
||||
void SetAddressKey(VAddr key, u32 val) {
|
||||
address_key = key;
|
||||
address_key_value = val;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool HasWaiters() const {
|
||||
return !waiter_list.empty();
|
||||
}
|
||||
|
||||
[[nodiscard]] s32 GetNumKernelWaiters() const {
|
||||
return num_kernel_waiters;
|
||||
}
|
||||
|
||||
[[nodiscard]] u64 GetConditionVariableKey() const {
|
||||
return condvar_key;
|
||||
}
|
||||
|
||||
[[nodiscard]] u64 GetAddressArbiterKey() const {
|
||||
return condvar_key;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr size_t PriorityInheritanceCountMax = 10;
|
||||
union SyncObjectBuffer {
|
||||
std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> sync_objects{};
|
||||
std::array<Handle,
|
||||
Svc::ArgumentHandleCountMax*(sizeof(KSynchronizationObject*) / sizeof(Handle))>
|
||||
handles;
|
||||
constexpr SyncObjectBuffer() {}
|
||||
};
|
||||
static_assert(sizeof(SyncObjectBuffer::sync_objects) == sizeof(SyncObjectBuffer::handles));
|
||||
|
||||
struct ConditionVariableComparator {
|
||||
struct LightCompareType {
|
||||
u64 cv_key{};
|
||||
s32 priority{};
|
||||
|
||||
[[nodiscard]] constexpr u64 GetConditionVariableKey() const {
|
||||
return cv_key;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr s32 GetPriority() const {
|
||||
return priority;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
requires(
|
||||
std::same_as<T, KThread> ||
|
||||
std::same_as<T, LightCompareType>) static constexpr int Compare(const T& lhs,
|
||||
const KThread& rhs) {
|
||||
const u64 l_key = lhs.GetConditionVariableKey();
|
||||
const u64 r_key = rhs.GetConditionVariableKey();
|
||||
|
||||
if (l_key < r_key) {
|
||||
// Sort first by key
|
||||
return -1;
|
||||
} else if (l_key == r_key && lhs.GetPriority() < rhs.GetPriority()) {
|
||||
// And then by priority.
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void AddWaiterImpl(KThread* thread);
|
||||
|
||||
void RemoveWaiterImpl(KThread* thread);
|
||||
|
||||
void StartTermination();
|
||||
|
||||
[[nodiscard]] ResultCode Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top,
|
||||
s32 prio, s32 virt_core, Process* owner, ThreadType type);
|
||||
|
||||
[[nodiscard]] static ResultCode InitializeThread(KThread* thread, KThreadFunction func,
|
||||
uintptr_t arg, VAddr user_stack_top, s32 prio,
|
||||
s32 core, Process* owner, ThreadType type);
|
||||
|
||||
static void RestorePriority(KernelCore& kernel, KThread* thread);
|
||||
|
||||
// For core KThread implementation
|
||||
ThreadContext32 thread_context_32{};
|
||||
ThreadContext64 thread_context_64{};
|
||||
Common::IntrusiveRedBlackTreeNode condvar_arbiter_tree_node{};
|
||||
s32 priority{};
|
||||
using ConditionVariableThreadTreeTraits =
|
||||
Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<
|
||||
&KThread::condvar_arbiter_tree_node>;
|
||||
using ConditionVariableThreadTree =
|
||||
ConditionVariableThreadTreeTraits::TreeType<ConditionVariableComparator>;
|
||||
ConditionVariableThreadTree* condvar_tree{};
|
||||
u64 condvar_key{};
|
||||
u64 virtual_affinity_mask{};
|
||||
KAffinityMask physical_affinity_mask{};
|
||||
u64 thread_id{};
|
||||
std::atomic<s64> cpu_time{};
|
||||
KSynchronizationObject* synced_object{};
|
||||
VAddr address_key{};
|
||||
Process* parent{};
|
||||
VAddr kernel_stack_top{};
|
||||
u32* light_ipc_data{};
|
||||
VAddr tls_address{};
|
||||
KLightLock activity_pause_lock;
|
||||
s64 schedule_count{};
|
||||
s64 last_scheduled_tick{};
|
||||
std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> per_core_priority_queue_entry{};
|
||||
KThreadQueue* sleeping_queue{};
|
||||
WaiterList waiter_list{};
|
||||
WaiterList pinned_waiter_list{};
|
||||
KThread* lock_owner{};
|
||||
u32 address_key_value{};
|
||||
u32 suspend_request_flags{};
|
||||
u32 suspend_allowed_flags{};
|
||||
ResultCode wait_result{RESULT_SUCCESS};
|
||||
s32 base_priority{};
|
||||
s32 physical_ideal_core_id{};
|
||||
s32 virtual_ideal_core_id{};
|
||||
s32 num_kernel_waiters{};
|
||||
s32 current_core_id{};
|
||||
s32 core_id{};
|
||||
KAffinityMask original_physical_affinity_mask{};
|
||||
s32 original_physical_ideal_core_id{};
|
||||
s32 num_core_migration_disables{};
|
||||
ThreadState thread_state{};
|
||||
std::atomic<bool> termination_requested{};
|
||||
bool wait_cancelled{};
|
||||
bool cancellable{};
|
||||
bool signaled{};
|
||||
bool initialized{};
|
||||
bool debug_attached{};
|
||||
s8 priority_inheritance_count{};
|
||||
bool resource_limit_release_hint{};
|
||||
StackParameters stack_parameters{};
|
||||
Common::SpinLock context_guard{};
|
||||
|
||||
// For emulation
|
||||
std::shared_ptr<Common::Fiber> host_context{};
|
||||
|
||||
// For debugging
|
||||
std::vector<KSynchronizationObject*> wait_objects_for_debugging;
|
||||
VAddr mutex_wait_address_for_debugging{};
|
||||
ThreadWaitReasonForDebugging wait_reason_for_debugging{};
|
||||
ThreadType thread_type_for_debugging{};
|
||||
std::string name;
|
||||
|
||||
public:
|
||||
using ConditionVariableThreadTreeType = ConditionVariableThreadTree;
|
||||
|
||||
void SetConditionVariable(ConditionVariableThreadTree* tree, VAddr address, u64 cv_key,
|
||||
u32 value) {
|
||||
condvar_tree = tree;
|
||||
condvar_key = cv_key;
|
||||
address_key = address;
|
||||
address_key_value = value;
|
||||
}
|
||||
|
||||
void ClearConditionVariable() {
|
||||
condvar_tree = nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsWaitingForConditionVariable() const {
|
||||
return condvar_tree != nullptr;
|
||||
}
|
||||
|
||||
void SetAddressArbiter(ConditionVariableThreadTree* tree, u64 address) {
|
||||
condvar_tree = tree;
|
||||
condvar_key = address;
|
||||
}
|
||||
|
||||
void ClearAddressArbiter() {
|
||||
condvar_tree = nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsWaitingForAddressArbiter() const {
|
||||
return condvar_tree != nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] ConditionVariableThreadTree* GetConditionVariableTree() const {
|
||||
return condvar_tree;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
81
src/core/hle/kernel/k_thread_queue.h
Normal file
81
src/core/hle/kernel/k_thread_queue.h
Normal file
@@ -0,0 +1,81 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KThreadQueue {
|
||||
public:
|
||||
explicit KThreadQueue(KernelCore& kernel) : kernel{kernel} {}
|
||||
|
||||
bool IsEmpty() const {
|
||||
return wait_list.empty();
|
||||
}
|
||||
|
||||
KThread::WaiterList::iterator begin() {
|
||||
return wait_list.begin();
|
||||
}
|
||||
KThread::WaiterList::iterator end() {
|
||||
return wait_list.end();
|
||||
}
|
||||
|
||||
bool SleepThread(KThread* t) {
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
// If the thread needs terminating, don't enqueue it.
|
||||
if (t->IsTerminationRequested()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set the thread's queue and mark it as waiting.
|
||||
t->SetSleepingQueue(this);
|
||||
t->SetState(ThreadState::Waiting);
|
||||
|
||||
// Add the thread to the queue.
|
||||
wait_list.push_back(*t);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WakeupThread(KThread* t) {
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
// Remove the thread from the queue.
|
||||
wait_list.erase(wait_list.iterator_to(*t));
|
||||
|
||||
// Mark the thread as no longer sleeping.
|
||||
t->SetState(ThreadState::Runnable);
|
||||
t->SetSleepingQueue(nullptr);
|
||||
}
|
||||
|
||||
KThread* WakeupFrontThread() {
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
if (wait_list.empty()) {
|
||||
return nullptr;
|
||||
} else {
|
||||
// Remove the thread from the queue.
|
||||
auto it = wait_list.begin();
|
||||
KThread* thread = std::addressof(*it);
|
||||
wait_list.erase(it);
|
||||
|
||||
ASSERT(thread->GetState() == ThreadState::Waiting);
|
||||
|
||||
// Mark the thread as no longer sleeping.
|
||||
thread->SetState(ThreadState::Runnable);
|
||||
thread->SetSleepingQueue(nullptr);
|
||||
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
KernelCore& kernel;
|
||||
KThread::WaiterList wait_list{};
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/memory/memory_layout.h"
|
||||
#include "core/hle/kernel/memory/memory_manager.h"
|
||||
@@ -38,7 +39,6 @@
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
#include "core/hle/kernel/service_thread.h"
|
||||
#include "core/hle/kernel/shared_memory.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/time_manager.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/hle/result.h"
|
||||
@@ -57,11 +57,13 @@ struct KernelCore::Impl {
|
||||
}
|
||||
|
||||
void Initialize(KernelCore& kernel) {
|
||||
global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
|
||||
|
||||
RegisterHostThread();
|
||||
|
||||
global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
|
||||
service_thread_manager =
|
||||
std::make_unique<Common::ThreadWorker>(1, "yuzu:ServiceThreadManager");
|
||||
is_phantom_mode_for_singlecore = false;
|
||||
|
||||
InitializePhysicalCores();
|
||||
InitializeSystemResourceLimit(kernel);
|
||||
@@ -116,14 +118,14 @@ struct KernelCore::Impl {
|
||||
void InitializePhysicalCores() {
|
||||
exclusive_monitor =
|
||||
Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES);
|
||||
for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
|
||||
for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
|
||||
schedulers[i] = std::make_unique<Kernel::KScheduler>(system, i);
|
||||
cores.emplace_back(i, system, *schedulers[i], interrupts);
|
||||
}
|
||||
}
|
||||
|
||||
void InitializeSchedulers() {
|
||||
for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
|
||||
for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
|
||||
cores[i].Scheduler().Initialize();
|
||||
}
|
||||
}
|
||||
@@ -168,11 +170,9 @@ struct KernelCore::Impl {
|
||||
std::string name = "Suspend Thread Id:" + std::to_string(i);
|
||||
std::function<void(void*)> init_func = Core::CpuManager::GetSuspendThreadStartFunc();
|
||||
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
|
||||
const auto type =
|
||||
static_cast<ThreadType>(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_SUSPEND);
|
||||
auto thread_res =
|
||||
Thread::Create(system, type, std::move(name), 0, 0, 0, static_cast<u32>(i), 0,
|
||||
nullptr, std::move(init_func), init_func_parameter);
|
||||
auto thread_res = KThread::Create(system, ThreadType::HighPriority, std::move(name), 0,
|
||||
0, 0, static_cast<u32>(i), 0, nullptr,
|
||||
std::move(init_func), init_func_parameter);
|
||||
|
||||
suspend_threads[i] = std::move(thread_res).Unwrap();
|
||||
}
|
||||
@@ -207,6 +207,17 @@ struct KernelCore::Impl {
|
||||
return host_thread_id;
|
||||
}
|
||||
|
||||
// Gets the dummy KThread for the caller, allocating a new one if this is the first time
|
||||
KThread* GetHostDummyThread() {
|
||||
const thread_local auto thread =
|
||||
KThread::Create(
|
||||
system, ThreadType::Main, fmt::format("DummyThread:{}", GetHostThreadId()), 0,
|
||||
KThread::DefaultThreadPriority, 0, static_cast<u32>(3), 0, nullptr,
|
||||
[]([[maybe_unused]] void* arg) { UNREACHABLE(); }, nullptr)
|
||||
.Unwrap();
|
||||
return thread.get();
|
||||
}
|
||||
|
||||
/// Registers a CPU core thread by allocating a host thread ID for it
|
||||
void RegisterCoreThread(std::size_t core_id) {
|
||||
ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
|
||||
@@ -219,6 +230,7 @@ struct KernelCore::Impl {
|
||||
/// Registers a new host thread by allocating a host thread ID for it
|
||||
void RegisterHostThread() {
|
||||
[[maybe_unused]] const auto this_id = GetHostThreadId();
|
||||
[[maybe_unused]] const auto dummy_thread = GetHostDummyThread();
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 GetCurrentHostThreadID() {
|
||||
@@ -229,20 +241,21 @@ struct KernelCore::Impl {
|
||||
return this_id;
|
||||
}
|
||||
|
||||
[[nodiscard]] Core::EmuThreadHandle GetCurrentEmuThreadID() {
|
||||
Core::EmuThreadHandle result = Core::EmuThreadHandle::InvalidHandle();
|
||||
result.host_handle = GetCurrentHostThreadID();
|
||||
if (result.host_handle >= Core::Hardware::NUM_CPU_CORES) {
|
||||
return result;
|
||||
bool IsPhantomModeForSingleCore() const {
|
||||
return is_phantom_mode_for_singlecore;
|
||||
}
|
||||
|
||||
void SetIsPhantomModeForSingleCore(bool value) {
|
||||
ASSERT(!is_multicore);
|
||||
is_phantom_mode_for_singlecore = value;
|
||||
}
|
||||
|
||||
KThread* GetCurrentEmuThread() {
|
||||
const auto thread_id = GetCurrentHostThreadID();
|
||||
if (thread_id >= Core::Hardware::NUM_CPU_CORES) {
|
||||
return GetHostDummyThread();
|
||||
}
|
||||
const Kernel::KScheduler& sched = cores[result.host_handle].Scheduler();
|
||||
const Kernel::Thread* current = sched.GetCurrentThread();
|
||||
if (current != nullptr && !current->IsPhantomMode()) {
|
||||
result.guest_handle = current->GetGlobalHandle();
|
||||
} else {
|
||||
result.guest_handle = InvalidHandle;
|
||||
}
|
||||
return result;
|
||||
return schedulers[thread_id]->GetCurrentThread();
|
||||
}
|
||||
|
||||
void InitializeMemoryLayout() {
|
||||
@@ -342,11 +355,12 @@ struct KernelCore::Impl {
|
||||
// the release of itself
|
||||
std::unique_ptr<Common::ThreadWorker> service_thread_manager;
|
||||
|
||||
std::array<std::shared_ptr<Thread>, Core::Hardware::NUM_CPU_CORES> suspend_threads{};
|
||||
std::array<std::shared_ptr<KThread>, Core::Hardware::NUM_CPU_CORES> suspend_threads{};
|
||||
std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};
|
||||
std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
|
||||
|
||||
bool is_multicore{};
|
||||
bool is_phantom_mode_for_singlecore{};
|
||||
u32 single_core_thread_id{};
|
||||
|
||||
std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{};
|
||||
@@ -380,8 +394,8 @@ std::shared_ptr<ResourceLimit> KernelCore::GetSystemResourceLimit() const {
|
||||
return impl->system_resource_limit;
|
||||
}
|
||||
|
||||
std::shared_ptr<Thread> KernelCore::RetrieveThreadFromGlobalHandleTable(Handle handle) const {
|
||||
return impl->global_handle_table.Get<Thread>(handle);
|
||||
std::shared_ptr<KThread> KernelCore::RetrieveThreadFromGlobalHandleTable(Handle handle) const {
|
||||
return impl->global_handle_table.Get<KThread>(handle);
|
||||
}
|
||||
|
||||
void KernelCore::AppendNewProcess(std::shared_ptr<Process> process) {
|
||||
@@ -546,8 +560,8 @@ u32 KernelCore::GetCurrentHostThreadID() const {
|
||||
return impl->GetCurrentHostThreadID();
|
||||
}
|
||||
|
||||
Core::EmuThreadHandle KernelCore::GetCurrentEmuThreadID() const {
|
||||
return impl->GetCurrentEmuThreadID();
|
||||
KThread* KernelCore::GetCurrentEmuThread() const {
|
||||
return impl->GetCurrentEmuThread();
|
||||
}
|
||||
|
||||
Memory::MemoryManager& KernelCore::MemoryManager() {
|
||||
@@ -645,4 +659,12 @@ void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> servi
|
||||
});
|
||||
}
|
||||
|
||||
bool KernelCore::IsPhantomModeForSingleCore() const {
|
||||
return impl->IsPhantomModeForSingleCore();
|
||||
}
|
||||
|
||||
void KernelCore::SetIsPhantomModeForSingleCore(bool value) {
|
||||
impl->SetIsPhantomModeForSingleCore(value);
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -43,9 +43,13 @@ class KScheduler;
|
||||
class SharedMemory;
|
||||
class ServiceThread;
|
||||
class Synchronization;
|
||||
class Thread;
|
||||
class KThread;
|
||||
class TimeManager;
|
||||
|
||||
using EmuThreadHandle = uintptr_t;
|
||||
constexpr EmuThreadHandle EmuThreadHandleInvalid{};
|
||||
constexpr EmuThreadHandle EmuThreadHandleReserved{1ULL << 63};
|
||||
|
||||
/// Represents a single instance of the kernel.
|
||||
class KernelCore {
|
||||
private:
|
||||
@@ -84,7 +88,7 @@ public:
|
||||
std::shared_ptr<ResourceLimit> GetSystemResourceLimit() const;
|
||||
|
||||
/// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table.
|
||||
std::shared_ptr<Thread> RetrieveThreadFromGlobalHandleTable(Handle handle) const;
|
||||
std::shared_ptr<KThread> RetrieveThreadFromGlobalHandleTable(Handle handle) const;
|
||||
|
||||
/// Adds the given shared pointer to an internal list of active processes.
|
||||
void AppendNewProcess(std::shared_ptr<Process> process);
|
||||
@@ -161,8 +165,8 @@ public:
|
||||
/// Determines whether or not the given port is a valid named port.
|
||||
bool IsValidNamedPort(NamedPortTable::const_iterator port) const;
|
||||
|
||||
/// Gets the current host_thread/guest_thread handle.
|
||||
Core::EmuThreadHandle GetCurrentEmuThreadID() const;
|
||||
/// Gets the current host_thread/guest_thread pointer.
|
||||
KThread* GetCurrentEmuThread() const;
|
||||
|
||||
/// Gets the current host_thread handle.
|
||||
u32 GetCurrentHostThreadID() const;
|
||||
@@ -237,10 +241,14 @@ public:
|
||||
*/
|
||||
void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread);
|
||||
|
||||
/// Workaround for single-core mode when preempting threads while idle.
|
||||
bool IsPhantomModeForSingleCore() const;
|
||||
void SetIsPhantomModeForSingleCore(bool value);
|
||||
|
||||
private:
|
||||
friend class Object;
|
||||
friend class Process;
|
||||
friend class Thread;
|
||||
friend class KThread;
|
||||
|
||||
/// Creates a new object ID, incrementing the internal object ID counter.
|
||||
u32 CreateNewObjectID();
|
||||
|
||||
@@ -61,6 +61,8 @@ public:
|
||||
*/
|
||||
bool IsWaitable() const;
|
||||
|
||||
virtual void Finalize() = 0;
|
||||
|
||||
protected:
|
||||
/// The kernel instance this object was created under.
|
||||
KernelCore& kernel;
|
||||
|
||||
@@ -16,13 +16,13 @@
|
||||
#include "core/hle/kernel/code_set.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/memory/memory_block_manager.h"
|
||||
#include "core/hle/kernel/memory/page_table.h"
|
||||
#include "core/hle/kernel/memory/slab_heap.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/settings.h"
|
||||
@@ -38,11 +38,10 @@ namespace {
|
||||
*/
|
||||
void SetupMainThread(Core::System& system, Process& owner_process, u32 priority, VAddr stack_top) {
|
||||
const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart();
|
||||
ThreadType type = THREADTYPE_USER;
|
||||
auto thread_res = Thread::Create(system, type, "main", entry_point, priority, 0,
|
||||
owner_process.GetIdealCore(), stack_top, &owner_process);
|
||||
auto thread_res = KThread::Create(system, ThreadType::User, "main", entry_point, priority, 0,
|
||||
owner_process.GetIdealCoreId(), stack_top, &owner_process);
|
||||
|
||||
std::shared_ptr<Thread> thread = std::move(thread_res).Unwrap();
|
||||
std::shared_ptr<KThread> thread = std::move(thread_res).Unwrap();
|
||||
|
||||
// Register 1 must be a handle to the main thread
|
||||
const Handle thread_handle = owner_process.GetHandleTable().Create(thread).Unwrap();
|
||||
@@ -137,6 +136,23 @@ std::shared_ptr<ResourceLimit> Process::GetResourceLimit() const {
|
||||
return resource_limit;
|
||||
}
|
||||
|
||||
void Process::IncrementThreadCount() {
|
||||
ASSERT(num_threads >= 0);
|
||||
num_created_threads++;
|
||||
|
||||
if (const auto count = ++num_threads; count > peak_num_threads) {
|
||||
peak_num_threads = count;
|
||||
}
|
||||
}
|
||||
|
||||
void Process::DecrementThreadCount() {
|
||||
ASSERT(num_threads > 0);
|
||||
|
||||
if (const auto count = --num_threads; count == 0) {
|
||||
UNIMPLEMENTED_MSG("Process termination is not implemented!");
|
||||
}
|
||||
}
|
||||
|
||||
u64 Process::GetTotalPhysicalMemoryAvailable() const {
|
||||
const u64 capacity{resource_limit->GetCurrentResourceValue(ResourceType::PhysicalMemory) +
|
||||
page_table->GetTotalHeapSize() + GetSystemResourceSize() + image_size +
|
||||
@@ -162,11 +178,66 @@ u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const {
|
||||
return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage();
|
||||
}
|
||||
|
||||
void Process::RegisterThread(const Thread* thread) {
|
||||
bool Process::ReleaseUserException(KThread* thread) {
|
||||
KScopedSchedulerLock sl{kernel};
|
||||
|
||||
if (exception_thread == thread) {
|
||||
exception_thread = nullptr;
|
||||
|
||||
// Remove waiter thread.
|
||||
s32 num_waiters{};
|
||||
KThread* next = thread->RemoveWaiterByKey(
|
||||
std::addressof(num_waiters),
|
||||
reinterpret_cast<uintptr_t>(std::addressof(exception_thread)));
|
||||
if (next != nullptr) {
|
||||
if (next->GetState() == ThreadState::Waiting) {
|
||||
next->SetState(ThreadState::Runnable);
|
||||
} else {
|
||||
KScheduler::SetSchedulerUpdateNeeded(kernel);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Process::PinCurrentThread() {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
|
||||
// Get the current thread.
|
||||
const s32 core_id = GetCurrentCoreId(kernel);
|
||||
KThread* cur_thread = GetCurrentThreadPointer(kernel);
|
||||
|
||||
// Pin it.
|
||||
PinThread(core_id, cur_thread);
|
||||
cur_thread->Pin();
|
||||
|
||||
// An update is needed.
|
||||
KScheduler::SetSchedulerUpdateNeeded(kernel);
|
||||
}
|
||||
|
||||
void Process::UnpinCurrentThread() {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
|
||||
// Get the current thread.
|
||||
const s32 core_id = GetCurrentCoreId(kernel);
|
||||
KThread* cur_thread = GetCurrentThreadPointer(kernel);
|
||||
|
||||
// Unpin it.
|
||||
cur_thread->Unpin();
|
||||
UnpinThread(core_id, cur_thread);
|
||||
|
||||
// An update is needed.
|
||||
KScheduler::SetSchedulerUpdateNeeded(kernel);
|
||||
}
|
||||
|
||||
void Process::RegisterThread(const KThread* thread) {
|
||||
thread_list.push_back(thread);
|
||||
}
|
||||
|
||||
void Process::UnregisterThread(const Thread* thread) {
|
||||
void Process::UnregisterThread(const KThread* thread) {
|
||||
thread_list.remove(thread);
|
||||
}
|
||||
|
||||
@@ -267,7 +338,7 @@ void Process::Run(s32 main_thread_priority, u64 stack_size) {
|
||||
void Process::PrepareForTermination() {
|
||||
ChangeStatus(ProcessStatus::Exiting);
|
||||
|
||||
const auto stop_threads = [this](const std::vector<std::shared_ptr<Thread>>& thread_list) {
|
||||
const auto stop_threads = [this](const std::vector<std::shared_ptr<KThread>>& thread_list) {
|
||||
for (auto& thread : thread_list) {
|
||||
if (thread->GetOwnerProcess() != this)
|
||||
continue;
|
||||
@@ -279,7 +350,7 @@ void Process::PrepareForTermination() {
|
||||
ASSERT_MSG(thread->GetState() == ThreadState::Waiting,
|
||||
"Exiting processes with non-waiting threads is currently unimplemented");
|
||||
|
||||
thread->Stop();
|
||||
thread->Exit();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -372,7 +443,7 @@ bool Process::IsSignaled() const {
|
||||
Process::Process(Core::System& system)
|
||||
: KSynchronizationObject{system.Kernel()},
|
||||
page_table{std::make_unique<Memory::PageTable>(system)}, handle_table{system.Kernel()},
|
||||
address_arbiter{system}, condition_var{system}, system{system} {}
|
||||
address_arbiter{system}, condition_var{system}, state_lock{system.Kernel()}, system{system} {}
|
||||
|
||||
Process::~Process() = default;
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Kernel {
|
||||
|
||||
class KernelCore;
|
||||
class ResourceLimit;
|
||||
class Thread;
|
||||
class KThread;
|
||||
class TLSPage;
|
||||
|
||||
struct CodeSet;
|
||||
@@ -173,10 +173,15 @@ public:
|
||||
std::shared_ptr<ResourceLimit> GetResourceLimit() const;
|
||||
|
||||
/// Gets the ideal CPU core ID for this process
|
||||
u8 GetIdealCore() const {
|
||||
u8 GetIdealCoreId() const {
|
||||
return 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 capabilities.GetCoreMask();
|
||||
@@ -212,6 +217,14 @@ public:
|
||||
return is_64bit_process;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsSuspended() const {
|
||||
return is_suspended;
|
||||
}
|
||||
|
||||
void SetSuspended(bool suspended) {
|
||||
is_suspended = suspended;
|
||||
}
|
||||
|
||||
/// Gets the total running time of the process instance in ticks.
|
||||
u64 GetCPUTimeTicks() const {
|
||||
return total_process_running_time_ticks;
|
||||
@@ -232,6 +245,33 @@ public:
|
||||
++schedule_count;
|
||||
}
|
||||
|
||||
void IncrementThreadCount();
|
||||
void DecrementThreadCount();
|
||||
|
||||
void SetRunningThread(s32 core, KThread* thread, u64 idle_count) {
|
||||
running_threads[core] = thread;
|
||||
running_thread_idle_counts[core] = idle_count;
|
||||
}
|
||||
|
||||
void ClearRunningThread(KThread* thread) {
|
||||
for (size_t i = 0; i < running_threads.size(); ++i) {
|
||||
if (running_threads[i] == thread) {
|
||||
running_threads[i] = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] KThread* GetRunningThread(s32 core) const {
|
||||
return 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 pinned_threads[core_id];
|
||||
}
|
||||
|
||||
/// Gets 8 bytes of random data for svcGetInfo RandomEntropy
|
||||
u64 GetRandomEntropy(std::size_t index) const {
|
||||
return random_entropy.at(index);
|
||||
@@ -252,17 +292,17 @@ public:
|
||||
u64 GetTotalPhysicalMemoryUsedWithoutSystemResource() const;
|
||||
|
||||
/// Gets the list of all threads created with this process as their owner.
|
||||
const std::list<const Thread*>& GetThreadList() const {
|
||||
const std::list<const KThread*>& GetThreadList() const {
|
||||
return thread_list;
|
||||
}
|
||||
|
||||
/// Registers a thread as being created under this process,
|
||||
/// adding it to this process' thread list.
|
||||
void RegisterThread(const Thread* thread);
|
||||
void RegisterThread(const KThread* thread);
|
||||
|
||||
/// Unregisters a thread from this process, removing it
|
||||
/// from this process' thread list.
|
||||
void UnregisterThread(const Thread* thread);
|
||||
void UnregisterThread(const KThread* thread);
|
||||
|
||||
/// Clears the signaled state of the process if and only if it's signaled.
|
||||
///
|
||||
@@ -303,6 +343,15 @@ public:
|
||||
|
||||
bool IsSignaled() const override;
|
||||
|
||||
void Finalize() override {}
|
||||
|
||||
void PinCurrentThread();
|
||||
void UnpinCurrentThread();
|
||||
|
||||
KLightLock& GetStateLock() {
|
||||
return state_lock;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Thread-local storage management
|
||||
|
||||
@@ -313,6 +362,20 @@ public:
|
||||
void FreeTLSRegion(VAddr tls_address);
|
||||
|
||||
private:
|
||||
void PinThread(s32 core_id, KThread* thread) {
|
||||
ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
|
||||
ASSERT(thread != nullptr);
|
||||
ASSERT(pinned_threads[core_id] == nullptr);
|
||||
pinned_threads[core_id] = thread;
|
||||
}
|
||||
|
||||
void UnpinThread(s32 core_id, KThread* thread) {
|
||||
ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES));
|
||||
ASSERT(thread != nullptr);
|
||||
ASSERT(pinned_threads[core_id] == thread);
|
||||
pinned_threads[core_id] = nullptr;
|
||||
}
|
||||
|
||||
/// Changes the process status. If the status is different
|
||||
/// from the current process status, then this will trigger
|
||||
/// a process signal.
|
||||
@@ -380,7 +443,7 @@ private:
|
||||
std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy{};
|
||||
|
||||
/// List of threads that are running with this process as their owner.
|
||||
std::list<const Thread*> thread_list;
|
||||
std::list<const KThread*> thread_list;
|
||||
|
||||
/// Address of the top of the main thread's stack
|
||||
VAddr main_thread_stack_top{};
|
||||
@@ -401,6 +464,19 @@ private:
|
||||
s64 schedule_count{};
|
||||
|
||||
bool is_signaled{};
|
||||
bool is_suspended{};
|
||||
|
||||
std::atomic<s32> num_created_threads{};
|
||||
std::atomic<u16> num_threads{};
|
||||
u16 peak_num_threads{};
|
||||
|
||||
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> running_threads{};
|
||||
std::array<u64, Core::Hardware::NUM_CPU_CORES> running_thread_idle_counts{};
|
||||
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> pinned_threads{};
|
||||
|
||||
KThread* exception_thread{};
|
||||
|
||||
KLightLock state_lock;
|
||||
|
||||
/// System context
|
||||
Core::System& system;
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
|
||||
@@ -47,6 +47,8 @@ public:
|
||||
|
||||
bool IsSignaled() const override;
|
||||
|
||||
void Finalize() override {}
|
||||
|
||||
private:
|
||||
explicit ReadableEvent(KernelCore& kernel);
|
||||
|
||||
|
||||
@@ -85,6 +85,8 @@ public:
|
||||
*/
|
||||
ResultCode SetLimitValue(ResourceType resource, s64 value);
|
||||
|
||||
void Finalize() override {}
|
||||
|
||||
private:
|
||||
// TODO(Subv): Increment resource limit current values in their respective Kernel::T::Create
|
||||
// functions
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
#include "common/assert.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/server_port.h"
|
||||
#include "core/hle/kernel/server_session.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
|
||||
@@ -81,6 +81,8 @@ public:
|
||||
|
||||
bool IsSignaled() const override;
|
||||
|
||||
void Finalize() override {}
|
||||
|
||||
private:
|
||||
/// ServerSessions waiting to be accepted by the port
|
||||
std::vector<std::shared_ptr<ServerSession>> pending_sessions;
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/server_session.h"
|
||||
#include "core/hle/kernel/session.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Kernel {
|
||||
@@ -116,7 +116,7 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode ServerSession::QueueSyncRequest(std::shared_ptr<Thread> thread,
|
||||
ResultCode ServerSession::QueueSyncRequest(std::shared_ptr<KThread> thread,
|
||||
Core::Memory::Memory& memory) {
|
||||
u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(thread->GetTLSAddress()))};
|
||||
auto context =
|
||||
@@ -154,14 +154,14 @@ ResultCode ServerSession::CompleteSyncRequest(HLERequestContext& context) {
|
||||
KScopedSchedulerLock lock(kernel);
|
||||
if (!context.IsThreadWaiting()) {
|
||||
context.GetThread().Wakeup();
|
||||
context.GetThread().SetSynchronizationResults(nullptr, result);
|
||||
context.GetThread().SetSyncedObject(nullptr, result);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread,
|
||||
ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<KThread> thread,
|
||||
Core::Memory::Memory& memory,
|
||||
Core::Timing::CoreTiming& core_timing) {
|
||||
return QueueSyncRequest(std::move(thread), memory);
|
||||
|
||||
@@ -29,7 +29,7 @@ class HLERequestContext;
|
||||
class KernelCore;
|
||||
class Session;
|
||||
class SessionRequestHandler;
|
||||
class Thread;
|
||||
class KThread;
|
||||
|
||||
/**
|
||||
* Kernel object representing the server endpoint of an IPC session. Sessions are the basic CTR-OS
|
||||
@@ -95,7 +95,7 @@ public:
|
||||
*
|
||||
* @returns ResultCode from the operation.
|
||||
*/
|
||||
ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory,
|
||||
ResultCode HandleSyncRequest(std::shared_ptr<KThread> thread, Core::Memory::Memory& memory,
|
||||
Core::Timing::CoreTiming& core_timing);
|
||||
|
||||
/// Called when a client disconnection occurs.
|
||||
@@ -126,9 +126,11 @@ public:
|
||||
|
||||
bool IsSignaled() const override;
|
||||
|
||||
void Finalize() override {}
|
||||
|
||||
private:
|
||||
/// Queues a sync request from the emulated application.
|
||||
ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory);
|
||||
ResultCode QueueSyncRequest(std::shared_ptr<KThread> thread, Core::Memory::Memory& memory);
|
||||
|
||||
/// Completes a sync request from the emulated application.
|
||||
ResultCode CompleteSyncRequest(HLERequestContext& context);
|
||||
@@ -149,12 +151,12 @@ private:
|
||||
/// List of threads that are pending a response after a sync request. This list is processed in
|
||||
/// a LIFO manner, thus, the last request will be dispatched first.
|
||||
/// TODO(Subv): Verify if this is indeed processed in LIFO using a hardware test.
|
||||
std::vector<std::shared_ptr<Thread>> pending_requesting_threads;
|
||||
std::vector<std::shared_ptr<KThread>> pending_requesting_threads;
|
||||
|
||||
/// Thread whose request is currently being handled. A request is considered "handled" when a
|
||||
/// response is sent via svcReplyAndReceive.
|
||||
/// TODO(Subv): Find a better name for this.
|
||||
std::shared_ptr<Thread> currently_handling;
|
||||
std::shared_ptr<KThread> currently_handling;
|
||||
|
||||
/// When set to True, converts the session to a domain at the end of the command
|
||||
bool convert_to_domain{};
|
||||
|
||||
@@ -39,6 +39,8 @@ public:
|
||||
|
||||
bool IsSignaled() const override;
|
||||
|
||||
void Finalize() override {}
|
||||
|
||||
std::shared_ptr<ClientSession> Client() {
|
||||
if (auto result{client.lock()}) {
|
||||
return result;
|
||||
|
||||
@@ -71,6 +71,8 @@ public:
|
||||
return device_memory.GetPointer(physical_address + offset);
|
||||
}
|
||||
|
||||
void Finalize() override {}
|
||||
|
||||
private:
|
||||
Core::DeviceMemory& device_memory;
|
||||
Process* owner_process{};
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
|
||||
#include "core/hle/kernel/k_synchronization_object.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/memory/memory_block.h"
|
||||
#include "core/hle/kernel/memory/memory_layout.h"
|
||||
@@ -42,7 +43,6 @@
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
#include "core/hle/kernel/svc_types.h"
|
||||
#include "core/hle/kernel/svc_wrap.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/time_manager.h"
|
||||
#include "core/hle/kernel/transfer_memory.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
@@ -351,7 +351,8 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
|
||||
session->SendSyncRequest(SharedFrom(thread), system.Memory(), system.CoreTiming());
|
||||
}
|
||||
|
||||
return thread->GetSignalingResult();
|
||||
KSynchronizationObject* dummy{};
|
||||
return thread->GetWaitResult(std::addressof(dummy));
|
||||
}
|
||||
|
||||
static ResultCode SendSyncRequest32(Core::System& system, Handle handle) {
|
||||
@@ -359,27 +360,26 @@ static ResultCode SendSyncRequest32(Core::System& system, Handle handle) {
|
||||
}
|
||||
|
||||
/// Get the ID for the specified thread.
|
||||
static ResultCode GetThreadId(Core::System& system, u64* thread_id, Handle thread_handle) {
|
||||
static ResultCode GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) {
|
||||
LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
|
||||
|
||||
// Get the thread from its handle.
|
||||
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
|
||||
const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle);
|
||||
if (!thread) {
|
||||
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", thread_handle);
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle);
|
||||
R_UNLESS(thread, Svc::ResultInvalidHandle);
|
||||
|
||||
*thread_id = thread->GetThreadID();
|
||||
// Get the thread's id.
|
||||
*out_thread_id = thread->GetThreadID();
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static ResultCode GetThreadId32(Core::System& system, u32* thread_id_low, u32* thread_id_high,
|
||||
Handle thread_handle) {
|
||||
u64 thread_id{};
|
||||
const ResultCode result{GetThreadId(system, &thread_id, thread_handle)};
|
||||
static ResultCode GetThreadId32(Core::System& system, u32* out_thread_id_low,
|
||||
u32* out_thread_id_high, Handle thread_handle) {
|
||||
u64 out_thread_id{};
|
||||
const ResultCode result{GetThreadId(system, &out_thread_id, thread_handle)};
|
||||
|
||||
*thread_id_low = static_cast<u32>(thread_id >> 32);
|
||||
*thread_id_high = static_cast<u32>(thread_id & std::numeric_limits<u32>::max());
|
||||
*out_thread_id_low = static_cast<u32>(out_thread_id >> 32);
|
||||
*out_thread_id_high = static_cast<u32>(out_thread_id & std::numeric_limits<u32>::max());
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -395,7 +395,7 @@ static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle han
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(handle);
|
||||
const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(handle);
|
||||
if (thread) {
|
||||
const Process* const owner_process = thread->GetOwnerProcess();
|
||||
if (!owner_process) {
|
||||
@@ -473,15 +473,13 @@ static ResultCode WaitSynchronization32(Core::System& system, u32 timeout_low, u
|
||||
static ResultCode CancelSynchronization(Core::System& system, Handle thread_handle) {
|
||||
LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle);
|
||||
|
||||
// Get the thread from its handle.
|
||||
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
|
||||
std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle);
|
||||
if (!thread) {
|
||||
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
|
||||
thread_handle);
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle);
|
||||
R_UNLESS(thread, Svc::ResultInvalidHandle);
|
||||
|
||||
thread->CancelWait();
|
||||
// Cancel the thread's wait.
|
||||
thread->WaitCancel();
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -630,7 +628,7 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
|
||||
handle_debug_buffer(info1, info2);
|
||||
|
||||
auto* const current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
|
||||
const auto thread_processor_id = current_thread->GetProcessorID();
|
||||
const auto thread_processor_id = current_thread->GetActiveCore();
|
||||
system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace();
|
||||
}
|
||||
}
|
||||
@@ -872,7 +870,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
const auto thread = system.Kernel().CurrentProcess()->GetHandleTable().Get<Thread>(
|
||||
const auto thread = system.Kernel().CurrentProcess()->GetHandleTable().Get<KThread>(
|
||||
static_cast<Handle>(handle));
|
||||
if (!thread) {
|
||||
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}",
|
||||
@@ -888,7 +886,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
|
||||
const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTicks();
|
||||
u64 out_ticks = 0;
|
||||
if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
|
||||
const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks();
|
||||
const u64 thread_ticks = current_thread->GetCpuTime();
|
||||
|
||||
out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks);
|
||||
} else if (same_thread && info_sub_id == system.CurrentCoreIndex()) {
|
||||
@@ -1025,129 +1023,109 @@ static ResultCode UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size
|
||||
return UnmapPhysicalMemory(system, addr, size);
|
||||
}
|
||||
|
||||
/// Sets the thread activity
|
||||
static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) {
|
||||
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity);
|
||||
if (activity > static_cast<u32>(ThreadActivity::Paused)) {
|
||||
return ERR_INVALID_ENUM_VALUE;
|
||||
constexpr bool IsValidThreadActivity(Svc::ThreadActivity thread_activity) {
|
||||
switch (thread_activity) {
|
||||
case Svc::ThreadActivity::Runnable:
|
||||
case Svc::ThreadActivity::Paused:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto* current_process = system.Kernel().CurrentProcess();
|
||||
const std::shared_ptr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
|
||||
if (!thread) {
|
||||
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (thread->GetOwnerProcess() != current_process) {
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"The current process does not own the current thread, thread_handle={:08X} "
|
||||
"thread_pid={}, "
|
||||
"current_process_pid={}",
|
||||
handle, thread->GetOwnerProcess()->GetProcessID(),
|
||||
current_process->GetProcessID());
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (thread.get() == system.Kernel().CurrentScheduler()->GetCurrentThread()) {
|
||||
LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread");
|
||||
return ERR_BUSY;
|
||||
}
|
||||
|
||||
return thread->SetActivity(static_cast<ThreadActivity>(activity));
|
||||
}
|
||||
|
||||
static ResultCode SetThreadActivity32(Core::System& system, Handle handle, u32 activity) {
|
||||
return SetThreadActivity(system, handle, activity);
|
||||
/// Sets the thread activity
|
||||
static ResultCode SetThreadActivity(Core::System& system, Handle thread_handle,
|
||||
Svc::ThreadActivity thread_activity) {
|
||||
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", thread_handle,
|
||||
thread_activity);
|
||||
|
||||
// Validate the activity.
|
||||
R_UNLESS(IsValidThreadActivity(thread_activity), Svc::ResultInvalidEnumValue);
|
||||
|
||||
// Get the thread from its handle.
|
||||
auto& kernel = system.Kernel();
|
||||
const auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
|
||||
const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle);
|
||||
R_UNLESS(thread, Svc::ResultInvalidHandle);
|
||||
|
||||
// Check that the activity is being set on a non-current thread for the current process.
|
||||
R_UNLESS(thread->GetOwnerProcess() == kernel.CurrentProcess(), Svc::ResultInvalidHandle);
|
||||
R_UNLESS(thread.get() != GetCurrentThreadPointer(kernel), Svc::ResultBusy);
|
||||
|
||||
// Set the activity.
|
||||
R_TRY(thread->SetActivity(thread_activity));
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static ResultCode SetThreadActivity32(Core::System& system, Handle thread_handle,
|
||||
Svc::ThreadActivity thread_activity) {
|
||||
return SetThreadActivity(system, thread_handle, thread_activity);
|
||||
}
|
||||
|
||||
/// Gets the thread context
|
||||
static ResultCode GetThreadContext(Core::System& system, VAddr thread_context, Handle handle) {
|
||||
LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle);
|
||||
static ResultCode GetThreadContext(Core::System& system, VAddr out_context, Handle thread_handle) {
|
||||
LOG_DEBUG(Kernel_SVC, "called, out_context=0x{:08X}, thread_handle=0x{:X}", out_context,
|
||||
thread_handle);
|
||||
|
||||
// Get the thread from its handle.
|
||||
const auto* current_process = system.Kernel().CurrentProcess();
|
||||
const std::shared_ptr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
|
||||
if (!thread) {
|
||||
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
const std::shared_ptr<KThread> thread =
|
||||
current_process->GetHandleTable().Get<KThread>(thread_handle);
|
||||
R_UNLESS(thread, Svc::ResultInvalidHandle);
|
||||
|
||||
if (thread->GetOwnerProcess() != current_process) {
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"The current process does not own the current thread, thread_handle={:08X} "
|
||||
"thread_pid={}, "
|
||||
"current_process_pid={}",
|
||||
handle, thread->GetOwnerProcess()->GetProcessID(),
|
||||
current_process->GetProcessID());
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
// Require the handle be to a non-current thread in the current process.
|
||||
R_UNLESS(thread->GetOwnerProcess() == current_process, Svc::ResultInvalidHandle);
|
||||
R_UNLESS(thread.get() != system.Kernel().CurrentScheduler()->GetCurrentThread(),
|
||||
Svc::ResultBusy);
|
||||
|
||||
if (thread.get() == system.Kernel().CurrentScheduler()->GetCurrentThread()) {
|
||||
LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread");
|
||||
return ERR_BUSY;
|
||||
}
|
||||
// Get the thread context.
|
||||
std::vector<u8> context;
|
||||
R_TRY(thread->GetThreadContext3(context));
|
||||
|
||||
Core::ARM_Interface::ThreadContext64 ctx = thread->GetContext64();
|
||||
// Mask away mode bits, interrupt bits, IL bit, and other reserved bits.
|
||||
ctx.pstate &= 0xFF0FFE20;
|
||||
// Copy the thread context to user space.
|
||||
system.Memory().WriteBlock(out_context, context.data(), context.size());
|
||||
|
||||
// If 64-bit, we can just write the context registers directly and we're good.
|
||||
// However, if 32-bit, we have to ensure some registers are zeroed out.
|
||||
if (!current_process->Is64BitProcess()) {
|
||||
std::fill(ctx.cpu_registers.begin() + 15, ctx.cpu_registers.end(), 0);
|
||||
std::fill(ctx.vector_registers.begin() + 16, ctx.vector_registers.end(), u128{});
|
||||
}
|
||||
|
||||
system.Memory().WriteBlock(thread_context, &ctx, sizeof(ctx));
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static ResultCode GetThreadContext32(Core::System& system, u32 thread_context, Handle handle) {
|
||||
return GetThreadContext(system, thread_context, handle);
|
||||
static ResultCode GetThreadContext32(Core::System& system, u32 out_context, Handle thread_handle) {
|
||||
return GetThreadContext(system, out_context, thread_handle);
|
||||
}
|
||||
|
||||
/// Gets the priority for the specified thread
|
||||
static ResultCode GetThreadPriority(Core::System& system, u32* priority, Handle handle) {
|
||||
static ResultCode GetThreadPriority(Core::System& system, u32* out_priority, Handle handle) {
|
||||
LOG_TRACE(Kernel_SVC, "called");
|
||||
|
||||
// Get the thread from its handle.
|
||||
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
|
||||
const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(handle);
|
||||
if (!thread) {
|
||||
*priority = 0;
|
||||
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(handle);
|
||||
R_UNLESS(thread, Svc::ResultInvalidHandle);
|
||||
|
||||
*priority = thread->GetPriority();
|
||||
// Get the thread's priority.
|
||||
*out_priority = thread->GetPriority();
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static ResultCode GetThreadPriority32(Core::System& system, u32* priority, Handle handle) {
|
||||
return GetThreadPriority(system, priority, handle);
|
||||
static ResultCode GetThreadPriority32(Core::System& system, u32* out_priority, Handle handle) {
|
||||
return GetThreadPriority(system, out_priority, handle);
|
||||
}
|
||||
|
||||
/// Sets the priority for the specified thread
|
||||
static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 priority) {
|
||||
LOG_TRACE(Kernel_SVC, "called");
|
||||
|
||||
if (priority > THREADPRIO_LOWEST) {
|
||||
LOG_ERROR(
|
||||
Kernel_SVC,
|
||||
"An invalid priority was specified, expected {} but got {} for thread_handle={:08X}",
|
||||
THREADPRIO_LOWEST, priority, handle);
|
||||
return ERR_INVALID_THREAD_PRIORITY;
|
||||
}
|
||||
// Validate the priority.
|
||||
R_UNLESS(Svc::HighestThreadPriority <= priority && priority <= Svc::LowestThreadPriority,
|
||||
Svc::ResultInvalidPriority);
|
||||
|
||||
const auto* const current_process = system.Kernel().CurrentProcess();
|
||||
|
||||
std::shared_ptr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
|
||||
if (!thread) {
|
||||
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
// Get the thread from its handle.
|
||||
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
|
||||
const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(handle);
|
||||
R_UNLESS(thread, Svc::ResultInvalidHandle);
|
||||
|
||||
// Set the thread priority.
|
||||
thread->SetBasePriority(priority);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1438,62 +1416,50 @@ static void ExitProcess(Core::System& system) {
|
||||
current_process->PrepareForTermination();
|
||||
|
||||
// Kill the current thread
|
||||
system.Kernel().CurrentScheduler()->GetCurrentThread()->Stop();
|
||||
system.Kernel().CurrentScheduler()->GetCurrentThread()->Exit();
|
||||
}
|
||||
|
||||
static void ExitProcess32(Core::System& system) {
|
||||
ExitProcess(system);
|
||||
}
|
||||
|
||||
static constexpr bool IsValidCoreId(int32_t core_id) {
|
||||
return (0 <= core_id && core_id < static_cast<int32_t>(Core::Hardware::NUM_CPU_CORES));
|
||||
}
|
||||
|
||||
/// Creates a new thread
|
||||
static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg,
|
||||
VAddr stack_top, u32 priority, s32 processor_id) {
|
||||
VAddr stack_bottom, u32 priority, s32 core_id) {
|
||||
LOG_DEBUG(Kernel_SVC,
|
||||
"called entrypoint=0x{:08X}, arg=0x{:08X}, stacktop=0x{:08X}, "
|
||||
"threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}",
|
||||
entry_point, arg, stack_top, priority, processor_id, *out_handle);
|
||||
|
||||
auto* const current_process = system.Kernel().CurrentProcess();
|
||||
|
||||
if (processor_id == THREADPROCESSORID_IDEAL) {
|
||||
// Set the target CPU to the one specified by the process.
|
||||
processor_id = current_process->GetIdealCore();
|
||||
ASSERT(processor_id != THREADPROCESSORID_IDEAL);
|
||||
}
|
||||
|
||||
if (processor_id < THREADPROCESSORID_0 || processor_id > THREADPROCESSORID_3) {
|
||||
LOG_ERROR(Kernel_SVC, "Invalid thread processor ID: {}", processor_id);
|
||||
return ERR_INVALID_PROCESSOR_ID;
|
||||
}
|
||||
|
||||
const u64 core_mask = current_process->GetCoreMask();
|
||||
if ((core_mask | (1ULL << processor_id)) != core_mask) {
|
||||
LOG_ERROR(Kernel_SVC, "Invalid thread core specified ({})", processor_id);
|
||||
return ERR_INVALID_PROCESSOR_ID;
|
||||
}
|
||||
|
||||
if (priority > THREADPRIO_LOWEST) {
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"Invalid thread priority specified ({}). Must be within the range 0-64",
|
||||
priority);
|
||||
return ERR_INVALID_THREAD_PRIORITY;
|
||||
}
|
||||
|
||||
if (((1ULL << priority) & current_process->GetPriorityMask()) == 0) {
|
||||
LOG_ERROR(Kernel_SVC, "Invalid thread priority specified ({})", priority);
|
||||
return ERR_INVALID_THREAD_PRIORITY;
|
||||
}
|
||||
"called entry_point=0x{:08X}, arg=0x{:08X}, stack_bottom=0x{:08X}, "
|
||||
"priority=0x{:08X}, core_id=0x{:08X}",
|
||||
entry_point, arg, stack_bottom, priority, core_id);
|
||||
|
||||
// Adjust core id, if it's the default magic.
|
||||
auto& kernel = system.Kernel();
|
||||
auto& process = *kernel.CurrentProcess();
|
||||
if (core_id == Svc::IdealCoreUseProcessValue) {
|
||||
core_id = process.GetIdealCoreId();
|
||||
}
|
||||
|
||||
ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(ResourceType::Threads, 1));
|
||||
// Validate arguments.
|
||||
R_UNLESS(IsValidCoreId(core_id), Svc::ResultInvalidCoreId);
|
||||
R_UNLESS(((1ULL << core_id) & process.GetCoreMask()) != 0, Svc::ResultInvalidCoreId);
|
||||
|
||||
ThreadType type = THREADTYPE_USER;
|
||||
CASCADE_RESULT(std::shared_ptr<Thread> thread,
|
||||
Thread::Create(system, type, "", entry_point, priority, arg, processor_id,
|
||||
stack_top, current_process));
|
||||
R_UNLESS(Svc::HighestThreadPriority <= priority && priority <= Svc::LowestThreadPriority,
|
||||
Svc::ResultInvalidPriority);
|
||||
R_UNLESS(process.CheckThreadPriority(priority), Svc::ResultInvalidPriority);
|
||||
|
||||
const auto new_thread_handle = current_process->GetHandleTable().Create(thread);
|
||||
ASSERT(process.GetResourceLimit()->Reserve(ResourceType::Threads, 1));
|
||||
|
||||
std::shared_ptr<KThread> thread;
|
||||
{
|
||||
KScopedLightLock lk{process.GetStateLock()};
|
||||
CASCADE_RESULT(thread, KThread::Create(system, ThreadType::User, "", entry_point, priority,
|
||||
arg, core_id, stack_bottom, &process));
|
||||
}
|
||||
|
||||
const auto new_thread_handle = process.GetHandleTable().Create(thread);
|
||||
if (new_thread_handle.Failed()) {
|
||||
LOG_ERROR(Kernel_SVC, "Failed to create handle with error=0x{:X}",
|
||||
new_thread_handle.Code().raw);
|
||||
@@ -1517,17 +1483,15 @@ static ResultCode CreateThread32(Core::System& system, Handle* out_handle, u32 p
|
||||
static ResultCode StartThread(Core::System& system, Handle thread_handle) {
|
||||
LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
|
||||
|
||||
// Get the thread from its handle.
|
||||
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
|
||||
const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle);
|
||||
if (!thread) {
|
||||
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
|
||||
thread_handle);
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle);
|
||||
R_UNLESS(thread, Svc::ResultInvalidHandle);
|
||||
|
||||
ASSERT(thread->GetState() == ThreadState::Initialized);
|
||||
// Try to start the thread.
|
||||
R_TRY(thread->Run());
|
||||
|
||||
return thread->Start();
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static ResultCode StartThread32(Core::System& system, Handle thread_handle) {
|
||||
@@ -1540,7 +1504,7 @@ static void ExitThread(Core::System& system) {
|
||||
|
||||
auto* const current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
|
||||
system.GlobalSchedulerContext().RemoveThread(SharedFrom(current_thread));
|
||||
current_thread->Stop();
|
||||
current_thread->Exit();
|
||||
}
|
||||
|
||||
static void ExitThread32(Core::System& system) {
|
||||
@@ -1549,34 +1513,28 @@ static void ExitThread32(Core::System& system) {
|
||||
|
||||
/// Sleep the current thread
|
||||
static void SleepThread(Core::System& system, s64 nanoseconds) {
|
||||
auto& kernel = system.Kernel();
|
||||
const auto yield_type = static_cast<Svc::YieldType>(nanoseconds);
|
||||
|
||||
LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds);
|
||||
|
||||
enum class SleepType : s64 {
|
||||
YieldWithoutCoreMigration = 0,
|
||||
YieldWithCoreMigration = -1,
|
||||
YieldAndWaitForLoadBalancing = -2,
|
||||
};
|
||||
// When the input tick is positive, sleep.
|
||||
if (nanoseconds > 0) {
|
||||
// Convert the timeout from nanoseconds to ticks.
|
||||
// NOTE: Nintendo does not use this conversion logic in WaitSynchronization...
|
||||
|
||||
auto& scheduler = *system.Kernel().CurrentScheduler();
|
||||
if (nanoseconds <= 0) {
|
||||
switch (static_cast<SleepType>(nanoseconds)) {
|
||||
case SleepType::YieldWithoutCoreMigration: {
|
||||
scheduler.YieldWithoutCoreMigration();
|
||||
break;
|
||||
}
|
||||
case SleepType::YieldWithCoreMigration: {
|
||||
scheduler.YieldWithCoreMigration();
|
||||
break;
|
||||
}
|
||||
case SleepType::YieldAndWaitForLoadBalancing: {
|
||||
scheduler.YieldToAnyThread();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds);
|
||||
}
|
||||
// Sleep.
|
||||
// NOTE: Nintendo does not check the result of this sleep.
|
||||
static_cast<void>(GetCurrentThread(kernel).Sleep(nanoseconds));
|
||||
} else if (yield_type == Svc::YieldType::WithoutCoreMigration) {
|
||||
KScheduler::YieldWithoutCoreMigration(kernel);
|
||||
} else if (yield_type == Svc::YieldType::WithCoreMigration) {
|
||||
KScheduler::YieldWithCoreMigration(kernel);
|
||||
} else if (yield_type == Svc::YieldType::ToAnyThread) {
|
||||
KScheduler::YieldToAnyThread(kernel);
|
||||
} else {
|
||||
scheduler.GetCurrentThread()->Sleep(nanoseconds);
|
||||
// Nintendo does nothing at all if an otherwise invalid value is passed.
|
||||
UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1839,95 +1797,72 @@ static ResultCode CreateTransferMemory32(Core::System& system, Handle* handle, u
|
||||
return CreateTransferMemory(system, handle, addr, size, permissions);
|
||||
}
|
||||
|
||||
static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, u32* core,
|
||||
u64* mask) {
|
||||
static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, s32* out_core_id,
|
||||
u64* out_affinity_mask) {
|
||||
LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
|
||||
|
||||
// Get the thread from its handle.
|
||||
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
|
||||
const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle);
|
||||
if (!thread) {
|
||||
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
|
||||
thread_handle);
|
||||
*core = 0;
|
||||
*mask = 0;
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle);
|
||||
R_UNLESS(thread, Svc::ResultInvalidHandle);
|
||||
|
||||
*core = thread->GetIdealCore();
|
||||
*mask = thread->GetAffinityMask().GetAffinityMask();
|
||||
// Get the core mask.
|
||||
R_TRY(thread->GetCoreMask(out_core_id, out_affinity_mask));
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static ResultCode GetThreadCoreMask32(Core::System& system, Handle thread_handle, u32* core,
|
||||
u32* mask_low, u32* mask_high) {
|
||||
u64 mask{};
|
||||
const auto result = GetThreadCoreMask(system, thread_handle, core, &mask);
|
||||
*mask_high = static_cast<u32>(mask >> 32);
|
||||
*mask_low = static_cast<u32>(mask);
|
||||
static ResultCode GetThreadCoreMask32(Core::System& system, Handle thread_handle, s32* out_core_id,
|
||||
u32* out_affinity_mask_low, u32* out_affinity_mask_high) {
|
||||
u64 out_affinity_mask{};
|
||||
const auto result = GetThreadCoreMask(system, thread_handle, out_core_id, &out_affinity_mask);
|
||||
*out_affinity_mask_high = static_cast<u32>(out_affinity_mask >> 32);
|
||||
*out_affinity_mask_low = static_cast<u32>(out_affinity_mask);
|
||||
return result;
|
||||
}
|
||||
|
||||
static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, u32 core,
|
||||
static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id,
|
||||
u64 affinity_mask) {
|
||||
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, core=0x{:X}, affinity_mask=0x{:016X}",
|
||||
thread_handle, core, affinity_mask);
|
||||
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, core_id=0x{:X}, affinity_mask=0x{:016X}",
|
||||
thread_handle, core_id, affinity_mask);
|
||||
|
||||
const auto* const current_process = system.Kernel().CurrentProcess();
|
||||
const auto& current_process = *system.Kernel().CurrentProcess();
|
||||
|
||||
if (core == static_cast<u32>(THREADPROCESSORID_IDEAL)) {
|
||||
const u8 ideal_cpu_core = current_process->GetIdealCore();
|
||||
|
||||
ASSERT(ideal_cpu_core != static_cast<u8>(THREADPROCESSORID_IDEAL));
|
||||
|
||||
// Set the target CPU to the ideal core specified by the process.
|
||||
core = ideal_cpu_core;
|
||||
affinity_mask = 1ULL << core;
|
||||
// Determine the core id/affinity mask.
|
||||
if (core_id == Svc::IdealCoreUseProcessValue) {
|
||||
core_id = current_process.GetIdealCoreId();
|
||||
affinity_mask = (1ULL << core_id);
|
||||
} else {
|
||||
const u64 core_mask = current_process->GetCoreMask();
|
||||
// Validate the affinity mask.
|
||||
const u64 process_core_mask = current_process.GetCoreMask();
|
||||
R_UNLESS((affinity_mask | process_core_mask) == process_core_mask,
|
||||
Svc::ResultInvalidCoreId);
|
||||
R_UNLESS(affinity_mask != 0, Svc::ResultInvalidCombination);
|
||||
|
||||
if ((core_mask | affinity_mask) != core_mask) {
|
||||
LOG_ERROR(
|
||||
Kernel_SVC,
|
||||
"Invalid processor ID specified (core_mask=0x{:08X}, affinity_mask=0x{:016X})",
|
||||
core_mask, affinity_mask);
|
||||
return ERR_INVALID_PROCESSOR_ID;
|
||||
}
|
||||
|
||||
if (affinity_mask == 0) {
|
||||
LOG_ERROR(Kernel_SVC, "Specfified affinity mask is zero.");
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
if (core < Core::Hardware::NUM_CPU_CORES) {
|
||||
if ((affinity_mask & (1ULL << core)) == 0) {
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"Core is not enabled for the current mask, core={}, mask={:016X}", core,
|
||||
affinity_mask);
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
} else if (core != static_cast<u32>(THREADPROCESSORID_DONT_CARE) &&
|
||||
core != static_cast<u32>(THREADPROCESSORID_DONT_UPDATE)) {
|
||||
LOG_ERROR(Kernel_SVC, "Invalid processor ID specified (core={}).", core);
|
||||
return ERR_INVALID_PROCESSOR_ID;
|
||||
// Validate the core id.
|
||||
if (IsValidCoreId(core_id)) {
|
||||
R_UNLESS(((1ULL << core_id) & affinity_mask) != 0, Svc::ResultInvalidCombination);
|
||||
} else {
|
||||
R_UNLESS(core_id == Svc::IdealCoreNoUpdate || core_id == Svc::IdealCoreDontCare,
|
||||
Svc::ResultInvalidCoreId);
|
||||
}
|
||||
}
|
||||
|
||||
const auto& handle_table = current_process->GetHandleTable();
|
||||
const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle);
|
||||
if (!thread) {
|
||||
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
|
||||
thread_handle);
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
// Get the thread from its handle.
|
||||
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
|
||||
const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle);
|
||||
R_UNLESS(thread, Svc::ResultInvalidHandle);
|
||||
|
||||
return thread->SetCoreAndAffinityMask(core, affinity_mask);
|
||||
// Set the core mask.
|
||||
R_TRY(thread->SetCoreMask(core_id, affinity_mask));
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static ResultCode SetThreadCoreMask32(Core::System& system, Handle thread_handle, u32 core,
|
||||
static ResultCode SetThreadCoreMask32(Core::System& system, Handle thread_handle, s32 core_id,
|
||||
u32 affinity_mask_low, u32 affinity_mask_high) {
|
||||
const auto affinity_mask = u64{affinity_mask_low} | (u64{affinity_mask_high} << 32);
|
||||
return SetThreadCoreMask(system, thread_handle, core, affinity_mask);
|
||||
return SetThreadCoreMask(system, thread_handle, core_id, affinity_mask);
|
||||
}
|
||||
|
||||
static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle* read_handle) {
|
||||
@@ -2491,7 +2426,7 @@ void Call(Core::System& system, u32 immediate) {
|
||||
kernel.EnterSVCProfile();
|
||||
|
||||
auto* thread = kernel.CurrentScheduler()->GetCurrentThread();
|
||||
thread->SetContinuousOnSVC(true);
|
||||
thread->SetIsCallingSvc();
|
||||
|
||||
const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate)
|
||||
: GetSVCInfo32(immediate);
|
||||
@@ -2507,7 +2442,7 @@ void Call(Core::System& system, u32 immediate) {
|
||||
|
||||
kernel.ExitSVCProfile();
|
||||
|
||||
if (!thread->IsContinuousOnSVC()) {
|
||||
if (!thread->IsCallingSvc()) {
|
||||
auto* host_context = thread->GetHostContext().get();
|
||||
host_context->Rewind();
|
||||
}
|
||||
|
||||
@@ -8,13 +8,18 @@
|
||||
|
||||
namespace Kernel::Svc {
|
||||
|
||||
constexpr ResultCode ResultNoSynchronizationObject{ErrorModule::Kernel, 57};
|
||||
constexpr ResultCode ResultTerminationRequested{ErrorModule::Kernel, 59};
|
||||
constexpr ResultCode ResultInvalidAddress{ErrorModule::Kernel, 102};
|
||||
constexpr ResultCode ResultInvalidCurrentMemory{ErrorModule::Kernel, 106};
|
||||
constexpr ResultCode ResultInvalidPriority{ErrorModule::Kernel, 112};
|
||||
constexpr ResultCode ResultInvalidCoreId{ErrorModule::Kernel, 113};
|
||||
constexpr ResultCode ResultInvalidHandle{ErrorModule::Kernel, 114};
|
||||
constexpr ResultCode ResultInvalidCombination{ErrorModule::Kernel, 116};
|
||||
constexpr ResultCode ResultTimedOut{ErrorModule::Kernel, 117};
|
||||
constexpr ResultCode ResultCancelled{ErrorModule::Kernel, 118};
|
||||
constexpr ResultCode ResultInvalidEnumValue{ErrorModule::Kernel, 120};
|
||||
constexpr ResultCode ResultBusy{ErrorModule::Kernel, 122};
|
||||
constexpr ResultCode ResultInvalidState{ErrorModule::Kernel, 125};
|
||||
|
||||
} // namespace Kernel::Svc
|
||||
|
||||
@@ -77,4 +77,22 @@ enum class ArbitrationType : u32 {
|
||||
WaitIfEqual = 2,
|
||||
};
|
||||
|
||||
enum class YieldType : s64 {
|
||||
WithoutCoreMigration = 0,
|
||||
WithCoreMigration = -1,
|
||||
ToAnyThread = -2,
|
||||
};
|
||||
|
||||
enum class ThreadActivity : u32 {
|
||||
Runnable = 0,
|
||||
Paused = 1,
|
||||
};
|
||||
|
||||
constexpr inline s32 IdealCoreDontCare = -1;
|
||||
constexpr inline s32 IdealCoreUseProcessValue = -2;
|
||||
constexpr inline s32 IdealCoreNoUpdate = -3;
|
||||
|
||||
constexpr inline s32 LowestThreadPriority = 63;
|
||||
constexpr inline s32 HighestThreadPriority = 0;
|
||||
|
||||
} // namespace Kernel::Svc
|
||||
|
||||
@@ -58,6 +58,14 @@ void SvcWrap64(Core::System& system) {
|
||||
func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw);
|
||||
}
|
||||
|
||||
// Used by SetThreadActivity
|
||||
template <ResultCode func(Core::System&, Handle, Svc::ThreadActivity)>
|
||||
void SvcWrap64(Core::System& system) {
|
||||
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)),
|
||||
static_cast<Svc::ThreadActivity>(Param(system, 1)))
|
||||
.raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u32, u64, u64, u64)>
|
||||
void SvcWrap64(Core::System& system) {
|
||||
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
|
||||
@@ -158,9 +166,18 @@ void SvcWrap64(Core::System& system) {
|
||||
.raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(Core::System&, u32, u32*, u64*)>
|
||||
// Used by SetThreadCoreMask
|
||||
template <ResultCode func(Core::System&, Handle, s32, u64)>
|
||||
void SvcWrap64(Core::System& system) {
|
||||
u32 param_1 = 0;
|
||||
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)),
|
||||
static_cast<s32>(Param(system, 1)), Param(system, 2))
|
||||
.raw);
|
||||
}
|
||||
|
||||
// Used by GetThreadCoreMask
|
||||
template <ResultCode func(Core::System&, Handle, s32*, u64*)>
|
||||
void SvcWrap64(Core::System& system) {
|
||||
s32 param_1 = 0;
|
||||
u64 param_2 = 0;
|
||||
const ResultCode retval = func(system, static_cast<u32>(Param(system, 2)), ¶m_1, ¶m_2);
|
||||
|
||||
@@ -473,12 +490,35 @@ void SvcWrap32(Core::System& system) {
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
// Used by GetThreadCoreMask32
|
||||
template <ResultCode func(Core::System&, Handle, s32*, u32*, u32*)>
|
||||
void SvcWrap32(Core::System& system) {
|
||||
s32 param_1 = 0;
|
||||
u32 param_2 = 0;
|
||||
u32 param_3 = 0;
|
||||
|
||||
const u32 retval = func(system, Param32(system, 2), ¶m_1, ¶m_2, ¶m_3).raw;
|
||||
system.CurrentArmInterface().SetReg(1, param_1);
|
||||
system.CurrentArmInterface().SetReg(2, param_2);
|
||||
system.CurrentArmInterface().SetReg(3, param_3);
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
// Used by SignalProcessWideKey32
|
||||
template <void func(Core::System&, u32, s32)>
|
||||
void SvcWrap32(Core::System& system) {
|
||||
func(system, static_cast<u32>(Param(system, 0)), static_cast<s32>(Param(system, 1)));
|
||||
}
|
||||
|
||||
// Used by SetThreadActivity32
|
||||
template <ResultCode func(Core::System&, Handle, Svc::ThreadActivity)>
|
||||
void SvcWrap32(Core::System& system) {
|
||||
const u32 retval = func(system, static_cast<Handle>(Param(system, 0)),
|
||||
static_cast<Svc::ThreadActivity>(Param(system, 1)))
|
||||
.raw;
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
// Used by SetThreadPriority32
|
||||
template <ResultCode func(Core::System&, Handle, u32)>
|
||||
void SvcWrap32(Core::System& system) {
|
||||
@@ -487,7 +527,7 @@ void SvcWrap32(Core::System& system) {
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
// Used by SetThreadCoreMask32
|
||||
// Used by SetMemoryAttribute32
|
||||
template <ResultCode func(Core::System&, Handle, u32, u32, u32)>
|
||||
void SvcWrap32(Core::System& system) {
|
||||
const u32 retval =
|
||||
@@ -497,6 +537,16 @@ void SvcWrap32(Core::System& system) {
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
// Used by SetThreadCoreMask32
|
||||
template <ResultCode func(Core::System&, Handle, s32, u32, u32)>
|
||||
void SvcWrap32(Core::System& system) {
|
||||
const u32 retval =
|
||||
func(system, static_cast<Handle>(Param(system, 0)), static_cast<s32>(Param(system, 1)),
|
||||
static_cast<u32>(Param(system, 2)), static_cast<u32>(Param(system, 3)))
|
||||
.raw;
|
||||
FuncReturn(system, retval);
|
||||
}
|
||||
|
||||
// Used by WaitProcessWideKeyAtomic32
|
||||
template <ResultCode func(Core::System&, u32, u32, Handle, u32, u32)>
|
||||
void SvcWrap32(Core::System& system) {
|
||||
|
||||
@@ -1,460 +0,0 @@
|
||||
// Copyright 2014 Citra Emulator Project / PPSSPP Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/fiber.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/thread_queue_list.h"
|
||||
#include "core/core.h"
|
||||
#include "core/cpu_manager.h"
|
||||
#include "core/hardware_properties.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/k_condition_variable.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/memory/memory_layout.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/time_manager.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#include "core/arm/dynarmic/arm_dynarmic_32.h"
|
||||
#include "core/arm/dynarmic/arm_dynarmic_64.h"
|
||||
#endif
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
bool Thread::IsSignaled() const {
|
||||
return signaled;
|
||||
}
|
||||
|
||||
Thread::Thread(KernelCore& kernel) : KSynchronizationObject{kernel} {}
|
||||
Thread::~Thread() = default;
|
||||
|
||||
void Thread::Stop() {
|
||||
{
|
||||
KScopedSchedulerLock lock(kernel);
|
||||
SetState(ThreadState::Terminated);
|
||||
signaled = true;
|
||||
NotifyAvailable();
|
||||
kernel.GlobalHandleTable().Close(global_handle);
|
||||
|
||||
if (owner_process) {
|
||||
owner_process->UnregisterThread(this);
|
||||
|
||||
// Mark the TLS slot in the thread's page as free.
|
||||
owner_process->FreeTLSRegion(tls_address);
|
||||
}
|
||||
has_exited = true;
|
||||
}
|
||||
global_handle = 0;
|
||||
}
|
||||
|
||||
void Thread::Wakeup() {
|
||||
KScopedSchedulerLock lock(kernel);
|
||||
SetState(ThreadState::Runnable);
|
||||
}
|
||||
|
||||
ResultCode Thread::Start() {
|
||||
KScopedSchedulerLock lock(kernel);
|
||||
SetState(ThreadState::Runnable);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
void Thread::CancelWait() {
|
||||
KScopedSchedulerLock lock(kernel);
|
||||
if (GetState() != ThreadState::Waiting || !is_cancellable) {
|
||||
is_sync_cancelled = true;
|
||||
return;
|
||||
}
|
||||
// TODO(Blinkhawk): Implement cancel of server session
|
||||
is_sync_cancelled = false;
|
||||
SetSynchronizationResults(nullptr, ERR_SYNCHRONIZATION_CANCELED);
|
||||
SetState(ThreadState::Runnable);
|
||||
}
|
||||
|
||||
static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context, u32 stack_top,
|
||||
u32 entry_point, u32 arg) {
|
||||
context = {};
|
||||
context.cpu_registers[0] = arg;
|
||||
context.cpu_registers[15] = entry_point;
|
||||
context.cpu_registers[13] = stack_top;
|
||||
}
|
||||
|
||||
static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context, VAddr stack_top,
|
||||
VAddr entry_point, u64 arg) {
|
||||
context = {};
|
||||
context.cpu_registers[0] = arg;
|
||||
context.pc = entry_point;
|
||||
context.sp = stack_top;
|
||||
// TODO(merry): Perform a hardware test to determine the below value.
|
||||
context.fpcr = 0;
|
||||
}
|
||||
|
||||
std::shared_ptr<Common::Fiber>& Thread::GetHostContext() {
|
||||
return host_context;
|
||||
}
|
||||
|
||||
ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadType type_flags,
|
||||
std::string name, VAddr entry_point, u32 priority,
|
||||
u64 arg, s32 processor_id, VAddr stack_top,
|
||||
Process* owner_process) {
|
||||
std::function<void(void*)> init_func = Core::CpuManager::GetGuestThreadStartFunc();
|
||||
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
|
||||
return Create(system, type_flags, name, entry_point, priority, arg, processor_id, stack_top,
|
||||
owner_process, std::move(init_func), init_func_parameter);
|
||||
}
|
||||
|
||||
ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadType type_flags,
|
||||
std::string name, VAddr entry_point, u32 priority,
|
||||
u64 arg, s32 processor_id, VAddr stack_top,
|
||||
Process* owner_process,
|
||||
std::function<void(void*)>&& thread_start_func,
|
||||
void* thread_start_parameter) {
|
||||
auto& kernel = system.Kernel();
|
||||
// Check if priority is in ranged. Lowest priority -> highest priority id.
|
||||
if (priority > THREADPRIO_LOWEST && ((type_flags & THREADTYPE_IDLE) == 0)) {
|
||||
LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority);
|
||||
return ERR_INVALID_THREAD_PRIORITY;
|
||||
}
|
||||
|
||||
if (processor_id > THREADPROCESSORID_MAX) {
|
||||
LOG_ERROR(Kernel_SVC, "Invalid processor id: {}", processor_id);
|
||||
return ERR_INVALID_PROCESSOR_ID;
|
||||
}
|
||||
|
||||
if (owner_process) {
|
||||
if (!system.Memory().IsValidVirtualAddress(*owner_process, entry_point)) {
|
||||
LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point);
|
||||
// TODO (bunnei): Find the correct error code to use here
|
||||
return RESULT_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Thread> thread = std::make_shared<Thread>(kernel);
|
||||
|
||||
thread->thread_id = kernel.CreateNewThreadID();
|
||||
thread->thread_state = ThreadState::Initialized;
|
||||
thread->entry_point = entry_point;
|
||||
thread->stack_top = stack_top;
|
||||
thread->disable_count = 1;
|
||||
thread->tpidr_el0 = 0;
|
||||
thread->current_priority = priority;
|
||||
thread->base_priority = priority;
|
||||
thread->lock_owner = nullptr;
|
||||
thread->schedule_count = -1;
|
||||
thread->last_scheduled_tick = 0;
|
||||
thread->processor_id = processor_id;
|
||||
thread->ideal_core = processor_id;
|
||||
thread->affinity_mask.SetAffinity(processor_id, true);
|
||||
thread->name = std::move(name);
|
||||
thread->global_handle = kernel.GlobalHandleTable().Create(thread).Unwrap();
|
||||
thread->owner_process = owner_process;
|
||||
thread->type = type_flags;
|
||||
thread->signaled = false;
|
||||
if ((type_flags & THREADTYPE_IDLE) == 0) {
|
||||
auto& scheduler = kernel.GlobalSchedulerContext();
|
||||
scheduler.AddThread(thread);
|
||||
}
|
||||
if (owner_process) {
|
||||
thread->tls_address = thread->owner_process->CreateTLSRegion();
|
||||
thread->owner_process->RegisterThread(thread.get());
|
||||
} else {
|
||||
thread->tls_address = 0;
|
||||
}
|
||||
|
||||
// TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
|
||||
// to initialize the context
|
||||
if ((type_flags & THREADTYPE_HLE) == 0) {
|
||||
ResetThreadContext32(thread->context_32, static_cast<u32>(stack_top),
|
||||
static_cast<u32>(entry_point), static_cast<u32>(arg));
|
||||
ResetThreadContext64(thread->context_64, stack_top, entry_point, arg);
|
||||
}
|
||||
thread->host_context =
|
||||
std::make_shared<Common::Fiber>(std::move(thread_start_func), thread_start_parameter);
|
||||
|
||||
return MakeResult<std::shared_ptr<Thread>>(std::move(thread));
|
||||
}
|
||||
|
||||
void Thread::SetBasePriority(u32 priority) {
|
||||
ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST,
|
||||
"Invalid priority value.");
|
||||
|
||||
KScopedSchedulerLock lock(kernel);
|
||||
|
||||
// Change our base priority.
|
||||
base_priority = priority;
|
||||
|
||||
// Perform a priority restoration.
|
||||
RestorePriority(kernel, this);
|
||||
}
|
||||
|
||||
void Thread::SetSynchronizationResults(KSynchronizationObject* object, ResultCode result) {
|
||||
signaling_object = object;
|
||||
signaling_result = result;
|
||||
}
|
||||
|
||||
VAddr Thread::GetCommandBufferAddress() const {
|
||||
// Offset from the start of TLS at which the IPC command buffer begins.
|
||||
constexpr u64 command_header_offset = 0x80;
|
||||
return GetTLSAddress() + command_header_offset;
|
||||
}
|
||||
|
||||
void Thread::SetState(ThreadState state) {
|
||||
KScopedSchedulerLock sl(kernel);
|
||||
|
||||
// Clear debugging state
|
||||
SetMutexWaitAddressForDebugging({});
|
||||
SetWaitReasonForDebugging({});
|
||||
|
||||
const ThreadState old_state = thread_state;
|
||||
thread_state =
|
||||
static_cast<ThreadState>((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask));
|
||||
if (thread_state != old_state) {
|
||||
KScheduler::OnThreadStateChanged(kernel, this, old_state);
|
||||
}
|
||||
}
|
||||
|
||||
void Thread::AddWaiterImpl(Thread* thread) {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
|
||||
// Find the right spot to insert the waiter.
|
||||
auto it = waiter_list.begin();
|
||||
while (it != waiter_list.end()) {
|
||||
if (it->GetPriority() > thread->GetPriority()) {
|
||||
break;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
|
||||
// Keep track of how many kernel waiters we have.
|
||||
if (Memory::IsKernelAddressKey(thread->GetAddressKey())) {
|
||||
ASSERT((num_kernel_waiters++) >= 0);
|
||||
}
|
||||
|
||||
// Insert the waiter.
|
||||
waiter_list.insert(it, *thread);
|
||||
thread->SetLockOwner(this);
|
||||
}
|
||||
|
||||
void Thread::RemoveWaiterImpl(Thread* thread) {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
|
||||
// Keep track of how many kernel waiters we have.
|
||||
if (Memory::IsKernelAddressKey(thread->GetAddressKey())) {
|
||||
ASSERT((num_kernel_waiters--) > 0);
|
||||
}
|
||||
|
||||
// Remove the waiter.
|
||||
waiter_list.erase(waiter_list.iterator_to(*thread));
|
||||
thread->SetLockOwner(nullptr);
|
||||
}
|
||||
|
||||
void Thread::RestorePriority(KernelCore& kernel, Thread* thread) {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
|
||||
while (true) {
|
||||
// We want to inherit priority where possible.
|
||||
s32 new_priority = thread->GetBasePriority();
|
||||
if (thread->HasWaiters()) {
|
||||
new_priority = std::min(new_priority, thread->waiter_list.front().GetPriority());
|
||||
}
|
||||
|
||||
// If the priority we would inherit is not different from ours, don't do anything.
|
||||
if (new_priority == thread->GetPriority()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure we don't violate condition variable red black tree invariants.
|
||||
if (auto* cv_tree = thread->GetConditionVariableTree(); cv_tree != nullptr) {
|
||||
BeforeUpdatePriority(kernel, cv_tree, thread);
|
||||
}
|
||||
|
||||
// Change the priority.
|
||||
const s32 old_priority = thread->GetPriority();
|
||||
thread->SetPriority(new_priority);
|
||||
|
||||
// Restore the condition variable, if relevant.
|
||||
if (auto* cv_tree = thread->GetConditionVariableTree(); cv_tree != nullptr) {
|
||||
AfterUpdatePriority(kernel, cv_tree, thread);
|
||||
}
|
||||
|
||||
// Update the scheduler.
|
||||
KScheduler::OnThreadPriorityChanged(kernel, thread, old_priority);
|
||||
|
||||
// Keep the lock owner up to date.
|
||||
Thread* lock_owner = thread->GetLockOwner();
|
||||
if (lock_owner == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the thread in the lock owner's sorted list, and continue inheriting.
|
||||
lock_owner->RemoveWaiterImpl(thread);
|
||||
lock_owner->AddWaiterImpl(thread);
|
||||
thread = lock_owner;
|
||||
}
|
||||
}
|
||||
|
||||
void Thread::AddWaiter(Thread* thread) {
|
||||
AddWaiterImpl(thread);
|
||||
RestorePriority(kernel, this);
|
||||
}
|
||||
|
||||
void Thread::RemoveWaiter(Thread* thread) {
|
||||
RemoveWaiterImpl(thread);
|
||||
RestorePriority(kernel, this);
|
||||
}
|
||||
|
||||
Thread* Thread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) {
|
||||
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
||||
|
||||
s32 num_waiters{};
|
||||
Thread* next_lock_owner{};
|
||||
auto it = waiter_list.begin();
|
||||
while (it != waiter_list.end()) {
|
||||
if (it->GetAddressKey() == key) {
|
||||
Thread* thread = std::addressof(*it);
|
||||
|
||||
// Keep track of how many kernel waiters we have.
|
||||
if (Memory::IsKernelAddressKey(thread->GetAddressKey())) {
|
||||
ASSERT((num_kernel_waiters--) > 0);
|
||||
}
|
||||
it = waiter_list.erase(it);
|
||||
|
||||
// Update the next lock owner.
|
||||
if (next_lock_owner == nullptr) {
|
||||
next_lock_owner = thread;
|
||||
next_lock_owner->SetLockOwner(nullptr);
|
||||
} else {
|
||||
next_lock_owner->AddWaiterImpl(thread);
|
||||
}
|
||||
num_waiters++;
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
// Do priority updates, if we have a next owner.
|
||||
if (next_lock_owner) {
|
||||
RestorePriority(kernel, this);
|
||||
RestorePriority(kernel, next_lock_owner);
|
||||
}
|
||||
|
||||
// Return output.
|
||||
*out_num_waiters = num_waiters;
|
||||
return next_lock_owner;
|
||||
}
|
||||
|
||||
ResultCode Thread::SetActivity(ThreadActivity value) {
|
||||
KScopedSchedulerLock lock(kernel);
|
||||
|
||||
auto sched_status = GetState();
|
||||
|
||||
if (sched_status != ThreadState::Runnable && sched_status != ThreadState::Waiting) {
|
||||
return ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (IsTerminationRequested()) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
if (value == ThreadActivity::Paused) {
|
||||
if ((pausing_state & static_cast<u32>(ThreadSchedFlags::ThreadPauseFlag)) != 0) {
|
||||
return ERR_INVALID_STATE;
|
||||
}
|
||||
AddSchedulingFlag(ThreadSchedFlags::ThreadPauseFlag);
|
||||
} else {
|
||||
if ((pausing_state & static_cast<u32>(ThreadSchedFlags::ThreadPauseFlag)) == 0) {
|
||||
return ERR_INVALID_STATE;
|
||||
}
|
||||
RemoveSchedulingFlag(ThreadSchedFlags::ThreadPauseFlag);
|
||||
}
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode Thread::Sleep(s64 nanoseconds) {
|
||||
Handle event_handle{};
|
||||
{
|
||||
KScopedSchedulerLockAndSleep lock(kernel, event_handle, this, nanoseconds);
|
||||
SetState(ThreadState::Waiting);
|
||||
SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Sleep);
|
||||
}
|
||||
|
||||
if (event_handle != InvalidHandle) {
|
||||
auto& time_manager = kernel.TimeManager();
|
||||
time_manager.UnscheduleTimeEvent(event_handle);
|
||||
}
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
void Thread::AddSchedulingFlag(ThreadSchedFlags flag) {
|
||||
const auto old_state = GetRawState();
|
||||
pausing_state |= static_cast<u32>(flag);
|
||||
const auto base_scheduling = GetState();
|
||||
thread_state = base_scheduling | static_cast<ThreadState>(pausing_state);
|
||||
KScheduler::OnThreadStateChanged(kernel, this, old_state);
|
||||
}
|
||||
|
||||
void Thread::RemoveSchedulingFlag(ThreadSchedFlags flag) {
|
||||
const auto old_state = GetRawState();
|
||||
pausing_state &= ~static_cast<u32>(flag);
|
||||
const auto base_scheduling = GetState();
|
||||
thread_state = base_scheduling | static_cast<ThreadState>(pausing_state);
|
||||
KScheduler::OnThreadStateChanged(kernel, this, old_state);
|
||||
}
|
||||
|
||||
ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) {
|
||||
KScopedSchedulerLock lock(kernel);
|
||||
const auto HighestSetCore = [](u64 mask, u32 max_cores) {
|
||||
for (s32 core = static_cast<s32>(max_cores - 1); core >= 0; core--) {
|
||||
if (((mask >> core) & 1) != 0) {
|
||||
return core;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
const bool use_override = affinity_override_count != 0;
|
||||
if (new_core == THREADPROCESSORID_DONT_UPDATE) {
|
||||
new_core = use_override ? ideal_core_override : ideal_core;
|
||||
if ((new_affinity_mask & (1ULL << new_core)) == 0) {
|
||||
LOG_ERROR(Kernel, "New affinity mask is incorrect! new_core={}, new_affinity_mask={}",
|
||||
new_core, new_affinity_mask);
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
}
|
||||
if (use_override) {
|
||||
ideal_core_override = new_core;
|
||||
} else {
|
||||
const auto old_affinity_mask = affinity_mask;
|
||||
affinity_mask.SetAffinityMask(new_affinity_mask);
|
||||
ideal_core = new_core;
|
||||
if (old_affinity_mask.GetAffinityMask() != new_affinity_mask) {
|
||||
const s32 old_core = processor_id;
|
||||
if (processor_id >= 0 && !affinity_mask.GetAffinity(processor_id)) {
|
||||
if (static_cast<s32>(ideal_core) < 0) {
|
||||
processor_id = HighestSetCore(affinity_mask.GetAffinityMask(),
|
||||
Core::Hardware::NUM_CPU_CORES);
|
||||
} else {
|
||||
processor_id = ideal_core;
|
||||
}
|
||||
}
|
||||
KScheduler::OnThreadAffinityMaskChanged(kernel, this, old_affinity_mask, old_core);
|
||||
}
|
||||
}
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -1,782 +0,0 @@
|
||||
// Copyright 2014 Citra Emulator Project / PPSSPP Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/intrusive/list.hpp>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/intrusive_red_black_tree.h"
|
||||
#include "common/spin_lock.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/hle/kernel/k_affinity_mask.h"
|
||||
#include "core/hle/kernel/k_synchronization_object.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/svc_common.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Common {
|
||||
class Fiber;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class ARM_Interface;
|
||||
class System;
|
||||
} // namespace Core
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class GlobalSchedulerContext;
|
||||
class KernelCore;
|
||||
class Process;
|
||||
class KScheduler;
|
||||
|
||||
enum ThreadPriority : u32 {
|
||||
THREADPRIO_HIGHEST = 0, ///< Highest thread priority
|
||||
THREADPRIO_MAX_CORE_MIGRATION = 2, ///< Highest priority for a core migration
|
||||
THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps
|
||||
THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps
|
||||
THREADPRIO_LOWEST = 63, ///< Lowest thread priority
|
||||
THREADPRIO_COUNT = 64, ///< Total number of possible thread priorities.
|
||||
};
|
||||
|
||||
enum ThreadType : u32 {
|
||||
THREADTYPE_USER = 0x1,
|
||||
THREADTYPE_KERNEL = 0x2,
|
||||
THREADTYPE_HLE = 0x4,
|
||||
THREADTYPE_IDLE = 0x8,
|
||||
THREADTYPE_SUSPEND = 0x10,
|
||||
};
|
||||
|
||||
enum ThreadProcessorId : s32 {
|
||||
/// Indicates that no particular processor core is preferred.
|
||||
THREADPROCESSORID_DONT_CARE = -1,
|
||||
|
||||
/// Run thread on the ideal core specified by the process.
|
||||
THREADPROCESSORID_IDEAL = -2,
|
||||
|
||||
/// Indicates that the preferred processor ID shouldn't be updated in
|
||||
/// a core mask setting operation.
|
||||
THREADPROCESSORID_DONT_UPDATE = -3,
|
||||
|
||||
THREADPROCESSORID_0 = 0, ///< Run thread on core 0
|
||||
THREADPROCESSORID_1 = 1, ///< Run thread on core 1
|
||||
THREADPROCESSORID_2 = 2, ///< Run thread on core 2
|
||||
THREADPROCESSORID_3 = 3, ///< Run thread on core 3
|
||||
THREADPROCESSORID_MAX = 4, ///< Processor ID must be less than this
|
||||
|
||||
/// Allowed CPU mask
|
||||
THREADPROCESSORID_DEFAULT_MASK = (1 << THREADPROCESSORID_0) | (1 << THREADPROCESSORID_1) |
|
||||
(1 << THREADPROCESSORID_2) | (1 << THREADPROCESSORID_3)
|
||||
};
|
||||
|
||||
enum class ThreadState : u16 {
|
||||
Initialized = 0,
|
||||
Waiting = 1,
|
||||
Runnable = 2,
|
||||
Terminated = 3,
|
||||
|
||||
SuspendShift = 4,
|
||||
Mask = (1 << SuspendShift) - 1,
|
||||
|
||||
ProcessSuspended = (1 << (0 + SuspendShift)),
|
||||
ThreadSuspended = (1 << (1 + SuspendShift)),
|
||||
DebugSuspended = (1 << (2 + SuspendShift)),
|
||||
BacktraceSuspended = (1 << (3 + SuspendShift)),
|
||||
InitSuspended = (1 << (4 + SuspendShift)),
|
||||
|
||||
SuspendFlagMask = ((1 << 5) - 1) << SuspendShift,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(ThreadState);
|
||||
|
||||
enum class ThreadWakeupReason {
|
||||
Signal, // The thread was woken up by WakeupAllWaitingThreads due to an object signal.
|
||||
Timeout // The thread was woken up due to a wait timeout.
|
||||
};
|
||||
|
||||
enum class ThreadActivity : u32 {
|
||||
Normal = 0,
|
||||
Paused = 1,
|
||||
};
|
||||
|
||||
enum class ThreadSchedFlags : u32 {
|
||||
ProcessPauseFlag = 1 << 4,
|
||||
ThreadPauseFlag = 1 << 5,
|
||||
ProcessDebugPauseFlag = 1 << 6,
|
||||
KernelInitPauseFlag = 1 << 8,
|
||||
};
|
||||
|
||||
enum class ThreadWaitReasonForDebugging : u32 {
|
||||
None, ///< Thread is not waiting
|
||||
Sleep, ///< Thread is waiting due to a SleepThread SVC
|
||||
IPC, ///< Thread is waiting for the reply from an IPC request
|
||||
Synchronization, ///< Thread is waiting due to a WaitSynchronization SVC
|
||||
ConditionVar, ///< Thread is waiting due to a WaitProcessWideKey SVC
|
||||
Arbitration, ///< Thread is waiting due to a SignalToAddress/WaitForAddress SVC
|
||||
Suspended, ///< Thread is waiting due to process suspension
|
||||
};
|
||||
|
||||
class Thread final : public KSynchronizationObject, public boost::intrusive::list_base_hook<> {
|
||||
friend class KScheduler;
|
||||
friend class Process;
|
||||
|
||||
public:
|
||||
explicit Thread(KernelCore& kernel);
|
||||
~Thread() override;
|
||||
|
||||
using MutexWaitingThreads = std::vector<std::shared_ptr<Thread>>;
|
||||
|
||||
using ThreadContext32 = Core::ARM_Interface::ThreadContext32;
|
||||
using ThreadContext64 = Core::ARM_Interface::ThreadContext64;
|
||||
|
||||
/**
|
||||
* Creates and returns a new thread. The new thread is immediately scheduled
|
||||
* @param system The instance of the whole system
|
||||
* @param name The friendly name desired for the thread
|
||||
* @param entry_point The address at which the thread should start execution
|
||||
* @param priority The thread's priority
|
||||
* @param arg User data to pass to the thread
|
||||
* @param processor_id The ID(s) of the processors on which the thread is desired to be run
|
||||
* @param stack_top The address of the thread's stack top
|
||||
* @param owner_process The parent process for the thread, if null, it's a kernel thread
|
||||
* @return A shared pointer to the newly created thread
|
||||
*/
|
||||
static ResultVal<std::shared_ptr<Thread>> Create(Core::System& system, ThreadType type_flags,
|
||||
std::string name, VAddr entry_point,
|
||||
u32 priority, u64 arg, s32 processor_id,
|
||||
VAddr stack_top, Process* owner_process);
|
||||
|
||||
/**
|
||||
* Creates and returns a new thread. The new thread is immediately scheduled
|
||||
* @param system The instance of the whole system
|
||||
* @param name The friendly name desired for the thread
|
||||
* @param entry_point The address at which the thread should start execution
|
||||
* @param priority The thread's priority
|
||||
* @param arg User data to pass to the thread
|
||||
* @param processor_id The ID(s) of the processors on which the thread is desired to be run
|
||||
* @param stack_top The address of the thread's stack top
|
||||
* @param owner_process The parent process for the thread, if null, it's a kernel thread
|
||||
* @param thread_start_func The function where the host context will start.
|
||||
* @param thread_start_parameter The parameter which will passed to host context on init
|
||||
* @return A shared pointer to the newly created thread
|
||||
*/
|
||||
static ResultVal<std::shared_ptr<Thread>> Create(Core::System& system, ThreadType type_flags,
|
||||
std::string name, VAddr entry_point,
|
||||
u32 priority, u64 arg, s32 processor_id,
|
||||
VAddr stack_top, Process* owner_process,
|
||||
std::function<void(void*)>&& thread_start_func,
|
||||
void* thread_start_parameter);
|
||||
|
||||
std::string GetName() const override {
|
||||
return name;
|
||||
}
|
||||
|
||||
void SetName(std::string new_name) {
|
||||
name = std::move(new_name);
|
||||
}
|
||||
|
||||
std::string GetTypeName() const override {
|
||||
return "Thread";
|
||||
}
|
||||
|
||||
static constexpr HandleType HANDLE_TYPE = HandleType::Thread;
|
||||
HandleType GetHandleType() const override {
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the thread's current priority
|
||||
* @return The current thread's priority
|
||||
*/
|
||||
[[nodiscard]] s32 GetPriority() const {
|
||||
return current_priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the thread's current priority.
|
||||
* @param priority The new priority.
|
||||
*/
|
||||
void SetPriority(s32 priority) {
|
||||
current_priority = priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the thread's nominal priority.
|
||||
* @return The current thread's nominal priority.
|
||||
*/
|
||||
[[nodiscard]] s32 GetBasePriority() const {
|
||||
return base_priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the thread's nominal priority.
|
||||
* @param priority The new priority.
|
||||
*/
|
||||
void SetBasePriority(u32 priority);
|
||||
|
||||
/// Changes the core that the thread is running or scheduled to run on.
|
||||
[[nodiscard]] ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask);
|
||||
|
||||
/**
|
||||
* Gets the thread's thread ID
|
||||
* @return The thread's ID
|
||||
*/
|
||||
[[nodiscard]] u64 GetThreadID() const {
|
||||
return thread_id;
|
||||
}
|
||||
|
||||
/// Resumes a thread from waiting
|
||||
void Wakeup();
|
||||
|
||||
ResultCode Start();
|
||||
|
||||
virtual bool IsSignaled() const override;
|
||||
|
||||
/// Cancels a waiting operation that this thread may or may not be within.
|
||||
///
|
||||
/// When the thread is within a waiting state, this will set the thread's
|
||||
/// waiting result to signal a canceled wait. The function will then resume
|
||||
/// this thread.
|
||||
///
|
||||
void CancelWait();
|
||||
|
||||
void SetSynchronizationResults(KSynchronizationObject* object, ResultCode result);
|
||||
|
||||
void SetSyncedObject(KSynchronizationObject* object, ResultCode result) {
|
||||
SetSynchronizationResults(object, result);
|
||||
}
|
||||
|
||||
ResultCode GetWaitResult(KSynchronizationObject** out) const {
|
||||
*out = signaling_object;
|
||||
return signaling_result;
|
||||
}
|
||||
|
||||
ResultCode GetSignalingResult() const {
|
||||
return signaling_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops a thread, invalidating it from further use
|
||||
*/
|
||||
void Stop();
|
||||
|
||||
/*
|
||||
* Returns the Thread Local Storage address of the current thread
|
||||
* @returns VAddr of the thread's TLS
|
||||
*/
|
||||
VAddr GetTLSAddress() const {
|
||||
return tls_address;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the value of the TPIDR_EL0 Read/Write system register for this thread.
|
||||
* @returns The value of the TPIDR_EL0 register.
|
||||
*/
|
||||
u64 GetTPIDR_EL0() const {
|
||||
return tpidr_el0;
|
||||
}
|
||||
|
||||
/// Sets the value of the TPIDR_EL0 Read/Write system register for this thread.
|
||||
void SetTPIDR_EL0(u64 value) {
|
||||
tpidr_el0 = value;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the address of the current thread's command buffer, located in the TLS.
|
||||
* @returns VAddr of the thread's command buffer.
|
||||
*/
|
||||
VAddr GetCommandBufferAddress() const;
|
||||
|
||||
ThreadContext32& GetContext32() {
|
||||
return context_32;
|
||||
}
|
||||
|
||||
const ThreadContext32& GetContext32() const {
|
||||
return context_32;
|
||||
}
|
||||
|
||||
ThreadContext64& GetContext64() {
|
||||
return context_64;
|
||||
}
|
||||
|
||||
const ThreadContext64& GetContext64() const {
|
||||
return context_64;
|
||||
}
|
||||
|
||||
bool IsHLEThread() const {
|
||||
return (type & THREADTYPE_HLE) != 0;
|
||||
}
|
||||
|
||||
bool IsSuspendThread() const {
|
||||
return (type & THREADTYPE_SUSPEND) != 0;
|
||||
}
|
||||
|
||||
bool IsIdleThread() const {
|
||||
return (type & THREADTYPE_IDLE) != 0;
|
||||
}
|
||||
|
||||
bool WasRunning() const {
|
||||
return was_running;
|
||||
}
|
||||
|
||||
void SetWasRunning(bool value) {
|
||||
was_running = value;
|
||||
}
|
||||
|
||||
std::shared_ptr<Common::Fiber>& GetHostContext();
|
||||
|
||||
ThreadState GetState() const {
|
||||
return thread_state & ThreadState::Mask;
|
||||
}
|
||||
|
||||
ThreadState GetRawState() const {
|
||||
return thread_state;
|
||||
}
|
||||
|
||||
void SetState(ThreadState state);
|
||||
|
||||
s64 GetLastScheduledTick() const {
|
||||
return last_scheduled_tick;
|
||||
}
|
||||
|
||||
void SetLastScheduledTick(s64 tick) {
|
||||
last_scheduled_tick = tick;
|
||||
}
|
||||
|
||||
u64 GetTotalCPUTimeTicks() const {
|
||||
return total_cpu_time_ticks;
|
||||
}
|
||||
|
||||
void UpdateCPUTimeTicks(u64 ticks) {
|
||||
total_cpu_time_ticks += ticks;
|
||||
}
|
||||
|
||||
s32 GetProcessorID() const {
|
||||
return processor_id;
|
||||
}
|
||||
|
||||
s32 GetActiveCore() const {
|
||||
return GetProcessorID();
|
||||
}
|
||||
|
||||
void SetProcessorID(s32 new_core) {
|
||||
processor_id = new_core;
|
||||
}
|
||||
|
||||
void SetActiveCore(s32 new_core) {
|
||||
processor_id = new_core;
|
||||
}
|
||||
|
||||
Process* GetOwnerProcess() {
|
||||
return owner_process;
|
||||
}
|
||||
|
||||
const Process* GetOwnerProcess() const {
|
||||
return owner_process;
|
||||
}
|
||||
|
||||
const MutexWaitingThreads& GetMutexWaitingThreads() const {
|
||||
return wait_mutex_threads;
|
||||
}
|
||||
|
||||
Thread* GetLockOwner() const {
|
||||
return lock_owner;
|
||||
}
|
||||
|
||||
void SetLockOwner(Thread* owner) {
|
||||
lock_owner = owner;
|
||||
}
|
||||
|
||||
u32 GetIdealCore() const {
|
||||
return ideal_core;
|
||||
}
|
||||
|
||||
const KAffinityMask& GetAffinityMask() const {
|
||||
return affinity_mask;
|
||||
}
|
||||
|
||||
ResultCode SetActivity(ThreadActivity value);
|
||||
|
||||
/// Sleeps this thread for the given amount of nanoseconds.
|
||||
ResultCode Sleep(s64 nanoseconds);
|
||||
|
||||
s64 GetYieldScheduleCount() const {
|
||||
return schedule_count;
|
||||
}
|
||||
|
||||
void SetYieldScheduleCount(s64 count) {
|
||||
schedule_count = count;
|
||||
}
|
||||
|
||||
bool IsRunning() const {
|
||||
return is_running;
|
||||
}
|
||||
|
||||
void SetIsRunning(bool value) {
|
||||
is_running = value;
|
||||
}
|
||||
|
||||
bool IsWaitCancelled() const {
|
||||
return is_sync_cancelled;
|
||||
}
|
||||
|
||||
void ClearWaitCancelled() {
|
||||
is_sync_cancelled = false;
|
||||
}
|
||||
|
||||
Handle GetGlobalHandle() const {
|
||||
return global_handle;
|
||||
}
|
||||
|
||||
bool IsCancellable() const {
|
||||
return is_cancellable;
|
||||
}
|
||||
|
||||
void SetCancellable() {
|
||||
is_cancellable = true;
|
||||
}
|
||||
|
||||
void ClearCancellable() {
|
||||
is_cancellable = false;
|
||||
}
|
||||
|
||||
bool IsTerminationRequested() const {
|
||||
return will_be_terminated || GetRawState() == ThreadState::Terminated;
|
||||
}
|
||||
|
||||
bool IsPaused() const {
|
||||
return pausing_state != 0;
|
||||
}
|
||||
|
||||
bool IsContinuousOnSVC() const {
|
||||
return is_continuous_on_svc;
|
||||
}
|
||||
|
||||
void SetContinuousOnSVC(bool is_continuous) {
|
||||
is_continuous_on_svc = is_continuous;
|
||||
}
|
||||
|
||||
bool IsPhantomMode() const {
|
||||
return is_phantom_mode;
|
||||
}
|
||||
|
||||
void SetPhantomMode(bool phantom) {
|
||||
is_phantom_mode = phantom;
|
||||
}
|
||||
|
||||
bool HasExited() const {
|
||||
return has_exited;
|
||||
}
|
||||
|
||||
class QueueEntry {
|
||||
public:
|
||||
constexpr QueueEntry() = default;
|
||||
|
||||
constexpr void Initialize() {
|
||||
prev = nullptr;
|
||||
next = nullptr;
|
||||
}
|
||||
|
||||
constexpr Thread* GetPrev() const {
|
||||
return prev;
|
||||
}
|
||||
constexpr Thread* GetNext() const {
|
||||
return next;
|
||||
}
|
||||
constexpr void SetPrev(Thread* thread) {
|
||||
prev = thread;
|
||||
}
|
||||
constexpr void SetNext(Thread* thread) {
|
||||
next = thread;
|
||||
}
|
||||
|
||||
private:
|
||||
Thread* prev{};
|
||||
Thread* next{};
|
||||
};
|
||||
|
||||
QueueEntry& GetPriorityQueueEntry(s32 core) {
|
||||
return per_core_priority_queue_entry[core];
|
||||
}
|
||||
|
||||
const QueueEntry& GetPriorityQueueEntry(s32 core) const {
|
||||
return per_core_priority_queue_entry[core];
|
||||
}
|
||||
|
||||
s32 GetDisableDispatchCount() const {
|
||||
return disable_count;
|
||||
}
|
||||
|
||||
void DisableDispatch() {
|
||||
ASSERT(GetDisableDispatchCount() >= 0);
|
||||
disable_count++;
|
||||
}
|
||||
|
||||
void EnableDispatch() {
|
||||
ASSERT(GetDisableDispatchCount() > 0);
|
||||
disable_count--;
|
||||
}
|
||||
|
||||
void SetWaitReasonForDebugging(ThreadWaitReasonForDebugging reason) {
|
||||
wait_reason_for_debugging = reason;
|
||||
}
|
||||
|
||||
[[nodiscard]] ThreadWaitReasonForDebugging GetWaitReasonForDebugging() const {
|
||||
return wait_reason_for_debugging;
|
||||
}
|
||||
|
||||
void SetWaitObjectsForDebugging(const std::span<KSynchronizationObject*>& objects) {
|
||||
wait_objects_for_debugging.clear();
|
||||
wait_objects_for_debugging.reserve(objects.size());
|
||||
for (const auto& object : objects) {
|
||||
wait_objects_for_debugging.emplace_back(object);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<KSynchronizationObject*>& GetWaitObjectsForDebugging() const {
|
||||
return wait_objects_for_debugging;
|
||||
}
|
||||
|
||||
void SetMutexWaitAddressForDebugging(VAddr address) {
|
||||
mutex_wait_address_for_debugging = address;
|
||||
}
|
||||
|
||||
[[nodiscard]] VAddr GetMutexWaitAddressForDebugging() const {
|
||||
return mutex_wait_address_for_debugging;
|
||||
}
|
||||
|
||||
void AddWaiter(Thread* thread);
|
||||
|
||||
void RemoveWaiter(Thread* thread);
|
||||
|
||||
[[nodiscard]] Thread* RemoveWaiterByKey(s32* out_num_waiters, VAddr key);
|
||||
|
||||
[[nodiscard]] VAddr GetAddressKey() const {
|
||||
return address_key;
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 GetAddressKeyValue() const {
|
||||
return address_key_value;
|
||||
}
|
||||
|
||||
void SetAddressKey(VAddr key) {
|
||||
address_key = key;
|
||||
}
|
||||
|
||||
void SetAddressKey(VAddr key, u32 val) {
|
||||
address_key = key;
|
||||
address_key_value = val;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr size_t PriorityInheritanceCountMax = 10;
|
||||
union SyncObjectBuffer {
|
||||
std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> sync_objects{};
|
||||
std::array<Handle,
|
||||
Svc::ArgumentHandleCountMax*(sizeof(KSynchronizationObject*) / sizeof(Handle))>
|
||||
handles;
|
||||
constexpr SyncObjectBuffer() {}
|
||||
};
|
||||
static_assert(sizeof(SyncObjectBuffer::sync_objects) == sizeof(SyncObjectBuffer::handles));
|
||||
|
||||
struct ConditionVariableComparator {
|
||||
struct LightCompareType {
|
||||
u64 cv_key{};
|
||||
s32 priority{};
|
||||
|
||||
[[nodiscard]] constexpr u64 GetConditionVariableKey() const {
|
||||
return cv_key;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr s32 GetPriority() const {
|
||||
return priority;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
requires(
|
||||
std::same_as<T, Thread> ||
|
||||
std::same_as<T, LightCompareType>) static constexpr int Compare(const T& lhs,
|
||||
const Thread& rhs) {
|
||||
const uintptr_t l_key = lhs.GetConditionVariableKey();
|
||||
const uintptr_t r_key = rhs.GetConditionVariableKey();
|
||||
|
||||
if (l_key < r_key) {
|
||||
// Sort first by key
|
||||
return -1;
|
||||
} else if (l_key == r_key && lhs.GetPriority() < rhs.GetPriority()) {
|
||||
// And then by priority.
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Common::IntrusiveRedBlackTreeNode condvar_arbiter_tree_node{};
|
||||
|
||||
using ConditionVariableThreadTreeTraits =
|
||||
Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&Thread::condvar_arbiter_tree_node>;
|
||||
using ConditionVariableThreadTree =
|
||||
ConditionVariableThreadTreeTraits::TreeType<ConditionVariableComparator>;
|
||||
|
||||
public:
|
||||
using ConditionVariableThreadTreeType = ConditionVariableThreadTree;
|
||||
|
||||
[[nodiscard]] uintptr_t GetConditionVariableKey() const {
|
||||
return condvar_key;
|
||||
}
|
||||
|
||||
[[nodiscard]] uintptr_t GetAddressArbiterKey() const {
|
||||
return condvar_key;
|
||||
}
|
||||
|
||||
void SetConditionVariable(ConditionVariableThreadTree* tree, VAddr address, uintptr_t cv_key,
|
||||
u32 value) {
|
||||
condvar_tree = tree;
|
||||
condvar_key = cv_key;
|
||||
address_key = address;
|
||||
address_key_value = value;
|
||||
}
|
||||
|
||||
void ClearConditionVariable() {
|
||||
condvar_tree = nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsWaitingForConditionVariable() const {
|
||||
return condvar_tree != nullptr;
|
||||
}
|
||||
|
||||
void SetAddressArbiter(ConditionVariableThreadTree* tree, uintptr_t address) {
|
||||
condvar_tree = tree;
|
||||
condvar_key = address;
|
||||
}
|
||||
|
||||
void ClearAddressArbiter() {
|
||||
condvar_tree = nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsWaitingForAddressArbiter() const {
|
||||
return condvar_tree != nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] ConditionVariableThreadTree* GetConditionVariableTree() const {
|
||||
return condvar_tree;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool HasWaiters() const {
|
||||
return !waiter_list.empty();
|
||||
}
|
||||
|
||||
private:
|
||||
void AddSchedulingFlag(ThreadSchedFlags flag);
|
||||
void RemoveSchedulingFlag(ThreadSchedFlags flag);
|
||||
void AddWaiterImpl(Thread* thread);
|
||||
void RemoveWaiterImpl(Thread* thread);
|
||||
static void RestorePriority(KernelCore& kernel, Thread* thread);
|
||||
|
||||
Common::SpinLock context_guard{};
|
||||
ThreadContext32 context_32{};
|
||||
ThreadContext64 context_64{};
|
||||
std::shared_ptr<Common::Fiber> host_context{};
|
||||
|
||||
ThreadState thread_state = ThreadState::Initialized;
|
||||
|
||||
u64 thread_id = 0;
|
||||
|
||||
VAddr entry_point = 0;
|
||||
VAddr stack_top = 0;
|
||||
std::atomic_int disable_count = 0;
|
||||
|
||||
ThreadType type;
|
||||
|
||||
/// Nominal thread priority, as set by the emulated application.
|
||||
/// The nominal priority is the thread priority without priority
|
||||
/// inheritance taken into account.
|
||||
s32 base_priority{};
|
||||
|
||||
/// Current thread priority. This may change over the course of the
|
||||
/// thread's lifetime in order to facilitate priority inheritance.
|
||||
s32 current_priority{};
|
||||
|
||||
u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks.
|
||||
s64 schedule_count{};
|
||||
s64 last_scheduled_tick{};
|
||||
|
||||
s32 processor_id = 0;
|
||||
|
||||
VAddr tls_address = 0; ///< Virtual address of the Thread Local Storage of the thread
|
||||
u64 tpidr_el0 = 0; ///< TPIDR_EL0 read/write system register.
|
||||
|
||||
/// Process that owns this thread
|
||||
Process* owner_process;
|
||||
|
||||
/// Objects that the thread is waiting on, in the same order as they were
|
||||
/// passed to WaitSynchronization. This is used for debugging only.
|
||||
std::vector<KSynchronizationObject*> wait_objects_for_debugging;
|
||||
|
||||
/// The current mutex wait address. This is used for debugging only.
|
||||
VAddr mutex_wait_address_for_debugging{};
|
||||
|
||||
/// The reason the thread is waiting. This is used for debugging only.
|
||||
ThreadWaitReasonForDebugging wait_reason_for_debugging{};
|
||||
|
||||
KSynchronizationObject* signaling_object;
|
||||
ResultCode signaling_result{RESULT_SUCCESS};
|
||||
|
||||
/// List of threads that are waiting for a mutex that is held by this thread.
|
||||
MutexWaitingThreads wait_mutex_threads;
|
||||
|
||||
/// Thread that owns the lock that this thread is waiting for.
|
||||
Thread* lock_owner{};
|
||||
|
||||
/// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
|
||||
Handle global_handle = 0;
|
||||
|
||||
KScheduler* scheduler = nullptr;
|
||||
|
||||
std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> per_core_priority_queue_entry{};
|
||||
|
||||
u32 ideal_core{0xFFFFFFFF};
|
||||
KAffinityMask affinity_mask{};
|
||||
|
||||
s32 ideal_core_override = -1;
|
||||
u32 affinity_override_count = 0;
|
||||
|
||||
u32 pausing_state = 0;
|
||||
bool is_running = false;
|
||||
bool is_cancellable = false;
|
||||
bool is_sync_cancelled = false;
|
||||
|
||||
bool is_continuous_on_svc = false;
|
||||
|
||||
bool will_be_terminated = false;
|
||||
bool is_phantom_mode = false;
|
||||
bool has_exited = false;
|
||||
|
||||
bool was_running = false;
|
||||
|
||||
bool signaled{};
|
||||
|
||||
ConditionVariableThreadTree* condvar_tree{};
|
||||
uintptr_t condvar_key{};
|
||||
VAddr address_key{};
|
||||
u32 address_key_value{};
|
||||
s32 num_kernel_waiters{};
|
||||
|
||||
using WaiterList = boost::intrusive::list<Thread>;
|
||||
WaiterList waiter_list{};
|
||||
WaiterList pinned_waiter_list{};
|
||||
|
||||
std::string name;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -8,8 +8,8 @@
|
||||
#include "core/core_timing_util.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/time_manager.h"
|
||||
|
||||
namespace Kernel {
|
||||
@@ -18,50 +18,30 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} {
|
||||
time_manager_event_type = Core::Timing::CreateEvent(
|
||||
"Kernel::TimeManagerCallback",
|
||||
[this](std::uintptr_t thread_handle, std::chrono::nanoseconds) {
|
||||
std::shared_ptr<Thread> thread;
|
||||
std::shared_ptr<KThread> thread;
|
||||
{
|
||||
std::lock_guard lock{mutex};
|
||||
const auto proper_handle = static_cast<Handle>(thread_handle);
|
||||
if (cancelled_events[proper_handle]) {
|
||||
return;
|
||||
}
|
||||
thread = system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle);
|
||||
}
|
||||
|
||||
if (thread) {
|
||||
// Thread can be null if process has exited
|
||||
thread->Wakeup();
|
||||
thread = SharedFrom<KThread>(reinterpret_cast<KThread*>(thread_handle));
|
||||
}
|
||||
thread->Wakeup();
|
||||
});
|
||||
}
|
||||
|
||||
void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 nanoseconds) {
|
||||
void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) {
|
||||
std::lock_guard lock{mutex};
|
||||
event_handle = timetask->GetGlobalHandle();
|
||||
if (nanoseconds > 0) {
|
||||
ASSERT(timetask);
|
||||
ASSERT(timetask->GetState() != ThreadState::Runnable);
|
||||
ASSERT(thread);
|
||||
ASSERT(thread->GetState() != ThreadState::Runnable);
|
||||
system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{nanoseconds},
|
||||
time_manager_event_type, event_handle);
|
||||
} else {
|
||||
event_handle = InvalidHandle;
|
||||
time_manager_event_type,
|
||||
reinterpret_cast<uintptr_t>(thread));
|
||||
}
|
||||
cancelled_events[event_handle] = false;
|
||||
}
|
||||
|
||||
void TimeManager::UnscheduleTimeEvent(Handle event_handle) {
|
||||
void TimeManager::UnscheduleTimeEvent(KThread* thread) {
|
||||
std::lock_guard lock{mutex};
|
||||
if (event_handle == InvalidHandle) {
|
||||
return;
|
||||
}
|
||||
system.CoreTiming().UnscheduleEvent(time_manager_event_type, event_handle);
|
||||
cancelled_events[event_handle] = true;
|
||||
}
|
||||
|
||||
void TimeManager::CancelTimeEvent(Thread* time_task) {
|
||||
std::lock_guard lock{mutex};
|
||||
const Handle event_handle = time_task->GetGlobalHandle();
|
||||
UnscheduleTimeEvent(event_handle);
|
||||
system.CoreTiming().UnscheduleEvent(time_manager_event_type,
|
||||
reinterpret_cast<uintptr_t>(thread));
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -20,7 +20,7 @@ struct EventType;
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class Thread;
|
||||
class KThread;
|
||||
|
||||
/**
|
||||
* The `TimeManager` takes care of scheduling time events on threads and executes their TimeUp
|
||||
@@ -31,18 +31,14 @@ public:
|
||||
explicit TimeManager(Core::System& system);
|
||||
|
||||
/// Schedule a time event on `timetask` thread that will expire in 'nanoseconds'
|
||||
/// returns a non-invalid handle in `event_handle` if correctly scheduled
|
||||
void ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 nanoseconds);
|
||||
void ScheduleTimeEvent(KThread* time_task, s64 nanoseconds);
|
||||
|
||||
/// Unschedule an existing time event
|
||||
void UnscheduleTimeEvent(Handle event_handle);
|
||||
|
||||
void CancelTimeEvent(Thread* time_task);
|
||||
void UnscheduleTimeEvent(KThread* thread);
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
std::shared_ptr<Core::Timing::EventType> time_manager_event_type;
|
||||
std::unordered_map<Handle, bool> cancelled_events;
|
||||
std::mutex mutex;
|
||||
};
|
||||
|
||||
|
||||
@@ -72,6 +72,8 @@ public:
|
||||
/// is closed.
|
||||
ResultCode Reset();
|
||||
|
||||
void Finalize() override {}
|
||||
|
||||
private:
|
||||
/// The base address for the memory managed by this instance.
|
||||
VAddr base_address{};
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include "common/assert.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
|
||||
namespace Kernel {
|
||||
@@ -38,8 +38,4 @@ void WritableEvent::Clear() {
|
||||
readable->Clear();
|
||||
}
|
||||
|
||||
bool WritableEvent::IsSignaled() const {
|
||||
return readable->IsSignaled();
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -46,7 +46,8 @@ public:
|
||||
|
||||
void Signal();
|
||||
void Clear();
|
||||
bool IsSignaled() const;
|
||||
|
||||
void Finalize() override {}
|
||||
|
||||
private:
|
||||
explicit WritableEvent(KernelCore& kernel);
|
||||
|
||||
@@ -32,9 +32,15 @@
|
||||
|
||||
namespace Service::Account {
|
||||
|
||||
constexpr ResultCode ERR_INVALID_BUFFER_SIZE{ErrorModule::Account, 30};
|
||||
constexpr ResultCode ERR_INVALID_USER_ID{ErrorModule::Account, 20};
|
||||
constexpr ResultCode ERR_INVALID_APPLICATION_ID{ErrorModule::Account, 22};
|
||||
constexpr ResultCode ERR_INVALID_BUFFER{ErrorModule::Account, 30};
|
||||
constexpr ResultCode ERR_INVALID_BUFFER_SIZE{ErrorModule::Account, 31};
|
||||
constexpr ResultCode ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100};
|
||||
|
||||
// Thumbnails are hard coded to be at least this size
|
||||
constexpr std::size_t THUMBNAIL_SIZE = 0x24000;
|
||||
|
||||
static std::string GetImagePath(Common::UUID uuid) {
|
||||
return Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
|
||||
"/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
|
||||
@@ -369,7 +375,7 @@ protected:
|
||||
if (user_data.size() < sizeof(ProfileData)) {
|
||||
LOG_ERROR(Service_ACC, "ProfileData buffer too small!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_INVALID_BUFFER_SIZE);
|
||||
rb.Push(ERR_INVALID_BUFFER);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -402,7 +408,7 @@ protected:
|
||||
if (user_data.size() < sizeof(ProfileData)) {
|
||||
LOG_ERROR(Service_ACC, "ProfileData buffer too small!");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_INVALID_BUFFER_SIZE);
|
||||
rb.Push(ERR_INVALID_BUFFER);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -534,7 +540,7 @@ private:
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
Common::UUID user_id;
|
||||
Common::UUID user_id{Common::INVALID_UUID};
|
||||
};
|
||||
|
||||
// 6.0.0+
|
||||
@@ -811,6 +817,55 @@ void Module::Interface::ListOpenContextStoredUsers(Kernel::HLERequestContext& ct
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Module::Interface::StoreSaveDataThumbnailApplication(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto uuid = rp.PopRaw<Common::UUID>();
|
||||
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called, uuid={}", uuid.Format());
|
||||
|
||||
// TODO(ogniK): Check if application ID is zero on acc initialize. As we don't have a reliable
|
||||
// way of confirming things like the TID, we're going to assume a non zero value for the time
|
||||
// being.
|
||||
constexpr u64 tid{1};
|
||||
StoreSaveDataThumbnail(ctx, uuid, tid);
|
||||
}
|
||||
|
||||
void Module::Interface::StoreSaveDataThumbnailSystem(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto uuid = rp.PopRaw<Common::UUID>();
|
||||
const auto tid = rp.Pop<u64_le>();
|
||||
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called, uuid={}, tid={:016X}", uuid.Format(), tid);
|
||||
StoreSaveDataThumbnail(ctx, uuid, tid);
|
||||
}
|
||||
|
||||
void Module::Interface::StoreSaveDataThumbnail(Kernel::HLERequestContext& ctx,
|
||||
const Common::UUID& uuid, const u64 tid) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
|
||||
if (tid == 0) {
|
||||
LOG_ERROR(Service_ACC, "TitleID is not valid!");
|
||||
rb.Push(ERR_INVALID_APPLICATION_ID);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!uuid) {
|
||||
LOG_ERROR(Service_ACC, "User ID is not valid!");
|
||||
rb.Push(ERR_INVALID_USER_ID);
|
||||
return;
|
||||
}
|
||||
const auto thumbnail_size = ctx.GetReadBufferSize();
|
||||
if (thumbnail_size != THUMBNAIL_SIZE) {
|
||||
LOG_ERROR(Service_ACC, "Buffer size is empty! size={:X} expecting {:X}", thumbnail_size,
|
||||
THUMBNAIL_SIZE);
|
||||
rb.Push(ERR_INVALID_BUFFER_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(ogniK): Construct save data thumbnail
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
// A u8 is passed into this function which we can safely ignore. It's to determine if we have
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/uuid.h"
|
||||
#include "core/hle/service/glue/manager.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
@@ -36,9 +37,13 @@ public:
|
||||
void ListQualifiedUsers(Kernel::HLERequestContext& ctx);
|
||||
void LoadOpenContext(Kernel::HLERequestContext& ctx);
|
||||
void ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx);
|
||||
void StoreSaveDataThumbnailApplication(Kernel::HLERequestContext& ctx);
|
||||
void StoreSaveDataThumbnailSystem(Kernel::HLERequestContext& ctx);
|
||||
|
||||
private:
|
||||
ResultCode InitializeApplicationInfoBase();
|
||||
void StoreSaveDataThumbnail(Kernel::HLERequestContext& ctx, const Common::UUID& uuid,
|
||||
const u64 tid);
|
||||
|
||||
enum class ApplicationType : u32_le {
|
||||
GameCard = 0,
|
||||
|
||||
@@ -29,7 +29,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
|
||||
{104, nullptr, "GetProfileUpdateNotifier"},
|
||||
{105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, // 4.0.0+
|
||||
{106, nullptr, "GetProfileSyncNotifier"}, // 9.0.0+
|
||||
{110, nullptr, "StoreSaveDataThumbnail"},
|
||||
{110, &ACC_SU::StoreSaveDataThumbnailSystem, "StoreSaveDataThumbnail"},
|
||||
{111, nullptr, "ClearSaveDataThumbnail"},
|
||||
{112, nullptr, "LoadSaveDataThumbnail"},
|
||||
{113, nullptr, "GetSaveDataThumbnailExistence"}, // 5.0.0+
|
||||
|
||||
@@ -26,7 +26,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
|
||||
{101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"},
|
||||
{102, nullptr, "AuthenticateApplicationAsync"},
|
||||
{103, nullptr, "CheckNetworkServiceAvailabilityAsync"}, // 4.0.0+
|
||||
{110, nullptr, "StoreSaveDataThumbnail"},
|
||||
{110, &ACC_U0::StoreSaveDataThumbnailApplication, "StoreSaveDataThumbnail"},
|
||||
{111, nullptr, "ClearSaveDataThumbnail"},
|
||||
{120, nullptr, "CreateGuestLoginRequest"},
|
||||
{130, &ACC_U0::LoadOpenContext, "LoadOpenContext"}, // 5.0.0+
|
||||
|
||||
@@ -29,7 +29,7 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
|
||||
{104, nullptr, "GetProfileUpdateNotifier"},
|
||||
{105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, // 4.0.0+
|
||||
{106, nullptr, "GetProfileSyncNotifier"}, // 9.0.0+
|
||||
{110, nullptr, "StoreSaveDataThumbnail"},
|
||||
{110, &ACC_U1::StoreSaveDataThumbnailApplication, "StoreSaveDataThumbnail"},
|
||||
{111, nullptr, "ClearSaveDataThumbnail"},
|
||||
{112, nullptr, "LoadSaveDataThumbnail"},
|
||||
{113, nullptr, "GetSaveDataThumbnailExistence"}, // 5.0.0+
|
||||
|
||||
@@ -227,17 +227,17 @@ void ProfileManager::CloseUser(UUID uuid) {
|
||||
|
||||
/// Gets all valid user ids on the system
|
||||
UserIDArray ProfileManager::GetAllUsers() const {
|
||||
UserIDArray output;
|
||||
std::transform(profiles.begin(), profiles.end(), output.begin(),
|
||||
[](const ProfileInfo& p) { return p.user_uuid; });
|
||||
UserIDArray output{};
|
||||
std::ranges::transform(profiles, output.begin(),
|
||||
[](const ProfileInfo& p) { return p.user_uuid; });
|
||||
return output;
|
||||
}
|
||||
|
||||
/// Get all the open users on the system and zero out the rest of the data. This is specifically
|
||||
/// needed for GetOpenUsers and we need to ensure the rest of the output buffer is zero'd out
|
||||
UserIDArray ProfileManager::GetOpenUsers() const {
|
||||
UserIDArray output;
|
||||
std::transform(profiles.begin(), profiles.end(), output.begin(), [](const ProfileInfo& p) {
|
||||
UserIDArray output{};
|
||||
std::ranges::transform(profiles, output.begin(), [](const ProfileInfo& p) {
|
||||
if (p.is_open)
|
||||
return p.user_uuid;
|
||||
return UUID{Common::INVALID_UUID};
|
||||
|
||||
@@ -23,12 +23,12 @@ using UserIDArray = std::array<Common::UUID, MAX_USERS>;
|
||||
/// Contains extra data related to a user.
|
||||
/// TODO: RE this structure
|
||||
struct ProfileData {
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u32 icon_id{};
|
||||
u8 bg_color_id{};
|
||||
INSERT_PADDING_BYTES(0x7);
|
||||
INSERT_PADDING_BYTES(0x10);
|
||||
INSERT_PADDING_BYTES(0x60);
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u32 icon_id;
|
||||
u8 bg_color_id;
|
||||
INSERT_PADDING_BYTES_NOINIT(0x7);
|
||||
INSERT_PADDING_BYTES_NOINIT(0x10);
|
||||
INSERT_PADDING_BYTES_NOINIT(0x60);
|
||||
};
|
||||
static_assert(sizeof(ProfileData) == 0x80, "ProfileData structure has incorrect size");
|
||||
|
||||
@@ -43,9 +43,9 @@ struct ProfileInfo {
|
||||
};
|
||||
|
||||
struct ProfileBase {
|
||||
Common::UUID user_uuid{Common::INVALID_UUID};
|
||||
u64_le timestamp{};
|
||||
ProfileUsername username{};
|
||||
Common::UUID user_uuid;
|
||||
u64_le timestamp;
|
||||
ProfileUsername username;
|
||||
|
||||
// Zero out all the fields to make the profile slot considered "Empty"
|
||||
void Invalidate() {
|
||||
|
||||
@@ -635,7 +635,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_,
|
||||
{50, &ICommonStateGetter::IsVrModeEnabled, "IsVrModeEnabled"},
|
||||
{51, &ICommonStateGetter::SetVrModeEnabled, "SetVrModeEnabled"},
|
||||
{52, &ICommonStateGetter::SetLcdBacklighOffEnabled, "SetLcdBacklighOffEnabled"},
|
||||
{53, nullptr, "BeginVrModeEx"},
|
||||
{53, &ICommonStateGetter::BeginVrModeEx, "BeginVrModeEx"},
|
||||
{54, &ICommonStateGetter::EndVrModeEx, "EndVrModeEx"},
|
||||
{55, nullptr, "IsInControllerFirmwareUpdateSection"},
|
||||
{60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"},
|
||||
@@ -732,6 +732,13 @@ void ICommonStateGetter::SetLcdBacklighOffEnabled(Kernel::HLERequestContext& ctx
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void ICommonStateGetter::BeginVrModeEx(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void ICommonStateGetter::EndVrModeEx(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
@@ -856,7 +863,7 @@ public:
|
||||
{25, nullptr, "Terminate"},
|
||||
{30, &ILibraryAppletAccessor::GetResult, "GetResult"},
|
||||
{50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"},
|
||||
{60, nullptr, "PresetLibraryAppletGpuTimeSliceZero"},
|
||||
{60, &ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero, "PresetLibraryAppletGpuTimeSliceZero"},
|
||||
{100, &ILibraryAppletAccessor::PushInData, "PushInData"},
|
||||
{101, &ILibraryAppletAccessor::PopOutData, "PopOutData"},
|
||||
{102, nullptr, "PushExtraStorage"},
|
||||
@@ -900,6 +907,13 @@ private:
|
||||
rb.Push(applet->GetStatus());
|
||||
}
|
||||
|
||||
void PresetLibraryAppletGpuTimeSliceZero(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Start(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
|
||||
@@ -189,6 +189,7 @@ private:
|
||||
void IsVrModeEnabled(Kernel::HLERequestContext& ctx);
|
||||
void SetVrModeEnabled(Kernel::HLERequestContext& ctx);
|
||||
void SetLcdBacklighOffEnabled(Kernel::HLERequestContext& ctx);
|
||||
void BeginVrModeEx(Kernel::HLERequestContext& ctx);
|
||||
void EndVrModeEx(Kernel::HLERequestContext& ctx);
|
||||
void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx);
|
||||
void SetCpuBoostMode(Kernel::HLERequestContext& ctx);
|
||||
|
||||
@@ -29,7 +29,7 @@ constexpr int DefaultSampleRate{48000};
|
||||
struct AudoutParams {
|
||||
s32_le sample_rate;
|
||||
u16_le channel_count;
|
||||
INSERT_PADDING_BYTES(2);
|
||||
INSERT_PADDING_BYTES_NOINIT(2);
|
||||
};
|
||||
static_assert(sizeof(AudoutParams) == 0x8, "AudoutParams is an invalid size");
|
||||
|
||||
|
||||
@@ -141,7 +141,9 @@ bool Controller_NPad::IsDeviceHandleValid(const DeviceHandle& device_handle) {
|
||||
device_handle.device_index < DeviceIndex::MaxDeviceIndex;
|
||||
}
|
||||
|
||||
Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {}
|
||||
Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {
|
||||
latest_vibration_values.fill({DEFAULT_VIBRATION_VALUE, DEFAULT_VIBRATION_VALUE});
|
||||
}
|
||||
|
||||
Controller_NPad::~Controller_NPad() {
|
||||
OnRelease();
|
||||
@@ -732,7 +734,7 @@ bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size
|
||||
// Send an empty vibration to stop any vibrations.
|
||||
vibrations[npad_index][device_index]->SetRumblePlay(0.0f, 160.0f, 0.0f, 320.0f);
|
||||
// Then reset the vibration value to its default value.
|
||||
latest_vibration_values[npad_index][device_index] = {};
|
||||
latest_vibration_values[npad_index][device_index] = DEFAULT_VIBRATION_VALUE;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -890,7 +892,7 @@ void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::siz
|
||||
return;
|
||||
}
|
||||
|
||||
if (controller == NPadControllerType::Handheld) {
|
||||
if (controller == NPadControllerType::Handheld && npad_index == HANDHELD_INDEX) {
|
||||
Settings::values.players.GetValue()[HANDHELD_INDEX].controller_type =
|
||||
MapNPadToSettingsType(controller);
|
||||
Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true;
|
||||
@@ -944,6 +946,23 @@ void Controller_NPad::SetSixAxisEnabled(bool six_axis_status) {
|
||||
sixaxis_sensors_enabled = six_axis_status;
|
||||
}
|
||||
|
||||
void Controller_NPad::SetSixAxisFusionParameters(f32 parameter1, f32 parameter2) {
|
||||
sixaxis_fusion_parameter1 = parameter1;
|
||||
sixaxis_fusion_parameter2 = parameter2;
|
||||
}
|
||||
|
||||
std::pair<f32, f32> Controller_NPad::GetSixAxisFusionParameters() {
|
||||
return {
|
||||
sixaxis_fusion_parameter1,
|
||||
sixaxis_fusion_parameter2,
|
||||
};
|
||||
}
|
||||
|
||||
void Controller_NPad::ResetSixAxisFusionParameters() {
|
||||
sixaxis_fusion_parameter1 = 0.0f;
|
||||
sixaxis_fusion_parameter2 = 0.0f;
|
||||
}
|
||||
|
||||
void Controller_NPad::MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2) {
|
||||
const auto npad_index_1 = NPadIdToIndex(npad_id_1);
|
||||
const auto npad_index_2 = NPadIdToIndex(npad_id_2);
|
||||
|
||||
@@ -97,10 +97,10 @@ public:
|
||||
};
|
||||
|
||||
struct DeviceHandle {
|
||||
NpadType npad_type{};
|
||||
u8 npad_id{};
|
||||
DeviceIndex device_index{};
|
||||
INSERT_PADDING_BYTES(1);
|
||||
NpadType npad_type;
|
||||
u8 npad_id;
|
||||
DeviceIndex device_index;
|
||||
INSERT_PADDING_BYTES_NOINIT(1);
|
||||
};
|
||||
static_assert(sizeof(DeviceHandle) == 4, "DeviceHandle is an invalid size");
|
||||
|
||||
@@ -120,13 +120,20 @@ public:
|
||||
static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size");
|
||||
|
||||
struct VibrationValue {
|
||||
f32 amp_low{0.0f};
|
||||
f32 freq_low{160.0f};
|
||||
f32 amp_high{0.0f};
|
||||
f32 freq_high{320.0f};
|
||||
f32 amp_low;
|
||||
f32 freq_low;
|
||||
f32 amp_high;
|
||||
f32 freq_high;
|
||||
};
|
||||
static_assert(sizeof(VibrationValue) == 0x10, "Vibration is an invalid size");
|
||||
|
||||
static constexpr VibrationValue DEFAULT_VIBRATION_VALUE{
|
||||
.amp_low = 0.0f,
|
||||
.freq_low = 160.0f,
|
||||
.amp_high = 0.0f,
|
||||
.freq_high = 320.0f,
|
||||
};
|
||||
|
||||
struct LedPattern {
|
||||
explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
|
||||
position1.Assign(light1);
|
||||
@@ -195,6 +202,9 @@ public:
|
||||
GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const;
|
||||
bool IsSixAxisSensorAtRest() const;
|
||||
void SetSixAxisEnabled(bool six_axis_status);
|
||||
void SetSixAxisFusionParameters(f32 parameter1, f32 parameter2);
|
||||
std::pair<f32, f32> GetSixAxisFusionParameters();
|
||||
void ResetSixAxisFusionParameters();
|
||||
LedPattern GetLedPattern(u32 npad_id);
|
||||
bool IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const;
|
||||
void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, u32 npad_id);
|
||||
@@ -451,6 +461,8 @@ private:
|
||||
std::array<bool, 10> unintended_home_button_input_protection{};
|
||||
GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
|
||||
bool sixaxis_sensors_enabled{true};
|
||||
f32 sixaxis_fusion_parameter1{};
|
||||
f32 sixaxis_fusion_parameter2{};
|
||||
bool sixaxis_at_rest{true};
|
||||
std::array<ControllerPad, 10> npad_pad_states{};
|
||||
bool is_in_lr_assignment_mode{false};
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include "common/common_types.h"
|
||||
#include "core/core_timing.h"
|
||||
@@ -16,7 +17,13 @@ constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
|
||||
Controller_Touchscreen::Controller_Touchscreen(Core::System& system) : ControllerBase(system) {}
|
||||
Controller_Touchscreen::~Controller_Touchscreen() = default;
|
||||
|
||||
void Controller_Touchscreen::OnInit() {}
|
||||
void Controller_Touchscreen::OnInit() {
|
||||
for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
|
||||
mouse_finger_id[id] = MAX_FINGERS;
|
||||
keyboard_finger_id[id] = MAX_FINGERS;
|
||||
udp_finger_id[id] = MAX_FINGERS;
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_Touchscreen::OnRelease() {}
|
||||
|
||||
@@ -40,38 +47,106 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
|
||||
cur_entry.sampling_number = last_entry.sampling_number + 1;
|
||||
cur_entry.sampling_number2 = cur_entry.sampling_number;
|
||||
|
||||
bool pressed = false;
|
||||
float x, y;
|
||||
std::tie(x, y, pressed) = touch_device->GetStatus();
|
||||
auto& touch_entry = cur_entry.states[0];
|
||||
touch_entry.attribute.raw = 0;
|
||||
if (!pressed && touch_btn_device) {
|
||||
std::tie(x, y, pressed) = touch_btn_device->GetStatus();
|
||||
}
|
||||
if (pressed && Settings::values.touchscreen.enabled) {
|
||||
touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width);
|
||||
touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height);
|
||||
touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
|
||||
touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
|
||||
touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
|
||||
const u64 tick = core_timing.GetCPUTicks();
|
||||
touch_entry.delta_time = tick - last_touch;
|
||||
last_touch = tick;
|
||||
touch_entry.finger = Settings::values.touchscreen.finger;
|
||||
cur_entry.entry_count = 1;
|
||||
} else {
|
||||
cur_entry.entry_count = 0;
|
||||
const Input::TouchStatus& mouse_status = touch_mouse_device->GetStatus();
|
||||
const Input::TouchStatus& udp_status = touch_udp_device->GetStatus();
|
||||
for (std::size_t id = 0; id < mouse_status.size(); ++id) {
|
||||
mouse_finger_id[id] = UpdateTouchInputEvent(mouse_status[id], mouse_finger_id[id]);
|
||||
udp_finger_id[id] = UpdateTouchInputEvent(udp_status[id], udp_finger_id[id]);
|
||||
}
|
||||
|
||||
if (Settings::values.use_touch_from_button) {
|
||||
const Input::TouchStatus& keyboard_status = touch_btn_device->GetStatus();
|
||||
for (std::size_t id = 0; id < mouse_status.size(); ++id) {
|
||||
keyboard_finger_id[id] =
|
||||
UpdateTouchInputEvent(keyboard_status[id], keyboard_finger_id[id]);
|
||||
}
|
||||
}
|
||||
|
||||
std::array<Finger, 16> active_fingers;
|
||||
const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),
|
||||
[](const auto& finger) { return finger.pressed; });
|
||||
const auto active_fingers_count =
|
||||
static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
|
||||
|
||||
const u64 tick = core_timing.GetCPUTicks();
|
||||
cur_entry.entry_count = static_cast<s32_le>(active_fingers_count);
|
||||
for (std::size_t id = 0; id < MAX_FINGERS; ++id) {
|
||||
auto& touch_entry = cur_entry.states[id];
|
||||
if (id < active_fingers_count) {
|
||||
touch_entry.x = static_cast<u16>(active_fingers[id].x * Layout::ScreenUndocked::Width);
|
||||
touch_entry.y = static_cast<u16>(active_fingers[id].y * Layout::ScreenUndocked::Height);
|
||||
touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
|
||||
touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
|
||||
touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
|
||||
touch_entry.delta_time = tick - active_fingers[id].last_touch;
|
||||
fingers[active_fingers[id].id].last_touch = tick;
|
||||
touch_entry.finger = active_fingers[id].id;
|
||||
touch_entry.attribute.raw = active_fingers[id].attribute.raw;
|
||||
} else {
|
||||
// Clear touch entry
|
||||
touch_entry.attribute.raw = 0;
|
||||
touch_entry.x = 0;
|
||||
touch_entry.y = 0;
|
||||
touch_entry.diameter_x = 0;
|
||||
touch_entry.diameter_y = 0;
|
||||
touch_entry.rotation_angle = 0;
|
||||
touch_entry.delta_time = 0;
|
||||
touch_entry.finger = 0;
|
||||
}
|
||||
}
|
||||
std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(TouchScreenSharedMemory));
|
||||
}
|
||||
|
||||
void Controller_Touchscreen::OnLoadInputDevices() {
|
||||
touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touchscreen.device);
|
||||
if (Settings::values.use_touch_from_button) {
|
||||
touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button");
|
||||
} else {
|
||||
touch_btn_device.reset();
|
||||
}
|
||||
touch_mouse_device = Input::CreateDevice<Input::TouchDevice>("engine:emu_window");
|
||||
touch_udp_device = Input::CreateDevice<Input::TouchDevice>("engine:cemuhookudp");
|
||||
touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button");
|
||||
}
|
||||
|
||||
std::optional<std::size_t> Controller_Touchscreen::GetUnusedFingerID() const {
|
||||
std::size_t first_free_id = 0;
|
||||
while (first_free_id < MAX_FINGERS) {
|
||||
if (!fingers[first_free_id].pressed) {
|
||||
return first_free_id;
|
||||
} else {
|
||||
first_free_id++;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::size_t Controller_Touchscreen::UpdateTouchInputEvent(
|
||||
const std::tuple<float, float, bool>& touch_input, std::size_t finger_id) {
|
||||
const auto& [x, y, pressed] = touch_input;
|
||||
if (pressed) {
|
||||
Attributes attribute{};
|
||||
if (finger_id == MAX_FINGERS) {
|
||||
const auto first_free_id = GetUnusedFingerID();
|
||||
if (!first_free_id) {
|
||||
// Invalid finger id do nothing
|
||||
return MAX_FINGERS;
|
||||
}
|
||||
finger_id = first_free_id.value();
|
||||
fingers[finger_id].pressed = true;
|
||||
fingers[finger_id].id = static_cast<u32_le>(finger_id);
|
||||
attribute.start_touch.Assign(1);
|
||||
}
|
||||
fingers[finger_id].x = x;
|
||||
fingers[finger_id].y = y;
|
||||
fingers[finger_id].attribute = attribute;
|
||||
return finger_id;
|
||||
}
|
||||
|
||||
if (finger_id != MAX_FINGERS) {
|
||||
if (!fingers[finger_id].attribute.end_touch) {
|
||||
fingers[finger_id].attribute.end_touch.Assign(1);
|
||||
fingers[finger_id].attribute.start_touch.Assign(0);
|
||||
return finger_id;
|
||||
}
|
||||
fingers[finger_id].pressed = false;
|
||||
}
|
||||
|
||||
return MAX_FINGERS;
|
||||
}
|
||||
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -30,6 +30,18 @@ public:
|
||||
void OnLoadInputDevices() override;
|
||||
|
||||
private:
|
||||
static constexpr std::size_t MAX_FINGERS = 16;
|
||||
|
||||
// Returns an unused finger id, if there is no fingers available std::nullopt will be returned
|
||||
std::optional<std::size_t> GetUnusedFingerID() const;
|
||||
|
||||
// If the touch is new it tries to assing a new finger id, if there is no fingers avaliable no
|
||||
// changes will be made. Updates the coordinates if the finger id it's already set. If the touch
|
||||
// ends delays the output by one frame to set the end_touch flag before finally freeing the
|
||||
// finger id
|
||||
std::size_t UpdateTouchInputEvent(const std::tuple<float, float, bool>& touch_input,
|
||||
std::size_t finger_id);
|
||||
|
||||
struct Attributes {
|
||||
union {
|
||||
u32 raw{};
|
||||
@@ -55,7 +67,7 @@ private:
|
||||
s64_le sampling_number;
|
||||
s64_le sampling_number2;
|
||||
s32_le entry_count;
|
||||
std::array<TouchState, 16> states;
|
||||
std::array<TouchState, MAX_FINGERS> states;
|
||||
};
|
||||
static_assert(sizeof(TouchScreenEntry) == 0x298, "TouchScreenEntry is an invalid size");
|
||||
|
||||
@@ -66,9 +78,23 @@ private:
|
||||
};
|
||||
static_assert(sizeof(TouchScreenSharedMemory) == 0x3000,
|
||||
"TouchScreenSharedMemory is an invalid size");
|
||||
|
||||
struct Finger {
|
||||
u64_le last_touch{};
|
||||
float x{};
|
||||
float y{};
|
||||
u32_le id{};
|
||||
bool pressed{};
|
||||
Attributes attribute;
|
||||
};
|
||||
|
||||
TouchScreenSharedMemory shared_memory{};
|
||||
std::unique_ptr<Input::TouchDevice> touch_device;
|
||||
std::unique_ptr<Input::TouchDevice> touch_mouse_device;
|
||||
std::unique_ptr<Input::TouchDevice> touch_udp_device;
|
||||
std::unique_ptr<Input::TouchDevice> touch_btn_device;
|
||||
s64_le last_touch{};
|
||||
std::array<std::size_t, MAX_FINGERS> mouse_finger_id;
|
||||
std::array<std::size_t, MAX_FINGERS> keyboard_finger_id;
|
||||
std::array<std::size_t, MAX_FINGERS> udp_finger_id;
|
||||
std::array<Finger, MAX_FINGERS> fingers;
|
||||
};
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -209,9 +209,9 @@ Hid::Hid(Core::System& system_) : ServiceFramework{system_, "hid"} {
|
||||
{67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"},
|
||||
{68, nullptr, "IsSixAxisSensorFusionEnabled"},
|
||||
{69, &Hid::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"},
|
||||
{70, nullptr, "SetSixAxisSensorFusionParameters"},
|
||||
{71, nullptr, "GetSixAxisSensorFusionParameters"},
|
||||
{72, nullptr, "ResetSixAxisSensorFusionParameters"},
|
||||
{70, &Hid::SetSixAxisSensorFusionParameters, "SetSixAxisSensorFusionParameters"},
|
||||
{71, &Hid::GetSixAxisSensorFusionParameters, "GetSixAxisSensorFusionParameters"},
|
||||
{72, &Hid::ResetSixAxisSensorFusionParameters, "ResetSixAxisSensorFusionParameters"},
|
||||
{73, nullptr, "SetAccelerometerParameters"},
|
||||
{74, nullptr, "GetAccelerometerParameters"},
|
||||
{75, nullptr, "ResetAccelerometerParameters"},
|
||||
@@ -401,9 +401,9 @@ void Hid::SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx) {
|
||||
void Hid::ActivateXpad(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
u32 basic_xpad_id{};
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u64 applet_resource_user_id{};
|
||||
u32 basic_xpad_id;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
@@ -431,9 +431,9 @@ void Hid::GetXpadIDs(Kernel::HLERequestContext& ctx) {
|
||||
void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
Controller_NPad::DeviceHandle sixaxis_handle{};
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u64 applet_resource_user_id{};
|
||||
Controller_NPad::DeviceHandle sixaxis_handle;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
@@ -452,9 +452,9 @@ void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
|
||||
void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
Controller_NPad::DeviceHandle sixaxis_handle{};
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u64 applet_resource_user_id{};
|
||||
Controller_NPad::DeviceHandle sixaxis_handle;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
@@ -473,9 +473,9 @@ void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
|
||||
void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
Controller_NPad::DeviceHandle sixaxis_handle{};
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u64 applet_resource_user_id{};
|
||||
Controller_NPad::DeviceHandle sixaxis_handle;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
@@ -494,9 +494,9 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
|
||||
void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
Controller_NPad::DeviceHandle sixaxis_handle{};
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u64 applet_resource_user_id{};
|
||||
Controller_NPad::DeviceHandle sixaxis_handle;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
@@ -515,11 +515,12 @@ void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
|
||||
void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
bool enable_sixaxis_sensor_fusion{};
|
||||
INSERT_PADDING_BYTES(3);
|
||||
Controller_NPad::DeviceHandle sixaxis_handle{};
|
||||
u64 applet_resource_user_id{};
|
||||
bool enable_sixaxis_sensor_fusion;
|
||||
INSERT_PADDING_BYTES_NOINIT(3);
|
||||
Controller_NPad::DeviceHandle sixaxis_handle;
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
@@ -534,6 +535,83 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
Controller_NPad::DeviceHandle sixaxis_handle;
|
||||
f32 parameter1;
|
||||
f32 parameter2;
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
||||
.SetSixAxisFusionParameters(parameters.parameter1, parameters.parameter2);
|
||||
|
||||
LOG_WARNING(Service_HID,
|
||||
"(STUBBED) called, npad_type={}, npad_id={}, device_index={}, parameter1={}, "
|
||||
"parameter2={}, applet_resource_user_id={}",
|
||||
parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
|
||||
parameters.sixaxis_handle.device_index, parameters.parameter1,
|
||||
parameters.parameter2, parameters.applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
Controller_NPad::DeviceHandle sixaxis_handle;
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
|
||||
|
||||
f32 parameter1 = 0;
|
||||
f32 parameter2 = 0;
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
std::tie(parameter1, parameter2) =
|
||||
applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
||||
.GetSixAxisFusionParameters();
|
||||
|
||||
LOG_WARNING(
|
||||
Service_HID,
|
||||
"(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
|
||||
parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
|
||||
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(parameter1);
|
||||
rb.Push(parameter2);
|
||||
}
|
||||
|
||||
void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
Controller_NPad::DeviceHandle sixaxis_handle;
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
applet_resource->GetController<Controller_NPad>(HidController::NPad)
|
||||
.ResetSixAxisFusionParameters();
|
||||
|
||||
LOG_WARNING(
|
||||
Service_HID,
|
||||
"(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
|
||||
parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
|
||||
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto sixaxis_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()};
|
||||
@@ -556,9 +634,9 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
|
||||
void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
Controller_NPad::DeviceHandle sixaxis_handle{};
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u64 applet_resource_user_id{};
|
||||
Controller_NPad::DeviceHandle sixaxis_handle;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
@@ -577,9 +655,9 @@ void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
|
||||
void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
Controller_NPad::DeviceHandle sixaxis_handle{};
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u64 applet_resource_user_id{};
|
||||
Controller_NPad::DeviceHandle sixaxis_handle;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
@@ -599,9 +677,9 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
|
||||
void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
Controller_NPad::DeviceHandle sixaxis_handle{};
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u64 applet_resource_user_id{};
|
||||
Controller_NPad::DeviceHandle sixaxis_handle;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
@@ -620,9 +698,9 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
|
||||
void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
u32 unknown{};
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u64 applet_resource_user_id{};
|
||||
u32 unknown;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
@@ -702,10 +780,10 @@ void Hid::DeactivateNpad(Kernel::HLERequestContext& ctx) {
|
||||
void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
u32 npad_id{};
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u64 applet_resource_user_id{};
|
||||
u64 unknown{};
|
||||
u32 npad_id;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
u64 unknown;
|
||||
};
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
@@ -722,9 +800,9 @@ void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
|
||||
void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
u32 npad_id{};
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u64 applet_resource_user_id{};
|
||||
u32 npad_id;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
@@ -756,9 +834,9 @@ void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
|
||||
// Should have no effect with how our npad sets up the data
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
u32 unknown{};
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u64 applet_resource_user_id{};
|
||||
u32 unknown;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
@@ -800,9 +878,9 @@ void Hid::GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
|
||||
void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
u32 npad_id{};
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u64 applet_resource_user_id{};
|
||||
u32 npad_id;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
@@ -821,10 +899,10 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
|
||||
// TODO: Check the differences between this and SetNpadJoyAssignmentModeSingleByDefault
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
u32 npad_id{};
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u64 applet_resource_user_id{};
|
||||
u64 npad_joy_device_type{};
|
||||
u32 npad_id;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
u64 npad_joy_device_type;
|
||||
};
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
@@ -844,9 +922,9 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
|
||||
void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
u32 npad_id{};
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u64 applet_resource_user_id{};
|
||||
u32 npad_id;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
@@ -952,9 +1030,9 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
|
||||
void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
u32 npad_id{};
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u64 applet_resource_user_id{};
|
||||
u32 npad_id;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
@@ -971,10 +1049,10 @@ void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext
|
||||
void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
bool unintended_home_button_input_protection{};
|
||||
INSERT_PADDING_BYTES(3);
|
||||
u32 npad_id{};
|
||||
u64 applet_resource_user_id{};
|
||||
bool unintended_home_button_input_protection;
|
||||
INSERT_PADDING_BYTES_NOINIT(3);
|
||||
u32 npad_id;
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
@@ -1026,10 +1104,10 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
|
||||
void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
Controller_NPad::DeviceHandle vibration_device_handle{};
|
||||
Controller_NPad::VibrationValue vibration_value{};
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u64 applet_resource_user_id{};
|
||||
Controller_NPad::DeviceHandle vibration_device_handle;
|
||||
Controller_NPad::VibrationValue vibration_value;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
@@ -1050,9 +1128,9 @@ void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) {
|
||||
void Hid::GetActualVibrationValue(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
Controller_NPad::DeviceHandle vibration_device_handle{};
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u64 applet_resource_user_id{};
|
||||
Controller_NPad::DeviceHandle vibration_device_handle;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
@@ -1147,9 +1225,9 @@ void Hid::EndPermitVibrationSession(Kernel::HLERequestContext& ctx) {
|
||||
void Hid::IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
Controller_NPad::DeviceHandle vibration_device_handle{};
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u64 applet_resource_user_id{};
|
||||
Controller_NPad::DeviceHandle vibration_device_handle;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
@@ -1180,9 +1258,9 @@ void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
|
||||
void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
Controller_NPad::DeviceHandle sixaxis_handle{};
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u64 applet_resource_user_id{};
|
||||
Controller_NPad::DeviceHandle sixaxis_handle;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
@@ -1200,9 +1278,9 @@ void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
|
||||
void Hid::StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
Controller_NPad::DeviceHandle sixaxis_handle{};
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u64 applet_resource_user_id{};
|
||||
Controller_NPad::DeviceHandle sixaxis_handle;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
@@ -97,6 +97,9 @@ private:
|
||||
void StartSixAxisSensor(Kernel::HLERequestContext& ctx);
|
||||
void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
|
||||
void EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx);
|
||||
void SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx);
|
||||
void GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx);
|
||||
void ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx);
|
||||
void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
|
||||
void GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
|
||||
void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
|
||||
|
||||
@@ -20,30 +20,30 @@ public:
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "SaveCurrentSetting"},
|
||||
{1, nullptr, "LoadCurrentSetting"},
|
||||
{2, nullptr, "SetCurrentBrightnessSetting"},
|
||||
{3, nullptr, "GetCurrentBrightnessSetting"},
|
||||
{2, &LBL::SetCurrentBrightnessSetting, "SetCurrentBrightnessSetting"},
|
||||
{3, &LBL::GetCurrentBrightnessSetting, "GetCurrentBrightnessSetting"},
|
||||
{4, nullptr, "ApplyCurrentBrightnessSettingToBacklight"},
|
||||
{5, nullptr, "GetBrightnessSettingAppliedToBacklight"},
|
||||
{6, nullptr, "SwitchBacklightOn"},
|
||||
{7, nullptr, "SwitchBacklightOff"},
|
||||
{8, nullptr, "GetBacklightSwitchStatus"},
|
||||
{9, nullptr, "EnableDimming"},
|
||||
{10, nullptr, "DisableDimming"},
|
||||
{11, nullptr, "IsDimmingEnabled"},
|
||||
{12, nullptr, "EnableAutoBrightnessControl"},
|
||||
{13, nullptr, "DisableAutoBrightnessControl"},
|
||||
{14, nullptr, "IsAutoBrightnessControlEnabled"},
|
||||
{15, nullptr, "SetAmbientLightSensorValue"},
|
||||
{16, nullptr, "GetAmbientLightSensorValue"},
|
||||
{17, nullptr, "SetBrightnessReflectionDelayLevel"},
|
||||
{18, nullptr, "GetBrightnessReflectionDelayLevel"},
|
||||
{19, nullptr, "SetCurrentBrightnessMapping"},
|
||||
{20, nullptr, "GetCurrentBrightnessMapping"},
|
||||
{21, nullptr, "SetCurrentAmbientLightSensorMapping"},
|
||||
{22, nullptr, "GetCurrentAmbientLightSensorMapping"},
|
||||
{23, nullptr, "IsAmbientLightSensorAvailable"},
|
||||
{24, nullptr, "SetCurrentBrightnessSettingForVrMode"},
|
||||
{25, nullptr, "GetCurrentBrightnessSettingForVrMode"},
|
||||
{6, &LBL::SwitchBacklightOn, "SwitchBacklightOn"},
|
||||
{7, &LBL::SwitchBacklightOff, "SwitchBacklightOff"},
|
||||
{8, &LBL::GetBacklightSwitchStatus, "GetBacklightSwitchStatus"},
|
||||
{9, &LBL::EnableDimming, "EnableDimming"},
|
||||
{10, &LBL::DisableDimming, "DisableDimming"},
|
||||
{11, &LBL::IsDimmingEnabled, "IsDimmingEnabled"},
|
||||
{12, &LBL::EnableAutoBrightnessControl, "EnableAutoBrightnessControl"},
|
||||
{13, &LBL::DisableAutoBrightnessControl, "DisableAutoBrightnessControl"},
|
||||
{14, &LBL::IsAutoBrightnessControlEnabled, "IsAutoBrightnessControlEnabled"},
|
||||
{15, &LBL::SetAmbientLightSensorValue, "SetAmbientLightSensorValue"},
|
||||
{16, &LBL::GetAmbientLightSensorValue, "GetAmbientLightSensorValue"},
|
||||
{17, &LBL::SetBrightnessReflectionDelayLevel, "SetBrightnessReflectionDelayLevel"},
|
||||
{18, &LBL::GetBrightnessReflectionDelayLevel, "GetBrightnessReflectionDelayLevel"},
|
||||
{19, &LBL::SetCurrentBrightnessMapping, "SetCurrentBrightnessMapping"},
|
||||
{20, &LBL::GetCurrentBrightnessMapping, "GetCurrentBrightnessMapping"},
|
||||
{21, &LBL::SetCurrentAmbientLightSensorMapping, "SetCurrentAmbientLightSensorMapping"},
|
||||
{22, &LBL::GetCurrentAmbientLightSensorMapping, "GetCurrentAmbientLightSensorMapping"},
|
||||
{23, &LBL::IsAmbientLightSensorAvailable, "IsAmbientLightSensorAvailable"},
|
||||
{24, &LBL::SetCurrentBrightnessSettingForVrMode, "SetCurrentBrightnessSettingForVrMode"},
|
||||
{25, &LBL::GetCurrentBrightnessSettingForVrMode, "GetCurrentBrightnessSettingForVrMode"},
|
||||
{26, &LBL::EnableVrMode, "EnableVrMode"},
|
||||
{27, &LBL::DisableVrMode, "DisableVrMode"},
|
||||
{28, &LBL::IsVrModeEnabled, "IsVrModeEnabled"},
|
||||
@@ -55,6 +55,237 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
enum class BacklightSwitchStatus : u32 {
|
||||
Off = 0,
|
||||
On = 1,
|
||||
};
|
||||
|
||||
void SetCurrentBrightnessSetting(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto brightness = rp.Pop<float>();
|
||||
|
||||
if (!std::isfinite(brightness)) {
|
||||
LOG_ERROR(Service_LBL, "Brightness is infinite!");
|
||||
brightness = 0.0f;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_LBL, "called brightness={}", brightness);
|
||||
|
||||
current_brightness = brightness;
|
||||
update_instantly = true;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetCurrentBrightnessSetting(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto brightness = current_brightness;
|
||||
if (!std::isfinite(brightness)) {
|
||||
LOG_ERROR(Service_LBL, "Brightness is infinite!");
|
||||
brightness = 0.0f;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_LBL, "called brightness={}", brightness);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(brightness);
|
||||
}
|
||||
|
||||
void SwitchBacklightOn(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto fade_time = rp.Pop<u64_le>();
|
||||
LOG_WARNING(Service_LBL, "(STUBBED) called, fade_time={}", fade_time);
|
||||
|
||||
backlight_enabled = true;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void SwitchBacklightOff(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto fade_time = rp.Pop<u64_le>();
|
||||
LOG_WARNING(Service_LBL, "(STUBBED) called, fade_time={}", fade_time);
|
||||
|
||||
backlight_enabled = false;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetBacklightSwitchStatus(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushEnum<BacklightSwitchStatus>(backlight_enabled ? BacklightSwitchStatus::On
|
||||
: BacklightSwitchStatus::Off);
|
||||
}
|
||||
|
||||
void EnableDimming(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
dimming = true;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void DisableDimming(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
dimming = false;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void IsDimmingEnabled(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(dimming);
|
||||
}
|
||||
|
||||
void EnableAutoBrightnessControl(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
auto_brightness = true;
|
||||
update_instantly = true;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void DisableAutoBrightnessControl(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
auto_brightness = false;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void IsAutoBrightnessControlEnabled(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(auto_brightness);
|
||||
}
|
||||
|
||||
void SetAmbientLightSensorValue(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto light_value = rp.Pop<float>();
|
||||
|
||||
LOG_DEBUG(Service_LBL, "called light_value={}", light_value);
|
||||
|
||||
ambient_light_value = light_value;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetAmbientLightSensorValue(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(ambient_light_value);
|
||||
}
|
||||
|
||||
void SetBrightnessReflectionDelayLevel(Kernel::HLERequestContext& ctx) {
|
||||
// This is Intentional, this function does absolutely nothing
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetBrightnessReflectionDelayLevel(Kernel::HLERequestContext& ctx) {
|
||||
// This is intentional, the function is hard coded to return 0.0f on hardware
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(0.0f);
|
||||
}
|
||||
|
||||
void SetCurrentBrightnessMapping(Kernel::HLERequestContext& ctx) {
|
||||
// This is Intentional, this function does absolutely nothing
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetCurrentBrightnessMapping(Kernel::HLERequestContext& ctx) {
|
||||
// This is Intentional, this function does absolutely nothing
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
// This function is suppose to return something but it seems like it doesn't
|
||||
}
|
||||
|
||||
void SetCurrentAmbientLightSensorMapping(Kernel::HLERequestContext& ctx) {
|
||||
// This is Intentional, this function does absolutely nothing
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetCurrentAmbientLightSensorMapping(Kernel::HLERequestContext& ctx) {
|
||||
// This is Intentional, this function does absolutely nothing
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
// This function is suppose to return something but it seems like it doesn't
|
||||
}
|
||||
|
||||
void IsAmbientLightSensorAvailable(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_LBL, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
// TODO(ogniK): Only return true if there's no device error
|
||||
rb.Push(true);
|
||||
}
|
||||
|
||||
void SetCurrentBrightnessSettingForVrMode(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto brightness = rp.Pop<float>();
|
||||
|
||||
if (!std::isfinite(brightness)) {
|
||||
LOG_ERROR(Service_LBL, "Brightness is infinite!");
|
||||
brightness = 0.0f;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_LBL, "called brightness={}", brightness);
|
||||
|
||||
current_vr_brightness = brightness;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetCurrentBrightnessSettingForVrMode(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto brightness = current_vr_brightness;
|
||||
if (!std::isfinite(brightness)) {
|
||||
LOG_ERROR(Service_LBL, "Brightness is infinite!");
|
||||
brightness = 0.0f;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_LBL, "called brightness={}", brightness);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(brightness);
|
||||
}
|
||||
|
||||
void EnableVrMode(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LBL, "called");
|
||||
|
||||
@@ -82,6 +313,14 @@ private:
|
||||
}
|
||||
|
||||
bool vr_mode_enabled = false;
|
||||
float current_brightness = 1.0f;
|
||||
float backlight_brightness = 1.0f;
|
||||
float ambient_light_value = 0.0f;
|
||||
float current_vr_brightness = 1.0f;
|
||||
bool dimming = true;
|
||||
bool backlight_enabled = true;
|
||||
bool update_instantly = false;
|
||||
bool auto_brightness = false; // TODO(ogniK): Move to system settings
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
|
||||
|
||||
@@ -5,22 +5,71 @@
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/lm/lm.h"
|
||||
#include "core/hle/service/lm/manager.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Service::LM {
|
||||
enum class LogSeverity : u8 {
|
||||
Trace = 0,
|
||||
Info = 1,
|
||||
Warning = 2,
|
||||
Error = 3,
|
||||
Fatal = 4,
|
||||
};
|
||||
|
||||
// To keep flags out of hashing as well as the payload size
|
||||
struct LogPacketHeaderEntry {
|
||||
u64_le pid{};
|
||||
u64_le tid{};
|
||||
LogSeverity severity{};
|
||||
u8 verbosity{};
|
||||
|
||||
auto operator<=>(const LogPacketHeaderEntry&) const = default;
|
||||
};
|
||||
} // namespace Service::LM
|
||||
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<Service::LM::LogPacketHeaderEntry> {
|
||||
std::size_t operator()(const Service::LM::LogPacketHeaderEntry& k) const noexcept {
|
||||
std::size_t seed{};
|
||||
boost::hash_combine(seed, k.pid);
|
||||
boost::hash_combine(seed, k.tid);
|
||||
boost::hash_combine(seed, k.severity);
|
||||
boost::hash_combine(seed, k.verbosity);
|
||||
return seed;
|
||||
};
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
namespace Service::LM {
|
||||
|
||||
enum class LogDestination : u32 {
|
||||
TargetManager = 1 << 0,
|
||||
Uart = 1 << 1,
|
||||
UartSleep = 1 << 2,
|
||||
All = 0xffff,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(LogDestination);
|
||||
|
||||
enum class LogPacketFlags : u8 {
|
||||
Head = 1 << 0,
|
||||
Tail = 1 << 1,
|
||||
LittleEndian = 1 << 2,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(LogPacketFlags);
|
||||
|
||||
class ILogger final : public ServiceFramework<ILogger> {
|
||||
public:
|
||||
explicit ILogger(Core::System& system_)
|
||||
: ServiceFramework{system_, "ILogger"}, manager{system_.GetLogManager()},
|
||||
memory{system_.Memory()} {
|
||||
explicit ILogger(Core::System& system_) : ServiceFramework{system_, "ILogger"} {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ILogger::Log, "Log"},
|
||||
{1, &ILogger::SetDestination, "SetDestination"},
|
||||
@@ -30,54 +79,262 @@ public:
|
||||
|
||||
private:
|
||||
void Log(Kernel::HLERequestContext& ctx) {
|
||||
std::size_t offset{};
|
||||
const auto data = ctx.ReadBuffer();
|
||||
|
||||
// This function only succeeds - Get that out of the way
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
// Read MessageHeader, despite not doing anything with it right now
|
||||
MessageHeader header{};
|
||||
VAddr addr{ctx.BufferDescriptorX()[0].Address()};
|
||||
const VAddr end_addr{addr + ctx.BufferDescriptorX()[0].size};
|
||||
memory.ReadBlock(addr, &header, sizeof(MessageHeader));
|
||||
addr += sizeof(MessageHeader);
|
||||
|
||||
FieldMap fields;
|
||||
while (addr < end_addr) {
|
||||
const auto field = static_cast<Field>(memory.Read8(addr++));
|
||||
const auto length = memory.Read8(addr++);
|
||||
|
||||
if (static_cast<Field>(memory.Read8(addr)) == Field::Skip) {
|
||||
++addr;
|
||||
}
|
||||
|
||||
SCOPE_EXIT({ addr += length; });
|
||||
|
||||
if (field == Field::Skip) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<u8> data(length);
|
||||
memory.ReadBlock(addr, data.data(), length);
|
||||
fields.emplace(field, std::move(data));
|
||||
if (data.size() < sizeof(LogPacketHeader)) {
|
||||
LOG_ERROR(Service_LM, "Data size is too small for header! size={}", data.size());
|
||||
return;
|
||||
}
|
||||
|
||||
manager.Log({header, std::move(fields)});
|
||||
LogPacketHeader header{};
|
||||
std::memcpy(&header, data.data(), sizeof(LogPacketHeader));
|
||||
offset += sizeof(LogPacketHeader);
|
||||
|
||||
LogPacketHeaderEntry entry{
|
||||
.pid = header.pid,
|
||||
.tid = header.tid,
|
||||
.severity = header.severity,
|
||||
.verbosity = header.verbosity,
|
||||
};
|
||||
|
||||
if (True(header.flags & LogPacketFlags::Head)) {
|
||||
std::vector<u8> tmp(data.size() - sizeof(LogPacketHeader));
|
||||
std::memcpy(tmp.data(), data.data() + offset, tmp.size());
|
||||
entries[entry] = std::move(tmp);
|
||||
} else {
|
||||
// Append to existing entry
|
||||
if (!entries.contains(entry)) {
|
||||
LOG_ERROR(Service_LM, "Log entry does not exist!");
|
||||
return;
|
||||
}
|
||||
std::vector<u8> tmp(data.size() - sizeof(LogPacketHeader));
|
||||
|
||||
auto& existing_entry = entries[entry];
|
||||
const auto base = existing_entry.size();
|
||||
existing_entry.resize(base + (data.size() - sizeof(LogPacketHeader)));
|
||||
std::memcpy(existing_entry.data() + base, data.data() + offset,
|
||||
(data.size() - sizeof(LogPacketHeader)));
|
||||
}
|
||||
|
||||
if (True(header.flags & LogPacketFlags::Tail)) {
|
||||
auto it = entries.find(entry);
|
||||
if (it == entries.end()) {
|
||||
LOG_ERROR(Service_LM, "Log entry does not exist!");
|
||||
return;
|
||||
}
|
||||
ParseLog(it->first, it->second);
|
||||
entries.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void SetDestination(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto destination = rp.PopEnum<DestinationFlag>();
|
||||
const auto log_destination = rp.PopEnum<LogDestination>();
|
||||
|
||||
LOG_DEBUG(Service_LM, "called, destination={:08X}", destination);
|
||||
|
||||
manager.SetDestination(destination);
|
||||
LOG_DEBUG(Service_LM, "called, destination={}", DestinationToString(log_destination));
|
||||
destination = log_destination;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
Manager& manager;
|
||||
Core::Memory::Memory& memory;
|
||||
u32 ReadLeb128(const std::vector<u8>& data, std::size_t& offset) {
|
||||
u32 result{};
|
||||
u32 shift{};
|
||||
do {
|
||||
result |= (data[offset] & 0x7f) << shift;
|
||||
shift += 7;
|
||||
offset++;
|
||||
if (offset >= data.size()) {
|
||||
break;
|
||||
}
|
||||
} while ((data[offset] & 0x80) != 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<std::string> ReadString(const std::vector<u8>& data, std::size_t& offset,
|
||||
std::size_t length) {
|
||||
if (length == 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto length_to_read = std::min(length, data.size() - offset);
|
||||
|
||||
std::string output(length_to_read, '\0');
|
||||
std::memcpy(output.data(), data.data() + offset, length_to_read);
|
||||
offset += length_to_read;
|
||||
return output;
|
||||
}
|
||||
|
||||
u32_le ReadAsU32(const std::vector<u8>& data, std::size_t& offset, std::size_t length) {
|
||||
ASSERT(length == sizeof(u32));
|
||||
u32_le output{};
|
||||
std::memcpy(&output, data.data() + offset, sizeof(u32));
|
||||
offset += length;
|
||||
return output;
|
||||
}
|
||||
|
||||
u64_le ReadAsU64(const std::vector<u8>& data, std::size_t& offset, std::size_t length) {
|
||||
ASSERT(length == sizeof(u64));
|
||||
u64_le output{};
|
||||
std::memcpy(&output, data.data() + offset, sizeof(u64));
|
||||
offset += length;
|
||||
return output;
|
||||
}
|
||||
|
||||
void ParseLog(const LogPacketHeaderEntry entry, const std::vector<u8>& log_data) {
|
||||
// Possible entries
|
||||
std::optional<std::string> text_log;
|
||||
std::optional<u32> line_number;
|
||||
std::optional<std::string> file_name;
|
||||
std::optional<std::string> function_name;
|
||||
std::optional<std::string> module_name;
|
||||
std::optional<std::string> thread_name;
|
||||
std::optional<u64> log_pack_drop_count;
|
||||
std::optional<s64> user_system_clock;
|
||||
std::optional<std::string> process_name;
|
||||
|
||||
std::size_t offset{};
|
||||
while (offset < log_data.size()) {
|
||||
const auto key = static_cast<LogDataChunkKey>(ReadLeb128(log_data, offset));
|
||||
const auto chunk_size = ReadLeb128(log_data, offset);
|
||||
|
||||
switch (key) {
|
||||
case LogDataChunkKey::LogSessionBegin:
|
||||
case LogDataChunkKey::LogSessionEnd:
|
||||
break;
|
||||
case LogDataChunkKey::TextLog:
|
||||
text_log = ReadString(log_data, offset, chunk_size);
|
||||
break;
|
||||
case LogDataChunkKey::LineNumber:
|
||||
line_number = ReadAsU32(log_data, offset, chunk_size);
|
||||
break;
|
||||
case LogDataChunkKey::FileName:
|
||||
file_name = ReadString(log_data, offset, chunk_size);
|
||||
break;
|
||||
case LogDataChunkKey::FunctionName:
|
||||
function_name = ReadString(log_data, offset, chunk_size);
|
||||
break;
|
||||
case LogDataChunkKey::ModuleName:
|
||||
module_name = ReadString(log_data, offset, chunk_size);
|
||||
break;
|
||||
case LogDataChunkKey::ThreadName:
|
||||
thread_name = ReadString(log_data, offset, chunk_size);
|
||||
break;
|
||||
case LogDataChunkKey::LogPacketDropCount:
|
||||
log_pack_drop_count = ReadAsU64(log_data, offset, chunk_size);
|
||||
break;
|
||||
case LogDataChunkKey::UserSystemClock:
|
||||
user_system_clock = ReadAsU64(log_data, offset, chunk_size);
|
||||
break;
|
||||
case LogDataChunkKey::ProcessName:
|
||||
process_name = ReadString(log_data, offset, chunk_size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::string output_log{};
|
||||
if (process_name) {
|
||||
output_log += fmt::format("Process: {}\n", *process_name);
|
||||
}
|
||||
if (module_name) {
|
||||
output_log += fmt::format("Module: {}\n", *module_name);
|
||||
}
|
||||
if (file_name) {
|
||||
output_log += fmt::format("File: {}\n", *file_name);
|
||||
}
|
||||
if (function_name) {
|
||||
output_log += fmt::format("Function: {}\n", *function_name);
|
||||
}
|
||||
if (line_number && *line_number != 0) {
|
||||
output_log += fmt::format("Line: {}\n", *line_number);
|
||||
}
|
||||
output_log += fmt::format("ProcessID: {:X}\n", entry.pid);
|
||||
output_log += fmt::format("ThreadID: {:X}\n", entry.tid);
|
||||
|
||||
if (text_log) {
|
||||
output_log += fmt::format("Log Text: {}\n", *text_log);
|
||||
}
|
||||
|
||||
switch (entry.severity) {
|
||||
case LogSeverity::Trace:
|
||||
LOG_DEBUG(Service_LM, "LogManager DEBUG ({}):\n{}", DestinationToString(destination),
|
||||
output_log);
|
||||
break;
|
||||
case LogSeverity::Info:
|
||||
LOG_INFO(Service_LM, "LogManager INFO ({}):\n{}", DestinationToString(destination),
|
||||
output_log);
|
||||
break;
|
||||
case LogSeverity::Warning:
|
||||
LOG_WARNING(Service_LM, "LogManager WARNING ({}):\n{}",
|
||||
DestinationToString(destination), output_log);
|
||||
break;
|
||||
case LogSeverity::Error:
|
||||
LOG_ERROR(Service_LM, "LogManager ERROR ({}):\n{}", DestinationToString(destination),
|
||||
output_log);
|
||||
break;
|
||||
case LogSeverity::Fatal:
|
||||
LOG_CRITICAL(Service_LM, "LogManager FATAL ({}):\n{}", DestinationToString(destination),
|
||||
output_log);
|
||||
break;
|
||||
default:
|
||||
LOG_CRITICAL(Service_LM, "LogManager UNKNOWN ({}):\n{}",
|
||||
DestinationToString(destination), output_log);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static std::string DestinationToString(LogDestination destination) {
|
||||
if (True(destination & LogDestination::All)) {
|
||||
return "TargetManager | Uart | UartSleep";
|
||||
}
|
||||
std::string output{};
|
||||
if (True(destination & LogDestination::TargetManager)) {
|
||||
output += "| TargetManager";
|
||||
}
|
||||
if (True(destination & LogDestination::Uart)) {
|
||||
output += "| Uart";
|
||||
}
|
||||
if (True(destination & LogDestination::UartSleep)) {
|
||||
output += "| UartSleep";
|
||||
}
|
||||
if (output.length() > 0) {
|
||||
return output.substr(2);
|
||||
}
|
||||
return "No Destination";
|
||||
}
|
||||
|
||||
enum class LogDataChunkKey : u32 {
|
||||
LogSessionBegin = 0,
|
||||
LogSessionEnd = 1,
|
||||
TextLog = 2,
|
||||
LineNumber = 3,
|
||||
FileName = 4,
|
||||
FunctionName = 5,
|
||||
ModuleName = 6,
|
||||
ThreadName = 7,
|
||||
LogPacketDropCount = 8,
|
||||
UserSystemClock = 9,
|
||||
ProcessName = 10,
|
||||
};
|
||||
|
||||
struct LogPacketHeader {
|
||||
u64_le pid{};
|
||||
u64_le tid{};
|
||||
LogPacketFlags flags{};
|
||||
INSERT_PADDING_BYTES(1);
|
||||
LogSeverity severity{};
|
||||
u8 verbosity{};
|
||||
u32_le payload_size{};
|
||||
};
|
||||
static_assert(sizeof(LogPacketHeader) == 0x18, "LogPacketHeader is an invalid size");
|
||||
|
||||
std::unordered_map<LogPacketHeaderEntry, std::vector<u8>> entries{};
|
||||
LogDestination destination{LogDestination::All};
|
||||
};
|
||||
|
||||
class LM final : public ServiceFramework<LM> {
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/hle/service/lm/manager.h"
|
||||
#include "core/reporter.h"
|
||||
|
||||
namespace Service::LM {
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, DestinationFlag dest) {
|
||||
std::vector<std::string> array;
|
||||
const auto check_single_flag = [dest, &array](DestinationFlag check, std::string name) {
|
||||
if ((static_cast<u32>(check) & static_cast<u32>(dest)) != 0) {
|
||||
array.emplace_back(std::move(name));
|
||||
}
|
||||
};
|
||||
|
||||
check_single_flag(DestinationFlag::Default, "Default");
|
||||
check_single_flag(DestinationFlag::UART, "UART");
|
||||
check_single_flag(DestinationFlag::UARTSleeping, "UART (Sleeping)");
|
||||
|
||||
os << "[";
|
||||
for (const auto& entry : array) {
|
||||
os << entry << ", ";
|
||||
}
|
||||
return os << "]";
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, MessageHeader::Severity severity) {
|
||||
switch (severity) {
|
||||
case MessageHeader::Severity::Trace:
|
||||
return os << "Trace";
|
||||
case MessageHeader::Severity::Info:
|
||||
return os << "Info";
|
||||
case MessageHeader::Severity::Warning:
|
||||
return os << "Warning";
|
||||
case MessageHeader::Severity::Error:
|
||||
return os << "Error";
|
||||
case MessageHeader::Severity::Critical:
|
||||
return os << "Critical";
|
||||
default:
|
||||
return os << fmt::format("{:08X}", static_cast<u32>(severity));
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, Field field) {
|
||||
switch (field) {
|
||||
case Field::Skip:
|
||||
return os << "Skip";
|
||||
case Field::Message:
|
||||
return os << "Message";
|
||||
case Field::Line:
|
||||
return os << "Line";
|
||||
case Field::Filename:
|
||||
return os << "Filename";
|
||||
case Field::Function:
|
||||
return os << "Function";
|
||||
case Field::Module:
|
||||
return os << "Module";
|
||||
case Field::Thread:
|
||||
return os << "Thread";
|
||||
default:
|
||||
return os << fmt::format("{:08X}", static_cast<u32>(field));
|
||||
}
|
||||
}
|
||||
|
||||
std::string FormatField(Field type, const std::vector<u8>& data) {
|
||||
switch (type) {
|
||||
case Field::Skip:
|
||||
return "";
|
||||
case Field::Line:
|
||||
if (data.size() >= sizeof(u32)) {
|
||||
u32 line;
|
||||
std::memcpy(&line, data.data(), sizeof(u32));
|
||||
return fmt::format("{}", line);
|
||||
}
|
||||
return "[ERROR DECODING LINE NUMBER]";
|
||||
case Field::Message:
|
||||
case Field::Filename:
|
||||
case Field::Function:
|
||||
case Field::Module:
|
||||
case Field::Thread:
|
||||
return Common::StringFromFixedZeroTerminatedBuffer(
|
||||
reinterpret_cast<const char*>(data.data()), data.size());
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented field type={}", type);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
Manager::Manager(Core::Reporter& reporter) : reporter(reporter) {}
|
||||
|
||||
Manager::~Manager() = default;
|
||||
|
||||
void Manager::SetEnabled(bool enabled) {
|
||||
this->enabled = enabled;
|
||||
}
|
||||
|
||||
void Manager::SetDestination(DestinationFlag destination) {
|
||||
this->destination = destination;
|
||||
}
|
||||
|
||||
void Manager::Log(LogMessage message) {
|
||||
if (message.header.IsHeadLog()) {
|
||||
InitializeLog();
|
||||
}
|
||||
|
||||
current_log.emplace_back(std::move(message));
|
||||
|
||||
if (current_log.back().header.IsTailLog()) {
|
||||
FinalizeLog();
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::Flush() {
|
||||
FinalizeLog();
|
||||
}
|
||||
|
||||
void Manager::InitializeLog() {
|
||||
current_log.clear();
|
||||
|
||||
LOG_INFO(Service_LM, "Initialized new log session");
|
||||
}
|
||||
|
||||
void Manager::FinalizeLog() {
|
||||
reporter.SaveLogReport(static_cast<u32>(destination), std::move(current_log));
|
||||
|
||||
LOG_INFO(Service_LM, "Finalized current log session");
|
||||
}
|
||||
|
||||
} // namespace Service::LM
|
||||
@@ -1,106 +0,0 @@
|
||||
// Copyright 2019 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <ostream>
|
||||
#include <vector>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
|
||||
namespace Core {
|
||||
class Reporter;
|
||||
}
|
||||
|
||||
namespace Service::LM {
|
||||
|
||||
enum class DestinationFlag : u32 {
|
||||
Default = 1,
|
||||
UART = 2,
|
||||
UARTSleeping = 4,
|
||||
|
||||
All = 0xFFFF,
|
||||
};
|
||||
|
||||
struct MessageHeader {
|
||||
enum Flags : u32_le {
|
||||
IsHead = 1,
|
||||
IsTail = 2,
|
||||
};
|
||||
enum Severity : u32_le {
|
||||
Trace,
|
||||
Info,
|
||||
Warning,
|
||||
Error,
|
||||
Critical,
|
||||
};
|
||||
|
||||
u64_le pid;
|
||||
u64_le thread_context;
|
||||
union {
|
||||
BitField<0, 16, Flags> flags;
|
||||
BitField<16, 8, Severity> severity;
|
||||
BitField<24, 8, u32> verbosity;
|
||||
};
|
||||
u32_le payload_size;
|
||||
|
||||
bool IsHeadLog() const {
|
||||
return flags & IsHead;
|
||||
}
|
||||
bool IsTailLog() const {
|
||||
return flags & IsTail;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(MessageHeader) == 0x18, "MessageHeader is incorrect size");
|
||||
|
||||
enum class Field : u8 {
|
||||
Skip = 1,
|
||||
Message = 2,
|
||||
Line = 3,
|
||||
Filename = 4,
|
||||
Function = 5,
|
||||
Module = 6,
|
||||
Thread = 7,
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, DestinationFlag dest);
|
||||
std::ostream& operator<<(std::ostream& os, MessageHeader::Severity severity);
|
||||
std::ostream& operator<<(std::ostream& os, Field field);
|
||||
|
||||
using FieldMap = std::map<Field, std::vector<u8>>;
|
||||
|
||||
struct LogMessage {
|
||||
MessageHeader header;
|
||||
FieldMap fields;
|
||||
};
|
||||
|
||||
std::string FormatField(Field type, const std::vector<u8>& data);
|
||||
|
||||
class Manager {
|
||||
public:
|
||||
explicit Manager(Core::Reporter& reporter);
|
||||
~Manager();
|
||||
|
||||
void SetEnabled(bool enabled);
|
||||
void SetDestination(DestinationFlag destination);
|
||||
|
||||
void Log(LogMessage message);
|
||||
|
||||
void Flush();
|
||||
|
||||
private:
|
||||
void InitializeLog();
|
||||
void FinalizeLog();
|
||||
|
||||
bool enabled = true;
|
||||
DestinationFlag destination = DestinationFlag::All;
|
||||
|
||||
std::vector<LogMessage> current_log;
|
||||
|
||||
Core::Reporter& reporter;
|
||||
};
|
||||
|
||||
} // namespace Service::LM
|
||||
@@ -21,7 +21,7 @@ namespace {
|
||||
|
||||
constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4};
|
||||
|
||||
constexpr std::size_t DefaultMiiCount{sizeof(RawData::DefaultMii) / sizeof(DefaultMii)};
|
||||
constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()};
|
||||
|
||||
constexpr MiiStoreData::Name DefaultMiiName{u'y', u'u', u'z', u'u'};
|
||||
constexpr std::array<u8, 8> HairColorLookup{8, 1, 2, 3, 4, 5, 6, 7};
|
||||
@@ -100,6 +100,7 @@ MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) {
|
||||
.mole_scale = static_cast<u8>(bf.mole_scale.Value()),
|
||||
.mole_x = static_cast<u8>(bf.mole_x.Value()),
|
||||
.mole_y = static_cast<u8>(bf.mole_y.Value()),
|
||||
.padding = 0,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -140,13 +141,6 @@ T GetRandomValue(T max) {
|
||||
return GetRandomValue<T>({}, max);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T GetArrayValue(const u8* data, std::size_t index) {
|
||||
T result{};
|
||||
std::memcpy(&result, &data[index * sizeof(T)], sizeof(T));
|
||||
return result;
|
||||
}
|
||||
|
||||
MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Common::UUID& user_id) {
|
||||
MiiStoreBitFields bf{};
|
||||
|
||||
@@ -192,32 +186,20 @@ MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Commo
|
||||
const std::size_t index{3 * static_cast<std::size_t>(age) +
|
||||
9 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race)};
|
||||
|
||||
const auto faceline_type_info{
|
||||
GetArrayValue<RandomMiiData4>(&RawData::RandomMiiFaceline[0], index)};
|
||||
const auto faceline_color_info{GetArrayValue<RandomMiiData3>(
|
||||
RawData::RandomMiiFacelineColor.data(),
|
||||
const auto faceline_type_info{RawData::RandomMiiFaceline.at(index)};
|
||||
const auto faceline_color_info{RawData::RandomMiiFacelineColor.at(
|
||||
3 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race))};
|
||||
const auto faceline_wrinkle_info{
|
||||
GetArrayValue<RandomMiiData4>(RawData::RandomMiiFacelineWrinkle.data(), index)};
|
||||
const auto faceline_makeup_info{
|
||||
GetArrayValue<RandomMiiData4>(RawData::RandomMiiFacelineMakeup.data(), index)};
|
||||
const auto hair_type_info{
|
||||
GetArrayValue<RandomMiiData4>(RawData::RandomMiiHairType.data(), index)};
|
||||
const auto hair_color_info{GetArrayValue<RandomMiiData3>(RawData::RandomMiiHairColor.data(),
|
||||
3 * static_cast<std::size_t>(race) +
|
||||
static_cast<std::size_t>(age))};
|
||||
const auto eye_type_info{
|
||||
GetArrayValue<RandomMiiData4>(RawData::RandomMiiEyeType.data(), index)};
|
||||
const auto eye_color_info{GetArrayValue<RandomMiiData2>(RawData::RandomMiiEyeColor.data(),
|
||||
static_cast<std::size_t>(race))};
|
||||
const auto eyebrow_type_info{
|
||||
GetArrayValue<RandomMiiData4>(RawData::RandomMiiEyebrowType.data(), index)};
|
||||
const auto nose_type_info{
|
||||
GetArrayValue<RandomMiiData4>(RawData::RandomMiiNoseType.data(), index)};
|
||||
const auto mouth_type_info{
|
||||
GetArrayValue<RandomMiiData4>(RawData::RandomMiiMouthType.data(), index)};
|
||||
const auto glasses_type_info{GetArrayValue<RandomMiiData2>(RawData::RandomMiiGlassType.data(),
|
||||
static_cast<std::size_t>(age))};
|
||||
const auto faceline_wrinkle_info{RawData::RandomMiiFacelineWrinkle.at(index)};
|
||||
const auto faceline_makeup_info{RawData::RandomMiiFacelineMakeup.at(index)};
|
||||
const auto hair_type_info{RawData::RandomMiiHairType.at(index)};
|
||||
const auto hair_color_info{RawData::RandomMiiHairColor.at(3 * static_cast<std::size_t>(race) +
|
||||
static_cast<std::size_t>(age))};
|
||||
const auto eye_type_info{RawData::RandomMiiEyeType.at(index)};
|
||||
const auto eye_color_info{RawData::RandomMiiEyeColor.at(static_cast<std::size_t>(race))};
|
||||
const auto eyebrow_type_info{RawData::RandomMiiEyebrowType.at(index)};
|
||||
const auto nose_type_info{RawData::RandomMiiNoseType.at(index)};
|
||||
const auto mouth_type_info{RawData::RandomMiiMouthType.at(index)};
|
||||
const auto glasses_type_info{RawData::RandomMiiGlassType.at(static_cast<std::size_t>(age))};
|
||||
|
||||
bf.faceline_type.Assign(
|
||||
faceline_type_info.values[GetRandomValue<std::size_t>(faceline_type_info.values_count)]);
|
||||
@@ -454,8 +436,7 @@ MiiInfo MiiManager::BuildRandom(Age age, Gender gender, Race race) {
|
||||
}
|
||||
|
||||
MiiInfo MiiManager::BuildDefault(std::size_t index) {
|
||||
return ConvertStoreDataToInfo(BuildDefaultStoreData(
|
||||
GetArrayValue<DefaultMii>(RawData::DefaultMii.data(), index), user_id));
|
||||
return ConvertStoreDataToInfo(BuildDefaultStoreData(RawData::DefaultMii.at(index), user_id));
|
||||
}
|
||||
|
||||
ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) {
|
||||
|
||||
@@ -27,58 +27,58 @@ enum class SourceFlag : u32 {
|
||||
DECLARE_ENUM_FLAG_OPERATORS(SourceFlag);
|
||||
|
||||
struct MiiInfo {
|
||||
Common::UUID uuid{Common::INVALID_UUID};
|
||||
std::array<char16_t, 11> name{};
|
||||
u8 font_region{};
|
||||
u8 favorite_color{};
|
||||
u8 gender{};
|
||||
u8 height{};
|
||||
u8 build{};
|
||||
u8 type{};
|
||||
u8 region_move{};
|
||||
u8 faceline_type{};
|
||||
u8 faceline_color{};
|
||||
u8 faceline_wrinkle{};
|
||||
u8 faceline_make{};
|
||||
u8 hair_type{};
|
||||
u8 hair_color{};
|
||||
u8 hair_flip{};
|
||||
u8 eye_type{};
|
||||
u8 eye_color{};
|
||||
u8 eye_scale{};
|
||||
u8 eye_aspect{};
|
||||
u8 eye_rotate{};
|
||||
u8 eye_x{};
|
||||
u8 eye_y{};
|
||||
u8 eyebrow_type{};
|
||||
u8 eyebrow_color{};
|
||||
u8 eyebrow_scale{};
|
||||
u8 eyebrow_aspect{};
|
||||
u8 eyebrow_rotate{};
|
||||
u8 eyebrow_x{};
|
||||
u8 eyebrow_y{};
|
||||
u8 nose_type{};
|
||||
u8 nose_scale{};
|
||||
u8 nose_y{};
|
||||
u8 mouth_type{};
|
||||
u8 mouth_color{};
|
||||
u8 mouth_scale{};
|
||||
u8 mouth_aspect{};
|
||||
u8 mouth_y{};
|
||||
u8 beard_color{};
|
||||
u8 beard_type{};
|
||||
u8 mustache_type{};
|
||||
u8 mustache_scale{};
|
||||
u8 mustache_y{};
|
||||
u8 glasses_type{};
|
||||
u8 glasses_color{};
|
||||
u8 glasses_scale{};
|
||||
u8 glasses_y{};
|
||||
u8 mole_type{};
|
||||
u8 mole_scale{};
|
||||
u8 mole_x{};
|
||||
u8 mole_y{};
|
||||
INSERT_PADDING_BYTES(1);
|
||||
Common::UUID uuid;
|
||||
std::array<char16_t, 11> name;
|
||||
u8 font_region;
|
||||
u8 favorite_color;
|
||||
u8 gender;
|
||||
u8 height;
|
||||
u8 build;
|
||||
u8 type;
|
||||
u8 region_move;
|
||||
u8 faceline_type;
|
||||
u8 faceline_color;
|
||||
u8 faceline_wrinkle;
|
||||
u8 faceline_make;
|
||||
u8 hair_type;
|
||||
u8 hair_color;
|
||||
u8 hair_flip;
|
||||
u8 eye_type;
|
||||
u8 eye_color;
|
||||
u8 eye_scale;
|
||||
u8 eye_aspect;
|
||||
u8 eye_rotate;
|
||||
u8 eye_x;
|
||||
u8 eye_y;
|
||||
u8 eyebrow_type;
|
||||
u8 eyebrow_color;
|
||||
u8 eyebrow_scale;
|
||||
u8 eyebrow_aspect;
|
||||
u8 eyebrow_rotate;
|
||||
u8 eyebrow_x;
|
||||
u8 eyebrow_y;
|
||||
u8 nose_type;
|
||||
u8 nose_scale;
|
||||
u8 nose_y;
|
||||
u8 mouth_type;
|
||||
u8 mouth_color;
|
||||
u8 mouth_scale;
|
||||
u8 mouth_aspect;
|
||||
u8 mouth_y;
|
||||
u8 beard_color;
|
||||
u8 beard_type;
|
||||
u8 mustache_type;
|
||||
u8 mustache_scale;
|
||||
u8 mustache_y;
|
||||
u8 glasses_type;
|
||||
u8 glasses_color;
|
||||
u8 glasses_scale;
|
||||
u8 glasses_y;
|
||||
u8 mole_type;
|
||||
u8 mole_scale;
|
||||
u8 mole_x;
|
||||
u8 mole_y;
|
||||
u8 padding;
|
||||
|
||||
std::u16string Name() const;
|
||||
};
|
||||
@@ -233,7 +233,7 @@ struct RandomMiiData4 {
|
||||
Age age{};
|
||||
Race race{};
|
||||
u32 values_count{};
|
||||
std::array<u8, 0xbc> values{};
|
||||
std::array<u32, 47> values{};
|
||||
};
|
||||
static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size.");
|
||||
|
||||
@@ -241,14 +241,14 @@ struct RandomMiiData3 {
|
||||
u32 arg_1;
|
||||
u32 arg_2;
|
||||
u32 values_count;
|
||||
std::array<u8, 0xbc> values{};
|
||||
std::array<u32, 47> values{};
|
||||
};
|
||||
static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size.");
|
||||
|
||||
struct RandomMiiData2 {
|
||||
u32 arg_1;
|
||||
u32 values_count;
|
||||
std::array<u8, 0xbc> values{};
|
||||
std::array<u32, 47> values{};
|
||||
};
|
||||
static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size.");
|
||||
|
||||
@@ -324,7 +324,7 @@ public:
|
||||
ResultCode GetIndex(const MiiInfo& info, u32& index);
|
||||
|
||||
private:
|
||||
const Common::UUID user_id;
|
||||
const Common::UUID user_id{Common::INVALID_UUID};
|
||||
u64 update_counter{};
|
||||
};
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,21 +7,22 @@
|
||||
#include <array>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/mii/manager.h"
|
||||
|
||||
namespace Service::Mii::RawData {
|
||||
|
||||
extern const std::array<u8, 1728> DefaultMii;
|
||||
extern const std::array<u8, 3672> RandomMiiFaceline;
|
||||
extern const std::array<u8, 1200> RandomMiiFacelineColor;
|
||||
extern const std::array<u8, 3672> RandomMiiFacelineWrinkle;
|
||||
extern const std::array<u8, 3672> RandomMiiFacelineMakeup;
|
||||
extern const std::array<u8, 3672> RandomMiiHairType;
|
||||
extern const std::array<u8, 1800> RandomMiiHairColor;
|
||||
extern const std::array<u8, 3672> RandomMiiEyeType;
|
||||
extern const std::array<u8, 588> RandomMiiEyeColor;
|
||||
extern const std::array<u8, 3672> RandomMiiEyebrowType;
|
||||
extern const std::array<u8, 3672> RandomMiiNoseType;
|
||||
extern const std::array<u8, 3672> RandomMiiMouthType;
|
||||
extern const std::array<u8, 588> RandomMiiGlassType;
|
||||
extern const std::array<Service::Mii::DefaultMii, 8> DefaultMii;
|
||||
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFaceline;
|
||||
extern const std::array<Service::Mii::RandomMiiData3, 6> RandomMiiFacelineColor;
|
||||
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFacelineWrinkle;
|
||||
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFacelineMakeup;
|
||||
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiHairType;
|
||||
extern const std::array<Service::Mii::RandomMiiData3, 9> RandomMiiHairColor;
|
||||
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiEyeType;
|
||||
extern const std::array<Service::Mii::RandomMiiData2, 3> RandomMiiEyeColor;
|
||||
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiEyebrowType;
|
||||
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiNoseType;
|
||||
extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiMouthType;
|
||||
extern const std::array<Service::Mii::RandomMiiData2, 3> RandomMiiGlassType;
|
||||
|
||||
} // namespace Service::Mii::RawData
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/hle/service/nfp/nfp.h"
|
||||
|
||||
@@ -155,7 +155,13 @@ NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::ve
|
||||
return NvResult::BadParameter;
|
||||
}
|
||||
if (events_interface.registered[event_id]) {
|
||||
return NvResult::BadParameter;
|
||||
const auto event_state = events_interface.status[event_id];
|
||||
if (event_state != EventState::Free) {
|
||||
LOG_WARNING(Service_NVDRV, "Event already registered! Unregistering previous event");
|
||||
events_interface.UnregisterEvent(event_id);
|
||||
} else {
|
||||
return NvResult::BadParameter;
|
||||
}
|
||||
}
|
||||
events_interface.RegisterEvent(event_id);
|
||||
return NvResult::Success;
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/nvdrv/interface.h"
|
||||
#include "core/hle/service/nvdrv/nvdata.h"
|
||||
|
||||
@@ -50,11 +50,11 @@ public:
|
||||
{1046, nullptr, "DisableFeaturesForReset"},
|
||||
{1047, nullptr, "NotifyApplicationDownloadStarted"},
|
||||
{1048, nullptr, "NotifyNetworkProfileCreated"},
|
||||
{1061, nullptr, "ConfirmStereoVisionRestrictionConfigurable"},
|
||||
{1062, nullptr, "GetStereoVisionRestriction"},
|
||||
{1063, nullptr, "SetStereoVisionRestriction"},
|
||||
{1064, nullptr, "ResetConfirmedStereoVisionPermission"},
|
||||
{1065, nullptr, "IsStereoVisionPermitted"},
|
||||
{1061, &IParentalControlService::ConfirmStereoVisionRestrictionConfigurable, "ConfirmStereoVisionRestrictionConfigurable"},
|
||||
{1062, &IParentalControlService::GetStereoVisionRestriction, "GetStereoVisionRestriction"},
|
||||
{1063, &IParentalControlService::SetStereoVisionRestriction, "SetStereoVisionRestriction"},
|
||||
{1064, &IParentalControlService::ResetConfirmedStereoVisionPermission, "ResetConfirmedStereoVisionPermission"},
|
||||
{1065, &IParentalControlService::IsStereoVisionPermitted, "IsStereoVisionPermitted"},
|
||||
{1201, nullptr, "UnlockRestrictionTemporarily"},
|
||||
{1202, nullptr, "UnlockSystemSettingsRestriction"},
|
||||
{1203, nullptr, "SetPinCode"},
|
||||
@@ -114,6 +114,7 @@ public:
|
||||
{2015, nullptr, "FinishSynchronizeParentalControlSettingsWithLastUpdated"},
|
||||
{2016, nullptr, "RequestUpdateExemptionListAsync"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -131,6 +132,49 @@ private:
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void ConfirmStereoVisionRestrictionConfigurable(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_PCTL, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void IsStereoVisionPermitted(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_PCTL, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(true);
|
||||
}
|
||||
|
||||
void SetStereoVisionRestriction(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto can_use = rp.Pop<bool>();
|
||||
LOG_WARNING(Service_PCTL, "(STUBBED) called, can_use={}", can_use);
|
||||
|
||||
can_use_stereo_vision = can_use;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetStereoVisionRestriction(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_PCTL, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(can_use_stereo_vision);
|
||||
}
|
||||
|
||||
void ResetConfirmedStereoVisionPermission(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_PCTL, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
bool can_use_stereo_vision = true;
|
||||
};
|
||||
|
||||
void Module::Interface::CreateService(Kernel::HLERequestContext& ctx) {
|
||||
@@ -149,7 +193,8 @@ void Module::Interface::CreateServiceWithoutInitialize(Kernel::HLERequestContext
|
||||
rb.PushIpcInterface<IParentalControlService>(system);
|
||||
}
|
||||
|
||||
Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_, const char* name)
|
||||
Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_,
|
||||
const char* name)
|
||||
: ServiceFramework{system_, name}, module{std::move(module_)} {}
|
||||
|
||||
Module::Interface::~Interface() = default;
|
||||
|
||||
@@ -23,8 +23,8 @@ public:
|
||||
{10101, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::Old>, "SaveReportWithUserOld"},
|
||||
{10102, &PlayReport::SaveReport<Core::Reporter::PlayReportType::Old2>, "SaveReportOld2"},
|
||||
{10103, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::Old2>, "SaveReportWithUserOld2"},
|
||||
{10104, nullptr, "SaveReport"},
|
||||
{10105, nullptr, "SaveReportWithUser"},
|
||||
{10104, &PlayReport::SaveReport<Core::Reporter::PlayReportType::New>, "SaveReport"},
|
||||
{10105, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::New>, "SaveReportWithUser"},
|
||||
{10200, nullptr, "RequestImmediateTransmission"},
|
||||
{10300, nullptr, "GetTransmissionStatus"},
|
||||
{10400, nullptr, "GetSystemSessionId"},
|
||||
@@ -59,16 +59,22 @@ private:
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto process_id = rp.PopRaw<u64>();
|
||||
|
||||
std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)};
|
||||
if constexpr (Type == Core::Reporter::PlayReportType::Old2) {
|
||||
data.emplace_back(ctx.ReadBuffer(1));
|
||||
}
|
||||
const auto data1 = ctx.ReadBuffer(0);
|
||||
const auto data2 = [ctx] {
|
||||
if (ctx.CanReadBuffer(1)) {
|
||||
return ctx.ReadBuffer(1);
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_PREPO, "called, type={:02X}, process_id={:016X}, data1_size={:016X}",
|
||||
Type, process_id, data[0].size());
|
||||
return std::vector<u8>{};
|
||||
}();
|
||||
|
||||
LOG_DEBUG(Service_PREPO,
|
||||
"called, type={:02X}, process_id={:016X}, data1_size={:016X}, data2_size={:016X}",
|
||||
Type, process_id, data1.size(), data2.size());
|
||||
|
||||
const auto& reporter{system.GetReporter()};
|
||||
reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), data, process_id);
|
||||
reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), {data1, data2},
|
||||
process_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -79,24 +85,24 @@ private:
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto user_id = rp.PopRaw<u128>();
|
||||
const auto process_id = rp.PopRaw<u64>();
|
||||
std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)};
|
||||
|
||||
if constexpr (Type == Core::Reporter::PlayReportType::Old2) {
|
||||
const auto read_buffer_count =
|
||||
ctx.BufferDescriptorX().size() + ctx.BufferDescriptorA().size();
|
||||
if (read_buffer_count > 1) {
|
||||
data.emplace_back(ctx.ReadBuffer(1));
|
||||
const auto data1 = ctx.ReadBuffer(0);
|
||||
const auto data2 = [ctx] {
|
||||
if (ctx.CanReadBuffer(1)) {
|
||||
return ctx.ReadBuffer(1);
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DEBUG(
|
||||
Service_PREPO,
|
||||
"called, type={:02X}, user_id={:016X}{:016X}, process_id={:016X}, data1_size={:016X}",
|
||||
Type, user_id[1], user_id[0], process_id, data[0].size());
|
||||
return std::vector<u8>{};
|
||||
}();
|
||||
|
||||
LOG_DEBUG(Service_PREPO,
|
||||
"called, type={:02X}, user_id={:016X}{:016X}, process_id={:016X}, "
|
||||
"data1_size={:016X}, data2_size={:016X}",
|
||||
Type, user_id[1], user_id[0], process_id, data1.size(), data2.size());
|
||||
|
||||
const auto& reporter{system.GetReporter()};
|
||||
reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), data, process_id,
|
||||
user_id);
|
||||
reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), {data1, data2},
|
||||
process_id, user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -107,7 +113,13 @@ private:
|
||||
const auto title_id = rp.PopRaw<u64>();
|
||||
|
||||
const auto data1 = ctx.ReadBuffer(0);
|
||||
const auto data2 = ctx.ReadBuffer(1);
|
||||
const auto data2 = [ctx] {
|
||||
if (ctx.CanReadBuffer(1)) {
|
||||
return ctx.ReadBuffer(1);
|
||||
}
|
||||
|
||||
return std::vector<u8>{};
|
||||
}();
|
||||
|
||||
LOG_DEBUG(Service_PREPO, "called, title_id={:016X}, data1_size={:016X}, data2_size={:016X}",
|
||||
title_id, data1.size(), data2.size());
|
||||
@@ -125,7 +137,13 @@ private:
|
||||
const auto title_id = rp.PopRaw<u64>();
|
||||
|
||||
const auto data1 = ctx.ReadBuffer(0);
|
||||
const auto data2 = ctx.ReadBuffer(1);
|
||||
const auto data2 = [ctx] {
|
||||
if (ctx.CanReadBuffer(1)) {
|
||||
return ctx.ReadBuffer(1);
|
||||
}
|
||||
|
||||
return std::vector<u8>{};
|
||||
}();
|
||||
|
||||
LOG_DEBUG(Service_PREPO,
|
||||
"called, user_id={:016X}{:016X}, title_id={:016X}, data1_size={:016X}, "
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user