Thread local storage for x86
authorBarret Rhoden <brho@cs.berkeley.edu>
Thu, 7 Jan 2010 21:53:47 +0000 (13:53 -0800)
committerBarret Rhoden <brho@cs.berkeley.edu>
Thu, 7 Jan 2010 21:53:47 +0000 (13:53 -0800)
On parlib, using the TLS array.  There is no protection from
segmentation: userspace can write its own LDT (exposed via procdata),
which could also be a decent way to select different TLSs for a core
(for user threading).

This also fixes a bug where the stack_ptr_array pointer was too small
and was clobbering things next to it.

Note that procdata and procinfo are not currently contiguous, which
means if the kernel accesses them and they are more than one page long,
it is accessing random memory.

16 files changed:
kern/arch/i386/env.c
kern/arch/i386/mmu.h
kern/arch/i386/pmap.c
kern/arch/i386/smp_boot.c
kern/arch/i386/trap.h
kern/arch/i386/trapentry.S
kern/include/ros/procdata.h
kern/include/smp.h
kern/src/Makefrag
kern/src/kfs.c
kern/src/manager.c
user/apps/parlib/mhello.c [new file with mode: 0644]
user/parlib/src/hart.c
user/parlib/src/i386/Makefrag
user/parlib/src/i386/entry.S
user/parlib/src/i386/ldt.c [new file with mode: 0644]

index d241bab..83c05bc 100644 (file)
 //
 void env_pop_tf(trapframe_t *tf)
 {
+       /* Load the LDT for this process.  Slightly ghetto doing it here. */
+       segdesc_t *my_gdt = per_cpu_info[core_id()].gdt;
+       /* using local space so we can use this macro */
+       segdesc_t ldt_temp = SEG_SYS(STS_LDT, (uint32_t)current->env_procdata->ldt,
+                                    8192, 3);
+
+       my_gdt[GD_LDT >> 3] = ldt_temp;
+       asm volatile("lldt %%ax" :: "a"(GD_LDT));
+
        /* In case they are enabled elsewhere.  We can't take an interrupt in these
         * routines, due to how they play with the kernel stack pointer. */
        disable_irq();
@@ -31,6 +40,8 @@ void env_pop_tf(trapframe_t *tf)
                 */
                asm volatile ("movl %0,%%esp;           "
                              "popal;                   "
+                             "popl %%gs;               "
+                             "popl %%fs;               "
                              "popl %%es;               "
                              "popl %%ds;               "
                              "addl $0x8,%%esp;         "
@@ -55,6 +66,8 @@ void env_pop_tf(trapframe_t *tf)
                tf->tf_esp = read_esp();
                asm volatile ("movl %0,%%esp;           "
                              "popal;                   "
+                             "popl %%gs;               "
+                             "popl %%fs;               "
                              "popl %%es;               "
                              "popl %%ds;               "
                              "addl $0x10,%%esp;        "
index a008a78..869a3e8 100644 (file)
  */
 
 // Global descriptor numbers
+#define GD_NULL   0x00     // NULL descriptor
 #define GD_KT     0x08     // kernel text
 #define GD_KD     0x10     // kernel data
 #define GD_UT     0x18     // user text
 #define GD_UD     0x20     // user data
 #define GD_TSS    0x28     // Task segment selector
+#define GD_LDT    0x30     // local descriptor table
 
 #ifdef __ASSEMBLER__
 
@@ -207,6 +209,11 @@ typedef struct Segdesc {
 { ((lim) >> 12) & 0xffff, (base) & 0xffff, ((base) >> 16) & 0xff,      \
     type, 1, dpl, 1, (unsigned) (lim) >> 28, 0, 0, 1, 1,                       \
     (unsigned) (base) >> 24 }
+// System segment (LDT)
+#define SEG_SYS(type, base, lim, dpl)                                                                  \
+{ ((lim) >> 12) & 0xffff, (base) & 0xffff, ((base) >> 16) & 0xff,      \
+    type, 0, dpl, 1, (unsigned) (lim) >> 28, 0, 0, 1, 1,                       \
+    (unsigned) (base) >> 24 }
 
 #define SEG16(type, base, lim, dpl)                                                            \
 { (lim) & 0xffff, (base) & 0xffff, ((base) >> 16) & 0xff,                      \
@@ -254,7 +261,7 @@ typedef struct Segdesc {
 #define STS_IG32       0xE         // 32-bit Interrupt Gate
 #define STS_TG32       0xF         // 32-bit Trap Gate
 
-#define SEG_COUNT      6               // Number of segments in the steady state
+#define SEG_COUNT      7               // Number of segments in the steady state
 
 /*
  *
index d31d721..9cf620f 100644 (file)
@@ -53,7 +53,10 @@ segdesc_t gdt[] =
        [GD_UD >> 3] = SEG(STA_W, 0x0, 0xffffffff, 3),
 
        // 0x28 - tss, initialized in idt_init()
-       [GD_TSS >> 3] = SEG_NULL
+       [GD_TSS >> 3] = SEG_NULL,
+
+       // 0x30 - LDT, set per-process in proc_startcore()
+       [GD_LDT >> 3] = SEG_NULL
 };
 
 pseudodesc_t gdt_pd = {
@@ -414,6 +417,10 @@ vm_init(void)
 
        // Final mapping: KERNBASE+x => KERNBASE+x => x.
 
+       // need to store this for access to change the LDT later
+       assert(core_id() == 0);
+       per_cpu_info[core_id()].gdt = gdt;
+
        // This mapping was only used after paging was turned on but
        // before the segment registers were reloaded.
        pgdir[0] = 0;
index 8ea2d4d..0036595 100644 (file)
@@ -215,6 +215,7 @@ uint32_t smp_main(void)
        // GDT should be 4-byte aligned.  TS isn't aligned.  Not sure if it matters.
        pseudodesc_t *my_gdt_pd = get_my_gdt_pd(my_stack);
        segdesc_t *COUNT(SEG_COUNT) my_gdt = get_my_gdt(my_stack);
+       per_cpu_info[core_id()].gdt = my_gdt;
        // TS also needs to be permanent
        taskstate_t *my_ts = get_my_ts(my_stack);
        // Usable portion of the KSTACK grows down from here
index b12eba2..5a6227b 100644 (file)
@@ -77,21 +77,25 @@ typedef struct PushRegs {
 
 typedef struct Trapframe {
        push_regs_t tf_regs;
-       uint16_t tf_es;
+       uint16_t tf_gs;
        uint16_t tf_padding1;
-       uint16_t tf_ds;
+       uint16_t tf_fs;
        uint16_t tf_padding2;
+       uint16_t tf_es;
+       uint16_t tf_padding3;
+       uint16_t tf_ds;
+       uint16_t tf_padding4;
        uint32_t tf_trapno;
        /* below here defined by x86 hardware */
        uint32_t tf_err;
        uintptr_t tf_eip;
        uint16_t tf_cs;
-       uint16_t tf_padding3;
+       uint16_t tf_padding5;
        uint32_t tf_eflags;
        /* below here only when crossing rings, such as from user to kernel */
        uintptr_t tf_esp;
        uint16_t tf_ss;
-       uint16_t tf_padding4;
+       uint16_t tf_padding6;
 } trapframe_t;
 
 typedef struct AncillaryState {
index f934025..81448c1 100644 (file)
@@ -186,6 +186,8 @@ _alltraps:
        cld
        pushl %ds
        pushl %es
+       pushl %fs
+       pushl %gs
        pushal
        movw $GD_KD, %ax                # data segments aren't accessible by default
        movw %ax, %ds
@@ -195,6 +197,8 @@ _alltraps:
        call trap
        popl %esp
        popal
+       popl %gs
+       popl %fs
        popl %es
        popl %ds
        addl $0x8, %esp                 # skip trapno and err
@@ -207,6 +211,8 @@ _allirqs:
        cld
        pushl %ds
        pushl %es
+       pushl %fs
+       pushl %gs
        pushal
        movw $GD_KD, %ax                # data segments aren't accessible by default
        movw %ax, %ds
@@ -216,6 +222,8 @@ _allirqs:
        call irq_handler
        popl %esp
        popal
+       popl %gs
+       popl %fs
        popl %es
        popl %ds
        addl $0x8, %esp                 # skip IRQ number and err (which is 0)
@@ -233,6 +241,8 @@ sysenter_handler:
        pushl $T_SYSCALL                # helps with print_trapframe
        pushl %ds
        pushl %es
+       pushl %fs
+       pushl %gs
        pushal
        movw $GD_KD, %ax
        movw %ax, %ds
@@ -242,6 +252,8 @@ sysenter_handler:
        call sysenter_callwrapper
        popl %esp
        popal
+       popl %gs
+       popl %fs
        popl %es
        popl %ds
        addl $0x10, %esp                # pop T_SYSCALL and the three zeros
index 73c3eea..c430003 100644 (file)
@@ -24,6 +24,9 @@ typedef struct procinfo {
 #define PROCINFO_NUM_PAGES  ((sizeof(procinfo_t)-1)/PGSIZE + 1)        
 
 typedef struct procdata {
+#ifdef __i386__
+       segdesc_t ldt[8192];
+#endif
        // The actual ring buffers for communicating with user space
        syscall_sring_t  syscallring;  // Per-process ring buffer for async syscalls
        sysevent_sring_t syseventring; // Per-process ring buffer for async sysevents
index 7bceab9..1427f45 100644 (file)
@@ -34,6 +34,9 @@ struct per_cpu_info {
        // zra: Used by Ivy. Let me know if this should go elsewhere.
        sharC_env_t sharC_env;
 #endif
+#ifdef __i386__
+       segdesc_t *gdt;
+#endif
 
        spinlock_t amsg_lock;
        struct active_msg_list NTPTV(a0t) NTPTV(a1t) NTPTV(a2t) active_msgs;
index 5e291b8..6b76900 100644 (file)
@@ -60,6 +60,7 @@ KERN_APPFILES += \
                  $(USER_APPS_PARLIB_DIR)/channel_test_client \
                  $(USER_APPS_PARLIB_DIR)/channel_test_server \
                  $(USER_APPS_PARLIB_DIR)/hello \
+                 $(USER_APPS_PARLIB_DIR)/mhello \
                  $(USER_APPS_PARLIB_DIR)/manycore_test
 endif
 
index 0a92add..1d286e2 100644 (file)
@@ -42,6 +42,7 @@ DECL_PROG(parlib_draw_nanwan_standalone);
 DECL_PROG(parlib_channel_test_client);
 DECL_PROG(parlib_channel_test_server);
 DECL_PROG(parlib_hello);
+DECL_PROG(parlib_mhello);
 DECL_PROG(parlib_manycore_test);
 #endif
 
@@ -60,6 +61,7 @@ struct kfs_entry kfs[MAX_KFS_FILES] = {
        KFS_ENTRY(parlib_channel_test_client)
        KFS_ENTRY(parlib_channel_test_server)
        KFS_ENTRY(parlib_hello)
+       KFS_ENTRY(parlib_mhello)
        KFS_ENTRY(parlib_manycore_test)
 #endif
 };
index 51ce4e1..7e9545a 100644 (file)
@@ -60,8 +60,9 @@ void manager_brho(void)
        switch (progress++) {
                case 0:
                        // TODO: need to store the pid for future manager runs, not the *p
+                       p = kfs_proc_create(kfs_lookup_path("parlib_mhello"));
                        //p = kfs_proc_create(kfs_lookup_path("roslib_mhello"));
-                       p = kfs_proc_create(kfs_lookup_path("roslib_mproctests"));
+                       //p = kfs_proc_create(kfs_lookup_path("roslib_mproctests"));
                        //p = kfs_proc_create(kfs_lookup_path("roslib_spawn"));
                        // being proper and all:
                        spin_lock_irqsave(&p->proc_lock);
diff --git a/user/apps/parlib/mhello.c b/user/apps/parlib/mhello.c
new file mode 100644 (file)
index 0000000..51d5ef2
--- /dev/null
@@ -0,0 +1,54 @@
+#include <parlib.h>
+#include <ros/mman.h>
+#include <ros/resource.h>
+#include <stdio.h>
+#include <hart.h>
+
+// ghetto udelay, put in a lib somewhere and export the tsc freq
+#include <arch/arch.h>
+void udelay(uint64_t usec, uint64_t tsc_freq)
+{
+       uint64_t start, end, now;
+
+       start = read_tsc();
+    end = start + (tsc_freq * usec) / 1000000;
+       if (end == 0) printf("This is terribly wrong \n");
+       do {
+        cpu_relax();
+        now = read_tsc();
+       } while (now < end || (now > start && end < start));
+       return;
+}
+
+__thread int temp;
+
+int main(int argc, char** argv)
+{
+
+       uint32_t vcoreid;
+       error_t retval;
+
+       if ((vcoreid = hart_self())) {
+               printf("Should never see me! (from vcore %d)\n", vcoreid);
+       } else { // core 0
+               temp = 0xdeadbeef;
+               printf("Hello from vcore %d with temp addr = %p and temp = %p\n",
+                      vcoreid, &temp, temp);
+               printf("Multi-Goodbye, world, from PID: %d!\n", sys_getpid());
+               //retval = sys_resource_req(RES_CORES, 2, 0);
+               retval = hart_request(2);
+               //debug("retval = %d\n", retval);
+       }
+       printf("Vcore %d Done!\n", vcoreid);
+       while (1);
+       return 0;
+}
+
+void hart_entry(void)
+{
+       uint32_t vcoreid = hart_self();
+       temp = 0xcafebabe;
+       printf("Hello from hart_entry in vcore %d with temp addr %p and temp %p\n",
+              vcoreid, &temp, temp);
+       while(1);
+}
index d4bcdad..61819b7 100644 (file)
@@ -30,7 +30,7 @@ static void _hart_init()
 error_t hart_request(size_t k)
 {
        size_t i,j;
-       const int user_stack_size = 1024*1024, tls_size = 1024*1024;
+       const int user_stack_size = 1024*1024, tls_size = PARLIB_TLS_SIZE;
 
        extern void** stack_ptr_array;
        extern void** tls_array;
index 69896f1..8bfe6c0 100644 (file)
@@ -10,5 +10,5 @@ OBJDIRS += $(USER_PARLIB_ARCH_SRC_DIR)
 # We also snatch the use of a couple handy source files
 # from the lib directory, to avoid gratuitous code duplication.
 USER_PARLIB_ARCH_SRCFILES := $(USER_PARLIB_ARCH_SRC_DIR)/syscall.c \
-                             $(USER_PARLIB_ARCH_SRC_DIR)/newlib_backend.c
-
+                             $(USER_PARLIB_ARCH_SRC_DIR)/newlib_backend.c \
+                             $(USER_PARLIB_ARCH_SRC_DIR)/ldt.c
index 30740aa..d00311a 100644 (file)
 // Stack pointers, to be allocated by the hart lib
        .globl stack_ptr_array
        stack_ptr_array:
-               .word 0
+               .long 0
        .globl tls_array
        tls_array:
-               .word 0
+               .long 0
 
 // TODO: We're not exposing these yet.  Think about how to do so judiciously.
 //     .globl vpt
@@ -34,6 +34,13 @@ _start:
        test    %eax,%eax
        jne     notcore0
 
+       // Push the core id onto the stack
+       push    %eax
+       // Setup the LDT
+       call ldt_init
+       // Restore the core id
+       pop     %eax
+
        // argc/argv are provided by procinfo and are accessed in parlibmain
        call parlib_main
 
@@ -44,6 +51,13 @@ notcore0:
        mov     stack_ptr_array,%edx
        mov     (%edx,%eax,4),%esp
 
+       // Push the core id onto the stack
+       push    %eax
+       // Setup the LDT
+       call ldt_init
+       // Restore the core id
+       pop     %eax
+
        call    parlib_unmain
 
        // illegal instruction in case hart_yield accidentally returns
diff --git a/user/parlib/src/i386/ldt.c b/user/parlib/src/i386/ldt.c
new file mode 100644 (file)
index 0000000..790155e
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2010 The Regents of the University of California
+ * See LICENSE for details.
+ */
+
+/** @file
+ * @brief x86 LDT init.
+ *
+ * This function is responsible for correctly setting up the the LDT entry for a
+ * given core. This is in support of thread local storage on x86.  x86 expects
+ * the memory at %gs:0x0 to hold the address of the top of the TLS.
+ *
+ * @author Paul Pearce <pearce@eecs.berkeley.edu>
+ *
+ */
+
+
+#include <ros/common.h>
+#include <parlib.h>
+#include <arch/mmu.h>
+
+void* core0_tls_top_ptr;
+
+void ldt_init(uint32_t core_id) {
+
+       extern void **tls_array;
+       extern char core0_tls[];
+
+       void **tls_handle;
+       
+       core0_tls_top_ptr = core0_tls + PARLIB_TLS_SIZE;
+
+       // Get a handle to this core's tls
+       if (core_id == 0) {
+               tls_handle = &core0_tls_top_ptr;        
+       } else {
+               tls_handle = &tls_array[core_id];
+       }
+
+       // Build the segment
+       segdesc_t tmp = SEG(STA_W, (uint32_t)tls_handle, (uint32_t)tls_handle + 4, 3);
+
+       // Setup the correct LDT entry for this hart
+       procdata.ldt[core_id] = tmp;
+
+       // Create the GS register.
+       uint32_t gs = (core_id << 3) | 0x07;
+
+       // Set the GS register.
+       asm volatile("movl %0,%%gs" : : "r" (gs));
+
+       // Profit!
+}