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 | ... | ... |