#ifndef _VIRTIO_RING_H #define _VIRTIO_RING_H #include "types.h" // u64 #include "memmap.h" // PAGE_SIZE #define PAGE_SHIFT 12 #define PAGE_MASK (PAGE_SIZE-1) #define virt_to_phys(v) (unsigned long)(v) #define phys_to_virt(p) (void*)(p) /* Compiler barrier is enough as an x86 CPU does not reorder reads or writes */ #define smp_rmb() barrier() #define smp_wmb() barrier() /* Status byte for guest to report progress, and synchronize features. */ /* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */ #define VIRTIO_CONFIG_S_ACKNOWLEDGE 1 /* We have found a driver for the device. */ #define VIRTIO_CONFIG_S_DRIVER 2 /* Driver has used its parts of the config, and is happy */ #define VIRTIO_CONFIG_S_DRIVER_OK 4 /* We've given up on this device. */ #define VIRTIO_CONFIG_S_FAILED 0x80 #define MAX_QUEUE_NUM (128) #define VRING_DESC_F_NEXT 1 #define VRING_DESC_F_WRITE 2 #define VRING_AVAIL_F_NO_INTERRUPT 1 #define VRING_USED_F_NO_NOTIFY 1 struct vring_desc { u64 addr; u32 len; u16 flags; u16 next; }; struct vring_avail { u16 flags; u16 idx; u16 ring[0]; }; struct vring_used_elem { u32 id; u32 len; }; struct vring_used { u16 flags; u16 idx; struct vring_used_elem ring[]; }; struct vring { unsigned int num; struct vring_desc *desc; struct vring_avail *avail; struct vring_used *used; }; #define vring_size(num) \ (((((sizeof(struct vring_desc) * num) + \ (sizeof(struct vring_avail) + sizeof(u16) * num)) \ + PAGE_MASK) & ~PAGE_MASK) + \ (sizeof(struct vring_used) + sizeof(struct vring_used_elem) * num)) typedef unsigned char virtio_queue_t[vring_size(MAX_QUEUE_NUM)]; struct vring_virtqueue { virtio_queue_t queue; struct vring vring; u16 free_head; u16 last_used_idx; u16 vdata[MAX_QUEUE_NUM]; /* PCI */ int queue_index; }; struct vring_list { char *addr; unsigned int length; }; static inline void vring_init(struct vring *vr, unsigned int num, unsigned char *queue) { unsigned int i; unsigned long pa; ASSERT32FLAT(); vr->num = num; /* physical address of desc must be page aligned */ pa = virt_to_phys(queue); pa = (pa + PAGE_MASK) & ~PAGE_MASK; vr->desc = phys_to_virt(pa); vr->avail = (struct vring_avail *)&vr->desc[num]; /* disable interrupts */ vr->avail->flags |= VRING_AVAIL_F_NO_INTERRUPT; /* physical address of used must be page aligned */ pa = virt_to_phys(&vr->avail->ring[num]); pa = (pa + PAGE_MASK) & ~PAGE_MASK; vr->used = phys_to_virt(pa); for (i = 0; i < num - 1; i++) vr->desc[i].next = i + 1; vr->desc[i].next = 0; } int vring_more_used(struct vring_virtqueue *vq); void vring_detach(struct vring_virtqueue *vq, unsigned int head); int vring_get_buf(struct vring_virtqueue *vq, unsigned int *len); void vring_add_buf(struct vring_virtqueue *vq, struct vring_list list[], unsigned int out, unsigned int in, int index, int num_added); void vring_kick(unsigned int ioaddr, struct vring_virtqueue *vq, int num_added); #endif /* _VIRTIO_RING_H_ */