Initialize
[sdk/emulator/qemu.git] / hw / syborg_virtio.c
1 /*
2  * Virtio Syborg bindings
3  *
4  * Copyright (c) 2009 CodeSourcery
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * 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
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24
25 #include "syborg.h"
26 #include "sysbus.h"
27 #include "virtio.h"
28 #include "virtio-net.h"
29 #include "sysemu.h"
30
31 //#define DEBUG_SYBORG_VIRTIO
32
33 #ifdef DEBUG_SYBORG_VIRTIO
34 #define DPRINTF(fmt, ...) \
35 do { printf("syborg_virtio: " fmt , ## __VA_ARGS__); } while (0)
36 #define BADF(fmt, ...) \
37 do { fprintf(stderr, "syborg_virtio: error: " fmt , ## __VA_ARGS__); \
38     exit(1);} while (0)
39 #else
40 #define DPRINTF(fmt, ...) do {} while(0)
41 #define BADF(fmt, ...) \
42 do { fprintf(stderr, "syborg_virtio: error: " fmt , ## __VA_ARGS__);} while (0)
43 #endif
44
45 enum {
46     SYBORG_VIRTIO_ID             = 0,
47     SYBORG_VIRTIO_DEVTYPE        = 1,
48     SYBORG_VIRTIO_HOST_FEATURES  = 2,
49     SYBORG_VIRTIO_GUEST_FEATURES = 3,
50     SYBORG_VIRTIO_QUEUE_BASE     = 4,
51     SYBORG_VIRTIO_QUEUE_NUM      = 5,
52     SYBORG_VIRTIO_QUEUE_SEL      = 6,
53     SYBORG_VIRTIO_QUEUE_NOTIFY   = 7,
54     SYBORG_VIRTIO_STATUS         = 8,
55     SYBORG_VIRTIO_INT_ENABLE     = 9,
56     SYBORG_VIRTIO_INT_STATUS     = 10
57 };
58
59 #define SYBORG_VIRTIO_CONFIG 0x100
60
61 /* Device independent interface.  */
62
63 typedef struct {
64     SysBusDevice busdev;
65     VirtIODevice *vdev;
66     qemu_irq irq;
67     uint32_t int_enable;
68     uint32_t id;
69     NICConf nic;
70     uint32_t host_features;
71     virtio_net_conf net;
72 } SyborgVirtIOProxy;
73
74 static uint32_t syborg_virtio_readl(void *opaque, target_phys_addr_t offset)
75 {
76     SyborgVirtIOProxy *s = opaque;
77     VirtIODevice *vdev = s->vdev;
78     uint32_t ret;
79
80     DPRINTF("readl 0x%x\n", (int)offset);
81     if (offset >= SYBORG_VIRTIO_CONFIG) {
82         return virtio_config_readl(vdev, offset - SYBORG_VIRTIO_CONFIG);
83     }
84     switch(offset >> 2) {
85     case SYBORG_VIRTIO_ID:
86         ret = SYBORG_ID_VIRTIO;
87         break;
88     case SYBORG_VIRTIO_DEVTYPE:
89         ret = s->id;
90         break;
91     case SYBORG_VIRTIO_HOST_FEATURES:
92         ret = s->host_features;
93         break;
94     case SYBORG_VIRTIO_GUEST_FEATURES:
95         ret = vdev->guest_features;
96         break;
97     case SYBORG_VIRTIO_QUEUE_BASE:
98         ret = virtio_queue_get_addr(vdev, vdev->queue_sel);
99         break;
100     case SYBORG_VIRTIO_QUEUE_NUM:
101         ret = virtio_queue_get_num(vdev, vdev->queue_sel);
102         break;
103     case SYBORG_VIRTIO_QUEUE_SEL:
104         ret = vdev->queue_sel;
105         break;
106     case SYBORG_VIRTIO_STATUS:
107         ret = vdev->status;
108         break;
109     case SYBORG_VIRTIO_INT_ENABLE:
110         ret = s->int_enable;
111         break;
112     case SYBORG_VIRTIO_INT_STATUS:
113         ret = vdev->isr;
114         break;
115     default:
116         BADF("Bad read offset 0x%x\n", (int)offset);
117         return 0;
118     }
119     return ret;
120 }
121
122 static void syborg_virtio_writel(void *opaque, target_phys_addr_t offset,
123                                  uint32_t value)
124 {
125     SyborgVirtIOProxy *s = opaque;
126     VirtIODevice *vdev = s->vdev;
127
128     DPRINTF("writel 0x%x = 0x%x\n", (int)offset, value);
129     if (offset >= SYBORG_VIRTIO_CONFIG) {
130         return virtio_config_writel(vdev, offset - SYBORG_VIRTIO_CONFIG,
131                                     value);
132     }
133     switch (offset >> 2) {
134     case SYBORG_VIRTIO_GUEST_FEATURES:
135         if (vdev->set_features)
136             vdev->set_features(vdev, value);
137         vdev->guest_features = value;
138         break;
139     case SYBORG_VIRTIO_QUEUE_BASE:
140         if (value == 0)
141             virtio_reset(vdev);
142         else
143             virtio_queue_set_addr(vdev, vdev->queue_sel, value);
144         break;
145     case SYBORG_VIRTIO_QUEUE_SEL:
146         if (value < VIRTIO_PCI_QUEUE_MAX)
147             vdev->queue_sel = value;
148         break;
149     case SYBORG_VIRTIO_QUEUE_NOTIFY:
150         virtio_queue_notify(vdev, value);
151         break;
152     case SYBORG_VIRTIO_STATUS:
153         virtio_set_status(vdev, value & 0xFF);
154         if (vdev->status == 0)
155             virtio_reset(vdev);
156         break;
157     case SYBORG_VIRTIO_INT_ENABLE:
158         s->int_enable = value;
159         virtio_update_irq(vdev);
160         break;
161     case SYBORG_VIRTIO_INT_STATUS:
162         vdev->isr &= ~value;
163         virtio_update_irq(vdev);
164         break;
165     default:
166         BADF("Bad write offset 0x%x\n", (int)offset);
167         break;
168     }
169 }
170
171 static uint32_t syborg_virtio_readw(void *opaque, target_phys_addr_t offset)
172 {
173     SyborgVirtIOProxy *s = opaque;
174     VirtIODevice *vdev = s->vdev;
175
176     DPRINTF("readw 0x%x\n", (int)offset);
177     if (offset >= SYBORG_VIRTIO_CONFIG) {
178         return virtio_config_readw(vdev, offset - SYBORG_VIRTIO_CONFIG);
179     }
180     BADF("Bad halfword read offset 0x%x\n", (int)offset);
181     return -1;
182 }
183
184 static void syborg_virtio_writew(void *opaque, target_phys_addr_t offset,
185                                  uint32_t value)
186 {
187     SyborgVirtIOProxy *s = opaque;
188     VirtIODevice *vdev = s->vdev;
189
190     DPRINTF("writew 0x%x = 0x%x\n", (int)offset, value);
191     if (offset >= SYBORG_VIRTIO_CONFIG) {
192         return virtio_config_writew(vdev, offset - SYBORG_VIRTIO_CONFIG,
193                                     value);
194     }
195     BADF("Bad halfword write offset 0x%x\n", (int)offset);
196 }
197
198 static uint32_t syborg_virtio_readb(void *opaque, target_phys_addr_t offset)
199 {
200     SyborgVirtIOProxy *s = opaque;
201     VirtIODevice *vdev = s->vdev;
202
203     DPRINTF("readb 0x%x\n", (int)offset);
204     if (offset >= SYBORG_VIRTIO_CONFIG) {
205         return virtio_config_readb(vdev, offset - SYBORG_VIRTIO_CONFIG);
206     }
207     BADF("Bad byte read offset 0x%x\n", (int)offset);
208     return -1;
209 }
210
211 static void syborg_virtio_writeb(void *opaque, target_phys_addr_t offset,
212                                  uint32_t value)
213 {
214     SyborgVirtIOProxy *s = opaque;
215     VirtIODevice *vdev = s->vdev;
216
217     DPRINTF("writeb 0x%x = 0x%x\n", (int)offset, value);
218     if (offset >= SYBORG_VIRTIO_CONFIG) {
219         return virtio_config_writeb(vdev, offset - SYBORG_VIRTIO_CONFIG,
220                                     value);
221     }
222     BADF("Bad byte write offset 0x%x\n", (int)offset);
223 }
224
225 static CPUReadMemoryFunc * const syborg_virtio_readfn[] = {
226      syborg_virtio_readb,
227      syborg_virtio_readw,
228      syborg_virtio_readl
229 };
230
231 static CPUWriteMemoryFunc * const syborg_virtio_writefn[] = {
232      syborg_virtio_writeb,
233      syborg_virtio_writew,
234      syborg_virtio_writel
235 };
236
237 static void syborg_virtio_update_irq(void *opaque, uint16_t vector)
238 {
239     SyborgVirtIOProxy *proxy = opaque;
240     int level;
241
242     level = proxy->int_enable & proxy->vdev->isr;
243     DPRINTF("IRQ %d\n", level);
244     qemu_set_irq(proxy->irq, level != 0);
245 }
246
247 static unsigned syborg_virtio_get_features(void *opaque)
248 {
249     SyborgVirtIOProxy *proxy = opaque;
250     return proxy->host_features;
251 }
252
253 static VirtIOBindings syborg_virtio_bindings = {
254     .notify = syborg_virtio_update_irq,
255     .get_features = syborg_virtio_get_features,
256 };
257
258 static int syborg_virtio_init(SyborgVirtIOProxy *proxy, VirtIODevice *vdev)
259 {
260     int iomemtype;
261
262     proxy->vdev = vdev;
263
264     /* Don't support multiple vectors */
265     proxy->vdev->nvectors = 0;
266     sysbus_init_irq(&proxy->busdev, &proxy->irq);
267     iomemtype = cpu_register_io_memory(syborg_virtio_readfn,
268                                        syborg_virtio_writefn, proxy,
269                                        DEVICE_NATIVE_ENDIAN);
270     sysbus_init_mmio(&proxy->busdev, 0x1000, iomemtype);
271
272     proxy->id = ((uint32_t)0x1af4 << 16) | vdev->device_id;
273
274     qemu_register_reset(virtio_reset, vdev);
275
276     virtio_bind_device(vdev, &syborg_virtio_bindings, proxy);
277     proxy->host_features |= (0x1 << VIRTIO_F_NOTIFY_ON_EMPTY);
278     proxy->host_features = vdev->get_features(vdev, proxy->host_features);
279     return 0;
280 }
281
282 /* Device specific bindings.  */
283
284 static int syborg_virtio_net_init(SysBusDevice *dev)
285 {
286     VirtIODevice *vdev;
287     SyborgVirtIOProxy *proxy = FROM_SYSBUS(SyborgVirtIOProxy, dev);
288
289     vdev = virtio_net_init(&dev->qdev, &proxy->nic, &proxy->net);
290     return syborg_virtio_init(proxy, vdev);
291 }
292
293 static SysBusDeviceInfo syborg_virtio_net_info = {
294     .init = syborg_virtio_net_init,
295     .qdev.name  = "syborg,virtio-net",
296     .qdev.size  = sizeof(SyborgVirtIOProxy),
297     .qdev.props = (Property[]) {
298         DEFINE_NIC_PROPERTIES(SyborgVirtIOProxy, nic),
299         DEFINE_VIRTIO_NET_FEATURES(SyborgVirtIOProxy, host_features),
300         DEFINE_PROP_UINT32("x-txtimer", SyborgVirtIOProxy,
301                            net.txtimer, TX_TIMER_INTERVAL),
302         DEFINE_PROP_INT32("x-txburst", SyborgVirtIOProxy,
303                           net.txburst, TX_BURST),
304         DEFINE_PROP_STRING("tx", SyborgVirtIOProxy, net.tx),
305         DEFINE_PROP_END_OF_LIST(),
306     }
307 };
308
309 static void syborg_virtio_register_devices(void)
310 {
311     sysbus_register_withprop(&syborg_virtio_net_info);
312 }
313
314 device_init(syborg_virtio_register_devices)