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(a)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 :|