1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2020-2023 Loongson Technology Corporation Limited
4 */
5
6#include <linux/err.h>
7#include <linux/errno.h>
8#include <asm/kvm_csr.h>
9#include <asm/kvm_vcpu.h>
10
11static unsigned int priority_to_irq[EXCCODE_INT_NUM] = {
12	[INT_TI]	= CPU_TIMER,
13	[INT_IPI]	= CPU_IPI,
14	[INT_SWI0]	= CPU_SIP0,
15	[INT_SWI1]	= CPU_SIP1,
16	[INT_HWI0]	= CPU_IP0,
17	[INT_HWI1]	= CPU_IP1,
18	[INT_HWI2]	= CPU_IP2,
19	[INT_HWI3]	= CPU_IP3,
20	[INT_HWI4]	= CPU_IP4,
21	[INT_HWI5]	= CPU_IP5,
22	[INT_HWI6]	= CPU_IP6,
23	[INT_HWI7]	= CPU_IP7,
24};
25
26static int kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned int priority)
27{
28	unsigned int irq = 0;
29
30	clear_bit(priority, &vcpu->arch.irq_pending);
31	if (priority < EXCCODE_INT_NUM)
32		irq = priority_to_irq[priority];
33
34	switch (priority) {
35	case INT_TI:
36	case INT_IPI:
37	case INT_SWI0:
38	case INT_SWI1:
39		set_gcsr_estat(irq);
40		break;
41
42	case INT_HWI0 ... INT_HWI7:
43		set_csr_gintc(irq);
44		break;
45
46	default:
47		break;
48	}
49
50	return 1;
51}
52
53static int kvm_irq_clear(struct kvm_vcpu *vcpu, unsigned int priority)
54{
55	unsigned int irq = 0;
56
57	clear_bit(priority, &vcpu->arch.irq_clear);
58	if (priority < EXCCODE_INT_NUM)
59		irq = priority_to_irq[priority];
60
61	switch (priority) {
62	case INT_TI:
63	case INT_IPI:
64	case INT_SWI0:
65	case INT_SWI1:
66		clear_gcsr_estat(irq);
67		break;
68
69	case INT_HWI0 ... INT_HWI7:
70		clear_csr_gintc(irq);
71		break;
72
73	default:
74		break;
75	}
76
77	return 1;
78}
79
80void kvm_deliver_intr(struct kvm_vcpu *vcpu)
81{
82	unsigned int priority;
83	unsigned long *pending = &vcpu->arch.irq_pending;
84	unsigned long *pending_clr = &vcpu->arch.irq_clear;
85
86	if (!(*pending) && !(*pending_clr))
87		return;
88
89	if (*pending_clr) {
90		priority = __ffs(*pending_clr);
91		while (priority <= INT_IPI) {
92			kvm_irq_clear(vcpu, priority);
93			priority = find_next_bit(pending_clr,
94					BITS_PER_BYTE * sizeof(*pending_clr),
95					priority + 1);
96		}
97	}
98
99	if (*pending) {
100		priority = __ffs(*pending);
101		while (priority <= INT_IPI) {
102			kvm_irq_deliver(vcpu, priority);
103			priority = find_next_bit(pending,
104					BITS_PER_BYTE * sizeof(*pending),
105					priority + 1);
106		}
107	}
108}
109
110int kvm_pending_timer(struct kvm_vcpu *vcpu)
111{
112	return test_bit(INT_TI, &vcpu->arch.irq_pending);
113}
114
115/*
116 * Only support illegal instruction or illegal Address Error exception,
117 * Other exceptions are injected by hardware in kvm mode
118 */
119static void _kvm_deliver_exception(struct kvm_vcpu *vcpu,
120				unsigned int code, unsigned int subcode)
121{
122	unsigned long val, vec_size;
123
124	/*
125	 * BADV is added for EXCCODE_ADE exception
126	 *  Use PC register (GVA address) if it is instruction exeception
127	 *  Else use BADV from host side (GPA address) for data exeception
128	 */
129	if (code == EXCCODE_ADE) {
130		if (subcode == EXSUBCODE_ADEF)
131			val = vcpu->arch.pc;
132		else
133			val = vcpu->arch.badv;
134		kvm_write_hw_gcsr(LOONGARCH_CSR_BADV, val);
135	}
136
137	/* Set exception instruction */
138	kvm_write_hw_gcsr(LOONGARCH_CSR_BADI, vcpu->arch.badi);
139
140	/*
141	 * Save CRMD in PRMD
142	 * Set IRQ disabled and PLV0 with CRMD
143	 */
144	val = kvm_read_hw_gcsr(LOONGARCH_CSR_CRMD);
145	kvm_write_hw_gcsr(LOONGARCH_CSR_PRMD, val);
146	val = val & ~(CSR_CRMD_PLV | CSR_CRMD_IE);
147	kvm_write_hw_gcsr(LOONGARCH_CSR_CRMD, val);
148
149	/* Set exception PC address */
150	kvm_write_hw_gcsr(LOONGARCH_CSR_ERA, vcpu->arch.pc);
151
152	/*
153	 * Set exception code
154	 * Exception and interrupt can be inject at the same time
155	 * Hardware will handle exception first and then extern interrupt
156	 * Exception code is Ecode in ESTAT[16:21]
157	 * Interrupt code in ESTAT[0:12]
158	 */
159	val = kvm_read_hw_gcsr(LOONGARCH_CSR_ESTAT);
160	val = (val & ~CSR_ESTAT_EXC) | code;
161	kvm_write_hw_gcsr(LOONGARCH_CSR_ESTAT, val);
162
163	/* Calculate expcetion entry address */
164	val = kvm_read_hw_gcsr(LOONGARCH_CSR_ECFG);
165	vec_size = (val & CSR_ECFG_VS) >> CSR_ECFG_VS_SHIFT;
166	if (vec_size)
167		vec_size = (1 << vec_size) * 4;
168	val =  kvm_read_hw_gcsr(LOONGARCH_CSR_EENTRY);
169	vcpu->arch.pc = val + code * vec_size;
170}
171
172void kvm_deliver_exception(struct kvm_vcpu *vcpu)
173{
174	unsigned int code;
175	unsigned long *pending = &vcpu->arch.exception_pending;
176
177	if (*pending) {
178		code = __ffs(*pending);
179		_kvm_deliver_exception(vcpu, code, vcpu->arch.esubcode);
180		*pending = 0;
181		vcpu->arch.esubcode = 0;
182	}
183}
184