@@ -67,7 +67,138 @@ for all possible configuration options.
67
67
68
68
#![ warn( missing_docs) ]
69
69
70
- /// Provides a function to turn a bootloader executable into a disk image.
71
- ///
72
- /// Used by the `builder` binary. Only available when the `builder` feature is enabled.
73
- pub mod disk_image;
70
+ use std:: {
71
+ fs:: { self , File } ,
72
+ io:: { self , Seek } ,
73
+ path:: Path ,
74
+ } ;
75
+
76
+ use anyhow:: Context ;
77
+
78
+ pub fn create_uefi_disk_image (
79
+ kernel_binary : & Path ,
80
+ out_fat_path : & Path ,
81
+ out_gpt_path : & Path ,
82
+ ) -> anyhow:: Result < ( ) > {
83
+ let bootloader_path = Path :: new ( env ! ( "UEFI_BOOTLOADER_PATH" ) ) ;
84
+
85
+ create_fat_filesystem ( bootloader_path, kernel_binary, & out_fat_path)
86
+ . context ( "failed to create UEFI FAT filesystem" ) ?;
87
+ create_gpt_disk ( out_fat_path, out_gpt_path) ;
88
+
89
+ Ok ( ( ) )
90
+ }
91
+
92
+ fn create_fat_filesystem (
93
+ bootloader_efi_file : & Path ,
94
+ kernel_binary : & Path ,
95
+ out_fat_path : & Path ,
96
+ ) -> anyhow:: Result < ( ) > {
97
+ const MB : u64 = 1024 * 1024 ;
98
+
99
+ // retrieve size of `.efi` file and round it up
100
+ let efi_size = fs:: metadata ( & bootloader_efi_file) . unwrap ( ) . len ( ) ;
101
+ // size of a megabyte
102
+ // round it to next megabyte
103
+ let efi_size_rounded = ( ( efi_size - 1 ) / MB + 1 ) * MB ;
104
+
105
+ // create new filesystem image file at the given path and set its length
106
+ let fat_file = fs:: OpenOptions :: new ( )
107
+ . read ( true )
108
+ . write ( true )
109
+ . create ( true )
110
+ . truncate ( true )
111
+ . open ( & out_fat_path)
112
+ . unwrap ( ) ;
113
+ fat_file. set_len ( efi_size_rounded) . unwrap ( ) ;
114
+
115
+ // create new FAT file system and open it
116
+ let label = {
117
+ if let Some ( name) = bootloader_efi_file. file_stem ( ) {
118
+ let converted = name. to_string_lossy ( ) ;
119
+ let name = converted. as_bytes ( ) ;
120
+ let mut label = [ 0u8 ; 11 ] ;
121
+ let name = & name[ ..label. len ( ) ] ;
122
+ let slice = & mut label[ ..name. len ( ) ] ;
123
+ slice. copy_from_slice ( name) ;
124
+ label
125
+ } else {
126
+ * b"MY_RUST_OS!"
127
+ }
128
+ } ;
129
+ let format_options = fatfs:: FormatVolumeOptions :: new ( ) . volume_label ( label) ;
130
+ fatfs:: format_volume ( & fat_file, format_options) . context ( "Failed to format UEFI FAT file" ) ?;
131
+ let filesystem = fatfs:: FileSystem :: new ( & fat_file, fatfs:: FsOptions :: new ( ) )
132
+ . context ( "Failed to open FAT file system of UEFI FAT file" ) ?;
133
+
134
+ // copy EFI file to FAT filesystem
135
+ let root_dir = filesystem. root_dir ( ) ;
136
+ root_dir. create_dir ( "efi" ) . unwrap ( ) ;
137
+ root_dir. create_dir ( "efi/boot" ) . unwrap ( ) ;
138
+ let mut bootx64 = root_dir. create_file ( "efi/boot/bootx64.efi" ) . unwrap ( ) ;
139
+ bootx64. truncate ( ) . unwrap ( ) ;
140
+ io:: copy (
141
+ & mut fs:: File :: open ( & bootloader_efi_file) . unwrap ( ) ,
142
+ & mut bootx64,
143
+ )
144
+ . unwrap ( ) ;
145
+
146
+ // copy kernel to FAT filesystem
147
+ let mut kernel_file = root_dir. create_file ( "kernel-x86_64" ) ?;
148
+ kernel_file. truncate ( ) ?;
149
+ io:: copy ( & mut fs:: File :: open ( & kernel_binary) ?, & mut kernel_file) ?;
150
+
151
+ Ok ( ( ) )
152
+ }
153
+
154
+ fn create_gpt_disk ( fat_image : & Path , out_gpt_path : & Path ) {
155
+ // create new file
156
+ let mut disk = fs:: OpenOptions :: new ( )
157
+ . create ( true )
158
+ . truncate ( true )
159
+ . read ( true )
160
+ . write ( true )
161
+ . open ( & out_gpt_path)
162
+ . unwrap ( ) ;
163
+
164
+ // set file size
165
+ let partition_size: u64 = fs:: metadata ( & fat_image) . unwrap ( ) . len ( ) ;
166
+ let disk_size = partition_size + 1024 * 64 ; // for GPT headers
167
+ disk. set_len ( disk_size) . unwrap ( ) ;
168
+
169
+ // create a protective MBR at LBA0 so that disk is not considered
170
+ // unformatted on BIOS systems
171
+ let mbr = gpt:: mbr:: ProtectiveMBR :: with_lb_size (
172
+ u32:: try_from ( ( disk_size / 512 ) - 1 ) . unwrap_or ( 0xFF_FF_FF_FF ) ,
173
+ ) ;
174
+ mbr. overwrite_lba0 ( & mut disk) . unwrap ( ) ;
175
+
176
+ // create new GPT structure
177
+ let block_size = gpt:: disk:: LogicalBlockSize :: Lb512 ;
178
+ let mut gpt = gpt:: GptConfig :: new ( )
179
+ . writable ( true )
180
+ . initialized ( false )
181
+ . logical_block_size ( block_size)
182
+ . create_from_device ( Box :: new ( & mut disk) , None )
183
+ . unwrap ( ) ;
184
+ gpt. update_partitions ( Default :: default ( ) ) . unwrap ( ) ;
185
+
186
+ // add new EFI system partition and get its byte offset in the file
187
+ let partition_id = gpt
188
+ . add_partition ( "boot" , partition_size, gpt:: partition_types:: EFI , 0 , None )
189
+ . unwrap ( ) ;
190
+ let partition = gpt. partitions ( ) . get ( & partition_id) . unwrap ( ) ;
191
+ let start_offset = partition. bytes_start ( block_size) . unwrap ( ) ;
192
+
193
+ // close the GPT structure and write out changes
194
+ gpt. write ( ) . unwrap ( ) ;
195
+
196
+ // place the FAT filesystem in the newly created partition
197
+ disk. seek ( io:: SeekFrom :: Start ( start_offset) ) . unwrap ( ) ;
198
+ io:: copy ( & mut File :: open ( & fat_image) . unwrap ( ) , & mut disk) . unwrap ( ) ;
199
+ }
200
+
201
+ // Provides a function to turn a bootloader executable into a disk image.
202
+ //
203
+ // Used by the `builder` binary. Only available when the `builder` feature is enabled.
204
+ // pub mod disk_image;
0 commit comments