123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- * Loongson-3 Virtual IPI interrupt support.
- *
- * Copyright (C) 2019 Loongson Technologies, Inc. All rights reserved.
- *
- * Authors: Chen Zhu <[email protected]>
- * Authors: Huacai Chen <[email protected]>
- */
- #include <linux/kvm_host.h>
- #define IPI_BASE 0x3ff01000ULL
- #define CORE0_STATUS_OFF 0x000
- #define CORE0_EN_OFF 0x004
- #define CORE0_SET_OFF 0x008
- #define CORE0_CLEAR_OFF 0x00c
- #define CORE0_BUF_20 0x020
- #define CORE0_BUF_28 0x028
- #define CORE0_BUF_30 0x030
- #define CORE0_BUF_38 0x038
- #define CORE1_STATUS_OFF 0x100
- #define CORE1_EN_OFF 0x104
- #define CORE1_SET_OFF 0x108
- #define CORE1_CLEAR_OFF 0x10c
- #define CORE1_BUF_20 0x120
- #define CORE1_BUF_28 0x128
- #define CORE1_BUF_30 0x130
- #define CORE1_BUF_38 0x138
- #define CORE2_STATUS_OFF 0x200
- #define CORE2_EN_OFF 0x204
- #define CORE2_SET_OFF 0x208
- #define CORE2_CLEAR_OFF 0x20c
- #define CORE2_BUF_20 0x220
- #define CORE2_BUF_28 0x228
- #define CORE2_BUF_30 0x230
- #define CORE2_BUF_38 0x238
- #define CORE3_STATUS_OFF 0x300
- #define CORE3_EN_OFF 0x304
- #define CORE3_SET_OFF 0x308
- #define CORE3_CLEAR_OFF 0x30c
- #define CORE3_BUF_20 0x320
- #define CORE3_BUF_28 0x328
- #define CORE3_BUF_30 0x330
- #define CORE3_BUF_38 0x338
- static int loongson_vipi_read(struct loongson_kvm_ipi *ipi,
- gpa_t addr, int len, void *val)
- {
- uint32_t core = (addr >> 8) & 3;
- uint32_t node = (addr >> 44) & 3;
- uint32_t id = core + node * 4;
- uint64_t offset = addr & 0xff;
- void *pbuf;
- struct ipi_state *s = &(ipi->ipistate[id]);
- BUG_ON(offset & (len - 1));
- switch (offset) {
- case CORE0_STATUS_OFF:
- *(uint64_t *)val = s->status;
- break;
- case CORE0_EN_OFF:
- *(uint64_t *)val = s->en;
- break;
- case CORE0_SET_OFF:
- *(uint64_t *)val = 0;
- break;
- case CORE0_CLEAR_OFF:
- *(uint64_t *)val = 0;
- break;
- case CORE0_BUF_20 ... CORE0_BUF_38:
- pbuf = (void *)s->buf + (offset - 0x20);
- if (len == 8)
- *(uint64_t *)val = *(uint64_t *)pbuf;
- else /* Assume len == 4 */
- *(uint32_t *)val = *(uint32_t *)pbuf;
- break;
- default:
- pr_notice("%s with unknown addr %llx\n", __func__, addr);
- break;
- }
- return 0;
- }
- static int loongson_vipi_write(struct loongson_kvm_ipi *ipi,
- gpa_t addr, int len, const void *val)
- {
- uint32_t core = (addr >> 8) & 3;
- uint32_t node = (addr >> 44) & 3;
- uint32_t id = core + node * 4;
- uint64_t data, offset = addr & 0xff;
- void *pbuf;
- struct kvm *kvm = ipi->kvm;
- struct kvm_mips_interrupt irq;
- struct ipi_state *s = &(ipi->ipistate[id]);
- data = *(uint64_t *)val;
- BUG_ON(offset & (len - 1));
- switch (offset) {
- case CORE0_STATUS_OFF:
- break;
- case CORE0_EN_OFF:
- s->en = data;
- break;
- case CORE0_SET_OFF:
- s->status |= data;
- irq.cpu = id;
- irq.irq = 6;
- kvm_vcpu_ioctl_interrupt(kvm_get_vcpu(kvm, id), &irq);
- break;
- case CORE0_CLEAR_OFF:
- s->status &= ~data;
- if (!s->status) {
- irq.cpu = id;
- irq.irq = -6;
- kvm_vcpu_ioctl_interrupt(kvm_get_vcpu(kvm, id), &irq);
- }
- break;
- case CORE0_BUF_20 ... CORE0_BUF_38:
- pbuf = (void *)s->buf + (offset - 0x20);
- if (len == 8)
- *(uint64_t *)pbuf = (uint64_t)data;
- else /* Assume len == 4 */
- *(uint32_t *)pbuf = (uint32_t)data;
- break;
- default:
- pr_notice("%s with unknown addr %llx\n", __func__, addr);
- break;
- }
- return 0;
- }
- static int kvm_ipi_read(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
- gpa_t addr, int len, void *val)
- {
- unsigned long flags;
- struct loongson_kvm_ipi *ipi;
- struct ipi_io_device *ipi_device;
- ipi_device = container_of(dev, struct ipi_io_device, device);
- ipi = ipi_device->ipi;
- spin_lock_irqsave(&ipi->lock, flags);
- loongson_vipi_read(ipi, addr, len, val);
- spin_unlock_irqrestore(&ipi->lock, flags);
- return 0;
- }
- static int kvm_ipi_write(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
- gpa_t addr, int len, const void *val)
- {
- unsigned long flags;
- struct loongson_kvm_ipi *ipi;
- struct ipi_io_device *ipi_device;
- ipi_device = container_of(dev, struct ipi_io_device, device);
- ipi = ipi_device->ipi;
- spin_lock_irqsave(&ipi->lock, flags);
- loongson_vipi_write(ipi, addr, len, val);
- spin_unlock_irqrestore(&ipi->lock, flags);
- return 0;
- }
- static const struct kvm_io_device_ops kvm_ipi_ops = {
- .read = kvm_ipi_read,
- .write = kvm_ipi_write,
- };
- void kvm_init_loongson_ipi(struct kvm *kvm)
- {
- int i;
- unsigned long addr;
- struct loongson_kvm_ipi *s;
- struct kvm_io_device *device;
- s = &kvm->arch.ipi;
- s->kvm = kvm;
- spin_lock_init(&s->lock);
- /*
- * Initialize IPI device
- */
- for (i = 0; i < 4; i++) {
- device = &s->dev_ipi[i].device;
- kvm_iodevice_init(device, &kvm_ipi_ops);
- addr = (((unsigned long)i) << 44) + IPI_BASE;
- mutex_lock(&kvm->slots_lock);
- kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, addr, 0x400, device);
- mutex_unlock(&kvm->slots_lock);
- s->dev_ipi[i].ipi = s;
- s->dev_ipi[i].node_id = i;
- }
- }
|