113 lines
2.3 KiB
C
Executable File
113 lines
2.3 KiB
C
Executable File
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2023 MediaTek Inc.
|
|
*/
|
|
|
|
#include <linux/device.h>
|
|
#include <linux/soc/mediatek/gzvm_drv.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/rcuwait.h>
|
|
|
|
/**
|
|
* gzvm_handle_guest_exception() - Handle guest exception
|
|
* @vcpu: Pointer to struct gzvm_vcpu_run in userspace
|
|
* Return:
|
|
* * true - This exception has been processed, no need to back to VMM.
|
|
* * false - This exception has not been processed, require userspace.
|
|
*/
|
|
bool gzvm_handle_guest_exception(struct gzvm_vcpu *vcpu)
|
|
{
|
|
int ret;
|
|
|
|
for (int i = 0; i < ARRAY_SIZE(vcpu->run->exception.reserved); i++) {
|
|
if (vcpu->run->exception.reserved[i])
|
|
return false;
|
|
}
|
|
|
|
switch (vcpu->run->exception.exception) {
|
|
case GZVM_EXCEPTION_PAGE_FAULT:
|
|
ret = gzvm_handle_page_fault(vcpu);
|
|
break;
|
|
case GZVM_EXCEPTION_UNKNOWN:
|
|
fallthrough;
|
|
default:
|
|
ret = -EFAULT;
|
|
}
|
|
|
|
if (!ret)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* gzvm_handle_guest_hvc() - Handle guest hvc
|
|
* @vcpu: Pointer to struct gzvm_vcpu struct
|
|
* Return:
|
|
* * true - This hvc has been processed, no need to back to VMM.
|
|
* * false - This hvc has not been processed, require userspace.
|
|
*/
|
|
bool gzvm_handle_guest_hvc(struct gzvm_vcpu *vcpu)
|
|
{
|
|
unsigned long ipa;
|
|
int ret;
|
|
|
|
switch (vcpu->run->hypercall.args[0]) {
|
|
case GZVM_HVC_MEM_RELINQUISH:
|
|
ipa = vcpu->run->hypercall.args[1];
|
|
ret = gzvm_handle_relinquish(vcpu, ipa);
|
|
return (ret == 0) ? true : false;
|
|
default:
|
|
return gzvm_arch_handle_guest_hvc(vcpu);
|
|
}
|
|
}
|
|
|
|
static void vcpu_block_wait(struct gzvm_vcpu *vcpu)
|
|
{
|
|
struct rcuwait *wait = &vcpu->wait;
|
|
|
|
prepare_to_rcuwait(wait);
|
|
|
|
while (true) {
|
|
set_current_state(TASK_INTERRUPTIBLE);
|
|
if (vcpu->idle_events.virtio_irq) {
|
|
vcpu->idle_events.virtio_irq = 0;
|
|
break;
|
|
}
|
|
if (vcpu->idle_events.vtimer_irq) {
|
|
vcpu->idle_events.vtimer_irq = 0;
|
|
break;
|
|
}
|
|
if (signal_pending(current))
|
|
break;
|
|
schedule();
|
|
}
|
|
finish_rcuwait(wait);
|
|
}
|
|
|
|
/**
|
|
* gzvm_handle_guest_idle() - Handle guest vm entering idle
|
|
* @vcpu: Pointer to struct gzvm_vcpu struct
|
|
* Return:
|
|
*/
|
|
int gzvm_handle_guest_idle(struct gzvm_vcpu *vcpu)
|
|
{
|
|
int ret = 0;
|
|
u64 ns = 0;
|
|
|
|
ns = gzvm_vcpu_arch_get_timer_delay_ns(vcpu);
|
|
|
|
if (ns) {
|
|
gzvm_vtimer_set(vcpu, ns);
|
|
vcpu_block_wait(vcpu);
|
|
gzvm_vtimer_release(vcpu);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void gzvm_handle_guest_ipi(struct gzvm_vcpu *vcpu)
|
|
{
|
|
gzvm_vcpu_wakeup_all(vcpu->gzvm);
|
|
}
|