From: Thomas Lamprecht <t.lamprecht@proxmox.com>
To: Proxmox VE development discussion <pve-devel@lists.proxmox.com>,
Markus Frank <m.frank@proxmox.com>
Subject: Re: [pve-devel] [PATCH qemu-server v12 3/6] config: add AMD SEV support
Date: Sun, 17 Nov 2024 19:49:17 +0100 [thread overview]
Message-ID: <5829b642-0524-4776-b0ba-ac26b673e181@proxmox.com> (raw)
In-Reply-To: <20241111135713.212601-4-m.frank@proxmox.com>
Am 11.11.24 um 14:57 schrieb Markus Frank:
> This patch is for enabling AMD SEV (Secure Encrypted Virtualization)
> support in QEMU.
>
> VM-Config-Examples:
> amd_sev: type=std,no-debug=1,no-key-sharing=1
> amd_sev: es,no-debug=1,kernel-hashes=1
>
> kernel-hashes, reduced-phys-bits & cbitpos correspond to the variables
> with the same name in QEMU.
>
> kernel-hashes=1 adds kernel hashes to enable measured linux kernel
> launch since it is per default off for backward compatibility.
>
> reduced-phys-bios and cbitpos are system specific and are read out by
> the query-machine-capabilities c program and saved to the
> /run/qemu-server/host-hw-capabilities.json file. This file is parsed
> and than used by qemu-server to correctly start a AMD SEV VM.
>
> type=std stands for standard sev to differentiate it from sev-es (es)
> or sev-snp (snp) when support is upstream.
>
> QEMU's sev-guest policy gets calculated with the parameters no-debug
> & no-key-sharing. These parameters correspond to policy-bits 0 & 1.
> If type is 'es' than policy-bit 2 gets set to 1 to activate SEV-ES.
> Policy bit 3 (nosend) is always set to 1, because migration features
> for sev are not upstream yet and are attackable.
>
> SEV-ES is highly experimental since it could not be tested.
>
> see coherent doc patch
looks mostly OK some more minor stuff inline
>
> Signed-off-by: Markus Frank <m.frank@proxmox.com>
> Reviewed-by: Fiona Ebner <f.ebner@proxmox.com>
> ---
> changes v12:
> * a eval for decode_json()
> * get_amd_sev_object: use the three properties as parameters instead of
> the whole config
> * removed efidisk check, as it is obsolete
> * small perl style changes
>
> PVE/QemuServer.pm | 13 +++++-
> PVE/QemuServer/CPUConfig.pm | 88 ++++++++++++++++++++++++++++++++++++-
> 2 files changed, 99 insertions(+), 2 deletions(-)
>
> diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
> index 0df3bda0..dd6ae93f 100644
> --- a/PVE/QemuServer.pm
> +++ b/PVE/QemuServer.pm
> @@ -53,7 +53,7 @@ use PVE::QemuConfig;
> use PVE::QemuServer::Helpers qw(config_aware_timeout min_version windows_version);
> use PVE::QemuServer::Cloudinit;
> use PVE::QemuServer::CGroup;
> -use PVE::QemuServer::CPUConfig qw(print_cpu_device get_cpu_options get_cpu_bitness is_native_arch);
> +use PVE::QemuServer::CPUConfig qw(print_cpu_device get_cpu_options get_cpu_bitness is_native_arch get_amd_sev_object);
> use PVE::QemuServer::Drive qw(is_valid_drivename drive_is_cloudinit drive_is_cdrom drive_is_read_only parse_drive print_drive);
> use PVE::QemuServer::Machine;
> use PVE::QemuServer::Memory qw(get_current_memory);
> @@ -358,6 +358,12 @@ my $confdesc = {
> description => "Memory properties.",
> format => $PVE::QemuServer::Memory::memory_fmt
> },
> + amd_sev => {
as of now this can be named 'amd-sev'
> + description => "Secure Encrypted Virtualization (SEV) features by AMD CPUs",
> + optional => 1,
> + format => 'pve-qemu-sev-fmt',
> + type => 'string',
> + },
> balloon => {
> optional => 1,
> type => 'integer',
> @@ -4159,6 +4165,11 @@ sub config_to_command {
> }
> }
>
> + if ($conf->{amd_sev}) {
> + push @$devices, '-object', get_amd_sev_object($conf->{amd_sev}, $conf->{bios}, $conf->{efidisk0});
> + 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);
> diff --git a/PVE/QemuServer/CPUConfig.pm b/PVE/QemuServer/CPUConfig.pm
> index 33f7524f..5110b37e 100644
> --- a/PVE/QemuServer/CPUConfig.pm
> +++ b/PVE/QemuServer/CPUConfig.pm
> @@ -3,9 +3,11 @@ package PVE::QemuServer::CPUConfig;
> use strict;
> use warnings;
>
> +use JSON;
> +
> use PVE::JSONSchema;
> use PVE::Cluster qw(cfs_register_file cfs_read_file);
> -use PVE::Tools qw(get_host_arch);
> +use PVE::Tools qw(run_command get_host_arch);
> use PVE::QemuServer::Helpers qw(min_version);
>
> use base qw(PVE::SectionConfig Exporter);
> @@ -15,6 +17,7 @@ print_cpu_device
> get_cpu_options
> get_cpu_bitness
> is_native_arch
> +get_amd_sev_object
> );
>
> # under certain race-conditions, this module might be loaded before pve-cluster
> @@ -225,6 +228,37 @@ my $cpu_fmt = {
> },
> };
>
> +my $sev_fmt = {
> + type => {
> + description => "Enable standard SEV with type='std' or enable"
> + ." experimental SEV-ES with the 'es' option.",
> + type => 'string',
> + default_key => 1,
> + format_description => "sev-type",
> + enum => ['std', 'es'],
> + maxLength => 3,
> + },
> + 'no-debug' => {
> + description => "Sets policy bit 0 to 1 to disallow debugging of guest",
> + type => 'boolean',
> + default => 0,
> + optional => 1,
> + },
> + 'no-key-sharing' => {
> + description => "Sets policy bit 1 to 1 to disallow key sharing with other guests",
> + type => 'boolean',
> + default => 0,
> + optional => 1,
> + },
> + "kernel-hashes" => {
> + description => "Add kernel hashes to guest firmware for measured linux kernel launch",
> + type => 'boolean',
> + default => 0,
> + optional => 1,
> + },
> +};
> +PVE::JSONSchema::register_format('pve-qemu-sev-fmt', $sev_fmt);
> +
> PVE::JSONSchema::register_format('pve-phys-bits', \&parse_phys_bits);
> sub parse_phys_bits {
> my ($str, $noerr) = @_;
> @@ -773,6 +807,58 @@ sub get_cpu_bitness {
> die "unsupported architecture '$arch'\n";
> }
>
> +sub get_hw_capabilities {
> + # Get reduced-phys-bits & cbitpos from host-hw-capabilities.json
> + my $filename = '/run/qemu-server/host-hw-capabilities.json';
hmm, rethinking this it might be good to move that to somewhere accessible for non-root,
but can be done also in the future.
> + if (! -e $filename) {
> + run_command("/usr/libexec/qemu-server/query-machine-capabilities");
maybe once at boot through a systemd service file could be nicer than this a bit
subtle way.
> + }
> + my $json_text = PVE::Tools::file_get_contents($filename);
> + ($json_text) = $json_text =~ /(.*)/; # untaint json text
> + my $hw_capabilities;
> + eval {
> + $hw_capabilities = decode_json($json_text);
> + };
can be shortened to:
my $hw_capabilities = eval { decode_json($json_text) };
> + if (my $err = $@) {
> + die $err;
> + }
> + return $hw_capabilities;
> +}
> +
> +sub get_amd_sev_object {
> + my ($amd_sev, $bios, $efidisk0) = @_;
> +
> + my $amd_sev_conf = PVE::JSONSchema::parse_property_string($sev_fmt, $amd_sev);
> + my $sev_hw_caps = get_hw_capabilities()->{'amd-sev'};
> +
> + if (!$sev_hw_caps->{'sev-support'}) {
> + die "Your CPU does not support AMD SEV.\n";
> + }
> + if ($amd_sev_conf->{type} eq 'es' && !$sev_hw_caps->{'sev-support-es'}) {
> + die "Your CPU does not support AMD SEV-ES.\n";
> + }
> + if (!$bios || ($bios && $bios ne 'ovmf')) {
In general `!a || (a && b) == !a || b` so above can be shortened to
if (!$bios || $bios ne 'ovmf') {
# ...
> + die "To use AMD SEV, you need to change the BIOS to OVMF.\n";
> + }
> +
> + my $sev_mem_object = 'sev-guest,id=sev0';
> + $sev_mem_object .= ',cbitpos='.$sev_hw_caps->{cbitpos};
> + $sev_mem_object .= ',reduced-phys-bits='.$sev_hw_caps->{'reduced-phys-bits'};
> +
> + # guest policy bit calculation as described here:
> + # https://documentation.suse.com/sles/15-SP5/html/SLES-amd-sev/article-amd-sev.html#table-guestpolicy
> + my $policy = 0b0000;
> + $policy += 0b0001 if $amd_sev_conf->{'no-debug'};
> + $policy += 0b0010 if $amd_sev_conf->{'no-key-sharing'};
> + $policy += 0b0100 if $amd_sev_conf->{type} eq 'es';
> + # disable migration with bit 3 nosend to prevent amd-sev-migration-attack
> + $policy += 0b1000;
> +
> + $sev_mem_object .= ',policy='.sprintf("%#x", $policy);
> + $sev_mem_object .= ',kernel-hashes=on' if ($amd_sev_conf->{'kernel-hashes'});
> + return $sev_mem_object;
> +}
> +
> __PACKAGE__->register();
> __PACKAGE__->init();
>
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
next prev parent reply other threads:[~2024-11-17 18:49 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-11-11 13:57 [pve-devel] [PATCH guest-common/qemu-server/docs/manager v12 0/6] AMD SEV Markus Frank
2024-11-11 13:57 ` [pve-devel] [PATCH guest-common v12 1/6] AbstractConfig: add abstract method to check for resources preventing a snapshot Markus Frank
2024-11-17 17:08 ` [pve-devel] applied: " Thomas Lamprecht
2024-11-11 13:57 ` [pve-devel] [PATCH qemu-server v12 2/6] add C program to get hardware capabilities from CPUID Markus Frank
2024-11-17 18:41 ` [pve-devel] applied: " Thomas Lamprecht
2024-11-11 13:57 ` [pve-devel] [PATCH qemu-server v12 3/6] config: add AMD SEV support Markus Frank
2024-11-17 18:49 ` Thomas Lamprecht [this message]
2024-11-11 13:57 ` [pve-devel] [PATCH qemu-server v12 4/6] migration: add check_non_migratable_resources function Markus Frank
2024-11-11 13:57 ` [pve-devel] [PATCH docs v12 5/6] add AMD SEV documentation Markus Frank
2024-11-11 13:57 ` [pve-devel] [PATCH manager v12 6/6] ui: add AMD SEV configuration to Options Markus Frank
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=5829b642-0524-4776-b0ba-ac26b673e181@proxmox.com \
--to=t.lamprecht@proxmox.com \
--cc=m.frank@proxmox.com \
--cc=pve-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.