mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-22 05:35:25 +00:00
tools: support validating SEV-ES initial vCPU state measurements
With the SEV-ES policy the VMSA state of each vCPU must be included in the measured data. The VMSA state can be generated using the 'sevctl' tool, by telling it a QEMU VMSA is required, and passing the hypevisor's CPU SKU (family, model, stepping). Reviewed-by: Cole Robinson <crobinso@redhat.com> Reviewed-by: Ján Tomko <jtomko@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
This commit is contained in:
parent
7d55c815c6
commit
3e7b7da9e0
@ -116,6 +116,23 @@ content if omitted.
|
||||
String containing any kernel command line parameters used during boot of the
|
||||
domain. Defaults to the empty string if omitted.
|
||||
|
||||
``-n COUNT``, ``--num-cpus=COUNT``
|
||||
|
||||
The number of virtual CPUs for the domain. This is required when the
|
||||
domain policy is set to require SEV-ES.
|
||||
|
||||
``-0 PATH``, ``--vmsa-cpu0=PATH``
|
||||
|
||||
Path to the VMSA initial state for the boot CPU. This is required when
|
||||
the domain policy is set to require SEV-ES. The file contents must be
|
||||
exactly 4096 bytes in length.
|
||||
|
||||
``-1 PATH``, ``--vmsa-cpu1=PATH``
|
||||
|
||||
Path to the VMSA initial state for the non-boot CPU. This is required when
|
||||
the domain policy is set to require SEV-ES and the domain has more than one
|
||||
CPU present. The file contents must be exactly 4096 bytes in length.
|
||||
|
||||
``--tik PATH``
|
||||
|
||||
TIK file for domain. This file must be exactly 16 bytes in size and contains the
|
||||
@ -212,6 +229,22 @@ Validate the measurement of a SEV guest with direct kernel boot:
|
||||
--build-id 13 \
|
||||
--policy 3
|
||||
|
||||
Validate the measurement of a SEV-ES SMP guest booting from disk:
|
||||
|
||||
::
|
||||
|
||||
# virt-dom-sev-validate \
|
||||
--firmware OVMF.sev.fd \
|
||||
--num-cpus 2 \
|
||||
--vmsa-cpu0 vmsa0.bin \
|
||||
--vmsa-cpu1 vmsa1.bin \
|
||||
--tk this-guest-tk.bin \
|
||||
--measurement Zs2pf19ubFSafpZ2WKkwquXvACx9Wt/BV+eJwQ/taO8jhyIj/F8swFrybR1fZ2ID \
|
||||
--api-major 0 \
|
||||
--api-minor 24 \
|
||||
--build-id 13 \
|
||||
--policy 7
|
||||
|
||||
Fetch from remote libvirt
|
||||
-------------------------
|
||||
|
||||
@ -245,6 +278,19 @@ Validate the measurement of a SEV guest with direct kernel boot:
|
||||
--tk this-guest-tk.bin \
|
||||
--domain fedora34x86_64
|
||||
|
||||
Validate the measurement of a SEV-ES SMP guest booting from disk:
|
||||
|
||||
::
|
||||
|
||||
# virt-dom-sev-validate \
|
||||
--connect qemu+ssh://root@some.remote.host/system \
|
||||
--firmware OVMF.sev.fd \
|
||||
--num-cpus 2 \
|
||||
--vmsa-cpu0 vmsa0.bin \
|
||||
--vmsa-cpu1 vmsa1.bin \
|
||||
--tk this-guest-tk.bin \
|
||||
--domain fedora34x86_64
|
||||
|
||||
Fetch from local libvirt
|
||||
------------------------
|
||||
|
||||
@ -274,6 +320,18 @@ Validate the measurement of a SEV guest with direct kernel boot:
|
||||
--tk this-guest-tk.bin \
|
||||
--domain fedora34x86_64
|
||||
|
||||
Validate the measurement of a SEV-ES SMP guest booting from disk:
|
||||
|
||||
::
|
||||
|
||||
# virt-dom-sev-validate \
|
||||
--insecure \
|
||||
--num-cpus 2 \
|
||||
--vmsa-cpu0 vmsa0.bin \
|
||||
--vmsa-cpu1 vmsa1.bin \
|
||||
--tk this-guest-tk.bin \
|
||||
--domain fedora34x86_64
|
||||
|
||||
EXIT STATUS
|
||||
===========
|
||||
|
||||
|
@ -158,13 +158,16 @@ class KernelTable(GUIDTable):
|
||||
|
||||
|
||||
class ConfidentialVM(object):
|
||||
POLICY_BIT_SEV_ES = 2
|
||||
POLICY_VAL_SEV_ES = (1 << POLICY_BIT_SEV_ES)
|
||||
|
||||
def __init__(self,
|
||||
measurement=None,
|
||||
api_major=None,
|
||||
api_minor=None,
|
||||
build_id=None,
|
||||
policy=None):
|
||||
policy=None,
|
||||
num_cpus=None):
|
||||
self.measurement = measurement
|
||||
self.api_major = api_major
|
||||
self.api_minor = api_minor
|
||||
@ -175,8 +178,15 @@ class ConfidentialVM(object):
|
||||
self.tik = None
|
||||
self.tek = None
|
||||
|
||||
self.num_cpus = num_cpus
|
||||
self.vmsa_cpu0 = None
|
||||
self.vmsa_cpu1 = None
|
||||
|
||||
self.kernel_table = KernelTable()
|
||||
|
||||
def is_sev_es(self):
|
||||
return self.policy & self.POLICY_VAL_SEV_ES
|
||||
|
||||
def load_tik_tek(self, tik_path, tek_path):
|
||||
with open(tik_path, 'rb') as fh:
|
||||
self.tik = fh.read()
|
||||
@ -212,6 +222,43 @@ class ConfidentialVM(object):
|
||||
self.firmware = fh.read()
|
||||
log.debug("Firmware(sha256): %s", sha256(self.firmware).hexdigest())
|
||||
|
||||
@staticmethod
|
||||
def _load_vmsa(path):
|
||||
with open(path, 'rb') as fh:
|
||||
vmsa = fh.read()
|
||||
|
||||
if len(vmsa) != 4096:
|
||||
raise UnsupportedUsageException(
|
||||
"VMSA must be 4096 bytes in length")
|
||||
return vmsa
|
||||
|
||||
def load_vmsa_cpu0(self, path):
|
||||
self.vmsa_cpu0 = self._load_vmsa(path)
|
||||
log.debug("VMSA CPU 0(sha256): %s",
|
||||
sha256(self.vmsa_cpu0).hexdigest())
|
||||
|
||||
def load_vmsa_cpu1(self, path):
|
||||
self.vmsa_cpu1 = self._load_vmsa(path)
|
||||
log.debug("VMSA CPU 1(sha256): %s",
|
||||
sha256(self.vmsa_cpu1).hexdigest())
|
||||
|
||||
def get_cpu_state(self):
|
||||
if self.num_cpus is None:
|
||||
raise UnsupportedUsageException(
|
||||
"Number of virtual CPUs must be specified for SEV-ES domain")
|
||||
|
||||
if self.vmsa_cpu0 is None:
|
||||
raise UnsupportedUsageException(
|
||||
"VMSA for boot CPU required for SEV-ES domain")
|
||||
|
||||
if self.num_cpus > 1 and self.vmsa_cpu1 is None:
|
||||
raise UnsupportedUsageException(
|
||||
"VMSA for additional CPUs required for SEV-ES domain with SMP")
|
||||
|
||||
vmsa = self.vmsa_cpu0 + (self.vmsa_cpu1 * (self.num_cpus - 1))
|
||||
log.debug("VMSA(sha256): %s", sha256(vmsa).hexdigest())
|
||||
return vmsa
|
||||
|
||||
# Get the full set of measured launch data for the domain
|
||||
#
|
||||
# The measured data that the guest is initialized with is the concatenation
|
||||
@ -222,6 +269,8 @@ class ConfidentialVM(object):
|
||||
def get_measured_data(self):
|
||||
measured_data = (self.firmware +
|
||||
self.kernel_table.build())
|
||||
if self.is_sev_es():
|
||||
measured_data += self.get_cpu_state()
|
||||
log.debug("Measured-data(sha256): %s",
|
||||
sha256(measured_data).hexdigest())
|
||||
return measured_data
|
||||
@ -459,6 +508,12 @@ def parse_command_line():
|
||||
help='Path to the initrd binary')
|
||||
vmconfig.add_argument('--cmdline', '-e',
|
||||
help='Cmdline string booted with')
|
||||
vmconfig.add_argument('--num-cpus', '-n', type=int,
|
||||
help='Number of virtual CPUs')
|
||||
vmconfig.add_argument('--vmsa-cpu0', '-0',
|
||||
help='VMSA state for the boot CPU')
|
||||
vmconfig.add_argument('--vmsa-cpu1', '-1',
|
||||
help='VMSA state for the additional CPUs')
|
||||
vmconfig.add_argument('--tik',
|
||||
help='TIK file for domain')
|
||||
vmconfig.add_argument('--tek',
|
||||
@ -529,13 +584,15 @@ def attest(args):
|
||||
api_major=args.api_major,
|
||||
api_minor=args.api_minor,
|
||||
build_id=args.build_id,
|
||||
policy=args.policy)
|
||||
policy=args.policy,
|
||||
num_cpus=args.num_cpus)
|
||||
else:
|
||||
cvm = LibvirtConfidentialVM(measurement=args.measurement,
|
||||
api_major=args.api_major,
|
||||
api_minor=args.api_minor,
|
||||
build_id=args.build_id,
|
||||
policy=args.policy)
|
||||
policy=args.policy,
|
||||
num_cpus=args.num_cpus)
|
||||
|
||||
if args.firmware is not None:
|
||||
cvm.load_firmware(args.firmware)
|
||||
@ -554,6 +611,12 @@ def attest(args):
|
||||
if args.cmdline is not None:
|
||||
cvm.kernel_table.load_cmdline(args.cmdline)
|
||||
|
||||
if args.vmsa_cpu0 is not None:
|
||||
cvm.load_vmsa_cpu0(args.vmsa_cpu0)
|
||||
|
||||
if args.vmsa_cpu1 is not None:
|
||||
cvm.load_vmsa_cpu1(args.vmsa_cpu1)
|
||||
|
||||
if args.domain is not None:
|
||||
cvm.load_domain(args.connect,
|
||||
args.domain,
|
||||
|
Loading…
Reference in New Issue
Block a user