1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2023 MediaTek Inc. 4 */ 5 6 #include <linux/device.h> 7 #include <linux/soc/mediatek/gzvm_drv.h> 8 #include <linux/sched.h> 9 #include <linux/rcuwait.h> 10 11 /** 12 * gzvm_handle_guest_exception() - Handle guest exception 13 * @vcpu: Pointer to struct gzvm_vcpu_run in userspace 14 * Return: 15 * * true - This exception has been processed, no need to back to VMM. 16 * * false - This exception has not been processed, require userspace. 17 */ gzvm_handle_guest_exception(struct gzvm_vcpu * vcpu)18bool gzvm_handle_guest_exception(struct gzvm_vcpu *vcpu) 19 { 20 int ret; 21 22 for (int i = 0; i < ARRAY_SIZE(vcpu->run->exception.reserved); i++) { 23 if (vcpu->run->exception.reserved[i]) 24 return false; 25 } 26 27 switch (vcpu->run->exception.exception) { 28 case GZVM_EXCEPTION_PAGE_FAULT: 29 ret = gzvm_handle_page_fault(vcpu); 30 break; 31 case GZVM_EXCEPTION_UNKNOWN: 32 fallthrough; 33 default: 34 ret = -EFAULT; 35 } 36 37 if (!ret) 38 return true; 39 else 40 return false; 41 } 42 43 /** 44 * gzvm_handle_guest_hvc() - Handle guest hvc 45 * @vcpu: Pointer to struct gzvm_vcpu struct 46 * Return: 47 * * true - This hvc has been processed, no need to back to VMM. 48 * * false - This hvc has not been processed, require userspace. 49 */ gzvm_handle_guest_hvc(struct gzvm_vcpu * vcpu)50bool gzvm_handle_guest_hvc(struct gzvm_vcpu *vcpu) 51 { 52 unsigned long ipa; 53 int ret; 54 55 switch (vcpu->run->hypercall.args[0]) { 56 case GZVM_HVC_MEM_RELINQUISH: 57 ipa = vcpu->run->hypercall.args[1]; 58 ret = gzvm_handle_relinquish(vcpu, ipa); 59 return (ret == 0) ? true : false; 60 default: 61 return gzvm_arch_handle_guest_hvc(vcpu); 62 } 63 } 64 vcpu_block_wait(struct gzvm_vcpu * vcpu)65static void vcpu_block_wait(struct gzvm_vcpu *vcpu) 66 { 67 struct rcuwait *wait = &vcpu->wait; 68 69 prepare_to_rcuwait(wait); 70 71 while (true) { 72 set_current_state(TASK_INTERRUPTIBLE); 73 if (vcpu->idle_events.virtio_irq) { 74 vcpu->idle_events.virtio_irq = 0; 75 break; 76 } 77 if (vcpu->idle_events.vtimer_irq) { 78 vcpu->idle_events.vtimer_irq = 0; 79 break; 80 } 81 if (signal_pending(current)) 82 break; 83 schedule(); 84 } 85 finish_rcuwait(wait); 86 } 87 88 /** 89 * gzvm_handle_guest_idle() - Handle guest vm entering idle 90 * @vcpu: Pointer to struct gzvm_vcpu struct 91 * Return: 92 */ gzvm_handle_guest_idle(struct gzvm_vcpu * vcpu)93int gzvm_handle_guest_idle(struct gzvm_vcpu *vcpu) 94 { 95 int ret = 0; 96 u64 ns = 0; 97 98 ns = gzvm_vcpu_arch_get_timer_delay_ns(vcpu); 99 100 if (ns) { 101 gzvm_vtimer_set(vcpu, ns); 102 vcpu_block_wait(vcpu); 103 gzvm_vtimer_release(vcpu); 104 } 105 106 return ret; 107 } 108 gzvm_handle_guest_ipi(struct gzvm_vcpu * vcpu)109void gzvm_handle_guest_ipi(struct gzvm_vcpu *vcpu) 110 { 111 gzvm_vcpu_wakeup_all(vcpu->gzvm); 112 } 113