Skip to content

Commit 46c7725

Browse files
committed
Create migration guides
1 parent 7ea5ecf commit 46c7725

File tree

2 files changed

+250
-0
lines changed

2 files changed

+250
-0
lines changed

doc/migration/v0.10.md

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# Migration from bootloader `v0.10`
2+
3+
This guide summarizes the steps for migrating from `bootloader v0.10.X` to `bootloader v0.11`.
4+
5+
## Kernel
6+
7+
- Replace the `bootloader` dependency of your kernel with a dependency on the `bootloader_api` crate and adjust the import path in your `main.rs`:
8+
```diff
9+
# in Cargo.toml
10+
11+
-bootloader = { version = "0.10.13" }
12+
+bootloader_api = "0.11"
13+
```
14+
```diff
15+
// in main.rs
16+
17+
-use bootloader::{entry_point, BootInfo};
18+
+use bootloader_api::{entry_point, BootInfo};
19+
```
20+
- If you used optional features, such as `map-physical-memory`, you can enable them again through the `entry_point` macro:
21+
```rust
22+
use bootloader_api::config::{BootloaderConfig, Mapping};
23+
24+
pub static BOOTLOADER_CONFIG: BootloaderConfig = {
25+
let mut config = BootloaderConfig::new_default();
26+
config.mappings.physical_memory = Some(Mapping::Dynamic);
27+
config
28+
};
29+
30+
// add a `config` argument to the `entry_point` macro call
31+
entry_point!(kernel_main, config = &BOOTLOADER_CONFIG);
32+
```
33+
See the [`BootloaderConfig`](https://docs.rs/bootloader_api/0.11/bootloader_api/config/struct.BootloaderConfig.html) struct for all configuration options.
34+
35+
To build your kernel, run **`cargo build --target x86_64-unknown-none`**. Since the `x86_64-unknown-none` target is a Tier-2 target, there is no need for `bootimage`, `cargo-xbuild`, or `xargo` anymore. Instead, you can run `rustup target add x86_64-unknown-none` to download precompiled versions of the `core` and `alloc` crates. There is no need for custom JSON-based target files anymore.
36+
37+
## Booting
38+
39+
The `bootloader v0.11` release simplifies the disk image creation. The [`bootloader`](https://docs.rs/bootloader/0.11) crate now provides simple functions to create bootable disk images from a kernel. The basic idea is to build your kernel first and then invoke a builder function that calls the disk image creation functions of the `bootloader` crate.
40+
41+
A good way to implement this is to move your kernel into a `kernel` subdirectory. Then you can create
42+
a new `os` crate at the top level that defines a [workspace](https://doc.rust-lang.org/cargo/reference/workspaces.html). The root package has build-dependencies on the `kernel` [artifact](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#artifact-dependencies) and on the bootloader crate. This allows you to create the bootable disk image in a [cargo build script](https://doc.rust-lang.org/cargo/reference/build-scripts.html) and launch the created image in QEMU in the `main` function.
43+
44+
The files could look like this:
45+
46+
```toml
47+
# .cargo/config.toml
48+
49+
[unstable]
50+
# enable the unstable artifact-dependencies feature, see
51+
# https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#artifact-dependencies
52+
bindeps = true
53+
```
54+
55+
```toml
56+
# Cargo.toml
57+
58+
[package]
59+
name = "os" # or any other name
60+
version = "0.1.0"
61+
62+
[build-dependencies]
63+
bootloader = "0.11"
64+
test-kernel = { path = "kernel", artifact = "bin", target = "x86_64-unknown-none" }
65+
66+
[dependencies]
67+
# used for UEFI booting in QEMU
68+
ovmf_prebuilt = "0.1.0-alpha.1"
69+
70+
[workspace]
71+
members = ["kernel"]
72+
```
73+
74+
```rust
75+
// build.rs
76+
77+
fn main() {
78+
// set by cargo, build scripts should use this directory for output files
79+
let out_dir = env::var_os("OUT_DIR").unwrap();
80+
// set by cargo's artifact dependency feature, see
81+
// https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#artifact-dependencies
82+
let kernel = env!("CARGO_BIN_FILE_KERNEL");
83+
84+
// create an UEFI disk image (optional)
85+
let uefi_path = out_dir.join("uefi.img");
86+
bootloader::UefiBoot::new(&kernel).create_disk_image(uefi_path).unwrap();
87+
88+
// create a BIOS disk image (optional)
89+
let out_bios_path = out_dir.join("bios.img");
90+
bootloader::BiosBoot::new(&kernel).create_disk_image(uefi_path).unwrap();
91+
92+
// pass the disk image paths as env variables to the `main.rs`
93+
println!("cargo:rustc-env=UEFI_PATH={}", uefi_path.display());
94+
println!("cargo:rustc-env=BIOS_PATH={}", bios_path.display());
95+
}
96+
```
97+
98+
```rust
99+
// src/main.rs
100+
101+
fn main() {
102+
// read env variables that were set in build script
103+
let uefi_path = env!("UEFI_PATH");
104+
let bios_path = env!("BIOS_PATH");
105+
106+
// choose whether to start the UEFI or BIOS image
107+
let uefi = true;
108+
109+
let mut cmd = std::process::Command::new("qemu-system-x86_64");
110+
if uefi {
111+
cmd.arg("-bios").arg(ovmf_prebuilt::ovmf_pure_efi());
112+
cmd.arg("-drive").arg(format!("format=raw,file={uefi_path}"));
113+
} else {
114+
cmd.arg("-drive").arg(format!("format=raw,file={bios_path}"));
115+
}
116+
let mut child = cmd.spawn()?;
117+
child.wait()?;
118+
}
119+
```
120+
121+
Now you should be able to use `cargo build` to create a bootable disk image and `cargo run` to run in QEMU. Your kernel is automatically recompiled when it changes. For more advanced usage, you can add command-line arguments to your `main.rs` to e.g. pass additional arguments to QEMU or to copy the disk images to some path to make it easier to find them (e.g. for copying them to an thumb drive).

doc/migration/v0.9.md

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# Migration from bootloader `v0.9`
2+
3+
This guide summarizes the steps for migrating from `bootloader v0.9.X` to `bootloader v0.11`. Note that some bigger changes are required to support UEFI booting (to support the framebuffer and APIC).
4+
5+
## Kernel
6+
7+
- Replace the `bootloader` dependency of your kernel with a dependency on the `bootloader_api` crate:
8+
```diff
9+
-bootloader = { version = "0.9.23", features = [...]}
10+
+bootloader_api = "0.11"
11+
```
12+
- In your `main.rs`, adjust the import path and change the signature of the entry point function:
13+
```diff
14+
-use bootloader::{entry_point, BootInfo};
15+
+use bootloader_api::{entry_point, BootInfo};
16+
17+
entry_point!(kernel_main);
18+
19+
-fn kernel_main(boot_info: &'static BootInfo) -> ! {
20+
+fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
21+
```
22+
- If you used optional features, such as `map_physical_memory`, you can enable them again through the `entry_point` macro:
23+
```rust
24+
use bootloader_api::config::{BootloaderConfig, Mapping};
25+
26+
pub static BOOTLOADER_CONFIG: BootloaderConfig = {
27+
let mut config = BootloaderConfig::new_default();
28+
config.mappings.physical_memory = Some(Mapping::Dynamic);
29+
config
30+
};
31+
32+
// add a `config` argument to the `entry_point` macro call
33+
entry_point!(kernel_main, config = &BOOTLOADER_CONFIG);
34+
```
35+
See the [`BootloaderConfig`](https://docs.rs/bootloader_api/0.11/bootloader_api/config/struct.BootloaderConfig.html) struct for all configuration options.
36+
- The `v0.11` version of the bootloader sets up a pixel-based framebuffer instead of using the VGA text mode. This means that you have to rewrite your screen output code. See the [`common/logger.rs`](../../common/src/logger.rs) module for an example implementation based on the [`noto-sans-mono-bitmap`](https://docs.rs/noto-sans-mono-bitmap/latest/noto_sans_mono_bitmap/index.html) and [`log`](https://docs.rs/log/latest) crates.
37+
- If you want to use UEFI booting, you cannot use the [legacy PIC](https://wiki.osdev.org/8259_PIC) for interrupt handling. Instead, you have to set up the [`APIC`](https://wiki.osdev.org/APIC). Unfortunately, we don't have a guide for this yet, but the basic steps are:
38+
- The `boot_info.rsdp_addr` field will tell you the physical address of the [`RSDP`](https://wiki.osdev.org/RSDP) structure, which is part of the [`ACPI`](https://en.wikipedia.org/wiki/ACPI) standard. Note that `ACPI` (_Advanced Configuration and Power Interface_) and `APIC` (_Advanced Programmable Interrupt Controller_) are completely different things that just have confusingly similar acronyms.
39+
- Use the [`acpi`](https://docs.rs/acpi/4.1.1/acpi/index.html) crate and its [`AcpiTables::from_rsdp`](https://docs.rs/acpi/4.1.1/acpi/struct.AcpiTables.html#method.from_rsdp) function to load the ACPI tables. Then use the [`platform_info`](https://docs.rs/acpi/4.1.1/acpi/struct.AcpiTables.html#method.platform_info) method and read the [`interrupt_model`](https://docs.rs/acpi/4.1.1/acpi/platform/struct.PlatformInfo.html#structfield.interrupt_model) field of the returned `PlatformInfo`. This field gives you all the necessary information about the system's interrupt controller.
40+
- Parse and set up the local and IO APIC. We're working on a [crate](https://github.com/rust-osdev/apic) for that, but it's still in a very early state. We would love contributions! Alternatively, there are some other crates on crates.io that might work too.
41+
- If you reload the GDT at some point, ensure that all segment registers are written, including `ss` and `ds`. The `v0.9` version of the bootloader used to initialize some of them to 0, but this is no longer the case. If you don't do this, [a general protection fault might happen on `iretq`](https://github.com/rust-osdev/bootloader/issues/196).
42+
43+
To build your kernel, run **`cargo build --target x86_64-unknown-none`**. Since the `x86_64-unknown-none` target is a Tier-2 target, there is no need for `bootimage`, `cargo-xbuild`, or `xargo` anymore. Instead, you can run `rustup target add x86_64-unknown-none` to download precompiled versions of the `core` and `alloc` crates. There is no need for custom JSON-based target files anymore.
44+
45+
## Booting
46+
47+
The `bootloader v0.11` release does not use the `bootimage` tool anymore. Instead, the [`bootloader`](https://docs.rs/bootloader/0.11) crate provides functions to create bootable disk images from a kernel. The basic idea is to build your kernel first and then invoke a builder function that calls the disk image creation functions of the `bootloader` crate.
48+
49+
A good way to implement this is to move your kernel into a `kernel` subdirectory. Then you can create
50+
a new `os` crate at the top level that defines a [workspace](https://doc.rust-lang.org/cargo/reference/workspaces.html). The root package has build-dependencies on the `kernel` [artifact](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#artifact-dependencies) and on the bootloader crate. This allows you to create the bootable disk image in a [cargo build script](https://doc.rust-lang.org/cargo/reference/build-scripts.html) and launch the created image in QEMU in the `main` function.
51+
52+
The files could look like this:
53+
54+
```toml
55+
# .cargo/config.toml
56+
57+
[unstable]
58+
# enable the unstable artifact-dependencies feature, see
59+
# https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#artifact-dependencies
60+
bindeps = true
61+
```
62+
63+
```toml
64+
# Cargo.toml
65+
66+
[package]
67+
name = "os" # or any other name
68+
version = "0.1.0"
69+
70+
[build-dependencies]
71+
bootloader = "0.11"
72+
test-kernel = { path = "kernel", artifact = "bin", target = "x86_64-unknown-none" }
73+
74+
[dependencies]
75+
# used for UEFI booting in QEMU
76+
ovmf_prebuilt = "0.1.0-alpha.1"
77+
78+
[workspace]
79+
members = ["kernel"]
80+
```
81+
82+
```rust
83+
// build.rs
84+
85+
fn main() {
86+
// set by cargo, build scripts should use this directory for output files
87+
let out_dir = env::var_os("OUT_DIR").unwrap();
88+
// set by cargo's artifact dependency feature, see
89+
// https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#artifact-dependencies
90+
let kernel = env!("CARGO_BIN_FILE_KERNEL");
91+
92+
// create an UEFI disk image (optional)
93+
let uefi_path = out_dir.join("uefi.img");
94+
bootloader::UefiBoot::new(&kernel).create_disk_image(uefi_path).unwrap();
95+
96+
// create a BIOS disk image (optional)
97+
let out_bios_path = out_dir.join("bios.img");
98+
bootloader::BiosBoot::new(&kernel).create_disk_image(uefi_path).unwrap();
99+
100+
// pass the disk image paths as env variables to the `main.rs`
101+
println!("cargo:rustc-env=UEFI_PATH={}", uefi_path.display());
102+
println!("cargo:rustc-env=BIOS_PATH={}", bios_path.display());
103+
}
104+
```
105+
106+
```rust
107+
// src/main.rs
108+
109+
fn main() {
110+
// read env variables that were set in build script
111+
let uefi_path = env!("UEFI_PATH");
112+
let bios_path = env!("BIOS_PATH");
113+
114+
// choose whether to start the UEFI or BIOS image
115+
let uefi = true;
116+
117+
let mut cmd = std::process::Command::new("qemu-system-x86_64");
118+
if uefi {
119+
cmd.arg("-bios").arg(ovmf_prebuilt::ovmf_pure_efi());
120+
cmd.arg("-drive").arg(format!("format=raw,file={uefi_path}"));
121+
} else {
122+
cmd.arg("-drive").arg(format!("format=raw,file={bios_path}"));
123+
}
124+
let mut child = cmd.spawn()?;
125+
child.wait()?;
126+
}
127+
```
128+
129+
Now you should be able to use `cargo build` to create a bootable disk image and `cargo run` to run in QEMU. Your kernel is automatically recompiled when it changes. For more advanced usage, you can add command-line arguments to your `main.rs` to e.g. pass additional arguments to QEMU or to copy the disk images to some path to make it easier to find them (e.g. for copying them to an thumb drive).

0 commit comments

Comments
 (0)