]> git.proxmox.com Git - mirror_qemu.git/blob - hw/intc/loongarch_ipi.c
machine: Improve error message when using default RAM backend id
[mirror_qemu.git] / hw / intc / loongarch_ipi.c
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3 * LoongArch ipi interrupt support
4 *
5 * Copyright (C) 2021 Loongson Technology Corporation Limited
6 */
7
8 #include "qemu/osdep.h"
9 #include "hw/sysbus.h"
10 #include "hw/intc/loongarch_ipi.h"
11 #include "hw/irq.h"
12 #include "qapi/error.h"
13 #include "qemu/log.h"
14 #include "exec/address-spaces.h"
15 #include "hw/loongarch/virt.h"
16 #include "migration/vmstate.h"
17 #include "target/loongarch/internals.h"
18 #include "trace.h"
19
20 static void loongarch_ipi_writel(void *, hwaddr, uint64_t, unsigned);
21
22 static uint64_t loongarch_ipi_readl(void *opaque, hwaddr addr, unsigned size)
23 {
24 IPICore *s = opaque;
25 uint64_t ret = 0;
26 int index = 0;
27
28 addr &= 0xff;
29 switch (addr) {
30 case CORE_STATUS_OFF:
31 ret = s->status;
32 break;
33 case CORE_EN_OFF:
34 ret = s->en;
35 break;
36 case CORE_SET_OFF:
37 ret = 0;
38 break;
39 case CORE_CLEAR_OFF:
40 ret = 0;
41 break;
42 case CORE_BUF_20 ... CORE_BUF_38 + 4:
43 index = (addr - CORE_BUF_20) >> 2;
44 ret = s->buf[index];
45 break;
46 default:
47 qemu_log_mask(LOG_UNIMP, "invalid read: %x", (uint32_t)addr);
48 break;
49 }
50
51 trace_loongarch_ipi_read(size, (uint64_t)addr, ret);
52 return ret;
53 }
54
55 static void send_ipi_data(CPULoongArchState *env, uint64_t val, hwaddr addr)
56 {
57 int i, mask = 0, data = 0;
58
59 /*
60 * bit 27-30 is mask for byte writing,
61 * if the mask is 0, we need not to do anything.
62 */
63 if ((val >> 27) & 0xf) {
64 data = address_space_ldl(&env->address_space_iocsr, addr,
65 MEMTXATTRS_UNSPECIFIED, NULL);
66 for (i = 0; i < 4; i++) {
67 /* get mask for byte writing */
68 if (val & (0x1 << (27 + i))) {
69 mask |= 0xff << (i * 8);
70 }
71 }
72 }
73
74 data &= mask;
75 data |= (val >> 32) & ~mask;
76 address_space_stl(&env->address_space_iocsr, addr,
77 data, MEMTXATTRS_UNSPECIFIED, NULL);
78 }
79
80 static int archid_cmp(const void *a, const void *b)
81 {
82 CPUArchId *archid_a = (CPUArchId *)a;
83 CPUArchId *archid_b = (CPUArchId *)b;
84
85 return archid_a->arch_id - archid_b->arch_id;
86 }
87
88 static CPUArchId *find_cpu_by_archid(MachineState *ms, uint32_t id)
89 {
90 CPUArchId apic_id, *found_cpu;
91
92 apic_id.arch_id = id;
93 found_cpu = bsearch(&apic_id, ms->possible_cpus->cpus,
94 ms->possible_cpus->len, sizeof(*ms->possible_cpus->cpus),
95 archid_cmp);
96
97 return found_cpu;
98 }
99
100 static CPUState *ipi_getcpu(int arch_id)
101 {
102 MachineState *machine = MACHINE(qdev_get_machine());
103 CPUArchId *archid;
104
105 archid = find_cpu_by_archid(machine, arch_id);
106 return CPU(archid->cpu);
107 }
108
109 static void ipi_send(uint64_t val)
110 {
111 uint32_t cpuid;
112 uint8_t vector;
113 CPUState *cs;
114 LoongArchCPU *cpu;
115 LoongArchIPI *s;
116
117 cpuid = extract32(val, 16, 10);
118 if (cpuid >= LOONGARCH_MAX_CPUS) {
119 trace_loongarch_ipi_unsupported_cpuid("IOCSR_IPI_SEND", cpuid);
120 return;
121 }
122
123 /* IPI status vector */
124 vector = extract8(val, 0, 5);
125
126 cs = ipi_getcpu(cpuid);
127 cpu = LOONGARCH_CPU(cs);
128 s = LOONGARCH_IPI(cpu->env.ipistate);
129 loongarch_ipi_writel(&s->ipi_core, CORE_SET_OFF, BIT(vector), 4);
130 }
131
132 static void mail_send(uint64_t val)
133 {
134 uint32_t cpuid;
135 hwaddr addr;
136 CPULoongArchState *env;
137 CPUState *cs;
138 LoongArchCPU *cpu;
139
140 cpuid = extract32(val, 16, 10);
141 if (cpuid >= LOONGARCH_MAX_CPUS) {
142 trace_loongarch_ipi_unsupported_cpuid("IOCSR_MAIL_SEND", cpuid);
143 return;
144 }
145
146 addr = 0x1020 + (val & 0x1c);
147 cs = ipi_getcpu(cpuid);
148 cpu = LOONGARCH_CPU(cs);
149 env = &cpu->env;
150 send_ipi_data(env, val, addr);
151 }
152
153 static void any_send(uint64_t val)
154 {
155 uint32_t cpuid;
156 hwaddr addr;
157 CPULoongArchState *env;
158 CPUState *cs;
159 LoongArchCPU *cpu;
160
161 cpuid = extract32(val, 16, 10);
162 if (cpuid >= LOONGARCH_MAX_CPUS) {
163 trace_loongarch_ipi_unsupported_cpuid("IOCSR_ANY_SEND", cpuid);
164 return;
165 }
166
167 addr = val & 0xffff;
168 cs = ipi_getcpu(cpuid);
169 cpu = LOONGARCH_CPU(cs);
170 env = &cpu->env;
171 send_ipi_data(env, val, addr);
172 }
173
174 static void loongarch_ipi_writel(void *opaque, hwaddr addr, uint64_t val,
175 unsigned size)
176 {
177 IPICore *s = opaque;
178 int index = 0;
179
180 addr &= 0xff;
181 trace_loongarch_ipi_write(size, (uint64_t)addr, val);
182 switch (addr) {
183 case CORE_STATUS_OFF:
184 qemu_log_mask(LOG_GUEST_ERROR, "can not be written");
185 break;
186 case CORE_EN_OFF:
187 s->en = val;
188 break;
189 case CORE_SET_OFF:
190 s->status |= val;
191 if (s->status != 0 && (s->status & s->en) != 0) {
192 qemu_irq_raise(s->irq);
193 }
194 break;
195 case CORE_CLEAR_OFF:
196 s->status &= ~val;
197 if (s->status == 0 && s->en != 0) {
198 qemu_irq_lower(s->irq);
199 }
200 break;
201 case CORE_BUF_20 ... CORE_BUF_38 + 4:
202 index = (addr - CORE_BUF_20) >> 2;
203 s->buf[index] = val;
204 break;
205 case IOCSR_IPI_SEND:
206 ipi_send(val);
207 break;
208 default:
209 qemu_log_mask(LOG_UNIMP, "invalid write: %x", (uint32_t)addr);
210 break;
211 }
212 }
213
214 static const MemoryRegionOps loongarch_ipi_ops = {
215 .read = loongarch_ipi_readl,
216 .write = loongarch_ipi_writel,
217 .impl.min_access_size = 4,
218 .impl.max_access_size = 4,
219 .valid.min_access_size = 4,
220 .valid.max_access_size = 8,
221 .endianness = DEVICE_LITTLE_ENDIAN,
222 };
223
224 /* mail send and any send only support writeq */
225 static void loongarch_ipi_writeq(void *opaque, hwaddr addr, uint64_t val,
226 unsigned size)
227 {
228 addr &= 0xfff;
229 switch (addr) {
230 case MAIL_SEND_OFFSET:
231 mail_send(val);
232 break;
233 case ANY_SEND_OFFSET:
234 any_send(val);
235 break;
236 default:
237 break;
238 }
239 }
240
241 static const MemoryRegionOps loongarch_ipi64_ops = {
242 .write = loongarch_ipi_writeq,
243 .impl.min_access_size = 8,
244 .impl.max_access_size = 8,
245 .valid.min_access_size = 8,
246 .valid.max_access_size = 8,
247 .endianness = DEVICE_LITTLE_ENDIAN,
248 };
249
250 static void loongarch_ipi_init(Object *obj)
251 {
252 LoongArchIPI *s = LOONGARCH_IPI(obj);
253 SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
254
255 memory_region_init_io(&s->ipi_iocsr_mem, obj, &loongarch_ipi_ops,
256 &s->ipi_core, "loongarch_ipi_iocsr", 0x48);
257
258 /* loongarch_ipi_iocsr performs re-entrant IO through ipi_send */
259 s->ipi_iocsr_mem.disable_reentrancy_guard = true;
260
261 sysbus_init_mmio(sbd, &s->ipi_iocsr_mem);
262
263 memory_region_init_io(&s->ipi64_iocsr_mem, obj, &loongarch_ipi64_ops,
264 &s->ipi_core, "loongarch_ipi64_iocsr", 0x118);
265 sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem);
266 qdev_init_gpio_out(DEVICE(obj), &s->ipi_core.irq, 1);
267 }
268
269 static const VMStateDescription vmstate_ipi_core = {
270 .name = "ipi-single",
271 .version_id = 2,
272 .minimum_version_id = 2,
273 .fields = (VMStateField[]) {
274 VMSTATE_UINT32(status, IPICore),
275 VMSTATE_UINT32(en, IPICore),
276 VMSTATE_UINT32(set, IPICore),
277 VMSTATE_UINT32(clear, IPICore),
278 VMSTATE_UINT32_ARRAY(buf, IPICore, IPI_MBX_NUM * 2),
279 VMSTATE_END_OF_LIST()
280 }
281 };
282
283 static const VMStateDescription vmstate_loongarch_ipi = {
284 .name = TYPE_LOONGARCH_IPI,
285 .version_id = 1,
286 .minimum_version_id = 1,
287 .fields = (VMStateField[]) {
288 VMSTATE_STRUCT(ipi_core, LoongArchIPI, 0, vmstate_ipi_core, IPICore),
289 VMSTATE_END_OF_LIST()
290 }
291 };
292
293 static void loongarch_ipi_class_init(ObjectClass *klass, void *data)
294 {
295 DeviceClass *dc = DEVICE_CLASS(klass);
296
297 dc->vmsd = &vmstate_loongarch_ipi;
298 }
299
300 static const TypeInfo loongarch_ipi_info = {
301 .name = TYPE_LOONGARCH_IPI,
302 .parent = TYPE_SYS_BUS_DEVICE,
303 .instance_size = sizeof(LoongArchIPI),
304 .instance_init = loongarch_ipi_init,
305 .class_init = loongarch_ipi_class_init,
306 };
307
308 static void loongarch_ipi_register_types(void)
309 {
310 type_register_static(&loongarch_ipi_info);
311 }
312
313 type_init(loongarch_ipi_register_types)