aboutsummaryrefslogtreecommitdiffstats
path: root/arch/ia64/ia32/ia32_ldt.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/ia64/ia32/ia32_ldt.c')
-rw-r--r--arch/ia64/ia32/ia32_ldt.c147
1 files changed, 147 insertions, 0 deletions
diff --git a/arch/ia64/ia32/ia32_ldt.c b/arch/ia64/ia32/ia32_ldt.c
new file mode 100644
index 000000000000..a152738c7d0d
--- /dev/null
+++ b/arch/ia64/ia32/ia32_ldt.c
@@ -0,0 +1,147 @@
1/*
2 * Copyright (C) 2001, 2004 Hewlett-Packard Co
3 * 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
18#include "ia32priv.h"
19
20/*
21 * read_ldt() is not really atomic - this is not a problem since synchronization of reads
22 * and writes done to the LDT has to be assured by user-space anyway. Writes are atomic,
23 * to protect the security checks done on new descriptors.
24 */
25static int
26read_ldt (void __user *ptr, unsigned long bytecount)
27{
28 unsigned long bytes_left, n;
29 char __user *src, *dst;
30 char buf[256]; /* temporary buffer (don't overflow kernel stack!) */
31
32 if (bytecount > IA32_LDT_ENTRIES*IA32_LDT_ENTRY_SIZE)
33 bytecount = IA32_LDT_ENTRIES*IA32_LDT_ENTRY_SIZE;
34
35 bytes_left = bytecount;
36
37 src = (void __user *) IA32_LDT_OFFSET;
38 dst = ptr;
39
40 while (bytes_left) {
41 n = sizeof(buf);
42 if (n > bytes_left)
43 n = bytes_left;
44
45 /*
46 * We know we're reading valid memory, but we still must guard against
47 * running out of memory.
48 */
49 if (__copy_from_user(buf, src, n))
50 return -EFAULT;
51
52 if (copy_to_user(dst, buf, n))
53 return -EFAULT;
54
55 src += n;
56 dst += n;
57 bytes_left -= n;
58 }
59 return bytecount;
60}
61
62static int
63read_default_ldt (void __user * ptr, unsigned long bytecount)
64{
65 unsigned long size;
66 int err;
67
68 /* XXX fix me: should return equivalent of default_ldt[0] */
69 err = 0;
70 size = 8;
71 if (size > bytecount)
72 size = bytecount;
73
74 err = size;
75 if (clear_user(ptr, size))
76 err = -EFAULT;
77
78 return err;
79}
80
81static int
82write_ldt (void __user * ptr, unsigned long bytecount, int oldmode)
83{
84 struct ia32_user_desc ldt_info;
85 __u64 entry;
86 int ret;
87
88 if (bytecount != sizeof(ldt_info))
89 return -EINVAL;
90 if (copy_from_user(&ldt_info, ptr, sizeof(ldt_info)))
91 return -EFAULT;
92
93 if (ldt_info.entry_number >= IA32_LDT_ENTRIES)
94 return -EINVAL;
95 if (ldt_info.contents == 3) {
96 if (oldmode)
97 return -EINVAL;
98 if (ldt_info.seg_not_present == 0)
99 return -EINVAL;
100 }
101
102 if (ldt_info.base_addr == 0 && ldt_info.limit == 0
103 && (oldmode || (ldt_info.contents == 0 && ldt_info.read_exec_only == 1
104 && ldt_info.seg_32bit == 0 && ldt_info.limit_in_pages == 0
105 && ldt_info.seg_not_present == 1 && ldt_info.useable == 0)))
106 /* allow LDTs to be cleared by the user */
107 entry = 0;
108 else
109 /* we must set the "Accessed" bit as IVE doesn't emulate it */
110 entry = IA32_SEG_DESCRIPTOR(ldt_info.base_addr, ldt_info.limit,
111 (((ldt_info.read_exec_only ^ 1) << 1)
112 | (ldt_info.contents << 2)) | 1,
113 1, 3, ldt_info.seg_not_present ^ 1,
114 (oldmode ? 0 : ldt_info.useable),
115 ldt_info.seg_32bit,
116 ldt_info.limit_in_pages);
117 /*
118 * Install the new entry. We know we're accessing valid (mapped) user-level
119 * memory, but we still need to guard against out-of-memory, hence we must use
120 * put_user().
121 */
122 ret = __put_user(entry, (__u64 __user *) IA32_LDT_OFFSET + ldt_info.entry_number);
123 ia32_load_segment_descriptors(current);
124 return ret;
125}
126
127asmlinkage int
128sys32_modify_ldt (int func, unsigned int ptr, unsigned int bytecount)
129{
130 int ret = -ENOSYS;
131
132 switch (func) {
133 case 0:
134 ret = read_ldt(compat_ptr(ptr), bytecount);
135 break;
136 case 1:
137 ret = write_ldt(compat_ptr(ptr), bytecount, 1);
138 break;
139 case 2:
140 ret = read_default_ldt(compat_ptr(ptr), bytecount);
141 break;
142 case 0x11:
143 ret = write_ldt(compat_ptr(ptr), bytecount, 0);
144 break;
145 }
146 return ret;
147}