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