i386 TLS support
authorbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>
Wed, 14 Nov 2007 15:18:40 +0000 (15:18 +0000)
committerbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>
Wed, 14 Nov 2007 15:18:40 +0000 (15:18 +0000)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3644 c046a42c-6fe2-441c-8c8c-71466251a162

linux-user/i386/syscall.h
linux-user/main.c
linux-user/syscall.c
linux-user/x86_64/syscall.h

index 8045e1c..266e2c4 100644 (file)
@@ -25,6 +25,7 @@ struct target_pt_regs {
 #define TARGET_LDT_ENTRIES      8192
 #define TARGET_LDT_ENTRY_SIZE  8
 
+#define TARGET_GDT_ENTRIES             9
 #define TARGET_GDT_ENTRY_TLS_ENTRIES   3
 #define TARGET_GDT_ENTRY_TLS_MIN       6
 #define TARGET_GDT_ENTRY_TLS_MAX       (TARGET_GDT_ENTRY_TLS_MIN + TARGET_GDT_ENTRY_TLS_ENTRIES - 1)
index eb5861a..ac7a174 100644 (file)
@@ -159,7 +159,6 @@ static void set_gate(void *ptr, unsigned int type, unsigned int dpl,
     p[1] = tswapl(e2);
 }
 
-uint64_t gdt_table[6];
 uint64_t idt_table[256];
 
 /* only dpl matters as we do only user space emulation */
@@ -2129,14 +2128,18 @@ int main(int argc, char **argv)
     set_idt(0x80, 3);
 
     /* linux segment setup */
-    env->gdt.base = h2g(gdt_table);
-    env->gdt.limit = sizeof(gdt_table) - 1;
-    write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff,
-             DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
-             (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT));
-    write_dt(&gdt_table[__USER_DS >> 3], 0, 0xfffff,
-             DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
-             (3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT));
+    {
+        uint64_t *gdt_table;
+        gdt_table = qemu_mallocz(sizeof(uint64_t) * TARGET_GDT_ENTRIES);
+        env->gdt.base = h2g(gdt_table);
+        env->gdt.limit = sizeof(uint64_t) * TARGET_GDT_ENTRIES - 1;
+        write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff,
+                 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
+                 (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT));
+        write_dt(&gdt_table[__USER_DS >> 3], 0, 0xfffff,
+                 DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
+                 (3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT));
+    }
     cpu_x86_load_seg(env, R_CS, __USER_CS);
     cpu_x86_load_seg(env, R_DS, __USER_DS);
     cpu_x86_load_seg(env, R_ES, __USER_DS);
index 1e4ad96..01d042a 100644 (file)
@@ -2285,7 +2285,7 @@ static abi_long write_ldt(CPUX86State *env,
     struct target_modify_ldt_ldt_s ldt_info;
     struct target_modify_ldt_ldt_s *target_ldt_info;
     int seg_32bit, contents, read_exec_only, limit_in_pages;
-    int seg_not_present, useable;
+    int seg_not_present, useable, lm;
     uint32_t *lp, entry_1, entry_2;
 
     if (bytecount != sizeof(ldt_info))
@@ -2306,7 +2306,11 @@ static abi_long write_ldt(CPUX86State *env,
     limit_in_pages = (ldt_info.flags >> 4) & 1;
     seg_not_present = (ldt_info.flags >> 5) & 1;
     useable = (ldt_info.flags >> 6) & 1;
-
+#ifdef TARGET_ABI32
+    lm = 0;
+#else
+    lm = (ldt_info.flags >> 7) & 1;
+#endif
     if (contents == 3) {
         if (oldmode)
             return -TARGET_EINVAL;
@@ -2349,6 +2353,7 @@ static abi_long write_ldt(CPUX86State *env,
         ((seg_not_present ^ 1) << 15) |
         (seg_32bit << 22) |
         (limit_in_pages << 23) |
+        (lm << 21) |
         0x7000;
     if (!oldmode)
         entry_2 |= (useable << 20);
@@ -2384,6 +2389,138 @@ abi_long do_modify_ldt(CPUX86State *env, int func, abi_ulong ptr,
     return ret;
 }
 
+abi_long do_set_thread_area(CPUX86State *env, abi_ulong ptr)
+{
+    uint64_t *gdt_table = g2h(env->gdt.base);
+    struct target_modify_ldt_ldt_s ldt_info;
+    struct target_modify_ldt_ldt_s *target_ldt_info;
+    int seg_32bit, contents, read_exec_only, limit_in_pages;
+    int seg_not_present, useable, lm;
+    uint32_t *lp, entry_1, entry_2;
+    int i;
+
+    lock_user_struct(VERIFY_WRITE, target_ldt_info, ptr, 1);
+    if (!target_ldt_info)
+        return -TARGET_EFAULT;
+    ldt_info.entry_number = tswap32(target_ldt_info->entry_number);
+    ldt_info.base_addr = tswapl(target_ldt_info->base_addr);
+    ldt_info.limit = tswap32(target_ldt_info->limit);
+    ldt_info.flags = tswap32(target_ldt_info->flags);
+    if (ldt_info.entry_number == -1) {
+        for (i=TARGET_GDT_ENTRY_TLS_MIN; i<=TARGET_GDT_ENTRY_TLS_MAX; i++) {
+            if (gdt_table[i] == 0) {
+                ldt_info.entry_number = i;
+                target_ldt_info->entry_number = tswap32(i);
+                break;
+            }
+        }
+    }
+    unlock_user_struct(target_ldt_info, ptr, 1);
+
+    if (ldt_info.entry_number < TARGET_GDT_ENTRY_TLS_MIN || 
+        ldt_info.entry_number > TARGET_GDT_ENTRY_TLS_MAX)
+           return -TARGET_EINVAL;
+    seg_32bit = ldt_info.flags & 1;
+    contents = (ldt_info.flags >> 1) & 3;
+    read_exec_only = (ldt_info.flags >> 3) & 1;
+    limit_in_pages = (ldt_info.flags >> 4) & 1;
+    seg_not_present = (ldt_info.flags >> 5) & 1;
+    useable = (ldt_info.flags >> 6) & 1;
+#ifdef TARGET_ABI32
+    lm = 0;
+#else
+    lm = (ldt_info.flags >> 7) & 1;
+#endif
+
+    if (contents == 3) {
+        if (seg_not_present == 0)
+            return -TARGET_EINVAL;
+    }
+
+    /* NOTE: same code as Linux kernel */
+    /* Allow LDTs to be cleared by the user. */
+    if (ldt_info.base_addr == 0 && ldt_info.limit == 0) {
+        if ((contents == 0             &&
+             read_exec_only == 1       &&
+             seg_32bit == 0            &&
+             limit_in_pages == 0       &&
+             seg_not_present == 1      &&
+             useable == 0 )) {
+            entry_1 = 0;
+            entry_2 = 0;
+            goto install;
+        }
+    }
+
+    entry_1 = ((ldt_info.base_addr & 0x0000ffff) << 16) |
+        (ldt_info.limit & 0x0ffff);
+    entry_2 = (ldt_info.base_addr & 0xff000000) |
+        ((ldt_info.base_addr & 0x00ff0000) >> 16) |
+        (ldt_info.limit & 0xf0000) |
+        ((read_exec_only ^ 1) << 9) |
+        (contents << 10) |
+        ((seg_not_present ^ 1) << 15) |
+        (seg_32bit << 22) |
+        (limit_in_pages << 23) |
+        (useable << 20) |
+        (lm << 21) |
+        0x7000;
+
+    /* Install the new entry ...  */
+install:
+    lp = (uint32_t *)(gdt_table + ldt_info.entry_number);
+    lp[0] = tswap32(entry_1);
+    lp[1] = tswap32(entry_2);
+    return 0;
+}
+
+abi_long do_get_thread_area(CPUX86State *env, abi_ulong ptr)
+{
+    struct target_modify_ldt_ldt_s *target_ldt_info;
+    uint64_t *gdt_table = g2h(env->gdt.base);
+    uint32_t base_addr, limit, flags;
+    int seg_32bit, contents, read_exec_only, limit_in_pages, idx;
+    int seg_not_present, useable, lm;
+    uint32_t *lp, entry_1, entry_2;
+
+    lock_user_struct(VERIFY_WRITE, target_ldt_info, ptr, 1);
+    if (!target_ldt_info)
+        return -TARGET_EFAULT;
+    idx = tswap32(target_ldt_info->entry_number);
+    if (idx < TARGET_GDT_ENTRY_TLS_MIN ||
+        idx > TARGET_GDT_ENTRY_TLS_MAX) {
+        unlock_user_struct(target_ldt_info, ptr, 1);
+        return -TARGET_EINVAL;
+    }
+    lp = (uint32_t *)(gdt_table + idx);
+    entry_1 = tswap32(lp[0]);
+    entry_2 = tswap32(lp[1]);
+    
+    read_exec_only = ((entry_2 >> 9) & 1) ^ 1;
+    contents = (entry_2 >> 10) & 3;
+    seg_not_present = ((entry_2 >> 15) & 1) ^ 1;
+    seg_32bit = (entry_2 >> 22) & 1;
+    limit_in_pages = (entry_2 >> 23) & 1;
+    useable = (entry_2 >> 20) & 1;
+#ifdef TARGET_ABI32
+    lm = 0;
+#else
+    lm = (entry_2 >> 21) & 1;
+#endif
+    flags = (seg_32bit << 0) | (contents << 1) |
+        (read_exec_only << 3) | (limit_in_pages << 4) |
+        (seg_not_present << 5) | (useable << 6) | (lm << 7);
+    limit = (entry_1 & 0xffff) | (entry_2  & 0xf0000);
+    base_addr = (entry_1 >> 16) | 
+        (entry_2 & 0xff000000) | 
+        ((entry_2 & 0xff) << 16);
+    target_ldt_info->base_addr = tswapl(base_addr);
+    target_ldt_info->limit = tswap32(limit);
+    target_ldt_info->flags = tswap32(flags);
+    unlock_user_struct(target_ldt_info, ptr, 1);
+    return 0;
+}
+
 #endif /* defined(TARGET_I386) */
 
 /* this stack is the equivalent of the kernel stack associated with a
@@ -5136,18 +5273,25 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
 #endif
 #ifdef TARGET_NR_set_thread_area
     case TARGET_NR_set_thread_area:
-#ifdef TARGET_MIPS
+#if defined(TARGET_MIPS)
       ((CPUMIPSState *) cpu_env)->tls_value = arg1;
       ret = 0;
       break;
+#elif defined(TARGET_I386) && defined(TARGET_ABI32)
+      ret = do_set_thread_area(cpu_env, arg1);
+      break;
 #else
       goto unimplemented_nowarn;
 #endif
 #endif
 #ifdef TARGET_NR_get_thread_area
     case TARGET_NR_get_thread_area:
+#if defined(TARGET_I386) && defined(TARGET_ABI32)
+        ret = do_get_thread_area(cpu_env, arg1);
+#else
         goto unimplemented_nowarn;
 #endif
+#endif
 #ifdef TARGET_NR_getdomainname
     case TARGET_NR_getdomainname:
         goto unimplemented_nowarn;
index cdba432..f82589c 100644 (file)
@@ -34,6 +34,7 @@ struct target_pt_regs {
 /* The size of each LDT entry. */
 #define TARGET_LDT_ENTRY_SIZE  8
 
+#define TARGET_GDT_ENTRIES 16
 #define TARGET_GDT_ENTRY_TLS_ENTRIES 3
 #define TARGET_GDT_ENTRY_TLS_MIN 12
 #define TARGET_GDT_ENTRY_TLS_MAX 14