Skip to content

Commit 6efb49e

Browse files
committed
fix: rework TLS, fix alignments
1 parent d7effff commit 6efb49e

File tree

5 files changed

+203
-244
lines changed

5 files changed

+203
-244
lines changed

src/arch/aarch64/kernel/scheduler.rs

Lines changed: 3 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
//! Architecture dependent interface to initialize a task
22
3-
#[cfg(not(feature = "common-os"))]
4-
use alloc::alloc::{Layout, alloc_zeroed};
5-
#[cfg(not(feature = "common-os"))]
6-
use alloc::boxed::Box;
73
use core::arch::naked_asm;
8-
#[cfg(not(feature = "common-os"))]
9-
use core::slice;
104
use core::sync::atomic::Ordering;
115
use core::{mem, ptr};
126

@@ -17,8 +11,6 @@ use memory_addresses::arch::aarch64::{PhysAddr, VirtAddr};
1711
use crate::arch::aarch64::kernel::CURRENT_STACK_ADDRESS;
1812
use crate::arch::aarch64::kernel::core_local::core_scheduler;
1913
use crate::arch::aarch64::mm::paging::{BasePageSize, PageSize, PageTableEntryFlags};
20-
#[cfg(not(feature = "common-os"))]
21-
use crate::env;
2214
use crate::mm::physicalmem::PHYSICAL_FREE_LIST;
2315
use crate::mm::virtualmem::KERNEL_FREE_LIST;
2416
#[cfg(target_os = "none")]
@@ -266,77 +258,6 @@ impl Drop for TaskStacks {
266258
* of the TLS implementations.
267259
*/
268260

269-
#[cfg(not(feature = "common-os"))]
270-
#[derive(Copy, Clone)]
271-
#[repr(C)]
272-
struct DtvPointer {
273-
val: *const (),
274-
to_free: *const (),
275-
}
276-
277-
#[cfg(not(feature = "common-os"))]
278-
#[repr(C)]
279-
union Dtv {
280-
counter: usize,
281-
pointer: DtvPointer,
282-
}
283-
284-
#[cfg(not(feature = "common-os"))]
285-
#[repr(C)]
286-
pub struct TaskTLS {
287-
dtv: Box<[Dtv; 2]>,
288-
_private: usize,
289-
block: [u8],
290-
}
291-
292-
#[cfg(not(feature = "common-os"))]
293-
impl TaskTLS {
294-
fn from_environment() -> Option<Box<Self>> {
295-
let tls_info = env::boot_info().load_info.tls_info?;
296-
assert_ne!(tls_info.memsz, 0);
297-
298-
// Get TLS initialization image
299-
let tls_init_image = {
300-
let tls_init_data = ptr::with_exposed_provenance(tls_info.start.try_into().unwrap());
301-
let tls_init_len = tls_info.filesz.try_into().unwrap();
302-
303-
// SAFETY: We will have to trust the environment here.
304-
unsafe { slice::from_raw_parts(tls_init_data, tls_init_len) }
305-
};
306-
307-
let off = core::cmp::max(16, usize::try_from(tls_info.align).unwrap()) - 16;
308-
let block_len = usize::try_from(tls_info.memsz).unwrap() + off;
309-
let len = mem::size_of::<Box<[Dtv; 2]>>() + mem::size_of::<usize>() + block_len;
310-
311-
let layout = Layout::from_size_align(len, 16).unwrap();
312-
let mut this = unsafe {
313-
let data = alloc_zeroed(layout);
314-
let raw = ptr::slice_from_raw_parts_mut(data, block_len) as *mut TaskTLS;
315-
316-
let addr = (*raw).block.as_ptr().add(off).cast::<()>();
317-
(&raw mut (*raw).dtv).write(Box::new([
318-
Dtv { counter: 1 },
319-
Dtv {
320-
pointer: DtvPointer {
321-
val: addr,
322-
to_free: ptr::null(),
323-
},
324-
},
325-
]));
326-
327-
Box::from_raw(raw)
328-
};
329-
330-
this.block[off..off + tls_init_image.len()].copy_from_slice(tls_init_image);
331-
332-
Some(this)
333-
}
334-
335-
fn thread_ptr(&self) -> *const Box<[Dtv; 2]> {
336-
&raw const self.dtv
337-
}
338-
}
339-
340261
#[cfg(not(target_os = "none"))]
341262
extern "C" fn task_start(_f: extern "C" fn(usize), _arg: usize, _user_stack: u64) -> ! {
342263
unimplemented!()
@@ -373,7 +294,9 @@ impl TaskFrame for Task {
373294
// Check if TLS is allocated already and if the task uses thread-local storage.
374295
#[cfg(not(feature = "common-os"))]
375296
if self.tls.is_none() {
376-
self.tls = TaskTLS::from_environment();
297+
use crate::scheduler::task::tls::Tls;
298+
299+
self.tls = Tls::from_env();
377300
}
378301

379302
unsafe {

src/arch/riscv64/kernel/scheduler.rs

Lines changed: 4 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,3 @@
1-
#[cfg(not(feature = "common-os"))]
2-
use alloc::alloc::{Layout, alloc, dealloc};
3-
#[cfg(not(feature = "common-os"))]
4-
use alloc::boxed::Box;
5-
#[cfg(not(feature = "common-os"))]
6-
use core::convert::TryInto;
71
use core::{mem, ptr};
82

93
use align_address::Align;
@@ -13,8 +7,6 @@ use memory_addresses::{PhysAddr, VirtAddr};
137
use crate::arch::riscv64::kernel::core_local::core_scheduler;
148
use crate::arch::riscv64::kernel::processor::set_oneshot_timer;
159
use crate::arch::riscv64::mm::paging::{BasePageSize, PageSize, PageTableEntryFlags};
16-
#[cfg(not(feature = "common-os"))]
17-
use crate::env;
1810
use crate::mm::physicalmem::PHYSICAL_FREE_LIST;
1911
use crate::mm::virtualmem::KERNEL_FREE_LIST;
2012
use crate::scheduler::task::{Task, TaskFrame};
@@ -277,84 +269,6 @@ impl Drop for TaskStacks {
277269
}
278270
}
279271

280-
#[cfg(not(feature = "common-os"))]
281-
pub struct TaskTLS {
282-
address: VirtAddr,
283-
tp: VirtAddr,
284-
layout: Layout,
285-
}
286-
287-
#[cfg(not(feature = "common-os"))]
288-
impl TaskTLS {
289-
pub fn from_environment() -> Option<Box<Self>> {
290-
let tls_info = env::boot_info().load_info.tls_info?;
291-
assert_ne!(tls_info.memsz, 0);
292-
293-
let tls_size = tls_info.memsz as usize;
294-
// determine the size of tdata (tls without tbss)
295-
let tdata_size = tls_info.filesz as usize;
296-
let tls_start = VirtAddr::new(tls_info.start);
297-
// Yes, it does, so we have to allocate TLS memory.
298-
// Allocate enough space for the given size and one more variable of type usize, which holds the tls_pointer.
299-
let tls_allocation_size = tls_size.align_up(32usize); // + mem::size_of::<usize>();
300-
// We allocate in 128 byte granularity (= cache line size) to avoid false sharing
301-
let memory_size = tls_allocation_size.align_up(128usize);
302-
let layout =
303-
Layout::from_size_align(memory_size, 128).expect("TLS has an invalid size / alignment");
304-
let ptr = VirtAddr::new(unsafe { alloc(layout) as u64 });
305-
306-
// The tls_pointer is the address to the end of the TLS area requested by the task.
307-
let tls_pointer = ptr; // + tls_size.align_up(32);
308-
309-
unsafe {
310-
// Copy over TLS variables with their initial values.
311-
ptr::copy_nonoverlapping(tls_start.as_ptr::<u8>(), ptr.as_mut_ptr::<u8>(), tdata_size);
312-
313-
ptr::write_bytes(
314-
ptr.as_mut_ptr::<u8>()
315-
.offset(tdata_size.try_into().unwrap()),
316-
0,
317-
tls_size.align_up(32usize) - tdata_size,
318-
);
319-
320-
// The x86-64 TLS specification also requires that the tls_pointer can be accessed at fs:0.
321-
// This allows TLS variable values to be accessed by "mov rax, fs:0" and a later "lea rdx, [rax+VARIABLE_OFFSET]".
322-
// See "ELF Handling For Thread-Local Storage", version 0.20 by Ulrich Drepper, page 12 for details.
323-
//
324-
// fs:0 is where tls_pointer points to and we have reserved space for a usize value above.
325-
//*(tls_pointer.as_mut_ptr::<u64>()) = tls_pointer.as_u64();
326-
}
327-
328-
debug!(
329-
"Set up TLS at {tls_pointer:#x}, tdata_size {tdata_size:#x}, tls_size {tls_size:#x}"
330-
);
331-
332-
Some(Box::new(Self {
333-
address: ptr,
334-
tp: tls_pointer,
335-
layout,
336-
}))
337-
}
338-
339-
pub fn tp(&self) -> VirtAddr {
340-
self.tp
341-
}
342-
}
343-
344-
#[cfg(not(feature = "common-os"))]
345-
impl Drop for TaskTLS {
346-
fn drop(&mut self) {
347-
debug!(
348-
"Deallocate TLS at 0x{:x} (layout {:?})",
349-
self.address, self.layout,
350-
);
351-
352-
unsafe {
353-
dealloc(self.address.as_mut_ptr::<u8>(), self.layout);
354-
}
355-
}
356-
}
357-
358272
#[unsafe(no_mangle)]
359273
extern "C" fn task_entry(func: extern "C" fn(usize), arg: usize) {
360274
use crate::scheduler::PerCoreSchedulerExt;
@@ -401,7 +315,9 @@ impl TaskFrame for Task {
401315
// check is TLS is already allocated
402316
#[cfg(not(feature = "common-os"))]
403317
if self.tls.is_none() {
404-
self.tls = TaskTLS::from_environment();
318+
use crate::scheduler::task::tls::Tls;
319+
320+
self.tls = Tls::from_env();
405321
}
406322

407323
unsafe {
@@ -416,7 +332,7 @@ impl TaskFrame for Task {
416332
let state = stack.as_mut_ptr::<State>();
417333
#[cfg(not(feature = "common-os"))]
418334
if let Some(tls) = &self.tls {
419-
(*state).tp = tls.tp().as_usize();
335+
(*state).tp = tls.thread_ptr() as usize;
420336
}
421337
(*state).ra = task_start as usize;
422338
(*state).a0 = func as usize;

src/arch/x86_64/kernel/scheduler.rs

Lines changed: 3 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
//! Architecture dependent interface to initialize a task
22
3-
#[cfg(not(feature = "common-os"))]
4-
use alloc::boxed::Box;
53
use core::arch::naked_asm;
6-
#[cfg(not(feature = "common-os"))]
7-
use core::mem::MaybeUninit;
8-
#[cfg(not(feature = "common-os"))]
9-
use core::slice;
104
use core::{mem, ptr};
115

126
use align_address::Align;
@@ -259,71 +253,6 @@ impl Drop for TaskStacks {
259253
}
260254
}
261255

262-
#[cfg(not(feature = "common-os"))]
263-
pub struct TaskTLS {
264-
_block: Box<[MaybeUninit<u8>]>,
265-
thread_ptr: *mut (),
266-
}
267-
268-
#[cfg(not(feature = "common-os"))]
269-
impl TaskTLS {
270-
// For details on thread-local storage data structures see
271-
//
272-
// “ELF Handling For Thread-Local Storage” Section 3.4.6: x86-64 Specific Definitions for Run-Time Handling of TLS
273-
// https://akkadia.org/drepper/tls.pdf
274-
fn from_environment() -> Option<Box<Self>> {
275-
let tls_info = env::boot_info().load_info.tls_info?;
276-
assert_ne!(tls_info.memsz, 0);
277-
278-
// Get TLS initialization image
279-
let tls_init_image = {
280-
let tls_init_data = ptr::with_exposed_provenance(tls_info.start.try_into().unwrap());
281-
let tls_init_len = tls_info.filesz.try_into().unwrap();
282-
283-
// SAFETY: We will have to trust the environment here.
284-
unsafe { slice::from_raw_parts(tls_init_data, tls_init_len) }
285-
};
286-
287-
// As described in “ELF Handling For Thread-Local Storage”
288-
let tls_offset = usize::try_from(tls_info.memsz)
289-
.unwrap()
290-
.align_up(usize::try_from(tls_info.align).unwrap());
291-
292-
// Allocate TLS block
293-
let mut block = {
294-
// To access TLS blocks on x86-64, TLS offsets are *subtracted* from the thread register value.
295-
// So the thread pointer needs to be `block_ptr + tls_offset`.
296-
// GNU style TLS requires `fs:0` to represent the same address as the thread pointer.
297-
// Since the thread pointer points to the end of the TLS blocks, we need to store it there.
298-
let tcb_size = mem::size_of::<*mut ()>();
299-
300-
vec![MaybeUninit::<u8>::uninit(); tls_offset + tcb_size].into_boxed_slice()
301-
};
302-
303-
// Initialize beginning of the TLS block with TLS initialization image
304-
block[..tls_init_image.len()].copy_from_slice(tls_init_image);
305-
306-
// Fill the rest of the block with zeros
307-
block[tls_init_image.len()..tls_offset].fill(MaybeUninit::new(0));
308-
309-
// thread_ptr = block_ptr + tls_offset
310-
let thread_ptr = block[tls_offset..].as_mut_ptr().cast::<()>();
311-
unsafe {
312-
thread_ptr.cast::<*mut ()>().write_unaligned(thread_ptr);
313-
}
314-
315-
let this = Self {
316-
_block: block,
317-
thread_ptr,
318-
};
319-
Some(Box::new(this))
320-
}
321-
322-
fn thread_ptr(&self) -> *mut () {
323-
self.thread_ptr
324-
}
325-
}
326-
327256
#[cfg(not(target_os = "none"))]
328257
extern "C" fn task_start(_f: extern "C" fn(usize), _arg: usize, _user_stack: u64) -> ! {
329258
unimplemented!()
@@ -358,7 +287,9 @@ impl TaskFrame for Task {
358287
// Check if TLS is allocated already and if the task uses thread-local storage.
359288
#[cfg(not(feature = "common-os"))]
360289
if self.tls.is_none() {
361-
self.tls = TaskTLS::from_environment();
290+
use crate::scheduler::task::tls::Tls;
291+
292+
self.tls = Tls::from_env();
362293
}
363294

364295
unsafe {

src/scheduler/task.rs renamed to src/scheduler/task/mod.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
#![allow(clippy::type_complexity)]
22

33
#[cfg(not(feature = "common-os"))]
4-
use alloc::boxed::Box;
4+
pub(crate) mod tls;
5+
56
use alloc::collections::{LinkedList, VecDeque};
67
use alloc::rc::Rc;
78
use alloc::sync::Arc;
@@ -15,10 +16,10 @@ use hashbrown::HashMap;
1516
use hermit_sync::{OnceCell, RwSpinLock};
1617
use memory_addresses::VirtAddr;
1718

19+
#[cfg(not(feature = "common-os"))]
20+
use self::tls::Tls;
1821
use crate::arch::core_local::*;
1922
use crate::arch::scheduler::TaskStacks;
20-
#[cfg(not(feature = "common-os"))]
21-
use crate::arch::scheduler::TaskTLS;
2223
use crate::errno::Errno;
2324
use crate::executor::poll_on;
2425
use crate::fd::stdio::*;
@@ -397,7 +398,7 @@ pub(crate) struct Task {
397398
>,
398399
/// Task Thread-Local-Storage (TLS)
399400
#[cfg(not(feature = "common-os"))]
400-
pub tls: Option<Box<TaskTLS>>,
401+
pub tls: Option<Tls>,
401402
// Physical address of the 1st level page table
402403
#[cfg(all(target_arch = "x86_64", feature = "common-os"))]
403404
pub root_page_table: usize,

0 commit comments

Comments
 (0)