This patch implements sys_vcpu support in UML. diff --git a/arch/um/include/kern_util.h b/arch/um/include/kern_util.h index 0a91cb1..3421c47 100644 --- a/arch/um/include/kern_util.h +++ b/arch/um/include/kern_util.h @@ -22,7 +22,7 @@ extern void free_stack(unsigned long stack, int order); extern void do_signal(void); extern void copy_sc(struct uml_pt_regs *regs, void *from); -extern void interrupt_end(void); +extern int interrupt_end(void); extern void relay_signal(int sig, struct uml_pt_regs *regs); extern unsigned long segv(struct faultinfo fi, unsigned long ip, diff --git a/arch/um/include/os.h b/arch/um/include/os.h index 32c799e..309dd51 100644 --- a/arch/um/include/os.h +++ b/arch/um/include/os.h @@ -265,6 +265,7 @@ extern int is_skas_winch(int pid, int fd, void *data); extern int start_userspace(unsigned long stub_stack); extern int copy_context_skas0(unsigned long stack, int pid); extern void userspace(struct uml_pt_regs *regs); +extern void vcpu_userspace(struct uml_pt_regs *regs, int mm_fd); extern int map_stub_pages(int fd, unsigned long code, unsigned long data, unsigned long stack); extern void new_thread(void *stack, jmp_buf *buf, void (*handler)(void)); diff --git a/arch/um/include/skas/skas.h b/arch/um/include/skas/skas.h index ea56428..590fcff 100644 --- a/arch/um/include/skas/skas.h +++ b/arch/um/include/skas/skas.h @@ -15,15 +15,18 @@ #ifdef UML_CONFIG_X86_32 #define __NR_new_mm 327 #define __NR_switch_mm 328 +#define __NR_vcpu 329 #else #define __NR_new_mm 288 #define __NR_switch_mm 289 +#define __NR_vcpu 290 #endif #define PTRACE_SWITCH_MM 34 #ifndef __ASSEMBLY__ +#include <asm/user.h> #include "sysdep/ptrace.h" #define STUB_ADDR(x) (STUB_CODE + (unsigned long) (x) - \ @@ -36,6 +39,7 @@ extern int skas_needs_stub; extern int have_switch_mm; extern int have_ptrace_switch_mm; extern int have_siginfo_segv; +extern int have_vcpu; extern int self_mm_fd; extern int user_thread(unsigned long stack, int flags); @@ -48,7 +52,18 @@ extern unsigned long current_stub_stack(void); #ifndef __KERNEL__ #include <errno.h> -#include <asm/user.h> +#include <asm/ldt.h> +#include "siginfo_segv.h" + +#ifdef UML_CONFIG_X86_32 +#define GDT_ENTRY_TLS_ENTRIES 3 + +struct vcpu_arch { + struct user_desc tls_array[GDT_ENTRY_TLS_ENTRIES]; +}; +#else +struct vcpu_arch { }; +#endif struct user_regs { unsigned long regs[MAX_REG_NR]; @@ -61,6 +76,13 @@ struct user_regs { #endif }; +struct vcpu_user { + enum { VCPU_SYSCALL, VCPU_SIGNAL } event; + struct user_regs regs; + siginfo_t siginfo; + struct vcpu_arch arch; +}; + static inline long new_mm(void) { int ret = syscall(__NR_new_mm, 0, 0, 0, 0, 0, 0); @@ -83,6 +105,27 @@ static inline long switch_mm(int mm_fd, struct user_regs *save_regs, return 0; } + +static inline long vcpu(long mm_fd, struct vcpu_user *vcpu) +{ + int ret = syscall(__NR_vcpu, mm_fd, vcpu, 0, 0, 0, 0); + + if (ret < 0) + return -errno; + + return ret; +} + +static inline int get_thread_area(struct user_desc *u_info) +{ + int ret = syscall(__NR_get_thread_area, u_info, 0, 0, 0, 0, 0); + + if (ret < 0) + return -errno; + + return ret; +} + #endif #endif diff --git a/arch/um/include/sysdep-i386/tls.h b/arch/um/include/sysdep-i386/tls.h index 918fd3c..844f0c2 100644 --- a/arch/um/include/sysdep-i386/tls.h +++ b/arch/um/include/sysdep-i386/tls.h @@ -1,7 +1,7 @@ #ifndef _SYSDEP_TLS_H #define _SYSDEP_TLS_H -# ifndef __KERNEL__ +#ifndef __KERNEL__ /* Change name to avoid conflicts with the original one from <asm/ldt.h>, which * may be named user_desc (but in 2.4 and in header matching its API was named @@ -19,13 +19,19 @@ typedef struct um_dup_user_desc { unsigned int useable:1; } user_desc_t; -# else /* __KERNEL__ */ +#else /* __KERNEL__ */ -# include <asm/ldt.h> +#include <asm/host_ldt.h> typedef struct user_desc user_desc_t; # endif /* __KERNEL__ */ +struct uml_tls_struct { + user_desc_t tls; + unsigned flushed:1; + unsigned present:1; +}; + #define GDT_ENTRY_TLS_MIN_I386 6 #define GDT_ENTRY_TLS_MIN_X86_64 12 diff --git a/arch/um/include/sysdep-x86_64/ptrace.h b/arch/um/include/sysdep-x86_64/ptrace.h index d3d1dda..18ad3a8 100644 --- a/arch/um/include/sysdep-x86_64/ptrace.h +++ b/arch/um/include/sysdep-x86_64/ptrace.h @@ -233,8 +233,6 @@ struct syscall_args { #define UPT_FAULTINFO(r) (&(r)->faultinfo) -static inline void arch_init_registers(int pid) -{ -} +extern void arch_init_registers(int pid); #endif diff --git a/arch/um/kernel/process.c b/arch/um/kernel/process.c index 0963fcd..7f07ad3 100644 --- a/arch/um/kernel/process.c +++ b/arch/um/kernel/process.c @@ -111,12 +111,13 @@ void *_switch_to(void *prev, void *next, void *last) } -void interrupt_end(void) +int interrupt_end(void) { if (need_resched()) schedule(); if (test_thread_flag(TIF_SIGPENDING)) do_signal(); + return current->mm->context.id.u.mm_fd; } void exit_thread(void) @@ -152,7 +153,11 @@ void new_thread_handler(void) if (n == 1) { /* Handle any immediate reschedules or signals */ interrupt_end(); - userspace(¤t->thread.regs.regs); + if (have_vcpu) + vcpu_userspace(¤t->thread.regs.regs, + current->mm->context.id.u.mm_fd); + else + userspace(¤t->thread.regs.regs); } else do_exit(0); } @@ -176,7 +181,11 @@ void fork_handler(void) /* Handle any immediate reschedules or signals */ interrupt_end(); - userspace(¤t->thread.regs.regs); + if (have_vcpu) + vcpu_userspace(¤t->thread.regs.regs, + current->mm->context.id.u.mm_fd); + else + userspace(¤t->thread.regs.regs); } int copy_thread(int nr, unsigned long clone_flags, unsigned long sp, diff --git a/arch/um/kernel/skas/clone.c b/arch/um/kernel/skas/clone.c index 2c8583c..6b19d0a 100644 --- a/arch/um/kernel/skas/clone.c +++ b/arch/um/kernel/skas/clone.c @@ -3,8 +3,8 @@ * Licensed under the GPL */ -#include <signal.h> #include <sched.h> +#include <signal.h> #include <asm/unistd.h> #include <sys/time.h> #include "as-layout.h" diff --git a/arch/um/kernel/skas/process.c b/arch/um/kernel/skas/process.c index 32e84d7..ec82db3 100644 --- a/arch/um/kernel/skas/process.c +++ b/arch/um/kernel/skas/process.c @@ -55,7 +55,8 @@ int __init start_uml(void) { stack_protections((unsigned long) &cpu0_irqstack); set_sigstack(cpu0_irqstack, THREAD_SIZE); - if (proc_mm || have_switch_mm) { + + if (!have_vcpu && (proc_mm || have_switch_mm)) { userspace_pid[0] = start_userspace(0); if (userspace_pid[0] < 0) { printf("start_uml - start_userspace returned %d\n", diff --git a/arch/um/os-Linux/skas/mem.c b/arch/um/os-Linux/skas/mem.c index 42a3026..73b1dff 100644 --- a/arch/um/os-Linux/skas/mem.c +++ b/arch/um/os-Linux/skas/mem.c @@ -6,7 +6,6 @@ #include <stddef.h> #include <unistd.h> #include <errno.h> -#include <signal.h> #include <string.h> #include <sys/mman.h> #include "init.h" @@ -122,11 +121,12 @@ static long do_syscall_stub(struct mm_id *mm_idp, void **addr) return ret; } +static struct user_regs return_regs; + long do_syscall_stub_skas4(struct mm_id *mm_idp, void **addr, unsigned long ip, unsigned long sp) { long ret; - struct user_regs return_regs; unsigned long *ptr; int err; sigset_t sigs, old; @@ -221,9 +221,10 @@ long syscall_stub_data(struct mm_id *mm_idp, unsigned long *data, return ret; stack = check_init_stack(mm_idp, *addr); - *stack++ = data_count * sizeof(long); + *stack = data_count; + *addr = stack++; - memcpy(stack, data, data_count * sizeof(long)); + memcpy(stack, data, data_count); *stub_addr = (void *)(((unsigned long) stack & ~UM_KERN_PAGE_MASK) + STUB_DATA); diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/process.c index 23a9b42..593df24 100644 --- a/arch/um/os-Linux/skas/process.c +++ b/arch/um/os-Linux/skas/process.c @@ -358,6 +358,85 @@ int start_userspace(unsigned long stub_stack) return err; } +#ifdef UML_CONFIG_X86_32 +extern void init_vcpu_tls(struct user_desc *tls); + +static void arch_init_vcpu(struct vcpu_arch *vcpu) +{ + init_vcpu_tls(vcpu->tls_array); +} +#else +static void arch_init_vcpu(struct vcpu_arch *vcpu) +{ +} +#endif + +extern unsigned long fp_regs[FP_SIZE]; + +void vcpu_userspace(struct uml_pt_regs *regs, int mm_fd) +{ + struct vcpu_user vcpu_state; + int err; + + memcpy(&vcpu_state.regs.fpregs, fp_regs, sizeof(fp_regs)); + vcpu_state.regs.fp_state = &vcpu_state.regs.fpregs; + while (1) { + memcpy(&vcpu_state.regs.regs, ®s->gp, + sizeof(vcpu_state.regs.regs)); + arch_init_vcpu(&vcpu_state.arch); + + err = vcpu(mm_fd, &vcpu_state); + if (err) + panic("userspace - could not resume userspace process, " + "errno = %d\n", errno); + + regs->is_user = 1; + memcpy(®s->gp, &vcpu_state.regs.regs, + sizeof(vcpu_state.regs.regs)); + + UPT_SYSCALL_NR(regs) = -1; /* Assume: It's not a syscall */ + if (vcpu_state.event == VCPU_SYSCALL) { + UPT_SYSCALL_NR(regs) = PT_SYSCALL_NR(regs->gp); + handle_syscall(regs); + } + else if (vcpu_state.event == VCPU_SIGNAL){ + int sig = vcpu_state.siginfo.si_signo; + switch(sig) { + case SIGSEGV: + GET_FAULTINFO_FROM_SI(regs->faultinfo, + vcpu_state.siginfo); + (*sig_info[SIGSEGV])(SIGSEGV, regs); + break; + case SIGTRAP: + relay_signal(SIGTRAP, regs); + break; + case SIGVTALRM: + block_signals(); + (*sig_info[sig])(sig, regs); + unblock_signals(); + break; + case SIGIO: + case SIGILL: + case SIGBUS: + case SIGFPE: + case SIGWINCH: + block_signals(); + (*sig_info[sig])(sig, regs); + unblock_signals(); + break; + default: + printk(UM_KERN_ERR "userspace - child stopped " + "with signal %d\n", sig); + } + /* Avoid -ERESTARTSYS handling in host */ + if (PT_SYSCALL_NR_OFFSET != PT_SYSCALL_RET_OFFSET) + PT_SYSCALL_NR(regs->gp) = -1; + } + + mm_fd = interrupt_end(); + } +} + void userspace(struct uml_pt_regs *regs) { struct itimerval timer; @@ -778,8 +857,11 @@ void reboot_skas(void) void __switch_mm(struct mm_id *mm_idp) { int err; - /* FIXME: need cpu pid in __switch_mm */ + + if (have_vcpu) + return; + if (proc_mm) { err = ptrace(OLD_PTRACE_SWITCH_MM, userspace_pid[0], 0, mm_idp->u.mm_fd); diff --git a/arch/um/os-Linux/start_up.c b/arch/um/os-Linux/start_up.c index 021d41c..28a7984 100644 --- a/arch/um/os-Linux/start_up.c +++ b/arch/um/os-Linux/start_up.c @@ -29,6 +29,7 @@ #include "skas.h" #include "skas_ptrace.h" #include "sysdep/sigcontext.h" +#include "user.h" static int ptrace_child(void) { @@ -165,6 +166,9 @@ static int disable_siginfo_segv; int have_ptrace_switch_mm; static int disable_ptrace_switch_mm; +int have_vcpu; +static int disable_vcpu; + int skas_needs_stub; static int __init skas0_cmd_param(char *str, int* add) @@ -176,6 +180,7 @@ static int __init skas0_cmd_param(char *str, int* add) disable_switch_mm = 1; disable_siginfo_segv = 1; disable_ptrace_switch_mm = 1; + disable_vcpu = 1; return 0; } @@ -503,7 +508,7 @@ static void __init can_do_skas3(void) static void *fault_address; -static int check_fault_info(struct faultinfo *fi) +static __init int check_fault_info(struct faultinfo *fi) { return (FAULT_ADDRESS(*fi) == (unsigned long) fault_address) && FAULT_WRITE(*fi) && SEGV_IS_FIXABLE(fi); @@ -637,7 +642,7 @@ int self_mm_fd; static int switch_mm_works; -static void after_switch(void) +static __init void after_switch(void) { /* * If we are really in a new address space, setting this to @@ -804,13 +809,88 @@ static int __init check_ptrace_switch_mm(void) return 0; } +#ifdef UML_CONFIG_X86_32 +extern int host_gdt_entry_tls_min; +extern void host_tls_support(void); + +static __init int init_vcpu_arch(struct vcpu_arch *vcpu){ + struct user_desc *tls = vcpu->tls_array; + int i, err; + + host_tls_support(); + memset(tls, 0, sizeof(vcpu->tls_array)); + for (i = 0; i < ARRAY_SIZE(vcpu->tls_array); i++) { + tls[i].entry_number = host_gdt_entry_tls_min + i; + err = get_thread_area(&tls[i]); + if (err) { + perror("get_thread_area"); + return err; + } + } + return 0; +} +#else +static int init_vcpu_arch(struct vcpu_arch *vcpu){ + return 0; +} +#endif + +static struct vcpu_user vcpu_data; + +static __init int check_vcpu(void) +{ + void *stack; + int err; + + non_fatal("\tvcpu ... "); + + stack = mmap(NULL, UM_KERN_PAGE_SIZE, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (stack == MAP_FAILED) + goto bad; + + get_safe_registers(vcpu_data.regs.regs); + vcpu_data.regs.regs[REGS_IP_INDEX] = (unsigned long) ptrace_child; + vcpu_data.regs.regs[REGS_SP_INDEX] = (unsigned long) stack + + UM_KERN_PAGE_SIZE - sizeof(void *); + + if (init_vcpu_arch(&vcpu_data.arch)) + goto bad; + + err = vcpu(-1, &vcpu_data); + munmap(stack, UM_KERN_PAGE_SIZE); + if (err) { + non_fatal("vcpu failed with errno %d\n", err); + goto bad; + } + + if (vcpu_data.event != VCPU_SYSCALL) { + non_fatal("vcpu returned with event = %d\n", vcpu_data.event); + goto bad; + } + + non_fatal("OK\n"); + + if (disable_vcpu) + non_fatal("vcpu support disabled on command line\n"); + else + have_vcpu = 1; + + return 1; + + bad: + non_fatal("Failed\n"); + return 0; +} + static int __init can_do_skas4(void) { int ret; non_fatal("Checking for SKAS4 support in the host:\n"); - ret = check_switch_mm() && check_ptrace_switch_mm() && check_siginfo(); + ret = check_switch_mm() && check_ptrace_switch_mm() && check_siginfo() + && check_vcpu(); if (ret) skas_needs_stub = 1; diff --git a/arch/um/os-Linux/sys-i386/registers.c b/arch/um/os-Linux/sys-i386/registers.c index b613473..6dfd56f 100644 --- a/arch/um/os-Linux/sys-i386/registers.c +++ b/arch/um/os-Linux/sys-i386/registers.c @@ -4,10 +4,16 @@ * Licensed under the GPL */ +#include <stdio.h> +#include <stdlib.h> #include <errno.h> +#include <asm/ldt.h> +#include <sys/syscall.h> +#include <unistd.h> #include "kern_constants.h" #include "longjmp.h" #include "user.h" +#include "skas.h" #include "sysdep/ptrace_user.h" int save_fp_registers(int pid, unsigned long *fp_regs) @@ -72,12 +78,32 @@ int put_fp_registers(int pid, unsigned long *regs) return restore_fp_registers(pid, regs); } +extern int host_gdt_entry_tls_min; + +#define GDT_ENTRY_TLS_ENTRIES 3 +#define GDT_ENTRY_TLS_MIN 6 +#define GDT_ENTRY_TLS_MAX (GDT_ENTRY_TLS_MIN + GDT_ENTRY_TLS_ENTRIES - 1) + +struct user_desc tls[GDT_ENTRY_TLS_ENTRIES]; + +unsigned long fp_regs[FP_SIZE]; + void arch_init_registers(int pid) { - unsigned long fpx_regs[HOST_XFP_SIZE]; - int err; + struct user_desc *entry; + int err, i; - err = ptrace(PTRACE_GETFPXREGS, pid, 0, fpx_regs); + for (i = 0; i < GDT_ENTRY_TLS_ENTRIES; i++) { + entry = &tls[i]; + entry->entry_number = i + GDT_ENTRY_TLS_MIN; + err = get_thread_area(entry); + if (err) { + perror("get_thread_area"); + exit(1); + } + } + + err = ptrace(PTRACE_GETFPXREGS, pid, 0, fp_regs); if (!err) return; @@ -87,3 +113,4 @@ void arch_init_registers(int pid) have_fpx_regs = 0; } + diff --git a/arch/um/os-Linux/sys-x86_64/registers.c b/arch/um/os-Linux/sys-x86_64/registers.c index 594d97a..43731fe 100644 --- a/arch/um/os-Linux/sys-x86_64/registers.c +++ b/arch/um/os-Linux/sys-x86_64/registers.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 - 2007 Jeff Dike ([EMAIL PROTECTED],linux.intel}.com) + * Copyright (C) 2006 - 2008 Jeff Dike ([EMAIL PROTECTED],linux.intel}.com) * Licensed under the GPL */ @@ -10,6 +10,7 @@ #include "kern_constants.h" #include "longjmp.h" #include "user.h" +#include "sysdep/ptrace_user.h" int save_fp_registers(int pid, unsigned long *fp_regs) { @@ -50,3 +51,15 @@ int put_fp_registers(int pid, unsigned long *regs) { return restore_fp_registers(pid, regs); } + +unsigned long fp_regs[FP_SIZE]; + +void arch_init_registers(int pid) +{ + int err; + + err = ptrace(PTRACE_GETFPREGS, pid, 0, fp_regs); + if(err) + panic("arch_init_registers : PTRACE_GETFPREGS failed, " + "errno = %d", errno); +} diff --git a/arch/um/sys-i386/signal.c b/arch/um/sys-i386/signal.c index fd0c25a..68251f2 100644 --- a/arch/um/sys-i386/signal.c +++ b/arch/um/sys-i386/signal.c @@ -164,6 +164,8 @@ static int convert_fxsr_from_user(struct user_fxsr_struct *fxsave, extern int have_fpx_regs; +extern unsigned long fp_regs[FP_SIZE]; + static int copy_sc_from_user(struct pt_regs *regs, struct sigcontext __user *from) { @@ -177,24 +179,12 @@ static int copy_sc_from_user(struct pt_regs *regs, pid = userspace_pid[current_thread_info()->cpu]; copy_sc(®s->regs, &sc); if (have_fpx_regs) { - struct user_fxsr_struct fpx; - - err = copy_from_user(&fpx, &sc.fpstate->_fxsr_env[0], - sizeof(struct user_fxsr_struct)); - if (err) - return 1; + struct user_fxsr_struct *fpx = + (struct user_fxsr_struct *) &fp_regs; - err = convert_fxsr_from_user(&fpx, sc.fpstate); + err = convert_fxsr_from_user(fpx, sc.fpstate); if (err) return 1; - - err = restore_fpx_registers(pid, (unsigned long *) &fpx); - if (err < 0) { - printk(KERN_ERR "copy_sc_from_user - " - "restore_fpx_registers failed, errno = %d\n", - -err); - return 1; - } } else { struct user_i387_struct fp; @@ -250,25 +240,19 @@ static int copy_sc_to_user(struct sigcontext __user *to, pid = userspace_pid[current_thread_info()->cpu]; if (have_fpx_regs) { - struct user_fxsr_struct fpx; - - err = save_fpx_registers(pid, (unsigned long *) &fpx); - if (err < 0){ - printk(KERN_ERR "copy_sc_to_user - save_fpx_registers " - "failed, errno = %d\n", err); - return 1; - } + struct user_fxsr_struct *fpx = + (struct user_fxsr_struct *) &fp_regs; - err = convert_fxsr_to_user(to_fp, &fpx); + err = convert_fxsr_to_user(to_fp, fpx); if (err) return 1; - err |= __put_user(fpx.swd, &to_fp->status); + err |= __put_user(fpx->swd, &to_fp->status); err |= __put_user(X86_FXSR_MAGIC, &to_fp->magic); if (err) return 1; - if (copy_to_user(&to_fp->_fxsr_env[0], &fpx, + if (copy_to_user(&to_fp->_fxsr_env[0], fpx, sizeof(struct user_fxsr_struct))) return 1; } diff --git a/arch/um/sys-i386/tls.c b/arch/um/sys-i386/tls.c index c6c7131..a45d7ab 100644 --- a/arch/um/sys-i386/tls.c +++ b/arch/um/sys-i386/tls.c @@ -6,10 +6,19 @@ #include "linux/percpu.h" #include "linux/sched.h" #include "asm/uaccess.h" +#include <asm/unistd.h> +#include <asm/segment.h> +#include "kern.h" #include "os.h" #include "skas.h" #include "sysdep/tls.h" +void copy_tls(struct user_desc *to) +{ + memcpy(to, current->thread.arch.tls_array, + sizeof(current->thread.arch.tls_array)); +} + /* * If needed we can detect when it's uninitialized. * @@ -18,11 +27,14 @@ static int host_supports_tls = -1; int host_gdt_entry_tls_min; -int do_set_thread_area(struct user_desc *info) +static int do_set_thread_area(struct user_desc *info) { int ret; u32 cpu; + if(have_vcpu) + return 0; + cpu = get_cpu(); ret = os_set_thread_area(info, userspace_pid[cpu]); put_cpu(); @@ -300,6 +312,7 @@ int sys_set_thread_area(struct user_desc __user *user_desc) ret = do_set_thread_area(&info); if (ret) return ret; + return set_tls_entry(current, &info, idx, 1); } @@ -366,31 +379,38 @@ out: return ret; } +extern struct user_desc tls[GDT_ENTRY_TLS_ENTRIES]; + /* * This code is really i386-only, but it detects and logs x86_64 GDT indexes * if a 32-bit UML is running on a 64-bit host. */ -static int __init __setup_host_supports_tls(void) +void __init host_tls_support(void) { check_host_supports_tls(&host_supports_tls, &host_gdt_entry_tls_min); if (host_supports_tls) { - printk(KERN_INFO "Host TLS support detected\n"); - printk(KERN_INFO "Detected host type: "); + printf("Host TLS support detected\n"); + printf("Detected host type: "); switch (host_gdt_entry_tls_min) { case GDT_ENTRY_TLS_MIN_I386: - printk(KERN_CONT "i386"); + printf("i386\n"); break; case GDT_ENTRY_TLS_MIN_X86_64: - printk(KERN_CONT "x86_64"); + printf("x86_64\n"); break; } - printk(KERN_CONT " (GDT indexes %d to %d)\n", - host_gdt_entry_tls_min, + printf(" (GDT indexes %d to %d)\n", host_gdt_entry_tls_min, host_gdt_entry_tls_min + GDT_ENTRY_TLS_ENTRIES); } else - printk(KERN_ERR " Host TLS support NOT detected! " - "TLS support inside UML will not work\n"); - return 0; + printf("Host TLS support NOT detected! " + "TLS support inside UML will not work\n"); } -__initcall(__setup_host_supports_tls); +void init_vcpu_tls(struct user_desc *to) +{ + struct uml_tls_struct *tls = current->thread.arch.tls_array; + int i; + + for (i = 0; i < ARRAY_SIZE(current->thread.arch.tls_array); i++) + to[i] = tls[i].tls; +} diff --git a/arch/um/sys-x86_64/signal.c b/arch/um/sys-x86_64/signal.c index 1a899a7..1e426f8 100644 --- a/arch/um/sys-x86_64/signal.c +++ b/arch/um/sys-x86_64/signal.c @@ -42,6 +42,8 @@ void copy_sc(struct uml_pt_regs *regs, void *from) #undef GETREG } +static unsigned long fp_regs[HOST_FP_SIZE]; + static int copy_sc_from_user(struct pt_regs *regs, struct sigcontext __user *from, struct _fpstate __user *fpp) @@ -81,13 +83,17 @@ static int copy_sc_from_user(struct pt_regs *regs, if (err) return 1; - err = restore_fp_registers(userspace_pid[current_thread_info()->cpu], - (unsigned long *) &fp); - if (err < 0) { - printk(KERN_ERR "copy_sc_from_user - " - "restore_fp_registers failed, errno = %d\n", - -err); - return 1; + if (have_vcpu) + memcpy(fp_regs, &fp, sizeof(fp_regs)); + else { + err = restore_fp_registers(userspace_pid[current_thread_info()->cpu], + (unsigned long *) &fp); + if (err < 0) { + printk(KERN_ERR "copy_sc_from_user - " + "restore_fp_registers failed, errno = %d\n", + -err); + return 1; + } } return 0; @@ -143,14 +149,18 @@ static int copy_sc_to_user(struct sigcontext __user *to, if (err) return 1; - err = save_fp_registers(userspace_pid[current_thread_info()->cpu], - (unsigned long *) &fp); - if (err < 0) { - printk(KERN_ERR "copy_sc_from_user - restore_fp_registers " - "failed, errno = %d\n", -err); - return 1; + if (have_vcpu) + memcpy(&fp, fp_regs, sizeof(fp)); + else { + err = save_fp_registers(userspace_pid[current_thread_info()->cpu], + (unsigned long *) &fp); + if (err < 0) { + printk(KERN_ERR "copy_sc_from_user - " + "restore_fp_registers failed, errno = %d\n", + -err); + return 1; + } } - if (copy_to_user(to_fp, &fp, sizeof(struct user_i387_struct))) return 1; diff --git a/arch/um/sys-x86_64/syscalls.c b/arch/um/sys-x86_64/syscalls.c index e861ad6..fbbc903 100644 --- a/arch/um/sys-x86_64/syscalls.c +++ b/arch/um/sys-x86_64/syscalls.c @@ -28,61 +28,78 @@ asmlinkage long sys_uname64(struct new_utsname __user * name) long arch_prctl(struct task_struct *task, int code, unsigned long __user *addr) { - unsigned long *ptr = addr, tmp; - long ret; - int pid = userspace_pid[0]; + long ret = 0; + + if (have_vcpu) { + unsigned long *regs = task->thread.regs.regs.gp; + switch (code) { + case ARCH_SET_FS: + task->thread.arch.fs = (unsigned long) addr; + regs[HOST_FS_BASE] = (unsigned long) addr; + break; + case ARCH_SET_GS: + regs[HOST_GS_BASE] = (unsigned long) addr; + break; + case ARCH_GET_FS: + ret = put_user(regs[HOST_FS_BASE], addr); + break; + case ARCH_GET_GS: + ret = put_user(regs[HOST_GS_BASE], addr); + break; + } + } else { + unsigned long *ptr = addr, tmp; + int pid = userspace_pid[0]; - /* - * With ARCH_SET_FS (and ARCH_SET_GS is treated similarly to - * be safe), we need to call arch_prctl on the host because - * setting %fs may result in something else happening (like a - * GDT or thread.fs being set instead). So, we let the host - * fiddle the registers and thread struct and restore the - * registers afterwards. - * - * So, the saved registers are stored to the process (this - * needed because a stub may have been the last thing to run), - * arch_prctl is run on the host, then the registers are read - * back. - */ - switch (code) { - case ARCH_SET_FS: - case ARCH_SET_GS: - ret = restore_registers(pid, ¤t->thread.regs.regs); - if (ret) - return ret; - break; - case ARCH_GET_FS: - case ARCH_GET_GS: /* - * With these two, we read to a local pointer and - * put_user it to the userspace pointer that we were - * given. If addr isn't valid (because it hasn't been - * faulted in or is just bogus), we want put_user to - * fault it in (or return -EFAULT) instead of having - * the host return -EFAULT. + * With ARCH_SET_FS (and ARCH_SET_GS is treated similarly to + * be safe), we need to call arch_prctl on the host because + * setting %fs may result in something else happening (like a + * GDT or thread.fs being set instead). So, we let the host + * fiddle the registers and thread struct and restore the + * registers afterwards. + * + * So, the saved registers are stored to the process (this + * needed because a stub may have been the last thing to run), + * arch_prctl is run on the host, then the registers are read + * back. */ - ptr = &tmp; - } - - ret = os_arch_prctl(pid, code, ptr); - if (ret) - return ret; + switch (code) { + case ARCH_SET_FS: + case ARCH_SET_GS: + restore_registers(pid, ¤t->thread.regs.regs); + break; + case ARCH_GET_FS: + case ARCH_GET_GS: + /* + * With these two, we read to a local pointer and + * put_user it to the userspace pointer that we were + * given. If addr isn't valid (because it hasn't been + * faulted in or is just bogus), we want put_user to + * fault it in (or return -EFAULT) instead of having + * the host return -EFAULT. + */ + ptr = &tmp; + } - switch (code) { - case ARCH_SET_FS: - current->thread.arch.fs = (unsigned long) ptr; - ret = save_registers(pid, ¤t->thread.regs.regs); - break; - case ARCH_SET_GS: - ret = save_registers(pid, ¤t->thread.regs.regs); - break; - case ARCH_GET_FS: - ret = put_user(tmp, addr); - break; - case ARCH_GET_GS: - ret = put_user(tmp, addr); - break; + ret = os_arch_prctl(pid, code, ptr); + if (ret) + return ret; + switch (code) { + case ARCH_SET_FS: + current->thread.arch.fs = (unsigned long) ptr; + save_registers(pid, ¤t->thread.regs.regs); + break; + case ARCH_SET_GS: + save_registers(pid, ¤t->thread.regs.regs); + break; + case ARCH_GET_FS: + ret = put_user(tmp, addr); + break; + case ARCH_GET_GS: + ret = put_user(tmp, addr); + break; + } } return ret; diff --git a/include/asm-um/processor-i386.h b/include/asm-um/processor-i386.h index a2b7fe1..d7bca3e 100644 --- a/include/asm-um/processor-i386.h +++ b/include/asm-um/processor-i386.h @@ -1,25 +1,19 @@ /* - * Copyright (C) 2002 Jeff Dike ([EMAIL PROTECTED]) + * Copyright (C) 2002 - 2008 Jeff Dike ([EMAIL PROTECTED],linux.intel}.com) * Licensed under the GPL */ #ifndef __UM_PROCESSOR_I386_H #define __UM_PROCESSOR_I386_H -#include "linux/string.h" -#include "asm/host_ldt.h" -#include "asm/segment.h" - -extern int host_has_cmov; - -/* include faultinfo structure */ +#include <linux/kernel.h> +#include <linux/string.h> +#include <asm/host_ldt.h> +#include <asm/segment.h> #include "sysdep/faultinfo.h" +#include "sysdep/tls.h" -struct uml_tls_struct { - struct user_desc tls; - unsigned flushed:1; - unsigned present:1; -}; +extern int host_has_cmov; struct arch_thread { struct uml_tls_struct tls_array[GDT_ENTRY_TLS_ENTRIES]; @@ -38,8 +32,12 @@ struct arch_thread { static inline void arch_flush_thread(struct arch_thread *thread) { + int i; + /* Clear any TLS still hanging */ memset(&thread->tls_array, 0, sizeof(thread->tls_array)); + for (i = 0; i < ARRAY_SIZE(thread->tls_array); i++) + thread->tls_array[i].tls.entry_number = GDT_ENTRY_TLS_MIN + i; } static inline void arch_copy_thread(struct arch_thread *from,
------------------------------------------------------------------------- This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008. http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/ _______________________________________________ User-mode-linux-user mailing list User-mode-linux-user@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/user-mode-linux-user