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}