Skip to content

Commit

Permalink
x86_64: create and map root ring modules and create instances
Browse files Browse the repository at this point in the history
  • Loading branch information
Qix- committed Sep 16, 2024
1 parent 5f75a4c commit 2f0689f
Show file tree
Hide file tree
Showing 2 changed files with 173 additions and 4 deletions.
120 changes: 117 additions & 3 deletions oro-arch-x86_64/src/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@ use crate::{
gdt::{Gdt, SysEntry},
handler::Handler,
lapic::Lapic,
mem::address_space::AddressSpaceLayout,
tss::Tss,
};
use core::{cell::UnsafeCell, mem::MaybeUninit};
use oro_debug::{dbg, dbg_warn};
use oro_debug::{dbg, dbg_err, dbg_warn};
use oro_elf::{ElfSegment, ElfSegmentType};
use oro_kernel::KernelState;
use oro_mem::translate::{OffsetTranslator, Translator};
use oro_mem::{
mapper::AddressSegment,
pfa::alloc::Alloc,
translate::{OffsetTranslator, Translator},
};
use oro_sync::spinlock::unfair_critical::UnfairCriticalSpinlock;

/// The global kernel state. Initialized once during boot
Expand All @@ -19,6 +25,9 @@ pub static mut KERNEL_STATE: MaybeUninit<KernelState<crate::Arch>> = MaybeUninit

/// Initializes the global state of the architecture.
///
/// # Panics
/// Panics if loading root ring modules fails in any way.
///
/// # Safety
/// Must be called exactly once for the lifetime of the system,
/// only by the boot processor at boot time (_not_ at any
Expand Down Expand Up @@ -49,6 +58,8 @@ pub unsafe fn initialize_primary(pat: OffsetTranslator, pfa: crate::Pfa) {
)
.expect("failed to create global kernel state");

let state = KERNEL_STATE.assume_init_ref();

// TODO(qix-): Not sure that I like that this is ELF-aware. This may get
// TODO(qix-): refactored at some point.
if let Some(oro_boot_protocol::modules::ModulesKind::V0(modules)) =
Expand All @@ -57,7 +68,9 @@ pub unsafe fn initialize_primary(pat: OffsetTranslator, pfa: crate::Pfa) {
let modules = modules.assume_init_ref();
let mut next = modules.next;

while next != 0 {
let root_ring = state.root_ring();

'module: while next != 0 {
let module = &*pat.translate::<oro_boot_protocol::Module>(next);
next = module.next;

Expand All @@ -81,6 +94,107 @@ pub unsafe fn initialize_primary(pat: OffsetTranslator, pfa: crate::Pfa) {
module.base,
module.length
);

let module_handle = state
.create_module(id.clone())
.expect("failed to create root ring module");

{
let module_lock = module_handle
.try_lock::<crate::sync::InterruptController>()
.expect("failed to lock module");

let mapper = module_lock.mapper();

let elf_base = pat.translate::<u8>(module.base);
let elf = oro_elf::Elf::parse(
elf_base,
usize::try_from(module.length).unwrap(),
crate::ELF_ENDIANNESS,
crate::ELF_CLASS,
crate::ELF_MACHINE,
)
.expect("failed to parse ELF");

for segment in elf.segments() {
let mapper_segment = match segment.ty() {
ElfSegmentType::Ignored => continue 'module,
ElfSegmentType::Invalid { flags, ptype } => {
dbg_err!(
"root ring module {id} has invalid segment; skipping: \
ptype={ptype:?} flags={flags:?}",
);
continue 'module;
}
ElfSegmentType::ModuleCode => AddressSpaceLayout::module_code(),
ElfSegmentType::ModuleData => AddressSpaceLayout::module_data(),
ElfSegmentType::ModuleRoData => AddressSpaceLayout::module_rodata(),
ty => {
dbg_err!("root ring module {id} has invalid segment {ty:?}; skipping",);
continue 'module;
}
};

dbg!(
"{id}: loading {:?} segment: {:016X} {:016X} -> {:016X} ({})",
segment.ty(),
segment.load_address(),
segment.load_size(),
segment.target_address(),
segment.target_size()
);

let mut pfa = state
.pfa()
.try_lock::<crate::sync::InterruptController>()
.expect("failed to lock pfa");

// NOTE(qix-): This will almost definitely be improved in the future.
// NOTE(qix-): At the very least, hugepages will change this.
// NOTE(qix-): There will probably be some better machinery for
// NOTE(qix-): mapping ranges of memory in the future.
for page in 0..(segment.target_size().saturating_add(0xFFF) >> 12) {
let phys_addr = pfa
.allocate()
.expect("failed to map root ring module; out of memory");

let byte_offset = page << 12;
// Saturating sub here since the target size might exceed the file size,
// in which case we have to keep allocating those pages and zeroing them.
let load_size = segment.load_size().saturating_sub(byte_offset).min(4096);
let load_virt = segment.load_address() + byte_offset;
let target_virt = segment.target_address() + byte_offset;

let local_page_virt = pat.translate_mut::<u8>(phys_addr);

// SAFETY(qix-): We can assume the kernel module is valid given that it's
// SAFETY(qix-): been loaded by the bootloader.
let (src, dest) = unsafe {
(
core::slice::from_raw_parts(load_virt as *const u8, load_size),
core::slice::from_raw_parts_mut(local_page_virt, 4096),
)
};

// copy data
if load_size > 0 {
dest[..load_size].copy_from_slice(&src[..load_size]);
}
// zero remaining
if load_size < 4096 {
dest[load_size..].fill(0);
}

mapper_segment
.map_nofree(mapper, &mut *pfa, &pat, target_virt, phys_addr)
.expect("failed to map segment");
}
}
}

let _instance = state
.create_instance(module_handle, root_ring.clone())
.expect("failed to create root ring instance");
}
}
}
Expand Down
57 changes: 56 additions & 1 deletion oro-arch-x86_64/src/mem/address_space.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ impl AddressSpaceLayout {
/// Only used during boot; do not change. Can overlap
/// if the segment is used for userspace.
pub const KERNEL_SECONDARY_BOOT_IDX: usize = 0;

/// The index for the module segments.
pub const MODULE_EXE_IDX: (usize, usize) = (5, 16);

/// The recursive index for the page table.
pub const RECURSIVE_IDX: usize = 256;
/// The stack space range
Expand Down Expand Up @@ -95,6 +99,16 @@ impl AddressSpaceLayout {
pub const KERNEL_EXE_IDX: usize = 511;
}

/// Intermediate page table entry template for the module code/data segments.
///
/// Defined here so that the overlapping module segments can share the same
/// intermediate entry, differences between which would cause indeterministic
/// behavior.
const MODULE_EXE_INTERMEDIATE_ENTRY: PageTableEntry = PageTableEntry::new()
.with_user()
.with_present()
.with_writable();

impl AddressSpaceLayout {
/// Adds the recursive mapping to the provided page table.
pub fn map_recursive_entry<P: Translator>(handle: &AddressSpaceHandle, pat: &P) {
Expand Down Expand Up @@ -176,6 +190,48 @@ impl AddressSpaceLayout {

&DESCRIPTOR
}

/// Returns a segment for the module code segment.
#[must_use]
pub fn module_code() -> &'static AddressSegment {
#[expect(clippy::missing_docs_in_private_items)]
const DESCRIPTOR: AddressSegment = AddressSegment {
valid_range: AddressSpaceLayout::MODULE_EXE_IDX,
entry_template: PageTableEntry::new().with_user().with_present(),
intermediate_entry_template: MODULE_EXE_INTERMEDIATE_ENTRY,
};

&DESCRIPTOR
}

/// Returns a segment for the module data segment.
#[must_use]
pub fn module_data() -> &'static AddressSegment {
#[expect(clippy::missing_docs_in_private_items)]
const DESCRIPTOR: AddressSegment = AddressSegment {
valid_range: AddressSpaceLayout::MODULE_EXE_IDX,
entry_template: PageTableEntry::new()
.with_present()
.with_no_exec()
.with_writable(),
intermediate_entry_template: MODULE_EXE_INTERMEDIATE_ENTRY,
};

&DESCRIPTOR
}

/// Returns a segment for the module read-only data segment.
#[must_use]
pub fn module_rodata() -> &'static AddressSegment {
#[expect(clippy::missing_docs_in_private_items)]
const DESCRIPTOR: AddressSegment = AddressSegment {
valid_range: AddressSpaceLayout::MODULE_EXE_IDX,
entry_template: PageTableEntry::new().with_present().with_no_exec(),
intermediate_entry_template: MODULE_EXE_INTERMEDIATE_ENTRY,
};

&DESCRIPTOR
}
}

#[expect(clippy::missing_docs_in_private_items)]
Expand Down Expand Up @@ -209,7 +265,6 @@ macro_rules! registries {
/// intermediate entry, differences between which would cause indeterministic
/// behavior.
const KERNEL_EXE_INTERMEDIATE_ENTRY: PageTableEntry = PageTableEntry::new()
.with_user()
.with_global()
.with_present()
.with_writable();
Expand Down

0 comments on commit 2f0689f

Please sign in to comment.