1use crate::Disk;
7use crate::PetriInitrd;
8use crate::ResolvedArtifact;
9use crate::mshv_image::MshvImage;
10use std::sync::Arc;
11
12#[derive(Debug, Clone)]
18pub struct MshvBoot {
19 pub host_boot: ResolvedArtifact,
21 pub kernel: ResolvedArtifact,
23 pub initrd: PetriInitrd,
25 pub boot_artifacts: ResolvedArtifact,
27 pub kernel_cmdline: String,
29 pub hypervisor_cmdline: String,
31}
32
33impl MshvBoot {
34 pub(crate) fn build(&self, use_virtio_vsock: bool) -> anyhow::Result<Disk> {
39 let image = tempfile::NamedTempFile::new()?.into_temp_path();
40 fs_err::remove_file(&image)?;
41 let boot = self.boot_artifacts.get().join("boot");
42 MshvImage {
43 host_boot: self.host_boot.get(),
44 payload: &boot,
45 kernel: self.kernel.get(),
46 initrd: self.initrd.path.as_ref(),
47 kernel_cmdline: &format!(
48 "{} {} rdinit={}",
49 self.kernel_cmdline,
50 crate::vm::linux_vsock_blacklist(use_virtio_vsock),
51 self.initrd.rdinit_param
52 ),
53 hypervisor_cmdline: &self.hypervisor_cmdline,
54 }
55 .build(&image)?;
56 Ok(Disk::Temporary(Arc::new(image)))
57 }
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63 use std::io::Read;
64 use test_with_tracing::test;
65
66 #[test]
67 fn explicit_inputs_build_guest_media_without_a_vm() -> anyhow::Result<()> {
68 let temp = tempfile::tempdir()?;
69 let boot = temp.path().join("boot");
70 fs_err::create_dir_all(boot.join("Windows/System32"))?;
71 for name in [
72 "HvLoader.efi",
73 "lxhvloader.dll",
74 "Windows/System32/hvix64.exe",
75 "Windows/System32/hvax64.exe",
76 "mshv_host_boot.efi",
77 ] {
78 fs_err::write(boot.join(name), name)?;
79 }
80 let kernel = temp.path().join("root-kernel");
81 let initrd_path = temp.path().join("prepared-initrd");
82 fs_err::write(&kernel, b"explicit root kernel")?;
83 let prepared = b"prepared initrd contents";
84 fs_err::write(&initrd_path, prepared)?;
85 let initrd = PetriInitrd {
86 path: crate::TempOrPersistentPath::Persistent(initrd_path.clone()),
87 rdinit_param: "/caller-init".into(),
88 };
89 let disk = MshvBoot {
90 host_boot: ResolvedArtifact::from_local_path(boot.join("mshv_host_boot.efi")),
91 kernel: ResolvedArtifact::from_local_path(kernel),
92 initrd,
93 boot_artifacts: ResolvedArtifact::from_local_path(temp.path().to_path_buf()),
94 kernel_cmdline: "test_option=caller".into(),
95 hypervisor_cmdline: "MSHV_SCHEDULER_TYPE=ROOT".into(),
96 }
97 .build(true)?;
98 let Disk::Temporary(path) = &disk else {
99 panic!("expected owned temporary disk")
100 };
101 let output = path.to_path_buf();
102 let mut image = fs_err::File::open(&output)?;
103 let gpt = gptman::GPT::read_from(&mut image, 512)?;
104 let partition = fscommon::StreamSlice::new(
105 image,
106 gpt[1].starting_lba * 512,
107 (gpt[1].ending_lba + 1) * 512,
108 )?;
109 let fs = fatfs::FileSystem::new(partition, fatfs::FsOptions::new())?;
110 let mut bytes = Vec::new();
111 fs.root_dir()
112 .open_file("bzImage")?
113 .read_to_end(&mut bytes)?;
114 assert_eq!(bytes, b"explicit root kernel");
115 bytes.clear();
116 fs.root_dir()
117 .open_file("initramfs.cpio")?
118 .read_to_end(&mut bytes)?;
119 assert_eq!(bytes, prepared);
120 let mut config = String::new();
121 fs.root_dir()
122 .open_file(mshv_host_boot::KERNEL_CMDLINE_FILE)?
123 .read_to_string(&mut config)?;
124 assert_eq!(
125 config,
126 "initrd=\\initramfs.cpio test_option=caller initcall_blacklist=hv_sock_init rdinit=/caller-init\n"
127 );
128 config.clear();
129 fs.root_dir()
130 .open_file(mshv_host_boot::HYPERVISOR_CMDLINE_FILE)?
131 .read_to_string(&mut config)?;
132 assert_eq!(config, "\\lxhvloader.dll MSHV_SCHEDULER_TYPE=ROOT\n");
133 fs.unmount()?;
134 assert_eq!(fs_err::read(&initrd_path)?, prepared);
135 drop(disk);
136 assert!(!output.exists());
137 Ok(())
138 }
139}