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(&region, ci->ci_gdt, (uint16_t)(MAXGDTSIZ - 1));
190 #else
191 	setregion(&region, ci->ci_gdt, (uint16_t)(gdt_size - 1));
192 #endif
193 	lgdt(&region);
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(&region, ci->ci_gdt, MAXGDTSIZ - 1);
204 #else
205 	setregion(&region, ci->ci_gdt, gdt_size - 1);
206 #endif
207 	lgdt(&region);
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