mirror of
https://github.com/libvirt/libvirt.git
synced 2025-02-25 18:55:26 -06: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
|
String containing any kernel command line parameters used during boot of the
|
||||||
domain. Defaults to the empty string if omitted.
|
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 PATH``
|
||||||
|
|
||||||
TIK file for domain. This file must be exactly 16 bytes in size and contains the
|
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 \
|
--build-id 13 \
|
||||||
--policy 3
|
--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
|
Fetch from remote libvirt
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
@ -245,6 +278,19 @@ Validate the measurement of a SEV guest with direct kernel boot:
|
|||||||
--tk this-guest-tk.bin \
|
--tk this-guest-tk.bin \
|
||||||
--domain fedora34x86_64
|
--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
|
Fetch from local libvirt
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
@ -274,6 +320,18 @@ Validate the measurement of a SEV guest with direct kernel boot:
|
|||||||
--tk this-guest-tk.bin \
|
--tk this-guest-tk.bin \
|
||||||
--domain fedora34x86_64
|
--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
|
EXIT STATUS
|
||||||
===========
|
===========
|
||||||
|
|
||||||
|
@ -158,13 +158,16 @@ class KernelTable(GUIDTable):
|
|||||||
|
|
||||||
|
|
||||||
class ConfidentialVM(object):
|
class ConfidentialVM(object):
|
||||||
|
POLICY_BIT_SEV_ES = 2
|
||||||
|
POLICY_VAL_SEV_ES = (1 << POLICY_BIT_SEV_ES)
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
measurement=None,
|
measurement=None,
|
||||||
api_major=None,
|
api_major=None,
|
||||||
api_minor=None,
|
api_minor=None,
|
||||||
build_id=None,
|
build_id=None,
|
||||||
policy=None):
|
policy=None,
|
||||||
|
num_cpus=None):
|
||||||
self.measurement = measurement
|
self.measurement = measurement
|
||||||
self.api_major = api_major
|
self.api_major = api_major
|
||||||
self.api_minor = api_minor
|
self.api_minor = api_minor
|
||||||
@ -175,8 +178,15 @@ class ConfidentialVM(object):
|
|||||||
self.tik = None
|
self.tik = None
|
||||||
self.tek = None
|
self.tek = None
|
||||||
|
|
||||||
|
self.num_cpus = num_cpus
|
||||||
|
self.vmsa_cpu0 = None
|
||||||
|
self.vmsa_cpu1 = None
|
||||||
|
|
||||||
self.kernel_table = KernelTable()
|
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):
|
def load_tik_tek(self, tik_path, tek_path):
|
||||||
with open(tik_path, 'rb') as fh:
|
with open(tik_path, 'rb') as fh:
|
||||||
self.tik = fh.read()
|
self.tik = fh.read()
|
||||||
@ -212,6 +222,43 @@ class ConfidentialVM(object):
|
|||||||
self.firmware = fh.read()
|
self.firmware = fh.read()
|
||||||
log.debug("Firmware(sha256): %s", sha256(self.firmware).hexdigest())
|
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
|
# Get the full set of measured launch data for the domain
|
||||||
#
|
#
|
||||||
# The measured data that the guest is initialized with is the concatenation
|
# The measured data that the guest is initialized with is the concatenation
|
||||||
@ -222,6 +269,8 @@ class ConfidentialVM(object):
|
|||||||
def get_measured_data(self):
|
def get_measured_data(self):
|
||||||
measured_data = (self.firmware +
|
measured_data = (self.firmware +
|
||||||
self.kernel_table.build())
|
self.kernel_table.build())
|
||||||
|
if self.is_sev_es():
|
||||||
|
measured_data += self.get_cpu_state()
|
||||||
log.debug("Measured-data(sha256): %s",
|
log.debug("Measured-data(sha256): %s",
|
||||||
sha256(measured_data).hexdigest())
|
sha256(measured_data).hexdigest())
|
||||||
return measured_data
|
return measured_data
|
||||||
@ -459,6 +508,12 @@ def parse_command_line():
|
|||||||
help='Path to the initrd binary')
|
help='Path to the initrd binary')
|
||||||
vmconfig.add_argument('--cmdline', '-e',
|
vmconfig.add_argument('--cmdline', '-e',
|
||||||
help='Cmdline string booted with')
|
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',
|
vmconfig.add_argument('--tik',
|
||||||
help='TIK file for domain')
|
help='TIK file for domain')
|
||||||
vmconfig.add_argument('--tek',
|
vmconfig.add_argument('--tek',
|
||||||
@ -529,13 +584,15 @@ def attest(args):
|
|||||||
api_major=args.api_major,
|
api_major=args.api_major,
|
||||||
api_minor=args.api_minor,
|
api_minor=args.api_minor,
|
||||||
build_id=args.build_id,
|
build_id=args.build_id,
|
||||||
policy=args.policy)
|
policy=args.policy,
|
||||||
|
num_cpus=args.num_cpus)
|
||||||
else:
|
else:
|
||||||
cvm = LibvirtConfidentialVM(measurement=args.measurement,
|
cvm = LibvirtConfidentialVM(measurement=args.measurement,
|
||||||
api_major=args.api_major,
|
api_major=args.api_major,
|
||||||
api_minor=args.api_minor,
|
api_minor=args.api_minor,
|
||||||
build_id=args.build_id,
|
build_id=args.build_id,
|
||||||
policy=args.policy)
|
policy=args.policy,
|
||||||
|
num_cpus=args.num_cpus)
|
||||||
|
|
||||||
if args.firmware is not None:
|
if args.firmware is not None:
|
||||||
cvm.load_firmware(args.firmware)
|
cvm.load_firmware(args.firmware)
|
||||||
@ -554,6 +611,12 @@ def attest(args):
|
|||||||
if args.cmdline is not None:
|
if args.cmdline is not None:
|
||||||
cvm.kernel_table.load_cmdline(args.cmdline)
|
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:
|
if args.domain is not None:
|
||||||
cvm.load_domain(args.connect,
|
cvm.load_domain(args.connect,
|
||||||
args.domain,
|
args.domain,
|
||||||
|
Loading…
Reference in New Issue
Block a user