blob: 02806af4e57927fdac674f5166d58f89dee5ba27 [file] [log] [blame]
sewardj92a59562002-09-30 00:53:10 +00001
2/*--------------------------------------------------------------------*/
3/*--- Simulation of Local Descriptor Tables vg_ldt.c ---*/
4/*--------------------------------------------------------------------*/
5
6/*
7 This file is part of Valgrind, an x86 protected-mode emulator
8 designed for debugging and profiling binaries on x86-Unixes.
9
10 Copyright (C) 2000-2002 Julian Seward
11 jseward@acm.org
12
13 This program is free software; you can redistribute it and/or
14 modify it under the terms of the GNU General Public License as
15 published by the Free Software Foundation; either version 2 of the
16 License, or (at your option) any later version.
17
18 This program is distributed in the hope that it will be useful, but
19 WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
26 02111-1307, USA.
27
28 The GNU General Public License is contained in the file COPYING.
29*/
30
31#include "vg_include.h"
32/* Allocate and deallocate LDTs for threads. */
33
34/* Create an LDT. If the parent_ldt is NULL, zero out the
35 new one. If non-NULL, copy the parent. */
36VgLdtEntry* VG_(allocate_LDT_for_thread) ( VgLdtEntry* parent_ldt )
37{
38 UInt nbytes, i;
39 VgLdtEntry* ldt;
40
njn854e7152002-09-30 10:47:35 +000041 if (VG_(clo_verbosity) > 1)
42 VG_(printf)("allocate_LDT_for_thread: parent = %p\n", parent_ldt );
sewardj92a59562002-09-30 00:53:10 +000043 vg_assert(VG_LDT_ENTRY_SIZE == sizeof(VgLdtEntry));
44 nbytes = VG_M_LDT_ENTRIES * VG_LDT_ENTRY_SIZE;
45
46 if (parent_ldt == NULL) {
47 /* Allocate a new zeroed-out one. */
48 ldt = (VgLdtEntry*)VG_(arena_calloc)(VG_AR_CORE, nbytes, 1);
49 } else {
50 ldt = (VgLdtEntry*)VG_(arena_malloc)(VG_AR_CORE, nbytes);
51 for (i = 0; i < VG_M_LDT_ENTRIES; i++)
52 ldt[i] = parent_ldt[i];
53 }
54
55 return ldt;
56}
57
58/* Free an LDT created by the above function. */
59void VG_(deallocate_LDT_for_thread) ( VgLdtEntry* ldt )
60{
njn854e7152002-09-30 10:47:35 +000061 if (VG_(clo_verbosity) > 1)
62 VG_(printf)("deallocate_LDT_for_thread: ldt = %p\n", ldt );
sewardj92a59562002-09-30 00:53:10 +000063 if (ldt != NULL)
64 VG_(arena_free)(VG_AR_CORE, ldt);
65}
66
67
68
69/* Fish the base field out of an VgLdtEntry. This is the only part we
70 are particularly interested in. */
71
72static
73void *wine_ldt_get_base( const VgLdtEntry *ent )
74{
75 return (void *)(ent->LdtEnt.Bits.BaseLow |
76 ((unsigned long)ent->LdtEnt.Bits.BaseMid) << 16 |
77 ((unsigned long)ent->LdtEnt.Bits.BaseHi) << 24);
78}
79
80#if 0
81inline static unsigned int wine_ldt_get_limit( const VgLdtEntry *ent )
82{
83 unsigned int limit = ent->LimitLow | (ent->HighWord.Bits.LimitHi << 16);
84 if (ent->HighWord.Bits.Granularity) limit = (limit << 12) | 0xfff;
85 return limit;
86}
87#endif
88
89
90
91/* Translate a struct modify_ldt_ldt_s to an VgLdtEntry, using the
92 Linux kernel's logic (cut-n-paste of code in linux/kernel/ldt.c). */
93
94static
95void translate_to_hw_format ( /* IN */ struct vki_modify_ldt_ldt_s* inn,
96 /* OUT */ VgLdtEntry* out,
97 Int oldmode )
98{
99 UInt entry_1, entry_2;
100
101 /* Allow LDTs to be cleared by the user. */
102 if (inn->base_addr == 0 && inn->limit == 0) {
103 if (oldmode ||
104 (inn->contents == 0 &&
105 inn->read_exec_only == 1 &&
106 inn->seg_32bit == 0 &&
107 inn->limit_in_pages == 0 &&
108 inn->seg_not_present == 1 &&
109 inn->useable == 0 )) {
110 entry_1 = 0;
111 entry_2 = 0;
112 goto install;
113 }
114 }
115
116 entry_1 = ((inn->base_addr & 0x0000ffff) << 16) |
117 (inn->limit & 0x0ffff);
118 entry_2 = (inn->base_addr & 0xff000000) |
119 ((inn->base_addr & 0x00ff0000) >> 16) |
120 (inn->limit & 0xf0000) |
121 ((inn->read_exec_only ^ 1) << 9) |
122 (inn->contents << 10) |
123 ((inn->seg_not_present ^ 1) << 15) |
124 (inn->seg_32bit << 22) |
125 (inn->limit_in_pages << 23) |
126 0x7000;
127 if (!oldmode)
128 entry_2 |= (inn->useable << 20);
129
130 /* Install the new entry ... */
131 install:
132 out->LdtEnt.Words.word1 = entry_1;
133 out->LdtEnt.Words.word2 = entry_2;
134}
135
136
137/*
138 * linux/kernel/ldt.c
139 *
140 * Copyright (C) 1992 Krishna Balasubramanian and Linus Torvalds
141 * Copyright (C) 1999 Ingo Molnar <mingo@redhat.com>
142 */
143
144/*
145 * read_ldt() is not really atomic - this is not a problem since
146 * synchronization of reads and writes done to the LDT has to be
147 * assured by user-space anyway. Writes are atomic, to protect
148 * the security checks done on new descriptors.
149 */
150static
151Int read_ldt ( ThreadId tid, UChar* ptr, UInt bytecount )
152{
153 Int err;
154 UInt i, size;
155 Char* ldt;
156
njn854e7152002-09-30 10:47:35 +0000157 if (VG_(clo_verbosity) > 1)
158 VG_(printf)("read_ldt: tid = %d, ptr = %p, bytecount = %d\n",
159 tid, ptr, bytecount );
sewardj92a59562002-09-30 00:53:10 +0000160
161 ldt = (Char*)(VG_(threads)[tid].ldt);
162 err = 0;
163 if (ldt == NULL)
164 /* LDT not allocated, meaning all entries are null */
165 goto out;
166
167 size = VG_M_LDT_ENTRIES * VG_LDT_ENTRY_SIZE;
168 if (size > bytecount)
169 size = bytecount;
170
171 err = size;
172 for (i = 0; i < size; i++)
173 ptr[i] = ldt[i];
174
175 out:
176 return err;
177}
178
179
180static
181Int write_ldt ( ThreadId tid, void* ptr, UInt bytecount, Int oldmode )
182{
183 Int error;
184 VgLdtEntry* ldt;
185 struct vki_modify_ldt_ldt_s* ldt_info;
186
njn854e7152002-09-30 10:47:35 +0000187 if (VG_(clo_verbosity) > 1)
188 VG_(printf)("write_ldt: tid = %d, ptr = %p, "
189 "bytecount = %d, oldmode = %d\n",
190 tid, ptr, bytecount, oldmode );
sewardj92a59562002-09-30 00:53:10 +0000191
192 ldt = VG_(threads)[tid].ldt;
193 ldt_info = (struct vki_modify_ldt_ldt_s*)ptr;
194
195 error = -VKI_EINVAL;
196 if (bytecount != sizeof(struct vki_modify_ldt_ldt_s))
197 goto out;
198
199 error = -VKI_EINVAL;
200 if (ldt_info->entry_number >= VG_M_LDT_ENTRIES)
201 goto out;
202 if (ldt_info->contents == 3) {
203 if (oldmode)
204 goto out;
205 if (ldt_info->seg_not_present == 0)
206 goto out;
207 }
208
209 /* If this thread doesn't have an LDT, we'd better allocate it
210 now. */
211 if (ldt == NULL) {
212 ldt = VG_(allocate_LDT_for_thread)( NULL );
213 VG_(threads)[tid].ldt = ldt;
214 }
215
216 /* Install the new entry ... */
217 translate_to_hw_format ( ldt_info, &ldt[ldt_info->entry_number], oldmode );
218 error = 0;
219
220 out:
221 return error;
222}
223
224
225Int VG_(sys_modify_ldt) ( ThreadId tid,
226 Int func, void* ptr, UInt bytecount )
227{
228 Int ret = -VKI_ENOSYS;
229
230 switch (func) {
231 case 0:
232 ret = read_ldt(tid, ptr, bytecount);
233 break;
234 case 1:
235 ret = write_ldt(tid, ptr, bytecount, 1);
236 break;
237 case 2:
238 VG_(unimplemented)("sys_modify_ldt: func == 2");
239 /* god knows what this is about */
240 /* ret = read_default_ldt(ptr, bytecount); */
241 /*UNREACHED*/
242 break;
243 case 0x11:
244 ret = write_ldt(tid, ptr, bytecount, 0);
245 break;
246 }
247 return ret;
248}
249
250
251/*--------------------------------------------------------------------*/
252/*--- end vg_ldt.c ---*/
253/*--------------------------------------------------------------------*/