blob: 249f8039c716cbc3a76f8a25dc4adc93841e9bef [file] [log] [blame]
/******************************************************************************
* 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;
}