1/*
2 * Copyright (c) 2014 ETH Zurich.
3 * All rights reserved.
4 *
5 * This file is distributed under the terms in the attached LICENSE file.
6 * If you do not find this file, copies can be found by writing to:
7 * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
8 */
9
10#include <string.h>
11
12#include <barrelfish/barrelfish.h>
13
14#include <virtio/virtio.h>
15#include <virtio/virtqueue.h>
16#include <virtio/virtio_device.h>
17
18#ifndef __VIRTIO_HOST__
19#include <virtio/virtio_guest.h>
20#endif
21
22#include "device.h"
23#include "backends/virtio_mmio.h"
24#include "backends/virtio_pci.h"
25
26#include "debug.h"
27
28/**
29 * \brief initializes a new VirtIO device based on the values passed with the
30 *        device init struct. The device registers have already to be mapped. *
31 *
32 * \param dev       device structure to initialize
33 * \param init      additional information passed for the init process
34 * \param dev_regs  memory location of the device registers
35 */
36errval_t virtio_device_open(struct virtio_device **dev,
37                            struct virtio_device_setup *setup)
38{
39    errval_t err = SYS_ERR_OK;
40
41    VIRTIO_DEBUG_DEV("virtio_device_open: [%s]\n",
42                     setup->dev_name);
43
44    switch (setup->backend.type) {
45        case VIRTIO_DEVICE_BACKEND_PCI:
46            /*
47             * TODO: intialize the PCI device backend
48             */
49            assert(!"NYI: handling of the PCI backend\n");
50            break;
51        case VIRTIO_DEVICE_BACKEND_MMIO:
52            if (setup->backend.args.mmio.dev_base == NULL
53                            || setup->backend.args.mmio.dev_size == 0) {
54                    return VIRTIO_ERR_DEVICE_REGISTER;
55                }
56            err = virtio_device_mmio_init(dev, setup);
57            break;
58        case VIRTIO_DEVICE_BACKEND_IO:
59            /*
60             * TODO: intialize the IO device backend
61             */
62            assert(!"NYI: handling of the IO backend\n");
63            break;
64        default:
65            err = VIRTIO_ERR_BACKEND;
66            VIRTIO_DEBUG_DEV("ERROR: unsupported backend: %u\n",
67                             setup->backend.type);
68            break;
69    }
70
71    if (err_is_fail(err)) {
72        return err;
73    }
74
75    struct virtio_device *vdev = *dev;
76
77    assert(vdev);
78
79    vdev->state = VIRTIO_DEVICE_S_INITIALIZING;
80    strncpy(vdev->dev_name, setup->dev_name, sizeof(vdev->dev_name));
81    vdev->setup_fn = setup->setup_fn;
82    vdev->dev_t_st = setup->dev_t_st;
83    vdev->dev_cap = setup->dev_cap;
84
85    /* 1. Reset the device. */
86    err = virtio_device_reset(vdev);
87    if (err_is_fail(err)) {
88        goto failed;
89    }
90
91    /* 2. Set the ACKNOWLEDGE status bit: the guest OS has notice the device.*/
92    err = virtio_device_set_status(vdev, VIRTIO_DEVICE_STATUS_ACKNOWLEDGE);
93    if (err_is_fail(err)) {
94        goto failed;
95    }
96
97    /* 3. Set the DRIVER status bit: the guest OS knows how to drive the device.*/
98    err = virtio_device_set_status(vdev, VIRTIO_DEVICE_STATUS_DRIVER);
99    if (err_is_fail(err)) {
100        goto failed;
101    }
102
103    /* 4. Read device feature bits, and write the subset of feature bits understood by the OS and driver to the
104     device. During this step the driver MAY read (but MUST NOT write) the device-specific configuration
105     fields to check that it can support the device before accepting it.*/
106    err = virtio_device_feature_negotiate(vdev, setup->features);
107    if (err_is_fail(err)) {
108        goto failed;
109    }
110
111    /* 5. Set the FEATURES_OK status bit. The driver MUST not accept new feature bits after this step.*/
112    err = virtio_device_set_status(vdev, VIRTIO_DEVICE_STATUS_FEATURES_OK);
113    if (err_is_fail(err)) {
114        goto failed;
115    }
116
117    /* 6. Re-read device status to ensure the FEATURES_OK bit is still set: otherwise, the device does not
118     support our subset of features and the device is unusable.*/
119    uint32_t status = 0;
120    err = virtio_device_get_status(vdev, &status);
121    assert(err_is_ok(err));
122
123    if (!virtio_mmio_status_features_ok_extract(status)) {
124        goto failed;
125    }
126
127    /* 7. Perform device-specific setup, including discovery of virtqueues for the device, optional per-bus setup,
128     reading and possibly writing the device���s virtio configuration space, and population of virtqueues.*/
129    err = virtio_device_specific_setup(vdev, setup->setup_arg);
130    if (err_is_fail(err)) {
131        goto failed;
132    }
133    /* 8. Set the DRIVER_OK status bit. At this point the device is ���live���. */
134    VIRTIO_DEBUG_DEV("Device [%s] is now live.\n", vdev->dev_name);
135    err = virtio_device_set_status(vdev, VIRTIO_DEVICE_STATUS_DRIVER_OK);
136    assert(err_is_ok(err));
137
138    return SYS_ERR_OK;
139
140    failed:
141    VIRTIO_DEBUG_DEV("Device initialization for [%s] failed..\n", vdev->dev_name);
142    virtio_device_set_status(vdev, VIRTIO_DEVICE_STATUS_FAILED);
143
144    return err;
145}
146
147/**
148 * \brief initializes a new VirtIO device based on the values passed with the
149 *        device init struct. The supplied cap contains the memory range of the
150 *        device registers.
151 *
152 * \param dev       device structure to initialize
153 * \param init      additional information passed for the init process
154 * \param dev_cap   capability representing the device registers
155 */
156errval_t virtio_device_open_with_cap(struct virtio_device **dev,
157                                     struct virtio_device_setup *setup,
158                                     struct capref dev_cap)
159{
160    errval_t err;
161
162    assert(!capref_is_null(dev_cap));
163
164    struct frame_identity id;
165    err = frame_identify(dev_cap, &id);
166    if (err_is_fail(err)) {
167        VIRTIO_DEBUG_DEV("ERROR: could not identify the device frame.\n");
168        return err;
169    }
170    size_t dev_size = id.bytes;
171    void *dev_base;
172    err = vspace_map_one_frame_attr(&dev_base, dev_size, dev_cap,
173                                    VIRTIO_VREGION_FLAGS_DEVICE,
174                                    NULL, NULL);
175    if (err_is_fail(err)) {
176        VIRTIO_DEBUG_DEV("ERROR: mapping the device register frame failed.\n");
177        return err;
178    }
179
180    switch (setup->backend.type) {
181        case VIRTIO_DEVICE_BACKEND_IO:
182        case VIRTIO_DEVICE_BACKEND_PCI:
183            return LIB_ERR_NOT_IMPLEMENTED;
184            break;
185        case VIRTIO_DEVICE_BACKEND_MMIO:
186            setup->backend.args.mmio.dev_size = dev_size;
187            setup->backend.args.mmio.dev_base = dev_base;
188            break;
189        default:
190            break;
191    }
192
193    VIRTIO_DEBUG_DEV("mapped device registers: [0x%016lx] -> [0x%016lx]\n",
194                     id.base,
195                     (uintptr_t )dev_base);
196
197    setup->dev_cap = dev_cap;
198
199    err = virtio_device_open(dev, setup);
200    if (err_is_fail(err)) {
201        vspace_unmap(dev_base);
202    }
203
204    return err;
205}
206
207/**
208 * \brief checks if the device supports a certain feature
209 *
210 * \param dev       the device to query for the feature
211 * \param feature   the featurebit to check
212 *
213 * \returns true  if the device supports that feature
214 *          false if the device does not support that feature
215 */
216bool virtio_device_has_feature(struct virtio_device *dev,
217                               uint8_t feature)
218{
219    /*
220     * if the device is not configured yet, we don't know the features
221     */
222    if (dev->dev_status & VIRTIO_DEVICE_STATUS_FEATURES_OK) {
223        return false;
224    }
225
226    return (dev->features & (1UL << feature)) != 0;
227}
228
229errval_t virtio_device_specific_setup(struct virtio_device *dev,
230                                      void *arg)
231{
232    if (dev->setup_fn) {
233        return dev->setup_fn(dev, arg);
234    }
235
236    return SYS_ERR_OK;
237}
238
239/**
240 * \brief resets the virtio device
241 *
242 * \param dev   the device to reset
243 *
244 * \returns SYS_ERR_OK on success
245 */
246errval_t virtio_device_reset(struct virtio_device *dev)
247{
248    if (dev->f->reset) {
249        return dev->f->reset(dev);
250    }
251    return SYS_ERR_OK;
252}
253
254/**
255 * \brief returns the status of a virtio device
256 *
257 * \param the device to query for status
258 * \param returned status
259 *
260 * \returns SYS_ERR_OK on success
261 */
262errval_t virtio_device_get_status(struct virtio_device *dev,
263                                  uint32_t *ret_status)
264{
265    if (dev->f->get_status) {
266        return dev->f->get_status(dev, ret_status);
267    }
268    return VIRTIO_ERR_BACKEND;
269}
270
271/**
272 * \brief
273 *
274 * \param
275 */
276errval_t virtio_device_set_status(struct virtio_device *dev,
277                                  uint8_t status)
278{
279    if (dev->f->set_status) {
280        return dev->f->set_status(dev, status);
281    }
282    return VIRTIO_ERR_BACKEND;
283}
284
285/**
286 * \brief Returns the pointer to the device specific structure
287 *
288 * \param vdev to get the device specific pointer
289 *
290 * \returns device specific struct pointer
291 */
292void *virtio_device_get_type_state(struct virtio_device *vdev)
293{
294    return vdev->dev_t_st;
295}
296
297/**
298 * \brief notifies the host about new descriptors available in the
299 *        available ring
300 *
301 * \param vdev      VirtIO device
302 * \param virtq_id  the virtq to signal on
303 *
304 * \return SYS_ERR_OK on success
305 */
306errval_t virtio_device_notify_host(struct virtio_device *vdev,
307                                   uint16_t virtq_id)
308{
309    if (vdev->f->notify) {
310        vdev->f->notify(vdev, virtq_id);
311    }
312    return VIRTIO_ERR_BACKEND;
313}
314
315/**
316 * \brief reads the device configuration space and copies it into a local buffer
317 *
318 * \param vdev  virtio device
319 * \param buf   pointer to the buffer to store the data
320 * \param len   the length of the buffer
321 *
322 * \returns SYS_ERR_OK on success
323 */
324errval_t virtio_device_config_read(struct virtio_device *vdev,
325                                   void *buf,
326                                   size_t len)
327{
328    if (vdev->f->get_config) {
329        return vdev->f->get_config(vdev, buf, len);
330    }
331
332    return VIRTIO_ERR_BACKEND;
333}
334
335/**
336 * \brief writes to the configuration space of a device
337 *
338 * \param vdev  virtio device
339 * \param buf   pointer to the buffer with data to update
340 * \param len   the length of the buffer
341 *
342 * \returns SYS_ERR_OK on success
343 */
344errval_t virtio_device_config_write(struct virtio_device *dev,
345                                    void *config,
346                                    size_t offset,
347                                    size_t length)
348{
349    if (dev->f->set_config) {
350        return dev->f->set_config(dev, config, offset, length);
351    }
352
353    return VIRTIO_ERR_BACKEND;
354}
355
356errval_t virtio_device_set_driver_features(struct virtio_device *dev,
357                                           uint64_t features)
358{
359    assert(!"NYI:");
360    return SYS_ERR_OK;
361}
362
363errval_t virtio_device_get_device_features(struct virtio_device *dev,
364                                           uint64_t *ret_features)
365{
366    assert(!"NYI:");
367    return SYS_ERR_OK;
368}
369
370errval_t virtio_device_feature_negotiate(struct virtio_device *dev,
371                                         uint64_t driver_features)
372{
373    if (dev->f->negotiate_features) {
374        return dev->f->negotiate_features(dev, driver_features);
375    }
376
377    return VIRTIO_ERR_BACKEND;
378}
379
380
381#ifdef __VIRTIO_HOST__
382/**
383 * \brief returns a pointer to a virtqueue of the device
384 *
385 * \param vdev   VirtIO device
386 * \param vq_idx the queue index of the queue we want
387 *
388 * \returns pointer to the requested virtqueue
389 *          NULL if no such virtqueue exists
390 */
391struct virtqueue_host *virtio_device_get_host_virtq(struct virtio_device *vdev,
392                                                    uint16_t vq_idx)
393{
394    if (vq_idx < vdev->vq_num) {
395        return vdev->vqh[vq_idx];
396    }
397
398    return NULL;
399}
400
401#else
402
403errval_t virtio_device_set_virtq(struct virtio_device *dev,
404                                 struct virtqueue *vq)
405{
406    if (dev->f->set_virtq) {
407        return dev->f->set_virtq(dev, vq);
408    }
409
410    return VIRTIO_ERR_BACKEND;
411}
412
413/**
414 * \brief allocates the virtqueues for this device based on the setup information
415 *
416 * \param vdev      virtio device to allocate the queues for
417 * \param vq_setup  setup information for the virtqueues
418 * \param vq_num    number of virtqueues to allocate
419 *
420 * \returns SYS_ERR_OK on success
421 */
422errval_t virtio_device_virtqueue_alloc(struct virtio_device *vdev,
423                                       struct virtqueue_setup *vq_setup,
424                                       uint16_t vq_num)
425{
426    errval_t err;
427
428    VIRTIO_DEBUG_VQ("Allocating %u virtqueues for [%s].\n", vq_num, vdev->dev_name);
429
430    vdev->vq = calloc(vq_num, sizeof(void *));
431    if (vdev->vq == NULL) {
432        return LIB_ERR_MALLOC_FAIL;
433    }
434
435    struct virtqueue_setup *setup;
436    for (uint16_t i = 0; i < vq_num; ++i) {
437        setup = &vq_setup[i];
438        setup->queue_id = i;
439        setup->device = vdev;
440        err = virtio_virtqueue_alloc(setup, &vdev->vq[i]);
441        if (err_is_fail(err)) {
442            for (uint16_t j = 0; j < i; ++j) {
443                virtio_virtqueue_free(vdev->vq[i]);
444            }
445            free(vdev->vq);
446            return err;
447        }
448    }
449
450    for (uint16_t i = 0; i < vq_num; ++i) {
451        setup = &vq_setup[i];
452        struct virtqueue *vq = vdev->vq[i];
453        if (setup->auto_add) {
454            err = virtio_guest_add_virtq(vq);
455            if (err_is_fail(err)) {
456                USER_PANIC_ERR(err, "could not add the viring\n");
457            }
458            err = virtio_device_set_virtq(vdev, vq);
459            if (err_is_fail(err)) {
460                USER_PANIC_ERR(err, "adding the virtqueue to the deivce\n");
461            }
462            // TODO propper error handling
463        }
464    }
465
466    vdev->vq_num = vq_num;
467
468    return SYS_ERR_OK;
469}
470
471/**
472 * \brief returns a pointer to a virtqueue of the device
473 *
474 * \param vdev   VirtIO device
475 * \param vq_idx the queue index of the queue we want
476 *
477 * \returns pointer to the requested virtqueue
478 *          NULL if no such virtqueue exists
479 */
480struct virtqueue *virtio_device_get_virtq(struct virtio_device *vdev,
481                                          uint16_t vq_idx)
482{
483    if (vq_idx < vdev->vq_num) {
484        return vdev->vq[vq_idx];
485    }
486
487    return NULL;
488}
489#endif
490