[PATCH] igvm: add initial support for non-cc firmware in igvm format

Gerd Hoffmann posted 1 patch 4 months, 1 week ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20250709123007.1039675-1-kraxel@redhat.com
Maintainers: Eric Blake <eblake@redhat.com>, Markus Armbruster <armbru@redhat.com>, Paolo Bonzini <pbonzini@redhat.com>, "Daniel P. Berrangé" <berrange@redhat.com>, Eduardo Habkost <eduardo@habkost.net>, Zhao Liu <zhao1.liu@intel.com>
target/i386/nocc.c      | 283 ++++++++++++++++++++++++++++++++++++++++
qapi/qom.json           |   1 +
target/i386/meson.build |   1 +
3 files changed, 285 insertions(+)
create mode 100644 target/i386/nocc.c
[PATCH] igvm: add initial support for non-cc firmware in igvm format
Posted by Gerd Hoffmann 4 months, 1 week ago
Implement a ConfidentialGuestSupportClass for non-confidential VMs.
This allows the igvm support code work without sev/tdx.

RfC: Not fully sure this is the best way to implement this.
Alternatively we could add this directly into the igvm backend and run
it in case no confidential guest support object is present.

TODO: Implement proper reset.
 - re-initialize memory regions from igvm file content.
 - load initial register state (if present).

Usage:
  qemu-system-x86_64 \
    -object nocc,id=nocc0 \
    -machine confidential-guest-support=nocc0

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 target/i386/nocc.c      | 283 ++++++++++++++++++++++++++++++++++++++++
 qapi/qom.json           |   1 +
 target/i386/meson.build |   1 +
 3 files changed, 285 insertions(+)
 create mode 100644 target/i386/nocc.c

diff --git a/target/i386/nocc.c b/target/i386/nocc.c
new file mode 100644
index 000000000000..5d92fb0a4033
--- /dev/null
+++ b/target/i386/nocc.c
@@ -0,0 +1,283 @@
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+
+#include "qapi/error.h"
+#include "qom/object_interfaces.h"
+#include "hw/i386/pc.h"
+#include "hw/i386/e820_memory_layout.h"
+
+#include "cpu.h"
+#include "confidential-guest.h"
+
+#define TYPE_NO_CC "nocc"
+OBJECT_DECLARE_TYPE(NoCCState, NoCCStateClass, NO_CC);
+
+struct IgvmNativeVpContextX64 {
+    uint64_t rax;
+    uint64_t rcx;
+    uint64_t rdx;
+    uint64_t rbx;
+    uint64_t rsp;
+    uint64_t rbp;
+    uint64_t rsi;
+    uint64_t rdi;
+    uint64_t r8;
+    uint64_t r9;
+    uint64_t r10;
+    uint64_t r11;
+    uint64_t r12;
+    uint64_t r13;
+    uint64_t r14;
+    uint64_t r15;
+    uint64_t rip;
+    uint64_t rflags;
+    uint64_t idtr_base;
+    uint16_t idtr_limit;
+    uint16_t reserved[2];
+    uint16_t gdtr_limit;
+    uint64_t gdtr_base;
+
+    uint16_t code_selector;
+    uint16_t code_attributes;
+    uint32_t code_base;
+    uint32_t code_limit;
+
+    uint16_t data_selector;
+    uint16_t data_attributes;
+    uint32_t data_base;
+    uint32_t data_limit;
+
+    uint64_t gs_base;
+    uint64_t cr0;
+    uint64_t cr3;
+    uint64_t cr4;
+    uint64_t efer;
+};
+
+struct NoCCState {
+    X86ConfidentialGuest parent_obj;
+    struct IgvmNativeVpContextX64 regs;
+    bool have_regs;
+};
+
+struct NoCCStateClass {
+    X86ConfidentialGuestClass parent_class;
+};
+
+#define FLAGS_TO_SEGCACHE(flags)                \
+    (((unsigned int)flags) << 8)
+
+static void no_cc_set_regs(NoCCState *nocc)
+{
+    X86CPU *x86 = X86_CPU(first_cpu);
+    CPUX86State *env = &x86->env;
+
+    cpu_load_efer(env, nocc->regs.efer);
+    cpu_x86_update_cr4(env, nocc->regs.cr4);
+    cpu_x86_update_cr0(env, nocc->regs.cr0);
+    cpu_x86_update_cr3(env, nocc->regs.cr3);
+
+    cpu_x86_load_seg_cache(
+        env, R_CS, nocc->regs.code_selector,
+        nocc->regs.code_base, nocc->regs.code_limit,
+        FLAGS_TO_SEGCACHE(nocc->regs.code_attributes));
+    cpu_x86_load_seg_cache(
+        env, R_DS, nocc->regs.data_selector,
+        nocc->regs.data_base, nocc->regs.data_limit,
+        FLAGS_TO_SEGCACHE(nocc->regs.data_attributes));
+    cpu_x86_load_seg_cache(
+        env, R_ES, nocc->regs.data_selector,
+        nocc->regs.data_base, nocc->regs.data_limit,
+        FLAGS_TO_SEGCACHE(nocc->regs.data_attributes));
+    cpu_x86_load_seg_cache(
+        env, R_FS, nocc->regs.data_selector,
+        nocc->regs.data_base, nocc->regs.data_limit,
+        FLAGS_TO_SEGCACHE(nocc->regs.data_attributes));
+    cpu_x86_load_seg_cache(
+        env, R_GS, nocc->regs.data_selector,
+        nocc->regs.data_base, nocc->regs.data_limit,
+        FLAGS_TO_SEGCACHE(nocc->regs.data_attributes));
+    cpu_x86_load_seg_cache(
+        env, R_SS, nocc->regs.data_selector,
+        nocc->regs.data_base, nocc->regs.data_limit,
+        FLAGS_TO_SEGCACHE(nocc->regs.data_attributes));
+
+    env->gdt.base = nocc->regs.gdtr_base;
+    env->gdt.limit = nocc->regs.gdtr_limit;
+    env->idt.base = nocc->regs.idtr_base;
+    env->idt.limit = nocc->regs.idtr_limit;
+
+    env->regs[R_EAX] = nocc->regs.rax;
+    env->regs[R_ECX] = nocc->regs.rcx;
+    env->regs[R_EDX] = nocc->regs.rdx;
+    env->regs[R_EBX] = nocc->regs.rbx;
+    env->regs[R_ESP] = nocc->regs.rsp;
+    env->regs[R_EBP] = nocc->regs.rbp;
+    env->regs[R_ESI] = nocc->regs.rsi;
+    env->regs[R_EDI] = nocc->regs.rdi;
+#ifdef TARGET_X86_64
+    env->regs[R_R8] = nocc->regs.r8;
+    env->regs[R_R9] = nocc->regs.r9;
+    env->regs[R_R10] = nocc->regs.r10;
+    env->regs[R_R11] = nocc->regs.r11;
+    env->regs[R_R12] = nocc->regs.r12;
+    env->regs[R_R13] = nocc->regs.r13;
+    env->regs[R_R14] = nocc->regs.r14;
+    env->regs[R_R15] = nocc->regs.r15;
+#endif
+    env->eip = nocc->regs.rip;
+    env->eflags = nocc->regs.rflags;
+}
+
+static int no_cc_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
+{
+    info_report("%s:", __func__);
+    return 0;
+}
+
+static int no_cc_kvm_reset(ConfidentialGuestSupport *cgs, Error **errp)
+{
+    NoCCState *nocc = NO_CC(cgs);
+
+    info_report("%s: have regs %s", __func__, nocc->have_regs ? "yes" : "no");
+
+    if (nocc->have_regs) {
+        no_cc_set_regs(NO_CC(cgs));
+    }
+    return 0;
+}
+
+static bool no_cc_check_support(ConfidentialGuestPlatformType platform,
+                                uint16_t platform_version, uint8_t highest_vtl,
+                                uint64_t shared_gpa_boundary)
+{
+    return false;
+}
+
+static int no_cc_set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len,
+                                 ConfidentialGuestPageType memory_type,
+                                 uint16_t cpu_index, Error **errp)
+{
+    static const char *names[] = {
+        [ CGS_PAGE_TYPE_NORMAL ]          = "normal",
+        [ CGS_PAGE_TYPE_VMSA ]            = "vmsa",
+        [ CGS_PAGE_TYPE_ZERO ]            = "zero",
+        [ CGS_PAGE_TYPE_UNMEASURED ]      = "unmeasured",
+        [ CGS_PAGE_TYPE_SECRETS ]         = "secrets",
+        [ CGS_PAGE_TYPE_CPUID ]           = "cpuid",
+        [ CGS_PAGE_TYPE_REQUIRED_MEMORY ] = "required-mem",
+    };
+
+    ConfidentialGuestSupport *cgs = MACHINE(qdev_get_machine())->cgs;
+    NoCCState *nocc = NO_CC(cgs);
+    struct IgvmNativeVpContextX64 *regs;
+
+    switch (memory_type) {
+    case CGS_PAGE_TYPE_VMSA:
+        info_report("%s: %lx +%lx [%s, unsupported]",
+                    __func__, gpa, len, names[memory_type]);
+        regs = (void *)ptr;
+        nocc->regs = *regs;
+        nocc->have_regs = true;
+        /*
+         * FIXME: need some non-hackish way to hook up cgs->kvm_reset() + apply
+         * initial register state in non-CC mode.
+         *
+         * Initial register state is optional.  If not present the platform
+         * default should be used (i.e. real mode entry at f000:fff0 on x86).
+         * In that case we have nothing special to do.
+         *
+         * Throw an error for now if we got some initial register state.
+         */
+        return -1;
+
+    case CGS_PAGE_TYPE_NORMAL:
+    case CGS_PAGE_TYPE_ZERO:
+    case CGS_PAGE_TYPE_UNMEASURED:
+    case CGS_PAGE_TYPE_REQUIRED_MEMORY:
+        info_report("%s: %lx +%lx [%s]",
+                    __func__, gpa, len, names[memory_type]);
+        return 0;
+
+    case CGS_PAGE_TYPE_SECRETS:
+    case CGS_PAGE_TYPE_CPUID:
+        error_report("%s: %lx +%lx [%s, unsupported]",
+                     __func__, gpa, len, names[memory_type]);
+        return -1;
+
+    default:
+        error_setg(errp, "%s: unknown memory type: %d", __func__, memory_type);
+        return -1;
+    }
+}
+
+static int no_cc_get_mem_map_entry(int index,
+                                   ConfidentialGuestMemoryMapEntry *entry,
+                                   Error **errp)
+{
+    struct e820_entry *table;
+    int num_entries;
+
+    num_entries = e820_get_table(&table);
+    if ((index < 0) || (index >= num_entries)) {
+        return 1;
+    }
+    entry->gpa = table[index].address;
+    entry->size = table[index].length;
+    switch (table[index].type) {
+    case E820_RAM:
+        info_report("%s: ram: %lx +%lx", __func__, entry->gpa, entry->size);
+        entry->type = CGS_MEM_RAM;
+        break;
+    case E820_RESERVED:
+        info_report("%s: reserved: %lx +%lx", __func__, entry->gpa, entry->size);
+        entry->type = CGS_MEM_RESERVED;
+        break;
+    default:
+        return -1;
+    }
+    return 0;
+}
+
+static void
+no_cc_class_init(ObjectClass *oc, const void *data)
+{
+    ConfidentialGuestSupportClass *cgsc =
+        CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc);
+
+    cgsc->kvm_init = no_cc_kvm_init;
+    cgsc->kvm_reset = no_cc_kvm_reset;
+    cgsc->check_support = no_cc_check_support;
+    cgsc->set_guest_state = no_cc_set_guest_state;
+    cgsc->get_mem_map_entry = no_cc_get_mem_map_entry;
+}
+
+static void
+no_cc_instance_init(Object *obj)
+{
+    ConfidentialGuestSupport *cgs =
+        CONFIDENTIAL_GUEST_SUPPORT(obj);
+
+    cgs->ready = true;
+}
+
+static const TypeInfo no_cc_info = {
+    .parent = TYPE_X86_CONFIDENTIAL_GUEST,
+    .name = TYPE_NO_CC,
+    .instance_size = sizeof(NoCCState),
+    .instance_init = no_cc_instance_init,
+    .class_size = sizeof(NoCCStateClass),
+    .class_init = no_cc_class_init,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_USER_CREATABLE },
+        { }
+    }
+};
+
+static void
+no_cc_register_types(void)
+{
+    type_register_static(&no_cc_info);
+}
+
+type_init(no_cc_register_types);
diff --git a/qapi/qom.json b/qapi/qom.json
index bbdb56dced66..bd5d03916efa 100644
--- a/qapi/qom.json
+++ b/qapi/qom.json
@@ -1184,6 +1184,7 @@
       'if': 'CONFIG_SECRET_KEYRING' },
     'sev-guest',
     'sev-snp-guest',
+    'nocc',
     'thread-context',
     's390-pv-guest',
     'tdx-guest',
diff --git a/target/i386/meson.build b/target/i386/meson.build
index c1aacea61356..d37bc4b567ab 100644
--- a/target/i386/meson.build
+++ b/target/i386/meson.build
@@ -5,6 +5,7 @@ i386_ss.add(files(
   'helper.c',
   'xsave_helper.c',
   'cpu-dump.c',
+  'nocc.c',
 ))
 i386_ss.add(when: 'CONFIG_SEV', if_true: files('host-cpu.c', 'confidential-guest.c'))
 
-- 
2.50.0
Re: [PATCH] igvm: add initial support for non-cc firmware in igvm format
Posted by Gerd Hoffmann 2 months ago
On Wed, Jul 09, 2025 at 02:30:07PM +0200, Gerd Hoffmann wrote:
> Implement a ConfidentialGuestSupportClass for non-confidential VMs.
> This allows the igvm support code work without sev/tdx.
> 
> RfC: Not fully sure this is the best way to implement this.
> Alternatively we could add this directly into the igvm backend and run
> it in case no confidential guest support object is present.

Started to look at this again.  Noticed that at least simple native
igvm files (with memory regions only) boot just fine without an
ConfidentialGuestSupportClass.  Which kind-of underlines that going for
a pseudo-ConfidentialGuestSupportClass for native mode is maybe not the
best design idea ...

Nevertheless we'll go need target-specific code for some IGVM features.
That is obviously the case for loading some custom initial CPU state.
But also things like the memory map are not the same across targets,
even though some targets might share an implementation (e820 on x86,
fdt elsewhere).

Suggestions how to wire that up best?

take care,
  Gerd
Re: [PATCH] igvm: add initial support for non-cc firmware in igvm format
Posted by Daniel P. Berrangé 2 months ago
On Fri, Sep 12, 2025 at 03:25:08PM +0200, Gerd Hoffmann wrote:
> On Wed, Jul 09, 2025 at 02:30:07PM +0200, Gerd Hoffmann wrote:
> > Implement a ConfidentialGuestSupportClass for non-confidential VMs.
> > This allows the igvm support code work without sev/tdx.
> > 
> > RfC: Not fully sure this is the best way to implement this.
> > Alternatively we could add this directly into the igvm backend and run
> > it in case no confidential guest support object is present.
> 
> Started to look at this again.  Noticed that at least simple native
> igvm files (with memory regions only) boot just fine without an
> ConfidentialGuestSupportClass.  Which kind-of underlines that going for
> a pseudo-ConfidentialGuestSupportClass for native mode is maybe not the
> best design idea ...
> 
> Nevertheless we'll go need target-specific code for some IGVM features.
> That is obviously the case for loading some custom initial CPU state.
> But also things like the memory map are not the same across targets,
> even though some targets might share an implementation (e820 on x86,
> fdt elsewhere).
> 
> Suggestions how to wire that up best?

I'm not familiar with the details of IGVM wrt varying technologies.
Consider that x86 can boot a guest non-confidential, SEV-SNP or TDX.
Do we have a single IGVM file that provides enough data for all
three scenarios, or does each deployment technology need a separate
IGVM file to be provided ?

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 :|
Re: [PATCH] igvm: add initial support for non-cc firmware in igvm format
Posted by Gerd Hoffmann 2 months ago
On Fri, Sep 12, 2025 at 02:29:16PM +0100, Daniel P. Berrangé wrote:
> On Fri, Sep 12, 2025 at 03:25:08PM +0200, Gerd Hoffmann wrote:
> > On Wed, Jul 09, 2025 at 02:30:07PM +0200, Gerd Hoffmann wrote:
> > > Implement a ConfidentialGuestSupportClass for non-confidential VMs.
> > > This allows the igvm support code work without sev/tdx.
> > > 
> > > RfC: Not fully sure this is the best way to implement this.
> > > Alternatively we could add this directly into the igvm backend and run
> > > it in case no confidential guest support object is present.
> > 
> > Started to look at this again.  Noticed that at least simple native
> > igvm files (with memory regions only) boot just fine without an
> > ConfidentialGuestSupportClass.  Which kind-of underlines that going for
> > a pseudo-ConfidentialGuestSupportClass for native mode is maybe not the
> > best design idea ...
> > 
> > Nevertheless we'll go need target-specific code for some IGVM features.
> > That is obviously the case for loading some custom initial CPU state.
> > But also things like the memory map are not the same across targets,
> > even though some targets might share an implementation (e820 on x86,
> > fdt elsewhere).
> > 
> > Suggestions how to wire that up best?
> 
> I'm not familiar with the details of IGVM wrt varying technologies.
> Consider that x86 can boot a guest non-confidential, SEV-SNP or TDX.
> Do we have a single IGVM file that provides enough data for all
> three scenarios, or does each deployment technology need a separate
> IGVM file to be provided ?

You can have a single igvm file supporting all platforms.

take care,
  Gerd
Re: [PATCH] igvm: add initial support for non-cc firmware in igvm format
Posted by Philippe Mathieu-Daudé 4 months, 1 week ago
Hi Gerd,

On 9/7/25 14:30, Gerd Hoffmann wrote:
> Implement a ConfidentialGuestSupportClass for non-confidential VMs.
> This allows the igvm support code work without sev/tdx.

Is this something we only want in non-KVM builds due to security
boundary concerns?

> 
> RfC: Not fully sure this is the best way to implement this.
> Alternatively we could add this directly into the igvm backend and run
> it in case no confidential guest support object is present.
> 
> TODO: Implement proper reset.
>   - re-initialize memory regions from igvm file content.
>   - load initial register state (if present).
> 
> Usage:
>    qemu-system-x86_64 \
>      -object nocc,id=nocc0 \
>      -machine confidential-guest-support=nocc0
> 
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> ---
>   target/i386/nocc.c      | 283 ++++++++++++++++++++++++++++++++++++++++
>   qapi/qom.json           |   1 +
>   target/i386/meson.build |   1 +
>   3 files changed, 285 insertions(+)
>   create mode 100644 target/i386/nocc.c
Re: [PATCH] igvm: add initial support for non-cc firmware in igvm format
Posted by Gerd Hoffmann 4 months, 1 week ago
On Wed, Jul 09, 2025 at 03:34:20PM +0200, Philippe Mathieu-Daudé wrote:
> Hi Gerd,
> 
> On 9/7/25 14:30, Gerd Hoffmann wrote:
> > Implement a ConfidentialGuestSupportClass for non-confidential VMs.
> > This allows the igvm support code work without sev/tdx.
> 
> Is this something we only want in non-KVM builds due to security
> boundary concerns?

Hmm, not sure what exactly you have in mind.  This is not about
emulating sev or something.  The IGVM format can also be used to
package native, non-confidential firmware, seabios for example.

take care,
  Gerd