[pve-devel] [PATCH qemu-server v2 1/2] QEMU AMD SEV enable

Markus Frank m.frank at proxmox.com
Fri Nov 11 15:27:15 CET 2022


This Patch is for enabling AMD SEV (Secure Encrypted
Virtualization) support in QEMU and enabling future
memory encryption technologies like INTEL MKTME
(Multi-key Total Memory Encryption) and SEV-SNP.

Config-Example:
memory_encryption: type=sev,cbitpos=47,policy=0x0001,reduced-phys-bits=1

reduced-phys-bios, cbitpos and policy correspond to the varibles with the
same name in qemu.

reduced-phys-bios and cbitpos are system specific and can be read out
with QMP. If not set by the user, a dummy-vm gets started to read QMP
for these variables out and save them to config. Afterwards the dummy-vm gets
stopped.

policy can be calculated with the links in comments & description.
To test SEV-ES (CPU register encryption) the policy should be set
somewhere between 0x4 and 0x7 or 0xC and 0xF, etc.
(Bit-2 has to be set 1 (LSB 0 bit numbering))

SEV needs edk2-OVMF to work.

Signed-off-by: Markus Frank <m.frank at proxmox.com>
---
 PVE/QemuServer.pm | 133 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 133 insertions(+)

diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index 513a248..2ea8abd 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -175,6 +175,58 @@ my $agent_fmt = {
     },
 };
 
+my $memory_encryption_fmt = {
+    type => {
+	type => 'string',
+	default_key => 1,
+	description => "Memory Encryption Type:"
+	    ." for AMD SEV -> 'memory_encryption: type=sev';"
+	    ." for AMD SEV-ES -> use 'sev' and change policy to between 0x4 and 0x7;"
+	    ." (Bit-2 has to be set 1 (LSB 0 bit numbering))"
+	    #. "for AMD SEV-SNP -> 'memory_encryption: type=sev-snp'"
+	    ." (sev requires edk2-ovmf & sev kernel support by guest operating system &"
+	    ." on host: add kernel-parameters 'mem_encrypt=on kvm_amd.sev=1')"
+	    ." see https://github.com/AMDESE/AMDSEV &"
+	    ." https://documentation.suse.com/sles/15-SP1/html/SLES-amd-sev/index.html",
+	format_description => "qemu-memory-encryption-type",
+	# TODO enable sev-snp option when feature can be tested on 3rd-gen EPYC
+	# https://www.phoronix.com/news/AMD-SEV-SNP-Arrives-Linux-5.19
+	# enum => ['sev','sev-snp','mktme'],
+	enum => ['sev'],
+	maxLength => 10,
+    },
+    'reduced-phys-bits' => {
+	description => "Number of bits the physical address space is reduced by. System dependent",
+	type => 'integer',
+	default => 1,
+	optional => 1,
+	minimum => 0,
+	maximum => 100,
+    },
+    cbitpos => {
+	description => "C-bit: marks if a memory page is protected. System dependent",
+	type => 'integer',
+	default => 47,
+	optional => 1,
+	minimum => 0,
+	maximum => 100,
+    },
+    policy => {
+	description => "SEV Guest Policy"
+	    . "see Capter 3:"
+	    . "https://www.amd.com/system/files/TechDocs/55766_SEV-KM_API_Specification.pdf"
+	    . "& https://www.qemu.org/docs/master/system/i386/amd-memory-encryption.html",
+
+	format_description => "qemu-memory-encryption-policy",
+	type => 'string',
+	default => '0x0000',
+	pattern => '0[xX][0-9a-fA-F]{1,4}',
+	optional => 1,
+	maxLength => 6,
+    },
+};
+PVE::JSONSchema::register_format('pve-qemu-memory-encryption-fmt', $memory_encryption_fmt);
+
 my $vga_fmt = {
     type => {
 	description => "Select the VGA type.",
@@ -349,6 +401,12 @@ my $confdesc = {
 	minimum => 16,
 	default => 512,
     },
+    memory_encryption => {
+	description => "Memory Encryption",
+	optional => 1,
+	format => 'pve-qemu-memory-encryption-fmt',
+	type => 'string',
+    },
     balloon => {
 	optional => 1,
 	type => 'integer',
@@ -2113,6 +2171,17 @@ sub parse_guest_agent {
     return $res;
 }
 
+sub parse_memory_encryption {
+    my ($value) = @_;
+
+    return if !$value;
+
+    my $res = eval { parse_property_string($memory_encryption_fmt, $value) };
+    warn $@ if $@;
+    return $res;
+}
+
+
 sub get_qga_key {
     my ($conf, $key) = @_;
     return undef if !defined($conf->{agent});
@@ -4085,6 +4154,70 @@ sub config_to_command {
     }
     push @$machineFlags, "type=${machine_type_min}";
 
+    # Memory Encryption
+    my $memory_encryption = parse_memory_encryption($conf->{'memory_encryption'});
+
+    # Die if bios is not ovmf
+    if (
+	$memory_encryption->{'type'}
+	&& $memory_encryption->{type} eq 'sev'
+	&& $conf->{bios} ne 'ovmf'
+    ) {
+	die "SEV needs ovmf\n";
+    }
+
+    # AMD SEV
+    if ($memory_encryption->{'type'} && $memory_encryption->{type} =~ /(sev|sev-snp)/) {
+	# Get reduced-phys-bits & cbitpos from QMP, if not set
+	if (
+	    !$memory_encryption->{'reduced-phys-bits'}
+	    || !$memory_encryption->{cbitpos}
+	) {
+	    my $fakevmid = -1;
+	    my $qemu_cmd = get_command_for_arch($arch);
+	    my $pidfile = PVE::QemuServer::Helpers::pidfile_name($fakevmid);
+	    my $default_machine = $default_machines->{$arch};
+	    my $cmd = [
+		$qemu_cmd,
+		'-machine', $default_machine,
+		'-display', 'none',
+		'-chardev', "socket,id=qmp,path=/var/run/qemu-server/$fakevmid.qmp,server=on,wait=off",
+		'-mon', 'chardev=qmp,mode=control',
+		'-pidfile', $pidfile,
+		'-S', '-daemonize'
+	    ];
+	    my $rc = run_command($cmd, noerr => 1, quiet => 0);
+	    die "QEMU flag querying VM exited with code " . $rc . "\n" if $rc;
+	    my $res = mon_cmd($fakevmid, 'query-sev-capabilities');
+	    $memory_encryption->{'reduced-phys-bits'} = $res->{'reduced-phys-bits'};
+	    $memory_encryption->{cbitpos} = $res->{cbitpos};
+	    $conf->{'memory_encryption'} = PVE::JSONSchema::print_property_string(
+		$memory_encryption,
+		$memory_encryption_fmt
+	    );
+	    PVE::QemuConfig->write_config($vmid, $conf);
+	    vm_stop(undef, $fakevmid, 1, 1, 10, 0, 1);
+	}
+	# qemu-Example: -object 'sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=1';
+	# see https://www.qemu.org/docs/master/system/i386/amd-memory-encryption.html
+	my $memobjcmd = "";
+	if ($memory_encryption->{type} eq 'sev-snp') {
+	    # future feature: cannot be reached
+	    $memobjcmd = 'sev-snp-guest';
+	} else {
+	    $memobjcmd = 'sev-guest';
+	}
+	$memobjcmd .= ',id=sev0,cbitpos='.$memory_encryption->{cbitpos}
+	    .',reduced-phys-bits='.$memory_encryption->{'reduced-phys-bits'};
+	if ($memory_encryption->{type} eq 'sev' && $memory_encryption->{policy}) {
+	    $memobjcmd .= ',policy='.$memory_encryption->{policy}
+	}
+	push @$devices, '-object' , $memobjcmd;
+	# old qemu-Example: -machine 'type=q35+pve0,memory-encryption=sev0'
+	# https://fossies.org/linux/qemu/docs/system/confidential-guest-support.rst
+	push @$machineFlags, 'confidential-guest-support=sev0';
+    }
+
     push @$cmd, @$devices;
     push @$cmd, '-rtc', join(',', @$rtcFlags) if scalar(@$rtcFlags);
     push @$cmd, '-machine', join(',', @$machineFlags) if scalar(@$machineFlags);
-- 
2.30.2






More information about the pve-devel mailing list