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