summaryrefslogtreecommitdiffstats
path: root/src/core/hle/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/kernel')
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp181
-rw-r--r--src/core/hle/kernel/address_arbiter.h80
-rw-r--r--src/core/hle/kernel/client_port.cpp9
-rw-r--r--src/core/hle/kernel/client_session.cpp14
-rw-r--r--src/core/hle/kernel/client_session.h9
-rw-r--r--src/core/hle/kernel/code_set.cpp12
-rw-r--r--src/core/hle/kernel/code_set.h90
-rw-r--r--src/core/hle/kernel/errors.h1
-rw-r--r--src/core/hle/kernel/handle_table.cpp40
-rw-r--r--src/core/hle/kernel/handle_table.h25
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp22
-rw-r--r--src/core/hle/kernel/hle_ipc.h25
-rw-r--r--src/core/hle/kernel/kernel.cpp14
-rw-r--r--src/core/hle/kernel/kernel.h21
-rw-r--r--src/core/hle/kernel/process.cpp34
-rw-r--r--src/core/hle/kernel/process.h69
-rw-r--r--src/core/hle/kernel/process_capability.cpp4
-rw-r--r--src/core/hle/kernel/process_capability.h4
-rw-r--r--src/core/hle/kernel/scheduler.cpp20
-rw-r--r--src/core/hle/kernel/scheduler.h6
-rw-r--r--src/core/hle/kernel/server_port.cpp4
-rw-r--r--src/core/hle/kernel/server_port.h35
-rw-r--r--src/core/hle/kernel/server_session.cpp91
-rw-r--r--src/core/hle/kernel/server_session.h53
-rw-r--r--src/core/hle/kernel/shared_memory.cpp5
-rw-r--r--src/core/hle/kernel/svc.cpp105
-rw-r--r--src/core/hle/kernel/thread.cpp92
-rw-r--r--src/core/hle/kernel/thread.h23
-rw-r--r--src/core/hle/kernel/vm_manager.cpp53
-rw-r--r--src/core/hle/kernel/vm_manager.h33
30 files changed, 744 insertions, 430 deletions
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index 57157beb4..352190da8 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -9,6 +9,7 @@
#include "common/common_types.h"
#include "core/core.h"
#include "core/core_cpu.h"
+#include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
@@ -18,58 +19,15 @@
#include "core/memory.h"
namespace Kernel {
-namespace AddressArbiter {
-
-// Performs actual address waiting logic.
-static ResultCode WaitForAddress(VAddr address, s64 timeout) {
- SharedPtr<Thread> current_thread = GetCurrentThread();
- current_thread->SetArbiterWaitAddress(address);
- current_thread->SetStatus(ThreadStatus::WaitArb);
- current_thread->InvalidateWakeupCallback();
-
- current_thread->WakeAfterDelay(timeout);
-
- Core::System::GetInstance().CpuCore(current_thread->GetProcessorID()).PrepareReschedule();
- return RESULT_TIMEOUT;
-}
-
-// Gets the threads waiting on an address.
-static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) {
- const auto RetrieveWaitingThreads = [](std::size_t core_index,
- std::vector<SharedPtr<Thread>>& waiting_threads,
- VAddr arb_addr) {
- const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
- const auto& thread_list = scheduler.GetThreadList();
-
- for (const auto& thread : thread_list) {
- if (thread->GetArbiterWaitAddress() == arb_addr)
- waiting_threads.push_back(thread);
- }
- };
-
- // Retrieve all threads that are waiting for this address.
- std::vector<SharedPtr<Thread>> threads;
- RetrieveWaitingThreads(0, threads, address);
- RetrieveWaitingThreads(1, threads, address);
- RetrieveWaitingThreads(2, threads, address);
- RetrieveWaitingThreads(3, threads, address);
-
- // Sort them by priority, such that the highest priority ones come first.
- std::sort(threads.begin(), threads.end(),
- [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
- return lhs->GetPriority() < rhs->GetPriority();
- });
-
- return threads;
-}
-
+namespace {
// Wake up num_to_wake (or all) threads in a vector.
-static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_to_wake) {
+void WakeThreads(const std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_to_wake) {
// Only process up to 'target' threads, unless 'target' is <= 0, in which case process
// them all.
std::size_t last = waiting_threads.size();
- if (num_to_wake > 0)
+ if (num_to_wake > 0) {
last = num_to_wake;
+ }
// Signal the waiting threads.
for (std::size_t i = 0; i < last; i++) {
@@ -79,42 +37,55 @@ static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num
waiting_threads[i]->ResumeFromWait();
}
}
+} // Anonymous namespace
+
+AddressArbiter::AddressArbiter(Core::System& system) : system{system} {}
+AddressArbiter::~AddressArbiter() = default;
+
+ResultCode AddressArbiter::SignalToAddress(VAddr address, SignalType type, s32 value,
+ s32 num_to_wake) {
+ switch (type) {
+ case SignalType::Signal:
+ return SignalToAddressOnly(address, num_to_wake);
+ case SignalType::IncrementAndSignalIfEqual:
+ return IncrementAndSignalToAddressIfEqual(address, value, num_to_wake);
+ case SignalType::ModifyByWaitingCountAndSignalIfEqual:
+ return ModifyByWaitingCountAndSignalToAddressIfEqual(address, value, num_to_wake);
+ default:
+ return ERR_INVALID_ENUM_VALUE;
+ }
+}
-// Signals an address being waited on.
-ResultCode SignalToAddress(VAddr address, s32 num_to_wake) {
- std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
-
+ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) {
+ const std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
WakeThreads(waiting_threads, num_to_wake);
return RESULT_SUCCESS;
}
-// Signals an address being waited on and increments its value if equal to the value argument.
-ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake) {
+ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value,
+ s32 num_to_wake) {
// Ensure that we can write to the address.
if (!Memory::IsValidVirtualAddress(address)) {
return ERR_INVALID_ADDRESS_STATE;
}
- if (static_cast<s32>(Memory::Read32(address)) == value) {
- Memory::Write32(address, static_cast<u32>(value + 1));
- } else {
+ if (static_cast<s32>(Memory::Read32(address)) != value) {
return ERR_INVALID_STATE;
}
- return SignalToAddress(address, num_to_wake);
+ Memory::Write32(address, static_cast<u32>(value + 1));
+ return SignalToAddressOnly(address, num_to_wake);
}
-// Signals an address being waited on and modifies its value based on waiting thread count if equal
-// to the value argument.
-ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
- s32 num_to_wake) {
+ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
+ s32 num_to_wake) {
// Ensure that we can write to the address.
if (!Memory::IsValidVirtualAddress(address)) {
return ERR_INVALID_ADDRESS_STATE;
}
// Get threads waiting on the address.
- std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
+ const std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
// Determine the modified value depending on the waiting count.
s32 updated_value;
@@ -126,41 +97,54 @@ ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 valu
updated_value = value;
}
- if (static_cast<s32>(Memory::Read32(address)) == value) {
- Memory::Write32(address, static_cast<u32>(updated_value));
- } else {
+ if (static_cast<s32>(Memory::Read32(address)) != value) {
return ERR_INVALID_STATE;
}
+ Memory::Write32(address, static_cast<u32>(updated_value));
WakeThreads(waiting_threads, num_to_wake);
return RESULT_SUCCESS;
}
-// Waits on an address if the value passed is less than the argument value, optionally decrementing.
-ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement) {
+ResultCode AddressArbiter::WaitForAddress(VAddr address, ArbitrationType type, s32 value,
+ s64 timeout_ns) {
+ switch (type) {
+ case ArbitrationType::WaitIfLessThan:
+ return WaitForAddressIfLessThan(address, value, timeout_ns, false);
+ case ArbitrationType::DecrementAndWaitIfLessThan:
+ return WaitForAddressIfLessThan(address, value, timeout_ns, true);
+ case ArbitrationType::WaitIfEqual:
+ return WaitForAddressIfEqual(address, value, timeout_ns);
+ default:
+ return ERR_INVALID_ENUM_VALUE;
+ }
+}
+
+ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout,
+ bool should_decrement) {
// Ensure that we can read the address.
if (!Memory::IsValidVirtualAddress(address)) {
return ERR_INVALID_ADDRESS_STATE;
}
- s32 cur_value = static_cast<s32>(Memory::Read32(address));
- if (cur_value < value) {
- if (should_decrement) {
- Memory::Write32(address, static_cast<u32>(cur_value - 1));
- }
- } else {
+ const s32 cur_value = static_cast<s32>(Memory::Read32(address));
+ if (cur_value >= value) {
return ERR_INVALID_STATE;
}
+
+ if (should_decrement) {
+ Memory::Write32(address, static_cast<u32>(cur_value - 1));
+ }
+
// Short-circuit without rescheduling, if timeout is zero.
if (timeout == 0) {
return RESULT_TIMEOUT;
}
- return WaitForAddress(address, timeout);
+ return WaitForAddressImpl(address, timeout);
}
-// Waits on an address if the value passed is equal to the argument value.
-ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
+ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
// Ensure that we can read the address.
if (!Memory::IsValidVirtualAddress(address)) {
return ERR_INVALID_ADDRESS_STATE;
@@ -174,7 +158,48 @@ ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
return RESULT_TIMEOUT;
}
- return WaitForAddress(address, timeout);
+ return WaitForAddressImpl(address, timeout);
+}
+
+ResultCode AddressArbiter::WaitForAddressImpl(VAddr address, s64 timeout) {
+ SharedPtr<Thread> current_thread = system.CurrentScheduler().GetCurrentThread();
+ current_thread->SetArbiterWaitAddress(address);
+ current_thread->SetStatus(ThreadStatus::WaitArb);
+ current_thread->InvalidateWakeupCallback();
+
+ current_thread->WakeAfterDelay(timeout);
+
+ system.CpuCore(current_thread->GetProcessorID()).PrepareReschedule();
+ return RESULT_TIMEOUT;
+}
+
+std::vector<SharedPtr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress(VAddr address) const {
+ const auto RetrieveWaitingThreads = [this](std::size_t core_index,
+ std::vector<SharedPtr<Thread>>& waiting_threads,
+ VAddr arb_addr) {
+ const auto& scheduler = system.Scheduler(core_index);
+ const auto& thread_list = scheduler.GetThreadList();
+
+ for (const auto& thread : thread_list) {
+ if (thread->GetArbiterWaitAddress() == arb_addr) {
+ waiting_threads.push_back(thread);
+ }
+ }
+ };
+
+ // Retrieve all threads that are waiting for this address.
+ std::vector<SharedPtr<Thread>> threads;
+ RetrieveWaitingThreads(0, threads, address);
+ RetrieveWaitingThreads(1, threads, address);
+ RetrieveWaitingThreads(2, threads, address);
+ RetrieveWaitingThreads(3, threads, address);
+
+ // Sort them by priority, such that the highest priority ones come first.
+ std::sort(threads.begin(), threads.end(),
+ [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
+ return lhs->GetPriority() < rhs->GetPriority();
+ });
+
+ return threads;
}
-} // namespace AddressArbiter
} // namespace Kernel
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h
index e3657b8e9..ed0d0e69f 100644
--- a/src/core/hle/kernel/address_arbiter.h
+++ b/src/core/hle/kernel/address_arbiter.h
@@ -4,31 +4,77 @@
#pragma once
+#include <vector>
+
#include "common/common_types.h"
+#include "core/hle/kernel/object.h"
union ResultCode;
+namespace Core {
+class System;
+}
+
namespace Kernel {
-namespace AddressArbiter {
-enum class ArbitrationType {
- WaitIfLessThan = 0,
- DecrementAndWaitIfLessThan = 1,
- WaitIfEqual = 2,
-};
+class Thread;
-enum class SignalType {
- Signal = 0,
- IncrementAndSignalIfEqual = 1,
- ModifyByWaitingCountAndSignalIfEqual = 2,
-};
+class AddressArbiter {
+public:
+ enum class ArbitrationType {
+ WaitIfLessThan = 0,
+ DecrementAndWaitIfLessThan = 1,
+ WaitIfEqual = 2,
+ };
+
+ enum class SignalType {
+ Signal = 0,
+ IncrementAndSignalIfEqual = 1,
+ ModifyByWaitingCountAndSignalIfEqual = 2,
+ };
+
+ explicit AddressArbiter(Core::System& system);
+ ~AddressArbiter();
+
+ AddressArbiter(const AddressArbiter&) = delete;
+ AddressArbiter& operator=(const AddressArbiter&) = delete;
+
+ AddressArbiter(AddressArbiter&&) = default;
+ AddressArbiter& operator=(AddressArbiter&&) = delete;
+
+ /// Signals an address being waited on with a particular signaling type.
+ ResultCode SignalToAddress(VAddr address, SignalType type, s32 value, s32 num_to_wake);
-ResultCode SignalToAddress(VAddr address, s32 num_to_wake);
-ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake);
-ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake);
+ /// Waits on an address with a particular arbitration type.
+ ResultCode WaitForAddress(VAddr address, ArbitrationType type, s32 value, s64 timeout_ns);
-ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement);
-ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout);
-} // namespace AddressArbiter
+private:
+ /// Signals an address being waited on.
+ ResultCode SignalToAddressOnly(VAddr address, s32 num_to_wake);
+
+ /// Signals an address being waited on and increments its value if equal to the value argument.
+ ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake);
+
+ /// Signals an address being waited on and modifies its value based on waiting thread count if
+ /// equal to the value argument.
+ ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
+ s32 num_to_wake);
+
+ /// Waits on an address if the value passed is less than the argument value,
+ /// optionally decrementing.
+ ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout,
+ bool should_decrement);
+
+ /// Waits on an address if the value passed is equal to the argument value.
+ ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout);
+
+ // Waits on the given address with a timeout in nanoseconds
+ ResultCode WaitForAddressImpl(VAddr address, s64 timeout);
+
+ // Gets the threads waiting on an address.
+ std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) const;
+
+ Core::System& system;
+};
} // namespace Kernel
diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp
index d4c91d529..aa432658e 100644
--- a/src/core/hle/kernel/client_port.cpp
+++ b/src/core/hle/kernel/client_port.cpp
@@ -33,10 +33,11 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
// Create a new session pair, let the created sessions inherit the parent port's HLE handler.
auto sessions = ServerSession::CreateSessionPair(kernel, server_port->GetName(), this);
- if (server_port->hle_handler)
- server_port->hle_handler->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions));
- else
- server_port->pending_sessions.push_back(std::get<SharedPtr<ServerSession>>(sessions));
+ if (server_port->HasHLEHandler()) {
+ server_port->GetHLEHandler()->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions));
+ } else {
+ server_port->AppendPendingSession(std::get<SharedPtr<ServerSession>>(sessions));
+ }
// Wake the threads waiting on the ServerPort
server_port->WakeupAllWaitingThreads();
diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp
index 704e82824..c17baa50a 100644
--- a/src/core/hle/kernel/client_session.cpp
+++ b/src/core/hle/kernel/client_session.cpp
@@ -17,21 +17,11 @@ ClientSession::~ClientSession() {
// This destructor will be called automatically when the last ClientSession handle is closed by
// the emulated application.
- // Local references to ServerSession and SessionRequestHandler are necessary to guarantee they
+ // A local reference to the ServerSession is necessary to guarantee it
// will be kept alive until after ClientDisconnected() returns.
SharedPtr<ServerSession> server = parent->server;
if (server) {
- std::shared_ptr<SessionRequestHandler> hle_handler = server->hle_handler;
- if (hle_handler)
- hle_handler->ClientDisconnected(server);
-
- // TODO(Subv): Force a wake up of all the ServerSession's waiting threads and set
- // their WaitSynchronization result to 0xC920181A.
-
- // Clean up the list of client threads with pending requests, they are unneeded now that the
- // client endpoint is closed.
- server->pending_requesting_threads.clear();
- server->currently_handling = nullptr;
+ server->ClientDisconnected();
}
parent->client = nullptr;
diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h
index 4c18de69c..b1f39aad7 100644
--- a/src/core/hle/kernel/client_session.h
+++ b/src/core/hle/kernel/client_session.h
@@ -36,14 +36,15 @@ public:
ResultCode SendSyncRequest(SharedPtr<Thread> thread);
- std::string name; ///< Name of client port (optional)
+private:
+ explicit ClientSession(KernelCore& kernel);
+ ~ClientSession() override;
/// The parent session, which links to the server endpoint.
std::shared_ptr<Session> parent;
-private:
- explicit ClientSession(KernelCore& kernel);
- ~ClientSession() override;
+ /// Name of the client session (optional)
+ std::string name;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/code_set.cpp b/src/core/hle/kernel/code_set.cpp
new file mode 100644
index 000000000..1f434e9af
--- /dev/null
+++ b/src/core/hle/kernel/code_set.cpp
@@ -0,0 +1,12 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/kernel/code_set.h"
+
+namespace Kernel {
+
+CodeSet::CodeSet() = default;
+CodeSet::~CodeSet() = default;
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/code_set.h b/src/core/hle/kernel/code_set.h
new file mode 100644
index 000000000..834fd23d2
--- /dev/null
+++ b/src/core/hle/kernel/code_set.h
@@ -0,0 +1,90 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <cstddef>
+#include <memory>
+#include <vector>
+
+#include "common/common_types.h"
+
+namespace Kernel {
+
+/**
+ * Represents executable data that may be loaded into a kernel process.
+ *
+ * A code set consists of three basic segments:
+ * - A code (AKA text) segment,
+ * - A read-only data segment (rodata)
+ * - A data segment
+ *
+ * The code segment is the portion of the object file that contains
+ * executable instructions.
+ *
+ * The read-only data segment in the portion of the object file that
+ * contains (as one would expect) read-only data, such as fixed constant
+ * values and data structures.
+ *
+ * The data segment is similar to the read-only data segment -- it contains
+ * variables and data structures that have predefined values, however,
+ * entities within this segment can be modified.
+ */
+struct CodeSet final {
+ /// A single segment within a code set.
+ struct Segment final {
+ /// The byte offset that this segment is located at.
+ std::size_t offset = 0;
+
+ /// The address to map this segment to.
+ VAddr addr = 0;
+
+ /// The size of this segment in bytes.
+ u32 size = 0;
+ };
+
+ explicit CodeSet();
+ ~CodeSet();
+
+ CodeSet(const CodeSet&) = delete;
+ CodeSet& operator=(const CodeSet&) = delete;
+
+ CodeSet(CodeSet&&) = default;
+ CodeSet& operator=(CodeSet&&) = default;
+
+ Segment& CodeSegment() {
+ return segments[0];
+ }
+
+ const Segment& CodeSegment() const {
+ return segments[0];
+ }
+
+ Segment& RODataSegment() {
+ return segments[1];
+ }
+
+ const Segment& RODataSegment() const {
+ return segments[1];
+ }
+
+ Segment& DataSegment() {
+ return segments[2];
+ }
+
+ const Segment& DataSegment() const {
+ return segments[2];
+ }
+
+ /// The overall data that backs this code set.
+ std::shared_ptr<std::vector<u8>> memory;
+
+ /// The segments that comprise this code set.
+ std::array<Segment, 3> segments;
+
+ /// The entry point address for this code set.
+ VAddr entrypoint = 0;
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
index d17eb0cb6..8097b3863 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -14,6 +14,7 @@ constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7};
constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14};
constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101};
constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102};
+constexpr ResultCode ERR_OUT_OF_MEMORY{ErrorModule::Kernel, 104};
constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105};
constexpr ResultCode ERR_INVALID_ADDRESS_STATE{ErrorModule::Kernel, 106};
constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS{ErrorModule::Kernel, 108};
diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp
index c8acde5b1..bdfaa977f 100644
--- a/src/core/hle/kernel/handle_table.cpp
+++ b/src/core/hle/kernel/handle_table.cpp
@@ -14,32 +14,47 @@
namespace Kernel {
namespace {
constexpr u16 GetSlot(Handle handle) {
- return handle >> 15;
+ return static_cast<u16>(handle >> 15);
}
constexpr u16 GetGeneration(Handle handle) {
- return handle & 0x7FFF;
+ return static_cast<u16>(handle & 0x7FFF);
}
} // Anonymous namespace
HandleTable::HandleTable() {
- next_generation = 1;
Clear();
}
HandleTable::~HandleTable() = default;
+ResultCode HandleTable::SetSize(s32 handle_table_size) {
+ if (static_cast<u32>(handle_table_size) > MAX_COUNT) {
+ return ERR_OUT_OF_MEMORY;
+ }
+
+ // Values less than or equal to zero indicate to use the maximum allowable
+ // size for the handle table in the actual kernel, so we ignore the given
+ // value in that case, since we assume this by default unless this function
+ // is called.
+ if (handle_table_size > 0) {
+ table_size = static_cast<u16>(handle_table_size);
+ }
+
+ return RESULT_SUCCESS;
+}
+
ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) {
DEBUG_ASSERT(obj != nullptr);
- u16 slot = next_free_slot;
- if (slot >= generations.size()) {
+ const u16 slot = next_free_slot;
+ if (slot >= table_size) {
LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use.");
return ERR_HANDLE_TABLE_FULL;
}
next_free_slot = generations[slot];
- u16 generation = next_generation++;
+ const u16 generation = next_generation++;
// Overflow count so it fits in the 15 bits dedicated to the generation in the handle.
// Horizon OS uses zero to represent an invalid handle, so skip to 1.
@@ -64,10 +79,11 @@ ResultVal<Handle> HandleTable::Duplicate(Handle handle) {
}
ResultCode HandleTable::Close(Handle handle) {
- if (!IsValid(handle))
+ if (!IsValid(handle)) {
return ERR_INVALID_HANDLE;
+ }
- u16 slot = GetSlot(handle);
+ const u16 slot = GetSlot(handle);
objects[slot] = nullptr;
@@ -77,10 +93,10 @@ ResultCode HandleTable::Close(Handle handle) {
}
bool HandleTable::IsValid(Handle handle) const {
- std::size_t slot = GetSlot(handle);
- u16 generation = GetGeneration(handle);
+ const std::size_t slot = GetSlot(handle);
+ const u16 generation = GetGeneration(handle);
- return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation;
+ return slot < table_size && objects[slot] != nullptr && generations[slot] == generation;
}
SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const {
@@ -97,7 +113,7 @@ SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const {
}
void HandleTable::Clear() {
- for (u16 i = 0; i < MAX_COUNT; ++i) {
+ for (u16 i = 0; i < table_size; ++i) {
generations[i] = i + 1;
objects[i] = nullptr;
}
diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h
index 89a3bc740..44901391b 100644
--- a/src/core/hle/kernel/handle_table.h
+++ b/src/core/hle/kernel/handle_table.h
@@ -50,6 +50,20 @@ public:
~HandleTable();
/**
+ * Sets the number of handles that may be in use at one time
+ * for this handle table.
+ *
+ * @param handle_table_size The desired size to limit the handle table to.
+ *
+ * @returns an error code indicating if initialization was successful.
+ * If initialization was not successful, then ERR_OUT_OF_MEMORY
+ * will be returned.
+ *
+ * @pre handle_table_size must be within the range [0, 1024]
+ */
+ ResultCode SetSize(s32 handle_table_size);
+
+ /**
* Allocates a handle for the given object.
* @return The created Handle or one of the following errors:
* - `ERR_HANDLE_TABLE_FULL`: the maximum number of handles has been exceeded.
@@ -104,13 +118,20 @@ private:
std::array<u16, MAX_COUNT> generations;
/**
+ * The limited size of the handle table. This can be specified by process
+ * capabilities in order to restrict the overall number of handles that
+ * can be created in a process instance
+ */
+ u16 table_size = static_cast<u16>(MAX_COUNT);
+
+ /**
* Global counter of the number of created handles. Stored in `generations` when a handle is
* created, and wraps around to 1 when it hits 0x8000.
*/
- u16 next_generation;
+ u16 next_generation = 1;
/// Head of the free slots linked list.
- u16 next_free_slot;
+ u16 next_free_slot = 0;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 5dd855db8..fe710eb6e 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -86,7 +86,7 @@ HLERequestContext::~HLERequestContext() = default;
void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf,
bool incoming) {
IPC::RequestParser rp(src_cmdbuf);
- command_header = std::make_shared<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>());
+ command_header = rp.PopRaw<IPC::CommandHeader>();
if (command_header->type == IPC::CommandType::Close) {
// Close does not populate the rest of the IPC header
@@ -95,8 +95,7 @@ void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_
// If handle descriptor is present, add size of it
if (command_header->enable_handle_descriptor) {
- handle_descriptor_header =
- std::make_shared<IPC::HandleDescriptorHeader>(rp.PopRaw<IPC::HandleDescriptorHeader>());
+ handle_descriptor_header = rp.PopRaw<IPC::HandleDescriptorHeader>();
if (handle_descriptor_header->send_current_pid) {
rp.Skip(2, false);
}
@@ -140,16 +139,15 @@ void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_
// If this is an incoming message, only CommandType "Request" has a domain header
// All outgoing domain messages have the domain header, if only incoming has it
if (incoming || domain_message_header) {
- domain_message_header =
- std::make_shared<IPC::DomainMessageHeader>(rp.PopRaw<IPC::DomainMessageHeader>());
+ domain_message_header = rp.PopRaw<IPC::DomainMessageHeader>();
} else {
- if (Session()->IsDomain())
+ if (Session()->IsDomain()) {
LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!");
+ }
}
}
- data_payload_header =
- std::make_shared<IPC::DataPayloadHeader>(rp.PopRaw<IPC::DataPayloadHeader>());
+ data_payload_header = rp.PopRaw<IPC::DataPayloadHeader>();
data_payload_offset = rp.GetCurrentOffset();
@@ -264,11 +262,11 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
// Write the domain objects to the command buffer, these go after the raw untranslated data.
// TODO(Subv): This completely ignores C buffers.
std::size_t domain_offset = size - domain_message_header->num_objects;
- auto& request_handlers = server_session->domain_request_handlers;
- for (auto& object : domain_objects) {
- request_handlers.emplace_back(object);
- dst_cmdbuf[domain_offset++] = static_cast<u32_le>(request_handlers.size());
+ for (const auto& object : domain_objects) {
+ server_session->AppendDomainRequestHandler(object);
+ dst_cmdbuf[domain_offset++] =
+ static_cast<u32_le>(server_session->NumDomainRequestHandlers());
}
}
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index cb1c5aff3..2bdd9f02c 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -6,6 +6,7 @@
#include <array>
#include <memory>
+#include <optional>
#include <string>
#include <type_traits>
#include <vector>
@@ -15,6 +16,8 @@
#include "core/hle/ipc.h"
#include "core/hle/kernel/object.h"
+union ResultCode;
+
namespace Service {
class ServiceFrameworkBase;
}
@@ -166,12 +169,12 @@ public:
return buffer_c_desciptors;
}
- const IPC::DomainMessageHeader* GetDomainMessageHeader() const {
- return domain_message_header.get();
+ const IPC::DomainMessageHeader& GetDomainMessageHeader() const {
+ return domain_message_header.value();
}
bool HasDomainMessageHeader() const {
- return domain_message_header != nullptr;
+ return domain_message_header.has_value();
}
/// Helper function to read a buffer using the appropriate buffer descriptor
@@ -208,14 +211,12 @@ public:
template <typename T>
SharedPtr<T> GetCopyObject(std::size_t index) {
- ASSERT(index < copy_objects.size());
- return DynamicObjectCast<T>(copy_objects[index]);
+ return DynamicObjectCast<T>(copy_objects.at(index));
}
template <typename T>
SharedPtr<T> GetMoveObject(std::size_t index) {
- ASSERT(index < move_objects.size());
- return DynamicObjectCast<T>(move_objects[index]);
+ return DynamicObjectCast<T>(move_objects.at(index));
}
void AddMoveObject(SharedPtr<Object> object) {
@@ -232,7 +233,7 @@ public:
template <typename T>
std::shared_ptr<T> GetDomainRequestHandler(std::size_t index) const {
- return std::static_pointer_cast<T>(domain_request_handlers[index]);
+ return std::static_pointer_cast<T>(domain_request_handlers.at(index));
}
void SetDomainRequestHandlers(
@@ -272,10 +273,10 @@ private:
boost::container::small_vector<SharedPtr<Object>, 8> copy_objects;
boost::container::small_vector<std::shared_ptr<SessionRequestHandler>, 8> domain_objects;
- std::shared_ptr<IPC::CommandHeader> command_header;
- std::shared_ptr<IPC::HandleDescriptorHeader> handle_descriptor_header;
- std::shared_ptr<IPC::DataPayloadHeader> data_payload_header;
- std::shared_ptr<IPC::DomainMessageHeader> domain_message_header;
+ std::optional<IPC::CommandHeader> command_header;
+ std::optional<IPC::HandleDescriptorHeader> handle_descriptor_header;
+ std::optional<IPC::DataPayloadHeader> data_payload_header;
+ std::optional<IPC::DomainMessageHeader> domain_message_header;
std::vector<IPC::BufferDescriptorX> buffer_x_desciptors;
std::vector<IPC::BufferDescriptorABW> buffer_a_desciptors;
std::vector<IPC::BufferDescriptorABW> buffer_b_desciptors;
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 7a524ce5a..4d224d01d 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -12,6 +12,7 @@
#include "core/core.h"
#include "core/core_timing.h"
+#include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
@@ -86,6 +87,8 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_
}
struct KernelCore::Impl {
+ explicit Impl(Core::System& system) : system{system} {}
+
void Initialize(KernelCore& kernel) {
Shutdown();
@@ -124,7 +127,7 @@ struct KernelCore::Impl {
void InitializeThreads() {
thread_wakeup_event_type =
- CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
+ system.CoreTiming().RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
}
std::atomic<u32> next_object_id{0};
@@ -137,7 +140,7 @@ struct KernelCore::Impl {
SharedPtr<ResourceLimit> system_resource_limit;
- CoreTiming::EventType* thread_wakeup_event_type = nullptr;
+ Core::Timing::EventType* thread_wakeup_event_type = nullptr;
// TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future,
// allowing us to simply use a pool index or similar.
Kernel::HandleTable thread_wakeup_callback_handle_table;
@@ -145,9 +148,12 @@ struct KernelCore::Impl {
/// Map of named ports managed by the kernel, which can be retrieved using
/// the ConnectToPort SVC.
NamedPortTable named_ports;
+
+ // System context
+ Core::System& system;
};
-KernelCore::KernelCore() : impl{std::make_unique<Impl>()} {}
+KernelCore::KernelCore(Core::System& system) : impl{std::make_unique<Impl>(system)} {}
KernelCore::~KernelCore() {
Shutdown();
}
@@ -213,7 +219,7 @@ u64 KernelCore::CreateNewProcessID() {
return impl->next_process_id++;
}
-CoreTiming::EventType* KernelCore::ThreadWakeupCallbackEventType() const {
+Core::Timing::EventType* KernelCore::ThreadWakeupCallbackEventType() const {
return impl->thread_wakeup_event_type;
}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index c643a6401..ff17ff865 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -11,12 +11,18 @@
template <typename T>
class ResultVal;
-namespace CoreTiming {
-struct EventType;
+namespace Core {
+class System;
}
+namespace Core::Timing {
+class CoreTiming;
+struct EventType;
+} // namespace Core::Timing
+
namespace Kernel {
+class AddressArbiter;
class ClientPort;
class HandleTable;
class Process;
@@ -29,7 +35,14 @@ private:
using NamedPortTable = std::unordered_map<std::string, SharedPtr<ClientPort>>;
public:
- KernelCore();
+ /// Constructs an instance of the kernel using the given System
+ /// instance as a context for any necessary system-related state,
+ /// such as threads, CPU core state, etc.
+ ///
+ /// @post After execution of the constructor, the provided System
+ /// object *must* outlive the kernel instance itself.
+ ///
+ explicit KernelCore(Core::System& system);
~KernelCore();
KernelCore(const KernelCore&) = delete;
@@ -89,7 +102,7 @@ private:
u64 CreateNewThreadID();
/// Retrieves the event type used for thread wakeup callbacks.
- CoreTiming::EventType* ThreadWakeupCallbackEventType() const;
+ Core::Timing::EventType* ThreadWakeupCallbackEventType() const;
/// Provides a reference to the thread wakeup callback handle table.
Kernel::HandleTable& ThreadWakeupCallbackHandleTable();
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index c5aa19afa..15a16ae14 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -9,6 +9,7 @@
#include "common/logging/log.h"
#include "core/core.h"
#include "core/file_sys/program_metadata.h"
+#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
@@ -31,7 +32,7 @@ namespace {
*/
void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_point, u32 priority) {
// Setup page table so we can write to memory
- SetCurrentPageTable(&owner_process.VMManager().page_table);
+ Memory::SetCurrentPageTable(&owner_process.VMManager().page_table);
// Initialize new "main" thread
const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress();
@@ -50,12 +51,10 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_poi
}
} // Anonymous namespace
-CodeSet::CodeSet() = default;
-CodeSet::~CodeSet() = default;
-
-SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
- SharedPtr<Process> process(new Process(kernel));
+SharedPtr<Process> Process::Create(Core::System& system, std::string&& name) {
+ auto& kernel = system.Kernel();
+ SharedPtr<Process> process(new Process(system));
process->name = std::move(name);
process->resource_limit = kernel.GetSystemResourceLimit();
process->status = ProcessStatus::Created;
@@ -99,7 +98,13 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
vm_manager.Reset(metadata.GetAddressSpaceType());
const auto& caps = metadata.GetKernelCapabilities();
- return capabilities.InitializeForUserProcess(caps.data(), caps.size(), vm_manager);
+ const auto capability_init_result =
+ capabilities.InitializeForUserProcess(caps.data(), caps.size(), vm_manager);
+ if (capability_init_result.IsError()) {
+ return capability_init_result;
+ }
+
+ return handle_table.SetSize(capabilities.GetHandleTableSize());
}
void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
@@ -126,7 +131,7 @@ void Process::PrepareForTermination() {
if (thread->GetOwnerProcess() != this)
continue;
- if (thread == GetCurrentThread())
+ if (thread == system.CurrentScheduler().GetCurrentThread())
continue;
// TODO(Subv): When are the other running/ready threads terminated?
@@ -138,7 +143,6 @@ void Process::PrepareForTermination() {
}
};
- const auto& system = Core::System::GetInstance();
stop_threads(system.Scheduler(0).GetThreadList());
stop_threads(system.Scheduler(1).GetThreadList());
stop_threads(system.Scheduler(2).GetThreadList());
@@ -206,7 +210,7 @@ void Process::FreeTLSSlot(VAddr tls_address) {
}
void Process::LoadModule(CodeSet module_, VAddr base_addr) {
- const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions,
+ const auto MapSegment = [&](const CodeSet::Segment& segment, VMAPermission permissions,
MemoryState memory_state) {
const auto vma = vm_manager
.MapMemoryBlock(segment.addr + base_addr, module_.memory,
@@ -221,14 +225,12 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable);
// Clear instruction cache in CPU JIT
- Core::System::GetInstance().ArmInterface(0).ClearInstructionCache();
- Core::System::GetInstance().ArmInterface(1).ClearInstructionCache();
- Core::System::GetInstance().ArmInterface(2).ClearInstructionCache();
- Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
+ system.InvalidateCpuInstructionCaches();
}
-Kernel::Process::Process(KernelCore& kernel) : WaitObject{kernel} {}
-Kernel::Process::~Process() {}
+Process::Process(Core::System& system)
+ : WaitObject{system.Kernel()}, address_arbiter{system}, system{system} {}
+Process::~Process() = default;
void Process::Acquire(Thread* thread) {
ASSERT_MSG(!ShouldWait(thread), "Object unavailable!");
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index dcc57ae9f..3ae7c922c 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -7,17 +7,21 @@
#include <array>
#include <bitset>
#include <cstddef>
-#include <memory>
#include <string>
#include <vector>
#include <boost/container/static_vector.hpp>
#include "common/common_types.h"
+#include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/process_capability.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/hle/kernel/wait_object.h"
#include "core/hle/result.h"
+namespace Core {
+class System;
+}
+
namespace FileSys {
class ProgramMetadata;
}
@@ -28,6 +32,8 @@ class KernelCore;
class ResourceLimit;
class Thread;
+struct CodeSet;
+
struct AddressMapping {
// Address and size must be page-aligned
VAddr address;
@@ -60,46 +66,6 @@ enum class ProcessStatus {
DebugBreak,
};
-struct CodeSet final {
- struct Segment {
- std::size_t offset = 0;
- VAddr addr = 0;
- u32 size = 0;
- };
-
- explicit CodeSet();
- ~CodeSet();
-
- Segment& CodeSegment() {
- return segments[0];
- }
-
- const Segment& CodeSegment() const {
- return segments[0];
- }
-
- Segment& RODataSegment() {
- return segments[1];
- }
-
- const Segment& RODataSegment() const {
- return segments[1];
- }
-
- Segment& DataSegment() {
- return segments[2];
- }
-
- const Segment& DataSegment() const {
- return segments[2];
- }
-
- std::shared_ptr<std::vector<u8>> memory;
-
- std::array<Segment, 3> segments;
- VAddr entrypoint = 0;
-};
-
class Process final : public WaitObject {
public:
enum : u64 {
@@ -116,7 +82,7 @@ public:
static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
- static SharedPtr<Process> Create(KernelCore& kernel, std::string&& name);
+ static SharedPtr<Process> Create(Core::System& system, std::string&& name);
std::string GetTypeName() const override {
return "Process";
@@ -150,6 +116,16 @@ public:
return handle_table;
}
+ /// Gets a reference to the process' address arbiter.
+ AddressArbiter& GetAddressArbiter() {
+ return address_arbiter;
+ }
+
+ /// Gets a const reference to the process' address arbiter.
+ const AddressArbiter& GetAddressArbiter() const {
+ return address_arbiter;
+ }
+
/// Gets the current status of the process
ProcessStatus GetStatus() const {
return status;
@@ -251,7 +227,7 @@ public:
void FreeTLSSlot(VAddr tls_address);
private:
- explicit Process(KernelCore& kernel);
+ explicit Process(Core::System& system);
~Process() override;
/// Checks if the specified thread should wait until this process is available.
@@ -309,9 +285,16 @@ private:
/// Per-process handle table for storing created object handles in.
HandleTable handle_table;
+ /// Per-process address arbiter.
+ AddressArbiter address_arbiter;
+
/// Random values for svcGetInfo RandomEntropy
std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy;
+ /// System context
+ Core::System& system;
+
+ /// Name of this process
std::string name;
};
diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp
index 3a2164b25..583e35b79 100644
--- a/src/core/hle/kernel/process_capability.cpp
+++ b/src/core/hle/kernel/process_capability.cpp
@@ -96,7 +96,7 @@ void ProcessCapabilities::InitializeForMetadatalessProcess() {
interrupt_capabilities.set();
// Allow using the maximum possible amount of handles
- handle_table_size = static_cast<u32>(HandleTable::MAX_COUNT);
+ handle_table_size = static_cast<s32>(HandleTable::MAX_COUNT);
// Allow all debugging capabilities.
is_debuggable = true;
@@ -337,7 +337,7 @@ ResultCode ProcessCapabilities::HandleHandleTableFlags(u32 flags) {
return ERR_RESERVED_VALUE;
}
- handle_table_size = (flags >> 16) & 0x3FF;
+ handle_table_size = static_cast<s32>((flags >> 16) & 0x3FF);
return RESULT_SUCCESS;
}
diff --git a/src/core/hle/kernel/process_capability.h b/src/core/hle/kernel/process_capability.h
index fbc8812a3..5cdd80747 100644
--- a/src/core/hle/kernel/process_capability.h
+++ b/src/core/hle/kernel/process_capability.h
@@ -156,7 +156,7 @@ public:
}
/// Gets the number of total allowable handles for the process' handle table.
- u32 GetHandleTableSize() const {
+ s32 GetHandleTableSize() const {
return handle_table_size;
}
@@ -252,7 +252,7 @@ private:
u64 core_mask = 0;
u64 priority_mask = 0;
- u32 handle_table_size = 0;
+ s32 handle_table_size = 0;
u32 kernel_version = 0;
ProgramType program_type = ProgramType::SysModule;
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index df4d6cf0a..cc189cc64 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -19,7 +19,8 @@ namespace Kernel {
std::mutex Scheduler::scheduler_mutex;
-Scheduler::Scheduler(Core::ARM_Interface& cpu_core) : cpu_core(cpu_core) {}
+Scheduler::Scheduler(Core::System& system, Core::ARM_Interface& cpu_core)
+ : cpu_core{cpu_core}, system{system} {}
Scheduler::~Scheduler() {
for (auto& thread : thread_list) {
@@ -61,7 +62,7 @@ Thread* Scheduler::PopNextReadyThread() {
void Scheduler::SwitchContext(Thread* new_thread) {
Thread* const previous_thread = GetCurrentThread();
- Process* const previous_process = Core::CurrentProcess();
+ Process* const previous_process = system.Kernel().CurrentProcess();
UpdateLastContextSwitchTime(previous_thread, previous_process);
@@ -94,8 +95,8 @@ void Scheduler::SwitchContext(Thread* new_thread) {
auto* const thread_owner_process = current_thread->GetOwnerProcess();
if (previous_process != thread_owner_process) {
- Core::System::GetInstance().Kernel().MakeCurrentProcess(thread_owner_process);
- SetCurrentPageTable(&Core::CurrentProcess()->VMManager().page_table);
+ system.Kernel().MakeCurrentProcess(thread_owner_process);
+ Memory::SetCurrentPageTable(&thread_owner_process->VMManager().page_table);
}
cpu_core.LoadContext(new_thread->GetContext());
@@ -111,7 +112,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
const u64 prev_switch_ticks = last_context_switch_time;
- const u64 most_recent_switch_ticks = CoreTiming::GetTicks();
+ const u64 most_recent_switch_ticks = system.CoreTiming().GetTicks();
const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
if (thread != nullptr) {
@@ -198,8 +199,7 @@ void Scheduler::YieldWithoutLoadBalancing(Thread* thread) {
ASSERT(thread->GetPriority() < THREADPRIO_COUNT);
// Yield this thread -- sleep for zero time and force reschedule to different thread
- WaitCurrentThread_Sleep();
- GetCurrentThread()->WakeAfterDelay(0);
+ GetCurrentThread()->Sleep(0);
}
void Scheduler::YieldWithLoadBalancing(Thread* thread) {
@@ -214,8 +214,7 @@ void Scheduler::YieldWithLoadBalancing(Thread* thread) {
ASSERT(priority < THREADPRIO_COUNT);
// Sleep for zero time to be able to force reschedule to different thread
- WaitCurrentThread_Sleep();
- GetCurrentThread()->WakeAfterDelay(0);
+ GetCurrentThread()->Sleep(0);
Thread* suggested_thread = nullptr;
@@ -223,8 +222,7 @@ void Scheduler::YieldWithLoadBalancing(Thread* thread) {
// Take the first non-nullptr one
for (unsigned cur_core = 0; cur_core < Core::NUM_CPU_CORES; ++cur_core) {
const auto res =
- Core::System::GetInstance().CpuCore(cur_core).Scheduler().GetNextSuggestedThread(
- core, priority);
+ system.CpuCore(cur_core).Scheduler().GetNextSuggestedThread(core, priority);
// If scheduler provides a suggested thread
if (res != nullptr) {
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h
index 97ced4dfc..1c5bf57d9 100644
--- a/src/core/hle/kernel/scheduler.h
+++ b/src/core/hle/kernel/scheduler.h
@@ -13,7 +13,8 @@
namespace Core {
class ARM_Interface;
-}
+class System;
+} // namespace Core
namespace Kernel {
@@ -21,7 +22,7 @@ class Process;
class Scheduler final {
public:
- explicit Scheduler(Core::ARM_Interface& cpu_core);
+ explicit Scheduler(Core::System& system, Core::ARM_Interface& cpu_core);
~Scheduler();
/// Returns whether there are any threads that are ready to run.
@@ -162,6 +163,7 @@ private:
Core::ARM_Interface& cpu_core;
u64 last_context_switch_time = 0;
+ Core::System& system;
static std::mutex scheduler_mutex;
};
diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp
index d6ceeb2da..0e1515c89 100644
--- a/src/core/hle/kernel/server_port.cpp
+++ b/src/core/hle/kernel/server_port.cpp
@@ -26,6 +26,10 @@ ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() {
return MakeResult(std::move(session));
}
+void ServerPort::AppendPendingSession(SharedPtr<ServerSession> pending_session) {
+ pending_sessions.push_back(std::move(pending_session));
+}
+
bool ServerPort::ShouldWait(Thread* thread) const {
// If there are no pending sessions, we wait until a new one is added.
return pending_sessions.empty();
diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h
index e52f8245f..9bc667cf2 100644
--- a/src/core/hle/kernel/server_port.h
+++ b/src/core/hle/kernel/server_port.h
@@ -22,6 +22,8 @@ class SessionRequestHandler;
class ServerPort final : public WaitObject {
public:
+ using HLEHandler = std::shared_ptr<SessionRequestHandler>;
+
/**
* Creates a pair of ServerPort and an associated ClientPort.
*
@@ -51,22 +53,27 @@ public:
*/
ResultVal<SharedPtr<ServerSession>> Accept();
+ /// Whether or not this server port has an HLE handler available.
+ bool HasHLEHandler() const {
+ return hle_handler != nullptr;
+ }
+
+ /// Gets the HLE handler for this port.
+ HLEHandler GetHLEHandler() const {
+ return hle_handler;
+ }
+
/**
* Sets the HLE handler template for the port. ServerSessions crated by connecting to this port
* will inherit a reference to this handler.
*/
- void SetHleHandler(std::shared_ptr<SessionRequestHandler> hle_handler_) {
+ void SetHleHandler(HLEHandler hle_handler_) {
hle_handler = std::move(hle_handler_);
}
- std::string name; ///< Name of port (optional)
-
- /// ServerSessions waiting to be accepted by the port
- std::vector<SharedPtr<ServerSession>> pending_sessions;
-
- /// This session's HLE request handler template (optional)
- /// ServerSessions created from this port inherit a reference to this handler.
- std::shared_ptr<SessionRequestHandler> hle_handler;
+ /// Appends a ServerSession to the collection of ServerSessions
+ /// waiting to be accepted by this port.
+ void AppendPendingSession(SharedPtr<ServerSession> pending_session);
bool ShouldWait(Thread* thread) const override;
void Acquire(Thread* thread) override;
@@ -74,6 +81,16 @@ public:
private:
explicit ServerPort(KernelCore& kernel);
~ServerPort() override;
+
+ /// ServerSessions waiting to be accepted by the port
+ std::vector<SharedPtr<ServerSession>> pending_sessions;
+
+ /// This session's HLE request handler template (optional)
+ /// ServerSessions created from this port inherit a reference to this handler.
+ HLEHandler hle_handler;
+
+ /// Name of the port (optional)
+ std::string name;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index 027434f92..4d8a337a7 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -63,42 +63,71 @@ void ServerSession::Acquire(Thread* thread) {
pending_requesting_threads.pop_back();
}
+void ServerSession::ClientDisconnected() {
+ // We keep a shared pointer to the hle handler to keep it alive throughout
+ // the call to ClientDisconnected, as ClientDisconnected invalidates the
+ // hle_handler member itself during the course of the function executing.
+ std::shared_ptr<SessionRequestHandler> handler = hle_handler;
+ if (handler) {
+ // Note that after this returns, this server session's hle_handler is
+ // invalidated (set to null).
+ handler->ClientDisconnected(this);
+ }
+
+ // TODO(Subv): Force a wake up of all the ServerSession's waiting threads and set
+ // their WaitSynchronization result to 0xC920181A.
+
+ // Clean up the list of client threads with pending requests, they are unneeded now that the
+ // client endpoint is closed.
+ pending_requesting_threads.clear();
+ currently_handling = nullptr;
+}
+
+void ServerSession::AppendDomainRequestHandler(std::shared_ptr<SessionRequestHandler> handler) {
+ domain_request_handlers.push_back(std::move(handler));
+}
+
+std::size_t ServerSession::NumDomainRequestHandlers() const {
+ return domain_request_handlers.size();
+}
+
ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) {
- auto* const domain_message_header = context.GetDomainMessageHeader();
- if (domain_message_header) {
- // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs
- context.SetDomainRequestHandlers(domain_request_handlers);
-
- // If there is a DomainMessageHeader, then this is CommandType "Request"
- const u32 object_id{context.GetDomainMessageHeader()->object_id};
- switch (domain_message_header->command) {
- case IPC::DomainMessageHeader::CommandType::SendMessage:
- if (object_id > domain_request_handlers.size()) {
- LOG_CRITICAL(IPC,
- "object_id {} is too big! This probably means a recent service call "
- "to {} needed to return a new interface!",
- object_id, name);
- UNREACHABLE();
- return RESULT_SUCCESS; // Ignore error if asserts are off
- }
- return domain_request_handlers[object_id - 1]->HandleSyncRequest(context);
-
- case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
- LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id);
-
- domain_request_handlers[object_id - 1] = nullptr;
-
- IPC::ResponseBuilder rb{context, 2};
- rb.Push(RESULT_SUCCESS);
- return RESULT_SUCCESS;
- }
+ if (!context.HasDomainMessageHeader()) {
+ return RESULT_SUCCESS;
+ }
+
+ // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs
+ context.SetDomainRequestHandlers(domain_request_handlers);
+
+ // If there is a DomainMessageHeader, then this is CommandType "Request"
+ const auto& domain_message_header = context.GetDomainMessageHeader();
+ const u32 object_id{domain_message_header.object_id};
+ switch (domain_message_header.command) {
+ case IPC::DomainMessageHeader::CommandType::SendMessage:
+ if (object_id > domain_request_handlers.size()) {
+ LOG_CRITICAL(IPC,
+ "object_id {} is too big! This probably means a recent service call "
+ "to {} needed to return a new interface!",
+ object_id, name);
+ UNREACHABLE();
+ return RESULT_SUCCESS; // Ignore error if asserts are off
}
+ return domain_request_handlers[object_id - 1]->HandleSyncRequest(context);
- LOG_CRITICAL(IPC, "Unknown domain command={}",
- static_cast<int>(domain_message_header->command.Value()));
- ASSERT(false);
+ case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
+ LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id);
+
+ domain_request_handlers[object_id - 1] = nullptr;
+
+ IPC::ResponseBuilder rb{context, 2};
+ rb.Push(RESULT_SUCCESS);
+ return RESULT_SUCCESS;
+ }
}
+ LOG_CRITICAL(IPC, "Unknown domain command={}",
+ static_cast<int>(domain_message_header.command.Value()));
+ ASSERT(false);
return RESULT_SUCCESS;
}
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h
index e0e9d64c8..aea4ccfeb 100644
--- a/src/core/hle/kernel/server_session.h
+++ b/src/core/hle/kernel/server_session.h
@@ -46,6 +46,14 @@ public:
return HANDLE_TYPE;
}
+ Session* GetParent() {
+ return parent.get();
+ }
+
+ const Session* GetParent() const {
+ return parent.get();
+ }
+
using SessionPair = std::tuple<SharedPtr<ServerSession>, SharedPtr<ClientSession>>;
/**
@@ -78,23 +86,16 @@ public:
void Acquire(Thread* thread) override;
- std::string name; ///< The name of this session (optional)
- std::shared_ptr<Session> parent; ///< The parent session, which links to the client endpoint.
- std::shared_ptr<SessionRequestHandler>
- hle_handler; ///< This session's HLE request handler (applicable when not a domain)
+ /// Called when a client disconnection occurs.
+ void ClientDisconnected();
- /// This is the list of domain request handlers (after conversion to a domain)
- std::vector<std::shared_ptr<SessionRequestHandler>> domain_request_handlers;
-
- /// 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<SharedPtr<Thread>> pending_requesting_threads;
+ /// Adds a new domain request handler to the collection of request handlers within
+ /// this ServerSession instance.
+ void AppendDomainRequestHandler(std::shared_ptr<SessionRequestHandler> handler);
- /// 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.
- SharedPtr<Thread> currently_handling;
+ /// Retrieves the total number of domain request handlers that have been
+ /// appended to this ServerSession instance.
+ std::size_t NumDomainRequestHandlers() const;
/// Returns true if the session has been converted to a domain, otherwise False
bool IsDomain() const {
@@ -129,8 +130,30 @@ private:
/// object handle.
ResultCode HandleDomainSyncRequest(Kernel::HLERequestContext& context);
+ /// The parent session, which links to the client endpoint.
+ std::shared_ptr<Session> parent;
+
+ /// This session's HLE request handler (applicable when not a domain)
+ std::shared_ptr<SessionRequestHandler> hle_handler;
+
+ /// This is the list of domain request handlers (after conversion to a domain)
+ std::vector<std::shared_ptr<SessionRequestHandler>> domain_request_handlers;
+
+ /// 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<SharedPtr<Thread>> 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.
+ SharedPtr<Thread> currently_handling;
+
/// When set to True, converts the session to a domain at the end of the command
bool convert_to_domain{};
+
+ /// The name of this session (optional)
+ std::string name;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index 22d0c1dd5..62861da36 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -6,7 +6,6 @@
#include "common/assert.h"
#include "common/logging/log.h"
-#include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/shared_memory.h"
@@ -34,8 +33,8 @@ SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, Process* owner_
shared_memory->backing_block_offset = 0;
// Refresh the address mappings for the current process.
- if (Core::CurrentProcess() != nullptr) {
- Core::CurrentProcess()->VMManager().RefreshMemoryBlockMappings(
+ if (kernel.CurrentProcess() != nullptr) {
+ kernel.CurrentProcess()->VMManager().RefreshMemoryBlockMappings(
shared_memory->backing_block.get());
}
} else {
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 7cfecb68c..047fa0c19 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -20,6 +20,7 @@
#include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
+#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/mutex.h"
@@ -47,23 +48,6 @@ constexpr bool IsValidAddressRange(VAddr address, u64 size) {
return address + size > address;
}
-// Checks if a given address range lies within a larger address range.
-constexpr bool IsInsideAddressRange(VAddr address, u64 size, VAddr address_range_begin,
- VAddr address_range_end) {
- const VAddr end_address = address + size - 1;
- return address_range_begin <= address && end_address <= address_range_end - 1;
-}
-
-bool IsInsideAddressSpace(const VMManager& vm, VAddr address, u64 size) {
- return IsInsideAddressRange(address, size, vm.GetAddressSpaceBaseAddress(),
- vm.GetAddressSpaceEndAddress());
-}
-
-bool IsInsideNewMapRegion(const VMManager& vm, VAddr address, u64 size) {
- return IsInsideAddressRange(address, size, vm.GetNewMapRegionBaseAddress(),
- vm.GetNewMapRegionEndAddress());
-}
-
// 8 GiB
constexpr u64 MAIN_MEMORY_SIZE = 0x200000000;
@@ -105,14 +89,14 @@ ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_add
return ERR_INVALID_ADDRESS_STATE;
}
- if (!IsInsideAddressSpace(vm_manager, src_addr, size)) {
+ if (!vm_manager.IsWithinAddressSpace(src_addr, size)) {
LOG_ERROR(Kernel_SVC,
"Source is not within the address space, addr=0x{:016X}, size=0x{:016X}",
src_addr, size);
return ERR_INVALID_ADDRESS_STATE;
}
- if (!IsInsideNewMapRegion(vm_manager, dst_addr, size)) {
+ if (!vm_manager.IsWithinNewMapRegion(dst_addr, size)) {
LOG_ERROR(Kernel_SVC,
"Destination is not within the new map region, addr=0x{:016X}, size=0x{:016X}",
dst_addr, size);
@@ -238,7 +222,7 @@ static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) {
auto* const current_process = Core::CurrentProcess();
auto& vm_manager = current_process->VMManager();
- if (!IsInsideAddressSpace(vm_manager, addr, size)) {
+ if (!vm_manager.IsWithinAddressSpace(addr, size)) {
LOG_ERROR(Kernel_SVC,
"Source is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
size);
@@ -299,7 +283,7 @@ static ResultCode SetMemoryAttribute(VAddr address, u64 size, u32 mask, u32 attr
}
auto& vm_manager = Core::CurrentProcess()->VMManager();
- if (!IsInsideAddressSpace(vm_manager, address, size)) {
+ if (!vm_manager.IsWithinAddressSpace(address, size)) {
LOG_ERROR(Kernel_SVC,
"Given address (0x{:016X}) is outside the bounds of the address space.", address);
return ERR_INVALID_ADDRESS_STATE;
@@ -918,6 +902,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
}
const auto& system = Core::System::GetInstance();
+ const auto& core_timing = system.CoreTiming();
const auto& scheduler = system.CurrentScheduler();
const auto* const current_thread = scheduler.GetCurrentThread();
const bool same_thread = current_thread == thread;
@@ -927,9 +912,9 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks();
- out_ticks = thread_ticks + (CoreTiming::GetTicks() - prev_ctx_ticks);
+ out_ticks = thread_ticks + (core_timing.GetTicks() - prev_ctx_ticks);
} else if (same_thread && info_sub_id == system.CurrentCoreIndex()) {
- out_ticks = CoreTiming::GetTicks() - prev_ctx_ticks;
+ out_ticks = core_timing.GetTicks() - prev_ctx_ticks;
}
*result = out_ticks;
@@ -1299,10 +1284,14 @@ static ResultCode StartThread(Handle thread_handle) {
/// Called when a thread exits
static void ExitThread() {
- LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", Core::CurrentArmInterface().GetPC());
+ auto& system = Core::System::GetInstance();
- ExitCurrentThread();
- Core::System::GetInstance().PrepareReschedule();
+ LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
+
+ auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
+ current_thread->Stop();
+ system.CurrentScheduler().RemoveThread(current_thread);
+ system.PrepareReschedule();
}
/// Sleep the current thread
@@ -1315,32 +1304,32 @@ static void SleepThread(s64 nanoseconds) {
YieldAndWaitForLoadBalancing = -2,
};
+ auto& system = Core::System::GetInstance();
+ auto& scheduler = system.CurrentScheduler();
+ auto* const current_thread = scheduler.GetCurrentThread();
+
if (nanoseconds <= 0) {
- auto& scheduler{Core::System::GetInstance().CurrentScheduler()};
switch (static_cast<SleepType>(nanoseconds)) {
case SleepType::YieldWithoutLoadBalancing:
- scheduler.YieldWithoutLoadBalancing(GetCurrentThread());
+ scheduler.YieldWithoutLoadBalancing(current_thread);
break;
case SleepType::YieldWithLoadBalancing:
- scheduler.YieldWithLoadBalancing(GetCurrentThread());
+ scheduler.YieldWithLoadBalancing(current_thread);
break;
case SleepType::YieldAndWaitForLoadBalancing:
- scheduler.YieldAndWaitForLoadBalancing(GetCurrentThread());
+ scheduler.YieldAndWaitForLoadBalancing(current_thread);
break;
default:
UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds);
}
} else {
- // Sleep current thread and check for next thread to schedule
- WaitCurrentThread_Sleep();
-
- // Create an event to wake the thread up after the specified nanosecond delay has passed
- GetCurrentThread()->WakeAfterDelay(nanoseconds);
+ current_thread->Sleep(nanoseconds);
}
// Reschedule all CPU cores
- for (std::size_t i = 0; i < Core::NUM_CPU_CORES; ++i)
- Core::System::GetInstance().CpuCore(i).PrepareReschedule();
+ for (std::size_t i = 0; i < Core::NUM_CPU_CORES; ++i) {
+ system.CpuCore(i).PrepareReschedule();
+ }
}
/// Wait process wide key atomic
@@ -1494,20 +1483,10 @@ static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout
return ERR_INVALID_ADDRESS;
}
- switch (static_cast<AddressArbiter::ArbitrationType>(type)) {
- case AddressArbiter::ArbitrationType::WaitIfLessThan:
- return AddressArbiter::WaitForAddressIfLessThan(address, value, timeout, false);
- case AddressArbiter::ArbitrationType::DecrementAndWaitIfLessThan:
- return AddressArbiter::WaitForAddressIfLessThan(address, value, timeout, true);
- case AddressArbiter::ArbitrationType::WaitIfEqual:
- return AddressArbiter::WaitForAddressIfEqual(address, value, timeout);
- default:
- LOG_ERROR(Kernel_SVC,
- "Invalid arbitration type, expected WaitIfLessThan, DecrementAndWaitIfLessThan "
- "or WaitIfEqual but got {}",
- type);
- return ERR_INVALID_ENUM_VALUE;
- }
+ const auto arbitration_type = static_cast<AddressArbiter::ArbitrationType>(type);
+ auto& address_arbiter =
+ Core::System::GetInstance().Kernel().CurrentProcess()->GetAddressArbiter();
+ return address_arbiter.WaitForAddress(address, arbitration_type, value, timeout);
}
// Signals to an address (via Address Arbiter)
@@ -1525,31 +1504,21 @@ static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to
return ERR_INVALID_ADDRESS;
}
- switch (static_cast<AddressArbiter::SignalType>(type)) {
- case AddressArbiter::SignalType::Signal:
- return AddressArbiter::SignalToAddress(address, num_to_wake);
- case AddressArbiter::SignalType::IncrementAndSignalIfEqual:
- return AddressArbiter::IncrementAndSignalToAddressIfEqual(address, value, num_to_wake);
- case AddressArbiter::SignalType::ModifyByWaitingCountAndSignalIfEqual:
- return AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(address, value,
- num_to_wake);
- default:
- LOG_ERROR(Kernel_SVC,
- "Invalid signal type, expected Signal, IncrementAndSignalIfEqual "
- "or ModifyByWaitingCountAndSignalIfEqual but got {}",
- type);
- return ERR_INVALID_ENUM_VALUE;
- }
+ const auto signal_type = static_cast<AddressArbiter::SignalType>(type);
+ auto& address_arbiter =
+ Core::System::GetInstance().Kernel().CurrentProcess()->GetAddressArbiter();
+ return address_arbiter.SignalToAddress(address, signal_type, value, num_to_wake);
}
/// This returns the total CPU ticks elapsed since the CPU was powered-on
static u64 GetSystemTick() {
LOG_TRACE(Kernel_SVC, "called");
- const u64 result{CoreTiming::GetTicks()};
+ auto& core_timing = Core::System::GetInstance().CoreTiming();
+ const u64 result{core_timing.GetTicks()};
// Advance time to defeat dumb games that busy-wait for the frame to end.
- CoreTiming::AddTicks(400);
+ core_timing.AddTicks(400);
return result;
}
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index d3984dfc4..3b22e8e0d 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -7,8 +7,6 @@
#include <optional>
#include <vector>
-#include <boost/range/algorithm_ext/erase.hpp>
-
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
@@ -43,7 +41,8 @@ Thread::~Thread() = default;
void Thread::Stop() {
// Cancel any outstanding wakeup events for this thread
- CoreTiming::UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), callback_handle);
+ Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(),
+ callback_handle);
kernel.ThreadWakeupCallbackHandleTable().Close(callback_handle);
callback_handle = 0;
@@ -67,17 +66,6 @@ void Thread::Stop() {
owner_process->FreeTLSSlot(tls_address);
}
-void WaitCurrentThread_Sleep() {
- Thread* thread = GetCurrentThread();
- thread->SetStatus(ThreadStatus::WaitSleep);
-}
-
-void ExitCurrentThread() {
- Thread* thread = GetCurrentThread();
- thread->Stop();
- Core::System::GetInstance().CurrentScheduler().RemoveThread(thread);
-}
-
void Thread::WakeAfterDelay(s64 nanoseconds) {
// Don't schedule a wakeup if the thread wants to wait forever
if (nanoseconds == -1)
@@ -85,12 +73,14 @@ void Thread::WakeAfterDelay(s64 nanoseconds) {
// This function might be called from any thread so we have to be cautious and use the
// thread-safe version of ScheduleEvent.
- CoreTiming::ScheduleEventThreadsafe(CoreTiming::nsToCycles(nanoseconds),
- kernel.ThreadWakeupCallbackEventType(), callback_handle);
+ Core::System::GetInstance().CoreTiming().ScheduleEventThreadsafe(
+ Core::Timing::nsToCycles(nanoseconds), kernel.ThreadWakeupCallbackEventType(),
+ callback_handle);
}
void Thread::CancelWakeupTimer() {
- CoreTiming::UnscheduleEventThreadsafe(kernel.ThreadWakeupCallbackEventType(), callback_handle);
+ Core::System::GetInstance().CoreTiming().UnscheduleEventThreadsafe(
+ kernel.ThreadWakeupCallbackEventType(), callback_handle);
}
static std::optional<s32> GetNextProcessorId(u64 mask) {
@@ -181,14 +171,13 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
return ERR_INVALID_PROCESSOR_ID;
}
- // TODO(yuriks): Other checks, returning 0xD9001BEA
-
if (!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 ResultCode(-1);
}
+ auto& system = Core::System::GetInstance();
SharedPtr<Thread> thread(new Thread(kernel));
thread->thread_id = kernel.CreateNewThreadID();
@@ -197,7 +186,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
thread->stack_top = stack_top;
thread->tpidr_el0 = 0;
thread->nominal_priority = thread->current_priority = priority;
- thread->last_running_ticks = CoreTiming::GetTicks();
+ thread->last_running_ticks = system.CoreTiming().GetTicks();
thread->processor_id = processor_id;
thread->ideal_core = processor_id;
thread->affinity_mask = 1ULL << processor_id;
@@ -208,7 +197,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
thread->name = std::move(name);
thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();
thread->owner_process = &owner_process;
- thread->scheduler = &Core::System::GetInstance().Scheduler(processor_id);
+ thread->scheduler = &system.Scheduler(processor_id);
thread->scheduler->AddThread(thread, priority);
thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
@@ -257,7 +246,7 @@ void Thread::SetStatus(ThreadStatus new_status) {
}
if (status == ThreadStatus::Running) {
- last_running_ticks = CoreTiming::GetTicks();
+ last_running_ticks = Core::System::GetInstance().CoreTiming().GetTicks();
}
status = new_status;
@@ -267,8 +256,8 @@ void Thread::AddMutexWaiter(SharedPtr<Thread> thread) {
if (thread->lock_owner == this) {
// If the thread is already waiting for this thread to release the mutex, ensure that the
// waiters list is consistent and return without doing anything.
- auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
- ASSERT(itr != wait_mutex_threads.end());
+ const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
+ ASSERT(iter != wait_mutex_threads.end());
return;
}
@@ -276,11 +265,16 @@ void Thread::AddMutexWaiter(SharedPtr<Thread> thread) {
ASSERT(thread->lock_owner == nullptr);
// Ensure that the thread is not already in the list of mutex waiters
- auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
- ASSERT(itr == wait_mutex_threads.end());
-
+ const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
+ ASSERT(iter == wait_mutex_threads.end());
+
+ // Keep the list in an ordered fashion
+ const auto insertion_point = std::find_if(
+ wait_mutex_threads.begin(), wait_mutex_threads.end(),
+ [&thread](const auto& entry) { return entry->GetPriority() > thread->GetPriority(); });
+ wait_mutex_threads.insert(insertion_point, thread);
thread->lock_owner = this;
- wait_mutex_threads.emplace_back(std::move(thread));
+
UpdatePriority();
}
@@ -288,32 +282,44 @@ void Thread::RemoveMutexWaiter(SharedPtr<Thread> thread) {
ASSERT(thread->lock_owner == this);
// Ensure that the thread is in the list of mutex waiters
- auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
- ASSERT(itr != wait_mutex_threads.end());
+ const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
+ ASSERT(iter != wait_mutex_threads.end());
+
+ wait_mutex_threads.erase(iter);
- boost::remove_erase(wait_mutex_threads, thread);
thread->lock_owner = nullptr;
UpdatePriority();
}
void Thread::UpdatePriority() {
- // Find the highest priority among all the threads that are waiting for this thread's lock
+ // If any of the threads waiting on the mutex have a higher priority
+ // (taking into account priority inheritance), then this thread inherits
+ // that thread's priority.
u32 new_priority = nominal_priority;
- for (const auto& thread : wait_mutex_threads) {
- if (thread->nominal_priority < new_priority)
- new_priority = thread->nominal_priority;
+ if (!wait_mutex_threads.empty()) {
+ if (wait_mutex_threads.front()->current_priority < new_priority) {
+ new_priority = wait_mutex_threads.front()->current_priority;
+ }
}
- if (new_priority == current_priority)
+ if (new_priority == current_priority) {
return;
+ }
scheduler->SetThreadPriority(this, new_priority);
-
current_priority = new_priority;
+ if (!lock_owner) {
+ return;
+ }
+
+ // Ensure that the thread is within the correct location in the waiting list.
+ auto old_owner = lock_owner;
+ lock_owner->RemoveMutexWaiter(this);
+ old_owner->AddMutexWaiter(this);
+
// Recursively update the priority of the thread that depends on the priority of this one.
- if (lock_owner)
- lock_owner->UpdatePriority();
+ lock_owner->UpdatePriority();
}
void Thread::ChangeCore(u32 core, u64 mask) {
@@ -389,6 +395,14 @@ void Thread::SetActivity(ThreadActivity value) {
}
}
+void Thread::Sleep(s64 nanoseconds) {
+ // Sleep current thread and check for next thread to schedule
+ SetStatus(ThreadStatus::WaitSleep);
+
+ // Create an event to wake the thread up after the specified nanosecond delay has passed
+ WakeAfterDelay(nanoseconds);
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
/**
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index c48b21aba..faad5f391 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -383,6 +383,9 @@ public:
void SetActivity(ThreadActivity value);
+ /// Sleeps this thread for the given amount of nanoseconds.
+ void Sleep(s64 nanoseconds);
+
private:
explicit Thread(KernelCore& kernel);
~Thread() override;
@@ -398,8 +401,14 @@ private:
VAddr entry_point = 0;
VAddr stack_top = 0;
- u32 nominal_priority = 0; ///< Nominal thread priority, as set by the emulated application
- u32 current_priority = 0; ///< Current thread priority, can be temporarily changed
+ /// Nominal thread priority, as set by the emulated application.
+ /// The nominal priority is the thread priority without priority
+ /// inheritance taken into account.
+ u32 nominal_priority = 0;
+
+ /// Current thread priority. This may change over the course of the
+ /// thread's lifetime in order to facilitate priority inheritance.
+ u32 current_priority = 0;
u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks.
u64 last_running_ticks = 0; ///< CPU tick when thread was last running
@@ -460,14 +469,4 @@ private:
*/
Thread* GetCurrentThread();
-/**
- * Waits the current thread on a sleep
- */
-void WaitCurrentThread_Sleep();
-
-/**
- * Stops the current thread and removes it from the thread_list
- */
-void ExitCurrentThread();
-
} // namespace Kernel
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index 10ad94aa6..3def3e52c 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -7,18 +7,18 @@
#include <utility>
#include "common/assert.h"
#include "common/logging/log.h"
+#include "common/memory_hook.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/memory.h"
-#include "core/memory_hook.h"
#include "core/memory_setup.h"
namespace Kernel {
-
-static const char* GetMemoryStateName(MemoryState state) {
+namespace {
+const char* GetMemoryStateName(MemoryState state) {
static constexpr const char* names[] = {
"Unmapped", "Io",
"Normal", "CodeStatic",
@@ -35,6 +35,14 @@ static const char* GetMemoryStateName(MemoryState state) {
return names[ToSvcMemoryState(state)];
}
+// Checks if a given address range lies within a larger address range.
+constexpr bool IsInsideAddressRange(VAddr address, u64 size, VAddr address_range_begin,
+ VAddr address_range_end) {
+ const VAddr end_address = address + size - 1;
+ return address_range_begin <= address && end_address <= address_range_end - 1;
+}
+} // Anonymous namespace
+
bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
ASSERT(base + size == next.base);
if (permissions != next.permissions || state != next.state || attribute != next.attribute ||
@@ -169,7 +177,7 @@ ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const {
ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size,
MemoryState state,
- Memory::MemoryHookPointer mmio_handler) {
+ Common::MemoryHookPointer mmio_handler) {
// This is the appropriately sized VMA that will turn into our allocation.
CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size));
VirtualMemoryArea& final_vma = vma_handle->second;
@@ -249,8 +257,7 @@ ResultCode VMManager::ReprotectRange(VAddr target, u64 size, VMAPermission new_p
}
ResultVal<VAddr> VMManager::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
- if (target < GetHeapRegionBaseAddress() || target + size > GetHeapRegionEndAddress() ||
- target + size < target) {
+ if (!IsWithinHeapRegion(target, size)) {
return ERR_INVALID_ADDRESS;
}
@@ -285,8 +292,7 @@ ResultVal<VAddr> VMManager::HeapAllocate(VAddr target, u64 size, VMAPermission p
}
ResultCode VMManager::HeapFree(VAddr target, u64 size) {
- if (target < GetHeapRegionBaseAddress() || target + size > GetHeapRegionEndAddress() ||
- target + size < target) {
+ if (!IsWithinHeapRegion(target, size)) {
return ERR_INVALID_ADDRESS;
}
@@ -618,7 +624,7 @@ void VMManager::ClearPageTable() {
std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr);
page_table.special_regions.clear();
std::fill(page_table.attributes.begin(), page_table.attributes.end(),
- Memory::PageType::Unmapped);
+ Common::PageType::Unmapped);
}
VMManager::CheckResults VMManager::CheckRangeState(VAddr address, u64 size, MemoryState state_mask,
@@ -706,6 +712,11 @@ u64 VMManager::GetAddressSpaceWidth() const {
return address_space_width;
}
+bool VMManager::IsWithinAddressSpace(VAddr address, u64 size) const {
+ return IsInsideAddressRange(address, size, GetAddressSpaceBaseAddress(),
+ GetAddressSpaceEndAddress());
+}
+
VAddr VMManager::GetASLRRegionBaseAddress() const {
return aslr_region_base;
}
@@ -750,6 +761,11 @@ u64 VMManager::GetCodeRegionSize() const {
return code_region_end - code_region_base;
}
+bool VMManager::IsWithinCodeRegion(VAddr address, u64 size) const {
+ return IsInsideAddressRange(address, size, GetCodeRegionBaseAddress(),
+ GetCodeRegionEndAddress());
+}
+
VAddr VMManager::GetHeapRegionBaseAddress() const {
return heap_region_base;
}
@@ -762,6 +778,11 @@ u64 VMManager::GetHeapRegionSize() const {
return heap_region_end - heap_region_base;
}
+bool VMManager::IsWithinHeapRegion(VAddr address, u64 size) const {
+ return IsInsideAddressRange(address, size, GetHeapRegionBaseAddress(),
+ GetHeapRegionEndAddress());
+}
+
VAddr VMManager::GetMapRegionBaseAddress() const {
return map_region_base;
}
@@ -774,6 +795,10 @@ u64 VMManager::GetMapRegionSize() const {
return map_region_end - map_region_base;
}
+bool VMManager::IsWithinMapRegion(VAddr address, u64 size) const {
+ return IsInsideAddressRange(address, size, GetMapRegionBaseAddress(), GetMapRegionEndAddress());
+}
+
VAddr VMManager::GetNewMapRegionBaseAddress() const {
return new_map_region_base;
}
@@ -786,6 +811,11 @@ u64 VMManager::GetNewMapRegionSize() const {
return new_map_region_end - new_map_region_base;
}
+bool VMManager::IsWithinNewMapRegion(VAddr address, u64 size) const {
+ return IsInsideAddressRange(address, size, GetNewMapRegionBaseAddress(),
+ GetNewMapRegionEndAddress());
+}
+
VAddr VMManager::GetTLSIORegionBaseAddress() const {
return tls_io_region_base;
}
@@ -798,4 +828,9 @@ u64 VMManager::GetTLSIORegionSize() const {
return tls_io_region_end - tls_io_region_base;
}
+bool VMManager::IsWithinTLSIORegion(VAddr address, u64 size) const {
+ return IsInsideAddressRange(address, size, GetTLSIORegionBaseAddress(),
+ GetTLSIORegionEndAddress());
+}
+
} // namespace Kernel
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 6091533bc..b96980f8f 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -9,9 +9,10 @@
#include <tuple>
#include <vector>
#include "common/common_types.h"
+#include "common/memory_hook.h"
+#include "common/page_table.h"
#include "core/hle/result.h"
#include "core/memory.h"
-#include "core/memory_hook.h"
namespace FileSys {
enum class ProgramAddressSpaceType : u8;
@@ -290,7 +291,7 @@ struct VirtualMemoryArea {
// Settings for type = MMIO
/// Physical address of the register area this VMA maps to.
PAddr paddr = 0;
- Memory::MemoryHookPointer mmio_handler = nullptr;
+ Common::MemoryHookPointer mmio_handler = nullptr;
/// Tests if this area can be merged to the right with `next`.
bool CanBeMergedWith(const VirtualMemoryArea& next) const;
@@ -368,7 +369,7 @@ public:
* @param mmio_handler The handler that will implement read and write for this MMIO region.
*/
ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u64 size, MemoryState state,
- Memory::MemoryHookPointer mmio_handler);
+ Common::MemoryHookPointer mmio_handler);
/// Unmaps a range of addresses, splitting VMAs as necessary.
ResultCode UnmapRange(VAddr target, u64 size);
@@ -432,18 +433,21 @@ public:
/// Gets the address space width in bits.
u64 GetAddressSpaceWidth() const;
+ /// Determines whether or not the given address range lies within the address space.
+ bool IsWithinAddressSpace(VAddr address, u64 size) const;
+
/// Gets the base address of the ASLR region.
VAddr GetASLRRegionBaseAddress() const;
/// Gets the end address of the ASLR region.
VAddr GetASLRRegionEndAddress() const;
- /// Determines whether or not the specified address range is within the ASLR region.
- bool IsWithinASLRRegion(VAddr address, u64 size) const;
-
/// Gets the size of the ASLR region
u64 GetASLRRegionSize() const;
+ /// Determines whether or not the specified address range is within the ASLR region.
+ bool IsWithinASLRRegion(VAddr address, u64 size) const;
+
/// Gets the base address of the code region.
VAddr GetCodeRegionBaseAddress() const;
@@ -453,6 +457,9 @@ public:
/// Gets the total size of the code region in bytes.
u64 GetCodeRegionSize() const;
+ /// Determines whether or not the specified range is within the code region.
+ bool IsWithinCodeRegion(VAddr address, u64 size) const;
+
/// Gets the base address of the heap region.
VAddr GetHeapRegionBaseAddress() const;
@@ -462,6 +469,9 @@ public:
/// Gets the total size of the heap region in bytes.
u64 GetHeapRegionSize() const;
+ /// Determines whether or not the specified range is within the heap region.
+ bool IsWithinHeapRegion(VAddr address, u64 size) const;
+
/// Gets the base address of the map region.
VAddr GetMapRegionBaseAddress() const;
@@ -471,6 +481,9 @@ public:
/// Gets the total size of the map region in bytes.
u64 GetMapRegionSize() const;
+ /// Determines whether or not the specified range is within the map region.
+ bool IsWithinMapRegion(VAddr address, u64 size) const;
+
/// Gets the base address of the new map region.
VAddr GetNewMapRegionBaseAddress() const;
@@ -480,6 +493,9 @@ public:
/// Gets the total size of the new map region in bytes.
u64 GetNewMapRegionSize() const;
+ /// Determines whether or not the given address range is within the new map region
+ bool IsWithinNewMapRegion(VAddr address, u64 size) const;
+
/// Gets the base address of the TLS IO region.
VAddr GetTLSIORegionBaseAddress() const;
@@ -489,9 +505,12 @@ public:
/// Gets the total size of the TLS IO region in bytes.
u64 GetTLSIORegionSize() const;
+ /// Determines if the given address range is within the TLS IO region.
+ bool IsWithinTLSIORegion(VAddr address, u64 size) const;
+
/// Each VMManager has its own page table, which is set as the main one when the owning process
/// is scheduled.
- Memory::PageTable page_table;
+ Common::PageTable page_table{Memory::PAGE_BITS};
private:
using VMAIter = VMAMap::iterator;