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,6 +25,7 @@ struct target_pt_regs { | ||
25 | #define TARGET_LDT_ENTRIES 8192 | 25 | #define TARGET_LDT_ENTRIES 8192 |
26 | #define TARGET_LDT_ENTRY_SIZE 8 | 26 | #define TARGET_LDT_ENTRY_SIZE 8 |
27 | 27 | ||
28 | +#define TARGET_GDT_ENTRIES 9 | ||
28 | #define TARGET_GDT_ENTRY_TLS_ENTRIES 3 | 29 | #define TARGET_GDT_ENTRY_TLS_ENTRIES 3 |
29 | #define TARGET_GDT_ENTRY_TLS_MIN 6 | 30 | #define TARGET_GDT_ENTRY_TLS_MIN 6 |
30 | #define TARGET_GDT_ENTRY_TLS_MAX (TARGET_GDT_ENTRY_TLS_MIN + TARGET_GDT_ENTRY_TLS_ENTRIES - 1) | 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,7 +159,6 @@ static void set_gate(void *ptr, unsigned int type, unsigned int dpl, | ||
159 | p[1] = tswapl(e2); | 159 | p[1] = tswapl(e2); |
160 | } | 160 | } |
161 | 161 | ||
162 | -uint64_t gdt_table[6]; | ||
163 | uint64_t idt_table[256]; | 162 | uint64_t idt_table[256]; |
164 | 163 | ||
165 | /* only dpl matters as we do only user space emulation */ | 164 | /* only dpl matters as we do only user space emulation */ |
@@ -2129,14 +2128,18 @@ int main(int argc, char **argv) | @@ -2129,14 +2128,18 @@ int main(int argc, char **argv) | ||
2129 | set_idt(0x80, 3); | 2128 | set_idt(0x80, 3); |
2130 | 2129 | ||
2131 | /* linux segment setup */ | 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 | cpu_x86_load_seg(env, R_CS, __USER_CS); | 2143 | cpu_x86_load_seg(env, R_CS, __USER_CS); |
2141 | cpu_x86_load_seg(env, R_DS, __USER_DS); | 2144 | cpu_x86_load_seg(env, R_DS, __USER_DS); |
2142 | cpu_x86_load_seg(env, R_ES, __USER_DS); | 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,7 +2285,7 @@ static abi_long write_ldt(CPUX86State *env, | ||
2285 | struct target_modify_ldt_ldt_s ldt_info; | 2285 | struct target_modify_ldt_ldt_s ldt_info; |
2286 | struct target_modify_ldt_ldt_s *target_ldt_info; | 2286 | struct target_modify_ldt_ldt_s *target_ldt_info; |
2287 | int seg_32bit, contents, read_exec_only, limit_in_pages; | 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 | uint32_t *lp, entry_1, entry_2; | 2289 | uint32_t *lp, entry_1, entry_2; |
2290 | 2290 | ||
2291 | if (bytecount != sizeof(ldt_info)) | 2291 | if (bytecount != sizeof(ldt_info)) |
@@ -2306,7 +2306,11 @@ static abi_long write_ldt(CPUX86State *env, | @@ -2306,7 +2306,11 @@ static abi_long write_ldt(CPUX86State *env, | ||
2306 | limit_in_pages = (ldt_info.flags >> 4) & 1; | 2306 | limit_in_pages = (ldt_info.flags >> 4) & 1; |
2307 | seg_not_present = (ldt_info.flags >> 5) & 1; | 2307 | seg_not_present = (ldt_info.flags >> 5) & 1; |
2308 | useable = (ldt_info.flags >> 6) & 1; | 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 | if (contents == 3) { | 2314 | if (contents == 3) { |
2311 | if (oldmode) | 2315 | if (oldmode) |
2312 | return -TARGET_EINVAL; | 2316 | return -TARGET_EINVAL; |
@@ -2349,6 +2353,7 @@ static abi_long write_ldt(CPUX86State *env, | @@ -2349,6 +2353,7 @@ static abi_long write_ldt(CPUX86State *env, | ||
2349 | ((seg_not_present ^ 1) << 15) | | 2353 | ((seg_not_present ^ 1) << 15) | |
2350 | (seg_32bit << 22) | | 2354 | (seg_32bit << 22) | |
2351 | (limit_in_pages << 23) | | 2355 | (limit_in_pages << 23) | |
2356 | + (lm << 21) | | ||
2352 | 0x7000; | 2357 | 0x7000; |
2353 | if (!oldmode) | 2358 | if (!oldmode) |
2354 | entry_2 |= (useable << 20); | 2359 | entry_2 |= (useable << 20); |
@@ -2384,6 +2389,138 @@ abi_long do_modify_ldt(CPUX86State *env, int func, abi_ulong ptr, | @@ -2384,6 +2389,138 @@ abi_long do_modify_ldt(CPUX86State *env, int func, abi_ulong ptr, | ||
2384 | return ret; | 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 | #endif /* defined(TARGET_I386) */ | 2524 | #endif /* defined(TARGET_I386) */ |
2388 | 2525 | ||
2389 | /* this stack is the equivalent of the kernel stack associated with a | 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,18 +5273,25 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, | ||
5136 | #endif | 5273 | #endif |
5137 | #ifdef TARGET_NR_set_thread_area | 5274 | #ifdef TARGET_NR_set_thread_area |
5138 | case TARGET_NR_set_thread_area: | 5275 | case TARGET_NR_set_thread_area: |
5139 | -#ifdef TARGET_MIPS | 5276 | +#if defined(TARGET_MIPS) |
5140 | ((CPUMIPSState *) cpu_env)->tls_value = arg1; | 5277 | ((CPUMIPSState *) cpu_env)->tls_value = arg1; |
5141 | ret = 0; | 5278 | ret = 0; |
5142 | break; | 5279 | break; |
5280 | +#elif defined(TARGET_I386) && defined(TARGET_ABI32) | ||
5281 | + ret = do_set_thread_area(cpu_env, arg1); | ||
5282 | + break; | ||
5143 | #else | 5283 | #else |
5144 | goto unimplemented_nowarn; | 5284 | goto unimplemented_nowarn; |
5145 | #endif | 5285 | #endif |
5146 | #endif | 5286 | #endif |
5147 | #ifdef TARGET_NR_get_thread_area | 5287 | #ifdef TARGET_NR_get_thread_area |
5148 | case TARGET_NR_get_thread_area: | 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 | goto unimplemented_nowarn; | 5292 | goto unimplemented_nowarn; |
5150 | #endif | 5293 | #endif |
5294 | +#endif | ||
5151 | #ifdef TARGET_NR_getdomainname | 5295 | #ifdef TARGET_NR_getdomainname |
5152 | case TARGET_NR_getdomainname: | 5296 | case TARGET_NR_getdomainname: |
5153 | goto unimplemented_nowarn; | 5297 | goto unimplemented_nowarn; |
linux-user/x86_64/syscall.h
@@ -34,6 +34,7 @@ struct target_pt_regs { | @@ -34,6 +34,7 @@ struct target_pt_regs { | ||
34 | /* The size of each LDT entry. */ | 34 | /* The size of each LDT entry. */ |
35 | #define TARGET_LDT_ENTRY_SIZE 8 | 35 | #define TARGET_LDT_ENTRY_SIZE 8 |
36 | 36 | ||
37 | +#define TARGET_GDT_ENTRIES 16 | ||
37 | #define TARGET_GDT_ENTRY_TLS_ENTRIES 3 | 38 | #define TARGET_GDT_ENTRY_TLS_ENTRIES 3 |
38 | #define TARGET_GDT_ENTRY_TLS_MIN 12 | 39 | #define TARGET_GDT_ENTRY_TLS_MIN 12 |
39 | #define TARGET_GDT_ENTRY_TLS_MAX 14 | 40 | #define TARGET_GDT_ENTRY_TLS_MAX 14 |