1 /*---------------------------------------------------------------------------*
2 Project: Horizon
3 File: os_Thread.cpp
4 Copyright (C)2009 Nintendo Co., Ltd. All rights reserved.
5 These coded instructions, statements, and computer programs contain
6 proprietary information of Nintendo of America Inc. and/or Nintendo
7 Company Ltd., and are protected by Federal copyright law. They may
8 not be disclosed to third parties or copied or duplicated in any form,
9 in whole or in part, without the prior written consent of Nintendo.
10 $Rev: 35648 $
11 *---------------------------------------------------------------------------
12
13
14 */
15
16 #include <nn/assert.h>
17 #include <nn/Handle.h>
18 #include <nn/svc/svc_Stub.h>
19 #include <nn/dbg.h>
20 #include <nn/os.h>
21 #include <nn/os/os_PrivatePriority.h>
22 #include <nn/os/CTR/os_CppException.h>
23 #include <nn/err.h>
24 #include <rt_fp.h>
25 //---------------------------------------------------------------------------
26
27 using namespace nn;
28 using namespace fnd;
29 using namespace nn::svc;
30 using namespace nn::os;
31
32 namespace nn{ namespace os{
33
34 namespace
35 {
36 struct StackBufferAdapter
37 {
38 uptr stackBottom;
StackBufferAdapternn::os::__anon3dfb261b0111::StackBufferAdapter39 StackBufferAdapter(uptr stackBottom) : stackBottom(stackBottom) {}
GetStackBottomnn::os::__anon3dfb261b0111::StackBufferAdapter40 uptr GetStackBottom() const { return stackBottom; }
41 };
42 }
43
44 namespace detail
45 {
ConvertSvcToLibraryPriority(s32 svc)46 s32 ConvertSvcToLibraryPriority(s32 svc)
47 {
48 if( svc >= SVC_USER_THREAD_PRIORITY_HIGHEST )
49 {
50 const s32 offset = svc - SVC_USER_THREAD_PRIORITY_HIGHEST;
51 return offset;
52 }
53 else if( svc >= SVC_LIBRARY_THREAD_PRIORITY_HIGHEST )
54 {
55 const s32 offset = svc - SVC_LIBRARY_THREAD_PRIORITY_HIGHEST;
56 return LIBRARY_THREAD_PRIORITY_BASE + offset;
57 }
58 else
59 {
60 return PRIVILEGED_THREAD_PRIORITY_BASE + svc;
61 }
62 }
ConvertLibraryToSvcPriority(s32 lib)63 s32 ConvertLibraryToSvcPriority(s32 lib)
64 {
65 if ( (USER_THREAD_PRIORITY_HIGHEST <= lib)
66 && (lib <= USER_THREAD_PRIORITY_LOWEST) )
67 {
68 return SVC_USER_THREAD_PRIORITY_HIGHEST + lib;
69 }
70 if ( (LIBRARY_THREAD_PRIORITY_HIGHEST <= lib)
71 && (lib <= LIBRARY_THREAD_PRIORITY_LOWEST) )
72 {
73 const s32 offset = lib - LIBRARY_THREAD_PRIORITY_HIGHEST;
74 return SVC_LIBRARY_THREAD_PRIORITY_HIGHEST + offset;
75 }
76 if ( (PRIVILEGED_THREAD_PRIORITY_HIGHEST <= lib)
77 && (lib <= PRIVILEGED_THREAD_PRIORITY_LOWEST) )
78 {
79 const s32 offset = lib - PRIVILEGED_THREAD_PRIORITY_HIGHEST;
80 return SVC_PRIVILEGED_THREAD_PRIORITY_HIGHEST + offset;
81 }
82 return -1;
83 }
InitializeThreadEnvrionment()84 void InitializeThreadEnvrionment()
85 {
86 #ifdef NN_PROCESSOR_ARM_V6
87 #if ! defined(NN_HARDWARE_CTR_LEGACY)
88 ThreadLocalStorage::ClearAllSlots();
89 CTR::SetupThreadCppExceptionEnvironment();
90 #endif
91 _fp_init();
92 #endif
93 }
94 }
95
96
97
98
99 /* Please see man pages for details
100
101 */
102 struct Thread::FunctionInfo
103 {
104 void (*destroy)(void* p); //
105 void (*invoke)(ThreadFunc f, const void* p); //
106 void (*f)(uptr); //
107 void* p; //
108 void* pStackBottom;
109 bool isAutoStack;
110 NN_PADDING3;
111
112 // Calls the handler.
Invokenn::os::Thread::FunctionInfo113 void Invoke()
114 {
115 invoke(f, p);
116 };
117
118 // Finalization process after calling the handler.
Destroynn::os::Thread::FunctionInfo119 void Destroy()
120 {
121 destroy(p);
122 }
123 };
124
125
126
127 Thread Thread::s_MainThread = Thread::InitializeAsCurrentTag();
128 Thread::AutoStackManager* Thread::s_pAutoStackManager = NULL;
129
130
131
132
133
134 // Write any require processes when initializing or finalizing threads.
135 // TLS may be required to transfer data.
136
OnThreadStart()137 inline void Thread::OnThreadStart()
138 {
139 nn::os::detail::InitializeThreadEnvrionment();
140 }
141
OnThreadExit()142 inline void Thread::OnThreadExit()
143 {
144 // Nop
145 }
146
NoParameterFunc(void (* f)())147 void Thread::NoParameterFunc(void (*f)())
148 {
149 f();
150 }
151
152 #include <nn/hw/ARM/code32.h>
CallDestructorAndExit(void * pStackBottom NN_IS_UNUSED_VAR)153 asm void Thread::CallDestructorAndExit(void* pStackBottom NN_IS_UNUSED_VAR)
154 {
155 // r0: pStackBottom
156
157 // Calls the stack destructor
158 // (s_pAutoStackManager->*f)(pStackBottom, false);
159
160 mov r2, #0 // Set false to the second argument
161 mov r1, r0 // Set pStackBottom to the first argument
162
163 ldr r0, =__cpp(&s_pAutoStackManager) // Gets the s_pAutoStackManager address
164 ldr r0, [r0, #0] // Loads s_pAutoStackManager
165
166 ldr r3, [r0, #0] // Loads the vtable address
167 ldr r3, [r3, #4] // Gets the virtual function address from vtable
168
169 ldr lr, =__cpp(nn::svc::ExitThread) // Set return destination to ExitThread
170 bx r3
171
172 LTORG
173 }
174 #include <nn/hw/ARM/codereset.h>
175
176 // Thread process executed from a different context. Calls the handler.
ThreadStart(uptr p)177 void Thread::ThreadStart(uptr p)
178 {
179 FunctionInfo& info = *reinterpret_cast<FunctionInfo*>(p);
180
181 OnThreadStart();
182 info.Invoke();
183 info.Destroy();
184 OnThreadExit();
185
186 if( info.isAutoStack )
187 {
188 CallDestructorAndExit(info.pStackBottom);
189 }
190
191 nn::svc::ExitThread();
192
193 NN_TASSERT_(0);
194 }
195
TryInitializeAndStartImpl(const TypeInfo & typeInfo,ThreadFunc f,const void * p,uptr stackBottom,s32 priority,s32 coreNo,bool isAutoStack)196 Result Thread::TryInitializeAndStartImpl(const TypeInfo& typeInfo, ThreadFunc f, const void* p, uptr stackBottom, s32 priority, s32 coreNo, bool isAutoStack)
197 {
198 // Called in the order: Thread::Start -> CreateThread ---Different context from here---> ThreadStart -> f
199
200 // stack require 8-byte alignment.
201 // TODO: Technically, this portion depends on ARM, so the file must be divided.
202 uptr stack = stackBottom;
203
204 // The parameter passed to the handler is copied to the stack region.
205 stack -= typeInfo.size;
206 stack &= 0xfffffff8;
207 void* obj = reinterpret_cast<void*>(stack);
208 typeInfo.copy(p, obj);
209
210 // Information passed to the thread is written to the stack region.
211 stack -= sizeof(FunctionInfo);
212 stack &= 0xfffffff8;
213 FunctionInfo& info = *reinterpret_cast<FunctionInfo*>(stack);
214 info.destroy = typeInfo.destroy;
215 info.invoke = typeInfo.invoke;
216 info.f = f;
217 info.p = obj;
218 info.isAutoStack = isAutoStack;
219 info.pStackBottom = reinterpret_cast<void*>(stackBottom);
220
221 Handle handle;
222 NN_UTIL_RETURN_IF_FAILED(
223 nn::svc::CreateThread(
224 &handle,
225 ThreadStart,
226 stack,
227 stack,
228 os::detail::ConvertLibraryToSvcPriority(priority),
229 coreNo) );
230
231 this->SetHandle(handle);
232 this->m_CanFinalize = false;
233 this->m_UsingAutoStack = false;
234 return ResultSuccess();
235 }
236
237
SleepImpl(nn::fnd::TimeSpan span)238 void Thread::SleepImpl(nn::fnd::TimeSpan span)
239 {
240 if( span.GetNanoSeconds() >= SLEEP_SPIN_THRESHOLD )
241 {
242 nn::svc::SleepThread(span.GetNanoSeconds());
243 }
244 else
245 {
246 SpinWaitCpuCycles(Tick(span));
247 }
248 }
249
250
251 #if NN_PLATFORM_HAS_MMU
TryInitializeAndStartImplUsingAutoStack(const TypeInfo & typeInfo,ThreadFunc f,const void * p,size_t stackSize,s32 priority,s32 coreNo)252 Result Thread::TryInitializeAndStartImplUsingAutoStack(const TypeInfo& typeInfo, ThreadFunc f, const void* p, size_t stackSize, s32 priority, s32 coreNo)
253 {
254 // Called in the order: Thread::Start -> CreateThread ---Different context from here---> ThreadStart -> f
255 NN_NULL_TASSERT_(s_pAutoStackManager);
256
257 void* pStackBottom = s_pAutoStackManager->Construct(stackSize);
258 uptr stack = reinterpret_cast<uptr>(pStackBottom);
259
260 Result result = TryInitializeAndStartImpl(typeInfo, f, p, stack, priority, coreNo, true);
261
262 if (result.IsFailure())
263 {
264 s_pAutoStackManager->Destruct(pStackBottom, true);
265 return result;
266 }
267
268 this->m_UsingAutoStack = true;
269 return ResultSuccess();
270 }
271 #endif // if NN_PLATFORM_HAS_MMU
272
273 // For the main thread
Thread(const Thread::InitializeAsCurrentTag &)274 Thread::Thread(const Thread::InitializeAsCurrentTag&)
275 {
276 Handle handle;
277 NN_ERR_THROW_FATAL(nn::svc::DuplicateHandle(&handle, PSEUDO_HANDLE_CURRENT_THREAD));
278 this->SetHandle(handle);
279 this->m_CanFinalize = false;
280 this->m_UsingAutoStack = false;
281 }
282
SetAutoStackManager(AutoStackManager * pManager)283 void Thread::SetAutoStackManager(AutoStackManager* pManager)
284 {
285 s_pAutoStackManager = pManager;
286 }
287
288 }} // namespace nn::os
289
290
291
292
293 #include <new>
294 using namespace nn::os;
295
296 extern "C" {
297
nnosThreadInitializeAndStart(nnosThread * p,void (* f)(uptr),uptr param,uptr stackBottom,s32 priority,s32 coreNo)298 void nnosThreadInitializeAndStart(nnosThread* p, void (*f)(uptr), uptr param, uptr stackBottom, s32 priority, s32 coreNo)
299 {
300 Thread* pThread = new (p) Thread();
301 StackBufferAdapter stack(stackBottom);
302 pThread->Start(f, param, stack, priority, coreNo);
303 }
304
nnosThreadTryInitializeAndStart(nnosThread * p,void (* f)(uptr),uptr param,uptr stackBottom,s32 priority,s32 coreNo)305 bool nnosThreadTryInitializeAndStart(nnosThread* p, void (*f)(uptr), uptr param, uptr stackBottom, s32 priority, s32 coreNo)
306 {
307 Thread* pThread = new (p) Thread();
308 StackBufferAdapter stack(stackBottom);
309 Result result = pThread->TryStart(f, param, stack, priority, coreNo);
310 return result.IsSuccess();
311 }
312
nnosThreadFinalize(nnosThread * p)313 void nnosThreadFinalize(nnosThread* p)
314 {
315 Thread* pThread = reinterpret_cast<Thread*>(p);
316 pThread->~Thread();
317 }
318
nnosThreadJoin(nnosThread * p)319 void nnosThreadJoin(nnosThread* p)
320 {
321 Thread* pThread = reinterpret_cast<Thread*>(p);
322 pThread->Join();
323 }
324
nnosThreadSleep(s64 nanoSeconds)325 void nnosThreadSleep(s64 nanoSeconds)
326 {
327 Thread::Sleep(TimeSpan::FromNanoSeconds(nanoSeconds));
328 }
329
nnosThreadYield()330 void nnosThreadYield()
331 {
332 Thread::Yield();
333 }
334
nnosThreadGetCurrentId(void)335 bit32 nnosThreadGetCurrentId(void)
336 {
337 return Thread::GetCurrentId();
338 }
339
nnosThreadGetPriority(const nnosThread * p)340 s32 nnosThreadGetPriority(const nnosThread* p)
341 {
342 const Thread* pThread = reinterpret_cast<const Thread*>(p);
343 return pThread->GetPriority();
344 }
345
nnosThreadGetCurrentPriority()346 s32 nnosThreadGetCurrentPriority()
347 {
348 return Thread::GetCurrentPriority();
349 }
350
nnosThreadChangePriority(nnosThread * p,s32 priority)351 void nnosThreadChangePriority(nnosThread* p, s32 priority)
352 {
353 Thread* pThread = reinterpret_cast<Thread*>(p);
354 return pThread->ChangePriority(priority);
355 }
356
nnosThreadChangeCurrentPriority(s32 priority)357 void nnosThreadChangeCurrentPriority(s32 priority)
358 {
359 Thread::ChangeCurrentPriority(priority);
360 }
361
362 // Sets and gets thread Affinity
nnosThreadGetAffinityMask(const nnosThread * p,bit8 * pAffinityMask,s32 numProcessor)363 void nnosThreadGetAffinityMask(const nnosThread* p, bit8* pAffinityMask, s32 numProcessor)
364 {
365 const Thread* pThread = reinterpret_cast<const Thread*>(p);
366 pThread->GetAffinityMask(pAffinityMask, numProcessor);
367 }
368
nnosThreadGetCurrentAffinityMask(bit8 * pAffinityMask,s32 numProcessor)369 void nnosThreadGetCurrentAffinityMask(bit8* pAffinityMask, s32 numProcessor)
370 {
371 Thread::GetCurrentAffinityMask(pAffinityMask, numProcessor);
372 }
373
nnosThreadGetDefaultAffinityMask(bit8 * pAffinityMask,s32 numProcessor)374 void nnosThreadGetDefaultAffinityMask(bit8* pAffinityMask, s32 numProcessor)
375 {
376 Thread::GetDefaultAffinityMask(pAffinityMask, numProcessor);
377 }
378
nnosThreadChangeAffinityMask(nnosThread * p,const bit8 * pAffinityMask,s32 numProcessor)379 void nnosThreadChangeAffinityMask(nnosThread* p, const bit8* pAffinityMask, s32 numProcessor)
380 {
381 Thread* pThread = reinterpret_cast<Thread*>(p);
382 pThread->ChangeAffinityMask(pAffinityMask, numProcessor);
383 }
384
nnosThreadChangeCurrentAffinityMask(const bit8 * pAffinityMask,s32 numProcessor)385 void nnosThreadChangeCurrentAffinityMask(const bit8* pAffinityMask, s32 numProcessor)
386 {
387 Thread::ChangeCurrentAffinityMask(pAffinityMask, numProcessor);
388 }
389
nnosThreadSetDefaultAffinityMask(const bit8 * pAffinityMask,s32 numProcessor)390 void nnosThreadSetDefaultAffinityMask(const bit8* pAffinityMask, s32 numProcessor)
391 {
392 Thread::SetDefaultAffinityMask(pAffinityMask, numProcessor);
393 }
394
395 // Sets and gets IdealProcessor
nnosThreadGetIdealProcessor(const nnosThread * p)396 s32 nnosThreadGetIdealProcessor(const nnosThread* p)
397 {
398 const Thread* pThread = reinterpret_cast<const Thread*>(p);
399 return pThread->GetIdealProcessor();
400 }
401
nnosThreadGetCurrentIdealProcessor()402 s32 nnosThreadGetCurrentIdealProcessor()
403 {
404 return Thread::GetCurrentIdealProcessor();
405 }
406
nnosThreadGetDefaultIdealProcessor()407 s32 nnosThreadGetDefaultIdealProcessor()
408 {
409 return Thread::GetDefaultIdealProcessor();
410 }
411
nnosThreadChangeIdealProcessor(nnosThread * p,s32 coreNo)412 void nnosThreadChangeIdealProcessor(nnosThread* p, s32 coreNo)
413 {
414 Thread* pThread = reinterpret_cast<Thread*>(p);
415 pThread->ChangeIdealProcessor(coreNo);
416 }
417
nnosThreadChangeCurrentIdealProcessor(s32 coreNo)418 void nnosThreadChangeCurrentIdealProcessor(s32 coreNo)
419 {
420 Thread::ChangeCurrentIdealProcessor(coreNo);
421 }
422
nnosThreadSetDefaultIdealProcessor(s32 coreNo)423 void nnosThreadSetDefaultIdealProcessor(s32 coreNo)
424 {
425 Thread::SetDefaultIdealProcessor(coreNo);
426 }
427
nnosThreadGetCurrentProcessorNumber()428 s32 nnosThreadGetCurrentProcessorNumber()
429 {
430 return Thread::GetCurrentProcessorNumber();
431 }
432
nnosThreadGetId(nnosThread * p)433 bit32 nnosThreadGetId(nnosThread* p)
434 {
435 Thread* pThread = reinterpret_cast<Thread*>(p);
436 return pThread->GetId();
437 }
438
nnosThreadIsAlive(nnosThread * p)439 bool nnosThreadIsAlive(nnosThread* p)
440 {
441 Thread* pThread = reinterpret_cast<Thread*>(p);
442 return pThread->IsAlive();
443 }
444
nnosThreadGetMainThread(void)445 nnosThread* nnosThreadGetMainThread(void)
446 {
447 return reinterpret_cast<nnosThread*>(&nn::os::Thread::GetMainThread());
448 }
449
450 }
451