diff options
Diffstat (limited to 'sysdeps/mach/hurd/i386/tls.h')
-rw-r--r-- | sysdeps/mach/hurd/i386/tls.h | 97 |
1 files changed, 83 insertions, 14 deletions
diff --git a/sysdeps/mach/hurd/i386/tls.h b/sysdeps/mach/hurd/i386/tls.h index 3ec5bb032b..379f22e972 100644 --- a/sysdeps/mach/hurd/i386/tls.h +++ b/sysdeps/mach/hurd/i386/tls.h @@ -53,6 +53,10 @@ typedef struct void *__private_tm[4]; /* GCC split stack support. */ void *__private_ss; + + /* Keep this field last */ + mach_port_t reply_port; /* This thread's reply port. */ + struct hurd_sigstate *_hurd_sigstate; } tcbhead_t; #endif @@ -72,6 +76,8 @@ typedef struct # define __i386_set_gdt(thr, sel, desc) ((void) (thr), (void) (sel), (void) (desc), MIG_BAD_ID) # endif +#define __i386_selector_is_ldt(sel) (!!((sel) & 4)) + # include <errno.h> # include <assert.h> @@ -88,41 +94,57 @@ typedef struct | (((unsigned int) (tcb)) & 0xff000000) /* base 24..31 */ \ } +# define HURD_DESC_TLS(desc) \ + ({ \ + (tcbhead_t *) ( (desc->low_word >> 16) \ + | ((desc->high_word & 0xff) << 16) \ + | (desc->high_word & 0xff000000) \ + );}) + +#define __LIBC_NO_TLS() \ + ({ unsigned short ds, gs; \ + asm ("movw %%ds,%w0; movw %%gs,%w1" : "=q" (ds), "=q" (gs)); \ + ds == gs; }) static inline const char * __attribute__ ((unused)) _hurd_tls_init (tcbhead_t *tcb) { HURD_TLS_DESC_DECL (desc, tcb); + thread_t self = __mach_thread_self (); + const char *msg = NULL; /* This field is used by TLS accesses to get our "thread pointer" from the TLS point of view. */ tcb->tcb = tcb; - /* Cache our thread port. */ - tcb->self = __mach_thread_self (); - /* Get the first available selector. */ int sel = -1; - error_t err = __i386_set_gdt (tcb->self, &sel, desc); + kern_return_t err = __i386_set_gdt (tcb->self, &sel, desc); if (err == MIG_BAD_ID) { /* Old kernel, use a per-thread LDT. */ sel = 0x27; - err = __i386_set_ldt (tcb->self, sel, &desc, 1); + err = __i386_set_ldt (self, sel, &desc, 1); assert_perror (err); if (err) - return "i386_set_ldt failed"; + { + msg = "i386_set_ldt failed"; + goto out; + } } else if (err) { assert_perror (err); /* Separate from above with different line #. */ - return "i386_set_gdt failed"; + msg = "i386_set_gdt failed"; + goto out; } /* Now install the new selector. */ asm volatile ("mov %w0, %%gs" :: "q" (sel)); - return 0; +out: + __mach_port_deallocate (__mach_task_self (), self); + return msg; } /* Code to initially initialize the thread pointer. This might need @@ -138,6 +160,20 @@ _hurd_tls_init (tcbhead_t *tcb) : "i" (offsetof (tcbhead_t, tcb))); \ __tcb;}) +/* Return the TCB address of a thread given its state. */ +# define THREAD_TCB(thread, thread_state) \ + ({ int __sel = (thread_state)->basic.gs; \ + struct descriptor __desc, *___desc = &__desc; \ + unsigned int __count = 1; \ + kern_return_t __err; \ + if (__builtin_expect (__sel, 0x48) & 4) /* LDT selector */ \ + __err = __i386_get_ldt ((thread), __sel, 1, &___desc, &__count); \ + else \ + __err = __i386_get_gdt ((thread), __sel, &__desc); \ + assert_perror (__err); \ + assert (__count == 1); \ + HURD_DESC_TLS(___desc);}) + /* Install new dtv for current thread. */ # define INSTALL_NEW_DTV(dtvp) \ ({ asm volatile ("movl %0,%%gs:%P1" \ @@ -151,9 +187,40 @@ _hurd_tls_init (tcbhead_t *tcb) # include <mach/machine/thread_status.h> -/* Set up TLS in the new thread of a fork child, copying from our own. */ -static inline error_t __attribute__ ((unused)) -_hurd_tls_fork (thread_t child, struct i386_thread_state *state) +/* Set up TLS in the new thread of a fork child, copying from the original. */ +static inline kern_return_t __attribute__ ((unused)) +_hurd_tls_fork (thread_t child, thread_t orig, struct i386_thread_state *state) +{ + /* Fetch the selector set by _hurd_tls_init. */ + int sel; + asm ("mov %%gs, %w0" : "=q" (sel) : "0" (0)); + if (sel == state->ds) /* _hurd_tls_init was never called. */ + return 0; + + struct descriptor desc, *_desc = &desc; + kern_return_t err; + unsigned int count = 1; + + if (__glibc_unlikely (__i386_selector_is_ldt(sel))) + err = __i386_get_ldt (orig, sel, 1, &_desc, &count); + else + err = __i386_get_gdt (orig, sel, &desc); + + assert_perror (err); + if (err) + return err; + + if (__glibc_unlikely (__i386_selector_is_ldt(sel))) + err = __i386_set_ldt (child, sel, &desc, 1); + else + err = __i386_set_gdt (child, &sel, desc); + + state->gs = sel; + return err; +} + +static inline kern_return_t __attribute__ ((unused)) +_hurd_tls_new (thread_t child, struct i386_thread_state *state, tcbhead_t *tcb) { /* Fetch the selector set by _hurd_tls_init. */ int sel; @@ -161,11 +228,13 @@ _hurd_tls_fork (thread_t child, struct i386_thread_state *state) if (sel == state->ds) /* _hurd_tls_init was never called. */ return 0; - tcbhead_t *const tcb = THREAD_SELF; HURD_TLS_DESC_DECL (desc, tcb); - error_t err; + kern_return_t err; + + tcb->tcb = tcb; + tcb->self = child; - if (__builtin_expect (sel, 0x50) & 4) /* LDT selector */ + if (__glibc_unlikely (__i386_selector_is_ldt(sel))) err = __i386_set_ldt (child, sel, &desc, 1); else err = __i386_set_gdt (child, &sel, desc); |