diff options
author | bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162> | 2003-03-16 18:05:05 +0000 |
---|---|---|
committer | bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162> | 2003-03-16 18:05:05 +0000 |
commit | 6dbad63eef5947c6c8750e44f408138779b6d0bb (patch) | |
tree | e96572a87e9adb75efde4cd9213472f1903ec742 /linux-user | |
parent | 27362c82e9df7770554943ceda36ec4e5638c49d (diff) |
added minimal segment support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@28 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'linux-user')
-rw-r--r-- | linux-user/main.c | 58 | ||||
-rw-r--r-- | linux-user/qemu.h | 2 | ||||
-rw-r--r-- | linux-user/syscall.c | 126 | ||||
-rw-r--r-- | linux-user/syscall_types.h | 1 |
4 files changed, 163 insertions, 24 deletions
diff --git a/linux-user/main.c b/linux-user/main.c index 6aefe3afb6..b59c85d9c5 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -1,5 +1,5 @@ /* - * emu main + * gemu main * * Copyright (c) 2003 Fabrice Bellard * @@ -80,10 +80,28 @@ int cpu_x86_inl(int addr) return 0; } +/* default linux values for the selectors */ +#define __USER_CS (0x23) +#define __USER_DS (0x2B) -/* XXX: currently we use LDT entries */ -#define __USER_CS (0x23|4) -#define __USER_DS (0x2B|4) +void write_dt(void *ptr, unsigned long addr, unsigned long limit, + int seg32_bit) +{ + unsigned int e1, e2, limit_in_pages; + limit_in_pages = 0; + if (limit > 0xffff) { + limit = limit >> 12; + limit_in_pages = 1; + } + e1 = (addr << 16) | (limit & 0xffff); + e2 = ((addr >> 16) & 0xff) | (addr & 0xff000000) | (limit & 0x000f0000); + e2 |= limit_in_pages << 23; /* byte granularity */ + e2 |= seg32_bit << 22; /* 32 bit segment */ + stl((uint8_t *)ptr, e1); + stl((uint8_t *)ptr + 4, e2); +} + +uint64_t gdt_table[6]; void usage(void) { @@ -94,6 +112,8 @@ void usage(void) exit(1); } + + int main(int argc, char **argv) { const char *filename; @@ -149,6 +169,7 @@ int main(int argc, char **argv) env = cpu_x86_init(); + /* linux register setup */ env->regs[R_EAX] = regs->eax; env->regs[R_EBX] = regs->ebx; env->regs[R_ECX] = regs->ecx; @@ -157,23 +178,19 @@ int main(int argc, char **argv) env->regs[R_EDI] = regs->edi; env->regs[R_EBP] = regs->ebp; env->regs[R_ESP] = regs->esp; - env->segs[R_CS] = __USER_CS; - env->segs[R_DS] = __USER_DS; - env->segs[R_ES] = __USER_DS; - env->segs[R_SS] = __USER_DS; - env->segs[R_FS] = __USER_DS; - env->segs[R_GS] = __USER_DS; env->pc = regs->eip; -#if 0 - LDT[__USER_CS >> 3].w86Flags = DF_PRESENT | DF_PAGES | DF_32; - LDT[__USER_CS >> 3].dwSelLimit = 0xfffff; - LDT[__USER_CS >> 3].lpSelBase = NULL; - - LDT[__USER_DS >> 3].w86Flags = DF_PRESENT | DF_PAGES | DF_32; - LDT[__USER_DS >> 3].dwSelLimit = 0xfffff; - LDT[__USER_DS >> 3].lpSelBase = NULL; -#endif + /* linux segment setup */ + env->gdt.base = (void *)gdt_table; + env->gdt.limit = sizeof(gdt_table) - 1; + write_dt(&gdt_table[__USER_CS >> 3], 0, 0xffffffff, 1); + write_dt(&gdt_table[__USER_DS >> 3], 0, 0xffffffff, 1); + 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); + cpu_x86_load_seg(env, R_SS, __USER_DS); + cpu_x86_load_seg(env, R_FS, __USER_DS); + cpu_x86_load_seg(env, R_GS, __USER_DS); for(;;) { int err; @@ -186,7 +203,8 @@ int main(int argc, char **argv) if (pc[0] == 0xcd && pc[1] == 0x80) { /* syscall */ env->pc += 2; - env->regs[R_EAX] = do_syscall(env->regs[R_EAX], + env->regs[R_EAX] = do_syscall(env, + env->regs[R_EAX], env->regs[R_EBX], env->regs[R_ECX], env->regs[R_EDX], diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 0b9de6b3fe..4f09e6fde2 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -48,7 +48,7 @@ int elf_exec(const char * filename, char ** argv, char ** envp, void target_set_brk(char *new_brk); void syscall_init(void); -long do_syscall(int num, long arg1, long arg2, long arg3, +long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, long arg4, long arg5, long arg6); void gemu_log(const char *fmt, ...) __attribute__((format(printf,1,2))); diff --git a/linux-user/syscall.c b/linux-user/syscall.c index ac40cf19ef..9ed8daa0f8 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -69,6 +69,7 @@ struct dirent { #include "syscall_defs.h" #ifdef TARGET_I386 +#include "cpu-i386.h" #include "syscall-i386.h" #endif @@ -607,6 +608,124 @@ StructEntry struct_termios_def = { .align = { __alignof__(struct target_termios), __alignof__(struct host_termios) }, }; +#ifdef TARGET_I386 + +/* NOTE: there is really one LDT for all the threads */ +uint8_t *ldt_table; + +static int read_ldt(void *ptr, unsigned long bytecount) +{ + int size; + + if (!ldt_table) + return 0; + size = TARGET_LDT_ENTRIES * TARGET_LDT_ENTRY_SIZE; + if (size > bytecount) + size = bytecount; + memcpy(ptr, ldt_table, size); + return size; +} + +/* XXX: add locking support */ +static int write_ldt(CPUX86State *env, + void *ptr, unsigned long bytecount, int oldmode) +{ + struct target_modify_ldt_ldt_s ldt_info; + int seg_32bit, contents, read_exec_only, limit_in_pages; + int seg_not_present, useable; + uint32_t *lp, entry_1, entry_2; + + if (bytecount != sizeof(ldt_info)) + return -EINVAL; + memcpy(&ldt_info, ptr, sizeof(ldt_info)); + tswap32s(&ldt_info.entry_number); + tswapls((long *)&ldt_info.base_addr); + tswap32s(&ldt_info.limit); + tswap32s(&ldt_info.flags); + + if (ldt_info.entry_number >= TARGET_LDT_ENTRIES) + return -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; + + if (contents == 3) { + if (oldmode) + return -EINVAL; + if (seg_not_present == 0) + return -EINVAL; + } + /* allocate the LDT */ + if (!ldt_table) { + ldt_table = malloc(TARGET_LDT_ENTRIES * TARGET_LDT_ENTRY_SIZE); + if (!ldt_table) + return -ENOMEM; + memset(ldt_table, 0, TARGET_LDT_ENTRIES * TARGET_LDT_ENTRY_SIZE); + env->ldt.base = ldt_table; + env->ldt.limit = 0xffff; + } + + /* 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 (oldmode || + (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) | + 0x7000; + if (!oldmode) + entry_2 |= (useable << 20); + + /* Install the new entry ... */ +install: + lp = (uint32_t *)(ldt_table + (ldt_info.entry_number << 3)); + lp[0] = tswap32(entry_1); + lp[1] = tswap32(entry_2); + return 0; +} + +/* specific and weird i386 syscalls */ +int gemu_modify_ldt(CPUX86State *env, int func, void *ptr, unsigned long bytecount) +{ + int ret = -ENOSYS; + + switch (func) { + case 0: + ret = read_ldt(ptr, bytecount); + break; + case 1: + ret = write_ldt(env, ptr, bytecount, 1); + break; + case 0x11: + ret = write_ldt(env, ptr, bytecount, 0); + break; + } + return ret; +} +#endif + void syscall_init(void) { #define STRUCT(name, list...) thunk_register_struct(STRUCT_ ## name, #name, struct_ ## name ## _def); @@ -616,7 +735,7 @@ void syscall_init(void) #undef STRUCT_SPECIAL } -long do_syscall(int num, long arg1, long arg2, long arg3, +long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, long arg4, long arg5, long arg6) { long ret; @@ -1095,8 +1214,11 @@ long do_syscall(int num, long arg1, long arg2, long arg3, /* no need to transcode because we use the linux syscall */ ret = get_errno(sys_uname((struct new_utsname *)arg1)); break; +#ifdef TARGET_I386 case TARGET_NR_modify_ldt: - goto unimplemented; + ret = get_errno(gemu_modify_ldt(cpu_env, arg1, (void *)arg2, arg3)); + break; +#endif case TARGET_NR_adjtimex: goto unimplemented; case TARGET_NR_mprotect: diff --git a/linux-user/syscall_types.h b/linux-user/syscall_types.h index 63852d3af0..540114430b 100644 --- a/linux-user/syscall_types.h +++ b/linux-user/syscall_types.h @@ -61,4 +61,3 @@ STRUCT(cdrom_read_audio, STRUCT(hd_geometry, TYPE_CHAR, TYPE_CHAR, TYPE_SHORT, TYPE_ULONG) - |