sewardj | 92a5956 | 2002-09-30 00:53:10 +0000 | [diff] [blame] | 1 | |
| 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. */ |
| 36 | VgLdtEntry* VG_(allocate_LDT_for_thread) ( VgLdtEntry* parent_ldt ) |
| 37 | { |
| 38 | UInt nbytes, i; |
| 39 | VgLdtEntry* ldt; |
| 40 | |
njn | 854e715 | 2002-09-30 10:47:35 +0000 | [diff] [blame] | 41 | if (VG_(clo_verbosity) > 1) |
| 42 | VG_(printf)("allocate_LDT_for_thread: parent = %p\n", parent_ldt ); |
sewardj | 92a5956 | 2002-09-30 00:53:10 +0000 | [diff] [blame] | 43 | 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. */ |
| 59 | void VG_(deallocate_LDT_for_thread) ( VgLdtEntry* ldt ) |
| 60 | { |
njn | 854e715 | 2002-09-30 10:47:35 +0000 | [diff] [blame] | 61 | if (VG_(clo_verbosity) > 1) |
| 62 | VG_(printf)("deallocate_LDT_for_thread: ldt = %p\n", ldt ); |
sewardj | 92a5956 | 2002-09-30 00:53:10 +0000 | [diff] [blame] | 63 | 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 | |
| 72 | static |
| 73 | void *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 |
| 81 | inline 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 | |
| 94 | static |
| 95 | void 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 | */ |
| 150 | static |
| 151 | Int read_ldt ( ThreadId tid, UChar* ptr, UInt bytecount ) |
| 152 | { |
| 153 | Int err; |
| 154 | UInt i, size; |
| 155 | Char* ldt; |
| 156 | |
njn | 854e715 | 2002-09-30 10:47:35 +0000 | [diff] [blame] | 157 | if (VG_(clo_verbosity) > 1) |
| 158 | VG_(printf)("read_ldt: tid = %d, ptr = %p, bytecount = %d\n", |
| 159 | tid, ptr, bytecount ); |
sewardj | 92a5956 | 2002-09-30 00:53:10 +0000 | [diff] [blame] | 160 | |
| 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 | |
| 180 | static |
| 181 | Int 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 | |
njn | 854e715 | 2002-09-30 10:47:35 +0000 | [diff] [blame] | 187 | 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 ); |
sewardj | 92a5956 | 2002-09-30 00:53:10 +0000 | [diff] [blame] | 191 | |
| 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 | |
| 225 | Int 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 | /*--------------------------------------------------------------------*/ |