162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 362306a36Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 462306a36Sopenharmony_ci * for more details. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * KVM/MIPS MMU handling in the KVM module. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. 962306a36Sopenharmony_ci * Authors: Sanjay Lal <sanjayl@kymasys.com> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/highmem.h> 1362306a36Sopenharmony_ci#include <linux/kvm_host.h> 1462306a36Sopenharmony_ci#include <linux/uaccess.h> 1562306a36Sopenharmony_ci#include <asm/mmu_context.h> 1662306a36Sopenharmony_ci#include <asm/pgalloc.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* 1962306a36Sopenharmony_ci * KVM_MMU_CACHE_MIN_PAGES is the number of GPA page table translation levels 2062306a36Sopenharmony_ci * for which pages need to be cached. 2162306a36Sopenharmony_ci */ 2262306a36Sopenharmony_ci#if defined(__PAGETABLE_PMD_FOLDED) 2362306a36Sopenharmony_ci#define KVM_MMU_CACHE_MIN_PAGES 1 2462306a36Sopenharmony_ci#else 2562306a36Sopenharmony_ci#define KVM_MMU_CACHE_MIN_PAGES 2 2662306a36Sopenharmony_ci#endif 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_civoid kvm_mmu_free_memory_caches(struct kvm_vcpu *vcpu) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci kvm_mmu_free_memory_cache(&vcpu->arch.mmu_page_cache); 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/** 3462306a36Sopenharmony_ci * kvm_pgd_init() - Initialise KVM GPA page directory. 3562306a36Sopenharmony_ci * @page: Pointer to page directory (PGD) for KVM GPA. 3662306a36Sopenharmony_ci * 3762306a36Sopenharmony_ci * Initialise a KVM GPA page directory with pointers to the invalid table, i.e. 3862306a36Sopenharmony_ci * representing no mappings. This is similar to pgd_init(), however it 3962306a36Sopenharmony_ci * initialises all the page directory pointers, not just the ones corresponding 4062306a36Sopenharmony_ci * to the userland address space (since it is for the guest physical address 4162306a36Sopenharmony_ci * space rather than a virtual address space). 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_cistatic void kvm_pgd_init(void *page) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci unsigned long *p, *end; 4662306a36Sopenharmony_ci unsigned long entry; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#ifdef __PAGETABLE_PMD_FOLDED 4962306a36Sopenharmony_ci entry = (unsigned long)invalid_pte_table; 5062306a36Sopenharmony_ci#else 5162306a36Sopenharmony_ci entry = (unsigned long)invalid_pmd_table; 5262306a36Sopenharmony_ci#endif 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci p = (unsigned long *)page; 5562306a36Sopenharmony_ci end = p + PTRS_PER_PGD; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci do { 5862306a36Sopenharmony_ci p[0] = entry; 5962306a36Sopenharmony_ci p[1] = entry; 6062306a36Sopenharmony_ci p[2] = entry; 6162306a36Sopenharmony_ci p[3] = entry; 6262306a36Sopenharmony_ci p[4] = entry; 6362306a36Sopenharmony_ci p += 8; 6462306a36Sopenharmony_ci p[-3] = entry; 6562306a36Sopenharmony_ci p[-2] = entry; 6662306a36Sopenharmony_ci p[-1] = entry; 6762306a36Sopenharmony_ci } while (p != end); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/** 7162306a36Sopenharmony_ci * kvm_pgd_alloc() - Allocate and initialise a KVM GPA page directory. 7262306a36Sopenharmony_ci * 7362306a36Sopenharmony_ci * Allocate a blank KVM GPA page directory (PGD) for representing guest physical 7462306a36Sopenharmony_ci * to host physical page mappings. 7562306a36Sopenharmony_ci * 7662306a36Sopenharmony_ci * Returns: Pointer to new KVM GPA page directory. 7762306a36Sopenharmony_ci * NULL on allocation failure. 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_cipgd_t *kvm_pgd_alloc(void) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci pgd_t *ret; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci ret = (pgd_t *)__get_free_pages(GFP_KERNEL, PGD_TABLE_ORDER); 8462306a36Sopenharmony_ci if (ret) 8562306a36Sopenharmony_ci kvm_pgd_init(ret); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci return ret; 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/** 9162306a36Sopenharmony_ci * kvm_mips_walk_pgd() - Walk page table with optional allocation. 9262306a36Sopenharmony_ci * @pgd: Page directory pointer. 9362306a36Sopenharmony_ci * @addr: Address to index page table using. 9462306a36Sopenharmony_ci * @cache: MMU page cache to allocate new page tables from, or NULL. 9562306a36Sopenharmony_ci * 9662306a36Sopenharmony_ci * Walk the page tables pointed to by @pgd to find the PTE corresponding to the 9762306a36Sopenharmony_ci * address @addr. If page tables don't exist for @addr, they will be created 9862306a36Sopenharmony_ci * from the MMU cache if @cache is not NULL. 9962306a36Sopenharmony_ci * 10062306a36Sopenharmony_ci * Returns: Pointer to pte_t corresponding to @addr. 10162306a36Sopenharmony_ci * NULL if a page table doesn't exist for @addr and !@cache. 10262306a36Sopenharmony_ci * NULL if a page table allocation failed. 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_cistatic pte_t *kvm_mips_walk_pgd(pgd_t *pgd, struct kvm_mmu_memory_cache *cache, 10562306a36Sopenharmony_ci unsigned long addr) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci p4d_t *p4d; 10862306a36Sopenharmony_ci pud_t *pud; 10962306a36Sopenharmony_ci pmd_t *pmd; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci pgd += pgd_index(addr); 11262306a36Sopenharmony_ci if (pgd_none(*pgd)) { 11362306a36Sopenharmony_ci /* Not used on MIPS yet */ 11462306a36Sopenharmony_ci BUG(); 11562306a36Sopenharmony_ci return NULL; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci p4d = p4d_offset(pgd, addr); 11862306a36Sopenharmony_ci pud = pud_offset(p4d, addr); 11962306a36Sopenharmony_ci if (pud_none(*pud)) { 12062306a36Sopenharmony_ci pmd_t *new_pmd; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (!cache) 12362306a36Sopenharmony_ci return NULL; 12462306a36Sopenharmony_ci new_pmd = kvm_mmu_memory_cache_alloc(cache); 12562306a36Sopenharmony_ci pmd_init(new_pmd); 12662306a36Sopenharmony_ci pud_populate(NULL, pud, new_pmd); 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci pmd = pmd_offset(pud, addr); 12962306a36Sopenharmony_ci if (pmd_none(*pmd)) { 13062306a36Sopenharmony_ci pte_t *new_pte; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (!cache) 13362306a36Sopenharmony_ci return NULL; 13462306a36Sopenharmony_ci new_pte = kvm_mmu_memory_cache_alloc(cache); 13562306a36Sopenharmony_ci clear_page(new_pte); 13662306a36Sopenharmony_ci pmd_populate_kernel(NULL, pmd, new_pte); 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci return pte_offset_kernel(pmd, addr); 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/* Caller must hold kvm->mm_lock */ 14262306a36Sopenharmony_cistatic pte_t *kvm_mips_pte_for_gpa(struct kvm *kvm, 14362306a36Sopenharmony_ci struct kvm_mmu_memory_cache *cache, 14462306a36Sopenharmony_ci unsigned long addr) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci return kvm_mips_walk_pgd(kvm->arch.gpa_mm.pgd, cache, addr); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/* 15062306a36Sopenharmony_ci * kvm_mips_flush_gpa_{pte,pmd,pud,pgd,pt}. 15162306a36Sopenharmony_ci * Flush a range of guest physical address space from the VM's GPA page tables. 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic bool kvm_mips_flush_gpa_pte(pte_t *pte, unsigned long start_gpa, 15562306a36Sopenharmony_ci unsigned long end_gpa) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci int i_min = pte_index(start_gpa); 15862306a36Sopenharmony_ci int i_max = pte_index(end_gpa); 15962306a36Sopenharmony_ci bool safe_to_remove = (i_min == 0 && i_max == PTRS_PER_PTE - 1); 16062306a36Sopenharmony_ci int i; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci for (i = i_min; i <= i_max; ++i) { 16362306a36Sopenharmony_ci if (!pte_present(pte[i])) 16462306a36Sopenharmony_ci continue; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci set_pte(pte + i, __pte(0)); 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci return safe_to_remove; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic bool kvm_mips_flush_gpa_pmd(pmd_t *pmd, unsigned long start_gpa, 17262306a36Sopenharmony_ci unsigned long end_gpa) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci pte_t *pte; 17562306a36Sopenharmony_ci unsigned long end = ~0ul; 17662306a36Sopenharmony_ci int i_min = pmd_index(start_gpa); 17762306a36Sopenharmony_ci int i_max = pmd_index(end_gpa); 17862306a36Sopenharmony_ci bool safe_to_remove = (i_min == 0 && i_max == PTRS_PER_PMD - 1); 17962306a36Sopenharmony_ci int i; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci for (i = i_min; i <= i_max; ++i, start_gpa = 0) { 18262306a36Sopenharmony_ci if (!pmd_present(pmd[i])) 18362306a36Sopenharmony_ci continue; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci pte = pte_offset_kernel(pmd + i, 0); 18662306a36Sopenharmony_ci if (i == i_max) 18762306a36Sopenharmony_ci end = end_gpa; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (kvm_mips_flush_gpa_pte(pte, start_gpa, end)) { 19062306a36Sopenharmony_ci pmd_clear(pmd + i); 19162306a36Sopenharmony_ci pte_free_kernel(NULL, pte); 19262306a36Sopenharmony_ci } else { 19362306a36Sopenharmony_ci safe_to_remove = false; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci return safe_to_remove; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic bool kvm_mips_flush_gpa_pud(pud_t *pud, unsigned long start_gpa, 20062306a36Sopenharmony_ci unsigned long end_gpa) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci pmd_t *pmd; 20362306a36Sopenharmony_ci unsigned long end = ~0ul; 20462306a36Sopenharmony_ci int i_min = pud_index(start_gpa); 20562306a36Sopenharmony_ci int i_max = pud_index(end_gpa); 20662306a36Sopenharmony_ci bool safe_to_remove = (i_min == 0 && i_max == PTRS_PER_PUD - 1); 20762306a36Sopenharmony_ci int i; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci for (i = i_min; i <= i_max; ++i, start_gpa = 0) { 21062306a36Sopenharmony_ci if (!pud_present(pud[i])) 21162306a36Sopenharmony_ci continue; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci pmd = pmd_offset(pud + i, 0); 21462306a36Sopenharmony_ci if (i == i_max) 21562306a36Sopenharmony_ci end = end_gpa; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (kvm_mips_flush_gpa_pmd(pmd, start_gpa, end)) { 21862306a36Sopenharmony_ci pud_clear(pud + i); 21962306a36Sopenharmony_ci pmd_free(NULL, pmd); 22062306a36Sopenharmony_ci } else { 22162306a36Sopenharmony_ci safe_to_remove = false; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci return safe_to_remove; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic bool kvm_mips_flush_gpa_pgd(pgd_t *pgd, unsigned long start_gpa, 22862306a36Sopenharmony_ci unsigned long end_gpa) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci p4d_t *p4d; 23162306a36Sopenharmony_ci pud_t *pud; 23262306a36Sopenharmony_ci unsigned long end = ~0ul; 23362306a36Sopenharmony_ci int i_min = pgd_index(start_gpa); 23462306a36Sopenharmony_ci int i_max = pgd_index(end_gpa); 23562306a36Sopenharmony_ci bool safe_to_remove = (i_min == 0 && i_max == PTRS_PER_PGD - 1); 23662306a36Sopenharmony_ci int i; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci for (i = i_min; i <= i_max; ++i, start_gpa = 0) { 23962306a36Sopenharmony_ci if (!pgd_present(pgd[i])) 24062306a36Sopenharmony_ci continue; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci p4d = p4d_offset(pgd, 0); 24362306a36Sopenharmony_ci pud = pud_offset(p4d + i, 0); 24462306a36Sopenharmony_ci if (i == i_max) 24562306a36Sopenharmony_ci end = end_gpa; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (kvm_mips_flush_gpa_pud(pud, start_gpa, end)) { 24862306a36Sopenharmony_ci pgd_clear(pgd + i); 24962306a36Sopenharmony_ci pud_free(NULL, pud); 25062306a36Sopenharmony_ci } else { 25162306a36Sopenharmony_ci safe_to_remove = false; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci return safe_to_remove; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci/** 25862306a36Sopenharmony_ci * kvm_mips_flush_gpa_pt() - Flush a range of guest physical addresses. 25962306a36Sopenharmony_ci * @kvm: KVM pointer. 26062306a36Sopenharmony_ci * @start_gfn: Guest frame number of first page in GPA range to flush. 26162306a36Sopenharmony_ci * @end_gfn: Guest frame number of last page in GPA range to flush. 26262306a36Sopenharmony_ci * 26362306a36Sopenharmony_ci * Flushes a range of GPA mappings from the GPA page tables. 26462306a36Sopenharmony_ci * 26562306a36Sopenharmony_ci * The caller must hold the @kvm->mmu_lock spinlock. 26662306a36Sopenharmony_ci * 26762306a36Sopenharmony_ci * Returns: Whether its safe to remove the top level page directory because 26862306a36Sopenharmony_ci * all lower levels have been removed. 26962306a36Sopenharmony_ci */ 27062306a36Sopenharmony_cibool kvm_mips_flush_gpa_pt(struct kvm *kvm, gfn_t start_gfn, gfn_t end_gfn) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci return kvm_mips_flush_gpa_pgd(kvm->arch.gpa_mm.pgd, 27362306a36Sopenharmony_ci start_gfn << PAGE_SHIFT, 27462306a36Sopenharmony_ci end_gfn << PAGE_SHIFT); 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci#define BUILD_PTE_RANGE_OP(name, op) \ 27862306a36Sopenharmony_cistatic int kvm_mips_##name##_pte(pte_t *pte, unsigned long start, \ 27962306a36Sopenharmony_ci unsigned long end) \ 28062306a36Sopenharmony_ci{ \ 28162306a36Sopenharmony_ci int ret = 0; \ 28262306a36Sopenharmony_ci int i_min = pte_index(start); \ 28362306a36Sopenharmony_ci int i_max = pte_index(end); \ 28462306a36Sopenharmony_ci int i; \ 28562306a36Sopenharmony_ci pte_t old, new; \ 28662306a36Sopenharmony_ci \ 28762306a36Sopenharmony_ci for (i = i_min; i <= i_max; ++i) { \ 28862306a36Sopenharmony_ci if (!pte_present(pte[i])) \ 28962306a36Sopenharmony_ci continue; \ 29062306a36Sopenharmony_ci \ 29162306a36Sopenharmony_ci old = pte[i]; \ 29262306a36Sopenharmony_ci new = op(old); \ 29362306a36Sopenharmony_ci if (pte_val(new) == pte_val(old)) \ 29462306a36Sopenharmony_ci continue; \ 29562306a36Sopenharmony_ci set_pte(pte + i, new); \ 29662306a36Sopenharmony_ci ret = 1; \ 29762306a36Sopenharmony_ci } \ 29862306a36Sopenharmony_ci return ret; \ 29962306a36Sopenharmony_ci} \ 30062306a36Sopenharmony_ci \ 30162306a36Sopenharmony_ci/* returns true if anything was done */ \ 30262306a36Sopenharmony_cistatic int kvm_mips_##name##_pmd(pmd_t *pmd, unsigned long start, \ 30362306a36Sopenharmony_ci unsigned long end) \ 30462306a36Sopenharmony_ci{ \ 30562306a36Sopenharmony_ci int ret = 0; \ 30662306a36Sopenharmony_ci pte_t *pte; \ 30762306a36Sopenharmony_ci unsigned long cur_end = ~0ul; \ 30862306a36Sopenharmony_ci int i_min = pmd_index(start); \ 30962306a36Sopenharmony_ci int i_max = pmd_index(end); \ 31062306a36Sopenharmony_ci int i; \ 31162306a36Sopenharmony_ci \ 31262306a36Sopenharmony_ci for (i = i_min; i <= i_max; ++i, start = 0) { \ 31362306a36Sopenharmony_ci if (!pmd_present(pmd[i])) \ 31462306a36Sopenharmony_ci continue; \ 31562306a36Sopenharmony_ci \ 31662306a36Sopenharmony_ci pte = pte_offset_kernel(pmd + i, 0); \ 31762306a36Sopenharmony_ci if (i == i_max) \ 31862306a36Sopenharmony_ci cur_end = end; \ 31962306a36Sopenharmony_ci \ 32062306a36Sopenharmony_ci ret |= kvm_mips_##name##_pte(pte, start, cur_end); \ 32162306a36Sopenharmony_ci } \ 32262306a36Sopenharmony_ci return ret; \ 32362306a36Sopenharmony_ci} \ 32462306a36Sopenharmony_ci \ 32562306a36Sopenharmony_cistatic int kvm_mips_##name##_pud(pud_t *pud, unsigned long start, \ 32662306a36Sopenharmony_ci unsigned long end) \ 32762306a36Sopenharmony_ci{ \ 32862306a36Sopenharmony_ci int ret = 0; \ 32962306a36Sopenharmony_ci pmd_t *pmd; \ 33062306a36Sopenharmony_ci unsigned long cur_end = ~0ul; \ 33162306a36Sopenharmony_ci int i_min = pud_index(start); \ 33262306a36Sopenharmony_ci int i_max = pud_index(end); \ 33362306a36Sopenharmony_ci int i; \ 33462306a36Sopenharmony_ci \ 33562306a36Sopenharmony_ci for (i = i_min; i <= i_max; ++i, start = 0) { \ 33662306a36Sopenharmony_ci if (!pud_present(pud[i])) \ 33762306a36Sopenharmony_ci continue; \ 33862306a36Sopenharmony_ci \ 33962306a36Sopenharmony_ci pmd = pmd_offset(pud + i, 0); \ 34062306a36Sopenharmony_ci if (i == i_max) \ 34162306a36Sopenharmony_ci cur_end = end; \ 34262306a36Sopenharmony_ci \ 34362306a36Sopenharmony_ci ret |= kvm_mips_##name##_pmd(pmd, start, cur_end); \ 34462306a36Sopenharmony_ci } \ 34562306a36Sopenharmony_ci return ret; \ 34662306a36Sopenharmony_ci} \ 34762306a36Sopenharmony_ci \ 34862306a36Sopenharmony_cistatic int kvm_mips_##name##_pgd(pgd_t *pgd, unsigned long start, \ 34962306a36Sopenharmony_ci unsigned long end) \ 35062306a36Sopenharmony_ci{ \ 35162306a36Sopenharmony_ci int ret = 0; \ 35262306a36Sopenharmony_ci p4d_t *p4d; \ 35362306a36Sopenharmony_ci pud_t *pud; \ 35462306a36Sopenharmony_ci unsigned long cur_end = ~0ul; \ 35562306a36Sopenharmony_ci int i_min = pgd_index(start); \ 35662306a36Sopenharmony_ci int i_max = pgd_index(end); \ 35762306a36Sopenharmony_ci int i; \ 35862306a36Sopenharmony_ci \ 35962306a36Sopenharmony_ci for (i = i_min; i <= i_max; ++i, start = 0) { \ 36062306a36Sopenharmony_ci if (!pgd_present(pgd[i])) \ 36162306a36Sopenharmony_ci continue; \ 36262306a36Sopenharmony_ci \ 36362306a36Sopenharmony_ci p4d = p4d_offset(pgd, 0); \ 36462306a36Sopenharmony_ci pud = pud_offset(p4d + i, 0); \ 36562306a36Sopenharmony_ci if (i == i_max) \ 36662306a36Sopenharmony_ci cur_end = end; \ 36762306a36Sopenharmony_ci \ 36862306a36Sopenharmony_ci ret |= kvm_mips_##name##_pud(pud, start, cur_end); \ 36962306a36Sopenharmony_ci } \ 37062306a36Sopenharmony_ci return ret; \ 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci/* 37462306a36Sopenharmony_ci * kvm_mips_mkclean_gpa_pt. 37562306a36Sopenharmony_ci * Mark a range of guest physical address space clean (writes fault) in the VM's 37662306a36Sopenharmony_ci * GPA page table to allow dirty page tracking. 37762306a36Sopenharmony_ci */ 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ciBUILD_PTE_RANGE_OP(mkclean, pte_mkclean) 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci/** 38262306a36Sopenharmony_ci * kvm_mips_mkclean_gpa_pt() - Make a range of guest physical addresses clean. 38362306a36Sopenharmony_ci * @kvm: KVM pointer. 38462306a36Sopenharmony_ci * @start_gfn: Guest frame number of first page in GPA range to flush. 38562306a36Sopenharmony_ci * @end_gfn: Guest frame number of last page in GPA range to flush. 38662306a36Sopenharmony_ci * 38762306a36Sopenharmony_ci * Make a range of GPA mappings clean so that guest writes will fault and 38862306a36Sopenharmony_ci * trigger dirty page logging. 38962306a36Sopenharmony_ci * 39062306a36Sopenharmony_ci * The caller must hold the @kvm->mmu_lock spinlock. 39162306a36Sopenharmony_ci * 39262306a36Sopenharmony_ci * Returns: Whether any GPA mappings were modified, which would require 39362306a36Sopenharmony_ci * derived mappings (GVA page tables & TLB enties) to be 39462306a36Sopenharmony_ci * invalidated. 39562306a36Sopenharmony_ci */ 39662306a36Sopenharmony_ciint kvm_mips_mkclean_gpa_pt(struct kvm *kvm, gfn_t start_gfn, gfn_t end_gfn) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci return kvm_mips_mkclean_pgd(kvm->arch.gpa_mm.pgd, 39962306a36Sopenharmony_ci start_gfn << PAGE_SHIFT, 40062306a36Sopenharmony_ci end_gfn << PAGE_SHIFT); 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci/** 40462306a36Sopenharmony_ci * kvm_arch_mmu_enable_log_dirty_pt_masked() - write protect dirty pages 40562306a36Sopenharmony_ci * @kvm: The KVM pointer 40662306a36Sopenharmony_ci * @slot: The memory slot associated with mask 40762306a36Sopenharmony_ci * @gfn_offset: The gfn offset in memory slot 40862306a36Sopenharmony_ci * @mask: The mask of dirty pages at offset 'gfn_offset' in this memory 40962306a36Sopenharmony_ci * slot to be write protected 41062306a36Sopenharmony_ci * 41162306a36Sopenharmony_ci * Walks bits set in mask write protects the associated pte's. Caller must 41262306a36Sopenharmony_ci * acquire @kvm->mmu_lock. 41362306a36Sopenharmony_ci */ 41462306a36Sopenharmony_civoid kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm, 41562306a36Sopenharmony_ci struct kvm_memory_slot *slot, 41662306a36Sopenharmony_ci gfn_t gfn_offset, unsigned long mask) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci gfn_t base_gfn = slot->base_gfn + gfn_offset; 41962306a36Sopenharmony_ci gfn_t start = base_gfn + __ffs(mask); 42062306a36Sopenharmony_ci gfn_t end = base_gfn + __fls(mask); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci kvm_mips_mkclean_gpa_pt(kvm, start, end); 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci/* 42662306a36Sopenharmony_ci * kvm_mips_mkold_gpa_pt. 42762306a36Sopenharmony_ci * Mark a range of guest physical address space old (all accesses fault) in the 42862306a36Sopenharmony_ci * VM's GPA page table to allow detection of commonly used pages. 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ciBUILD_PTE_RANGE_OP(mkold, pte_mkold) 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic int kvm_mips_mkold_gpa_pt(struct kvm *kvm, gfn_t start_gfn, 43462306a36Sopenharmony_ci gfn_t end_gfn) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci return kvm_mips_mkold_pgd(kvm->arch.gpa_mm.pgd, 43762306a36Sopenharmony_ci start_gfn << PAGE_SHIFT, 43862306a36Sopenharmony_ci end_gfn << PAGE_SHIFT); 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_cibool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci kvm_mips_flush_gpa_pt(kvm, range->start, range->end); 44462306a36Sopenharmony_ci return true; 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cibool kvm_set_spte_gfn(struct kvm *kvm, struct kvm_gfn_range *range) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci gpa_t gpa = range->start << PAGE_SHIFT; 45062306a36Sopenharmony_ci pte_t hva_pte = range->arg.pte; 45162306a36Sopenharmony_ci pte_t *gpa_pte = kvm_mips_pte_for_gpa(kvm, NULL, gpa); 45262306a36Sopenharmony_ci pte_t old_pte; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci if (!gpa_pte) 45562306a36Sopenharmony_ci return false; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci /* Mapping may need adjusting depending on memslot flags */ 45862306a36Sopenharmony_ci old_pte = *gpa_pte; 45962306a36Sopenharmony_ci if (range->slot->flags & KVM_MEM_LOG_DIRTY_PAGES && !pte_dirty(old_pte)) 46062306a36Sopenharmony_ci hva_pte = pte_mkclean(hva_pte); 46162306a36Sopenharmony_ci else if (range->slot->flags & KVM_MEM_READONLY) 46262306a36Sopenharmony_ci hva_pte = pte_wrprotect(hva_pte); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci set_pte(gpa_pte, hva_pte); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci /* Replacing an absent or old page doesn't need flushes */ 46762306a36Sopenharmony_ci if (!pte_present(old_pte) || !pte_young(old_pte)) 46862306a36Sopenharmony_ci return false; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci /* Pages swapped, aged, moved, or cleaned require flushes */ 47162306a36Sopenharmony_ci return !pte_present(hva_pte) || 47262306a36Sopenharmony_ci !pte_young(hva_pte) || 47362306a36Sopenharmony_ci pte_pfn(old_pte) != pte_pfn(hva_pte) || 47462306a36Sopenharmony_ci (pte_dirty(old_pte) && !pte_dirty(hva_pte)); 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cibool kvm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci return kvm_mips_mkold_gpa_pt(kvm, range->start, range->end); 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cibool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci gpa_t gpa = range->start << PAGE_SHIFT; 48562306a36Sopenharmony_ci pte_t *gpa_pte = kvm_mips_pte_for_gpa(kvm, NULL, gpa); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if (!gpa_pte) 48862306a36Sopenharmony_ci return false; 48962306a36Sopenharmony_ci return pte_young(*gpa_pte); 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci/** 49362306a36Sopenharmony_ci * _kvm_mips_map_page_fast() - Fast path GPA fault handler. 49462306a36Sopenharmony_ci * @vcpu: VCPU pointer. 49562306a36Sopenharmony_ci * @gpa: Guest physical address of fault. 49662306a36Sopenharmony_ci * @write_fault: Whether the fault was due to a write. 49762306a36Sopenharmony_ci * @out_entry: New PTE for @gpa (written on success unless NULL). 49862306a36Sopenharmony_ci * @out_buddy: New PTE for @gpa's buddy (written on success unless 49962306a36Sopenharmony_ci * NULL). 50062306a36Sopenharmony_ci * 50162306a36Sopenharmony_ci * Perform fast path GPA fault handling, doing all that can be done without 50262306a36Sopenharmony_ci * calling into KVM. This handles marking old pages young (for idle page 50362306a36Sopenharmony_ci * tracking), and dirtying of clean pages (for dirty page logging). 50462306a36Sopenharmony_ci * 50562306a36Sopenharmony_ci * Returns: 0 on success, in which case we can update derived mappings and 50662306a36Sopenharmony_ci * resume guest execution. 50762306a36Sopenharmony_ci * -EFAULT on failure due to absent GPA mapping or write to 50862306a36Sopenharmony_ci * read-only page, in which case KVM must be consulted. 50962306a36Sopenharmony_ci */ 51062306a36Sopenharmony_cistatic int _kvm_mips_map_page_fast(struct kvm_vcpu *vcpu, unsigned long gpa, 51162306a36Sopenharmony_ci bool write_fault, 51262306a36Sopenharmony_ci pte_t *out_entry, pte_t *out_buddy) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci struct kvm *kvm = vcpu->kvm; 51562306a36Sopenharmony_ci gfn_t gfn = gpa >> PAGE_SHIFT; 51662306a36Sopenharmony_ci pte_t *ptep; 51762306a36Sopenharmony_ci kvm_pfn_t pfn = 0; /* silence bogus GCC warning */ 51862306a36Sopenharmony_ci bool pfn_valid = false; 51962306a36Sopenharmony_ci int ret = 0; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci spin_lock(&kvm->mmu_lock); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* Fast path - just check GPA page table for an existing entry */ 52462306a36Sopenharmony_ci ptep = kvm_mips_pte_for_gpa(kvm, NULL, gpa); 52562306a36Sopenharmony_ci if (!ptep || !pte_present(*ptep)) { 52662306a36Sopenharmony_ci ret = -EFAULT; 52762306a36Sopenharmony_ci goto out; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci /* Track access to pages marked old */ 53162306a36Sopenharmony_ci if (!pte_young(*ptep)) { 53262306a36Sopenharmony_ci set_pte(ptep, pte_mkyoung(*ptep)); 53362306a36Sopenharmony_ci pfn = pte_pfn(*ptep); 53462306a36Sopenharmony_ci pfn_valid = true; 53562306a36Sopenharmony_ci /* call kvm_set_pfn_accessed() after unlock */ 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci if (write_fault && !pte_dirty(*ptep)) { 53862306a36Sopenharmony_ci if (!pte_write(*ptep)) { 53962306a36Sopenharmony_ci ret = -EFAULT; 54062306a36Sopenharmony_ci goto out; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci /* Track dirtying of writeable pages */ 54462306a36Sopenharmony_ci set_pte(ptep, pte_mkdirty(*ptep)); 54562306a36Sopenharmony_ci pfn = pte_pfn(*ptep); 54662306a36Sopenharmony_ci mark_page_dirty(kvm, gfn); 54762306a36Sopenharmony_ci kvm_set_pfn_dirty(pfn); 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci if (out_entry) 55162306a36Sopenharmony_ci *out_entry = *ptep; 55262306a36Sopenharmony_ci if (out_buddy) 55362306a36Sopenharmony_ci *out_buddy = *ptep_buddy(ptep); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ciout: 55662306a36Sopenharmony_ci spin_unlock(&kvm->mmu_lock); 55762306a36Sopenharmony_ci if (pfn_valid) 55862306a36Sopenharmony_ci kvm_set_pfn_accessed(pfn); 55962306a36Sopenharmony_ci return ret; 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci/** 56362306a36Sopenharmony_ci * kvm_mips_map_page() - Map a guest physical page. 56462306a36Sopenharmony_ci * @vcpu: VCPU pointer. 56562306a36Sopenharmony_ci * @gpa: Guest physical address of fault. 56662306a36Sopenharmony_ci * @write_fault: Whether the fault was due to a write. 56762306a36Sopenharmony_ci * @out_entry: New PTE for @gpa (written on success unless NULL). 56862306a36Sopenharmony_ci * @out_buddy: New PTE for @gpa's buddy (written on success unless 56962306a36Sopenharmony_ci * NULL). 57062306a36Sopenharmony_ci * 57162306a36Sopenharmony_ci * Handle GPA faults by creating a new GPA mapping (or updating an existing 57262306a36Sopenharmony_ci * one). 57362306a36Sopenharmony_ci * 57462306a36Sopenharmony_ci * This takes care of marking pages young or dirty (idle/dirty page tracking), 57562306a36Sopenharmony_ci * asking KVM for the corresponding PFN, and creating a mapping in the GPA page 57662306a36Sopenharmony_ci * tables. Derived mappings (GVA page tables and TLBs) must be handled by the 57762306a36Sopenharmony_ci * caller. 57862306a36Sopenharmony_ci * 57962306a36Sopenharmony_ci * Returns: 0 on success, in which case the caller may use the @out_entry 58062306a36Sopenharmony_ci * and @out_buddy PTEs to update derived mappings and resume guest 58162306a36Sopenharmony_ci * execution. 58262306a36Sopenharmony_ci * -EFAULT if there is no memory region at @gpa or a write was 58362306a36Sopenharmony_ci * attempted to a read-only memory region. This is usually handled 58462306a36Sopenharmony_ci * as an MMIO access. 58562306a36Sopenharmony_ci */ 58662306a36Sopenharmony_cistatic int kvm_mips_map_page(struct kvm_vcpu *vcpu, unsigned long gpa, 58762306a36Sopenharmony_ci bool write_fault, 58862306a36Sopenharmony_ci pte_t *out_entry, pte_t *out_buddy) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci struct kvm *kvm = vcpu->kvm; 59162306a36Sopenharmony_ci struct kvm_mmu_memory_cache *memcache = &vcpu->arch.mmu_page_cache; 59262306a36Sopenharmony_ci gfn_t gfn = gpa >> PAGE_SHIFT; 59362306a36Sopenharmony_ci int srcu_idx, err; 59462306a36Sopenharmony_ci kvm_pfn_t pfn; 59562306a36Sopenharmony_ci pte_t *ptep, entry; 59662306a36Sopenharmony_ci bool writeable; 59762306a36Sopenharmony_ci unsigned long prot_bits; 59862306a36Sopenharmony_ci unsigned long mmu_seq; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci /* Try the fast path to handle old / clean pages */ 60162306a36Sopenharmony_ci srcu_idx = srcu_read_lock(&kvm->srcu); 60262306a36Sopenharmony_ci err = _kvm_mips_map_page_fast(vcpu, gpa, write_fault, out_entry, 60362306a36Sopenharmony_ci out_buddy); 60462306a36Sopenharmony_ci if (!err) 60562306a36Sopenharmony_ci goto out; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci /* We need a minimum of cached pages ready for page table creation */ 60862306a36Sopenharmony_ci err = kvm_mmu_topup_memory_cache(memcache, KVM_MMU_CACHE_MIN_PAGES); 60962306a36Sopenharmony_ci if (err) 61062306a36Sopenharmony_ci goto out; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ciretry: 61362306a36Sopenharmony_ci /* 61462306a36Sopenharmony_ci * Used to check for invalidations in progress, of the pfn that is 61562306a36Sopenharmony_ci * returned by pfn_to_pfn_prot below. 61662306a36Sopenharmony_ci */ 61762306a36Sopenharmony_ci mmu_seq = kvm->mmu_invalidate_seq; 61862306a36Sopenharmony_ci /* 61962306a36Sopenharmony_ci * Ensure the read of mmu_invalidate_seq isn't reordered with PTE reads 62062306a36Sopenharmony_ci * in gfn_to_pfn_prot() (which calls get_user_pages()), so that we don't 62162306a36Sopenharmony_ci * risk the page we get a reference to getting unmapped before we have a 62262306a36Sopenharmony_ci * chance to grab the mmu_lock without mmu_invalidate_retry() noticing. 62362306a36Sopenharmony_ci * 62462306a36Sopenharmony_ci * This smp_rmb() pairs with the effective smp_wmb() of the combination 62562306a36Sopenharmony_ci * of the pte_unmap_unlock() after the PTE is zapped, and the 62662306a36Sopenharmony_ci * spin_lock() in kvm_mmu_notifier_invalidate_<page|range_end>() before 62762306a36Sopenharmony_ci * mmu_invalidate_seq is incremented. 62862306a36Sopenharmony_ci */ 62962306a36Sopenharmony_ci smp_rmb(); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci /* Slow path - ask KVM core whether we can access this GPA */ 63262306a36Sopenharmony_ci pfn = gfn_to_pfn_prot(kvm, gfn, write_fault, &writeable); 63362306a36Sopenharmony_ci if (is_error_noslot_pfn(pfn)) { 63462306a36Sopenharmony_ci err = -EFAULT; 63562306a36Sopenharmony_ci goto out; 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci spin_lock(&kvm->mmu_lock); 63962306a36Sopenharmony_ci /* Check if an invalidation has taken place since we got pfn */ 64062306a36Sopenharmony_ci if (mmu_invalidate_retry(kvm, mmu_seq)) { 64162306a36Sopenharmony_ci /* 64262306a36Sopenharmony_ci * This can happen when mappings are changed asynchronously, but 64362306a36Sopenharmony_ci * also synchronously if a COW is triggered by 64462306a36Sopenharmony_ci * gfn_to_pfn_prot(). 64562306a36Sopenharmony_ci */ 64662306a36Sopenharmony_ci spin_unlock(&kvm->mmu_lock); 64762306a36Sopenharmony_ci kvm_release_pfn_clean(pfn); 64862306a36Sopenharmony_ci goto retry; 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci /* Ensure page tables are allocated */ 65262306a36Sopenharmony_ci ptep = kvm_mips_pte_for_gpa(kvm, memcache, gpa); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci /* Set up the PTE */ 65562306a36Sopenharmony_ci prot_bits = _PAGE_PRESENT | __READABLE | _page_cachable_default; 65662306a36Sopenharmony_ci if (writeable) { 65762306a36Sopenharmony_ci prot_bits |= _PAGE_WRITE; 65862306a36Sopenharmony_ci if (write_fault) { 65962306a36Sopenharmony_ci prot_bits |= __WRITEABLE; 66062306a36Sopenharmony_ci mark_page_dirty(kvm, gfn); 66162306a36Sopenharmony_ci kvm_set_pfn_dirty(pfn); 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci entry = pfn_pte(pfn, __pgprot(prot_bits)); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci /* Write the PTE */ 66762306a36Sopenharmony_ci set_pte(ptep, entry); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci err = 0; 67062306a36Sopenharmony_ci if (out_entry) 67162306a36Sopenharmony_ci *out_entry = *ptep; 67262306a36Sopenharmony_ci if (out_buddy) 67362306a36Sopenharmony_ci *out_buddy = *ptep_buddy(ptep); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci spin_unlock(&kvm->mmu_lock); 67662306a36Sopenharmony_ci kvm_release_pfn_clean(pfn); 67762306a36Sopenharmony_ci kvm_set_pfn_accessed(pfn); 67862306a36Sopenharmony_ciout: 67962306a36Sopenharmony_ci srcu_read_unlock(&kvm->srcu, srcu_idx); 68062306a36Sopenharmony_ci return err; 68162306a36Sopenharmony_ci} 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ciint kvm_mips_handle_vz_root_tlb_fault(unsigned long badvaddr, 68462306a36Sopenharmony_ci struct kvm_vcpu *vcpu, 68562306a36Sopenharmony_ci bool write_fault) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci int ret; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci ret = kvm_mips_map_page(vcpu, badvaddr, write_fault, NULL, NULL); 69062306a36Sopenharmony_ci if (ret) 69162306a36Sopenharmony_ci return ret; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci /* Invalidate this entry in the TLB */ 69462306a36Sopenharmony_ci return kvm_vz_host_tlb_inv(vcpu, badvaddr); 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci/** 69862306a36Sopenharmony_ci * kvm_mips_migrate_count() - Migrate timer. 69962306a36Sopenharmony_ci * @vcpu: Virtual CPU. 70062306a36Sopenharmony_ci * 70162306a36Sopenharmony_ci * Migrate CP0_Count hrtimer to the current CPU by cancelling and restarting it 70262306a36Sopenharmony_ci * if it was running prior to being cancelled. 70362306a36Sopenharmony_ci * 70462306a36Sopenharmony_ci * Must be called when the VCPU is migrated to a different CPU to ensure that 70562306a36Sopenharmony_ci * timer expiry during guest execution interrupts the guest and causes the 70662306a36Sopenharmony_ci * interrupt to be delivered in a timely manner. 70762306a36Sopenharmony_ci */ 70862306a36Sopenharmony_cistatic void kvm_mips_migrate_count(struct kvm_vcpu *vcpu) 70962306a36Sopenharmony_ci{ 71062306a36Sopenharmony_ci if (hrtimer_cancel(&vcpu->arch.comparecount_timer)) 71162306a36Sopenharmony_ci hrtimer_restart(&vcpu->arch.comparecount_timer); 71262306a36Sopenharmony_ci} 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci/* Restore ASID once we are scheduled back after preemption */ 71562306a36Sopenharmony_civoid kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) 71662306a36Sopenharmony_ci{ 71762306a36Sopenharmony_ci unsigned long flags; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci kvm_debug("%s: vcpu %p, cpu: %d\n", __func__, vcpu, cpu); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci local_irq_save(flags); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci vcpu->cpu = cpu; 72462306a36Sopenharmony_ci if (vcpu->arch.last_sched_cpu != cpu) { 72562306a36Sopenharmony_ci kvm_debug("[%d->%d]KVM VCPU[%d] switch\n", 72662306a36Sopenharmony_ci vcpu->arch.last_sched_cpu, cpu, vcpu->vcpu_id); 72762306a36Sopenharmony_ci /* 72862306a36Sopenharmony_ci * Migrate the timer interrupt to the current CPU so that it 72962306a36Sopenharmony_ci * always interrupts the guest and synchronously triggers a 73062306a36Sopenharmony_ci * guest timer interrupt. 73162306a36Sopenharmony_ci */ 73262306a36Sopenharmony_ci kvm_mips_migrate_count(vcpu); 73362306a36Sopenharmony_ci } 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci /* restore guest state to registers */ 73662306a36Sopenharmony_ci kvm_mips_callbacks->vcpu_load(vcpu, cpu); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci local_irq_restore(flags); 73962306a36Sopenharmony_ci} 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci/* ASID can change if another task is scheduled during preemption */ 74262306a36Sopenharmony_civoid kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) 74362306a36Sopenharmony_ci{ 74462306a36Sopenharmony_ci unsigned long flags; 74562306a36Sopenharmony_ci int cpu; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci local_irq_save(flags); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci cpu = smp_processor_id(); 75062306a36Sopenharmony_ci vcpu->arch.last_sched_cpu = cpu; 75162306a36Sopenharmony_ci vcpu->cpu = -1; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci /* save guest state in registers */ 75462306a36Sopenharmony_ci kvm_mips_callbacks->vcpu_put(vcpu, cpu); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci local_irq_restore(flags); 75762306a36Sopenharmony_ci} 758