i386 TLS support

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3644 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
bellard 2007-11-14 15:18:40 +00:00
parent 4683b130e5
commit 8d18e89309
4 changed files with 161 additions and 12 deletions

View File

@ -25,6 +25,7 @@ struct target_pt_regs {
#define TARGET_LDT_ENTRIES 8192
#define TARGET_LDT_ENTRY_SIZE 8
#define TARGET_GDT_ENTRIES 9
#define TARGET_GDT_ENTRY_TLS_ENTRIES 3
#define TARGET_GDT_ENTRY_TLS_MIN 6
#define TARGET_GDT_ENTRY_TLS_MAX (TARGET_GDT_ENTRY_TLS_MIN + TARGET_GDT_ENTRY_TLS_ENTRIES - 1)

View File

@ -159,7 +159,6 @@ static void set_gate(void *ptr, unsigned int type, unsigned int dpl,
p[1] = tswapl(e2);
}
uint64_t gdt_table[6];
uint64_t idt_table[256];
/* only dpl matters as we do only user space emulation */
@ -2129,14 +2128,18 @@ int main(int argc, char **argv)
set_idt(0x80, 3);
/* linux segment setup */
env->gdt.base = h2g(gdt_table);
env->gdt.limit = sizeof(gdt_table) - 1;
write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff,
DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
(3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT));
write_dt(&gdt_table[__USER_DS >> 3], 0, 0xfffff,
DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
(3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT));
{
uint64_t *gdt_table;
gdt_table = qemu_mallocz(sizeof(uint64_t) * TARGET_GDT_ENTRIES);
env->gdt.base = h2g(gdt_table);
env->gdt.limit = sizeof(uint64_t) * TARGET_GDT_ENTRIES - 1;
write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff,
DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
(3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT));
write_dt(&gdt_table[__USER_DS >> 3], 0, 0xfffff,
DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
(3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT));
}
cpu_x86_load_seg(env, R_CS, __USER_CS);
cpu_x86_load_seg(env, R_DS, __USER_DS);
cpu_x86_load_seg(env, R_ES, __USER_DS);

View File

@ -2285,7 +2285,7 @@ static abi_long write_ldt(CPUX86State *env,
struct target_modify_ldt_ldt_s ldt_info;
struct target_modify_ldt_ldt_s *target_ldt_info;
int seg_32bit, contents, read_exec_only, limit_in_pages;
int seg_not_present, useable;
int seg_not_present, useable, lm;
uint32_t *lp, entry_1, entry_2;
if (bytecount != sizeof(ldt_info))
@ -2306,7 +2306,11 @@ static abi_long write_ldt(CPUX86State *env,
limit_in_pages = (ldt_info.flags >> 4) & 1;
seg_not_present = (ldt_info.flags >> 5) & 1;
useable = (ldt_info.flags >> 6) & 1;
#ifdef TARGET_ABI32
lm = 0;
#else
lm = (ldt_info.flags >> 7) & 1;
#endif
if (contents == 3) {
if (oldmode)
return -TARGET_EINVAL;
@ -2349,6 +2353,7 @@ static abi_long write_ldt(CPUX86State *env,
((seg_not_present ^ 1) << 15) |
(seg_32bit << 22) |
(limit_in_pages << 23) |
(lm << 21) |
0x7000;
if (!oldmode)
entry_2 |= (useable << 20);
@ -2384,6 +2389,138 @@ abi_long do_modify_ldt(CPUX86State *env, int func, abi_ulong ptr,
return ret;
}
abi_long do_set_thread_area(CPUX86State *env, abi_ulong ptr)
{
uint64_t *gdt_table = g2h(env->gdt.base);
struct target_modify_ldt_ldt_s ldt_info;
struct target_modify_ldt_ldt_s *target_ldt_info;
int seg_32bit, contents, read_exec_only, limit_in_pages;
int seg_not_present, useable, lm;
uint32_t *lp, entry_1, entry_2;
int i;
lock_user_struct(VERIFY_WRITE, target_ldt_info, ptr, 1);
if (!target_ldt_info)
return -TARGET_EFAULT;
ldt_info.entry_number = tswap32(target_ldt_info->entry_number);
ldt_info.base_addr = tswapl(target_ldt_info->base_addr);
ldt_info.limit = tswap32(target_ldt_info->limit);
ldt_info.flags = tswap32(target_ldt_info->flags);
if (ldt_info.entry_number == -1) {
for (i=TARGET_GDT_ENTRY_TLS_MIN; i<=TARGET_GDT_ENTRY_TLS_MAX; i++) {
if (gdt_table[i] == 0) {
ldt_info.entry_number = i;
target_ldt_info->entry_number = tswap32(i);
break;
}
}
}
unlock_user_struct(target_ldt_info, ptr, 1);
if (ldt_info.entry_number < TARGET_GDT_ENTRY_TLS_MIN ||
ldt_info.entry_number > TARGET_GDT_ENTRY_TLS_MAX)
return -TARGET_EINVAL;
seg_32bit = ldt_info.flags & 1;
contents = (ldt_info.flags >> 1) & 3;
read_exec_only = (ldt_info.flags >> 3) & 1;
limit_in_pages = (ldt_info.flags >> 4) & 1;
seg_not_present = (ldt_info.flags >> 5) & 1;
useable = (ldt_info.flags >> 6) & 1;
#ifdef TARGET_ABI32
lm = 0;
#else
lm = (ldt_info.flags >> 7) & 1;
#endif
if (contents == 3) {
if (seg_not_present == 0)
return -TARGET_EINVAL;
}
/* NOTE: same code as Linux kernel */
/* Allow LDTs to be cleared by the user. */
if (ldt_info.base_addr == 0 && ldt_info.limit == 0) {
if ((contents == 0 &&
read_exec_only == 1 &&
seg_32bit == 0 &&
limit_in_pages == 0 &&
seg_not_present == 1 &&
useable == 0 )) {
entry_1 = 0;
entry_2 = 0;
goto install;
}
}
entry_1 = ((ldt_info.base_addr & 0x0000ffff) << 16) |
(ldt_info.limit & 0x0ffff);
entry_2 = (ldt_info.base_addr & 0xff000000) |
((ldt_info.base_addr & 0x00ff0000) >> 16) |
(ldt_info.limit & 0xf0000) |
((read_exec_only ^ 1) << 9) |
(contents << 10) |
((seg_not_present ^ 1) << 15) |
(seg_32bit << 22) |
(limit_in_pages << 23) |
(useable << 20) |
(lm << 21) |
0x7000;
/* Install the new entry ... */
install:
lp = (uint32_t *)(gdt_table + ldt_info.entry_number);
lp[0] = tswap32(entry_1);
lp[1] = tswap32(entry_2);
return 0;
}
abi_long do_get_thread_area(CPUX86State *env, abi_ulong ptr)
{
struct target_modify_ldt_ldt_s *target_ldt_info;
uint64_t *gdt_table = g2h(env->gdt.base);
uint32_t base_addr, limit, flags;
int seg_32bit, contents, read_exec_only, limit_in_pages, idx;
int seg_not_present, useable, lm;
uint32_t *lp, entry_1, entry_2;
lock_user_struct(VERIFY_WRITE, target_ldt_info, ptr, 1);
if (!target_ldt_info)
return -TARGET_EFAULT;
idx = tswap32(target_ldt_info->entry_number);
if (idx < TARGET_GDT_ENTRY_TLS_MIN ||
idx > TARGET_GDT_ENTRY_TLS_MAX) {
unlock_user_struct(target_ldt_info, ptr, 1);
return -TARGET_EINVAL;
}
lp = (uint32_t *)(gdt_table + idx);
entry_1 = tswap32(lp[0]);
entry_2 = tswap32(lp[1]);
read_exec_only = ((entry_2 >> 9) & 1) ^ 1;
contents = (entry_2 >> 10) & 3;
seg_not_present = ((entry_2 >> 15) & 1) ^ 1;
seg_32bit = (entry_2 >> 22) & 1;
limit_in_pages = (entry_2 >> 23) & 1;
useable = (entry_2 >> 20) & 1;
#ifdef TARGET_ABI32
lm = 0;
#else
lm = (entry_2 >> 21) & 1;
#endif
flags = (seg_32bit << 0) | (contents << 1) |
(read_exec_only << 3) | (limit_in_pages << 4) |
(seg_not_present << 5) | (useable << 6) | (lm << 7);
limit = (entry_1 & 0xffff) | (entry_2 & 0xf0000);
base_addr = (entry_1 >> 16) |
(entry_2 & 0xff000000) |
((entry_2 & 0xff) << 16);
target_ldt_info->base_addr = tswapl(base_addr);
target_ldt_info->limit = tswap32(limit);
target_ldt_info->flags = tswap32(flags);
unlock_user_struct(target_ldt_info, ptr, 1);
return 0;
}
#endif /* defined(TARGET_I386) */
/* 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,
#endif
#ifdef TARGET_NR_set_thread_area
case TARGET_NR_set_thread_area:
#ifdef TARGET_MIPS
#if defined(TARGET_MIPS)
((CPUMIPSState *) cpu_env)->tls_value = arg1;
ret = 0;
break;
#elif defined(TARGET_I386) && defined(TARGET_ABI32)
ret = do_set_thread_area(cpu_env, arg1);
break;
#else
goto unimplemented_nowarn;
#endif
#endif
#ifdef TARGET_NR_get_thread_area
case TARGET_NR_get_thread_area:
#if defined(TARGET_I386) && defined(TARGET_ABI32)
ret = do_get_thread_area(cpu_env, arg1);
#else
goto unimplemented_nowarn;
#endif
#endif
#ifdef TARGET_NR_getdomainname
case TARGET_NR_getdomainname:
goto unimplemented_nowarn;

View File

@ -34,6 +34,7 @@ struct target_pt_regs {
/* The size of each LDT entry. */
#define TARGET_LDT_ENTRY_SIZE 8
#define TARGET_GDT_ENTRIES 16
#define TARGET_GDT_ENTRY_TLS_ENTRIES 3
#define TARGET_GDT_ENTRY_TLS_MIN 12
#define TARGET_GDT_ENTRY_TLS_MAX 14