blob: a431ac9222b96c3e22c9feea4ff51606b7c216bf [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
41 VG_(printf)("allocate_LDT_for_thread: parent = %p\n", parent_ldt );
42 vg_assert(VG_LDT_ENTRY_SIZE == sizeof(VgLdtEntry));
43 nbytes = VG_M_LDT_ENTRIES * VG_LDT_ENTRY_SIZE;
44
45 if (parent_ldt == NULL) {
46 /* Allocate a new zeroed-out one. */
47 ldt = (VgLdtEntry*)VG_(arena_calloc)(VG_AR_CORE, nbytes, 1);
48 } else {
49 ldt = (VgLdtEntry*)VG_(arena_malloc)(VG_AR_CORE, nbytes);
50 for (i = 0; i < VG_M_LDT_ENTRIES; i++)
51 ldt[i] = parent_ldt[i];
52 }
53
54 return ldt;
55}
56
57/* Free an LDT created by the above function. */
58void VG_(deallocate_LDT_for_thread) ( VgLdtEntry* ldt )
59{
60 VG_(printf)("deallocate_LDT_for_thread: ldt = %p\n", ldt );
61 if (ldt != NULL)
62 VG_(arena_free)(VG_AR_CORE, ldt);
63}
64
65
66
67/* Fish the base field out of an VgLdtEntry. This is the only part we
68 are particularly interested in. */
69
70static
71void *wine_ldt_get_base( const VgLdtEntry *ent )
72{
73 return (void *)(ent->LdtEnt.Bits.BaseLow |
74 ((unsigned long)ent->LdtEnt.Bits.BaseMid) << 16 |
75 ((unsigned long)ent->LdtEnt.Bits.BaseHi) << 24);
76}
77
78#if 0
79inline static unsigned int wine_ldt_get_limit( const VgLdtEntry *ent )
80{
81 unsigned int limit = ent->LimitLow | (ent->HighWord.Bits.LimitHi << 16);
82 if (ent->HighWord.Bits.Granularity) limit = (limit << 12) | 0xfff;
83 return limit;
84}
85#endif
86
87
88
89/* Translate a struct modify_ldt_ldt_s to an VgLdtEntry, using the
90 Linux kernel's logic (cut-n-paste of code in linux/kernel/ldt.c). */
91
92static
93void translate_to_hw_format ( /* IN */ struct vki_modify_ldt_ldt_s* inn,
94 /* OUT */ VgLdtEntry* out,
95 Int oldmode )
96{
97 UInt entry_1, entry_2;
98
99 /* Allow LDTs to be cleared by the user. */
100 if (inn->base_addr == 0 && inn->limit == 0) {
101 if (oldmode ||
102 (inn->contents == 0 &&
103 inn->read_exec_only == 1 &&
104 inn->seg_32bit == 0 &&
105 inn->limit_in_pages == 0 &&
106 inn->seg_not_present == 1 &&
107 inn->useable == 0 )) {
108 entry_1 = 0;
109 entry_2 = 0;
110 goto install;
111 }
112 }
113
114 entry_1 = ((inn->base_addr & 0x0000ffff) << 16) |
115 (inn->limit & 0x0ffff);
116 entry_2 = (inn->base_addr & 0xff000000) |
117 ((inn->base_addr & 0x00ff0000) >> 16) |
118 (inn->limit & 0xf0000) |
119 ((inn->read_exec_only ^ 1) << 9) |
120 (inn->contents << 10) |
121 ((inn->seg_not_present ^ 1) << 15) |
122 (inn->seg_32bit << 22) |
123 (inn->limit_in_pages << 23) |
124 0x7000;
125 if (!oldmode)
126 entry_2 |= (inn->useable << 20);
127
128 /* Install the new entry ... */
129 install:
130 out->LdtEnt.Words.word1 = entry_1;
131 out->LdtEnt.Words.word2 = entry_2;
132}
133
134
135/*
136 * linux/kernel/ldt.c
137 *
138 * Copyright (C) 1992 Krishna Balasubramanian and Linus Torvalds
139 * Copyright (C) 1999 Ingo Molnar <mingo@redhat.com>
140 */
141
142/*
143 * read_ldt() is not really atomic - this is not a problem since
144 * synchronization of reads and writes done to the LDT has to be
145 * assured by user-space anyway. Writes are atomic, to protect
146 * the security checks done on new descriptors.
147 */
148static
149Int read_ldt ( ThreadId tid, UChar* ptr, UInt bytecount )
150{
151 Int err;
152 UInt i, size;
153 Char* ldt;
154
155 VG_(printf)("read_ldt: tid = %d, ptr = %p, bytecount = %d\n",
156 tid, ptr, bytecount );
157
158 ldt = (Char*)(VG_(threads)[tid].ldt);
159 err = 0;
160 if (ldt == NULL)
161 /* LDT not allocated, meaning all entries are null */
162 goto out;
163
164 size = VG_M_LDT_ENTRIES * VG_LDT_ENTRY_SIZE;
165 if (size > bytecount)
166 size = bytecount;
167
168 err = size;
169 for (i = 0; i < size; i++)
170 ptr[i] = ldt[i];
171
172 out:
173 return err;
174}
175
176
177static
178Int write_ldt ( ThreadId tid, void* ptr, UInt bytecount, Int oldmode )
179{
180 Int error;
181 VgLdtEntry* ldt;
182 struct vki_modify_ldt_ldt_s* ldt_info;
183
184 VG_(printf)("write_ldt: tid = %d, ptr = %p, "
185 "bytecount = %d, oldmode = %d\n",
186 tid, ptr, bytecount, oldmode );
187
188 ldt = VG_(threads)[tid].ldt;
189 ldt_info = (struct vki_modify_ldt_ldt_s*)ptr;
190
191 error = -VKI_EINVAL;
192 if (bytecount != sizeof(struct vki_modify_ldt_ldt_s))
193 goto out;
194
195 error = -VKI_EINVAL;
196 if (ldt_info->entry_number >= VG_M_LDT_ENTRIES)
197 goto out;
198 if (ldt_info->contents == 3) {
199 if (oldmode)
200 goto out;
201 if (ldt_info->seg_not_present == 0)
202 goto out;
203 }
204
205 /* If this thread doesn't have an LDT, we'd better allocate it
206 now. */
207 if (ldt == NULL) {
208 ldt = VG_(allocate_LDT_for_thread)( NULL );
209 VG_(threads)[tid].ldt = ldt;
210 }
211
212 /* Install the new entry ... */
213 translate_to_hw_format ( ldt_info, &ldt[ldt_info->entry_number], oldmode );
214 error = 0;
215
216 out:
217 return error;
218}
219
220
221Int VG_(sys_modify_ldt) ( ThreadId tid,
222 Int func, void* ptr, UInt bytecount )
223{
224 Int ret = -VKI_ENOSYS;
225
226 switch (func) {
227 case 0:
228 ret = read_ldt(tid, ptr, bytecount);
229 break;
230 case 1:
231 ret = write_ldt(tid, ptr, bytecount, 1);
232 break;
233 case 2:
234 VG_(unimplemented)("sys_modify_ldt: func == 2");
235 /* god knows what this is about */
236 /* ret = read_default_ldt(ptr, bytecount); */
237 /*UNREACHED*/
238 break;
239 case 0x11:
240 ret = write_ldt(tid, ptr, bytecount, 0);
241 break;
242 }
243 return ret;
244}
245
246
247/*--------------------------------------------------------------------*/
248/*--- end vg_ldt.c ---*/
249/*--------------------------------------------------------------------*/