npt.c (254675) | npt.c (259579) |
---|---|
1/*- 2 * Copyright (c) 2013 Anish Gupta (akgupt3@gmail.com) 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright --- 11 unchanged lines hidden (view full) --- 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> | 1/*- 2 * Copyright (c) 2013 Anish Gupta (akgupt3@gmail.com) 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright --- 11 unchanged lines hidden (view full) --- 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> |
28__FBSDID("$FreeBSD: projects/bhyve_svm/sys/amd64/vmm/amd/npt.c 254675 2013-08-22 22:26:46Z grehan $"); | 28__FBSDID("$FreeBSD: projects/bhyve_svm/sys/amd64/vmm/amd/npt.c 259579 2013-12-18 23:39:42Z grehan $"); |
29 30#include <sys/param.h> | 29 30#include <sys/param.h> |
31#include <sys/kernel.h> |
|
31#include <sys/systm.h> | 32#include <sys/systm.h> |
32#include <sys/malloc.h> | 33#include <sys/sysctl.h> |
33 34#include <vm/vm.h> 35#include <vm/pmap.h> | 34 35#include <vm/vm.h> 36#include <vm/pmap.h> |
37#include <vm/vm_extern.h> |
|
36 37#include <machine/pmap.h> 38#include <machine/md_var.h> 39#include <machine/vmparam.h> 40#include <machine/vmm.h> 41 42#include "svm.h" 43#include "vmcb.h" 44#include "svm_softc.h" 45#include "npt.h" 46 | 38 39#include <machine/pmap.h> 40#include <machine/md_var.h> 41#include <machine/vmparam.h> 42#include <machine/vmm.h> 43 44#include "svm.h" 45#include "vmcb.h" 46#include "svm_softc.h" 47#include "npt.h" 48 |
47/* 48 * "Nested Paging" is an optional SVM feature that provides two levels of 49 * address translation, thus eliminating the need for the VMM to maintain 50 * shadow page tables. 51 * 52 * Documented in APMv2, section 15.25, Nested Paging. 53 */ | 49SYSCTL_DECL(_hw_vmm); 50SYSCTL_NODE(_hw_vmm, OID_AUTO, npt, CTLFLAG_RW, NULL, NULL); |
54 | 51 |
55#define PAGE_4KB (4 * 1024) 56#define PAGE_2MB (2 * 1024 * 1024UL) 57#define PAGE_1GB (1024 * 1024 * 1024UL) 58 59#define GPA_UNMAPPED ((vm_paddr_t)~0) 60 61/* Get page entry to physical address. */ 62#define PTE2PA(x) ((uint64_t)(x) & ~PAGE_MASK) 63 64MALLOC_DECLARE(M_SVM); 65 66static uint64_t svm_npt_create(pml4_entry_t *pml4, vm_paddr_t gpa, 67 vm_paddr_t hpa, vm_memattr_t attr, 68 int prot, uint64_t size); 69 70static const int PT_INDEX_MASK = 0x1FF; 71static const int PT_SHIFT = 9; 72 | 52static int npt_flags; 53SYSCTL_INT(_hw_vmm_npt, OID_AUTO, pmap_flags, CTLFLAG_RD, 54 &npt_flags, 0, NULL); |
73/* | 55/* |
74 * Helper function to create nested page table entries for a page 75 * of size 1GB, 2MB or 4KB. 76 * 77 * Starting from PML4 create a PDPTE, PDE or PTE depending on 'pg_size' 78 * value of 1GB, 2MB or 4KB respectively. 79 * 80 * Return size of the mapping created on success and 0 on failure. 81 * 82 * XXX: NPT PAT settings. 83 */ 84static uint64_t 85svm_npt_create(pml4_entry_t * pml4, vm_paddr_t gpa, vm_paddr_t hpa, 86 vm_memattr_t attr, int prot, uint64_t pg_size) 87{ 88 uint64_t *pt, *page, pa; 89 pt_entry_t mode; 90 int shift, index; 91 92 KASSERT(pg_size, ("Size of page must be 1GB, 2MB or 4KB")); 93 if (hpa & (pg_size - 1)) { 94 ERR("HPA(0x%lx) is not aligned, size:0x%lx\n", hpa, pg_size); 95 return (0); 96 } 97 98 if (gpa & (pg_size - 1)) { 99 ERR("GPA(0x%lx) is not aligned, size (0x%lx)\n", gpa, pg_size); 100 return (0); 101 } 102 103 /* Find out mode bits for PTE */ 104 mode = PG_U | PG_V; 105 if (prot & VM_PROT_WRITE) 106 mode |= PG_RW; 107 if ((prot & VM_PROT_EXECUTE) == 0) 108 mode |= pg_nx; 109 110 pt = (uint64_t *)pml4; 111 shift = PML4SHIFT; 112 113 while ((shift > PAGE_SHIFT) && (pg_size < (1UL << shift))) { 114 /* Get PDP, PD or PT index from guest physical address. */ 115 index = (gpa >> shift) & PT_INDEX_MASK; 116 117 /* If page entry is missing, allocate new page for table.*/ 118 if (pt[index] == 0) { 119 page = malloc(PAGE_SIZE, M_SVM, M_WAITOK | M_ZERO); 120 pt[index] = vtophys(page) | mode; 121 } 122 123 pa = PTE2PA(pt[index]);; 124 pt = (uint64_t *)PHYS_TO_DMAP(pa); 125 shift -= PT_SHIFT; 126 } 127 128 /* Create leaf entry mapping. */ 129 index = (gpa >> shift) & PT_INDEX_MASK; 130 131 if (prot != VM_PROT_NONE) { 132 pt[index] = hpa | mode; 133 pt[index] |= (pg_size > PAGE_SIZE) ? PG_PS : 0; 134 } else 135 pt[index] = 0; 136 137 return (1UL << shift); 138} 139 140/* 141 * Map guest physical address to host physical address. 142 */ 143int 144svm_npt_vmmap_set(void *arg, vm_paddr_t gpa, vm_paddr_t hpa, 145 size_t size, vm_memattr_t attr, int prot, boolean_t spok) 146{ 147 pml4_entry_t *pml4; 148 struct svm_softc *svm_sc; 149 uint64_t len, mapped, pg_size; 150 151 svm_sc = arg; 152 pml4 = svm_sc->np_pml4; 153 154 pg_size = PAGE_4KB; 155 if (spok) { 156 pg_size = PAGE_2MB; 157 if (amd_feature & AMDID_PAGE1GB) 158 pg_size = PAGE_1GB; 159 } 160 161 /* Compute the largest page mapping that can be used */ 162 while (pg_size > PAGE_4KB) { 163 if (size >= pg_size && 164 (gpa & (pg_size - 1)) == 0 && 165 (hpa & (pg_size - 1)) == 0) { 166 break; 167 } 168 pg_size >>= PT_SHIFT; 169 } 170 171 len = 0; 172 while (len < size) { 173 mapped = svm_npt_create(pml4, gpa + len, hpa + len, attr, prot, 174 pg_size); 175 len += mapped; 176 } 177 178 return (0); 179} 180 181/* 182 * Get HPA for a given GPA. 183 */ 184vm_paddr_t 185svm_npt_vmmap_get(void *arg, vm_paddr_t gpa) 186{ 187 struct svm_softc *svm_sc; 188 pml4_entry_t *pml4; 189 uint64_t *pt, pa, hpa, pgmask; 190 int shift, index; 191 192 svm_sc = arg; 193 pml4 = svm_sc->np_pml4; 194 195 pt = (uint64_t *)pml4; 196 shift = PML4SHIFT; 197 198 while (shift > PAGE_SHIFT) { 199 /* Get PDP, PD or PT index from GPA */ 200 index = (gpa >> shift) & PT_INDEX_MASK; 201 if (pt[index] == 0) { 202 ERR("No entry for GPA:0x%lx.", gpa); 203 return (GPA_UNMAPPED); 204 } 205 206 if (pt[index] & PG_PS) { 207 break; 208 } 209 210 pa = PTE2PA(pt[index]);; 211 pt = (uint64_t *)PHYS_TO_DMAP(pa); 212 shift -= PT_SHIFT; 213 } 214 215 index = (gpa >> shift) & PT_INDEX_MASK; 216 if (pt[index] == 0) { 217 ERR("No mapping for GPA:0x%lx.\n", gpa); 218 return (GPA_UNMAPPED); 219 } 220 221 /* Add GPA offset to HPA */ 222 pgmask = (1UL << shift) - 1; 223 hpa = (PTE2PA(pt[index]) & ~pgmask) | (gpa & pgmask); 224 225 return (hpa); 226} 227 228/* | |
229 * AMD nested page table init. 230 */ 231int 232svm_npt_init(void) 233{ | 56 * AMD nested page table init. 57 */ 58int 59svm_npt_init(void) 60{ |
61 int enable_superpage = 1; 62 63 TUNABLE_INT_FETCH("hw.vmm.npt.enable_superpage", &enable_superpage); 64 if (enable_superpage) 65 npt_flags |= PMAP_PDE_SUPERPAGE; |
|
234 235 return (0); 236} 237 | 66 67 return (0); 68} 69 |
238/* 239 * Free Page Table page. 240 */ 241static void 242free_pt(pd_entry_t pde) 243{ 244 pt_entry_t *pt; | |
245 | 70 |
246 pt = (pt_entry_t *)PHYS_TO_DMAP(PTE2PA(pde)); 247 free(pt, M_SVM); 248} | |
249 | 71 |
250/* 251 * Free Page Directory page. 252 */ 253static void 254free_pd(pdp_entry_t pdpe) | 72static int 73npt_pinit(pmap_t pmap) |
255{ | 74{ |
256 pd_entry_t *pd; 257 int i; | |
258 | 75 |
259 pd = (pd_entry_t *)PHYS_TO_DMAP(PTE2PA(pdpe)); 260 for (i = 0; i < NPDEPG; i++) { 261 /* Skip not-present or superpage entries */ 262 if ((pd[i] == 0) || (pd[i] & PG_PS)) 263 continue; 264 265 free_pt(pd[i]); 266 } 267 268 free(pd, M_SVM); | 76 return (pmap_pinit_type(pmap, PT_RVI, npt_flags)); |
269} 270 | 77} 78 |
271/* 272 * Free Page Directory Pointer page. 273 */ 274static void 275free_pdp(pml4_entry_t pml4e) | 79struct vmspace * 80svm_npt_alloc(vm_offset_t min, vm_offset_t max) |
276{ | 81{ |
277 pdp_entry_t *pdp; 278 int i; 279 280 pdp = (pdp_entry_t *)PHYS_TO_DMAP(PTE2PA(pml4e)); 281 for (i = 0; i < NPDPEPG; i++) { 282 /* Skip not-present or superpage entries */ 283 if ((pdp[i] == 0) || (pdp[i] & PG_PS)) 284 continue; 285 286 free_pd(pdp[i]); 287 } 288 289 free(pdp, M_SVM); | 82 83 return (vmspace_alloc(min, max, npt_pinit)); |
290} 291 | 84} 85 |
292/* 293 * Free the guest's nested page table. 294 */ 295int 296svm_npt_cleanup(struct svm_softc *svm_sc) | 86void 87svm_npt_free(struct vmspace *vmspace) |
297{ | 88{ |
298 pml4_entry_t *pml4; 299 int i; | |
300 | 89 |
301 pml4 = svm_sc->np_pml4; 302 303 for (i = 0; i < NPML4EPG; i++) { 304 if (pml4[i] != 0) { 305 free_pdp(pml4[i]); 306 pml4[i] = 0; 307 } 308 } 309 310 return (0); | 90 vmspace_free(vmspace); |
311} | 91} |