1 // Copyright 2016 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #pragma once
6 
7 #include <zircon/compiler.h>
8 #include <zircon/syscalls.h>
9 #include <zircon/syscalls/object.h>
10 
11 __BEGIN_CDECLS
12 
13 #pragma GCC visibility push(hidden)
14 
15 // Get and set the thread pointer.
16 static inline void* zxr_tp_get(void);
17 static inline void zxr_tp_set(zx_handle_t self, void* tp);
18 
19 #if defined(__aarch64__)
20 
zxr_tp_get(void)21 __NO_SAFESTACK static inline void* zxr_tp_get(void) {
22     // This just emits "mrs %[reg], tpidr_el0", but the compiler
23     // knows what exactly it's doing (unlike an asm).  So it can
24     // e.g. CSE it with another implicit thread-pointer fetch it
25     // generated for its own reasons.
26     return __builtin_thread_pointer();
27 }
28 
zxr_tp_set(zx_handle_t self,void * tp)29 __NO_SAFESTACK static inline void zxr_tp_set(zx_handle_t self, void* tp) {
30     __asm__ volatile("msr tpidr_el0, %0"
31                      :
32                      : "r"(tp));
33 }
34 
35 #elif defined(__x86_64__)
36 
zxr_tp_get(void)37 __NO_SAFESTACK static inline void* zxr_tp_get(void) {
38 // This fetches %fs:0, but the compiler knows what it's doing.
39 // LLVM knows that in the Fuchsia ABI %fs:0 always stores the
40 // %fs.base address, and its optimizer will see through this
41 // to integrate *(zxr_tp_get() + N) as a direct "mov %fs:N, ...".
42 // Note that these special pointer types can be used to access
43 // memory, but they cannot be cast to a normal pointer type
44 // (which in the abstract should add in the base address,
45 // but the compiler doesn't know how to do that).
46 #ifdef __clang__
47     // Clang does it via magic address_space numbers (256 is %gs).
48     void* __attribute__((address_space(257)))* fs = 0;
49     // TODO(mcgrathr): GCC 6 supports this syntax instead (and __seg_gs):
50     //     void* __seg_fs* fs = 0;
51     // Unfortunately, it allows it only in C and not in C++.
52     // It also requires -fasm under -std=c11 (et al), see:
53     //     https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79609
54     // It's also buggy for the special case of 0, see:
55     //     https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79619
56     return *fs;
57 #else
58     void* tp;
59     __asm__ __volatile__("mov %%fs:0,%0"
60                          : "=r"(tp));
61     return tp;
62 #endif
63 }
64 
zxr_tp_set(zx_handle_t self,void * tp)65 __NO_SAFESTACK static inline void zxr_tp_set(zx_handle_t self, void* tp) {
66     zx_status_t status = _zx_object_set_property(
67         self, ZX_PROP_REGISTER_FS, (uintptr_t*)&tp, sizeof(uintptr_t));
68     if (status != ZX_OK)
69         __builtin_trap();
70 }
71 
72 #else
73 
74 #error Unsupported architecture
75 
76 #endif
77 
78 #pragma GCC visibility pop
79 
80 __END_CDECLS
81