Commit 8d18e89309f08efc44eb777631aba2fbee70375a

Authored by bellard
1 parent 4683b130

i386 TLS support


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3644 c046a42c-6fe2-441c-8c8c-71466251a162
linux-user/i386/syscall.h
... ... @@ -25,6 +25,7 @@ struct target_pt_regs {
25 25 #define TARGET_LDT_ENTRIES 8192
26 26 #define TARGET_LDT_ENTRY_SIZE 8
27 27  
  28 +#define TARGET_GDT_ENTRIES 9
28 29 #define TARGET_GDT_ENTRY_TLS_ENTRIES 3
29 30 #define TARGET_GDT_ENTRY_TLS_MIN 6
30 31 #define TARGET_GDT_ENTRY_TLS_MAX (TARGET_GDT_ENTRY_TLS_MIN + TARGET_GDT_ENTRY_TLS_ENTRIES - 1)
... ...
linux-user/main.c
... ... @@ -159,7 +159,6 @@ static void set_gate(void *ptr, unsigned int type, unsigned int dpl,
159 159 p[1] = tswapl(e2);
160 160 }
161 161  
162   -uint64_t gdt_table[6];
163 162 uint64_t idt_table[256];
164 163  
165 164 /* only dpl matters as we do only user space emulation */
... ... @@ -2129,14 +2128,18 @@ int main(int argc, char **argv)
2129 2128 set_idt(0x80, 3);
2130 2129  
2131 2130 /* linux segment setup */
2132   - env->gdt.base = h2g(gdt_table);
2133   - env->gdt.limit = sizeof(gdt_table) - 1;
2134   - write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff,
2135   - DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
2136   - (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT));
2137   - write_dt(&gdt_table[__USER_DS >> 3], 0, 0xfffff,
2138   - DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
2139   - (3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT));
  2131 + {
  2132 + uint64_t *gdt_table;
  2133 + gdt_table = qemu_mallocz(sizeof(uint64_t) * TARGET_GDT_ENTRIES);
  2134 + env->gdt.base = h2g(gdt_table);
  2135 + env->gdt.limit = sizeof(uint64_t) * TARGET_GDT_ENTRIES - 1;
  2136 + write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff,
  2137 + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
  2138 + (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT));
  2139 + write_dt(&gdt_table[__USER_DS >> 3], 0, 0xfffff,
  2140 + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
  2141 + (3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT));
  2142 + }
2140 2143 cpu_x86_load_seg(env, R_CS, __USER_CS);
2141 2144 cpu_x86_load_seg(env, R_DS, __USER_DS);
2142 2145 cpu_x86_load_seg(env, R_ES, __USER_DS);
... ...
linux-user/syscall.c
... ... @@ -2285,7 +2285,7 @@ static abi_long write_ldt(CPUX86State *env,
2285 2285 struct target_modify_ldt_ldt_s ldt_info;
2286 2286 struct target_modify_ldt_ldt_s *target_ldt_info;
2287 2287 int seg_32bit, contents, read_exec_only, limit_in_pages;
2288   - int seg_not_present, useable;
  2288 + int seg_not_present, useable, lm;
2289 2289 uint32_t *lp, entry_1, entry_2;
2290 2290  
2291 2291 if (bytecount != sizeof(ldt_info))
... ... @@ -2306,7 +2306,11 @@ static abi_long write_ldt(CPUX86State *env,
2306 2306 limit_in_pages = (ldt_info.flags >> 4) & 1;
2307 2307 seg_not_present = (ldt_info.flags >> 5) & 1;
2308 2308 useable = (ldt_info.flags >> 6) & 1;
2309   -
  2309 +#ifdef TARGET_ABI32
  2310 + lm = 0;
  2311 +#else
  2312 + lm = (ldt_info.flags >> 7) & 1;
  2313 +#endif
2310 2314 if (contents == 3) {
2311 2315 if (oldmode)
2312 2316 return -TARGET_EINVAL;
... ... @@ -2349,6 +2353,7 @@ static abi_long write_ldt(CPUX86State *env,
2349 2353 ((seg_not_present ^ 1) << 15) |
2350 2354 (seg_32bit << 22) |
2351 2355 (limit_in_pages << 23) |
  2356 + (lm << 21) |
2352 2357 0x7000;
2353 2358 if (!oldmode)
2354 2359 entry_2 |= (useable << 20);
... ... @@ -2384,6 +2389,138 @@ abi_long do_modify_ldt(CPUX86State *env, int func, abi_ulong ptr,
2384 2389 return ret;
2385 2390 }
2386 2391  
  2392 +abi_long do_set_thread_area(CPUX86State *env, abi_ulong ptr)
  2393 +{
  2394 + uint64_t *gdt_table = g2h(env->gdt.base);
  2395 + struct target_modify_ldt_ldt_s ldt_info;
  2396 + struct target_modify_ldt_ldt_s *target_ldt_info;
  2397 + int seg_32bit, contents, read_exec_only, limit_in_pages;
  2398 + int seg_not_present, useable, lm;
  2399 + uint32_t *lp, entry_1, entry_2;
  2400 + int i;
  2401 +
  2402 + lock_user_struct(VERIFY_WRITE, target_ldt_info, ptr, 1);
  2403 + if (!target_ldt_info)
  2404 + return -TARGET_EFAULT;
  2405 + ldt_info.entry_number = tswap32(target_ldt_info->entry_number);
  2406 + ldt_info.base_addr = tswapl(target_ldt_info->base_addr);
  2407 + ldt_info.limit = tswap32(target_ldt_info->limit);
  2408 + ldt_info.flags = tswap32(target_ldt_info->flags);
  2409 + if (ldt_info.entry_number == -1) {
  2410 + for (i=TARGET_GDT_ENTRY_TLS_MIN; i<=TARGET_GDT_ENTRY_TLS_MAX; i++) {
  2411 + if (gdt_table[i] == 0) {
  2412 + ldt_info.entry_number = i;
  2413 + target_ldt_info->entry_number = tswap32(i);
  2414 + break;
  2415 + }
  2416 + }
  2417 + }
  2418 + unlock_user_struct(target_ldt_info, ptr, 1);
  2419 +
  2420 + if (ldt_info.entry_number < TARGET_GDT_ENTRY_TLS_MIN ||
  2421 + ldt_info.entry_number > TARGET_GDT_ENTRY_TLS_MAX)
  2422 + return -TARGET_EINVAL;
  2423 + seg_32bit = ldt_info.flags & 1;
  2424 + contents = (ldt_info.flags >> 1) & 3;
  2425 + read_exec_only = (ldt_info.flags >> 3) & 1;
  2426 + limit_in_pages = (ldt_info.flags >> 4) & 1;
  2427 + seg_not_present = (ldt_info.flags >> 5) & 1;
  2428 + useable = (ldt_info.flags >> 6) & 1;
  2429 +#ifdef TARGET_ABI32
  2430 + lm = 0;
  2431 +#else
  2432 + lm = (ldt_info.flags >> 7) & 1;
  2433 +#endif
  2434 +
  2435 + if (contents == 3) {
  2436 + if (seg_not_present == 0)
  2437 + return -TARGET_EINVAL;
  2438 + }
  2439 +
  2440 + /* NOTE: same code as Linux kernel */
  2441 + /* Allow LDTs to be cleared by the user. */
  2442 + if (ldt_info.base_addr == 0 && ldt_info.limit == 0) {
  2443 + if ((contents == 0 &&
  2444 + read_exec_only == 1 &&
  2445 + seg_32bit == 0 &&
  2446 + limit_in_pages == 0 &&
  2447 + seg_not_present == 1 &&
  2448 + useable == 0 )) {
  2449 + entry_1 = 0;
  2450 + entry_2 = 0;
  2451 + goto install;
  2452 + }
  2453 + }
  2454 +
  2455 + entry_1 = ((ldt_info.base_addr & 0x0000ffff) << 16) |
  2456 + (ldt_info.limit & 0x0ffff);
  2457 + entry_2 = (ldt_info.base_addr & 0xff000000) |
  2458 + ((ldt_info.base_addr & 0x00ff0000) >> 16) |
  2459 + (ldt_info.limit & 0xf0000) |
  2460 + ((read_exec_only ^ 1) << 9) |
  2461 + (contents << 10) |
  2462 + ((seg_not_present ^ 1) << 15) |
  2463 + (seg_32bit << 22) |
  2464 + (limit_in_pages << 23) |
  2465 + (useable << 20) |
  2466 + (lm << 21) |
  2467 + 0x7000;
  2468 +
  2469 + /* Install the new entry ... */
  2470 +install:
  2471 + lp = (uint32_t *)(gdt_table + ldt_info.entry_number);
  2472 + lp[0] = tswap32(entry_1);
  2473 + lp[1] = tswap32(entry_2);
  2474 + return 0;
  2475 +}
  2476 +
  2477 +abi_long do_get_thread_area(CPUX86State *env, abi_ulong ptr)
  2478 +{
  2479 + struct target_modify_ldt_ldt_s *target_ldt_info;
  2480 + uint64_t *gdt_table = g2h(env->gdt.base);
  2481 + uint32_t base_addr, limit, flags;
  2482 + int seg_32bit, contents, read_exec_only, limit_in_pages, idx;
  2483 + int seg_not_present, useable, lm;
  2484 + uint32_t *lp, entry_1, entry_2;
  2485 +
  2486 + lock_user_struct(VERIFY_WRITE, target_ldt_info, ptr, 1);
  2487 + if (!target_ldt_info)
  2488 + return -TARGET_EFAULT;
  2489 + idx = tswap32(target_ldt_info->entry_number);
  2490 + if (idx < TARGET_GDT_ENTRY_TLS_MIN ||
  2491 + idx > TARGET_GDT_ENTRY_TLS_MAX) {
  2492 + unlock_user_struct(target_ldt_info, ptr, 1);
  2493 + return -TARGET_EINVAL;
  2494 + }
  2495 + lp = (uint32_t *)(gdt_table + idx);
  2496 + entry_1 = tswap32(lp[0]);
  2497 + entry_2 = tswap32(lp[1]);
  2498 +
  2499 + read_exec_only = ((entry_2 >> 9) & 1) ^ 1;
  2500 + contents = (entry_2 >> 10) & 3;
  2501 + seg_not_present = ((entry_2 >> 15) & 1) ^ 1;
  2502 + seg_32bit = (entry_2 >> 22) & 1;
  2503 + limit_in_pages = (entry_2 >> 23) & 1;
  2504 + useable = (entry_2 >> 20) & 1;
  2505 +#ifdef TARGET_ABI32
  2506 + lm = 0;
  2507 +#else
  2508 + lm = (entry_2 >> 21) & 1;
  2509 +#endif
  2510 + flags = (seg_32bit << 0) | (contents << 1) |
  2511 + (read_exec_only << 3) | (limit_in_pages << 4) |
  2512 + (seg_not_present << 5) | (useable << 6) | (lm << 7);
  2513 + limit = (entry_1 & 0xffff) | (entry_2 & 0xf0000);
  2514 + base_addr = (entry_1 >> 16) |
  2515 + (entry_2 & 0xff000000) |
  2516 + ((entry_2 & 0xff) << 16);
  2517 + target_ldt_info->base_addr = tswapl(base_addr);
  2518 + target_ldt_info->limit = tswap32(limit);
  2519 + target_ldt_info->flags = tswap32(flags);
  2520 + unlock_user_struct(target_ldt_info, ptr, 1);
  2521 + return 0;
  2522 +}
  2523 +
2387 2524 #endif /* defined(TARGET_I386) */
2388 2525  
2389 2526 /* this stack is the equivalent of the kernel stack associated with a
... ... @@ -5136,18 +5273,25 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
5136 5273 #endif
5137 5274 #ifdef TARGET_NR_set_thread_area
5138 5275 case TARGET_NR_set_thread_area:
5139   -#ifdef TARGET_MIPS
  5276 +#if defined(TARGET_MIPS)
5140 5277 ((CPUMIPSState *) cpu_env)->tls_value = arg1;
5141 5278 ret = 0;
5142 5279 break;
  5280 +#elif defined(TARGET_I386) && defined(TARGET_ABI32)
  5281 + ret = do_set_thread_area(cpu_env, arg1);
  5282 + break;
5143 5283 #else
5144 5284 goto unimplemented_nowarn;
5145 5285 #endif
5146 5286 #endif
5147 5287 #ifdef TARGET_NR_get_thread_area
5148 5288 case TARGET_NR_get_thread_area:
  5289 +#if defined(TARGET_I386) && defined(TARGET_ABI32)
  5290 + ret = do_get_thread_area(cpu_env, arg1);
  5291 +#else
5149 5292 goto unimplemented_nowarn;
5150 5293 #endif
  5294 +#endif
5151 5295 #ifdef TARGET_NR_getdomainname
5152 5296 case TARGET_NR_getdomainname:
5153 5297 goto unimplemented_nowarn;
... ...
linux-user/x86_64/syscall.h
... ... @@ -34,6 +34,7 @@ struct target_pt_regs {
34 34 /* The size of each LDT entry. */
35 35 #define TARGET_LDT_ENTRY_SIZE 8
36 36  
  37 +#define TARGET_GDT_ENTRIES 16
37 38 #define TARGET_GDT_ENTRY_TLS_ENTRIES 3
38 39 #define TARGET_GDT_ENTRY_TLS_MIN 12
39 40 #define TARGET_GDT_ENTRY_TLS_MAX 14
... ...