/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) 2020-2023 Loongson Technology Corporation Limited */ #ifndef __ASM_LOONGARCH_KVM_MMU_H__ #define __ASM_LOONGARCH_KVM_MMU_H__ #include #include #include /* * KVM_MMU_CACHE_MIN_PAGES is the number of GPA page table translation levels * for which pages need to be cached. */ #define KVM_MMU_CACHE_MIN_PAGES (CONFIG_PGTABLE_LEVELS - 1) #define _KVM_FLUSH_PGTABLE 0x1 #define _KVM_HAS_PGMASK 0x2 #define kvm_pfn_pte(pfn, prot) (((pfn) << PFN_PTE_SHIFT) | pgprot_val(prot)) #define kvm_pte_pfn(x) ((phys_addr_t)((x & _PFN_MASK) >> PFN_PTE_SHIFT)) typedef unsigned long kvm_pte_t; typedef struct kvm_ptw_ctx kvm_ptw_ctx; typedef int (*kvm_pte_ops)(kvm_pte_t *pte, phys_addr_t addr, kvm_ptw_ctx *ctx); struct kvm_ptw_ctx { kvm_pte_ops ops; unsigned long flag; /* for kvm_arch_mmu_enable_log_dirty_pt_masked use */ unsigned long mask; unsigned long gfn; /* page walk mmu info */ unsigned int level; unsigned long pgtable_shift; unsigned long invalid_entry; unsigned long *invalid_ptes; unsigned int *pte_shifts; void *opaque; /* free pte table page list */ struct list_head list; }; kvm_pte_t *kvm_pgd_alloc(void); static inline void kvm_set_pte(kvm_pte_t *ptep, kvm_pte_t val) { WRITE_ONCE(*ptep, val); } static inline int kvm_pte_write(kvm_pte_t pte) { return pte & _PAGE_WRITE; } static inline int kvm_pte_dirty(kvm_pte_t pte) { return pte & _PAGE_DIRTY; } static inline int kvm_pte_young(kvm_pte_t pte) { return pte & _PAGE_ACCESSED; } static inline int kvm_pte_huge(kvm_pte_t pte) { return pte & _PAGE_HUGE; } static inline kvm_pte_t kvm_pte_mkyoung(kvm_pte_t pte) { return pte | _PAGE_ACCESSED; } static inline kvm_pte_t kvm_pte_mkold(kvm_pte_t pte) { return pte & ~_PAGE_ACCESSED; } static inline kvm_pte_t kvm_pte_mkdirty(kvm_pte_t pte) { return pte | _PAGE_DIRTY; } static inline kvm_pte_t kvm_pte_mkclean(kvm_pte_t pte) { return pte & ~_PAGE_DIRTY; } static inline kvm_pte_t kvm_pte_mkhuge(kvm_pte_t pte) { return pte | _PAGE_HUGE; } static inline kvm_pte_t kvm_pte_mksmall(kvm_pte_t pte) { return pte & ~_PAGE_HUGE; } static inline int kvm_need_flush(kvm_ptw_ctx *ctx) { return ctx->flag & _KVM_FLUSH_PGTABLE; } static inline kvm_pte_t *kvm_pgtable_offset(kvm_ptw_ctx *ctx, kvm_pte_t *table, phys_addr_t addr) { return table + ((addr >> ctx->pgtable_shift) & (PTRS_PER_PTE - 1)); } static inline phys_addr_t kvm_pgtable_addr_end(kvm_ptw_ctx *ctx, phys_addr_t addr, phys_addr_t end) { phys_addr_t boundary, size; size = 0x1UL << ctx->pgtable_shift; boundary = (addr + size) & ~(size - 1); return (boundary - 1 < end - 1) ? boundary : end; } static inline int kvm_pte_present(kvm_ptw_ctx *ctx, kvm_pte_t *entry) { if (!ctx || ctx->level == 0) return !!(*entry & _PAGE_PRESENT); return *entry != ctx->invalid_entry; } static inline int kvm_pte_none(kvm_ptw_ctx *ctx, kvm_pte_t *entry) { return *entry == ctx->invalid_entry; } static inline void kvm_ptw_enter(kvm_ptw_ctx *ctx) { ctx->level--; ctx->pgtable_shift = ctx->pte_shifts[ctx->level]; ctx->invalid_entry = ctx->invalid_ptes[ctx->level]; } static inline void kvm_ptw_exit(kvm_ptw_ctx *ctx) { ctx->level++; ctx->pgtable_shift = ctx->pte_shifts[ctx->level]; ctx->invalid_entry = ctx->invalid_ptes[ctx->level]; } #endif /* __ASM_LOONGARCH_KVM_MMU_H__ */