xref: /openbmc/qemu/hw/misc/edu.c (revision 890ad550)
1 /*
2  * QEMU educational PCI device
3  *
4  * Copyright (c) 2012-2015 Jiri Slaby
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  */
24 
25 #include "qemu/osdep.h"
26 #include "hw/pci/pci.h"
27 #include "qemu/timer.h"
28 #include "qemu/main-loop.h" /* iothread mutex */
29 #include "qapi/visitor.h"
30 
31 #define EDU(obj)        OBJECT_CHECK(EduState, obj, "edu")
32 
33 #define FACT_IRQ        0x00000001
34 #define DMA_IRQ         0x00000100
35 
36 #define DMA_START       0x40000
37 #define DMA_SIZE        4096
38 
39 typedef struct {
40     PCIDevice pdev;
41     MemoryRegion mmio;
42 
43     QemuThread thread;
44     QemuMutex thr_mutex;
45     QemuCond thr_cond;
46     bool stopping;
47 
48     uint32_t addr4;
49     uint32_t fact;
50 #define EDU_STATUS_COMPUTING    0x01
51 #define EDU_STATUS_IRQFACT      0x80
52     uint32_t status;
53 
54     uint32_t irq_status;
55 
56 #define EDU_DMA_RUN             0x1
57 #define EDU_DMA_DIR(cmd)        (((cmd) & 0x2) >> 1)
58 # define EDU_DMA_FROM_PCI       0
59 # define EDU_DMA_TO_PCI         1
60 #define EDU_DMA_IRQ             0x4
61     struct dma_state {
62         dma_addr_t src;
63         dma_addr_t dst;
64         dma_addr_t cnt;
65         dma_addr_t cmd;
66     } dma;
67     QEMUTimer dma_timer;
68     char dma_buf[DMA_SIZE];
69     uint64_t dma_mask;
70 } EduState;
71 
72 static void edu_raise_irq(EduState *edu, uint32_t val)
73 {
74     edu->irq_status |= val;
75     if (edu->irq_status) {
76         pci_set_irq(&edu->pdev, 1);
77     }
78 }
79 
80 static void edu_lower_irq(EduState *edu, uint32_t val)
81 {
82     edu->irq_status &= ~val;
83 
84     if (!edu->irq_status) {
85         pci_set_irq(&edu->pdev, 0);
86     }
87 }
88 
89 static bool within(uint32_t addr, uint32_t start, uint32_t end)
90 {
91     return start <= addr && addr < end;
92 }
93 
94 static void edu_check_range(uint32_t addr, uint32_t size1, uint32_t start,
95                 uint32_t size2)
96 {
97     uint32_t end1 = addr + size1;
98     uint32_t end2 = start + size2;
99 
100     if (within(addr, start, end2) &&
101             end1 > addr && within(end1, start, end2)) {
102         return;
103     }
104 
105     hw_error("EDU: DMA range 0x%.8x-0x%.8x out of bounds (0x%.8x-0x%.8x)!",
106             addr, end1 - 1, start, end2 - 1);
107 }
108 
109 static dma_addr_t edu_clamp_addr(const EduState *edu, dma_addr_t addr)
110 {
111     dma_addr_t res = addr & edu->dma_mask;
112 
113     if (addr != res) {
114         printf("EDU: clamping DMA %#.16"PRIx64" to %#.16"PRIx64"!\n", addr, res);
115     }
116 
117     return res;
118 }
119 
120 static void edu_dma_timer(void *opaque)
121 {
122     EduState *edu = opaque;
123     bool raise_irq = false;
124 
125     if (!(edu->dma.cmd & EDU_DMA_RUN)) {
126         return;
127     }
128 
129     if (EDU_DMA_DIR(edu->dma.cmd) == EDU_DMA_FROM_PCI) {
130         uint32_t dst = edu->dma.dst;
131         edu_check_range(dst, edu->dma.cnt, DMA_START, DMA_SIZE);
132         dst -= DMA_START;
133         pci_dma_read(&edu->pdev, edu_clamp_addr(edu, edu->dma.src),
134                 edu->dma_buf + dst, edu->dma.cnt);
135     } else {
136         uint32_t src = edu->dma.src;
137         edu_check_range(src, edu->dma.cnt, DMA_START, DMA_SIZE);
138         src -= DMA_START;
139         pci_dma_write(&edu->pdev, edu_clamp_addr(edu, edu->dma.dst),
140                 edu->dma_buf + src, edu->dma.cnt);
141     }
142 
143     edu->dma.cmd &= ~EDU_DMA_RUN;
144     if (edu->dma.cmd & EDU_DMA_IRQ) {
145         raise_irq = true;
146     }
147 
148     if (raise_irq) {
149         edu_raise_irq(edu, DMA_IRQ);
150     }
151 }
152 
153 static void dma_rw(EduState *edu, bool write, dma_addr_t *val, dma_addr_t *dma,
154                 bool timer)
155 {
156     if (write && (edu->dma.cmd & EDU_DMA_RUN)) {
157         return;
158     }
159 
160     if (write) {
161         *dma = *val;
162     } else {
163         *val = *dma;
164     }
165 
166     if (timer) {
167         timer_mod(&edu->dma_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 100);
168     }
169 }
170 
171 static uint64_t edu_mmio_read(void *opaque, hwaddr addr, unsigned size)
172 {
173     EduState *edu = opaque;
174     uint64_t val = ~0ULL;
175 
176     if (size != 4) {
177         return val;
178     }
179 
180     switch (addr) {
181     case 0x00:
182         val = 0x010000edu;
183         break;
184     case 0x04:
185         val = edu->addr4;
186         break;
187     case 0x08:
188         qemu_mutex_lock(&edu->thr_mutex);
189         val = edu->fact;
190         qemu_mutex_unlock(&edu->thr_mutex);
191         break;
192     case 0x20:
193         val = atomic_read(&edu->status);
194         break;
195     case 0x24:
196         val = edu->irq_status;
197         break;
198     case 0x80:
199         dma_rw(edu, false, &val, &edu->dma.src, false);
200         break;
201     case 0x88:
202         dma_rw(edu, false, &val, &edu->dma.dst, false);
203         break;
204     case 0x90:
205         dma_rw(edu, false, &val, &edu->dma.cnt, false);
206         break;
207     case 0x98:
208         dma_rw(edu, false, &val, &edu->dma.cmd, false);
209         break;
210     }
211 
212     return val;
213 }
214 
215 static void edu_mmio_write(void *opaque, hwaddr addr, uint64_t val,
216                 unsigned size)
217 {
218     EduState *edu = opaque;
219 
220     if (addr < 0x80 && size != 4) {
221         return;
222     }
223 
224     if (addr >= 0x80 && size != 4 && size != 8) {
225         return;
226     }
227 
228     switch (addr) {
229     case 0x04:
230         edu->addr4 = ~val;
231         break;
232     case 0x08:
233         if (atomic_read(&edu->status) & EDU_STATUS_COMPUTING) {
234             break;
235         }
236         /* EDU_STATUS_COMPUTING cannot go 0->1 concurrently, because it is only
237          * set in this function and it is under the iothread mutex.
238          */
239         qemu_mutex_lock(&edu->thr_mutex);
240         edu->fact = val;
241         atomic_or(&edu->status, EDU_STATUS_COMPUTING);
242         qemu_cond_signal(&edu->thr_cond);
243         qemu_mutex_unlock(&edu->thr_mutex);
244         break;
245     case 0x20:
246         if (val & EDU_STATUS_IRQFACT) {
247             atomic_or(&edu->status, EDU_STATUS_IRQFACT);
248         } else {
249             atomic_and(&edu->status, ~EDU_STATUS_IRQFACT);
250         }
251         break;
252     case 0x60:
253         edu_raise_irq(edu, val);
254         break;
255     case 0x64:
256         edu_lower_irq(edu, val);
257         break;
258     case 0x80:
259         dma_rw(edu, true, &val, &edu->dma.src, false);
260         break;
261     case 0x88:
262         dma_rw(edu, true, &val, &edu->dma.dst, false);
263         break;
264     case 0x90:
265         dma_rw(edu, true, &val, &edu->dma.cnt, false);
266         break;
267     case 0x98:
268         if (!(val & EDU_DMA_RUN)) {
269             break;
270         }
271         dma_rw(edu, true, &val, &edu->dma.cmd, true);
272         break;
273     }
274 }
275 
276 static const MemoryRegionOps edu_mmio_ops = {
277     .read = edu_mmio_read,
278     .write = edu_mmio_write,
279     .endianness = DEVICE_NATIVE_ENDIAN,
280 };
281 
282 /*
283  * We purposely use a thread, so that users are forced to wait for the status
284  * register.
285  */
286 static void *edu_fact_thread(void *opaque)
287 {
288     EduState *edu = opaque;
289 
290     while (1) {
291         uint32_t val, ret = 1;
292 
293         qemu_mutex_lock(&edu->thr_mutex);
294         while ((atomic_read(&edu->status) & EDU_STATUS_COMPUTING) == 0 &&
295                         !edu->stopping) {
296             qemu_cond_wait(&edu->thr_cond, &edu->thr_mutex);
297         }
298 
299         if (edu->stopping) {
300             qemu_mutex_unlock(&edu->thr_mutex);
301             break;
302         }
303 
304         val = edu->fact;
305         qemu_mutex_unlock(&edu->thr_mutex);
306 
307         while (val > 0) {
308             ret *= val--;
309         }
310 
311         /*
312          * We should sleep for a random period here, so that students are
313          * forced to check the status properly.
314          */
315 
316         qemu_mutex_lock(&edu->thr_mutex);
317         edu->fact = ret;
318         qemu_mutex_unlock(&edu->thr_mutex);
319         atomic_and(&edu->status, ~EDU_STATUS_COMPUTING);
320 
321         if (atomic_read(&edu->status) & EDU_STATUS_IRQFACT) {
322             qemu_mutex_lock_iothread();
323             edu_raise_irq(edu, FACT_IRQ);
324             qemu_mutex_unlock_iothread();
325         }
326     }
327 
328     return NULL;
329 }
330 
331 static void pci_edu_realize(PCIDevice *pdev, Error **errp)
332 {
333     EduState *edu = DO_UPCAST(EduState, pdev, pdev);
334     uint8_t *pci_conf = pdev->config;
335 
336     timer_init_ms(&edu->dma_timer, QEMU_CLOCK_VIRTUAL, edu_dma_timer, edu);
337 
338     qemu_mutex_init(&edu->thr_mutex);
339     qemu_cond_init(&edu->thr_cond);
340     qemu_thread_create(&edu->thread, "edu", edu_fact_thread,
341                        edu, QEMU_THREAD_JOINABLE);
342 
343     pci_config_set_interrupt_pin(pci_conf, 1);
344 
345     memory_region_init_io(&edu->mmio, OBJECT(edu), &edu_mmio_ops, edu,
346                     "edu-mmio", 1 << 20);
347     pci_register_bar(pdev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &edu->mmio);
348 }
349 
350 static void pci_edu_uninit(PCIDevice *pdev)
351 {
352     EduState *edu = DO_UPCAST(EduState, pdev, pdev);
353 
354     qemu_mutex_lock(&edu->thr_mutex);
355     edu->stopping = true;
356     qemu_mutex_unlock(&edu->thr_mutex);
357     qemu_cond_signal(&edu->thr_cond);
358     qemu_thread_join(&edu->thread);
359 
360     qemu_cond_destroy(&edu->thr_cond);
361     qemu_mutex_destroy(&edu->thr_mutex);
362 
363     timer_del(&edu->dma_timer);
364 }
365 
366 static void edu_obj_uint64(Object *obj, Visitor *v, const char *name,
367                            void *opaque, Error **errp)
368 {
369     uint64_t *val = opaque;
370 
371     visit_type_uint64(v, name, val, errp);
372 }
373 
374 static void edu_instance_init(Object *obj)
375 {
376     EduState *edu = EDU(obj);
377 
378     edu->dma_mask = (1UL << 28) - 1;
379     object_property_add(obj, "dma_mask", "uint64", edu_obj_uint64,
380                     edu_obj_uint64, NULL, &edu->dma_mask, NULL);
381 }
382 
383 static void edu_class_init(ObjectClass *class, void *data)
384 {
385     PCIDeviceClass *k = PCI_DEVICE_CLASS(class);
386 
387     k->realize = pci_edu_realize;
388     k->exit = pci_edu_uninit;
389     k->vendor_id = PCI_VENDOR_ID_QEMU;
390     k->device_id = 0x11e8;
391     k->revision = 0x10;
392     k->class_id = PCI_CLASS_OTHERS;
393 }
394 
395 static void pci_edu_register_types(void)
396 {
397     static const TypeInfo edu_info = {
398         .name          = "edu",
399         .parent        = TYPE_PCI_DEVICE,
400         .instance_size = sizeof(EduState),
401         .instance_init = edu_instance_init,
402         .class_init    = edu_class_init,
403     };
404 
405     type_register_static(&edu_info);
406 }
407 type_init(pci_edu_register_types)
408