1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
4 */
5
6#include <linux/kernel.h>
7#include <linux/kvm_host.h>
8#include <linux/sched/stat.h>
9#include <asm/kvm_para.h>
10#include <asm/paravirt.h>
11#include "intc/ls3a_ipi.h"
12
13int kvm_virt_ipi(struct kvm_vcpu *vcpu)
14{
15	int ret = 0;
16	u64 ipi_bitmap;
17	unsigned int min, action, cpu;
18
19	ipi_bitmap = vcpu->arch.gprs[REG_A1];
20	min = vcpu->arch.gprs[REG_A2];
21	action = vcpu->arch.gprs[REG_A3];
22
23	if (ipi_bitmap) {
24		cpu = find_first_bit((void *)&ipi_bitmap, BITS_PER_LONG);
25		while (cpu < BITS_PER_LONG) {
26			kvm_helper_send_ipi(vcpu, cpu + min, action);
27			cpu = find_next_bit((void *)&ipi_bitmap, BITS_PER_LONG, cpu + 1);
28		}
29	}
30
31	return ret;
32}
33
34int kvm_save_notify(struct kvm_vcpu *vcpu)
35{
36	unsigned long num, id, data;
37	struct gfn_to_pfn_cache *cache = &vcpu->arch.st.cache;
38
39	int ret = 0;
40
41	num = vcpu->arch.gprs[REG_A0];
42	id = vcpu->arch.gprs[REG_A1];
43	data = vcpu->arch.gprs[REG_A2];
44
45	switch (id) {
46	case KVM_FEATURE_STEAL_TIME:
47		if (!sched_info_on())
48			break;
49
50		if (vcpu->arch.st.guest_addr && (data == 0))
51			kvm_release_pfn(cache->pfn, cache->dirty, cache);
52
53		vcpu->arch.st.guest_addr = data;
54		kvm_debug("cpu :%d addr:%lx\n", vcpu->vcpu_id, data);
55		vcpu->arch.st.last_steal = current->sched_info.run_delay;
56		kvm_make_request(KVM_REQ_RECORD_STEAL, vcpu);
57		break;
58	default:
59		break;
60	};
61
62	return ret;
63};
64
65static int _kvm_pv_feature(struct kvm_vcpu *vcpu)
66{
67	int feature = vcpu->arch.gprs[REG_A1];
68	int ret = KVM_RET_NOT_SUPPORTED;
69	switch (feature) {
70	case KVM_FEATURE_STEAL_TIME:
71		if (sched_info_on())
72			ret = KVM_RET_SUC;
73		break;
74	case KVM_FEATURE_MULTI_IPI:
75		ret = KVM_RET_SUC;
76		break;
77	case KVM_FEATURE_PARAVIRT_SPINLOCK:
78#ifdef CONFIG_PARAVIRT_SPINLOCKS
79		ret = KVM_RET_SUC;
80#endif
81		break;
82	default:
83		break;
84	}
85	return ret;
86}
87
88static int kvm_pv_kick_cpu(struct kvm_vcpu *vcpu)
89{
90	int cpu = vcpu->arch.gprs[REG_A1];
91	struct kvm_vcpu *dst_vcpu = vcpu->kvm->vcpus[cpu];
92	int ret = 0;
93
94	dst_vcpu->arch.pv.pv_unhalted = true;
95	kvm_make_request(KVM_REQ_EVENT, dst_vcpu);
96	kvm_vcpu_kick(dst_vcpu);
97
98	return ret;
99}
100
101/*
102 * hypcall emulation always return to guest, Caller should check retval.
103 */
104int _kvm_handle_pv_hcall(struct kvm_vcpu *vcpu)
105{
106	unsigned long func = vcpu->arch.gprs[REG_A0];
107	int hyp_ret = KVM_RET_NOT_SUPPORTED;
108
109	switch (func) {
110	case KVM_HC_FUNC_FEATURE:
111		hyp_ret = _kvm_pv_feature(vcpu);
112		break;
113	case KVM_HC_FUNC_NOTIFY:
114		hyp_ret = kvm_save_notify(vcpu);
115		break;
116	case KVM_HC_FUNC_IPI:
117		hyp_ret = kvm_virt_ipi(vcpu);
118		break;
119	case KVM_HC_KICK_CPU:
120		hyp_ret = kvm_pv_kick_cpu(vcpu);
121		break;
122	default:
123		kvm_info("[%#lx] hvc func:%#lx unsupported\n", vcpu->arch.pc, func);
124		break;
125	};
126
127	vcpu->arch.gprs[REG_A0] = hyp_ret;
128
129	return RESUME_GUEST;
130}
131