Generalize memory encryption models

A number of hardware platforms are implementing mechanisms whereby the
 hypervisor does not have unfettered access to guest memory, in order
 to mitigate the security impact of a compromised hypervisor.
 
 AMD's SEV implements this with in-cpu memory encryption, and Intel has
 its own memory encryption mechanism.  POWER has an upcoming mechanism
 to accomplish this in a different way, using a new memory protection
 level plus a small trusted ultravisor.  s390 also has a protected
 execution environment.
 
 The current code (committed or draft) for these features has each
 platform's version configured entirely differently.  That doesn't seem
 ideal for users, or particularly for management layers.
 
 AMD SEV introduces a notionally generic machine option
 "machine-encryption", but it doesn't actually cover any cases other
 than SEV.
 
 This series is a proposal to at least partially unify configuration
 for these mechanisms, by renaming and generalizing AMD's
 "memory-encryption" property.  It is replaced by a
 "confidential-guest-support" property pointing to a platform specific
 object which configures and manages the specific details.
 
 Note to Ram Pai: the documentation I've included for PEF is very
 minimal.  If you could send a patch expanding on that, it would be
 very helpful.
 
 Changes since v8:
  * Rebase
  * Fixed some cosmetic typos
 Changes since v7:
  * Tweaked and clarified meaning of the 'ready' flag
  * Polished the interface to the PEF internals
  * Shifted initialization for s390 PV later (I hope I've finally got
    this after apply_cpu_model() where it needs to be)
 Changes since v6:
  * Moved to using OBJECT_DECLARE_TYPE and OBJECT_DEFINE_TYPE macros
  * Assorted minor fixes
 Changes since v5:
  * Renamed from "securable guest memory" to "confidential guest
    support"
  * Simpler reworking of x86 boot time flash encryption
  * Added a bunch of documentation
  * Fixed some compile errors on POWER
 Changes since v4:
  * Renamed from "host trust limitation" to "securable guest memory",
    which I think is marginally more descriptive
  * Re-organized initialization, because the previous model called at
    kvm_init didn't work for s390
  * Assorted fixes to the s390 implementation; rudimentary testing
    (gitlab CI) only
 Changes since v3:
  * Rebased
  * Added first cut at handling of s390 protected virtualization
 Changes since RFCv2:
  * Rebased
  * Removed preliminary SEV cleanups (they've been merged)
  * Changed name to "host trust limitation"
  * Added migration blocker to the PEF code (based on SEV's version)
 Changes since RFCv1:
  * Rebased
  * Fixed some errors pointed out by Dave Gilbert
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEdfRlhq5hpmzETofcbDjKyiDZs5IFAmAg1R8ACgkQbDjKyiDZ
 s5KCVRAAgm/xlgEv2hDZ7z+MuOTNesCpR3uU4iX02xNktox96Qai7XlrA7bhDf1v
 y/0FLnOOL6Kn5OHeS2CiDPIgWIUfapSwDsTPooZ6GqfzCI+r0jIaSBu59IBhvJRh
 o3ZTfT2fsckY9Gy2YN29ssN87ovDTPNlvRAxGH/71mMKEGJcK6QWxGcsyJDmeKq4
 0/tOQaLMFRRagTpwqCT1eacMzyQwkoDcywQHfi0Is+Q4voWPKgDY0qPqLd1OG2XI
 cMQ8fagums3NkPpVbKAW7sIvDiHtH1HNDoHKTiwKtTUsN5LBz+LN87LoKAdBasV0
 AiRm8gi+CkF/NOA2RjwaFmThxt7sr8kTKVuIqTo5m8agqkhJr97+gBxUym49CxTx
 1Zjo9TWsprKXnXl8vfGtAIZ4pkYQzomMDT3AilEST3+zbpRuwTMGOJ5vLF7RrKtF
 AtF2XBiPGZ/NztpbmaukuG/R49wwW5we4dR1zySMcoTsAl1rIzxpfwBnYatOY0Hg
 sVc9gABwQ0kacsseVIX72c+30U02cR8f6uRfuqNAEUW13vdAo/5/PXxGVlevMkw5
 33MYr16CkGnYgtgJtORK+x8/vPlAYiBzZrn71Wym7yKCamf8LMbzPNXKjUaD/GT8
 TZG7abTV8vuS0m7V/hGgV8nTVaG/6VLEyAtO6YpjQ+1p+dO8xBc=
 =TTeT
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/dg-gitlab/tags/cgs-pull-request' into staging

Generalize memory encryption models

A number of hardware platforms are implementing mechanisms whereby the
hypervisor does not have unfettered access to guest memory, in order
to mitigate the security impact of a compromised hypervisor.

AMD's SEV implements this with in-cpu memory encryption, and Intel has
its own memory encryption mechanism.  POWER has an upcoming mechanism
to accomplish this in a different way, using a new memory protection
level plus a small trusted ultravisor.  s390 also has a protected
execution environment.

The current code (committed or draft) for these features has each
platform's version configured entirely differently.  That doesn't seem
ideal for users, or particularly for management layers.

AMD SEV introduces a notionally generic machine option
"machine-encryption", but it doesn't actually cover any cases other
than SEV.

This series is a proposal to at least partially unify configuration
for these mechanisms, by renaming and generalizing AMD's
"memory-encryption" property.  It is replaced by a
"confidential-guest-support" property pointing to a platform specific
object which configures and manages the specific details.

Note to Ram Pai: the documentation I've included for PEF is very
minimal.  If you could send a patch expanding on that, it would be
very helpful.

Changes since v8:
 * Rebase
 * Fixed some cosmetic typos
Changes since v7:
 * Tweaked and clarified meaning of the 'ready' flag
 * Polished the interface to the PEF internals
 * Shifted initialization for s390 PV later (I hope I've finally got
   this after apply_cpu_model() where it needs to be)
Changes since v6:
 * Moved to using OBJECT_DECLARE_TYPE and OBJECT_DEFINE_TYPE macros
 * Assorted minor fixes
Changes since v5:
 * Renamed from "securable guest memory" to "confidential guest
   support"
 * Simpler reworking of x86 boot time flash encryption
 * Added a bunch of documentation
 * Fixed some compile errors on POWER
Changes since v4:
 * Renamed from "host trust limitation" to "securable guest memory",
   which I think is marginally more descriptive
 * Re-organized initialization, because the previous model called at
   kvm_init didn't work for s390
 * Assorted fixes to the s390 implementation; rudimentary testing
   (gitlab CI) only
Changes since v3:
 * Rebased
 * Added first cut at handling of s390 protected virtualization
Changes since RFCv2:
 * Rebased
 * Removed preliminary SEV cleanups (they've been merged)
 * Changed name to "host trust limitation"
 * Added migration blocker to the PEF code (based on SEV's version)
Changes since RFCv1:
 * Rebased
 * Fixed some errors pointed out by Dave Gilbert

# gpg: Signature made Mon 08 Feb 2021 06:07:27 GMT
# gpg:                using RSA key 75F46586AE61A66CC44E87DC6C38CACA20D9B392
# gpg: Good signature from "David Gibson <david@gibson.dropbear.id.au>" [full]
# gpg:                 aka "David Gibson (Red Hat) <dgibson@redhat.com>" [full]
# gpg:                 aka "David Gibson (ozlabs.org) <dgibson@ozlabs.org>" [full]
# gpg:                 aka "David Gibson (kernel.org) <dwg@kernel.org>" [unknown]
# Primary key fingerprint: 75F4 6586 AE61 A66C C44E  87DC 6C38 CACA 20D9 B392

* remotes/dg-gitlab/tags/cgs-pull-request:
  s390: Recognize confidential-guest-support option
  confidential guest support: Alter virtio default properties for protected guests
  spapr: PEF: prevent migration
  spapr: Add PEF based confidential guest support
  confidential guest support: Update documentation
  confidential guest support: Move SEV initialization into arch specific code
  confidential guest support: Introduce cgs "ready" flag
  sev: Add Error ** to sev_kvm_init()
  confidential guest support: Rework the "memory-encryption" property
  confidential guest support: Move side effect out of machine_set_memory_encryption()
  sev: Remove false abstraction of flash encryption
  confidential guest support: Introduce new confidential guest support class
  qom: Allow optional sugar props

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2021-02-08 11:11:26 +00:00
commit 6f0e9c26db
32 changed files with 597 additions and 193 deletions

View File

@ -123,10 +123,6 @@ struct KVMState
KVMMemoryListener memory_listener;
QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus;
/* memory encryption */
void *memcrypt_handle;
int (*memcrypt_encrypt_data)(void *handle, uint8_t *ptr, uint64_t len);
/* For "info mtree -f" to tell if an MR is registered in KVM */
int nr_as;
struct KVMAs {
@ -225,26 +221,6 @@ int kvm_get_max_memslots(void)
return s->nr_slots;
}
bool kvm_memcrypt_enabled(void)
{
if (kvm_state && kvm_state->memcrypt_handle) {
return true;
}
return false;
}
int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len)
{
if (kvm_state->memcrypt_handle &&
kvm_state->memcrypt_encrypt_data) {
return kvm_state->memcrypt_encrypt_data(kvm_state->memcrypt_handle,
ptr, len);
}
return 1;
}
/* Called with KVMMemoryListener.slots_lock held */
static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml)
{
@ -2204,20 +2180,6 @@ static int kvm_init(MachineState *ms)
kvm_state = s;
/*
* if memory encryption object is specified then initialize the memory
* encryption context.
*/
if (ms->memory_encryption) {
kvm_state->memcrypt_handle = sev_guest_init(ms->memory_encryption);
if (!kvm_state->memcrypt_handle) {
ret = -1;
goto err;
}
kvm_state->memcrypt_encrypt_data = sev_encrypt_data;
}
ret = kvm_arch_init(ms, s);
if (ret < 0) {
goto err;

View File

@ -15,12 +15,8 @@
#include "qemu-common.h"
#include "sysemu/sev.h"
int sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len)
int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
{
abort();
}
void *sev_guest_init(const char *id)
{
return NULL;
/* If we get here, cgs must be some non-SEV thing */
return 0;
}

View File

@ -81,16 +81,6 @@ int kvm_on_sigbus(int code, void *addr)
return 1;
}
bool kvm_memcrypt_enabled(void)
{
return false;
}
int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len)
{
return 1;
}
#ifndef CONFIG_USER_ONLY
int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev)
{

View File

@ -0,0 +1,33 @@
/*
* QEMU Confidential Guest support
*
* Copyright Red Hat.
*
* Authors:
* David Gibson <david@gibson.dropbear.id.au>
*
* This work is licensed under the terms of the GNU GPL, version 2 or
* later. See the COPYING file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "exec/confidential-guest-support.h"
OBJECT_DEFINE_ABSTRACT_TYPE(ConfidentialGuestSupport,
confidential_guest_support,
CONFIDENTIAL_GUEST_SUPPORT,
OBJECT)
static void confidential_guest_support_class_init(ObjectClass *oc, void *data)
{
}
static void confidential_guest_support_init(Object *obj)
{
}
static void confidential_guest_support_finalize(Object *obj)
{
}

View File

@ -6,6 +6,7 @@ softmmu_ss.add([files(
'rng-builtin.c',
'rng-egd.c',
'rng.c',
'confidential-guest-support.c',
), numa])
softmmu_ss.add(when: 'CONFIG_POSIX', if_true: files('rng-random.c'))

View File

@ -73,7 +73,7 @@ complete flow chart.
To launch a SEV guest
# ${QEMU} \
-machine ...,memory-encryption=sev0 \
-machine ...,confidential-guest-support=sev0 \
-object sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=1
Debugging

View File

@ -0,0 +1,49 @@
Confidential Guest Support
==========================
Traditionally, hypervisors such as QEMU have complete access to a
guest's memory and other state, meaning that a compromised hypervisor
can compromise any of its guests. A number of platforms have added
mechanisms in hardware and/or firmware which give guests at least some
protection from a compromised hypervisor. This is obviously
especially desirable for public cloud environments.
These mechanisms have different names and different modes of
operation, but are often referred to as Secure Guests or Confidential
Guests. We use the term "Confidential Guest Support" to distinguish
this from other aspects of guest security (such as security against
attacks from other guests, or from network sources).
Running a Confidential Guest
----------------------------
To run a confidential guest you need to add two command line parameters:
1. Use "-object" to create a "confidential guest support" object. The
type and parameters will vary with the specific mechanism to be
used
2. Set the "confidential-guest-support" machine parameter to the ID of
the object from (1).
Example (for AMD SEV)::
qemu-system-x86_64 \
<other parameters> \
-machine ...,confidential-guest-support=sev0 \
-object sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=1
Supported mechanisms
--------------------
Currently supported confidential guest mechanisms are:
AMD Secure Encrypted Virtualization (SEV)
docs/amd-memory-encryption.txt
POWER Protected Execution Facility (PEF)
docs/papr-pef.txt
s390x Protected Virtualization (PV)
docs/system/s390x/protvirt.rst
Other mechanisms may be supported in future.

30
docs/papr-pef.txt Normal file
View File

@ -0,0 +1,30 @@
POWER (PAPR) Protected Execution Facility (PEF)
===============================================
Protected Execution Facility (PEF), also known as Secure Guest support
is a feature found on IBM POWER9 and POWER10 processors.
If a suitable firmware including an Ultravisor is installed, it adds
an extra memory protection mode to the CPU. The ultravisor manages a
pool of secure memory which cannot be accessed by the hypervisor.
When this feature is enabled in QEMU, a guest can use ultracalls to
enter "secure mode". This transfers most of its memory to secure
memory, where it cannot be eavesdropped by a compromised hypervisor.
Launching
---------
To launch a guest which will be permitted to enter PEF secure mode:
# ${QEMU} \
-object pef-guest,id=pef0 \
-machine confidential-guest-support=pef0 \
...
Live Migration
----------------
Live migration is not yet implemented for PEF guests. For
consistency, we currently prevent migration if the PEF feature is
enabled, whether or not the guest has actually entered secure mode.

View File

@ -22,15 +22,22 @@ If those requirements are met, the capability `KVM_CAP_S390_PROTECTED`
will indicate that KVM can support PVMs on that LPAR.
QEMU Settings
-------------
Running a Protected Virtual Machine
-----------------------------------
To indicate to the VM that it can transition into protected mode, the
To run a PVM you will need to select a CPU model which includes the
`Unpack facility` (stfle bit 161 represented by the feature
`unpack`/`S390_FEAT_UNPACK`) needs to be part of the cpu model of
the VM.
`unpack`/`S390_FEAT_UNPACK`), and add these options to the command line::
-object s390-pv-guest,id=pv0 \
-machine confidential-guest-support=pv0
Adding these options will:
* Ensure the `unpack` facility is available
* Enable the IOMMU by default for all I/O devices
* Initialize the PV mechanism
All I/O devices need to use the IOMMU.
Passthrough (vfio) devices are currently not supported.
Host huge page backings are not supported. However guests can use huge

View File

@ -32,6 +32,9 @@
#include "hw/mem/nvdimm.h"
#include "migration/global_state.h"
#include "migration/vmstate.h"
#include "exec/confidential-guest-support.h"
#include "hw/virtio/virtio.h"
#include "hw/virtio/virtio-pci.h"
GlobalProperty hw_compat_5_2[] = {};
const size_t hw_compat_5_2_len = G_N_ELEMENTS(hw_compat_5_2);
@ -427,24 +430,37 @@ static char *machine_get_memory_encryption(Object *obj, Error **errp)
{
MachineState *ms = MACHINE(obj);
return g_strdup(ms->memory_encryption);
if (ms->cgs) {
return g_strdup(object_get_canonical_path_component(OBJECT(ms->cgs)));
}
return NULL;
}
static void machine_set_memory_encryption(Object *obj, const char *value,
Error **errp)
{
MachineState *ms = MACHINE(obj);
Object *cgs =
object_resolve_path_component(object_get_objects_root(), value);
g_free(ms->memory_encryption);
ms->memory_encryption = g_strdup(value);
/*
* With memory encryption, the host can't see the real contents of RAM,
* so there's no point in it trying to merge areas.
*/
if (value) {
machine_set_mem_merge(obj, false, errp);
if (!cgs) {
error_setg(errp, "No such memory encryption object '%s'", value);
return;
}
object_property_set_link(obj, "confidential-guest-support", cgs, errp);
}
static void machine_check_confidential_guest_support(const Object *obj,
const char *name,
Object *new_target,
Error **errp)
{
/*
* So far the only constraint is that the target has the
* TYPE_CONFIDENTIAL_GUEST_SUPPORT interface, and that's checked
* by the QOM core
*/
}
static bool machine_get_nvdimm(Object *obj, Error **errp)
@ -844,6 +860,15 @@ static void machine_class_init(ObjectClass *oc, void *data)
object_class_property_set_description(oc, "suppress-vmdesc",
"Set on to disable self-describing migration");
object_class_property_add_link(oc, "confidential-guest-support",
TYPE_CONFIDENTIAL_GUEST_SUPPORT,
offsetof(MachineState, cgs),
machine_check_confidential_guest_support,
OBJ_PROP_LINK_STRONG);
object_class_property_set_description(oc, "confidential-guest-support",
"Set confidential guest scheme to support");
/* For compatibility */
object_class_property_add_str(oc, "memory-encryption",
machine_get_memory_encryption, machine_set_memory_encryption);
object_class_property_set_description(oc, "memory-encryption",
@ -1166,6 +1191,26 @@ void machine_run_board_init(MachineState *machine)
cc->deprecation_note);
}
if (machine->cgs) {
/*
* With confidential guests, the host can't see the real
* contents of RAM, so there's no point in it trying to merge
* areas.
*/
machine_set_mem_merge(OBJECT(machine), false, &error_abort);
/*
* Virtio devices can't count on directly accessing guest
* memory, so they need iommu_platform=on to use normal DMA
* mechanisms. That requires also disabling legacy virtio
* support for those virtio pci devices which allow it.
*/
object_register_sugar_prop(TYPE_VIRTIO_PCI, "disable-legacy",
"on", true);
object_register_sugar_prop(TYPE_VIRTIO_DEVICE, "iommu_platform",
"on", false);
}
machine_class->init(machine);
phase_advance(PHASE_MACHINE_INITIALIZED);
}

View File

@ -38,6 +38,7 @@
#include "sysemu/sysemu.h"
#include "hw/block/flash.h"
#include "sysemu/kvm.h"
#include "sysemu/sev.h"
#define FLASH_SECTOR_SIZE 4096
@ -147,7 +148,7 @@ static void pc_system_flash_map(PCMachineState *pcms,
PFlashCFI01 *system_flash;
MemoryRegion *flash_mem;
void *flash_ptr;
int ret, flash_size;
int flash_size;
assert(PC_MACHINE_GET_CLASS(pcms)->pci_enabled);
@ -191,16 +192,10 @@ static void pc_system_flash_map(PCMachineState *pcms,
flash_mem = pflash_cfi01_get_memory(system_flash);
pc_isa_bios_init(rom_memory, flash_mem, size);
/* Encrypt the pflash boot ROM */
if (kvm_memcrypt_enabled()) {
flash_ptr = memory_region_get_ram_ptr(flash_mem);
flash_size = memory_region_size(flash_mem);
ret = kvm_memcrypt_encrypt_data(flash_ptr, flash_size);
if (ret) {
error_report("failed to encrypt pflash rom");
exit(1);
}
}
/* Encrypt the pflash boot ROM, if necessary */
flash_ptr = memory_region_get_ram_ptr(flash_mem);
flash_size = memory_region_size(flash_mem);
sev_encrypt_flash(flash_ptr, flash_size, &error_fatal);
}
}
}

View File

@ -27,6 +27,7 @@ ppc_ss.add(when: 'CONFIG_PSERIES', if_true: files(
'spapr_nvdimm.c',
'spapr_rtas_ddw.c',
'spapr_numa.c',
'pef.c',
))
ppc_ss.add(when: 'CONFIG_SPAPR_RNG', if_true: files('spapr_rng.c'))
ppc_ss.add(when: ['CONFIG_PSERIES', 'CONFIG_LINUX'], if_true: files(

140
hw/ppc/pef.c Normal file
View File

@ -0,0 +1,140 @@
/*
* PEF (Protected Execution Facility) for POWER support
*
* Copyright Red Hat.
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qom/object_interfaces.h"
#include "sysemu/kvm.h"
#include "migration/blocker.h"
#include "exec/confidential-guest-support.h"
#include "hw/ppc/pef.h"
#define TYPE_PEF_GUEST "pef-guest"
OBJECT_DECLARE_SIMPLE_TYPE(PefGuest, PEF_GUEST)
typedef struct PefGuest PefGuest;
typedef struct PefGuestClass PefGuestClass;
struct PefGuestClass {
ConfidentialGuestSupportClass parent_class;
};
/**
* PefGuest:
*
* The PefGuest object is used for creating and managing a PEF
* guest.
*
* # $QEMU \
* -object pef-guest,id=pef0 \
* -machine ...,confidential-guest-support=pef0
*/
struct PefGuest {
ConfidentialGuestSupport parent_obj;
};
static int kvmppc_svm_init(Error **errp)
{
#ifdef CONFIG_KVM
static Error *pef_mig_blocker;
if (!kvm_check_extension(kvm_state, KVM_CAP_PPC_SECURE_GUEST)) {
error_setg(errp,
"KVM implementation does not support Secure VMs (is an ultravisor running?)");
return -1;
} else {
int ret = kvm_vm_enable_cap(kvm_state, KVM_CAP_PPC_SECURE_GUEST, 0, 1);
if (ret < 0) {
error_setg(errp,
"Error enabling PEF with KVM");
return -1;
}
}
/* add migration blocker */
error_setg(&pef_mig_blocker, "PEF: Migration is not implemented");
/* NB: This can fail if --only-migratable is used */
migrate_add_blocker(pef_mig_blocker, &error_fatal);
return 0;
#else
g_assert_not_reached();
#endif
}
/*
* Don't set error if KVM_PPC_SVM_OFF ioctl is invoked on kernels
* that don't support this ioctl.
*/
static int kvmppc_svm_off(Error **errp)
{
#ifdef CONFIG_KVM
int rc;
rc = kvm_vm_ioctl(KVM_STATE(current_accel()), KVM_PPC_SVM_OFF);
if (rc && rc != -ENOTTY) {
error_setg_errno(errp, -rc, "KVM_PPC_SVM_OFF ioctl failed");
return rc;
}
return 0;
#else
g_assert_not_reached();
#endif
}
int pef_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
{
if (!object_dynamic_cast(OBJECT(cgs), TYPE_PEF_GUEST)) {
return 0;
}
if (!kvm_enabled()) {
error_setg(errp, "PEF requires KVM");
return -1;
}
return kvmppc_svm_init(errp);
}
int pef_kvm_reset(ConfidentialGuestSupport *cgs, Error **errp)
{
if (!object_dynamic_cast(OBJECT(cgs), TYPE_PEF_GUEST)) {
return 0;
}
/*
* If we don't have KVM we should never have been able to
* initialize PEF, so we should never get this far
*/
assert(kvm_enabled());
return kvmppc_svm_off(errp);
}
OBJECT_DEFINE_TYPE_WITH_INTERFACES(PefGuest,
pef_guest,
PEF_GUEST,
CONFIDENTIAL_GUEST_SUPPORT,
{ TYPE_USER_CREATABLE },
{ NULL })
static void pef_guest_class_init(ObjectClass *oc, void *data)
{
}
static void pef_guest_init(Object *obj)
{
}
static void pef_guest_finalize(Object *obj)
{
}

View File

@ -83,6 +83,7 @@
#include "hw/ppc/spapr_tpm_proxy.h"
#include "hw/ppc/spapr_nvdimm.h"
#include "hw/ppc/spapr_numa.h"
#include "hw/ppc/pef.h"
#include "monitor/monitor.h"
@ -1574,7 +1575,7 @@ static void spapr_machine_reset(MachineState *machine)
void *fdt;
int rc;
kvmppc_svm_off(&error_fatal);
pef_kvm_reset(machine->cgs, &error_fatal);
spapr_caps_apply(spapr);
first_ppc_cpu = POWERPC_CPU(first_cpu);
@ -2658,6 +2659,11 @@ static void spapr_machine_init(MachineState *machine)
char *filename;
Error *resize_hpt_err = NULL;
/*
* if Secure VM (PEF) support is configured, then initialize it
*/
pef_kvm_init(machine->cgs, &error_fatal);
msi_nonbroken = true;
QLIST_INIT(&spapr->phbs);

View File

@ -14,8 +14,11 @@
#include <linux/kvm.h>
#include "cpu.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "sysemu/kvm.h"
#include "qom/object_interfaces.h"
#include "exec/confidential-guest-support.h"
#include "hw/s390x/ipl.h"
#include "hw/s390x/pv.h"
@ -111,3 +114,62 @@ void s390_pv_inject_reset_error(CPUState *cs)
/* Report that we are unable to enter protected mode */
env->regs[r1 + 1] = DIAG_308_RC_INVAL_FOR_PV;
}
#define TYPE_S390_PV_GUEST "s390-pv-guest"
OBJECT_DECLARE_SIMPLE_TYPE(S390PVGuest, S390_PV_GUEST)
/**
* S390PVGuest:
*
* The S390PVGuest object is basically a dummy used to tell the
* confidential guest support system to use s390's PV mechanism.
*
* # $QEMU \
* -object s390-pv-guest,id=pv0 \
* -machine ...,confidential-guest-support=pv0
*/
struct S390PVGuest {
ConfidentialGuestSupport parent_obj;
};
typedef struct S390PVGuestClass S390PVGuestClass;
struct S390PVGuestClass {
ConfidentialGuestSupportClass parent_class;
};
int s390_pv_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
{
if (!object_dynamic_cast(OBJECT(cgs), TYPE_S390_PV_GUEST)) {
return 0;
}
if (!s390_has_feat(S390_FEAT_UNPACK)) {
error_setg(errp,
"CPU model does not support Protected Virtualization");
return -1;
}
cgs->ready = true;
return 0;
}
OBJECT_DEFINE_TYPE_WITH_INTERFACES(S390PVGuest,
s390_pv_guest,
S390_PV_GUEST,
CONFIDENTIAL_GUEST_SUPPORT,
{ TYPE_USER_CREATABLE },
{ NULL })
static void s390_pv_guest_class_init(ObjectClass *oc, void *data)
{
}
static void s390_pv_guest_init(Object *obj)
{
}
static void s390_pv_guest_finalize(Object *obj)
{
}

View File

@ -250,6 +250,9 @@ static void ccw_init(MachineState *machine)
/* init CPUs (incl. CPU model) early so s390_has_feature() works */
s390_init_cpus(machine);
/* Need CPU model to be determined before we can set up PV */
s390_pv_init(machine->cgs, &error_fatal);
s390_flic_init();
/* init the SIGP facility */

View File

@ -0,0 +1,62 @@
/*
* QEMU Confidential Guest support
* This interface describes the common pieces between various
* schemes for protecting guest memory or other state against a
* compromised hypervisor. This includes memory encryption (AMD's
* SEV and Intel's MKTME) or special protection modes (PEF on POWER,
* or PV on s390x).
*
* Copyright Red Hat.
*
* Authors:
* David Gibson <david@gibson.dropbear.id.au>
*
* This work is licensed under the terms of the GNU GPL, version 2 or
* later. See the COPYING file in the top-level directory.
*
*/
#ifndef QEMU_CONFIDENTIAL_GUEST_SUPPORT_H
#define QEMU_CONFIDENTIAL_GUEST_SUPPORT_H
#ifndef CONFIG_USER_ONLY
#include "qom/object.h"
#define TYPE_CONFIDENTIAL_GUEST_SUPPORT "confidential-guest-support"
OBJECT_DECLARE_SIMPLE_TYPE(ConfidentialGuestSupport, CONFIDENTIAL_GUEST_SUPPORT)
struct ConfidentialGuestSupport {
Object parent;
/*
* ready: flag set by CGS initialization code once it's ready to
* start executing instructions in a potentially-secure
* guest
*
* The definition here is a bit fuzzy, because this is essentially
* part of a self-sanity-check, rather than a strict mechanism.
*
* It's not feasible to have a single point in the common machine
* init path to configure confidential guest support, because
* different mechanisms have different interdependencies requiring
* initialization in different places, often in arch or machine
* type specific code. It's also usually not possible to check
* for invalid configurations until that initialization code.
* That means it would be very easy to have a bug allowing CGS
* init to be bypassed entirely in certain configurations.
*
* Silently ignoring a requested security feature would be bad, so
* to avoid that we check late in init that this 'ready' flag is
* set if CGS was requested. If the CGS init hasn't happened, and
* so 'ready' is not set, we'll abort.
*/
bool ready;
};
typedef struct ConfidentialGuestSupportClass {
ObjectClass parent;
} ConfidentialGuestSupportClass;
#endif /* !CONFIG_USER_ONLY */
#endif /* QEMU_CONFIDENTIAL_GUEST_SUPPORT_H */

View File

@ -270,7 +270,7 @@ struct MachineState {
bool iommu;
bool suppress_vmdesc;
bool enable_graphics;
char *memory_encryption;
ConfidentialGuestSupport *cgs;
char *ram_memdev_id;
/*
* convenience alias to ram_memdev_id backend memory region

17
include/hw/ppc/pef.h Normal file
View File

@ -0,0 +1,17 @@
/*
* PEF (Protected Execution Facility) for POWER support
*
* Copyright Red Hat.
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#ifndef HW_PPC_PEF_H
#define HW_PPC_PEF_H
int pef_kvm_init(ConfidentialGuestSupport *cgs, Error **errp);
int pef_kvm_reset(ConfidentialGuestSupport *cgs, Error **errp);
#endif /* HW_PPC_PEF_H */

View File

@ -12,6 +12,9 @@
#ifndef HW_S390_PV_H
#define HW_S390_PV_H
#include "qapi/error.h"
#include "sysemu/kvm.h"
#ifdef CONFIG_KVM
#include "cpu.h"
#include "hw/s390x/s390-virtio-ccw.h"
@ -55,4 +58,18 @@ static inline void s390_pv_unshare(void) {}
static inline void s390_pv_inject_reset_error(CPUState *cs) {};
#endif /* CONFIG_KVM */
int s390_pv_kvm_init(ConfidentialGuestSupport *cgs, Error **errp);
static inline int s390_pv_init(ConfidentialGuestSupport *cgs, Error **errp)
{
if (!cgs) {
return 0;
}
if (kvm_enabled()) {
return s390_pv_kvm_init(cgs, errp);
}
error_setg(errp, "Protected Virtualization requires KVM");
return -1;
}
#endif /* HW_S390_PV_H */

View File

@ -37,6 +37,7 @@ typedef struct Chardev Chardev;
typedef struct Clock Clock;
typedef struct CompatProperty CompatProperty;
typedef struct CoMutex CoMutex;
typedef struct ConfidentialGuestSupport ConfidentialGuestSupport;
typedef struct CPUAddressSpace CPUAddressSpace;
typedef struct CPUState CPUState;
typedef struct DeviceListener DeviceListener;

View File

@ -638,7 +638,8 @@ bool object_apply_global_props(Object *obj, const GPtrArray *props,
Error **errp);
void object_set_machine_compat_props(GPtrArray *compat_props);
void object_set_accelerator_compat_props(GPtrArray *compat_props);
void object_register_sugar_prop(const char *driver, const char *prop, const char *value);
void object_register_sugar_prop(const char *driver, const char *prop,
const char *value, bool optional);
void object_apply_compat_props(Object *obj);
/**

View File

@ -233,22 +233,6 @@ int kvm_has_intx_set_mask(void);
*/
bool kvm_arm_supports_user_irq(void);
/**
* kvm_memcrypt_enabled - return boolean indicating whether memory encryption
* is enabled
* Returns: 1 memory encryption is enabled
* 0 memory encryption is disabled
*/
bool kvm_memcrypt_enabled(void);
/**
* kvm_memcrypt_encrypt_data: encrypt the memory range
*
* Return: 1 failed to encrypt the range
* 0 succesfully encrypted memory region
*/
int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len);
#ifdef NEED_CPU_H
#include "cpu.h"

View File

@ -16,8 +16,8 @@
#include "sysemu/kvm.h"
void *sev_guest_init(const char *id);
int sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len);
int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp);
int sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp);
int sev_inject_launch_secret(const char *hdr, const char *secret,
uint64_t gpa, Error **errp);
#endif

View File

@ -442,7 +442,8 @@ static GPtrArray *object_compat_props[3];
* other than "-global". These are generally used for syntactic
* sugar and legacy command line options.
*/
void object_register_sugar_prop(const char *driver, const char *prop, const char *value)
void object_register_sugar_prop(const char *driver, const char *prop,
const char *value, bool optional)
{
GlobalProperty *g;
if (!object_compat_props[2]) {
@ -452,6 +453,7 @@ void object_register_sugar_prop(const char *driver, const char *prop, const char
g->driver = g_strdup(driver);
g->property = g_strdup(prop);
g->value = g_strdup(value);
g->optional = optional;
g_ptr_array_add(object_compat_props[2], g);
}

View File

@ -179,7 +179,8 @@ void configure_rtc(QemuOpts *opts)
if (!strcmp(value, "slew")) {
object_register_sugar_prop("mc146818rtc",
"lost_tick_policy",
"slew");
"slew",
false);
} else if (!strcmp(value, "none")) {
/* discard is default */
} else {

View File

@ -101,6 +101,7 @@
#include "qemu/plugin.h"
#include "qemu/queue.h"
#include "sysemu/arch_init.h"
#include "exec/confidential-guest-support.h"
#include "ui/qemu-spice.h"
#include "qapi/string-input-visitor.h"
@ -1663,16 +1664,20 @@ static int machine_set_property(void *opaque,
return 0;
}
if (g_str_equal(qom_name, "igd-passthru")) {
object_register_sugar_prop(ACCEL_CLASS_NAME("xen"), qom_name, value);
object_register_sugar_prop(ACCEL_CLASS_NAME("xen"), qom_name, value,
false);
return 0;
}
if (g_str_equal(qom_name, "kvm-shadow-mem")) {
object_register_sugar_prop(ACCEL_CLASS_NAME("kvm"), qom_name, value);
object_register_sugar_prop(ACCEL_CLASS_NAME("kvm"), qom_name, value,
false);
return 0;
}
if (g_str_equal(qom_name, "kernel-irqchip")) {
object_register_sugar_prop(ACCEL_CLASS_NAME("kvm"), qom_name, value);
object_register_sugar_prop(ACCEL_CLASS_NAME("whpx"), qom_name, value);
object_register_sugar_prop(ACCEL_CLASS_NAME("kvm"), qom_name, value,
false);
object_register_sugar_prop(ACCEL_CLASS_NAME("whpx"), qom_name, value,
false);
return 0;
}
@ -2298,9 +2303,10 @@ static void qemu_process_sugar_options(void)
val = g_strdup_printf("%d",
(uint32_t) qemu_opt_get_number(qemu_find_opts_singleton("smp-opts"), "cpus", 1));
object_register_sugar_prop("memory-backend", "prealloc-threads", val);
object_register_sugar_prop("memory-backend", "prealloc-threads", val,
false);
g_free(val);
object_register_sugar_prop("memory-backend", "prealloc", "on");
object_register_sugar_prop("memory-backend", "prealloc", "on", false);
}
if (watchdog) {
@ -2493,6 +2499,8 @@ static void qemu_create_cli_devices(void)
static void qemu_machine_creation_done(void)
{
MachineState *machine = MACHINE(qdev_get_machine());
/* Did we create any drives that we failed to create a device for? */
drive_check_orphaned();
@ -2512,6 +2520,13 @@ static void qemu_machine_creation_done(void)
qdev_machine_creation_done();
if (machine->cgs) {
/*
* Verify that Confidential Guest Support has actually been initialized
*/
assert(machine->cgs->ready);
}
if (foreach_device_config(DEV_GDB, gdbserver_start) < 0) {
exit(1);
}

View File

@ -42,6 +42,7 @@
#include "hw/i386/intel_iommu.h"
#include "hw/i386/x86-iommu.h"
#include "hw/i386/e820_memory_layout.h"
#include "sysemu/sev.h"
#include "hw/pci/pci.h"
#include "hw/pci/msi.h"
@ -2135,6 +2136,25 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
uint64_t shadow_mem;
int ret;
struct utsname utsname;
Error *local_err = NULL;
/*
* Initialize SEV context, if required
*
* If no memory encryption is requested (ms->cgs == NULL) this is
* a no-op.
*
* It's also a no-op if a non-SEV confidential guest support
* mechanism is selected. SEV is the only mechanism available to
* select on x86 at present, so this doesn't arise, but if new
* mechanisms are supported in future (e.g. TDX), they'll need
* their own initialization either here or elsewhere.
*/
ret = sev_kvm_init(ms->cgs, &local_err);
if (ret < 0) {
error_report_err(local_err);
return ret;
}
if (!kvm_check_extension(s, KVM_CAP_IRQ_ROUTING)) {
error_report("kvm: KVM_CAP_IRQ_ROUTING not supported by KVM");

View File

@ -54,3 +54,8 @@ int sev_inject_launch_secret(const char *hdr, const char *secret,
{
return 1;
}
int sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp)
{
return 0;
}

View File

@ -31,6 +31,7 @@
#include "qom/object.h"
#include "exec/address-spaces.h"
#include "monitor/monitor.h"
#include "exec/confidential-guest-support.h"
#define TYPE_SEV_GUEST "sev-guest"
OBJECT_DECLARE_SIMPLE_TYPE(SevGuestState, SEV_GUEST)
@ -47,7 +48,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(SevGuestState, SEV_GUEST)
* -machine ...,memory-encryption=sev0
*/
struct SevGuestState {
Object parent_obj;
ConfidentialGuestSupport parent_obj;
/* configuration parameters */
char *sev_device;
@ -322,7 +323,7 @@ sev_guest_instance_init(Object *obj)
/* sev guest info */
static const TypeInfo sev_guest_info = {
.parent = TYPE_OBJECT,
.parent = TYPE_CONFIDENTIAL_GUEST_SUPPORT,
.name = TYPE_SEV_GUEST,
.instance_size = sizeof(SevGuestState),
.instance_finalize = sev_guest_finalize,
@ -334,26 +335,6 @@ static const TypeInfo sev_guest_info = {
}
};
static SevGuestState *
lookup_sev_guest_info(const char *id)
{
Object *obj;
SevGuestState *info;
obj = object_resolve_path_component(object_get_objects_root(), id);
if (!obj) {
return NULL;
}
info = (SevGuestState *)
object_dynamic_cast(obj, TYPE_SEV_GUEST);
if (!info) {
return NULL;
}
return info;
}
bool
sev_enabled(void)
{
@ -681,27 +662,24 @@ sev_vm_state_change(void *opaque, int running, RunState state)
}
}
void *
sev_guest_init(const char *id)
int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
{
SevGuestState *sev;
SevGuestState *sev
= (SevGuestState *)object_dynamic_cast(OBJECT(cgs), TYPE_SEV_GUEST);
char *devname;
int ret, fw_error;
uint32_t ebx;
uint32_t host_cbitpos;
struct sev_user_data_status status = {};
if (!sev) {
return 0;
}
ret = ram_block_discard_disable(true);
if (ret) {
error_report("%s: cannot disable RAM discard", __func__);
return NULL;
}
sev = lookup_sev_guest_info(id);
if (!sev) {
error_report("%s: '%s' is not a valid '%s' object",
__func__, id, TYPE_SEV_GUEST);
goto err;
return -1;
}
sev_guest = sev;
@ -711,14 +689,14 @@ sev_guest_init(const char *id)
host_cbitpos = ebx & 0x3f;
if (host_cbitpos != sev->cbitpos) {
error_report("%s: cbitpos check failed, host '%d' requested '%d'",
__func__, host_cbitpos, sev->cbitpos);
error_setg(errp, "%s: cbitpos check failed, host '%d' requested '%d'",
__func__, host_cbitpos, sev->cbitpos);
goto err;
}
if (sev->reduced_phys_bits < 1) {
error_report("%s: reduced_phys_bits check failed, it should be >=1,"
" requested '%d'", __func__, sev->reduced_phys_bits);
error_setg(errp, "%s: reduced_phys_bits check failed, it should be >=1,"
" requested '%d'", __func__, sev->reduced_phys_bits);
goto err;
}
@ -727,20 +705,19 @@ sev_guest_init(const char *id)
devname = object_property_get_str(OBJECT(sev), "sev-device", NULL);
sev->sev_fd = open(devname, O_RDWR);
if (sev->sev_fd < 0) {
error_report("%s: Failed to open %s '%s'", __func__,
devname, strerror(errno));
}
g_free(devname);
if (sev->sev_fd < 0) {
error_setg(errp, "%s: Failed to open %s '%s'", __func__,
devname, strerror(errno));
g_free(devname);
goto err;
}
g_free(devname);
ret = sev_platform_ioctl(sev->sev_fd, SEV_PLATFORM_STATUS, &status,
&fw_error);
if (ret) {
error_report("%s: failed to get platform status ret=%d "
"fw_error='%d: %s'", __func__, ret, fw_error,
fw_error_to_str(fw_error));
error_setg(errp, "%s: failed to get platform status ret=%d "
"fw_error='%d: %s'", __func__, ret, fw_error,
fw_error_to_str(fw_error));
goto err;
}
sev->build_id = status.build;
@ -750,14 +727,14 @@ sev_guest_init(const char *id)
trace_kvm_sev_init();
ret = sev_ioctl(sev->sev_fd, KVM_SEV_INIT, NULL, &fw_error);
if (ret) {
error_report("%s: failed to initialize ret=%d fw_error=%d '%s'",
__func__, ret, fw_error, fw_error_to_str(fw_error));
error_setg(errp, "%s: failed to initialize ret=%d fw_error=%d '%s'",
__func__, ret, fw_error, fw_error_to_str(fw_error));
goto err;
}
ret = sev_launch_start(sev);
if (ret) {
error_report("%s: failed to create encryption context", __func__);
error_setg(errp, "%s: failed to create encryption context", __func__);
goto err;
}
@ -765,23 +742,29 @@ sev_guest_init(const char *id)
qemu_add_machine_init_done_notifier(&sev_machine_done_notify);
qemu_add_vm_change_state_handler(sev_vm_state_change, sev);
return sev;
cgs->ready = true;
return 0;
err:
sev_guest = NULL;
ram_block_discard_disable(false);
return NULL;
return -1;
}
int
sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len)
sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp)
{
SevGuestState *sev = handle;
assert(sev);
if (!sev_guest) {
return 0;
}
/* if SEV is in update state then encrypt the data else do nothing */
if (sev_check_state(sev, SEV_STATE_LAUNCH_UPDATE)) {
return sev_launch_update_data(sev, ptr, len);
if (sev_check_state(sev_guest, SEV_STATE_LAUNCH_UPDATE)) {
int ret = sev_launch_update_data(sev_guest, ptr, len);
if (ret < 0) {
error_setg(errp, "failed to encrypt pflash rom");
return ret;
}
}
return 0;

View File

@ -2929,21 +2929,3 @@ void kvmppc_set_reg_tb_offset(PowerPCCPU *cpu, int64_t tb_offset)
kvm_set_one_reg(cs, KVM_REG_PPC_TB_OFFSET, &tb_offset);
}
}
/*
* Don't set error if KVM_PPC_SVM_OFF ioctl is invoked on kernels
* that don't support this ioctl.
*/
void kvmppc_svm_off(Error **errp)
{
int rc;
if (!kvm_enabled()) {
return;
}
rc = kvm_vm_ioctl(KVM_STATE(current_accel()), KVM_PPC_SVM_OFF);
if (rc && rc != -ENOTTY) {
error_setg_errno(errp, -rc, "KVM_PPC_SVM_OFF ioctl failed");
}
}

View File

@ -39,7 +39,6 @@ int kvmppc_booke_watchdog_enable(PowerPCCPU *cpu);
target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu,
bool radix, bool gtse,
uint64_t proc_tbl);
void kvmppc_svm_off(Error **errp);
#ifndef CONFIG_USER_ONLY
bool kvmppc_spapr_use_multitce(void);
int kvmppc_spapr_enable_inkernel_multitce(void);
@ -216,11 +215,6 @@ static inline target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu,
return 0;
}
static inline void kvmppc_svm_off(Error **errp)
{
return;
}
static inline void kvmppc_set_reg_ppc_online(PowerPCCPU *cpu,
unsigned int online)
{