Skip to content

Commit

Permalink
Merge pull request #494 from Wasabi375/fixed_entry
Browse files Browse the repository at this point in the history
Specify kernel-code virt addr in BootloaderConfig
  • Loading branch information
Freax13 authored Feb 18, 2025
2 parents d30906c + fa4be39 commit 55918ea
Show file tree
Hide file tree
Showing 18 changed files with 313 additions and 47 deletions.
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ test_kernel_config_file = { path = "tests/test_kernels/config_file", artifact =
test_kernel_min_stack = { path = "tests/test_kernels/min_stack", artifact = "bin", target = "x86_64-unknown-none" }
test_kernel_lower_memory_free = { path = "tests/test_kernels/lower_memory_free", artifact = "bin", target = "x86_64-unknown-none" }
test_kernel_write_usable_memory = { path = "tests/test_kernels/write_usable_memory", artifact = "bin", target = "x86_64-unknown-none" }
test_kernel_fixed_kernel_address = { path = "tests/test_kernels/fixed_kernel_address", artifact = "bin", target = "x86_64-unknown-none" }

[profile.dev]
panic = "abort"
Expand Down
6 changes: 6 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Unreleased

## Features

* add `kernel_base` mapping to the BootloaderConfig to specify the virtual address
at which position-independent kernels are loaded.


# 0.11.10 – 2025-02-10

* [Remove "UEFI boot" log message](https://github.com/rust-osdev/bootloader/pull/476)
Expand Down
9 changes: 5 additions & 4 deletions api/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@ fn main() {
(31, 9),
(40, 9),
(49, 9),
(58, 10),
(68, 10),
(78, 1),
(79, 9),
(58, 9),
(67, 10),
(77, 10),
(87, 1),
(88, 9),
(97, 9),
(106, 9),
(115, 9),
(124, 9),
];

let mut code = String::new();
Expand Down
35 changes: 24 additions & 11 deletions api/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ impl BootloaderConfig {
0x3D,
];
#[doc(hidden)]
pub const SERIALIZED_LEN: usize = 124;
pub const SERIALIZED_LEN: usize = 133;

/// Creates a new default configuration with the following values:
///
Expand Down Expand Up @@ -77,6 +77,7 @@ impl BootloaderConfig {
} = version;
let Mappings {
kernel_stack,
kernel_base,
boot_info,
framebuffer,
physical_memory,
Expand All @@ -97,53 +98,56 @@ impl BootloaderConfig {
concat_4_3(one, two)
};
let buf = concat_16_7(Self::UUID, version);

let buf = concat_23_8(buf, kernel_stack_size.to_le_bytes());

let buf = concat_31_9(buf, kernel_stack.serialize());
let buf = concat_40_9(buf, boot_info.serialize());
let buf = concat_49_9(buf, framebuffer.serialize());
let buf = concat_40_9(buf, kernel_base.serialize());

let buf = concat_49_9(buf, boot_info.serialize());
let buf = concat_58_9(buf, framebuffer.serialize());

let buf = concat_58_10(
let buf = concat_67_10(
buf,
match physical_memory {
Option::None => [0; 10],
Option::Some(m) => concat_1_9([1], m.serialize()),
},
);
let buf = concat_68_10(
let buf = concat_77_10(
buf,
match page_table_recursive {
Option::None => [0; 10],
Option::Some(m) => concat_1_9([1], m.serialize()),
},
);
let buf = concat_78_1(buf, [(*aslr) as u8]);
let buf = concat_79_9(
let buf = concat_87_1(buf, [(*aslr) as u8]);
let buf = concat_88_9(
buf,
match dynamic_range_start {
Option::None => [0; 9],
Option::Some(addr) => concat_1_8([1], addr.to_le_bytes()),
},
);
let buf = concat_88_9(
let buf = concat_97_9(
buf,
match dynamic_range_end {
Option::None => [0; 9],
Option::Some(addr) => concat_1_8([1], addr.to_le_bytes()),
},
);

let buf = concat_97_9(buf, ramdisk_memory.serialize());
let buf = concat_106_9(buf, ramdisk_memory.serialize());

let buf = concat_106_9(
let buf = concat_115_9(
buf,
match minimum_framebuffer_height {
Option::None => [0; 9],
Option::Some(addr) => concat_1_8([1], addr.to_le_bytes()),
},
);

concat_115_9(
concat_124_9(
buf,
match minimum_framebuffer_width {
Option::None => [0; 9],
Expand Down Expand Up @@ -196,6 +200,7 @@ impl BootloaderConfig {

let (mappings, s) = {
let (&kernel_stack, s) = split_array_ref(s);
let (&kernel_base, s) = split_array_ref(s);
let (&boot_info, s) = split_array_ref(s);
let (&framebuffer, s) = split_array_ref(s);
let (&physical_memory_some, s) = split_array_ref(s);
Expand All @@ -211,6 +216,7 @@ impl BootloaderConfig {

let mappings = Mappings {
kernel_stack: Mapping::deserialize(&kernel_stack)?,
kernel_base: Mapping::deserialize(&kernel_base)?,
boot_info: Mapping::deserialize(&boot_info)?,
framebuffer: Mapping::deserialize(&framebuffer)?,
physical_memory: match physical_memory_some {
Expand Down Expand Up @@ -371,6 +377,11 @@ pub struct Mappings {
/// `FixedAddress(0xf_0000_0000)` will result in a guard page at address
/// `0xf_0000_0000` and the kernel stack starting at address `0xf_0000_1000`.
pub kernel_stack: Mapping,
/// Configures the base address of the kernel.
///
/// If a fixed address is set, it must be paged aligned and the kernel must be
/// a position-independent exectuable.
pub kernel_base: Mapping,
/// Specifies where the [`crate::BootInfo`] struct should be placed in virtual memory.
pub boot_info: Mapping,
/// Specifies the mapping of the frame buffer memory region.
Expand Down Expand Up @@ -413,6 +424,7 @@ impl Mappings {
pub const fn new_default() -> Self {
Self {
kernel_stack: Mapping::new_default(),
kernel_base: Mapping::new_default(),
boot_info: Mapping::new_default(),
framebuffer: Mapping::new_default(),
physical_memory: Option::None,
Expand All @@ -430,6 +442,7 @@ impl Mappings {
let recursive = rand::random();
Self {
kernel_stack: Mapping::random(),
kernel_base: Mapping::random(),
boot_info: Mapping::random(),
framebuffer: Mapping::random(),
physical_memory: if phys {
Expand Down
6 changes: 1 addition & 5 deletions common/src/legacy_memory_region.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
use bootloader_api::info::{MemoryRegion, MemoryRegionKind};
use core::{
cmp,
iter::{empty, Empty},
mem::MaybeUninit,
};
use core::{cmp, mem::MaybeUninit};
use x86_64::{
align_down, align_up,
structures::paging::{FrameAllocator, PhysFrame, Size4KiB},
Expand Down
30 changes: 26 additions & 4 deletions common/src/level_4_entries.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use crate::{entropy, load_kernel::VirtualAddressOffset, BootInfo, RawFrameBufferInfo};
use crate::{
entropy,
load_kernel::{calc_elf_memory_requirements, ElfMemoryRequirements, VirtualAddressOffset},
BootInfo, RawFrameBufferInfo,
};
use bootloader_api::{config, info::MemoryRegion, BootloaderConfig};
use core::{alloc::Layout, iter::Step};
use rand::{
Expand All @@ -11,7 +15,7 @@ use x86_64::{
structures::paging::{Page, PageTableIndex, Size4KiB},
PhysAddr, VirtAddr,
};
use xmas_elf::program::ProgramHeader;
use xmas_elf::{header, program::ProgramHeader, ElfFile};

/// Keeps track of used entries in a level 4 page table.
///
Expand All @@ -33,7 +37,8 @@ impl UsedLevel4Entries {
regions_len: usize,
framebuffer: Option<&RawFrameBufferInfo>,
config: &BootloaderConfig,
) -> Self {
kernel_elf: &ElfFile<'_>,
) -> Result<Self, &'static str> {
let mut used = UsedLevel4Entries {
entry_state: [false; 512],
rng: config.mappings.aslr.then(entropy::build_rng),
Expand Down Expand Up @@ -73,6 +78,23 @@ impl UsedLevel4Entries {
used.mark_range_as_used(kernel_stack_address, config.kernel_stack_size);
}

if let config::Mapping::FixedAddress(kernel_base) = config.mappings.kernel_base {
let ElfMemoryRequirements { size, align, .. } =
calc_elf_memory_requirements(kernel_elf);

if !VirtAddr::new(kernel_base).is_aligned(align) {
return Err("kernel_code mapping alignment does not match elf file");
}

used.mark_range_as_used(kernel_base, size);
}
if kernel_elf.header.pt2.type_().as_type() == header::Type::Executable {
let ElfMemoryRequirements { size, min_addr, .. } =
calc_elf_memory_requirements(kernel_elf);

used.mark_range_as_used(min_addr, size);
}

if let config::Mapping::FixedAddress(boot_info_address) = config.mappings.boot_info {
let boot_info_layout = Layout::new::<BootInfo>();
let regions = regions_len + 1; // one region might be split into used/unused
Expand Down Expand Up @@ -110,7 +132,7 @@ impl UsedLevel4Entries {
}
}

used
Ok(used)
}

/// Marks all p4 entries in the range `[address..address+size)` as used.
Expand Down
4 changes: 3 additions & 1 deletion common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,9 @@ where
frame_allocator.len(),
framebuffer,
config,
);
&kernel.elf,
)
.expect("Failed to mark level 4 entries as used");

// Enable support for the no-execute bit in page tables.
enable_nxe_bit();
Expand Down
80 changes: 59 additions & 21 deletions common/src/load_kernel.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{level_4_entries::UsedLevel4Entries, PAGE_SIZE};
use bootloader_api::info::TlsTemplate;
use bootloader_api::{config::Mapping, info::TlsTemplate};
use core::{cmp, iter::Step, mem::size_of, ops::Add};

use x86_64::{
Expand Down Expand Up @@ -59,27 +59,30 @@ where
let virtual_address_offset = match elf_file.header.pt2.type_().as_type() {
header::Type::None => unimplemented!(),
header::Type::Relocatable => unimplemented!(),
header::Type::Executable => VirtualAddressOffset::zero(),
header::Type::Executable => match kernel.config.mappings.kernel_base {
Mapping::Dynamic => VirtualAddressOffset::zero(),
_ => {
return Err(concat!(
"Invalid kernel_code mapping. ",
"Executable can only be mapped at virtual_address_offset 0."
))
}
},
header::Type::SharedObject => {
// Find the highest virtual memory address and the biggest alignment.
let load_program_headers = elf_file
.program_iter()
.filter(|h| matches!(h.get_type(), Ok(Type::Load)));
let max_addr = load_program_headers
.clone()
.map(|h| h.virtual_addr() + h.mem_size())
.max()
.unwrap_or(0);
let min_addr = load_program_headers
.clone()
.map(|h| h.virtual_addr())
.min()
.unwrap_or(0);
let size = max_addr - min_addr;
let align = load_program_headers.map(|h| h.align()).max().unwrap_or(1);

let offset = used_entries.get_free_address(size, align).as_u64();
VirtualAddressOffset::new(i128::from(offset) - i128::from(min_addr))
let ElfMemoryRequirements {
size,
align,
min_addr,
} = calc_elf_memory_requirements(&elf_file);
match kernel.config.mappings.kernel_base {
Mapping::Dynamic => {
let offset = used_entries.get_free_address(size, align).as_u64();
VirtualAddressOffset::new(i128::from(offset) - i128::from(min_addr))
}
Mapping::FixedAddress(address) => {
VirtualAddressOffset::new(i128::from(address))
}
}
}
header::Type::Core => unimplemented!(),
header::Type::ProcessorSpecific(_) => unimplemented!(),
Expand Down Expand Up @@ -750,6 +753,41 @@ pub fn load_kernel(
))
}

/// Basic information about the memory segments of an elf file.
pub struct ElfMemoryRequirements {
/// total size needed for all segments
pub size: u64,
/// memory alignment for the elf file
pub align: u64,
/// the smallest virtual address used by the elf file
pub min_addr: u64,
}

/// Calculates basic requirements needed to allocate memory for an elf file.
pub fn calc_elf_memory_requirements(elf_file: &ElfFile) -> ElfMemoryRequirements {
// Find the highest virtual memory address and the biggest alignment.
let load_program_headers = elf_file
.program_iter()
.filter(|h| matches!(h.get_type(), Ok(Type::Load)));
let max_addr = load_program_headers
.clone()
.map(|h| h.virtual_addr() + h.mem_size())
.max()
.unwrap_or(0);
let min_addr = load_program_headers
.clone()
.map(|h| h.virtual_addr())
.min()
.unwrap_or(0);
let size = max_addr - min_addr;
let align = load_program_headers.map(|h| h.align()).max().unwrap_or(1);
ElfMemoryRequirements {
size,
align,
min_addr,
}
}

/// A helper type used to offset virtual addresses for position independent
/// executables.
#[derive(Clone, Copy)]
Expand Down
Loading

0 comments on commit 55918ea

Please sign in to comment.