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