1 /* $NetBSD: gdt.c,v 1.26 2015/11/22 13:41:24 maxv 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.26 2015/11/22 13:41:24 maxv 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
update_descriptor(void * tp,void * ep)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
set_sys_gdt(int slot,void * base,size_t limit,int type,int dpl,int gran)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
gdt_init(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
gdt_alloc_cpu(struct cpu_info * ci)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
gdt_init_cpu(struct cpu_info * ci)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
gdt_reload_cpu(struct cpu_info * ci)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
gdt_grow(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
gdt_get_slot(void)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
gdt_put_slot(int slot)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
tss_alloc(struct x86_64_tss * tss)299 tss_alloc(struct x86_64_tss *tss)
300 {
301 #ifndef XEN
302 int slot;
303
304 mutex_enter(&cpu_lock);
305 slot = gdt_get_slot();
306 set_sys_gdt(slot, tss, sizeof (struct x86_64_tss) - 1,
307 SDT_SYS386TSS, SEL_KPL, 0);
308 mutex_exit(&cpu_lock);
309 return GDYNSEL(slot, SEL_KPL);
310 #else /* XEN */
311 /* TSS, what for? */
312 return GSEL(GNULL_SEL, SEL_KPL);
313 #endif
314 }
315
316 void
tss_free(int sel)317 tss_free(int sel)
318 {
319 #ifndef XEN
320 mutex_enter(&cpu_lock);
321 gdt_put_slot(IDXDYNSEL(sel));
322 mutex_exit(&cpu_lock);
323 #else
324 KASSERT(sel == GSEL(GNULL_SEL, SEL_KPL));
325 #endif
326 }
327
328 #ifdef USER_LDT
329 void
ldt_alloc(struct pmap * pmap,char * ldt,size_t len)330 ldt_alloc(struct pmap *pmap, char *ldt, size_t len)
331 {
332 int slot;
333 struct sys_segment_descriptor *gdt;
334
335 KASSERT(mutex_owned(&cpu_lock));
336
337 gdt = (struct sys_segment_descriptor *)&gdtstore[DYNSEL_START];
338
339 slot = gdt_get_slot();
340 set_sys_gdt(slot, ldt, len - 1, SDT_SYSLDT, SEL_KPL, 0);
341 pmap->pm_ldt_sel = GSEL(slot, SEL_KPL);
342 }
343
344 void
ldt_free(struct pmap * pmap)345 ldt_free(struct pmap *pmap)
346 {
347 int slot;
348
349 KASSERT(mutex_owned(&cpu_lock));
350
351 slot = IDXDYNSEL(pmap->pm_ldt_sel);
352
353 gdt_put_slot(slot);
354 }
355 #endif
356
357 #ifdef XEN
358 void
lgdt(struct region_descriptor * desc)359 lgdt(struct region_descriptor *desc)
360 {
361 paddr_t frames[16];
362 int i;
363 vaddr_t va;
364
365 /*
366 * XXX: Xen even checks descriptors AFTER limit.
367 * Zero out last frame after limit if needed.
368 */
369 va = desc->rd_base + desc->rd_limit + 1;
370 __PRINTK(("memset 0x%lx -> 0x%lx\n", va, roundup(va, PAGE_SIZE)));
371 memset((void *) va, 0, roundup(va, PAGE_SIZE) - va);
372 for (i = 0; i < roundup(desc->rd_limit, PAGE_SIZE) >> PAGE_SHIFT; i++) {
373
374 /*
375 * The lgdt instruction uses virtual addresses,
376 * do some translation for Xen.
377 * Mark pages R/O too, else Xen will refuse to use them.
378 */
379
380 frames[i] = ((paddr_t) xpmap_ptetomach(
381 (pt_entry_t *) (desc->rd_base + (i << PAGE_SHIFT))))
382 >> PAGE_SHIFT;
383 __PRINTK(("frames[%d] = 0x%lx (pa 0x%lx)\n", i, frames[i],
384 xpmap_mtop(frames[i] << PAGE_SHIFT)));
385 pmap_pte_clearbits(kvtopte(desc->rd_base + (i << PAGE_SHIFT)),
386 PG_RW);
387 }
388 __PRINTK(("HYPERVISOR_set_gdt(%d)\n", (desc->rd_limit + 1) >> 3));
389
390 if (HYPERVISOR_set_gdt(frames, (desc->rd_limit + 1) >> 3))
391 panic("lgdt(): HYPERVISOR_set_gdt() failed");
392 lgdt_finish();
393 }
394 #endif
395