18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (C) 2005 Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
38c2ecf20Sopenharmony_ci * Licensed under the GPL
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/percpu.h>
78c2ecf20Sopenharmony_ci#include <linux/sched.h>
88c2ecf20Sopenharmony_ci#include <linux/syscalls.h>
98c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
108c2ecf20Sopenharmony_ci#include <asm/ptrace-abi.h>
118c2ecf20Sopenharmony_ci#include <os.h>
128c2ecf20Sopenharmony_ci#include <skas.h>
138c2ecf20Sopenharmony_ci#include <sysdep/tls.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci/*
168c2ecf20Sopenharmony_ci * If needed we can detect when it's uninitialized.
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * These are initialized in an initcall and unchanged thereafter.
198c2ecf20Sopenharmony_ci */
208c2ecf20Sopenharmony_cistatic int host_supports_tls = -1;
218c2ecf20Sopenharmony_ciint host_gdt_entry_tls_min;
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ciint do_set_thread_area(struct user_desc *info)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	int ret;
268c2ecf20Sopenharmony_ci	u32 cpu;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	cpu = get_cpu();
298c2ecf20Sopenharmony_ci	ret = os_set_thread_area(info, userspace_pid[cpu]);
308c2ecf20Sopenharmony_ci	put_cpu();
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	if (ret)
338c2ecf20Sopenharmony_ci		printk(KERN_ERR "PTRACE_SET_THREAD_AREA failed, err = %d, "
348c2ecf20Sopenharmony_ci		       "index = %d\n", ret, info->entry_number);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	return ret;
378c2ecf20Sopenharmony_ci}
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ciint do_get_thread_area(struct user_desc *info)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	int ret;
428c2ecf20Sopenharmony_ci	u32 cpu;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	cpu = get_cpu();
458c2ecf20Sopenharmony_ci	ret = os_get_thread_area(info, userspace_pid[cpu]);
468c2ecf20Sopenharmony_ci	put_cpu();
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	if (ret)
498c2ecf20Sopenharmony_ci		printk(KERN_ERR "PTRACE_GET_THREAD_AREA failed, err = %d, "
508c2ecf20Sopenharmony_ci		       "index = %d\n", ret, info->entry_number);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	return ret;
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci/*
568c2ecf20Sopenharmony_ci * sys_get_thread_area: get a yet unused TLS descriptor index.
578c2ecf20Sopenharmony_ci * XXX: Consider leaving one free slot for glibc usage at first place. This must
588c2ecf20Sopenharmony_ci * be done here (and by changing GDT_ENTRY_TLS_* macros) and nowhere else.
598c2ecf20Sopenharmony_ci *
608c2ecf20Sopenharmony_ci * Also, this must be tested when compiling in SKAS mode with dynamic linking
618c2ecf20Sopenharmony_ci * and running against NPTL.
628c2ecf20Sopenharmony_ci */
638c2ecf20Sopenharmony_cistatic int get_free_idx(struct task_struct* task)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	struct thread_struct *t = &task->thread;
668c2ecf20Sopenharmony_ci	int idx;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	for (idx = 0; idx < GDT_ENTRY_TLS_ENTRIES; idx++)
698c2ecf20Sopenharmony_ci		if (!t->arch.tls_array[idx].present)
708c2ecf20Sopenharmony_ci			return idx + GDT_ENTRY_TLS_MIN;
718c2ecf20Sopenharmony_ci	return -ESRCH;
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic inline void clear_user_desc(struct user_desc* info)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	/* Postcondition: LDT_empty(info) returns true. */
778c2ecf20Sopenharmony_ci	memset(info, 0, sizeof(*info));
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	/*
808c2ecf20Sopenharmony_ci	 * Check the LDT_empty or the i386 sys_get_thread_area code - we obtain
818c2ecf20Sopenharmony_ci	 * indeed an empty user_desc.
828c2ecf20Sopenharmony_ci	 */
838c2ecf20Sopenharmony_ci	info->read_exec_only = 1;
848c2ecf20Sopenharmony_ci	info->seg_not_present = 1;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci#define O_FORCE 1
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic int load_TLS(int flags, struct task_struct *to)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	int ret = 0;
928c2ecf20Sopenharmony_ci	int idx;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	for (idx = GDT_ENTRY_TLS_MIN; idx < GDT_ENTRY_TLS_MAX; idx++) {
958c2ecf20Sopenharmony_ci		struct uml_tls_struct* curr =
968c2ecf20Sopenharmony_ci			&to->thread.arch.tls_array[idx - GDT_ENTRY_TLS_MIN];
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci		/*
998c2ecf20Sopenharmony_ci		 * Actually, now if it wasn't flushed it gets cleared and
1008c2ecf20Sopenharmony_ci		 * flushed to the host, which will clear it.
1018c2ecf20Sopenharmony_ci		 */
1028c2ecf20Sopenharmony_ci		if (!curr->present) {
1038c2ecf20Sopenharmony_ci			if (!curr->flushed) {
1048c2ecf20Sopenharmony_ci				clear_user_desc(&curr->tls);
1058c2ecf20Sopenharmony_ci				curr->tls.entry_number = idx;
1068c2ecf20Sopenharmony_ci			} else {
1078c2ecf20Sopenharmony_ci				WARN_ON(!LDT_empty(&curr->tls));
1088c2ecf20Sopenharmony_ci				continue;
1098c2ecf20Sopenharmony_ci			}
1108c2ecf20Sopenharmony_ci		}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci		if (!(flags & O_FORCE) && curr->flushed)
1138c2ecf20Sopenharmony_ci			continue;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci		ret = do_set_thread_area(&curr->tls);
1168c2ecf20Sopenharmony_ci		if (ret)
1178c2ecf20Sopenharmony_ci			goto out;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci		curr->flushed = 1;
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ciout:
1228c2ecf20Sopenharmony_ci	return ret;
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci/*
1268c2ecf20Sopenharmony_ci * Verify if we need to do a flush for the new process, i.e. if there are any
1278c2ecf20Sopenharmony_ci * present desc's, only if they haven't been flushed.
1288c2ecf20Sopenharmony_ci */
1298c2ecf20Sopenharmony_cistatic inline int needs_TLS_update(struct task_struct *task)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	int i;
1328c2ecf20Sopenharmony_ci	int ret = 0;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	for (i = GDT_ENTRY_TLS_MIN; i < GDT_ENTRY_TLS_MAX; i++) {
1358c2ecf20Sopenharmony_ci		struct uml_tls_struct* curr =
1368c2ecf20Sopenharmony_ci			&task->thread.arch.tls_array[i - GDT_ENTRY_TLS_MIN];
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci		/*
1398c2ecf20Sopenharmony_ci		 * Can't test curr->present, we may need to clear a descriptor
1408c2ecf20Sopenharmony_ci		 * which had a value.
1418c2ecf20Sopenharmony_ci		 */
1428c2ecf20Sopenharmony_ci		if (curr->flushed)
1438c2ecf20Sopenharmony_ci			continue;
1448c2ecf20Sopenharmony_ci		ret = 1;
1458c2ecf20Sopenharmony_ci		break;
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci	return ret;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci/*
1518c2ecf20Sopenharmony_ci * On a newly forked process, the TLS descriptors haven't yet been flushed. So
1528c2ecf20Sopenharmony_ci * we mark them as such and the first switch_to will do the job.
1538c2ecf20Sopenharmony_ci */
1548c2ecf20Sopenharmony_civoid clear_flushed_tls(struct task_struct *task)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	int i;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	for (i = GDT_ENTRY_TLS_MIN; i < GDT_ENTRY_TLS_MAX; i++) {
1598c2ecf20Sopenharmony_ci		struct uml_tls_struct* curr =
1608c2ecf20Sopenharmony_ci			&task->thread.arch.tls_array[i - GDT_ENTRY_TLS_MIN];
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci		/*
1638c2ecf20Sopenharmony_ci		 * Still correct to do this, if it wasn't present on the host it
1648c2ecf20Sopenharmony_ci		 * will remain as flushed as it was.
1658c2ecf20Sopenharmony_ci		 */
1668c2ecf20Sopenharmony_ci		if (!curr->present)
1678c2ecf20Sopenharmony_ci			continue;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci		curr->flushed = 0;
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci/*
1748c2ecf20Sopenharmony_ci * In SKAS0 mode, currently, multiple guest threads sharing the same ->mm have a
1758c2ecf20Sopenharmony_ci * common host process. So this is needed in SKAS0 too.
1768c2ecf20Sopenharmony_ci *
1778c2ecf20Sopenharmony_ci * However, if each thread had a different host process (and this was discussed
1788c2ecf20Sopenharmony_ci * for SMP support) this won't be needed.
1798c2ecf20Sopenharmony_ci *
1808c2ecf20Sopenharmony_ci * And this will not need be used when (and if) we'll add support to the host
1818c2ecf20Sopenharmony_ci * SKAS patch.
1828c2ecf20Sopenharmony_ci */
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ciint arch_switch_tls(struct task_struct *to)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	if (!host_supports_tls)
1878c2ecf20Sopenharmony_ci		return 0;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	/*
1908c2ecf20Sopenharmony_ci	 * We have no need whatsoever to switch TLS for kernel threads; beyond
1918c2ecf20Sopenharmony_ci	 * that, that would also result in us calling os_set_thread_area with
1928c2ecf20Sopenharmony_ci	 * userspace_pid[cpu] == 0, which gives an error.
1938c2ecf20Sopenharmony_ci	 */
1948c2ecf20Sopenharmony_ci	if (likely(to->mm))
1958c2ecf20Sopenharmony_ci		return load_TLS(O_FORCE, to);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	return 0;
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic int set_tls_entry(struct task_struct* task, struct user_desc *info,
2018c2ecf20Sopenharmony_ci			 int idx, int flushed)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	struct thread_struct *t = &task->thread;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX)
2068c2ecf20Sopenharmony_ci		return -EINVAL;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].tls = *info;
2098c2ecf20Sopenharmony_ci	t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].present = 1;
2108c2ecf20Sopenharmony_ci	t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].flushed = flushed;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	return 0;
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ciint arch_set_tls(struct task_struct *new, unsigned long tls)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	struct user_desc info;
2188c2ecf20Sopenharmony_ci	int idx, ret = -EFAULT;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	if (copy_from_user(&info, (void __user *) tls, sizeof(info)))
2218c2ecf20Sopenharmony_ci		goto out;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	ret = -EINVAL;
2248c2ecf20Sopenharmony_ci	if (LDT_empty(&info))
2258c2ecf20Sopenharmony_ci		goto out;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	idx = info.entry_number;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	ret = set_tls_entry(new, &info, idx, 0);
2308c2ecf20Sopenharmony_ciout:
2318c2ecf20Sopenharmony_ci	return ret;
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci/* XXX: use do_get_thread_area to read the host value? I'm not at all sure! */
2358c2ecf20Sopenharmony_cistatic int get_tls_entry(struct task_struct *task, struct user_desc *info,
2368c2ecf20Sopenharmony_ci			 int idx)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	struct thread_struct *t = &task->thread;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX)
2418c2ecf20Sopenharmony_ci		return -EINVAL;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	if (!t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].present)
2448c2ecf20Sopenharmony_ci		goto clear;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	*info = t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].tls;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ciout:
2498c2ecf20Sopenharmony_ci	/*
2508c2ecf20Sopenharmony_ci	 * Temporary debugging check, to make sure that things have been
2518c2ecf20Sopenharmony_ci	 * flushed. This could be triggered if load_TLS() failed.
2528c2ecf20Sopenharmony_ci	 */
2538c2ecf20Sopenharmony_ci	if (unlikely(task == current &&
2548c2ecf20Sopenharmony_ci		     !t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].flushed)) {
2558c2ecf20Sopenharmony_ci		printk(KERN_ERR "get_tls_entry: task with pid %d got here "
2568c2ecf20Sopenharmony_ci				"without flushed TLS.", current->pid);
2578c2ecf20Sopenharmony_ci	}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	return 0;
2608c2ecf20Sopenharmony_ciclear:
2618c2ecf20Sopenharmony_ci	/*
2628c2ecf20Sopenharmony_ci	 * When the TLS entry has not been set, the values read to user in the
2638c2ecf20Sopenharmony_ci	 * tls_array are 0 (because it's cleared at boot, see
2648c2ecf20Sopenharmony_ci	 * arch/i386/kernel/head.S:cpu_gdt_table). Emulate that.
2658c2ecf20Sopenharmony_ci	 */
2668c2ecf20Sopenharmony_ci	clear_user_desc(info);
2678c2ecf20Sopenharmony_ci	info->entry_number = idx;
2688c2ecf20Sopenharmony_ci	goto out;
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ciSYSCALL_DEFINE1(set_thread_area, struct user_desc __user *, user_desc)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	struct user_desc info;
2748c2ecf20Sopenharmony_ci	int idx, ret;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	if (!host_supports_tls)
2778c2ecf20Sopenharmony_ci		return -ENOSYS;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	if (copy_from_user(&info, user_desc, sizeof(info)))
2808c2ecf20Sopenharmony_ci		return -EFAULT;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	idx = info.entry_number;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	if (idx == -1) {
2858c2ecf20Sopenharmony_ci		idx = get_free_idx(current);
2868c2ecf20Sopenharmony_ci		if (idx < 0)
2878c2ecf20Sopenharmony_ci			return idx;
2888c2ecf20Sopenharmony_ci		info.entry_number = idx;
2898c2ecf20Sopenharmony_ci		/* Tell the user which slot we chose for him.*/
2908c2ecf20Sopenharmony_ci		if (put_user(idx, &user_desc->entry_number))
2918c2ecf20Sopenharmony_ci			return -EFAULT;
2928c2ecf20Sopenharmony_ci	}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	ret = do_set_thread_area(&info);
2958c2ecf20Sopenharmony_ci	if (ret)
2968c2ecf20Sopenharmony_ci		return ret;
2978c2ecf20Sopenharmony_ci	return set_tls_entry(current, &info, idx, 1);
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci/*
3018c2ecf20Sopenharmony_ci * Perform set_thread_area on behalf of the traced child.
3028c2ecf20Sopenharmony_ci * Note: error handling is not done on the deferred load, and this differ from
3038c2ecf20Sopenharmony_ci * i386. However the only possible error are caused by bugs.
3048c2ecf20Sopenharmony_ci */
3058c2ecf20Sopenharmony_ciint ptrace_set_thread_area(struct task_struct *child, int idx,
3068c2ecf20Sopenharmony_ci			   struct user_desc __user *user_desc)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	struct user_desc info;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	if (!host_supports_tls)
3118c2ecf20Sopenharmony_ci		return -EIO;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	if (copy_from_user(&info, user_desc, sizeof(info)))
3148c2ecf20Sopenharmony_ci		return -EFAULT;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	return set_tls_entry(child, &info, idx, 0);
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ciSYSCALL_DEFINE1(get_thread_area, struct user_desc __user *, user_desc)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	struct user_desc info;
3228c2ecf20Sopenharmony_ci	int idx, ret;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	if (!host_supports_tls)
3258c2ecf20Sopenharmony_ci		return -ENOSYS;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	if (get_user(idx, &user_desc->entry_number))
3288c2ecf20Sopenharmony_ci		return -EFAULT;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	ret = get_tls_entry(current, &info, idx);
3318c2ecf20Sopenharmony_ci	if (ret < 0)
3328c2ecf20Sopenharmony_ci		goto out;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	if (copy_to_user(user_desc, &info, sizeof(info)))
3358c2ecf20Sopenharmony_ci		ret = -EFAULT;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ciout:
3388c2ecf20Sopenharmony_ci	return ret;
3398c2ecf20Sopenharmony_ci}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci/*
3428c2ecf20Sopenharmony_ci * Perform get_thread_area on behalf of the traced child.
3438c2ecf20Sopenharmony_ci */
3448c2ecf20Sopenharmony_ciint ptrace_get_thread_area(struct task_struct *child, int idx,
3458c2ecf20Sopenharmony_ci		struct user_desc __user *user_desc)
3468c2ecf20Sopenharmony_ci{
3478c2ecf20Sopenharmony_ci	struct user_desc info;
3488c2ecf20Sopenharmony_ci	int ret;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	if (!host_supports_tls)
3518c2ecf20Sopenharmony_ci		return -EIO;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	ret = get_tls_entry(child, &info, idx);
3548c2ecf20Sopenharmony_ci	if (ret < 0)
3558c2ecf20Sopenharmony_ci		goto out;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	if (copy_to_user(user_desc, &info, sizeof(info)))
3588c2ecf20Sopenharmony_ci		ret = -EFAULT;
3598c2ecf20Sopenharmony_ciout:
3608c2ecf20Sopenharmony_ci	return ret;
3618c2ecf20Sopenharmony_ci}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci/*
3648c2ecf20Sopenharmony_ci * This code is really i386-only, but it detects and logs x86_64 GDT indexes
3658c2ecf20Sopenharmony_ci * if a 32-bit UML is running on a 64-bit host.
3668c2ecf20Sopenharmony_ci */
3678c2ecf20Sopenharmony_cistatic int __init __setup_host_supports_tls(void)
3688c2ecf20Sopenharmony_ci{
3698c2ecf20Sopenharmony_ci	check_host_supports_tls(&host_supports_tls, &host_gdt_entry_tls_min);
3708c2ecf20Sopenharmony_ci	if (host_supports_tls) {
3718c2ecf20Sopenharmony_ci		printk(KERN_INFO "Host TLS support detected\n");
3728c2ecf20Sopenharmony_ci		printk(KERN_INFO "Detected host type: ");
3738c2ecf20Sopenharmony_ci		switch (host_gdt_entry_tls_min) {
3748c2ecf20Sopenharmony_ci		case GDT_ENTRY_TLS_MIN_I386:
3758c2ecf20Sopenharmony_ci			printk(KERN_CONT "i386");
3768c2ecf20Sopenharmony_ci			break;
3778c2ecf20Sopenharmony_ci		case GDT_ENTRY_TLS_MIN_X86_64:
3788c2ecf20Sopenharmony_ci			printk(KERN_CONT "x86_64");
3798c2ecf20Sopenharmony_ci			break;
3808c2ecf20Sopenharmony_ci		}
3818c2ecf20Sopenharmony_ci		printk(KERN_CONT " (GDT indexes %d to %d)\n",
3828c2ecf20Sopenharmony_ci		       host_gdt_entry_tls_min,
3838c2ecf20Sopenharmony_ci		       host_gdt_entry_tls_min + GDT_ENTRY_TLS_ENTRIES);
3848c2ecf20Sopenharmony_ci	} else
3858c2ecf20Sopenharmony_ci		printk(KERN_ERR "  Host TLS support NOT detected! "
3868c2ecf20Sopenharmony_ci				"TLS support inside UML will not work\n");
3878c2ecf20Sopenharmony_ci	return 0;
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci__initcall(__setup_host_supports_tls);
391