diff --git a/src/bootinfo/mod.rs b/src/bootinfo/mod.rs index a936bae1..16f6c4c1 100644 --- a/src/bootinfo/mod.rs +++ b/src/bootinfo/mod.rs @@ -42,6 +42,7 @@ pub struct BootInfo { /// can be safely accessed. #[cfg(feature = "map_physical_memory")] pub physical_memory_offset: u64, + tls_template: TlsTemplate, _non_exhaustive: u8, // `()` is not FFI safe } @@ -51,11 +52,18 @@ impl BootInfo { #[doc(hidden)] pub fn new( memory_map: MemoryMap, + tls_template: Option, recursive_page_table_addr: u64, physical_memory_offset: u64, ) -> Self { + let tls_template = tls_template.unwrap_or(TlsTemplate { + start_addr: 0, + file_size: 0, + mem_size: 0, + }); BootInfo { memory_map, + tls_template, #[cfg(feature = "recursive_page_table")] recursive_page_table_addr, #[cfg(feature = "map_physical_memory")] @@ -63,6 +71,42 @@ impl BootInfo { _non_exhaustive: 0, } } + + /// Returns information about the thread local storage segment of the kernel. + /// + /// Returns `None` if the kernel has no thread local storage segment. + /// + /// (The reason this is a method instead of a normal field is that `Option` + /// is not FFI-safe.) + pub fn tls_template(&self) -> Option { + if self.tls_template.mem_size > 0 { + Some(self.tls_template) + } else { + None + } + } +} + +/// Information about the thread local storage (TLS) template. +/// +/// This template can be used to set up thread local storage for threads. For +/// each thread, a new memory location of size `mem_size` must be initialized. +/// Then the first `file_size` bytes of this template needs to be copied to the +/// location. The additional `mem_size - file_size` bytes must be initialized with +/// zero. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(C)] +pub struct TlsTemplate { + /// The virtual start address of the thread local storage template. + pub start_addr: u64, + /// The number of data bytes in the template. + /// + /// Corresponds to the length of the `.tdata` section. + pub file_size: u64, + /// The total number of bytes that the TLS segment should have in memory. + /// + /// Corresponds to the combined length of the `.tdata` and `.tbss` sections. + pub mem_size: u64, } extern "C" { diff --git a/src/main.rs b/src/main.rs index a9e206d1..a937f228 100644 --- a/src/main.rs +++ b/src/main.rs @@ -271,7 +271,7 @@ fn bootloader_main( }; // Map kernel segments. - let stack_end = page_table::map_kernel( + let kernel_memory_info = page_table::map_kernel( kernel_start.phys(), kernel_stack_address, KERNEL_STACK_SIZE, @@ -324,6 +324,7 @@ fn bootloader_main( // Construct boot info structure. let mut boot_info = BootInfo::new( memory_map, + kernel_memory_info.tls_segment, recursive_page_table_addr.as_u64(), physical_memory_offset, ); @@ -352,7 +353,7 @@ fn bootloader_main( sse::enable_sse(); let entry_point = VirtAddr::new(entry_point); - unsafe { context_switch(boot_info_addr, entry_point, stack_end) }; + unsafe { context_switch(boot_info_addr, entry_point, kernel_memory_info.stack_end) }; } fn enable_nxe_bit() { diff --git a/src/page_table.rs b/src/page_table.rs index 7aa1180c..8f943afb 100644 --- a/src/page_table.rs +++ b/src/page_table.rs @@ -1,5 +1,6 @@ use crate::frame_allocator::FrameAllocator; use bootloader::bootinfo::MemoryRegionType; +use bootloader::bootinfo::TlsTemplate; use fixedvec::FixedVec; use x86_64::structures::paging::mapper::{MapToError, MapperFlush, UnmapError}; use x86_64::structures::paging::{ @@ -9,6 +10,24 @@ use x86_64::structures::paging::{ use x86_64::{align_up, PhysAddr, VirtAddr}; use xmas_elf::program::{self, ProgramHeader64}; +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct MemoryInfo { + pub stack_end: VirtAddr, + pub tls_segment: Option, +} + +#[derive(Debug)] +pub enum MapKernelError { + Mapping(MapToError), + MultipleTlsSegments, +} + +impl From for MapKernelError { + fn from(e: MapToError) -> Self { + MapKernelError::Mapping(e) + } +} + pub(crate) fn map_kernel( kernel_start: PhysAddr, stack_start: Page, @@ -16,9 +35,15 @@ pub(crate) fn map_kernel( segments: &FixedVec, page_table: &mut RecursivePageTable, frame_allocator: &mut FrameAllocator, -) -> Result { +) -> Result { + let mut tls_segment = None; for segment in segments { - map_segment(segment, kernel_start, page_table, frame_allocator)?; + let tls = map_segment(segment, kernel_start, page_table, frame_allocator)?; + if let Some(tls) = tls { + if tls_segment.replace(tls).is_some() { + return Err(MapKernelError::MultipleTlsSegments); + } + } } // Create a stack @@ -35,7 +60,10 @@ pub(crate) fn map_kernel( unsafe { map_page(page, frame, flags, page_table, frame_allocator)? }.flush(); } - Ok(stack_end.start_address()) + Ok(MemoryInfo { + stack_end: stack_end.start_address(), + tls_segment, + }) } pub(crate) fn map_segment( @@ -43,7 +71,7 @@ pub(crate) fn map_segment( kernel_start: PhysAddr, page_table: &mut RecursivePageTable, frame_allocator: &mut FrameAllocator, -) -> Result<(), MapToError> { +) -> Result, MapToError> { let typ = segment.get_type().unwrap(); match typ { program::Type::Load => { @@ -160,10 +188,16 @@ pub(crate) fn map_segment( unsafe { addr.as_mut_ptr::().write(0) }; } } + + Ok(None) } - _ => {} + program::Type::Tls => Ok(Some(TlsTemplate { + start_addr: segment.virtual_addr, + mem_size: segment.mem_size, + file_size: segment.file_size, + })), + _ => Ok(None), } - Ok(()) } pub(crate) unsafe fn map_page<'a, S>(