1
1
/*!
2
2
An 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.
66
3
*/
67
4
68
5
#![ warn( missing_docs) ]
69
6
70
7
use anyhow:: Context ;
71
- use std:: { collections:: BTreeMap , path:: Path } ;
8
+ use std:: {
9
+ collections:: BTreeMap ,
10
+ path:: { Path , PathBuf } ,
11
+ } ;
12
+ use tempfile:: NamedTempFile ;
72
13
73
14
mod fat;
74
15
mod gpt;
@@ -79,61 +20,116 @@ const KERNEL_FILE_NAME: &str = "kernel-x86_64";
79
20
const BIOS_STAGE_3 : & str = "boot-stage-3" ;
80
21
const BIOS_STAGE_4 : & str = "boot-stage-4" ;
81
22
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 ,
97
26
}
98
27
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
+ }
107
76
}
108
77
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 ,
125
81
}
126
82
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
+ }
139
135
}
0 commit comments