virtinst/guest: enable a TPM by default if UEFI is present

The bare metal world is moving to a situation where UEFI is going to be
the only supported firmware and there will be a strong expectation for
TPM and SecureBoot support.

With this in mind, if we're enabling UEFI on a VM, it makes sense to
also provide a TPM alongside it.

Since this requires swtpm to be installed we can't do this
unconditionally. The forthcoming libvirt release expands the domain
capabilities to report whether TPMs are supported, so we check that.

The user can disable the default TPM by requesting --tpm none

https://github.com/virt-manager/virt-manager/issues/310
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
This commit is contained in:
Daniel P. Berrangé 2021-11-24 16:05:16 +00:00 committed by Cole Robinson
parent 7e1f886aa9
commit eb58c09f48
7 changed files with 115 additions and 0 deletions

View File

@ -118,6 +118,16 @@
<value>vfio</value>
</enum>
</hostdev>
<tpm supported='yes'>
<enum name='model'>
<value>tpm-tis</value>
<value>tpm-crb</value>
</enum>
<enum name='backendModel'>
<value>passthrough</value>
<value>emulator</value>
</enum>
</tpm>
</devices>
<features>
<gic supported='no'/>

View File

@ -0,0 +1,65 @@
<domain type="kvm">
<name>vm1</name>
<uuid>00000000-1111-2222-3333-444444444444</uuid>
<memory>65536</memory>
<currentMemory>65536</currentMemory>
<vcpu>1</vcpu>
<os>
<type arch="x86_64" machine="q35">hvm</type>
<loader readonly="yes" secure="yes" type="pflash">/usr/share/ovmf/OVMF_CODE.secboot.fd</loader>
<boot dev="hd"/>
</os>
<features>
<acpi/>
<apic/>
<smm state="on"/>
<vmport state="off"/>
</features>
<cpu mode="host-model"/>
<clock offset="utc">
<timer name="rtc" tickpolicy="catchup"/>
<timer name="pit" tickpolicy="delay"/>
<timer name="hpet" present="no"/>
</clock>
<pm>
<suspend-to-mem enabled="no"/>
<suspend-to-disk enabled="no"/>
</pm>
<devices>
<emulator>/usr/bin/qemu-kvm</emulator>
<disk type="file" device="disk">
<driver name="qemu" type="qcow2"/>
<source file="/var/lib/libvirt/images/disk.qcow2"/>
<target dev="sda" bus="sata"/>
</disk>
<controller type="usb" model="ich9-ehci1"/>
<controller type="usb" model="ich9-uhci1">
<master startport="0"/>
</controller>
<controller type="usb" model="ich9-uhci2">
<master startport="2"/>
</controller>
<controller type="usb" model="ich9-uhci3">
<master startport="4"/>
</controller>
<interface type="bridge">
<source bridge="testsuitebr0"/>
<mac address="00:11:22:33:44:55"/>
<model type="e1000e"/>
</interface>
<console type="pty"/>
<channel type="spicevmc">
<target type="virtio" name="com.redhat.spice.0"/>
</channel>
<input type="tablet" bus="usb"/>
<graphics type="spice" port="-1" tlsPort="-1" autoport="yes">
<image compression="off"/>
</graphics>
<sound model="ich9"/>
<video>
<model type="qxl"/>
</video>
<redirdev bus="usb" type="spicevmc"/>
<redirdev bus="usb" type="spicevmc"/>
</devices>
</domain>

View File

@ -47,6 +47,9 @@
<target type="virtio" name="com.redhat.spice.0"/>
</channel>
<input type="tablet" bus="usb"/>
<tpm model="tpm-tis">
<backend type="emulator"/>
</tpm>
<graphics type="spice" port="-1" tlsPort="-1" autoport="yes">
<image compression="off"/>
</graphics>

View File

@ -1076,6 +1076,7 @@ c.add_invalid("--boot uefi --machine q35 --launchSecurity sev,policy=0x0001 --co
c = vinst.add_category("kvm-q35", "--noautoconsole --connect " + utils.URIs.kvm_q35)
c.add_compare("--boot uefi --disk none", "boot-uefi")
c.add_compare("--boot uefi --disk size=8 --tpm none", "boot-uefi-notpm")
c = vinst.add_category("kvm-arm", "--connect %(URI-KVM)s --noautoconsole", precompare_check="3.3.0") # required qemu-xhci from libvirt 3.3.0

View File

@ -4079,6 +4079,7 @@ class ParserTPM(VirtCLIParser):
cli_arg_name = "tpm"
guest_propname = "devices.tpm"
remove_first = "type"
stub_none = False
aliases = {
"backend.type": "type",
"backend.version": "version",
@ -4086,6 +4087,10 @@ class ParserTPM(VirtCLIParser):
}
def _parse(self, inst):
if self.optstr == "none":
self.guest.skip_default_tpm = True
return
if (self.optdict.get("type", "").startswith("/")):
self.optdict["path"] = self.optdict.pop("type")
return super()._parse(inst)

View File

@ -101,6 +101,7 @@ class _Devices(_CapsBlock):
disk = XMLChildProperty(_make_capsblock("disk"), is_single=True)
video = XMLChildProperty(_make_capsblock("video"), is_single=True)
graphics = XMLChildProperty(_make_capsblock("graphics"), is_single=True)
tpm = XMLChildProperty(_make_capsblock("tpm"), is_single=True)
class _Features(_CapsBlock):
@ -351,6 +352,15 @@ class DomainCapabilities(XMLBuilder):
models = self.devices.video.get_enum("modelType").get_values()
return bool("bochs" in models)
def supports_tpm_emulator(self):
"""
Returns False if either libvirt or qemu do not have support for
emulating a TPM.
"""
models = self.devices.tpm.get_enum("model").get_values()
backends = self.devices.tpm.get_enum("backendModel").get_values()
return len(models) > 0 and bool("emulator" in backends)
def supports_graphics_spice(self):
if not self.devices.graphics.supported:
# domcaps is too old, or the driver doesn't advertise graphics

View File

@ -199,6 +199,7 @@ class Guest(XMLBuilder):
self.skip_default_usbredir = False
self.skip_default_graphics = False
self.skip_default_rng = False
self.skip_default_tpm = False
self.x86_cpu_default = self.cpu.SPECIAL_MODE_APP_DEFAULT
self.skip_default_osinfo = False
@ -724,6 +725,11 @@ class Guest(XMLBuilder):
self._add_default_channels()
self._add_default_rng()
self._add_default_memballoon()
if self.is_uefi():
# If the guest is using UEFI, we take that as a
# flag that the VM is targeting a modern platform
# and thus we should also provide an emulated TPM.
self._add_default_tpm()
self.clock.set_defaults(self)
self.cpu.set_defaults(self)
@ -947,6 +953,21 @@ class Guest(XMLBuilder):
dev.device = "/dev/urandom"
self.add_device(dev)
def _add_default_tpm(self):
if self.skip_default_tpm:
return
if self.devices.tpm:
return
if not self.lookup_domcaps().supports_tpm_emulator():
log.debug("Domain caps doesn't report TPM support")
return
log.debug("Adding default TPM")
dev = DeviceTpm(self.conn)
dev.type = DeviceTpm.TYPE_EMULATOR
self.add_device(dev)
def _add_default_memballoon(self):
if self.devices.memballoon:
return