[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