Re: [PATCH v1 0/2] x86/vmware: Share steal-time storage in encrypted guests

From: Zack Rusin

Date: Wed Sep 16 2026 - 12:13:16 EST


On Wed, Sep 16, 2026 at 8:18 AM Kiryl Shutsemau <kas@xxxxxxxxxx> wrote:
>
> On Wed, Sep 16, 2026 at 01:05:38AM -0400, Zack Rusin wrote:
> > VMware registers each per-CPU steal-time GPA with the host. An encrypted
> > guest must first convert that storage to shared memory, but the existing
> > setup publishes the address without conversion.
> >
> > Patch 1 makes the decrypted per-CPU section available with
> > CONFIG_X86_MEM_ENCRYPT, including TDX-only configurations. Patch 2 defers
> > encrypted-guest setup until allocator-backed page-table splitting is
> > available, converts every possible CPU's storage before publishing any
> > GPA, and attempts to roll back all conversions on failure.
>
> I acked 1/2, but I don't like where 2/2 does the conversion.

I saw that, thank you. Since I'll be respinning v2 of this I can go
ahead and drop the unused DECLARE_PER_CPU_DECRYPTED in that one if you
want as well.

> A variable declared with DEFINE_PER_CPU_DECRYPTED() ends up in a
> dedicated, page-aligned linker section. The point of the section is that
> one place converts it. Instead every user does it itself: KVM in
> sev_map_percpu_data(), and now VMware in vmware_decrypt_steal_time(),
> each with its own vendor checks and failure handling. The macro today
> only buys page isolation, not the shared mapping its name promises.
>
> The underlying problem is that the whole "decrypted section"
> infrastructure is built around SME/SEV and was never generalized.
> __bss_decrypted, early_set_memory_decrypted() and
> mem_encrypt_free_decrypted_mem() are all under CONFIG_AMD_MEM_ENCRYPT
> and implemented in mem_encrypt_amd.c. sme_postprocess_startup() converts
> .bss..decrypted only if sme_get_me_mask() is set, so a TDX guest never
> shares it. 1/2 moves the per-CPU linker section to X86_MEM_ENCRYPT, but
> nothing that would act on that section follows.
>
> Rather than have every TDX user reinvent the conversion, I would rather
> see the infrastructure made vendor-neutral: boundary symbols for the
> per-CPU decrypted section like the ones .bss..decrypted has, an early
> conversion primitive that works on TDX as well as SEV, and a single
> conversion of both sections at boot. Then sev_map_percpu_data() and
> this driver's loop go away.
>
> > TDX's conversion callback uses __pa(), so patch 2 preflights every possible
> > CPU and leaves steal time disabled if a TDX guest uses vmalloc-backed
> > per-CPU storage. This covers percpu_alloc=page and automatic allocator
> > fallback. AMD encrypted guests support those mappings and are not rejected.
> > Supporting them in TDX would require a separate conversion-API change.
>
> Refusing vmalloc-backed storage on TDX is the right call, and not because
> of the __pa() in the callback. Converting a vmalloc alias means either
> fracturing the direct map or leaving a private direct-map alias to a
> shared page, and the latter is a guest shutdown the moment
> load_unaligned_zeropad() steps into it. See the comment in
> tdx_early_init() and the earlier discussion of the same idea:
>
> https://lore.kernel.org/all/xqi2bkulnhen2vax5msbzczlaywx3dsc7ezpn7oo5qn7u7xzap@xmaseinov7tf/
>
> But that decision belongs in the same central place as the conversion.
> If the first per-CPU chunk is vmalloc-backed on TDX, the decrypted
> section is simply not shared, and users see that, instead of every
> driver re-deriving it.

I went through the linked thread, including the dropped 2022
Quote-buffer alias approach and the 2024 netvsc work's kexec
regression, fix and re-review discussion. I will keep TDX vmalloc
support out of this series and correct the explanation to cover alias
consistency, direct-map splitting and load_unaligned_zeropad(),
including removing the suggestion that per-page GPA lookup alone
resolves it.

After tracing the boot paths, I think there are two distinct pieces
here. The SEV boot GHCB needs .bss..decrypted before BSS is cleared,
whereas the SMP per-CPU instances do not exist until
setup_per_cpu_areas(). KVM then registers its boot-CPU per-CPU buffers
in smp_prepare_boot_cpu(), so the common conversion needs the early
primitive you proposed. A shared early_initcall would be too late for
KVM. The UP registration path probably also needs handling separately

My suggestion would be to do one of two things (or at least I think
those two options are realistic to me, given that I'm no expert here):
- add the central per-CPU infrastructure and migrate KVM/VMware
together, removing both loops and putting the TDX vmalloc rejection
and readiness state in common code. Would you and the KVM maintainers
be happy with that first, and generalizing .bss..decrypted separately
while preserving its earliest SEV setup? afaict the latter also needs
to broaden the annotation and linker guards, and changes kvmclock
storage and sharing on TDX-only builds.
- a smaller VMware fix using a separately allocated, direct-mapped
page per CPU while the common infrastructure is developed. Using the
normal allocator would also defer ordinary-guest registration, unless
I kept their current static storage as a second path. This option
would retain caller-managed sharing and buffer lifetime handling.

The second option is, of course, easier for me, but I'm happy to do
the first to get steal-time storage working for us. Do you have any
thoughts on this?

z

Attachment: smime.p7s
Description: S/MIME Cryptographic Signature