i386 TLS support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3644 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
4683b130e5
commit
8d18e89309
@ -25,6 +25,7 @@ struct target_pt_regs {
|
|||||||
#define TARGET_LDT_ENTRIES 8192
|
#define TARGET_LDT_ENTRIES 8192
|
||||||
#define TARGET_LDT_ENTRY_SIZE 8
|
#define TARGET_LDT_ENTRY_SIZE 8
|
||||||
|
|
||||||
|
#define TARGET_GDT_ENTRIES 9
|
||||||
#define TARGET_GDT_ENTRY_TLS_ENTRIES 3
|
#define TARGET_GDT_ENTRY_TLS_ENTRIES 3
|
||||||
#define TARGET_GDT_ENTRY_TLS_MIN 6
|
#define TARGET_GDT_ENTRY_TLS_MIN 6
|
||||||
#define TARGET_GDT_ENTRY_TLS_MAX (TARGET_GDT_ENTRY_TLS_MIN + TARGET_GDT_ENTRY_TLS_ENTRIES - 1)
|
#define TARGET_GDT_ENTRY_TLS_MAX (TARGET_GDT_ENTRY_TLS_MIN + TARGET_GDT_ENTRY_TLS_ENTRIES - 1)
|
||||||
|
@ -159,7 +159,6 @@ static void set_gate(void *ptr, unsigned int type, unsigned int dpl,
|
|||||||
p[1] = tswapl(e2);
|
p[1] = tswapl(e2);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t gdt_table[6];
|
|
||||||
uint64_t idt_table[256];
|
uint64_t idt_table[256];
|
||||||
|
|
||||||
/* only dpl matters as we do only user space emulation */
|
/* only dpl matters as we do only user space emulation */
|
||||||
@ -2129,14 +2128,18 @@ int main(int argc, char **argv)
|
|||||||
set_idt(0x80, 3);
|
set_idt(0x80, 3);
|
||||||
|
|
||||||
/* linux segment setup */
|
/* linux segment setup */
|
||||||
env->gdt.base = h2g(gdt_table);
|
{
|
||||||
env->gdt.limit = sizeof(gdt_table) - 1;
|
uint64_t *gdt_table;
|
||||||
write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff,
|
gdt_table = qemu_mallocz(sizeof(uint64_t) * TARGET_GDT_ENTRIES);
|
||||||
DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
|
env->gdt.base = h2g(gdt_table);
|
||||||
(3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT));
|
env->gdt.limit = sizeof(uint64_t) * TARGET_GDT_ENTRIES - 1;
|
||||||
write_dt(&gdt_table[__USER_DS >> 3], 0, 0xfffff,
|
write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff,
|
||||||
DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
|
DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
|
||||||
(3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT));
|
(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_CS, __USER_CS);
|
||||||
cpu_x86_load_seg(env, R_DS, __USER_DS);
|
cpu_x86_load_seg(env, R_DS, __USER_DS);
|
||||||
cpu_x86_load_seg(env, R_ES, __USER_DS);
|
cpu_x86_load_seg(env, R_ES, __USER_DS);
|
||||||
|
@ -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 ldt_info;
|
||||||
struct target_modify_ldt_ldt_s *target_ldt_info;
|
struct target_modify_ldt_ldt_s *target_ldt_info;
|
||||||
int seg_32bit, contents, read_exec_only, limit_in_pages;
|
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;
|
uint32_t *lp, entry_1, entry_2;
|
||||||
|
|
||||||
if (bytecount != sizeof(ldt_info))
|
if (bytecount != sizeof(ldt_info))
|
||||||
@ -2306,7 +2306,11 @@ static abi_long write_ldt(CPUX86State *env,
|
|||||||
limit_in_pages = (ldt_info.flags >> 4) & 1;
|
limit_in_pages = (ldt_info.flags >> 4) & 1;
|
||||||
seg_not_present = (ldt_info.flags >> 5) & 1;
|
seg_not_present = (ldt_info.flags >> 5) & 1;
|
||||||
useable = (ldt_info.flags >> 6) & 1;
|
useable = (ldt_info.flags >> 6) & 1;
|
||||||
|
#ifdef TARGET_ABI32
|
||||||
|
lm = 0;
|
||||||
|
#else
|
||||||
|
lm = (ldt_info.flags >> 7) & 1;
|
||||||
|
#endif
|
||||||
if (contents == 3) {
|
if (contents == 3) {
|
||||||
if (oldmode)
|
if (oldmode)
|
||||||
return -TARGET_EINVAL;
|
return -TARGET_EINVAL;
|
||||||
@ -2349,6 +2353,7 @@ static abi_long write_ldt(CPUX86State *env,
|
|||||||
((seg_not_present ^ 1) << 15) |
|
((seg_not_present ^ 1) << 15) |
|
||||||
(seg_32bit << 22) |
|
(seg_32bit << 22) |
|
||||||
(limit_in_pages << 23) |
|
(limit_in_pages << 23) |
|
||||||
|
(lm << 21) |
|
||||||
0x7000;
|
0x7000;
|
||||||
if (!oldmode)
|
if (!oldmode)
|
||||||
entry_2 |= (useable << 20);
|
entry_2 |= (useable << 20);
|
||||||
@ -2384,6 +2389,138 @@ abi_long do_modify_ldt(CPUX86State *env, int func, abi_ulong ptr,
|
|||||||
return ret;
|
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) */
|
#endif /* defined(TARGET_I386) */
|
||||||
|
|
||||||
/* this stack is the equivalent of the kernel stack associated with a
|
/* 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
|
#endif
|
||||||
#ifdef TARGET_NR_set_thread_area
|
#ifdef TARGET_NR_set_thread_area
|
||||||
case TARGET_NR_set_thread_area:
|
case TARGET_NR_set_thread_area:
|
||||||
#ifdef TARGET_MIPS
|
#if defined(TARGET_MIPS)
|
||||||
((CPUMIPSState *) cpu_env)->tls_value = arg1;
|
((CPUMIPSState *) cpu_env)->tls_value = arg1;
|
||||||
ret = 0;
|
ret = 0;
|
||||||
break;
|
break;
|
||||||
|
#elif defined(TARGET_I386) && defined(TARGET_ABI32)
|
||||||
|
ret = do_set_thread_area(cpu_env, arg1);
|
||||||
|
break;
|
||||||
#else
|
#else
|
||||||
goto unimplemented_nowarn;
|
goto unimplemented_nowarn;
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#ifdef TARGET_NR_get_thread_area
|
#ifdef TARGET_NR_get_thread_area
|
||||||
case 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;
|
goto unimplemented_nowarn;
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
#ifdef TARGET_NR_getdomainname
|
#ifdef TARGET_NR_getdomainname
|
||||||
case TARGET_NR_getdomainname:
|
case TARGET_NR_getdomainname:
|
||||||
goto unimplemented_nowarn;
|
goto unimplemented_nowarn;
|
||||||
|
@ -34,6 +34,7 @@ struct target_pt_regs {
|
|||||||
/* The size of each LDT entry. */
|
/* The size of each LDT entry. */
|
||||||
#define TARGET_LDT_ENTRY_SIZE 8
|
#define TARGET_LDT_ENTRY_SIZE 8
|
||||||
|
|
||||||
|
#define TARGET_GDT_ENTRIES 16
|
||||||
#define TARGET_GDT_ENTRY_TLS_ENTRIES 3
|
#define TARGET_GDT_ENTRY_TLS_ENTRIES 3
|
||||||
#define TARGET_GDT_ENTRY_TLS_MIN 12
|
#define TARGET_GDT_ENTRY_TLS_MIN 12
|
||||||
#define TARGET_GDT_ENTRY_TLS_MAX 14
|
#define TARGET_GDT_ENTRY_TLS_MAX 14
|
||||||
|
Loading…
x
Reference in New Issue
Block a user