Revision 6dbad63e linux-user/syscall.c
b/linux-user/syscall.c | ||
---|---|---|
69 | 69 |
#include "syscall_defs.h" |
70 | 70 |
|
71 | 71 |
#ifdef TARGET_I386 |
72 |
#include "cpu-i386.h" |
|
72 | 73 |
#include "syscall-i386.h" |
73 | 74 |
#endif |
74 | 75 |
|
... | ... | |
607 | 608 |
.align = { __alignof__(struct target_termios), __alignof__(struct host_termios) }, |
608 | 609 |
}; |
609 | 610 |
|
611 |
#ifdef TARGET_I386 |
|
612 |
|
|
613 |
/* NOTE: there is really one LDT for all the threads */ |
|
614 |
uint8_t *ldt_table; |
|
615 |
|
|
616 |
static int read_ldt(void *ptr, unsigned long bytecount) |
|
617 |
{ |
|
618 |
int size; |
|
619 |
|
|
620 |
if (!ldt_table) |
|
621 |
return 0; |
|
622 |
size = TARGET_LDT_ENTRIES * TARGET_LDT_ENTRY_SIZE; |
|
623 |
if (size > bytecount) |
|
624 |
size = bytecount; |
|
625 |
memcpy(ptr, ldt_table, size); |
|
626 |
return size; |
|
627 |
} |
|
628 |
|
|
629 |
/* XXX: add locking support */ |
|
630 |
static int write_ldt(CPUX86State *env, |
|
631 |
void *ptr, unsigned long bytecount, int oldmode) |
|
632 |
{ |
|
633 |
struct target_modify_ldt_ldt_s ldt_info; |
|
634 |
int seg_32bit, contents, read_exec_only, limit_in_pages; |
|
635 |
int seg_not_present, useable; |
|
636 |
uint32_t *lp, entry_1, entry_2; |
|
637 |
|
|
638 |
if (bytecount != sizeof(ldt_info)) |
|
639 |
return -EINVAL; |
|
640 |
memcpy(&ldt_info, ptr, sizeof(ldt_info)); |
|
641 |
tswap32s(&ldt_info.entry_number); |
|
642 |
tswapls((long *)&ldt_info.base_addr); |
|
643 |
tswap32s(&ldt_info.limit); |
|
644 |
tswap32s(&ldt_info.flags); |
|
645 |
|
|
646 |
if (ldt_info.entry_number >= TARGET_LDT_ENTRIES) |
|
647 |
return -EINVAL; |
|
648 |
seg_32bit = ldt_info.flags & 1; |
|
649 |
contents = (ldt_info.flags >> 1) & 3; |
|
650 |
read_exec_only = (ldt_info.flags >> 3) & 1; |
|
651 |
limit_in_pages = (ldt_info.flags >> 4) & 1; |
|
652 |
seg_not_present = (ldt_info.flags >> 5) & 1; |
|
653 |
useable = (ldt_info.flags >> 6) & 1; |
|
654 |
|
|
655 |
if (contents == 3) { |
|
656 |
if (oldmode) |
|
657 |
return -EINVAL; |
|
658 |
if (seg_not_present == 0) |
|
659 |
return -EINVAL; |
|
660 |
} |
|
661 |
/* allocate the LDT */ |
|
662 |
if (!ldt_table) { |
|
663 |
ldt_table = malloc(TARGET_LDT_ENTRIES * TARGET_LDT_ENTRY_SIZE); |
|
664 |
if (!ldt_table) |
|
665 |
return -ENOMEM; |
|
666 |
memset(ldt_table, 0, TARGET_LDT_ENTRIES * TARGET_LDT_ENTRY_SIZE); |
|
667 |
env->ldt.base = ldt_table; |
|
668 |
env->ldt.limit = 0xffff; |
|
669 |
} |
|
670 |
|
|
671 |
/* NOTE: same code as Linux kernel */ |
|
672 |
/* Allow LDTs to be cleared by the user. */ |
|
673 |
if (ldt_info.base_addr == 0 && ldt_info.limit == 0) { |
|
674 |
if (oldmode || |
|
675 |
(contents == 0 && |
|
676 |
read_exec_only == 1 && |
|
677 |
seg_32bit == 0 && |
|
678 |
limit_in_pages == 0 && |
|
679 |
seg_not_present == 1 && |
|
680 |
useable == 0 )) { |
|
681 |
entry_1 = 0; |
|
682 |
entry_2 = 0; |
|
683 |
goto install; |
|
684 |
} |
|
685 |
} |
|
686 |
|
|
687 |
entry_1 = ((ldt_info.base_addr & 0x0000ffff) << 16) | |
|
688 |
(ldt_info.limit & 0x0ffff); |
|
689 |
entry_2 = (ldt_info.base_addr & 0xff000000) | |
|
690 |
((ldt_info.base_addr & 0x00ff0000) >> 16) | |
|
691 |
(ldt_info.limit & 0xf0000) | |
|
692 |
((read_exec_only ^ 1) << 9) | |
|
693 |
(contents << 10) | |
|
694 |
((seg_not_present ^ 1) << 15) | |
|
695 |
(seg_32bit << 22) | |
|
696 |
(limit_in_pages << 23) | |
|
697 |
0x7000; |
|
698 |
if (!oldmode) |
|
699 |
entry_2 |= (useable << 20); |
|
700 |
|
|
701 |
/* Install the new entry ... */ |
|
702 |
install: |
|
703 |
lp = (uint32_t *)(ldt_table + (ldt_info.entry_number << 3)); |
|
704 |
lp[0] = tswap32(entry_1); |
|
705 |
lp[1] = tswap32(entry_2); |
|
706 |
return 0; |
|
707 |
} |
|
708 |
|
|
709 |
/* specific and weird i386 syscalls */ |
|
710 |
int gemu_modify_ldt(CPUX86State *env, int func, void *ptr, unsigned long bytecount) |
|
711 |
{ |
|
712 |
int ret = -ENOSYS; |
|
713 |
|
|
714 |
switch (func) { |
|
715 |
case 0: |
|
716 |
ret = read_ldt(ptr, bytecount); |
|
717 |
break; |
|
718 |
case 1: |
|
719 |
ret = write_ldt(env, ptr, bytecount, 1); |
|
720 |
break; |
|
721 |
case 0x11: |
|
722 |
ret = write_ldt(env, ptr, bytecount, 0); |
|
723 |
break; |
|
724 |
} |
|
725 |
return ret; |
|
726 |
} |
|
727 |
#endif |
|
728 |
|
|
610 | 729 |
void syscall_init(void) |
611 | 730 |
{ |
612 | 731 |
#define STRUCT(name, list...) thunk_register_struct(STRUCT_ ## name, #name, struct_ ## name ## _def); |
... | ... | |
616 | 735 |
#undef STRUCT_SPECIAL |
617 | 736 |
} |
618 | 737 |
|
619 |
long do_syscall(int num, long arg1, long arg2, long arg3, |
|
738 |
long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
|
|
620 | 739 |
long arg4, long arg5, long arg6) |
621 | 740 |
{ |
622 | 741 |
long ret; |
... | ... | |
1095 | 1214 |
/* no need to transcode because we use the linux syscall */ |
1096 | 1215 |
ret = get_errno(sys_uname((struct new_utsname *)arg1)); |
1097 | 1216 |
break; |
1217 |
#ifdef TARGET_I386 |
|
1098 | 1218 |
case TARGET_NR_modify_ldt: |
1099 |
goto unimplemented; |
|
1219 |
ret = get_errno(gemu_modify_ldt(cpu_env, arg1, (void *)arg2, arg3)); |
|
1220 |
break; |
|
1221 |
#endif |
|
1100 | 1222 |
case TARGET_NR_adjtimex: |
1101 | 1223 |
goto unimplemented; |
1102 | 1224 |
case TARGET_NR_mprotect: |
Also available in: Unified diff