Home | History | Annotate | Line # | Download | only in amd64
      1 /*	$NetBSD: gdt.c,v 1.49 2026/08/23 22:08:40 riastradh 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 (at) 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.49 2026/08/23 22:08:40 riastradh 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 #include <machine/pmap_private.h>
     56 
     57 #ifdef XENPV
     58 #include <xen/hypervisor.h>
     59 #endif
     60 
     61 #define NSLOTS(sz)	\
     62 	(((sz) - DYNSEL_START) / sizeof(struct sys_segment_descriptor))
     63 #define NDYNSLOTS	NSLOTS(MAXGDTSIZ)
     64 
     65 typedef struct {
     66 	bool busy[NDYNSLOTS];
     67 	size_t nslots;
     68 } gdt_bitmap_t;
     69 
     70 /* size of GDT in bytes */
     71 #ifdef XENPV
     72 const size_t gdt_size = FIRST_RESERVED_GDT_BYTE;
     73 #else
     74 const size_t gdt_size = MAXGDTSIZ;
     75 #endif
     76 
     77 /* bitmap of busy slots */
     78 static gdt_bitmap_t gdt_bitmap;
     79 
     80 #if defined(USER_LDT) || !defined(XENPV)
     81 static void set_sys_gdt(int, void *, size_t, int, int, int);
     82 #endif
     83 
     84 void
     85 update_descriptor(void *tp, void *ep)
     86 {
     87 	uint64_t *table, *entry;
     88 
     89 	table = tp;
     90 	entry = ep;
     91 
     92 #ifndef XENPV
     93 	*table = *entry;
     94 #else
     95 	paddr_t pa;
     96 
     97 	if (!pmap_extract_ma(pmap_kernel(), (vaddr_t)table, &pa) ||
     98 	    HYPERVISOR_update_descriptor(pa, *entry))
     99 		panic("HYPERVISOR_update_descriptor failed");
    100 #endif
    101 }
    102 
    103 #if defined(USER_LDT) || !defined(XENPV)
    104 /*
    105  * Called on a newly-allocated GDT slot, so no race between CPUs.
    106  */
    107 static void
    108 set_sys_gdt(int slot, void *base, size_t limit, int type, int dpl, int gran)
    109 {
    110 	union {
    111 		struct sys_segment_descriptor sd;
    112 		uint64_t bits[2];
    113 	} d;
    114 	CPU_INFO_ITERATOR cii;
    115 	struct cpu_info *ci;
    116 	int idx;
    117 
    118 	set_sys_segment(&d.sd, base, limit, type, dpl, gran);
    119 	idx = IDXSEL(GDYNSEL(slot, SEL_KPL));
    120 	for (CPU_INFO_FOREACH(cii, ci)) {
    121 		KASSERT(ci->ci_gdt != NULL);
    122 		update_descriptor(&ci->ci_gdt[idx + 0], &d.bits[0]);
    123 		update_descriptor(&ci->ci_gdt[idx + 1], &d.bits[1]);
    124 	}
    125 }
    126 #endif	/* USER_LDT || !XENPV */
    127 
    128 /*
    129  * Initialize the GDT. We already have a gdtstore, which was temporarily used
    130  * by the bootstrap code. Now, we allocate a new gdtstore, and put it in cpu0.
    131  */
    132 void
    133 gdt_init(void)
    134 {
    135 	char *old_gdt;
    136 	struct cpu_info *ci = &cpu_info_primary;
    137 
    138 	/* Initialize the global values */
    139 	memset(&gdt_bitmap.busy, 0, sizeof(gdt_bitmap.busy));
    140 	gdt_bitmap.nslots = NSLOTS(gdt_size);
    141 
    142 	old_gdt = gdtstore;
    143 
    144 #ifdef __HAVE_PCPU_AREA
    145 	/* The GDT is part of the pcpuarea */
    146 	gdtstore = (char *)&pcpuarea->ent[cpu_index(ci)].gdt;
    147 #else
    148 	struct vm_page *pg;
    149 	vaddr_t va;
    150 
    151 	/* Allocate gdt_size bytes of memory. */
    152 	gdtstore = (char *)uvm_km_alloc(kernel_map, gdt_size, 0,
    153 	    UVM_KMF_VAONLY);
    154 	for (va = (vaddr_t)gdtstore; va < (vaddr_t)gdtstore + gdt_size;
    155 	    va += PAGE_SIZE) {
    156 		pg = uvm_pagealloc(NULL, 0, NULL, UVM_PGA_ZERO);
    157 		if (pg == NULL) {
    158 			panic("gdt_init: no pages");
    159 		}
    160 		pmap_kenter_pa(va, VM_PAGE_TO_PHYS(pg),
    161 		    VM_PROT_READ | VM_PROT_WRITE, 0);
    162 	}
    163 	pmap_update(pmap_kernel());
    164 #endif
    165 
    166 	/* Copy the initial bootstrap GDT into the new area. */
    167 	memcpy(gdtstore, old_gdt, DYNSEL_START);
    168 	ci->ci_gdt = (void *)gdtstore;
    169 #ifndef XENPV
    170 	set_sys_segment(GDT_ADDR_SYS(gdtstore, GLDT_SEL), ldtstore,
    171 	    LDT_SIZE - 1, SDT_SYSLDT, SEL_KPL, 0);
    172 #endif
    173 
    174 	gdt_init_cpu(ci);
    175 }
    176 
    177 /*
    178  * Allocate shadow GDT for a secondary CPU. It contains the same values as the
    179  * GDT present in cpu0 (gdtstore).
    180  */
    181 void
    182 gdt_alloc_cpu(struct cpu_info *ci)
    183 {
    184 #ifdef __HAVE_PCPU_AREA
    185 	ci->ci_gdt = (union descriptor *)&pcpuarea->ent[cpu_index(ci)].gdt;
    186 #else
    187 	uint64_t ticket;
    188 	struct vm_page *pg;
    189 	vaddr_t va;
    190 
    191 	ci->ci_gdt = (union descriptor *)uvm_km_alloc(kernel_map, gdt_size,
    192 	    0, UVM_KMF_VAONLY);
    193 	for (va = (vaddr_t)ci->ci_gdt; va < (vaddr_t)ci->ci_gdt + gdt_size;
    194 	    va += PAGE_SIZE) {
    195 		while (ticket = uvm_wait_prepare(),
    196 		    (pg = uvm_pagealloc(NULL, 0, NULL, UVM_PGA_ZERO))
    197 		    == NULL) {
    198 			uvm_wait("gdt_alloc_cpu", ticket);
    199 		}
    200 		pmap_kenter_pa(va, VM_PAGE_TO_PHYS(pg),
    201 		    VM_PROT_READ | VM_PROT_WRITE, 0);
    202 	}
    203 	pmap_update(pmap_kernel());
    204 #endif
    205 
    206 	memcpy(ci->ci_gdt, gdtstore, gdt_size);
    207 }
    208 
    209 /*
    210  * Load appropriate GDT descriptor into the currently running CPU, which must
    211  * be ci.
    212  */
    213 void
    214 gdt_init_cpu(struct cpu_info *ci)
    215 {
    216 	struct region_descriptor region;
    217 
    218 	KASSERT(curcpu() == ci);
    219 
    220 	setregion(&region, ci->ci_gdt, (uint16_t)(gdt_size - 1));
    221 	lgdt(&region);
    222 }
    223 
    224 #if !defined(XENPV) || defined(USER_LDT)
    225 static int
    226 gdt_get_slot(void)
    227 {
    228 	size_t i;
    229 
    230 	KASSERT(mutex_owned(&cpu_lock));
    231 
    232 	for (i = 0; i < gdt_bitmap.nslots; i++) {
    233 		if (!gdt_bitmap.busy[i]) {
    234 			gdt_bitmap.busy[i] = true;
    235 			return (int)i;
    236 		}
    237 	}
    238 	panic("gdt_get_slot: out of memory");
    239 
    240 	/* NOTREACHED */
    241 	return 0;
    242 }
    243 
    244 static void
    245 gdt_put_slot(int slot)
    246 {
    247 	KASSERT(mutex_owned(&cpu_lock));
    248 	KASSERT(slot < gdt_bitmap.nslots);
    249 	gdt_bitmap.busy[slot] = false;
    250 }
    251 #endif
    252 
    253 int
    254 tss_alloc(struct x86_64_tss *tss)
    255 {
    256 #ifndef XENPV
    257 	int slot;
    258 
    259 	mutex_enter(&cpu_lock);
    260 
    261 	slot = gdt_get_slot();
    262 	set_sys_gdt(slot, tss, sizeof(struct x86_64_tss) - 1, SDT_SYS386TSS,
    263 	    SEL_KPL, 0);
    264 
    265 	mutex_exit(&cpu_lock);
    266 
    267 	return GDYNSEL(slot, SEL_KPL);
    268 #else
    269 	/* TSS, what for? */
    270 	return GSEL(GNULL_SEL, SEL_KPL);
    271 #endif
    272 }
    273 
    274 void
    275 tss_free(int sel)
    276 {
    277 #ifndef XENPV
    278 	mutex_enter(&cpu_lock);
    279 	gdt_put_slot(IDXDYNSEL(sel));
    280 	mutex_exit(&cpu_lock);
    281 #else
    282 	KASSERT(sel == GSEL(GNULL_SEL, SEL_KPL));
    283 #endif
    284 }
    285 
    286 #ifdef USER_LDT
    287 int
    288 ldt_alloc(void *ldtp, size_t len)
    289 {
    290 	int slot;
    291 
    292 	KASSERT(mutex_owned(&cpu_lock));
    293 
    294 	slot = gdt_get_slot();
    295 	set_sys_gdt(slot, ldtp, len - 1, SDT_SYSLDT, SEL_KPL, 0);
    296 
    297 	return GDYNSEL(slot, SEL_KPL);
    298 }
    299 
    300 void
    301 ldt_free(int sel)
    302 {
    303 	int slot;
    304 
    305 	KASSERT(mutex_owned(&cpu_lock));
    306 
    307 	slot = IDXDYNSEL(sel);
    308 
    309 	gdt_put_slot(slot);
    310 }
    311 #endif
    312 
    313 #ifdef XENPV
    314 void
    315 lgdt(struct region_descriptor *desc)
    316 {
    317 	paddr_t frames[16];
    318 	size_t i;
    319 	vaddr_t va;
    320 
    321 	/*
    322 	 * Xen even checks descriptors AFTER limit. Zero out last frame after
    323 	 * limit if needed.
    324 	 */
    325 	va = desc->rd_base + desc->rd_limit + 1;
    326 	memset((void *)va, 0, roundup(va, PAGE_SIZE) - va);
    327 
    328 	/*
    329 	 * The lgdt instruction uses virtual addresses, do some translation for
    330 	 * Xen. Mark pages R/O too, otherwise Xen will refuse to use them.
    331 	 */
    332 	for (i = 0; i < roundup(desc->rd_limit, PAGE_SIZE) >> PAGE_SHIFT; i++) {
    333 		va = desc->rd_base + (i << PAGE_SHIFT);
    334 		frames[i] = ((paddr_t)xpmap_ptetomach((pt_entry_t *)va)) >>
    335 		    PAGE_SHIFT;
    336 		pmap_pte_clearbits(kvtopte(va), PTE_W);
    337 	}
    338 
    339 	if (HYPERVISOR_set_gdt(frames, (desc->rd_limit + 1) >> 3))
    340 		panic("lgdt(): HYPERVISOR_set_gdt() failed");
    341 	lgdt_finish();
    342 }
    343 #endif
    344