Skip to content

Commit d6a1ccd

Browse files
committed
Specify kernel-code virt addr in BootloaderConfig
1 parent 170320b commit d6a1ccd

16 files changed

+487
-220
lines changed

Cargo.lock

+205-177
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+6-5
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,15 @@ exclude = ["examples/basic", "examples/test_framework", "tests/test_kernels/*"]
2222

2323
[workspace.package]
2424
# don't forget to update `workspace.dependencies` below
25-
version = "0.11.10"
25+
version = "0.12.0"
2626
license = "MIT OR Apache-2.0"
2727
repository = "https://github.com/rust-osdev/bootloader"
2828

2929
[workspace.dependencies]
30-
bootloader_api = { version = "0.11.10", path = "api" }
31-
bootloader-x86_64-common = { version = "0.11.10", path = "common" }
32-
bootloader-boot-config = { version = "0.11.10", path = "common/config" }
33-
bootloader-x86_64-bios-common = { version = "0.11.10", path = "bios/common" }
30+
bootloader_api = { version = "0.12.0", path = "api" }
31+
bootloader-x86_64-common = { version = "0.12.0", path = "common" }
32+
bootloader-boot-config = { version = "0.12.0", path = "common/config" }
33+
bootloader-x86_64-bios-common = { version = "0.12.0", path = "bios/common" }
3434

3535
[features]
3636
default = ["bios", "uefi"]
@@ -60,6 +60,7 @@ test_kernel_config_file = { path = "tests/test_kernels/config_file", artifact =
6060
test_kernel_min_stack = { path = "tests/test_kernels/min_stack", artifact = "bin", target = "x86_64-unknown-none" }
6161
test_kernel_lower_memory_free = { path = "tests/test_kernels/lower_memory_free", artifact = "bin", target = "x86_64-unknown-none" }
6262
test_kernel_write_usable_memory = { path = "tests/test_kernels/write_usable_memory", artifact = "bin", target = "x86_64-unknown-none" }
63+
test_kernel_fixed_kernel_address = { path = "tests/test_kernels/fixed_kernel_address", artifact = "bin", target = "x86_64-unknown-none" }
6364

6465
[profile.dev]
6566
panic = "abort"

Changelog.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Unreleased
22

3+
## Features
4+
5+
* add `kernel_code` mapping to the BootloaderConfig to specify the virtual address
6+
at which the kernel is loaded.
7+
8+
39
# 0.11.10 – 2025-02-10
410

511
* [Remove "UEFI boot" log message](https://github.com/rust-osdev/bootloader/pull/476)

api/build.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,16 @@ fn main() {
1515
(31, 9),
1616
(40, 9),
1717
(49, 9),
18-
(58, 10),
19-
(68, 10),
20-
(78, 1),
21-
(79, 9),
18+
(58, 9),
19+
(67, 10),
20+
(77, 10),
21+
(87, 1),
2222
(88, 9),
2323
(97, 9),
2424
(106, 9),
2525
(115, 9),
26+
(124, 9),
27+
(133, 9),
2628
];
2729

2830
let mut code = String::new();

api/src/config.rs

+24-11
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ impl BootloaderConfig {
4343
0x3D,
4444
];
4545
#[doc(hidden)]
46-
pub const SERIALIZED_LEN: usize = 124;
46+
pub const SERIALIZED_LEN: usize = 133;
4747

4848
/// Creates a new default configuration with the following values:
4949
///
@@ -77,6 +77,7 @@ impl BootloaderConfig {
7777
} = version;
7878
let Mappings {
7979
kernel_stack,
80+
kernel_code,
8081
boot_info,
8182
framebuffer,
8283
physical_memory,
@@ -97,53 +98,56 @@ impl BootloaderConfig {
9798
concat_4_3(one, two)
9899
};
99100
let buf = concat_16_7(Self::UUID, version);
101+
100102
let buf = concat_23_8(buf, kernel_stack_size.to_le_bytes());
101103

102104
let buf = concat_31_9(buf, kernel_stack.serialize());
103-
let buf = concat_40_9(buf, boot_info.serialize());
104-
let buf = concat_49_9(buf, framebuffer.serialize());
105+
let buf = concat_40_9(buf, kernel_code.serialize());
106+
107+
let buf = concat_49_9(buf, boot_info.serialize());
108+
let buf = concat_58_9(buf, framebuffer.serialize());
105109

106-
let buf = concat_58_10(
110+
let buf = concat_67_10(
107111
buf,
108112
match physical_memory {
109113
Option::None => [0; 10],
110114
Option::Some(m) => concat_1_9([1], m.serialize()),
111115
},
112116
);
113-
let buf = concat_68_10(
117+
let buf = concat_77_10(
114118
buf,
115119
match page_table_recursive {
116120
Option::None => [0; 10],
117121
Option::Some(m) => concat_1_9([1], m.serialize()),
118122
},
119123
);
120-
let buf = concat_78_1(buf, [(*aslr) as u8]);
121-
let buf = concat_79_9(
124+
let buf = concat_87_1(buf, [(*aslr) as u8]);
125+
let buf = concat_88_9(
122126
buf,
123127
match dynamic_range_start {
124128
Option::None => [0; 9],
125129
Option::Some(addr) => concat_1_8([1], addr.to_le_bytes()),
126130
},
127131
);
128-
let buf = concat_88_9(
132+
let buf = concat_97_9(
129133
buf,
130134
match dynamic_range_end {
131135
Option::None => [0; 9],
132136
Option::Some(addr) => concat_1_8([1], addr.to_le_bytes()),
133137
},
134138
);
135139

136-
let buf = concat_97_9(buf, ramdisk_memory.serialize());
140+
let buf = concat_106_9(buf, ramdisk_memory.serialize());
137141

138-
let buf = concat_106_9(
142+
let buf = concat_115_9(
139143
buf,
140144
match minimum_framebuffer_height {
141145
Option::None => [0; 9],
142146
Option::Some(addr) => concat_1_8([1], addr.to_le_bytes()),
143147
},
144148
);
145149

146-
concat_115_9(
150+
concat_124_9(
147151
buf,
148152
match minimum_framebuffer_width {
149153
Option::None => [0; 9],
@@ -196,6 +200,7 @@ impl BootloaderConfig {
196200

197201
let (mappings, s) = {
198202
let (&kernel_stack, s) = split_array_ref(s);
203+
let (&kernel_code, s) = split_array_ref(s);
199204
let (&boot_info, s) = split_array_ref(s);
200205
let (&framebuffer, s) = split_array_ref(s);
201206
let (&physical_memory_some, s) = split_array_ref(s);
@@ -211,6 +216,7 @@ impl BootloaderConfig {
211216

212217
let mappings = Mappings {
213218
kernel_stack: Mapping::deserialize(&kernel_stack)?,
219+
kernel_code: Mapping::deserialize(&kernel_code)?,
214220
boot_info: Mapping::deserialize(&boot_info)?,
215221
framebuffer: Mapping::deserialize(&framebuffer)?,
216222
physical_memory: match physical_memory_some {
@@ -371,6 +377,11 @@ pub struct Mappings {
371377
/// `FixedAddress(0xf_0000_0000)` will result in a guard page at address
372378
/// `0xf_0000_0000` and the kernel stack starting at address `0xf_0000_1000`.
373379
pub kernel_stack: Mapping,
380+
/// Configures how the kernel code should be mapped.
381+
///
382+
/// If a fixed address is set, it must be paged alignned and requires the kernel to
383+
/// be a shared object.
384+
pub kernel_code: Mapping,
374385
/// Specifies where the [`crate::BootInfo`] struct should be placed in virtual memory.
375386
pub boot_info: Mapping,
376387
/// Specifies the mapping of the frame buffer memory region.
@@ -413,6 +424,7 @@ impl Mappings {
413424
pub const fn new_default() -> Self {
414425
Self {
415426
kernel_stack: Mapping::new_default(),
427+
kernel_code: Mapping::new_default(),
416428
boot_info: Mapping::new_default(),
417429
framebuffer: Mapping::new_default(),
418430
physical_memory: Option::None,
@@ -430,6 +442,7 @@ impl Mappings {
430442
let recursive = rand::random();
431443
Self {
432444
kernel_stack: Mapping::random(),
445+
kernel_code: Mapping::random(),
433446
boot_info: Mapping::random(),
434447
framebuffer: Mapping::random(),
435448
physical_memory: if phys {

common/src/level_4_entries.rs

+23-1
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,10 @@ impl UsedLevel4Entries {
126126
}
127127
}
128128

129-
fn mark_p4_index_as_used(&mut self, p4_index: PageTableIndex) {
129+
fn mark_p4_index_as_used(&mut self, p4_index: PageTableIndex) -> bool {
130+
let used = self.entry_state[usize::from(p4_index)];
130131
self.entry_state[usize::from(p4_index)] = true;
132+
used
131133
}
132134

133135
/// Marks the virtual address range of all segments as used.
@@ -144,6 +146,26 @@ impl UsedLevel4Entries {
144146
}
145147
}
146148

149+
/// Marks all p4 entries in the range `[address..address+size)` as used.
150+
pub fn try_mark_range_as_used(&mut self, address: u64, size: u64) -> Result<(), ()> {
151+
let start = VirtAddr::new(address);
152+
let end_inclusive = (start + size) - 1;
153+
let start_page = Page::<Size4KiB>::containing_address(start);
154+
let end_page_inclusive = Page::<Size4KiB>::containing_address(end_inclusive);
155+
156+
let mut any_used_before = false;
157+
for p4_index in u16::from(start_page.p4_index())..=u16::from(end_page_inclusive.p4_index())
158+
{
159+
any_used_before |= self.mark_p4_index_as_used(PageTableIndex::new(p4_index));
160+
}
161+
162+
if any_used_before {
163+
Err(())
164+
} else {
165+
Ok(())
166+
}
167+
}
168+
147169
/// Returns the first index of a `num` contiguous unused level 4 entries and marks them as
148170
/// used. If `CONFIG.aslr` is enabled, this will return random contiguous available entries.
149171
///

common/src/load_kernel.rs

+37-21
Original file line numberDiff line numberDiff line change
@@ -59,27 +59,43 @@ where
5959
let virtual_address_offset = match elf_file.header.pt2.type_().as_type() {
6060
header::Type::None => unimplemented!(),
6161
header::Type::Relocatable => unimplemented!(),
62-
header::Type::Executable => VirtualAddressOffset::zero(),
62+
header::Type::Executable => match kernel.config.mappings.kernel_code {
63+
bootloader_api::config::Mapping::Dynamic
64+
| bootloader_api::config::Mapping::FixedAddress(0) => VirtualAddressOffset::zero(),
65+
_ => return Err("Invalid kernel_code mapping. Executable can only be mapped at virtual_address_offset 0."),
66+
},
6367
header::Type::SharedObject => {
64-
// Find the highest virtual memory address and the biggest alignment.
65-
let load_program_headers = elf_file
66-
.program_iter()
67-
.filter(|h| matches!(h.get_type(), Ok(Type::Load)));
68-
let max_addr = load_program_headers
69-
.clone()
70-
.map(|h| h.virtual_addr() + h.mem_size())
71-
.max()
72-
.unwrap_or(0);
73-
let min_addr = load_program_headers
74-
.clone()
75-
.map(|h| h.virtual_addr())
76-
.min()
77-
.unwrap_or(0);
78-
let size = max_addr - min_addr;
79-
let align = load_program_headers.map(|h| h.align()).max().unwrap_or(1);
80-
81-
let offset = used_entries.get_free_address(size, align).as_u64();
82-
VirtualAddressOffset::new(i128::from(offset) - i128::from(min_addr))
68+
// Find the highest virtual memory address and the biggest alignment.
69+
let load_program_headers = elf_file
70+
.program_iter()
71+
.filter(|h| matches!(h.get_type(), Ok(Type::Load)));
72+
let max_addr = load_program_headers
73+
.clone()
74+
.map(|h| h.virtual_addr() + h.mem_size())
75+
.max()
76+
.unwrap_or(0);
77+
let min_addr = load_program_headers
78+
.clone()
79+
.map(|h| h.virtual_addr())
80+
.min()
81+
.unwrap_or(0);
82+
let size = max_addr - min_addr;
83+
let align = load_program_headers.map(|h| h.align()).max().unwrap_or(1);
84+
match kernel.config.mappings.kernel_code {
85+
bootloader_api::config::Mapping::Dynamic => {
86+
let offset = used_entries.get_free_address(size, align).as_u64();
87+
VirtualAddressOffset::new(i128::from(offset) - i128::from(min_addr))
88+
}
89+
bootloader_api::config::Mapping::FixedAddress(address) => {
90+
if !VirtAddr::new(address).is_aligned(align) {
91+
return Err("kernel_code mapping alignment does not match elf file");
92+
}
93+
if used_entries.try_mark_range_as_used(address, size).is_err() {
94+
return Err("kernel_code mapping is already in use");
95+
}
96+
VirtualAddressOffset::new(i128::from(address))
97+
}
98+
}
8399
}
84100
header::Type::Core => unimplemented!(),
85101
header::Type::ProcessorSpecific(_) => unimplemented!(),
@@ -151,7 +167,7 @@ where
151167
Ok(tls_template)
152168
}
153169

154-
fn entry_point(&self) -> VirtAddr {
170+
pub fn entry_point(&self) -> VirtAddr {
155171
VirtAddr::new(self.inner.virtual_address_offset + self.elf_file.header.pt2.entry_point())
156172
}
157173
}

tests/fixed_kernel_address.rs

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
use bootloader_test_runner::run_test_kernel;
2+
3+
#[test]
4+
fn basic_boot() {
5+
run_test_kernel(env!(
6+
"CARGO_BIN_FILE_TEST_KERNEL_FIXED_KERNEL_ADDRESS_basic_boot"
7+
));
8+
}
9+
10+
#[test]
11+
fn should_panic() {
12+
run_test_kernel(env!(
13+
"CARGO_BIN_FILE_TEST_KERNEL_FIXED_KERNEL_ADDRESS_should_panic"
14+
));
15+
}
16+
17+
#[test]
18+
fn check_boot_info() {
19+
run_test_kernel(env!(
20+
"CARGO_BIN_FILE_TEST_KERNEL_FIXED_KERNEL_ADDRESS_check_boot_info"
21+
));
22+
}
23+
24+
#[test]
25+
fn verify_kernel_address() {
26+
run_test_kernel(env!(
27+
"CARGO_BIN_FILE_TEST_KERNEL_FIXED_KERNEL_ADDRESS_verify_kernel_address"
28+
));
29+
}

tests/test_kernels/Cargo.lock

+10-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/test_kernels/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ members = [
1212
"min_stack",
1313
"lower_memory_free",
1414
"write_usable_memory",
15+
"fixed_kernel_address",
1516
]
1617

1718
[profile.release]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "test_kernel_fixed_kernel_address"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[target.'cfg(target_arch = "x86_64")'.dependencies]
7+
bootloader_api = { path = "../../../api" }
8+
x86_64 = { version = "0.15.2", default-features = false, features = [
9+
"instructions",
10+
] }
11+
uart_16550 = "0.2.10"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#![no_std] // don't link the Rust standard library
2+
#![no_main] // disable all Rust-level entry points
3+
4+
use bootloader_api::{entry_point, BootInfo};
5+
use test_kernel_fixed_kernel_address::{exit_qemu, QemuExitCode, BOOTLOADER_CONFIG};
6+
7+
entry_point!(kernel_main, config = &BOOTLOADER_CONFIG);
8+
9+
fn kernel_main(_boot_info: &'static mut BootInfo) -> ! {
10+
exit_qemu(QemuExitCode::Success);
11+
}
12+
13+
/// This function is called on panic.
14+
#[cfg(not(test))]
15+
#[panic_handler]
16+
fn panic(_info: &core::panic::PanicInfo) -> ! {
17+
exit_qemu(QemuExitCode::Failed);
18+
}

0 commit comments

Comments
 (0)