From 4d79b40a53d855c41137fa44bcafa62f5ae9f55d Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Sun, 29 Mar 2020 17:13:12 +0200 Subject: Fix 64bit TSS/LDT system descriptors * i386/i386/seg.h (real_descriptor64): New structure. (fill_descriptor64): New function. * i386/i386/gdt.h [__x86_64__] (KERNEL_TSS): Set to 0x40 instead of 0x20. [__x86_64__] (USER_TSS): Set to 0x58 instead of 0x30. [__x86_64__] (GDTSZ): Set to 12 instead of 11. (_fill_gdt_descriptor): New macro. (_fill_gdt_descriptor64, fill_gdt_descriptor64): New macros. (_fill_gdt_sys_descriptor, fill_gdt_sys_descriptor): New macros. * i386/i386/ktss.c (ktss_init): Use fill_gdt_sys_descriptor instead of fill_gdt_descriptor to set KERNEL_TSS GDT entry. * i386/i386/ldt.c (ldt_init): Likewise for KERNEL_LDT GDT entry. * i386/i386/ldt.h (fill_ldt_descriptor, fill_ldt_gate): Use sel_idx instead of reimplementing it. * i386/i386/mp_desc.c (mp_desc_init): Use _fill_gdt_sys_descriptor instead of reimplementing it. --- i386/i386/gdt.h | 59 +++++++++++++++++++++++++++++++++++++++++++++++++---- i386/i386/ktss.c | 10 +++------ i386/i386/ldt.c | 12 ++++------- i386/i386/ldt.h | 6 +++--- i386/i386/mp_desc.c | 7 ++----- i386/i386/seg.h | 50 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 117 insertions(+), 27 deletions(-) diff --git a/i386/i386/gdt.h b/i386/i386/gdt.h index d865640b..9879ad3e 100644 --- a/i386/i386/gdt.h +++ b/i386/i386/gdt.h @@ -40,28 +40,79 @@ */ #define KERNEL_CS (0x08 | KERNEL_RING) /* kernel code */ #define KERNEL_DS (0x10 | KERNEL_RING) /* kernel data */ + + #ifndef MACH_PV_DESCRIPTORS #define KERNEL_LDT 0x18 /* master LDT */ #endif /* MACH_PV_DESCRIPTORS */ + +#ifdef __x86_64__ +/* LDT needs two entries */ +#define KERNEL_TSS 0x40 /* master TSS (uniprocessor) */ +#else #define KERNEL_TSS 0x20 /* master TSS (uniprocessor) */ +#endif + + #define USER_LDT 0x28 /* place for per-thread LDT */ + +#ifdef __x86_64__ +/* LDT needs two entries */ +#define USER_TSS 0x58 /* place for per-thread TSS + that holds IO bitmap */ +#else #define USER_TSS 0x30 /* place for per-thread TSS that holds IO bitmap */ +#endif + + #ifndef MACH_PV_DESCRIPTORS #define LINEAR_DS 0x38 /* linear mapping */ #endif /* MACH_PV_DESCRIPTORS */ -/* 0x40 was USER_FPREGS, now free */ -#define USER_GDT 0x48 /* user-defined GDT entries */ +/* 0x40 was USER_FPREGS, now used by TSS in 64bit mode */ + +#define USER_GDT 0x48 /* user-defined 32bit GDT entries */ #define USER_GDT_SLOTS 2 -#define GDTSZ (USER_GDT/8 + USER_GDT_SLOTS) +/* 0x58 used by user TSS in 64bit mode */ + +#ifdef __x86_64__ +#define GDTSZ sel_idx(0x60) +#else +#define GDTSZ sel_idx(0x58) +#endif extern struct real_descriptor gdt[GDTSZ]; /* Fill a segment descriptor in the GDT. */ +#define _fill_gdt_descriptor(_gdt, segment, base, limit, access, sizebits) \ + fill_descriptor(&_gdt[sel_idx(segment)], base, limit, access, sizebits) + #define fill_gdt_descriptor(segment, base, limit, access, sizebits) \ - fill_descriptor(&gdt[segment/8], base, limit, access, sizebits) + _fill_gdt_descriptor(gdt, segment, base, limit, access, sizebits) + +/* 64bit variant */ +#ifdef __x86_64__ +#define _fill_gdt_descriptor64(_gdt, segment, base, limit, access, sizebits) \ + fill_descriptor64((struct real_descriptor64 *) &_gdt[sel_idx(segment)], base, limit, access, sizebits) + +#define fill_gdt_descriptor64(segment, base, limit, access, sizebits) \ + _fill_gdt_descriptor64(gdt, segment, base, limit, access, sizebits) +#endif + +/* System descriptor variants */ +#ifdef __x86_64__ +#define _fill_gdt_sys_descriptor(_gdt, segment, base, limit, access, sizebits) \ + _fill_gdt_descriptor64(_gdt, segment, base, limit, access, sizebits) +#define fill_gdt_sys_descriptor(segment, base, limit, access, sizebits) \ + fill_gdt_descriptor64(segment, base, limit, access, sizebits) +#else +#define _fill_gdt_sys_descriptor(_gdt, segment, base, limit, access, sizebits) \ + _fill_gdt_descriptor(_gdt, segment, base, limit, access, sizebits) +#define fill_gdt_sys_descriptor(segment, base, limit, access, sizebits) \ + fill_gdt_descriptor(segment, base, limit, access, sizebits) +#endif extern void gdt_init(void); diff --git a/i386/i386/ktss.c b/i386/i386/ktss.c index 62de2a27..917e6305 100644 --- a/i386/i386/ktss.c +++ b/i386/i386/ktss.c @@ -51,14 +51,10 @@ ktss_init(void) if (hyp_stack_switch(KERNEL_DS, (unsigned long)(exception_stack+1024))) panic("couldn't register exception stack\n"); #else /* MACH_RING1 */ - -#ifdef __x86_64__ -#warning FIXME -#endif /* Initialize the master TSS descriptor. */ - fill_gdt_descriptor(KERNEL_TSS, - kvtolin(&ktss), sizeof(struct task_tss) - 1, - ACC_PL_K|ACC_TSS, 0); + fill_gdt_sys_descriptor(KERNEL_TSS, + kvtolin(&ktss), sizeof(struct task_tss) - 1, + ACC_PL_K|ACC_TSS, 0); /* Initialize the master TSS. */ ktss.tss.ss0 = KERNEL_DS; diff --git a/i386/i386/ldt.c b/i386/i386/ldt.c index 99d2f7fe..261df93a 100644 --- a/i386/i386/ldt.c +++ b/i386/i386/ldt.c @@ -47,22 +47,18 @@ struct real_descriptor ldt[LDTSZ]; void ldt_init(void) { -#ifdef __x86_64__ -#warning FIXME -#endif - #ifdef MACH_PV_DESCRIPTORS #ifdef MACH_PV_PAGETABLES pmap_set_page_readwrite(ldt); #endif /* MACH_PV_PAGETABLES */ #else /* MACH_PV_DESCRIPTORS */ /* Initialize the master LDT descriptor in the GDT. */ - fill_gdt_descriptor(KERNEL_LDT, - kvtolin(&ldt), sizeof(ldt)-1, - ACC_PL_K|ACC_LDT, 0); + fill_gdt_sys_descriptor(KERNEL_LDT, + kvtolin(&ldt), sizeof(ldt)-1, + ACC_PL_K|ACC_LDT, 0); #endif /* MACH_PV_DESCRIPTORS */ - /* Initialize the LDT descriptors. */ + /* Initialize the 32bit LDT descriptors. */ fill_ldt_gate(USER_SCALL, (vm_offset_t)&syscall, KERNEL_CS, ACC_PL_U|ACC_CALL_GATE, 0); diff --git a/i386/i386/ldt.h b/i386/i386/ldt.h index 81c49782..1f0d7014 100644 --- a/i386/i386/ldt.h +++ b/i386/i386/ldt.h @@ -56,12 +56,12 @@ extern struct real_descriptor ldt[LDTSZ]; -/* Fill a segment descriptor in the LDT. */ +/* Fill a 32bit segment descriptor in the LDT. */ #define fill_ldt_descriptor(selector, base, limit, access, sizebits) \ - fill_descriptor(&ldt[selector/8], base, limit, access, sizebits) + fill_descriptor(&ldt[sel_idx(selector)], base, limit, access, sizebits) #define fill_ldt_gate(selector, offset, dest_selector, access, word_count) \ - fill_gate((struct real_gate*)&ldt[selector/8], \ + fill_gate((struct real_gate*)&ldt[sel_idx(selector)], \ offset, dest_selector, access, word_count) void ldt_init(void); diff --git a/i386/i386/mp_desc.c b/i386/i386/mp_desc.c index d62d4958..07cc389a 100644 --- a/i386/i386/mp_desc.c +++ b/i386/i386/mp_desc.c @@ -146,14 +146,11 @@ mp_desc_init(int mycpu) #ifdef MACH_RING1 panic("TODO %s:%d\n",__FILE__,__LINE__); #else /* MACH_RING1 */ -#ifdef __x86_64__ -#warning FIXME -#endif - fill_descriptor(&mpt->gdt[sel_idx(KERNEL_LDT)], + _fill_gdt_sys_descriptor(mpt->gdt, KERNEL_LDT, (unsigned)&mpt->ldt, LDTSZ * sizeof(struct real_descriptor) - 1, ACC_P|ACC_PL_K|ACC_LDT, 0); - fill_descriptor(&mpt->gdt[sel_idx(KERNEL_TSS)], + _fill_gdt_sys_descriptor(mpt->gdt, KERNEL_TSS, (unsigned)&mpt->ktss, sizeof(struct task_tss) - 1, ACC_P|ACC_PL_K|ACC_TSS, 0); diff --git a/i386/i386/seg.h b/i386/i386/seg.h index 86e73c34..b1a14fe4 100644 --- a/i386/i386/seg.h +++ b/i386/i386/seg.h @@ -59,6 +59,22 @@ struct real_descriptor { base_high:8; /* base 24..31 */ }; +#ifdef __x86_64__ +struct real_descriptor64 { + unsigned int limit_low:16, /* limit 0..15 */ + base_low:16, /* base 0..15 */ + base_med:8, /* base 16..23 */ + access:8, /* access byte */ + limit_high:4, /* limit 16..19 */ + granularity:4, /* granularity */ + base_high:8, /* base 24..31 */ + base_ext:32, /* base 32..63 */ + reserved1:8, + zero:5, + reserved2:19; +}; +#endif + struct real_gate { unsigned int offset_low:16, /* offset 0..15 */ selector:16, @@ -189,6 +205,40 @@ fill_descriptor(struct real_descriptor *_desc, unsigned base, unsigned limit, #endif /* MACH_PV_DESCRIPTORS */ } +#ifdef __x86_64__ +MACH_INLINE void +fill_descriptor64(struct real_descriptor64 *_desc, unsigned long base, unsigned limit, + unsigned char access, unsigned char sizebits) +{ + /* TODO: when !MACH_PV_DESCRIPTORS, setting desc and just memcpy isn't simpler actually */ +#ifdef MACH_PV_DESCRIPTORS + struct real_descriptor64 __desc, *desc = &__desc; +#else /* MACH_PV_DESCRIPTORS */ + struct real_descriptor64 *desc = _desc; +#endif /* MACH_PV_DESCRIPTORS */ + if (limit > 0xfffff) + { + limit >>= 12; + sizebits |= SZ_G; + } + desc->limit_low = limit & 0xffff; + desc->base_low = base & 0xffff; + desc->base_med = (base >> 16) & 0xff; + desc->access = access | ACC_P; + desc->limit_high = limit >> 16; + desc->granularity = sizebits; + desc->base_high = base >> 24; + desc->base_ext = base >> 32; + desc->reserved1 = 0; + desc->zero = 0; + desc->reserved2 = 0; +#ifdef MACH_PV_DESCRIPTORS + if (hyp_do_update_descriptor(kv_to_ma(_desc), *(uint64_t*)desc)) + panic("couldn't update descriptor(%lu to %08lx%08lx)\n", (vm_offset_t) kv_to_ma(_desc), *(((unsigned long*)desc)+1), *(unsigned long *)desc); +#endif /* MACH_PV_DESCRIPTORS */ +} +#endif + /* Fill a gate with particular values. */ MACH_INLINE void fill_gate(struct real_gate *gate, unsigned offset, unsigned short selector, -- cgit v1.2.3