1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     os_Thread.cpp
4 
5   Copyright (C)2009 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: 38846 $
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 <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::__anon682e02bf0111::StackBufferAdapter39             StackBufferAdapter(uptr stackBottom) : stackBottom(stackBottom) {}
GetStackBottomnn::os::__anon682e02bf0111::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_OS_ERROR_IF_FAILED(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 
FinalizeImpl()288 void Thread::FinalizeImpl()
289 {
290     if (!m_CanFinalize)
291     {
292         NN_TASSERTMSG_(m_CanFinalize, "Thread should be Joined or Detached before being Finalized.");
293         this->WaitOne();
294         this->m_CanFinalize = true;
295     }
296 }
297 
298 }} // namespace nn::os
299 
300 
301 
302 
303 #include <new>
304 using namespace nn::os;
305 
306 extern "C" {
307 
nnosThreadInitializeAndStart(nnosThread * p,void (* f)(uptr),uptr param,uptr stackBottom,s32 priority,s32 coreNo)308 void nnosThreadInitializeAndStart(nnosThread* p, void (*f)(uptr), uptr param, uptr stackBottom, s32 priority, s32 coreNo)
309 {
310     Thread* pThread = new (p) Thread();
311     StackBufferAdapter stack(stackBottom);
312     pThread->Start(f, param, stack, priority, coreNo);
313 }
314 
nnosThreadTryInitializeAndStart(nnosThread * p,void (* f)(uptr),uptr param,uptr stackBottom,s32 priority,s32 coreNo)315 bool nnosThreadTryInitializeAndStart(nnosThread* p, void (*f)(uptr), uptr param, uptr stackBottom, s32 priority, s32 coreNo)
316 {
317     Thread* pThread = new (p) Thread();
318     StackBufferAdapter stack(stackBottom);
319     Result result = pThread->TryStart(f, param, stack, priority, coreNo);
320     return result.IsSuccess();
321 }
322 
nnosThreadFinalize(nnosThread * p)323 void nnosThreadFinalize(nnosThread* p)
324 {
325     Thread* pThread = reinterpret_cast<Thread*>(p);
326     pThread->~Thread();
327 }
328 
nnosThreadJoin(nnosThread * p)329 void nnosThreadJoin(nnosThread* p)
330 {
331     Thread* pThread = reinterpret_cast<Thread*>(p);
332     pThread->Join();
333 }
334 
nnosThreadSleep(s64 nanoSeconds)335 void nnosThreadSleep(s64 nanoSeconds)
336 {
337     Thread::Sleep(TimeSpan::FromNanoSeconds(nanoSeconds));
338 }
339 
nnosThreadYield()340 void nnosThreadYield()
341 {
342     Thread::Yield();
343 }
344 
nnosThreadGetCurrentId(void)345 bit32 nnosThreadGetCurrentId(void)
346 {
347     return Thread::GetCurrentId();
348 }
349 
nnosThreadGetPriority(const nnosThread * p)350 s32 nnosThreadGetPriority(const nnosThread* p)
351 {
352     const Thread* pThread = reinterpret_cast<const Thread*>(p);
353     return pThread->GetPriority();
354 }
355 
nnosThreadGetCurrentPriority()356 s32 nnosThreadGetCurrentPriority()
357 {
358     return Thread::GetCurrentPriority();
359 }
360 
nnosThreadChangePriority(nnosThread * p,s32 priority)361 void nnosThreadChangePriority(nnosThread* p, s32 priority)
362 {
363     Thread* pThread = reinterpret_cast<Thread*>(p);
364     return pThread->ChangePriority(priority);
365 }
366 
nnosThreadChangeCurrentPriority(s32 priority)367 void nnosThreadChangeCurrentPriority(s32 priority)
368 {
369     Thread::ChangeCurrentPriority(priority);
370 }
371 
372 // Sets and gets thread Affinity
nnosThreadGetAffinityMask(const nnosThread * p,bit8 * pAffinityMask,s32 numProcessor)373 void nnosThreadGetAffinityMask(const nnosThread* p, bit8* pAffinityMask, s32 numProcessor)
374 {
375     const Thread* pThread = reinterpret_cast<const Thread*>(p);
376     pThread->GetAffinityMask(pAffinityMask, numProcessor);
377 }
378 
nnosThreadGetCurrentAffinityMask(bit8 * pAffinityMask,s32 numProcessor)379 void nnosThreadGetCurrentAffinityMask(bit8* pAffinityMask, s32 numProcessor)
380 {
381     Thread::GetCurrentAffinityMask(pAffinityMask, numProcessor);
382 }
383 
nnosThreadGetDefaultAffinityMask(bit8 * pAffinityMask,s32 numProcessor)384 void nnosThreadGetDefaultAffinityMask(bit8* pAffinityMask, s32 numProcessor)
385 {
386     Thread::GetDefaultAffinityMask(pAffinityMask, numProcessor);
387 }
388 
nnosThreadChangeAffinityMask(nnosThread * p,const bit8 * pAffinityMask,s32 numProcessor)389 void nnosThreadChangeAffinityMask(nnosThread* p, const bit8* pAffinityMask, s32 numProcessor)
390 {
391     Thread* pThread = reinterpret_cast<Thread*>(p);
392     pThread->ChangeAffinityMask(pAffinityMask, numProcessor);
393 }
394 
nnosThreadChangeCurrentAffinityMask(const bit8 * pAffinityMask,s32 numProcessor)395 void nnosThreadChangeCurrentAffinityMask(const bit8* pAffinityMask, s32 numProcessor)
396 {
397     Thread::ChangeCurrentAffinityMask(pAffinityMask, numProcessor);
398 }
399 
nnosThreadSetDefaultAffinityMask(const bit8 * pAffinityMask,s32 numProcessor)400 void nnosThreadSetDefaultAffinityMask(const bit8* pAffinityMask, s32 numProcessor)
401 {
402     Thread::SetDefaultAffinityMask(pAffinityMask, numProcessor);
403 }
404 
405 // Sets and gets IdealProcessor
nnosThreadGetIdealProcessor(const nnosThread * p)406 s32 nnosThreadGetIdealProcessor(const nnosThread* p)
407 {
408     const Thread* pThread = reinterpret_cast<const Thread*>(p);
409     return pThread->GetIdealProcessor();
410 }
411 
nnosThreadGetCurrentIdealProcessor()412 s32 nnosThreadGetCurrentIdealProcessor()
413 {
414     return Thread::GetCurrentIdealProcessor();
415 }
416 
nnosThreadGetDefaultIdealProcessor()417 s32 nnosThreadGetDefaultIdealProcessor()
418 {
419     return Thread::GetDefaultIdealProcessor();
420 }
421 
nnosThreadChangeIdealProcessor(nnosThread * p,s32 coreNo)422 void nnosThreadChangeIdealProcessor(nnosThread* p, s32 coreNo)
423 {
424     Thread* pThread = reinterpret_cast<Thread*>(p);
425     pThread->ChangeIdealProcessor(coreNo);
426 }
427 
nnosThreadChangeCurrentIdealProcessor(s32 coreNo)428 void nnosThreadChangeCurrentIdealProcessor(s32 coreNo)
429 {
430     Thread::ChangeCurrentIdealProcessor(coreNo);
431 }
432 
nnosThreadSetDefaultIdealProcessor(s32 coreNo)433 void nnosThreadSetDefaultIdealProcessor(s32 coreNo)
434 {
435     Thread::SetDefaultIdealProcessor(coreNo);
436 }
437 
nnosThreadGetCurrentProcessorNumber()438 s32 nnosThreadGetCurrentProcessorNumber()
439 {
440     return Thread::GetCurrentProcessorNumber();
441 }
442 
nnosThreadGetId(nnosThread * p)443 bit32 nnosThreadGetId(nnosThread* p)
444 {
445     Thread* pThread = reinterpret_cast<Thread*>(p);
446     return pThread->GetId();
447 }
448 
nnosThreadIsAlive(nnosThread * p)449 bool nnosThreadIsAlive(nnosThread* p)
450 {
451     Thread* pThread = reinterpret_cast<Thread*>(p);
452     return pThread->IsAlive();
453 }
454 
nnosThreadGetMainThread(void)455 nnosThread* nnosThreadGetMainThread(void)
456 {
457     return reinterpret_cast<nnosThread*>(&nn::os::Thread::GetMainThread());
458 }
459 
460 }
461