forked from deepin-community/kernel
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
LoongArch: KVM: Implement kvm mmu operations
Implement LoongArch kvm mmu, it is used to switch gpa to hpa when guest exit because of address translation exception. This patch implement: allocating gpa page table, searching gpa from it, and flushing guest gpa in the table. Reviewed-by: Bibo Mao <[email protected]> Tested-by: Huacai Chen <[email protected]> Signed-off-by: Tianrui Zhao <[email protected]> Signed-off-by: Huacai Chen <[email protected]>
- Loading branch information
1 parent
d7f4ed4
commit 752e2cd
Showing
2 changed files
with
1,053 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
/* 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 <linux/kvm_host.h> | ||
#include <asm/pgalloc.h> | ||
#include <asm/tlb.h> | ||
|
||
/* | ||
* 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__ */ |
Oops, something went wrong.