Skip to content

Commit 6f88877

Browse files
authored
Merge pull request #50 from rust-osdev/next-version
Version 0.4.0
2 parents 71899f3 + d0c6faa commit 6f88877

File tree

12 files changed

+207
-108
lines changed

12 files changed

+207
-108
lines changed

Cargo.toml

+6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ license = "MIT/Apache-2.0"
88
description = "An experimental pure-Rust x86 bootloader."
99
repository = "https://github.com/rust-osdev/bootloader"
1010
publish-lockfile = true
11+
edition = "2018"
1112

1213
[dependencies]
1314
xmas-elf = "0.6.2"
@@ -23,6 +24,8 @@ features = ["unicode"]
2324
[features]
2425
default = []
2526
vga_320x200 = []
27+
recursive_page_table = []
28+
map_physical_memory = []
2629

2730
[profile.dev]
2831
panic = "abort"
@@ -31,3 +34,6 @@ panic = "abort"
3134
panic = "abort"
3235
lto = false
3336
debug = true
37+
38+
[package.metadata.docs.rs]
39+
features = [ "recursive_page_table", "map_physical_memory" ]

Changelog.md

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
## Breaking
2+
3+
- The level 4 page table is only recursively mapped if the `recursive_page_table` feature is enabled.
4+
- Rename `BootInfo::p4_table_addr` to `BootInfo::recursive_page_table_addr` (only present if the cargo feature is enabled)
5+
- Remove `From<PhysFrameRange>` implemenations for x86_64 `FrameRange`
6+
- This only works when the versions align, so it is not a good general solution.
7+
- Remove unimplemented `BootInfo::package` field.
8+
- Make `BootInfo` non-exhaustive so that we can add additional fields later.
9+
10+
## Other
11+
12+
- Add a `map_physical_memory` feature that maps the complete physical memory to the virtual address space at `BootInfo::physical_memory_offset`.
13+
- Re-export `BootInfo` at the root.

azure-pipelines.yml

+15-18
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,20 @@ steps:
4343
continueOnError: true
4444

4545
- script: |
46+
set -euxo pipefail
4647
curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain $RUSTUP_TOOLCHAIN
4748
echo "##vso[task.setvariable variable=PATH;]$PATH:$HOME/.cargo/bin"
4849
condition: or(eq( variables['Agent.OS'], 'Linux' ), eq( variables['Agent.OS'], 'Darwin' ))
4950
displayName: 'Install Rust (Linux/macOS)'
5051

52+
- script: curl -sSf -o rustup-init.exe https://win.rustup.rs && rustup-init.exe -y --default-toolchain %RUSTUP_TOOLCHAIN%
53+
condition: eq( variables['Agent.OS'], 'Windows_NT' )
54+
displayName: 'Install Rust (Windows)'
55+
5156
- script: |
52-
curl -sSf -o rustup-init.exe https://win.rustup.rs
53-
rustup-init.exe -y --default-toolchain %RUSTUP_TOOLCHAIN%
5457
echo ##vso[task.setvariable variable=PATH;]%PATH%;%USERPROFILE%\.cargo\bin
5558
condition: eq( variables['Agent.OS'], 'Windows_NT' )
56-
displayName: 'Install Rust (Windows)'
59+
displayName: 'Add ~/.cargo/bin to PATH (Windows)'
5760

5861
- script: |
5962
rustc -Vv
@@ -64,16 +67,15 @@ steps:
6467
- script: rustup component add rust-src
6568
displayName: 'Install Rustup Src Component'
6669

67-
- script: |
68-
cargo install cargo-xbuild --debug
69-
cargo install bootimage --debug
70+
- script: cargo install cargo-xbuild bootimage --debug
7071
displayName: 'Install cargo-xbuild and bootimage'
7172

7273
- script: sudo apt install qemu-system-x86
7374
condition: eq( variables['Agent.OS'], 'Linux' )
7475
displayName: 'Install QEMU (Linux)'
7576

7677
- script: |
78+
set -euxo pipefail
7779
export HOMEBREW_NO_AUTO_UPDATE=1
7880
export HOMEBREW_NO_BOTTLE_SOURCE_FALLBACK=1
7981
export HOMEBREW_NO_INSTALL_CLEANUP=1
@@ -87,29 +89,24 @@ steps:
8789
set PATH=%PATH%;C:\Program Files\qemu
8890
qemu-system-x86_64 --version
8991
condition: eq( variables['Agent.OS'], 'Windows_NT' )
92+
failOnStderr: true
9093
displayName: 'Install QEMU (Windows)'
9194

92-
- script: |
93-
cd example-kernel
94-
cargo xbuild --target x86_64-example-kernel.json
95-
cd ..
95+
- script: cargo xbuild --target x86_64-example-kernel.json
96+
workingDirectory: example-kernel
9697
displayName: 'Build Example Kernel'
9798

98-
- script: |
99-
cd builder
100-
cargo run -- --kernel ../example-kernel/target/x86_64-example-kernel/debug/example-kernel
101-
cd ..
99+
- script: cargo run -- --kernel ../example-kernel/target/x86_64-example-kernel/debug/example-kernel
100+
workingDirectory: builder
102101
displayName: 'Build Bootloader'
103102

104103
- bash: |
105104
qemu-system-x86_64 -drive format=raw,file=target/x86_64-bootloader/release/bootimage.bin -device isa-debug-exit,iobase=0xf4,iosize=0x04 -display none
106105
if [ $? -eq 123 ]; then (exit 0); else (exit 1); fi
107106
displayName: 'Test Bootloader'
108107

109-
- script: |
110-
cd builder
111-
cargo run -- --kernel ../example-kernel/target/x86_64-example-kernel/debug/example-kernel --features vga_320x200
112-
cd ..
108+
- script: cargo run -- --kernel ../example-kernel/target/x86_64-example-kernel/debug/example-kernel --features vga_320x200
109+
workingDirectory: builder
113110
displayName: 'Build Bootloader (Feature vga_320x200)'
114111

115112
- bash: |

builder/Cargo.lock

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

builder/src/main.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,13 @@ fn main() {
3333
let mut kernel_file = match File::open(kernel_path) {
3434
Ok(file) => file,
3535
Err(err) => {
36-
writeln!(io::stderr(), "Failed to open kernel at {:?}: {}", kernel_path, err)
37-
.expect("Failed to write to stderr");
36+
writeln!(
37+
io::stderr(),
38+
"Failed to open kernel at {:?}: {}",
39+
kernel_path,
40+
err
41+
)
42+
.expect("Failed to write to stderr");
3843
process::exit(1);
3944
}
4045
};

example-kernel/Cargo.lock

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

src/bootinfo/memory_map.rs

+35-12
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const PAGE_SIZE: u64 = 4096;
55

66
const MAX_MEMORY_MAP_SIZE: usize = 64;
77

8+
/// A map of the physical memory regions of the underlying machine.
89
#[repr(C)]
910
pub struct MemoryMap {
1011
entries: [MemoryRegion; MAX_MEMORY_MAP_SIZE],
@@ -13,6 +14,7 @@ pub struct MemoryMap {
1314
next_entry_index: u64,
1415
}
1516

17+
#[doc(hidden)]
1618
impl MemoryMap {
1719
pub fn new() -> Self {
1820
MemoryMap {
@@ -83,13 +85,17 @@ impl fmt::Debug for MemoryMap {
8385
}
8486
}
8587

88+
/// Represents a region of physical memory.
8689
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8790
#[repr(C)]
8891
pub struct MemoryRegion {
92+
/// The range of frames that belong to the region.
8993
pub range: FrameRange,
94+
/// The type of the region.
9095
pub region_type: MemoryRegionType,
9196
}
9297

98+
#[doc(hidden)]
9399
impl MemoryRegion {
94100
pub fn empty() -> Self {
95101
MemoryRegion {
@@ -102,11 +108,19 @@ impl MemoryRegion {
102108
}
103109
}
104110

111+
/// A range of frames with an exclusive upper bound.
105112
#[derive(Clone, Copy, PartialEq, Eq)]
106113
#[repr(C)]
107114
pub struct FrameRange {
115+
/// The frame _number_ of the first 4KiB frame in the region.
116+
///
117+
/// This convert this frame number to a physical address, multiply it with the
118+
/// page size (4KiB).
108119
pub start_frame_number: u64,
109-
// exclusive
120+
/// The frame _number_ of the first 4KiB frame that does no longer belong to the region.
121+
///
122+
/// This convert this frame number to a physical address, multiply it with the
123+
/// page size (4KiB).
110124
pub end_frame_number: u64,
111125
}
112126

@@ -122,14 +136,17 @@ impl FrameRange {
122136
}
123137
}
124138

139+
/// Returns true if the frame range contains no frames.
125140
pub fn is_empty(&self) -> bool {
126141
self.start_frame_number == self.end_frame_number
127142
}
128143

144+
/// Returns the physical start address of the memory region.
129145
pub fn start_addr(&self) -> u64 {
130146
self.start_frame_number * PAGE_SIZE
131147
}
132148

149+
/// Returns the physical end address of the memory region.
133150
pub fn end_addr(&self) -> u64 {
134151
self.end_frame_number * PAGE_SIZE
135152
}
@@ -146,41 +163,47 @@ impl fmt::Debug for FrameRange {
146163
}
147164
}
148165

166+
/// Represents possible types for memory regions.
149167
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
150168
#[repr(C)]
151169
pub enum MemoryRegionType {
152-
/// free RAM
170+
/// Unused memory, can be freely used by the kernel.
153171
Usable,
154-
/// used RAM
172+
/// Memory that is already in use.
155173
InUse,
156-
/// unusable
174+
/// Memory reserved by the hardware. Not usable.
157175
Reserved,
158176
/// ACPI reclaimable memory
159177
AcpiReclaimable,
160178
/// ACPI NVS memory
161179
AcpiNvs,
162180
/// Area containing bad memory
163181
BadMemory,
164-
/// kernel memory
182+
/// Memory used for loading the kernel.
165183
Kernel,
166-
/// kernel stack memory
184+
/// Memory used for the kernel stack.
167185
KernelStack,
168-
/// memory used by page tables
186+
/// Memory used for creating page tables.
169187
PageTable,
170-
/// memory used by the bootloader
188+
/// Memory used by the bootloader.
171189
Bootloader,
172-
/// frame at address zero
190+
/// Frame at address zero.
173191
///
174192
/// (shouldn't be used because it's easy to make mistakes related to null pointers)
175193
FrameZero,
176-
/// an empty region with size 0
194+
/// An empty region with size 0
177195
Empty,
178-
/// used for storing the boot information
196+
/// Memory used for storing the boot information.
179197
BootInfo,
180-
/// used for storing the supplied package
198+
/// Memory used for storing the supplied package
181199
Package,
200+
/// Additional variant to ensure that we can add more variants in the future without
201+
/// breaking backwards compatibility.
202+
#[doc(hidden)]
203+
NonExhaustive,
182204
}
183205

206+
#[doc(hidden)]
184207
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
185208
#[repr(C)]
186209
pub struct E820MemoryRegion {

src/bootinfo/mod.rs

+44-47
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,66 @@
1+
//! Provides boot information to the kernel.
2+
13
#![deny(improper_ctypes)]
24

35
pub use self::memory_map::*;
4-
use core::ops::Deref;
5-
use core::slice;
66

77
mod memory_map;
88

9+
/// This structure represents the information that the bootloader passes to the kernel.
10+
///
11+
/// The information is passed as an argument to the entry point:
12+
///
13+
/// ```ignore
14+
/// pub extern "C" fn _start(boot_info: &'static BootInfo) -> ! {
15+
/// // […]
16+
/// }
17+
/// ```
18+
///
19+
/// Note that no type checking occurs for the entry point function, so be careful to
20+
/// use the correct argument types. To ensure that the entry point function has the correct
21+
/// signature, use the [`entry_point`] macro.
922
#[derive(Debug)]
1023
#[repr(C)]
1124
pub struct BootInfo {
12-
pub p4_table_addr: u64,
25+
/// A map of the physical memory regions of the underlying machine.
26+
///
27+
/// The bootloader queries this information from the BIOS/UEFI firmware and translates this
28+
/// information to Rust types. It also marks any memory regions that the bootloader uses in
29+
/// the memory map before passing it to the kernel. Regions marked as usable can be freely
30+
/// used by the kernel.
1331
pub memory_map: MemoryMap,
14-
pub package: Package,
15-
}
16-
17-
#[derive(Debug)]
18-
#[repr(C)]
19-
pub struct Package {
20-
ptr: *const u8,
21-
len: u64,
22-
}
23-
24-
impl Deref for Package {
25-
type Target = [u8];
26-
fn deref(&self) -> &[u8] {
27-
unsafe { slice::from_raw_parts(self.ptr, self.len as usize) }
28-
}
32+
/// The virtual address of the recursively mapped level 4 page table.
33+
#[cfg(feature = "recursive_page_table")]
34+
pub recursive_page_table_addr: u64,
35+
/// The offset into the virtual address space where the physical memory is mapped.
36+
///
37+
/// Physical addresses can be converted to virtual addresses by adding this offset to them.
38+
///
39+
/// The mapping of the physical memory allows to access arbitrary physical frames. Accessing
40+
/// frames that are also mapped at other virtual addresses can easily break memory safety and
41+
/// cause undefined behavior. Only frames reported as `USABLE` by the memory map in the `BootInfo`
42+
/// can be safely accessed.
43+
#[cfg(feature = "map_physical_memory")]
44+
pub physical_memory_offset: u64,
45+
_non_exhaustive: u8, // `()` is not FFI safe
2946
}
3047

3148
impl BootInfo {
32-
pub fn new(p4_table_addr: u64, memory_map: MemoryMap, package: &'static [u8]) -> Self {
49+
/// Create a new boot information structure. This function is only for internal purposes.
50+
#[allow(unused_variables)]
51+
#[doc(hidden)]
52+
pub fn new(memory_map: MemoryMap, recursive_page_table_addr: u64, physical_memory_offset: u64) -> Self {
3353
BootInfo {
34-
p4_table_addr,
3554
memory_map,
36-
package: Package {
37-
ptr: package.as_ptr(),
38-
len: package.len() as u64,
39-
},
55+
#[cfg(feature = "recursive_page_table")]
56+
recursive_page_table_addr,
57+
#[cfg(feature = "map_physical_memory")]
58+
physical_memory_offset,
59+
_non_exhaustive: 0,
4060
}
4161
}
4262
}
4363

4464
extern "C" {
4565
fn _improper_ctypes_check(_boot_info: BootInfo);
4666
}
47-
48-
use x86_64::{
49-
structures::paging::{PhysFrame, PhysFrameRange},
50-
PhysAddr,
51-
};
52-
53-
impl From<FrameRange> for PhysFrameRange {
54-
fn from(range: FrameRange) -> Self {
55-
PhysFrameRange {
56-
start: PhysFrame::from_start_address(PhysAddr::new(range.start_addr())).unwrap(),
57-
end: PhysFrame::from_start_address(PhysAddr::new(range.end_addr())).unwrap(),
58-
}
59-
}
60-
}
61-
62-
impl From<PhysFrameRange> for FrameRange {
63-
fn from(range: PhysFrameRange) -> Self {
64-
FrameRange::new(
65-
range.start.start_address().as_u64(),
66-
range.end.start_address().as_u64(),
67-
)
68-
}
69-
}

0 commit comments

Comments
 (0)