]>
Commit | Line | Data |
---|---|---|
9ae1329e CLG |
1 | /* |
2 | * QEMU PowerPC PowerNV (POWER8) PHB3 model | |
3 | * | |
4 | * Copyright (c) 2014-2020, IBM Corporation. | |
5 | * | |
6 | * This code is licensed under the GPL version 2 or later. See the | |
7 | * COPYING file in the top-level directory. | |
8 | */ | |
9 | #include "qemu/osdep.h" | |
10 | #include "qemu/log.h" | |
11 | #include "qapi/error.h" | |
9ae1329e CLG |
12 | #include "hw/pci-host/pnv_phb3_regs.h" |
13 | #include "hw/pci-host/pnv_phb3.h" | |
14 | #include "hw/ppc/pnv.h" | |
15 | #include "hw/pci/msi.h" | |
16 | #include "monitor/monitor.h" | |
17 | #include "hw/irq.h" | |
18 | #include "hw/qdev-properties.h" | |
19 | #include "sysemu/reset.h" | |
20 | ||
21 | static uint64_t phb3_msi_ive_addr(PnvPHB3 *phb, int srcno) | |
22 | { | |
23 | uint64_t ivtbar = phb->regs[PHB_IVT_BAR >> 3]; | |
24 | uint64_t phbctl = phb->regs[PHB_CONTROL >> 3]; | |
25 | ||
26 | if (!(ivtbar & PHB_IVT_BAR_ENABLE)) { | |
27 | qemu_log_mask(LOG_GUEST_ERROR, "Failed access to disable IVT BAR !"); | |
28 | return 0; | |
29 | } | |
30 | ||
31 | if (srcno >= (ivtbar & PHB_IVT_LENGTH_MASK)) { | |
32 | qemu_log_mask(LOG_GUEST_ERROR, "MSI out of bounds (%d vs 0x%"PRIx64")", | |
33 | srcno, (uint64_t) (ivtbar & PHB_IVT_LENGTH_MASK)); | |
34 | return 0; | |
35 | } | |
36 | ||
37 | ivtbar &= PHB_IVT_BASE_ADDRESS_MASK; | |
38 | ||
39 | if (phbctl & PHB_CTRL_IVE_128_BYTES) { | |
40 | return ivtbar + 128 * srcno; | |
41 | } else { | |
42 | return ivtbar + 16 * srcno; | |
43 | } | |
44 | } | |
45 | ||
46 | static bool phb3_msi_read_ive(PnvPHB3 *phb, int srcno, uint64_t *out_ive) | |
47 | { | |
48 | uint64_t ive_addr, ive; | |
49 | ||
50 | ive_addr = phb3_msi_ive_addr(phb, srcno); | |
51 | if (!ive_addr) { | |
52 | return false; | |
53 | } | |
54 | ||
ba06fe8a PMD |
55 | if (dma_memory_read(&address_space_memory, ive_addr, |
56 | &ive, sizeof(ive), MEMTXATTRS_UNSPECIFIED)) { | |
9ae1329e CLG |
57 | qemu_log_mask(LOG_GUEST_ERROR, "Failed to read IVE at 0x%" PRIx64, |
58 | ive_addr); | |
59 | return false; | |
60 | } | |
61 | *out_ive = be64_to_cpu(ive); | |
62 | ||
63 | return true; | |
64 | } | |
65 | ||
66 | static void phb3_msi_set_p(Phb3MsiState *msi, int srcno, uint8_t gen) | |
67 | { | |
68 | uint64_t ive_addr; | |
69 | uint8_t p = 0x01 | (gen << 1); | |
70 | ||
71 | ive_addr = phb3_msi_ive_addr(msi->phb, srcno); | |
72 | if (!ive_addr) { | |
73 | return; | |
74 | } | |
75 | ||
ba06fe8a PMD |
76 | if (dma_memory_write(&address_space_memory, ive_addr + 4, |
77 | &p, 1, MEMTXATTRS_UNSPECIFIED)) { | |
9ae1329e CLG |
78 | qemu_log_mask(LOG_GUEST_ERROR, |
79 | "Failed to write IVE (set P) at 0x%" PRIx64, ive_addr); | |
80 | } | |
81 | } | |
82 | ||
83 | static void phb3_msi_set_q(Phb3MsiState *msi, int srcno) | |
84 | { | |
85 | uint64_t ive_addr; | |
86 | uint8_t q = 0x01; | |
87 | ||
88 | ive_addr = phb3_msi_ive_addr(msi->phb, srcno); | |
89 | if (!ive_addr) { | |
90 | return; | |
91 | } | |
92 | ||
ba06fe8a PMD |
93 | if (dma_memory_write(&address_space_memory, ive_addr + 5, |
94 | &q, 1, MEMTXATTRS_UNSPECIFIED)) { | |
9ae1329e CLG |
95 | qemu_log_mask(LOG_GUEST_ERROR, |
96 | "Failed to write IVE (set Q) at 0x%" PRIx64, ive_addr); | |
97 | } | |
98 | } | |
99 | ||
100 | static void phb3_msi_try_send(Phb3MsiState *msi, int srcno, bool force) | |
101 | { | |
102 | ICSState *ics = ICS(msi); | |
103 | uint64_t ive; | |
104 | uint64_t server, prio, pq, gen; | |
105 | ||
106 | if (!phb3_msi_read_ive(msi->phb, srcno, &ive)) { | |
107 | return; | |
108 | } | |
109 | ||
110 | server = GETFIELD(IODA2_IVT_SERVER, ive); | |
111 | prio = GETFIELD(IODA2_IVT_PRIORITY, ive); | |
112 | if (!force) { | |
113 | pq = GETFIELD(IODA2_IVT_Q, ive) | (GETFIELD(IODA2_IVT_P, ive) << 1); | |
114 | } else { | |
115 | pq = 0; | |
116 | } | |
117 | gen = GETFIELD(IODA2_IVT_GEN, ive); | |
118 | ||
119 | /* | |
120 | * The low order 2 bits are the link pointer (Type II interrupts). | |
121 | * Shift back to get a valid IRQ server. | |
122 | */ | |
123 | server >>= 2; | |
124 | ||
125 | switch (pq) { | |
126 | case 0: /* 00 */ | |
127 | if (prio == 0xff) { | |
128 | /* Masked, set Q */ | |
129 | phb3_msi_set_q(msi, srcno); | |
130 | } else { | |
131 | /* Enabled, set P and send */ | |
132 | phb3_msi_set_p(msi, srcno, gen); | |
133 | icp_irq(ics, server, srcno + ics->offset, prio); | |
134 | } | |
135 | break; | |
136 | case 2: /* 10 */ | |
137 | /* Already pending, set Q */ | |
138 | phb3_msi_set_q(msi, srcno); | |
139 | break; | |
140 | case 1: /* 01 */ | |
141 | case 3: /* 11 */ | |
142 | default: | |
143 | /* Just drop stuff if Q already set */ | |
144 | break; | |
145 | } | |
146 | } | |
147 | ||
148 | static void phb3_msi_set_irq(void *opaque, int srcno, int val) | |
149 | { | |
150 | Phb3MsiState *msi = PHB3_MSI(opaque); | |
151 | ||
152 | if (val) { | |
153 | phb3_msi_try_send(msi, srcno, false); | |
154 | } | |
155 | } | |
156 | ||
157 | ||
158 | void pnv_phb3_msi_send(Phb3MsiState *msi, uint64_t addr, uint16_t data, | |
159 | int32_t dev_pe) | |
160 | { | |
161 | ICSState *ics = ICS(msi); | |
162 | uint64_t ive; | |
163 | uint16_t pe; | |
164 | uint32_t src = ((addr >> 4) & 0xffff) | (data & 0x1f); | |
165 | ||
166 | if (src >= ics->nr_irqs) { | |
167 | qemu_log_mask(LOG_GUEST_ERROR, "MSI %d out of bounds", src); | |
168 | return; | |
169 | } | |
170 | if (dev_pe >= 0) { | |
171 | if (!phb3_msi_read_ive(msi->phb, src, &ive)) { | |
172 | return; | |
173 | } | |
174 | pe = GETFIELD(IODA2_IVT_PE, ive); | |
175 | if (pe != dev_pe) { | |
176 | qemu_log_mask(LOG_GUEST_ERROR, | |
177 | "MSI %d send by PE#%d but assigned to PE#%d", | |
178 | src, dev_pe, pe); | |
179 | return; | |
180 | } | |
181 | } | |
182 | qemu_irq_pulse(msi->qirqs[src]); | |
183 | } | |
184 | ||
185 | void pnv_phb3_msi_ffi(Phb3MsiState *msi, uint64_t val) | |
186 | { | |
187 | /* Emit interrupt */ | |
188 | pnv_phb3_msi_send(msi, val, 0, -1); | |
189 | ||
190 | /* Clear FFI lock */ | |
191 | msi->phb->regs[PHB_FFI_LOCK >> 3] = 0; | |
192 | } | |
193 | ||
194 | static void phb3_msi_reject(ICSState *ics, uint32_t nr) | |
195 | { | |
196 | Phb3MsiState *msi = PHB3_MSI(ics); | |
197 | unsigned int srcno = nr - ics->offset; | |
198 | unsigned int idx = srcno >> 6; | |
199 | unsigned int bit = 1ull << (srcno & 0x3f); | |
200 | ||
201 | assert(srcno < PHB3_MAX_MSI); | |
202 | ||
203 | msi->rba[idx] |= bit; | |
204 | msi->rba_sum |= (1u << idx); | |
205 | } | |
206 | ||
207 | static void phb3_msi_resend(ICSState *ics) | |
208 | { | |
209 | Phb3MsiState *msi = PHB3_MSI(ics); | |
210 | unsigned int i, j; | |
211 | ||
212 | if (msi->rba_sum == 0) { | |
213 | return; | |
214 | } | |
215 | ||
216 | for (i = 0; i < 32; i++) { | |
217 | if ((msi->rba_sum & (1u << i)) == 0) { | |
218 | continue; | |
219 | } | |
220 | msi->rba_sum &= ~(1u << i); | |
221 | for (j = 0; j < 64; j++) { | |
222 | if ((msi->rba[i] & (1ull << j)) == 0) { | |
223 | continue; | |
224 | } | |
5a205fcf | 225 | msi->rba[i] &= ~(1ull << j); |
9ae1329e CLG |
226 | phb3_msi_try_send(msi, i * 64 + j, true); |
227 | } | |
228 | } | |
229 | } | |
230 | ||
a0c2e80a | 231 | static void phb3_msi_reset_hold(Object *obj) |
9ae1329e | 232 | { |
a0c2e80a PM |
233 | Phb3MsiState *msi = PHB3_MSI(obj); |
234 | ICSStateClass *icsc = ICS_GET_CLASS(obj); | |
9ae1329e | 235 | |
a0c2e80a PM |
236 | if (icsc->parent_phases.hold) { |
237 | icsc->parent_phases.hold(obj); | |
238 | } | |
9ae1329e CLG |
239 | |
240 | memset(msi->rba, 0, sizeof(msi->rba)); | |
241 | msi->rba_sum = 0; | |
242 | } | |
243 | ||
9ae1329e CLG |
244 | void pnv_phb3_msi_update_config(Phb3MsiState *msi, uint32_t base, |
245 | uint32_t count) | |
246 | { | |
247 | ICSState *ics = ICS(msi); | |
248 | ||
249 | if (count > PHB3_MAX_MSI) { | |
250 | count = PHB3_MAX_MSI; | |
251 | } | |
252 | ics->nr_irqs = count; | |
253 | ics->offset = base; | |
254 | } | |
255 | ||
256 | static void phb3_msi_realize(DeviceState *dev, Error **errp) | |
257 | { | |
258 | Phb3MsiState *msi = PHB3_MSI(dev); | |
259 | ICSState *ics = ICS(msi); | |
260 | ICSStateClass *icsc = ICS_GET_CLASS(ics); | |
261 | Error *local_err = NULL; | |
262 | ||
263 | assert(msi->phb); | |
264 | ||
265 | icsc->parent_realize(dev, &local_err); | |
266 | if (local_err) { | |
267 | error_propagate(errp, local_err); | |
268 | return; | |
269 | } | |
270 | ||
271 | msi->qirqs = qemu_allocate_irqs(phb3_msi_set_irq, msi, ics->nr_irqs); | |
9ae1329e CLG |
272 | } |
273 | ||
274 | static void phb3_msi_instance_init(Object *obj) | |
275 | { | |
276 | Phb3MsiState *msi = PHB3_MSI(obj); | |
277 | ICSState *ics = ICS(obj); | |
278 | ||
279 | object_property_add_link(obj, "phb", TYPE_PNV_PHB3, | |
280 | (Object **)&msi->phb, | |
281 | object_property_allow_set_link, | |
d2623129 | 282 | OBJ_PROP_LINK_STRONG); |
9ae1329e CLG |
283 | |
284 | /* Will be overriden later */ | |
285 | ics->offset = 0; | |
286 | } | |
287 | ||
288 | static void phb3_msi_class_init(ObjectClass *klass, void *data) | |
289 | { | |
290 | DeviceClass *dc = DEVICE_CLASS(klass); | |
291 | ICSStateClass *isc = ICS_CLASS(klass); | |
a0c2e80a | 292 | ResettableClass *rc = RESETTABLE_CLASS(klass); |
9ae1329e CLG |
293 | |
294 | device_class_set_parent_realize(dc, phb3_msi_realize, | |
295 | &isc->parent_realize); | |
a0c2e80a PM |
296 | resettable_class_set_parent_phases(rc, NULL, phb3_msi_reset_hold, NULL, |
297 | &isc->parent_phases); | |
9ae1329e CLG |
298 | |
299 | isc->reject = phb3_msi_reject; | |
300 | isc->resend = phb3_msi_resend; | |
301 | } | |
302 | ||
303 | static const TypeInfo phb3_msi_info = { | |
304 | .name = TYPE_PHB3_MSI, | |
305 | .parent = TYPE_ICS, | |
306 | .instance_size = sizeof(Phb3MsiState), | |
307 | .class_init = phb3_msi_class_init, | |
308 | .class_size = sizeof(ICSStateClass), | |
309 | .instance_init = phb3_msi_instance_init, | |
310 | }; | |
311 | ||
312 | static void pnv_phb3_msi_register_types(void) | |
313 | { | |
314 | type_register_static(&phb3_msi_info); | |
315 | } | |
316 | ||
317 | type_init(pnv_phb3_msi_register_types); | |
318 | ||
319 | void pnv_phb3_msi_pic_print_info(Phb3MsiState *msi, Monitor *mon) | |
320 | { | |
321 | ICSState *ics = ICS(msi); | |
322 | int i; | |
323 | ||
324 | monitor_printf(mon, "ICS %4x..%4x %p\n", | |
325 | ics->offset, ics->offset + ics->nr_irqs - 1, ics); | |
326 | ||
327 | for (i = 0; i < ics->nr_irqs; i++) { | |
328 | uint64_t ive; | |
329 | ||
330 | if (!phb3_msi_read_ive(msi->phb, i, &ive)) { | |
331 | return; | |
332 | } | |
333 | ||
334 | if (GETFIELD(IODA2_IVT_PRIORITY, ive) == 0xff) { | |
335 | continue; | |
336 | } | |
337 | ||
338 | monitor_printf(mon, " %4x %c%c server=%04x prio=%02x gen=%d\n", | |
339 | ics->offset + i, | |
340 | GETFIELD(IODA2_IVT_P, ive) ? 'P' : '-', | |
341 | GETFIELD(IODA2_IVT_Q, ive) ? 'Q' : '-', | |
342 | (uint32_t) GETFIELD(IODA2_IVT_SERVER, ive) >> 2, | |
343 | (uint32_t) GETFIELD(IODA2_IVT_PRIORITY, ive), | |
344 | (uint32_t) GETFIELD(IODA2_IVT_GEN, ive)); | |
345 | } | |
346 | } |