added minimal segment support

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@28 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
bellard
2003-03-16 18:05:05 +00:00
parent 27362c82e9
commit 6dbad63eef
12 changed files with 550 additions and 103 deletions

View File

@ -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],

View File

@ -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)));

View File

@ -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:

View File

@ -61,4 +61,3 @@ STRUCT(cdrom_read_audio,
STRUCT(hd_geometry,
TYPE_CHAR, TYPE_CHAR, TYPE_SHORT, TYPE_ULONG)