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  #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  
loongson_vipi_read(struct loongson_kvm_ipi * ipi,gpa_t addr,int len,void * val)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  
loongson_vipi_write(struct loongson_kvm_ipi * ipi,gpa_t addr,int len,const void * val)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  
kvm_ipi_read(struct kvm_vcpu * vcpu,struct kvm_io_device * dev,gpa_t addr,int len,void * val)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  
kvm_ipi_write(struct kvm_vcpu * vcpu,struct kvm_io_device * dev,gpa_t addr,int len,const void * val)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  
kvm_init_loongson_ipi(struct kvm * kvm)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