xref: /kvmtool/virtio/mmio-legacy.c (revision 22a0823676f13ade6a8d561eaf45c6d4e3218059)
1*22a08236SJean-Philippe Brucker #include "kvm/ioport.h"
2*22a08236SJean-Philippe Brucker #include "kvm/virtio.h"
3*22a08236SJean-Philippe Brucker #include "kvm/virtio-mmio.h"
4*22a08236SJean-Philippe Brucker 
5*22a08236SJean-Philippe Brucker #include <linux/virtio_mmio.h>
6*22a08236SJean-Philippe Brucker 
7*22a08236SJean-Philippe Brucker #define vmmio_selected_vq(vdev, vmmio) \
8*22a08236SJean-Philippe Brucker 	(vdev)->ops->get_vq((vmmio)->kvm, (vmmio)->dev, (vmmio)->hdr.queue_sel)
9*22a08236SJean-Philippe Brucker 
virtio_mmio_config_in(struct kvm_cpu * vcpu,u64 addr,void * data,u32 len,struct virtio_device * vdev)10*22a08236SJean-Philippe Brucker static void virtio_mmio_config_in(struct kvm_cpu *vcpu,
11*22a08236SJean-Philippe Brucker 				  u64 addr, void *data, u32 len,
12*22a08236SJean-Philippe Brucker 				  struct virtio_device *vdev)
13*22a08236SJean-Philippe Brucker {
14*22a08236SJean-Philippe Brucker 	struct virtio_mmio *vmmio = vdev->virtio;
15*22a08236SJean-Philippe Brucker 	struct virt_queue *vq;
16*22a08236SJean-Philippe Brucker 	u32 val = 0;
17*22a08236SJean-Philippe Brucker 
18*22a08236SJean-Philippe Brucker 	switch (addr) {
19*22a08236SJean-Philippe Brucker 	case VIRTIO_MMIO_MAGIC_VALUE:
20*22a08236SJean-Philippe Brucker 	case VIRTIO_MMIO_VERSION:
21*22a08236SJean-Philippe Brucker 	case VIRTIO_MMIO_DEVICE_ID:
22*22a08236SJean-Philippe Brucker 	case VIRTIO_MMIO_VENDOR_ID:
23*22a08236SJean-Philippe Brucker 	case VIRTIO_MMIO_STATUS:
24*22a08236SJean-Philippe Brucker 	case VIRTIO_MMIO_INTERRUPT_STATUS:
25*22a08236SJean-Philippe Brucker 		ioport__write32(data, *(u32 *)(((void *)&vmmio->hdr) + addr));
26*22a08236SJean-Philippe Brucker 		break;
27*22a08236SJean-Philippe Brucker 	case VIRTIO_MMIO_DEVICE_FEATURES:
28*22a08236SJean-Philippe Brucker 		if (vmmio->hdr.host_features_sel == 0)
29*22a08236SJean-Philippe Brucker 			val = vdev->ops->get_host_features(vmmio->kvm,
30*22a08236SJean-Philippe Brucker 							   vmmio->dev);
31*22a08236SJean-Philippe Brucker 		ioport__write32(data, val);
32*22a08236SJean-Philippe Brucker 		break;
33*22a08236SJean-Philippe Brucker 	case VIRTIO_MMIO_QUEUE_PFN:
34*22a08236SJean-Philippe Brucker 		vq = vmmio_selected_vq(vdev, vmmio);
35*22a08236SJean-Philippe Brucker 		ioport__write32(data, vq->vring_addr.pfn);
36*22a08236SJean-Philippe Brucker 		break;
37*22a08236SJean-Philippe Brucker 	case VIRTIO_MMIO_QUEUE_NUM_MAX:
38*22a08236SJean-Philippe Brucker 		val = vdev->ops->get_size_vq(vmmio->kvm, vmmio->dev,
39*22a08236SJean-Philippe Brucker 					     vmmio->hdr.queue_sel);
40*22a08236SJean-Philippe Brucker 		ioport__write32(data, val);
41*22a08236SJean-Philippe Brucker 		break;
42*22a08236SJean-Philippe Brucker 	default:
43*22a08236SJean-Philippe Brucker 		break;
44*22a08236SJean-Philippe Brucker 	}
45*22a08236SJean-Philippe Brucker }
46*22a08236SJean-Philippe Brucker 
virtio_mmio_config_out(struct kvm_cpu * vcpu,u64 addr,void * data,u32 len,struct virtio_device * vdev)47*22a08236SJean-Philippe Brucker static void virtio_mmio_config_out(struct kvm_cpu *vcpu,
48*22a08236SJean-Philippe Brucker 				   u64 addr, void *data, u32 len,
49*22a08236SJean-Philippe Brucker 				   struct virtio_device *vdev)
50*22a08236SJean-Philippe Brucker {
51*22a08236SJean-Philippe Brucker 	struct virtio_mmio *vmmio = vdev->virtio;
52*22a08236SJean-Philippe Brucker 	struct kvm *kvm = vmmio->kvm;
53*22a08236SJean-Philippe Brucker 	unsigned int vq_count = vdev->ops->get_vq_count(kvm, vmmio->dev);
54*22a08236SJean-Philippe Brucker 	struct virt_queue *vq;
55*22a08236SJean-Philippe Brucker 	u32 val = 0;
56*22a08236SJean-Philippe Brucker 
57*22a08236SJean-Philippe Brucker 	switch (addr) {
58*22a08236SJean-Philippe Brucker 	case VIRTIO_MMIO_DEVICE_FEATURES_SEL:
59*22a08236SJean-Philippe Brucker 	case VIRTIO_MMIO_DRIVER_FEATURES_SEL:
60*22a08236SJean-Philippe Brucker 		val = ioport__read32(data);
61*22a08236SJean-Philippe Brucker 		*(u32 *)(((void *)&vmmio->hdr) + addr) = val;
62*22a08236SJean-Philippe Brucker 		break;
63*22a08236SJean-Philippe Brucker 	case VIRTIO_MMIO_QUEUE_SEL:
64*22a08236SJean-Philippe Brucker 		val = ioport__read32(data);
65*22a08236SJean-Philippe Brucker 		if (val >= vq_count) {
66*22a08236SJean-Philippe Brucker 			WARN_ONCE(1, "QUEUE_SEL value (%u) is larger than VQ count (%u)\n",
67*22a08236SJean-Philippe Brucker 				val, vq_count);
68*22a08236SJean-Philippe Brucker 			break;
69*22a08236SJean-Philippe Brucker 		}
70*22a08236SJean-Philippe Brucker 		*(u32 *)(((void *)&vmmio->hdr) + addr) = val;
71*22a08236SJean-Philippe Brucker 		break;
72*22a08236SJean-Philippe Brucker 	case VIRTIO_MMIO_STATUS:
73*22a08236SJean-Philippe Brucker 		vmmio->hdr.status = ioport__read32(data);
74*22a08236SJean-Philippe Brucker 		if (!vmmio->hdr.status) /* Sample endianness on reset */
75*22a08236SJean-Philippe Brucker 			vdev->endian = kvm_cpu__get_endianness(vcpu);
76*22a08236SJean-Philippe Brucker 		virtio_notify_status(kvm, vdev, vmmio->dev, vmmio->hdr.status);
77*22a08236SJean-Philippe Brucker 		break;
78*22a08236SJean-Philippe Brucker 	case VIRTIO_MMIO_DRIVER_FEATURES:
79*22a08236SJean-Philippe Brucker 		if (vmmio->hdr.guest_features_sel == 0) {
80*22a08236SJean-Philippe Brucker 			val = ioport__read32(data);
81*22a08236SJean-Philippe Brucker 			virtio_set_guest_features(vmmio->kvm, vdev,
82*22a08236SJean-Philippe Brucker 						  vmmio->dev, val);
83*22a08236SJean-Philippe Brucker 		}
84*22a08236SJean-Philippe Brucker 		break;
85*22a08236SJean-Philippe Brucker 	case VIRTIO_MMIO_GUEST_PAGE_SIZE:
86*22a08236SJean-Philippe Brucker 		val = ioport__read32(data);
87*22a08236SJean-Philippe Brucker 		vmmio->hdr.guest_page_size = val;
88*22a08236SJean-Philippe Brucker 		break;
89*22a08236SJean-Philippe Brucker 	case VIRTIO_MMIO_QUEUE_NUM:
90*22a08236SJean-Philippe Brucker 		val = ioport__read32(data);
91*22a08236SJean-Philippe Brucker 		vmmio->hdr.queue_num = val;
92*22a08236SJean-Philippe Brucker 		vdev->ops->set_size_vq(vmmio->kvm, vmmio->dev,
93*22a08236SJean-Philippe Brucker 				       vmmio->hdr.queue_sel, val);
94*22a08236SJean-Philippe Brucker 		break;
95*22a08236SJean-Philippe Brucker 	case VIRTIO_MMIO_QUEUE_ALIGN:
96*22a08236SJean-Philippe Brucker 		val = ioport__read32(data);
97*22a08236SJean-Philippe Brucker 		vmmio->hdr.queue_align = val;
98*22a08236SJean-Philippe Brucker 		break;
99*22a08236SJean-Philippe Brucker 	case VIRTIO_MMIO_QUEUE_PFN:
100*22a08236SJean-Philippe Brucker 		val = ioport__read32(data);
101*22a08236SJean-Philippe Brucker 		if (val) {
102*22a08236SJean-Philippe Brucker 			vq = vmmio_selected_vq(vdev, vmmio);
103*22a08236SJean-Philippe Brucker 			vq->vring_addr = (struct vring_addr) {
104*22a08236SJean-Philippe Brucker 				.legacy	= true,
105*22a08236SJean-Philippe Brucker 				.pfn	= val,
106*22a08236SJean-Philippe Brucker 				.align	= vmmio->hdr.queue_align,
107*22a08236SJean-Philippe Brucker 				.pgsize	= vmmio->hdr.guest_page_size,
108*22a08236SJean-Philippe Brucker 			};
109*22a08236SJean-Philippe Brucker 			virtio_mmio_init_vq(kvm, vdev, vmmio->hdr.queue_sel);
110*22a08236SJean-Philippe Brucker 		} else {
111*22a08236SJean-Philippe Brucker 			virtio_mmio_exit_vq(kvm, vdev, vmmio->hdr.queue_sel);
112*22a08236SJean-Philippe Brucker 		}
113*22a08236SJean-Philippe Brucker 		break;
114*22a08236SJean-Philippe Brucker 	case VIRTIO_MMIO_QUEUE_NOTIFY:
115*22a08236SJean-Philippe Brucker 		val = ioport__read32(data);
116*22a08236SJean-Philippe Brucker 		if (val >= vq_count) {
117*22a08236SJean-Philippe Brucker 			WARN_ONCE(1, "QUEUE_NOTIFY value (%u) is larger than VQ count (%u)\n",
118*22a08236SJean-Philippe Brucker 				val, vq_count);
119*22a08236SJean-Philippe Brucker 			break;
120*22a08236SJean-Philippe Brucker 		}
121*22a08236SJean-Philippe Brucker 		vdev->ops->notify_vq(vmmio->kvm, vmmio->dev, val);
122*22a08236SJean-Philippe Brucker 		break;
123*22a08236SJean-Philippe Brucker 	case VIRTIO_MMIO_INTERRUPT_ACK:
124*22a08236SJean-Philippe Brucker 		val = ioport__read32(data);
125*22a08236SJean-Philippe Brucker 		vmmio->hdr.interrupt_state &= ~val;
126*22a08236SJean-Philippe Brucker 		break;
127*22a08236SJean-Philippe Brucker 	default:
128*22a08236SJean-Philippe Brucker 		break;
129*22a08236SJean-Philippe Brucker 	};
130*22a08236SJean-Philippe Brucker }
131*22a08236SJean-Philippe Brucker 
virtio_mmio_legacy_callback(struct kvm_cpu * vcpu,u64 addr,u8 * data,u32 len,u8 is_write,void * ptr)132*22a08236SJean-Philippe Brucker void virtio_mmio_legacy_callback(struct kvm_cpu *vcpu, u64 addr, u8 *data,
133*22a08236SJean-Philippe Brucker 				 u32 len, u8 is_write, void *ptr)
134*22a08236SJean-Philippe Brucker {
135*22a08236SJean-Philippe Brucker 	struct virtio_device *vdev = ptr;
136*22a08236SJean-Philippe Brucker 	struct virtio_mmio *vmmio = vdev->virtio;
137*22a08236SJean-Philippe Brucker 	u32 offset = addr - vmmio->addr;
138*22a08236SJean-Philippe Brucker 
139*22a08236SJean-Philippe Brucker 	if (offset >= VIRTIO_MMIO_CONFIG) {
140*22a08236SJean-Philippe Brucker 		offset -= VIRTIO_MMIO_CONFIG;
141*22a08236SJean-Philippe Brucker 		virtio_access_config(vmmio->kvm, vdev, vmmio->dev, offset, data,
142*22a08236SJean-Philippe Brucker 				     len, is_write);
143*22a08236SJean-Philippe Brucker 		return;
144*22a08236SJean-Philippe Brucker 	}
145*22a08236SJean-Philippe Brucker 
146*22a08236SJean-Philippe Brucker 	if (is_write)
147*22a08236SJean-Philippe Brucker 		virtio_mmio_config_out(vcpu, offset, data, len, ptr);
148*22a08236SJean-Philippe Brucker 	else
149*22a08236SJean-Philippe Brucker 		virtio_mmio_config_in(vcpu, offset, data, len, ptr);
150*22a08236SJean-Philippe Brucker }
151