Skip to content

Commit

Permalink
LoongArch: KVM: Implement kvm mmu operations
Browse files Browse the repository at this point in the history
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
zhaotianrui-loongson authored and chenhuacai committed Oct 2, 2023
1 parent d7f4ed4 commit 752e2cd
Show file tree
Hide file tree
Showing 2 changed files with 1,053 additions and 0 deletions.
139 changes: 139 additions & 0 deletions arch/loongarch/include/asm/kvm_mmu.h
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__ */
Loading

0 comments on commit 752e2cd

Please sign in to comment.