Skip to content

Add basic support for ELF thread local storage segments #96

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/bootinfo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand All @@ -51,18 +52,61 @@ impl BootInfo {
#[doc(hidden)]
pub fn new(
memory_map: MemoryMap,
tls_template: Option<TlsTemplate>,
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")]
physical_memory_offset,
_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<TlsTemplate> {
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" {
Expand Down
5 changes: 3 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
);
Expand Down Expand Up @@ -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() {
Expand Down
46 changes: 40 additions & 6 deletions src/page_table.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand All @@ -9,16 +10,40 @@ 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<TlsTemplate>,
}

#[derive(Debug)]
pub enum MapKernelError {
Mapping(MapToError),
MultipleTlsSegments,
}

impl From<MapToError> for MapKernelError {
fn from(e: MapToError) -> Self {
MapKernelError::Mapping(e)
}
}

pub(crate) fn map_kernel(
kernel_start: PhysAddr,
stack_start: Page,
stack_size: u64,
segments: &FixedVec<ProgramHeader64>,
page_table: &mut RecursivePageTable,
frame_allocator: &mut FrameAllocator,
) -> Result<VirtAddr, MapToError> {
) -> Result<MemoryInfo, MapKernelError> {
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
Expand All @@ -35,15 +60,18 @@ 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(
segment: &ProgramHeader64,
kernel_start: PhysAddr,
page_table: &mut RecursivePageTable,
frame_allocator: &mut FrameAllocator,
) -> Result<(), MapToError> {
) -> Result<Option<TlsTemplate>, MapToError> {
let typ = segment.get_type().unwrap();
match typ {
program::Type::Load => {
Expand Down Expand Up @@ -160,10 +188,16 @@ pub(crate) fn map_segment(
unsafe { addr.as_mut_ptr::<u8>().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>(
Expand Down