diff options
| -rw-r--r-- | arch/um/include/os.h | 1 | ||||
| -rw-r--r-- | arch/um/include/sysdep-i386/tls.h | 4 | ||||
| -rw-r--r-- | arch/um/include/user_util.h | 3 | ||||
| -rw-r--r-- | arch/um/os-Linux/sys-i386/Makefile | 2 | ||||
| -rw-r--r-- | arch/um/os-Linux/sys-i386/tls.c | 33 | ||||
| -rw-r--r-- | arch/um/os-Linux/tls.c | 4 | ||||
| -rw-r--r-- | arch/um/sys-i386/tls.c | 55 | ||||
| -rw-r--r-- | include/asm-um/segment.h | 6 |
8 files changed, 102 insertions, 6 deletions
diff --git a/arch/um/include/os.h b/arch/um/include/os.h index 1b780b5dacbe..f88856c28a66 100644 --- a/arch/um/include/os.h +++ b/arch/um/include/os.h | |||
| @@ -173,6 +173,7 @@ extern int os_fchange_dir(int fd); | |||
| 173 | extern void os_early_checks(void); | 173 | extern void os_early_checks(void); |
| 174 | extern int can_do_skas(void); | 174 | extern int can_do_skas(void); |
| 175 | extern void os_check_bugs(void); | 175 | extern void os_check_bugs(void); |
| 176 | extern void check_host_supports_tls(int *supports_tls, int *tls_min); | ||
| 176 | 177 | ||
| 177 | /* Make sure they are clear when running in TT mode. Required by | 178 | /* Make sure they are clear when running in TT mode. Required by |
| 178 | * SEGV_MAYBE_FIXABLE */ | 179 | * SEGV_MAYBE_FIXABLE */ |
diff --git a/arch/um/include/sysdep-i386/tls.h b/arch/um/include/sysdep-i386/tls.h index 938f953b26cc..918fd3c5ff9c 100644 --- a/arch/um/include/sysdep-i386/tls.h +++ b/arch/um/include/sysdep-i386/tls.h | |||
| @@ -25,4 +25,8 @@ typedef struct um_dup_user_desc { | |||
| 25 | typedef struct user_desc user_desc_t; | 25 | typedef struct user_desc user_desc_t; |
| 26 | 26 | ||
| 27 | # endif /* __KERNEL__ */ | 27 | # endif /* __KERNEL__ */ |
| 28 | |||
| 29 | #define GDT_ENTRY_TLS_MIN_I386 6 | ||
| 30 | #define GDT_ENTRY_TLS_MIN_X86_64 12 | ||
| 31 | |||
| 28 | #endif /* _SYSDEP_TLS_H */ | 32 | #endif /* _SYSDEP_TLS_H */ |
diff --git a/arch/um/include/user_util.h b/arch/um/include/user_util.h index 615f2f0a32d0..fe0c29b5144d 100644 --- a/arch/um/include/user_util.h +++ b/arch/um/include/user_util.h | |||
| @@ -8,6 +8,9 @@ | |||
| 8 | 8 | ||
| 9 | #include "sysdep/ptrace.h" | 9 | #include "sysdep/ptrace.h" |
| 10 | 10 | ||
| 11 | /* Copied from kernel.h */ | ||
| 12 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) | ||
| 13 | |||
| 11 | #define CATCH_EINTR(expr) while ((errno = 0, ((expr) < 0)) && (errno == EINTR)) | 14 | #define CATCH_EINTR(expr) while ((errno = 0, ((expr) < 0)) && (errno == EINTR)) |
| 12 | 15 | ||
| 13 | extern int mode_tt; | 16 | extern int mode_tt; |
diff --git a/arch/um/os-Linux/sys-i386/Makefile b/arch/um/os-Linux/sys-i386/Makefile index 340ef26f5944..b3213613c41c 100644 --- a/arch/um/os-Linux/sys-i386/Makefile +++ b/arch/um/os-Linux/sys-i386/Makefile | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | # Licensed under the GPL | 3 | # Licensed under the GPL |
| 4 | # | 4 | # |
| 5 | 5 | ||
| 6 | obj-$(CONFIG_MODE_SKAS) = registers.o | 6 | obj-$(CONFIG_MODE_SKAS) = registers.o tls.o |
| 7 | 7 | ||
| 8 | USER_OBJS := $(obj-y) | 8 | USER_OBJS := $(obj-y) |
| 9 | 9 | ||
diff --git a/arch/um/os-Linux/sys-i386/tls.c b/arch/um/os-Linux/sys-i386/tls.c new file mode 100644 index 000000000000..ba21f0e04a2f --- /dev/null +++ b/arch/um/os-Linux/sys-i386/tls.c | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | #include <linux/unistd.h> | ||
| 2 | #include "sysdep/tls.h" | ||
| 3 | #include "user_util.h" | ||
| 4 | |||
| 5 | static _syscall1(int, get_thread_area, user_desc_t *, u_info); | ||
| 6 | |||
| 7 | /* Checks whether host supports TLS, and sets *tls_min according to the value | ||
| 8 | * valid on the host. | ||
| 9 | * i386 host have it == 6; x86_64 host have it == 12, for i386 emulation. */ | ||
| 10 | void check_host_supports_tls(int *supports_tls, int *tls_min) { | ||
| 11 | /* Values for x86 and x86_64.*/ | ||
| 12 | int val[] = {GDT_ENTRY_TLS_MIN_I386, GDT_ENTRY_TLS_MIN_X86_64}; | ||
| 13 | int i; | ||
| 14 | |||
| 15 | for (i = 0; i < ARRAY_SIZE(val); i++) { | ||
| 16 | user_desc_t info; | ||
| 17 | info.entry_number = val[i]; | ||
| 18 | |||
| 19 | if (get_thread_area(&info) == 0) { | ||
| 20 | *tls_min = val[i]; | ||
| 21 | *supports_tls = 1; | ||
| 22 | return; | ||
| 23 | } else { | ||
| 24 | if (errno == EINVAL) | ||
| 25 | continue; | ||
| 26 | else if (errno == ENOSYS) | ||
| 27 | *supports_tls = 0; | ||
| 28 | return; | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | *supports_tls = 0; | ||
| 33 | } | ||
diff --git a/arch/um/os-Linux/tls.c b/arch/um/os-Linux/tls.c index 642db553ab60..9cb09a45546b 100644 --- a/arch/um/os-Linux/tls.c +++ b/arch/um/os-Linux/tls.c | |||
| @@ -48,8 +48,8 @@ int os_get_thread_area(user_desc_t *info, int pid) | |||
| 48 | #ifdef UML_CONFIG_MODE_TT | 48 | #ifdef UML_CONFIG_MODE_TT |
| 49 | #include "linux/unistd.h" | 49 | #include "linux/unistd.h" |
| 50 | 50 | ||
| 51 | _syscall1(int, get_thread_area, user_desc_t *, u_info); | 51 | static _syscall1(int, get_thread_area, user_desc_t *, u_info); |
| 52 | _syscall1(int, set_thread_area, user_desc_t *, u_info); | 52 | static _syscall1(int, set_thread_area, user_desc_t *, u_info); |
| 53 | 53 | ||
| 54 | int do_set_thread_area_tt(user_desc_t *info) | 54 | int do_set_thread_area_tt(user_desc_t *info) |
| 55 | { | 55 | { |
diff --git a/arch/um/sys-i386/tls.c b/arch/um/sys-i386/tls.c index 2251654c6b45..a3188e861cc7 100644 --- a/arch/um/sys-i386/tls.c +++ b/arch/um/sys-i386/tls.c | |||
| @@ -24,6 +24,10 @@ | |||
| 24 | #include "skas.h" | 24 | #include "skas.h" |
| 25 | #endif | 25 | #endif |
| 26 | 26 | ||
| 27 | /* If needed we can detect when it's uninitialized. */ | ||
| 28 | static int host_supports_tls = -1; | ||
| 29 | int host_gdt_entry_tls_min = -1; | ||
| 30 | |||
| 27 | #ifdef CONFIG_MODE_SKAS | 31 | #ifdef CONFIG_MODE_SKAS |
| 28 | int do_set_thread_area_skas(struct user_desc *info) | 32 | int do_set_thread_area_skas(struct user_desc *info) |
| 29 | { | 33 | { |
| @@ -157,11 +161,20 @@ void clear_flushed_tls(struct task_struct *task) | |||
| 157 | } | 161 | } |
| 158 | } | 162 | } |
| 159 | 163 | ||
| 160 | /* This in SKAS0 does not need to be used, since we have different host | 164 | /* In SKAS0 mode, currently, multiple guest threads sharing the same ->mm have a |
| 161 | * processes. Nor will this need to be used when we'll add support to the host | 165 | * common host process. So this is needed in SKAS0 too. |
| 166 | * | ||
| 167 | * However, if each thread had a different host process (and this was discussed | ||
| 168 | * for SMP support) this won't be needed. | ||
| 169 | * | ||
| 170 | * And this will not need be used when (and if) we'll add support to the host | ||
| 162 | * SKAS patch. */ | 171 | * SKAS patch. */ |
| 172 | |||
| 163 | int arch_switch_tls_skas(struct task_struct *from, struct task_struct *to) | 173 | int arch_switch_tls_skas(struct task_struct *from, struct task_struct *to) |
| 164 | { | 174 | { |
| 175 | if (!host_supports_tls) | ||
| 176 | return 0; | ||
| 177 | |||
| 165 | /* We have no need whatsoever to switch TLS for kernel threads; beyond | 178 | /* We have no need whatsoever to switch TLS for kernel threads; beyond |
| 166 | * that, that would also result in us calling os_set_thread_area with | 179 | * that, that would also result in us calling os_set_thread_area with |
| 167 | * userspace_pid[cpu] == 0, which gives an error. */ | 180 | * userspace_pid[cpu] == 0, which gives an error. */ |
| @@ -173,6 +186,9 @@ int arch_switch_tls_skas(struct task_struct *from, struct task_struct *to) | |||
| 173 | 186 | ||
| 174 | int arch_switch_tls_tt(struct task_struct *from, struct task_struct *to) | 187 | int arch_switch_tls_tt(struct task_struct *from, struct task_struct *to) |
| 175 | { | 188 | { |
| 189 | if (!host_supports_tls) | ||
| 190 | return 0; | ||
| 191 | |||
| 176 | if (needs_TLS_update(to)) | 192 | if (needs_TLS_update(to)) |
| 177 | return load_TLS(0, to); | 193 | return load_TLS(0, to); |
| 178 | 194 | ||
| @@ -256,6 +272,9 @@ asmlinkage int sys_set_thread_area(struct user_desc __user *user_desc) | |||
| 256 | struct user_desc info; | 272 | struct user_desc info; |
| 257 | int idx, ret; | 273 | int idx, ret; |
| 258 | 274 | ||
| 275 | if (!host_supports_tls) | ||
| 276 | return -ENOSYS; | ||
| 277 | |||
| 259 | if (copy_from_user(&info, user_desc, sizeof(info))) | 278 | if (copy_from_user(&info, user_desc, sizeof(info))) |
| 260 | return -EFAULT; | 279 | return -EFAULT; |
| 261 | 280 | ||
| @@ -287,6 +306,9 @@ int ptrace_set_thread_area(struct task_struct *child, int idx, | |||
| 287 | { | 306 | { |
| 288 | struct user_desc info; | 307 | struct user_desc info; |
| 289 | 308 | ||
| 309 | if (!host_supports_tls) | ||
| 310 | return -EIO; | ||
| 311 | |||
| 290 | if (copy_from_user(&info, user_desc, sizeof(info))) | 312 | if (copy_from_user(&info, user_desc, sizeof(info))) |
| 291 | return -EFAULT; | 313 | return -EFAULT; |
| 292 | 314 | ||
| @@ -298,6 +320,9 @@ asmlinkage int sys_get_thread_area(struct user_desc __user *user_desc) | |||
| 298 | struct user_desc info; | 320 | struct user_desc info; |
| 299 | int idx, ret; | 321 | int idx, ret; |
| 300 | 322 | ||
| 323 | if (!host_supports_tls) | ||
| 324 | return -ENOSYS; | ||
| 325 | |||
| 301 | if (get_user(idx, &user_desc->entry_number)) | 326 | if (get_user(idx, &user_desc->entry_number)) |
| 302 | return -EFAULT; | 327 | return -EFAULT; |
| 303 | 328 | ||
| @@ -321,6 +346,9 @@ int ptrace_get_thread_area(struct task_struct *child, int idx, | |||
| 321 | struct user_desc info; | 346 | struct user_desc info; |
| 322 | int ret; | 347 | int ret; |
| 323 | 348 | ||
| 349 | if (!host_supports_tls) | ||
| 350 | return -EIO; | ||
| 351 | |||
| 324 | ret = get_tls_entry(child, &info, idx); | 352 | ret = get_tls_entry(child, &info, idx); |
| 325 | if (ret < 0) | 353 | if (ret < 0) |
| 326 | goto out; | 354 | goto out; |
| @@ -331,3 +359,26 @@ out: | |||
| 331 | return ret; | 359 | return ret; |
| 332 | } | 360 | } |
| 333 | 361 | ||
| 362 | |||
| 363 | /* XXX: This part is probably common to i386 and x86-64. Don't create a common | ||
| 364 | * file for now, do that when implementing x86-64 support.*/ | ||
| 365 | static int __init __setup_host_supports_tls(void) { | ||
| 366 | check_host_supports_tls(&host_supports_tls, &host_gdt_entry_tls_min); | ||
| 367 | if (host_supports_tls) { | ||
| 368 | printk(KERN_INFO "Host TLS support detected\n"); | ||
| 369 | printk(KERN_INFO "Detected host type: "); | ||
| 370 | switch (host_gdt_entry_tls_min) { | ||
| 371 | case GDT_ENTRY_TLS_MIN_I386: | ||
| 372 | printk("i386\n"); | ||
| 373 | break; | ||
| 374 | case GDT_ENTRY_TLS_MIN_X86_64: | ||
| 375 | printk("x86_64\n"); | ||
| 376 | break; | ||
| 377 | } | ||
| 378 | } else | ||
| 379 | printk(KERN_ERR " Host TLS support NOT detected! " | ||
| 380 | "TLS support inside UML will not work\n"); | ||
| 381 | return 1; | ||
| 382 | } | ||
| 383 | |||
| 384 | __initcall(__setup_host_supports_tls); | ||
diff --git a/include/asm-um/segment.h b/include/asm-um/segment.h index 48775452e2cc..45183fcd10b6 100644 --- a/include/asm-um/segment.h +++ b/include/asm-um/segment.h | |||
| @@ -1,6 +1,10 @@ | |||
| 1 | #ifndef __UM_SEGMENT_H | 1 | #ifndef __UM_SEGMENT_H |
| 2 | #define __UM_SEGMENT_H | 2 | #define __UM_SEGMENT_H |
| 3 | 3 | ||
| 4 | #include "asm/arch/segment.h" | 4 | extern int host_gdt_entry_tls_min; |
| 5 | |||
| 6 | #define GDT_ENTRY_TLS_ENTRIES 3 | ||
| 7 | #define GDT_ENTRY_TLS_MIN host_gdt_entry_tls_min | ||
| 8 | #define GDT_ENTRY_TLS_MAX (GDT_ENTRY_TLS_MIN + GDT_ENTRY_TLS_ENTRIES - 1) | ||
| 5 | 9 | ||
| 6 | #endif | 10 | #endif |
