PERFORCE change 113545 for review
Jung-uk Kim
jkim at FreeBSD.org
Fri Jan 26 04:49:57 UTC 2007
http://perforce.freebsd.org/chv.cgi?CH=113545
Change 113545 by jkim at jkim_hammer on 2007/01/26 04:49:44
Almost working TLS implementation for AMD64.
Simple applications run fine but complex ones may fail.
Affected files ...
.. //depot/projects/linuxolator/src/sys/amd64/amd64/cpu_switch.S#3 edit
.. //depot/projects/linuxolator/src/sys/amd64/amd64/genassym.c#5 edit
.. //depot/projects/linuxolator/src/sys/amd64/amd64/machdep.c#12 edit
.. //depot/projects/linuxolator/src/sys/amd64/ia32/ia32_signal.c#3 edit
.. //depot/projects/linuxolator/src/sys/amd64/include/pcb.h#2 edit
.. //depot/projects/linuxolator/src/sys/amd64/include/segments.h#2 edit
.. //depot/projects/linuxolator/src/sys/amd64/linux32/linux.h#16 edit
.. //depot/projects/linuxolator/src/sys/amd64/linux32/linux32_machdep.c#28 edit
.. //depot/projects/linuxolator/src/sys/amd64/linux32/linux32_sysvec.c#7 edit
Differences ...
==== //depot/projects/linuxolator/src/sys/amd64/amd64/cpu_switch.S#3 (text+ko) ====
@@ -109,6 +109,7 @@
movl %es,PCB_ES(%r8)
movl %fs,PCB_FS(%r8)
movl %gs,PCB_GS(%r8)
+
1:
/* Test if debug registers should be saved. */
testl $PCB_DBREGS,PCB_FLAGS(%r8)
@@ -183,22 +184,31 @@
rdmsr
movl PCB_GS(%r8),%gs
wrmsr
+
+ /* Restore userland 32 bit GS descriptor for Linuxulator */
+ movq PCB_GS32P(%r8),%rax
+ testq %rax,%rax
+ jz 3f /* no, skip over */
+
+ movq PCB_GS32SD(%r8),%rcx
+ movq %rcx,(%rax)
jmp 2f
+
1:
-
/* Restore userland %fs */
movl $MSR_FSBASE,%ecx
movl PCB_FSBASE(%r8),%eax
movl PCB_FSBASE+4(%r8),%edx
wrmsr
+2:
/* Restore userland %gs */
movl $MSR_KGSBASE,%ecx
movl PCB_GSBASE(%r8),%eax
movl PCB_GSBASE+4(%r8),%edx
wrmsr
-2:
+3:
/* Update the TSS_RSP0 pointer for the next interrupt */
movq PCPU(TSSP), %rax
addq $COMMON_TSS_RSP0, %rax
==== //depot/projects/linuxolator/src/sys/amd64/amd64/genassym.c#5 (text+ko) ====
@@ -147,6 +147,9 @@
ASSYM(COMMON_TSS_RSP0, offsetof(struct amd64tss, tss_rsp0));
+ASSYM(PCB_GS32P, offsetof(struct pcb, pcb_gs32p));
+ASSYM(PCB_GS32SD, offsetof(struct pcb, pcb_gs32sd));
+
ASSYM(TF_R15, offsetof(struct trapframe, tf_r15));
ASSYM(TF_R14, offsetof(struct trapframe, tf_r14));
ASSYM(TF_R13, offsetof(struct trapframe, tf_r13));
==== //depot/projects/linuxolator/src/sys/amd64/amd64/machdep.c#12 (text+ko) ====
@@ -156,7 +156,7 @@
extern vm_offset_t ksym_start, ksym_end;
#endif
-int _udatasel, _ucodesel, _ucode32sel;
+int _udatasel, _ucodesel, _ucode32sel, _ugs32sel;
int cold = 1;
@@ -725,6 +725,15 @@
0, /* long */
0, /* default 32 vs 16 bit size */
0 /* limit granularity (byte/page units)*/ },
+/* GUGS32_SEL 8 32 bit GS Descriptor for user */
+{ 0x0, /* segment base address */
+ 0xfffff, /* length - all address space */
+ SDT_MEMRWA, /* segment type */
+ SEL_UPL, /* segment descriptor priority level */
+ 1, /* segment descriptor present */
+ 0, /* long */
+ 1, /* default 32 vs 16 bit size */
+ 1 /* limit granularity (byte/page units)*/ },
};
void
@@ -1279,6 +1288,7 @@
_ucodesel = GSEL(GUCODE_SEL, SEL_UPL);
_udatasel = GSEL(GUDATA_SEL, SEL_UPL);
_ucode32sel = GSEL(GUCODE32_SEL, SEL_UPL);
+ _ugs32sel = GSEL(GUGS32_SEL, SEL_UPL);
/* setup proc 0's pcb */
thread0.td_pcb->pcb_flags = 0; /* XXXKSE */
==== //depot/projects/linuxolator/src/sys/amd64/ia32/ia32_signal.c#3 (text+ko) ====
@@ -727,6 +727,7 @@
pcb->pcb_es = _udatasel;
pcb->pcb_fs = _udatasel;
pcb->pcb_gs = _udatasel;
+ pcb->pcb_gs32p = NULL;
bzero((char *)regs, sizeof(struct trapframe));
regs->tf_rip = entry;
==== //depot/projects/linuxolator/src/sys/amd64/include/pcb.h#2 (text+ko) ====
@@ -73,6 +73,9 @@
#define PCB_FULLCTX 0x80 /* full context restore on sysret */
caddr_t pcb_onfault; /* copyin/out fault recovery */
+
+ caddr_t pcb_gs32p; /* XXX pointer to gdt[GUGS32_SEL] */
+ u_int64_t pcb_gs32sd; /* 32 bit GS segment descriptor */
};
#ifdef _KERNEL
==== //depot/projects/linuxolator/src/sys/amd64/include/segments.h#2 (text+ko) ====
@@ -200,9 +200,10 @@
#define GUCODE32_SEL 3 /* User 32 bit code Descriptor */
#define GUDATA_SEL 4 /* User 32/64 bit Data Descriptor */
#define GUCODE_SEL 5 /* User 64 bit Code Descriptor */
-#define GPROC0_SEL 6 /* TSS for entering kernel etc */
+#define GPROC0_SEL 6 /* TSS for entering kernel etc */
/* slot 6 is second half of GPROC0_SEL */
-#define NGDT 8
+#define GUGS32_SEL 8 /* User 32 bit GS Descriptor */
+#define NGDT 9
#ifdef _KERNEL
extern struct user_segment_descriptor gdt[];
==== //depot/projects/linuxolator/src/sys/amd64/linux32/linux.h#16 (text+ko) ====
@@ -774,7 +774,6 @@
l_uint limit_in_pages:1;
l_uint seg_not_present:1;
l_uint useable:1;
- l_uint lm:1;
};
struct l_desc_struct {
@@ -798,7 +797,6 @@
#define ENTRY_B_USEABLE 20
#define ENTRY_B_SEG32BIT 22
#define ENTRY_B_LIMIT 23
-#define ENTRY_B_LONGMODE 21
#define LDT_entry_b(info) \
(((info)->base_addr & 0xff000000) | \
@@ -819,8 +817,7 @@
(info)->read_exec_only == 1 && \
(info)->seg_32bit == 0 && \
(info)->limit_in_pages == 0 && \
- (info)->useable == 0 && \
- (info)->lm == 0)
+ (info)->useable == 0 )
/* macros for converting segments, they do the same as those in arch/i386/kernel/process.c */
#define GET_BASE(desc) ( \
@@ -838,7 +835,6 @@
#define GET_LIMIT_PAGES(desc) (((desc)->b >> ENTRY_B_LIMIT) & 1)
#define GET_PRESENT(desc) (((desc)->b >> ENTRY_B_SEG_NOT_PRESENT) & 1)
#define GET_USEABLE(desc) (((desc)->b >> ENTRY_B_USEABLE) & 1)
-#define GET_LONGMODE(desc) (((desc)->b >> ENTRY_B_LONGMODE) & 1)
#define LINUX_CLOCK_REALTIME 0
#define LINUX_CLOCK_MONOTONIC 1
==== //depot/projects/linuxolator/src/sys/amd64/linux32/linux32_machdep.c#28 (text+ko) ====
@@ -52,6 +52,7 @@
#include <sys/unistd.h>
#include <machine/frame.h>
+#include <machine/segments.h>
#include <vm/vm.h>
#include <vm/pmap.h>
@@ -671,7 +672,9 @@
td2->td_frame->tf_rsp = PTROUT(args->stack);
if (args->flags & CLONE_SETTLS) {
+ struct user_segment_descriptor sd;
struct l_user_desc info;
+ int a[2];
int idx;
error = copyin((void *)td->td_frame->tf_rsi, &info, sizeof(struct l_user_desc));
@@ -684,26 +687,43 @@
* looks like we're getting the idx we returned
* in the set_thread_area() syscall
*/
- if (idx != 6 && idx != 4)
+ if (idx != 6 && idx != GUGS32_SEL)
return (EINVAL);
/* this doesnt happen in practice */
if (idx == 6) {
- /* we might copy out the entry_number as 4 */
- info.entry_number = 4;
+ /* we might copy out the entry_number as GUGS32_SEL */
+ info.entry_number = GUGS32_SEL;
error = copyout(&info, (void *) td->td_frame->tf_rsi, sizeof(struct l_user_desc));
if (error)
return (error);
}
- /* this is taken from amd64 version of cpu_set_user_tls() */
- critical_enter();
- /* set %gs */
+ a[0] = LDT_entry_a(&info);
+ a[1] = LDT_entry_b(&info);
+
+ memcpy(&sd, &a, sizeof(a));
+#ifdef DEBUG
+ if (ldebug(clone))
+ printf("Segment created in clone with CLONE_SETTLS: "
+ "lobase: %x, hibase: %x, lolimit: %x, hilimit: %x, "
+ "type: %i, dpl: %i, p: %i, xx: %i, long: %i, "
+ "def32: %i, gran: %i\n",
+ sd.sd_lobase,
+ sd.sd_hibase,
+ sd.sd_lolimit,
+ sd.sd_hilimit,
+ sd.sd_type,
+ sd.sd_dpl,
+ sd.sd_p,
+ sd.sd_xx,
+ sd.sd_long,
+ sd.sd_def32,
+ sd.sd_gran);
+#endif
td2->td_pcb->pcb_gsbase = (register_t)info.base_addr;
-#if 0
- wrmsr(MSR_KGSBASE, td->td_pcb->pcb_fsbase);
-#endif
- critical_exit();
+ td2->td_pcb->pcb_gs32p = (caddr_t)&gdt[GUGS32_SEL];
+ memcpy(&td2->td_pcb->pcb_gs32sd, &sd, sizeof(sd));
}
#ifdef DEBUG
@@ -1239,6 +1259,8 @@
linux_set_thread_area(struct thread *td, struct linux_set_thread_area_args *args)
{
struct l_user_desc info;
+ struct user_segment_descriptor sd;
+ int a[2];
int error;
int idx;
@@ -1248,7 +1270,7 @@
#ifdef DEBUG
if (ldebug(set_thread_area))
- printf(ARGS(set_thread_area, "%i, %x, %x, %i, %i, %i, %i, %i, %i, %i\n"),
+ printf(ARGS(set_thread_area, "%i, %x, %x, %i, %i, %i, %i, %i, %i"),
info.entry_number,
info.base_addr,
info.limit,
@@ -1257,8 +1279,7 @@
info.read_exec_only,
info.limit_in_pages,
info.seg_not_present,
- info.useable,
- info.lm);
+ info.useable);
#endif
idx = info.entry_number;
@@ -1282,25 +1303,54 @@
* we should let 4 proceed as well because we use this segment so
* if code does two subsequent calls it should succeed
*/
- if (idx != 6 && idx != -1 && idx != 4)
+ if (idx != 6 && idx != -1 && idx != GUGS32_SEL)
return (EINVAL);
/*
* we have to copy out the GDT entry we use
- * FreeBSD uses GDT entry #3 for storing %gs so load that
+ * FreeBSD uses GUGS32_SEL for storing %gs so load that
* XXX: what if userspace program doesnt check this value and tries
* to use 6, 7 or 8?
*/
- idx = info.entry_number = 4;
+ idx = info.entry_number = GUGS32_SEL;
error = copyout(&info, args->desc, sizeof(struct l_user_desc));
if (error)
return (error);
+ if (LDT_empty(&info)) {
+ a[0] = 0;
+ a[1] = 0;
+ } else {
+ a[0] = LDT_entry_a(&info);
+ a[1] = LDT_entry_b(&info);
+ }
+
+ memcpy(&sd, &a, sizeof(a));
+#ifdef DEBUG
+ if (ldebug(set_thread_area))
+ printf("Segment created in set_thread_area: "
+ "lobase: %x, hibase: %x, lolimit: %x, hilimit: %x, "
+ "type: %i, dpl: %i, p: %i, xx: %i, long: %i, "
+ "def32: %i, gran: %i\n",
+ sd.sd_lobase,
+ sd.sd_hibase,
+ sd.sd_lolimit,
+ sd.sd_hilimit,
+ sd.sd_type,
+ sd.sd_dpl,
+ sd.sd_p,
+ sd.sd_xx,
+ sd.sd_long,
+ sd.sd_def32,
+ sd.sd_gran);
+#endif
+
critical_enter();
- /* set %gs */
td->td_pcb->pcb_gsbase = (register_t)info.base_addr;
+ td->td_pcb->pcb_gs32p = (caddr_t)&gdt[GUGS32_SEL];
+ memcpy(&td->td_pcb->pcb_gs32sd, &sd, sizeof(sd));
wrmsr(MSR_KGSBASE, td->td_pcb->pcb_gsbase);
-
+ gdt[GUGS32_SEL] = sd;
critical_exit();
return (0);
==== //depot/projects/linuxolator/src/sys/amd64/linux32/linux32_sysvec.c#7 (text+ko) ====
@@ -280,7 +280,7 @@
return 0;
}
-extern int _ucodesel, _ucode32sel, _udatasel;
+extern int _ucodesel, _ucode32sel, _udatasel, _ugs32sel;
extern unsigned long linux_sznonrtsigcode;
static void
@@ -820,11 +820,12 @@
load_ds(_udatasel);
load_es(_udatasel);
load_fs(_udatasel);
- load_gs(0);
+ load_gs(_udatasel);
pcb->pcb_ds = _udatasel;
pcb->pcb_es = _udatasel;
pcb->pcb_fs = _udatasel;
- pcb->pcb_gs = 0;
+ pcb->pcb_gs = _udatasel;
+ pcb->pcb_gs32p = NULL;
bzero((char *)regs, sizeof(struct trapframe));
regs->tf_rip = entry;
More information about the p4-projects
mailing list