qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

hw/intc: Add Loongson LIOINTC support

Loongson-3 has an integrated liointc (Local I/O Interrupt Controller).
It is similar to Goldfish interrupt controller, but more powerful (e.g.,
it can route external interrupt to multi-cores).

Documents about Loongson-3's liointc:
1, https://wiki.godson.ac.cn/ip_block:liointc;
2, The "I/O中断" section of Loongson-3's user mannual, part 1.

Signed-off-by: Huacai Chen <chenhc@lemote.com>
Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
Signed-off-by: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com>
Reviewed-by: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com>
Message-Id: <1592995531-32600-3-git-send-email-chenhc@lemote.com>

authored by

Huacai Chen and committed by
Aleksandar Markovic
c012e0b1 719d109b

+246
+3
hw/intc/Kconfig
··· 64 64 65 65 config RX_ICU 66 66 bool 67 + 68 + config LOONGSON_LIOINTC 69 + bool
+1
hw/intc/Makefile.objs
··· 51 51 obj-$(CONFIG_NIOS2) += nios2_iic.o 52 52 obj-$(CONFIG_OMPIC) += ompic.o 53 53 obj-$(CONFIG_IBEX) += ibex_plic.o 54 + obj-$(CONFIG_LOONGSON_LIOINTC) += loongson_liointc.o
+242
hw/intc/loongson_liointc.c
··· 1 + /* 2 + * QEMU Loongson Local I/O interrupt controler. 3 + * 4 + * Copyright (c) 2020 Jiaxun Yang <jiaxun.yang@flygoat.com> 5 + * 6 + * This program is free software: you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation, either version 2 of the License, or 9 + * (at your option) any later version. 10 + * 11 + * This program is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + * 16 + * You should have received a copy of the GNU General Public License 17 + * along with this program. If not, see <https://www.gnu.org/licenses/>. 18 + * 19 + */ 20 + 21 + #include "qemu/osdep.h" 22 + #include "hw/sysbus.h" 23 + #include "qemu/module.h" 24 + #include "hw/irq.h" 25 + #include "hw/qdev-properties.h" 26 + 27 + #define D(x) 28 + 29 + #define NUM_IRQS 32 30 + 31 + #define NUM_CORES 4 32 + #define NUM_IPS 4 33 + #define NUM_PARENTS (NUM_CORES * NUM_IPS) 34 + #define PARENT_COREx_IPy(x, y) (NUM_IPS * x + y) 35 + 36 + #define R_MAPPER_START 0x0 37 + #define R_MAPPER_END 0x20 38 + #define R_ISR R_MAPPER_END 39 + #define R_IEN 0x24 40 + #define R_IEN_SET 0x28 41 + #define R_IEN_CLR 0x2c 42 + #define R_PERCORE_ISR(x) (0x40 + 0x8 * x) 43 + #define R_END 0x64 44 + 45 + #define TYPE_LOONGSON_LIOINTC "loongson.liointc" 46 + #define LOONGSON_LIOINTC(obj) \ 47 + OBJECT_CHECK(struct loongson_liointc, (obj), TYPE_LOONGSON_LIOINTC) 48 + 49 + struct loongson_liointc { 50 + SysBusDevice parent_obj; 51 + 52 + MemoryRegion mmio; 53 + qemu_irq parent_irq[NUM_PARENTS]; 54 + 55 + uint8_t mapper[NUM_IRQS]; /* 0:3 for core, 4:7 for IP */ 56 + uint32_t isr; 57 + uint32_t ien; 58 + uint32_t per_core_isr[NUM_CORES]; 59 + 60 + /* state of the interrupt input pins */ 61 + uint32_t pin_state; 62 + bool parent_state[NUM_PARENTS]; 63 + }; 64 + 65 + static void update_irq(struct loongson_liointc *p) 66 + { 67 + uint32_t irq, core, ip; 68 + uint32_t per_ip_isr[NUM_IPS] = {0}; 69 + 70 + /* level triggered interrupt */ 71 + p->isr = p->pin_state; 72 + 73 + /* Clear disabled IRQs */ 74 + p->isr &= p->ien; 75 + 76 + /* Clear per_core_isr */ 77 + for (core = 0; core < NUM_CORES; core++) { 78 + p->per_core_isr[core] = 0; 79 + } 80 + 81 + /* Update per_core_isr and per_ip_isr */ 82 + for (irq = 0; irq < NUM_IRQS; irq++) { 83 + if (!(p->isr & (1 << irq))) { 84 + continue; 85 + } 86 + 87 + for (core = 0; core < NUM_CORES; core++) { 88 + if ((p->mapper[irq] & (1 << core))) { 89 + p->per_core_isr[core] |= (1 << irq); 90 + } 91 + } 92 + 93 + for (ip = 0; ip < NUM_IPS; ip++) { 94 + if ((p->mapper[irq] & (1 << (ip + 4)))) { 95 + per_ip_isr[ip] |= (1 << irq); 96 + } 97 + } 98 + } 99 + 100 + /* Emit IRQ to parent! */ 101 + for (core = 0; core < NUM_CORES; core++) { 102 + for (ip = 0; ip < NUM_IPS; ip++) { 103 + int parent = PARENT_COREx_IPy(core, ip); 104 + if (p->parent_state[parent] != 105 + (!!p->per_core_isr[core] && !!per_ip_isr[ip])) { 106 + p->parent_state[parent] = !p->parent_state[parent]; 107 + qemu_set_irq(p->parent_irq[parent], p->parent_state[parent]); 108 + } 109 + } 110 + } 111 + } 112 + 113 + static uint64_t 114 + liointc_read(void *opaque, hwaddr addr, unsigned int size) 115 + { 116 + struct loongson_liointc *p = opaque; 117 + uint32_t r = 0; 118 + 119 + /* Mapper is 1 byte */ 120 + if (size == 1 && addr < R_MAPPER_END) { 121 + r = p->mapper[addr]; 122 + goto out; 123 + } 124 + 125 + /* Rest is 4 byte */ 126 + if (size != 4 || (addr % 4)) { 127 + goto out; 128 + } 129 + 130 + if (addr >= R_PERCORE_ISR(0) && 131 + addr < R_PERCORE_ISR(NUM_CORES)) { 132 + int core = (addr - R_PERCORE_ISR(0)) / 4; 133 + r = p->per_core_isr[core]; 134 + goto out; 135 + } 136 + 137 + switch (addr) { 138 + case R_ISR: 139 + r = p->isr; 140 + break; 141 + case R_IEN: 142 + r = p->ien; 143 + break; 144 + default: 145 + break; 146 + } 147 + 148 + out: 149 + D(qemu_log("%s: size=%d addr=%lx val=%x\n", __func__, size, addr, r)); 150 + return r; 151 + } 152 + 153 + static void 154 + liointc_write(void *opaque, hwaddr addr, 155 + uint64_t val64, unsigned int size) 156 + { 157 + struct loongson_liointc *p = opaque; 158 + uint32_t value = val64; 159 + 160 + D(qemu_log("%s: size=%d, addr=%lx val=%x\n", __func__, size, addr, value)); 161 + 162 + /* Mapper is 1 byte */ 163 + if (size == 1 && addr < R_MAPPER_END) { 164 + p->mapper[addr] = value; 165 + goto out; 166 + } 167 + 168 + /* Rest is 4 byte */ 169 + if (size != 4 || (addr % 4)) { 170 + goto out; 171 + } 172 + 173 + if (addr >= R_PERCORE_ISR(0) && 174 + addr < R_PERCORE_ISR(NUM_CORES)) { 175 + int core = (addr - R_PERCORE_ISR(0)) / 4; 176 + p->per_core_isr[core] = value; 177 + goto out; 178 + } 179 + 180 + switch (addr) { 181 + case R_IEN_SET: 182 + p->ien |= value; 183 + break; 184 + case R_IEN_CLR: 185 + p->ien &= ~value; 186 + break; 187 + default: 188 + break; 189 + } 190 + 191 + out: 192 + update_irq(p); 193 + } 194 + 195 + static const MemoryRegionOps pic_ops = { 196 + .read = liointc_read, 197 + .write = liointc_write, 198 + .endianness = DEVICE_NATIVE_ENDIAN, 199 + .valid = { 200 + .min_access_size = 1, 201 + .max_access_size = 4 202 + } 203 + }; 204 + 205 + static void irq_handler(void *opaque, int irq, int level) 206 + { 207 + struct loongson_liointc *p = opaque; 208 + 209 + p->pin_state &= ~(1 << irq); 210 + p->pin_state |= level << irq; 211 + update_irq(p); 212 + } 213 + 214 + static void loongson_liointc_init(Object *obj) 215 + { 216 + struct loongson_liointc *p = LOONGSON_LIOINTC(obj); 217 + int i; 218 + 219 + qdev_init_gpio_in(DEVICE(obj), irq_handler, 32); 220 + 221 + for (i = 0; i < NUM_PARENTS; i++) { 222 + sysbus_init_irq(SYS_BUS_DEVICE(obj), &p->parent_irq[i]); 223 + } 224 + 225 + memory_region_init_io(&p->mmio, obj, &pic_ops, p, 226 + "loongson.liointc", R_END); 227 + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &p->mmio); 228 + } 229 + 230 + static const TypeInfo loongson_liointc_info = { 231 + .name = TYPE_LOONGSON_LIOINTC, 232 + .parent = TYPE_SYS_BUS_DEVICE, 233 + .instance_size = sizeof(struct loongson_liointc), 234 + .instance_init = loongson_liointc_init, 235 + }; 236 + 237 + static void loongson_liointc_register_types(void) 238 + { 239 + type_register_static(&loongson_liointc_info); 240 + } 241 + 242 + type_init(loongson_liointc_register_types)