Skip to content

Commit f5f31ef

Browse files
lixianglaichenhuacai
authored andcommitted
LoongArch: KVM: Add PCHPIC read and write functions
Add implementation of IPI interrupt controller's address space read and write function simulation. Implement interrupt injection interface under loongarch. Signed-off-by: Tianrui Zhao <[email protected]> Signed-off-by: Xianglai Li <[email protected]> Signed-off-by: Huacai Chen <[email protected]>
1 parent e785dfa commit f5f31ef

File tree

3 files changed

+322
-2
lines changed

3 files changed

+322
-2
lines changed

arch/loongarch/include/asm/kvm_host.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ struct kvm_vm_stat {
5151
u64 ipi_write_exits;
5252
u64 eiointc_read_exits;
5353
u64 eiointc_write_exits;
54+
u64 pch_pic_read_exits;
55+
u64 pch_pic_write_exits;
5456
};
5557

5658
struct kvm_vcpu_stat {

arch/loongarch/include/asm/kvm_pch_pic.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,35 @@
88

99
#include <kvm/iodev.h>
1010

11+
#define PCH_PIC_SIZE 0x3e8
12+
13+
#define PCH_PIC_INT_ID_START 0x0
14+
#define PCH_PIC_INT_ID_END 0x7
15+
#define PCH_PIC_MASK_START 0x20
16+
#define PCH_PIC_MASK_END 0x27
17+
#define PCH_PIC_HTMSI_EN_START 0x40
18+
#define PCH_PIC_HTMSI_EN_END 0x47
19+
#define PCH_PIC_EDGE_START 0x60
20+
#define PCH_PIC_EDGE_END 0x67
21+
#define PCH_PIC_CLEAR_START 0x80
22+
#define PCH_PIC_CLEAR_END 0x87
23+
#define PCH_PIC_AUTO_CTRL0_START 0xc0
24+
#define PCH_PIC_AUTO_CTRL0_END 0xc7
25+
#define PCH_PIC_AUTO_CTRL1_START 0xe0
26+
#define PCH_PIC_AUTO_CTRL1_END 0xe7
27+
#define PCH_PIC_ROUTE_ENTRY_START 0x100
28+
#define PCH_PIC_ROUTE_ENTRY_END 0x13f
29+
#define PCH_PIC_HTMSI_VEC_START 0x200
30+
#define PCH_PIC_HTMSI_VEC_END 0x23f
31+
#define PCH_PIC_INT_IRR_START 0x380
32+
#define PCH_PIC_INT_IRR_END 0x38f
33+
#define PCH_PIC_INT_ISR_START 0x3a0
34+
#define PCH_PIC_INT_ISR_END 0x3af
35+
#define PCH_PIC_POLARITY_START 0x3e0
36+
#define PCH_PIC_POLARITY_END 0x3e7
37+
#define PCH_PIC_INT_ID_VAL 0x7000000UL
38+
#define PCH_PIC_INT_ID_VER 0x1UL
39+
1140
struct loongarch_pch_pic {
1241
spinlock_t lock;
1342
struct kvm *kvm;
@@ -27,5 +56,7 @@ struct loongarch_pch_pic {
2756
};
2857

2958
int kvm_loongarch_register_pch_pic_device(void);
59+
void pch_pic_set_irq(struct loongarch_pch_pic *s, int irq, int level);
60+
void pch_msi_set_irq(struct kvm *kvm, int irq, int level);
3061

3162
#endif /* __ASM_KVM_PCH_PIC_H */

arch/loongarch/kvm/intc/pch_pic.c

Lines changed: 289 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,305 @@
88
#include <asm/kvm_vcpu.h>
99
#include <linux/count_zeros.h>
1010

11+
/* update the isr according to irq level and route irq to eiointc */
12+
static void pch_pic_update_irq(struct loongarch_pch_pic *s, int irq, int level)
13+
{
14+
u64 mask = BIT(irq);
15+
16+
/*
17+
* set isr and route irq to eiointc and
18+
* the route table is in htmsi_vector[]
19+
*/
20+
if (level) {
21+
if (mask & s->irr & ~s->mask) {
22+
s->isr |= mask;
23+
irq = s->htmsi_vector[irq];
24+
eiointc_set_irq(s->kvm->arch.eiointc, irq, level);
25+
}
26+
} else {
27+
if (mask & s->isr & ~s->irr) {
28+
s->isr &= ~mask;
29+
irq = s->htmsi_vector[irq];
30+
eiointc_set_irq(s->kvm->arch.eiointc, irq, level);
31+
}
32+
}
33+
}
34+
35+
/* update batch irqs, the irq_mask is a bitmap of irqs */
36+
static void pch_pic_update_batch_irqs(struct loongarch_pch_pic *s, u64 irq_mask, int level)
37+
{
38+
int irq, bits;
39+
40+
/* find each irq by irqs bitmap and update each irq */
41+
bits = sizeof(irq_mask) * 8;
42+
irq = find_first_bit((void *)&irq_mask, bits);
43+
while (irq < bits) {
44+
pch_pic_update_irq(s, irq, level);
45+
bitmap_clear((void *)&irq_mask, irq, 1);
46+
irq = find_first_bit((void *)&irq_mask, bits);
47+
}
48+
}
49+
50+
/* called when a irq is triggered in pch pic */
51+
void pch_pic_set_irq(struct loongarch_pch_pic *s, int irq, int level)
52+
{
53+
u64 mask = BIT(irq);
54+
55+
spin_lock(&s->lock);
56+
if (level)
57+
s->irr |= mask; /* set irr */
58+
else {
59+
/*
60+
* In edge triggered mode, 0 does not mean to clear irq
61+
* The irr register variable is cleared when cpu writes to the
62+
* PCH_PIC_CLEAR_START address area
63+
*/
64+
if (s->edge & mask) {
65+
spin_unlock(&s->lock);
66+
return;
67+
}
68+
s->irr &= ~mask;
69+
}
70+
pch_pic_update_irq(s, irq, level);
71+
spin_unlock(&s->lock);
72+
}
73+
74+
/* msi irq handler */
75+
void pch_msi_set_irq(struct kvm *kvm, int irq, int level)
76+
{
77+
eiointc_set_irq(kvm->arch.eiointc, irq, level);
78+
}
79+
80+
/*
81+
* pch pic register is 64-bit, but it is accessed by 32-bit,
82+
* so we use high to get whether low or high 32 bits we want
83+
* to read.
84+
*/
85+
static u32 pch_pic_read_reg(u64 *s, int high)
86+
{
87+
u64 val = *s;
88+
89+
/* read the high 32 bits when high is 1 */
90+
return high ? (u32)(val >> 32) : (u32)val;
91+
}
92+
93+
/*
94+
* pch pic register is 64-bit, but it is accessed by 32-bit,
95+
* so we use high to get whether low or high 32 bits we want
96+
* to write.
97+
*/
98+
static u32 pch_pic_write_reg(u64 *s, int high, u32 v)
99+
{
100+
u64 val = *s, data = v;
101+
102+
if (high) {
103+
/*
104+
* Clear val high 32 bits
105+
* Write the high 32 bits when the high is 1
106+
*/
107+
*s = (val << 32 >> 32) | (data << 32);
108+
val >>= 32;
109+
} else
110+
/*
111+
* Clear val low 32 bits
112+
* Write the low 32 bits when the high is 0
113+
*/
114+
*s = (val >> 32 << 32) | v;
115+
116+
return (u32)val;
117+
}
118+
119+
static int loongarch_pch_pic_read(struct loongarch_pch_pic *s, gpa_t addr, int len, void *val)
120+
{
121+
int offset, index, ret = 0;
122+
u32 data = 0;
123+
u64 int_id = 0;
124+
125+
offset = addr - s->pch_pic_base;
126+
127+
spin_lock(&s->lock);
128+
switch (offset) {
129+
case PCH_PIC_INT_ID_START ... PCH_PIC_INT_ID_END:
130+
/* int id version */
131+
int_id |= (u64)PCH_PIC_INT_ID_VER << 32;
132+
/* irq number */
133+
int_id |= (u64)31 << (32 + 16);
134+
/* int id value */
135+
int_id |= PCH_PIC_INT_ID_VAL;
136+
*(u64 *)val = int_id;
137+
break;
138+
case PCH_PIC_MASK_START ... PCH_PIC_MASK_END:
139+
offset -= PCH_PIC_MASK_START;
140+
index = offset >> 2;
141+
/* read mask reg */
142+
data = pch_pic_read_reg(&s->mask, index);
143+
*(u32 *)val = data;
144+
break;
145+
case PCH_PIC_HTMSI_EN_START ... PCH_PIC_HTMSI_EN_END:
146+
offset -= PCH_PIC_HTMSI_EN_START;
147+
index = offset >> 2;
148+
/* read htmsi enable reg */
149+
data = pch_pic_read_reg(&s->htmsi_en, index);
150+
*(u32 *)val = data;
151+
break;
152+
case PCH_PIC_EDGE_START ... PCH_PIC_EDGE_END:
153+
offset -= PCH_PIC_EDGE_START;
154+
index = offset >> 2;
155+
/* read edge enable reg */
156+
data = pch_pic_read_reg(&s->edge, index);
157+
*(u32 *)val = data;
158+
break;
159+
case PCH_PIC_AUTO_CTRL0_START ... PCH_PIC_AUTO_CTRL0_END:
160+
case PCH_PIC_AUTO_CTRL1_START ... PCH_PIC_AUTO_CTRL1_END:
161+
/* we only use default mode: fixed interrupt distribution mode */
162+
*(u32 *)val = 0;
163+
break;
164+
case PCH_PIC_ROUTE_ENTRY_START ... PCH_PIC_ROUTE_ENTRY_END:
165+
/* only route to int0: eiointc */
166+
*(u8 *)val = 1;
167+
break;
168+
case PCH_PIC_HTMSI_VEC_START ... PCH_PIC_HTMSI_VEC_END:
169+
offset -= PCH_PIC_HTMSI_VEC_START;
170+
/* read htmsi vector */
171+
data = s->htmsi_vector[offset];
172+
*(u8 *)val = data;
173+
break;
174+
case PCH_PIC_POLARITY_START ... PCH_PIC_POLARITY_END:
175+
/* we only use defalut value 0: high level triggered */
176+
*(u32 *)val = 0;
177+
break;
178+
default:
179+
ret = -EINVAL;
180+
}
181+
spin_unlock(&s->lock);
182+
183+
return ret;
184+
}
185+
11186
static int kvm_pch_pic_read(struct kvm_vcpu *vcpu,
12187
struct kvm_io_device *dev,
13188
gpa_t addr, int len, void *val)
14189
{
15-
return 0;
190+
int ret;
191+
struct loongarch_pch_pic *s = vcpu->kvm->arch.pch_pic;
192+
193+
if (!s) {
194+
kvm_err("%s: pch pic irqchip not valid!\n", __func__);
195+
return -EINVAL;
196+
}
197+
198+
/* statistics of pch pic reading */
199+
vcpu->kvm->stat.pch_pic_read_exits++;
200+
ret = loongarch_pch_pic_read(s, addr, len, val);
201+
202+
return ret;
203+
}
204+
205+
static int loongarch_pch_pic_write(struct loongarch_pch_pic *s, gpa_t addr,
206+
int len, const void *val)
207+
{
208+
int ret;
209+
u32 old, data, offset, index;
210+
u64 irq;
211+
212+
ret = 0;
213+
data = *(u32 *)val;
214+
offset = addr - s->pch_pic_base;
215+
216+
spin_lock(&s->lock);
217+
switch (offset) {
218+
case PCH_PIC_MASK_START ... PCH_PIC_MASK_END:
219+
offset -= PCH_PIC_MASK_START;
220+
/* get whether high or low 32 bits we want to write */
221+
index = offset >> 2;
222+
old = pch_pic_write_reg(&s->mask, index, data);
223+
/* enable irq when mask value change to 0 */
224+
irq = (old & ~data) << (32 * index);
225+
pch_pic_update_batch_irqs(s, irq, 1);
226+
/* disable irq when mask value change to 1 */
227+
irq = (~old & data) << (32 * index);
228+
pch_pic_update_batch_irqs(s, irq, 0);
229+
break;
230+
case PCH_PIC_HTMSI_EN_START ... PCH_PIC_HTMSI_EN_END:
231+
offset -= PCH_PIC_HTMSI_EN_START;
232+
index = offset >> 2;
233+
pch_pic_write_reg(&s->htmsi_en, index, data);
234+
break;
235+
case PCH_PIC_EDGE_START ... PCH_PIC_EDGE_END:
236+
offset -= PCH_PIC_EDGE_START;
237+
index = offset >> 2;
238+
/* 1: edge triggered, 0: level triggered */
239+
pch_pic_write_reg(&s->edge, index, data);
240+
break;
241+
case PCH_PIC_CLEAR_START ... PCH_PIC_CLEAR_END:
242+
offset -= PCH_PIC_CLEAR_START;
243+
index = offset >> 2;
244+
/* write 1 to clear edge irq */
245+
old = pch_pic_read_reg(&s->irr, index);
246+
/*
247+
* get the irq bitmap which is edge triggered and
248+
* already set and to be cleared
249+
*/
250+
irq = old & pch_pic_read_reg(&s->edge, index) & data;
251+
/* write irr to the new state where irqs have been cleared */
252+
pch_pic_write_reg(&s->irr, index, old & ~irq);
253+
/* update cleared irqs */
254+
pch_pic_update_batch_irqs(s, irq, 0);
255+
break;
256+
case PCH_PIC_AUTO_CTRL0_START ... PCH_PIC_AUTO_CTRL0_END:
257+
offset -= PCH_PIC_AUTO_CTRL0_START;
258+
index = offset >> 2;
259+
/* we only use default mode: fixed interrupt distribution mode */
260+
pch_pic_write_reg(&s->auto_ctrl0, index, 0);
261+
break;
262+
case PCH_PIC_AUTO_CTRL1_START ... PCH_PIC_AUTO_CTRL1_END:
263+
offset -= PCH_PIC_AUTO_CTRL1_START;
264+
index = offset >> 2;
265+
/* we only use default mode: fixed interrupt distribution mode */
266+
pch_pic_write_reg(&s->auto_ctrl1, index, 0);
267+
break;
268+
case PCH_PIC_ROUTE_ENTRY_START ... PCH_PIC_ROUTE_ENTRY_END:
269+
offset -= PCH_PIC_ROUTE_ENTRY_START;
270+
/* only route to int0: eiointc */
271+
s->route_entry[offset] = 1;
272+
break;
273+
case PCH_PIC_HTMSI_VEC_START ... PCH_PIC_HTMSI_VEC_END:
274+
/* route table to eiointc */
275+
offset -= PCH_PIC_HTMSI_VEC_START;
276+
s->htmsi_vector[offset] = (u8)data;
277+
break;
278+
case PCH_PIC_POLARITY_START ... PCH_PIC_POLARITY_END:
279+
offset -= PCH_PIC_POLARITY_START;
280+
index = offset >> 2;
281+
/* we only use defalut value 0: high level triggered */
282+
pch_pic_write_reg(&s->polarity, index, 0);
283+
break;
284+
default:
285+
ret = -EINVAL;
286+
break;
287+
}
288+
spin_unlock(&s->lock);
289+
290+
return ret;
16291
}
17292

18293
static int kvm_pch_pic_write(struct kvm_vcpu *vcpu,
19294
struct kvm_io_device *dev,
20295
gpa_t addr, int len, const void *val)
21296
{
22-
return 0;
297+
int ret;
298+
struct loongarch_pch_pic *s = vcpu->kvm->arch.pch_pic;
299+
300+
if (!s) {
301+
kvm_err("%s: pch pic irqchip not valid!\n", __func__);
302+
return -EINVAL;
303+
}
304+
305+
/* statistics of pch pic writing */
306+
vcpu->kvm->stat.pch_pic_write_exits++;
307+
ret = loongarch_pch_pic_write(s, addr, len, val);
308+
309+
return ret;
23310
}
24311

25312
static const struct kvm_io_device_ops kvm_pch_pic_ops = {

0 commit comments

Comments
 (0)