@@ -4,20 +4,169 @@ 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
1923pub use bootloader_boot_config:: BootConfig ;
2024
2125const KERNEL_FILE_NAME : & str = "kernel-x86_64" ;
2226const RAMDISK_FILE_NAME : & str = "ramdisk" ;
2327const 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