1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     os_Thread.cpp
4 
5   Copyright (C)2009-2012 Nintendo Co., Ltd.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Rev: 50238 $
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/os/os_ErrorHandlerSelect.h>
24 #include <nn/os/os_Private.h>
25 #include <rt_fp.h>
26 
27 #include "os_Limits.h"
28 
29 //---------------------------------------------------------------------------
30 
31 using namespace nn;
32 using namespace fnd;
33 using namespace nn::svc;
34 using namespace nn::os;
35 
36 namespace nn{ namespace os{
37 
38     namespace
39     {
40         ThreadLocalRegion* s_pTlr = NULL;
41 
42         struct StackBufferAdapter
43         {
44             uptr stackBottom;
StackBufferAdapternn::os::__anon7a8676cc0111::StackBufferAdapter45             StackBufferAdapter(uptr stackBottom) : stackBottom(stackBottom) {}
GetStackBottomnn::os::__anon7a8676cc0111::StackBufferAdapter46             uptr GetStackBottom() const { return stackBottom; }
47         };
48     }
49 
50     namespace detail
51     {
ConvertSvcToLibraryPriority(s32 svc)52         s32 ConvertSvcToLibraryPriority(s32 svc)
53         {
54             if( svc >= SVC_USER_THREAD_PRIORITY_HIGHEST )
55             {
56                 const s32 offset = svc - SVC_USER_THREAD_PRIORITY_HIGHEST;
57                 return offset;
58             }
59             else if( svc >= SVC_LIBRARY_THREAD_PRIORITY_HIGHEST )
60             {
61                 const s32 offset = svc - SVC_LIBRARY_THREAD_PRIORITY_HIGHEST;
62                 return LIBRARY_THREAD_PRIORITY_BASE + offset;
63             }
64             else
65             {
66                 return PRIVILEGED_THREAD_PRIORITY_BASE + svc;
67             }
68         }
ConvertLibraryToSvcPriority(s32 lib)69         s32 ConvertLibraryToSvcPriority(s32 lib)
70         {
71             if ( (USER_THREAD_PRIORITY_HIGHEST <= lib)
72                                               && (lib <= USER_THREAD_PRIORITY_LOWEST) )
73             {
74                 return SVC_USER_THREAD_PRIORITY_HIGHEST + lib;
75             }
76             if ( (LIBRARY_THREAD_PRIORITY_HIGHEST <= lib)
77                                                  && (lib <= LIBRARY_THREAD_PRIORITY_LOWEST) )
78             {
79                 const s32 offset = lib - LIBRARY_THREAD_PRIORITY_HIGHEST;
80                 return SVC_LIBRARY_THREAD_PRIORITY_HIGHEST + offset;
81             }
82             if ( (PRIVILEGED_THREAD_PRIORITY_HIGHEST <= lib)
83                                                     && (lib <= PRIVILEGED_THREAD_PRIORITY_LOWEST) )
84             {
85                 const s32 offset = lib - PRIVILEGED_THREAD_PRIORITY_HIGHEST;
86                 return SVC_PRIVILEGED_THREAD_PRIORITY_HIGHEST + offset;
87             }
88             return -1;
89         }
InitializeThreadEnvrionment()90         void InitializeThreadEnvrionment()
91         {
92 #ifdef NN_PROCESSOR_ARM_V6
93     #if ! defined(NN_HARDWARE_CTR_LEGACY)
94             ThreadLocalStorage::ClearAllSlots();
95             CTR::SetupThreadCppExceptionEnvironment();
96     #endif
97             _fp_init();
98 #endif
99         }
SaveThreadLocalRegionAddress()100         void SaveThreadLocalRegionAddress()
101         {
102             NN_TASSERT_( s_pTlr == NULL );
103             s_pTlr = GetThreadLocalRegion();
104         }
GetMainThreadThreadLocalRegion()105         ThreadLocalRegion* GetMainThreadThreadLocalRegion()
106         {
107             return s_pTlr;
108         }
109 
110     }
111 
112 
113 
114 
115 /* Please see man pages for details
116 
117  */
118 struct Thread::FunctionInfo
119 {
120     void (*destroy)(void* p);                    //
121     void (*invoke)(ThreadFunc f, const void* p); //
122     void (*f)(uptr);                             //
123     void* p;                                     //
124     void* pAutoStackBuffer;
125 
126     // Call handler.
Invokenn::os::Thread::FunctionInfo127     void Invoke()
128     {
129         invoke(f, p);
130     };
131 
132     // End processing after handler call.
Destroynn::os::Thread::FunctionInfo133     void Destroy()
134     {
135         destroy(p);
136     }
137 };
138 
139 
140 
141 Thread Thread::s_MainThread = Thread::InitializeAsCurrentTag();
142 Thread::AutoStackManager*    Thread::s_pAutoStackManager = NULL;
143 
144 
145 
146 
147 
148 // When initializing/ending a thread, denote whatever processes may be necessary here.
149 // TLS may be necessary for transferring data.
150 
OnThreadStart()151 inline void Thread::OnThreadStart()
152 {
153     nn::os::detail::InitializeThreadEnvrionment();
154 }
155 
OnThreadExit()156 inline void Thread::OnThreadExit()
157 {
158     // Nop
159 }
160 
NoParameterFunc(void (* f)())161 void Thread::NoParameterFunc(void (*f)())
162 {
163     f();
164 }
165 
166 #include <nn/hw/ARM/code32.h>
CallDestructorAndExit(void * pStackBottom NN_IS_UNUSED_VAR)167 asm void Thread::CallDestructorAndExit(void* pStackBottom NN_IS_UNUSED_VAR)
168 {
169     // r0: pStackBottom
170 
171     // Call stack destructor.
172     // (s_pAutoStackManager->*f)(pStackBottom, false);
173 
174     mov     r2, #0                                  // Put false as the second argument
175     mov     r1, r0                                  // Put pStackBottom as the first argument
176 
177     ldr     r0, =__cpp(&s_pAutoStackManager)        // Get the address of s_pAutoStackManager
178     ldr     r0, [r0, #0]                            // Load s_pAutoStackManager
179 
180     ldr     r3, [r0, #0]                                                    // Load the address of vtable
181     ldr     r3, [r3, #__vcall_offsetof_vfunc(AutoStackManager,Destruct)]    // Get virtual function address from vtable
182 
183     ldr     lr, =__cpp(nn::svc::ExitThread)         // Put return location in ExitThread
184     bx      r3
185 
186     LTORG
187 }
188 #include <nn/hw/ARM/codereset.h>
189 
190 // Thread process that runs from different context. Call handler.
ThreadStart(uptr p)191 void Thread::ThreadStart(uptr p)
192 {
193     FunctionInfo& info = *reinterpret_cast<FunctionInfo*>(p);
194 
195     OnThreadStart();
196     info.Invoke();
197     info.Destroy();
198     OnThreadExit();
199 
200     if( info.pAutoStackBuffer != NULL )
201     {
202         CallDestructorAndExit(info.pAutoStackBuffer);
203     }
204 
205     nn::svc::ExitThread();
206 
207     NN_TASSERT_(0);
208 }
209 
TryInitializeAndStartImpl(const TypeInfo & typeInfo,ThreadFunc f,const void * p,uptr stackBottom,s32 priority,s32 coreNo,bool isAutoStack)210 Result Thread::TryInitializeAndStartImpl(
211         const TypeInfo& typeInfo,
212         ThreadFunc      f,
213         const void*     p,
214         uptr            stackBottom,
215         s32             priority,
216         s32             coreNo,
217         bool            isAutoStack )
218 {
219     return TryInitializeAndStartImpl(
220             typeInfo,
221             f,
222             p,
223             stackBottom,
224             priority,
225             coreNo,
226             (isAutoStack ? stackBottom: NULL) );
227 }
228 
TryInitializeAndStartImpl(const TypeInfo & typeInfo,ThreadFunc f,const void * p,uptr stackBottom,s32 priority,s32 coreNo,uptr autoStackBuffer)229 Result Thread::TryInitializeAndStartImpl(
230         const TypeInfo& typeInfo,
231         ThreadFunc      f,
232         const void*     p,
233         uptr            stackBottom,
234         s32             priority,
235         s32             coreNo,
236         uptr            autoStackBuffer )
237 {
238     // Thread::Start -> CreateThread --- Different context from here ---> ThreadStart -> Called in the order of f
239 
240     // The stack requires 8-byte alignment.
241     // TODO: Strictly speaking, the files must be separated because this part is ARM-dependent.
242     uptr stack = stackBottom;
243 
244     // Copy the parameter that is passed to the handler to the stack region.
245     stack -= typeInfo.size;
246     stack &= 0xfffffff8;
247     void* obj = reinterpret_cast<void*>(stack);
248     typeInfo.copy(p, obj);
249 
250     // Write information that is passed to the thread to the stack region.
251     stack -= sizeof(FunctionInfo);
252     stack &= 0xfffffff8;
253     FunctionInfo& info = *reinterpret_cast<FunctionInfo*>(stack);
254     info.destroy = typeInfo.destroy;
255     info.invoke = typeInfo.invoke;
256     info.f = f;
257     info.p = obj;
258     info.pAutoStackBuffer = reinterpret_cast<void*>(autoStackBuffer);
259 
260     Handle handle;
261     NN_UTIL_RETURN_IF_FAILED(
262         nn::svc::CreateThread(
263             &handle,
264             ThreadStart,
265             stack,
266             stack,
267             os::detail::ConvertLibraryToSvcPriority(priority),
268             coreNo) );
269 
270     this->SetHandle(handle);
271     this->m_CanFinalize = false;
272     this->m_UsingAutoStack = false;
273     return ResultSuccess();
274 }
275 
276 
SleepImpl(nn::fnd::TimeSpan span)277 void Thread::SleepImpl(nn::fnd::TimeSpan span)
278 {
279     if( span.GetNanoSeconds() >= SLEEP_SPIN_THRESHOLD )
280     {
281         nn::svc::SleepThread(span.GetNanoSeconds());
282     }
283     else
284     {
285         SpinWaitCpuCycles(Tick(span));
286     }
287 }
288 
289 
290 #if NN_PLATFORM_HAS_MMU
PreStartUsingAutoStack(size_t stackSize)291 uptr Thread::PreStartUsingAutoStack(size_t stackSize)
292 {
293     NN_NULL_TASSERT_(s_pAutoStackManager);
294 
295     void* pStackBottom = s_pAutoStackManager->Construct(stackSize);
296     NN_POINTER_TASSERT_(pStackBottom);
297     NN_ALIGN_TASSERT_(pStackBottom, 8);
298 
299     return reinterpret_cast<uptr>(pStackBottom);
300 }
PostStartUsingAutoStack(Result result,uptr stackBottom)301 Result Thread::PostStartUsingAutoStack(Result result, uptr stackBottom)
302 {
303     if (result.IsFailure())
304     {
305         s_pAutoStackManager->Destruct(reinterpret_cast<void*>(stackBottom), true);
306         return result;
307     }
308 
309     this->m_UsingAutoStack = true;
310     return ResultSuccess();
311 }
TryInitializeAndStartImplUsingAutoStack(const TypeInfo & typeInfo,ThreadFunc f,const void * p,size_t stackSize,s32 priority,s32 coreNo)312 Result Thread::TryInitializeAndStartImplUsingAutoStack(const TypeInfo& typeInfo, ThreadFunc f, const void* p, size_t stackSize, s32 priority, s32 coreNo)
313 {
314     // Thread::Start -> CreateThread --- Different context from here ---> ThreadStart -> Called in the order of f
315 
316     const uptr stackBottom = PreStartUsingAutoStack(stackSize);
317     Result result = TryInitializeAndStartImpl(typeInfo, f, p, stackBottom, priority, coreNo, true);
318     return PostStartUsingAutoStack(result, stackBottom);
319 }
320 #endif  // if NN_PLATFORM_HAS_MMU
321 
322 // For the main thread
Thread(const Thread::InitializeAsCurrentTag &)323 Thread::Thread(const Thread::InitializeAsCurrentTag&)
324 {
325     Handle handle;
326     NN_OS_ERROR_IF_FAILED(nn::svc::DuplicateHandle(&handle, PSEUDO_HANDLE_CURRENT_THREAD));
327     this->SetHandle(handle);
328     this->m_CanFinalize = false;
329     this->m_UsingAutoStack = false;
330 }
331 
SetAutoStackManager(AutoStackManager * pManager)332 void Thread::SetAutoStackManager(AutoStackManager* pManager)
333 {
334     s_pAutoStackManager = pManager;
335 }
336 
FinalizeImpl()337 void Thread::FinalizeImpl()
338 {
339     if (!m_CanFinalize)
340     {
341         NN_TASSERTMSG_(m_CanFinalize, "Thread should be Joined or Detached before being Finalized.");
342         this->WaitOne();
343         this->m_CanFinalize = true;
344     }
345 }
346 
347 #if NN_PLATFORM_HAS_MMU
GetCurrentCount()348 s32  Thread::GetCurrentCount()
349 {
350     return os::detail::GetLimitCurrentCount(LIMITABLE_RESOURCE_MAX_THREAD);
351 }
352 
GetMaxCount()353 s32  Thread::GetMaxCount()
354 {
355     return os::detail::GetLimitMaxCount(LIMITABLE_RESOURCE_MAX_THREAD);
356 }
357 #endif  // if NN_PLATFORM_HAS_MMU
358 
359 }} // namespace nn::os
360 
361 
362 
363 
364 #include <new>
365 using namespace nn::os;
366 
367 extern "C" {
368 
nnosThreadInitializeAndStart(nnosThread * p,void (* f)(uptr),uptr param,uptr stackBottom,s32 priority,s32 coreNo)369 void nnosThreadInitializeAndStart(nnosThread* p, void (*f)(uptr), uptr param, uptr stackBottom, s32 priority, s32 coreNo)
370 {
371     Thread* pThread = new (p) Thread();
372     StackBufferAdapter stack(stackBottom);
373     pThread->Start(f, param, stack, priority, coreNo);
374 }
375 
nnosThreadTryInitializeAndStart(nnosThread * p,void (* f)(uptr),uptr param,uptr stackBottom,s32 priority,s32 coreNo)376 bool nnosThreadTryInitializeAndStart(nnosThread* p, void (*f)(uptr), uptr param, uptr stackBottom, s32 priority, s32 coreNo)
377 {
378     Thread* pThread = new (p) Thread();
379     StackBufferAdapter stack(stackBottom);
380     Result result = pThread->TryStart(f, param, stack, priority, coreNo);
381     return result.IsSuccess();
382 }
383 
nnosThreadFinalize(nnosThread * p)384 void nnosThreadFinalize(nnosThread* p)
385 {
386     Thread* pThread = reinterpret_cast<Thread*>(p);
387     pThread->~Thread();
388 }
389 
nnosThreadJoin(nnosThread * p)390 void nnosThreadJoin(nnosThread* p)
391 {
392     Thread* pThread = reinterpret_cast<Thread*>(p);
393     pThread->Join();
394 }
395 
nnosThreadSleep(s64 nanoSeconds)396 void nnosThreadSleep(s64 nanoSeconds)
397 {
398     Thread::Sleep(TimeSpan::FromNanoSeconds(nanoSeconds));
399 }
400 
nnosThreadYield()401 void nnosThreadYield()
402 {
403     Thread::Yield();
404 }
405 
nnosThreadGetCurrentId(void)406 bit32 nnosThreadGetCurrentId(void)
407 {
408     return Thread::GetCurrentId();
409 }
410 
nnosThreadGetPriority(const nnosThread * p)411 s32 nnosThreadGetPriority(const nnosThread* p)
412 {
413     const Thread* pThread = reinterpret_cast<const Thread*>(p);
414     return pThread->GetPriority();
415 }
416 
nnosThreadGetCurrentPriority()417 s32 nnosThreadGetCurrentPriority()
418 {
419     return Thread::GetCurrentPriority();
420 }
421 
nnosThreadChangePriority(nnosThread * p,s32 priority)422 void nnosThreadChangePriority(nnosThread* p, s32 priority)
423 {
424     Thread* pThread = reinterpret_cast<Thread*>(p);
425     return pThread->ChangePriority(priority);
426 }
427 
nnosThreadChangeCurrentPriority(s32 priority)428 void nnosThreadChangeCurrentPriority(s32 priority)
429 {
430     Thread::ChangeCurrentPriority(priority);
431 }
432 
433 // Get/set the thread affinity
434 //void nnosThreadGetAffinityMask(const nnosThread* p, bit8* pAffinityMask, s32 numProcessor)
435 //{
436 //    const Thread* pThread = reinterpret_cast<const Thread*>(p);
437 //    pThread->GetAffinityMask(pAffinityMask, numProcessor);
438 //}
439 //
440 //void nnosThreadGetCurrentAffinityMask(bit8* pAffinityMask, s32 numProcessor)
441 //{
442 //    Thread::GetCurrentAffinityMask(pAffinityMask, numProcessor);
443 //}
444 //
445 //void nnosThreadGetDefaultAffinityMask(bit8* pAffinityMask, s32 numProcessor)
446 //{
447 //    Thread::GetDefaultAffinityMask(pAffinityMask, numProcessor);
448 //}
449 //
450 //void nnosThreadChangeAffinityMask(nnosThread* p, const bit8* pAffinityMask, s32 numProcessor)
451 //{
452 //    Thread* pThread = reinterpret_cast<Thread*>(p);
453 //    pThread->ChangeAffinityMask(pAffinityMask, numProcessor);
454 //}
455 //
456 //void nnosThreadChangeCurrentAffinityMask(const bit8* pAffinityMask, s32 numProcessor)
457 //{
458 //    Thread::ChangeCurrentAffinityMask(pAffinityMask, numProcessor);
459 //}
460 //
461 //void nnosThreadSetDefaultAffinityMask(const bit8* pAffinityMask, s32 numProcessor)
462 //{
463 //    Thread::SetDefaultAffinityMask(pAffinityMask, numProcessor);
464 //}
465 
466 // Get/set IdealProcessor
nnosThreadGetIdealProcessor(const nnosThread * p)467 s32 nnosThreadGetIdealProcessor(const nnosThread* p)
468 {
469     const Thread* pThread = reinterpret_cast<const Thread*>(p);
470     return pThread->GetIdealProcessor();
471 }
472 
473 //s32 nnosThreadGetCurrentIdealProcessor()
474 //{
475 //    return Thread::GetCurrentIdealProcessor();
476 //}
477 
nnosThreadGetDefaultIdealProcessor()478 s32 nnosThreadGetDefaultIdealProcessor()
479 {
480     return Thread::GetDefaultIdealProcessor();
481 }
482 
483 //void nnosThreadChangeIdealProcessor(nnosThread* p, s32 coreNo)
484 //{
485 //    Thread* pThread = reinterpret_cast<Thread*>(p);
486 //    pThread->ChangeIdealProcessor(coreNo);
487 //}
488 //
489 //void nnosThreadChangeCurrentIdealProcessor(s32 coreNo)
490 //{
491 //    Thread::ChangeCurrentIdealProcessor(coreNo);
492 //}
493 //
494 //void nnosThreadSetDefaultIdealProcessor(s32 coreNo)
495 //{
496 //    Thread::SetDefaultIdealProcessor(coreNo);
497 //}
498 
nnosThreadGetCurrentProcessorNumber()499 s32 nnosThreadGetCurrentProcessorNumber()
500 {
501     return Thread::GetCurrentProcessorNumber();
502 }
503 
nnosThreadGetId(nnosThread * p)504 bit32 nnosThreadGetId(nnosThread* p)
505 {
506     Thread* pThread = reinterpret_cast<Thread*>(p);
507     return pThread->GetId();
508 }
509 
nnosThreadIsAlive(nnosThread * p)510 bool nnosThreadIsAlive(nnosThread* p)
511 {
512     Thread* pThread = reinterpret_cast<Thread*>(p);
513     return pThread->IsAlive();
514 }
515 
nnosThreadGetMainThread(void)516 nnosThread* nnosThreadGetMainThread(void)
517 {
518     return reinterpret_cast<nnosThread*>(&nn::os::Thread::GetMainThread());
519 }
520 
521 }
522