1 /* $NetBSD: gdt.c,v 1.24 2010/07/07 01:14:52 chs Exp $ */ 2 3 /*- 4 * Copyright (c) 1996, 1997, 2009 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by John T. Kohl, by Charles M. Hannum, and by Andrew Doran. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Modified to deal with variable-length entries for NetBSD/x86_64 by 34 * fvdl@wasabisystems.com, may 2001 35 * XXX this file should be shared with the i386 code, the difference 36 * can be hidden in macros. 37 */ 38 39 #include <sys/cdefs.h> 40 __KERNEL_RCSID(0, "$NetBSD: gdt.c,v 1.24 2010/07/07 01:14:52 chs Exp $"); 41 42 #include "opt_multiprocessor.h" 43 #include "opt_xen.h" 44 #include "opt_user_ldt.h" 45 46 #include <sys/param.h> 47 #include <sys/systm.h> 48 #include <sys/proc.h> 49 #include <sys/mutex.h> 50 #include <sys/cpu.h> 51 52 #include <uvm/uvm.h> 53 54 #include <machine/gdt.h> 55 56 #ifdef XEN 57 #include <xen/hypervisor.h> 58 #endif 59 60 int gdt_size; /* size of GDT in bytes */ 61 int gdt_dyncount; /* number of dyn. allocated GDT entries in use */ 62 int gdt_dynavail; 63 int gdt_next; /* next available slot for sweeping */ 64 int gdt_free; /* next free slot; terminated with GNULL_SEL */ 65 66 void gdt_init(void); 67 void gdt_grow(void); 68 int gdt_get_slot(void); 69 void gdt_put_slot(int); 70 71 void 72 update_descriptor(void *tp, void *ep) 73 { 74 uint64_t *table, *entry; 75 76 table = tp; 77 entry = ep; 78 79 #ifndef XEN 80 *table = *entry; 81 #else 82 paddr_t pa; 83 84 if (!pmap_extract_ma(pmap_kernel(), (vaddr_t)table, &pa) || 85 HYPERVISOR_update_descriptor(pa, *entry)) 86 panic("HYPERVISOR_update_descriptor failed\n"); 87 #endif 88 } 89 90 void 91 set_sys_gdt(int slot, void *base, size_t limit, 92 int type, int dpl, int gran) 93 { 94 union { 95 struct sys_segment_descriptor sd; 96 uint64_t bits[2]; 97 } d; 98 CPU_INFO_ITERATOR cii; 99 struct cpu_info *ci; 100 int idx; 101 102 set_sys_segment(&d.sd, base, limit, type, dpl, gran); 103 idx = IDXSEL(GDYNSEL(slot, SEL_KPL)); 104 for (CPU_INFO_FOREACH(cii, ci)) { 105 KASSERT(ci->ci_gdt != NULL); 106 update_descriptor(&ci->ci_gdt[idx + 0], &d.bits[0]); 107 update_descriptor(&ci->ci_gdt[idx + 1], &d.bits[1]); 108 } 109 } 110 111 /* 112 * Initialize the GDT. 113 */ 114 void 115 gdt_init(void) 116 { 117 char *old_gdt; 118 struct vm_page *pg; 119 vaddr_t va; 120 struct cpu_info *ci = &cpu_info_primary; 121 122 gdt_size = MINGDTSIZ; 123 gdt_dyncount = 0; 124 gdt_next = 0; 125 gdt_free = GNULL_SEL; 126 gdt_dynavail = 127 (gdt_size - DYNSEL_START) / sizeof (struct sys_segment_descriptor); 128 129 old_gdt = gdtstore; 130 gdtstore = (char *)uvm_km_alloc(kernel_map, MAXGDTSIZ, 0, 131 UVM_KMF_VAONLY); 132 for (va = (vaddr_t)gdtstore; va < (vaddr_t)gdtstore + MINGDTSIZ; 133 va += PAGE_SIZE) { 134 pg = uvm_pagealloc(NULL, 0, NULL, UVM_PGA_ZERO); 135 if (pg == NULL) { 136 panic("gdt_init: no pages"); 137 } 138 pmap_kenter_pa(va, VM_PAGE_TO_PHYS(pg), 139 VM_PROT_READ | VM_PROT_WRITE, 0); 140 } 141 pmap_update(pmap_kernel()); 142 memcpy(gdtstore, old_gdt, DYNSEL_START); 143 ci->ci_gdt = (void *)gdtstore; 144 #ifndef XEN 145 set_sys_segment(GDT_ADDR_SYS(gdtstore, GLDT_SEL), ldtstore, 146 LDT_SIZE - 1, SDT_SYSLDT, SEL_KPL, 0); 147 #endif 148 gdt_init_cpu(ci); 149 } 150 151 /* 152 * Allocate shadow GDT for a slave CPU. 153 */ 154 void 155 gdt_alloc_cpu(struct cpu_info *ci) 156 { 157 int max_len = MAXGDTSIZ; 158 int min_len = MINGDTSIZ; 159 struct vm_page *pg; 160 vaddr_t va; 161 162 ci->ci_gdt = (union descriptor *)uvm_km_alloc(kernel_map, max_len, 163 0, UVM_KMF_VAONLY); 164 for (va = (vaddr_t)ci->ci_gdt; va < (vaddr_t)ci->ci_gdt + min_len; 165 va += PAGE_SIZE) { 166 while ((pg = uvm_pagealloc(NULL, 0, NULL, UVM_PGA_ZERO)) 167 == NULL) { 168 uvm_wait("gdt_alloc_cpu"); 169 } 170 pmap_kenter_pa(va, VM_PAGE_TO_PHYS(pg), 171 VM_PROT_READ | VM_PROT_WRITE, 0); 172 } 173 pmap_update(pmap_kernel()); 174 memset(ci->ci_gdt, 0, min_len); 175 memcpy(ci->ci_gdt, gdtstore, gdt_size); 176 } 177 178 179 /* 180 * Load appropriate gdt descriptor; we better be running on *ci 181 * (for the most part, this is how a CPU knows who it is). 182 */ 183 void 184 gdt_init_cpu(struct cpu_info *ci) 185 { 186 struct region_descriptor region; 187 188 #ifndef XEN 189 setregion(®ion, ci->ci_gdt, (uint16_t)(MAXGDTSIZ - 1)); 190 #else 191 setregion(®ion, ci->ci_gdt, (uint16_t)(gdt_size - 1)); 192 #endif 193 lgdt(®ion); 194 } 195 196 #ifdef MULTIPROCESSOR 197 void 198 gdt_reload_cpu(struct cpu_info *ci) 199 { 200 struct region_descriptor region; 201 202 #ifndef XEN 203 setregion(®ion, ci->ci_gdt, MAXGDTSIZ - 1); 204 #else 205 setregion(®ion, ci->ci_gdt, gdt_size - 1); 206 #endif 207 lgdt(®ion); 208 } 209 #endif 210 211 212 /* 213 * Grow or shrink the GDT. 214 */ 215 void 216 gdt_grow(void) 217 { 218 size_t old_len, new_len; 219 struct vm_page *pg; 220 vaddr_t va; 221 222 old_len = gdt_size; 223 gdt_size <<= 1; 224 new_len = old_len << 1; 225 gdt_dynavail = 226 (gdt_size - DYNSEL_START) / sizeof (struct sys_segment_descriptor); 227 228 for (va = (vaddr_t)gdtstore + old_len; va < (vaddr_t)gdtstore + new_len; 229 va += PAGE_SIZE) { 230 while ((pg = uvm_pagealloc(NULL, 0, NULL, UVM_PGA_ZERO)) == 231 NULL) { 232 uvm_wait("gdt_grow"); 233 } 234 pmap_kenter_pa(va, VM_PAGE_TO_PHYS(pg), 235 VM_PROT_READ | VM_PROT_WRITE, 0); 236 } 237 pmap_update(pmap_kernel()); 238 } 239 240 /* 241 * Allocate a GDT slot as follows: 242 * 1) If there are entries on the free list, use those. 243 * 2) If there are fewer than gdt_dynavail entries in use, there are free slots 244 * near the end that we can sweep through. 245 * 3) As a last resort, we increase the size of the GDT, and sweep through 246 * the new slots. 247 */ 248 int 249 gdt_get_slot(void) 250 { 251 int slot; 252 struct sys_segment_descriptor *gdt; 253 254 gdt = (struct sys_segment_descriptor *)&gdtstore[DYNSEL_START]; 255 256 KASSERT(mutex_owned(&cpu_lock)); 257 258 if (gdt_free != GNULL_SEL) { 259 slot = gdt_free; 260 gdt_free = gdt[slot].sd_xx3; /* XXXfvdl res. field abuse */ 261 } else { 262 #ifdef DIAGNOSTIC 263 if (gdt_next != gdt_dyncount) 264 panic("gdt_get_slot botch 1"); 265 #endif 266 if (gdt_next >= gdt_dynavail) { 267 #ifdef DIAGNOSTIC 268 if (gdt_size >= MAXGDTSIZ) 269 panic("gdt_get_slot botch 2"); 270 #endif 271 gdt_grow(); 272 } 273 slot = gdt_next++; 274 } 275 276 gdt_dyncount++; 277 return (slot); 278 } 279 280 /* 281 * Deallocate a GDT slot, putting it on the free list. 282 */ 283 void 284 gdt_put_slot(int slot) 285 { 286 struct sys_segment_descriptor *gdt; 287 288 KASSERT(mutex_owned(&cpu_lock)); 289 290 gdt = (struct sys_segment_descriptor *)&gdtstore[DYNSEL_START]; 291 292 gdt_dyncount--; 293 gdt[slot].sd_type = SDT_SYSNULL; 294 gdt[slot].sd_xx3 = gdt_free; 295 gdt_free = slot; 296 } 297 298 int 299 tss_alloc(struct x86_64_tss *tss) 300 { 301 #ifndef XEN 302 int slot; 303 struct sys_segment_descriptor *gdt; 304 305 gdt = (struct sys_segment_descriptor *)&gdtstore[DYNSEL_START]; 306 307 mutex_enter(&cpu_lock); 308 slot = gdt_get_slot(); 309 set_sys_gdt(slot, tss, sizeof (struct x86_64_tss) - 1, 310 SDT_SYS386TSS, SEL_KPL, 0); 311 mutex_exit(&cpu_lock); 312 return GDYNSEL(slot, SEL_KPL); 313 #else /* XEN */ 314 /* TSS, what for? */ 315 return GSEL(GNULL_SEL, SEL_KPL); 316 #endif 317 } 318 319 void 320 tss_free(int sel) 321 { 322 #ifndef XEN 323 mutex_enter(&cpu_lock); 324 gdt_put_slot(IDXDYNSEL(sel)); 325 mutex_exit(&cpu_lock); 326 #else 327 KASSERT(sel == GSEL(GNULL_SEL, SEL_KPL)); 328 #endif 329 } 330 331 #ifdef USER_LDT 332 void 333 ldt_alloc(struct pmap *pmap, char *ldt, size_t len) 334 { 335 int slot; 336 struct sys_segment_descriptor *gdt; 337 338 KASSERT(mutex_owned(&cpu_lock)); 339 340 gdt = (struct sys_segment_descriptor *)&gdtstore[DYNSEL_START]; 341 342 slot = gdt_get_slot(); 343 set_sys_gdt(slot, ldt, len - 1, SDT_SYSLDT, SEL_KPL, 0); 344 pmap->pm_ldt_sel = GSEL(slot, SEL_KPL); 345 } 346 347 void 348 ldt_free(struct pmap *pmap) 349 { 350 int slot; 351 352 KASSERT(mutex_owned(&cpu_lock)); 353 354 slot = IDXDYNSEL(pmap->pm_ldt_sel); 355 356 gdt_put_slot(slot); 357 } 358 #endif 359 360 #ifdef XEN 361 void 362 lgdt(struct region_descriptor *desc) 363 { 364 paddr_t frames[16]; 365 int i; 366 vaddr_t va; 367 368 /* 369 * XXX: Xen even checks descriptors AFTER limit. 370 * Zero out last frame after limit if needed. 371 */ 372 va = desc->rd_base + desc->rd_limit + 1; 373 __PRINTK(("memset 0x%lx -> 0x%lx\n", va, roundup(va, PAGE_SIZE))); 374 memset((void *) va, 0, roundup(va, PAGE_SIZE) - va); 375 for (i = 0; i < roundup(desc->rd_limit, PAGE_SIZE) >> PAGE_SHIFT; i++) { 376 377 /* 378 * The lgdt instruction uses virtual addresses, 379 * do some translation for Xen. 380 * Mark pages R/O too, else Xen will refuse to use them. 381 */ 382 383 frames[i] = ((paddr_t) xpmap_ptetomach( 384 (pt_entry_t *) (desc->rd_base + (i << PAGE_SHIFT)))) 385 >> PAGE_SHIFT; 386 __PRINTK(("frames[%d] = 0x%lx (pa 0x%lx)\n", i, frames[i], 387 xpmap_mtop(frames[i] << PAGE_SHIFT))); 388 pmap_pte_clearbits(kvtopte(desc->rd_base + (i << PAGE_SHIFT)), 389 PG_RW); 390 } 391 __PRINTK(("HYPERVISOR_set_gdt(%d)\n", (desc->rd_limit + 1) >> 3)); 392 393 if (HYPERVISOR_set_gdt(frames, (desc->rd_limit + 1) >> 3)) 394 panic("lgdt(): HYPERVISOR_set_gdt() failed"); 395 lgdt_finish(); 396 } 397 #endif 398