1 /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 /* 3 * LoongArch EXTIOI interrupt kvm support 4 * 5 * Copyright (C) 2025 Loongson Technology Corporation Limited 6 */ 7 8 #include "qemu/osdep.h" 9 #include "hw/intc/loongarch_extioi.h" 10 #include "linux/kvm.h" 11 #include "qapi/error.h" 12 #include "system/kvm.h" 13 14 static void kvm_extioi_access_reg(int fd, uint64_t addr, void *val, bool write) 15 { 16 kvm_device_access(fd, KVM_DEV_LOONGARCH_EXTIOI_GRP_REGS, 17 addr, val, write, &error_abort); 18 } 19 20 static void kvm_extioi_access_sw_state(int fd, uint64_t addr, 21 void *val, bool write) 22 { 23 kvm_device_access(fd, KVM_DEV_LOONGARCH_EXTIOI_GRP_SW_STATUS, 24 addr, val, write, &error_abort); 25 } 26 27 static void kvm_extioi_access_sw_status(void *opaque, bool write) 28 { 29 LoongArchExtIOICommonState *lecs = LOONGARCH_EXTIOI_COMMON(opaque); 30 LoongArchExtIOIState *les = LOONGARCH_EXTIOI(opaque); 31 int addr; 32 33 addr = KVM_DEV_LOONGARCH_EXTIOI_SW_STATUS_STATE; 34 kvm_extioi_access_sw_state(les->dev_fd, addr, &lecs->status, write); 35 } 36 37 static void kvm_extioi_access_regs(void *opaque, bool write) 38 { 39 LoongArchExtIOICommonState *lecs = LOONGARCH_EXTIOI_COMMON(opaque); 40 LoongArchExtIOIState *les = LOONGARCH_EXTIOI(opaque); 41 int fd = les->dev_fd; 42 int addr, offset, cpu; 43 44 for (addr = EXTIOI_NODETYPE_START; addr < EXTIOI_NODETYPE_END; addr += 4) { 45 offset = (addr - EXTIOI_NODETYPE_START) / 4; 46 kvm_extioi_access_reg(fd, addr, &lecs->nodetype[offset], write); 47 } 48 49 for (addr = EXTIOI_IPMAP_START; addr < EXTIOI_IPMAP_END; addr += 4) { 50 offset = (addr - EXTIOI_IPMAP_START) / 4; 51 kvm_extioi_access_reg(fd, addr, &lecs->ipmap[offset], write); 52 } 53 54 for (addr = EXTIOI_ENABLE_START; addr < EXTIOI_ENABLE_END; addr += 4) { 55 offset = (addr - EXTIOI_ENABLE_START) / 4; 56 kvm_extioi_access_reg(fd, addr, &lecs->enable[offset], write); 57 } 58 59 for (addr = EXTIOI_BOUNCE_START; addr < EXTIOI_BOUNCE_END; addr += 4) { 60 offset = (addr - EXTIOI_BOUNCE_START) / 4; 61 kvm_extioi_access_reg(fd, addr, &lecs->bounce[offset], write); 62 } 63 64 for (addr = EXTIOI_ISR_START; addr < EXTIOI_ISR_END; addr += 4) { 65 offset = (addr - EXTIOI_ISR_START) / 4; 66 kvm_extioi_access_reg(fd, addr, &lecs->isr[offset], write); 67 } 68 69 for (addr = EXTIOI_COREMAP_START; addr < EXTIOI_COREMAP_END; addr += 4) { 70 offset = (addr - EXTIOI_COREMAP_START) / 4; 71 kvm_extioi_access_reg(fd, addr, &lecs->coremap[offset], write); 72 } 73 74 for (cpu = 0; cpu < lecs->num_cpu; cpu++) { 75 for (addr = EXTIOI_COREISR_START; 76 addr < EXTIOI_COREISR_END; addr += 4) { 77 offset = (addr - EXTIOI_COREISR_START) / 4; 78 kvm_extioi_access_reg(fd, (cpu << 16) | addr, 79 &lecs->cpu[cpu].coreisr[offset], write); 80 } 81 } 82 } 83 84 int kvm_extioi_get(void *opaque) 85 { 86 kvm_extioi_access_regs(opaque, false); 87 kvm_extioi_access_sw_status(opaque, false); 88 return 0; 89 } 90 91 int kvm_extioi_put(void *opaque, int version_id) 92 { 93 LoongArchExtIOIState *les = LOONGARCH_EXTIOI(opaque); 94 int fd = les->dev_fd; 95 96 if (fd == 0) { 97 return 0; 98 } 99 100 kvm_extioi_access_regs(opaque, true); 101 kvm_extioi_access_sw_status(opaque, true); 102 kvm_device_access(fd, KVM_DEV_LOONGARCH_EXTIOI_GRP_CTRL, 103 KVM_DEV_LOONGARCH_EXTIOI_CTRL_LOAD_FINISHED, 104 NULL, true, &error_abort); 105 return 0; 106 } 107 108 void kvm_extioi_realize(DeviceState *dev, Error **errp) 109 { 110 LoongArchExtIOICommonState *lecs = LOONGARCH_EXTIOI_COMMON(dev); 111 LoongArchExtIOIState *les = LOONGARCH_EXTIOI(dev); 112 int ret; 113 114 ret = kvm_create_device(kvm_state, KVM_DEV_TYPE_LOONGARCH_EIOINTC, false); 115 if (ret < 0) { 116 fprintf(stderr, "create KVM_LOONGARCH_EIOINTC failed: %s\n", 117 strerror(-ret)); 118 abort(); 119 } 120 121 les->dev_fd = ret; 122 ret = kvm_device_access(les->dev_fd, KVM_DEV_LOONGARCH_EXTIOI_GRP_CTRL, 123 KVM_DEV_LOONGARCH_EXTIOI_CTRL_INIT_NUM_CPU, 124 &lecs->num_cpu, true, NULL); 125 if (ret < 0) { 126 fprintf(stderr, "KVM_LOONGARCH_EXTIOI_INIT_NUM_CPU failed: %s\n", 127 strerror(-ret)); 128 abort(); 129 } 130 131 ret = kvm_device_access(les->dev_fd, KVM_DEV_LOONGARCH_EXTIOI_GRP_CTRL, 132 KVM_DEV_LOONGARCH_EXTIOI_CTRL_INIT_FEATURE, 133 &lecs->features, true, NULL); 134 if (ret < 0) { 135 fprintf(stderr, "KVM_LOONGARCH_EXTIOI_INIT_FEATURE failed: %s\n", 136 strerror(-ret)); 137 abort(); 138 } 139 } 140