@@ -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,8 +176,16 @@ impl DomainBuilder {
160176 ) ?;
161177 writer. write_text_element ( "vcpu" , & vcpus. to_string ( ) ) ?;
162178
163- // OS section
164- writer. start_element ( "os" , & [ ] ) ?;
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 {
184+ writer. start_element ( "os" , & [ ( "firmware" , "efi" ) ] ) ?;
185+ } else {
186+ writer. start_element ( "os" , & [ ] ) ?;
187+ }
188+
165189 writer. write_text_element_with_attrs (
166190 "type" ,
167191 & arch_config. os_type ,
@@ -170,6 +194,12 @@ impl DomainBuilder {
170194 ( "machine" , & arch_config. machine ) ,
171195 ] ,
172196 ) ?;
197+
198+ if use_uefi && secure_boot {
199+ // Modern libvirt handles firmware paths automatically for secure boot
200+ writer. write_empty_element ( "loader" , & [ ( "secure" , "yes" ) ] ) ?;
201+ }
202+
173203 writer. write_empty_element ( "boot" , & [ ( "dev" , "hd" ) ] ) ?;
174204
175205 // Add kernel arguments if specified (for direct boot)
@@ -283,6 +313,13 @@ impl DomainBuilder {
283313 writer. end_element ( "filesystem" ) ?;
284314 }
285315
316+ // TPM device
317+ if self . tpm {
318+ writer. start_element ( "tpm" , & [ ( "model" , "tpm-tis" ) ] ) ?;
319+ writer. write_empty_element ( "backend" , & [ ( "type" , "emulator" ) , ( "version" , "2.0" ) ] ) ?;
320+ writer. end_element ( "tpm" ) ?;
321+ }
322+
286323 writer. end_element ( "devices" ) ?;
287324
288325 // QEMU commandline section (if we have QEMU args)
@@ -437,6 +474,78 @@ mod tests {
437474 assert ! ( xml. contains( "<timer name=\" rtc\" " ) ) ;
438475 }
439476
477+ #[ test]
478+ fn test_secure_boot_configuration ( ) {
479+ let builder = DomainBuilder :: new ( )
480+ . with_name ( "test-secure-boot" )
481+ . with_firmware ( "uefi-secure" ) ;
482+
483+ let xml = builder. build_xml ( ) . unwrap ( ) ;
484+
485+ // Should include secure boot loader configuration
486+ assert ! ( xml. contains( "loader" ) ) ;
487+ assert ! ( xml. contains( "secure=\" yes\" " ) ) ;
488+
489+ // Should use firmware="efi" for UEFI
490+ assert ! ( xml. contains( "firmware=\" efi\" " ) ) ;
491+
492+ // Test regular UEFI without secure boot
493+ let xml_regular = DomainBuilder :: new ( )
494+ . with_name ( "test-regular-uefi" )
495+ . with_firmware ( "uefi" )
496+ . build_xml ( )
497+ . unwrap ( ) ;
498+
499+ // Should use libvirt auto firmware selection
500+ assert ! ( xml_regular. contains( "firmware=\" efi\" " ) ) ;
501+ assert ! ( !xml_regular. contains( "secure=\" yes\" " ) ) ;
502+
503+ // Test BIOS firmware (no secure boot)
504+ let xml_bios = DomainBuilder :: new ( )
505+ . with_name ( "test-bios" )
506+ . with_firmware ( "bios" )
507+ . build_xml ( )
508+ . unwrap ( ) ;
509+
510+ // Should not have firmware="efi" or secure boot settings
511+ assert ! ( !xml_bios. contains( "firmware=\" efi\" " ) ) ;
512+ assert ! ( !xml_bios. contains( "secure=\" yes\" " ) ) ;
513+ }
514+
515+ #[ test]
516+ fn test_tpm_configuration ( ) {
517+ // Test TPM enabled (default)
518+ let xml = DomainBuilder :: new ( )
519+ . with_name ( "test-tpm-enabled" )
520+ . build_xml ( )
521+ . unwrap ( ) ;
522+
523+ // Should include TPM device by default
524+ assert ! ( xml. contains( "<tpm model=\" tpm-tis\" >" ) ) ;
525+ assert ! ( xml. contains( "<backend type=\" emulator\" version=\" 2.0\" />" ) ) ;
526+
527+ // Test TPM explicitly enabled
528+ let xml_enabled = DomainBuilder :: new ( )
529+ . with_name ( "test-tpm-explicit" )
530+ . with_tpm ( true )
531+ . build_xml ( )
532+ . unwrap ( ) ;
533+
534+ assert ! ( xml_enabled. contains( "<tpm model=\" tpm-tis\" >" ) ) ;
535+ assert ! ( xml_enabled. contains( "backend type=\" emulator\" " ) ) ;
536+
537+ // Test TPM disabled
538+ let xml_disabled = DomainBuilder :: new ( )
539+ . with_name ( "test-tpm-disabled" )
540+ . with_tpm ( false )
541+ . build_xml ( )
542+ . unwrap ( ) ;
543+
544+ // Should not contain TPM configuration
545+ assert ! ( !xml_disabled. contains( "<tpm" ) ) ;
546+ assert ! ( !xml_disabled. contains( "backend type=\" emulator\" " ) ) ;
547+ }
548+
440549 #[ test]
441550 fn test_virtiofs_filesystem_configuration ( ) {
442551 // Test read-write virtiofs filesystem
0 commit comments