xtask\tasks\guest_test\uefi/
gpt_efi_disk.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use anyhow::Context;
5use anyhow::Result;
6use fatfs::FileSystem;
7use fatfs::FormatVolumeOptions;
8use fatfs::FsOptions;
9use guid::Guid;
10use std::io::Cursor;
11use std::io::Seek;
12use std::path::Path;
13
14const SECTOR_SIZE: usize = 512;
15const EFI_GUID: Guid = guid::guid!("{c12a7328-f81f-11d2-ba4b-00a0c93ec93b}");
16
17pub fn create_gpt_efi_disk(out_img: &Path, with_files: &[(&Path, &Path)]) -> Result<()> {
18    if out_img.extension().unwrap_or_default() != "img" {
19        return Err(anyhow::anyhow!(
20            "only .img disk images are supported at this time"
21        ));
22    }
23
24    let disk_size = 1024 * 1024 * 32; // 32MB disk should be enough for our tests
25    let num_sectors = disk_size / SECTOR_SIZE;
26
27    let mut disk = vec![0; num_sectors * SECTOR_SIZE];
28
29    let efi_partition_range = {
30        let mut cur = Cursor::new(&mut disk);
31        let mut gpt =
32            gptman::GPT::new_from(&mut cur, SECTOR_SIZE as u64, Guid::new_random().into())?;
33
34        // Set up the "Protective" Master Boot Record
35        gptman::GPT::write_protective_mbr_into(&mut cur, SECTOR_SIZE as u64)?;
36
37        // Set up the GPT Partition Table Header
38        gpt[1] = gptman::GPTPartitionEntry {
39            partition_type_guid: EFI_GUID.into(),
40            unique_partition_guid: Guid::new_random().into(),
41            starting_lba: gpt.header.first_usable_lba,
42            ending_lba: gpt.header.last_usable_lba,
43            attribute_bits: 0,
44            partition_name: "EFI".into(),
45        };
46        gpt.write_into(&mut cur)?;
47
48        // calculate the EFI partition's usable range
49        let partition_start_byte = gpt[1].starting_lba as usize * SECTOR_SIZE;
50        let partition_num_bytes = (gpt[1].ending_lba - gpt[1].starting_lba) as usize * SECTOR_SIZE;
51        partition_start_byte..partition_start_byte + partition_num_bytes
52    };
53
54    init_fat(&mut disk[efi_partition_range], with_files).context("initializing FAT partition")?;
55
56    fs_err::write(out_img, &disk)?;
57    log::info!("Wrote test image to: {}", out_img.display());
58
59    Ok(())
60}
61
62fn init_fat(partition: &mut [u8], with_files: &[(&Path, &Path)]) -> Result<()> {
63    let efi_fs = {
64        let mut cursor = Cursor::new(partition);
65        fatfs::format_volume(
66            &mut cursor,
67            FormatVolumeOptions::new()
68                .fat_type(fatfs::FatType::Fat32)
69                .volume_label(*b"hvlite_test"),
70        )?;
71
72        cursor.rewind()?;
73        FileSystem::new(cursor, FsOptions::new().update_accessed_date(false))?
74    };
75
76    let root_dir = efi_fs.root_dir();
77    for (dst_file, src_file) in with_files {
78        let ancestors = dst_file.ancestors().collect::<Vec<_>>();
79        let num_ancestors = ancestors.len();
80
81        for (i, chunk) in ancestors.into_iter().rev().enumerate() {
82            // skip the root '/'
83            if i == 0 {
84                continue;
85            }
86
87            if i != num_ancestors - 1 {
88                log::info!("creating dir {}", chunk.display());
89                root_dir
90                    .create_dir(chunk.to_str().unwrap())
91                    .context("creating dir")?;
92            } else {
93                log::info!("creating file {}", chunk.display());
94                let mut file = root_dir
95                    .create_file(chunk.to_str().unwrap())
96                    .context(format!("creating file {}", chunk.display()))?;
97                std::io::copy(&mut fs_err::File::open(src_file)?, &mut file)?;
98            }
99        }
100    }
101
102    log::info!("{:?}", efi_fs.stats()?);
103
104    Ok(())
105}