File: /usr/src/linux/arch/ia64/ia32/ia32_ldt.c

1     /*
2      * Copyright (C) 2001 Hewlett-Packard Co
3      * Copyright (C) 2001 David Mosberger-Tang <davidm@hpl.hp.com>
4      *
5      * Adapted from arch/i386/kernel/ldt.c
6      */
7     
8     #include <linux/errno.h>
9     #include <linux/sched.h>
10     #include <linux/string.h>
11     #include <linux/mm.h>
12     #include <linux/smp.h>
13     #include <linux/smp_lock.h>
14     #include <linux/vmalloc.h>
15     
16     #include <asm/uaccess.h>
17     #include <asm/ia32.h>
18     
19     /*
20      * read_ldt() is not really atomic - this is not a problem since synchronization of reads
21      * and writes done to the LDT has to be assured by user-space anyway. Writes are atomic,
22      * to protect the security checks done on new descriptors.
23      */
24     static int
25     read_ldt (void *ptr, unsigned long bytecount)
26     {
27     	char *src, *dst, buf[256];	/* temporary buffer (don't overflow kernel stack!) */
28     	unsigned long bytes_left, n;
29     
30     	if (bytecount > IA32_LDT_ENTRIES*IA32_LDT_ENTRY_SIZE)
31     		bytecount = IA32_LDT_ENTRIES*IA32_LDT_ENTRY_SIZE;
32     
33     	bytes_left = bytecount;
34     
35     	src = (void *) IA32_LDT_OFFSET;
36     	dst = ptr;
37     
38     	while (bytes_left) {
39     		n = sizeof(buf);
40     		if (n > bytes_left)
41     			n = bytes_left;
42     
43     		/*
44     		 * We know we're reading valid memory, but we still must guard against
45     		 * running out of memory.
46     		 */
47     		if (__copy_from_user(buf, src, n))
48     			return -EFAULT;
49     
50     		if (copy_to_user(dst, buf, n))
51     			return -EFAULT;
52     
53     		src += n;
54     		dst += n;
55     		bytes_left -= n;
56     	}
57     	return bytecount;
58     }
59     
60     static int
61     write_ldt (void * ptr, unsigned long bytecount, int oldmode)
62     {
63     	struct ia32_modify_ldt_ldt_s ldt_info;
64     	__u64 entry;
65     
66     	if (bytecount != sizeof(ldt_info))
67     		return -EINVAL;
68     	if (copy_from_user(&ldt_info, ptr, sizeof(ldt_info)))
69     		return -EFAULT;
70     
71     	if (ldt_info.entry_number >= IA32_LDT_ENTRIES)
72     		return -EINVAL;
73     	if (ldt_info.contents == 3) {
74     		if (oldmode)
75     			return -EINVAL;
76     		if (ldt_info.seg_not_present == 0)
77     			return -EINVAL;
78     	}
79     
80     	if (ldt_info.base_addr == 0 && ldt_info.limit == 0
81     	    && (oldmode || (ldt_info.contents == 0 && ldt_info.read_exec_only == 1
82     			    && ldt_info.seg_32bit == 0 && ldt_info.limit_in_pages == 0
83     			    && ldt_info.seg_not_present == 1 && ldt_info.useable == 0)))
84     		/* allow LDTs to be cleared by the user */
85     		entry = 0;
86     	else
87     		/* we must set the "Accessed" bit as IVE doesn't emulate it */
88     		entry = IA32_SEG_DESCRIPTOR(ldt_info.base_addr, ldt_info.limit,
89     					    (((ldt_info.read_exec_only ^ 1) << 1)
90     					     | (ldt_info.contents << 2)) | 1,
91     					    1, 3, ldt_info.seg_not_present ^ 1,
92     					    (oldmode ? 0 : ldt_info.useable),
93     					    ldt_info.seg_32bit,
94     					    ldt_info.limit_in_pages);
95     	/*
96     	 * Install the new entry.  We know we're accessing valid (mapped) user-level
97     	 * memory, but we still need to guard against out-of-memory, hence we must use
98     	 * put_user().
99     	 */
100     	return __put_user(entry, (__u64 *) IA32_LDT_OFFSET + ldt_info.entry_number);
101     }
102     
103     asmlinkage int
104     sys32_modify_ldt (int func, void *ptr, unsigned int bytecount)
105     {
106     	int ret = -ENOSYS;
107     
108     	switch (func) {
109     	      case 0:
110     		ret = read_ldt(ptr, bytecount);
111     		break;
112     	      case 1:
113     		ret = write_ldt(ptr, bytecount, 1);
114     		break;
115     	      case 0x11:
116     		ret = write_ldt(ptr, bytecount, 0);
117     		break;
118     	}
119     	return ret;
120     }
121