| 1 | /* |
|---|
| 2 | * Hypercall implementations |
|---|
| 3 | * |
|---|
| 4 | * Copyright (C) 2005 Hewlett-Packard Co. |
|---|
| 5 | * Dan Magenheimer (dan.magenheimer@hp.com) |
|---|
| 6 | * |
|---|
| 7 | */ |
|---|
| 8 | |
|---|
| 9 | #include <xen/config.h> |
|---|
| 10 | #include <xen/sched.h> |
|---|
| 11 | #include <xen/hypercall.h> |
|---|
| 12 | #include <xen/multicall.h> |
|---|
| 13 | #include <xen/guest_access.h> |
|---|
| 14 | #include <xen/mm.h> |
|---|
| 15 | |
|---|
| 16 | #include <linux/efi.h> /* FOR EFI_UNIMPLEMENTED */ |
|---|
| 17 | #include <asm/sal.h> /* FOR struct ia64_sal_retval */ |
|---|
| 18 | #include <asm/fpswa.h> /* FOR struct fpswa_ret_t */ |
|---|
| 19 | |
|---|
| 20 | #include <asm/vmx_vcpu.h> |
|---|
| 21 | #include <asm/vcpu.h> |
|---|
| 22 | #include <asm/dom_fw.h> |
|---|
| 23 | #include <public/domctl.h> |
|---|
| 24 | #include <public/sysctl.h> |
|---|
| 25 | #include <public/event_channel.h> |
|---|
| 26 | #include <public/memory.h> |
|---|
| 27 | #include <public/sched.h> |
|---|
| 28 | #include <xen/irq.h> |
|---|
| 29 | #include <asm/hw_irq.h> |
|---|
| 30 | #include <public/physdev.h> |
|---|
| 31 | #include <xen/domain.h> |
|---|
| 32 | #include <public/callback.h> |
|---|
| 33 | #include <xen/event.h> |
|---|
| 34 | #include <xen/perfc.h> |
|---|
| 35 | |
|---|
| 36 | extern long do_physdev_op(int cmd, XEN_GUEST_HANDLE(void) arg); |
|---|
| 37 | extern long do_callback_op(int cmd, XEN_GUEST_HANDLE(void) arg); |
|---|
| 38 | |
|---|
| 39 | static IA64FAULT |
|---|
| 40 | xen_hypercall (struct pt_regs *regs) |
|---|
| 41 | { |
|---|
| 42 | uint32_t cmd = (uint32_t)regs->r2; |
|---|
| 43 | printk("Warning %s should not be called %d\n", __FUNCTION__, cmd); |
|---|
| 44 | return IA64_NO_FAULT; |
|---|
| 45 | } |
|---|
| 46 | |
|---|
| 47 | static IA64FAULT |
|---|
| 48 | xen_fast_hypercall (struct pt_regs *regs) |
|---|
| 49 | { |
|---|
| 50 | uint32_t cmd = (uint32_t)regs->r2; |
|---|
| 51 | switch (cmd) { |
|---|
| 52 | case __HYPERVISOR_ia64_fast_eoi: |
|---|
| 53 | printk("Warning %s should not be called %d\n", |
|---|
| 54 | __FUNCTION__, cmd); |
|---|
| 55 | break; |
|---|
| 56 | default: |
|---|
| 57 | regs->r8 = -ENOSYS; |
|---|
| 58 | } |
|---|
| 59 | return IA64_NO_FAULT; |
|---|
| 60 | } |
|---|
| 61 | |
|---|
| 62 | long do_pirq_guest_eoi(int pirq) |
|---|
| 63 | { |
|---|
| 64 | return pirq_guest_eoi(current->domain, pirq); |
|---|
| 65 | } |
|---|
| 66 | |
|---|
| 67 | |
|---|
| 68 | static void |
|---|
| 69 | fw_hypercall_ipi (struct pt_regs *regs) |
|---|
| 70 | { |
|---|
| 71 | int cpu = regs->r14; |
|---|
| 72 | int vector = regs->r15; |
|---|
| 73 | struct vcpu *targ; |
|---|
| 74 | struct domain *d = current->domain; |
|---|
| 75 | |
|---|
| 76 | /* Be sure the target exists. */ |
|---|
| 77 | if (cpu > MAX_VIRT_CPUS) |
|---|
| 78 | return; |
|---|
| 79 | targ = d->vcpu[cpu]; |
|---|
| 80 | if (targ == NULL) |
|---|
| 81 | return; |
|---|
| 82 | |
|---|
| 83 | if (vector == XEN_SAL_BOOT_RENDEZ_VEC |
|---|
| 84 | && (!targ->is_initialised |
|---|
| 85 | || test_bit(_VPF_down, &targ->pause_flags))) { |
|---|
| 86 | |
|---|
| 87 | /* First start: initialize vpcu. */ |
|---|
| 88 | if (!targ->is_initialised) { |
|---|
| 89 | struct vcpu_guest_context c; |
|---|
| 90 | |
|---|
| 91 | memset (&c, 0, sizeof (c)); |
|---|
| 92 | |
|---|
| 93 | if (arch_set_info_guest (targ, &c) != 0) { |
|---|
| 94 | printk ("arch_boot_vcpu: failure\n"); |
|---|
| 95 | return; |
|---|
| 96 | } |
|---|
| 97 | } |
|---|
| 98 | |
|---|
| 99 | /* First or next rendez-vous: set registers. */ |
|---|
| 100 | vcpu_init_regs (targ); |
|---|
| 101 | vcpu_regs (targ)->cr_iip = d->arch.sal_data->boot_rdv_ip; |
|---|
| 102 | vcpu_regs (targ)->r1 = d->arch.sal_data->boot_rdv_r1; |
|---|
| 103 | vcpu_regs (targ)->b0 = FW_HYPERCALL_SAL_RETURN_PADDR; |
|---|
| 104 | |
|---|
| 105 | if (test_and_clear_bit(_VPF_down, |
|---|
| 106 | &targ->pause_flags)) { |
|---|
| 107 | vcpu_wake(targ); |
|---|
| 108 | printk(XENLOG_INFO "arch_boot_vcpu: vcpu %d awaken\n", |
|---|
| 109 | targ->vcpu_id); |
|---|
| 110 | } |
|---|
| 111 | else |
|---|
| 112 | printk ("arch_boot_vcpu: huu, already awaken!\n"); |
|---|
| 113 | } |
|---|
| 114 | else { |
|---|
| 115 | int running = targ->is_running; |
|---|
| 116 | vcpu_pend_interrupt(targ, vector); |
|---|
| 117 | vcpu_unblock(targ); |
|---|
| 118 | if (running) |
|---|
| 119 | smp_send_event_check_cpu(targ->processor); |
|---|
| 120 | } |
|---|
| 121 | return; |
|---|
| 122 | } |
|---|
| 123 | |
|---|
| 124 | static fpswa_ret_t |
|---|
| 125 | fw_hypercall_fpswa (struct vcpu *v) |
|---|
| 126 | { |
|---|
| 127 | return PSCBX(v, fpswa_ret); |
|---|
| 128 | } |
|---|
| 129 | |
|---|
| 130 | IA64FAULT |
|---|
| 131 | ia64_hypercall(struct pt_regs *regs) |
|---|
| 132 | { |
|---|
| 133 | struct vcpu *v = current; |
|---|
| 134 | struct sal_ret_values x; |
|---|
| 135 | efi_status_t efi_ret_value; |
|---|
| 136 | fpswa_ret_t fpswa_ret; |
|---|
| 137 | IA64FAULT fault; |
|---|
| 138 | unsigned long index = regs->r2 & FW_HYPERCALL_NUM_MASK_HIGH; |
|---|
| 139 | |
|---|
| 140 | perfc_incra(fw_hypercall, index >> 8); |
|---|
| 141 | switch (index) { |
|---|
| 142 | case FW_HYPERCALL_XEN: |
|---|
| 143 | return xen_hypercall(regs); |
|---|
| 144 | |
|---|
| 145 | case FW_HYPERCALL_XEN_FAST: |
|---|
| 146 | return xen_fast_hypercall(regs); |
|---|
| 147 | |
|---|
| 148 | case FW_HYPERCALL_PAL_CALL: |
|---|
| 149 | //printk("*** PAL hypercall: index=%d\n",regs->r28); |
|---|
| 150 | //FIXME: This should call a C routine |
|---|
| 151 | #if 0 |
|---|
| 152 | // This is very conservative, but avoids a possible |
|---|
| 153 | // (and deadly) freeze in paravirtualized domains due |
|---|
| 154 | // to a yet-to-be-found bug where pending_interruption |
|---|
| 155 | // is zero when it shouldn't be. Since PAL is called |
|---|
| 156 | // in the idle loop, this should resolve it |
|---|
| 157 | VCPU(v,pending_interruption) = 1; |
|---|
| 158 | #endif |
|---|
| 159 | if (regs->r28 == PAL_HALT_LIGHT) { |
|---|
| 160 | if (vcpu_deliverable_interrupts(v) || |
|---|
| 161 | event_pending(v)) { |
|---|
| 162 | perfc_incr(idle_when_pending); |
|---|
| 163 | vcpu_pend_unspecified_interrupt(v); |
|---|
| 164 | //printk("idle w/int#%d pending!\n",pi); |
|---|
| 165 | //this shouldn't happen, but it apparently does quite a bit! so don't |
|---|
| 166 | //allow it to happen... i.e. if a domain has an interrupt pending and |
|---|
| 167 | //it tries to halt itself because it thinks it is idle, just return here |
|---|
| 168 | //as deliver_pending_interrupt is called on the way out and will deliver it |
|---|
| 169 | } |
|---|
| 170 | else { |
|---|
| 171 | perfc_incr(pal_halt_light); |
|---|
| 172 | migrate_timer(&v->arch.hlt_timer, |
|---|
| 173 | v->processor); |
|---|
| 174 | set_timer(&v->arch.hlt_timer, |
|---|
| 175 | vcpu_get_next_timer_ns(v)); |
|---|
| 176 | do_sched_op_compat(SCHEDOP_block, 0); |
|---|
| 177 | /* do_block only pends a softirq */ |
|---|
| 178 | do_softirq(); |
|---|
| 179 | stop_timer(&v->arch.hlt_timer); |
|---|
| 180 | } |
|---|
| 181 | regs->r8 = 0; |
|---|
| 182 | regs->r9 = 0; |
|---|
| 183 | regs->r10 = 0; |
|---|
| 184 | regs->r11 = 0; |
|---|
| 185 | } |
|---|
| 186 | else { |
|---|
| 187 | struct ia64_pal_retval y; |
|---|
| 188 | |
|---|
| 189 | if (regs->r28 >= PAL_COPY_PAL) |
|---|
| 190 | y = xen_pal_emulator |
|---|
| 191 | (regs->r28, vcpu_get_gr (v, 33), |
|---|
| 192 | vcpu_get_gr (v, 34), |
|---|
| 193 | vcpu_get_gr (v, 35)); |
|---|
| 194 | else |
|---|
| 195 | y = xen_pal_emulator(regs->r28,regs->r29, |
|---|
| 196 | regs->r30,regs->r31); |
|---|
| 197 | regs->r8 = y.status; regs->r9 = y.v0; |
|---|
| 198 | regs->r10 = y.v1; regs->r11 = y.v2; |
|---|
| 199 | } |
|---|
| 200 | break; |
|---|
| 201 | case FW_HYPERCALL_SAL_CALL: |
|---|
| 202 | x = sal_emulator(vcpu_get_gr(v,32),vcpu_get_gr(v,33), |
|---|
| 203 | vcpu_get_gr(v,34),vcpu_get_gr(v,35), |
|---|
| 204 | vcpu_get_gr(v,36),vcpu_get_gr(v,37), |
|---|
| 205 | vcpu_get_gr(v,38),vcpu_get_gr(v,39)); |
|---|
| 206 | regs->r8 = x.r8; regs->r9 = x.r9; |
|---|
| 207 | regs->r10 = x.r10; regs->r11 = x.r11; |
|---|
| 208 | break; |
|---|
| 209 | case FW_HYPERCALL_SAL_RETURN: |
|---|
| 210 | if ( !test_and_set_bit(_VPF_down, &v->pause_flags) ) |
|---|
| 211 | vcpu_sleep_nosync(v); |
|---|
| 212 | break; |
|---|
| 213 | case FW_HYPERCALL_EFI_CALL: |
|---|
| 214 | efi_ret_value = efi_emulator (regs, &fault); |
|---|
| 215 | if (fault != IA64_NO_FAULT) return fault; |
|---|
| 216 | regs->r8 = efi_ret_value; |
|---|
| 217 | break; |
|---|
| 218 | case FW_HYPERCALL_IPI: |
|---|
| 219 | fw_hypercall_ipi (regs); |
|---|
| 220 | break; |
|---|
| 221 | case FW_HYPERCALL_SET_SHARED_INFO_VA: |
|---|
| 222 | regs->r8 = domain_set_shared_info_va (regs->r28); |
|---|
| 223 | break; |
|---|
| 224 | case FW_HYPERCALL_FPSWA: |
|---|
| 225 | fpswa_ret = fw_hypercall_fpswa (v); |
|---|
| 226 | regs->r8 = fpswa_ret.status; |
|---|
| 227 | regs->r9 = fpswa_ret.err0; |
|---|
| 228 | regs->r10 = fpswa_ret.err1; |
|---|
| 229 | regs->r11 = fpswa_ret.err2; |
|---|
| 230 | break; |
|---|
| 231 | default: |
|---|
| 232 | printk("unknown ia64 fw hypercall %lx\n", regs->r2); |
|---|
| 233 | regs->r8 = do_ni_hypercall(); |
|---|
| 234 | } |
|---|
| 235 | return IA64_NO_FAULT; |
|---|
| 236 | } |
|---|
| 237 | |
|---|
| 238 | unsigned long hypercall_create_continuation( |
|---|
| 239 | unsigned int op, const char *format, ...) |
|---|
| 240 | { |
|---|
| 241 | struct mc_state *mcs = &this_cpu(mc_state); |
|---|
| 242 | struct vcpu *v = current; |
|---|
| 243 | const char *p = format; |
|---|
| 244 | unsigned long arg; |
|---|
| 245 | unsigned int i; |
|---|
| 246 | va_list args; |
|---|
| 247 | |
|---|
| 248 | va_start(args, format); |
|---|
| 249 | if (test_bit(_MCSF_in_multicall, &mcs->flags)) |
|---|
| 250 | panic("PREEMPT happen in multicall\n"); // Not support yet |
|---|
| 251 | |
|---|
| 252 | vcpu_set_gr(v, 15, op, 0); |
|---|
| 253 | |
|---|
| 254 | for (i = 0; *p != '\0'; i++) { |
|---|
| 255 | switch ( *p++ ) |
|---|
| 256 | { |
|---|
| 257 | case 'i': |
|---|
| 258 | arg = (unsigned long)va_arg(args, unsigned int); |
|---|
| 259 | break; |
|---|
| 260 | case 'l': |
|---|
| 261 | arg = (unsigned long)va_arg(args, unsigned long); |
|---|
| 262 | break; |
|---|
| 263 | case 'h': |
|---|
| 264 | arg = (unsigned long)va_arg(args, void *); |
|---|
| 265 | break; |
|---|
| 266 | default: |
|---|
| 267 | arg = 0; |
|---|
| 268 | BUG(); |
|---|
| 269 | } |
|---|
| 270 | vcpu_set_gr(v, 16 + i, arg, 0); |
|---|
| 271 | } |
|---|
| 272 | |
|---|
| 273 | if (i >= 6) |
|---|
| 274 | panic("Too many args for hypercall continuation\n"); |
|---|
| 275 | |
|---|
| 276 | // Clean other argument to 0 |
|---|
| 277 | while (i < 6) { |
|---|
| 278 | vcpu_set_gr(v, 16 + i, 0, 0); |
|---|
| 279 | i++; |
|---|
| 280 | } |
|---|
| 281 | |
|---|
| 282 | // re-execute break; |
|---|
| 283 | vcpu_decrement_iip(v); |
|---|
| 284 | |
|---|
| 285 | v->arch.hypercall_continuation = 1; |
|---|
| 286 | va_end(args); |
|---|
| 287 | return op; |
|---|
| 288 | } |
|---|
| 289 | |
|---|
| 290 | /* Need make this function common */ |
|---|
| 291 | extern int |
|---|
| 292 | iosapic_guest_read( |
|---|
| 293 | unsigned long physbase, unsigned int reg, u32 *pval); |
|---|
| 294 | extern int |
|---|
| 295 | iosapic_guest_write( |
|---|
| 296 | unsigned long physbase, unsigned int reg, u32 pval); |
|---|
| 297 | |
|---|
| 298 | long do_physdev_op(int cmd, XEN_GUEST_HANDLE(void) arg) |
|---|
| 299 | { |
|---|
| 300 | int irq; |
|---|
| 301 | long ret; |
|---|
| 302 | |
|---|
| 303 | switch ( cmd ) |
|---|
| 304 | { |
|---|
| 305 | case PHYSDEVOP_eoi: { |
|---|
| 306 | struct physdev_eoi eoi; |
|---|
| 307 | ret = -EFAULT; |
|---|
| 308 | if ( copy_from_guest(&eoi, arg, 1) != 0 ) |
|---|
| 309 | break; |
|---|
| 310 | ret = pirq_guest_eoi(current->domain, eoi.irq); |
|---|
| 311 | break; |
|---|
| 312 | } |
|---|
| 313 | |
|---|
| 314 | /* Legacy since 0x00030202. */ |
|---|
| 315 | case PHYSDEVOP_IRQ_UNMASK_NOTIFY: { |
|---|
| 316 | ret = pirq_guest_unmask(current->domain); |
|---|
| 317 | break; |
|---|
| 318 | } |
|---|
| 319 | |
|---|
| 320 | case PHYSDEVOP_irq_status_query: { |
|---|
| 321 | struct physdev_irq_status_query irq_status_query; |
|---|
| 322 | ret = -EFAULT; |
|---|
| 323 | if ( copy_from_guest(&irq_status_query, arg, 1) != 0 ) |
|---|
| 324 | break; |
|---|
| 325 | irq = irq_status_query.irq; |
|---|
| 326 | ret = -EINVAL; |
|---|
| 327 | if ( (irq < 0) || (irq >= NR_IRQS) ) |
|---|
| 328 | break; |
|---|
| 329 | irq_status_query.flags = 0; |
|---|
| 330 | /* Edge-triggered interrupts don't need an explicit unmask downcall. */ |
|---|
| 331 | if ( !strstr(irq_desc[irq_to_vector(irq)].handler->typename, "edge") ) |
|---|
| 332 | irq_status_query.flags |= XENIRQSTAT_needs_eoi; |
|---|
| 333 | ret = copy_to_guest(arg, &irq_status_query, 1) ? -EFAULT : 0; |
|---|
| 334 | break; |
|---|
| 335 | } |
|---|
| 336 | |
|---|
| 337 | case PHYSDEVOP_apic_read: { |
|---|
| 338 | struct physdev_apic apic; |
|---|
| 339 | ret = -EFAULT; |
|---|
| 340 | if ( copy_from_guest(&apic, arg, 1) != 0 ) |
|---|
| 341 | break; |
|---|
| 342 | ret = -EPERM; |
|---|
| 343 | if ( !IS_PRIV(current->domain) ) |
|---|
| 344 | break; |
|---|
| 345 | ret = iosapic_guest_read(apic.apic_physbase, apic.reg, &apic.value); |
|---|
| 346 | if ( copy_to_guest(arg, &apic, 1) != 0 ) |
|---|
| 347 | ret = -EFAULT; |
|---|
| 348 | break; |
|---|
| 349 | } |
|---|
| 350 | |
|---|
| 351 | case PHYSDEVOP_apic_write: { |
|---|
| 352 | struct physdev_apic apic; |
|---|
| 353 | ret = -EFAULT; |
|---|
| 354 | if ( copy_from_guest(&apic, arg, 1) != 0 ) |
|---|
| 355 | break; |
|---|
| 356 | ret = -EPERM; |
|---|
| 357 | if ( !IS_PRIV(current->domain) ) |
|---|
| 358 | break; |
|---|
| 359 | ret = iosapic_guest_write(apic.apic_physbase, apic.reg, apic.value); |
|---|
| 360 | break; |
|---|
| 361 | } |
|---|
| 362 | |
|---|
| 363 | case PHYSDEVOP_alloc_irq_vector: { |
|---|
| 364 | struct physdev_irq irq_op; |
|---|
| 365 | |
|---|
| 366 | ret = -EFAULT; |
|---|
| 367 | if ( copy_from_guest(&irq_op, arg, 1) != 0 ) |
|---|
| 368 | break; |
|---|
| 369 | |
|---|
| 370 | ret = -EPERM; |
|---|
| 371 | if ( !IS_PRIV(current->domain) ) |
|---|
| 372 | break; |
|---|
| 373 | |
|---|
| 374 | ret = -EINVAL; |
|---|
| 375 | if ( (irq = irq_op.irq) >= NR_IRQS ) |
|---|
| 376 | break; |
|---|
| 377 | |
|---|
| 378 | irq_op.vector = assign_irq_vector(irq); |
|---|
| 379 | ret = copy_to_guest(arg, &irq_op, 1) ? -EFAULT : 0; |
|---|
| 380 | break; |
|---|
| 381 | } |
|---|
| 382 | |
|---|
| 383 | case PHYSDEVOP_free_irq_vector: { |
|---|
| 384 | struct physdev_irq irq_op; |
|---|
| 385 | int vector; |
|---|
| 386 | |
|---|
| 387 | ret = -EFAULT; |
|---|
| 388 | if ( copy_from_guest(&irq_op, arg, 1) != 0 ) |
|---|
| 389 | break; |
|---|
| 390 | |
|---|
| 391 | ret = -EPERM; |
|---|
| 392 | if ( !IS_PRIV(current->domain) ) |
|---|
| 393 | break; |
|---|
| 394 | |
|---|
| 395 | ret = -EINVAL; |
|---|
| 396 | vector = irq_op.vector; |
|---|
| 397 | if (vector < IA64_FIRST_DEVICE_VECTOR || |
|---|
| 398 | vector > IA64_LAST_DEVICE_VECTOR) |
|---|
| 399 | break; |
|---|
| 400 | |
|---|
| 401 | /* XXX This should be called, but causes a NAT consumption via the |
|---|
| 402 | * reboot notifier_call_chain in dom0 if a device is hidden for |
|---|
| 403 | * a driver domain using pciback.hide= (specifically, hiding function |
|---|
| 404 | * 1 of a 2 port e1000 card). |
|---|
| 405 | * free_irq_vector(vector); |
|---|
| 406 | */ |
|---|
| 407 | ret = 0; |
|---|
| 408 | break; |
|---|
| 409 | } |
|---|
| 410 | |
|---|
| 411 | default: |
|---|
| 412 | ret = -ENOSYS; |
|---|
| 413 | break; |
|---|
| 414 | } |
|---|
| 415 | |
|---|
| 416 | return ret; |
|---|
| 417 | } |
|---|
| 418 | |
|---|
| 419 | static long register_guest_callback(struct callback_register *reg) |
|---|
| 420 | { |
|---|
| 421 | long ret = 0; |
|---|
| 422 | struct vcpu *v = current; |
|---|
| 423 | |
|---|
| 424 | if (IS_VMM_ADDRESS(reg->address)) |
|---|
| 425 | return -EINVAL; |
|---|
| 426 | |
|---|
| 427 | switch ( reg->type ) |
|---|
| 428 | { |
|---|
| 429 | case CALLBACKTYPE_event: |
|---|
| 430 | v->arch.event_callback_ip = reg->address; |
|---|
| 431 | break; |
|---|
| 432 | |
|---|
| 433 | case CALLBACKTYPE_failsafe: |
|---|
| 434 | v->arch.failsafe_callback_ip = reg->address; |
|---|
| 435 | break; |
|---|
| 436 | |
|---|
| 437 | default: |
|---|
| 438 | ret = -ENOSYS; |
|---|
| 439 | break; |
|---|
| 440 | } |
|---|
| 441 | |
|---|
| 442 | return ret; |
|---|
| 443 | } |
|---|
| 444 | |
|---|
| 445 | static long unregister_guest_callback(struct callback_unregister *unreg) |
|---|
| 446 | { |
|---|
| 447 | return -EINVAL; |
|---|
| 448 | } |
|---|
| 449 | |
|---|
| 450 | /* First time to add callback to xen/ia64, so let's just stick to |
|---|
| 451 | * the newer callback interface. |
|---|
| 452 | */ |
|---|
| 453 | long do_callback_op(int cmd, XEN_GUEST_HANDLE(void) arg) |
|---|
| 454 | { |
|---|
| 455 | long ret; |
|---|
| 456 | |
|---|
| 457 | switch ( cmd ) |
|---|
| 458 | { |
|---|
| 459 | case CALLBACKOP_register: |
|---|
| 460 | { |
|---|
| 461 | struct callback_register reg; |
|---|
| 462 | |
|---|
| 463 | ret = -EFAULT; |
|---|
| 464 | if ( copy_from_guest(®, arg, 1) ) |
|---|
| 465 | break; |
|---|
| 466 | |
|---|
| 467 | ret = register_guest_callback(®); |
|---|
| 468 | } |
|---|
| 469 | break; |
|---|
| 470 | |
|---|
| 471 | case CALLBACKOP_unregister: |
|---|
| 472 | { |
|---|
| 473 | struct callback_unregister unreg; |
|---|
| 474 | |
|---|
| 475 | ret = -EFAULT; |
|---|
| 476 | if ( copy_from_guest(&unreg, arg, 1) ) |
|---|
| 477 | break; |
|---|
| 478 | |
|---|
| 479 | ret = unregister_guest_callback(&unreg); |
|---|
| 480 | } |
|---|
| 481 | break; |
|---|
| 482 | |
|---|
| 483 | default: |
|---|
| 484 | ret = -ENOSYS; |
|---|
| 485 | break; |
|---|
| 486 | } |
|---|
| 487 | |
|---|
| 488 | return ret; |
|---|
| 489 | } |
|---|