hle: kernel: Recode implementation of KThread to be more accurate.

This commit is contained in:
bunnei 2021-01-20 13:42:27 -08:00
parent 1470338458
commit cdd14b03e5
14 changed files with 1583 additions and 806 deletions

View file

@ -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<KThread> thread = handle_table.Get<KThread>(thread_handle);
if (!thread) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", thread_handle);
return ERR_INVALID_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;
}
@ -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<KThread> thread = handle_table.Get<KThread>(thread_handle);
if (!thread) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
thread_handle);
return ERR_INVALID_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();
}
}
@ -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,127 +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<KThread> thread = current_process->GetHandleTable().Get<KThread>(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<KThread> thread = current_process->GetHandleTable().Get<KThread>(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<KThread> thread = handle_table.Get<KThread>(handle);
if (!thread) {
*priority = 0;
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
return ERR_INVALID_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 > Svc::LowestThreadPriority) {
LOG_ERROR(Kernel_SVC, "An invalid priority was specified {} for thread_handle={:08X}",
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<KThread> thread = current_process->GetHandleTable().Get<KThread>(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;
}
@ -1436,7 +1416,7 @@ 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) {
@ -1500,17 +1480,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<KThread> thread = handle_table.Get<KThread>(thread_handle);
if (!thread) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
thread_handle);
return ERR_INVALID_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) {
@ -1523,7 +1501,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) {
@ -1532,34 +1510,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);
}
}
@ -1822,95 +1794,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<KThread> thread = handle_table.Get<KThread>(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;
}
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>(Svc::IdealCoreUseProcessValue)) {
const u8 ideal_cpu_core = current_process->GetIdealCoreId();
ASSERT(ideal_cpu_core != static_cast<u8>(Svc::IdealCoreUseProcessValue));
// 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>(Svc::IdealCoreDontCare) &&
core != static_cast<u32>(Svc::IdealCoreNoUpdate)) {
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();
// 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);
if (!thread) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
thread_handle);
return ERR_INVALID_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) {
@ -2474,7 +2423,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);
@ -2490,7 +2439,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();
}