[libvirt PATCH 07/12] tools: support automatically constructing SEV-ES vCPU state
Daniel P. Berrangé
berrange at redhat.com
Tue Oct 18 09:18:08 UTC 2022
On Sun, Oct 16, 2022 at 03:00:25PM -0400, Cole Robinson wrote:
> On 10/7/22 7:43 AM, Daniel P. Berrangé wrote:
> > The VMSA files contain the expected CPU register state for the VM. Their
> > content varies based on a few pieces of the stack
> >
> > - AMD CPU architectural initial state
> > - KVM hypervisor VM CPU initialization
> > - QEMU userspace VM CPU initialization
> > - AMD CPU SKU (family/model/stepping)
> >
> > The first three pieces of information we can obtain through code
> > inspection. The last piece of information we can take on the command
> > line. This allows a user to validate a SEV-ES guest merely by providing
> > the CPU SKU information, using --cpu-family, --cpu-model,
> > --cpu-stepping. This avoids the need to obtain or construct VMSA files
> > directly.
> >
> > Signed-off-by: Daniel P. Berrangé <berrange at redhat.com>
> > ---
> > docs/manpages/virt-qemu-sev-validate.rst | 45 +++
> > tools/virt-qemu-sev-validate.py | 475 +++++++++++++++++++++++
> > 2 files changed, 520 insertions(+)
> >
> > diff --git a/docs/manpages/virt-qemu-sev-validate.rst b/docs/manpages/virt-qemu-sev-validate.rst
> > index 24bca98d28..7ba7323e13 100644
> > --- a/docs/manpages/virt-qemu-sev-validate.rst
> > +++ b/docs/manpages/virt-qemu-sev-validate.rst
> > @@ -243,6 +243,24 @@ Validate the measurement of a SEV-ES SMP guest booting from disk:
> > --build-id 13 \
> > --policy 7
> >
snip
> > +class OVMF(object):
> > +
> > + OVMF_TABLE_FOOTER_GUID = UUID("96b582de-1fb2-45f7-baea-a366c55a082d")
> > + SEV_INFO_BLOCK_GUID = UUID("00f771de-1a7e-4fcb-890e-68c77e2fb44e")
> > +
> > + def __init__(self):
> > + self.entries = {}
> > +
> > + def load(self, content):
> > + expect = OVMF.OVMF_TABLE_FOOTER_GUID.bytes_le
> > + actual = content[-48:-32]
> > + if expect != actual:
> > + raise Exception("OVMF footer GUID not found")
> > +
> > + tablelen = int.from_bytes(content[-50:-48], byteorder='little')
> > +
> > + if tablelen == 0:
> > + raise Exception("OVMF tables zero length")
> > +
> > + table = content[-(50 + tablelen):-50]
> > +
> > + self.parse_table(table)
> > +
> > + def parse_table(self, data):
> > + while len(data) > 0:
> > + entryuuid = UUID(bytes_le=data[-16:])
> > + entrylen = int.from_bytes(data[-18:-16], byteorder='little')
> > + entrydata = data[-entrylen:-18]
> > +
> > + self.entries[str(entryuuid)] = entrydata
> > +
> > + data = data[0:-(18 + entrylen)]
> > +
>
>
> I noticed this with your older branch, but the parsing here only works
> for the first entry, print(self.entries) will show you. That's all we
> need for the script, but this will fix later entry parsing:
>
>
> --- a/tools/virt-qemu-sev-validate.py
> +++ b/tools/virt-qemu-sev-validate.py
> @@ -480,7 +480,7 @@ class OVMF(object):
> if tablelen == 0:
> raise Exception("OVMF tables zero length")
>
> - table = content[-(50 + tablelen):-50]
> + table = content[-(32 + tablelen):-50]
>
> self.parse_table(table)
>
> @@ -492,7 +492,7 @@ class OVMF(object):
>
> self.entries[str(entryuuid)] = entrydata
>
> - data = data[0:-(18 + entrylen)]
> + data = data[0:-entrylen]
Ah yes, well spotted.
> > + def reset_addr(self):
> > + if str(OVMF.SEV_INFO_BLOCK_GUID) not in self.entries:
> > + raise Exception("SEV info block GUID not found")
> > +
> > + info = SevInfoBlock()
> > + info.unpack(self.entries[str(OVMF.SEV_INFO_BLOCK_GUID)])
> > +
> > + return info.reset_addr.value
>
> unpack() isn't implemented, so this will error. You could implement it
> but it's kinda overkill. All you need is:
Sigh. It *was* implemented fully. I tested every possible
scenario. Then right before sending this, I deleted the
unpack code as I thought it wasn't used. /facepalm.
>
> diff --git a/tools/virt-qemu-sev-validate.py
> b/tools/virt-qemu-sev-validate.py
> index 2c5ad9083d..78d94604d5 100755
> --- a/tools/virt-qemu-sev-validate.py
> +++ b/tools/virt-qemu-sev-validate.py
> @@ -454,13 +454,6 @@ class VMSA(Struct):
> self.cs_base.value = reset_cs
>
>
> -class SevInfoBlock(Struct):
> -
> - def __init__(self):
> - super().__init__(size=4)
> - self.register_field("reset_addr", Field.U32)
> -
> -
> class OVMF(object):
>
> OVMF_TABLE_FOOTER_GUID = UUID("96b582de-1fb2-45f7-baea-a366c55a082d")
> @@ -498,10 +491,9 @@ class OVMF(object):
> if str(OVMF.SEV_INFO_BLOCK_GUID) not in self.entries:
> raise Exception("SEV info block GUID not found")
>
> - info = SevInfoBlock()
> - info.unpack(self.entries[str(OVMF.SEV_INFO_BLOCK_GUID)])
> -
> - return info.reset_addr.value
> + reset_addr = int.from_bytes(
> + self.entries[str(OVMF.SEV_INFO_BLOCK_GUID)], "little")
> + return reset_addr
Yes, that is quiet simple, given we only need 1 int value.
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
More information about the libvir-list
mailing list