@@ -4,16 +4,160 @@ An experimental x86_64 bootloader that works on both BIOS and UEFI systems.
44
55#![ warn( missing_docs) ]
66
7+ #[ cfg( feature = "uefi" ) ]
8+ mod gpt;
79#[ cfg( feature = "bios" ) ]
8- mod bios;
10+ mod mbr;
11+
912mod fat;
10- #[ cfg( feature = "uefi" ) ]
11- mod uefi;
1213
13- #[ cfg( feature = "bios" ) ]
14- pub use bios:: BiosBoot ;
14+ use std:: {
15+ collections:: BTreeMap ,
16+ path:: { Path , PathBuf } ,
17+ } ;
1518
16- #[ cfg( feature = "uefi" ) ]
17- pub use uefi:: UefiBoot ;
19+ use anyhow:: Context ;
20+
21+ use tempfile:: NamedTempFile ;
1822
1923const 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