Commit 8d18e89309f08efc44eb777631aba2fbee70375a
1 parent
4683b130
i386 TLS support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3644 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
4 changed files
with
161 additions
and
12 deletions
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 | ... | ... |