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