mirror of
https://github.com/Mathewnd/Astral.git
synced 2026-11-01 15:08:21 +00:00
kernel: ldt support
This commit is contained in:
@@ -8,11 +8,17 @@
|
|||||||
#define ARCH_CTL_GET_GSBASE 2
|
#define ARCH_CTL_GET_GSBASE 2
|
||||||
#define ARCH_CTL_GET_FSBASE 3
|
#define ARCH_CTL_GET_FSBASE 3
|
||||||
#define ARCH_CTL_SET_SYSTRACE 4
|
#define ARCH_CTL_SET_SYSTRACE 4
|
||||||
|
#define ARCH_CTL_SET_LDT_ENTRY 5
|
||||||
|
|
||||||
#define ARCH_CTL_SYSTRACE_OFF 0
|
#define ARCH_CTL_SYSTRACE_OFF 0
|
||||||
#define ARCH_CTL_SYSTRACE_SELF 1
|
#define ARCH_CTL_SYSTRACE_SELF 1
|
||||||
#define ARCH_CTL_SYSTRACE_ALL 2
|
#define ARCH_CTL_SYSTRACE_ALL 2
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint64_t entry;
|
||||||
|
uint16_t seg;
|
||||||
|
} archctl_ldt_request_t;
|
||||||
|
|
||||||
static inline int copy_u64_to_user(void *ptr, uint64_t v) {
|
static inline int copy_u64_to_user(void *ptr, uint64_t v) {
|
||||||
return usercopy_touser(ptr, &v, sizeof(v));
|
return usercopy_touser(ptr, &v, sizeof(v));
|
||||||
}
|
}
|
||||||
@@ -25,9 +31,11 @@ syscallret_t syscall_archctl(context_t *context, int func, void *arg) {
|
|||||||
|
|
||||||
if (!IS_USER_ADDRESS(arg)) {
|
if (!IS_USER_ADDRESS(arg)) {
|
||||||
ret.errno = EFAULT;
|
ret.errno = EFAULT;
|
||||||
|
ret.ret = -1;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t scratch;
|
||||||
switch (func) {
|
switch (func) {
|
||||||
case ARCH_CTL_SET_GSBASE:
|
case ARCH_CTL_SET_GSBASE:
|
||||||
// kernelgsbase because it will be switched out to user
|
// kernelgsbase because it will be switched out to user
|
||||||
@@ -42,6 +50,14 @@ syscallret_t syscall_archctl(context_t *context, int func, void *arg) {
|
|||||||
case ARCH_CTL_GET_FSBASE:
|
case ARCH_CTL_GET_FSBASE:
|
||||||
ret.errno = copy_u64_to_user(arg, rdmsr(MSR_FSBASE));
|
ret.errno = copy_u64_to_user(arg, rdmsr(MSR_FSBASE));
|
||||||
break;
|
break;
|
||||||
|
case ARCH_CTL_SET_LDT_ENTRY:
|
||||||
|
archctl_ldt_request_t req;
|
||||||
|
ret.errno = usercopy_fromuser(&req, arg, sizeof(req));
|
||||||
|
if (ret.errno)
|
||||||
|
break;
|
||||||
|
|
||||||
|
ret.errno = arch_ldt_set_entry(req.seg, req.entry);
|
||||||
|
break;
|
||||||
case ARCH_CTL_SET_SYSTRACE:
|
case ARCH_CTL_SET_SYSTRACE:
|
||||||
#ifdef SYSCALL_LOGGING
|
#ifdef SYSCALL_LOGGING
|
||||||
switch ((uint64_t)arg) {
|
switch ((uint64_t)arg) {
|
||||||
@@ -69,5 +85,6 @@ syscallret_t syscall_archctl(context_t *context, int func, void *arg) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret.ret = ret.errno ? -1 : 0;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ arch_context_switch:
|
|||||||
add rsp,8 ; remove error code
|
add rsp,8 ; remove error code
|
||||||
|
|
||||||
; check if swapgs is needed
|
; check if swapgs is needed
|
||||||
cmp qword [rsp+8], 0x23
|
cmp qword [rsp+8], 0x8
|
||||||
jne .notneeded2
|
je .notneeded2
|
||||||
swapgs
|
swapgs
|
||||||
.notneeded2:
|
.notneeded2:
|
||||||
o64 iret
|
o64 iret
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <arch/cpuid.h>
|
#include <arch/cpuid.h>
|
||||||
#include <kernel/slab.h>
|
#include <kernel/slab.h>
|
||||||
#include <logging.h>
|
#include <logging.h>
|
||||||
|
#include <arch/gdt.h>
|
||||||
|
|
||||||
#define XCR0_X87 (1ull << 0)
|
#define XCR0_X87 (1ull << 0)
|
||||||
#define XCR0_SSE (1ull << 1)
|
#define XCR0_SSE (1ull << 1)
|
||||||
@@ -12,120 +13,135 @@
|
|||||||
#define XCR0_MASK (XCR0_AVX512 | XCR0_AVX | XCR0_SSE | XCR0_X87)
|
#define XCR0_MASK (XCR0_AVX512 | XCR0_AVX | XCR0_SSE | XCR0_X87)
|
||||||
|
|
||||||
static enum {
|
static enum {
|
||||||
FXSAVE,
|
FXSAVE,
|
||||||
XSAVE,
|
XSAVE,
|
||||||
XSAVEOPT,
|
XSAVEOPT,
|
||||||
} xsave_method;
|
} xsave_method;
|
||||||
size_t arch_xsave_size;
|
size_t arch_xsave_size;
|
||||||
static uint64_t xcr0_value;
|
static uint64_t xcr0_value;
|
||||||
static scache_t *xsave_cache;
|
static scache_t *xsave_cache;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint16_t fcw;
|
uint16_t fcw;
|
||||||
uint16_t fsw;
|
uint16_t fsw;
|
||||||
uint8_t ftw;
|
uint8_t ftw;
|
||||||
uint8_t reserved1;
|
uint8_t reserved1;
|
||||||
uint16_t fop;
|
uint16_t fop;
|
||||||
uint64_t fip;
|
uint64_t fip;
|
||||||
uint64_t fdp;
|
uint64_t fdp;
|
||||||
uint32_t mxcsr;
|
uint32_t mxcsr;
|
||||||
uint32_t mxcsr_mask;
|
uint32_t mxcsr_mask;
|
||||||
struct {
|
struct {
|
||||||
uint64_t data[2];
|
uint64_t data[2];
|
||||||
} mm[8];
|
} mm[8];
|
||||||
struct {
|
struct {
|
||||||
uint64_t data[2];
|
uint64_t data[2];
|
||||||
} xmm[16];
|
} xmm[16];
|
||||||
uint64_t reserved2[6];
|
uint64_t reserved2[6];
|
||||||
uint64_t unused[6];
|
uint64_t unused[6];
|
||||||
} fxsave_area_t;
|
} fxsave_area_t;
|
||||||
|
|
||||||
void arch_extracontext_detect(void) {
|
void arch_extracontext_detect(void) {
|
||||||
if (!arch_xsave_size) {
|
if (!arch_xsave_size) {
|
||||||
cpuid_results_t results;
|
cpuid_results_t results;
|
||||||
cpuid(1, &results);
|
cpuid(1, &results);
|
||||||
if (cpuid_base_max_leaf() >= 0x0d && (results.ecx & (1 << 26))) {
|
if (cpuid_base_max_leaf() >= 0x0d && (results.ecx & (1 << 26))) {
|
||||||
xsave_method = XSAVE;
|
xsave_method = XSAVE;
|
||||||
|
|
||||||
cpuid_with_ecx(0x0d, 0, &results);
|
cpuid_with_ecx(0x0d, 0, &results);
|
||||||
xcr0_value = ((uint64_t)results.edx << 32) | results.eax;
|
xcr0_value = ((uint64_t)results.edx << 32) | results.eax;
|
||||||
xcr0_value &= XCR0_MASK;
|
xcr0_value &= XCR0_MASK;
|
||||||
} else {
|
} else {
|
||||||
xsave_method = FXSAVE;
|
xsave_method = FXSAVE;
|
||||||
arch_xsave_size = 512;
|
arch_xsave_size = 512;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xsave_method == FXSAVE) return;
|
if (xsave_method == FXSAVE) return;
|
||||||
|
|
||||||
size_t cr4;
|
size_t cr4;
|
||||||
asm volatile("mov %%cr4, %0" : "=r" (cr4));
|
asm volatile("mov %%cr4, %0" : "=r" (cr4));
|
||||||
cr4 |= (1u << 18);
|
cr4 |= (1u << 18);
|
||||||
asm volatile("mov %0, %%cr4" :: "r" (cr4));
|
asm volatile("mov %0, %%cr4" :: "r" (cr4));
|
||||||
|
|
||||||
uint32_t low = xcr0_value;
|
uint32_t low = xcr0_value;
|
||||||
uint32_t high = xcr0_value >> 32;
|
uint32_t high = xcr0_value >> 32;
|
||||||
asm volatile("xsetbv" :: "a" (low), "d" (high), "c" (0u));
|
asm volatile("xsetbv" :: "a" (low), "d" (high), "c" (0u));
|
||||||
|
|
||||||
if (!arch_xsave_size) {
|
if (!arch_xsave_size) {
|
||||||
// after xsetbv ebx is updated to the size of the context for the enabled features
|
// after xsetbv ebx is updated to the size of the context for the enabled features
|
||||||
cpuid_results_t results;
|
cpuid_results_t results;
|
||||||
cpuid_with_ecx(0x0d, 0, &results);
|
cpuid_with_ecx(0x0d, 0, &results);
|
||||||
arch_xsave_size = results.ebx;
|
arch_xsave_size = results.ebx;
|
||||||
|
|
||||||
cpuid_with_ecx(0x0d, 1, &results);
|
cpuid_with_ecx(0x0d, 1, &results);
|
||||||
if (results.eax & 1) xsave_method = XSAVEOPT;
|
if (results.eax & 1) xsave_method = XSAVEOPT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int arch_extracontext_init(extracontext_t *context) {
|
int arch_extracontext_init(extracontext_t *context) {
|
||||||
if (!xsave_cache) {
|
if (!xsave_cache) {
|
||||||
xsave_cache = slab_newcache(arch_xsave_size, xsave_method == FXSAVE ? 16 : 64, NULL, NULL);
|
xsave_cache = slab_newcache(arch_xsave_size, xsave_method == FXSAVE ? 16 : 64, NULL, NULL);
|
||||||
__assert(xsave_cache);
|
__assert(xsave_cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
context->xsave = slab_allocate(xsave_cache);
|
context->xsave = slab_allocate(xsave_cache);
|
||||||
if (!context->xsave) return 1;
|
if (!context->xsave) return 1;
|
||||||
|
|
||||||
// all sse exceptions masked
|
// all sse exceptions masked
|
||||||
// initialise the x87 FPU state as it would be after the FNINIT instruction
|
// initialise the x87 FPU state as it would be after the FNINIT instruction
|
||||||
memset(context->xsave, 0, arch_xsave_size);
|
memset(context->xsave, 0, arch_xsave_size);
|
||||||
fxsave_area_t *area = context->xsave;
|
fxsave_area_t *area = context->xsave;
|
||||||
area->mxcsr = 0x1f80;
|
area->mxcsr = 0x1f80;
|
||||||
area->fcw = 0x37f;
|
area->fcw = 0x37f;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void arch_extracontext_free(extracontext_t *context) {
|
void arch_extracontext_free(extracontext_t *context) {
|
||||||
slab_free(xsave_cache, context->xsave);
|
slab_free(xsave_cache, context->xsave);
|
||||||
}
|
}
|
||||||
|
|
||||||
void arch_extracontext_save(extracontext_t *context) {
|
void arch_extracontext_save(extracontext_t *context) {
|
||||||
context->gsbase = rdmsr(MSR_KERNELGSBASE);
|
context->gsbase = rdmsr(MSR_KERNELGSBASE);
|
||||||
context->fsbase = rdmsr(MSR_FSBASE);
|
context->fsbase = rdmsr(MSR_FSBASE);
|
||||||
|
|
||||||
switch (xsave_method) {
|
asm volatile("mov %%gs, %0" : "=r"(context->gs));
|
||||||
case FXSAVE: asm("fxsaveq %0" ::"m"(*(char(*)[arch_xsave_size])context->xsave) : "memory"); break;
|
asm volatile("mov %%fs, %0" : "=r"(context->fs));
|
||||||
case XSAVE: asm("xsaveq %0" ::"m"(*(char(*)[arch_xsave_size])context->xsave), "d"(-1), "a"(-1) : "memory"); break;
|
|
||||||
case XSAVEOPT: asm("xsaveoptq %0" ::"m"(*(char(*)[arch_xsave_size])context->xsave), "d"(-1), "a"(-1) : "memory"); break;
|
switch (xsave_method) {
|
||||||
}
|
case FXSAVE: asm("fxsaveq %0" ::"m"(*(char(*)[arch_xsave_size])context->xsave) : "memory"); break;
|
||||||
|
case XSAVE: asm("xsaveq %0" ::"m"(*(char(*)[arch_xsave_size])context->xsave), "d"(-1), "a"(-1) : "memory"); break;
|
||||||
|
case XSAVEOPT: asm("xsaveoptq %0" ::"m"(*(char(*)[arch_xsave_size])context->xsave), "d"(-1), "a"(-1) : "memory"); break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void arch_extracontext_load(extracontext_t *context) {
|
void arch_extracontext_load(extracontext_t *context) {
|
||||||
wrmsr(MSR_KERNELGSBASE, context->gsbase);
|
if (current_thread()->proc && current_thread()->proc->ldt)
|
||||||
wrmsr(MSR_FSBASE, context->fsbase);
|
arch_gdt_set_ldt(current_thread()->proc->ldt, 0xffff);
|
||||||
|
else
|
||||||
|
arch_gdt_set_ldt(NULL, 0);
|
||||||
|
|
||||||
switch (xsave_method) {
|
void *old_base = current_cpu();
|
||||||
case FXSAVE: asm("fxrstorq %0" ::"m"(*(char(*)[arch_xsave_size])context->xsave) : "memory"); break;
|
if (!load_seg_gs(context->gs)) load_seg_gs(0);
|
||||||
case XSAVE:
|
wrmsr(MSR_GSBASE, (uint64_t)old_base);
|
||||||
case XSAVEOPT: asm("xrstorq %0" ::"m"(*(char(*)[arch_xsave_size])context->xsave), "d"(-1), "a"(-1) : "memory"); break;
|
wrmsr(MSR_KERNELGSBASE, context->gsbase);
|
||||||
}
|
|
||||||
|
if (!load_seg_fs(context->fs)) load_seg_fs(0);
|
||||||
|
wrmsr(MSR_FSBASE, context->fsbase);
|
||||||
|
|
||||||
|
switch (xsave_method) {
|
||||||
|
case FXSAVE: asm("fxrstorq %0" ::"m"(*(char(*)[arch_xsave_size])context->xsave) : "memory"); break;
|
||||||
|
case XSAVE:
|
||||||
|
case XSAVEOPT: asm("xrstorq %0" ::"m"(*(char(*)[arch_xsave_size])context->xsave), "d"(-1), "a"(-1) : "memory"); break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void arch_extracontext_copy(extracontext_t *dst, const extracontext_t *src) {
|
void arch_extracontext_copy(extracontext_t *dst, const extracontext_t *src) {
|
||||||
// XXX is this behavior correct? ucontext doesnt seem to save it on linux and so it will not be saved
|
// XXX is this behavior correct? ucontext doesnt seem to save it on linux and so it will not be saved
|
||||||
dst->gsbase = src->gsbase;
|
dst->gsbase = src->gsbase;
|
||||||
dst->fsbase = src->fsbase;
|
dst->fsbase = src->fsbase;
|
||||||
memcpy(dst->xsave, src->xsave, arch_xsave_size);
|
dst->fs = dst->fs;
|
||||||
|
dst->gs = dst->gs;
|
||||||
|
memcpy(dst->xsave, src->xsave, arch_xsave_size);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,13 @@ static void x87isr(isr_t *self, context_t *ctx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void reload_ldt(isr_t *self, context_t *ctx) {
|
||||||
|
if (current_thread() == NULL || current_thread()->proc == NULL || current_thread()->proc->ldt == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
arch_ldt_invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
void arch_nmi_isr(context_t *ctx) {
|
void arch_nmi_isr(context_t *ctx) {
|
||||||
// we are in an NMI context, running on IST0
|
// we are in an NMI context, running on IST0
|
||||||
// first load the proper gsbase to get access to cpu-local variables.
|
// first load the proper gsbase to get access to cpu-local variables.
|
||||||
@@ -219,6 +226,9 @@ void arch_cpu_init() {
|
|||||||
interrupt_register(16, x87isr, NULL, IPL_IGNORE);
|
interrupt_register(16, x87isr, NULL, IPL_IGNORE);
|
||||||
interrupt_register(19, simdisr, NULL, IPL_IGNORE);
|
interrupt_register(19, simdisr, NULL, IPL_IGNORE);
|
||||||
|
|
||||||
|
// and the ldt reload one
|
||||||
|
interrupt_register(0xfc, (void *)reload_ldt, NULL, IPL_MAX);
|
||||||
|
|
||||||
// get vendor string for cpu and max cpuid eax
|
// get vendor string for cpu and max cpuid eax
|
||||||
__get_cpuid(0, &eax, &ebx, &ecx, &edx);
|
__get_cpuid(0, &eax, &ebx, &ecx, &edx);
|
||||||
int *vendor_ptr = (int *)current_cpu()->vendor;
|
int *vendor_ptr = (int *)current_cpu()->vendor;
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
; segment is in rdi
|
||||||
|
; returns 1 for success or 0 for failure
|
||||||
|
load_seg_gs:
|
||||||
|
global load_seg_gs
|
||||||
|
xor rax, rax
|
||||||
|
mov gs, rdi
|
||||||
|
inc rax
|
||||||
|
ret
|
||||||
|
load_seg_gs_fallback:
|
||||||
|
global load_seg_gs_fallback
|
||||||
|
ret
|
||||||
|
|
||||||
|
; segment is in rdi
|
||||||
|
; returns 1 for success or 0 for failure
|
||||||
|
load_seg_fs:
|
||||||
|
global load_seg_fs
|
||||||
|
xor rax, rax
|
||||||
|
mov fs, rdi
|
||||||
|
inc rax
|
||||||
|
ret
|
||||||
|
load_seg_fs_fallback:
|
||||||
|
global load_seg_fs_fallback
|
||||||
|
ret
|
||||||
@@ -5,16 +5,36 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <printf.h>
|
#include <printf.h>
|
||||||
|
|
||||||
static uint64_t template[7] = {
|
static uint64_t template[9] = {
|
||||||
0, // NULL 0x0
|
0, // NULL 0x0
|
||||||
0x00af9b000000ffff, // code64 0x8
|
0x00af9b000000ffff, // code64 0x8
|
||||||
0x00af93000000ffff, // data64 0x10
|
0x00af93000000ffff, // data64 0x10
|
||||||
0x00aff3000000ffff, // udata64 0x18
|
0x00eff3000000ffff, // udata64 0x18
|
||||||
0x00affb000000ffff, // ucode64 0x20
|
0x00affb000000ffff, // ucode64 0x20
|
||||||
0x0020890000000000, // low ist 0x28
|
0x0020890000000000, // low ist 0x28
|
||||||
0x0000000000000000 // high ist
|
0x0000000000000000, // high ist
|
||||||
|
0x0000000000000000, // LDT low
|
||||||
|
0x0000000000000000 // LDT high
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// should be called with preemption DISABLED
|
||||||
|
void arch_gdt_set_ldt(void *base, size_t limit) {
|
||||||
|
if (base == NULL) {
|
||||||
|
current_cpu()->gdt[7] = 0;
|
||||||
|
current_cpu()->gdt[8] = 0;
|
||||||
|
asm volatile("lldt %%ax" : : "a"(0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t base_low = (uint64_t)base & 0xffffffff;
|
||||||
|
current_cpu()->gdt[7] = 0x82000000ffff; // present, ldt, limit 64k
|
||||||
|
current_cpu()->gdt[7] |= (base_low & 0xffff) << 16;
|
||||||
|
current_cpu()->gdt[7] |= ((base_low >> 16) & 0xff) << 32;
|
||||||
|
current_cpu()->gdt[7] |= ((base_low >> 24) & 0xff) << 56;
|
||||||
|
current_cpu()->gdt[8] = ((uint64_t)base >> 32) & 0xffffffff;
|
||||||
|
asm volatile("lldt %%ax" : : "a"(0x38));
|
||||||
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint16_t size;
|
uint16_t size;
|
||||||
uint64_t address;
|
uint64_t address;
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ isr_%+ i:
|
|||||||
|
|
||||||
isr_common:
|
isr_common:
|
||||||
; check if swapgs is needed
|
; check if swapgs is needed
|
||||||
cmp qword [rsp+24], 0x23
|
cmp qword [rsp+24], 0x8
|
||||||
jne .notneeded1
|
je .notneeded1
|
||||||
swapgs
|
swapgs
|
||||||
.notneeded1:
|
.notneeded1:
|
||||||
; push context
|
; push context
|
||||||
@@ -90,8 +90,8 @@ isr_common:
|
|||||||
add rsp,8 ; remove error code
|
add rsp,8 ; remove error code
|
||||||
|
|
||||||
; check if swapgs is needed
|
; check if swapgs is needed
|
||||||
cmp qword [rsp+8], 0x23
|
cmp qword [rsp+8], 0x8
|
||||||
jne .notneeded2
|
je .notneeded2
|
||||||
swapgs
|
swapgs
|
||||||
.notneeded2:
|
.notneeded2:
|
||||||
o64 iret
|
o64 iret
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
#include <arch/ldt.h>
|
||||||
|
#include <arch/gdt.h>
|
||||||
|
#include <kernel/alloc.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <arch/cpu.h>
|
||||||
|
#include <arch/smp.h>
|
||||||
|
#include <spinlock.h>
|
||||||
|
|
||||||
|
static spinlock_t ldt_invalidate_lock;
|
||||||
|
static size_t done;
|
||||||
|
|
||||||
|
void arch_ldt_invalidate(void) {
|
||||||
|
arch_gdt_set_ldt(current_thread()->proc->ldt, 0xffff);
|
||||||
|
__atomic_add_fetch(&done, 1, __ATOMIC_SEQ_CST);
|
||||||
|
}
|
||||||
|
|
||||||
|
int arch_ldt_set_entry(int which, ldt_entry_t entry) {
|
||||||
|
// is an ldt already loaded?
|
||||||
|
if (__atomic_load_n(¤t_thread()->proc->ldt, __ATOMIC_RELAXED) == NULL) {
|
||||||
|
// allocate one and atomically set it
|
||||||
|
ldt_entry_t *ldt = alloc(sizeof(ldt_entry_t) * 8192);
|
||||||
|
if (ldt == NULL)
|
||||||
|
return ENOMEM;
|
||||||
|
|
||||||
|
ldt_entry_t *expected = NULL;
|
||||||
|
|
||||||
|
bool swap_ok = __atomic_compare_exchange_n(¤t_thread()->proc->ldt, &expected, ldt, false, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);
|
||||||
|
if (!swap_ok)
|
||||||
|
free(ldt);
|
||||||
|
|
||||||
|
bool do_shootdown = // do shootdown if
|
||||||
|
swap_ok // we actually need to
|
||||||
|
&& arch_smp_cpusawake >= 2 // there are multiple cpus in the system
|
||||||
|
&& current_thread()->proc->runningthreadcount > 1; // in a process which has multiple threads running
|
||||||
|
|
||||||
|
if (do_shootdown) {
|
||||||
|
long old_ipl = spinlock_acquire_raise_ipl(&ldt_invalidate_lock, IPL_DPC);
|
||||||
|
size_t shootdown_total = 1;
|
||||||
|
done = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < arch_smp_cpusawake; ++i) {
|
||||||
|
// reading the vmm context like this is racey but there are no ill side effects other than spurious shootdowns
|
||||||
|
// if a cpu changes to this vmmctx, they will already have done a ldt invalidation.
|
||||||
|
// if it changes out of it, same thing
|
||||||
|
if (smp_cpus[i] == current_cpu() || (smp_cpus[i]->vmmctx != current_thread()->vmmctx))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
++shootdown_total;
|
||||||
|
arch_smp_send_ipi(smp_cpus[i], &smp_cpus[i]->isr[0xfc], ARCH_SMP_IPI_TARGET, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
arch_ldt_invalidate();
|
||||||
|
|
||||||
|
while (__atomic_load_n(&done, __ATOMIC_SEQ_CST) != shootdown_total) asm("pause");
|
||||||
|
|
||||||
|
spinlock_release_lower_ipl(&ldt_invalidate_lock, old_ipl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__atomic_store_n(¤t_thread()->proc->ldt[which], entry, __ATOMIC_RELAXED);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int arch_ldt_fork(ldt_entry_t **dst, ldt_entry_t *src) {
|
||||||
|
*dst = alloc(sizeof(ldt_entry_t) * 8192);
|
||||||
|
if (!*dst)
|
||||||
|
return ENOMEM;
|
||||||
|
|
||||||
|
memcpy(*dst, src, sizeof(ldt_entry_t) * 8192);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -349,6 +349,16 @@ static void pfisr(isr_t *self, context_t *ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void gpfisr(isr_t *self, context_t *ctx) {
|
static void gpfisr(isr_t *self, context_t *ctx) {
|
||||||
|
if (ctx->rip >= (uintptr_t)load_seg_gs && ctx->rip <= (uintptr_t)load_seg_gs_fallback) {
|
||||||
|
ctx->rip = (uintptr_t)load_seg_gs_fallback;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->rip >= (uintptr_t)load_seg_fs && ctx->rip <= (uintptr_t)load_seg_fs_fallback) {
|
||||||
|
ctx->rip = (uintptr_t)load_seg_fs_fallback;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
thread_t *thread = current_thread();
|
thread_t *thread = current_thread();
|
||||||
if (thread && thread->usercopyctx) {
|
if (thread && thread->usercopyctx) {
|
||||||
memcpy(ctx, thread->usercopyctx, sizeof(context_t));
|
memcpy(ctx, thread->usercopyctx, sizeof(context_t));
|
||||||
|
|||||||
@@ -13,6 +13,10 @@
|
|||||||
|
|
||||||
#include <kernel/thread.h>
|
#include <kernel/thread.h>
|
||||||
|
|
||||||
|
#ifdef __x86_64__
|
||||||
|
#include <arch/ldt.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#define PROC_STATE_NORMAL 0
|
#define PROC_STATE_NORMAL 0
|
||||||
#define PROC_STATE_ZOMBIE 1
|
#define PROC_STATE_ZOMBIE 1
|
||||||
|
|
||||||
@@ -46,6 +50,10 @@ typedef struct proc_t {
|
|||||||
spinlock_t exiting;
|
spinlock_t exiting;
|
||||||
int nice;
|
int nice;
|
||||||
|
|
||||||
|
#ifdef __x86_64__
|
||||||
|
ldt_entry_t *ldt;
|
||||||
|
#endif
|
||||||
|
|
||||||
spinlock_t jobctllock;
|
spinlock_t jobctllock;
|
||||||
struct {
|
struct {
|
||||||
// NULL if this process is leader
|
// NULL if this process is leader
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ typedef struct {
|
|||||||
uint64_t gsbase;
|
uint64_t gsbase;
|
||||||
uint64_t fsbase;
|
uint64_t fsbase;
|
||||||
void *xsave;
|
void *xsave;
|
||||||
|
uint16_t fs, gs;
|
||||||
} extracontext_t;
|
} extracontext_t;
|
||||||
|
|
||||||
extern size_t arch_xsave_size;
|
extern size_t arch_xsave_size;
|
||||||
@@ -106,6 +107,6 @@ void arch_extracontext_copy(extracontext_t *dst, const extracontext_t *src);
|
|||||||
arch_extracontext_save(c);
|
arch_extracontext_save(c);
|
||||||
|
|
||||||
#define ARCH_CONTEXT_INTSTATUS(x) ((x)->rflags & 0x200 ? true : false)
|
#define ARCH_CONTEXT_INTSTATUS(x) ((x)->rflags & 0x200 ? true : false)
|
||||||
#define ARCH_CONTEXT_ISUSER(x) ((x)->cs == 0x23)
|
#define ARCH_CONTEXT_ISUSER(x) ((x)->cs != 0x8)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ typedef struct cpu_t {
|
|||||||
|
|
||||||
// architecture specific, does not need to be exposed
|
// architecture specific, does not need to be exposed
|
||||||
|
|
||||||
uint64_t gdt[7];
|
uint64_t gdt[9];
|
||||||
char vendor[13];
|
char vendor[13];
|
||||||
ist_t ist;
|
ist_t ist;
|
||||||
int acpiid;
|
int acpiid;
|
||||||
@@ -90,6 +90,11 @@ typedef struct cpu_t {
|
|||||||
#define CPU_HALT() asm volatile("hlt")
|
#define CPU_HALT() asm volatile("hlt")
|
||||||
#define CPU_PAUSE() asm volatile("pause")
|
#define CPU_PAUSE() asm volatile("pause")
|
||||||
|
|
||||||
|
bool load_seg_gs(int segment);
|
||||||
|
void load_seg_gs_fallback(void);
|
||||||
|
bool load_seg_fs(int segment);
|
||||||
|
void load_seg_fs_fallback(void);
|
||||||
|
|
||||||
static inline uint32_t cpu_to_be_d(uint32_t d) {
|
static inline uint32_t cpu_to_be_d(uint32_t d) {
|
||||||
return __builtin_bswap32(d);
|
return __builtin_bswap32(d);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
#ifndef _GDT_H
|
#ifndef _GDT_H
|
||||||
#define _GDT_H
|
#define _GDT_H
|
||||||
|
|
||||||
void arch_gdt_reload();
|
#include <stddef.h>
|
||||||
|
|
||||||
|
void arch_gdt_reload(void);
|
||||||
|
|
||||||
|
// should be called with preemption DISABLED
|
||||||
|
void arch_gdt_set_ldt(void *base, size_t limit);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#ifndef _LDT_H
|
||||||
|
#define _LDT_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef uint64_t ldt_entry_t;
|
||||||
|
void arch_ldt_invalidate(void);
|
||||||
|
int arch_ldt_set_entry(int which, ldt_entry_t entry);
|
||||||
|
int arch_ldt_fork(ldt_entry_t **dst, ldt_entry_t *src);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -66,6 +66,12 @@ static inline void arch_sigframe_init(sigframe_t *sigframe) {
|
|||||||
sigframe->extracontext.xsave = sigframe->xsave;
|
sigframe->extracontext.xsave = sigframe->xsave;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define CS_sig(context) (*((WORD *)&(context)->uc_mcontext.gregs[REG_CSGSFS] + 0))
|
||||||
|
#define GS_sig(context) (*((WORD *)&(context)->uc_mcontext.gregs[REG_CSGSFS] + 1))
|
||||||
|
#define FS_sig(context) (*((WORD *)&(context)->uc_mcontext.gregs[REG_CSGSFS] + 2))
|
||||||
|
#define SS_sig(context) (*((WORD *)&(context)->uc_mcontext.gregs[REG_CSGSFS] + 3))
|
||||||
|
|
||||||
|
|
||||||
static inline void arch_sigframe_prepare_mcontext(void *stack, sigframe_t *sigframe) {
|
static inline void arch_sigframe_prepare_mcontext(void *stack, sigframe_t *sigframe) {
|
||||||
context_t *context = &sigframe->context;
|
context_t *context = &sigframe->context;
|
||||||
extracontext_t *extracontext = &sigframe->extracontext;
|
extracontext_t *extracontext = &sigframe->extracontext;
|
||||||
@@ -89,7 +95,7 @@ static inline void arch_sigframe_prepare_mcontext(void *stack, sigframe_t *sigfr
|
|||||||
gregs[MCONTEXT_REG_RSP] = context->rsp;
|
gregs[MCONTEXT_REG_RSP] = context->rsp;
|
||||||
gregs[MCONTEXT_REG_RIP] = context->rip;
|
gregs[MCONTEXT_REG_RIP] = context->rip;
|
||||||
gregs[MCONTEXT_REG_EFL] = context->rflags;
|
gregs[MCONTEXT_REG_EFL] = context->rflags;
|
||||||
gregs[MCONTEXT_REG_CSGSFS] = 0;
|
gregs[MCONTEXT_REG_CSGSFS] = (uint64_t)context->cs | ((uint64_t)context->gs << 16) | ((uint64_t)context->fs << 32) | ((uint64_t)context->ss << 48);
|
||||||
gregs[MCONTEXT_REG_ERR] = context->error;
|
gregs[MCONTEXT_REG_ERR] = context->error;
|
||||||
gregs[MCONTEXT_REG_TRAPNO] = context->irq;
|
gregs[MCONTEXT_REG_TRAPNO] = context->irq;
|
||||||
gregs[MCONTEXT_REG_OLDMASK] = 0;
|
gregs[MCONTEXT_REG_OLDMASK] = 0;
|
||||||
@@ -124,6 +130,10 @@ static inline void arch_sigframe_get_context_from_mcontext(sigframe_t *sigframe)
|
|||||||
context->rflags = gregs[MCONTEXT_REG_EFL];
|
context->rflags = gregs[MCONTEXT_REG_EFL];
|
||||||
context->error = gregs[MCONTEXT_REG_ERR];
|
context->error = gregs[MCONTEXT_REG_ERR];
|
||||||
context->cr2 = gregs[MCONTEXT_REG_CR2];
|
context->cr2 = gregs[MCONTEXT_REG_CR2];
|
||||||
|
context->cs = gregs[MCONTEXT_REG_CSGSFS] & 0xffff;
|
||||||
|
context->gs = (gregs[MCONTEXT_REG_CSGSFS] >> 16) & 0xffff;
|
||||||
|
context->fs = (gregs[MCONTEXT_REG_CSGSFS] >> 32) & 0xffff;
|
||||||
|
context->ss = (gregs[MCONTEXT_REG_CSGSFS] >> 48) & 0xffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -131,6 +131,9 @@ proc_t *proc_create() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void proc_destroy(proc_t *proc) {
|
void proc_destroy(proc_t *proc) {
|
||||||
|
#ifdef __x86_64__
|
||||||
|
if (proc->ldt) free(proc->ldt);
|
||||||
|
#endif
|
||||||
free(proc->fd);
|
free(proc->fd);
|
||||||
slab_free(processcache, proc);
|
slab_free(processcache, proc);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <kernel/jobctl.h>
|
#include <kernel/jobctl.h>
|
||||||
#include <kernel/interrupt.h>
|
#include <kernel/interrupt.h>
|
||||||
#include <arch/cpu.h>
|
#include <arch/cpu.h>
|
||||||
|
#include <logging.h>
|
||||||
|
|
||||||
syscallret_t syscall_fork(context_t *ctx) {
|
syscallret_t syscall_fork(context_t *ctx) {
|
||||||
syscallret_t ret = {
|
syscallret_t ret = {
|
||||||
@@ -37,6 +38,14 @@ syscallret_t syscall_fork(context_t *ctx) {
|
|||||||
if (ret.errno)
|
if (ret.errno)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
|
#ifdef __x86_64__
|
||||||
|
if (proc->ldt) {
|
||||||
|
ret.errno = arch_ldt_fork(&nproc->ldt, proc->ldt);
|
||||||
|
if (ret.errno)
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
MUTEX_ACQUIRE(&proc->mutex);
|
MUTEX_ACQUIRE(&proc->mutex);
|
||||||
|
|
||||||
nproc->parent = proc;
|
nproc->parent = proc;
|
||||||
@@ -80,5 +89,6 @@ syscallret_t syscall_fork(context_t *ctx) {
|
|||||||
PROC_RELEASE(nproc);
|
PROC_RELEASE(nproc);
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
|
// XXX no cleanup, TODO
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user