summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/core.cpp1
-rw-r--r--src/core/gdbstub/gdbstub.cpp5
-rw-r--r--src/core/hle/hle.cpp58
-rw-r--r--src/core/hle/kernel/event.cpp6
-rw-r--r--src/core/hle/kernel/event.h4
-rw-r--r--src/core/hle/kernel/kernel.cpp49
-rw-r--r--src/core/hle/kernel/kernel.h13
-rw-r--r--src/core/hle/kernel/mutex.cpp84
-rw-r--r--src/core/hle/kernel/mutex.h17
-rw-r--r--src/core/hle/kernel/semaphore.cpp7
-rw-r--r--src/core/hle/kernel/semaphore.h4
-rw-r--r--src/core/hle/kernel/server_port.cpp6
-rw-r--r--src/core/hle/kernel/server_port.h4
-rw-r--r--src/core/hle/kernel/server_session.cpp6
-rw-r--r--src/core/hle/kernel/server_session.h4
-rw-r--r--src/core/hle/kernel/thread.cpp98
-rw-r--r--src/core/hle/kernel/thread.h62
-rw-r--r--src/core/hle/kernel/timer.cpp6
-rw-r--r--src/core/hle/kernel/timer.h4
-rw-r--r--src/core/hle/service/boss/boss.cpp4
-rw-r--r--src/core/hle/service/cfg/cfg.cpp58
-rw-r--r--src/core/hle/service/cfg/cfg.h7
-rw-r--r--src/core/hle/service/mic_u.cpp5
-rw-r--r--src/core/hle/service/nfc/nfc.cpp20
-rw-r--r--src/core/hle/service/nfc/nfc.h17
-rw-r--r--src/core/hle/service/nfc/nfc_m.cpp2
-rw-r--r--src/core/hle/service/nfc/nfc_u.cpp2
-rw-r--r--src/core/hle/service/service.cpp1
-rw-r--r--src/core/hle/service/soc_u.cpp4
-rw-r--r--src/core/hle/svc.cpp80
-rw-r--r--src/core/loader/ncch.cpp24
-rw-r--r--src/core/loader/ncch.h5
-rw-r--r--src/core/settings.h4
33 files changed, 380 insertions, 291 deletions
diff --git a/src/core/core.cpp b/src/core/core.cpp
index ee5237096..202cd332b 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -159,6 +159,7 @@ void System::Shutdown() {
Kernel::Shutdown();
HW::Shutdown();
CoreTiming::Shutdown();
+ cpu_core.reset();
LOG_DEBUG(Core, "Shutdown OK");
}
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index d88e25073..5cf45ada5 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -57,7 +57,6 @@ const u32 SIGTERM = 15;
const u32 MSG_WAITALL = 8;
#endif
-const u32 R0_REGISTER = 0;
const u32 R15_REGISTER = 15;
const u32 CPSR_REGISTER = 25;
const u32 FPSCR_REGISTER = 58;
@@ -816,10 +815,6 @@ static void RemoveBreakpoint() {
auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
PAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset));
- start_offset = addr_pos + 1;
- u32 len =
- HexToInt(start_offset, static_cast<u32>((command_buffer + command_length) - start_offset));
-
if (type == BreakpointType::Access) {
// Access is made up of Read and Write types, so add both breakpoints
type = BreakpointType::Read;
diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp
deleted file mode 100644
index d73d98a70..000000000
--- a/src/core/hle/hle.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "common/assert.h"
-#include "common/logging/log.h"
-#include "core/arm/arm_interface.h"
-#include "core/core.h"
-#include "core/hle/hle.h"
-#include "core/hle/service/service.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-namespace {
-
-bool reschedule; ///< If true, immediately reschedules the CPU to a new thread
-}
-
-namespace HLE {
-
-void Reschedule(const char* reason) {
- DEBUG_ASSERT_MSG(reason != nullptr && strlen(reason) < 256,
- "Reschedule: Invalid or too long reason.");
-
- // TODO(bunnei): It seems that games depend on some CPU execution time elapsing during HLE
- // routines. This simulates that time by artificially advancing the number of CPU "ticks".
- // The value was chosen empirically, it seems to work well enough for everything tested, but
- // is likely not ideal. We should find a more accurate way to simulate timing with HLE.
- Core::AppCore().AddTicks(4000);
-
- Core::AppCore().PrepareReschedule();
-
- reschedule = true;
-}
-
-bool IsReschedulePending() {
- return reschedule;
-}
-
-void DoneRescheduling() {
- reschedule = false;
-}
-
-void Init() {
- Service::Init();
-
- reschedule = false;
-
- LOG_DEBUG(Kernel, "initialized OK");
-}
-
-void Shutdown() {
- Service::Shutdown();
-
- LOG_DEBUG(Kernel, "shutdown OK");
-}
-
-} // namespace
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp
index 3e116e3df..e1f42af05 100644
--- a/src/core/hle/kernel/event.cpp
+++ b/src/core/hle/kernel/event.cpp
@@ -30,12 +30,12 @@ SharedPtr<Event> Event::Create(ResetType reset_type, std::string name) {
return evt;
}
-bool Event::ShouldWait() {
+bool Event::ShouldWait(Thread* thread) const {
return !signaled;
}
-void Event::Acquire() {
- ASSERT_MSG(!ShouldWait(), "object unavailable!");
+void Event::Acquire(Thread* thread) {
+ ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
// Release the event if it's not sticky...
if (reset_type != ResetType::Sticky)
diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h
index 8dcd23edb..39452bf33 100644
--- a/src/core/hle/kernel/event.h
+++ b/src/core/hle/kernel/event.h
@@ -35,8 +35,8 @@ public:
bool signaled; ///< Whether the event has already been signaled
std::string name; ///< Name of event (optional)
- bool ShouldWait() override;
- void Acquire() override;
+ bool ShouldWait(Thread* thread) const override;
+ void Acquire(Thread* thread) override;
void Signal();
void Clear();
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 1db8e102f..f599916f0 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -3,7 +3,6 @@
// Refer to the license.txt file included.
#include <algorithm>
-#include <boost/range/algorithm_ext/erase.hpp>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/hle/config_mem.h"
@@ -28,32 +27,39 @@ void WaitObject::AddWaitingThread(SharedPtr<Thread> thread) {
void WaitObject::RemoveWaitingThread(Thread* thread) {
auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread);
+ // If a thread passed multiple handles to the same object,
+ // the kernel might attempt to remove the thread from the object's
+ // waiting threads list multiple times.
if (itr != waiting_threads.end())
waiting_threads.erase(itr);
}
SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
- // Remove the threads that are ready or already running from our waitlist
- boost::range::remove_erase_if(waiting_threads, [](const SharedPtr<Thread>& thread) {
- return thread->status == THREADSTATUS_RUNNING || thread->status == THREADSTATUS_READY ||
- thread->status == THREADSTATUS_DEAD;
- });
-
- // TODO(Subv): This call should be performed inside the loop below to check if an object can be
- // acquired by a particular thread. This is useful for things like recursive locking of Mutexes.
- if (ShouldWait())
- return nullptr;
-
Thread* candidate = nullptr;
s32 candidate_priority = THREADPRIO_LOWEST + 1;
for (const auto& thread : waiting_threads) {
+ // The list of waiting threads must not contain threads that are not waiting to be awakened.
+ ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
+ thread->status == THREADSTATUS_WAIT_SYNCH_ALL,
+ "Inconsistent thread statuses in waiting_threads");
+
if (thread->current_priority >= candidate_priority)
continue;
- bool ready_to_run =
- std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(),
- [](const SharedPtr<WaitObject>& object) { return object->ShouldWait(); });
+ if (ShouldWait(thread.get()))
+ continue;
+
+ // A thread is ready to run if it's either in THREADSTATUS_WAIT_SYNCH_ANY or
+ // in THREADSTATUS_WAIT_SYNCH_ALL and the rest of the objects it is waiting on are ready.
+ bool ready_to_run = true;
+ if (thread->status == THREADSTATUS_WAIT_SYNCH_ALL) {
+ ready_to_run = std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(),
+ [&thread](const SharedPtr<WaitObject>& object) {
+ return object->ShouldWait(thread.get());
+ });
+ }
+
if (ready_to_run) {
candidate = thread.get();
candidate_priority = thread->current_priority;
@@ -66,7 +72,7 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
void WaitObject::WakeupAllWaitingThreads() {
while (auto thread = GetHighestPriorityReadyThread()) {
if (!thread->IsSleepingOnWaitAll()) {
- Acquire();
+ Acquire(thread.get());
// Set the output index of the WaitSynchronizationN call to the index of this object.
if (thread->wait_set_output) {
thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(this));
@@ -74,18 +80,17 @@ void WaitObject::WakeupAllWaitingThreads() {
}
} else {
for (auto& object : thread->wait_objects) {
- object->Acquire();
- object->RemoveWaitingThread(thread.get());
+ object->Acquire(thread.get());
}
// Note: This case doesn't update the output index of WaitSynchronizationN.
- // Clear the thread's waitlist
- thread->wait_objects.clear();
}
+ for (auto& object : thread->wait_objects)
+ object->RemoveWaitingThread(thread.get());
+ thread->wait_objects.clear();
+
thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
thread->ResumeFromWait();
- // Note: Removing the thread from the object's waitlist will be
- // done by GetHighestPriorityReadyThread.
}
}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 9503e7d04..05097824b 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -132,25 +132,26 @@ using SharedPtr = boost::intrusive_ptr<T>;
class WaitObject : public Object {
public:
/**
- * Check if the current thread should wait until the object is available
+ * Check if the specified thread should wait until the object is available
+ * @param thread The thread about which we're deciding.
* @return True if the current thread should wait due to this object being unavailable
*/
- virtual bool ShouldWait() = 0;
+ virtual bool ShouldWait(Thread* thread) const = 0;
- /// Acquire/lock the object if it is available
- virtual void Acquire() = 0;
+ /// Acquire/lock the object for the specified thread if it is available
+ virtual void Acquire(Thread* thread) = 0;
/**
* Add a thread to wait on this object
* @param thread Pointer to thread to add
*/
- void AddWaitingThread(SharedPtr<Thread> thread);
+ virtual void AddWaitingThread(SharedPtr<Thread> thread);
/**
* Removes a thread from waiting on this object (e.g. if it was resumed already)
* @param thread Pointer to thread to remove
*/
- void RemoveWaitingThread(Thread* thread);
+ virtual void RemoveWaitingThread(Thread* thread);
/**
* Wake up all threads waiting on this object that can be awoken, in priority order,
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index 736944bae..cef961289 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -6,26 +6,18 @@
#include <vector>
#include <boost/range/algorithm_ext/erase.hpp>
#include "common/assert.h"
+#include "core/core.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/thread.h"
namespace Kernel {
-/**
- * Resumes a thread waiting for the specified mutex
- * @param mutex The mutex that some thread is waiting on
- */
-static void ResumeWaitingThread(Mutex* mutex) {
- // Reset mutex lock thread handle, nothing is waiting
- mutex->lock_count = 0;
- mutex->holding_thread = nullptr;
- mutex->WakeupAllWaitingThreads();
-}
-
void ReleaseThreadMutexes(Thread* thread) {
for (auto& mtx : thread->held_mutexes) {
- ResumeWaitingThread(mtx.get());
+ mtx->lock_count = 0;
+ mtx->holding_thread = nullptr;
+ mtx->WakeupAllWaitingThreads();
}
thread->held_mutexes.clear();
}
@@ -40,52 +32,74 @@ SharedPtr<Mutex> Mutex::Create(bool initial_locked, std::string name) {
mutex->name = std::move(name);
mutex->holding_thread = nullptr;
- // Acquire mutex with current thread if initialized as locked...
+ // Acquire mutex with current thread if initialized as locked
if (initial_locked)
- mutex->Acquire();
+ mutex->Acquire(GetCurrentThread());
return mutex;
}
-bool Mutex::ShouldWait() {
- auto thread = GetCurrentThread();
- bool wait = lock_count > 0 && holding_thread != thread;
-
- // If the holding thread of the mutex is lower priority than this thread, that thread should
- // temporarily inherit this thread's priority
- if (wait && thread->current_priority < holding_thread->current_priority)
- holding_thread->BoostPriority(thread->current_priority);
-
- return wait;
-}
-
-void Mutex::Acquire() {
- Acquire(GetCurrentThread());
+bool Mutex::ShouldWait(Thread* thread) const {
+ return lock_count > 0 && thread != holding_thread;
}
-void Mutex::Acquire(SharedPtr<Thread> thread) {
- ASSERT_MSG(!ShouldWait(), "object unavailable!");
+void Mutex::Acquire(Thread* thread) {
+ ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
- // Actually "acquire" the mutex only if we don't already have it...
+ // Actually "acquire" the mutex only if we don't already have it
if (lock_count == 0) {
+ priority = thread->current_priority;
thread->held_mutexes.insert(this);
- holding_thread = std::move(thread);
+ holding_thread = thread;
+ thread->UpdatePriority();
+ Core::System::GetInstance().PrepareReschedule();
}
lock_count++;
}
void Mutex::Release() {
- // Only release if the mutex is held...
+ // Only release if the mutex is held
if (lock_count > 0) {
lock_count--;
- // Yield to the next thread only if we've fully released the mutex...
+ // Yield to the next thread only if we've fully released the mutex
if (lock_count == 0) {
holding_thread->held_mutexes.erase(this);
- ResumeWaitingThread(this);
+ holding_thread->UpdatePriority();
+ holding_thread = nullptr;
+ WakeupAllWaitingThreads();
+ Core::System::GetInstance().PrepareReschedule();
}
}
}
+void Mutex::AddWaitingThread(SharedPtr<Thread> thread) {
+ WaitObject::AddWaitingThread(thread);
+ thread->pending_mutexes.insert(this);
+ UpdatePriority();
+}
+
+void Mutex::RemoveWaitingThread(Thread* thread) {
+ WaitObject::RemoveWaitingThread(thread);
+ thread->pending_mutexes.erase(this);
+ UpdatePriority();
+}
+
+void Mutex::UpdatePriority() {
+ if (!holding_thread)
+ return;
+
+ s32 best_priority = THREADPRIO_LOWEST;
+ for (auto& waiter : GetWaitingThreads()) {
+ if (waiter->current_priority < best_priority)
+ best_priority = waiter->current_priority;
+ }
+
+ if (best_priority != priority) {
+ priority = best_priority;
+ holding_thread->UpdatePriority();
+ }
+}
+
} // namespace
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h
index 53c3dc1f1..c57adf400 100644
--- a/src/core/hle/kernel/mutex.h
+++ b/src/core/hle/kernel/mutex.h
@@ -35,17 +35,22 @@ public:
}
int lock_count; ///< Number of times the mutex has been acquired
+ u32 priority; ///< The priority of the mutex, used for priority inheritance.
std::string name; ///< Name of mutex (optional)
SharedPtr<Thread> holding_thread; ///< Thread that has acquired the mutex
- bool ShouldWait() override;
- void Acquire() override;
-
/**
- * Acquires the specified mutex for the specified thread
- * @param thread Thread that will acquire the mutex
+ * Elevate the mutex priority to the best priority
+ * among the priorities of all its waiting threads.
*/
- void Acquire(SharedPtr<Thread> thread);
+ void UpdatePriority();
+
+ bool ShouldWait(Thread* thread) const override;
+ void Acquire(Thread* thread) override;
+
+ void AddWaitingThread(SharedPtr<Thread> thread) override;
+ void RemoveWaitingThread(Thread* thread) override;
+
void Release();
private:
diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp
index bf7600780..8bda2f75d 100644
--- a/src/core/hle/kernel/semaphore.cpp
+++ b/src/core/hle/kernel/semaphore.cpp
@@ -30,12 +30,13 @@ ResultVal<SharedPtr<Semaphore>> Semaphore::Create(s32 initial_count, s32 max_cou
return MakeResult<SharedPtr<Semaphore>>(std::move(semaphore));
}
-bool Semaphore::ShouldWait() {
+bool Semaphore::ShouldWait(Thread* thread) const {
return available_count <= 0;
}
-void Semaphore::Acquire() {
- ASSERT_MSG(!ShouldWait(), "object unavailable!");
+void Semaphore::Acquire(Thread* thread) {
+ if (available_count <= 0)
+ return;
--available_count;
}
diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h
index e01908a25..cde94f7cc 100644
--- a/src/core/hle/kernel/semaphore.h
+++ b/src/core/hle/kernel/semaphore.h
@@ -39,8 +39,8 @@ public:
s32 available_count; ///< Number of free slots left in the semaphore
std::string name; ///< Name of semaphore (optional)
- bool ShouldWait() override;
- void Acquire() override;
+ bool ShouldWait(Thread* thread) const override;
+ void Acquire(Thread* thread) override;
/**
* Releases a certain number of slots from a semaphore.
diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp
index 6c19aa7c0..fd3bbbcad 100644
--- a/src/core/hle/kernel/server_port.cpp
+++ b/src/core/hle/kernel/server_port.cpp
@@ -14,13 +14,13 @@ namespace Kernel {
ServerPort::ServerPort() {}
ServerPort::~ServerPort() {}
-bool ServerPort::ShouldWait() {
+bool ServerPort::ShouldWait(Thread* thread) const {
// If there are no pending sessions, we wait until a new one is added.
return pending_sessions.size() == 0;
}
-void ServerPort::Acquire() {
- ASSERT_MSG(!ShouldWait(), "object unavailable!");
+void ServerPort::Acquire(Thread* thread) {
+ ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
}
std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> ServerPort::CreatePortPair(
diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h
index b0f8df62c..6f8bdb6a9 100644
--- a/src/core/hle/kernel/server_port.h
+++ b/src/core/hle/kernel/server_port.h
@@ -53,8 +53,8 @@ public:
/// ServerSessions created from this port inherit a reference to this handler.
std::shared_ptr<Service::SessionRequestHandler> hle_handler;
- bool ShouldWait() override;
- void Acquire() override;
+ bool ShouldWait(Thread* thread) const override;
+ void Acquire(Thread* thread) override;
private:
ServerPort();
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index 146458c1c..9447ff236 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -29,12 +29,12 @@ ResultVal<SharedPtr<ServerSession>> ServerSession::Create(
return MakeResult<SharedPtr<ServerSession>>(std::move(server_session));
}
-bool ServerSession::ShouldWait() {
+bool ServerSession::ShouldWait(Thread* thread) const {
return !signaled;
}
-void ServerSession::Acquire() {
- ASSERT_MSG(!ShouldWait(), "object unavailable!");
+void ServerSession::Acquire(Thread* thread) {
+ ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
signaled = false;
}
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h
index 458284a5d..c088b9a19 100644
--- a/src/core/hle/kernel/server_session.h
+++ b/src/core/hle/kernel/server_session.h
@@ -57,9 +57,9 @@ public:
*/
ResultCode HandleSyncRequest();
- bool ShouldWait() override;
+ bool ShouldWait(Thread* thread) const override;
- void Acquire() override;
+ void Acquire(Thread* thread) override;
std::string name; ///< The name of this session (optional)
bool signaled; ///< Whether there's new data available to this ServerSession
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 5fb95dada..8c6fbcd04 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -27,12 +27,12 @@ namespace Kernel {
/// Event type for the thread wake up event
static int ThreadWakeupEventType;
-bool Thread::ShouldWait() {
+bool Thread::ShouldWait(Thread* thread) const {
return status != THREADSTATUS_DEAD;
}
-void Thread::Acquire() {
- ASSERT_MSG(!ShouldWait(), "object unavailable!");
+void Thread::Acquire(Thread* thread) {
+ ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
}
// TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, allowing
@@ -66,20 +66,6 @@ Thread* GetCurrentThread() {
}
/**
- * Check if a thread is waiting on the specified wait object
- * @param thread The thread to test
- * @param wait_object The object to test against
- * @return True if the thread is waiting, false otherwise
- */
-static bool CheckWait_WaitObject(const Thread* thread, WaitObject* wait_object) {
- if (thread->status != THREADSTATUS_WAIT_SYNCH)
- return false;
-
- auto itr = std::find(thread->wait_objects.begin(), thread->wait_objects.end(), wait_object);
- return itr != thread->wait_objects.end();
-}
-
-/**
* Check if the specified thread is waiting on the specified address to be arbitrated
* @param thread The thread to test
* @param wait_address The address to test against
@@ -90,9 +76,6 @@ static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) {
}
void Thread::Stop() {
- // Release all the mutexes that this thread holds
- ReleaseThreadMutexes(this);
-
// Cancel any outstanding wakeup events for this thread
CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle);
wakeup_callback_handle_table.Close(callback_handle);
@@ -114,6 +97,9 @@ void Thread::Stop() {
}
wait_objects.clear();
+ // Release all the mutexes that this thread holds
+ ReleaseThreadMutexes(this);
+
// Mark the TLS slot in the thread's page as free.
u32 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE;
u32 tls_slot =
@@ -155,28 +141,6 @@ void ArbitrateAllThreads(u32 address) {
}
}
-/// Boost low priority threads (temporarily) that have been starved
-static void PriorityBoostStarvedThreads() {
- u64 current_ticks = CoreTiming::GetTicks();
-
- for (auto& thread : thread_list) {
- // TODO(bunnei): Threads that have been waiting to be scheduled for `boost_ticks` (or
- // longer) will have their priority temporarily adjusted to 1 higher than the highest
- // priority thread to prevent thread starvation. This general behavior has been verified
- // on hardware. However, this is almost certainly not perfect, and the real CTR OS scheduler
- // should probably be reversed to verify this.
-
- const u64 boost_timeout = 2000000; // Boost threads that have been ready for > this long
-
- u64 delta = current_ticks - thread->last_running_ticks;
-
- if (thread->status == THREADSTATUS_READY && delta > boost_timeout) {
- const s32 priority = std::max(ready_queue.get_first()->current_priority - 1, 0);
- thread->BoostPriority(priority);
- }
- }
-}
-
/**
* Switches the CPU's active thread context to that of the specified thread
* @param new_thread The thread to switch to
@@ -199,8 +163,8 @@ static void SwitchContext(Thread* new_thread) {
// Load context of new thread
if (new_thread) {
- DEBUG_ASSERT_MSG(new_thread->status == THREADSTATUS_READY,
- "Thread must be ready to become running.");
+ ASSERT_MSG(new_thread->status == THREADSTATUS_READY,
+ "Thread must be ready to become running.");
// Cancel any outstanding wakeup events for this thread
CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle);
@@ -210,9 +174,6 @@ static void SwitchContext(Thread* new_thread) {
ready_queue.remove(new_thread->current_priority, new_thread);
new_thread->status = THREADSTATUS_RUNNING;
- // Restores thread to its nominal priority if it has been temporarily changed
- new_thread->current_priority = new_thread->nominal_priority;
-
Core::CPU().LoadContext(new_thread->context);
Core::CPU().SetCP15Register(CP15_THREAD_URO, new_thread->GetTLSAddress());
} else {
@@ -248,14 +209,6 @@ void WaitCurrentThread_Sleep() {
thread->status = THREADSTATUS_WAIT_SLEEP;
}
-void WaitCurrentThread_WaitSynchronization(std::vector<SharedPtr<WaitObject>> wait_objects,
- bool wait_set_output) {
- Thread* thread = GetCurrentThread();
- thread->wait_set_output = wait_set_output;
- thread->wait_objects = std::move(wait_objects);
- thread->status = THREADSTATUS_WAIT_SYNCH;
-}
-
void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) {
Thread* thread = GetCurrentThread();
thread->wait_address = wait_address;
@@ -281,7 +234,8 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
return;
}
- if (thread->status == THREADSTATUS_WAIT_SYNCH || thread->status == THREADSTATUS_WAIT_ARB) {
+ if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
+ thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB) {
thread->wait_set_output = false;
// Remove the thread from each of its waiting objects' waitlists
for (auto& object : thread->wait_objects)
@@ -305,8 +259,11 @@ void Thread::WakeAfterDelay(s64 nanoseconds) {
}
void Thread::ResumeFromWait() {
+ ASSERT_MSG(wait_objects.empty(), "Thread is waking up while waiting for objects");
+
switch (status) {
- case THREADSTATUS_WAIT_SYNCH:
+ case THREADSTATUS_WAIT_SYNCH_ALL:
+ case THREADSTATUS_WAIT_SYNCH_ANY:
case THREADSTATUS_WAIT_ARB:
case THREADSTATUS_WAIT_SLEEP:
break;
@@ -515,8 +472,21 @@ void Thread::SetPriority(s32 priority) {
nominal_priority = current_priority = priority;
}
+void Thread::UpdatePriority() {
+ s32 best_priority = nominal_priority;
+ for (auto& mutex : held_mutexes) {
+ if (mutex->priority < best_priority)
+ best_priority = mutex->priority;
+ }
+ BoostPriority(best_priority);
+}
+
void Thread::BoostPriority(s32 priority) {
- ready_queue.move(this, current_priority, priority);
+ // If thread was ready, adjust queues
+ if (status == THREADSTATUS_READY)
+ ready_queue.move(this, current_priority, priority);
+ else
+ ready_queue.prepare(priority);
current_priority = priority;
}
@@ -538,9 +508,11 @@ SharedPtr<Thread> SetupMainThread(u32 entry_point, s32 priority) {
return thread;
}
-void Reschedule() {
- PriorityBoostStarvedThreads();
+bool HaveReadyThreads() {
+ return ready_queue.get_first() != nullptr;
+}
+void Reschedule() {
Thread* cur = GetCurrentThread();
Thread* next = PopNextReadyThread();
@@ -563,6 +535,12 @@ void Thread::SetWaitSynchronizationOutput(s32 output) {
context.cpu_registers[1] = output;
}
+s32 Thread::GetWaitObjectIndex(WaitObject* object) const {
+ ASSERT_MSG(!wait_objects.empty(), "Thread is not waiting for anything");
+ auto match = std::find(wait_objects.rbegin(), wait_objects.rend(), object);
+ return std::distance(match, wait_objects.rend()) - 1;
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
void ThreadingInit() {
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index c77ac644d..c557a2279 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -31,13 +31,14 @@ enum ThreadProcessorId : s32 {
};
enum ThreadStatus {
- THREADSTATUS_RUNNING, ///< Currently running
- THREADSTATUS_READY, ///< Ready to run
- THREADSTATUS_WAIT_ARB, ///< Waiting on an address arbiter
- THREADSTATUS_WAIT_SLEEP, ///< Waiting due to a SleepThread SVC
- THREADSTATUS_WAIT_SYNCH, ///< Waiting due to a WaitSynchronization SVC
- THREADSTATUS_DORMANT, ///< Created but not yet made ready
- THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated
+ THREADSTATUS_RUNNING, ///< Currently running
+ THREADSTATUS_READY, ///< Ready to run
+ THREADSTATUS_WAIT_ARB, ///< Waiting on an address arbiter
+ THREADSTATUS_WAIT_SLEEP, ///< Waiting due to a SleepThread SVC
+ THREADSTATUS_WAIT_SYNCH_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false
+ THREADSTATUS_WAIT_SYNCH_ALL, ///< Waiting due to WaitSynchronizationN with wait_all = true
+ THREADSTATUS_DORMANT, ///< Created but not yet made ready
+ THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated
};
namespace Kernel {
@@ -72,8 +73,8 @@ public:
return HANDLE_TYPE;
}
- bool ShouldWait() override;
- void Acquire() override;
+ bool ShouldWait(Thread* thread) const override;
+ void Acquire(Thread* thread) override;
/**
* Gets the thread's current priority
@@ -90,6 +91,12 @@ public:
void SetPriority(s32 priority);
/**
+ * Boost's a thread's priority to the best priority among the thread's held mutexes.
+ * This prevents priority inversion via priority inheritance.
+ */
+ void UpdatePriority();
+
+ /**
* Temporarily boosts the thread's priority until the next time it is scheduled
* @param priority The new priority
*/
@@ -128,13 +135,14 @@ public:
/**
* Retrieves the index that this particular object occupies in the list of objects
- * that the thread passed to WaitSynchronizationN.
+ * that the thread passed to WaitSynchronizationN, starting the search from the last element.
* It is used to set the output value of WaitSynchronizationN when the thread is awakened.
+ * When a thread wakes up due to an object signal, the kernel will use the index of the last
+ * matching object in the wait objects list in case of having multiple instances of the same
+ * object in the list.
* @param object Object to query the index of.
*/
- s32 GetWaitObjectIndex(const WaitObject* object) const {
- return wait_objects_index.at(object->GetObjectId());
- }
+ s32 GetWaitObjectIndex(WaitObject* object) const;
/**
* Stops a thread, invalidating it from further use
@@ -152,10 +160,10 @@ public:
/**
* Returns whether this thread is waiting for all the objects in
* its wait list to become ready, as a result of a WaitSynchronizationN call
- * with wait_all = true, or a ReplyAndReceive call.
+ * with wait_all = true.
*/
bool IsSleepingOnWaitAll() const {
- return !wait_objects.empty();
+ return status == THREADSTATUS_WAIT_SYNCH_ALL;
}
ARM_Interface::ThreadContext context;
@@ -178,15 +186,15 @@ public:
/// Mutexes currently held by this thread, which will be released when it exits.
boost::container::flat_set<SharedPtr<Mutex>> held_mutexes;
+ /// Mutexes that this thread is currently waiting for.
+ boost::container::flat_set<SharedPtr<Mutex>> pending_mutexes;
+
SharedPtr<Process> owner_process; ///< Process that owns this thread
- /// Objects that the thread is waiting on.
- /// This is only populated when the thread should wait for all the objects to become ready.
+ /// Objects that the thread is waiting on, in the same order as they were
+ // passed to WaitSynchronization1/N.
std::vector<SharedPtr<WaitObject>> wait_objects;
- /// Mapping of Object ids to their position in the last waitlist that this object waited on.
- boost::container::flat_map<int, s32> wait_objects_index;
-
VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address
/// True if the WaitSynchronizationN output parameter should be set on thread wakeup.
@@ -211,6 +219,11 @@ private:
SharedPtr<Thread> SetupMainThread(u32 entry_point, s32 priority);
/**
+ * Returns whether there are any threads that are ready to run.
+ */
+bool HaveReadyThreads();
+
+/**
* Reschedules to the next available thread (call after current thread is suspended)
*/
void Reschedule();
@@ -238,15 +251,6 @@ Thread* GetCurrentThread();
void WaitCurrentThread_Sleep();
/**
- * Waits the current thread from a WaitSynchronization call
- * @param wait_objects Kernel objects that we are waiting on
- * @param wait_set_output If true, set the output parameter on thread wakeup (for
- * WaitSynchronizationN only)
- */
-void WaitCurrentThread_WaitSynchronization(std::vector<SharedPtr<WaitObject>> wait_objects,
- bool wait_set_output);
-
-/**
* Waits the current thread from an ArbitrateAddress call
* @param wait_address Arbitration address used to resume from wait
*/
diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp
index b50cf520d..8f2bc4c7f 100644
--- a/src/core/hle/kernel/timer.cpp
+++ b/src/core/hle/kernel/timer.cpp
@@ -39,12 +39,12 @@ SharedPtr<Timer> Timer::Create(ResetType reset_type, std::string name) {
return timer;
}
-bool Timer::ShouldWait() {
+bool Timer::ShouldWait(Thread* thread) const {
return !signaled;
}
-void Timer::Acquire() {
- ASSERT_MSG(!ShouldWait(), "object unavailable!");
+void Timer::Acquire(Thread* thread) {
+ ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
if (reset_type == ResetType::OneShot)
signaled = false;
diff --git a/src/core/hle/kernel/timer.h b/src/core/hle/kernel/timer.h
index 18ea0236b..2e3b31b23 100644
--- a/src/core/hle/kernel/timer.h
+++ b/src/core/hle/kernel/timer.h
@@ -39,8 +39,8 @@ public:
u64 initial_delay; ///< The delay until the timer fires for the first time
u64 interval_delay; ///< The delay until the timer fires after the first time
- bool ShouldWait() override;
- void Acquire() override;
+ bool ShouldWait(Thread* thread) const override;
+ void Acquire(Thread* thread) override;
/**
* Starts the timer, with the specified initial delay and interval.
diff --git a/src/core/hle/service/boss/boss.cpp b/src/core/hle/service/boss/boss.cpp
index 6ab16ccd5..e0de037f8 100644
--- a/src/core/hle/service/boss/boss.cpp
+++ b/src/core/hle/service/boss/boss.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <cinttypes>
#include "core/hle/service/boss/boss.h"
#include "core/hle/service/boss/boss_p.h"
#include "core/hle/service/boss/boss_u.h"
@@ -33,7 +34,8 @@ void InitializeSession(Service::Interface* self) {
cmd_buff[0] = IPC::MakeHeader(0x1, 0x1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
- LOG_WARNING(Service_BOSS, "(STUBBED) unk_param=0x%016X, translation=0x%08X, unk_param4=0x%08X",
+ LOG_WARNING(Service_BOSS,
+ "(STUBBED) unk_param=0x%016" PRIX64 ", translation=0x%08X, unk_param4=0x%08X",
unk_param, translation, unk_param4);
}
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp
index 65655f45d..59dd6d1cd 100644
--- a/src/core/hle/service/cfg/cfg.cpp
+++ b/src/core/hle/service/cfg/cfg.cpp
@@ -84,7 +84,6 @@ struct ConsoleCountryInfo {
static_assert(sizeof(ConsoleCountryInfo) == 4, "ConsoleCountryInfo must be exactly 4 bytes");
}
-static const u64 CFG_SAVE_ID = 0x00010017;
static const u64 CONSOLE_UNIQUE_ID = 0xDEADC0DE;
static const ConsoleModelInfo CONSOLE_MODEL = {NINTENDO_3DS_XL, {0, 0, 0}};
static const u8 CONSOLE_LANGUAGE = LANGUAGE_EN;
@@ -115,6 +114,8 @@ static const std::vector<u8> cfg_system_savedata_id = {
0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x01, 0x00,
};
+static u32 preferred_region_code = 0;
+
void GetCountryCodeString(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
u32 country_code_id = cmd_buff[1];
@@ -160,11 +161,18 @@ void GetCountryCodeID(Service::Interface* self) {
cmd_buff[2] = country_code_id;
}
+static u32 GetRegionValue() {
+ if (Settings::values.region_value == Settings::REGION_VALUE_AUTO_SELECT)
+ return preferred_region_code;
+
+ return Settings::values.region_value;
+}
+
void SecureInfoGetRegion(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
cmd_buff[1] = RESULT_SUCCESS.raw;
- cmd_buff[2] = Settings::values.region_value;
+ cmd_buff[2] = GetRegionValue();
}
void GenHashConsoleUnique(Service::Interface* self) {
@@ -184,7 +192,7 @@ void GetRegionCanadaUSA(Service::Interface* self) {
cmd_buff[1] = RESULT_SUCCESS.raw;
u8 canada_or_usa = 1;
- if (canada_or_usa == Settings::values.region_value) {
+ if (canada_or_usa == GetRegionValue()) {
cmd_buff[2] = 1;
} else {
cmd_buff[2] = 0;
@@ -314,10 +322,47 @@ static ResultVal<void*> GetConfigInfoBlockPointer(u32 block_id, u32 size, u32 fl
return MakeResult<void*>(pointer);
}
+/// Checks if the language is available in the chosen region, and returns a proper one
+static u8 AdjustLanguageInfoBlock(u32 region, u8 language) {
+ static const std::array<std::vector<u8>, 7> region_languages{{
+ // JPN
+ {LANGUAGE_JP},
+ // USA
+ {LANGUAGE_EN, LANGUAGE_FR, LANGUAGE_ES, LANGUAGE_PT},
+ // EUR
+ {LANGUAGE_EN, LANGUAGE_FR, LANGUAGE_DE, LANGUAGE_IT, LANGUAGE_ES, LANGUAGE_NL, LANGUAGE_PT,
+ LANGUAGE_RU},
+ // AUS
+ {LANGUAGE_EN, LANGUAGE_FR, LANGUAGE_DE, LANGUAGE_IT, LANGUAGE_ES, LANGUAGE_NL, LANGUAGE_PT,
+ LANGUAGE_RU},
+ // CHN
+ {LANGUAGE_ZH},
+ // KOR
+ {LANGUAGE_KO},
+ // TWN
+ {LANGUAGE_TW},
+ }};
+ const auto& available = region_languages[region];
+ if (std::find(available.begin(), available.end(), language) == available.end()) {
+ return available[0];
+ }
+ return language;
+}
+
ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, void* output) {
void* pointer;
CASCADE_RESULT(pointer, GetConfigInfoBlockPointer(block_id, size, flag));
memcpy(output, pointer, size);
+
+ // override the language setting if the region setting is auto
+ if (block_id == LanguageBlockID &&
+ Settings::values.region_value == Settings::REGION_VALUE_AUTO_SELECT) {
+ u8 language;
+ memcpy(&language, output, sizeof(u8));
+ language = AdjustLanguageInfoBlock(preferred_region_code, language);
+ memcpy(output, &language, sizeof(u8));
+ }
+
return RESULT_SUCCESS;
}
@@ -535,10 +580,17 @@ void Init() {
AddService(new CFG_U);
LoadConfigNANDSaveFile();
+
+ preferred_region_code = 0;
}
void Shutdown() {}
+void SetPreferredRegionCode(u32 region_code) {
+ preferred_region_code = region_code;
+ LOG_INFO(Service_CFG, "Preferred region code set to %u", preferred_region_code);
+}
+
void SetUsername(const std::u16string& name) {
ASSERT(name.size() <= 10);
UsernameBlock block{};
diff --git a/src/core/hle/service/cfg/cfg.h b/src/core/hle/service/cfg/cfg.h
index fb47c2aa5..618c9647e 100644
--- a/src/core/hle/service/cfg/cfg.h
+++ b/src/core/hle/service/cfg/cfg.h
@@ -282,6 +282,13 @@ void Init();
/// Shutdown the config service
void Shutdown();
+/**
+ * Set the region code preferred by the game so that CFG will adjust to it when the region setting
+ * is auto.
+ * @param region_code the preferred region code to set
+ */
+void SetPreferredRegionCode(u32 region_code);
+
// Utilities for frontend to set config data.
// Note: before calling these functions, LoadConfigNANDSaveFile should be called,
// and UpdateConfigNANDSavegame should be called after making changes to config data.
diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp
index 4f1dd2fce..c62f8afc6 100644
--- a/src/core/hle/service/mic_u.cpp
+++ b/src/core/hle/service/mic_u.cpp
@@ -99,7 +99,8 @@ static void StartSampling(Interface* self) {
is_sampling = true;
LOG_WARNING(Service_MIC, "(STUBBED) called, encoding=%u, sample_rate=%u, "
"audio_buffer_offset=%d, audio_buffer_size=%u, audio_buffer_loop=%u",
- encoding, sample_rate, audio_buffer_offset, audio_buffer_size, audio_buffer_loop);
+ static_cast<u32>(encoding), static_cast<u32>(sample_rate), audio_buffer_offset,
+ audio_buffer_size, audio_buffer_loop);
}
/**
@@ -114,7 +115,7 @@ static void AdjustSampling(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
sample_rate = static_cast<SampleRate>(cmd_buff[1] & 0xFF);
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- LOG_WARNING(Service_MIC, "(STUBBED) called, sample_rate=%u", sample_rate);
+ LOG_WARNING(Service_MIC, "(STUBBED) called, sample_rate=%u", static_cast<u32>(sample_rate));
}
/**
diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp
index d9738c6a1..e248285f9 100644
--- a/src/core/hle/service/nfc/nfc.cpp
+++ b/src/core/hle/service/nfc/nfc.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "core/hle/kernel/event.h"
#include "core/hle/service/nfc/nfc.h"
#include "core/hle/service/nfc/nfc_m.h"
#include "core/hle/service/nfc/nfc_u.h"
@@ -9,9 +10,28 @@
namespace Service {
namespace NFC {
+static Kernel::SharedPtr<Kernel::Event> tag_in_range_event;
+
+void GetTagInRangeEvent(Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[0] = IPC::MakeHeader(0xB, 1, 2);
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[2] = IPC::CopyHandleDesc();
+ cmd_buff[3] = Kernel::g_handle_table.Create(tag_in_range_event).MoveFrom();
+ LOG_WARNING(Service_NFC, "(STUBBED) called");
+}
+
void Init() {
AddService(new NFC_M());
AddService(new NFC_U());
+
+ tag_in_range_event =
+ Kernel::Event::Create(Kernel::ResetType::OneShot, "NFC::tag_in_range_event");
+}
+
+void Shutdown() {
+ tag_in_range_event = nullptr;
}
} // namespace NFC
diff --git a/src/core/hle/service/nfc/nfc.h b/src/core/hle/service/nfc/nfc.h
index cd65a5fdc..b02354201 100644
--- a/src/core/hle/service/nfc/nfc.h
+++ b/src/core/hle/service/nfc/nfc.h
@@ -5,10 +5,27 @@
#pragma once
namespace Service {
+
+class Interface;
+
namespace NFC {
+/**
+ * NFC::GetTagInRangeEvent service function
+ * Inputs:
+ * 0 : Header code [0x000B0000]
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Copy handle descriptor
+ * 3 : Event Handle
+ */
+void GetTagInRangeEvent(Interface* self);
+
/// Initialize all NFC services.
void Init();
+/// Shutdown all NFC services.
+void Shutdown();
+
} // namespace NFC
} // namespace Service
diff --git a/src/core/hle/service/nfc/nfc_m.cpp b/src/core/hle/service/nfc/nfc_m.cpp
index 717335c11..f43b4029a 100644
--- a/src/core/hle/service/nfc/nfc_m.cpp
+++ b/src/core/hle/service/nfc/nfc_m.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "core/hle/service/nfc/nfc.h"
#include "core/hle/service/nfc/nfc_m.h"
namespace Service {
@@ -19,6 +20,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00070000, nullptr, "LoadAmiiboData"},
{0x00080000, nullptr, "ResetTagScanState"},
{0x00090002, nullptr, "UpdateStoredAmiiboData"},
+ {0x000B0000, GetTagInRangeEvent, "GetTagInRangeEvent"},
{0x000D0000, nullptr, "GetTagState"},
{0x000F0000, nullptr, "CommunicationGetStatus"},
{0x00100000, nullptr, "GetTagInfo2"},
diff --git a/src/core/hle/service/nfc/nfc_u.cpp b/src/core/hle/service/nfc/nfc_u.cpp
index deffb0b4f..4b5200ae8 100644
--- a/src/core/hle/service/nfc/nfc_u.cpp
+++ b/src/core/hle/service/nfc/nfc_u.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "core/hle/service/nfc/nfc.h"
#include "core/hle/service/nfc/nfc_u.h"
namespace Service {
@@ -18,6 +19,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00070000, nullptr, "LoadAmiiboData"},
{0x00080000, nullptr, "ResetTagScanState"},
{0x00090002, nullptr, "UpdateStoredAmiiboData"},
+ {0x000B0000, GetTagInRangeEvent, "GetTagInRangeEvent"},
{0x000D0000, nullptr, "GetTagState"},
{0x000F0000, nullptr, "CommunicationGetStatus"},
{0x00100000, nullptr, "GetTagInfo2"},
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 7e52a05d9..f3190e0fa 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -178,6 +178,7 @@ void Init() {
/// Shutdown ServiceManager
void Shutdown() {
PTM::Shutdown();
+ NFC::Shutdown();
NIM::Shutdown();
NEWS::Shutdown();
NDM::Shutdown();
diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp
index c3918cdd0..dcc5c3c90 100644
--- a/src/core/hle/service/soc_u.cpp
+++ b/src/core/hle/service/soc_u.cpp
@@ -603,7 +603,6 @@ static void RecvFrom(Interface* self) {
u32 socket_handle = cmd_buffer[1];
u32 len = cmd_buffer[2];
u32 flags = cmd_buffer[3];
- socklen_t addr_len = static_cast<socklen_t>(cmd_buffer[4]);
struct {
u32 output_buffer_descriptor;
@@ -693,7 +692,6 @@ static void Poll(Interface* self) {
static void GetSockName(Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 socket_handle = cmd_buffer[1];
- socklen_t ctr_len = cmd_buffer[2];
// Memory address of the ctr_dest_addr structure
VAddr ctr_dest_addr_addr = cmd_buffer[0x104 >> 2];
@@ -734,7 +732,6 @@ static void Shutdown(Interface* self) {
static void GetPeerName(Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 socket_handle = cmd_buffer[1];
- socklen_t len = cmd_buffer[2];
// Memory address of the ctr_dest_addr structure
VAddr ctr_dest_addr_addr = cmd_buffer[0x104 >> 2];
@@ -765,7 +762,6 @@ static void Connect(Interface* self) {
// performing nonblocking operations and spinlock until the data is available
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 socket_handle = cmd_buffer[1];
- socklen_t len = cmd_buffer[2];
// Memory address of the ctr_input_addr structure
VAddr ctr_input_addr_addr = cmd_buffer[6];
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index 2ca270de3..2b242ff98 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -248,6 +248,8 @@ static ResultCode SendSyncRequest(Kernel::Handle handle) {
LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s)", handle, session->GetName().c_str());
+ Core::System::GetInstance().PrepareReschedule();
+
// TODO(Subv): svcSendSyncRequest should put the caller thread to sleep while the server
// responds and cause a reschedule.
return session->SendSyncRequest();
@@ -270,27 +272,27 @@ static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds)
LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle,
object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds);
- if (object->ShouldWait()) {
+ if (object->ShouldWait(thread)) {
if (nano_seconds == 0)
return ERR_SYNC_TIMEOUT;
+ thread->wait_objects = {object};
object->AddWaitingThread(thread);
- // TODO(Subv): Perform things like update the mutex lock owner's priority to
- // prevent priority inversion. Currently this is done in Mutex::ShouldWait,
- // but it should be moved to a function that is called from here.
- thread->status = THREADSTATUS_WAIT_SYNCH;
+ thread->status = THREADSTATUS_WAIT_SYNCH_ANY;
// Create an event to wake the thread up after the specified nanosecond delay has passed
thread->WakeAfterDelay(nano_seconds);
+ Core::System::GetInstance().PrepareReschedule();
+
// Note: The output of this SVC will be set to RESULT_SUCCESS if the thread
// resumes due to a signal in its wait objects.
// Otherwise we retain the default value of timeout.
return ERR_SYNC_TIMEOUT;
}
- object->Acquire();
+ object->Acquire(thread);
return RESULT_SUCCESS;
}
@@ -324,19 +326,14 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha
objects[i] = object;
}
- // Clear the mapping of wait object indices.
- // We don't want any lingering state in this map.
- // It will be repopulated later in the wait_all = false case.
- thread->wait_objects_index.clear();
-
if (wait_all) {
bool all_available =
std::all_of(objects.begin(), objects.end(),
- [](const ObjectPtr& object) { return !object->ShouldWait(); });
+ [thread](const ObjectPtr& object) { return !object->ShouldWait(thread); });
if (all_available) {
// We can acquire all objects right now, do so.
for (auto& object : objects)
- object->Acquire();
+ object->Acquire(thread);
// Note: In this case, the `out` parameter is not set,
// and retains whatever value it had before.
return RESULT_SUCCESS;
@@ -350,22 +347,20 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha
return ERR_SYNC_TIMEOUT;
// Put the thread to sleep
- thread->status = THREADSTATUS_WAIT_SYNCH;
+ thread->status = THREADSTATUS_WAIT_SYNCH_ALL;
// Add the thread to each of the objects' waiting threads.
for (auto& object : objects) {
object->AddWaitingThread(thread);
- // TODO(Subv): Perform things like update the mutex lock owner's priority to
- // prevent priority inversion. Currently this is done in Mutex::ShouldWait,
- // but it should be moved to a function that is called from here.
}
- // Set the thread's waitlist to the list of objects passed to WaitSynchronizationN
thread->wait_objects = std::move(objects);
// Create an event to wake the thread up after the specified nanosecond delay has passed
thread->WakeAfterDelay(nano_seconds);
+ Core::System::GetInstance().PrepareReschedule();
+
// This value gets set to -1 by default in this case, it is not modified after this.
*out = -1;
// Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to
@@ -373,13 +368,14 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha
return ERR_SYNC_TIMEOUT;
} else {
// Find the first object that is acquirable in the provided list of objects
- auto itr = std::find_if(objects.begin(), objects.end(),
- [](const ObjectPtr& object) { return !object->ShouldWait(); });
+ auto itr = std::find_if(objects.begin(), objects.end(), [thread](const ObjectPtr& object) {
+ return !object->ShouldWait(thread);
+ });
if (itr != objects.end()) {
// We found a ready object, acquire it and set the result value
Kernel::WaitObject* object = itr->get();
- object->Acquire();
+ object->Acquire(thread);
*out = std::distance(objects.begin(), itr);
return RESULT_SUCCESS;
}
@@ -392,28 +388,24 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha
return ERR_SYNC_TIMEOUT;
// Put the thread to sleep
- thread->status = THREADSTATUS_WAIT_SYNCH;
-
- // Clear the thread's waitlist, we won't use it for wait_all = false
- thread->wait_objects.clear();
+ thread->status = THREADSTATUS_WAIT_SYNCH_ANY;
// Add the thread to each of the objects' waiting threads.
for (size_t i = 0; i < objects.size(); ++i) {
Kernel::WaitObject* object = objects[i].get();
- // Set the index of this object in the mapping of Objects -> index for this thread.
- thread->wait_objects_index[object->GetObjectId()] = static_cast<int>(i);
object->AddWaitingThread(thread);
- // TODO(Subv): Perform things like update the mutex lock owner's priority to
- // prevent priority inversion. Currently this is done in Mutex::ShouldWait,
- // but it should be moved to a function that is called from here.
}
+ thread->wait_objects = std::move(objects);
+
// Note: If no handles and no timeout were given, then the thread will deadlock, this is
// consistent with hardware behavior.
// Create an event to wake the thread up after the specified nanosecond delay has passed
thread->WakeAfterDelay(nano_seconds);
+ Core::System::GetInstance().PrepareReschedule();
+
// Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a
// signal in one of its wait objects.
// Otherwise we retain the default value of timeout, and -1 in the out parameter
@@ -448,6 +440,9 @@ static ResultCode ArbitrateAddress(Kernel::Handle handle, u32 address, u32 type,
auto res = arbiter->ArbitrateAddress(static_cast<Kernel::ArbitrationType>(type), address, value,
nanoseconds);
+ // TODO(Subv): Identify in which specific cases this call should cause a reschedule.
+ Core::System::GetInstance().PrepareReschedule();
+
return res;
}
@@ -574,6 +569,8 @@ static ResultCode CreateThread(Kernel::Handle* out_handle, s32 priority, u32 ent
CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(thread)));
+ Core::System::GetInstance().PrepareReschedule();
+
LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, "
"threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X",
entry_point, name.c_str(), arg, stack_top, priority, processor_id, *out_handle);
@@ -586,6 +583,7 @@ static void ExitThread() {
LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::CPU().GetPC());
Kernel::ExitCurrentThread();
+ Core::System::GetInstance().PrepareReschedule();
}
/// Gets the priority for the specified thread
@@ -605,6 +603,13 @@ static ResultCode SetThreadPriority(Kernel::Handle handle, s32 priority) {
return ERR_INVALID_HANDLE;
thread->SetPriority(priority);
+ thread->UpdatePriority();
+
+ // Update the mutexes that this thread is waiting for
+ for (auto& mutex : thread->pending_mutexes)
+ mutex->UpdatePriority();
+
+ Core::System::GetInstance().PrepareReschedule();
return RESULT_SUCCESS;
}
@@ -844,11 +849,18 @@ static ResultCode CancelTimer(Kernel::Handle handle) {
static void SleepThread(s64 nanoseconds) {
LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds);
+ // Don't attempt to yield execution if there are no available threads to run,
+ // this way we avoid a useless reschedule to the idle thread.
+ if (nanoseconds == 0 && !Kernel::HaveReadyThreads())
+ return;
+
// Sleep current thread and check for next thread to schedule
Kernel::WaitCurrentThread_Sleep();
// Create an event to wake the thread up after the specified nanosecond delay has passed
Kernel::GetCurrentThread()->WakeAfterDelay(nanoseconds);
+
+ Core::System::GetInstance().PrepareReschedule();
}
/// This returns the total CPU ticks elapsed since the CPU was powered-on
@@ -890,7 +902,11 @@ static ResultCode CreateMemoryBlock(Kernel::Handle* out_handle, u32 addr, u32 si
return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS,
ErrorSummary::InvalidArgument, ErrorLevel::Usage);
- if (addr < Memory::PROCESS_IMAGE_VADDR || addr + size > Memory::SHARED_MEMORY_VADDR_END) {
+ // TODO(Subv): Processes with memory type APPLICATION are not allowed
+ // to create memory blocks with addr = 0, any attempts to do so
+ // should return error 0xD92007EA.
+ if ((addr < Memory::PROCESS_IMAGE_VADDR || addr + size > Memory::SHARED_MEMORY_VADDR_END) &&
+ addr != 0) {
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS,
ErrorSummary::InvalidArgument, ErrorLevel::Usage);
}
@@ -1184,8 +1200,6 @@ void CallSVC(u32 immediate) {
if (info) {
if (info->func) {
info->func();
- // TODO(Subv): Not all service functions should cause a reschedule in all cases.
- Core::System::GetInstance().PrepareReschedule();
} else {
LOG_ERROR(Kernel_SVC, "unimplemented SVC function %s(..)", info->name);
}
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
index 6f2164428..5df33f6d2 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -11,8 +11,10 @@
#include "core/file_sys/archive_romfs.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
+#include "core/hle/service/cfg/cfg.h"
#include "core/hle/service/fs/archive.h"
#include "core/loader/ncch.h"
+#include "core/loader/smdh.h"
#include "core/memory.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -286,7 +288,7 @@ ResultStatus AppLoader_NCCH::LoadExeFS() {
LOG_DEBUG(Loader, "Thread priority: 0x%X", priority);
LOG_DEBUG(Loader, "Resource limit category: %d", resource_limit_category);
LOG_DEBUG(Loader, "System Mode: %d",
- exheader_header.arm11_system_local_caps.system_mode);
+ static_cast<int>(exheader_header.arm11_system_local_caps.system_mode));
if (exheader_header.arm11_system_local_caps.program_id != ncch_header.program_id) {
LOG_ERROR(Loader, "ExHeader Program ID mismatch: the ROM is probably encrypted.");
@@ -309,6 +311,23 @@ ResultStatus AppLoader_NCCH::LoadExeFS() {
return ResultStatus::Success;
}
+void AppLoader_NCCH::ParseRegionLockoutInfo() {
+ std::vector<u8> smdh_buffer;
+ if (ReadIcon(smdh_buffer) == ResultStatus::Success && smdh_buffer.size() >= sizeof(SMDH)) {
+ SMDH smdh;
+ memcpy(&smdh, smdh_buffer.data(), sizeof(SMDH));
+ u32 region_lockout = smdh.region_lockout;
+ constexpr u32 REGION_COUNT = 7;
+ for (u32 region = 0; region < REGION_COUNT; ++region) {
+ if (region_lockout & 1) {
+ Service::CFG::SetPreferredRegionCode(region);
+ break;
+ }
+ region_lockout >>= 1;
+ }
+ }
+}
+
ResultStatus AppLoader_NCCH::Load() {
if (is_loaded)
return ResultStatus::ErrorAlreadyLoaded;
@@ -325,6 +344,9 @@ ResultStatus AppLoader_NCCH::Load() {
Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_RomFS>(*this),
Service::FS::ArchiveIdCode::RomFS);
+
+ ParseRegionLockoutInfo();
+
return ResultStatus::Success;
}
diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h
index 6afc171a5..4ef95b5c6 100644
--- a/src/core/loader/ncch.h
+++ b/src/core/loader/ncch.h
@@ -181,7 +181,7 @@ public:
* Loads the Exheader and returns the system mode for this application.
* @return Optional with the kernel system mode
*/
- boost::optional<u32> LoadKernelSystemMode();
+ boost::optional<u32> LoadKernelSystemMode() override;
ResultStatus ReadCode(std::vector<u8>& buffer) override;
@@ -229,6 +229,9 @@ private:
*/
ResultStatus LoadExeFS();
+ /// Reads the region lockout info in the SMDH and send it to CFG service
+ void ParseRegionLockoutInfo();
+
bool is_exefs_loaded = false;
bool is_compressed = false;
diff --git a/src/core/settings.h b/src/core/settings.h
index db4c8fada..4e7a4b1be 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -110,5 +110,9 @@ struct Values {
u16 gdbstub_port;
} extern values;
+// a special value for Values::region_value indicating that citra will automatically select a region
+// value to fit the region lockout info of the game
+static constexpr int REGION_VALUE_AUTO_SELECT = -1;
+
void Apply();
}