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