summary refs log tree commit diff stats
path: root/linux-user
diff options
context:
space:
mode:
Diffstat (limited to 'linux-user')
-rw-r--r--linux-user/main.c58
-rw-r--r--linux-user/qemu.h2
-rw-r--r--linux-user/syscall.c126
-rw-r--r--linux-user/syscall_types.h1
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)
-