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