@@ -1595,6 +1595,28 @@ static int tdx_sept_link_private_sp(struct kvm *kvm, gfn_t gfn,
return 0;
}
+static int tdx_sept_split_private_spte(struct kvm *kvm, gfn_t gfn,
+ enum pg_level level, void *sept_page)
+{
+ int tdx_level = pg_level_to_tdx_sept_level(level);
+ struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm);
+ gpa_t gpa = gfn << PAGE_SHIFT;
+ hpa_t hpa = __pa(sept_page);
+ struct tdx_module_output out;
+ u64 err;
+
+ /* See comment in tdx_sept_set_private_spte() */
+ spin_lock(&kvm_tdx->seamcall_lock);
+ err = tdh_mem_page_demote(kvm_tdx->tdr.pa, gpa, tdx_level, hpa, &out);
+ spin_unlock(&kvm_tdx->seamcall_lock);
+ if (KVM_BUG_ON(err, kvm)) {
+ pr_tdx_error(TDH_MEM_PAGE_DEMOTE, err, &out);
+ return -EIO;
+ }
+
+ return 0;
+}
+
static void tdx_sept_zap_private_spte(struct kvm *kvm, gfn_t gfn,
enum pg_level level)
{
@@ -1604,8 +1626,6 @@ static void tdx_sept_zap_private_spte(struct kvm *kvm, gfn_t gfn,
struct tdx_module_output out;
u64 err;
- /* For now large page isn't supported yet. */
- WARN_ON_ONCE(level != PG_LEVEL_4K);
spin_lock(&kvm_tdx->seamcall_lock);
err = tdh_mem_range_block(kvm_tdx->tdr.pa, gpa, tdx_level, &out);
spin_unlock(&kvm_tdx->seamcall_lock);
@@ -1717,13 +1737,11 @@ static void tdx_handle_changed_private_spte(
lockdep_assert_held(&kvm->mmu_lock);
if (change->new.is_present) {
- /* TDP MMU doesn't change present -> present */
- WARN_ON(change->old.is_present);
- /*
- * Use different call to either set up middle level
- * private page table, or leaf.
- */
- if (is_leaf)
+ if (level > PG_LEVEL_4K && was_leaf && !is_leaf) {
+ tdx_sept_zap_private_spte(kvm, gfn, level);
+ tdx_sept_tlb_remote_flush(kvm);
+ tdx_sept_split_private_spte(kvm, gfn, level, change->sept_page);
+ } else if (is_leaf)
tdx_sept_set_private_spte(
kvm, gfn, level, change->new.pfn);
else {
@@ -21,6 +21,7 @@
#define TDH_MNG_CREATE 9
#define TDH_VP_CREATE 10
#define TDH_MNG_RD 11
+#define TDH_MEM_PAGE_DEMOTE 15
#define TDH_MR_EXTEND 16
#define TDH_MR_FINALIZE 17
#define TDH_VP_FLUSH 18
@@ -127,6 +127,13 @@ static inline u64 tdh_mng_rd(hpa_t tdr, u64 field, struct tdx_module_output *out
return __seamcall(TDH_MNG_RD, tdr, field, 0, 0, out);
}
+static inline u64 tdh_mem_page_demote(hpa_t tdr, gpa_t gpa, int level, hpa_t page,
+ struct tdx_module_output *out)
+{
+ return seamcall_sept_retry(TDH_MEM_PAGE_DEMOTE, gpa | level, tdr, page,
+ 0, out);
+}
+
static inline u64 tdh_mr_extend(hpa_t tdr, gpa_t gpa,
struct tdx_module_output *out)
{