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/virtio_device.h>
16#include <virtio/devices/virtio_block.h>
17
18#include <dev/virtio/virtio_blk_dev.h>
19
20#include "device.h"
21#include "debug.h"
22
23/**
24 * \brief   returns the topology information
25 *
26 * \param   dev  the virtio block device
27 * \param   topo memory region to fill the topology information in
28 *               (only valid if VIRTIO_BLK_F_TOPOLOGY)
29 *
30 * \returns true if VIRTIO_BLK_F_TOPOLOGY
31 *          false otherwise
32 */
33bool virtio_block_get_topology(struct virtio_device_blk *dev,
34                               struct virtio_block_topology *topo)
35{
36    /* can't return any data */
37    if (!topo) {
38        return 0;
39    }
40
41    /* the device does not support topology */
42    if (!virtio_device_has_feature(dev->vdev, VIRTIO_BLOCK_F_TOPOLOGY)) {
43        return 0;
44    }
45
46    topo->alignment_offset = virtio_blk_topo_blocks_offset_aligned_rdf(&dev
47                    ->config_space);
48    topo->min_io_size = virtio_blk_topo_io_size_min_rdf(&dev->config_space);
49    topo->opt_io_size = virtio_blk_topo_io_size_opt_rdf(&dev->config_space);
50    topo->num_logic_per_phys = virtio_blk_topo_blocks_logic_per_phys_rdf(&dev
51                    ->config_space);
52
53    return 1;
54}
55
56/**
57 * \brief   returns the blocksize of
58 *
59 * \param   dev the virtio block device
60 * \param   geo memory region to fill the geometry information in
61 *              (only valid if VIRTIO_BLK_F_GEOMETRY)
62 *
63 * \returns true if VIRTIO_BLK_F_GEOMETRY
64 *          false otherwise
65 */
66bool virtio_block_get_geometry(struct virtio_device_blk *dev,
67                               struct virtio_block_geometry *geo)
68{
69    if (!geo) {
70        return 0;
71    }
72
73    if (!virtio_device_has_feature(dev->vdev, VIRTIO_BLOCK_F_GEOMETRY)) {
74        return 0;
75    }
76
77    geo->cylinders = virtio_blk_geometry_cylinders_rdf(&dev->config_space);
78    geo->heads = virtio_blk_geometry_heads_rdf(&dev->config_space);
79    geo->sectors = virtio_blk_geometry_sectors_rdf(&dev->config_space);
80
81    return 0;
82}
83
84/**
85 * \brief reads the device configuration and copies it into the local memory
86 *
87 * \param dev the block device to read the configuration space.
88 *
89 * \returns SYS_ERR_OK on success
90 */
91errval_t virtio_block_config_read(struct virtio_device_blk *dev)
92{
93    VIRTIO_DEBUG_DT("reading device configuration\n");
94    if (dev->config_addr == NULL) {
95        dev->config_addr = malloc(VIRTIO_BLOCK_CONFIG_SIZE);
96        if (dev->config_addr == NULL) {
97            return LIB_ERR_MALLOC_FAIL;
98        }
99    }
100
101    return virtio_device_config_read(dev->vdev,
102                                     dev->config_addr,
103                                     VIRTIO_BLOCK_CONFIG_SIZE);
104}
105
106#ifdef __VIRTIO_HOST__
107static errval_t virtio_block_init_common(struct virtio_device *vdev,
108                                         void *arg)
109{
110    return SYS_ERR_OK;
111}
112#else
113/**
114 * \brief   handles the VirtIO block device common initialization.
115 *
116 * \param   dev     the VirtIO block device
117 * \param   setup   the setup information
118 *
119 * \returns SYS_ERR_OK on success
120 */
121static errval_t virtio_block_init_common(struct virtio_device *vdev,
122                                         void *arg)
123{
124    errval_t err;
125
126    VIRTIO_DEBUG_DT("Doing device specific setup: Block Device\n");
127
128    struct virtio_device_setup *setup = arg;
129    struct virtio_device_blk *dev = virtio_device_get_type_state(vdev);
130
131    /* read the device configuration */
132    err = virtio_block_config_read(dev);
133    if (err_is_fail(err)) {
134        return err;
135    }
136
137    if (setup->vq_num != VIRTIO_BLOCK_NUM_VIRTQUEUES) {
138        /*
139         * TODO: handle this case.
140         */
141        assert(setup->vq_num == VIRTIO_BLOCK_NUM_VIRTQUEUES);
142    }
143
144    /*
145     * allocate the virtqueues
146     */
147    err = virtio_device_virtqueue_alloc(dev->vdev, setup->vq_setup, setup->vq_num);
148    if (err_is_fail(err)) {
149        return err;
150    }
151
152    dev->vq = virtio_device_get_virtq(dev->vdev, 0);
153    assert(dev->vq);
154
155    dev->vdev->state = VIRTIO_DEVICE_S_READY;
156
157    return SYS_ERR_OK;
158}
159#endif
160
161
162/**
163 * \brief   handles the VirtIO block device specific initialization.
164 *          with the device registers already mapped
165 *
166 * \param   dev     the VirtIO block device
167 * \param   setup   the setup information
168 *
169 * \returns SYS_ERR_OK on success
170 */
171errval_t virtio_block_init_device(struct virtio_device_blk *dev,
172                                  struct virtio_device_setup *setup)
173{
174    if (setup->dev_type != VIRTIO_DEVICE_TYPE_BLOCK) {
175        VIRTIO_DEBUG_DT("ERROR: Device type was not VIRTIO_DEVICE_TYPE_BLOCK\n");
176        return VIRTIO_ERR_DEVICE_TYPE;
177    }
178
179    setup->setup_fn = virtio_block_init_common;
180    setup->setup_arg = setup;
181    setup->dev_t_st = dev;
182
183    /* initialize the VirtIO device */
184    return virtio_device_open(&dev->vdev, setup);
185}
186
187/**
188 * \brief   handles the VirtIO block device specific initialization.
189 *          the device registers are not mapped yet
190 *
191 * \param   dev     the VirtIO block device
192 * \param   setup   the setup information
193 * \param   dev_cap the frame capability backing the device registers
194 *
195 * \returns SYS_ERR_OK on success
196 */
197errval_t virtio_block_init_device_with_cap(struct virtio_device_blk *dev,
198                                           struct virtio_device_setup *setup,
199                                           struct capref dev_cap)
200{
201    if (setup->dev_type != VIRTIO_DEVICE_TYPE_BLOCK) {
202        return VIRTIO_ERR_DEVICE_TYPE;
203    }
204
205    setup->setup_fn = virtio_block_init_common;
206    setup->setup_arg = setup;
207
208    /* initialize the VirtIO device */
209    return virtio_device_open_with_cap(&dev->vdev, setup, dev_cap);
210}
211
212
213#ifdef __VIRTIO_HOST__
214/*
215 * ----------------------------------------------------------------------------
216 * Additional functions for the host
217 * ----------------------------------------------------------------------------
218 */
219
220/**
221 * \brief writes the configuration space values to the device configuration space
222 *
223 * \param bdev  the VirtIO host block device
224 *
225 * \return SYS_ERR_OK on success
226 */
227errval_t virtio_block_host_write_config(struct virtio_host_blk *bdev)
228{
229    if (bdev->config_addr == NULL) {
230        return VIRTIO_ERR_ARG_INVALID;
231    }
232
233    if (bdev->config_tmp == NULL) {
234        bdev->config_tmp = malloc(VIRTIO_BLOCK_CONFIG_SIZE);
235    }
236
237    assert(bdev->config_tmp);
238
239
240
241
242    uint8_t signal = 0x0;
243    if (virtio_blk_capacity_rd(&bdev->config_space)) {
244        signal = 0x1;
245    }
246
247    // clear out the configuration space
248    memset(bdev->config_addr, 0, VIRTIO_BLOCK_CONFIG_SIZE);
249
250
251
252    // assert the config space changed interrupt
253    if (signal) {
254
255    }
256
257    return SYS_ERR_OK;
258}
259
260#endif
261
262