@@ -36,6 +36,8 @@ pub struct DomainBuilder {
3636 metadata : HashMap < String , String > ,
3737 qemu_args : Vec < String > ,
3838 virtiofs_filesystems : Vec < VirtiofsFilesystem > ,
39+ firmware : Option < String > , // "uefi" (default), "uefi-secure", or "bios"
40+ tpm : bool ,
3941}
4042
4143impl Default for DomainBuilder {
@@ -59,6 +61,8 @@ impl DomainBuilder {
5961 metadata : HashMap :: new ( ) ,
6062 qemu_args : Vec :: new ( ) ,
6163 virtiofs_filesystems : Vec :: new ( ) ,
64+ firmware : None , // Defaults to UEFI
65+ tpm : true , // Default to enabled
6266 }
6367 }
6468
@@ -122,6 +126,18 @@ impl DomainBuilder {
122126 self
123127 }
124128
129+ /// Set firmware type (\"uefi\", \"uefi-secure\", or \"bios\", defaults to \"uefi\")
130+ pub fn with_firmware ( mut self , firmware : & str ) -> Self {
131+ self . firmware = Some ( firmware. to_string ( ) ) ;
132+ self
133+ }
134+
135+ /// Enable TPM 2.0 support using swtpm
136+ pub fn with_tpm ( mut self , tpm : bool ) -> Self {
137+ self . tpm = tpm;
138+ self
139+ }
140+
125141 /// Build the domain XML
126142 pub fn build_xml ( self ) -> Result < String > {
127143 let name = self . name . ok_or_else ( || eyre ! ( "Domain name is required" ) ) ?;
@@ -160,17 +176,80 @@ impl DomainBuilder {
160176 ) ?;
161177 writer. write_text_element ( "vcpu" , & vcpus. to_string ( ) ) ?;
162178
163- // OS section
164- writer. start_element ( "os" , & [ ] ) ?;
165- writer. write_text_element_with_attrs (
166- "type" ,
167- & arch_config. os_type ,
168- & [
169- ( "arch" , & arch_config. arch ) ,
170- ( "machine" , & arch_config. machine ) ,
171- ] ,
172- ) ?;
173- writer. write_empty_element ( "boot" , & [ ( "dev" , "hd" ) ] ) ?;
179+ // OS section with firmware configuration
180+ let use_uefi = self . firmware . as_deref ( ) != Some ( "bios" ) ;
181+ let secure_boot = self . firmware . as_deref ( ) == Some ( "uefi-secure" ) ;
182+
183+ if use_uefi && secure_boot {
184+ // Secure boot requires explicit firmware paths
185+ writer. start_element ( "os" , & [ ( "firmware" , "efi" ) ] ) ?;
186+ writer. write_text_element_with_attrs (
187+ "type" ,
188+ & arch_config. os_type ,
189+ & [
190+ ( "arch" , & arch_config. arch ) ,
191+ ( "machine" , & arch_config. machine ) ,
192+ ] ,
193+ ) ?;
194+
195+ // Define architecture-specific firmware paths
196+ let ( code_path, nvram_template) = match arch_config. arch {
197+ "x86_64" => (
198+ "/usr/share/edk2/ovmf/OVMF_CODE.secboot.fd" ,
199+ "/usr/share/edk2/ovmf/OVMF_VARS.secboot.fd" ,
200+ ) ,
201+ "aarch64" => (
202+ "/usr/share/edk2/aarch64/QEMU_EFI.fd" ,
203+ "/usr/share/edk2/aarch64/QEMU_VARS.fd" ,
204+ ) ,
205+ _ => {
206+ return Err ( eyre ! (
207+ "Secure boot not supported for architecture: {}" ,
208+ arch_config. arch
209+ ) ) ;
210+ }
211+ } ;
212+
213+ writer. write_text_element_with_attrs (
214+ "loader" ,
215+ code_path,
216+ & [ ( "readonly" , "yes" ) , ( "type" , "pflash" ) , ( "secure" , "yes" ) ] ,
217+ ) ?;
218+
219+ // Generate per-domain NVRAM path
220+ let nvram_path = format ! ( "/var/lib/libvirt/qemu/nvram/{}_VARS.fd" , & name) ;
221+ writer. write_text_element_with_attrs (
222+ "nvram" ,
223+ & nvram_path,
224+ & [ ( "template" , nvram_template) ] ,
225+ ) ?;
226+
227+ writer. write_empty_element ( "boot" , & [ ( "dev" , "hd" ) ] ) ?;
228+ } else if use_uefi {
229+ // Regular UEFI without secure boot
230+ writer. start_element ( "os" , & [ ( "firmware" , "efi" ) ] ) ?;
231+ writer. write_text_element_with_attrs (
232+ "type" ,
233+ & arch_config. os_type ,
234+ & [
235+ ( "arch" , & arch_config. arch ) ,
236+ ( "machine" , & arch_config. machine ) ,
237+ ] ,
238+ ) ?;
239+ writer. write_empty_element ( "boot" , & [ ( "dev" , "hd" ) ] ) ?;
240+ } else {
241+ // BIOS firmware
242+ writer. start_element ( "os" , & [ ] ) ?;
243+ writer. write_text_element_with_attrs (
244+ "type" ,
245+ & arch_config. os_type ,
246+ & [
247+ ( "arch" , & arch_config. arch ) ,
248+ ( "machine" , & arch_config. machine ) ,
249+ ] ,
250+ ) ?;
251+ writer. write_empty_element ( "boot" , & [ ( "dev" , "hd" ) ] ) ?;
252+ }
174253
175254 // Add kernel arguments if specified (for direct boot)
176255 if let Some ( ref kargs) = self . kernel_args {
@@ -283,6 +362,15 @@ impl DomainBuilder {
283362 writer. end_element ( "filesystem" ) ?;
284363 }
285364
365+ // TPM device
366+ if self . tpm {
367+ writer. start_element ( "tpm" , & [ ( "model" , "tpm-tis" ) ] ) ?;
368+ writer. start_element ( "backend" , & [ ( "type" , "emulator" ) , ( "version" , "2.0" ) ] ) ?;
369+ writer. write_empty_element ( "device" , & [ ( "path" , "/dev/tpm0" ) ] ) ?;
370+ writer. end_element ( "backend" ) ?;
371+ writer. end_element ( "tpm" ) ?;
372+ }
373+
286374 writer. end_element ( "devices" ) ?;
287375
288376 // QEMU commandline section (if we have QEMU args)
@@ -437,6 +525,96 @@ mod tests {
437525 assert ! ( xml. contains( "<timer name=\" rtc\" " ) ) ;
438526 }
439527
528+ #[ test]
529+ fn test_secure_boot_configuration ( ) {
530+ // Test secure boot enabled with uefi-secure
531+ let xml = DomainBuilder :: new ( )
532+ . with_name ( "test-secure-boot" )
533+ . with_firmware ( "uefi-secure" )
534+ . build_xml ( )
535+ . unwrap ( ) ;
536+
537+ // Should include explicit loader and nvram configuration
538+ assert ! ( xml. contains( "loader" ) ) ;
539+ assert ! ( xml. contains( "nvram" ) ) ;
540+ assert ! ( xml. contains( "secure=\" yes\" " ) ) ;
541+ assert ! ( xml. contains( "template=" ) ) ;
542+
543+ // Should include secure boot firmware paths based on architecture
544+ let arch = std:: env:: consts:: ARCH ;
545+ match arch {
546+ "x86_64" => {
547+ assert ! ( xml. contains( "/usr/share/edk2/ovmf/OVMF_CODE.secboot.fd" ) ) ;
548+ assert ! ( xml. contains( "/usr/share/edk2/ovmf/OVMF_VARS.secboot.fd" ) ) ;
549+ }
550+ "aarch64" => {
551+ assert ! ( xml. contains( "/usr/share/edk2/aarch64/QEMU_EFI.fd" ) ) ;
552+ assert ! ( xml. contains( "/usr/share/edk2/aarch64/QEMU_VARS.fd" ) ) ;
553+ }
554+ _ => {
555+ // Test should still pass for unsupported architectures
556+ }
557+ }
558+
559+ // Test regular UEFI without secure boot
560+ let xml_regular = DomainBuilder :: new ( )
561+ . with_name ( "test-regular-uefi" )
562+ . with_firmware ( "uefi" )
563+ . build_xml ( )
564+ . unwrap ( ) ;
565+
566+ // Should use libvirt auto firmware selection
567+ assert ! ( xml_regular. contains( "firmware=\" efi\" " ) ) ;
568+ assert ! ( !xml_regular. contains( "secure=\" yes\" " ) ) ;
569+ assert ! ( !xml_regular. contains( "template=" ) ) ;
570+
571+ // Test BIOS firmware (no secure boot)
572+ let xml_bios = DomainBuilder :: new ( )
573+ . with_name ( "test-bios" )
574+ . with_firmware ( "bios" )
575+ . build_xml ( )
576+ . unwrap ( ) ;
577+
578+ // Should not have firmware="efi" or secure boot settings
579+ assert ! ( !xml_bios. contains( "firmware=\" efi\" " ) ) ;
580+ assert ! ( !xml_bios. contains( "secure=\" yes\" " ) ) ;
581+ }
582+
583+ #[ test]
584+ fn test_tpm_configuration ( ) {
585+ // Test TPM enabled (default)
586+ let xml = DomainBuilder :: new ( )
587+ . with_name ( "test-tpm-enabled" )
588+ . build_xml ( )
589+ . unwrap ( ) ;
590+
591+ // Should include TPM device by default
592+ assert ! ( xml. contains( "<tpm model=\" tpm-tis\" >" ) ) ;
593+ assert ! ( xml. contains( "<backend type=\" emulator\" version=\" 2.0\" >" ) ) ;
594+ assert ! ( xml. contains( "<device path=\" /dev/tpm0\" />" ) ) ;
595+
596+ // Test TPM explicitly enabled
597+ let xml_enabled = DomainBuilder :: new ( )
598+ . with_name ( "test-tpm-explicit" )
599+ . with_tpm ( true )
600+ . build_xml ( )
601+ . unwrap ( ) ;
602+
603+ assert ! ( xml_enabled. contains( "<tpm model=\" tpm-tis\" >" ) ) ;
604+ assert ! ( xml_enabled. contains( "backend type=\" emulator\" " ) ) ;
605+
606+ // Test TPM disabled
607+ let xml_disabled = DomainBuilder :: new ( )
608+ . with_name ( "test-tpm-disabled" )
609+ . with_tpm ( false )
610+ . build_xml ( )
611+ . unwrap ( ) ;
612+
613+ // Should not contain TPM configuration
614+ assert ! ( !xml_disabled. contains( "<tpm" ) ) ;
615+ assert ! ( !xml_disabled. contains( "backend type=\" emulator\" " ) ) ;
616+ }
617+
440618 #[ test]
441619 fn test_virtiofs_filesystem_configuration ( ) {
442620 // Test read-write virtiofs filesystem
0 commit comments