18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
38c2ecf20Sopenharmony_ci * Licensed under the GPL
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/mm.h>
78c2ecf20Sopenharmony_ci#include <linux/sched.h>
88c2ecf20Sopenharmony_ci#include <linux/slab.h>
98c2ecf20Sopenharmony_ci#include <linux/syscalls.h>
108c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
118c2ecf20Sopenharmony_ci#include <asm/unistd.h>
128c2ecf20Sopenharmony_ci#include <os.h>
138c2ecf20Sopenharmony_ci#include <skas.h>
148c2ecf20Sopenharmony_ci#include <sysdep/tls.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cistatic inline int modify_ldt (int func, void *ptr, unsigned long bytecount)
178c2ecf20Sopenharmony_ci{
188c2ecf20Sopenharmony_ci	return syscall(__NR_modify_ldt, func, ptr, bytecount);
198c2ecf20Sopenharmony_ci}
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic long write_ldt_entry(struct mm_id *mm_idp, int func,
228c2ecf20Sopenharmony_ci		     struct user_desc *desc, void **addr, int done)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	long res;
258c2ecf20Sopenharmony_ci	void *stub_addr;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(*desc) % sizeof(long));
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	res = syscall_stub_data(mm_idp, (unsigned long *)desc,
308c2ecf20Sopenharmony_ci				sizeof(*desc) / sizeof(long),
318c2ecf20Sopenharmony_ci				addr, &stub_addr);
328c2ecf20Sopenharmony_ci	if (!res) {
338c2ecf20Sopenharmony_ci		unsigned long args[] = { func,
348c2ecf20Sopenharmony_ci					 (unsigned long)stub_addr,
358c2ecf20Sopenharmony_ci					 sizeof(*desc),
368c2ecf20Sopenharmony_ci					 0, 0, 0 };
378c2ecf20Sopenharmony_ci		res = run_syscall_stub(mm_idp, __NR_modify_ldt, args,
388c2ecf20Sopenharmony_ci				       0, addr, done);
398c2ecf20Sopenharmony_ci	}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	return res;
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci/*
458c2ecf20Sopenharmony_ci * In skas mode, we hold our own ldt data in UML.
468c2ecf20Sopenharmony_ci * Thus, the code implementing sys_modify_ldt_skas
478c2ecf20Sopenharmony_ci * is very similar to (and mostly stolen from) sys_modify_ldt
488c2ecf20Sopenharmony_ci * for arch/i386/kernel/ldt.c
498c2ecf20Sopenharmony_ci * The routines copied and modified in part are:
508c2ecf20Sopenharmony_ci * - read_ldt
518c2ecf20Sopenharmony_ci * - read_default_ldt
528c2ecf20Sopenharmony_ci * - write_ldt
538c2ecf20Sopenharmony_ci * - sys_modify_ldt_skas
548c2ecf20Sopenharmony_ci */
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic int read_ldt(void __user * ptr, unsigned long bytecount)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	int i, err = 0;
598c2ecf20Sopenharmony_ci	unsigned long size;
608c2ecf20Sopenharmony_ci	uml_ldt_t *ldt = &current->mm->context.arch.ldt;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	if (!ldt->entry_count)
638c2ecf20Sopenharmony_ci		goto out;
648c2ecf20Sopenharmony_ci	if (bytecount > LDT_ENTRY_SIZE*LDT_ENTRIES)
658c2ecf20Sopenharmony_ci		bytecount = LDT_ENTRY_SIZE*LDT_ENTRIES;
668c2ecf20Sopenharmony_ci	err = bytecount;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	mutex_lock(&ldt->lock);
698c2ecf20Sopenharmony_ci	if (ldt->entry_count <= LDT_DIRECT_ENTRIES) {
708c2ecf20Sopenharmony_ci		size = LDT_ENTRY_SIZE*LDT_DIRECT_ENTRIES;
718c2ecf20Sopenharmony_ci		if (size > bytecount)
728c2ecf20Sopenharmony_ci			size = bytecount;
738c2ecf20Sopenharmony_ci		if (copy_to_user(ptr, ldt->u.entries, size))
748c2ecf20Sopenharmony_ci			err = -EFAULT;
758c2ecf20Sopenharmony_ci		bytecount -= size;
768c2ecf20Sopenharmony_ci		ptr += size;
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci	else {
798c2ecf20Sopenharmony_ci		for (i=0; i<ldt->entry_count/LDT_ENTRIES_PER_PAGE && bytecount;
808c2ecf20Sopenharmony_ci		     i++) {
818c2ecf20Sopenharmony_ci			size = PAGE_SIZE;
828c2ecf20Sopenharmony_ci			if (size > bytecount)
838c2ecf20Sopenharmony_ci				size = bytecount;
848c2ecf20Sopenharmony_ci			if (copy_to_user(ptr, ldt->u.pages[i], size)) {
858c2ecf20Sopenharmony_ci				err = -EFAULT;
868c2ecf20Sopenharmony_ci				break;
878c2ecf20Sopenharmony_ci			}
888c2ecf20Sopenharmony_ci			bytecount -= size;
898c2ecf20Sopenharmony_ci			ptr += size;
908c2ecf20Sopenharmony_ci		}
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci	mutex_unlock(&ldt->lock);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	if (bytecount == 0 || err == -EFAULT)
958c2ecf20Sopenharmony_ci		goto out;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	if (clear_user(ptr, bytecount))
988c2ecf20Sopenharmony_ci		err = -EFAULT;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ciout:
1018c2ecf20Sopenharmony_ci	return err;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic int read_default_ldt(void __user * ptr, unsigned long bytecount)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	int err;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	if (bytecount > 5*LDT_ENTRY_SIZE)
1098c2ecf20Sopenharmony_ci		bytecount = 5*LDT_ENTRY_SIZE;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	err = bytecount;
1128c2ecf20Sopenharmony_ci	/*
1138c2ecf20Sopenharmony_ci	 * UML doesn't support lcall7 and lcall27.
1148c2ecf20Sopenharmony_ci	 * So, we don't really have a default ldt, but emulate
1158c2ecf20Sopenharmony_ci	 * an empty ldt of common host default ldt size.
1168c2ecf20Sopenharmony_ci	 */
1178c2ecf20Sopenharmony_ci	if (clear_user(ptr, bytecount))
1188c2ecf20Sopenharmony_ci		err = -EFAULT;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	return err;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic int write_ldt(void __user * ptr, unsigned long bytecount, int func)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	uml_ldt_t *ldt = &current->mm->context.arch.ldt;
1268c2ecf20Sopenharmony_ci	struct mm_id * mm_idp = &current->mm->context.id;
1278c2ecf20Sopenharmony_ci	int i, err;
1288c2ecf20Sopenharmony_ci	struct user_desc ldt_info;
1298c2ecf20Sopenharmony_ci	struct ldt_entry entry0, *ldt_p;
1308c2ecf20Sopenharmony_ci	void *addr = NULL;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	err = -EINVAL;
1338c2ecf20Sopenharmony_ci	if (bytecount != sizeof(ldt_info))
1348c2ecf20Sopenharmony_ci		goto out;
1358c2ecf20Sopenharmony_ci	err = -EFAULT;
1368c2ecf20Sopenharmony_ci	if (copy_from_user(&ldt_info, ptr, sizeof(ldt_info)))
1378c2ecf20Sopenharmony_ci		goto out;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	err = -EINVAL;
1408c2ecf20Sopenharmony_ci	if (ldt_info.entry_number >= LDT_ENTRIES)
1418c2ecf20Sopenharmony_ci		goto out;
1428c2ecf20Sopenharmony_ci	if (ldt_info.contents == 3) {
1438c2ecf20Sopenharmony_ci		if (func == 1)
1448c2ecf20Sopenharmony_ci			goto out;
1458c2ecf20Sopenharmony_ci		if (ldt_info.seg_not_present == 0)
1468c2ecf20Sopenharmony_ci			goto out;
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	mutex_lock(&ldt->lock);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	err = write_ldt_entry(mm_idp, func, &ldt_info, &addr, 1);
1528c2ecf20Sopenharmony_ci	if (err)
1538c2ecf20Sopenharmony_ci		goto out_unlock;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	if (ldt_info.entry_number >= ldt->entry_count &&
1568c2ecf20Sopenharmony_ci	    ldt_info.entry_number >= LDT_DIRECT_ENTRIES) {
1578c2ecf20Sopenharmony_ci		for (i=ldt->entry_count/LDT_ENTRIES_PER_PAGE;
1588c2ecf20Sopenharmony_ci		     i*LDT_ENTRIES_PER_PAGE <= ldt_info.entry_number;
1598c2ecf20Sopenharmony_ci		     i++) {
1608c2ecf20Sopenharmony_ci			if (i == 0)
1618c2ecf20Sopenharmony_ci				memcpy(&entry0, ldt->u.entries,
1628c2ecf20Sopenharmony_ci				       sizeof(entry0));
1638c2ecf20Sopenharmony_ci			ldt->u.pages[i] = (struct ldt_entry *)
1648c2ecf20Sopenharmony_ci				__get_free_page(GFP_KERNEL|__GFP_ZERO);
1658c2ecf20Sopenharmony_ci			if (!ldt->u.pages[i]) {
1668c2ecf20Sopenharmony_ci				err = -ENOMEM;
1678c2ecf20Sopenharmony_ci				/* Undo the change in host */
1688c2ecf20Sopenharmony_ci				memset(&ldt_info, 0, sizeof(ldt_info));
1698c2ecf20Sopenharmony_ci				write_ldt_entry(mm_idp, 1, &ldt_info, &addr, 1);
1708c2ecf20Sopenharmony_ci				goto out_unlock;
1718c2ecf20Sopenharmony_ci			}
1728c2ecf20Sopenharmony_ci			if (i == 0) {
1738c2ecf20Sopenharmony_ci				memcpy(ldt->u.pages[0], &entry0,
1748c2ecf20Sopenharmony_ci				       sizeof(entry0));
1758c2ecf20Sopenharmony_ci				memcpy(ldt->u.pages[0]+1, ldt->u.entries+1,
1768c2ecf20Sopenharmony_ci				       sizeof(entry0)*(LDT_DIRECT_ENTRIES-1));
1778c2ecf20Sopenharmony_ci			}
1788c2ecf20Sopenharmony_ci			ldt->entry_count = (i + 1) * LDT_ENTRIES_PER_PAGE;
1798c2ecf20Sopenharmony_ci		}
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci	if (ldt->entry_count <= ldt_info.entry_number)
1828c2ecf20Sopenharmony_ci		ldt->entry_count = ldt_info.entry_number + 1;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	if (ldt->entry_count <= LDT_DIRECT_ENTRIES)
1858c2ecf20Sopenharmony_ci		ldt_p = ldt->u.entries + ldt_info.entry_number;
1868c2ecf20Sopenharmony_ci	else
1878c2ecf20Sopenharmony_ci		ldt_p = ldt->u.pages[ldt_info.entry_number/LDT_ENTRIES_PER_PAGE] +
1888c2ecf20Sopenharmony_ci			ldt_info.entry_number%LDT_ENTRIES_PER_PAGE;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	if (ldt_info.base_addr == 0 && ldt_info.limit == 0 &&
1918c2ecf20Sopenharmony_ci	   (func == 1 || LDT_empty(&ldt_info))) {
1928c2ecf20Sopenharmony_ci		ldt_p->a = 0;
1938c2ecf20Sopenharmony_ci		ldt_p->b = 0;
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci	else{
1968c2ecf20Sopenharmony_ci		if (func == 1)
1978c2ecf20Sopenharmony_ci			ldt_info.useable = 0;
1988c2ecf20Sopenharmony_ci		ldt_p->a = LDT_entry_a(&ldt_info);
1998c2ecf20Sopenharmony_ci		ldt_p->b = LDT_entry_b(&ldt_info);
2008c2ecf20Sopenharmony_ci	}
2018c2ecf20Sopenharmony_ci	err = 0;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ciout_unlock:
2048c2ecf20Sopenharmony_ci	mutex_unlock(&ldt->lock);
2058c2ecf20Sopenharmony_ciout:
2068c2ecf20Sopenharmony_ci	return err;
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic long do_modify_ldt_skas(int func, void __user *ptr,
2108c2ecf20Sopenharmony_ci			       unsigned long bytecount)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	int ret = -ENOSYS;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	switch (func) {
2158c2ecf20Sopenharmony_ci		case 0:
2168c2ecf20Sopenharmony_ci			ret = read_ldt(ptr, bytecount);
2178c2ecf20Sopenharmony_ci			break;
2188c2ecf20Sopenharmony_ci		case 1:
2198c2ecf20Sopenharmony_ci		case 0x11:
2208c2ecf20Sopenharmony_ci			ret = write_ldt(ptr, bytecount, func);
2218c2ecf20Sopenharmony_ci			break;
2228c2ecf20Sopenharmony_ci		case 2:
2238c2ecf20Sopenharmony_ci			ret = read_default_ldt(ptr, bytecount);
2248c2ecf20Sopenharmony_ci			break;
2258c2ecf20Sopenharmony_ci	}
2268c2ecf20Sopenharmony_ci	return ret;
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(host_ldt_lock);
2308c2ecf20Sopenharmony_cistatic short dummy_list[9] = {0, -1};
2318c2ecf20Sopenharmony_cistatic short * host_ldt_entries = NULL;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistatic void ldt_get_host_info(void)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	long ret;
2368c2ecf20Sopenharmony_ci	struct ldt_entry * ldt;
2378c2ecf20Sopenharmony_ci	short *tmp;
2388c2ecf20Sopenharmony_ci	int i, size, k, order;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	spin_lock(&host_ldt_lock);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	if (host_ldt_entries != NULL) {
2438c2ecf20Sopenharmony_ci		spin_unlock(&host_ldt_lock);
2448c2ecf20Sopenharmony_ci		return;
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ci	host_ldt_entries = dummy_list+1;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	spin_unlock(&host_ldt_lock);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	for (i = LDT_PAGES_MAX-1, order=0; i; i>>=1, order++)
2518c2ecf20Sopenharmony_ci		;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	ldt = (struct ldt_entry *)
2548c2ecf20Sopenharmony_ci	      __get_free_pages(GFP_KERNEL|__GFP_ZERO, order);
2558c2ecf20Sopenharmony_ci	if (ldt == NULL) {
2568c2ecf20Sopenharmony_ci		printk(KERN_ERR "ldt_get_host_info: couldn't allocate buffer "
2578c2ecf20Sopenharmony_ci		       "for host ldt\n");
2588c2ecf20Sopenharmony_ci		return;
2598c2ecf20Sopenharmony_ci	}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	ret = modify_ldt(0, ldt, (1<<order)*PAGE_SIZE);
2628c2ecf20Sopenharmony_ci	if (ret < 0) {
2638c2ecf20Sopenharmony_ci		printk(KERN_ERR "ldt_get_host_info: couldn't read host ldt\n");
2648c2ecf20Sopenharmony_ci		goto out_free;
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci	if (ret == 0) {
2678c2ecf20Sopenharmony_ci		/* default_ldt is active, simply write an empty entry 0 */
2688c2ecf20Sopenharmony_ci		host_ldt_entries = dummy_list;
2698c2ecf20Sopenharmony_ci		goto out_free;
2708c2ecf20Sopenharmony_ci	}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	for (i=0, size=0; i<ret/LDT_ENTRY_SIZE; i++) {
2738c2ecf20Sopenharmony_ci		if (ldt[i].a != 0 || ldt[i].b != 0)
2748c2ecf20Sopenharmony_ci			size++;
2758c2ecf20Sopenharmony_ci	}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	if (size < ARRAY_SIZE(dummy_list))
2788c2ecf20Sopenharmony_ci		host_ldt_entries = dummy_list;
2798c2ecf20Sopenharmony_ci	else {
2808c2ecf20Sopenharmony_ci		size = (size + 1) * sizeof(dummy_list[0]);
2818c2ecf20Sopenharmony_ci		tmp = kmalloc(size, GFP_KERNEL);
2828c2ecf20Sopenharmony_ci		if (tmp == NULL) {
2838c2ecf20Sopenharmony_ci			printk(KERN_ERR "ldt_get_host_info: couldn't allocate "
2848c2ecf20Sopenharmony_ci			       "host ldt list\n");
2858c2ecf20Sopenharmony_ci			goto out_free;
2868c2ecf20Sopenharmony_ci		}
2878c2ecf20Sopenharmony_ci		host_ldt_entries = tmp;
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	for (i=0, k=0; i<ret/LDT_ENTRY_SIZE; i++) {
2918c2ecf20Sopenharmony_ci		if (ldt[i].a != 0 || ldt[i].b != 0)
2928c2ecf20Sopenharmony_ci			host_ldt_entries[k++] = i;
2938c2ecf20Sopenharmony_ci	}
2948c2ecf20Sopenharmony_ci	host_ldt_entries[k] = -1;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ciout_free:
2978c2ecf20Sopenharmony_ci	free_pages((unsigned long)ldt, order);
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cilong init_new_ldt(struct mm_context *new_mm, struct mm_context *from_mm)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	struct user_desc desc;
3038c2ecf20Sopenharmony_ci	short * num_p;
3048c2ecf20Sopenharmony_ci	int i;
3058c2ecf20Sopenharmony_ci	long page, err=0;
3068c2ecf20Sopenharmony_ci	void *addr = NULL;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	mutex_init(&new_mm->arch.ldt.lock);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	if (!from_mm) {
3128c2ecf20Sopenharmony_ci		memset(&desc, 0, sizeof(desc));
3138c2ecf20Sopenharmony_ci		/*
3148c2ecf20Sopenharmony_ci		 * Now we try to retrieve info about the ldt, we
3158c2ecf20Sopenharmony_ci		 * inherited from the host. All ldt-entries found
3168c2ecf20Sopenharmony_ci		 * will be reset in the following loop
3178c2ecf20Sopenharmony_ci		 */
3188c2ecf20Sopenharmony_ci		ldt_get_host_info();
3198c2ecf20Sopenharmony_ci		for (num_p=host_ldt_entries; *num_p != -1; num_p++) {
3208c2ecf20Sopenharmony_ci			desc.entry_number = *num_p;
3218c2ecf20Sopenharmony_ci			err = write_ldt_entry(&new_mm->id, 1, &desc,
3228c2ecf20Sopenharmony_ci					      &addr, *(num_p + 1) == -1);
3238c2ecf20Sopenharmony_ci			if (err)
3248c2ecf20Sopenharmony_ci				break;
3258c2ecf20Sopenharmony_ci		}
3268c2ecf20Sopenharmony_ci		new_mm->arch.ldt.entry_count = 0;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci		goto out;
3298c2ecf20Sopenharmony_ci	}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	/*
3328c2ecf20Sopenharmony_ci	 * Our local LDT is used to supply the data for
3338c2ecf20Sopenharmony_ci	 * modify_ldt(READLDT), if PTRACE_LDT isn't available,
3348c2ecf20Sopenharmony_ci	 * i.e., we have to use the stub for modify_ldt, which
3358c2ecf20Sopenharmony_ci	 * can't handle the big read buffer of up to 64kB.
3368c2ecf20Sopenharmony_ci	 */
3378c2ecf20Sopenharmony_ci	mutex_lock(&from_mm->arch.ldt.lock);
3388c2ecf20Sopenharmony_ci	if (from_mm->arch.ldt.entry_count <= LDT_DIRECT_ENTRIES)
3398c2ecf20Sopenharmony_ci		memcpy(new_mm->arch.ldt.u.entries, from_mm->arch.ldt.u.entries,
3408c2ecf20Sopenharmony_ci		       sizeof(new_mm->arch.ldt.u.entries));
3418c2ecf20Sopenharmony_ci	else {
3428c2ecf20Sopenharmony_ci		i = from_mm->arch.ldt.entry_count / LDT_ENTRIES_PER_PAGE;
3438c2ecf20Sopenharmony_ci		while (i-->0) {
3448c2ecf20Sopenharmony_ci			page = __get_free_page(GFP_KERNEL|__GFP_ZERO);
3458c2ecf20Sopenharmony_ci			if (!page) {
3468c2ecf20Sopenharmony_ci				err = -ENOMEM;
3478c2ecf20Sopenharmony_ci				break;
3488c2ecf20Sopenharmony_ci			}
3498c2ecf20Sopenharmony_ci			new_mm->arch.ldt.u.pages[i] =
3508c2ecf20Sopenharmony_ci				(struct ldt_entry *) page;
3518c2ecf20Sopenharmony_ci			memcpy(new_mm->arch.ldt.u.pages[i],
3528c2ecf20Sopenharmony_ci			       from_mm->arch.ldt.u.pages[i], PAGE_SIZE);
3538c2ecf20Sopenharmony_ci		}
3548c2ecf20Sopenharmony_ci	}
3558c2ecf20Sopenharmony_ci	new_mm->arch.ldt.entry_count = from_mm->arch.ldt.entry_count;
3568c2ecf20Sopenharmony_ci	mutex_unlock(&from_mm->arch.ldt.lock);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci    out:
3598c2ecf20Sopenharmony_ci	return err;
3608c2ecf20Sopenharmony_ci}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_civoid free_ldt(struct mm_context *mm)
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	int i;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	if (mm->arch.ldt.entry_count > LDT_DIRECT_ENTRIES) {
3688c2ecf20Sopenharmony_ci		i = mm->arch.ldt.entry_count / LDT_ENTRIES_PER_PAGE;
3698c2ecf20Sopenharmony_ci		while (i-- > 0)
3708c2ecf20Sopenharmony_ci			free_page((long) mm->arch.ldt.u.pages[i]);
3718c2ecf20Sopenharmony_ci	}
3728c2ecf20Sopenharmony_ci	mm->arch.ldt.entry_count = 0;
3738c2ecf20Sopenharmony_ci}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ciSYSCALL_DEFINE3(modify_ldt, int , func , void __user * , ptr ,
3768c2ecf20Sopenharmony_ci		unsigned long , bytecount)
3778c2ecf20Sopenharmony_ci{
3788c2ecf20Sopenharmony_ci	/* See non-um modify_ldt() for why we do this cast */
3798c2ecf20Sopenharmony_ci	return (unsigned int)do_modify_ldt_skas(func, ptr, bytecount);
3808c2ecf20Sopenharmony_ci}
381