* [PATCH] x86/virt/tdx: Use precalculated TDVPR page physical address @ 2025-09-10 14:44 Dave Hansen 2025-09-10 16:06 ` Kiryl Shutsemau 2025-10-20 13:57 ` Sean Christopherson 0 siblings, 2 replies; 15+ messages in thread From: Dave Hansen @ 2025-09-10 14:44 UTC (permalink / raw) To: linux-kernel Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin, Kirill A. Shutemov, Rick Edgecombe, Sean Christopherson, Paolo Bonzini, Kai Huang, Isaku Yamahata, Vishal Annapurve, Thomas Huth, Adrian Hunter, linux-coco, kvm, Farrah Chen From: Kai Huang <kai.huang@intel.com> All of the x86 KVM guest types (VMX, SEV and TDX) do some special context tracking when entering guests. This means that the actual guest entry sequence must be noinstr. Part of entering a TDX guest is passing a physical address to the TDX module. Right now, that physical address is stored as a 'struct page' and converted to a physical address at guest entry. That page=>phys conversion can be complicated, can vary greatly based on kernel config, and it is definitely _not_ a noinstr path today. There have been a number of tinkering approaches to try and fix this up, but they all fall down due to some part of the page=>phys conversion infrastructure not being noinstr friendly. Precalculate the page=>phys conversion and store it in the existing 'tdx_vp' structure. Use the new field at every site that needs a tdvpr physical address. Remove the now redundant tdx_tdvpr_pa(). Remove the __flatten remnant from the tinkering. Note that only one user of the new field is actually noinstr. All others can use page_to_phys(). But, they might as well save the effort since there is a pre-calculated value sitting there for them. [ dhansen: rewrite all the text ] Signed-off-by: Kai Huang <kai.huang@intel.com> Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com> Tested-by: Farrah Chen <farrah.chen@intel.com> --- arch/x86/include/asm/tdx.h | 2 ++ arch/x86/kvm/vmx/tdx.c | 9 +++++++++ arch/x86/virt/vmx/tdx/tdx.c | 21 ++++++++------------- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h index 6120461bd5ff3..6b338d7f01b7d 100644 --- a/arch/x86/include/asm/tdx.h +++ b/arch/x86/include/asm/tdx.h @@ -171,6 +171,8 @@ struct tdx_td { struct tdx_vp { /* TDVP root page */ struct page *tdvpr_page; + /* precalculated page_to_phys(tdvpr_page) for use in noinstr code */ + phys_addr_t tdvpr_pa; /* TD vCPU control structure: */ struct page **tdcx_pages; diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index 04b6d332c1afa..75326a7449cc3 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -852,6 +852,7 @@ void tdx_vcpu_free(struct kvm_vcpu *vcpu) if (tdx->vp.tdvpr_page) { tdx_reclaim_control_page(tdx->vp.tdvpr_page); tdx->vp.tdvpr_page = 0; + tdx->vp.tdvpr_pa = 0; } tdx->state = VCPU_TD_STATE_UNINITIALIZED; @@ -2931,6 +2932,13 @@ static int tdx_td_vcpu_init(struct kvm_vcpu *vcpu, u64 vcpu_rcx) return -ENOMEM; tdx->vp.tdvpr_page = page; + /* + * page_to_phys() does not work in 'noinstr' code, like guest + * entry via tdh_vp_enter(). Precalculate and store it instead + * of doing it at runtime later. + */ + tdx->vp.tdvpr_pa = page_to_phys(tdx->vp.tdvpr_page); + tdx->vp.tdcx_pages = kcalloc(kvm_tdx->td.tdcx_nr_pages, sizeof(*tdx->vp.tdcx_pages), GFP_KERNEL); if (!tdx->vp.tdcx_pages) { @@ -2993,6 +3001,7 @@ static int tdx_td_vcpu_init(struct kvm_vcpu *vcpu, u64 vcpu_rcx) if (tdx->vp.tdvpr_page) __free_page(tdx->vp.tdvpr_page); tdx->vp.tdvpr_page = 0; + tdx->vp.tdvpr_pa = 0; return ret; } diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c index 330b560313afe..eac4032484626 100644 --- a/arch/x86/virt/vmx/tdx/tdx.c +++ b/arch/x86/virt/vmx/tdx/tdx.c @@ -1504,11 +1504,6 @@ static inline u64 tdx_tdr_pa(struct tdx_td *td) return page_to_phys(td->tdr_page); } -static inline u64 tdx_tdvpr_pa(struct tdx_vp *td) -{ - return page_to_phys(td->tdvpr_page); -} - /* * The TDX module exposes a CLFLUSH_BEFORE_ALLOC bit to specify whether * a CLFLUSH of pages is required before handing them to the TDX module. @@ -1520,9 +1515,9 @@ static void tdx_clflush_page(struct page *page) clflush_cache_range(page_to_virt(page), PAGE_SIZE); } -noinstr __flatten u64 tdh_vp_enter(struct tdx_vp *td, struct tdx_module_args *args) +noinstr u64 tdh_vp_enter(struct tdx_vp *td, struct tdx_module_args *args) { - args->rcx = tdx_tdvpr_pa(td); + args->rcx = td->tdvpr_pa; return __seamcall_dirty_cache(__seamcall_saved_ret, TDH_VP_ENTER, args); } @@ -1583,7 +1578,7 @@ u64 tdh_vp_addcx(struct tdx_vp *vp, struct page *tdcx_page) { struct tdx_module_args args = { .rcx = page_to_phys(tdcx_page), - .rdx = tdx_tdvpr_pa(vp), + .rdx = vp->tdvpr_pa, }; tdx_clflush_page(tdcx_page); @@ -1652,7 +1647,7 @@ EXPORT_SYMBOL_GPL(tdh_mng_create); u64 tdh_vp_create(struct tdx_td *td, struct tdx_vp *vp) { struct tdx_module_args args = { - .rcx = tdx_tdvpr_pa(vp), + .rcx = vp->tdvpr_pa, .rdx = tdx_tdr_pa(td), }; @@ -1708,7 +1703,7 @@ EXPORT_SYMBOL_GPL(tdh_mr_finalize); u64 tdh_vp_flush(struct tdx_vp *vp) { struct tdx_module_args args = { - .rcx = tdx_tdvpr_pa(vp), + .rcx = vp->tdvpr_pa, }; return seamcall(TDH_VP_FLUSH, &args); @@ -1754,7 +1749,7 @@ EXPORT_SYMBOL_GPL(tdh_mng_init); u64 tdh_vp_rd(struct tdx_vp *vp, u64 field, u64 *data) { struct tdx_module_args args = { - .rcx = tdx_tdvpr_pa(vp), + .rcx = vp->tdvpr_pa, .rdx = field, }; u64 ret; @@ -1771,7 +1766,7 @@ EXPORT_SYMBOL_GPL(tdh_vp_rd); u64 tdh_vp_wr(struct tdx_vp *vp, u64 field, u64 data, u64 mask) { struct tdx_module_args args = { - .rcx = tdx_tdvpr_pa(vp), + .rcx = vp->tdvpr_pa, .rdx = field, .r8 = data, .r9 = mask, @@ -1784,7 +1779,7 @@ EXPORT_SYMBOL_GPL(tdh_vp_wr); u64 tdh_vp_init(struct tdx_vp *vp, u64 initial_rcx, u32 x2apicid) { struct tdx_module_args args = { - .rcx = tdx_tdvpr_pa(vp), + .rcx = vp->tdvpr_pa, .rdx = initial_rcx, .r8 = x2apicid, }; -- 2.34.1 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH] x86/virt/tdx: Use precalculated TDVPR page physical address 2025-09-10 14:44 [PATCH] x86/virt/tdx: Use precalculated TDVPR page physical address Dave Hansen @ 2025-09-10 16:06 ` Kiryl Shutsemau 2025-09-10 16:10 ` Dave Hansen 2025-10-20 13:57 ` Sean Christopherson 1 sibling, 1 reply; 15+ messages in thread From: Kiryl Shutsemau @ 2025-09-10 16:06 UTC (permalink / raw) To: Dave Hansen Cc: linux-kernel, Thomas Gleixner, Ingo Molnar, Borislav Petkov, x86, H. Peter Anvin, Rick Edgecombe, Sean Christopherson, Paolo Bonzini, Kai Huang, Isaku Yamahata, Vishal Annapurve, Thomas Huth, Adrian Hunter, linux-coco, kvm, Farrah Chen On Wed, Sep 10, 2025 at 07:44:53AM -0700, Dave Hansen wrote: > From: Kai Huang <kai.huang@intel.com> > > All of the x86 KVM guest types (VMX, SEV and TDX) do some special context > tracking when entering guests. This means that the actual guest entry > sequence must be noinstr. > > Part of entering a TDX guest is passing a physical address to the TDX > module. Right now, that physical address is stored as a 'struct page' > and converted to a physical address at guest entry. That page=>phys > conversion can be complicated, can vary greatly based on kernel > config, and it is definitely _not_ a noinstr path today. > > There have been a number of tinkering approaches to try and fix this > up, but they all fall down due to some part of the page=>phys > conversion infrastructure not being noinstr friendly. > > Precalculate the page=>phys conversion and store it in the existing > 'tdx_vp' structure. Use the new field at every site that needs a > tdvpr physical address. Remove the now redundant tdx_tdvpr_pa(). > Remove the __flatten remnant from the tinkering. > > Note that only one user of the new field is actually noinstr. All > others can use page_to_phys(). But, they might as well save the effort > since there is a pre-calculated value sitting there for them. > > [ dhansen: rewrite all the text ] > > Signed-off-by: Kai Huang <kai.huang@intel.com> > Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com> > Tested-by: Farrah Chen <farrah.chen@intel.com> Reviewed-by: Kiryl Shutsemau <kas@kernel.org> One nitpick is below. > --- > arch/x86/include/asm/tdx.h | 2 ++ > arch/x86/kvm/vmx/tdx.c | 9 +++++++++ > arch/x86/virt/vmx/tdx/tdx.c | 21 ++++++++------------- > 3 files changed, 19 insertions(+), 13 deletions(-) > > diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h > index 6120461bd5ff3..6b338d7f01b7d 100644 > --- a/arch/x86/include/asm/tdx.h > +++ b/arch/x86/include/asm/tdx.h > @@ -171,6 +171,8 @@ struct tdx_td { > struct tdx_vp { > /* TDVP root page */ > struct page *tdvpr_page; > + /* precalculated page_to_phys(tdvpr_page) for use in noinstr code */ > + phys_addr_t tdvpr_pa; Missing newline above the new field? -- Kiryl Shutsemau / Kirill A. Shutemov ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] x86/virt/tdx: Use precalculated TDVPR page physical address 2025-09-10 16:06 ` Kiryl Shutsemau @ 2025-09-10 16:10 ` Dave Hansen 2025-09-10 16:12 ` Kiryl Shutsemau 0 siblings, 1 reply; 15+ messages in thread From: Dave Hansen @ 2025-09-10 16:10 UTC (permalink / raw) To: Kiryl Shutsemau, Dave Hansen Cc: linux-kernel, Thomas Gleixner, Ingo Molnar, Borislav Petkov, x86, H. Peter Anvin, Rick Edgecombe, Sean Christopherson, Paolo Bonzini, Kai Huang, Isaku Yamahata, Vishal Annapurve, Thomas Huth, Adrian Hunter, linux-coco, kvm, Farrah Chen On 9/10/25 09:06, Kiryl Shutsemau wrote: >> struct tdx_vp { >> /* TDVP root page */ >> struct page *tdvpr_page; >> + /* precalculated page_to_phys(tdvpr_page) for use in noinstr code */ >> + phys_addr_t tdvpr_pa; > Missing newline above the new field? I was actually trying to group the two fields together that are aliases for the same logical thing. Is that problematic? ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] x86/virt/tdx: Use precalculated TDVPR page physical address 2025-09-10 16:10 ` Dave Hansen @ 2025-09-10 16:12 ` Kiryl Shutsemau 2025-09-10 16:57 ` Dave Hansen 0 siblings, 1 reply; 15+ messages in thread From: Kiryl Shutsemau @ 2025-09-10 16:12 UTC (permalink / raw) To: Dave Hansen Cc: Dave Hansen, linux-kernel, Thomas Gleixner, Ingo Molnar, Borislav Petkov, x86, H. Peter Anvin, Rick Edgecombe, Sean Christopherson, Paolo Bonzini, Kai Huang, Isaku Yamahata, Vishal Annapurve, Thomas Huth, Adrian Hunter, linux-coco, kvm, Farrah Chen On Wed, Sep 10, 2025 at 09:10:06AM -0700, Dave Hansen wrote: > On 9/10/25 09:06, Kiryl Shutsemau wrote: > >> struct tdx_vp { > >> /* TDVP root page */ > >> struct page *tdvpr_page; > >> + /* precalculated page_to_phys(tdvpr_page) for use in noinstr code */ > >> + phys_addr_t tdvpr_pa; > > Missing newline above the new field? > > I was actually trying to group the two fields together that are aliases > for the same logical thing. > > Is that problematic? No. Just looks odd to me. But I see 'struct tdx_td' also uses similar style. -- Kiryl Shutsemau / Kirill A. Shutemov ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] x86/virt/tdx: Use precalculated TDVPR page physical address 2025-09-10 16:12 ` Kiryl Shutsemau @ 2025-09-10 16:57 ` Dave Hansen 2025-09-11 15:41 ` Kiryl Shutsemau 0 siblings, 1 reply; 15+ messages in thread From: Dave Hansen @ 2025-09-10 16:57 UTC (permalink / raw) To: Kiryl Shutsemau Cc: Dave Hansen, linux-kernel, Thomas Gleixner, Ingo Molnar, Borislav Petkov, x86, H. Peter Anvin, Rick Edgecombe, Sean Christopherson, Paolo Bonzini, Kai Huang, Isaku Yamahata, Vishal Annapurve, Thomas Huth, Adrian Hunter, linux-coco, kvm, Farrah Chen On 9/10/25 09:12, Kiryl Shutsemau wrote: > On Wed, Sep 10, 2025 at 09:10:06AM -0700, Dave Hansen wrote: >> On 9/10/25 09:06, Kiryl Shutsemau wrote: >>>> struct tdx_vp { >>>> /* TDVP root page */ >>>> struct page *tdvpr_page; >>>> + /* precalculated page_to_phys(tdvpr_page) for use in noinstr code */ >>>> + phys_addr_t tdvpr_pa; >>> Missing newline above the new field? >> I was actually trying to group the two fields together that are aliases >> for the same logical thing. >> >> Is that problematic? > No. Just looks odd to me. But I see 'struct tdx_td' also uses similar > style. Your review or ack tag there seems to have been mangled by your email client. Could you try to resend it, please? ;) ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] x86/virt/tdx: Use precalculated TDVPR page physical address 2025-09-10 16:57 ` Dave Hansen @ 2025-09-11 15:41 ` Kiryl Shutsemau 0 siblings, 0 replies; 15+ messages in thread From: Kiryl Shutsemau @ 2025-09-11 15:41 UTC (permalink / raw) To: Dave Hansen Cc: Dave Hansen, linux-kernel, Thomas Gleixner, Ingo Molnar, Borislav Petkov, x86, H. Peter Anvin, Rick Edgecombe, Sean Christopherson, Paolo Bonzini, Kai Huang, Isaku Yamahata, Vishal Annapurve, Thomas Huth, Adrian Hunter, linux-coco, kvm, Farrah Chen On Wed, Sep 10, 2025 at 09:57:13AM -0700, Dave Hansen wrote: > On 9/10/25 09:12, Kiryl Shutsemau wrote: > > On Wed, Sep 10, 2025 at 09:10:06AM -0700, Dave Hansen wrote: > >> On 9/10/25 09:06, Kiryl Shutsemau wrote: > >>>> struct tdx_vp { > >>>> /* TDVP root page */ > >>>> struct page *tdvpr_page; > >>>> + /* precalculated page_to_phys(tdvpr_page) for use in noinstr code */ > >>>> + phys_addr_t tdvpr_pa; > >>> Missing newline above the new field? > >> I was actually trying to group the two fields together that are aliases > >> for the same logical thing. > >> > >> Is that problematic? > > No. Just looks odd to me. But I see 'struct tdx_td' also uses similar > > style. > > Your review or ack tag there seems to have been mangled by your email > client. Could you try to resend it, please? ;) Do you mean my name spelling? I've decided to move to transliteration from Belarusian rather than Russian. I will update MAINTAINERS and mailmap later. -- Kiryl Shutsemau / Kirill A. Shutemov ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] x86/virt/tdx: Use precalculated TDVPR page physical address 2025-09-10 14:44 [PATCH] x86/virt/tdx: Use precalculated TDVPR page physical address Dave Hansen 2025-09-10 16:06 ` Kiryl Shutsemau @ 2025-10-20 13:57 ` Sean Christopherson 2025-10-20 14:14 ` Dave Hansen 1 sibling, 1 reply; 15+ messages in thread From: Sean Christopherson @ 2025-10-20 13:57 UTC (permalink / raw) To: Dave Hansen Cc: linux-kernel, Thomas Gleixner, Ingo Molnar, Borislav Petkov, x86, H. Peter Anvin, Kirill A. Shutemov, Rick Edgecombe, Paolo Bonzini, Kai Huang, Isaku Yamahata, Vishal Annapurve, Thomas Huth, Adrian Hunter, linux-coco, kvm, Farrah Chen On Wed, Sep 10, 2025, Dave Hansen wrote: > From: Kai Huang <kai.huang@intel.com> > > All of the x86 KVM guest types (VMX, SEV and TDX) do some special context > tracking when entering guests. This means that the actual guest entry > sequence must be noinstr. > > Part of entering a TDX guest is passing a physical address to the TDX > module. Right now, that physical address is stored as a 'struct page' > and converted to a physical address at guest entry. That page=>phys > conversion can be complicated, can vary greatly based on kernel > config, and it is definitely _not_ a noinstr path today. > > There have been a number of tinkering approaches to try and fix this > up, but they all fall down due to some part of the page=>phys > conversion infrastructure not being noinstr friendly. > > Precalculate the page=>phys conversion and store it in the existing > 'tdx_vp' structure. Use the new field at every site that needs a > tdvpr physical address. Remove the now redundant tdx_tdvpr_pa(). > Remove the __flatten remnant from the tinkering. > > Note that only one user of the new field is actually noinstr. All > others can use page_to_phys(). But, they might as well save the effort > since there is a pre-calculated value sitting there for them. > > [ dhansen: rewrite all the text ] > > Signed-off-by: Kai Huang <kai.huang@intel.com> > Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com> > Tested-by: Farrah Chen <farrah.chen@intel.com> > --- > arch/x86/include/asm/tdx.h | 2 ++ > arch/x86/kvm/vmx/tdx.c | 9 +++++++++ > arch/x86/virt/vmx/tdx/tdx.c | 21 ++++++++------------- > 3 files changed, 19 insertions(+), 13 deletions(-) > > diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h > index 6120461bd5ff3..6b338d7f01b7d 100644 > --- a/arch/x86/include/asm/tdx.h > +++ b/arch/x86/include/asm/tdx.h > @@ -171,6 +171,8 @@ struct tdx_td { > struct tdx_vp { > /* TDVP root page */ > struct page *tdvpr_page; > + /* precalculated page_to_phys(tdvpr_page) for use in noinstr code */ > + phys_addr_t tdvpr_pa; > > /* TD vCPU control structure: */ > struct page **tdcx_pages; > diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c > index 04b6d332c1afa..75326a7449cc3 100644 > --- a/arch/x86/kvm/vmx/tdx.c > +++ b/arch/x86/kvm/vmx/tdx.c > @@ -852,6 +852,7 @@ void tdx_vcpu_free(struct kvm_vcpu *vcpu) > if (tdx->vp.tdvpr_page) { > tdx_reclaim_control_page(tdx->vp.tdvpr_page); > tdx->vp.tdvpr_page = 0; > + tdx->vp.tdvpr_pa = 0; '0' is a perfectly legal physical address. And using '0' in the existing code to nullify a pointer is gross. Why do these structures track struct page everywhere? Nothing actually uses the struct page object (except calls to __free_page(). The leaf functions all take a physical address or a virtual address. Track one of those and then use __pa() or __va() to get at the other. Side topic, if you're going to bother tracking the number of pages in each struct despite them being global values, at least reap the benefits of __counted_by(). struct tdx_td { /* TD root structure: */ void *tdr_page; int tdcs_nr_pages; /* TD control structure: */ void *tdcs_pages[] __counted_by(tdcs_nr_pages); }; struct tdx_vp { /* TDVP root page */ void *tdvpr_page; int tdcx_nr_pages; /* TD vCPU control structure: */ void *tdcx_pages[] __counted_by(tdcx_nr_pages); }; ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] x86/virt/tdx: Use precalculated TDVPR page physical address 2025-10-20 13:57 ` Sean Christopherson @ 2025-10-20 14:14 ` Dave Hansen 2025-10-20 14:42 ` Sean Christopherson 0 siblings, 1 reply; 15+ messages in thread From: Dave Hansen @ 2025-10-20 14:14 UTC (permalink / raw) To: Sean Christopherson, Dave Hansen Cc: linux-kernel, Thomas Gleixner, Ingo Molnar, Borislav Petkov, x86, H. Peter Anvin, Kirill A. Shutemov, Rick Edgecombe, Paolo Bonzini, Kai Huang, Isaku Yamahata, Vishal Annapurve, Thomas Huth, Adrian Hunter, linux-coco, kvm, Farrah Chen On 10/20/25 06:57, Sean Christopherson wrote: > Why do these structures track struct page everywhere? I asked for it at some point. It allows an unambiguous reference to normal, (mostly) allocated physical memory. It means that you (mostly) can't accidentally swap in a virtual address, pfn, or something else that's not a physical address into the variable. The TDX ABI is just littered with u64's. There's almost no type safety anywhere. This is one place to bring a wee little bit of order to the chaos. In a perfect world, we'd have sparse annotations for the vaddr, paddr, pfn, dma_addr_t and all the other address spaces. Until then, I like passing struct page around. ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] x86/virt/tdx: Use precalculated TDVPR page physical address 2025-10-20 14:14 ` Dave Hansen @ 2025-10-20 14:42 ` Sean Christopherson 2025-10-20 15:10 ` Dave Hansen 2025-10-29 23:51 ` Dave Hansen 0 siblings, 2 replies; 15+ messages in thread From: Sean Christopherson @ 2025-10-20 14:42 UTC (permalink / raw) To: Dave Hansen Cc: Dave Hansen, linux-kernel, Thomas Gleixner, Ingo Molnar, Borislav Petkov, x86, H. Peter Anvin, Kirill A. Shutemov, Rick Edgecombe, Paolo Bonzini, Kai Huang, Isaku Yamahata, Vishal Annapurve, Thomas Huth, Adrian Hunter, linux-coco, kvm, Farrah Chen On Mon, Oct 20, 2025, Dave Hansen wrote: > On 10/20/25 06:57, Sean Christopherson wrote: > > Why do these structures track struct page everywhere? > > I asked for it at some point. It allows an unambiguous reference to > normal, (mostly) allocated physical memory. It means that you (mostly) > can't accidentally swap in a virtual address, pfn, or something else > that's not a physical address into the variable. > > The TDX ABI is just littered with u64's. There's almost no type safety > anywhere. This is one place to bring a wee little bit of order to the chaos. > > In a perfect world, we'd have sparse annotations for the vaddr, paddr, > pfn, dma_addr_t and all the other address spaces. Until then, I like > passing struct page around. But that clearly doesn't work since now the raw paddr is being passed in many places, and we end up with goofy code like this where one param takes a raw paddr, and another uses page_to_phys(). @@ -1583,7 +1578,7 @@ u64 tdh_vp_addcx(struct tdx_vp *vp, struct page *tdcx_page) { struct tdx_module_args args = { .rcx = page_to_phys(tdcx_page), - .rdx = tdx_tdvpr_pa(vp), + .rdx = vp->tdvpr_pa, }; If some form of type safety is the goal, why not do something like this? typedef void __private *tdx_page_t; Or maybe even define a new address space. # define __tdx __attribute__((noderef, address_space(__tdx))) The effective type safety is limited to sparse, but if you keep the tdx code free of warnings, then any and all warnings from build bots can be treated as "fatal" errors from a maintenance perspective. ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] x86/virt/tdx: Use precalculated TDVPR page physical address 2025-10-20 14:42 ` Sean Christopherson @ 2025-10-20 15:10 ` Dave Hansen 2025-10-20 15:25 ` Sean Christopherson 2025-10-29 23:51 ` Dave Hansen 1 sibling, 1 reply; 15+ messages in thread From: Dave Hansen @ 2025-10-20 15:10 UTC (permalink / raw) To: Sean Christopherson Cc: Dave Hansen, linux-kernel, Thomas Gleixner, Ingo Molnar, Borislav Petkov, x86, H. Peter Anvin, Kirill A. Shutemov, Rick Edgecombe, Paolo Bonzini, Kai Huang, Isaku Yamahata, Vishal Annapurve, Thomas Huth, Adrian Hunter, linux-coco, kvm, Farrah Chen On 10/20/25 07:42, Sean Christopherson wrote: >> In a perfect world, we'd have sparse annotations for the vaddr, paddr, >> pfn, dma_addr_t and all the other address spaces. Until then, I like >> passing struct page around. > But that clearly doesn't work since now the raw paddr is being passed in many > places, and we end up with goofy code like this where one param takes a raw paddr, > and another uses page_to_phys(). > > @@ -1583,7 +1578,7 @@ u64 tdh_vp_addcx(struct tdx_vp *vp, struct page *tdcx_page) > { > struct tdx_module_args args = { > .rcx = page_to_phys(tdcx_page), > - .rdx = tdx_tdvpr_pa(vp), > + .rdx = vp->tdvpr_pa, > }; I'm kinda dense normally and my coffee hasn't kicked in yet. What clearly does not work there? Yeah, vp->tdvpr_pa is storing a physical address as a raw u64 and not a 'struct page'. That's not ideal. But it's also for a pretty good reason. The "use 'struct page *' instead of u64 for physical addresses" thingy is a good pattern, not an absolute rule. Use it when you can, but abandon it for the greater good when necessary. I don't hate the idea of a tdx_page_t. I'm just not sure it's worth the trouble. I'd certainly take a good look at the patches if someone hacked it together. ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] x86/virt/tdx: Use precalculated TDVPR page physical address 2025-10-20 15:10 ` Dave Hansen @ 2025-10-20 15:25 ` Sean Christopherson 2025-10-20 15:43 ` Dave Hansen 0 siblings, 1 reply; 15+ messages in thread From: Sean Christopherson @ 2025-10-20 15:25 UTC (permalink / raw) To: Dave Hansen Cc: Dave Hansen, linux-kernel, Thomas Gleixner, Ingo Molnar, Borislav Petkov, x86, H. Peter Anvin, Kirill A. Shutemov, Rick Edgecombe, Paolo Bonzini, Kai Huang, Isaku Yamahata, Vishal Annapurve, Thomas Huth, Adrian Hunter, linux-coco, kvm, Farrah Chen On Mon, Oct 20, 2025, Dave Hansen wrote: > On 10/20/25 07:42, Sean Christopherson wrote: > >> In a perfect world, we'd have sparse annotations for the vaddr, paddr, > >> pfn, dma_addr_t and all the other address spaces. Until then, I like > >> passing struct page around. > > But that clearly doesn't work since now the raw paddr is being passed in many > > places, and we end up with goofy code like this where one param takes a raw paddr, > > and another uses page_to_phys(). > > > > @@ -1583,7 +1578,7 @@ u64 tdh_vp_addcx(struct tdx_vp *vp, struct page *tdcx_page) > > { > > struct tdx_module_args args = { > > .rcx = page_to_phys(tdcx_page), > > - .rdx = tdx_tdvpr_pa(vp), > > + .rdx = vp->tdvpr_pa, > > }; > > I'm kinda dense normally and my coffee hasn't kicked in yet. What > clearly does not work there? Relying on struct page to provide type safety. > Yeah, vp->tdvpr_pa is storing a physical address as a raw u64 and not a > 'struct page'. That's not ideal. But it's also for a pretty good reason. Right, but my point is that regradless of the justification, every exception to passing a struct page diminishes the benefits of using struct page in the first place. > The "use 'struct page *' instead of u64 for physical addresses" thingy > is a good pattern, not an absolute rule. Use it when you can, but > abandon it for the greater good when necessary. > > I don't hate the idea of a tdx_page_t. I'm just not sure it's worth the > trouble. I'd certainly take a good look at the patches if someone hacked > it together. ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] x86/virt/tdx: Use precalculated TDVPR page physical address 2025-10-20 15:25 ` Sean Christopherson @ 2025-10-20 15:43 ` Dave Hansen 2025-10-21 17:51 ` Sean Christopherson 0 siblings, 1 reply; 15+ messages in thread From: Dave Hansen @ 2025-10-20 15:43 UTC (permalink / raw) To: Sean Christopherson Cc: Dave Hansen, linux-kernel, Thomas Gleixner, Ingo Molnar, Borislav Petkov, x86, H. Peter Anvin, Kirill A. Shutemov, Rick Edgecombe, Paolo Bonzini, Kai Huang, Isaku Yamahata, Vishal Annapurve, Thomas Huth, Adrian Hunter, linux-coco, kvm, Farrah Chen On 10/20/25 08:25, Sean Christopherson wrote: >>> @@ -1583,7 +1578,7 @@ u64 tdh_vp_addcx(struct tdx_vp *vp, struct page *tdcx_page) >>> { >>> struct tdx_module_args args = { >>> .rcx = page_to_phys(tdcx_page), >>> - .rdx = tdx_tdvpr_pa(vp), >>> + .rdx = vp->tdvpr_pa, >>> }; >> I'm kinda dense normally and my coffee hasn't kicked in yet. What >> clearly does not work there? > Relying on struct page to provide type safety. > >> Yeah, vp->tdvpr_pa is storing a physical address as a raw u64 and not a >> 'struct page'. That's not ideal. But it's also for a pretty good reason. > Right, but my point is that regradless of the justification, every exception to > passing a struct page diminishes the benefits of using struct page in the first > place. Yeah, I'm in total agreement with you there. But I don't think there's any type scheme that won't have exceptions or other downsides. u64's are really nice for prototyping because you can just pass those suckers around anywhere and the compiler will never say a thing. But we know the downsides of too many plain integer types getting passed around. Sparse-enforced address spaces are pretty nifty, but they can get messy around the edges of the subsystem where the type is used. You end up with lots of ugly force casts there to bend the compiler to your will. 'struct page *' isn't perfect either. As we saw, you can't get from it to a physical address easily in noinstr code. It doesn't work everywhere either. So I dunno. Sounds like there is no shortage of imperfect ways skin this cat. Yay, engineering! But, seriously, if you're super confident that a sparse-enforced address space is the way to go, it's not *that* hard to go look at it. TDX isn't that big. I can go poke at it for a bit. ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] x86/virt/tdx: Use precalculated TDVPR page physical address 2025-10-20 15:43 ` Dave Hansen @ 2025-10-21 17:51 ` Sean Christopherson 0 siblings, 0 replies; 15+ messages in thread From: Sean Christopherson @ 2025-10-21 17:51 UTC (permalink / raw) To: Dave Hansen Cc: Dave Hansen, linux-kernel, Thomas Gleixner, Ingo Molnar, Borislav Petkov, x86, H. Peter Anvin, Kirill A. Shutemov, Rick Edgecombe, Paolo Bonzini, Kai Huang, Isaku Yamahata, Vishal Annapurve, Thomas Huth, Adrian Hunter, linux-coco, kvm, Farrah Chen On Mon, Oct 20, 2025, Dave Hansen wrote: > On 10/20/25 08:25, Sean Christopherson wrote: > >>> @@ -1583,7 +1578,7 @@ u64 tdh_vp_addcx(struct tdx_vp *vp, struct page *tdcx_page) > >>> { > >>> struct tdx_module_args args = { > >>> .rcx = page_to_phys(tdcx_page), > >>> - .rdx = tdx_tdvpr_pa(vp), > >>> + .rdx = vp->tdvpr_pa, > >>> }; > >> I'm kinda dense normally and my coffee hasn't kicked in yet. What > >> clearly does not work there? > > Relying on struct page to provide type safety. > > > >> Yeah, vp->tdvpr_pa is storing a physical address as a raw u64 and not a > >> 'struct page'. That's not ideal. But it's also for a pretty good reason. > > Right, but my point is that regradless of the justification, every exception to > > passing a struct page diminishes the benefits of using struct page in the first > > place. > > Yeah, I'm in total agreement with you there. > > But I don't think there's any type scheme that won't have exceptions or > other downsides. > > u64's are really nice for prototyping because you can just pass those > suckers around anywhere and the compiler will never say a thing. But we > know the downsides of too many plain integer types getting passed around. > > Sparse-enforced address spaces are pretty nifty, but they can get messy > around the edges of the subsystem where the type is used. You end up > with lots of ugly force casts there to bend the compiler to your will. > > 'struct page *' isn't perfect either. As we saw, you can't get from it > to a physical address easily in noinstr code. It doesn't work everywhere > either. > > So I dunno. Sounds like there is no shortage of imperfect ways skin this > cat. Yay, engineering! > > But, seriously, if you're super confident that a sparse-enforced address Heh, I dunno about "super confident", but I do think it will be the most robust overall, and will be helpful for readers by documenting which pages/assets are effectively opaque handles things that are owned by the TDX-Module. KVM uses the sparse approach in KVM's TDP MMU implementation to typedef PTE pointers, which are RCU-protected. typedef u64 __rcu *tdp_ptep_t; There are handful of one open-coded rcu_dereference() calls, but the vast majority of dereferences get routed through helpers that deal with the gory details. And of the open-coded calls, I distinctly remember two being interesting cases where the __rcu enforcement forced us to slow down and think about exactly the lifetime of the PTE. I.e. even the mildly painful "overhead" has been a net positive. > space is the way to go, it's not *that* hard to go look at it. TDX isn't > that big. I can go poke at it for a bit. ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] x86/virt/tdx: Use precalculated TDVPR page physical address 2025-10-20 14:42 ` Sean Christopherson 2025-10-20 15:10 ` Dave Hansen @ 2025-10-29 23:51 ` Dave Hansen 2025-10-30 15:42 ` Sean Christopherson 1 sibling, 1 reply; 15+ messages in thread From: Dave Hansen @ 2025-10-29 23:51 UTC (permalink / raw) To: Sean Christopherson Cc: Dave Hansen, linux-kernel, Thomas Gleixner, Ingo Molnar, Borislav Petkov, x86, H. Peter Anvin, Kirill A. Shutemov, Rick Edgecombe, Paolo Bonzini, Kai Huang, Isaku Yamahata, Vishal Annapurve, Thomas Huth, Adrian Hunter, linux-coco, kvm, Farrah Chen [-- Attachment #1: Type: text/plain, Size: 1600 bytes --] On 10/20/25 07:42, Sean Christopherson wrote: ... > If some form of type safety is the goal, why not do something like this? > > typedef void __private *tdx_page_t; > > Or maybe even define a new address space. > > # define __tdx __attribute__((noderef, address_space(__tdx))) Sean, I hacked up a TDX physical address namespace for sparse. It's not awful. It doesn't make the .c files any uglier (or prettier really). It definitely adds code because it needs a handful of conversion functions. But those are all one-liner functions. Net, this approach seems to add a few conversion functions versus the 'struct page' approach. That's because there are at least a couple of places that *need* a 'struct page' like tdx_unpin(). There's some wonkiness in this like using virtual addresses to back the "paddr" type. I did that so we could still do NULL checks instead of keeping some explicit "invalid paddr" value. It's hidden in the helpers and not exposed to the users, but it is weird for sure. The important part isn't what the type is in the end, it's that something is making it opaque. This can definitely be taken further like getting rid of tdx->vp.tdvpr_pa precalcuation. But it's mostly a straight s/struct page */tdx_paddr_t/ replacement. I'm not looking at this and jumping up and down for how much better it makes the code. It certainly *can* find a few things by leveraging sparse. But, honestly, after seeing that nobody runs or cares about sparse on this code, it's hard to take it seriously. Was this generally what you had in mind? Should I turn this into a real series? [-- Attachment #2: tdx-sparse.patch --] [-- Type: text/x-patch, Size: 20766 bytes --] diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h index 6b338d7f01b7d..644b53bcfdfed 100644 --- a/arch/x86/include/asm/tdx.h +++ b/arch/x86/include/asm/tdx.h @@ -37,6 +37,7 @@ #include <uapi/asm/mce.h> #include <asm/tdx_global_metadata.h> #include <linux/pgtable.h> +#include <linux/mm.h> /* * Used by the #VE exception handler to gather the #VE exception @@ -154,15 +155,61 @@ int tdx_guest_keyid_alloc(void); u32 tdx_get_nr_guest_keyids(void); void tdx_guest_keyid_free(unsigned int keyid); -void tdx_quirk_reset_page(struct page *page); +struct tdx_paddr; +#if defined(__CHECKER__) +#define __tdx __attribute__((noderef, address_space(__tdx))) +#else +#define __tdx +#endif +typedef struct tdx_paddr __tdx * tdx_paddr_t; + +static inline tdx_paddr_t tdx_alloc_page(gfp_t gfp_flags) +{ + return (__force tdx_paddr_t)kmalloc(PAGE_SIZE, gfp_flags); +} + +static inline void tdx_free_page(tdx_paddr_t paddr) +{ + kfree((__force void *)paddr); +} + +static inline phys_addr_t tdx_paddr_to_phys(tdx_paddr_t paddr) +{ + // tdx_paddr_t is actually a virtual address to kernel memory: + return __pa((__force void *)paddr); +} + +static inline struct page *tdx_paddr_to_page(tdx_paddr_t paddr) +{ + // tdx_paddr_t is actually a virtual address to kernel memory: + return virt_to_page((__force void *)paddr); +} + +static inline tdx_paddr_t tdx_page_to_paddr(struct page *page) +{ + return (__force tdx_paddr_t)page_to_virt(page); +} + +static inline tdx_paddr_t tdx_virt_to_paddr(void *vaddr) +{ + return (__force tdx_paddr_t)vaddr; +} + +void tdx_quirk_reset_page(tdx_paddr_t paddr); + +static inline struct page *tdx_paddr_to_virt(tdx_paddr_t paddr) +{ + // tdx_paddr_t is actually a virtual address to kernel memory: + return (__force void *)paddr; +} struct tdx_td { /* TD root structure: */ - struct page *tdr_page; + tdx_paddr_t tdr_page; int tdcs_nr_pages; /* TD control structure: */ - struct page **tdcs_pages; + tdx_paddr_t *tdcs_pages; /* Size of `tdcx_pages` in struct tdx_vp */ int tdcx_nr_pages; @@ -170,19 +217,19 @@ struct tdx_td { struct tdx_vp { /* TDVP root page */ - struct page *tdvpr_page; + tdx_paddr_t tdvpr_page; /* precalculated page_to_phys(tdvpr_page) for use in noinstr code */ phys_addr_t tdvpr_pa; /* TD vCPU control structure: */ - struct page **tdcx_pages; + tdx_paddr_t *tdcx_pages; }; -static inline u64 mk_keyed_paddr(u16 hkid, struct page *page) +static inline u64 mk_keyed_paddr(u16 hkid, tdx_paddr_t paddr) { u64 ret; - ret = page_to_phys(page); + ret = tdx_paddr_to_phys(paddr); /* KeyID bits are just above the physical address bits: */ ret |= (u64)hkid << boot_cpu_data.x86_phys_bits; @@ -196,11 +243,11 @@ static inline int pg_level_to_tdx_sept_level(enum pg_level level) } u64 tdh_vp_enter(struct tdx_vp *vp, struct tdx_module_args *args); -u64 tdh_mng_addcx(struct tdx_td *td, struct page *tdcs_page); -u64 tdh_mem_page_add(struct tdx_td *td, u64 gpa, struct page *page, struct page *source, u64 *ext_err1, u64 *ext_err2); -u64 tdh_mem_sept_add(struct tdx_td *td, u64 gpa, int level, struct page *page, u64 *ext_err1, u64 *ext_err2); -u64 tdh_vp_addcx(struct tdx_vp *vp, struct page *tdcx_page); -u64 tdh_mem_page_aug(struct tdx_td *td, u64 gpa, int level, struct page *page, u64 *ext_err1, u64 *ext_err2); +u64 tdh_mng_addcx(struct tdx_td *td, tdx_paddr_t tdcs_page); +u64 tdh_mem_page_add(struct tdx_td *td, u64 gpa, tdx_paddr_t paddr, tdx_paddr_t source, u64 *ext_err1, u64 *ext_err2); +u64 tdh_mem_sept_add(struct tdx_td *td, u64 gpa, int level, tdx_paddr_t paddr, u64 *ext_err1, u64 *ext_err2); +u64 tdh_vp_addcx(struct tdx_vp *vp, tdx_paddr_t tdcx_page); +u64 tdh_mem_page_aug(struct tdx_td *td, u64 gpa, int level, tdx_paddr_t paddr, u64 *ext_err1, u64 *ext_err2); u64 tdh_mem_range_block(struct tdx_td *td, u64 gpa, int level, u64 *ext_err1, u64 *ext_err2); u64 tdh_mng_key_config(struct tdx_td *td); u64 tdh_mng_create(struct tdx_td *td, u16 hkid); @@ -215,12 +262,12 @@ u64 tdh_mng_init(struct tdx_td *td, u64 td_params, u64 *extended_err); u64 tdh_vp_init(struct tdx_vp *vp, u64 initial_rcx, u32 x2apicid); u64 tdh_vp_rd(struct tdx_vp *vp, u64 field, u64 *data); u64 tdh_vp_wr(struct tdx_vp *vp, u64 field, u64 data, u64 mask); -u64 tdh_phymem_page_reclaim(struct page *page, u64 *tdx_pt, u64 *tdx_owner, u64 *tdx_size); +u64 tdh_phymem_page_reclaim(tdx_paddr_t paddr, u64 *tdx_pt, u64 *tdx_owner, u64 *tdx_size); u64 tdh_mem_track(struct tdx_td *tdr); u64 tdh_mem_page_remove(struct tdx_td *td, u64 gpa, u64 level, u64 *ext_err1, u64 *ext_err2); u64 tdh_phymem_cache_wb(bool resume); u64 tdh_phymem_page_wbinvd_tdr(struct tdx_td *td); -u64 tdh_phymem_page_wbinvd_hkid(u64 hkid, struct page *page); +u64 tdh_phymem_page_wbinvd_hkid(u64 hkid, tdx_paddr_t paddr); #else static inline void tdx_init(void) { } static inline int tdx_cpu_enable(void) { return -ENODEV; } diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index 0a49c863c811b..09abc0f6b27cb 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -302,11 +302,11 @@ static void tdx_no_vcpus_enter_stop(struct kvm *kvm) } /* TDH.PHYMEM.PAGE.RECLAIM is allowed only when destroying the TD. */ -static int __tdx_reclaim_page(struct page *page) +static int __tdx_reclaim_page(tdx_paddr_t paddr) { u64 err, rcx, rdx, r8; - err = tdh_phymem_page_reclaim(page, &rcx, &rdx, &r8); + err = tdh_phymem_page_reclaim(paddr, &rcx, &rdx, &r8); /* * No need to check for TDX_OPERAND_BUSY; all TD pages are freed @@ -320,13 +320,13 @@ static int __tdx_reclaim_page(struct page *page) return 0; } -static int tdx_reclaim_page(struct page *page) +static int tdx_reclaim_page(tdx_paddr_t paddr) { int r; - r = __tdx_reclaim_page(page); + r = __tdx_reclaim_page(paddr); if (!r) - tdx_quirk_reset_page(page); + tdx_quirk_reset_page(paddr); return r; } @@ -336,7 +336,7 @@ static int tdx_reclaim_page(struct page *page) * private KeyID. Assume the cache associated with the TDX private KeyID has * been flushed. */ -static void tdx_reclaim_control_page(struct page *ctrl_page) +static void tdx_reclaim_control_page(tdx_paddr_t ctrl_page) { /* * Leak the page if the kernel failed to reclaim the page. @@ -345,7 +345,7 @@ static void tdx_reclaim_control_page(struct page *ctrl_page) if (tdx_reclaim_page(ctrl_page)) return; - __free_page(ctrl_page); + __free_page(tdx_paddr_to_page(ctrl_page)); } struct tdx_flush_vp_arg { @@ -586,7 +586,7 @@ static void tdx_reclaim_td_control_pages(struct kvm *kvm) } tdx_quirk_reset_page(kvm_tdx->td.tdr_page); - __free_page(kvm_tdx->td.tdr_page); + tdx_free_page(kvm_tdx->td.tdr_page); kvm_tdx->td.tdr_page = NULL; } @@ -856,7 +856,7 @@ void tdx_vcpu_free(struct kvm_vcpu *vcpu) } if (tdx->vp.tdvpr_page) { tdx_reclaim_control_page(tdx->vp.tdvpr_page); - tdx->vp.tdvpr_page = 0; + tdx->vp.tdvpr_page = NULL; tdx->vp.tdvpr_pa = 0; } @@ -1583,13 +1583,13 @@ void tdx_load_mmu_pgd(struct kvm_vcpu *vcpu, hpa_t root_hpa, int pgd_level) td_vmcs_write64(to_tdx(vcpu), SHARED_EPT_POINTER, root_hpa); } -static void tdx_unpin(struct kvm *kvm, struct page *page) +static void tdx_unpin(struct kvm *kvm, tdx_paddr_t paddr) { - put_page(page); + put_page(tdx_paddr_to_page(paddr)); } static int tdx_mem_page_aug(struct kvm *kvm, gfn_t gfn, - enum pg_level level, struct page *page) + enum pg_level level, tdx_paddr_t paddr) { int tdx_level = pg_level_to_tdx_sept_level(level); struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm); @@ -1597,15 +1597,15 @@ static int tdx_mem_page_aug(struct kvm *kvm, gfn_t gfn, u64 entry, level_state; u64 err; - err = tdh_mem_page_aug(&kvm_tdx->td, gpa, tdx_level, page, &entry, &level_state); + err = tdh_mem_page_aug(&kvm_tdx->td, gpa, tdx_level, paddr, &entry, &level_state); if (unlikely(tdx_operand_busy(err))) { - tdx_unpin(kvm, page); + tdx_unpin(kvm, paddr); return -EBUSY; } if (KVM_BUG_ON(err, kvm)) { pr_tdx_error_2(TDH_MEM_PAGE_AUG, err, entry, level_state); - tdx_unpin(kvm, page); + tdx_unpin(kvm, paddr); return -EIO; } @@ -1661,13 +1661,13 @@ static int tdx_sept_set_private_spte(struct kvm *kvm, gfn_t gfn, */ smp_rmb(); if (likely(kvm_tdx->state == TD_STATE_RUNNABLE)) - return tdx_mem_page_aug(kvm, gfn, level, page); + return tdx_mem_page_aug(kvm, gfn, level, tdx_page_to_paddr(page)); return tdx_mem_page_record_premap_cnt(kvm, gfn, level, pfn); } static int tdx_sept_drop_private_spte(struct kvm *kvm, gfn_t gfn, - enum pg_level level, struct page *page) + enum pg_level level, tdx_paddr_t paddr) { int tdx_level = pg_level_to_tdx_sept_level(level); struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm); @@ -1705,14 +1705,14 @@ static int tdx_sept_drop_private_spte(struct kvm *kvm, gfn_t gfn, return -EIO; } - err = tdh_phymem_page_wbinvd_hkid((u16)kvm_tdx->hkid, page); + err = tdh_phymem_page_wbinvd_hkid((u16)kvm_tdx->hkid, paddr); if (KVM_BUG_ON(err, kvm)) { pr_tdx_error(TDH_PHYMEM_PAGE_WBINVD, err); return -EIO; } - tdx_quirk_reset_page(page); - tdx_unpin(kvm, page); + tdx_quirk_reset_page(paddr); + tdx_unpin(kvm, paddr); return 0; } @@ -1721,10 +1721,10 @@ static int tdx_sept_link_private_spt(struct kvm *kvm, gfn_t gfn, { int tdx_level = pg_level_to_tdx_sept_level(level); gpa_t gpa = gfn_to_gpa(gfn); - struct page *page = virt_to_page(private_spt); + tdx_paddr_t paddr = tdx_virt_to_paddr(private_spt); u64 err, entry, level_state; - err = tdh_mem_sept_add(&to_kvm_tdx(kvm)->td, gpa, tdx_level, page, &entry, + err = tdh_mem_sept_add(&to_kvm_tdx(kvm)->td, gpa, tdx_level, paddr, &entry, &level_state); if (unlikely(tdx_operand_busy(err))) return -EBUSY; @@ -1771,7 +1771,7 @@ static int tdx_is_sept_zap_err_due_to_premap(struct kvm_tdx *kvm_tdx, u64 err, } static int tdx_sept_zap_private_spte(struct kvm *kvm, gfn_t gfn, - enum pg_level level, struct page *page) + enum pg_level level, tdx_paddr_t paddr) { int tdx_level = pg_level_to_tdx_sept_level(level); struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm); @@ -1792,7 +1792,7 @@ static int tdx_sept_zap_private_spte(struct kvm *kvm, gfn_t gfn, if (tdx_is_sept_zap_err_due_to_premap(kvm_tdx, err, entry, level) && !KVM_BUG_ON(!atomic64_read(&kvm_tdx->nr_premapped), kvm)) { atomic64_dec(&kvm_tdx->nr_premapped); - tdx_unpin(kvm, page); + tdx_unpin(kvm, paddr); return 0; } @@ -1872,13 +1872,13 @@ static int tdx_sept_free_private_spt(struct kvm *kvm, gfn_t gfn, * The HKID assigned to this TD was already freed and cache was * already flushed. We don't have to flush again. */ - return tdx_reclaim_page(virt_to_page(private_spt)); + return tdx_reclaim_page(tdx_virt_to_paddr(private_spt)); } static int tdx_sept_remove_private_spte(struct kvm *kvm, gfn_t gfn, enum pg_level level, kvm_pfn_t pfn) { - struct page *page = pfn_to_page(pfn); + tdx_paddr_t paddr = tdx_page_to_paddr(pfn_to_page(pfn)); int ret; /* @@ -1889,7 +1889,7 @@ static int tdx_sept_remove_private_spte(struct kvm *kvm, gfn_t gfn, if (KVM_BUG_ON(!is_hkid_assigned(to_kvm_tdx(kvm)), kvm)) return -EINVAL; - ret = tdx_sept_zap_private_spte(kvm, gfn, level, page); + ret = tdx_sept_zap_private_spte(kvm, gfn, level, paddr); if (ret <= 0) return ret; @@ -1899,7 +1899,7 @@ static int tdx_sept_remove_private_spte(struct kvm *kvm, gfn_t gfn, */ tdx_track(kvm); - return tdx_sept_drop_private_spte(kvm, gfn, level, page); + return tdx_sept_drop_private_spte(kvm, gfn, level, paddr); } void tdx_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode, @@ -2461,8 +2461,8 @@ static int __tdx_td_init(struct kvm *kvm, struct td_params *td_params, { struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm); cpumask_var_t packages; - struct page **tdcs_pages = NULL; - struct page *tdr_page; + tdx_paddr_t *tdcs_pages = NULL; + tdx_paddr_t tdr_page; int ret, i; u64 err, rcx; @@ -2480,7 +2480,7 @@ static int __tdx_td_init(struct kvm *kvm, struct td_params *td_params, atomic_inc(&nr_configured_hkid); - tdr_page = alloc_page(GFP_KERNEL); + tdr_page = tdx_alloc_page(GFP_KERNEL); if (!tdr_page) goto free_hkid; @@ -2493,7 +2493,7 @@ static int __tdx_td_init(struct kvm *kvm, struct td_params *td_params, goto free_tdr; for (i = 0; i < kvm_tdx->td.tdcs_nr_pages; i++) { - tdcs_pages[i] = alloc_page(GFP_KERNEL); + tdcs_pages[i] = tdx_alloc_page(GFP_KERNEL); if (!tdcs_pages[i]) goto free_tdcs; } @@ -2615,7 +2615,7 @@ static int __tdx_td_init(struct kvm *kvm, struct td_params *td_params, /* Only free pages not yet added, so start at 'i' */ for (; i < kvm_tdx->td.tdcs_nr_pages; i++) { if (tdcs_pages[i]) { - __free_page(tdcs_pages[i]); + tdx_free_page(tdcs_pages[i]); tdcs_pages[i] = NULL; } } @@ -2634,15 +2634,15 @@ static int __tdx_td_init(struct kvm *kvm, struct td_params *td_params, free_tdcs: for (i = 0; i < kvm_tdx->td.tdcs_nr_pages; i++) { if (tdcs_pages[i]) - __free_page(tdcs_pages[i]); + tdx_free_page(tdcs_pages[i]); } kfree(tdcs_pages); kvm_tdx->td.tdcs_pages = NULL; free_tdr: if (tdr_page) - __free_page(tdr_page); - kvm_tdx->td.tdr_page = 0; + tdx_free_page(tdr_page); + kvm_tdx->td.tdr_page = NULL; free_hkid: tdx_hkid_free(kvm_tdx); @@ -2939,11 +2939,11 @@ static int tdx_td_vcpu_init(struct kvm_vcpu *vcpu, u64 vcpu_rcx) { struct kvm_tdx *kvm_tdx = to_kvm_tdx(vcpu->kvm); struct vcpu_tdx *tdx = to_tdx(vcpu); - struct page *page; + tdx_paddr_t page; int ret, i; u64 err; - page = alloc_page(GFP_KERNEL); + page = tdx_alloc_page(GFP_KERNEL); if (!page) return -ENOMEM; tdx->vp.tdvpr_page = page; @@ -2953,7 +2953,7 @@ static int tdx_td_vcpu_init(struct kvm_vcpu *vcpu, u64 vcpu_rcx) * entry via tdh_vp_enter(). Precalculate and store it instead * of doing it at runtime later. */ - tdx->vp.tdvpr_pa = page_to_phys(tdx->vp.tdvpr_page); + tdx->vp.tdvpr_pa = tdx_paddr_to_phys(tdx->vp.tdvpr_page); tdx->vp.tdcx_pages = kcalloc(kvm_tdx->td.tdcx_nr_pages, sizeof(*tdx->vp.tdcx_pages), GFP_KERNEL); @@ -2963,7 +2963,7 @@ static int tdx_td_vcpu_init(struct kvm_vcpu *vcpu, u64 vcpu_rcx) } for (i = 0; i < kvm_tdx->td.tdcx_nr_pages; i++) { - page = alloc_page(GFP_KERNEL); + page = tdx_alloc_page(GFP_KERNEL); if (!page) { ret = -ENOMEM; goto free_tdcx; @@ -2987,7 +2987,7 @@ static int tdx_td_vcpu_init(struct kvm_vcpu *vcpu, u64 vcpu_rcx) * method, but the rest are freed here. */ for (; i < kvm_tdx->td.tdcx_nr_pages; i++) { - __free_page(tdx->vp.tdcx_pages[i]); + tdx_free_page(tdx->vp.tdcx_pages[i]); tdx->vp.tdcx_pages[i] = NULL; } return -EIO; @@ -3007,7 +3007,7 @@ static int tdx_td_vcpu_init(struct kvm_vcpu *vcpu, u64 vcpu_rcx) free_tdcx: for (i = 0; i < kvm_tdx->td.tdcx_nr_pages; i++) { if (tdx->vp.tdcx_pages[i]) - __free_page(tdx->vp.tdcx_pages[i]); + tdx_free_page(tdx->vp.tdcx_pages[i]); tdx->vp.tdcx_pages[i] = NULL; } kfree(tdx->vp.tdcx_pages); @@ -3015,8 +3015,8 @@ static int tdx_td_vcpu_init(struct kvm_vcpu *vcpu, u64 vcpu_rcx) free_tdvpr: if (tdx->vp.tdvpr_page) - __free_page(tdx->vp.tdvpr_page); - tdx->vp.tdvpr_page = 0; + tdx_free_page(tdx->vp.tdvpr_page); + tdx->vp.tdvpr_page = NULL; tdx->vp.tdvpr_pa = 0; return ret; @@ -3054,7 +3054,8 @@ static int tdx_vcpu_get_cpuid_leaf(struct kvm_vcpu *vcpu, u32 leaf, int *entry_i static int tdx_vcpu_get_cpuid(struct kvm_vcpu *vcpu, struct kvm_tdx_cmd *cmd) { - struct kvm_cpuid2 __user *output, *td_cpuid; + struct kvm_cpuid2 __user *output; + struct kvm_cpuid2 *td_cpuid; int r = 0, i = 0, leaf; u32 level; @@ -3206,8 +3207,8 @@ static int tdx_gmem_post_populate(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn, } ret = 0; - err = tdh_mem_page_add(&kvm_tdx->td, gpa, pfn_to_page(pfn), - src_page, &entry, &level_state); + err = tdh_mem_page_add(&kvm_tdx->td, gpa, tdx_page_to_paddr(phys_to_page(PFN_PHYS(pfn))), + tdx_page_to_paddr(src_page), &entry, &level_state); if (err) { ret = unlikely(tdx_operand_busy(err)) ? -EBUSY : -EIO; goto out; diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c index eac4032484626..b55bbd38e5e1f 100644 --- a/arch/x86/virt/vmx/tdx/tdx.c +++ b/arch/x86/virt/vmx/tdx/tdx.c @@ -658,9 +658,9 @@ static void tdx_quirk_reset_paddr(unsigned long base, unsigned long size) mb(); } -void tdx_quirk_reset_page(struct page *page) +void tdx_quirk_reset_page(tdx_paddr_t paddr) { - tdx_quirk_reset_paddr(page_to_phys(page), PAGE_SIZE); + tdx_quirk_reset_paddr(tdx_paddr_to_phys(paddr), PAGE_SIZE); } EXPORT_SYMBOL_GPL(tdx_quirk_reset_page); @@ -1501,7 +1501,7 @@ EXPORT_SYMBOL_GPL(tdx_guest_keyid_free); static inline u64 tdx_tdr_pa(struct tdx_td *td) { - return page_to_phys(td->tdr_page); + return tdx_paddr_to_phys(td->tdr_page); } /* @@ -1510,9 +1510,9 @@ static inline u64 tdx_tdr_pa(struct tdx_td *td) * Be conservative and make the code simpler by doing the CLFLUSH * unconditionally. */ -static void tdx_clflush_page(struct page *page) +static void tdx_clflush_page(tdx_paddr_t paddr) { - clflush_cache_range(page_to_virt(page), PAGE_SIZE); + clflush_cache_range(tdx_paddr_to_virt(paddr), PAGE_SIZE); } noinstr u64 tdh_vp_enter(struct tdx_vp *td, struct tdx_module_args *args) @@ -1523,10 +1523,10 @@ noinstr u64 tdh_vp_enter(struct tdx_vp *td, struct tdx_module_args *args) } EXPORT_SYMBOL_GPL(tdh_vp_enter); -u64 tdh_mng_addcx(struct tdx_td *td, struct page *tdcs_page) +u64 tdh_mng_addcx(struct tdx_td *td, tdx_paddr_t tdcs_page) { struct tdx_module_args args = { - .rcx = page_to_phys(tdcs_page), + .rcx = tdx_paddr_to_phys(tdcs_page), .rdx = tdx_tdr_pa(td), }; @@ -1535,13 +1535,13 @@ u64 tdh_mng_addcx(struct tdx_td *td, struct page *tdcs_page) } EXPORT_SYMBOL_GPL(tdh_mng_addcx); -u64 tdh_mem_page_add(struct tdx_td *td, u64 gpa, struct page *page, struct page *source, u64 *ext_err1, u64 *ext_err2) +u64 tdh_mem_page_add(struct tdx_td *td, u64 gpa, tdx_paddr_t page, tdx_paddr_t source, u64 *ext_err1, u64 *ext_err2) { struct tdx_module_args args = { .rcx = gpa, .rdx = tdx_tdr_pa(td), - .r8 = page_to_phys(page), - .r9 = page_to_phys(source), + .r8 = tdx_paddr_to_phys(page), + .r9 = tdx_paddr_to_phys(source), }; u64 ret; @@ -1555,12 +1555,12 @@ u64 tdh_mem_page_add(struct tdx_td *td, u64 gpa, struct page *page, struct page } EXPORT_SYMBOL_GPL(tdh_mem_page_add); -u64 tdh_mem_sept_add(struct tdx_td *td, u64 gpa, int level, struct page *page, u64 *ext_err1, u64 *ext_err2) +u64 tdh_mem_sept_add(struct tdx_td *td, u64 gpa, int level, tdx_paddr_t page, u64 *ext_err1, u64 *ext_err2) { struct tdx_module_args args = { .rcx = gpa | level, .rdx = tdx_tdr_pa(td), - .r8 = page_to_phys(page), + .r8 = tdx_paddr_to_phys(page), }; u64 ret; @@ -1574,10 +1574,10 @@ u64 tdh_mem_sept_add(struct tdx_td *td, u64 gpa, int level, struct page *page, u } EXPORT_SYMBOL_GPL(tdh_mem_sept_add); -u64 tdh_vp_addcx(struct tdx_vp *vp, struct page *tdcx_page) +u64 tdh_vp_addcx(struct tdx_vp *vp, tdx_paddr_t tdcx_page) { struct tdx_module_args args = { - .rcx = page_to_phys(tdcx_page), + .rcx = tdx_paddr_to_phys(tdcx_page), .rdx = vp->tdvpr_pa, }; @@ -1586,12 +1586,12 @@ u64 tdh_vp_addcx(struct tdx_vp *vp, struct page *tdcx_page) } EXPORT_SYMBOL_GPL(tdh_vp_addcx); -u64 tdh_mem_page_aug(struct tdx_td *td, u64 gpa, int level, struct page *page, u64 *ext_err1, u64 *ext_err2) +u64 tdh_mem_page_aug(struct tdx_td *td, u64 gpa, int level, tdx_paddr_t page, u64 *ext_err1, u64 *ext_err2) { struct tdx_module_args args = { .rcx = gpa | level, .rdx = tdx_tdr_pa(td), - .r8 = page_to_phys(page), + .r8 = tdx_paddr_to_phys(page), }; u64 ret; @@ -1794,10 +1794,10 @@ EXPORT_SYMBOL_GPL(tdh_vp_init); * So despite the names, they must be interpted specially as described by the spec. Return * them only for error reporting purposes. */ -u64 tdh_phymem_page_reclaim(struct page *page, u64 *tdx_pt, u64 *tdx_owner, u64 *tdx_size) +u64 tdh_phymem_page_reclaim(tdx_paddr_t page, u64 *tdx_pt, u64 *tdx_owner, u64 *tdx_size) { struct tdx_module_args args = { - .rcx = page_to_phys(page), + .rcx = tdx_paddr_to_phys(page), }; u64 ret; @@ -1858,11 +1858,11 @@ u64 tdh_phymem_page_wbinvd_tdr(struct tdx_td *td) } EXPORT_SYMBOL_GPL(tdh_phymem_page_wbinvd_tdr); -u64 tdh_phymem_page_wbinvd_hkid(u64 hkid, struct page *page) +u64 tdh_phymem_page_wbinvd_hkid(u64 hkid, tdx_paddr_t paddr) { struct tdx_module_args args = {}; - args.rcx = mk_keyed_paddr(hkid, page); + args.rcx = mk_keyed_paddr(hkid, paddr); return seamcall(TDH_PHYMEM_PAGE_WBINVD, &args); } ^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH] x86/virt/tdx: Use precalculated TDVPR page physical address 2025-10-29 23:51 ` Dave Hansen @ 2025-10-30 15:42 ` Sean Christopherson 0 siblings, 0 replies; 15+ messages in thread From: Sean Christopherson @ 2025-10-30 15:42 UTC (permalink / raw) To: Dave Hansen Cc: Dave Hansen, linux-kernel, Thomas Gleixner, Ingo Molnar, Borislav Petkov, x86, H. Peter Anvin, Kirill A. Shutemov, Rick Edgecombe, Paolo Bonzini, Kai Huang, Isaku Yamahata, Vishal Annapurve, Thomas Huth, Adrian Hunter, linux-coco, kvm, Farrah Chen On Wed, Oct 29, 2025, Dave Hansen wrote: > On 10/20/25 07:42, Sean Christopherson wrote: > ... > > If some form of type safety is the goal, why not do something like this? > > > > typedef void __private *tdx_page_t; > > > > Or maybe even define a new address space. > > > > # define __tdx __attribute__((noderef, address_space(__tdx))) > > Sean, > > I hacked up a TDX physical address namespace for sparse. It's not awful. > It doesn't make the .c files any uglier (or prettier really). It > definitely adds code because it needs a handful of conversion functions. > But those are all one-liner functions. > > Net, this approach seems to add a few conversion functions versus the > 'struct page' approach. That's because there are at least a couple of > places that *need* a 'struct page' like tdx_unpin(). tdx_unpin() is going away[*] in v6.19 (hopefully; I'll post the next version today). In fact, that rework eliminates a handful of the helpers that are needed, and in general can help clean things up. [*] https://lore.kernel.org/all/20251017003244.186495-8-seanjc@google.com > There's some wonkiness in this like using virtual addresses to back the > "paddr" type. I did that so we could still do NULL checks instead of I really, really, REALLY don't like the tdx_paddr_t nomenclature. It's obviously not a physical address, KVM uses "paddr" to track actual physical address (and does so heavily in selftests), and I don't like that it drops the "page" aspect of things, i.e. loses the detail that the TDX-Module _only_ works with 4KiB pages. IMO, "tdx_page_t page" is a much better fit, as it's roughly analogous to "struct page *page", and which should be a familiar pattern for readers and thus more intuitive. > keeping some explicit "invalid paddr" value. It's hidden in the helpers > and not exposed to the users, but it is weird for sure. The important > part isn't what the type is in the end, it's that something is making it > opaque. > > This can definitely be taken further like getting rid of > tdx->vp.tdvpr_pa precalcuation. But it's mostly a straight s/struct page > */tdx_paddr_t/ replacement. > > I'm not looking at this and jumping up and down for how much better it > makes the code. It certainly *can* find a few things by leveraging > sparse. But, honestly, after seeing that nobody runs or cares about > sparse on this code, it's hard to take it seriously. Yeah, utilizing sparse can be difficult, because it's so noisy. My tactic is to rely on the bots to detect _new_ warnings, but for whateer reason that approach didn't work for TDX, probably because so much code came in all at once. In theory, if we get the initial support right, then the bots will help keep things clean. > Was this generally what you had in mind? Should I turn this into a real > series? More or less, ya. > diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h > index 6b338d7f01b7d..644b53bcfdfed 100644 > --- a/arch/x86/include/asm/tdx.h > +++ b/arch/x86/include/asm/tdx.h > @@ -37,6 +37,7 @@ > #include <uapi/asm/mce.h> > #include <asm/tdx_global_metadata.h> > #include <linux/pgtable.h> > +#include <linux/mm.h> > > /* > * Used by the #VE exception handler to gather the #VE exception > @@ -154,15 +155,61 @@ int tdx_guest_keyid_alloc(void); > u32 tdx_get_nr_guest_keyids(void); > void tdx_guest_keyid_free(unsigned int keyid); > > -void tdx_quirk_reset_page(struct page *page); > +struct tdx_paddr; Maybe "struct tdx_module_page", to hint to readers that these pages are ultimately used by the TDX-Module? Not sure if that's better or worse than e.g. "struct tdx_page". > +#if defined(__CHECKER__) > +#define __tdx __attribute__((noderef, address_space(__tdx))) > +#else > +#define __tdx > +#endif > +typedef struct tdx_paddr __tdx * tdx_paddr_t; > + > +static inline tdx_paddr_t tdx_alloc_page(gfp_t gfp_flags) > +{ > + return (__force tdx_paddr_t)kmalloc(PAGE_SIZE, gfp_flags); Is it worth going through alloc_page()? I'm not entirely clear on whether there's a meaningful difference in the allocator. If so, this can be: struct page *page = alloc_page(gfp_flags); return (__force tdx_page_t)(page ? page_to_virt(page) : NULL); > +} > + > +static inline void tdx_free_page(tdx_paddr_t paddr) > +{ > + kfree((__force void *)paddr); > +} > + > +static inline phys_addr_t tdx_paddr_to_phys(tdx_paddr_t paddr) > +{ > + // tdx_paddr_t is actually a virtual address to kernel memory: > + return __pa((__force void *)paddr); To eliminate a few of the open-coded __force, what if we add an equivalent to rcu_dereference()? That seems to work well with the tdx_page_t concept, e.g. static inline tdx_page_t tdx_alloc_page(gfp_t gfp_flags) { struct page *page = alloc_page(gfp_flags); return (__force tdx_page_t)(page ? page_to_virt(page) : NULL); } static inline struct tdx_module_page *tdx_page_dereference(tdx_page_t page) { return (__force struct tdx_module_page *)page; } static inline void tdx_free_page(tdx_page_t page) { free_page((unsigned long)tdx_page_dereference(page)); } static inline phys_addr_t tdx_page_to_phys(tdx_page_t page) { return __pa(tdx_page_dereference(page)); } static inline tdx_page_t tdx_phys_to_page(phys_addr_t pa) { return (__force tdx_page_t)phys_to_virt(pa); } > @@ -1872,13 +1872,13 @@ static int tdx_sept_free_private_spt(struct kvm *kvm, gfn_t gfn, > * The HKID assigned to this TD was already freed and cache was > * already flushed. We don't have to flush again. > */ > - return tdx_reclaim_page(virt_to_page(private_spt)); > + return tdx_reclaim_page(tdx_virt_to_paddr(private_spt)); Rather than cast, I vote to change kvm_mmu_page.external_spt to a tdx_page_t. There's already a comment above the field calling out that its used by TDX, I don't see any reason to add a layer of indirection there. That helps eliminate a helper or two. tdx_page_t external_spt; > } > > static int tdx_sept_remove_private_spte(struct kvm *kvm, gfn_t gfn, > enum pg_level level, kvm_pfn_t pfn) > { > - struct page *page = pfn_to_page(pfn); > + tdx_paddr_t paddr = tdx_page_to_paddr(pfn_to_page(pfn)); > int ret; Rather than do pfn_to_page() => tdx_page_to_paddr(), we can go straight to tdx_phys_to_page() and avoid another helper or two. And with the rework, it's gets even a bit more cleaner, because the KVM API takes in a mirror_spte instead of a raw pfn. Then KVM can have: static tdx_page_t tdx_mirror_spte_to_page(u64 mirror_spte) { return tdx_phys_to_page(spte_to_pfn(mirror_spte) << PAGE_SHIFT); } and this code becomes static int tdx_sept_set_private_spte(struct kvm *kvm, gfn_t gfn, enum pg_level level, u64 mirror_spte) { tdx_page_t page = tdx_mirror_spte_to_page(mirror_spte); ... Compile tested only on top of the rework, and I didn't check sparse yet. --- arch/x86/include/asm/kvm_host.h | 4 +- arch/x86/include/asm/tdx.h | 65 +++++++++++++++++++++------ arch/x86/kvm/mmu/mmu_internal.h | 2 +- arch/x86/kvm/vmx/tdx.c | 80 +++++++++++++++++---------------- arch/x86/virt/vmx/tdx/tdx.c | 36 +++++++-------- 5 files changed, 114 insertions(+), 73 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 9f9839bbce13..9129108dc99c 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1842,14 +1842,14 @@ struct kvm_x86_ops { /* Update external mapping with page table link. */ int (*link_external_spt)(struct kvm *kvm, gfn_t gfn, enum pg_level level, - void *external_spt); + tdx_page_t external_spt); /* Update the external page table from spte getting set. */ int (*set_external_spte)(struct kvm *kvm, gfn_t gfn, enum pg_level level, u64 mirror_spte); /* Update external page tables for page table about to be freed. */ int (*free_external_spt)(struct kvm *kvm, gfn_t gfn, enum pg_level level, - void *external_spt); + tdx_page_t external_spt); /* Update external page table from spte getting removed, and flush TLB. */ void (*remove_external_spte)(struct kvm *kvm, gfn_t gfn, enum pg_level level, diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h index 6b338d7f01b7..270018027a25 100644 --- a/arch/x86/include/asm/tdx.h +++ b/arch/x86/include/asm/tdx.h @@ -36,7 +36,9 @@ #include <uapi/asm/mce.h> #include <asm/tdx_global_metadata.h> +#include <linux/io.h> #include <linux/pgtable.h> +#include <linux/mm.h> /* * Used by the #VE exception handler to gather the #VE exception @@ -154,15 +156,50 @@ int tdx_guest_keyid_alloc(void); u32 tdx_get_nr_guest_keyids(void); void tdx_guest_keyid_free(unsigned int keyid); -void tdx_quirk_reset_page(struct page *page); +struct tdx_module_page; +#if defined(__CHECKER__) +#define __tdx __attribute__((noderef, address_space(__tdx))) +#else +#define __tdx +#endif +typedef struct tdx_module_page __tdx * tdx_page_t; + +static inline tdx_page_t tdx_alloc_page(gfp_t gfp_flags) +{ + struct page *page = alloc_page(gfp_flags); + + return (__force tdx_page_t)(page ? page_to_virt(page) : NULL); +} + +static inline struct tdx_module_page *tdx_page_dereference(tdx_page_t page) +{ + return (__force struct tdx_module_page *)page; +} + +static inline void tdx_free_page(tdx_page_t page) +{ + free_page((unsigned long)tdx_page_dereference(page)); +} + +static inline phys_addr_t tdx_page_to_phys(tdx_page_t page) +{ + return __pa(tdx_page_dereference(page)); +} + +static inline tdx_page_t tdx_phys_to_page(phys_addr_t pa) +{ + return (__force tdx_page_t)phys_to_virt(pa); +} + +void tdx_quirk_reset_page(tdx_page_t page); struct tdx_td { /* TD root structure: */ - struct page *tdr_page; + tdx_page_t tdr_page; int tdcs_nr_pages; /* TD control structure: */ - struct page **tdcs_pages; + tdx_page_t *tdcs_pages; /* Size of `tdcx_pages` in struct tdx_vp */ int tdcx_nr_pages; @@ -170,19 +207,19 @@ struct tdx_td { struct tdx_vp { /* TDVP root page */ - struct page *tdvpr_page; + tdx_page_t tdvpr_page; /* precalculated page_to_phys(tdvpr_page) for use in noinstr code */ phys_addr_t tdvpr_pa; /* TD vCPU control structure: */ - struct page **tdcx_pages; + tdx_page_t *tdcx_pages; }; -static inline u64 mk_keyed_paddr(u16 hkid, struct page *page) +static inline u64 mk_keyed_paddr(u16 hkid, tdx_page_t page) { u64 ret; - ret = page_to_phys(page); + ret = tdx_page_to_phys(page); /* KeyID bits are just above the physical address bits: */ ret |= (u64)hkid << boot_cpu_data.x86_phys_bits; @@ -196,11 +233,11 @@ static inline int pg_level_to_tdx_sept_level(enum pg_level level) } u64 tdh_vp_enter(struct tdx_vp *vp, struct tdx_module_args *args); -u64 tdh_mng_addcx(struct tdx_td *td, struct page *tdcs_page); -u64 tdh_mem_page_add(struct tdx_td *td, u64 gpa, struct page *page, struct page *source, u64 *ext_err1, u64 *ext_err2); -u64 tdh_mem_sept_add(struct tdx_td *td, u64 gpa, int level, struct page *page, u64 *ext_err1, u64 *ext_err2); -u64 tdh_vp_addcx(struct tdx_vp *vp, struct page *tdcx_page); -u64 tdh_mem_page_aug(struct tdx_td *td, u64 gpa, int level, struct page *page, u64 *ext_err1, u64 *ext_err2); +u64 tdh_mng_addcx(struct tdx_td *td, tdx_page_t tdcs_page); +u64 tdh_mem_page_add(struct tdx_td *td, u64 gpa, tdx_page_t page, struct page *source, u64 *ext_err1, u64 *ext_err2); +u64 tdh_mem_sept_add(struct tdx_td *td, u64 gpa, int level, tdx_page_t page, u64 *ext_err1, u64 *ext_err2); +u64 tdh_vp_addcx(struct tdx_vp *vp, tdx_page_t tdcx_page); +u64 tdh_mem_page_aug(struct tdx_td *td, u64 gpa, int level, tdx_page_t page, u64 *ext_err1, u64 *ext_err2); u64 tdh_mem_range_block(struct tdx_td *td, u64 gpa, int level, u64 *ext_err1, u64 *ext_err2); u64 tdh_mng_key_config(struct tdx_td *td); u64 tdh_mng_create(struct tdx_td *td, u16 hkid); @@ -215,12 +252,12 @@ u64 tdh_mng_init(struct tdx_td *td, u64 td_params, u64 *extended_err); u64 tdh_vp_init(struct tdx_vp *vp, u64 initial_rcx, u32 x2apicid); u64 tdh_vp_rd(struct tdx_vp *vp, u64 field, u64 *data); u64 tdh_vp_wr(struct tdx_vp *vp, u64 field, u64 data, u64 mask); -u64 tdh_phymem_page_reclaim(struct page *page, u64 *tdx_pt, u64 *tdx_owner, u64 *tdx_size); +u64 tdh_phymem_page_reclaim(tdx_page_t page, u64 *tdx_pt, u64 *tdx_owner, u64 *tdx_size); u64 tdh_mem_track(struct tdx_td *tdr); u64 tdh_mem_page_remove(struct tdx_td *td, u64 gpa, u64 level, u64 *ext_err1, u64 *ext_err2); u64 tdh_phymem_cache_wb(bool resume); u64 tdh_phymem_page_wbinvd_tdr(struct tdx_td *td); -u64 tdh_phymem_page_wbinvd_hkid(u64 hkid, struct page *page); +u64 tdh_phymem_page_wbinvd_hkid(u64 hkid, tdx_page_t page); #else static inline void tdx_init(void) { } static inline int tdx_cpu_enable(void) { return -ENODEV; } diff --git a/arch/x86/kvm/mmu/mmu_internal.h b/arch/x86/kvm/mmu/mmu_internal.h index 73cdcbccc89e..144f46b93b5e 100644 --- a/arch/x86/kvm/mmu/mmu_internal.h +++ b/arch/x86/kvm/mmu/mmu_internal.h @@ -110,7 +110,7 @@ struct kvm_mmu_page { * Page table page of external PT. * Passed to TDX module, not accessed by KVM. */ - void *external_spt; + tdx_page_t external_spt; }; union { struct kvm_rmap_head parent_ptes; /* rmap pointers to parent sptes */ diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c index ae43974d033c..5a8a5d50b529 100644 --- a/arch/x86/kvm/vmx/tdx.c +++ b/arch/x86/kvm/vmx/tdx.c @@ -324,7 +324,7 @@ static inline void tdx_disassociate_vp(struct kvm_vcpu *vcpu) }) /* TDH.PHYMEM.PAGE.RECLAIM is allowed only when destroying the TD. */ -static int __tdx_reclaim_page(struct page *page) +static int __tdx_reclaim_page(tdx_page_t page) { u64 err, rcx, rdx, r8; @@ -341,7 +341,7 @@ static int __tdx_reclaim_page(struct page *page) return 0; } -static int tdx_reclaim_page(struct page *page) +static int tdx_reclaim_page(tdx_page_t page) { int r; @@ -357,7 +357,7 @@ static int tdx_reclaim_page(struct page *page) * private KeyID. Assume the cache associated with the TDX private KeyID has * been flushed. */ -static void tdx_reclaim_control_page(struct page *ctrl_page) +static void tdx_reclaim_control_page(tdx_page_t ctrl_page) { /* * Leak the page if the kernel failed to reclaim the page. @@ -366,7 +366,7 @@ static void tdx_reclaim_control_page(struct page *ctrl_page) if (tdx_reclaim_page(ctrl_page)) return; - __free_page(ctrl_page); + tdx_free_page(ctrl_page); } struct tdx_flush_vp_arg { @@ -603,7 +603,7 @@ static void tdx_reclaim_td_control_pages(struct kvm *kvm) tdx_quirk_reset_page(kvm_tdx->td.tdr_page); - __free_page(kvm_tdx->td.tdr_page); + tdx_free_page(kvm_tdx->td.tdr_page); kvm_tdx->td.tdr_page = NULL; } @@ -898,7 +898,7 @@ void tdx_vcpu_free(struct kvm_vcpu *vcpu) } if (tdx->vp.tdvpr_page) { tdx_reclaim_control_page(tdx->vp.tdvpr_page); - tdx->vp.tdvpr_page = 0; + tdx->vp.tdvpr_page = NULL; tdx->vp.tdvpr_pa = 0; } @@ -1622,7 +1622,7 @@ void tdx_load_mmu_pgd(struct kvm_vcpu *vcpu, hpa_t root_hpa, int pgd_level) } static int tdx_mem_page_add(struct kvm *kvm, gfn_t gfn, enum pg_level level, - kvm_pfn_t pfn) + tdx_page_t page) { struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm); u64 err, entry, level_state; @@ -1634,8 +1634,8 @@ static int tdx_mem_page_add(struct kvm *kvm, gfn_t gfn, enum pg_level level, KVM_BUG_ON(!kvm_tdx->page_add_src, kvm)) return -EIO; - err = tdh_mem_page_add(&kvm_tdx->td, gpa, pfn_to_page(pfn), - kvm_tdx->page_add_src, &entry, &level_state); + err = tdh_mem_page_add(&kvm_tdx->td, gpa, page, kvm_tdx->page_add_src, + &entry, &level_state); if (unlikely(tdx_operand_busy(err))) return -EBUSY; @@ -1646,11 +1646,10 @@ static int tdx_mem_page_add(struct kvm *kvm, gfn_t gfn, enum pg_level level, } static int tdx_mem_page_aug(struct kvm *kvm, gfn_t gfn, - enum pg_level level, kvm_pfn_t pfn) + enum pg_level level, tdx_page_t page) { int tdx_level = pg_level_to_tdx_sept_level(level); struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm); - struct page *page = pfn_to_page(pfn); gpa_t gpa = gfn_to_gpa(gfn); u64 entry, level_state; u64 err; @@ -1665,11 +1664,16 @@ static int tdx_mem_page_aug(struct kvm *kvm, gfn_t gfn, return 0; } +static tdx_page_t tdx_mirror_spte_to_page(u64 mirror_spte) +{ + return tdx_phys_to_page(spte_to_pfn(mirror_spte) << PAGE_SHIFT); +} + static int tdx_sept_set_private_spte(struct kvm *kvm, gfn_t gfn, enum pg_level level, u64 mirror_spte) { + tdx_page_t page = tdx_mirror_spte_to_page(mirror_spte); struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm); - kvm_pfn_t pfn = spte_to_pfn(mirror_spte); /* TODO: handle large pages. */ if (KVM_BUG_ON(level != PG_LEVEL_4K, kvm)) @@ -1691,21 +1695,20 @@ static int tdx_sept_set_private_spte(struct kvm *kvm, gfn_t gfn, * the VM image via KVM_TDX_INIT_MEM_REGION; ADD the page to the TD. */ if (unlikely(kvm_tdx->state != TD_STATE_RUNNABLE)) - return tdx_mem_page_add(kvm, gfn, level, pfn); + return tdx_mem_page_add(kvm, gfn, level, page); - return tdx_mem_page_aug(kvm, gfn, level, pfn); + return tdx_mem_page_aug(kvm, gfn, level, page); } static int tdx_sept_link_private_spt(struct kvm *kvm, gfn_t gfn, - enum pg_level level, void *private_spt) + enum pg_level level, tdx_page_t private_spt) { int tdx_level = pg_level_to_tdx_sept_level(level); gpa_t gpa = gfn_to_gpa(gfn); - struct page *page = virt_to_page(private_spt); u64 err, entry, level_state; - err = tdh_mem_sept_add(&to_kvm_tdx(kvm)->td, gpa, tdx_level, page, &entry, - &level_state); + err = tdh_mem_sept_add(&to_kvm_tdx(kvm)->td, gpa, tdx_level, + private_spt, &entry, &level_state); if (unlikely(tdx_operand_busy(err))) return -EBUSY; @@ -1762,7 +1765,7 @@ static void tdx_track(struct kvm *kvm) } static int tdx_sept_free_private_spt(struct kvm *kvm, gfn_t gfn, - enum pg_level level, void *private_spt) + enum pg_level level, tdx_page_t private_spt) { struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm); @@ -1781,13 +1784,13 @@ static int tdx_sept_free_private_spt(struct kvm *kvm, gfn_t gfn, * The HKID assigned to this TD was already freed and cache was * already flushed. We don't have to flush again. */ - return tdx_reclaim_page(virt_to_page(private_spt)); + return tdx_reclaim_page(private_spt); } static void tdx_sept_remove_private_spte(struct kvm *kvm, gfn_t gfn, enum pg_level level, u64 mirror_spte) { - struct page *page = pfn_to_page(spte_to_pfn(mirror_spte)); + tdx_page_t page = tdx_mirror_spte_to_page(mirror_spte); int tdx_level = pg_level_to_tdx_sept_level(level); struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm); gpa_t gpa = gfn_to_gpa(gfn); @@ -2390,8 +2393,8 @@ static int __tdx_td_init(struct kvm *kvm, struct td_params *td_params, { struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm); cpumask_var_t packages; - struct page **tdcs_pages = NULL; - struct page *tdr_page; + tdx_page_t *tdcs_pages = NULL; + tdx_page_t tdr_page; int ret, i; u64 err, rcx; @@ -2409,7 +2412,7 @@ static int __tdx_td_init(struct kvm *kvm, struct td_params *td_params, atomic_inc(&nr_configured_hkid); - tdr_page = alloc_page(GFP_KERNEL); + tdr_page = tdx_alloc_page(GFP_KERNEL); if (!tdr_page) goto free_hkid; @@ -2422,7 +2425,7 @@ static int __tdx_td_init(struct kvm *kvm, struct td_params *td_params, goto free_tdr; for (i = 0; i < kvm_tdx->td.tdcs_nr_pages; i++) { - tdcs_pages[i] = alloc_page(GFP_KERNEL); + tdcs_pages[i] = tdx_alloc_page(GFP_KERNEL); if (!tdcs_pages[i]) goto free_tdcs; } @@ -2541,7 +2544,7 @@ static int __tdx_td_init(struct kvm *kvm, struct td_params *td_params, /* Only free pages not yet added, so start at 'i' */ for (; i < kvm_tdx->td.tdcs_nr_pages; i++) { if (tdcs_pages[i]) { - __free_page(tdcs_pages[i]); + tdx_free_page(tdcs_pages[i]); tdcs_pages[i] = NULL; } } @@ -2560,15 +2563,15 @@ static int __tdx_td_init(struct kvm *kvm, struct td_params *td_params, free_tdcs: for (i = 0; i < kvm_tdx->td.tdcs_nr_pages; i++) { if (tdcs_pages[i]) - __free_page(tdcs_pages[i]); + tdx_free_page(tdcs_pages[i]); } kfree(tdcs_pages); kvm_tdx->td.tdcs_pages = NULL; free_tdr: if (tdr_page) - __free_page(tdr_page); - kvm_tdx->td.tdr_page = 0; + tdx_free_page(tdr_page); + kvm_tdx->td.tdr_page = NULL; free_hkid: tdx_hkid_free(kvm_tdx); @@ -2893,11 +2896,11 @@ static int tdx_td_vcpu_init(struct kvm_vcpu *vcpu, u64 vcpu_rcx) { struct kvm_tdx *kvm_tdx = to_kvm_tdx(vcpu->kvm); struct vcpu_tdx *tdx = to_tdx(vcpu); - struct page *page; + tdx_page_t page; int ret, i; u64 err; - page = alloc_page(GFP_KERNEL); + page = tdx_alloc_page(GFP_KERNEL); if (!page) return -ENOMEM; tdx->vp.tdvpr_page = page; @@ -2907,7 +2910,7 @@ static int tdx_td_vcpu_init(struct kvm_vcpu *vcpu, u64 vcpu_rcx) * entry via tdh_vp_enter(). Precalculate and store it instead * of doing it at runtime later. */ - tdx->vp.tdvpr_pa = page_to_phys(tdx->vp.tdvpr_page); + tdx->vp.tdvpr_pa = tdx_page_to_phys(tdx->vp.tdvpr_page); tdx->vp.tdcx_pages = kcalloc(kvm_tdx->td.tdcx_nr_pages, sizeof(*tdx->vp.tdcx_pages), GFP_KERNEL); @@ -2917,7 +2920,7 @@ static int tdx_td_vcpu_init(struct kvm_vcpu *vcpu, u64 vcpu_rcx) } for (i = 0; i < kvm_tdx->td.tdcx_nr_pages; i++) { - page = alloc_page(GFP_KERNEL); + page = tdx_alloc_page(GFP_KERNEL); if (!page) { ret = -ENOMEM; goto free_tdcx; @@ -2939,7 +2942,7 @@ static int tdx_td_vcpu_init(struct kvm_vcpu *vcpu, u64 vcpu_rcx) * method, but the rest are freed here. */ for (; i < kvm_tdx->td.tdcx_nr_pages; i++) { - __free_page(tdx->vp.tdcx_pages[i]); + tdx_free_page(tdx->vp.tdcx_pages[i]); tdx->vp.tdcx_pages[i] = NULL; } return -EIO; @@ -2957,7 +2960,7 @@ static int tdx_td_vcpu_init(struct kvm_vcpu *vcpu, u64 vcpu_rcx) free_tdcx: for (i = 0; i < kvm_tdx->td.tdcx_nr_pages; i++) { if (tdx->vp.tdcx_pages[i]) - __free_page(tdx->vp.tdcx_pages[i]); + tdx_free_page(tdx->vp.tdcx_pages[i]); tdx->vp.tdcx_pages[i] = NULL; } kfree(tdx->vp.tdcx_pages); @@ -2965,8 +2968,8 @@ static int tdx_td_vcpu_init(struct kvm_vcpu *vcpu, u64 vcpu_rcx) free_tdvpr: if (tdx->vp.tdvpr_page) - __free_page(tdx->vp.tdvpr_page); - tdx->vp.tdvpr_page = 0; + tdx_free_page(tdx->vp.tdvpr_page); + tdx->vp.tdvpr_page = NULL; tdx->vp.tdvpr_pa = 0; return ret; @@ -3004,7 +3007,8 @@ static int tdx_vcpu_get_cpuid_leaf(struct kvm_vcpu *vcpu, u32 leaf, int *entry_i static int tdx_vcpu_get_cpuid(struct kvm_vcpu *vcpu, struct kvm_tdx_cmd *cmd) { - struct kvm_cpuid2 __user *output, *td_cpuid; + struct kvm_cpuid2 __user *output; + struct kvm_cpuid2 *td_cpuid; int r = 0, i = 0, leaf; u32 level; diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c index eac403248462..1429dbe4da85 100644 --- a/arch/x86/virt/vmx/tdx/tdx.c +++ b/arch/x86/virt/vmx/tdx/tdx.c @@ -658,9 +658,9 @@ static void tdx_quirk_reset_paddr(unsigned long base, unsigned long size) mb(); } -void tdx_quirk_reset_page(struct page *page) +void tdx_quirk_reset_page(tdx_page_t page) { - tdx_quirk_reset_paddr(page_to_phys(page), PAGE_SIZE); + tdx_quirk_reset_paddr(tdx_page_to_phys(page), PAGE_SIZE); } EXPORT_SYMBOL_GPL(tdx_quirk_reset_page); @@ -1501,7 +1501,7 @@ EXPORT_SYMBOL_GPL(tdx_guest_keyid_free); static inline u64 tdx_tdr_pa(struct tdx_td *td) { - return page_to_phys(td->tdr_page); + return tdx_page_to_phys(td->tdr_page); } /* @@ -1510,9 +1510,9 @@ static inline u64 tdx_tdr_pa(struct tdx_td *td) * Be conservative and make the code simpler by doing the CLFLUSH * unconditionally. */ -static void tdx_clflush_page(struct page *page) +static void tdx_clflush_page(tdx_page_t page) { - clflush_cache_range(page_to_virt(page), PAGE_SIZE); + clflush_cache_range(tdx_page_dereference(page), PAGE_SIZE); } noinstr u64 tdh_vp_enter(struct tdx_vp *td, struct tdx_module_args *args) @@ -1523,10 +1523,10 @@ noinstr u64 tdh_vp_enter(struct tdx_vp *td, struct tdx_module_args *args) } EXPORT_SYMBOL_GPL(tdh_vp_enter); -u64 tdh_mng_addcx(struct tdx_td *td, struct page *tdcs_page) +u64 tdh_mng_addcx(struct tdx_td *td, tdx_page_t tdcs_page) { struct tdx_module_args args = { - .rcx = page_to_phys(tdcs_page), + .rcx = tdx_page_to_phys(tdcs_page), .rdx = tdx_tdr_pa(td), }; @@ -1535,12 +1535,12 @@ u64 tdh_mng_addcx(struct tdx_td *td, struct page *tdcs_page) } EXPORT_SYMBOL_GPL(tdh_mng_addcx); -u64 tdh_mem_page_add(struct tdx_td *td, u64 gpa, struct page *page, struct page *source, u64 *ext_err1, u64 *ext_err2) +u64 tdh_mem_page_add(struct tdx_td *td, u64 gpa, tdx_page_t page, struct page *source, u64 *ext_err1, u64 *ext_err2) { struct tdx_module_args args = { .rcx = gpa, .rdx = tdx_tdr_pa(td), - .r8 = page_to_phys(page), + .r8 = tdx_page_to_phys(page), .r9 = page_to_phys(source), }; u64 ret; @@ -1555,12 +1555,12 @@ u64 tdh_mem_page_add(struct tdx_td *td, u64 gpa, struct page *page, struct page } EXPORT_SYMBOL_GPL(tdh_mem_page_add); -u64 tdh_mem_sept_add(struct tdx_td *td, u64 gpa, int level, struct page *page, u64 *ext_err1, u64 *ext_err2) +u64 tdh_mem_sept_add(struct tdx_td *td, u64 gpa, int level, tdx_page_t page, u64 *ext_err1, u64 *ext_err2) { struct tdx_module_args args = { .rcx = gpa | level, .rdx = tdx_tdr_pa(td), - .r8 = page_to_phys(page), + .r8 = tdx_page_to_phys(page), }; u64 ret; @@ -1574,10 +1574,10 @@ u64 tdh_mem_sept_add(struct tdx_td *td, u64 gpa, int level, struct page *page, u } EXPORT_SYMBOL_GPL(tdh_mem_sept_add); -u64 tdh_vp_addcx(struct tdx_vp *vp, struct page *tdcx_page) +u64 tdh_vp_addcx(struct tdx_vp *vp, tdx_page_t tdcx_page) { struct tdx_module_args args = { - .rcx = page_to_phys(tdcx_page), + .rcx = tdx_page_to_phys(tdcx_page), .rdx = vp->tdvpr_pa, }; @@ -1586,12 +1586,12 @@ u64 tdh_vp_addcx(struct tdx_vp *vp, struct page *tdcx_page) } EXPORT_SYMBOL_GPL(tdh_vp_addcx); -u64 tdh_mem_page_aug(struct tdx_td *td, u64 gpa, int level, struct page *page, u64 *ext_err1, u64 *ext_err2) +u64 tdh_mem_page_aug(struct tdx_td *td, u64 gpa, int level, tdx_page_t page, u64 *ext_err1, u64 *ext_err2) { struct tdx_module_args args = { .rcx = gpa | level, .rdx = tdx_tdr_pa(td), - .r8 = page_to_phys(page), + .r8 = tdx_page_to_phys(page), }; u64 ret; @@ -1794,10 +1794,10 @@ EXPORT_SYMBOL_GPL(tdh_vp_init); * So despite the names, they must be interpted specially as described by the spec. Return * them only for error reporting purposes. */ -u64 tdh_phymem_page_reclaim(struct page *page, u64 *tdx_pt, u64 *tdx_owner, u64 *tdx_size) +u64 tdh_phymem_page_reclaim(tdx_page_t page, u64 *tdx_pt, u64 *tdx_owner, u64 *tdx_size) { struct tdx_module_args args = { - .rcx = page_to_phys(page), + .rcx = tdx_page_to_phys(page), }; u64 ret; @@ -1858,7 +1858,7 @@ u64 tdh_phymem_page_wbinvd_tdr(struct tdx_td *td) } EXPORT_SYMBOL_GPL(tdh_phymem_page_wbinvd_tdr); -u64 tdh_phymem_page_wbinvd_hkid(u64 hkid, struct page *page) +u64 tdh_phymem_page_wbinvd_hkid(u64 hkid, tdx_page_t page) { struct tdx_module_args args = {}; base-commit: 0da566344bd6586a7c358ab4e19417748e7b0feb -- ^ permalink raw reply related [flat|nested] 15+ messages in thread
end of thread, other threads:[~2025-10-30 15:42 UTC | newest] Thread overview: 15+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-09-10 14:44 [PATCH] x86/virt/tdx: Use precalculated TDVPR page physical address Dave Hansen 2025-09-10 16:06 ` Kiryl Shutsemau 2025-09-10 16:10 ` Dave Hansen 2025-09-10 16:12 ` Kiryl Shutsemau 2025-09-10 16:57 ` Dave Hansen 2025-09-11 15:41 ` Kiryl Shutsemau 2025-10-20 13:57 ` Sean Christopherson 2025-10-20 14:14 ` Dave Hansen 2025-10-20 14:42 ` Sean Christopherson 2025-10-20 15:10 ` Dave Hansen 2025-10-20 15:25 ` Sean Christopherson 2025-10-20 15:43 ` Dave Hansen 2025-10-21 17:51 ` Sean Christopherson 2025-10-29 23:51 ` Dave Hansen 2025-10-30 15:42 ` Sean Christopherson
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).