229 lines
5.2 KiB
C
229 lines
5.2 KiB
C
/******************************************************************************
|
|
* Copyright (c) 2011 IBM Corporation
|
|
* All rights reserved.
|
|
* This program and the accompanying materials
|
|
* are made available under the terms of the BSD License
|
|
* which accompanies this distribution, and is available at
|
|
* http://www.opensource.org/licenses/bsd-license.php
|
|
*
|
|
* Contributors:
|
|
* IBM Corporation - initial implementation
|
|
*****************************************************************************/
|
|
|
|
#include <cpu.h>
|
|
#include <cache.h>
|
|
#include <byteorder.h>
|
|
#include "virtio.h"
|
|
|
|
/* PCI virtio header offsets */
|
|
#define VIRTIOHDR_DEVICE_FEATURES 0
|
|
#define VIRTIOHDR_GUEST_FEATURES 4
|
|
#define VIRTIOHDR_QUEUE_ADDRESS 8
|
|
#define VIRTIOHDR_QUEUE_SIZE 12
|
|
#define VIRTIOHDR_QUEUE_SELECT 14
|
|
#define VIRTIOHDR_QUEUE_NOTIFY 16
|
|
#define VIRTIOHDR_DEVICE_STATUS 18
|
|
#define VIRTIOHDR_ISR_STATUS 19
|
|
#define VIRTIOHDR_DEVICE_CONFIG 20
|
|
|
|
|
|
/**
|
|
* Calculate ring size according to queue size number
|
|
*/
|
|
unsigned long virtio_vring_size(unsigned int qsize)
|
|
{
|
|
return VQ_ALIGN(sizeof(struct vring_desc) * qsize + 2 * (2 + qsize))
|
|
+ VQ_ALIGN(sizeof(struct vring_used_elem) * qsize);
|
|
}
|
|
|
|
|
|
/**
|
|
* Get number of elements in a vring
|
|
* @param dev pointer to virtio device information
|
|
* @param queue virtio queue number
|
|
* @return number of elements
|
|
*/
|
|
int virtio_get_qsize(struct virtio_device *dev, int queue)
|
|
{
|
|
int size = 0;
|
|
|
|
if (dev->type == VIRTIO_TYPE_PCI) {
|
|
ci_write_16(dev->base+VIRTIOHDR_QUEUE_SELECT,
|
|
cpu_to_le16(queue));
|
|
eieio();
|
|
size = le16_to_cpu(ci_read_16(dev->base+VIRTIOHDR_QUEUE_SIZE));
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get address of descriptor vring
|
|
* @param dev pointer to virtio device information
|
|
* @param queue virtio queue number
|
|
* @return pointer to the descriptor ring
|
|
*/
|
|
struct vring_desc *virtio_get_vring_desc(struct virtio_device *dev, int queue)
|
|
{
|
|
struct vring_desc *desc = 0;
|
|
|
|
if (dev->type == VIRTIO_TYPE_PCI) {
|
|
ci_write_16(dev->base+VIRTIOHDR_QUEUE_SELECT,
|
|
cpu_to_le16(queue));
|
|
eieio();
|
|
desc = (void*)(4096L *
|
|
le32_to_cpu(ci_read_32(dev->base+VIRTIOHDR_QUEUE_ADDRESS)));
|
|
}
|
|
|
|
return desc;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get address of "available" vring
|
|
* @param dev pointer to virtio device information
|
|
* @param queue virtio queue number
|
|
* @return pointer to the "available" ring
|
|
*/
|
|
struct vring_avail *virtio_get_vring_avail(struct virtio_device *dev, int queue)
|
|
{
|
|
return (void*)((uint64_t)virtio_get_vring_desc(dev, queue)
|
|
+ virtio_get_qsize(dev, queue) * sizeof(struct vring_desc));
|
|
}
|
|
|
|
|
|
/**
|
|
* Get address of "used" vring
|
|
* @param dev pointer to virtio device information
|
|
* @param queue virtio queue number
|
|
* @return pointer to the "used" ring
|
|
*/
|
|
struct vring_used *virtio_get_vring_used(struct virtio_device *dev, int queue)
|
|
{
|
|
return (void*)VQ_ALIGN((uint64_t)virtio_get_vring_avail(dev, queue)
|
|
+ virtio_get_qsize(dev, queue)
|
|
* sizeof(struct vring_avail));
|
|
}
|
|
|
|
|
|
/**
|
|
* Reset virtio device
|
|
*/
|
|
void virtio_reset_device(struct virtio_device *dev)
|
|
{
|
|
if (dev->type == VIRTIO_TYPE_PCI) {
|
|
ci_write_8(dev->base+VIRTIOHDR_DEVICE_STATUS, 0);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Notify hypervisor about queue update
|
|
*/
|
|
void virtio_queue_notify(struct virtio_device *dev, int queue)
|
|
{
|
|
if (dev->type == VIRTIO_TYPE_PCI) {
|
|
ci_write_16(dev->base+VIRTIOHDR_QUEUE_NOTIFY, cpu_to_le16(queue));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set queue address
|
|
*/
|
|
void virtio_set_qaddr(struct virtio_device *dev, int queue, unsigned int qaddr)
|
|
{
|
|
if (dev->type == VIRTIO_TYPE_PCI) {
|
|
uint32_t val = qaddr;
|
|
val = val >> 12;
|
|
ci_write_16(dev->base+VIRTIOHDR_QUEUE_SELECT,
|
|
cpu_to_le16(queue));
|
|
eieio();
|
|
ci_write_32(dev->base+VIRTIOHDR_QUEUE_ADDRESS,
|
|
cpu_to_le32(val));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set device status bits
|
|
*/
|
|
void virtio_set_status(struct virtio_device *dev, int status)
|
|
{
|
|
if (dev->type == VIRTIO_TYPE_PCI) {
|
|
ci_write_8(dev->base+VIRTIOHDR_DEVICE_STATUS, status);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Set guest feature bits
|
|
*/
|
|
void virtio_set_guest_features(struct virtio_device *dev, int features)
|
|
|
|
{
|
|
if (dev->type == VIRTIO_TYPE_PCI) {
|
|
ci_write_32(dev->base+VIRTIOHDR_GUEST_FEATURES, features);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Get additional config values
|
|
*/
|
|
uint64_t virtio_get_config(struct virtio_device *dev, int offset, int size)
|
|
{
|
|
uint64_t val = ~0ULL;
|
|
void *confbase;
|
|
|
|
switch (dev->type) {
|
|
case VIRTIO_TYPE_PCI:
|
|
confbase = dev->base+VIRTIOHDR_DEVICE_CONFIG;
|
|
break;
|
|
default:
|
|
return ~0ULL;
|
|
}
|
|
switch (size) {
|
|
case 1:
|
|
val = ci_read_8(confbase+offset);
|
|
break;
|
|
case 2:
|
|
val = ci_read_16(confbase+offset);
|
|
break;
|
|
case 4:
|
|
val = ci_read_32(confbase+offset);
|
|
break;
|
|
case 8:
|
|
/* We don't support 8 bytes PIO accesses
|
|
* in qemu and this is all PIO
|
|
*/
|
|
val = ci_read_32(confbase+offset);
|
|
val <<= 32;
|
|
val |= ci_read_32(confbase+offset+4);
|
|
break;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
/**
|
|
* Get config blob
|
|
*/
|
|
int __virtio_read_config(struct virtio_device *dev, void *dst,
|
|
int offset, int len)
|
|
{
|
|
void *confbase;
|
|
unsigned char *buf = dst;
|
|
int i;
|
|
|
|
switch (dev->type) {
|
|
case VIRTIO_TYPE_PCI:
|
|
confbase = dev->base+VIRTIOHDR_DEVICE_CONFIG;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
for (i = 0; i < len; i++)
|
|
buf[i] = ci_read_8(confbase + offset + i);
|
|
return len;
|
|
}
|