diff options
Diffstat (limited to 'i386')
-rw-r--r-- | i386/i386/gdt.h | 4 | ||||
-rw-r--r-- | i386/i386/pcb.c | 7 | ||||
-rw-r--r-- | i386/i386/thread.h | 2 | ||||
-rw-r--r-- | i386/i386/user_ldt.c | 54 | ||||
-rw-r--r-- | i386/include/mach/i386/mach_i386.defs | 16 |
5 files changed, 82 insertions, 1 deletions
diff --git a/i386/i386/gdt.h b/i386/i386/gdt.h index 49cb0d3..4715759 100644 --- a/i386/i386/gdt.h +++ b/i386/i386/gdt.h @@ -49,8 +49,10 @@ #define USER_FPREGS 0x40 /* user-mode access to saved floating-point registers */ -#define GDTSZ 11 +#define USER_GDT 0x48 /* user-defined GDT entries */ +#define USER_GDT_SLOTS 2 +#define GDTSZ (USER_GDT/8 + USER_GDT_SLOTS) extern struct real_descriptor gdt[GDTSZ]; diff --git a/i386/i386/pcb.c b/i386/i386/pcb.c index 9665caa..a18a5e6 100644 --- a/i386/i386/pcb.c +++ b/i386/i386/pcb.c @@ -191,6 +191,13 @@ void switch_ktss(pcb) set_ldt(USER_LDT); } } + + /* Copy in the per-thread GDT slots. No reloading is necessary + because just restoring the segment registers on the way back to + user mode reloads the shadow registers from the in-memory GDT. */ + memcpy (gdt_desc_p (mycpu, USER_GDT), + pcb->ims.user_gdt, sizeof pcb->ims.user_gdt); + /* * Load the floating-point context, if necessary. */ diff --git a/i386/i386/thread.h b/i386/i386/thread.h index 027193a..337190e 100644 --- a/i386/i386/thread.h +++ b/i386/i386/thread.h @@ -41,6 +41,7 @@ #include <i386/iopb.h> #include <i386/tss.h> +#include "gdt.h" /* * i386_saved_state: @@ -161,6 +162,7 @@ struct i386_machine_state { struct user_ldt * ldt; struct i386_fpsave_state *ifps; struct v86_assist_state v86s; + struct real_descriptor user_gdt[USER_GDT_SLOTS]; }; typedef struct pcb { diff --git a/i386/i386/user_ldt.c b/i386/i386/user_ldt.c index 33724ec..3f37599 100644 --- a/i386/i386/user_ldt.c +++ b/i386/i386/user_ldt.c @@ -401,3 +401,57 @@ user_ldt_free(user_ldt) user_ldt->desc.limit_low + 1 + sizeof(struct real_descriptor)); } + + +kern_return_t +i386_set_gdt (thread_t thread, int *selector, struct real_descriptor desc) +{ + int idx; + + if (thread == THREAD_NULL) + return KERN_INVALID_ARGUMENT; + + if (*selector == -1) + { + for (idx = 0; idx < USER_GDT_SLOTS; ++idx) + if ((thread->pcb->ims.user_gdt[idx].access & ACC_P) == 0) + { + *selector = ((idx + sel_idx(USER_GDT)) << 3) | SEL_PL_U; + break; + } + if (idx == USER_GDT_SLOTS) + return KERN_NO_SPACE; /* ? */ + } + else if ((*selector & (SEL_LDT|SEL_PL)) != SEL_PL_U + || sel_idx (*selector) < sel_idx(USER_GDT) + || sel_idx (*selector) >= sel_idx(USER_GDT) + USER_GDT_SLOTS) + return KERN_INVALID_ARGUMENT; + else + idx = sel_idx (*selector) - sel_idx(USER_GDT); + + if ((desc.access & ACC_P) == 0) + memset (&thread->pcb->ims.user_gdt[idx], 0, + sizeof thread->pcb->ims.user_gdt[idx]); + else if ((desc.access & (ACC_TYPE_USER|ACC_PL)) != (ACC_TYPE_USER|ACC_PL_U)) + return KERN_INVALID_ARGUMENT; + else + thread->pcb->ims.user_gdt[idx] = desc; + + return KERN_SUCCESS; +} + +kern_return_t +i386_get_gdt (thread_t thread, int selector, struct real_descriptor *desc) +{ + if (thread == THREAD_NULL) + return KERN_INVALID_ARGUMENT; + + if ((selector & (SEL_LDT|SEL_PL)) != SEL_PL_U + || sel_idx (selector) < sel_idx(USER_GDT) + || sel_idx (selector) >= sel_idx(USER_GDT) + USER_GDT_SLOTS) + return KERN_INVALID_ARGUMENT; + + *desc = thread->pcb->ims.user_gdt[sel_idx (selector) - sel_idx(USER_GDT)]; + + return KERN_SUCCESS; +} diff --git a/i386/include/mach/i386/mach_i386.defs b/i386/include/mach/i386/mach_i386.defs index 55121b6..1f98dc3 100644 --- a/i386/include/mach/i386/mach_i386.defs +++ b/i386/include/mach/i386/mach_i386.defs @@ -66,3 +66,19 @@ routine i386_get_ldt( first_selector : int; selector_count : int; out desc_list : descriptor_list_t); + +/* Modify one of a few available thread-specific segment descriptor slots. + The SELECTOR must be a value from a previous call (on any thread), + or -1 to allocate an available slot and return the segment selector for it. + These slots are copied into the CPU on each thread switch. + Returns KERN_NO_SPACE when there are no more slots available. */ +routine i386_set_gdt( + target_thread : thread_t; + inout selector : int; + desc : descriptor_t); + +/* Fetch a segment descriptor set with a prior i386_set_gdt call. */ +routine i386_get_gdt( + target_thread : thread_t; + selector : int; + out desc : descriptor_t); |