axhal/
tls.rs

1//! Thread Local Storage (TLS) support.
2//!
3//! ## TLS layout for x86_64
4//!
5//! ```text
6//! aligned --> +-------------------------+- static_tls_offset
7//! allocation  |                         | \
8//!             | .tdata                  |  |
9//! | address   |                         |  |
10//! | grow up   + - - - - - - - - - - - - +   > Static TLS block
11//! v           |                         |  |  (length: static_tls_size)
12//!             | .tbss                   |  |
13//!             |                         |  |
14//!             +-------------------------+  |
15//!             | / PADDING / / / / / / / | /
16//!             +-------------------------+
17//!    tls_ptr -+-> self pointer (void *) | \
18//! (tp_offset) |                         |  |
19//!             | Custom TCB format       |   > Thread Control Block (TCB)
20//!             | (might be used          |  |  (length: TCB_SIZE)
21//!             |  by a libC)             |  |
22//!             |                         | /
23//!             +-------------------------+- (total length: tls_area_size)
24//! ```
25//!
26//! ## TLS layout for AArch64 and RISC-V
27//!
28//! ```text
29//!             +-------------------------+
30//!             |                         | \
31//!             | Custom TCB format       |  |
32//!             | (might be used          |   > Thread Control Block (TCB)
33//!             |  by a libC)             |  |  (length: TCB_SIZE)
34//!             |                         | /
35//!    tls_ptr -+-------------------------+
36//! (tp_offset) | GAP_ABOVE_TP            |
37//!             +-------------------------+- static_tls_offset
38//!             |                         | \
39//!             | .tdata                  |  |
40//!             |                         |  |
41//!             + - - - - - - - - - - - - +   > Static TLS block
42//!             |                         |  |  (length: static_tls_size)
43//!             | .tbss                   |  |
44//!             |                         | /
45//!             +-------------------------+- (total length: tls_area_size)
46//! ```
47//!
48//! Reference:
49//! 1. <https://github.com/unikraft/unikraft/blob/staging/arch/x86/x86_64/tls.c>
50//! 2. <https://github.com/unikraft/unikraft/blob/staging/arch/arm/arm64/tls.c>
51
52extern crate alloc;
53
54use memory_addr::align_up;
55
56use core::alloc::Layout;
57use core::ptr::NonNull;
58
59const TLS_ALIGN: usize = 0x10;
60
61cfg_if::cfg_if! {
62    if #[cfg(target_arch = "x86_64")] {
63        const TCB_SIZE: usize = 8; // to store TLS self pointer
64        const GAP_ABOVE_TP: usize = 0;
65    } else if #[cfg(target_arch = "arm")] {
66        const TCB_SIZE: usize = 0;
67        const GAP_ABOVE_TP: usize = 16;
68    } else if #[cfg(target_arch = "aarch64")] {
69        const TCB_SIZE: usize = 0;
70        const GAP_ABOVE_TP: usize = 16;
71    } else if #[cfg(target_arch = "riscv64")] {
72        const TCB_SIZE: usize = 0;
73        const GAP_ABOVE_TP: usize = 0;
74    } else if #[cfg(target_arch = "loongarch64")] {
75        const TCB_SIZE: usize = 0;
76        const GAP_ABOVE_TP: usize = 0;
77    }
78}
79
80unsafe extern "C" {
81    fn _stdata();
82    fn _etdata();
83    fn _etbss();
84}
85
86/// The memory region for thread-local storage.
87pub struct TlsArea {
88    base: NonNull<u8>,
89    layout: Layout,
90}
91
92impl Drop for TlsArea {
93    fn drop(&mut self) {
94        unsafe {
95            alloc::alloc::dealloc(self.base.as_ptr(), self.layout);
96        }
97    }
98}
99
100impl TlsArea {
101    /// Returns the pointer to the TLS static area.
102    ///
103    /// One should set the hardware thread pointer register to this value.
104    pub fn tls_ptr(&self) -> *mut u8 {
105        unsafe { self.base.as_ptr().add(tp_offset()) }
106    }
107
108    /// Allocates the memory region for TLS, and initializes it.
109    pub fn alloc() -> Self {
110        let layout = Layout::from_size_align(tls_area_size(), TLS_ALIGN).unwrap();
111        let area_base = unsafe { alloc::alloc::alloc_zeroed(layout) };
112
113        let tls_load_base = _stdata as *mut u8;
114        let tls_load_size = _etbss as usize - _stdata as usize;
115        unsafe {
116            // copy data from .tbdata section
117            core::ptr::copy_nonoverlapping(
118                tls_load_base,
119                area_base.add(static_tls_offset()),
120                tls_load_size,
121            );
122            // initialize TCB
123            init_tcb(area_base);
124        }
125
126        Self {
127            base: NonNull::new(area_base).unwrap(),
128            layout,
129        }
130    }
131}
132
133fn static_tls_size() -> usize {
134    align_up(_etbss as usize - _stdata as usize, TLS_ALIGN)
135}
136
137fn static_tls_offset() -> usize {
138    if cfg!(target_arch = "x86_64") {
139        0
140    } else if cfg!(any(
141        target_arch = "arm",
142        target_arch = "aarch64",
143        target_arch = "riscv64",
144        target_arch = "loongarch64"
145    )) {
146        TCB_SIZE + GAP_ABOVE_TP
147    } else {
148        unreachable!()
149    }
150}
151
152fn tp_offset() -> usize {
153    if cfg!(target_arch = "x86_64") {
154        static_tls_size()
155    } else if cfg!(any(
156        target_arch = "arm",
157        target_arch = "aarch64",
158        target_arch = "riscv64",
159        target_arch = "loongarch64"
160    )) {
161        TCB_SIZE
162    } else {
163        unreachable!()
164    }
165}
166
167fn tls_area_size() -> usize {
168    if cfg!(target_arch = "x86_64") {
169        static_tls_size() + TCB_SIZE
170    } else if cfg!(any(
171        target_arch = "arm",
172        target_arch = "aarch64",
173        target_arch = "riscv64",
174        target_arch = "loongarch64"
175    )) {
176        TCB_SIZE + GAP_ABOVE_TP + static_tls_size()
177    } else {
178        unreachable!()
179    }
180}
181
182unsafe fn init_tcb(tls_area: *mut u8) {
183    if cfg!(target_arch = "x86_64") {
184        let tp_addr = tls_area.add(tp_offset()).cast::<usize>();
185        tp_addr.write(tp_addr as usize); // write self pointer
186    }
187}
188
189/// Reads the thread pointer of the current CPU.
190/// `__aeabi_read_tp` is used by the Rust compiler to
191/// implement thread-local storage (TLS) access on ARM32.
192#[unsafe(no_mangle)]
193#[cfg(target_arch = "arm")]
194extern "C" fn __aeabi_read_tp() -> *mut u8 {
195    crate::asm::read_thread_pointer() as *mut u8
196}