11b09c13cSSteffen Eiden // SPDX-License-Identifier: GPL-2.0 21b09c13cSSteffen Eiden /* 31b09c13cSSteffen Eiden * KVM guest address space mapping code 41b09c13cSSteffen Eiden * 51b09c13cSSteffen Eiden * Copyright IBM Corp. 2007, 2020, 2024 61b09c13cSSteffen Eiden * Author(s): Claudio Imbrenda <imbrenda@linux.ibm.com> 71b09c13cSSteffen Eiden * Martin Schwidefsky <schwidefsky@de.ibm.com> 81b09c13cSSteffen Eiden * David Hildenbrand <david@redhat.com> 91b09c13cSSteffen Eiden * Janosch Frank <frankja@linux.ibm.com> 101b09c13cSSteffen Eiden */ 111b09c13cSSteffen Eiden 121b09c13cSSteffen Eiden #include <linux/kernel.h> 131b09c13cSSteffen Eiden #include <linux/pagewalk.h> 141b09c13cSSteffen Eiden #include <linux/swap.h> 151b09c13cSSteffen Eiden #include <linux/smp.h> 161b09c13cSSteffen Eiden #include <linux/spinlock.h> 171b09c13cSSteffen Eiden #include <linux/slab.h> 181b09c13cSSteffen Eiden #include <linux/swapops.h> 191b09c13cSSteffen Eiden #include <linux/ksm.h> 201b09c13cSSteffen Eiden #include <linux/mm.h> 211b09c13cSSteffen Eiden #include <linux/mman.h> 221b09c13cSSteffen Eiden #include <linux/pgtable.h> 231b09c13cSSteffen Eiden #include <linux/kvm_types.h> 241b09c13cSSteffen Eiden #include <linux/kvm_host.h> 251b09c13cSSteffen Eiden #include <linux/pgalloc.h> 261b09c13cSSteffen Eiden 271b09c13cSSteffen Eiden #include <asm/page-states.h> 281b09c13cSSteffen Eiden #include <asm/tlb.h> 291b09c13cSSteffen Eiden #include "dat.h" 301b09c13cSSteffen Eiden 311b09c13cSSteffen Eiden int kvm_s390_mmu_cache_topup(struct kvm_s390_mmu_cache *mc) 321b09c13cSSteffen Eiden { 331b09c13cSSteffen Eiden void *o; 341b09c13cSSteffen Eiden 351b09c13cSSteffen Eiden for ( ; mc->n_crsts < KVM_S390_MMU_CACHE_N_CRSTS; mc->n_crsts++) { 361b09c13cSSteffen Eiden o = (void *)__get_free_pages(GFP_KERNEL_ACCOUNT | __GFP_COMP, CRST_ALLOC_ORDER); 371b09c13cSSteffen Eiden if (!o) 381b09c13cSSteffen Eiden return -ENOMEM; 391b09c13cSSteffen Eiden mc->crsts[mc->n_crsts] = o; 401b09c13cSSteffen Eiden } 411b09c13cSSteffen Eiden for ( ; mc->n_pts < KVM_S390_MMU_CACHE_N_PTS; mc->n_pts++) { 421b09c13cSSteffen Eiden o = (void *)__get_free_page(GFP_KERNEL_ACCOUNT); 431b09c13cSSteffen Eiden if (!o) 441b09c13cSSteffen Eiden return -ENOMEM; 451b09c13cSSteffen Eiden mc->pts[mc->n_pts] = o; 461b09c13cSSteffen Eiden } 471b09c13cSSteffen Eiden for ( ; mc->n_rmaps < KVM_S390_MMU_CACHE_N_RMAPS; mc->n_rmaps++) { 481b09c13cSSteffen Eiden o = kzalloc_obj(struct vsie_rmap, GFP_KERNEL_ACCOUNT); 491b09c13cSSteffen Eiden if (!o) 501b09c13cSSteffen Eiden return -ENOMEM; 511b09c13cSSteffen Eiden mc->rmaps[mc->n_rmaps] = o; 521b09c13cSSteffen Eiden } 531b09c13cSSteffen Eiden return 0; 541b09c13cSSteffen Eiden } 551b09c13cSSteffen Eiden 561b09c13cSSteffen Eiden static inline struct page_table *dat_alloc_pt_noinit(struct kvm_s390_mmu_cache *mc) 571b09c13cSSteffen Eiden { 581b09c13cSSteffen Eiden struct page_table *res; 591b09c13cSSteffen Eiden 601b09c13cSSteffen Eiden res = kvm_s390_mmu_cache_alloc_pt(mc); 611b09c13cSSteffen Eiden if (res) 621b09c13cSSteffen Eiden __arch_set_page_dat(res, 1); 631b09c13cSSteffen Eiden return res; 641b09c13cSSteffen Eiden } 651b09c13cSSteffen Eiden 661b09c13cSSteffen Eiden static inline struct crst_table *dat_alloc_crst_noinit(struct kvm_s390_mmu_cache *mc) 671b09c13cSSteffen Eiden { 681b09c13cSSteffen Eiden struct crst_table *res; 691b09c13cSSteffen Eiden 701b09c13cSSteffen Eiden res = kvm_s390_mmu_cache_alloc_crst(mc); 711b09c13cSSteffen Eiden if (res) 721b09c13cSSteffen Eiden __arch_set_page_dat(res, 1UL << CRST_ALLOC_ORDER); 731b09c13cSSteffen Eiden return res; 741b09c13cSSteffen Eiden } 751b09c13cSSteffen Eiden 761b09c13cSSteffen Eiden struct crst_table *dat_alloc_crst_sleepable(unsigned long init) 771b09c13cSSteffen Eiden { 781b09c13cSSteffen Eiden struct page *page; 791b09c13cSSteffen Eiden void *virt; 801b09c13cSSteffen Eiden 811b09c13cSSteffen Eiden page = alloc_pages(GFP_KERNEL_ACCOUNT | __GFP_COMP, CRST_ALLOC_ORDER); 821b09c13cSSteffen Eiden if (!page) 831b09c13cSSteffen Eiden return NULL; 841b09c13cSSteffen Eiden virt = page_to_virt(page); 851b09c13cSSteffen Eiden __arch_set_page_dat(virt, 1UL << CRST_ALLOC_ORDER); 861b09c13cSSteffen Eiden crst_table_init(virt, init); 871b09c13cSSteffen Eiden return virt; 881b09c13cSSteffen Eiden } 891b09c13cSSteffen Eiden 901b09c13cSSteffen Eiden void dat_free_level(struct crst_table *table, bool owns_ptes) 911b09c13cSSteffen Eiden { 921b09c13cSSteffen Eiden unsigned int i; 931b09c13cSSteffen Eiden 941b09c13cSSteffen Eiden for (i = 0; i < _CRST_ENTRIES; i++) { 951b09c13cSSteffen Eiden if (table->crstes[i].h.fc || table->crstes[i].h.i) 961b09c13cSSteffen Eiden continue; 971b09c13cSSteffen Eiden if (!is_pmd(table->crstes[i])) 981b09c13cSSteffen Eiden dat_free_level(dereference_crste(table->crstes[i]), owns_ptes); 991b09c13cSSteffen Eiden else if (owns_ptes) 1001b09c13cSSteffen Eiden dat_free_pt(dereference_pmd(table->crstes[i].pmd)); 1011b09c13cSSteffen Eiden } 1021b09c13cSSteffen Eiden dat_free_crst(table); 1031b09c13cSSteffen Eiden } 1041b09c13cSSteffen Eiden 1051b09c13cSSteffen Eiden int dat_set_asce_limit(struct kvm_s390_mmu_cache *mc, union asce *asce, int newtype) 1061b09c13cSSteffen Eiden { 1071b09c13cSSteffen Eiden struct crst_table *table; 1081b09c13cSSteffen Eiden union crste crste; 1091b09c13cSSteffen Eiden 1101b09c13cSSteffen Eiden while (asce->dt > newtype) { 1111b09c13cSSteffen Eiden table = dereference_asce(*asce); 1121b09c13cSSteffen Eiden crste = table->crstes[0]; 1131b09c13cSSteffen Eiden if (crste.h.fc) 1141b09c13cSSteffen Eiden return 0; 1151b09c13cSSteffen Eiden if (!crste.h.i) { 1161b09c13cSSteffen Eiden asce->rsto = crste.h.fc0.to; 1171b09c13cSSteffen Eiden dat_free_crst(table); 1181b09c13cSSteffen Eiden } else { 1191b09c13cSSteffen Eiden crste.h.tt--; 1201b09c13cSSteffen Eiden crst_table_init((void *)table, crste.val); 1211b09c13cSSteffen Eiden } 1221b09c13cSSteffen Eiden asce->dt--; 1231b09c13cSSteffen Eiden } 1241b09c13cSSteffen Eiden while (asce->dt < newtype) { 1251b09c13cSSteffen Eiden crste = _crste_fc0(asce->rsto, asce->dt + 1); 1261b09c13cSSteffen Eiden table = dat_alloc_crst_noinit(mc); 1271b09c13cSSteffen Eiden if (!table) 1281b09c13cSSteffen Eiden return -ENOMEM; 1291b09c13cSSteffen Eiden crst_table_init((void *)table, _CRSTE_HOLE(crste.h.tt).val); 1301b09c13cSSteffen Eiden table->crstes[0] = crste; 1311b09c13cSSteffen Eiden asce->rsto = __pa(table) >> PAGE_SHIFT; 1321b09c13cSSteffen Eiden asce->dt++; 1331b09c13cSSteffen Eiden } 1341b09c13cSSteffen Eiden return 0; 1351b09c13cSSteffen Eiden } 1361b09c13cSSteffen Eiden 1371b09c13cSSteffen Eiden /** 1381b09c13cSSteffen Eiden * dat_crstep_xchg_atomic() - Atomically exchange a gmap CRSTE with another. 1391b09c13cSSteffen Eiden * @crstep: Pointer to the CRST entry. 1401b09c13cSSteffen Eiden * @old: Expected old value. 1411b09c13cSSteffen Eiden * @new: Replacement entry. 1421b09c13cSSteffen Eiden * @gfn: The affected guest address. 1431b09c13cSSteffen Eiden * @asce: The asce of the address space. 1441b09c13cSSteffen Eiden * 1451b09c13cSSteffen Eiden * This function is needed to atomically exchange a CRSTE that potentially 1461b09c13cSSteffen Eiden * maps a prefix area, without having to invalidate it inbetween. 1471b09c13cSSteffen Eiden * 1481b09c13cSSteffen Eiden * Context: This function is assumed to be called with kvm->mmu_lock held. 1491b09c13cSSteffen Eiden * 1501b09c13cSSteffen Eiden * Return: %true if the exchange was successful. 1511b09c13cSSteffen Eiden */ 1521b09c13cSSteffen Eiden bool __must_check dat_crstep_xchg_atomic(union crste *crstep, union crste old, union crste new, 1531b09c13cSSteffen Eiden gfn_t gfn, union asce asce) 1541b09c13cSSteffen Eiden { 1551b09c13cSSteffen Eiden if (old.h.i) 1561b09c13cSSteffen Eiden return arch_try_cmpxchg((long *)crstep, &old.val, new.val); 1571b09c13cSSteffen Eiden if (cpu_has_edat2()) 1581b09c13cSSteffen Eiden return crdte_crste(crstep, old, new, gfn, asce); 1591b09c13cSSteffen Eiden return cspg_crste(crstep, old, new); 1601b09c13cSSteffen Eiden } 1611b09c13cSSteffen Eiden 1621b09c13cSSteffen Eiden static void dat_set_storage_key_from_pgste(union pte pte, union pgste pgste) 1631b09c13cSSteffen Eiden { 1641b09c13cSSteffen Eiden union skey nkey = { .acc = pgste.acc, .fp = pgste.fp }; 1651b09c13cSSteffen Eiden 1661b09c13cSSteffen Eiden page_set_storage_key(pte_origin(pte), nkey.skey, 0); 1671b09c13cSSteffen Eiden } 1681b09c13cSSteffen Eiden 1691b09c13cSSteffen Eiden static void dat_move_storage_key(union pte old, union pte new) 1701b09c13cSSteffen Eiden { 1711b09c13cSSteffen Eiden page_set_storage_key(pte_origin(new), page_get_storage_key(pte_origin(old)), 1); 1721b09c13cSSteffen Eiden } 1731b09c13cSSteffen Eiden 1741b09c13cSSteffen Eiden static union pgste dat_save_storage_key_into_pgste(union pte pte, union pgste pgste) 1751b09c13cSSteffen Eiden { 1761b09c13cSSteffen Eiden union skey skey; 1771b09c13cSSteffen Eiden 1781b09c13cSSteffen Eiden skey.skey = page_get_storage_key(pte_origin(pte)); 1791b09c13cSSteffen Eiden 1801b09c13cSSteffen Eiden pgste.acc = skey.acc; 1811b09c13cSSteffen Eiden pgste.fp = skey.fp; 1821b09c13cSSteffen Eiden pgste.gr |= skey.r; 1831b09c13cSSteffen Eiden pgste.gc |= skey.c; 1841b09c13cSSteffen Eiden 1851b09c13cSSteffen Eiden return pgste; 1861b09c13cSSteffen Eiden } 1871b09c13cSSteffen Eiden 1881b09c13cSSteffen Eiden union pgste __dat_ptep_xchg(union pte *ptep, union pgste pgste, union pte new, gfn_t gfn, 1891b09c13cSSteffen Eiden union asce asce, bool uses_skeys) 1901b09c13cSSteffen Eiden { 1911b09c13cSSteffen Eiden union pte old = READ_ONCE(*ptep); 1921b09c13cSSteffen Eiden 1931b09c13cSSteffen Eiden /* Updating only the software bits while holding the pgste lock. */ 1941b09c13cSSteffen Eiden if (!((ptep->val ^ new.val) & ~_PAGE_SW_BITS)) { 1951b09c13cSSteffen Eiden WRITE_ONCE(ptep->swbyte, new.swbyte); 1961b09c13cSSteffen Eiden return pgste; 1971b09c13cSSteffen Eiden } 1981b09c13cSSteffen Eiden 1991b09c13cSSteffen Eiden if (!old.h.i) { 2001b09c13cSSteffen Eiden unsigned long opts = IPTE_GUEST_ASCE | (pgste.nodat ? IPTE_NODAT : 0); 2011b09c13cSSteffen Eiden 2021b09c13cSSteffen Eiden if (machine_has_tlb_guest()) 2031b09c13cSSteffen Eiden __ptep_ipte(gfn_to_gpa(gfn), (void *)ptep, opts, asce.val, IPTE_GLOBAL); 2041b09c13cSSteffen Eiden else 2051b09c13cSSteffen Eiden __ptep_ipte(gfn_to_gpa(gfn), (void *)ptep, 0, 0, IPTE_GLOBAL); 2061b09c13cSSteffen Eiden } 2071b09c13cSSteffen Eiden 2081b09c13cSSteffen Eiden if (uses_skeys) { 2091b09c13cSSteffen Eiden if (old.h.i && !new.h.i) 2101b09c13cSSteffen Eiden /* Invalid to valid: restore storage keys from PGSTE. */ 2111b09c13cSSteffen Eiden dat_set_storage_key_from_pgste(new, pgste); 2121b09c13cSSteffen Eiden else if (!old.h.i && new.h.i) 2131b09c13cSSteffen Eiden /* Valid to invalid: save storage keys to PGSTE. */ 2141b09c13cSSteffen Eiden pgste = dat_save_storage_key_into_pgste(old, pgste); 2151b09c13cSSteffen Eiden else if (!old.h.i && !new.h.i) 2161b09c13cSSteffen Eiden /* Valid to valid: move storage keys. */ 2171b09c13cSSteffen Eiden if (old.h.pfra != new.h.pfra) 2181b09c13cSSteffen Eiden dat_move_storage_key(old, new); 2191b09c13cSSteffen Eiden /* Invalid to invalid: nothing to do. */ 2201b09c13cSSteffen Eiden } 2211b09c13cSSteffen Eiden 2221b09c13cSSteffen Eiden WRITE_ONCE(*ptep, new); 2231b09c13cSSteffen Eiden return pgste; 2241b09c13cSSteffen Eiden } 2251b09c13cSSteffen Eiden 2261b09c13cSSteffen Eiden /* 2271b09c13cSSteffen Eiden * dat_split_ste() - Split a segment table entry into page table entries. 2281b09c13cSSteffen Eiden * 2291b09c13cSSteffen Eiden * Context: This function is assumed to be called with kvm->mmu_lock held. 2301b09c13cSSteffen Eiden * 2311b09c13cSSteffen Eiden * Return: 0 in case of success, -ENOMEM if running out of memory. 2321b09c13cSSteffen Eiden */ 2331b09c13cSSteffen Eiden static int dat_split_ste(struct kvm_s390_mmu_cache *mc, union pmd *pmdp, gfn_t gfn, 2341b09c13cSSteffen Eiden union asce asce, bool uses_skeys) 2351b09c13cSSteffen Eiden { 2361b09c13cSSteffen Eiden union pgste pgste_init; 2371b09c13cSSteffen Eiden struct page_table *pt; 2381b09c13cSSteffen Eiden union pmd new, old; 2391b09c13cSSteffen Eiden union pte init; 2401b09c13cSSteffen Eiden int i; 2411b09c13cSSteffen Eiden 2421b09c13cSSteffen Eiden BUG_ON(!mc); 2431b09c13cSSteffen Eiden old = READ_ONCE(*pmdp); 2441b09c13cSSteffen Eiden 2451b09c13cSSteffen Eiden /* Already split, nothing to do. */ 2461b09c13cSSteffen Eiden if (!old.h.i && !old.h.fc) 2471b09c13cSSteffen Eiden return 0; 2481b09c13cSSteffen Eiden 2491b09c13cSSteffen Eiden pt = dat_alloc_pt_noinit(mc); 2501b09c13cSSteffen Eiden if (!pt) 2511b09c13cSSteffen Eiden return -ENOMEM; 2521b09c13cSSteffen Eiden new.val = virt_to_phys(pt); 2531b09c13cSSteffen Eiden 2541b09c13cSSteffen Eiden while (old.h.i || old.h.fc) { 2551b09c13cSSteffen Eiden init.val = pmd_origin_large(old); 2561b09c13cSSteffen Eiden init.h.p = old.h.p; 2571b09c13cSSteffen Eiden init.h.i = old.h.i; 2581b09c13cSSteffen Eiden init.s.d = old.s.fc1.d; 2591b09c13cSSteffen Eiden init.s.w = old.s.fc1.w; 2601b09c13cSSteffen Eiden init.s.y = old.s.fc1.y; 2611b09c13cSSteffen Eiden init.s.sd = old.s.fc1.sd; 2621b09c13cSSteffen Eiden init.s.pr = old.s.fc1.pr; 2631b09c13cSSteffen Eiden pgste_init.val = 0; 2641b09c13cSSteffen Eiden if (old.h.fc) { 2651b09c13cSSteffen Eiden for (i = 0; i < _PAGE_ENTRIES; i++) 2661b09c13cSSteffen Eiden pt->ptes[i].val = init.val | i * PAGE_SIZE; 2671b09c13cSSteffen Eiden /* No need to take locks as the page table is not installed yet. */ 2681b09c13cSSteffen Eiden pgste_init.prefix_notif = old.s.fc1.prefix_notif; 2691b09c13cSSteffen Eiden pgste_init.vsie_notif = old.s.fc1.vsie_notif; 2701b09c13cSSteffen Eiden pgste_init.vsie_gmem = old.s.fc1.vsie_notif; 2711b09c13cSSteffen Eiden pgste_init.pcl = uses_skeys && init.h.i; 2721b09c13cSSteffen Eiden dat_init_pgstes(pt, pgste_init.val); 2731b09c13cSSteffen Eiden } else { 2741b09c13cSSteffen Eiden dat_init_page_table(pt, init.val, 0); 2751b09c13cSSteffen Eiden } 2761b09c13cSSteffen Eiden 2771b09c13cSSteffen Eiden if (dat_pmdp_xchg_atomic(pmdp, old, new, gfn, asce)) { 2781b09c13cSSteffen Eiden if (!pgste_init.pcl) 2791b09c13cSSteffen Eiden return 0; 2801b09c13cSSteffen Eiden for (i = 0; i < _PAGE_ENTRIES; i++) { 2811b09c13cSSteffen Eiden union pgste pgste = pt->pgstes[i]; 2821b09c13cSSteffen Eiden 2831b09c13cSSteffen Eiden pgste = dat_save_storage_key_into_pgste(pt->ptes[i], pgste); 2841b09c13cSSteffen Eiden pgste_set_unlock(pt->ptes + i, pgste); 2851b09c13cSSteffen Eiden } 2861b09c13cSSteffen Eiden return 0; 2871b09c13cSSteffen Eiden } 2881b09c13cSSteffen Eiden old = READ_ONCE(*pmdp); 2891b09c13cSSteffen Eiden } 2901b09c13cSSteffen Eiden 2911b09c13cSSteffen Eiden dat_free_pt(pt); 2921b09c13cSSteffen Eiden return 0; 2931b09c13cSSteffen Eiden } 2941b09c13cSSteffen Eiden 2951b09c13cSSteffen Eiden /* 2961b09c13cSSteffen Eiden * dat_split_crste() - Split a crste into smaller crstes. 2971b09c13cSSteffen Eiden * 2981b09c13cSSteffen Eiden * Context: This function is assumed to be called with kvm->mmu_lock held. 2991b09c13cSSteffen Eiden * 3001b09c13cSSteffen Eiden * Return: %0 in case of success, %-ENOMEM if running out of memory. 3011b09c13cSSteffen Eiden */ 3021b09c13cSSteffen Eiden static int dat_split_crste(struct kvm_s390_mmu_cache *mc, union crste *crstep, 3031b09c13cSSteffen Eiden gfn_t gfn, union asce asce, bool uses_skeys) 3041b09c13cSSteffen Eiden { 3051b09c13cSSteffen Eiden struct crst_table *table; 3061b09c13cSSteffen Eiden union crste old, new, init; 3071b09c13cSSteffen Eiden int i; 3081b09c13cSSteffen Eiden 3091b09c13cSSteffen Eiden old = READ_ONCE(*crstep); 3101b09c13cSSteffen Eiden if (is_pmd(old)) 3111b09c13cSSteffen Eiden return dat_split_ste(mc, &crstep->pmd, gfn, asce, uses_skeys); 3121b09c13cSSteffen Eiden 3131b09c13cSSteffen Eiden BUG_ON(!mc); 3141b09c13cSSteffen Eiden 3151b09c13cSSteffen Eiden /* Already split, nothing to do. */ 3161b09c13cSSteffen Eiden if (!old.h.i && !old.h.fc) 3171b09c13cSSteffen Eiden return 0; 3181b09c13cSSteffen Eiden 3191b09c13cSSteffen Eiden table = dat_alloc_crst_noinit(mc); 3201b09c13cSSteffen Eiden if (!table) 3211b09c13cSSteffen Eiden return -ENOMEM; 3221b09c13cSSteffen Eiden 3231b09c13cSSteffen Eiden new.val = virt_to_phys(table); 3241b09c13cSSteffen Eiden new.h.tt = old.h.tt; 3251b09c13cSSteffen Eiden new.h.fc0.tl = _REGION_ENTRY_LENGTH; 3261b09c13cSSteffen Eiden 3271b09c13cSSteffen Eiden while (old.h.i || old.h.fc) { 3281b09c13cSSteffen Eiden init = old; 3291b09c13cSSteffen Eiden init.h.tt--; 3301b09c13cSSteffen Eiden if (old.h.fc) { 3311b09c13cSSteffen Eiden for (i = 0; i < _CRST_ENTRIES; i++) 3321b09c13cSSteffen Eiden table->crstes[i].val = init.val | i * HPAGE_SIZE; 3331b09c13cSSteffen Eiden } else { 3341b09c13cSSteffen Eiden crst_table_init((void *)table, init.val); 3351b09c13cSSteffen Eiden } 3361b09c13cSSteffen Eiden if (dat_crstep_xchg_atomic(crstep, old, new, gfn, asce)) 3371b09c13cSSteffen Eiden return 0; 3381b09c13cSSteffen Eiden old = READ_ONCE(*crstep); 3391b09c13cSSteffen Eiden } 3401b09c13cSSteffen Eiden 3411b09c13cSSteffen Eiden dat_free_crst(table); 3421b09c13cSSteffen Eiden return 0; 3431b09c13cSSteffen Eiden } 3441b09c13cSSteffen Eiden 3451b09c13cSSteffen Eiden /** 3461b09c13cSSteffen Eiden * dat_entry_walk() - Walk the gmap page tables. 3471b09c13cSSteffen Eiden * @mc: Cache to use to allocate dat tables, if needed; can be NULL if neither 3481b09c13cSSteffen Eiden * %DAT_WALK_SPLIT or %DAT_WALK_ALLOC is specified in @flags. 3491b09c13cSSteffen Eiden * @gfn: Guest frame. 3501b09c13cSSteffen Eiden * @asce: The ASCE of the address space. 3511b09c13cSSteffen Eiden * @flags: Flags from WALK_* macros. 3521b09c13cSSteffen Eiden * @walk_level: Level to walk to, from LEVEL_* macros. 3531b09c13cSSteffen Eiden * @last: Will be filled the last visited non-pte DAT entry. 3541b09c13cSSteffen Eiden * @ptepp: Will be filled the last visited pte entry, if any, otherwise NULL. 3551b09c13cSSteffen Eiden * 3561b09c13cSSteffen Eiden * Returns a table entry pointer for the given guest address and @walk_level. 3571b09c13cSSteffen Eiden * 3581b09c13cSSteffen Eiden * The @flags have the following meanings: 3591b09c13cSSteffen Eiden * * %DAT_WALK_IGN_HOLES: consider holes as normal table entries 3601b09c13cSSteffen Eiden * * %DAT_WALK_ALLOC: allocate new tables to reach the requested level, if needed 3611b09c13cSSteffen Eiden * * %DAT_WALK_SPLIT: split existing large pages to reach the requested level, if needed 3621b09c13cSSteffen Eiden * * %DAT_WALK_LEAF: return successfully whenever a large page is encountered 3631b09c13cSSteffen Eiden * * %DAT_WALK_ANY: return successfully even if the requested level could not be reached 3641b09c13cSSteffen Eiden * * %DAT_WALK_CONTINUE: walk to the requested level with the specified flags, and then try to 3651b09c13cSSteffen Eiden * continue walking to ptes with only DAT_WALK_ANY 3661b09c13cSSteffen Eiden * * %DAT_WALK_USES_SKEYS: storage keys are in use 3671b09c13cSSteffen Eiden * 3681b09c13cSSteffen Eiden * Context: called with kvm->mmu_lock held. 3691b09c13cSSteffen Eiden * 3701b09c13cSSteffen Eiden * Return: 3711b09c13cSSteffen Eiden * * %PGM_ADDRESSING if the requested address lies outside memory 3721b09c13cSSteffen Eiden * * a PIC number if the requested address lies in a memory hole of type _DAT_TOKEN_PIC 3731b09c13cSSteffen Eiden * * %-EFAULT if the requested address lies inside a memory hole of a different type 3741b09c13cSSteffen Eiden * * %-EINVAL if the given ASCE is not compatible with the requested level 3751b09c13cSSteffen Eiden * * %-EFBIG if the requested level could not be reached because a larger frame was found 3761b09c13cSSteffen Eiden * * %-ENOENT if the requested level could not be reached for other reasons 3771b09c13cSSteffen Eiden * * %-ENOMEM if running out of memory while allocating or splitting a table 3781b09c13cSSteffen Eiden */ 3791b09c13cSSteffen Eiden int dat_entry_walk(struct kvm_s390_mmu_cache *mc, gfn_t gfn, union asce asce, int flags, 3801b09c13cSSteffen Eiden int walk_level, union crste **last, union pte **ptepp) 3811b09c13cSSteffen Eiden { 3821b09c13cSSteffen Eiden union vaddress vaddr = { .addr = gfn_to_gpa(gfn) }; 3831b09c13cSSteffen Eiden bool continue_anyway = flags & DAT_WALK_CONTINUE; 3841b09c13cSSteffen Eiden bool uses_skeys = flags & DAT_WALK_USES_SKEYS; 3851b09c13cSSteffen Eiden bool ign_holes = flags & DAT_WALK_IGN_HOLES; 3861b09c13cSSteffen Eiden bool allocate = flags & DAT_WALK_ALLOC; 3871b09c13cSSteffen Eiden bool split = flags & DAT_WALK_SPLIT; 3881b09c13cSSteffen Eiden bool leaf = flags & DAT_WALK_LEAF; 3891b09c13cSSteffen Eiden bool any = flags & DAT_WALK_ANY; 3901b09c13cSSteffen Eiden struct page_table *pgtable; 3911b09c13cSSteffen Eiden struct crst_table *table; 3921b09c13cSSteffen Eiden union crste entry; 3931b09c13cSSteffen Eiden int rc; 3941b09c13cSSteffen Eiden 3951b09c13cSSteffen Eiden *last = NULL; 3961b09c13cSSteffen Eiden *ptepp = NULL; 3971b09c13cSSteffen Eiden if (WARN_ON_ONCE(unlikely(!asce.val))) 3981b09c13cSSteffen Eiden return -EINVAL; 3991b09c13cSSteffen Eiden if (WARN_ON_ONCE(unlikely(walk_level > asce.dt))) 4001b09c13cSSteffen Eiden return -EINVAL; 4011b09c13cSSteffen Eiden if (!asce_contains_gfn(asce, gfn)) 4021b09c13cSSteffen Eiden return PGM_ADDRESSING; 4031b09c13cSSteffen Eiden 4041b09c13cSSteffen Eiden table = dereference_asce(asce); 4051b09c13cSSteffen Eiden if (asce.dt >= ASCE_TYPE_REGION1) { 4061b09c13cSSteffen Eiden *last = table->crstes + vaddr.rfx; 4071b09c13cSSteffen Eiden entry = READ_ONCE(**last); 4081b09c13cSSteffen Eiden if (WARN_ON_ONCE(entry.h.tt != TABLE_TYPE_REGION1)) 4091b09c13cSSteffen Eiden return -EINVAL; 4101b09c13cSSteffen Eiden if (crste_hole(entry) && !ign_holes) 4111b09c13cSSteffen Eiden return entry.tok.type == _DAT_TOKEN_PIC ? entry.tok.par : -EFAULT; 4121b09c13cSSteffen Eiden if (walk_level == TABLE_TYPE_REGION1) 4131b09c13cSSteffen Eiden return 0; 4141b09c13cSSteffen Eiden if (entry.pgd.h.i) { 4151b09c13cSSteffen Eiden if (!allocate) 4161b09c13cSSteffen Eiden return any ? 0 : -ENOENT; 4171b09c13cSSteffen Eiden rc = dat_split_crste(mc, *last, gfn, asce, uses_skeys); 4181b09c13cSSteffen Eiden if (rc) 4191b09c13cSSteffen Eiden return rc; 4201b09c13cSSteffen Eiden entry = READ_ONCE(**last); 4211b09c13cSSteffen Eiden } 4221b09c13cSSteffen Eiden table = dereference_crste(entry.pgd); 4231b09c13cSSteffen Eiden } 4241b09c13cSSteffen Eiden 4251b09c13cSSteffen Eiden if (asce.dt >= ASCE_TYPE_REGION2) { 4261b09c13cSSteffen Eiden *last = table->crstes + vaddr.rsx; 4271b09c13cSSteffen Eiden entry = READ_ONCE(**last); 4281b09c13cSSteffen Eiden if (WARN_ON_ONCE(entry.h.tt != TABLE_TYPE_REGION2)) 4291b09c13cSSteffen Eiden return -EINVAL; 4301b09c13cSSteffen Eiden if (crste_hole(entry) && !ign_holes) 4311b09c13cSSteffen Eiden return entry.tok.type == _DAT_TOKEN_PIC ? entry.tok.par : -EFAULT; 4321b09c13cSSteffen Eiden if (walk_level == TABLE_TYPE_REGION2) 4331b09c13cSSteffen Eiden return 0; 4341b09c13cSSteffen Eiden if (entry.p4d.h.i) { 4351b09c13cSSteffen Eiden if (!allocate) 4361b09c13cSSteffen Eiden return any ? 0 : -ENOENT; 4371b09c13cSSteffen Eiden rc = dat_split_crste(mc, *last, gfn, asce, uses_skeys); 4381b09c13cSSteffen Eiden if (rc) 4391b09c13cSSteffen Eiden return rc; 4401b09c13cSSteffen Eiden entry = READ_ONCE(**last); 4411b09c13cSSteffen Eiden } 4421b09c13cSSteffen Eiden table = dereference_crste(entry.p4d); 4431b09c13cSSteffen Eiden } 4441b09c13cSSteffen Eiden 4451b09c13cSSteffen Eiden if (asce.dt >= ASCE_TYPE_REGION3) { 4461b09c13cSSteffen Eiden *last = table->crstes + vaddr.rtx; 4471b09c13cSSteffen Eiden entry = READ_ONCE(**last); 4481b09c13cSSteffen Eiden if (WARN_ON_ONCE(entry.h.tt != TABLE_TYPE_REGION3)) 4491b09c13cSSteffen Eiden return -EINVAL; 4501b09c13cSSteffen Eiden if (crste_hole(entry) && !ign_holes) 4511b09c13cSSteffen Eiden return entry.tok.type == _DAT_TOKEN_PIC ? entry.tok.par : -EFAULT; 4521b09c13cSSteffen Eiden if (walk_level == TABLE_TYPE_REGION3 && 4531b09c13cSSteffen Eiden continue_anyway && !entry.pud.h.fc && !entry.h.i) { 4541b09c13cSSteffen Eiden walk_level = TABLE_TYPE_PAGE_TABLE; 4551b09c13cSSteffen Eiden allocate = false; 4561b09c13cSSteffen Eiden } 4571b09c13cSSteffen Eiden if (walk_level == TABLE_TYPE_REGION3 || ((leaf || any) && entry.pud.h.fc)) 4581b09c13cSSteffen Eiden return 0; 4591b09c13cSSteffen Eiden if (entry.pud.h.i && !entry.pud.h.fc) { 4601b09c13cSSteffen Eiden if (!allocate) 4611b09c13cSSteffen Eiden return any ? 0 : -ENOENT; 4621b09c13cSSteffen Eiden rc = dat_split_crste(mc, *last, gfn, asce, uses_skeys); 4631b09c13cSSteffen Eiden if (rc) 4641b09c13cSSteffen Eiden return rc; 4651b09c13cSSteffen Eiden entry = READ_ONCE(**last); 4661b09c13cSSteffen Eiden } 4671b09c13cSSteffen Eiden if (walk_level <= TABLE_TYPE_SEGMENT && entry.pud.h.fc) { 4681b09c13cSSteffen Eiden if (!split) 4691b09c13cSSteffen Eiden return -EFBIG; 4701b09c13cSSteffen Eiden rc = dat_split_crste(mc, *last, gfn, asce, uses_skeys); 4711b09c13cSSteffen Eiden if (rc) 4721b09c13cSSteffen Eiden return rc; 4731b09c13cSSteffen Eiden entry = READ_ONCE(**last); 4741b09c13cSSteffen Eiden } 4751b09c13cSSteffen Eiden table = dereference_crste(entry.pud); 4761b09c13cSSteffen Eiden } 4771b09c13cSSteffen Eiden 4781b09c13cSSteffen Eiden *last = table->crstes + vaddr.sx; 4791b09c13cSSteffen Eiden entry = READ_ONCE(**last); 4801b09c13cSSteffen Eiden if (WARN_ON_ONCE(entry.h.tt != TABLE_TYPE_SEGMENT)) 4811b09c13cSSteffen Eiden return -EINVAL; 4821b09c13cSSteffen Eiden if (crste_hole(entry) && !ign_holes) 4831b09c13cSSteffen Eiden return entry.tok.type == _DAT_TOKEN_PIC ? entry.tok.par : -EFAULT; 4841b09c13cSSteffen Eiden if (continue_anyway && !entry.pmd.h.fc && !entry.h.i) { 4851b09c13cSSteffen Eiden walk_level = TABLE_TYPE_PAGE_TABLE; 4861b09c13cSSteffen Eiden allocate = false; 4871b09c13cSSteffen Eiden } 4881b09c13cSSteffen Eiden if (walk_level == TABLE_TYPE_SEGMENT || ((leaf || any) && entry.pmd.h.fc)) 4891b09c13cSSteffen Eiden return 0; 4901b09c13cSSteffen Eiden 4911b09c13cSSteffen Eiden if (entry.pmd.h.i && !entry.pmd.h.fc) { 4921b09c13cSSteffen Eiden if (!allocate) 4931b09c13cSSteffen Eiden return any ? 0 : -ENOENT; 4941b09c13cSSteffen Eiden rc = dat_split_ste(mc, &(*last)->pmd, gfn, asce, uses_skeys); 4951b09c13cSSteffen Eiden if (rc) 4961b09c13cSSteffen Eiden return rc; 4971b09c13cSSteffen Eiden entry = READ_ONCE(**last); 4981b09c13cSSteffen Eiden } 4991b09c13cSSteffen Eiden if (walk_level <= TABLE_TYPE_PAGE_TABLE && entry.pmd.h.fc) { 5001b09c13cSSteffen Eiden if (!split) 5011b09c13cSSteffen Eiden return -EFBIG; 5021b09c13cSSteffen Eiden rc = dat_split_ste(mc, &(*last)->pmd, gfn, asce, uses_skeys); 5031b09c13cSSteffen Eiden if (rc) 5041b09c13cSSteffen Eiden return rc; 5051b09c13cSSteffen Eiden entry = READ_ONCE(**last); 5061b09c13cSSteffen Eiden } 5071b09c13cSSteffen Eiden pgtable = dereference_pmd(entry.pmd); 5081b09c13cSSteffen Eiden *ptepp = pgtable->ptes + vaddr.px; 5091b09c13cSSteffen Eiden if (pte_hole(**ptepp) && !ign_holes) 5101b09c13cSSteffen Eiden return (*ptepp)->tok.type == _DAT_TOKEN_PIC ? (*ptepp)->tok.par : -EFAULT; 5111b09c13cSSteffen Eiden return 0; 5121b09c13cSSteffen Eiden } 5131b09c13cSSteffen Eiden 5141b09c13cSSteffen Eiden static long dat_pte_walk_range(gfn_t gfn, gfn_t end, struct page_table *table, struct dat_walk *w) 5151b09c13cSSteffen Eiden { 5161b09c13cSSteffen Eiden unsigned int idx = gfn & (_PAGE_ENTRIES - 1); 5171b09c13cSSteffen Eiden long rc = 0; 5181b09c13cSSteffen Eiden 5191b09c13cSSteffen Eiden for ( ; gfn < end; idx++, gfn++) { 5201b09c13cSSteffen Eiden if (pte_hole(READ_ONCE(table->ptes[idx]))) { 5211b09c13cSSteffen Eiden if (!(w->flags & DAT_WALK_IGN_HOLES)) 5221b09c13cSSteffen Eiden return -EFAULT; 5231b09c13cSSteffen Eiden if (!(w->flags & DAT_WALK_ANY)) 5241b09c13cSSteffen Eiden continue; 5251b09c13cSSteffen Eiden } 5261b09c13cSSteffen Eiden 5271b09c13cSSteffen Eiden rc = w->ops->pte_entry(table->ptes + idx, gfn, gfn + 1, w); 5281b09c13cSSteffen Eiden if (rc) 5291b09c13cSSteffen Eiden break; 5301b09c13cSSteffen Eiden } 5311b09c13cSSteffen Eiden return rc; 5321b09c13cSSteffen Eiden } 5331b09c13cSSteffen Eiden 5341b09c13cSSteffen Eiden static long dat_crste_walk_range(gfn_t start, gfn_t end, struct crst_table *table, 5351b09c13cSSteffen Eiden struct dat_walk *walk) 5361b09c13cSSteffen Eiden { 5371b09c13cSSteffen Eiden unsigned long idx, cur_shift, cur_size; 5381b09c13cSSteffen Eiden dat_walk_op the_op; 5391b09c13cSSteffen Eiden union crste crste; 5401b09c13cSSteffen Eiden gfn_t cur, next; 5411b09c13cSSteffen Eiden long rc = 0; 5421b09c13cSSteffen Eiden 5431b09c13cSSteffen Eiden cur_shift = 8 + table->crstes[0].h.tt * 11; 5441b09c13cSSteffen Eiden idx = (start >> cur_shift) & (_CRST_ENTRIES - 1); 5451b09c13cSSteffen Eiden cur_size = 1UL << cur_shift; 5461b09c13cSSteffen Eiden 5471b09c13cSSteffen Eiden for (cur = ALIGN_DOWN(start, cur_size); cur < end; idx++, cur = next) { 5481b09c13cSSteffen Eiden next = cur + cur_size; 5491b09c13cSSteffen Eiden walk->last = table->crstes + idx; 5501b09c13cSSteffen Eiden crste = READ_ONCE(*walk->last); 5511b09c13cSSteffen Eiden 5521b09c13cSSteffen Eiden if (crste_hole(crste)) { 5531b09c13cSSteffen Eiden if (!(walk->flags & DAT_WALK_IGN_HOLES)) 5541b09c13cSSteffen Eiden return -EFAULT; 5551b09c13cSSteffen Eiden if (!(walk->flags & DAT_WALK_ANY)) 5561b09c13cSSteffen Eiden continue; 5571b09c13cSSteffen Eiden } 5581b09c13cSSteffen Eiden 5591b09c13cSSteffen Eiden the_op = walk->ops->crste_ops[crste.h.tt]; 5601b09c13cSSteffen Eiden if (the_op) { 5611b09c13cSSteffen Eiden rc = the_op(walk->last, cur, next, walk); 5621b09c13cSSteffen Eiden crste = READ_ONCE(*walk->last); 5631b09c13cSSteffen Eiden } 5641b09c13cSSteffen Eiden if (rc) 5651b09c13cSSteffen Eiden break; 5661b09c13cSSteffen Eiden if (!crste.h.i && !crste.h.fc) { 5671b09c13cSSteffen Eiden if (!is_pmd(crste)) 5681b09c13cSSteffen Eiden rc = dat_crste_walk_range(max(start, cur), min(end, next), 5691b09c13cSSteffen Eiden _dereference_crste(crste), walk); 5701b09c13cSSteffen Eiden else if (walk->ops->pte_entry) 5711b09c13cSSteffen Eiden rc = dat_pte_walk_range(max(start, cur), min(end, next), 5721b09c13cSSteffen Eiden dereference_pmd(crste.pmd), walk); 5731b09c13cSSteffen Eiden if (rc) 5741b09c13cSSteffen Eiden break; 5751b09c13cSSteffen Eiden } 5761b09c13cSSteffen Eiden } 5771b09c13cSSteffen Eiden return rc; 5781b09c13cSSteffen Eiden } 5791b09c13cSSteffen Eiden 5801b09c13cSSteffen Eiden /** 5811b09c13cSSteffen Eiden * _dat_walk_gfn_range() - Walk DAT tables. 5821b09c13cSSteffen Eiden * @start: The first guest page frame to walk. 5831b09c13cSSteffen Eiden * @end: The guest page frame immediately after the last one to walk. 5841b09c13cSSteffen Eiden * @asce: The ASCE of the guest mapping. 5851b09c13cSSteffen Eiden * @ops: The gmap_walk_ops that will be used to perform the walk. 5861b09c13cSSteffen Eiden * @flags: Flags from WALK_* (currently only WALK_IGN_HOLES is supported). 5871b09c13cSSteffen Eiden * @priv: Will be passed as-is to the callbacks. 5881b09c13cSSteffen Eiden * 5891b09c13cSSteffen Eiden * Any callback returning non-zero causes the walk to stop immediately. 5901b09c13cSSteffen Eiden * 5911b09c13cSSteffen Eiden * Return: %-EINVAL in case of error, %-EFAULT if @start is too high for the 5921b09c13cSSteffen Eiden * given ASCE unless the DAT_WALK_IGN_HOLES flag is specified, 5931b09c13cSSteffen Eiden * otherwise it returns whatever the callbacks return. 5941b09c13cSSteffen Eiden */ 5951b09c13cSSteffen Eiden long _dat_walk_gfn_range(gfn_t start, gfn_t end, union asce asce, 5961b09c13cSSteffen Eiden const struct dat_walk_ops *ops, int flags, void *priv) 5971b09c13cSSteffen Eiden { 5981b09c13cSSteffen Eiden struct crst_table *table = dereference_asce(asce); 5991b09c13cSSteffen Eiden struct dat_walk walk = { 6001b09c13cSSteffen Eiden .ops = ops, 6011b09c13cSSteffen Eiden .asce = asce, 6021b09c13cSSteffen Eiden .priv = priv, 6031b09c13cSSteffen Eiden .flags = flags, 6041b09c13cSSteffen Eiden .start = start, 6051b09c13cSSteffen Eiden .end = end, 6061b09c13cSSteffen Eiden }; 6071b09c13cSSteffen Eiden 6081b09c13cSSteffen Eiden if (WARN_ON_ONCE(unlikely(!asce.val))) 6091b09c13cSSteffen Eiden return -EINVAL; 6101b09c13cSSteffen Eiden if (!asce_contains_gfn(asce, start)) 6111b09c13cSSteffen Eiden return (flags & DAT_WALK_IGN_HOLES) ? 0 : -EFAULT; 6121b09c13cSSteffen Eiden 6131b09c13cSSteffen Eiden return dat_crste_walk_range(start, min(end, asce_end(asce)), table, &walk); 6141b09c13cSSteffen Eiden } 6151b09c13cSSteffen Eiden 616a07276d5SSteffen Eiden #if KVM_S390_MANAGES_S390_GUEST 6171b09c13cSSteffen Eiden int dat_get_storage_key(union asce asce, gfn_t gfn, union skey *skey) 6181b09c13cSSteffen Eiden { 6191b09c13cSSteffen Eiden union crste *crstep; 6201b09c13cSSteffen Eiden union pgste pgste; 6211b09c13cSSteffen Eiden union pte *ptep; 6221b09c13cSSteffen Eiden int rc; 6231b09c13cSSteffen Eiden 6241b09c13cSSteffen Eiden skey->skey = 0; 6251b09c13cSSteffen Eiden rc = dat_entry_walk(NULL, gfn, asce, DAT_WALK_ANY, TABLE_TYPE_PAGE_TABLE, &crstep, &ptep); 6261b09c13cSSteffen Eiden if (rc) 6271b09c13cSSteffen Eiden return rc; 6281b09c13cSSteffen Eiden 6291b09c13cSSteffen Eiden if (!ptep) { 6301b09c13cSSteffen Eiden union crste crste; 6311b09c13cSSteffen Eiden 6321b09c13cSSteffen Eiden crste = READ_ONCE(*crstep); 6331b09c13cSSteffen Eiden if (!crste.h.fc || !crste.s.fc1.pr) 6341b09c13cSSteffen Eiden return 0; 6351b09c13cSSteffen Eiden skey->skey = page_get_storage_key(large_crste_to_phys(crste, gfn)); 6361b09c13cSSteffen Eiden return 0; 6371b09c13cSSteffen Eiden } 6381b09c13cSSteffen Eiden pgste = pgste_get_lock(ptep); 6391b09c13cSSteffen Eiden if (ptep->h.i) { 6401b09c13cSSteffen Eiden skey->acc = pgste.acc; 6411b09c13cSSteffen Eiden skey->fp = pgste.fp; 6421b09c13cSSteffen Eiden } else { 6431b09c13cSSteffen Eiden skey->skey = page_get_storage_key(pte_origin(*ptep)); 6441b09c13cSSteffen Eiden } 6451b09c13cSSteffen Eiden skey->r |= pgste.gr; 6461b09c13cSSteffen Eiden skey->c |= pgste.gc; 6471b09c13cSSteffen Eiden pgste_set_unlock(ptep, pgste); 6481b09c13cSSteffen Eiden return 0; 6491b09c13cSSteffen Eiden } 6501b09c13cSSteffen Eiden 6511b09c13cSSteffen Eiden static void dat_update_ptep_sd(union pgste old, union pgste pgste, union pte *ptep) 6521b09c13cSSteffen Eiden { 6531b09c13cSSteffen Eiden if (pgste.acc != old.acc || pgste.fp != old.fp || pgste.gr != old.gr || pgste.gc != old.gc) 6541b09c13cSSteffen Eiden __atomic64_or(_PAGE_SD, &ptep->val); 6551b09c13cSSteffen Eiden } 6561b09c13cSSteffen Eiden 6571b09c13cSSteffen Eiden int dat_set_storage_key(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t gfn, 6581b09c13cSSteffen Eiden union skey skey, bool nq) 6591b09c13cSSteffen Eiden { 6601b09c13cSSteffen Eiden union pgste pgste, old; 6611b09c13cSSteffen Eiden union crste *crstep; 6621b09c13cSSteffen Eiden union pte *ptep; 6631b09c13cSSteffen Eiden int rc; 6641b09c13cSSteffen Eiden 6651b09c13cSSteffen Eiden rc = dat_entry_walk(mc, gfn, asce, DAT_WALK_LEAF_ALLOC, TABLE_TYPE_PAGE_TABLE, 6661b09c13cSSteffen Eiden &crstep, &ptep); 6671b09c13cSSteffen Eiden if (rc) 6681b09c13cSSteffen Eiden return rc; 6691b09c13cSSteffen Eiden 6701b09c13cSSteffen Eiden if (!ptep) { 6711b09c13cSSteffen Eiden page_set_storage_key(large_crste_to_phys(*crstep, gfn), skey.skey, !nq); 6721b09c13cSSteffen Eiden return 0; 6731b09c13cSSteffen Eiden } 6741b09c13cSSteffen Eiden 6751b09c13cSSteffen Eiden old = pgste_get_lock(ptep); 6761b09c13cSSteffen Eiden pgste = old; 6771b09c13cSSteffen Eiden 6781b09c13cSSteffen Eiden pgste.acc = skey.acc; 6791b09c13cSSteffen Eiden pgste.fp = skey.fp; 6801b09c13cSSteffen Eiden pgste.gc = skey.c; 6811b09c13cSSteffen Eiden pgste.gr = skey.r; 6821b09c13cSSteffen Eiden 6831b09c13cSSteffen Eiden if (!ptep->h.i) { 6841b09c13cSSteffen Eiden union skey old_skey; 6851b09c13cSSteffen Eiden 6861b09c13cSSteffen Eiden old_skey.skey = page_get_storage_key(pte_origin(*ptep)); 6871b09c13cSSteffen Eiden pgste.hc |= old_skey.c; 6881b09c13cSSteffen Eiden pgste.hr |= old_skey.r; 6891b09c13cSSteffen Eiden old_skey.c = old.gc; 6901b09c13cSSteffen Eiden old_skey.r = old.gr; 6911b09c13cSSteffen Eiden skey.r = 0; 6921b09c13cSSteffen Eiden skey.c = 0; 6931b09c13cSSteffen Eiden page_set_storage_key(pte_origin(*ptep), skey.skey, !nq); 6941b09c13cSSteffen Eiden } 6951b09c13cSSteffen Eiden 6961b09c13cSSteffen Eiden dat_update_ptep_sd(old, pgste, ptep); 6971b09c13cSSteffen Eiden pgste_set_unlock(ptep, pgste); 6981b09c13cSSteffen Eiden return 0; 6991b09c13cSSteffen Eiden } 7001b09c13cSSteffen Eiden 7011b09c13cSSteffen Eiden static bool page_cond_set_storage_key(phys_addr_t paddr, union skey skey, union skey *oldkey, 7021b09c13cSSteffen Eiden bool nq, bool mr, bool mc) 7031b09c13cSSteffen Eiden { 7041b09c13cSSteffen Eiden oldkey->skey = page_get_storage_key(paddr); 7051b09c13cSSteffen Eiden if (oldkey->acc == skey.acc && oldkey->fp == skey.fp && 7061b09c13cSSteffen Eiden (oldkey->r == skey.r || mr) && (oldkey->c == skey.c || mc)) 7071b09c13cSSteffen Eiden return false; 7081b09c13cSSteffen Eiden page_set_storage_key(paddr, skey.skey, !nq); 7091b09c13cSSteffen Eiden return true; 7101b09c13cSSteffen Eiden } 7111b09c13cSSteffen Eiden 7121b09c13cSSteffen Eiden int dat_cond_set_storage_key(struct kvm_s390_mmu_cache *mmc, union asce asce, gfn_t gfn, 7131b09c13cSSteffen Eiden union skey skey, union skey *oldkey, bool nq, bool mr, bool mc) 7141b09c13cSSteffen Eiden { 7151b09c13cSSteffen Eiden union pgste pgste, old; 7161b09c13cSSteffen Eiden union crste *crstep; 7171b09c13cSSteffen Eiden union skey prev; 7181b09c13cSSteffen Eiden union pte *ptep; 7191b09c13cSSteffen Eiden int rc; 7201b09c13cSSteffen Eiden 7211b09c13cSSteffen Eiden rc = dat_entry_walk(mmc, gfn, asce, DAT_WALK_LEAF_ALLOC, TABLE_TYPE_PAGE_TABLE, 7221b09c13cSSteffen Eiden &crstep, &ptep); 7231b09c13cSSteffen Eiden if (rc) 7241b09c13cSSteffen Eiden return rc; 7251b09c13cSSteffen Eiden 7261b09c13cSSteffen Eiden if (!ptep) { 7271b09c13cSSteffen Eiden if (!oldkey) 7281b09c13cSSteffen Eiden oldkey = &prev; 7291b09c13cSSteffen Eiden return page_cond_set_storage_key(large_crste_to_phys(*crstep, gfn), skey, oldkey, 7301b09c13cSSteffen Eiden nq, mr, mc); 7311b09c13cSSteffen Eiden } 7321b09c13cSSteffen Eiden 7331b09c13cSSteffen Eiden old = pgste_get_lock(ptep); 7341b09c13cSSteffen Eiden pgste = old; 7351b09c13cSSteffen Eiden 7361b09c13cSSteffen Eiden rc = 1; 7371b09c13cSSteffen Eiden pgste.acc = skey.acc; 7381b09c13cSSteffen Eiden pgste.fp = skey.fp; 7391b09c13cSSteffen Eiden pgste.gc = skey.c; 7401b09c13cSSteffen Eiden pgste.gr = skey.r; 7411b09c13cSSteffen Eiden prev.skey = 0; 7421b09c13cSSteffen Eiden 7431b09c13cSSteffen Eiden if (!ptep->h.i) { 7441b09c13cSSteffen Eiden rc = page_cond_set_storage_key(pte_origin(*ptep), skey, &prev, nq, mr, mc); 7451b09c13cSSteffen Eiden pgste.hc |= prev.c; 7461b09c13cSSteffen Eiden pgste.hr |= prev.r; 7471b09c13cSSteffen Eiden prev.c |= old.gc; 7481b09c13cSSteffen Eiden prev.r |= old.gr; 7491b09c13cSSteffen Eiden } else { 7501b09c13cSSteffen Eiden prev.acc = old.acc; 7511b09c13cSSteffen Eiden prev.fp = old.fp; 7521b09c13cSSteffen Eiden prev.c = old.gc; 7531b09c13cSSteffen Eiden prev.r = old.gr; 7541b09c13cSSteffen Eiden } 7551b09c13cSSteffen Eiden if (oldkey) 7561b09c13cSSteffen Eiden *oldkey = prev; 7571b09c13cSSteffen Eiden 7581b09c13cSSteffen Eiden dat_update_ptep_sd(old, pgste, ptep); 7591b09c13cSSteffen Eiden pgste_set_unlock(ptep, pgste); 7601b09c13cSSteffen Eiden return rc; 7611b09c13cSSteffen Eiden } 7621b09c13cSSteffen Eiden 7631b09c13cSSteffen Eiden int dat_reset_reference_bit(union asce asce, gfn_t gfn, union skey *skey) 7641b09c13cSSteffen Eiden { 7651b09c13cSSteffen Eiden union pgste pgste, old; 7661b09c13cSSteffen Eiden union crste *crstep; 7671b09c13cSSteffen Eiden union pte *ptep; 7681b09c13cSSteffen Eiden int rc; 7691b09c13cSSteffen Eiden 7701b09c13cSSteffen Eiden skey->skey = 0; 7711b09c13cSSteffen Eiden 7721b09c13cSSteffen Eiden rc = dat_entry_walk(NULL, gfn, asce, DAT_WALK_ANY, TABLE_TYPE_PAGE_TABLE, &crstep, &ptep); 7731b09c13cSSteffen Eiden if (rc) 7741b09c13cSSteffen Eiden return rc; 7751b09c13cSSteffen Eiden 7761b09c13cSSteffen Eiden if (!ptep) { 7771b09c13cSSteffen Eiden union crste crste = READ_ONCE(*crstep); 7781b09c13cSSteffen Eiden 7791b09c13cSSteffen Eiden if (!crste.h.fc || !crste.s.fc1.pr) 7801b09c13cSSteffen Eiden return 0; 7811b09c13cSSteffen Eiden skey->skey = page_reset_referenced(large_crste_to_phys(*crstep, gfn)) << 1; 7821b09c13cSSteffen Eiden return 0; 7831b09c13cSSteffen Eiden } 7841b09c13cSSteffen Eiden old = pgste_get_lock(ptep); 7851b09c13cSSteffen Eiden pgste = old; 7861b09c13cSSteffen Eiden 7871b09c13cSSteffen Eiden if (!ptep->h.i) { 7881b09c13cSSteffen Eiden skey->skey = page_reset_referenced(pte_origin(*ptep)) << 1; 7891b09c13cSSteffen Eiden pgste.hr = skey->r; 7901b09c13cSSteffen Eiden } 7911b09c13cSSteffen Eiden skey->r |= pgste.gr; 7921b09c13cSSteffen Eiden skey->c |= pgste.gc; 7931b09c13cSSteffen Eiden pgste.gr = 0; 7941b09c13cSSteffen Eiden 7951b09c13cSSteffen Eiden dat_update_ptep_sd(old, pgste, ptep); 7961b09c13cSSteffen Eiden pgste_set_unlock(ptep, pgste); 7971b09c13cSSteffen Eiden return 0; 7981b09c13cSSteffen Eiden } 7991b09c13cSSteffen Eiden 8001b09c13cSSteffen Eiden static long dat_reset_skeys_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk) 8011b09c13cSSteffen Eiden { 8021b09c13cSSteffen Eiden union pgste pgste; 8031b09c13cSSteffen Eiden 8041b09c13cSSteffen Eiden pgste = pgste_get_lock(ptep); 8051b09c13cSSteffen Eiden pgste.acc = 0; 8061b09c13cSSteffen Eiden pgste.fp = 0; 8071b09c13cSSteffen Eiden pgste.gr = 0; 8081b09c13cSSteffen Eiden pgste.gc = 0; 8091b09c13cSSteffen Eiden if (ptep->s.pr) 8101b09c13cSSteffen Eiden page_set_storage_key(pte_origin(*ptep), PAGE_DEFAULT_KEY, 1); 8111b09c13cSSteffen Eiden pgste_set_unlock(ptep, pgste); 8121b09c13cSSteffen Eiden 8131b09c13cSSteffen Eiden if (need_resched()) 8141b09c13cSSteffen Eiden return next; 8151b09c13cSSteffen Eiden return 0; 8161b09c13cSSteffen Eiden } 8171b09c13cSSteffen Eiden 8181b09c13cSSteffen Eiden static long dat_reset_skeys_crste(union crste *crstep, gfn_t gfn, gfn_t next, struct dat_walk *walk) 8191b09c13cSSteffen Eiden { 8201b09c13cSSteffen Eiden phys_addr_t addr, end, origin = crste_origin_large(*crstep); 8211b09c13cSSteffen Eiden 8221b09c13cSSteffen Eiden if (!crstep->h.fc || !crstep->s.fc1.pr) 8231b09c13cSSteffen Eiden return 0; 8241b09c13cSSteffen Eiden 8251b09c13cSSteffen Eiden addr = ((max(gfn, walk->start) - gfn) << PAGE_SHIFT) + origin; 8261b09c13cSSteffen Eiden end = ((min(next, walk->end) - gfn) << PAGE_SHIFT) + origin; 8271b09c13cSSteffen Eiden while (ALIGN(addr + 1, _SEGMENT_SIZE) <= end) 8281b09c13cSSteffen Eiden addr = sske_frame(addr, PAGE_DEFAULT_KEY); 8291b09c13cSSteffen Eiden for ( ; addr < end; addr += PAGE_SIZE) 8301b09c13cSSteffen Eiden page_set_storage_key(addr, PAGE_DEFAULT_KEY, 1); 8311b09c13cSSteffen Eiden 8321b09c13cSSteffen Eiden if (need_resched()) 8331b09c13cSSteffen Eiden return next; 8341b09c13cSSteffen Eiden return 0; 8351b09c13cSSteffen Eiden } 8361b09c13cSSteffen Eiden 8371b09c13cSSteffen Eiden long dat_reset_skeys(union asce asce, gfn_t start) 8381b09c13cSSteffen Eiden { 8391b09c13cSSteffen Eiden const struct dat_walk_ops ops = { 8401b09c13cSSteffen Eiden .pte_entry = dat_reset_skeys_pte, 8411b09c13cSSteffen Eiden .pmd_entry = dat_reset_skeys_crste, 8421b09c13cSSteffen Eiden .pud_entry = dat_reset_skeys_crste, 8431b09c13cSSteffen Eiden }; 8441b09c13cSSteffen Eiden 8451b09c13cSSteffen Eiden return _dat_walk_gfn_range(start, asce_end(asce), asce, &ops, DAT_WALK_IGN_HOLES, NULL); 8461b09c13cSSteffen Eiden } 847a07276d5SSteffen Eiden #endif /* KVM_S390_MANAGES_S390_GUEST */ 8481b09c13cSSteffen Eiden 8491b09c13cSSteffen Eiden struct slot_priv { 8501b09c13cSSteffen Eiden unsigned long token; 8511b09c13cSSteffen Eiden struct kvm_s390_mmu_cache *mc; 8521b09c13cSSteffen Eiden }; 8531b09c13cSSteffen Eiden 8541b09c13cSSteffen Eiden static long _dat_slot_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk) 8551b09c13cSSteffen Eiden { 8561b09c13cSSteffen Eiden struct slot_priv *p = walk->priv; 8571b09c13cSSteffen Eiden union crste dummy = { .val = p->token }; 8581b09c13cSSteffen Eiden union pte new_pte, pte = READ_ONCE(*ptep); 8591b09c13cSSteffen Eiden union pgste pgste; 8601b09c13cSSteffen Eiden 8611b09c13cSSteffen Eiden new_pte = _PTE_TOK(dummy.tok.type, dummy.tok.par); 8621b09c13cSSteffen Eiden 8631b09c13cSSteffen Eiden /* Table entry already in the desired state. */ 8641b09c13cSSteffen Eiden if (pte.val == new_pte.val) 8651b09c13cSSteffen Eiden return 0; 8661b09c13cSSteffen Eiden 8671b09c13cSSteffen Eiden pgste = pgste_get_lock(ptep); 8681b09c13cSSteffen Eiden pgste = __dat_ptep_xchg(ptep, pgste, new_pte, gfn, walk->asce, false); 8691b09c13cSSteffen Eiden pgste.cmma_d = 0; 8701b09c13cSSteffen Eiden pgste_set_unlock(ptep, pgste); 8711b09c13cSSteffen Eiden 8721b09c13cSSteffen Eiden return 0; 8731b09c13cSSteffen Eiden } 8741b09c13cSSteffen Eiden 8751b09c13cSSteffen Eiden static long _dat_slot_crste(union crste *crstep, gfn_t gfn, gfn_t next, struct dat_walk *walk) 8761b09c13cSSteffen Eiden { 8771b09c13cSSteffen Eiden union crste new_crste, crste = READ_ONCE(*crstep); 8781b09c13cSSteffen Eiden struct slot_priv *p = walk->priv; 8791b09c13cSSteffen Eiden 8801b09c13cSSteffen Eiden new_crste.val = p->token; 8811b09c13cSSteffen Eiden new_crste.h.tt = crste.h.tt; 8821b09c13cSSteffen Eiden 8831b09c13cSSteffen Eiden /* Table entry already in the desired state. */ 8841b09c13cSSteffen Eiden if (crste.val == new_crste.val) 8851b09c13cSSteffen Eiden return 0; 8861b09c13cSSteffen Eiden 8871b09c13cSSteffen Eiden /* This table entry needs to be updated. */ 8881b09c13cSSteffen Eiden if (walk->start <= gfn && walk->end >= next) { 8891b09c13cSSteffen Eiden if (!dat_crstep_xchg_atomic(crstep, crste, new_crste, gfn, walk->asce)) 8901b09c13cSSteffen Eiden return -EINVAL; 8911b09c13cSSteffen Eiden /* A lower level table was present, needs to be freed. */ 8921b09c13cSSteffen Eiden if (!crste.h.fc && !crste.h.i) { 8931b09c13cSSteffen Eiden if (is_pmd(crste)) 8941b09c13cSSteffen Eiden dat_free_pt(dereference_pmd(crste.pmd)); 8951b09c13cSSteffen Eiden else 8961b09c13cSSteffen Eiden dat_free_level(dereference_crste(crste), true); 8971b09c13cSSteffen Eiden } 8981b09c13cSSteffen Eiden return 0; 8991b09c13cSSteffen Eiden } 9001b09c13cSSteffen Eiden 9011b09c13cSSteffen Eiden /* A lower level table is present, things will handled there. */ 9021b09c13cSSteffen Eiden if (!crste.h.fc && !crste.h.i) 9031b09c13cSSteffen Eiden return 0; 9041b09c13cSSteffen Eiden /* Split (install a lower level table), and handle things there. */ 9051b09c13cSSteffen Eiden return dat_split_crste(p->mc, crstep, gfn, walk->asce, false); 9061b09c13cSSteffen Eiden } 9071b09c13cSSteffen Eiden 9081b09c13cSSteffen Eiden static const struct dat_walk_ops dat_slot_ops = { 9091b09c13cSSteffen Eiden .pte_entry = _dat_slot_pte, 9101b09c13cSSteffen Eiden .crste_ops = { _dat_slot_crste, _dat_slot_crste, _dat_slot_crste, _dat_slot_crste, }, 9111b09c13cSSteffen Eiden }; 9121b09c13cSSteffen Eiden 9131b09c13cSSteffen Eiden int dat_set_slot(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t start, gfn_t end, 9141b09c13cSSteffen Eiden u16 type, u16 param) 9151b09c13cSSteffen Eiden { 9161b09c13cSSteffen Eiden struct slot_priv priv = { 9171b09c13cSSteffen Eiden .token = _CRSTE_TOK(0, type, param).val, 9181b09c13cSSteffen Eiden .mc = mc, 9191b09c13cSSteffen Eiden }; 9201b09c13cSSteffen Eiden 9211b09c13cSSteffen Eiden return _dat_walk_gfn_range(start, end, asce, &dat_slot_ops, 9221b09c13cSSteffen Eiden DAT_WALK_IGN_HOLES | DAT_WALK_ANY, &priv); 9231b09c13cSSteffen Eiden } 9241b09c13cSSteffen Eiden 9251b09c13cSSteffen Eiden static void pgste_set_unlock_multiple(union pte *first, int n, union pgste *pgstes) 9261b09c13cSSteffen Eiden { 9271b09c13cSSteffen Eiden int i; 9281b09c13cSSteffen Eiden 9291b09c13cSSteffen Eiden for (i = 0; i < n; i++) 9301b09c13cSSteffen Eiden pgste_set_unlock(first + i, pgstes[i]); 9311b09c13cSSteffen Eiden } 9321b09c13cSSteffen Eiden 9331b09c13cSSteffen Eiden static bool pgste_get_trylock_multiple(union pte *first, int n, union pgste *pgstes) 9341b09c13cSSteffen Eiden { 9351b09c13cSSteffen Eiden int i; 9361b09c13cSSteffen Eiden 9371b09c13cSSteffen Eiden for (i = 0; i < n; i++) { 9381b09c13cSSteffen Eiden if (!pgste_get_trylock(first + i, pgstes + i)) 9391b09c13cSSteffen Eiden break; 9401b09c13cSSteffen Eiden } 9411b09c13cSSteffen Eiden if (i == n) 9421b09c13cSSteffen Eiden return true; 9431b09c13cSSteffen Eiden pgste_set_unlock_multiple(first, i, pgstes); 9441b09c13cSSteffen Eiden return false; 9451b09c13cSSteffen Eiden } 9461b09c13cSSteffen Eiden 9471b09c13cSSteffen Eiden unsigned long dat_get_ptval(struct page_table *table, struct ptval_param param) 9481b09c13cSSteffen Eiden { 9491b09c13cSSteffen Eiden union pgste pgstes[4] = {}; 9501b09c13cSSteffen Eiden unsigned long res = 0; 9511b09c13cSSteffen Eiden int i, n; 9521b09c13cSSteffen Eiden 9531b09c13cSSteffen Eiden n = param.len + 1; 9541b09c13cSSteffen Eiden 9551b09c13cSSteffen Eiden while (!pgste_get_trylock_multiple(table->ptes + param.offset, n, pgstes)) 9561b09c13cSSteffen Eiden cpu_relax(); 9571b09c13cSSteffen Eiden 9581b09c13cSSteffen Eiden for (i = 0; i < n; i++) 9591b09c13cSSteffen Eiden res = res << 16 | pgstes[i].val16; 9601b09c13cSSteffen Eiden 9611b09c13cSSteffen Eiden pgste_set_unlock_multiple(table->ptes + param.offset, n, pgstes); 9621b09c13cSSteffen Eiden return res; 9631b09c13cSSteffen Eiden } 9641b09c13cSSteffen Eiden 9651b09c13cSSteffen Eiden void dat_set_ptval(struct page_table *table, struct ptval_param param, unsigned long val) 9661b09c13cSSteffen Eiden { 9671b09c13cSSteffen Eiden union pgste pgstes[4] = {}; 9681b09c13cSSteffen Eiden int i, n; 9691b09c13cSSteffen Eiden 9701b09c13cSSteffen Eiden n = param.len + 1; 9711b09c13cSSteffen Eiden 9721b09c13cSSteffen Eiden while (!pgste_get_trylock_multiple(table->ptes + param.offset, n, pgstes)) 9731b09c13cSSteffen Eiden cpu_relax(); 9741b09c13cSSteffen Eiden 9751b09c13cSSteffen Eiden for (i = param.len; i >= 0; i--) { 9761b09c13cSSteffen Eiden pgstes[i].val16 = val; 9771b09c13cSSteffen Eiden val = val >> 16; 9781b09c13cSSteffen Eiden } 9791b09c13cSSteffen Eiden 9801b09c13cSSteffen Eiden pgste_set_unlock_multiple(table->ptes + param.offset, n, pgstes); 9811b09c13cSSteffen Eiden } 9821b09c13cSSteffen Eiden 9831b09c13cSSteffen Eiden static long _dat_test_young_pte(union pte *ptep, gfn_t start, gfn_t end, struct dat_walk *walk) 9841b09c13cSSteffen Eiden { 9851b09c13cSSteffen Eiden return ptep->s.y; 9861b09c13cSSteffen Eiden } 9871b09c13cSSteffen Eiden 9881b09c13cSSteffen Eiden static long _dat_test_young_crste(union crste *crstep, gfn_t start, gfn_t end, 9891b09c13cSSteffen Eiden struct dat_walk *walk) 9901b09c13cSSteffen Eiden { 9911b09c13cSSteffen Eiden return crstep->h.fc && crstep->s.fc1.y; 9921b09c13cSSteffen Eiden } 9931b09c13cSSteffen Eiden 9941b09c13cSSteffen Eiden static const struct dat_walk_ops test_age_ops = { 9951b09c13cSSteffen Eiden .pte_entry = _dat_test_young_pte, 9961b09c13cSSteffen Eiden .pmd_entry = _dat_test_young_crste, 9971b09c13cSSteffen Eiden .pud_entry = _dat_test_young_crste, 9981b09c13cSSteffen Eiden }; 9991b09c13cSSteffen Eiden 10001b09c13cSSteffen Eiden /** 10011b09c13cSSteffen Eiden * dat_test_age_gfn() - Test young. 10021b09c13cSSteffen Eiden * @asce: The ASCE whose address range is to be tested. 10031b09c13cSSteffen Eiden * @start: The first guest frame of the range to check. 10041b09c13cSSteffen Eiden * @end: The guest frame after the last in the range. 10051b09c13cSSteffen Eiden * 10061b09c13cSSteffen Eiden * Context: called by KVM common code with the kvm mmu write lock held. 10071b09c13cSSteffen Eiden * 10081b09c13cSSteffen Eiden * Return: %true if any page in the given range is young, otherwise %false. 10091b09c13cSSteffen Eiden */ 10101b09c13cSSteffen Eiden bool dat_test_age_gfn(union asce asce, gfn_t start, gfn_t end) 10111b09c13cSSteffen Eiden { 10121b09c13cSSteffen Eiden return _dat_walk_gfn_range(start, end, asce, &test_age_ops, 0, NULL) > 0; 10131b09c13cSSteffen Eiden } 10141b09c13cSSteffen Eiden 1015*188a15ccSSteffen Eiden #if KVM_S390_MANAGES_S390_GUEST 10161b09c13cSSteffen Eiden static long dat_set_pn_crste(union crste *crstep, gfn_t gfn, gfn_t next, struct dat_walk *walk) 10171b09c13cSSteffen Eiden { 10181b09c13cSSteffen Eiden union crste newcrste, oldcrste; 10191b09c13cSSteffen Eiden int *n = walk->priv; 10201b09c13cSSteffen Eiden 10211b09c13cSSteffen Eiden do { 10221b09c13cSSteffen Eiden oldcrste = READ_ONCE(*crstep); 10231b09c13cSSteffen Eiden if (!oldcrste.h.fc || oldcrste.h.i || oldcrste.h.p) 10241b09c13cSSteffen Eiden return 0; 10251b09c13cSSteffen Eiden if (oldcrste.s.fc1.prefix_notif) 10261b09c13cSSteffen Eiden break; 10271b09c13cSSteffen Eiden newcrste = oldcrste; 10281b09c13cSSteffen Eiden newcrste.s.fc1.prefix_notif = 1; 10291b09c13cSSteffen Eiden } while (!dat_crstep_xchg_atomic(crstep, oldcrste, newcrste, gfn, walk->asce)); 10301b09c13cSSteffen Eiden *n = 2; 10311b09c13cSSteffen Eiden return 0; 10321b09c13cSSteffen Eiden } 10331b09c13cSSteffen Eiden 10341b09c13cSSteffen Eiden static long dat_set_pn_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk) 10351b09c13cSSteffen Eiden { 10361b09c13cSSteffen Eiden int *n = walk->priv; 10371b09c13cSSteffen Eiden union pgste pgste; 10381b09c13cSSteffen Eiden 10391b09c13cSSteffen Eiden pgste = pgste_get_lock(ptep); 10401b09c13cSSteffen Eiden if (!ptep->h.i && !ptep->h.p) { 10411b09c13cSSteffen Eiden pgste.prefix_notif = 1; 10421b09c13cSSteffen Eiden *n += 1; 10431b09c13cSSteffen Eiden } 10441b09c13cSSteffen Eiden pgste_set_unlock(ptep, pgste); 10451b09c13cSSteffen Eiden return 0; 10461b09c13cSSteffen Eiden } 10471b09c13cSSteffen Eiden 10481b09c13cSSteffen Eiden int dat_set_prefix_notif_bit(union asce asce, gfn_t gfn) 10491b09c13cSSteffen Eiden { 10501b09c13cSSteffen Eiden static const struct dat_walk_ops ops = { 10511b09c13cSSteffen Eiden .pte_entry = dat_set_pn_pte, 10521b09c13cSSteffen Eiden .pmd_entry = dat_set_pn_crste, 10531b09c13cSSteffen Eiden .pud_entry = dat_set_pn_crste, 10541b09c13cSSteffen Eiden }; 10551b09c13cSSteffen Eiden 10561b09c13cSSteffen Eiden int n = 0; 10571b09c13cSSteffen Eiden 10581b09c13cSSteffen Eiden _dat_walk_gfn_range(gfn, gfn + 2, asce, &ops, DAT_WALK_IGN_HOLES, &n); 10591b09c13cSSteffen Eiden if (n != 2) 10601b09c13cSSteffen Eiden return -EAGAIN; 10611b09c13cSSteffen Eiden return 0; 10621b09c13cSSteffen Eiden } 10631b09c13cSSteffen Eiden 10641b09c13cSSteffen Eiden /** 10651b09c13cSSteffen Eiden * dat_perform_essa() - Perform ESSA actions on the PGSTE. 10661b09c13cSSteffen Eiden * @asce: The asce to operate on. 10671b09c13cSSteffen Eiden * @gfn: The guest page frame to operate on. 10681b09c13cSSteffen Eiden * @orc: The specific action to perform, see the ESSA_SET_* macros. 10691b09c13cSSteffen Eiden * @state: The storage attributes to be returned to the guest. 10701b09c13cSSteffen Eiden * @dirty: Returns whether the function dirtied a previously clean entry. 10711b09c13cSSteffen Eiden * 10721b09c13cSSteffen Eiden * Context: Called with kvm->mmu_lock held. 10731b09c13cSSteffen Eiden * 10741b09c13cSSteffen Eiden * Return: 10751b09c13cSSteffen Eiden * * %1 if the page state has been altered and the page is to be added to the CBRL 10761b09c13cSSteffen Eiden * * %0 if the page state has been altered, but the page is not to be added to the CBRL 10771b09c13cSSteffen Eiden * * %-1 if the page state has not been altered and the page is not to be added to the CBRL 10781b09c13cSSteffen Eiden */ 10791b09c13cSSteffen Eiden int dat_perform_essa(union asce asce, gfn_t gfn, int orc, union essa_state *state, bool *dirty) 10801b09c13cSSteffen Eiden { 10811b09c13cSSteffen Eiden union crste *crstep; 10821b09c13cSSteffen Eiden union pgste pgste; 10831b09c13cSSteffen Eiden union pte *ptep; 10841b09c13cSSteffen Eiden int res = 0; 10851b09c13cSSteffen Eiden 10861b09c13cSSteffen Eiden if (dat_entry_walk(NULL, gfn, asce, 0, TABLE_TYPE_PAGE_TABLE, &crstep, &ptep)) { 10871b09c13cSSteffen Eiden *state = (union essa_state) { .exception = 1 }; 10881b09c13cSSteffen Eiden return -1; 10891b09c13cSSteffen Eiden } 10901b09c13cSSteffen Eiden 10911b09c13cSSteffen Eiden pgste = pgste_get_lock(ptep); 10921b09c13cSSteffen Eiden 10931b09c13cSSteffen Eiden *state = (union essa_state) { 10941b09c13cSSteffen Eiden .content = (ptep->h.i << 1) + (ptep->h.i && pgste.zero), 10951b09c13cSSteffen Eiden .nodat = pgste.nodat, 10961b09c13cSSteffen Eiden .usage = pgste.usage, 10971b09c13cSSteffen Eiden }; 10981b09c13cSSteffen Eiden 10991b09c13cSSteffen Eiden switch (orc) { 11001b09c13cSSteffen Eiden case ESSA_GET_STATE: 11011b09c13cSSteffen Eiden res = -1; 11021b09c13cSSteffen Eiden break; 11031b09c13cSSteffen Eiden case ESSA_SET_STABLE: 11041b09c13cSSteffen Eiden pgste.usage = PGSTE_GPS_USAGE_STABLE; 11051b09c13cSSteffen Eiden pgste.nodat = 0; 11061b09c13cSSteffen Eiden break; 11071b09c13cSSteffen Eiden case ESSA_SET_UNUSED: 11081b09c13cSSteffen Eiden pgste.usage = PGSTE_GPS_USAGE_UNUSED; 11091b09c13cSSteffen Eiden if (ptep->h.i) 11101b09c13cSSteffen Eiden res = 1; 11111b09c13cSSteffen Eiden break; 11121b09c13cSSteffen Eiden case ESSA_SET_VOLATILE: 11131b09c13cSSteffen Eiden pgste.usage = PGSTE_GPS_USAGE_VOLATILE; 11141b09c13cSSteffen Eiden if (ptep->h.i) 11151b09c13cSSteffen Eiden res = 1; 11161b09c13cSSteffen Eiden break; 11171b09c13cSSteffen Eiden case ESSA_SET_POT_VOLATILE: 11181b09c13cSSteffen Eiden if (!ptep->h.i) { 11191b09c13cSSteffen Eiden pgste.usage = PGSTE_GPS_USAGE_POT_VOLATILE; 11201b09c13cSSteffen Eiden } else if (pgste.zero) { 11211b09c13cSSteffen Eiden pgste.usage = PGSTE_GPS_USAGE_VOLATILE; 11221b09c13cSSteffen Eiden } else if (!pgste.gc) { 11231b09c13cSSteffen Eiden pgste.usage = PGSTE_GPS_USAGE_VOLATILE; 11241b09c13cSSteffen Eiden res = 1; 11251b09c13cSSteffen Eiden } 11261b09c13cSSteffen Eiden break; 11271b09c13cSSteffen Eiden case ESSA_SET_STABLE_RESIDENT: 11281b09c13cSSteffen Eiden pgste.usage = PGSTE_GPS_USAGE_STABLE; 11291b09c13cSSteffen Eiden /* 11301b09c13cSSteffen Eiden * Since the resident state can go away any time after this 11311b09c13cSSteffen Eiden * call, we will not make this page resident. We can revisit 11321b09c13cSSteffen Eiden * this decision if a guest will ever start using this. 11331b09c13cSSteffen Eiden */ 11341b09c13cSSteffen Eiden break; 11351b09c13cSSteffen Eiden case ESSA_SET_STABLE_IF_RESIDENT: 11361b09c13cSSteffen Eiden if (!ptep->h.i) 11371b09c13cSSteffen Eiden pgste.usage = PGSTE_GPS_USAGE_STABLE; 11381b09c13cSSteffen Eiden break; 11391b09c13cSSteffen Eiden case ESSA_SET_STABLE_NODAT: 11401b09c13cSSteffen Eiden pgste.usage = PGSTE_GPS_USAGE_STABLE; 11411b09c13cSSteffen Eiden pgste.nodat = 1; 11421b09c13cSSteffen Eiden break; 11431b09c13cSSteffen Eiden default: 11441b09c13cSSteffen Eiden WARN_ONCE(1, "Invalid ORC!"); 11451b09c13cSSteffen Eiden res = -1; 11461b09c13cSSteffen Eiden break; 11471b09c13cSSteffen Eiden } 11481b09c13cSSteffen Eiden /* If we are discarding a page, set it to logical zero. */ 11491b09c13cSSteffen Eiden pgste.zero = res == 1; 11501b09c13cSSteffen Eiden if (orc > 0) { 11511b09c13cSSteffen Eiden *dirty = !pgste.cmma_d; 11521b09c13cSSteffen Eiden pgste.cmma_d = 1; 11531b09c13cSSteffen Eiden } 11541b09c13cSSteffen Eiden 11551b09c13cSSteffen Eiden pgste_set_unlock(ptep, pgste); 11561b09c13cSSteffen Eiden 11571b09c13cSSteffen Eiden return res; 11581b09c13cSSteffen Eiden } 11591b09c13cSSteffen Eiden 11601b09c13cSSteffen Eiden static long dat_reset_cmma_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk) 11611b09c13cSSteffen Eiden { 11621b09c13cSSteffen Eiden union pgste pgste; 11631b09c13cSSteffen Eiden 11641b09c13cSSteffen Eiden pgste = pgste_get_lock(ptep); 11651b09c13cSSteffen Eiden pgste.usage = 0; 11661b09c13cSSteffen Eiden pgste.nodat = 0; 11671b09c13cSSteffen Eiden pgste.cmma_d = 0; 11681b09c13cSSteffen Eiden pgste_set_unlock(ptep, pgste); 11691b09c13cSSteffen Eiden if (need_resched()) 11701b09c13cSSteffen Eiden return next; 11711b09c13cSSteffen Eiden return 0; 11721b09c13cSSteffen Eiden } 11731b09c13cSSteffen Eiden 11741b09c13cSSteffen Eiden long dat_reset_cmma(union asce asce, gfn_t start) 11751b09c13cSSteffen Eiden { 11761b09c13cSSteffen Eiden const struct dat_walk_ops dat_reset_cmma_ops = { 11771b09c13cSSteffen Eiden .pte_entry = dat_reset_cmma_pte, 11781b09c13cSSteffen Eiden }; 11791b09c13cSSteffen Eiden 11801b09c13cSSteffen Eiden return _dat_walk_gfn_range(start, asce_end(asce), asce, &dat_reset_cmma_ops, 11811b09c13cSSteffen Eiden DAT_WALK_IGN_HOLES, NULL); 11821b09c13cSSteffen Eiden } 11831b09c13cSSteffen Eiden 11841b09c13cSSteffen Eiden struct dat_get_cmma_state { 11851b09c13cSSteffen Eiden gfn_t start; 11861b09c13cSSteffen Eiden gfn_t end; 11871b09c13cSSteffen Eiden unsigned int count; 11881b09c13cSSteffen Eiden u8 *values; 11891b09c13cSSteffen Eiden atomic64_t *remaining; 11901b09c13cSSteffen Eiden }; 11911b09c13cSSteffen Eiden 11921b09c13cSSteffen Eiden static long __dat_peek_cmma_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk) 11931b09c13cSSteffen Eiden { 11941b09c13cSSteffen Eiden struct dat_get_cmma_state *state = walk->priv; 11951b09c13cSSteffen Eiden union pgste pgste; 11961b09c13cSSteffen Eiden 11971b09c13cSSteffen Eiden pgste = pgste_get_lock(ptep); 11981b09c13cSSteffen Eiden state->values[gfn - walk->start] = pgste.usage | (pgste.nodat << 6); 11991b09c13cSSteffen Eiden pgste_set_unlock(ptep, pgste); 12001b09c13cSSteffen Eiden state->end = next; 12011b09c13cSSteffen Eiden 12021b09c13cSSteffen Eiden return 0; 12031b09c13cSSteffen Eiden } 12041b09c13cSSteffen Eiden 12051b09c13cSSteffen Eiden static long __dat_peek_cmma_crste(union crste *crstep, gfn_t gfn, gfn_t next, struct dat_walk *walk) 12061b09c13cSSteffen Eiden { 12071b09c13cSSteffen Eiden struct dat_get_cmma_state *state = walk->priv; 12081b09c13cSSteffen Eiden 12091b09c13cSSteffen Eiden if (crstep->h.i) 12101b09c13cSSteffen Eiden state->end = min(walk->end, next); 12111b09c13cSSteffen Eiden return 0; 12121b09c13cSSteffen Eiden } 12131b09c13cSSteffen Eiden 12141b09c13cSSteffen Eiden int dat_peek_cmma(gfn_t start, union asce asce, unsigned int *count, u8 *values) 12151b09c13cSSteffen Eiden { 12161b09c13cSSteffen Eiden const struct dat_walk_ops ops = { 12171b09c13cSSteffen Eiden .pte_entry = __dat_peek_cmma_pte, 12181b09c13cSSteffen Eiden .pmd_entry = __dat_peek_cmma_crste, 12191b09c13cSSteffen Eiden .pud_entry = __dat_peek_cmma_crste, 12201b09c13cSSteffen Eiden .p4d_entry = __dat_peek_cmma_crste, 12211b09c13cSSteffen Eiden .pgd_entry = __dat_peek_cmma_crste, 12221b09c13cSSteffen Eiden }; 12231b09c13cSSteffen Eiden struct dat_get_cmma_state state = { .values = values, }; 12241b09c13cSSteffen Eiden int rc; 12251b09c13cSSteffen Eiden 12261b09c13cSSteffen Eiden rc = _dat_walk_gfn_range(start, start + *count, asce, &ops, DAT_WALK_DEFAULT, &state); 12271b09c13cSSteffen Eiden *count = state.end >= start ? state.end - start : 0; 12281b09c13cSSteffen Eiden /* Return success if at least one value was saved, otherwise an error. */ 12291b09c13cSSteffen Eiden return (rc == -EFAULT && *count > 0) ? 0 : rc; 12301b09c13cSSteffen Eiden } 12311b09c13cSSteffen Eiden 12321b09c13cSSteffen Eiden static long __dat_get_cmma_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk) 12331b09c13cSSteffen Eiden { 12341b09c13cSSteffen Eiden struct dat_get_cmma_state *state = walk->priv; 12351b09c13cSSteffen Eiden union pgste pgste; 12361b09c13cSSteffen Eiden 12371b09c13cSSteffen Eiden if (state->start != -1) { 12381b09c13cSSteffen Eiden if ((gfn - state->end) > KVM_S390_MAX_BIT_DISTANCE) 12391b09c13cSSteffen Eiden return 1; 12401b09c13cSSteffen Eiden if (gfn - state->start >= state->count) 12411b09c13cSSteffen Eiden return 1; 12421b09c13cSSteffen Eiden } 12431b09c13cSSteffen Eiden 12441b09c13cSSteffen Eiden if (!READ_ONCE(*pgste_of(ptep)).cmma_d) 12451b09c13cSSteffen Eiden return 0; 12461b09c13cSSteffen Eiden 12471b09c13cSSteffen Eiden pgste = pgste_get_lock(ptep); 12481b09c13cSSteffen Eiden if (pgste.cmma_d) { 12491b09c13cSSteffen Eiden if (state->start == -1) 12501b09c13cSSteffen Eiden state->start = gfn; 12511b09c13cSSteffen Eiden pgste.cmma_d = 0; 12521b09c13cSSteffen Eiden atomic64_dec(state->remaining); 12531b09c13cSSteffen Eiden state->values[gfn - state->start] = pgste.usage | pgste.nodat << 6; 12541b09c13cSSteffen Eiden state->end = next; 12551b09c13cSSteffen Eiden } 12561b09c13cSSteffen Eiden pgste_set_unlock(ptep, pgste); 12571b09c13cSSteffen Eiden return 0; 12581b09c13cSSteffen Eiden } 12591b09c13cSSteffen Eiden 12601b09c13cSSteffen Eiden int dat_get_cmma(union asce asce, gfn_t *start, unsigned int *count, u8 *values, atomic64_t *rem) 12611b09c13cSSteffen Eiden { 12621b09c13cSSteffen Eiden const struct dat_walk_ops ops = { .pte_entry = __dat_get_cmma_pte, }; 12631b09c13cSSteffen Eiden struct dat_get_cmma_state state = { 12641b09c13cSSteffen Eiden .remaining = rem, 12651b09c13cSSteffen Eiden .values = values, 12661b09c13cSSteffen Eiden .count = *count, 12671b09c13cSSteffen Eiden .start = -1, 12681b09c13cSSteffen Eiden }; 12691b09c13cSSteffen Eiden 12701b09c13cSSteffen Eiden _dat_walk_gfn_range(*start, asce_end(asce), asce, &ops, DAT_WALK_IGN_HOLES, &state); 12711b09c13cSSteffen Eiden /* If no dirty pages were found, wrap around and continue searching */ 12721b09c13cSSteffen Eiden if (*start && state.start == -1) 12731b09c13cSSteffen Eiden _dat_walk_gfn_range(0, *start, asce, &ops, DAT_WALK_IGN_HOLES, &state); 12741b09c13cSSteffen Eiden 12751b09c13cSSteffen Eiden if (state.start == -1) { 12761b09c13cSSteffen Eiden *count = 0; 12771b09c13cSSteffen Eiden } else { 12781b09c13cSSteffen Eiden *count = state.end - state.start; 12791b09c13cSSteffen Eiden *start = state.start; 12801b09c13cSSteffen Eiden } 12811b09c13cSSteffen Eiden 12821b09c13cSSteffen Eiden return 0; 12831b09c13cSSteffen Eiden } 12841b09c13cSSteffen Eiden 12851b09c13cSSteffen Eiden struct dat_set_cmma_state { 12861b09c13cSSteffen Eiden unsigned long mask; 12871b09c13cSSteffen Eiden const u8 *bits; 12881b09c13cSSteffen Eiden }; 12891b09c13cSSteffen Eiden 12901b09c13cSSteffen Eiden static long __dat_set_cmma_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk) 12911b09c13cSSteffen Eiden { 12921b09c13cSSteffen Eiden struct dat_set_cmma_state *state = walk->priv; 12931b09c13cSSteffen Eiden union pgste pgste, tmp; 12941b09c13cSSteffen Eiden 12951b09c13cSSteffen Eiden tmp.val = (state->bits[gfn - walk->start] << 24) & state->mask; 12961b09c13cSSteffen Eiden 12971b09c13cSSteffen Eiden pgste = pgste_get_lock(ptep); 12981b09c13cSSteffen Eiden pgste.usage = tmp.usage; 12991b09c13cSSteffen Eiden pgste.nodat = tmp.nodat; 13001b09c13cSSteffen Eiden pgste_set_unlock(ptep, pgste); 13011b09c13cSSteffen Eiden 13021b09c13cSSteffen Eiden return 0; 13031b09c13cSSteffen Eiden } 13041b09c13cSSteffen Eiden 13051b09c13cSSteffen Eiden /** 13061b09c13cSSteffen Eiden * dat_set_cmma_bits() - Set CMMA bits for a range of guest pages. 13071b09c13cSSteffen Eiden * @mc: Cache used for allocations. 13081b09c13cSSteffen Eiden * @asce: The ASCE of the guest. 13091b09c13cSSteffen Eiden * @gfn: The guest frame of the fist page whose CMMA bits are to set. 13101b09c13cSSteffen Eiden * @count: How many pages need to be processed. 13111b09c13cSSteffen Eiden * @mask: Which PGSTE bits should be set. 13121b09c13cSSteffen Eiden * @bits: Points to an array with the CMMA attributes. 13131b09c13cSSteffen Eiden * 13141b09c13cSSteffen Eiden * This function sets the CMMA attributes for the given pages. If the input 13151b09c13cSSteffen Eiden * buffer has zero length, no action is taken, otherwise the attributes are 13161b09c13cSSteffen Eiden * set and the mm->context.uses_cmm flag is set. 13171b09c13cSSteffen Eiden * 13181b09c13cSSteffen Eiden * Each byte in @bits contains new values for bits 32-39 of the PGSTE. 13191b09c13cSSteffen Eiden * Currently, only the fields NT and US are applied. 13201b09c13cSSteffen Eiden * 13211b09c13cSSteffen Eiden * Return: %0 in case of success, a negative error value otherwise. 13221b09c13cSSteffen Eiden */ 13231b09c13cSSteffen Eiden int dat_set_cmma_bits(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t gfn, 13241b09c13cSSteffen Eiden unsigned long count, unsigned long mask, const uint8_t *bits) 13251b09c13cSSteffen Eiden { 13261b09c13cSSteffen Eiden const struct dat_walk_ops ops = { .pte_entry = __dat_set_cmma_pte, }; 13271b09c13cSSteffen Eiden struct dat_set_cmma_state state = { .mask = mask, .bits = bits, }; 13281b09c13cSSteffen Eiden union crste *crstep; 13291b09c13cSSteffen Eiden union pte *ptep; 13301b09c13cSSteffen Eiden gfn_t cur; 13311b09c13cSSteffen Eiden int rc; 13321b09c13cSSteffen Eiden 13331b09c13cSSteffen Eiden for (cur = ALIGN_DOWN(gfn, _PAGE_ENTRIES); cur < gfn + count; cur += _PAGE_ENTRIES) { 13341b09c13cSSteffen Eiden rc = dat_entry_walk(mc, cur, asce, DAT_WALK_ALLOC, TABLE_TYPE_PAGE_TABLE, 13351b09c13cSSteffen Eiden &crstep, &ptep); 13361b09c13cSSteffen Eiden if (rc) 13371b09c13cSSteffen Eiden return rc; 13381b09c13cSSteffen Eiden } 13391b09c13cSSteffen Eiden return _dat_walk_gfn_range(gfn, gfn + count, asce, &ops, DAT_WALK_IGN_HOLES, &state); 13401b09c13cSSteffen Eiden } 1341ce4bee6aSSteffen Eiden #endif /* KVM_S390_MANAGES_S390_GUEST */ 1342