static int install_virtiocon(struct unit *unit) { struct virtiocon *vcon; int rc; int size; int i; // Setup unit information if (!unit) return -ENOSYS; unit->vendorname = "VIRTIO"; unit->productname = "VIRTIO Virtual Console Device"; // Allocate memory for device vcon = kmalloc(sizeof(struct virtiocon)); if (vcon == NULL) return -ENOMEM; memset(vcon, 0, sizeof(struct virtiocon)); // Initialize virtual device rc = virtio_device_init(&vcon->vd, unit, VIRTIO_CON_F_SIZE); if (rc < 0) return rc; // Get console device configuration virtio_get_config(&vcon->vd, &vcon->config, sizeof(vcon->config)); // Initialize queues for console rc = virtio_queue_init(&vcon->input_queue, &vcon->vd, 0, virtiocon_input_callback); if (rc < 0) return rc; rc = virtio_queue_init(&vcon->output_queue, &vcon->vd, 1, virtiocon_output_callback); if (rc < 0) return rc; // Fill input queue size = virtio_queue_size(&vcon->input_queue); for (i = 0; i < size; ++i) { struct scatterlist sg[1]; char *data = kmalloc(PAGESIZE); if (!data) return -ENOMEM; sg[0].data = data; sg[0].size = PAGESIZE; virtio_enqueue(&vcon->input_queue, sg, 0, 1, data); } virtio_kick(&vcon->input_queue); // Create device vcon->devno = dev_make("vc#", &virtiocon_driver, unit, vcon); virtio_setup_complete(&vcon->vd, 1); kprintf(KERN_INFO "%s: virtio console, %dx%d, %d ports, feats=%d\n", device(vcon->devno)->name, vcon->config.cols, vcon->config.rows, vcon->config.max_ports, vcon->vd.features); return 0; }
static int install_virtioblk(struct unit *unit) { struct virtioblk *vblk; int rc; // Setup unit information if (!unit) return -ENOSYS; unit->vendorname = "VIRTIO"; unit->productname = "VIRTIO Virtual Block Device"; // Allocate memory for device vblk = kmalloc(sizeof(struct virtioblk)); if (vblk == NULL) return -ENOMEM; memset(vblk, 0, sizeof(struct virtioblk)); // Initialize virtual device rc = virtio_device_init(&vblk->vd, unit, VIRTIO_BLK_F_SEG_MAX | VIRTIO_BLK_F_SIZE_MAX | VIRTIO_BLK_F_GEOMETRY | VIRTIO_BLK_F_RO | VIRTIO_BLK_F_BLK_SIZE | VIRTIO_BLK_F_FLUSH); if (rc < 0) return rc; // Get block device configuration virtio_get_config(&vblk->vd, &vblk->config, sizeof(vblk->config)); if ((vblk->config.capacity & ~0x7FFFFFFF)) { vblk->capacity = 0x7FFFFFFF; } else { vblk->capacity = (int) vblk->config.capacity; } // Initialize queue for disk requests rc = virtio_queue_init(&vblk->vq, &vblk->vd, 0, virtioblk_callback); if (rc < 0) return rc; // Create device vblk->devno = dev_make("vd#", &virtioblk_driver, unit, vblk); virtio_setup_complete(&vblk->vd, 1); kprintf(KERN_INFO "%s: virtio disk, %dMB\n", device(vblk->devno)->name, vblk->capacity / (1024 * 1024 / SECTORSIZE)); return 0; }
/** * virtio_9p_load * * Read a file from the 9P Server on the VIRTIO interface. * * @param file_name[in] File to read, use Linux style paths. * @param buffer[out] Where to read the file to. * @return +ve = amount of data read, -ve = error. */ int virtio_9p_load(struct virtio_device *dev, const char *file_name, uint8_t *buffer) { int rc; uint16_t tag_len; char tag_name[TAG_SIZE]; uint64_t offset = 0; uint8_t *pos = (uint8_t *)file_name; int start_fid = ROOT_FID; p9_connection_t connection = { .message_size = __buf_size, .fid = ROOT_FID, .uname = "slof" }; p9_file_t file = { .connection = &connection, .fid = FILE_FID, }; /* Get the share name from 9P config space. */ tag_len = virtio_get_config(dev, 0, sizeof(tag_len)); if (tag_len >= TAG_SIZE) tag_len = TAG_SIZE - 1; __virtio_read_config(dev, tag_name, 2, tag_len); connection.aname = tag_name; /* Connect to the 9P server. */ dprintf("%s : connecting, tag = %s, user = %s, msgsize = %d\n", __func__, connection.aname, connection.uname, connection.message_size); rc = p9_version(&connection); if (rc != 0) { printf("Version check failed, rc = %d\n", rc); goto cleanup_connection; } rc = p9_attach(&connection); if (rc != 0) { printf("Attach failed, rc = %d\n", rc); goto cleanup_connection; } dprintf("%s : connected, msgsize = %d\n", __func__, connection.message_size); /* Walk to the file. */ do { dprintf("%s : walk path %s\n", __func__, pos); rc = p9_walk(&connection, start_fid, FILE_FID, &pos); if (rc < 0) { /* Some error. */ printf("Walk failed, rc = %d\n", rc); goto cleanup_connection; } /* * If partial walk (*pos != 0) then continue the walk from * mid point with start_fid updated to current position * FILE_FID. FILE_FID will then be reused for the result of * the next call to walk. */ start_fid = FILE_FID; } while (*pos != 0); /* Open the file. */ dprintf("%s : stat and open\n", __func__); rc = p9_stat(&file); if (rc != 0) { printf("Stat failed, rc = %d\n", rc); goto cleanup_file; } rc = p9_open(&file, 0x00); /* TODO find include for "read mode" */ if (rc != 0) { printf("Open failed, rc = %d\n", rc); goto cleanup_file; } dprintf("%s : file opened, size %lld\n", __func__, file.length); /* Read the file contents to buffer. */ while (offset < file.length) { dprintf("%s : read from offset %llu\n", __func__, offset); rc = p9_read(&file, buffer + offset, file.length - offset, offset); dprintf("%s : read done, length was %d\n", __func__, rc); if (rc < 0) { printf("Read failed, rc = %d\n", rc); goto cleanup_file; } if (rc == 0) { break; } offset += rc; rc = 0; } /* Cleanup and disconnect. */ cleanup_file: dprintf("%s : clunking file\n", __func__); p9_clunk(&connection, file.fid); cleanup_connection: dprintf("%s : clunking connection\n", __func__); p9_clunk(&connection, connection.fid); dprintf("%s : complete, read %llu bytes\n", __func__, offset); return rc == 0 ? offset : rc; }
/** * Read blocks * @param reg pointer to "reg" property * @param buf pointer to destination buffer * @param blocknum block number of the first block that should be read * @param cnt amount of blocks that should be read * @return number of blocks that have been read successfully */ int virtioblk_read(struct virtio_device *dev, char *buf, long blocknum, long cnt) { struct vring_desc *desc; int id; static struct virtio_blk_req blkhdr; //struct virtio_blk_config *blkconf; uint64_t capacity; uint32_t vq_size, time; struct vring_desc *vq_desc; /* Descriptor vring */ struct vring_avail *vq_avail; /* "Available" vring */ struct vring_used *vq_used; /* "Used" vring */ volatile uint8_t status = -1; volatile uint16_t *current_used_idx; uint16_t last_used_idx; //printf("virtioblk_read: dev=%p buf=%p blocknum=%li count=%li\n", // dev, buf, blocknum, cnt); /* Check whether request is within disk capacity */ capacity = virtio_get_config(dev, 0, sizeof(capacity)); if (blocknum + cnt - 1 > capacity) { puts("virtioblk_read: Access beyond end of device!"); return 0; } vq_size = virtio_get_qsize(dev, 0); vq_desc = virtio_get_vring_desc(dev, 0); vq_avail = virtio_get_vring_avail(dev, 0); vq_used = virtio_get_vring_used(dev, 0); last_used_idx = vq_used->idx; current_used_idx = &vq_used->idx; /* Set up header */ blkhdr.type = VIRTIO_BLK_T_IN | VIRTIO_BLK_T_BARRIER; blkhdr.ioprio = 1; blkhdr.sector = blocknum; /* Determine descriptor index */ id = (vq_avail->idx * 3) % vq_size; /* Set up virtqueue descriptor for header */ desc = &vq_desc[id]; desc->addr = (uint64_t)&blkhdr; desc->len = sizeof(struct virtio_blk_req); desc->flags = VRING_DESC_F_NEXT; desc->next = (id + 1) % vq_size; /* Set up virtqueue descriptor for data */ desc = &vq_desc[(id + 1) % vq_size]; desc->addr = (uint64_t)buf; desc->len = cnt * 512; desc->flags = VRING_DESC_F_NEXT | VRING_DESC_F_WRITE; desc->next = (id + 2) % vq_size; /* Set up virtqueue descriptor for status */ desc = &vq_desc[(id + 2) % vq_size]; desc->addr = (uint64_t)&status; desc->len = 1; desc->flags = VRING_DESC_F_WRITE; desc->next = 0; vq_avail->ring[vq_avail->idx % vq_size] = id; mb(); vq_avail->idx += 1; /* Tell HV that the queue is ready */ virtio_queue_notify(dev, 0); /* Wait for host to consume the descriptor */ time = SLOF_GetTimer() + VIRTIO_TIMEOUT; while (*current_used_idx == last_used_idx) { // do something better mb(); if (time < SLOF_GetTimer()) break; } if (status == 0) return cnt; printf("virtioblk_read failed! status = %i\n", status); return 0; }
VOID RhelGetDiskGeometry( IN PVOID DeviceExtension ) { u64 cap; u32 v; struct virtio_blk_geometry vgeo; PADAPTER_EXTENSION adaptExt = (PADAPTER_EXTENSION)DeviceExtension; adaptExt->features = virtio_get_features(&adaptExt->vdev); if (CHECKBIT(adaptExt->features, VIRTIO_BLK_F_BARRIER)) { RhelDbgPrint(TRACE_LEVEL_INFORMATION, ("VIRTIO_BLK_F_BARRIER\n")); } if (CHECKBIT(adaptExt->features, VIRTIO_BLK_F_RO)) { RhelDbgPrint(TRACE_LEVEL_INFORMATION, ("VIRTIO_BLK_F_RO\n")); } if (CHECKBIT(adaptExt->features, VIRTIO_BLK_F_SIZE_MAX)) { virtio_get_config(&adaptExt->vdev, FIELD_OFFSET(blk_config, size_max), &v, sizeof(v)); adaptExt->info.size_max = v; } else { adaptExt->info.size_max = SECTOR_SIZE; } if (CHECKBIT(adaptExt->features, VIRTIO_BLK_F_SEG_MAX)) { virtio_get_config(&adaptExt->vdev, FIELD_OFFSET(blk_config, seg_max), &v, sizeof(v)); adaptExt->info.seg_max = v; RhelDbgPrint(TRACE_LEVEL_INFORMATION, ("VIRTIO_BLK_F_SEG_MAX = %d\n", adaptExt->info.seg_max)); } if (CHECKBIT(adaptExt->features, VIRTIO_BLK_F_BLK_SIZE)) { virtio_get_config(&adaptExt->vdev, FIELD_OFFSET(blk_config, blk_size), &v, sizeof(v)); adaptExt->info.blk_size = v; } else { adaptExt->info.blk_size = SECTOR_SIZE; } RhelDbgPrint(TRACE_LEVEL_INFORMATION, ("VIRTIO_BLK_F_BLK_SIZE = %d\n", adaptExt->info.blk_size)); if (CHECKBIT(adaptExt->features, VIRTIO_BLK_F_GEOMETRY)) { virtio_get_config(&adaptExt->vdev, FIELD_OFFSET(blk_config, geometry), &vgeo, sizeof(vgeo)); adaptExt->info.geometry.cylinders= vgeo.cylinders; adaptExt->info.geometry.heads = vgeo.heads; adaptExt->info.geometry.sectors = vgeo.sectors; RhelDbgPrint(TRACE_LEVEL_INFORMATION, ("VIRTIO_BLK_F_GEOMETRY. cylinders = %d heads = %d sectors = %d\n", adaptExt->info.geometry.cylinders, adaptExt->info.geometry.heads, adaptExt->info.geometry.sectors)); } virtio_get_config(&adaptExt->vdev, FIELD_OFFSET(blk_config, capacity), &cap, sizeof(cap)); adaptExt->info.capacity = cap; RhelDbgPrint(TRACE_LEVEL_INFORMATION, ("capacity = %08I64X\n", adaptExt->info.capacity)); if(CHECKBIT(adaptExt->features, VIRTIO_BLK_F_TOPOLOGY)) { virtio_get_config(&adaptExt->vdev, FIELD_OFFSET(blk_config, physical_block_exp), &adaptExt->info.physical_block_exp, sizeof(adaptExt->info.physical_block_exp)); RhelDbgPrint(TRACE_LEVEL_INFORMATION, ("physical_block_exp = %d\n", adaptExt->info.physical_block_exp)); virtio_get_config(&adaptExt->vdev, FIELD_OFFSET(blk_config, alignment_offset), &adaptExt->info.alignment_offset, sizeof(adaptExt->info.alignment_offset)); RhelDbgPrint(TRACE_LEVEL_INFORMATION, ("alignment_offset = %d\n", adaptExt->info.alignment_offset)); virtio_get_config(&adaptExt->vdev, FIELD_OFFSET(blk_config, min_io_size), &adaptExt->info.min_io_size, sizeof(adaptExt->info.min_io_size)); RhelDbgPrint(TRACE_LEVEL_INFORMATION, ("min_io_size = %d\n", adaptExt->info.min_io_size)); virtio_get_config(&adaptExt->vdev, FIELD_OFFSET(blk_config, opt_io_size), &adaptExt->info.opt_io_size, sizeof(adaptExt->info.opt_io_size)); RhelDbgPrint(TRACE_LEVEL_INFORMATION, ("opt_io_size = %d\n", adaptExt->info.opt_io_size)); } }