Skip to content

Version 0.4.0 #50

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 26 commits into from
Mar 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9f5cd16
Only enable recursive level 4 table mapping if a cargo feature is passed
phil-opp Jan 29, 2019
d4abc5b
Remove useless re-export in main.rs
phil-opp Jan 29, 2019
95663ac
Update to 2018 edition
phil-opp Jan 29, 2019
6688294
Run rustfmt
phil-opp Jan 29, 2019
00d7a99
Replace BootInfo::p4_table_addr with RECURSIVE_LEVEL_4_TABLE_ADDR con…
phil-opp Jan 29, 2019
31a8d24
Assert that exported address equals mapped address
phil-opp Jan 29, 2019
602a7a5
Remove `From<PhysFrameRange>` implementations for x86_64 `FrameRange`
phil-opp Jan 29, 2019
4672878
Remove BootInfo::package
phil-opp Jan 29, 2019
6bef649
Remove unused imports and run rustfmt
phil-opp Jan 29, 2019
7319ddc
Reexport BootInfo type
phil-opp Jan 29, 2019
42605b2
Add private field to BootInfo to allow future extension
phil-opp Jan 29, 2019
0b05c9e
Add a feature to map the complete physical memory into the virtual ad…
phil-opp Jan 29, 2019
4268a06
Add a changelog
phil-opp Jan 29, 2019
4ebc542
Don't use uniform paths to fix docs.rs build
phil-opp Jan 29, 2019
01f4043
Enable features on docs.rs
phil-opp Jan 29, 2019
5793367
Keep recursive level 4 address and physical memory offset in BootInfo
phil-opp Jan 29, 2019
50ad5a5
Rename `recursive_level_4_table` feature to `recursive_page_table`
phil-opp Mar 4, 2019
60d733b
Update Changelog
phil-opp Mar 4, 2019
6a9c36d
Use single line scripts or `set -euxo pipefail` for azure pipelines
phil-opp Mar 4, 2019
08ff6c8
Fix build
phil-opp Mar 4, 2019
cc308ab
Run `cargo fmt`
phil-opp Mar 4, 2019
817ab6d
The latest Rust version generates comments in Cargo.lock
phil-opp Mar 4, 2019
6cffc7a
set -euxo pipefail does not work on windows
phil-opp Mar 4, 2019
d16023b
Rustup prints to stderr by default
phil-opp Mar 4, 2019
e4aded3
Set PATH correctly on Windows
phil-opp Mar 4, 2019
d0c6faa
Document the crate and hide certain types in the docs
phil-opp Mar 9, 2019
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
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ license = "MIT/Apache-2.0"
description = "An experimental pure-Rust x86 bootloader."
repository = "https://github.com/rust-osdev/bootloader"
publish-lockfile = true
edition = "2018"

[dependencies]
xmas-elf = "0.6.2"
Expand All @@ -23,6 +24,8 @@ features = ["unicode"]
[features]
default = []
vga_320x200 = []
recursive_page_table = []
map_physical_memory = []

[profile.dev]
panic = "abort"
Expand All @@ -31,3 +34,6 @@ panic = "abort"
panic = "abort"
lto = false
debug = true

[package.metadata.docs.rs]
features = [ "recursive_page_table", "map_physical_memory" ]
13 changes: 13 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## Breaking

- The level 4 page table is only recursively mapped if the `recursive_page_table` feature is enabled.
- Rename `BootInfo::p4_table_addr` to `BootInfo::recursive_page_table_addr` (only present if the cargo feature is enabled)
- Remove `From<PhysFrameRange>` implemenations for x86_64 `FrameRange`
- This only works when the versions align, so it is not a good general solution.
- Remove unimplemented `BootInfo::package` field.
- Make `BootInfo` non-exhaustive so that we can add additional fields later.

## Other

- Add a `map_physical_memory` feature that maps the complete physical memory to the virtual address space at `BootInfo::physical_memory_offset`.
- Re-export `BootInfo` at the root.
33 changes: 15 additions & 18 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,20 @@ steps:
continueOnError: true

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

- script: curl -sSf -o rustup-init.exe https://win.rustup.rs && rustup-init.exe -y --default-toolchain %RUSTUP_TOOLCHAIN%
condition: eq( variables['Agent.OS'], 'Windows_NT' )
displayName: 'Install Rust (Windows)'

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

- script: |
rustc -Vv
Expand All @@ -64,16 +67,15 @@ steps:
- script: rustup component add rust-src
displayName: 'Install Rustup Src Component'

- script: |
cargo install cargo-xbuild --debug
cargo install bootimage --debug
- script: cargo install cargo-xbuild bootimage --debug
displayName: 'Install cargo-xbuild and bootimage'

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

- script: |
set -euxo pipefail
export HOMEBREW_NO_AUTO_UPDATE=1
export HOMEBREW_NO_BOTTLE_SOURCE_FALLBACK=1
export HOMEBREW_NO_INSTALL_CLEANUP=1
Expand All @@ -87,29 +89,24 @@ steps:
set PATH=%PATH%;C:\Program Files\qemu
qemu-system-x86_64 --version
condition: eq( variables['Agent.OS'], 'Windows_NT' )
failOnStderr: true
displayName: 'Install QEMU (Windows)'

- script: |
cd example-kernel
cargo xbuild --target x86_64-example-kernel.json
cd ..
- script: cargo xbuild --target x86_64-example-kernel.json
workingDirectory: example-kernel
displayName: 'Build Example Kernel'

- script: |
cd builder
cargo run -- --kernel ../example-kernel/target/x86_64-example-kernel/debug/example-kernel
cd ..
- script: cargo run -- --kernel ../example-kernel/target/x86_64-example-kernel/debug/example-kernel
workingDirectory: builder
displayName: 'Build Bootloader'

- bash: |
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
if [ $? -eq 123 ]; then (exit 0); else (exit 1); fi
displayName: 'Test Bootloader'

- script: |
cd builder
cargo run -- --kernel ../example-kernel/target/x86_64-example-kernel/debug/example-kernel --features vga_320x200
cd ..
- script: cargo run -- --kernel ../example-kernel/target/x86_64-example-kernel/debug/example-kernel --features vga_320x200
workingDirectory: builder
displayName: 'Build Bootloader (Feature vga_320x200)'

- bash: |
Expand Down
2 changes: 2 additions & 0 deletions builder/Cargo.lock

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

9 changes: 7 additions & 2 deletions builder/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,13 @@ fn main() {
let mut kernel_file = match File::open(kernel_path) {
Ok(file) => file,
Err(err) => {
writeln!(io::stderr(), "Failed to open kernel at {:?}: {}", kernel_path, err)
.expect("Failed to write to stderr");
writeln!(
io::stderr(),
"Failed to open kernel at {:?}: {}",
kernel_path,
err
)
.expect("Failed to write to stderr");
process::exit(1);
}
};
Expand Down
2 changes: 2 additions & 0 deletions example-kernel/Cargo.lock

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

47 changes: 35 additions & 12 deletions src/bootinfo/memory_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const PAGE_SIZE: u64 = 4096;

const MAX_MEMORY_MAP_SIZE: usize = 64;

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

#[doc(hidden)]
impl MemoryMap {
pub fn new() -> Self {
MemoryMap {
Expand Down Expand Up @@ -83,13 +85,17 @@ impl fmt::Debug for MemoryMap {
}
}

/// Represents a region of physical memory.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub struct MemoryRegion {
/// The range of frames that belong to the region.
pub range: FrameRange,
/// The type of the region.
pub region_type: MemoryRegionType,
}

#[doc(hidden)]
impl MemoryRegion {
pub fn empty() -> Self {
MemoryRegion {
Expand All @@ -102,11 +108,19 @@ impl MemoryRegion {
}
}

/// A range of frames with an exclusive upper bound.
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub struct FrameRange {
/// The frame _number_ of the first 4KiB frame in the region.
///
/// This convert this frame number to a physical address, multiply it with the
/// page size (4KiB).
pub start_frame_number: u64,
// exclusive
/// The frame _number_ of the first 4KiB frame that does no longer belong to the region.
///
/// This convert this frame number to a physical address, multiply it with the
/// page size (4KiB).
pub end_frame_number: u64,
}

Expand All @@ -122,14 +136,17 @@ impl FrameRange {
}
}

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

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

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

/// Represents possible types for memory regions.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub enum MemoryRegionType {
/// free RAM
/// Unused memory, can be freely used by the kernel.
Usable,
/// used RAM
/// Memory that is already in use.
InUse,
/// unusable
/// Memory reserved by the hardware. Not usable.
Reserved,
/// ACPI reclaimable memory
AcpiReclaimable,
/// ACPI NVS memory
AcpiNvs,
/// Area containing bad memory
BadMemory,
/// kernel memory
/// Memory used for loading the kernel.
Kernel,
/// kernel stack memory
/// Memory used for the kernel stack.
KernelStack,
/// memory used by page tables
/// Memory used for creating page tables.
PageTable,
/// memory used by the bootloader
/// Memory used by the bootloader.
Bootloader,
/// frame at address zero
/// Frame at address zero.
///
/// (shouldn't be used because it's easy to make mistakes related to null pointers)
FrameZero,
/// an empty region with size 0
/// An empty region with size 0
Empty,
/// used for storing the boot information
/// Memory used for storing the boot information.
BootInfo,
/// used for storing the supplied package
/// Memory used for storing the supplied package
Package,
/// Additional variant to ensure that we can add more variants in the future without
/// breaking backwards compatibility.
#[doc(hidden)]
NonExhaustive,
}

#[doc(hidden)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub struct E820MemoryRegion {
Expand Down
91 changes: 44 additions & 47 deletions src/bootinfo/mod.rs
Original file line number Diff line number Diff line change
@@ -1,69 +1,66 @@
//! Provides boot information to the kernel.

#![deny(improper_ctypes)]

pub use self::memory_map::*;
use core::ops::Deref;
use core::slice;

mod memory_map;

/// This structure represents the information that the bootloader passes to the kernel.
///
/// The information is passed as an argument to the entry point:
///
/// ```ignore
/// pub extern "C" fn _start(boot_info: &'static BootInfo) -> ! {
/// // […]
/// }
/// ```
///
/// Note that no type checking occurs for the entry point function, so be careful to
/// use the correct argument types. To ensure that the entry point function has the correct
/// signature, use the [`entry_point`] macro.
#[derive(Debug)]
#[repr(C)]
pub struct BootInfo {
pub p4_table_addr: u64,
/// A map of the physical memory regions of the underlying machine.
///
/// The bootloader queries this information from the BIOS/UEFI firmware and translates this
/// information to Rust types. It also marks any memory regions that the bootloader uses in
/// the memory map before passing it to the kernel. Regions marked as usable can be freely
/// used by the kernel.
pub memory_map: MemoryMap,
pub package: Package,
}

#[derive(Debug)]
#[repr(C)]
pub struct Package {
ptr: *const u8,
len: u64,
}

impl Deref for Package {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.ptr, self.len as usize) }
}
/// The virtual address of the recursively mapped level 4 page table.
#[cfg(feature = "recursive_page_table")]
pub recursive_page_table_addr: u64,
/// The offset into the virtual address space where the physical memory is mapped.
///
/// Physical addresses can be converted to virtual addresses by adding this offset to them.
///
/// The mapping of the physical memory allows to access arbitrary physical frames. Accessing
/// frames that are also mapped at other virtual addresses can easily break memory safety and
/// cause undefined behavior. Only frames reported as `USABLE` by the memory map in the `BootInfo`
/// can be safely accessed.
#[cfg(feature = "map_physical_memory")]
pub physical_memory_offset: u64,
_non_exhaustive: u8, // `()` is not FFI safe
}

impl BootInfo {
pub fn new(p4_table_addr: u64, memory_map: MemoryMap, package: &'static [u8]) -> Self {
/// Create a new boot information structure. This function is only for internal purposes.
#[allow(unused_variables)]
#[doc(hidden)]
pub fn new(memory_map: MemoryMap, recursive_page_table_addr: u64, physical_memory_offset: u64) -> Self {
BootInfo {
p4_table_addr,
memory_map,
package: Package {
ptr: package.as_ptr(),
len: package.len() as u64,
},
#[cfg(feature = "recursive_page_table")]
recursive_page_table_addr,
#[cfg(feature = "map_physical_memory")]
physical_memory_offset,
_non_exhaustive: 0,
}
}
}

extern "C" {
fn _improper_ctypes_check(_boot_info: BootInfo);
}

use x86_64::{
structures::paging::{PhysFrame, PhysFrameRange},
PhysAddr,
};

impl From<FrameRange> for PhysFrameRange {
fn from(range: FrameRange) -> Self {
PhysFrameRange {
start: PhysFrame::from_start_address(PhysAddr::new(range.start_addr())).unwrap(),
end: PhysFrame::from_start_address(PhysAddr::new(range.end_addr())).unwrap(),
}
}
}

impl From<PhysFrameRange> for FrameRange {
fn from(range: PhysFrameRange) -> Self {
FrameRange::new(
range.start.start_address().as_u64(),
range.end.start_address().as_u64(),
)
}
}
Loading