summaryrefslogtreecommitdiff
path: root/src/core/hle/kernel/kernel.cpp
diff options
context:
space:
mode:
authorGravatar Lioncash2018-08-28 12:30:33 -0400
committerGravatar Lioncash2018-08-28 22:31:51 -0400
commit0cbcd6ec9aeeafc298fe2e6e4ac10d68bb7267c5 (patch)
tree2d7bb143d490c3984bff6deda426b818bf27d552 /src/core/hle/kernel/kernel.cpp
parentMerge pull request #1193 from lioncash/priv (diff)
downloadyuzu-0cbcd6ec9aeeafc298fe2e6e4ac10d68bb7267c5.tar.gz
yuzu-0cbcd6ec9aeeafc298fe2e6e4ac10d68bb7267c5.tar.xz
yuzu-0cbcd6ec9aeeafc298fe2e6e4ac10d68bb7267c5.zip
kernel: Eliminate kernel global state
As means to pave the way for getting rid of global state within core, This eliminates kernel global state by removing all globals. Instead this introduces a KernelCore class which acts as a kernel instance. This instance lives in the System class, which keeps its lifetime contained to the lifetime of the System class. This also forces the kernel types to actually interact with the main kernel instance itself instead of having transient kernel state placed all over several translation units, keeping everything together. It also has a nice consequence of making dependencies much more explicit. This also makes our initialization a tad bit more correct. Previously we were creating a kernel process before the actual kernel was initialized, which doesn't really make much sense. The KernelCore class itself follows the PImpl idiom, which allows keeping all the implementation details sealed away from everything else, which forces the use of the exposed API and allows us to avoid any unnecessary inclusions within the main kernel header.
Diffstat (limited to 'src/core/hle/kernel/kernel.cpp')
-rw-r--r--src/core/hle/kernel/kernel.cpp283
1 files changed, 268 insertions, 15 deletions
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 8c19e86d3..615d7901a 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -2,38 +2,291 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <array>
6#include <atomic>
7#include <memory>
8#include <mutex>
9#include <utility>
10
11#include "common/assert.h"
12#include "common/logging/log.h"
13
14#include "core/core.h"
15#include "core/core_timing.h"
5#include "core/hle/kernel/handle_table.h" 16#include "core/hle/kernel/handle_table.h"
6#include "core/hle/kernel/kernel.h" 17#include "core/hle/kernel/kernel.h"
7#include "core/hle/kernel/process.h" 18#include "core/hle/kernel/process.h"
8#include "core/hle/kernel/resource_limit.h" 19#include "core/hle/kernel/resource_limit.h"
9#include "core/hle/kernel/thread.h" 20#include "core/hle/kernel/thread.h"
10#include "core/hle/kernel/timer.h" 21#include "core/hle/kernel/timer.h"
22#include "core/hle/lock.h"
23#include "core/hle/result.h"
11 24
12namespace Kernel { 25namespace Kernel {
13 26
14std::atomic<u32> Object::next_object_id{0}; 27/**
28 * Callback that will wake up the thread it was scheduled for
29 * @param thread_handle The handle of the thread that's been awoken
30 * @param cycles_late The number of CPU cycles that have passed since the desired wakeup time
31 */
32static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_late) {
33 const auto proper_handle = static_cast<Handle>(thread_handle);
34 auto& system = Core::System::GetInstance();
35
36 // Lock the global kernel mutex when we enter the kernel HLE.
37 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
38
39 SharedPtr<Thread> thread =
40 system.Kernel().RetrieveThreadFromWakeupCallbackHandleTable(proper_handle);
41 if (thread == nullptr) {
42 LOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", proper_handle);
43 return;
44 }
45
46 bool resume = true;
47
48 if (thread->status == ThreadStatus::WaitSynchAny ||
49 thread->status == ThreadStatus::WaitSynchAll ||
50 thread->status == ThreadStatus::WaitHLEEvent) {
51 // Remove the thread from each of its waiting objects' waitlists
52 for (auto& object : thread->wait_objects) {
53 object->RemoveWaitingThread(thread.get());
54 }
55 thread->wait_objects.clear();
56
57 // Invoke the wakeup callback before clearing the wait objects
58 if (thread->wakeup_callback) {
59 resume = thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr, 0);
60 }
61 }
62
63 if (thread->mutex_wait_address != 0 || thread->condvar_wait_address != 0 ||
64 thread->wait_handle) {
65 ASSERT(thread->status == ThreadStatus::WaitMutex);
66 thread->mutex_wait_address = 0;
67 thread->condvar_wait_address = 0;
68 thread->wait_handle = 0;
69
70 auto lock_owner = thread->lock_owner;
71 // Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance
72 // and don't have a lock owner unless SignalProcessWideKey was called first and the thread
73 // wasn't awakened due to the mutex already being acquired.
74 if (lock_owner) {
75 lock_owner->RemoveMutexWaiter(thread);
76 }
77 }
78
79 if (thread->arb_wait_address != 0) {
80 ASSERT(thread->status == ThreadStatus::WaitArb);
81 thread->arb_wait_address = 0;
82 }
83
84 if (resume) {
85 thread->ResumeFromWait();
86 }
87}
88
89/// The timer callback event, called when a timer is fired
90static void TimerCallback(u64 timer_handle, int cycles_late) {
91 const auto proper_handle = static_cast<Handle>(timer_handle);
92 auto& system = Core::System::GetInstance();
93 SharedPtr<Timer> timer = system.Kernel().RetrieveTimerFromCallbackHandleTable(proper_handle);
94
95 if (timer == nullptr) {
96 LOG_CRITICAL(Kernel, "Callback fired for invalid timer {:016X}", timer_handle);
97 return;
98 }
99
100 timer->Signal(cycles_late);
101}
15 102
16/// Initialize the kernel 103struct KernelCore::Impl {
17void Init() { 104 void Initialize(KernelCore& kernel) {
18 Kernel::ResourceLimitsInit(); 105 Shutdown();
19 Kernel::ThreadingInit();
20 Kernel::TimersInit();
21 106
22 Object::next_object_id = 0; 107 InitializeResourceLimits(kernel);
108 InitializeThreads();
109 InitializeTimers();
110 }
111
112 void Shutdown() {
113 next_object_id = 0;
114 next_process_id = 10;
115 next_thread_id = 1;
116
117 process_list.clear();
118
119 handle_table.Clear();
120 resource_limits.fill(nullptr);
121
122 thread_wakeup_callback_handle_table.Clear();
123 thread_wakeup_event_type = nullptr;
124
125 timer_callback_handle_table.Clear();
126 timer_callback_event_type = nullptr;
127 }
128
129 void InitializeResourceLimits(KernelCore& kernel) {
130 // Create the four resource limits that the system uses
131 // Create the APPLICATION resource limit
132 SharedPtr<ResourceLimit> resource_limit = ResourceLimit::Create(kernel, "Applications");
133 resource_limit->max_priority = 0x18;
134 resource_limit->max_commit = 0x4000000;
135 resource_limit->max_threads = 0x20;
136 resource_limit->max_events = 0x20;
137 resource_limit->max_mutexes = 0x20;
138 resource_limit->max_semaphores = 0x8;
139 resource_limit->max_timers = 0x8;
140 resource_limit->max_shared_mems = 0x10;
141 resource_limit->max_address_arbiters = 0x2;
142 resource_limit->max_cpu_time = 0x1E;
143 resource_limits[static_cast<u8>(ResourceLimitCategory::APPLICATION)] = resource_limit;
144
145 // Create the SYS_APPLET resource limit
146 resource_limit = ResourceLimit::Create(kernel, "System Applets");
147 resource_limit->max_priority = 0x4;
148 resource_limit->max_commit = 0x5E00000;
149 resource_limit->max_threads = 0x1D;
150 resource_limit->max_events = 0xB;
151 resource_limit->max_mutexes = 0x8;
152 resource_limit->max_semaphores = 0x4;
153 resource_limit->max_timers = 0x4;
154 resource_limit->max_shared_mems = 0x8;
155 resource_limit->max_address_arbiters = 0x3;
156 resource_limit->max_cpu_time = 0x2710;
157 resource_limits[static_cast<u8>(ResourceLimitCategory::SYS_APPLET)] = resource_limit;
158
159 // Create the LIB_APPLET resource limit
160 resource_limit = ResourceLimit::Create(kernel, "Library Applets");
161 resource_limit->max_priority = 0x4;
162 resource_limit->max_commit = 0x600000;
163 resource_limit->max_threads = 0xE;
164 resource_limit->max_events = 0x8;
165 resource_limit->max_mutexes = 0x8;
166 resource_limit->max_semaphores = 0x4;
167 resource_limit->max_timers = 0x4;
168 resource_limit->max_shared_mems = 0x8;
169 resource_limit->max_address_arbiters = 0x1;
170 resource_limit->max_cpu_time = 0x2710;
171 resource_limits[static_cast<u8>(ResourceLimitCategory::LIB_APPLET)] = resource_limit;
172
173 // Create the OTHER resource limit
174 resource_limit = ResourceLimit::Create(kernel, "Others");
175 resource_limit->max_priority = 0x4;
176 resource_limit->max_commit = 0x2180000;
177 resource_limit->max_threads = 0xE1;
178 resource_limit->max_events = 0x108;
179 resource_limit->max_mutexes = 0x25;
180 resource_limit->max_semaphores = 0x43;
181 resource_limit->max_timers = 0x2C;
182 resource_limit->max_shared_mems = 0x1F;
183 resource_limit->max_address_arbiters = 0x2D;
184 resource_limit->max_cpu_time = 0x3E8;
185 resource_limits[static_cast<u8>(ResourceLimitCategory::OTHER)] = resource_limit;
186 }
187
188 void InitializeThreads() {
189 thread_wakeup_event_type =
190 CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
191 }
192
193 void InitializeTimers() {
194 timer_callback_handle_table.Clear();
195 timer_callback_event_type = CoreTiming::RegisterEvent("TimerCallback", TimerCallback);
196 }
197
198 std::atomic<u32> next_object_id{0};
23 // TODO(Subv): Start the process ids from 10 for now, as lower PIDs are 199 // TODO(Subv): Start the process ids from 10 for now, as lower PIDs are
24 // reserved for low-level services 200 // reserved for low-level services
25 Process::next_process_id = 10; 201 std::atomic<u32> next_process_id{10};
202 std::atomic<u32> next_thread_id{1};
203
204 // Lists all processes that exist in the current session.
205 std::vector<SharedPtr<Process>> process_list;
206
207 Kernel::HandleTable handle_table;
208 std::array<SharedPtr<ResourceLimit>, 4> resource_limits;
209
210 /// The event type of the generic timer callback event
211 CoreTiming::EventType* timer_callback_event_type = nullptr;
212 // TODO(yuriks): This can be removed if Timer objects are explicitly pooled in the future,
213 // allowing us to simply use a pool index or similar.
214 Kernel::HandleTable timer_callback_handle_table;
215
216 CoreTiming::EventType* thread_wakeup_event_type = nullptr;
217 // TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future,
218 // allowing us to simply use a pool index or similar.
219 Kernel::HandleTable thread_wakeup_callback_handle_table;
220};
221
222KernelCore::KernelCore() : impl{std::make_unique<Impl>()} {}
223KernelCore::~KernelCore() {
224 Shutdown();
225}
226
227void KernelCore::Initialize() {
228 impl->Initialize(*this);
229}
230
231void KernelCore::Shutdown() {
232 impl->Shutdown();
233}
234
235Kernel::HandleTable& KernelCore::HandleTable() {
236 return impl->handle_table;
237}
238
239const Kernel::HandleTable& KernelCore::HandleTable() const {
240 return impl->handle_table;
241}
242
243SharedPtr<ResourceLimit> KernelCore::ResourceLimitForCategory(
244 ResourceLimitCategory category) const {
245 return impl->resource_limits.at(static_cast<std::size_t>(category));
246}
247
248SharedPtr<Thread> KernelCore::RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const {
249 return impl->thread_wakeup_callback_handle_table.Get<Thread>(handle);
250}
251
252SharedPtr<Timer> KernelCore::RetrieveTimerFromCallbackHandleTable(Handle handle) const {
253 return impl->timer_callback_handle_table.Get<Timer>(handle);
254}
255
256void KernelCore::AppendNewProcess(SharedPtr<Process> process) {
257 impl->process_list.push_back(std::move(process));
258}
259
260u32 KernelCore::CreateNewObjectID() {
261 return impl->next_object_id++;
262}
263
264u32 KernelCore::CreateNewThreadID() {
265 return impl->next_thread_id++;
26} 266}
27 267
28/// Shutdown the kernel 268u32 KernelCore::CreateNewProcessID() {
29void Shutdown() { 269 return impl->next_process_id++;
30 // Free all kernel objects 270}
31 g_handle_table.Clear();
32 271
33 Kernel::ThreadingShutdown(); 272ResultVal<Handle> KernelCore::CreateTimerCallbackHandle(const SharedPtr<Timer>& timer) {
273 return impl->timer_callback_handle_table.Create(timer);
274}
275
276CoreTiming::EventType* KernelCore::ThreadWakeupCallbackEventType() const {
277 return impl->thread_wakeup_event_type;
278}
279
280CoreTiming::EventType* KernelCore::TimerCallbackEventType() const {
281 return impl->timer_callback_event_type;
282}
283
284Kernel::HandleTable& KernelCore::ThreadWakeupCallbackHandleTable() {
285 return impl->thread_wakeup_callback_handle_table;
286}
34 287
35 Kernel::TimersShutdown(); 288const Kernel::HandleTable& KernelCore::ThreadWakeupCallbackHandleTable() const {
36 Kernel::ResourceLimitsShutdown(); 289 return impl->thread_wakeup_callback_handle_table;
37} 290}
38 291
39} // namespace Kernel 292} // namespace Kernel