diff options
-rw-r--r-- | xen/arch/arm/domain.c | 87 | ||||
-rw-r--r-- | xen/arch/arm/dummy.S | 1 | ||||
-rw-r--r-- | xen/arch/arm/traps.c | 29 | ||||
-rw-r--r-- | xen/include/asm-arm/bitops.h | 9 |
4 files changed, 115 insertions, 11 deletions
diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c index ac6a30dfde..57d8746fa3 100644 --- a/xen/arch/arm/domain.c +++ b/xen/arch/arm/domain.c @@ -5,6 +5,7 @@ #include <xen/softirq.h> #include <xen/wait.h> #include <xen/errno.h> +#include <xen/bitops.h> #include <asm/current.h> #include <asm/regs.h> @@ -224,6 +225,92 @@ void sync_vcpu_execstate(struct vcpu *v) /* Nothing to do -- no lazy switching */ } +#define next_arg(fmt, args) ({ \ + unsigned long __arg; \ + switch ( *(fmt)++ ) \ + { \ + case 'i': __arg = (unsigned long)va_arg(args, unsigned int); break; \ + case 'l': __arg = (unsigned long)va_arg(args, unsigned long); break; \ + case 'h': __arg = (unsigned long)va_arg(args, void *); break; \ + default: __arg = 0; BUG(); \ + } \ + __arg; \ +}) + +void hypercall_cancel_continuation(void) +{ + struct cpu_user_regs *regs = guest_cpu_user_regs(); + struct mc_state *mcs = ¤t->mc_state; + + if ( test_bit(_MCSF_in_multicall, &mcs->flags) ) + { + __clear_bit(_MCSF_call_preempted, &mcs->flags); + } + else + { + regs->pc += 4; /* undo re-execute 'hvc #XEN_HYPERCALL_TAG' */ + } +} + +unsigned long hypercall_create_continuation( + unsigned int op, const char *format, ...) +{ + struct mc_state *mcs = ¤t->mc_state; + struct cpu_user_regs *regs; + const char *p = format; + unsigned long arg, rc; + unsigned int i; + va_list args; + + /* All hypercalls take at least one argument */ + BUG_ON( !p || *p == '\0' ); + + va_start(args, format); + + if ( test_bit(_MCSF_in_multicall, &mcs->flags) ) + { + BUG(); /* XXX multicalls not implemented yet. */ + + __set_bit(_MCSF_call_preempted, &mcs->flags); + + for ( i = 0; *p != '\0'; i++ ) + mcs->call.args[i] = next_arg(p, args); + + /* Return value gets written back to mcs->call.result */ + rc = mcs->call.result; + } + else + { + regs = guest_cpu_user_regs(); + regs->r12 = op; + + /* Ensure the hypercall trap instruction is re-executed. */ + regs->pc -= 4; /* re-execute 'hvc #XEN_HYPERCALL_TAG' */ + + for ( i = 0; *p != '\0'; i++ ) + { + arg = next_arg(p, args); + + switch ( i ) + { + case 0: regs->r0 = arg; break; + case 1: regs->r1 = arg; break; + case 2: regs->r2 = arg; break; + case 3: regs->r3 = arg; break; + case 4: regs->r4 = arg; break; + case 5: regs->r5 = arg; break; + } + } + + /* Return value gets written back to r0 */ + rc = regs->r0; + } + + va_end(args); + + return rc; +} + void startup_cpu_idle_loop(void) { struct vcpu *v = current; diff --git a/xen/arch/arm/dummy.S b/xen/arch/arm/dummy.S index cab952233b..5406077232 100644 --- a/xen/arch/arm/dummy.S +++ b/xen/arch/arm/dummy.S @@ -46,7 +46,6 @@ DUMMY(domain_relinquish_resources); DUMMY(domain_set_time_offset); DUMMY(dom_cow); DUMMY(gmfn_to_mfn); -DUMMY(hypercall_create_continuation); DUMMY(send_timer_event); DUMMY(share_xen_page_with_privileged_guests); DUMMY(wallclock_time); diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c index d2adf4fee1..e4bed69291 100644 --- a/xen/arch/arm/traps.c +++ b/xen/arch/arm/traps.c @@ -470,6 +470,9 @@ static void do_debug_trap(struct cpu_user_regs *regs, unsigned int code) static void do_trap_hypercall(struct cpu_user_regs *regs, unsigned long iss) { arm_hypercall_fn_t call = NULL; +#ifndef NDEBUG + uint32_t orig_pc = regs->pc; +#endif if ( iss != XEN_HYPERCALL_TAG ) { @@ -495,17 +498,23 @@ static void do_trap_hypercall(struct cpu_user_regs *regs, unsigned long iss) regs->r0 = call(regs->r0, regs->r1, regs->r2, regs->r3, regs->r4); #ifndef NDEBUG - /* Clobber argument registers */ - switch ( arm_hypercall_table[regs->r12].nr_args ) { - case 5: regs->r4 = 0xDEADBEEF; - case 4: regs->r3 = 0xDEADBEEF; - case 3: regs->r2 = 0xDEADBEEF; - case 2: regs->r1 = 0xDEADBEEF; - case 1: /* Don't clobber r0 -- it's the return value */ - break; - default: BUG(); + /* + * Clobber argument registers only if pc is unchanged, otherwise + * this is a hypercall continuation. + */ + if ( orig_pc == regs->pc ) + { + switch ( arm_hypercall_table[regs->r12].nr_args ) { + case 5: regs->r4 = 0xDEADBEEF; + case 4: regs->r3 = 0xDEADBEEF; + case 3: regs->r2 = 0xDEADBEEF; + case 2: regs->r1 = 0xDEADBEEF; + case 1: /* Don't clobber r0 -- it's the return value */ + break; + default: BUG(); + } + regs->r12 = 0xDEADBEEF; } - regs->r12 = 0xDEADBEEF; #endif } diff --git a/xen/include/asm-arm/bitops.h b/xen/include/asm-arm/bitops.h index e5c1781506..87de5db167 100644 --- a/xen/include/asm-arm/bitops.h +++ b/xen/include/asm-arm/bitops.h @@ -23,6 +23,15 @@ extern int _test_and_change_bit(int nr, volatile void * p); #define test_and_clear_bit(n,p) _test_and_clear_bit(n,p) #define test_and_change_bit(n,p) _test_and_change_bit(n,p) +/* + * Non-atomic bit manipulation. + * + * Implemented using atomics to be interrupt safe. Could alternatively + * implement with local interrupt masking. + */ +#define __set_bit(n,p) _set_bit(n,p) +#define __clear_bit(n,p) _clear_bit(n,p) + #define BIT(nr) (1UL << (nr)) #define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) #define BIT_WORD(nr) ((nr) / BITS_PER_LONG) |