blob: 5d03de327e0908c7b7ae4e5f634a0fee7d532c49 [file] [log] [blame]
/* Copyright (c) 2015 Google Inc.
* Barret Rhoden <brho@cs.berkeley.edu>
* See LICENSE for details.
*
* 64 bit EPT helpers */
#pragma once
#include <arch/vmm/intel/vmx.h> /* for sync/flush helpers */
#include <smp.h> /* for current */
/* Some EPTE PTE flags are only valid for the last PTEs in a walk */
#define EPTE_R (1ULL << 0) /* Readable */
#define EPTE_W (1ULL << 1) /* Writeable */
#define EPTE_X (1ULL << 2) /* Executable */
#define EPTE_MEM_BITS (7ULL << 3) /* Memory type specifier */
#define EPTE_IGN_PAT (1ULL << 6) /* Ignore PAT */
#define EPTE_PS (1ULL << 7) /* Jumbo Page Size */
#define EPTE_A (1ULL << 8) /* Accessed */
#define EPTE_D (1ULL << 9) /* Dirty */
#define EPTE_SUP_VE (1ULL << 63) /* Suppress virt exceptions */
#define EPTE_P (EPTE_R | EPTE_W | EPTE_X)
/* Types available for the EPTE_MEM_TYPE */
#define EPT_MEM_TYPE_UC 0
#define EPT_MEM_TYPE_WC 1
#define EPT_MEM_TYPE_WT 4
#define EPT_MEM_TYPE_WP 5
#define EPT_MEM_TYPE_WB 6
/* Helper to align the type to its location in the PTE */
#define EPT_MEM_TYPE(type) ((type) << 3)
/* Some machines don't support A and D EPTE bits. We'll |= 1 in those cases. */
extern int x86_ept_pte_fix_ups;
static inline epte_t *kpte_to_epte(kpte_t *kpte)
{
return (epte_t*)(((uintptr_t)kpte) + PGSIZE);
}
static inline bool epte_is_present(epte_t *epte)
{
/* Actually, certain combos, like W but not R could be
* misconfigurations. */
return *epte & EPTE_P ? TRUE : FALSE;
}
static inline bool epte_is_unmapped(epte_t *epte)
{
return *epte == 0;
}
static inline bool epte_is_mapped(epte_t *epte)
{
return *epte != 0;
}
static inline bool epte_is_paged_out(epte_t *epte)
{
return *epte != 0;
}
/* Some Intel machines don't support A or D. In these cases, we must assume
* the pages have been accessed or dirtied... */
static inline bool epte_is_dirty(epte_t *epte)
{
return (*epte | x86_ept_pte_fix_ups) & EPTE_D ? TRUE : FALSE;
}
static inline bool epte_is_accessed(epte_t *epte)
{
return (*epte | x86_ept_pte_fix_ups) & EPTE_A ? TRUE : FALSE;
}
static inline bool epte_is_jumbo(epte_t *epte)
{
return *epte & EPTE_PS ? TRUE : FALSE;
}
static inline physaddr_t epte_get_paddr(epte_t *epte)
{
/* 63:52 are ignored/flags. 51:12 are the addr. Technically 51:N must
* be 0, where N is the physical addr width */
return *epte & 0x000ffffffffff000;
}
static inline int __pte_to_epte_perm(int perm)
{
switch (perm) {
/* Since we keep the EPT in lockstep with the KPT, we might get
* some mapping requests for the kernel (e.g. vmap_pmem). */
case PTE_KERN_RW:
case PTE_KERN_RO:
case PTE_NONE:
return 0;
case PTE_USER_RW:
return EPTE_W | EPTE_R | EPTE_X;
case PTE_USER_RO:
return EPTE_R | EPTE_X;
default:
panic("Bad PTE type 0x%x\n", perm);
}
}
static inline void epte_write(epte_t *epte, physaddr_t pa, int settings)
{
/* Could put in a check against the max physaddr len */
epte_t temp = pa;
temp |= __pte_to_epte_perm(settings & PTE_PERM);
temp |= settings & PTE_PS ? EPTE_PS : 0;
/* All memory is WB by default, but the guest can override that with
* their PAT on the first page walk (guest KPT/cr3) */
temp |= EPT_MEM_TYPE(EPT_MEM_TYPE_WB);
*epte = temp;
}
static inline void epte_clear_present(epte_t *epte)
{
*epte &= ~EPTE_P;
}
static inline void epte_clear_dirty(epte_t *epte)
{
*epte &= ~EPTE_D;
}
static inline void epte_clear(epte_t *epte)
{
*epte = 0;
}
static inline bool epte_has_perm_ur(epte_t *epte)
{
return (*epte & (EPTE_R | EPTE_X)) == (EPTE_R | EPTE_X);
}
static inline bool epte_has_perm_urw(epte_t *epte)
{
return (*epte & (EPTE_R | EPTE_W | EPTE_X)) == (EPTE_R | EPTE_W | EPTE_X);
}
static inline int epte_get_settings(epte_t *epte)
{
int settings = 0;
if (*epte & EPTE_P) {
/* We want to know User and Writable, in the 'PTE' sense. All
* present epte entries are User PTEs. */
settings |= PTE_P | PTE_U;
settings |= *epte & EPTE_W ? PTE_W : 0;
}
settings |= *epte & EPTE_PS ? PTE_PS : 0;
settings |= *epte & EPTE_A ? PTE_A : 0;
settings |= *epte & EPTE_D ? PTE_D : 0;
return settings;
}
/* Again, we're replacing the old perms with U and/or W. Any non-U are ignored,
* as with epte_write. */
static inline void epte_replace_perm(epte_t *epte, int perm)
{
*epte = (*epte & ~EPTE_P) | __pte_to_epte_perm(perm & PTE_PERM);
}
/* These ops might be the same for AMD as Intel; in which case we can move the
* body of these ept_sync_* funcs into here */
static inline void ept_inval_addr(unsigned long addr)
{
if (current && current->vmm.vmmcp)
ept_sync_individual_addr(current->env_pgdir.eptp, addr);
}
static inline void ept_inval_context(void)
{
if (current && current->vmm.vmmcp)
ept_sync_context(current->env_pgdir.eptp);
}
static inline void ept_inval_global(void)
{
ept_sync_global();
}