mirror of
https://github.com/switchbrew/libnx.git
synced 2025-08-07 00:29:23 +02:00
Merge pull request #18 from devkitPro/dka64-a5-updates
devkitA64 alpha 5 updates (locks, TLS, etc) {TEST BEFORE MERGE}
This commit is contained in:
commit
61ff5bd9a1
@ -1,7 +1,7 @@
|
|||||||
%rename link old_link
|
%rename link old_link
|
||||||
|
|
||||||
*link:
|
*link:
|
||||||
%(old_link) -T switch.ld%s -pie --gc-sections -z text
|
%(old_link) -T switch.ld%s -pie --gc-sections -z text -z nodynamic-undefined-weak
|
||||||
|
|
||||||
*startfile:
|
*startfile:
|
||||||
switch_crt0%O%s crti%O%s crtbegin%O%s
|
switch_crt0%O%s crti%O%s crtbegin%O%s
|
||||||
|
@ -32,7 +32,7 @@ INCLUDES := include
|
|||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# options for code generation
|
# options for code generation
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
ARCH := -march=armv8-a -fPIC
|
ARCH := -march=armv8-a -mtp=soft -fPIC
|
||||||
|
|
||||||
CFLAGS := -g -Wall -Werror \
|
CFLAGS := -g -Wall -Werror \
|
||||||
-ffunction-sections \
|
-ffunction-sections \
|
||||||
|
@ -1,16 +1,24 @@
|
|||||||
// Copyright 2017 plutoo
|
// Copyright 2017 plutoo
|
||||||
typedef struct {
|
#pragma once
|
||||||
u32 Tag;
|
#include <sys/lock.h>
|
||||||
} Mutex;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef _LOCK_T Mutex;
|
||||||
u32 Owner;
|
typedef _LOCK_RECURSIVE_T RMutex;
|
||||||
Mutex Lock;
|
|
||||||
size_t Count;
|
static inline void mutexInit(Mutex* m)
|
||||||
} RMutex;
|
{
|
||||||
|
*m = 0;
|
||||||
|
}
|
||||||
|
|
||||||
void mutexLock(Mutex* m);
|
void mutexLock(Mutex* m);
|
||||||
void mutexUnlock(Mutex* m);
|
void mutexUnlock(Mutex* m);
|
||||||
|
|
||||||
|
static inline void rmutexInit(RMutex* m)
|
||||||
|
{
|
||||||
|
m->lock = 0;
|
||||||
|
m->thread_tag = 0;
|
||||||
|
m->counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
void rmutexLock(RMutex* m);
|
void rmutexLock(RMutex* m);
|
||||||
void rmutexUnlock(RMutex* m);
|
void rmutexUnlock(RMutex* m);
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
Handle handle;
|
Handle handle;
|
||||||
ThreadFunc entry;
|
|
||||||
void* arg;
|
|
||||||
void* stack_mem;
|
void* stack_mem;
|
||||||
void* stack_mirror;
|
void* stack_mirror;
|
||||||
size_t stack_sz;
|
size_t stack_sz;
|
||||||
|
@ -29,4 +29,5 @@
|
|||||||
#define LIBNX_NOTFOUND 8
|
#define LIBNX_NOTFOUND 8
|
||||||
#define LIBNX_IOERROR 9
|
#define LIBNX_IOERROR 9
|
||||||
#define LIBNX_BADINPUT 10
|
#define LIBNX_BADINPUT 10
|
||||||
|
#define LIBNX_BADREENT 11
|
||||||
#define LIBNX_PARCEL_ERRBASE 100
|
#define LIBNX_PARCEL_ERRBASE 100
|
||||||
|
@ -1 +1 @@
|
|||||||
void fatalSimple(Result err);
|
__attribute__((noreturn)) void fatalSimple(Result err);
|
||||||
|
24
nx/source/internal.h
Normal file
24
nx/source/internal.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <sys/reent.h>
|
||||||
|
#include <switch.h>
|
||||||
|
|
||||||
|
#define THREADVARS_MAGIC 0x21545624 // !TV$
|
||||||
|
|
||||||
|
// This structure is exactly 0x20 bytes, if more is needed modify getThreadVars() below
|
||||||
|
typedef struct {
|
||||||
|
// Magic value used to check if the struct is initialized
|
||||||
|
u32 magic;
|
||||||
|
|
||||||
|
// Pointer to the current thread (if exists)
|
||||||
|
Thread* thread_ptr;
|
||||||
|
|
||||||
|
// Pointer to this thread's newlib state
|
||||||
|
struct _reent* reent;
|
||||||
|
|
||||||
|
// Pointer to this thread's thread-local segment
|
||||||
|
void* tls_tp; // !! Offset needs to be TLS+0x1F8 for __aarch64_read_tp !!
|
||||||
|
} ThreadVars;
|
||||||
|
|
||||||
|
static inline ThreadVars* getThreadVars(void) {
|
||||||
|
return (ThreadVars*)((u8*)armGetTls() + 0x1E0);
|
||||||
|
}
|
@ -11,7 +11,7 @@ static u32 _GetTag() {
|
|||||||
|
|
||||||
void mutexLock(Mutex* m) {
|
void mutexLock(Mutex* m) {
|
||||||
u32 self = _GetTag();
|
u32 self = _GetTag();
|
||||||
u32 cur = __sync_val_compare_and_swap(&m->Tag, 0, self);
|
u32 cur = __sync_val_compare_and_swap(m, 0, self);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
if (cur == 0) {
|
if (cur == 0) {
|
||||||
@ -26,44 +26,44 @@ void mutexLock(Mutex* m) {
|
|||||||
|
|
||||||
if (cur & HAS_LISTENERS) {
|
if (cur & HAS_LISTENERS) {
|
||||||
// The flag is already set, we can use the syscall.
|
// The flag is already set, we can use the syscall.
|
||||||
svcArbitrateLock(cur &~ HAS_LISTENERS, &m->Tag, self);
|
svcArbitrateLock(cur &~ HAS_LISTENERS, (u32*)m, self);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// The flag is not set, we need to set it.
|
// The flag is not set, we need to set it.
|
||||||
u32 old = __sync_val_compare_and_swap(&m->Tag, cur, cur | HAS_LISTENERS);
|
u32 old = __sync_val_compare_and_swap(m, cur, cur | HAS_LISTENERS);
|
||||||
|
|
||||||
if (old == cur) {
|
if (old == cur) {
|
||||||
// Flag was set successfully.
|
// Flag was set successfully.
|
||||||
svcArbitrateLock(cur &~ HAS_LISTENERS, &m->Tag, self);
|
svcArbitrateLock(cur &~ HAS_LISTENERS, (u32*)m, self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cur = __sync_val_compare_and_swap(&m->Tag, 0, self);
|
cur = __sync_val_compare_and_swap(m, 0, self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void mutexUnlock(Mutex* m) {
|
void mutexUnlock(Mutex* m) {
|
||||||
u32 self = _GetTag();
|
u32 self = _GetTag();
|
||||||
u32 old = __sync_val_compare_and_swap(&m->Tag, self, 0);
|
u32 old = __sync_val_compare_and_swap(m, self, 0);
|
||||||
|
|
||||||
if (old & HAS_LISTENERS) {
|
if (old & HAS_LISTENERS) {
|
||||||
svcArbitrateUnlock(&m->Tag);
|
svcArbitrateUnlock((u32*)m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void rmutexLock(RMutex* m) {
|
void rmutexLock(RMutex* m) {
|
||||||
if (m->Owner == _GetTag()) {
|
if (m->thread_tag == _GetTag()) {
|
||||||
m->Count++;
|
m->counter++;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mutexLock(&m->Lock);
|
mutexLock(&m->lock);
|
||||||
m->Owner = _GetTag();
|
m->thread_tag = _GetTag();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void rmutexUnlock(RMutex* m) {
|
void rmutexUnlock(RMutex* m) {
|
||||||
if (--m->Count == 0) {
|
if (--m->counter == 0) {
|
||||||
m->Owner = 0;
|
m->thread_tag = 0;
|
||||||
mutexUnlock(&m->Lock);
|
mutexUnlock(&m->lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,33 @@
|
|||||||
// Copyright 2017 plutoo
|
// Copyright 2017 plutoo
|
||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
|
#include "../internal.h"
|
||||||
|
|
||||||
static void _EntryWrap(Thread* t) {
|
extern const u8 __tdata_lma[];
|
||||||
t->entry(t->arg);
|
extern const u8 __tdata_lma_end[];
|
||||||
|
extern u8 __tls_start[];
|
||||||
|
extern u8 __tls_end[];
|
||||||
|
|
||||||
|
// Thread creation args; keep this struct's size 16-byte aligned
|
||||||
|
typedef struct {
|
||||||
|
Thread* t;
|
||||||
|
ThreadFunc entry;
|
||||||
|
void* arg;
|
||||||
|
struct _reent* reent;
|
||||||
|
void* tls;
|
||||||
|
void* padding;
|
||||||
|
} ThreadEntryArgs;
|
||||||
|
|
||||||
|
static void _EntryWrap(ThreadEntryArgs* args) {
|
||||||
|
// Initialize thread vars
|
||||||
|
ThreadVars* tv = getThreadVars();
|
||||||
|
tv->magic = THREADVARS_MAGIC;
|
||||||
|
tv->thread_ptr = args->t;
|
||||||
|
tv->reent = args->reent;
|
||||||
|
tv->tls_tp = (u8*)args->tls-2*sizeof(void*); // subtract size of Thread Control Block (TCB)
|
||||||
|
|
||||||
|
// Launch thread entrypoint
|
||||||
|
args->entry(args->arg);
|
||||||
svcExitThread();
|
svcExitThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -11,8 +35,12 @@ Result threadCreate(
|
|||||||
Thread* t, ThreadFunc entry, void* arg, size_t stack_sz, int prio,
|
Thread* t, ThreadFunc entry, void* arg, size_t stack_sz, int prio,
|
||||||
int cpuid)
|
int cpuid)
|
||||||
{
|
{
|
||||||
|
stack_sz = (stack_sz+0xFFF) &~ 0xFFF;
|
||||||
|
|
||||||
Result rc = 0;
|
Result rc = 0;
|
||||||
void* stack = memalign(0x1000, stack_sz);
|
size_t reent_sz = (sizeof(struct _reent)+0xF) &~ 0xF;
|
||||||
|
size_t tls_sz = (__tls_end-__tls_start+0xF) &~ 0xF;
|
||||||
|
void* stack = memalign(0x1000, stack_sz + reent_sz + tls_sz);
|
||||||
|
|
||||||
if (stack == NULL) {
|
if (stack == NULL) {
|
||||||
rc = MAKERESULT(MODULE_LIBNX, LIBNX_OUTOFMEM);
|
rc = MAKERESULT(MODULE_LIBNX, LIBNX_OUTOFMEM);
|
||||||
@ -24,21 +52,41 @@ Result threadCreate(
|
|||||||
|
|
||||||
if (R_SUCCEEDED(rc))
|
if (R_SUCCEEDED(rc))
|
||||||
{
|
{
|
||||||
u64 stack_top = ((u64)stack_mirror) + t->stack_sz;
|
u64 stack_top = ((u64)stack_mirror) + t->stack_sz - sizeof(ThreadEntryArgs);
|
||||||
|
ThreadEntryArgs* args = (ThreadEntryArgs*) stack_top;
|
||||||
Handle handle;
|
Handle handle;
|
||||||
|
|
||||||
rc = svcCreateThread(
|
rc = svcCreateThread(
|
||||||
&handle, (ThreadFunc) &_EntryWrap, (void*) t, (void*) stack_top,
|
&handle, (ThreadFunc) &_EntryWrap, args, (void*)stack_top,
|
||||||
prio, cpuid);
|
prio, cpuid);
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc))
|
if (R_SUCCEEDED(rc))
|
||||||
{
|
{
|
||||||
t->handle = handle;
|
t->handle = handle;
|
||||||
t->entry = entry;
|
|
||||||
t->arg = arg;
|
|
||||||
t->stack_mem = stack;
|
t->stack_mem = stack;
|
||||||
t->stack_mirror = stack_mirror;
|
t->stack_mirror = stack_mirror;
|
||||||
t->stack_sz = stack_sz;
|
t->stack_sz = stack_sz;
|
||||||
|
|
||||||
|
args->t = t;
|
||||||
|
args->entry = entry;
|
||||||
|
args->arg = arg;
|
||||||
|
args->reent = (struct _reent*)((u8*)stack + stack_sz);
|
||||||
|
args->tls = (u8*)stack + stack_sz + reent_sz;
|
||||||
|
|
||||||
|
// Set up child thread's reent struct, inheriting standard file handles
|
||||||
|
_REENT_INIT_PTR(args->reent);
|
||||||
|
struct _reent* cur = getThreadVars()->reent;
|
||||||
|
args->reent->_stdin = cur->_stdin;
|
||||||
|
args->reent->_stdout = cur->_stdout;
|
||||||
|
args->reent->_stderr = cur->_stderr;
|
||||||
|
|
||||||
|
// Set up child thread's TLS segment
|
||||||
|
size_t tls_load_sz = __tdata_lma_end-__tdata_lma;
|
||||||
|
size_t tls_bss_sz = tls_sz - tls_load_sz;
|
||||||
|
if (tls_load_sz)
|
||||||
|
memcpy(args->tls, __tdata_lma, tls_load_sz);
|
||||||
|
if (tls_bss_sz)
|
||||||
|
memset(args->tls+tls_load_sz, 0, tls_bss_sz);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (R_FAILED(rc)) {
|
if (R_FAILED(rc)) {
|
||||||
|
@ -36,4 +36,5 @@ void fatalSimple(Result err) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
((void(*)())0xBADC0DE)();
|
((void(*)())0xBADC0DE)();
|
||||||
|
__builtin_unreachable();
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ void __attribute__((weak)) __libnx_init(void)
|
|||||||
__libc_init_array();
|
__libc_init_array();
|
||||||
}
|
}
|
||||||
|
|
||||||
void __attribute__((weak)) NORETURN __libnx_exit(void)
|
void __attribute__((weak)) NORETURN __libnx_exit(int rc)
|
||||||
{
|
{
|
||||||
// Call destructors.
|
// Call destructors.
|
||||||
void __libc_fini_array(void);
|
void __libc_fini_array(void);
|
||||||
|
@ -3,16 +3,42 @@
|
|||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/lock.h>
|
#include <sys/lock.h>
|
||||||
#include <sys/reent.h>
|
#include <sys/reent.h>
|
||||||
|
#include "../internal.h"
|
||||||
|
|
||||||
void __attribute__((weak)) NORETURN __libnx_exit(void);
|
void __attribute__((weak)) NORETURN __libnx_exit(int rc);
|
||||||
|
|
||||||
|
extern const u8 __tdata_lma[];
|
||||||
|
extern const u8 __tdata_lma_end[];
|
||||||
|
extern u8 __tls_start[];
|
||||||
|
|
||||||
static void NORETURN _ExitImpl(int rc) {
|
static struct _reent* __libnx_get_reent() {
|
||||||
__libnx_exit();
|
ThreadVars* tv = getThreadVars();
|
||||||
|
if (tv->magic != THREADVARS_MAGIC)
|
||||||
|
fatalSimple(MAKERESULT(MODULE_LIBNX, LIBNX_BADREENT));
|
||||||
|
return tv->reent;
|
||||||
}
|
}
|
||||||
|
|
||||||
void newlibSetup() {
|
void newlibSetup() {
|
||||||
void exitImpl(int rc);
|
// Register newlib syscalls
|
||||||
__syscalls.exit = _ExitImpl;
|
__syscalls.exit = __libnx_exit;
|
||||||
|
__syscalls.getreent = __libnx_get_reent;
|
||||||
|
|
||||||
|
// Register locking syscalls
|
||||||
|
__syscalls.lock_init = mutexInit;
|
||||||
|
__syscalls.lock_acquire = mutexLock;
|
||||||
|
__syscalls.lock_release = mutexUnlock;
|
||||||
|
__syscalls.lock_init_recursive = rmutexInit;
|
||||||
|
__syscalls.lock_acquire_recursive = rmutexLock;
|
||||||
|
__syscalls.lock_release_recursive = rmutexUnlock;
|
||||||
|
|
||||||
|
// Initialize thread vars for the main thread
|
||||||
|
ThreadVars* tv = getThreadVars();
|
||||||
|
tv->magic = THREADVARS_MAGIC;
|
||||||
|
tv->thread_ptr = NULL;
|
||||||
|
tv->reent = _impure_ptr;
|
||||||
|
tv->tls_tp = __tls_start-2*sizeof(void*); // subtract size of Thread Control Block (TCB)
|
||||||
|
|
||||||
|
u32 tls_size = __tdata_lma_end - __tdata_lma;
|
||||||
|
if (tls_size)
|
||||||
|
memcpy(__tls_start, __tdata_lma, tls_size);
|
||||||
}
|
}
|
||||||
|
10
nx/source/system/readtp.s
Normal file
10
nx/source/system/readtp.s
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
.section .text.__aarch64_read_tp, "ax", %progbits
|
||||||
|
.global __aarch64_read_tp
|
||||||
|
.type __aarch64_read_tp, %function
|
||||||
|
.align 2
|
||||||
|
.cfi_startproc
|
||||||
|
__aarch64_read_tp:
|
||||||
|
mrs x0, tpidrro_el0
|
||||||
|
ldr x0, [x0, #0x1F8]
|
||||||
|
ret
|
||||||
|
.cfi_endproc
|
Loading…
Reference in New Issue
Block a user