Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/mips/kvm/loongson_ipi.c
26424 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* Loongson-3 Virtual IPI interrupt support.
4
*
5
* Copyright (C) 2019 Loongson Technologies, Inc. All rights reserved.
6
*
7
* Authors: Chen Zhu <[email protected]>
8
* Authors: Huacai Chen <[email protected]>
9
*/
10
11
#include <linux/kvm_host.h>
12
13
#include "interrupt.h"
14
15
#define IPI_BASE 0x3ff01000ULL
16
17
#define CORE0_STATUS_OFF 0x000
18
#define CORE0_EN_OFF 0x004
19
#define CORE0_SET_OFF 0x008
20
#define CORE0_CLEAR_OFF 0x00c
21
#define CORE0_BUF_20 0x020
22
#define CORE0_BUF_28 0x028
23
#define CORE0_BUF_30 0x030
24
#define CORE0_BUF_38 0x038
25
26
#define CORE1_STATUS_OFF 0x100
27
#define CORE1_EN_OFF 0x104
28
#define CORE1_SET_OFF 0x108
29
#define CORE1_CLEAR_OFF 0x10c
30
#define CORE1_BUF_20 0x120
31
#define CORE1_BUF_28 0x128
32
#define CORE1_BUF_30 0x130
33
#define CORE1_BUF_38 0x138
34
35
#define CORE2_STATUS_OFF 0x200
36
#define CORE2_EN_OFF 0x204
37
#define CORE2_SET_OFF 0x208
38
#define CORE2_CLEAR_OFF 0x20c
39
#define CORE2_BUF_20 0x220
40
#define CORE2_BUF_28 0x228
41
#define CORE2_BUF_30 0x230
42
#define CORE2_BUF_38 0x238
43
44
#define CORE3_STATUS_OFF 0x300
45
#define CORE3_EN_OFF 0x304
46
#define CORE3_SET_OFF 0x308
47
#define CORE3_CLEAR_OFF 0x30c
48
#define CORE3_BUF_20 0x320
49
#define CORE3_BUF_28 0x328
50
#define CORE3_BUF_30 0x330
51
#define CORE3_BUF_38 0x338
52
53
static int loongson_vipi_read(struct loongson_kvm_ipi *ipi,
54
gpa_t addr, int len, void *val)
55
{
56
uint32_t core = (addr >> 8) & 3;
57
uint32_t node = (addr >> 44) & 3;
58
uint32_t id = core + node * 4;
59
uint64_t offset = addr & 0xff;
60
void *pbuf;
61
struct ipi_state *s = &(ipi->ipistate[id]);
62
63
BUG_ON(offset & (len - 1));
64
65
switch (offset) {
66
case CORE0_STATUS_OFF:
67
*(uint64_t *)val = s->status;
68
break;
69
70
case CORE0_EN_OFF:
71
*(uint64_t *)val = s->en;
72
break;
73
74
case CORE0_SET_OFF:
75
*(uint64_t *)val = 0;
76
break;
77
78
case CORE0_CLEAR_OFF:
79
*(uint64_t *)val = 0;
80
break;
81
82
case CORE0_BUF_20 ... CORE0_BUF_38:
83
pbuf = (void *)s->buf + (offset - 0x20);
84
if (len == 8)
85
*(uint64_t *)val = *(uint64_t *)pbuf;
86
else /* Assume len == 4 */
87
*(uint32_t *)val = *(uint32_t *)pbuf;
88
break;
89
90
default:
91
pr_notice("%s with unknown addr %llx\n", __func__, addr);
92
break;
93
}
94
95
return 0;
96
}
97
98
static int loongson_vipi_write(struct loongson_kvm_ipi *ipi,
99
gpa_t addr, int len, const void *val)
100
{
101
uint32_t core = (addr >> 8) & 3;
102
uint32_t node = (addr >> 44) & 3;
103
uint32_t id = core + node * 4;
104
uint64_t data, offset = addr & 0xff;
105
void *pbuf;
106
struct kvm *kvm = ipi->kvm;
107
struct kvm_mips_interrupt irq;
108
struct ipi_state *s = &(ipi->ipistate[id]);
109
110
data = *(uint64_t *)val;
111
BUG_ON(offset & (len - 1));
112
113
switch (offset) {
114
case CORE0_STATUS_OFF:
115
break;
116
117
case CORE0_EN_OFF:
118
s->en = data;
119
break;
120
121
case CORE0_SET_OFF:
122
s->status |= data;
123
irq.cpu = id;
124
irq.irq = 6;
125
kvm_vcpu_ioctl_interrupt(kvm_get_vcpu(kvm, id), &irq);
126
break;
127
128
case CORE0_CLEAR_OFF:
129
s->status &= ~data;
130
if (!s->status) {
131
irq.cpu = id;
132
irq.irq = -6;
133
kvm_vcpu_ioctl_interrupt(kvm_get_vcpu(kvm, id), &irq);
134
}
135
break;
136
137
case CORE0_BUF_20 ... CORE0_BUF_38:
138
pbuf = (void *)s->buf + (offset - 0x20);
139
if (len == 8)
140
*(uint64_t *)pbuf = (uint64_t)data;
141
else /* Assume len == 4 */
142
*(uint32_t *)pbuf = (uint32_t)data;
143
break;
144
145
default:
146
pr_notice("%s with unknown addr %llx\n", __func__, addr);
147
break;
148
}
149
150
return 0;
151
}
152
153
static int kvm_ipi_read(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
154
gpa_t addr, int len, void *val)
155
{
156
unsigned long flags;
157
struct loongson_kvm_ipi *ipi;
158
struct ipi_io_device *ipi_device;
159
160
ipi_device = container_of(dev, struct ipi_io_device, device);
161
ipi = ipi_device->ipi;
162
163
spin_lock_irqsave(&ipi->lock, flags);
164
loongson_vipi_read(ipi, addr, len, val);
165
spin_unlock_irqrestore(&ipi->lock, flags);
166
167
return 0;
168
}
169
170
static int kvm_ipi_write(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
171
gpa_t addr, int len, const void *val)
172
{
173
unsigned long flags;
174
struct loongson_kvm_ipi *ipi;
175
struct ipi_io_device *ipi_device;
176
177
ipi_device = container_of(dev, struct ipi_io_device, device);
178
ipi = ipi_device->ipi;
179
180
spin_lock_irqsave(&ipi->lock, flags);
181
loongson_vipi_write(ipi, addr, len, val);
182
spin_unlock_irqrestore(&ipi->lock, flags);
183
184
return 0;
185
}
186
187
static const struct kvm_io_device_ops kvm_ipi_ops = {
188
.read = kvm_ipi_read,
189
.write = kvm_ipi_write,
190
};
191
192
void kvm_init_loongson_ipi(struct kvm *kvm)
193
{
194
int i;
195
unsigned long addr;
196
struct loongson_kvm_ipi *s;
197
struct kvm_io_device *device;
198
199
s = &kvm->arch.ipi;
200
s->kvm = kvm;
201
spin_lock_init(&s->lock);
202
203
/*
204
* Initialize IPI device
205
*/
206
for (i = 0; i < 4; i++) {
207
device = &s->dev_ipi[i].device;
208
kvm_iodevice_init(device, &kvm_ipi_ops);
209
addr = (((unsigned long)i) << 44) + IPI_BASE;
210
mutex_lock(&kvm->slots_lock);
211
kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, addr, 0x400, device);
212
mutex_unlock(&kvm->slots_lock);
213
s->dev_ipi[i].ipi = s;
214
s->dev_ipi[i].node_id = i;
215
}
216
}
217
218