@@ -4,20 +4,169 @@ An experimental x86_64 bootloader that works on both BIOS and UEFI systems.
4
4
5
5
#![ warn( missing_docs) ]
6
6
7
+ #[ cfg( feature = "uefi" ) ]
8
+ mod gpt;
7
9
#[ cfg( feature = "bios" ) ]
8
- mod bios;
10
+ mod mbr;
11
+
9
12
mod fat;
10
- #[ cfg( feature = "uefi" ) ]
11
- mod uefi;
12
13
13
- #[ cfg( feature = "bios" ) ]
14
- pub use bios:: BiosBoot ;
14
+ use std:: {
15
+ collections:: BTreeMap ,
16
+ path:: { Path , PathBuf } ,
17
+ } ;
15
18
16
- #[ cfg( feature = "uefi" ) ]
17
- pub use uefi:: UefiBoot ;
19
+ use anyhow:: Context ;
20
+
21
+ use tempfile:: NamedTempFile ;
18
22
19
23
pub use bootloader_boot_config:: BootConfig ;
20
24
21
25
const KERNEL_FILE_NAME : & str = "kernel-x86_64" ;
22
26
const RAMDISK_FILE_NAME : & str = "ramdisk" ;
23
27
const CONFIG_FILE_NAME : & str = "boot.json" ;
28
+
29
+ struct DiskImageFile < ' a > {
30
+ source : & ' a PathBuf ,
31
+ destination : & ' a str ,
32
+ }
33
+
34
+ /// DiskImageBuilder helps create disk images for a specified set of files.
35
+ /// It can currently create MBR (BIOS), GPT (UEFI), and TFTP (UEFI) images.
36
+ pub struct DiskImageBuilder < ' a > {
37
+ files : Vec < DiskImageFile < ' a > > ,
38
+ }
39
+
40
+ impl < ' a > DiskImageBuilder < ' a > {
41
+ /// Create a new instance of DiskImageBuilder, with the specified kernel.
42
+ pub fn new ( kernel : & ' a PathBuf ) -> Self {
43
+ let mut obj = Self :: empty ( ) ;
44
+ obj. set_kernel ( kernel) ;
45
+ obj
46
+ }
47
+
48
+ /// Create a new, empty instance of DiskImageBuilder
49
+ pub fn empty ( ) -> Self {
50
+ Self { files : Vec :: new ( ) }
51
+ }
52
+
53
+ /// Add or replace a kernel to be included in the final image.
54
+ pub fn set_kernel ( & mut self , path : & ' a PathBuf ) -> & mut Self {
55
+ self . add_or_replace_file ( path, KERNEL_FILE_NAME )
56
+ }
57
+
58
+ /// Add or replace a ramdisk to be included in the final image.
59
+ pub fn set_ramdisk ( & mut self , path : & ' a PathBuf ) -> & mut Self {
60
+ self . add_or_replace_file ( & path, RAMDISK_FILE_NAME )
61
+ }
62
+
63
+ /// Add or replace arbitrary files.
64
+ /// NOTE: You can overwrite internal files if you choose, such as EFI/BOOT/BOOTX64.EFI
65
+ /// This can be useful in situations where you want to generate an image, but not use the provided bootloader.
66
+ pub fn add_or_replace_file ( & mut self , path : & ' a PathBuf , target : & ' a str ) -> & mut Self {
67
+ self . files . insert (
68
+ 0 ,
69
+ DiskImageFile :: < ' a > {
70
+ source : & path,
71
+ destination : & target,
72
+ } ,
73
+ ) ;
74
+ self
75
+ }
76
+ fn create_fat_filesystem_image (
77
+ & self ,
78
+ internal_files : BTreeMap < & ' a str , & ' a Path > ,
79
+ ) -> anyhow:: Result < NamedTempFile > {
80
+ let mut local_map = BTreeMap :: new ( ) ;
81
+
82
+ for k in internal_files {
83
+ local_map. insert ( k. 0 , k. 1 ) ;
84
+ }
85
+
86
+ for f in self . files . as_slice ( ) {
87
+ local_map. insert ( f. destination , & f. source . as_path ( ) ) ;
88
+ }
89
+
90
+ let out_file = NamedTempFile :: new ( ) . context ( "failed to create temp file" ) ?;
91
+ fat:: create_fat_filesystem ( local_map, out_file. path ( ) )
92
+ . context ( "failed to create BIOS FAT filesystem" ) ?;
93
+
94
+ Ok ( out_file)
95
+ }
96
+ #[ cfg( feature = "bios" ) ]
97
+ /// Create an MBR disk image for booting on BIOS systems.
98
+ pub fn create_bios_image ( & self , image_filename : & Path ) -> anyhow:: Result < ( ) > {
99
+ const BIOS_STAGE_3 : & str = "boot-stage-3" ;
100
+ const BIOS_STAGE_4 : & str = "boot-stage-4" ;
101
+ let bootsector_path = Path :: new ( env ! ( "BIOS_BOOT_SECTOR_PATH" ) ) ;
102
+ let stage_2_path = Path :: new ( env ! ( "BIOS_STAGE_2_PATH" ) ) ;
103
+ let stage_3_path = Path :: new ( env ! ( "BIOS_STAGE_3_PATH" ) ) ;
104
+ let stage_4_path = Path :: new ( env ! ( "BIOS_STAGE_4_PATH" ) ) ;
105
+ let mut internal_files = BTreeMap :: new ( ) ;
106
+ internal_files. insert ( BIOS_STAGE_3 , stage_3_path) ;
107
+ internal_files. insert ( BIOS_STAGE_4 , stage_4_path) ;
108
+
109
+ let fat_partition = self
110
+ . create_fat_filesystem_image ( internal_files)
111
+ . context ( "failed to create FAT partition" ) ?;
112
+ mbr:: create_mbr_disk (
113
+ bootsector_path,
114
+ stage_2_path,
115
+ fat_partition. path ( ) ,
116
+ image_filename,
117
+ )
118
+ . context ( "failed to create BIOS MBR disk image" ) ?;
119
+
120
+ fat_partition
121
+ . close ( )
122
+ . context ( "failed to delete FAT partition after disk image creation" ) ?;
123
+ Ok ( ( ) )
124
+ }
125
+
126
+ #[ cfg( feature = "uefi" ) ]
127
+ /// Create a GPT disk image for booting on UEFI systems.
128
+ pub fn create_uefi_image ( & self , image_filename : & Path ) -> anyhow:: Result < ( ) > {
129
+ const UEFI_BOOT_FILENAME : & str = "efi/boot/bootx64.efi" ;
130
+ let bootloader_path = Path :: new ( env ! ( "UEFI_BOOTLOADER_PATH" ) ) ;
131
+ let mut internal_files = BTreeMap :: new ( ) ;
132
+ internal_files. insert ( UEFI_BOOT_FILENAME , bootloader_path) ;
133
+ let fat_partition = self
134
+ . create_fat_filesystem_image ( internal_files)
135
+ . context ( "failed to create FAT partition" ) ?;
136
+ gpt:: create_gpt_disk ( fat_partition. path ( ) , image_filename)
137
+ . context ( "failed to create UEFI GPT disk image" ) ?;
138
+ fat_partition
139
+ . close ( )
140
+ . context ( "failed to delete FAT partition after disk image creation" ) ?;
141
+
142
+ Ok ( ( ) )
143
+ }
144
+
145
+ #[ cfg( feature = "uefi" ) ]
146
+ /// Create a folder containing the needed files for UEFI TFTP/PXE booting.
147
+ pub fn create_uefi_tftp_folder ( & self , tftp_path : & Path ) -> anyhow:: Result < ( ) > {
148
+ const UEFI_TFTP_BOOT_FILENAME : & str = "bootloader" ;
149
+ let bootloader_path = Path :: new ( env ! ( "UEFI_BOOTLOADER_PATH" ) ) ;
150
+ std:: fs:: create_dir_all ( tftp_path)
151
+ . with_context ( || format ! ( "failed to create out dir at {}" , tftp_path. display( ) ) ) ?;
152
+
153
+ let to = tftp_path. join ( UEFI_TFTP_BOOT_FILENAME ) ;
154
+ std:: fs:: copy ( bootloader_path, & to) . with_context ( || {
155
+ format ! (
156
+ "failed to copy bootloader from {} to {}" ,
157
+ bootloader_path. display( ) ,
158
+ to. display( )
159
+ )
160
+ } ) ?;
161
+
162
+ for f in self . files . as_slice ( ) {
163
+ let to = tftp_path. join ( f. destination ) ;
164
+ let result = std:: fs:: copy ( f. source , to) ;
165
+ if result. is_err ( ) {
166
+ return Err ( anyhow:: Error :: from ( result. unwrap_err ( ) ) ) ;
167
+ }
168
+ }
169
+
170
+ Ok ( ( ) )
171
+ }
172
+ }
0 commit comments