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