| /* 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(); |
| } |