11/*!
22An experimental x86_64 bootloader that works on both BIOS and UEFI systems.
3-
4- To use this crate, specify it as a dependency in the `Cargo.toml` of your operating system
5- kernel. Then you can use the [`entry_point`] macro to mark your entry point function. This
6- gives you access to the [`BootInfo`] struct, which is passed by the bootloader.
7-
8- ## Disk Image Creation
9-
10- Including the `bootloader` crate as a dependency makes the kernel binary suitable for booting,
11- but does not create any bootable disk images. To create them, two additional steps are needed:
12-
13- 1. **Locate the source code of the `bootloader` dependency** on your local system. By using the
14- dependency source code directly, we ensure that the kernel and bootloader use the same version
15- of the [`BootInfo`] struct.
16- - When creating a builder binary written in Rust, the
17- [`bootloader_locator`](https://docs.rs/bootloader-locator/0.0.4/bootloader_locator/) crate can
18- be used to automate this step.
19- - Otherwise, the
20- [`cargo metadata`](https://doc.rust-lang.org/cargo/commands/cargo-metadata.html) subcommand
21- can be used to locate the dependency. The command outputs a JSON object with various metadata
22- for the current package. To find the `bootloader` source path in it, first look for the
23- "bootloader" dependency under `resolve.nodes.deps` to find out its ID (in the `pkg` field).
24- Then use that ID to find the bootloader in `packages`. Its `manifest_path` field contains the
25- local path to the `Cargo.toml` of the bootloader.
26- 2. **Run the following command** in the source code directory of the `bootloader` dependency to create
27- the bootable disk images:
28-
29- ```notrust
30- cargo builder --kernel-manifest path/to/kernel/Cargo.toml --kernel-binary path/to/kernel_bin
31- ```
32-
33- The `--kernel-manifest` argument should point to the `Cargo.toml` of your kernel. It is used
34- for applying configuration settings. The `--kernel-binary` argument should point to the kernel
35- executable that should be used for the bootable disk images.
36-
37- In addition to the `--kernel-manifest` and `--kernel-binary` arguments, it is recommended to also
38- set the `--target-dir` and `--out-dir` arguments. The former specifies the directory that should
39- used for cargo build artifacts and the latter specfies the directory where the resulting disk
40- images should be placed. It is recommended to set `--target-dir` to the `target` folder of your
41- kernel and `--out-dir` to the the parent folder of `--kernel-binary`.
42-
43- This will result in the following files, which are placed in the specified `--out-dir`:
44-
45- - A disk image suitable for BIOS booting, named `boot-bios-<kernel>.img`, where `<kernel>` is the
46- name of your kernel executable. This image can be started in QEMU or booted on a real machine
47- after burning it to an USB stick..
48- - A disk image suitable for UEFI booting, named `boot-uefi-<kernel>.img`. Like the BIOS disk image,
49- this can be started in QEMU (requires OVMF) and burned to an USB stick to run it on a real
50- machine.
51- - Intermediate UEFI files
52- - A FAT partition image named `boot-uefi-<kernel>.fat`, which can be directly started in QEMU
53- or written as an EFI system partition to a GPT-formatted disk.
54- - An EFI file named `boot-uefi-<kernel>.efi`. This executable is the combination of the
55- bootloader and kernel executables. It can be started in QEMU or used to construct a bootable
56- disk image: Create an EFI system partition formatted with the FAT filesystem and place the
57- EFI file under `efi\boot\bootx64.efi` on that filesystem.
58-
59- **You can find some examples that implement the above steps [in our GitHub repo](https://github.com/rust-osdev/bootloader/tree/main/examples).**
60-
61- ## Configuration
62-
63- The bootloader can be configured through a `[package.metadata.bootloader]` table in the
64- `Cargo.toml` of the kernel (the one passed as `--kernel-manifest`). See the [`Config`] struct
65- for all possible configuration options.
663*/
674
685#![ warn( missing_docs) ]
696
707use anyhow:: Context ;
71- use std:: { collections:: BTreeMap , path:: Path } ;
8+ use std:: {
9+ collections:: BTreeMap ,
10+ path:: { Path , PathBuf } ,
11+ } ;
12+ use tempfile:: NamedTempFile ;
7213
7314mod fat;
7415mod gpt;
@@ -79,61 +20,116 @@ const KERNEL_FILE_NAME: &str = "kernel-x86_64";
7920const BIOS_STAGE_3 : & str = "boot-stage-3" ;
8021const BIOS_STAGE_4 : & str = "boot-stage-4" ;
8122
82- /// Creates a bootable FAT partition at the given path.
83- pub fn create_boot_partition ( kernel_binary : & Path , out_path : & Path ) -> anyhow:: Result < ( ) > {
84- let bootloader_path = Path :: new ( env ! ( "UEFI_BOOTLOADER_PATH" ) ) ;
85- let stage_3_path = Path :: new ( env ! ( "BIOS_STAGE_3_PATH" ) ) ;
86- let stage_4_path = Path :: new ( env ! ( "BIOS_STAGE_4_PATH" ) ) ;
87-
88- let mut files = BTreeMap :: new ( ) ;
89- files. insert ( "efi/boot/bootx64.efi" , bootloader_path) ;
90- files. insert ( KERNEL_FILE_NAME , kernel_binary) ;
91- files. insert ( BIOS_STAGE_3 , stage_3_path) ;
92- files. insert ( BIOS_STAGE_4 , stage_4_path) ;
93-
94- fat:: create_fat_filesystem ( files, & out_path) . context ( "failed to create UEFI FAT filesystem" ) ?;
95-
96- Ok ( ( ) )
23+ /// Create disk images for booting on legacy BIOS systems.
24+ pub struct BiosBoot {
25+ kernel : PathBuf ,
9726}
9827
99- pub fn create_uefi_disk_image (
100- boot_partition_path : & Path ,
101- out_gpt_path : & Path ,
102- ) -> anyhow:: Result < ( ) > {
103- gpt:: create_gpt_disk ( boot_partition_path, out_gpt_path)
104- . context ( "failed to create UEFI GPT disk image" ) ?;
105-
106- Ok ( ( ) )
28+ impl BiosBoot {
29+ /// Start creating a disk image for the given bootloader ELF executable.
30+ pub fn new ( kernel_path : & Path ) -> Self {
31+ Self {
32+ kernel : kernel_path. to_owned ( ) ,
33+ }
34+ }
35+
36+ /// Create a bootable UEFI disk image at the given path.
37+ pub fn create_disk_image ( & self , out_path : & Path ) -> anyhow:: Result < ( ) > {
38+ let bootsector_path = Path :: new ( env ! ( "BIOS_BOOT_SECTOR_PATH" ) ) ;
39+ let stage_2_path = Path :: new ( env ! ( "BIOS_STAGE_2_PATH" ) ) ;
40+
41+ let fat_partition = self
42+ . create_fat_partition ( )
43+ . context ( "failed to create FAT partition" ) ?;
44+
45+ mbr:: create_mbr_disk (
46+ bootsector_path,
47+ stage_2_path,
48+ fat_partition. path ( ) ,
49+ out_path,
50+ )
51+ . context ( "failed to create BIOS MBR disk image" ) ?;
52+
53+ fat_partition
54+ . close ( )
55+ . context ( "failed to delete FAT partition after disk image creation" ) ?;
56+
57+ Ok ( ( ) )
58+ }
59+
60+ /// Creates an BIOS-bootable FAT partition with the kernel.
61+ fn create_fat_partition ( & self ) -> anyhow:: Result < NamedTempFile > {
62+ let stage_3_path = Path :: new ( env ! ( "BIOS_STAGE_3_PATH" ) ) ;
63+ let stage_4_path = Path :: new ( env ! ( "BIOS_STAGE_4_PATH" ) ) ;
64+
65+ let mut files = BTreeMap :: new ( ) ;
66+ files. insert ( KERNEL_FILE_NAME , self . kernel . as_path ( ) ) ;
67+ files. insert ( BIOS_STAGE_3 , stage_3_path) ;
68+ files. insert ( BIOS_STAGE_4 , stage_4_path) ;
69+
70+ let out_file = NamedTempFile :: new ( ) . context ( "failed to create temp file" ) ?;
71+ fat:: create_fat_filesystem ( files, out_file. path ( ) )
72+ . context ( "failed to create BIOS FAT filesystem" ) ?;
73+
74+ Ok ( out_file)
75+ }
10776}
10877
109- pub fn create_bios_disk_image (
110- boot_partition_path : & Path ,
111- out_mbr_path : & Path ,
112- ) -> anyhow:: Result < ( ) > {
113- let bootsector_path = Path :: new ( env ! ( "BIOS_BOOT_SECTOR_PATH" ) ) ;
114- let stage_2_path = Path :: new ( env ! ( "BIOS_STAGE_2_PATH" ) ) ;
115-
116- mbr:: create_mbr_disk (
117- bootsector_path,
118- stage_2_path,
119- boot_partition_path,
120- out_mbr_path,
121- )
122- . context ( "failed to create BIOS MBR disk image" ) ?;
123-
124- Ok ( ( ) )
78+ /// Create disk images for booting on UEFI systems.
79+ pub struct UefiBoot {
80+ kernel : PathBuf ,
12581}
12682
127- /// Prepare a folder for use with booting over UEFI_PXE.
128- ///
129- /// This places the bootloader executable under the path "bootloader". The
130- /// DHCP server should set the filename option to that path, otherwise the
131- /// bootloader won't be found.
132- pub fn create_uefi_pxe_tftp_folder ( kernel_binary : & Path , out_path : & Path ) -> anyhow:: Result < ( ) > {
133- let bootloader_path = Path :: new ( env ! ( "UEFI_BOOTLOADER_PATH" ) ) ;
134-
135- pxe:: create_uefi_tftp_folder ( bootloader_path, kernel_binary, out_path)
136- . context ( "failed to create UEFI PXE tftp folder" ) ?;
137-
138- Ok ( ( ) )
83+ impl UefiBoot {
84+ /// Start creating a disk image for the given bootloader ELF executable.
85+ pub fn new ( kernel_path : & Path ) -> Self {
86+ Self {
87+ kernel : kernel_path. to_owned ( ) ,
88+ }
89+ }
90+
91+ /// Create a bootable BIOS disk image at the given path.
92+ pub fn create_disk_image ( & self , out_path : & Path ) -> anyhow:: Result < ( ) > {
93+ let fat_partition = self
94+ . create_fat_partition ( )
95+ . context ( "failed to create FAT partition" ) ?;
96+
97+ gpt:: create_gpt_disk ( fat_partition. path ( ) , out_path)
98+ . context ( "failed to create UEFI GPT disk image" ) ?;
99+
100+ fat_partition
101+ . close ( )
102+ . context ( "failed to delete FAT partition after disk image creation" ) ?;
103+
104+ Ok ( ( ) )
105+ }
106+
107+ /// Prepare a folder for use with booting over UEFI_PXE.
108+ ///
109+ /// This places the bootloader executable under the path "bootloader". The
110+ /// DHCP server should set the filename option to that path, otherwise the
111+ /// bootloader won't be found.
112+ pub fn create_pxe_tftp_folder ( & self , out_path : & Path ) -> anyhow:: Result < ( ) > {
113+ let bootloader_path = Path :: new ( env ! ( "UEFI_BOOTLOADER_PATH" ) ) ;
114+
115+ pxe:: create_uefi_tftp_folder ( bootloader_path, self . kernel . as_path ( ) , out_path)
116+ . context ( "failed to create UEFI PXE tftp folder" ) ?;
117+
118+ Ok ( ( ) )
119+ }
120+
121+ /// Creates an UEFI-bootable FAT partition with the kernel.
122+ fn create_fat_partition ( & self ) -> anyhow:: Result < NamedTempFile > {
123+ let bootloader_path = Path :: new ( env ! ( "UEFI_BOOTLOADER_PATH" ) ) ;
124+
125+ let mut files = BTreeMap :: new ( ) ;
126+ files. insert ( "efi/boot/bootx64.efi" , bootloader_path) ;
127+ files. insert ( KERNEL_FILE_NAME , self . kernel . as_path ( ) ) ;
128+
129+ let out_file = NamedTempFile :: new ( ) . context ( "failed to create temp file" ) ?;
130+ fat:: create_fat_filesystem ( files, & out_file. path ( ) )
131+ . context ( "failed to create UEFI FAT filesystem" ) ?;
132+
133+ Ok ( out_file)
134+ }
139135}
0 commit comments