1- use anyhow:: { anyhow, Context } ;
1+ use anyhow:: { anyhow, bail , Context } ;
22use argh:: FromArgs ;
33use bootloader:: disk_image:: create_disk_image;
44use std:: {
5- fs, io,
5+ convert:: TryFrom ,
6+ fs:: { self , File } ,
7+ io:: { self , Seek } ,
68 path:: { Path , PathBuf } ,
79 process:: Command ,
810 str:: FromStr ,
@@ -129,55 +131,32 @@ fn main() -> anyhow::Result<()> {
129131
130132 assert_eq ! ( executables. len( ) , 1 ) ;
131133 let executable_path = executables. pop ( ) . unwrap ( ) ;
132- let executable_name = executable_path. file_stem ( ) . unwrap ( ) . to_str ( ) . unwrap ( ) ;
133- let kernel_name = args. kernel_binary . file_name ( ) . unwrap ( ) . to_str ( ) . unwrap ( ) ;
134+
135+ let executable_name = executable_path
136+ . file_stem ( )
137+ . and_then ( |stem| stem. to_str ( ) )
138+ . ok_or_else ( || {
139+ anyhow ! (
140+ "executable path `{}` has invalid file stem" ,
141+ executable_path. display( )
142+ )
143+ } ) ?;
144+ let kernel_name = args
145+ . kernel_binary
146+ . file_name ( )
147+ . and_then ( |name| name. to_str ( ) )
148+ . ok_or_else ( || {
149+ anyhow ! (
150+ "kernel binary path `{}` has invalid file name" ,
151+ args. kernel_binary. display( )
152+ )
153+ } ) ?;
134154
135155 if let Some ( out_dir) = & args. out_dir {
136156 let efi_file =
137157 out_dir. join ( format ! ( "bootimage-{}-{}.efi" , executable_name, kernel_name) ) ;
138- fs:: copy ( & executable_path, & efi_file) . context ( "failed to copy efi file to out dir" ) ?;
139-
140- let efi_size = fs:: metadata ( & efi_file)
141- . context ( "failed to read metadata of efi file" ) ?
142- . len ( ) ;
143-
144- // create fat partition
145- {
146- const MB : u64 = 1024 * 1024 ;
147-
148- let fat_path = efi_file. with_extension ( "fat" ) ;
149- dbg ! ( & fat_path) ;
150- let fat_file = fs:: OpenOptions :: new ( )
151- . read ( true )
152- . write ( true )
153- . create ( true )
154- . truncate ( true )
155- . open ( & fat_path)
156- . context ( "Failed to create UEFI FAT file" ) ?;
157- let efi_size_rounded = ( ( efi_size - 1 ) / MB + 1 ) * MB ;
158- fat_file
159- . set_len ( dbg ! ( efi_size_rounded) )
160- . context ( "failed to set UEFI FAT file length" ) ?;
161-
162- // create new FAT partition
163- fatfs:: format_volume ( & fat_file, fatfs:: FormatVolumeOptions :: new ( ) )
164- . context ( "Failed to format UEFI FAT file" ) ?;
165-
166- // copy EFI file to FAT filesystem
167- let partition = fatfs:: FileSystem :: new ( & fat_file, fatfs:: FsOptions :: new ( ) )
168- . context ( "Failed to open FAT file system of UEFI FAT file" ) ?;
169- let root_dir = partition. root_dir ( ) ;
170- root_dir. create_dir ( "efi" ) ?;
171- root_dir. create_dir ( "efi/boot" ) ?;
172- let mut bootx64 = root_dir. create_file ( "efi/boot/bootx64.efi" ) ?;
173- bootx64. truncate ( ) ?;
174- io:: copy ( & mut fs:: File :: open ( & executable_path) ?, & mut bootx64) ?;
175- }
176-
177- // create gpt disk
178- {
179- //todo!()
180- }
158+ create_uefi_disk_image ( & executable_path, & efi_file)
159+ . context ( "failed to create UEFI disk image" ) ?;
181160 }
182161 }
183162
@@ -228,7 +207,7 @@ fn main() -> anyhow::Result<()> {
228207 let mut output_bin_path = executable_path
229208 . parent ( )
230209 . unwrap ( )
231- . join ( format ! ( "bootimage-{}-{}.bin " , executable_name, kernel_name) ) ;
210+ . join ( format ! ( "bootimage-{}-{}.img " , executable_name, kernel_name) ) ;
232211
233212 create_disk_image ( & executable_path, & output_bin_path)
234213 . context ( "Failed to create bootable disk image" ) ?;
@@ -254,6 +233,132 @@ fn main() -> anyhow::Result<()> {
254233 Ok ( ( ) )
255234}
256235
236+ fn create_uefi_disk_image ( executable_path : & Path , efi_file : & Path ) -> anyhow:: Result < ( ) > {
237+ fs:: copy ( & executable_path, & efi_file) . context ( "failed to copy efi file to out dir" ) ?;
238+
239+ let efi_size = fs:: metadata ( & efi_file)
240+ . context ( "failed to read metadata of efi file" ) ?
241+ . len ( ) ;
242+
243+ // create fat partition
244+ let fat_file_path = {
245+ const MB : u64 = 1024 * 1024 ;
246+
247+ let fat_path = efi_file. with_extension ( "fat" ) ;
248+ let fat_file = fs:: OpenOptions :: new ( )
249+ . read ( true )
250+ . write ( true )
251+ . create ( true )
252+ . truncate ( true )
253+ . open ( & fat_path)
254+ . context ( "Failed to create UEFI FAT file" ) ?;
255+ let efi_size_rounded = ( ( efi_size - 1 ) / MB + 1 ) * MB ;
256+ fat_file
257+ . set_len ( efi_size_rounded)
258+ . context ( "failed to set UEFI FAT file length" ) ?;
259+
260+ // create new FAT partition
261+ let format_options = fatfs:: FormatVolumeOptions :: new ( ) . volume_label ( * b"FOOO " ) ;
262+ fatfs:: format_volume ( & fat_file, format_options)
263+ . context ( "Failed to format UEFI FAT file" ) ?;
264+
265+ // copy EFI file to FAT filesystem
266+ let partition = fatfs:: FileSystem :: new ( & fat_file, fatfs:: FsOptions :: new ( ) )
267+ . context ( "Failed to open FAT file system of UEFI FAT file" ) ?;
268+ let root_dir = partition. root_dir ( ) ;
269+ root_dir. create_dir ( "efi" ) ?;
270+ root_dir. create_dir ( "efi/boot" ) ?;
271+ let mut bootx64 = root_dir. create_file ( "efi/boot/bootx64.efi" ) ?;
272+ bootx64. truncate ( ) ?;
273+ io:: copy ( & mut fs:: File :: open ( & executable_path) ?, & mut bootx64) ?;
274+
275+ fat_path
276+ } ;
277+
278+ // create gpt disk
279+ {
280+ let image_path = efi_file. with_extension ( "img" ) ;
281+ let mut image = fs:: OpenOptions :: new ( )
282+ . create ( true )
283+ . truncate ( true )
284+ . read ( true )
285+ . write ( true )
286+ . open ( & image_path)
287+ . context ( "failed to create UEFI disk image" ) ?;
288+
289+ let partition_size: u64 = fs:: metadata ( & fat_file_path)
290+ . context ( "failed to read metadata of UEFI FAT partition" ) ?
291+ . len ( ) ;
292+ let image_size = partition_size + 1024 * 64 ;
293+ image
294+ . set_len ( image_size)
295+ . context ( "failed to set length of UEFI disk image" ) ?;
296+
297+ // Create a protective MBR at LBA0
298+ let mbr = gpt:: mbr:: ProtectiveMBR :: with_lb_size (
299+ u32:: try_from ( ( image_size / 512 ) - 1 ) . unwrap_or ( 0xFF_FF_FF_FF ) ,
300+ ) ;
301+ mbr. overwrite_lba0 ( & mut image)
302+ . context ( "failed to write protective MBR" ) ?;
303+
304+ // create new GPT in image file
305+ let block_size = gpt:: disk:: LogicalBlockSize :: Lb512 ;
306+ let block_size_bytes: u64 = block_size. into ( ) ;
307+ let mut disk = gpt:: GptConfig :: new ( )
308+ . writable ( true )
309+ . initialized ( false )
310+ . logical_block_size ( block_size)
311+ . create_from_device ( Box :: new ( & mut image) , None )
312+ . context ( "failed to open UEFI disk image" ) ?;
313+ disk. update_partitions ( Default :: default ( ) )
314+ . context ( "failed to initialize GPT partition table" ) ?;
315+
316+ // add add EFI system partition
317+ let partition_id = disk
318+ . add_partition ( "boot" , partition_size, gpt:: partition_types:: EFI , 0 )
319+ . context ( "failed to add boot partition" ) ?;
320+
321+ let partition = disk
322+ . partitions ( )
323+ . get ( & partition_id)
324+ . ok_or_else ( || anyhow ! ( "Partition doesn't exist after adding it" ) ) ?;
325+ let created_partition_size: u64 =
326+ ( partition. last_lba - partition. first_lba + 1u64 ) * block_size_bytes;
327+ if created_partition_size != partition_size {
328+ bail ! (
329+ "Created partition has invalid size (size is {:?}, expected {})" ,
330+ created_partition_size,
331+ partition_size
332+ ) ;
333+ }
334+ let start_offset = partition
335+ . bytes_start ( block_size)
336+ . context ( "failed to retrieve partition start offset" ) ?;
337+
338+ // Write the partition table
339+ disk. write ( )
340+ . context ( "failed to write GPT partition table to UEFI image file" ) ?;
341+
342+ image
343+ . seek ( io:: SeekFrom :: Start ( start_offset) )
344+ . context ( "failed to seek to boot partiiton start" ) ?;
345+ let bytes_written = io:: copy (
346+ & mut File :: open ( & fat_file_path) . context ( "failed to open fat image" ) ?,
347+ & mut image,
348+ )
349+ . context ( "failed to write boot partition content" ) ?;
350+ if bytes_written != partition_size {
351+ bail ! (
352+ "Invalid number of partition bytes written (expected {}, got {})" ,
353+ partition_size,
354+ bytes_written
355+ ) ;
356+ }
357+ }
358+
359+ Ok ( ( ) )
360+ }
361+
257362fn bios_run ( bin_path : & Path ) -> anyhow:: Result < Option < ExitCode > > {
258363 let mut qemu = Command :: new ( "qemu-system-x86_64" ) ;
259364 qemu. arg ( "-drive" )
0 commit comments