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@xxxxxxxxxx> > > --- > > 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 :|