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