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_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)
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user