1
- use std:: { io, path:: Path , process:: Command } ;
1
+ use anyhow:: Context ;
2
+ use std:: {
3
+ fs,
4
+ io:: { self , Seek , Write } ,
5
+ path:: Path ,
6
+ process:: Command ,
7
+ } ;
2
8
use thiserror:: Error ;
3
9
4
10
/// Creates a bootable disk image from the given bootloader executable.
5
11
pub fn create_disk_image (
6
12
bootloader_elf_path : & Path ,
7
13
output_bin_path : & Path ,
8
- ) -> Result < ( ) , DiskImageError > {
9
- let llvm_tools = llvm_tools:: LlvmTools :: new ( ) ?;
14
+ kernel_binary : & Path ,
15
+ ) -> anyhow:: Result < ( ) > {
16
+ let llvm_tools =
17
+ llvm_tools:: LlvmTools :: new ( ) . map_err ( |err| anyhow:: anyhow!( "failed to get llvm tools" ) ) ?;
10
18
let objcopy = llvm_tools
11
19
. tool ( & llvm_tools:: exe ( "llvm-objcopy" ) )
12
20
. ok_or ( DiskImageError :: LlvmObjcopyNotFound ) ?;
13
21
14
- // convert bootloader to binary
22
+ // convert first stage to binary
15
23
let mut cmd = Command :: new ( objcopy) ;
16
24
cmd. arg ( "-I" ) . arg ( "elf64-x86-64" ) ;
17
25
cmd. arg ( "-O" ) . arg ( "binary" ) ;
@@ -25,9 +33,77 @@ pub fn create_disk_image(
25
33
if !output. status . success ( ) {
26
34
return Err ( DiskImageError :: ObjcopyFailed {
27
35
stderr : output. stderr ,
28
- } ) ;
36
+ } )
37
+ . context ( "objcopy failed" ) ;
29
38
}
30
39
40
+ use std:: fs:: OpenOptions ;
41
+ let mut disk_image = OpenOptions :: new ( )
42
+ . write ( true )
43
+ . open ( & output_bin_path)
44
+ . map_err ( |err| DiskImageError :: Io {
45
+ message : "failed to open boot image" ,
46
+ error : err,
47
+ } ) ?;
48
+ let file_size = disk_image
49
+ . metadata ( )
50
+ . map_err ( |err| DiskImageError :: Io {
51
+ message : "failed to get size of boot image" ,
52
+ error : err,
53
+ } ) ?
54
+ . len ( ) ;
55
+ const BLOCK_SIZE : u64 = 512 ;
56
+ assert_eq ! ( file_size, BLOCK_SIZE ) ;
57
+
58
+ let kernel_size = fs:: metadata ( & kernel_binary)
59
+ . context ( "failed to read metadata of kernel binary" ) ?
60
+ . len ( ) ;
61
+
62
+ // create fat partition
63
+ const MB : u64 = 1024 * 1024 ;
64
+ let fat_size = kernel_size; // TODO plus second stage size
65
+ let fat_size_padded_and_rounded = ( ( fat_size + 1024 * 64 - 1 ) / MB + 1 ) * MB ;
66
+ let fat_file_path = {
67
+ let fat_path = output_bin_path. with_extension ( "fat" ) ;
68
+ let fat_file = fs:: OpenOptions :: new ( )
69
+ . read ( true )
70
+ . write ( true )
71
+ . create ( true )
72
+ . truncate ( true )
73
+ . open ( & fat_path)
74
+ . context ( "Failed to create UEFI FAT file" ) ?;
75
+ fat_file
76
+ . set_len ( fat_size_padded_and_rounded)
77
+ . context ( "failed to set UEFI FAT file length" ) ?;
78
+
79
+ // create new FAT partition
80
+ let format_options = fatfs:: FormatVolumeOptions :: new ( ) . volume_label ( * b"BOOT " ) ;
81
+ fatfs:: format_volume ( & fat_file, format_options)
82
+ . context ( "Failed to format UEFI FAT file" ) ?;
83
+
84
+ // copy kernel to FAT filesystem
85
+ let partition = fatfs:: FileSystem :: new ( & fat_file, fatfs:: FsOptions :: new ( ) )
86
+ . context ( "Failed to open FAT file system of UEFI FAT file" ) ?;
87
+ let root_dir = partition. root_dir ( ) ;
88
+ let mut kernel_file = root_dir. create_file ( "kernel-x86_64" ) ?;
89
+ kernel_file. truncate ( ) ?;
90
+ io:: copy ( & mut fs:: File :: open ( & kernel_binary) ?, & mut kernel_file) ?;
91
+
92
+ fat_path
93
+ } ;
94
+
95
+ disk_image. seek ( io:: SeekFrom :: Start ( 446 ) ) ?;
96
+ disk_image. write_all ( & [ 0x80 , 0 , 0 , 0 , 0x04 , 0 , 0 , 0 ] ) ?;
97
+ let start_sector = 1u32 . to_le_bytes ( ) ;
98
+ let size_sectors = u32:: try_from ( & fat_size_padded_and_rounded / 512 )
99
+ . unwrap ( )
100
+ . to_le_bytes ( ) ;
101
+ disk_image. write_all ( & start_sector) ?;
102
+ disk_image. write_all ( & size_sectors) ?;
103
+
104
+ disk_image. seek ( io:: SeekFrom :: Start ( 512 ) ) ?;
105
+ io:: copy ( & mut fs:: File :: open ( & kernel_binary) ?, & mut disk_image) ?;
106
+
31
107
pad_to_nearest_block_size ( output_bin_path) ?;
32
108
Ok ( ( ) )
33
109
}
0 commit comments