summaryrefslogtreecommitdiff
path: root/i386
diff options
context:
space:
mode:
Diffstat (limited to 'i386')
-rw-r--r--i386/i386/gdt.h4
-rw-r--r--i386/i386/pcb.c7
-rw-r--r--i386/i386/thread.h2
-rw-r--r--i386/i386/user_ldt.c54
-rw-r--r--i386/include/mach/i386/mach_i386.defs16
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);