xref: /kvmtool/virtio/mmio-modern.c (revision 5fe5eb04de80b8bc68f4d57443596d0b935907ef)
1*5fe5eb04SJean-Philippe Brucker #include "kvm/virtio.h"
2*5fe5eb04SJean-Philippe Brucker #include "kvm/virtio-mmio.h"
3*5fe5eb04SJean-Philippe Brucker 
4*5fe5eb04SJean-Philippe Brucker #include <linux/byteorder.h>
5*5fe5eb04SJean-Philippe Brucker 
6*5fe5eb04SJean-Philippe Brucker #define vmmio_selected_vq(vmmio) \
7*5fe5eb04SJean-Philippe Brucker 	vdev->ops->get_vq((vmmio)->kvm, (vmmio)->dev, (vmmio)->hdr.queue_sel)
8*5fe5eb04SJean-Philippe Brucker 
virtio_mmio_config_in(struct kvm_cpu * vcpu,u64 addr,u32 * data,u32 len,struct virtio_device * vdev)9*5fe5eb04SJean-Philippe Brucker static void virtio_mmio_config_in(struct kvm_cpu *vcpu,
10*5fe5eb04SJean-Philippe Brucker 				  u64 addr, u32 *data, u32 len,
11*5fe5eb04SJean-Philippe Brucker 				  struct virtio_device *vdev)
12*5fe5eb04SJean-Philippe Brucker {
13*5fe5eb04SJean-Philippe Brucker 	struct virtio_mmio *vmmio = vdev->virtio;
14*5fe5eb04SJean-Philippe Brucker 	u64 features = 1ULL << VIRTIO_F_VERSION_1;
15*5fe5eb04SJean-Philippe Brucker 	u32 val = 0;
16*5fe5eb04SJean-Philippe Brucker 
17*5fe5eb04SJean-Philippe Brucker 	switch (addr) {
18*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_MAGIC_VALUE:
19*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_VERSION:
20*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_DEVICE_ID:
21*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_VENDOR_ID:
22*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_STATUS:
23*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_INTERRUPT_STATUS:
24*5fe5eb04SJean-Philippe Brucker 		val = *(u32 *)(((void *)&vmmio->hdr) + addr);
25*5fe5eb04SJean-Philippe Brucker 		break;
26*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_DEVICE_FEATURES:
27*5fe5eb04SJean-Philippe Brucker 		if (vmmio->hdr.host_features_sel > 1)
28*5fe5eb04SJean-Philippe Brucker 			break;
29*5fe5eb04SJean-Philippe Brucker 		features |= vdev->ops->get_host_features(vmmio->kvm, vmmio->dev);
30*5fe5eb04SJean-Philippe Brucker 		val = features >> (32 * vmmio->hdr.host_features_sel);
31*5fe5eb04SJean-Philippe Brucker 		break;
32*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_QUEUE_NUM_MAX:
33*5fe5eb04SJean-Philippe Brucker 		val = vdev->ops->get_size_vq(vmmio->kvm, vmmio->dev,
34*5fe5eb04SJean-Philippe Brucker 					     vmmio->hdr.queue_sel);
35*5fe5eb04SJean-Philippe Brucker 		break;
36*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_QUEUE_READY:
37*5fe5eb04SJean-Philippe Brucker 		val = vmmio_selected_vq(vmmio)->enabled;
38*5fe5eb04SJean-Philippe Brucker 		break;
39*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_QUEUE_DESC_LOW:
40*5fe5eb04SJean-Philippe Brucker 		val = vmmio_selected_vq(vmmio)->vring_addr.desc_lo;
41*5fe5eb04SJean-Philippe Brucker 		break;
42*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_QUEUE_DESC_HIGH:
43*5fe5eb04SJean-Philippe Brucker 		val = vmmio_selected_vq(vmmio)->vring_addr.desc_hi;
44*5fe5eb04SJean-Philippe Brucker 		break;
45*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_QUEUE_USED_LOW:
46*5fe5eb04SJean-Philippe Brucker 		val = vmmio_selected_vq(vmmio)->vring_addr.used_lo;
47*5fe5eb04SJean-Philippe Brucker 		break;
48*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_QUEUE_USED_HIGH:
49*5fe5eb04SJean-Philippe Brucker 		val = vmmio_selected_vq(vmmio)->vring_addr.used_hi;
50*5fe5eb04SJean-Philippe Brucker 		break;
51*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_QUEUE_AVAIL_LOW:
52*5fe5eb04SJean-Philippe Brucker 		val = vmmio_selected_vq(vmmio)->vring_addr.avail_lo;
53*5fe5eb04SJean-Philippe Brucker 		break;
54*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_QUEUE_AVAIL_HIGH:
55*5fe5eb04SJean-Philippe Brucker 		val = vmmio_selected_vq(vmmio)->vring_addr.avail_hi;
56*5fe5eb04SJean-Philippe Brucker 		break;
57*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_CONFIG_GENERATION:
58*5fe5eb04SJean-Philippe Brucker 		/*
59*5fe5eb04SJean-Philippe Brucker 		 * The config generation changes when the device updates a
60*5fe5eb04SJean-Philippe Brucker 		 * config field larger than 32 bits, that the driver reads using
61*5fe5eb04SJean-Philippe Brucker 		 * multiple accesses. Since kvmtool doesn't use any mutable
62*5fe5eb04SJean-Philippe Brucker 		 * config field larger than 32 bits, the generation is constant.
63*5fe5eb04SJean-Philippe Brucker 		 */
64*5fe5eb04SJean-Philippe Brucker 		break;
65*5fe5eb04SJean-Philippe Brucker 	default:
66*5fe5eb04SJean-Philippe Brucker 		return;
67*5fe5eb04SJean-Philippe Brucker 	}
68*5fe5eb04SJean-Philippe Brucker 
69*5fe5eb04SJean-Philippe Brucker 	*data = cpu_to_le32(val);
70*5fe5eb04SJean-Philippe Brucker }
71*5fe5eb04SJean-Philippe Brucker 
virtio_mmio_config_out(struct kvm_cpu * vcpu,u64 addr,u32 * data,u32 len,struct virtio_device * vdev)72*5fe5eb04SJean-Philippe Brucker static void virtio_mmio_config_out(struct kvm_cpu *vcpu,
73*5fe5eb04SJean-Philippe Brucker 				   u64 addr, u32 *data, u32 len,
74*5fe5eb04SJean-Philippe Brucker 				   struct virtio_device *vdev)
75*5fe5eb04SJean-Philippe Brucker {
76*5fe5eb04SJean-Philippe Brucker 	struct virtio_mmio *vmmio = vdev->virtio;
77*5fe5eb04SJean-Philippe Brucker 	struct kvm *kvm = vmmio->kvm;
78*5fe5eb04SJean-Philippe Brucker 	u32 val = le32_to_cpu(*data);
79*5fe5eb04SJean-Philippe Brucker 	u64 features;
80*5fe5eb04SJean-Philippe Brucker 
81*5fe5eb04SJean-Philippe Brucker 	switch (addr) {
82*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_DEVICE_FEATURES_SEL:
83*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_DRIVER_FEATURES_SEL:
84*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_QUEUE_SEL:
85*5fe5eb04SJean-Philippe Brucker 		*(u32 *)(((void *)&vmmio->hdr) + addr) = val;
86*5fe5eb04SJean-Philippe Brucker 		break;
87*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_STATUS:
88*5fe5eb04SJean-Philippe Brucker 		vmmio->hdr.status = val;
89*5fe5eb04SJean-Philippe Brucker 		virtio_notify_status(kvm, vdev, vmmio->dev, val);
90*5fe5eb04SJean-Philippe Brucker 		break;
91*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_DRIVER_FEATURES:
92*5fe5eb04SJean-Philippe Brucker 		if (vmmio->hdr.guest_features_sel > 1)
93*5fe5eb04SJean-Philippe Brucker 			break;
94*5fe5eb04SJean-Philippe Brucker 
95*5fe5eb04SJean-Philippe Brucker 		features = (u64)val << (32 * vmmio->hdr.guest_features_sel);
96*5fe5eb04SJean-Philippe Brucker 		virtio_set_guest_features(vmmio->kvm, vdev, vmmio->dev,
97*5fe5eb04SJean-Philippe Brucker 					  features);
98*5fe5eb04SJean-Philippe Brucker 		break;
99*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_QUEUE_NUM:
100*5fe5eb04SJean-Philippe Brucker 		vmmio->hdr.queue_num = val;
101*5fe5eb04SJean-Philippe Brucker 		vdev->ops->set_size_vq(vmmio->kvm, vmmio->dev,
102*5fe5eb04SJean-Philippe Brucker 				       vmmio->hdr.queue_sel, val);
103*5fe5eb04SJean-Philippe Brucker 		break;
104*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_QUEUE_READY:
105*5fe5eb04SJean-Philippe Brucker 		if (val)
106*5fe5eb04SJean-Philippe Brucker 			virtio_mmio_init_vq(kvm, vdev, vmmio->hdr.queue_sel);
107*5fe5eb04SJean-Philippe Brucker 		else
108*5fe5eb04SJean-Philippe Brucker 			virtio_mmio_exit_vq(kvm, vdev, vmmio->hdr.queue_sel);
109*5fe5eb04SJean-Philippe Brucker 		break;
110*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_QUEUE_NOTIFY:
111*5fe5eb04SJean-Philippe Brucker 		vdev->ops->notify_vq(vmmio->kvm, vmmio->dev, val);
112*5fe5eb04SJean-Philippe Brucker 		break;
113*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_INTERRUPT_ACK:
114*5fe5eb04SJean-Philippe Brucker 		vmmio->hdr.interrupt_state &= ~val;
115*5fe5eb04SJean-Philippe Brucker 		break;
116*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_QUEUE_DESC_LOW:
117*5fe5eb04SJean-Philippe Brucker 		vmmio_selected_vq(vmmio)->vring_addr.desc_lo = val;
118*5fe5eb04SJean-Philippe Brucker 		break;
119*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_QUEUE_DESC_HIGH:
120*5fe5eb04SJean-Philippe Brucker 		vmmio_selected_vq(vmmio)->vring_addr.desc_hi = val;
121*5fe5eb04SJean-Philippe Brucker 		break;
122*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_QUEUE_USED_LOW:
123*5fe5eb04SJean-Philippe Brucker 		vmmio_selected_vq(vmmio)->vring_addr.used_lo = val;
124*5fe5eb04SJean-Philippe Brucker 		break;
125*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_QUEUE_USED_HIGH:
126*5fe5eb04SJean-Philippe Brucker 		vmmio_selected_vq(vmmio)->vring_addr.used_hi = val;
127*5fe5eb04SJean-Philippe Brucker 		break;
128*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_QUEUE_AVAIL_LOW:
129*5fe5eb04SJean-Philippe Brucker 		vmmio_selected_vq(vmmio)->vring_addr.avail_lo = val;
130*5fe5eb04SJean-Philippe Brucker 		break;
131*5fe5eb04SJean-Philippe Brucker 	case VIRTIO_MMIO_QUEUE_AVAIL_HIGH:
132*5fe5eb04SJean-Philippe Brucker 		vmmio_selected_vq(vmmio)->vring_addr.avail_hi = val;
133*5fe5eb04SJean-Philippe Brucker 		break;
134*5fe5eb04SJean-Philippe Brucker 	};
135*5fe5eb04SJean-Philippe Brucker }
136*5fe5eb04SJean-Philippe Brucker 
virtio_mmio_modern_callback(struct kvm_cpu * vcpu,u64 addr,u8 * data,u32 len,u8 is_write,void * ptr)137*5fe5eb04SJean-Philippe Brucker void virtio_mmio_modern_callback(struct kvm_cpu *vcpu, u64 addr, u8 *data,
138*5fe5eb04SJean-Philippe Brucker 				 u32 len, u8 is_write, void *ptr)
139*5fe5eb04SJean-Philippe Brucker {
140*5fe5eb04SJean-Philippe Brucker 	struct virtio_device *vdev = ptr;
141*5fe5eb04SJean-Philippe Brucker 	struct virtio_mmio *vmmio = vdev->virtio;
142*5fe5eb04SJean-Philippe Brucker 	u32 offset = addr - vmmio->addr;
143*5fe5eb04SJean-Philippe Brucker 
144*5fe5eb04SJean-Philippe Brucker 	if (offset >= VIRTIO_MMIO_CONFIG) {
145*5fe5eb04SJean-Philippe Brucker 		offset -= VIRTIO_MMIO_CONFIG;
146*5fe5eb04SJean-Philippe Brucker 		virtio_access_config(vmmio->kvm, vdev, vmmio->dev, offset, data,
147*5fe5eb04SJean-Philippe Brucker 				     len, is_write);
148*5fe5eb04SJean-Philippe Brucker 		return;
149*5fe5eb04SJean-Philippe Brucker 	}
150*5fe5eb04SJean-Philippe Brucker 
151*5fe5eb04SJean-Philippe Brucker 	if (len != 4) {
152*5fe5eb04SJean-Philippe Brucker 		pr_debug("Invalid %s size %d at 0x%llx", is_write ? "write" :
153*5fe5eb04SJean-Philippe Brucker 			 "read", len, addr);
154*5fe5eb04SJean-Philippe Brucker 		return;
155*5fe5eb04SJean-Philippe Brucker 	}
156*5fe5eb04SJean-Philippe Brucker 
157*5fe5eb04SJean-Philippe Brucker 	if (is_write)
158*5fe5eb04SJean-Philippe Brucker 		virtio_mmio_config_out(vcpu, offset, (void *)data, len, ptr);
159*5fe5eb04SJean-Philippe Brucker 	else
160*5fe5eb04SJean-Philippe Brucker 		virtio_mmio_config_in(vcpu, offset, (void *)data, len, ptr);
161*5fe5eb04SJean-Philippe Brucker }
162