linux/arch/mips/kvm/loongson_ipi.c
<<
>>
Prefs
   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
  51static 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
  96static 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->vcpus[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->vcpus[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
 151static 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
 168static 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
 185static const struct kvm_io_device_ops kvm_ipi_ops = {
 186        .read     = kvm_ipi_read,
 187        .write    = kvm_ipi_write,
 188};
 189
 190void 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