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