renamed initialization functions of virtio devices to open instead of init.
[barrelfish] / lib / virtio / device.c
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 <barrelfish/barrelfish.h>
11
12 #include <virtio/virtio.h>
13 #include <virtio/virtio_device.h>
14
15 #include "device.h"
16 #include "backends/virtio_mmio.h"
17 #include "backends/virtio_pci.h"
18
19 #include "debug.h"
20
21 /**
22  * \brief initializes a new VirtIO device based on the values passed with the
23  *        device init struct. The device registers have already to be mapped. *
24  *
25  * \param dev       device structure to initialize
26  * \param init      additional information passed for the init process
27  * \param dev_regs  memory location of the device registers
28  */
29 errval_t virtio_device_open(struct virtio_device **dev,
30                             struct virtio_device_setup *init)
31 {
32     errval_t err = SYS_ERR_OK;
33
34     if (init->dev_reg == NULL || init->dev_reg_size == 0) {
35         /*
36          * XXX: does this also hold for the PCI
37          */
38         return VIRTIO_ERR_DEVICE_REGISTER;
39     }
40
41     switch (init->backend) {
42         case VIRTIO_DEVICE_BACKEND_PCI:
43             /*
44              * TODO: intialize the PCI device backend
45              */
46             assert(!"NYI: handling of the PCI backend\n");
47             break;
48         case VIRTIO_DEVICE_BACKEND_MMIO:
49             err = virtio_device_mmio_init(dev, init);
50             break;
51         case VIRTIO_DEVICE_BACKEND_IO:
52             /*
53              * TODO: intialize the IO device backend
54              */
55             assert(!"NYI: handling of the IO backend\n");
56             break;
57         default:
58             err = VIRTIO_ERR_BACKEND;
59             break;
60     }
61
62     if (err_is_fail(err)) {
63         return err;
64     }
65
66     struct virtio_device *vdev = *dev;
67
68     /* 1. Reset the device. */
69     err = virtio_device_reset(vdev);
70     if (err_is_fail(err)) {
71         goto failed;
72     }
73
74     /* 2. Set the ACKNOWLEDGE status bit: the guest OS has notice the device.*/
75     err = virtio_device_set_status(vdev, VIRTIO_DEVICE_STATUS_ACKNOWLEDGE);
76     if (err_is_fail(err)) {
77         goto failed;
78     }
79
80     /* 3. Set the DRIVER status bit: the guest OS knows how to drive the device.*/
81     err = virtio_device_set_status(vdev, VIRTIO_DEVICE_STATUS_DRIVER);
82     if (err_is_fail(err)) {
83         goto failed;
84     }
85
86     /* 4. Read device feature bits, and write the subset of feature bits understood by the OS and driver to the
87      device. During this step the driver MAY read (but MUST NOT write) the device-specific configuration
88      fields to check that it can support the device before accepting it.*/
89     err = virtio_device_feature_negotiate(vdev);
90     if (err_is_fail(err)) {
91         goto failed;
92     }
93
94     /* 5. Set the FEATURES_OK status bit. The driver MUST not accept new feature bits after this step.*/
95     err = virtio_device_set_status(vdev, VIRTIO_DEVICE_STATUS_FEATURES_OK);
96     if (err_is_fail(err)) {
97         goto failed;
98     }
99
100     /* 6. Re-read device status to ensure the FEATURES_OK bit is still set: otherwise, the device does not
101      support our subset of features and the device is unusable.*/
102     uint8_t status;
103     err = virtio_device_get_status(vdev, &status);
104     assert(err_is_ok(err));
105
106     if (!virtio_mmio_status_features_ok_extract(status)) {
107         goto failed;
108     }
109
110     /* 7. Perform device-specific setup, including discovery of virtqueues for the device, optional per-bus setup,
111      reading and possibly writing the device’s virtio configuration space, and population of virtqueues.*/
112     err = virtio_device_specific_setup(vdev);
113     if (err_is_fail(err)) {
114         goto failed;
115     }
116     /* 8. Set the DRIVER_OK status bit. At this point the device is “live”. */
117     err = virtio_device_set_status(vdev, VIRTIO_DEVICE_STATUS_DRIVER_OK);
118     assert(err_is_ok(err));
119
120     if (init->device_setup) {
121         return init->device_setup(vdev);
122     }
123
124     return SYS_ERR_OK;
125
126     failed: virtio_device_set_status(vdev, VIRTIO_DEVICE_STATUS_FAILED);
127
128     return err;
129 }
130
131 /**
132  * \brief initializes a new VirtIO device based on the values passed with the
133  *        device init struct. The supplied cap contains the memory range of the
134  *        device registers.
135  *
136  * \param dev       device structure to initialize
137  * \param init      additional information passed for the init process
138  * \param dev_cap   capability representing the device registers
139  */
140 errval_t virtio_device_open_with_cap(struct virtio_device **dev,
141                                      struct virtio_device_setup *init,
142                                      struct capref dev_cap)
143 {
144     errval_t err;
145
146     assert(!capref_is_null(dev_cap));
147
148     struct frame_identity id;
149     err = invoke_frame_identify(dev_cap, &id);
150     if (err_is_fail(err)) {
151         VIRTIO_DEBUG_DEV("ERROR: could not identify the device frame.\n");
152         return err;
153     }
154
155     init->dev_reg_size = (1UL << id.bits);
156
157     err = vspace_map_one_frame_attr(&init->dev_reg, init->dev_reg_size, dev_cap,
158     VIRTIO_VREGION_FLAGS_DEVICE,
159                                     NULL, NULL);
160     if (err_is_fail(err)) {
161         VIRTIO_DEBUG_DEV("ERROR: mapping the device register frame failed.\n");
162         init->dev_reg = NULL;
163         init->dev_reg_size = 0;
164         return err;
165     }
166
167     VIRTIO_DEBUG_DEV("mapped device registers: [0x%016lx] -> [0x%016lx]\n",
168                      id.base,
169                      (uintptr_t )init->dev_reg);
170
171     err = virtio_device_init(dev, init);
172     if (err_is_fail(err)) {
173         vspace_unmap(init->dev_reg);
174     }
175
176     return err;
177 }
178