diff options
author | smh22@boulderdash.cl.cam.ac.uk <smh22@boulderdash.cl.cam.ac.uk> | 2002-12-10 14:45:04 +0000 |
---|---|---|
committer | smh22@boulderdash.cl.cam.ac.uk <smh22@boulderdash.cl.cam.ac.uk> | 2002-12-10 14:45:04 +0000 |
commit | 39b0acbcc9fadbad4d8a96ef934a8131bda99c5d (patch) | |
tree | cac1fb7fe697a64def893c8dbc52342a99cda177 | |
parent | 597358ac038a754c99d1b399f3ae2455e7fbe8e4 (diff) | |
parent | 2d79667816dfa2dff25df09bdfa01d2bdb33e923 (diff) | |
download | xen-39b0acbcc9fadbad4d8a96ef934a8131bda99c5d.tar.gz xen-39b0acbcc9fadbad4d8a96ef934a8131bda99c5d.tar.bz2 xen-39b0acbcc9fadbad4d8a96ef934a8131bda99c5d.zip |
bitkeeper revision 1.10 (3df5fdf08iqVVKnjFD3pfq_hggDi2Q)
Merge boulderdash.cl.cam.ac.uk:/usr/groups/xeno/BK/xeno
into boulderdash.cl.cam.ac.uk:/local/scratch/smh22/xeno.bk
-rw-r--r-- | BitKeeper/etc/logging_ok | 1 | ||||
-rw-r--r-- | xen-2.4.16/arch/i386/entry.S | 5 | ||||
-rw-r--r-- | xen-2.4.16/arch/i386/i387.c | 26 | ||||
-rw-r--r-- | xen-2.4.16/arch/i386/setup.c | 3 | ||||
-rw-r--r-- | xen-2.4.16/arch/i386/traps.c | 176 | ||||
-rw-r--r-- | xen-2.4.16/include/hypervisor-ifs/hypervisor-if.h | 2 | ||||
-rw-r--r-- | xenolinux-2.4.16-sparse/arch/xeno/kernel/process.c | 26 | ||||
-rw-r--r-- | xenolinux-2.4.16-sparse/arch/xeno/kernel/signal.c | 8 | ||||
-rw-r--r-- | xenolinux-2.4.16-sparse/arch/xeno/kernel/traps.c | 55 | ||||
-rw-r--r-- | xenolinux-2.4.16-sparse/include/asm-xeno/hypervisor.h | 22 |
10 files changed, 233 insertions, 91 deletions
diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok index f7137dd8c5..c3ebee9a35 100644 --- a/BitKeeper/etc/logging_ok +++ b/BitKeeper/etc/logging_ok @@ -1,4 +1,5 @@ akw27@boulderdash.cl.cam.ac.uk +kaf24@labyrinth.cl.cam.ac.uk kaf24@plym.cl.cam.ac.uk kaf24@striker.cl.cam.ac.uk smh22@boulderdash.cl.cam.ac.uk diff --git a/xen-2.4.16/arch/i386/entry.S b/xen-2.4.16/arch/i386/entry.S index 996c1eb82f..ecd878acd1 100644 --- a/xen-2.4.16/arch/i386/entry.S +++ b/xen-2.4.16/arch/i386/entry.S @@ -302,6 +302,9 @@ FAULT11:movl %eax,4(%esi) movl EFLAGS+4(%esp),%eax FAULT12:movl %eax,8(%esi) /* Rewrite our stack frame and return to ring 1. */ + /* IA32 Ref. Vol. 3: TF, VM, RF and NT flags are cleared on trap. */ + andl $0xfffcbeff,%eax + movl %eax,EFLAGS+4(%esp) movl %ds,OLDSS+4(%esp) movl %esi,OLDESP+4(%esp) movzwl %es:GTB_CS(%edx),%eax @@ -521,6 +524,8 @@ ENTRY(hypervisor_call_table) .long SYMBOL_NAME(kill_domain) .long SYMBOL_NAME(do_dom0_op) .long SYMBOL_NAME(do_network_op) + .long SYMBOL_NAME(do_set_debugreg) + .long SYMBOL_NAME(do_get_debugreg) .rept NR_syscalls-(.-hypervisor_call_table)/4 .long SYMBOL_NAME(sys_ni_syscall) .endr diff --git a/xen-2.4.16/arch/i386/i387.c b/xen-2.4.16/arch/i386/i387.c index dc94cc1dad..fe34ff16f5 100644 --- a/xen-2.4.16/arch/i386/i387.c +++ b/xen-2.4.16/arch/i386/i387.c @@ -22,20 +22,26 @@ void init_fpu(void) static inline void __save_init_fpu( struct task_struct *tsk ) { - if ( cpu_has_fxsr ) { - asm volatile( "fxsave %0 ; fnclex" - : "=m" (tsk->thread.i387.fxsave) ); - } else { - asm volatile( "fnsave %0 ; fwait" - : "=m" (tsk->thread.i387.fsave) ); - } - tsk->flags &= ~PF_USEDFPU; + if ( cpu_has_fxsr ) { + asm volatile( "fxsave %0 ; fnclex" + : "=m" (tsk->thread.i387.fxsave) ); + } else { + asm volatile( "fnsave %0 ; fwait" + : "=m" (tsk->thread.i387.fsave) ); + } + tsk->flags &= ~PF_USEDFPU; } void save_init_fpu( struct task_struct *tsk ) { - __save_init_fpu(tsk); - stts(); + /* + * The guest OS may have set the 'virtual STTS' flag. + * This causes us to set the real flag, so we'll need + * to temporarily clear it while saving f-p state. + */ + if ( tsk->flags & PF_GUEST_STTS ) clts(); + __save_init_fpu(tsk); + stts(); } void restore_fpu( struct task_struct *tsk ) diff --git a/xen-2.4.16/arch/i386/setup.c b/xen-2.4.16/arch/i386/setup.c index f9163148ed..3cb11f6b05 100644 --- a/xen-2.4.16/arch/i386/setup.c +++ b/xen-2.4.16/arch/i386/setup.c @@ -193,6 +193,9 @@ void __init cpu_init(void) /* No nested task. */ __asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl"); + /* Ensure FPU gets initialised for each domain. */ + stts(); + /* Set up and load the per-CPU TSS and LDT. */ t->ss0 = __HYPERVISOR_DS; t->esp0 = current->thread.esp0; diff --git a/xen-2.4.16/arch/i386/traps.c b/xen-2.4.16/arch/i386/traps.c index e84690ad1a..9c9948fe3f 100644 --- a/xen-2.4.16/arch/i386/traps.c +++ b/xen-2.4.16/arch/i386/traps.c @@ -273,7 +273,26 @@ asmlinkage void do_general_protection(struct pt_regs * regs, long error_code) if (!(regs->xcs & 3) || (error_code & 1)) goto gp_in_kernel; - if ( (error_code & 2) ) + /* + * Cunning trick to allow arbitrary "INT n" handling. + * + * We set DPL == 0 on all vectors in the IDT. This prevents any INT <n> + * instruction from trapping to the appropriate vector, when that might not + * be expected by Xen or the guest OS. For example, that entry might be for + * a fault handler (unlike traps, faults don't increment EIP), or might + * expect an error code on the stack (which a software trap never + * provides), or might be a hardware interrupt handler that doesn't like + * being called spuriously. + * + * Instead, a GPF occurs with the faulting IDT vector in the error code. + * Bit 1 is set to indicate that an IDT entry caused the fault. + * Bit 0 is clear to indicate that it's a software fault, not hardware. + * + * NOTE: Vectors 3 and 4 are dealt with from their own handler. This is okay + * because they can only be triggered by an explicit DPL-checked instruction. + * The DPL specified by the guest OS for these vectors is NOT CHECKED!! + */ + if ( (error_code & 3) == 2 ) { /* This fault must be due to <INT n> instruction. */ ti = current->thread.traps + (error_code>>3); @@ -362,7 +381,8 @@ asmlinkage void do_nmi(struct pt_regs * regs, long error_code) asmlinkage void math_state_restore(struct pt_regs *regs, long error_code) { - __asm__ __volatile__("clts"); + /* Prevent recursion. */ + clts(); if ( !(current->flags & PF_USEDFPU) ) { @@ -384,53 +404,42 @@ asmlinkage void math_state_restore(struct pt_regs *regs, long error_code) } -/* - * Our handling of the processor debug registers is non-trivial. - * We do not clear them on entry and exit from the kernel. Therefore - * it is possible to get a watchpoint trap here from inside the kernel. - * However, the code in ./ptrace.c has ensured that the user can - * only set watchpoints on userspace addresses. Therefore the in-kernel - * watchpoint trap can only occur in code which is reading/writing - * from user space. Such code must not hold kernel locks (since it - * can equally take a page fault), therefore it is safe to call - * force_sig_info even though that claims and releases locks. - * - * Code in ./signal.c ensures that the debug control register - * is restored before we deliver any signal, and therefore that - * user code runs with the correct debug control register even though - * we clear it here. - * - * Being careful here means that we don't have to be as careful in a - * lot of more complicated places (task switching can be a bit lazy - * about restoring all the debug state, and ptrace doesn't have to - * find every occurrence of the TF bit that could be saved away even - * by user code) - */ asmlinkage void do_debug(struct pt_regs * regs, long error_code) { unsigned int condition; struct task_struct *tsk = current; + struct guest_trap_bounce *gtb = guest_trap_bounce+smp_processor_id(); __asm__ __volatile__("movl %%db6,%0" : "=r" (condition)); /* Mask out spurious debug traps due to lazy DR7 setting */ - if (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) { - if (!tsk->thread.debugreg[7]) - goto clear_dr7; + if ( (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) && + (tsk->thread.debugreg[7] == 0) ) + { + __asm__("movl %0,%%db7" : : "r" (0)); + return; } - /* Save debug status register where ptrace can see it */ - tsk->thread.debugreg[6] = condition; + if ( (regs->xcs & 3) == 0 ) + { + /* Clear TF just for absolute sanity. */ + regs->eflags &= ~EF_TF; + /* + * Basically, we ignore watchpoints when they trigger in + * the hypervisor. This may happen when a buffer is passed + * to us which previously had a watchpoint set on it. + * No need to bump EIP; the only faulting trap is an + * instruction breakpoint, which can't happen to us. + */ + return; + } - panic("trap up to OS here, pehaps\n"); + /* Save debug status register where guest OS can peek at it */ + tsk->thread.debugreg[6] = condition; - /* Disable additional traps. They'll be re-enabled when - * the signal is delivered. - */ - clear_dr7: - __asm__("movl %0,%%db7" - : /* no output */ - : "r" (0)); + gtb->flags = GTBF_TRAP_NOCODE; + gtb->cs = tsk->thread.traps[1].cs; + gtb->eip = tsk->thread.traps[1].address; } @@ -515,9 +524,9 @@ void __init trap_init(void) set_trap_gate(0,÷_error); set_trap_gate(1,&debug); set_intr_gate(2,&nmi); - set_system_gate(3,&int3); /* int3-5 can be called from all */ - set_system_gate(4,&overflow); - set_system_gate(5,&bounds); + set_system_gate(3,&int3); /* usable from all privilege levels */ + set_system_gate(4,&overflow); /* usable from all privilege levels */ + set_trap_gate(5,&bounds); set_trap_gate(6,&invalid_op); set_trap_gate(7,&device_not_available); set_trap_gate(8,&double_fault); @@ -533,27 +542,6 @@ void __init trap_init(void) set_trap_gate(18,&machine_check); set_trap_gate(19,&simd_coprocessor_error); - /* - * Cunning trick to allow arbitrary "INT n" handling. - * - * 1. 3 <= N <= 5 is trivial, as these are intended to be explicit. - * - * 2. All others, we set gate DPL == 0. Any use of "INT n" will thus - * cause a GPF with CS:EIP pointing at the faulting instruction. - * We can then peek at the instruction at check if it is of the - * form "0xCD <imm8>". If so, we fake out an exception to the - * guest OS. If the protected read page faults, we patch that up as - * a page fault to the guest OS. - * [NB. Of course we check the "soft DPL" to check that guest OS - * wants to handle a particular 'n'. If not, we pass the GPF up - * to the guest OS untouched.] - * - * 3. For efficiency, we may want to allow direct traps by the guest - * OS for certain critical vectors (eg. 0x80 in Linux). These must - * therefore not be mapped by hardware interrupts, and so we'd need - * a static list of them, which we add to on demand. - */ - /* Only ring 1 can access monitor services. */ _set_gate(idt_table+HYPERVISOR_CALL_VECTOR,15,1,&hypervisor_call); @@ -593,3 +581,69 @@ long do_fpu_taskswitch(void) stts(); return 0; } + + +long do_set_debugreg(int reg, unsigned long value) +{ + int i; + + switch ( reg ) + { + case 0: + if ( value > (PAGE_OFFSET-4) ) return -EPERM; + __asm__ ( "movl %0, %%db0" : : "r" (value) ); + break; + case 1: + if ( value > (PAGE_OFFSET-4) ) return -EPERM; + __asm__ ( "movl %0, %%db1" : : "r" (value) ); + break; + case 2: + if ( value > (PAGE_OFFSET-4) ) return -EPERM; + __asm__ ( "movl %0, %%db2" : : "r" (value) ); + break; + case 3: + if ( value > (PAGE_OFFSET-4) ) return -EPERM; + __asm__ ( "movl %0, %%db3" : : "r" (value) ); + break; + case 6: + /* + * DR6: Bits 4-11,16-31 reserved (set to 1). + * Bit 12 reserved (set to 0). + */ + value &= 0xffffefff; /* reserved bits => 0 */ + value |= 0xffff0ff0; /* reserved bits => 1 */ + __asm__ ( "movl %0, %%db6" : : "r" (value) ); + break; + case 7: + /* + * DR7: Bit 10 reserved (set to 1). + * Bits 11-12,14-15 reserved (set to 0). + * Privileged bits: + * GD (bit 13): must be 0. + * R/Wn (bits 16-17,20-21,24-25,28-29): mustn't be 10. + * LENn (bits 18-19,22-23,26-27,30-31): mustn't be 10. + */ + /* DR7 == 0 => debugging disabled for this domain. */ + if ( value != 0 ) + { + value &= 0xffff27ff; /* reserved bits => 0 */ + value |= 0x00000400; /* reserved bits => 1 */ + if ( (value & (1<<13)) != 0 ) return -EPERM; + for ( i = 0; i < 16; i += 2 ) + if ( ((value >> (i+16)) & 3) == 2 ) return -EPERM; + } + __asm__ ( "movl %0, %%db7" : : "r" (value) ); + break; + default: + return -EINVAL; + } + + current->thread.debugreg[reg] = value; + return 0; +} + +unsigned long do_get_debugreg(int reg) +{ + if ( (reg < 0) || (reg > 7) ) return -EINVAL; + return current->thread.debugreg[reg]; +} diff --git a/xen-2.4.16/include/hypervisor-ifs/hypervisor-if.h b/xen-2.4.16/include/hypervisor-ifs/hypervisor-if.h index 2491557325..4f4d1a2d23 100644 --- a/xen-2.4.16/include/hypervisor-ifs/hypervisor-if.h +++ b/xen-2.4.16/include/hypervisor-ifs/hypervisor-if.h @@ -40,6 +40,8 @@ typedef struct #define __HYPERVISOR_exit 8 #define __HYPERVISOR_dom0_op 9 #define __HYPERVISOR_network_op 10 +#define __HYPERVISOR_set_debugreg 11 +#define __HYPERVISOR_get_debugreg 12 #define TRAP_INSTR "int $0x82" diff --git a/xenolinux-2.4.16-sparse/arch/xeno/kernel/process.c b/xenolinux-2.4.16-sparse/arch/xeno/kernel/process.c index e4c236bf98..9e26f3d65a 100644 --- a/xenolinux-2.4.16-sparse/arch/xeno/kernel/process.c +++ b/xenolinux-2.4.16-sparse/arch/xeno/kernel/process.c @@ -187,6 +187,7 @@ void flush_thread(void) struct task_struct *tsk = current; memset(tsk->thread.debugreg, 0, sizeof(unsigned long)*8); + /* * Forget coprocessor state.. */ @@ -307,14 +308,6 @@ void dump_thread(struct pt_regs * regs, struct user * dump) } /* - * This special macro can be used to load a debugging register - */ -#define loaddebug(thread,register) \ - __asm__("movl %0,%%db" #register \ - : /* no output */ \ - :"r" (thread->debugreg[register])) - -/* * switch_to(x,yn) should switch tasks from x to y. * * We fsave/fwait so that an exception goes off at the right time @@ -359,20 +352,19 @@ void __switch_to(struct task_struct *prev_p, struct task_struct *next_p) loadsegment(fs, next->fs); loadsegment(gs, next->gs); -#if 0 /* * Now maybe reload the debug registers */ - if (next->debugreg[7]){ - loaddebug(next, 0); - loaddebug(next, 1); - loaddebug(next, 2); - loaddebug(next, 3); + if ( next->debugreg[7] != 0 ) + { + HYPERVISOR_set_debugreg(0, next->debugreg[0]); + HYPERVISOR_set_debugreg(1, next->debugreg[1]); + HYPERVISOR_set_debugreg(2, next->debugreg[2]); + HYPERVISOR_set_debugreg(3, next->debugreg[3]); /* no 4 and 5 */ - loaddebug(next, 6); - loaddebug(next, 7); + HYPERVISOR_set_debugreg(6, next->debugreg[6]); + HYPERVISOR_set_debugreg(7, next->debugreg[7]); } -#endif } asmlinkage int sys_fork(struct pt_regs regs) diff --git a/xenolinux-2.4.16-sparse/arch/xeno/kernel/signal.c b/xenolinux-2.4.16-sparse/arch/xeno/kernel/signal.c index c0f43e6342..a23cec1dea 100644 --- a/xenolinux-2.4.16-sparse/arch/xeno/kernel/signal.c +++ b/xenolinux-2.4.16-sparse/arch/xeno/kernel/signal.c @@ -693,6 +693,14 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset) } } + /* Reenable any watchpoints before delivering the + * signal to user space. The processor register will + * have been cleared if the watchpoint triggered + * inside the kernel. + */ + if ( current->thread.debugreg[7] != 0 ) + HYPERVISOR_set_debugreg(7, current->thread.debugreg[7]); + /* Whee! Actually deliver the signal. */ handle_signal(signr, ka, &info, oldset, regs); return 1; diff --git a/xenolinux-2.4.16-sparse/arch/xeno/kernel/traps.c b/xenolinux-2.4.16-sparse/arch/xeno/kernel/traps.c index 6e8f347420..2a546b49d5 100644 --- a/xenolinux-2.4.16-sparse/arch/xeno/kernel/traps.c +++ b/xenolinux-2.4.16-sparse/arch/xeno/kernel/traps.c @@ -324,11 +324,60 @@ gp_in_kernel: asmlinkage void do_debug(struct pt_regs * regs, long error_code) { - /* - * We don't mess with breakpoints, so the only way this exception - * type can occur is through single-step mode. + unsigned int condition; + struct task_struct *tsk = current; + siginfo_t info; + + condition = HYPERVISOR_get_debugreg(6); + + /* Mask out spurious debug traps due to lazy DR7 setting */ + if (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) { + if (!tsk->thread.debugreg[7]) + goto clear_dr7; + } + + /* Save debug status register where ptrace can see it */ + tsk->thread.debugreg[6] = condition; + + /* Mask out spurious TF errors due to lazy TF clearing */ + if (condition & DR_STEP) { + /* + * The TF error should be masked out only if the current + * process is not traced and if the TRAP flag has been set + * previously by a tracing process (condition detected by + * the PT_DTRACE flag); remember that the i386 TRAP flag + * can be modified by the process itself in user mode, + * allowing programs to debug themselves without the ptrace() + * interface. + */ + if ((tsk->ptrace & (PT_DTRACE|PT_PTRACED)) == PT_DTRACE) + goto clear_TF; + } + + /* Ok, finally something we can handle */ + tsk->thread.trap_no = 1; + tsk->thread.error_code = error_code; + info.si_signo = SIGTRAP; + info.si_errno = 0; + info.si_code = TRAP_BRKPT; + + /* If this is a kernel mode trap, save the user PC on entry to + * the kernel, that's what the debugger can make sense of. */ + info.si_addr = ((regs->xcs & 3) == 0) ? (void *)tsk->thread.eip : + (void *)regs->eip; + force_sig_info(SIGTRAP, &info, tsk); + + /* Disable additional traps. They'll be re-enabled when + * the signal is delivered. + */ + clear_dr7: + HYPERVISOR_set_debugreg(7, 0); + return; + + clear_TF: regs->eflags &= ~TF_MASK; + return; } diff --git a/xenolinux-2.4.16-sparse/include/asm-xeno/hypervisor.h b/xenolinux-2.4.16-sparse/include/asm-xeno/hypervisor.h index 522f124e86..f46bfe4c44 100644 --- a/xenolinux-2.4.16-sparse/include/asm-xeno/hypervisor.h +++ b/xenolinux-2.4.16-sparse/include/asm-xeno/hypervisor.h @@ -163,4 +163,26 @@ static inline int HYPERVISOR_network_op(void *network_op) return ret; } +static inline int HYPERVISOR_set_debugreg(int reg, unsigned long value) +{ + int ret; + __asm__ __volatile__ ( + TRAP_INSTR + : "=a" (ret) : "0" (__HYPERVISOR_set_debugreg), + "b" (reg), "c" (value) ); + + return ret; +} + +static inline unsigned long HYPERVISOR_get_debugreg(int reg) +{ + unsigned long ret; + __asm__ __volatile__ ( + TRAP_INSTR + : "=a" (ret) : "0" (__HYPERVISOR_get_debugreg), + "b" (reg) ); + + return ret; +} + #endif /* __HYPERVISOR_H__ */ |