xref: /spdk/lib/virtio/virtio_pci.c (revision a6dbe3721eb3b5990707fc3e378c95e505dd8ab5)
1488570ebSJim Harris /*   SPDX-License-Identifier: BSD-3-Clause
2*a6dbe372Spaul luse  *   Copyright (C) 2010-2014 Intel Corporation. All rights reserved.
39dde5e5cSDariusz Stojaczyk  *   All rights reserved.
49dde5e5cSDariusz Stojaczyk  */
59dde5e5cSDariusz Stojaczyk 
69dde5e5cSDariusz Stojaczyk #include "spdk/stdinc.h"
79dde5e5cSDariusz Stojaczyk 
8e03861f1SDarek Stojaczyk #include "spdk/memory.h"
99dde5e5cSDariusz Stojaczyk #include "spdk/mmio.h"
109dde5e5cSDariusz Stojaczyk #include "spdk/string.h"
119dde5e5cSDariusz Stojaczyk #include "spdk/env.h"
129dde5e5cSDariusz Stojaczyk 
139dde5e5cSDariusz Stojaczyk #include "spdk_internal/virtio.h"
14ebea4dd6SJin Yu #include <linux/virtio_ids.h>
159dde5e5cSDariusz Stojaczyk 
169dde5e5cSDariusz Stojaczyk struct virtio_hw {
179dde5e5cSDariusz Stojaczyk 	uint8_t	    use_msix;
189dde5e5cSDariusz Stojaczyk 	uint32_t    notify_off_multiplier;
199dde5e5cSDariusz Stojaczyk 	uint8_t     *isr;
209dde5e5cSDariusz Stojaczyk 	uint16_t    *notify_base;
219dde5e5cSDariusz Stojaczyk 
229dde5e5cSDariusz Stojaczyk 	struct {
239dde5e5cSDariusz Stojaczyk 		/** Mem-mapped resources from given PCI BAR */
249dde5e5cSDariusz Stojaczyk 		void        *vaddr;
259dde5e5cSDariusz Stojaczyk 
269dde5e5cSDariusz Stojaczyk 		/** Length of the address space */
279dde5e5cSDariusz Stojaczyk 		uint32_t    len;
289dde5e5cSDariusz Stojaczyk 	} pci_bar[6];
299dde5e5cSDariusz Stojaczyk 
309dde5e5cSDariusz Stojaczyk 	struct virtio_pci_common_cfg *common_cfg;
319dde5e5cSDariusz Stojaczyk 	struct spdk_pci_device *pci_dev;
329dde5e5cSDariusz Stojaczyk 
339dde5e5cSDariusz Stojaczyk 	/** Device-specific PCI config space */
349dde5e5cSDariusz Stojaczyk 	void *dev_cfg;
355ee049eeSJin Yu 
36ebea4dd6SJin Yu 	struct virtio_dev *vdev;
375ee049eeSJin Yu 	bool is_remapped;
38ebea4dd6SJin Yu 	bool is_removing;
39ebea4dd6SJin Yu 	TAILQ_ENTRY(virtio_hw) tailq;
409dde5e5cSDariusz Stojaczyk };
419dde5e5cSDariusz Stojaczyk 
42724c9aa7SDariusz Stojaczyk struct virtio_pci_probe_ctx {
43724c9aa7SDariusz Stojaczyk 	virtio_pci_create_cb enum_cb;
445c0c104bSDariusz Stojaczyk 	void *enum_ctx;
45724c9aa7SDariusz Stojaczyk 	uint16_t device_id;
46724c9aa7SDariusz Stojaczyk };
47724c9aa7SDariusz Stojaczyk 
48ebea4dd6SJin Yu static TAILQ_HEAD(, virtio_hw) g_virtio_hws = TAILQ_HEAD_INITIALIZER(g_virtio_hws);
49ebea4dd6SJin Yu static pthread_mutex_t g_hw_mutex = PTHREAD_MUTEX_INITIALIZER;
505ee049eeSJin Yu __thread struct virtio_hw *g_thread_virtio_hw = NULL;
515ee049eeSJin Yu static uint16_t g_signal_lock;
525ee049eeSJin Yu static bool g_sigset = false;
535ee049eeSJin Yu 
549dde5e5cSDariusz Stojaczyk /*
559dde5e5cSDariusz Stojaczyk  * Following macros are derived from linux/pci_regs.h, however,
569dde5e5cSDariusz Stojaczyk  * we can't simply include that header here, as there is no such
579dde5e5cSDariusz Stojaczyk  * file for non-Linux platform.
589dde5e5cSDariusz Stojaczyk  */
599dde5e5cSDariusz Stojaczyk #define PCI_CAPABILITY_LIST	0x34
609dde5e5cSDariusz Stojaczyk #define PCI_CAP_ID_VNDR		0x09
619dde5e5cSDariusz Stojaczyk #define PCI_CAP_ID_MSIX		0x11
629dde5e5cSDariusz Stojaczyk 
635ee049eeSJin Yu static void
virtio_pci_dev_sigbus_handler(const void * failure_addr,void * ctx)64c4fafdb2SJim Harris virtio_pci_dev_sigbus_handler(const void *failure_addr, void *ctx)
655ee049eeSJin Yu {
66c4fafdb2SJim Harris 	void *map_address = NULL;
675ee049eeSJin Yu 	uint16_t flag = 0;
685ee049eeSJin Yu 	int i;
695ee049eeSJin Yu 
705ee049eeSJin Yu 	if (!__atomic_compare_exchange_n(&g_signal_lock, &flag, 1, false, __ATOMIC_ACQUIRE,
715ee049eeSJin Yu 					 __ATOMIC_RELAXED)) {
725ee049eeSJin Yu 		SPDK_DEBUGLOG(virtio_pci, "request g_signal_lock failed\n");
735ee049eeSJin Yu 		return;
745ee049eeSJin Yu 	}
755ee049eeSJin Yu 
765ee049eeSJin Yu 	if (g_thread_virtio_hw == NULL || g_thread_virtio_hw->is_remapped) {
775ee049eeSJin Yu 		__atomic_store_n(&g_signal_lock, 0, __ATOMIC_RELEASE);
785ee049eeSJin Yu 		return;
795ee049eeSJin Yu 	}
805ee049eeSJin Yu 
815ee049eeSJin Yu 	/* We remap each bar to the same VA to avoid subsequent sigbus error.
825ee049eeSJin Yu 	 * Because it is mapped to the same VA, such as hw->common_cfg and so on
835ee049eeSJin Yu 	 * do not need to be modified.
845ee049eeSJin Yu 	 */
855ee049eeSJin Yu 	for (i = 0; i < 6; ++i) {
865ee049eeSJin Yu 		if (g_thread_virtio_hw->pci_bar[i].vaddr == NULL) {
875ee049eeSJin Yu 			continue;
885ee049eeSJin Yu 		}
895ee049eeSJin Yu 
905ee049eeSJin Yu 		map_address = mmap(g_thread_virtio_hw->pci_bar[i].vaddr,
915ee049eeSJin Yu 				   g_thread_virtio_hw->pci_bar[i].len,
925ee049eeSJin Yu 				   PROT_READ | PROT_WRITE,
935ee049eeSJin Yu 				   MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
945ee049eeSJin Yu 		if (map_address == MAP_FAILED) {
955ee049eeSJin Yu 			SPDK_ERRLOG("mmap failed\n");
965ee049eeSJin Yu 			goto fail;
975ee049eeSJin Yu 		}
985ee049eeSJin Yu 		memset(map_address, 0xFF, g_thread_virtio_hw->pci_bar[i].len);
995ee049eeSJin Yu 	}
1005ee049eeSJin Yu 
1015ee049eeSJin Yu 	g_thread_virtio_hw->is_remapped = true;
1025ee049eeSJin Yu 	__atomic_store_n(&g_signal_lock, 0, __ATOMIC_RELEASE);
1035ee049eeSJin Yu 	return;
1045ee049eeSJin Yu fail:
1055ee049eeSJin Yu 	for (--i; i >= 0; i--) {
1065ee049eeSJin Yu 		if (g_thread_virtio_hw->pci_bar[i].vaddr == NULL) {
1075ee049eeSJin Yu 			continue;
1085ee049eeSJin Yu 		}
1095ee049eeSJin Yu 
1105ee049eeSJin Yu 		munmap(g_thread_virtio_hw->pci_bar[i].vaddr, g_thread_virtio_hw->pci_bar[i].len);
1115ee049eeSJin Yu 	}
1125ee049eeSJin Yu 	__atomic_store_n(&g_signal_lock, 0, __ATOMIC_RELEASE);
1135ee049eeSJin Yu }
1145ee049eeSJin Yu 
115ebea4dd6SJin Yu static struct virtio_hw *
virtio_pci_dev_get_by_addr(struct spdk_pci_addr * traddr)116ebea4dd6SJin Yu virtio_pci_dev_get_by_addr(struct spdk_pci_addr *traddr)
117ebea4dd6SJin Yu {
118ebea4dd6SJin Yu 	struct virtio_hw *hw;
119ebea4dd6SJin Yu 	struct spdk_pci_addr addr;
120ebea4dd6SJin Yu 
121ebea4dd6SJin Yu 	pthread_mutex_lock(&g_hw_mutex);
122ebea4dd6SJin Yu 	TAILQ_FOREACH(hw, &g_virtio_hws, tailq) {
123ebea4dd6SJin Yu 		addr = spdk_pci_device_get_addr(hw->pci_dev);
124ebea4dd6SJin Yu 		if (!spdk_pci_addr_compare(&addr, traddr)) {
125ebea4dd6SJin Yu 			pthread_mutex_unlock(&g_hw_mutex);
126ebea4dd6SJin Yu 			return hw;
127ebea4dd6SJin Yu 		}
128ebea4dd6SJin Yu 	}
129ebea4dd6SJin Yu 	pthread_mutex_unlock(&g_hw_mutex);
130ebea4dd6SJin Yu 
131ebea4dd6SJin Yu 	return NULL;
132ebea4dd6SJin Yu }
133ebea4dd6SJin Yu 
134ebea4dd6SJin Yu static const char *
virtio_pci_dev_check(struct virtio_hw * hw,uint16_t device_id_match)135ebea4dd6SJin Yu virtio_pci_dev_check(struct virtio_hw *hw, uint16_t device_id_match)
136ebea4dd6SJin Yu {
137ebea4dd6SJin Yu 	uint16_t pci_device_id, device_id;
138ebea4dd6SJin Yu 
139ebea4dd6SJin Yu 	pci_device_id = spdk_pci_device_get_device_id(hw->pci_dev);
140ebea4dd6SJin Yu 	if (pci_device_id < 0x1040) {
141ebea4dd6SJin Yu 		/* Transitional devices: use the PCI subsystem device id as
142ebea4dd6SJin Yu 		 * virtio device id, same as legacy driver always did.
143ebea4dd6SJin Yu 		 */
144ebea4dd6SJin Yu 		device_id = spdk_pci_device_get_subdevice_id(hw->pci_dev);
145ebea4dd6SJin Yu 	} else {
146ebea4dd6SJin Yu 		/* Modern devices: simply use PCI device id, but start from 0x1040. */
147ebea4dd6SJin Yu 		device_id = pci_device_id - 0x1040;
148ebea4dd6SJin Yu 	}
149ebea4dd6SJin Yu 
150ebea4dd6SJin Yu 	if (device_id == device_id_match) {
151ebea4dd6SJin Yu 		hw->is_removing = true;
152ebea4dd6SJin Yu 		return hw->vdev->name;
153ebea4dd6SJin Yu 	}
154ebea4dd6SJin Yu 
155ebea4dd6SJin Yu 	return NULL;
156ebea4dd6SJin Yu }
157ebea4dd6SJin Yu 
158ebea4dd6SJin Yu const char *
virtio_pci_dev_event_process(int fd,uint16_t device_id)159ebea4dd6SJin Yu virtio_pci_dev_event_process(int fd, uint16_t device_id)
160ebea4dd6SJin Yu {
161ebea4dd6SJin Yu 	struct spdk_pci_event event;
162ebea4dd6SJin Yu 	struct virtio_hw *hw, *tmp;
163ebea4dd6SJin Yu 	const char *vdev_name;
164ebea4dd6SJin Yu 
165ebea4dd6SJin Yu 	/* UIO remove handler */
166ebea4dd6SJin Yu 	if (spdk_pci_get_event(fd, &event) > 0) {
167ebea4dd6SJin Yu 		if (event.action == SPDK_UEVENT_REMOVE) {
168ebea4dd6SJin Yu 			hw = virtio_pci_dev_get_by_addr(&event.traddr);
169ebea4dd6SJin Yu 			if (hw == NULL || hw->is_removing) {
170ebea4dd6SJin Yu 				return NULL;
171ebea4dd6SJin Yu 			}
172ebea4dd6SJin Yu 
173ebea4dd6SJin Yu 			vdev_name = virtio_pci_dev_check(hw, device_id);
174ebea4dd6SJin Yu 			if (vdev_name != NULL) {
175ebea4dd6SJin Yu 				return vdev_name;
176ebea4dd6SJin Yu 			}
177ebea4dd6SJin Yu 		}
178ebea4dd6SJin Yu 	}
179ebea4dd6SJin Yu 
180ebea4dd6SJin Yu 	/* VFIO remove handler */
181ebea4dd6SJin Yu 	pthread_mutex_lock(&g_hw_mutex);
182ebea4dd6SJin Yu 	TAILQ_FOREACH_SAFE(hw, &g_virtio_hws, tailq, tmp) {
183ebea4dd6SJin Yu 		if (spdk_pci_device_is_removed(hw->pci_dev) && !hw->is_removing) {
184ebea4dd6SJin Yu 			vdev_name = virtio_pci_dev_check(hw, device_id);
185ebea4dd6SJin Yu 			if (vdev_name != NULL) {
186ebea4dd6SJin Yu 				pthread_mutex_unlock(&g_hw_mutex);
187ebea4dd6SJin Yu 				return vdev_name;
188ebea4dd6SJin Yu 			}
189ebea4dd6SJin Yu 		}
190ebea4dd6SJin Yu 	}
191ebea4dd6SJin Yu 	pthread_mutex_unlock(&g_hw_mutex);
192ebea4dd6SJin Yu 
193ebea4dd6SJin Yu 	return NULL;
194ebea4dd6SJin Yu }
195ebea4dd6SJin Yu 
1969dde5e5cSDariusz Stojaczyk static inline int
check_vq_phys_addr_ok(struct virtqueue * vq)1979dde5e5cSDariusz Stojaczyk check_vq_phys_addr_ok(struct virtqueue *vq)
1989dde5e5cSDariusz Stojaczyk {
1999dde5e5cSDariusz Stojaczyk 	/* Virtio PCI device VIRTIO_PCI_QUEUE_PF register is 32bit,
2009dde5e5cSDariusz Stojaczyk 	 * and only accepts 32 bit page frame number.
2019dde5e5cSDariusz Stojaczyk 	 * Check if the allocated physical memory exceeds 16TB.
2029dde5e5cSDariusz Stojaczyk 	 */
2039dde5e5cSDariusz Stojaczyk 	if ((vq->vq_ring_mem + vq->vq_ring_size - 1) >>
2049dde5e5cSDariusz Stojaczyk 	    (VIRTIO_PCI_QUEUE_ADDR_SHIFT + 32)) {
2059dde5e5cSDariusz Stojaczyk 		SPDK_ERRLOG("vring address shouldn't be above 16TB!\n");
2069dde5e5cSDariusz Stojaczyk 		return 0;
2079dde5e5cSDariusz Stojaczyk 	}
2089dde5e5cSDariusz Stojaczyk 
2099dde5e5cSDariusz Stojaczyk 	return 1;
2109dde5e5cSDariusz Stojaczyk }
2119dde5e5cSDariusz Stojaczyk 
2129dde5e5cSDariusz Stojaczyk static void
free_virtio_hw(struct virtio_hw * hw)2139dde5e5cSDariusz Stojaczyk free_virtio_hw(struct virtio_hw *hw)
2149dde5e5cSDariusz Stojaczyk {
2159dde5e5cSDariusz Stojaczyk 	unsigned i;
2169dde5e5cSDariusz Stojaczyk 
2179dde5e5cSDariusz Stojaczyk 	for (i = 0; i < 6; ++i) {
2189dde5e5cSDariusz Stojaczyk 		if (hw->pci_bar[i].vaddr == NULL) {
2199dde5e5cSDariusz Stojaczyk 			continue;
2209dde5e5cSDariusz Stojaczyk 		}
2219dde5e5cSDariusz Stojaczyk 
2229dde5e5cSDariusz Stojaczyk 		spdk_pci_device_unmap_bar(hw->pci_dev, i, hw->pci_bar[i].vaddr);
2239dde5e5cSDariusz Stojaczyk 	}
2249dde5e5cSDariusz Stojaczyk 
2259dde5e5cSDariusz Stojaczyk 	free(hw);
2269dde5e5cSDariusz Stojaczyk }
2279dde5e5cSDariusz Stojaczyk 
2289dde5e5cSDariusz Stojaczyk static void
pci_dump_json_info(struct virtio_dev * dev,struct spdk_json_write_ctx * w)2291a6dac40SPawel Wodkowski pci_dump_json_info(struct virtio_dev *dev, struct spdk_json_write_ctx *w)
2309dde5e5cSDariusz Stojaczyk {
2319dde5e5cSDariusz Stojaczyk 	struct virtio_hw *hw = dev->ctx;
2329dde5e5cSDariusz Stojaczyk 	struct spdk_pci_addr pci_addr = spdk_pci_device_get_addr((struct spdk_pci_device *)hw->pci_dev);
2339dde5e5cSDariusz Stojaczyk 	char addr[32];
2349dde5e5cSDariusz Stojaczyk 
2359dde5e5cSDariusz Stojaczyk 	spdk_json_write_name(w, "type");
2369dde5e5cSDariusz Stojaczyk 	if (dev->modern) {
2379dde5e5cSDariusz Stojaczyk 		spdk_json_write_string(w, "pci-modern");
2389dde5e5cSDariusz Stojaczyk 	} else {
2399dde5e5cSDariusz Stojaczyk 		spdk_json_write_string(w, "pci-legacy");
2409dde5e5cSDariusz Stojaczyk 	}
2419dde5e5cSDariusz Stojaczyk 
2429dde5e5cSDariusz Stojaczyk 	spdk_pci_addr_fmt(addr, sizeof(addr), &pci_addr);
243ab0f787eSShuhei Matsumoto 	spdk_json_write_named_string(w, "pci_address", addr);
2449dde5e5cSDariusz Stojaczyk }
2459dde5e5cSDariusz Stojaczyk 
2461a6dac40SPawel Wodkowski static void
pci_write_json_config(struct virtio_dev * dev,struct spdk_json_write_ctx * w)2471a6dac40SPawel Wodkowski pci_write_json_config(struct virtio_dev *dev, struct spdk_json_write_ctx *w)
2481a6dac40SPawel Wodkowski {
2491a6dac40SPawel Wodkowski 	struct virtio_hw *hw = dev->ctx;
2501a6dac40SPawel Wodkowski 	struct spdk_pci_addr pci_addr = spdk_pci_device_get_addr(hw->pci_dev);
2511a6dac40SPawel Wodkowski 	char addr[32];
2521a6dac40SPawel Wodkowski 
2531a6dac40SPawel Wodkowski 	spdk_pci_addr_fmt(addr, sizeof(addr), &pci_addr);
2541a6dac40SPawel Wodkowski 
2551a6dac40SPawel Wodkowski 	spdk_json_write_named_string(w, "trtype", "pci");
2561a6dac40SPawel Wodkowski 	spdk_json_write_named_string(w, "traddr", addr);
2571a6dac40SPawel Wodkowski }
2581a6dac40SPawel Wodkowski 
2599dde5e5cSDariusz Stojaczyk static inline void
io_write64_twopart(uint64_t val,uint32_t * lo,uint32_t * hi)2609dde5e5cSDariusz Stojaczyk io_write64_twopart(uint64_t val, uint32_t *lo, uint32_t *hi)
2619dde5e5cSDariusz Stojaczyk {
2629dde5e5cSDariusz Stojaczyk 	spdk_mmio_write_4(lo, val & ((1ULL << 32) - 1));
2639dde5e5cSDariusz Stojaczyk 	spdk_mmio_write_4(hi, val >> 32);
2649dde5e5cSDariusz Stojaczyk }
2659dde5e5cSDariusz Stojaczyk 
26662844ae3SDariusz Stojaczyk static int
modern_read_dev_config(struct virtio_dev * dev,size_t offset,void * dst,int length)2679dde5e5cSDariusz Stojaczyk modern_read_dev_config(struct virtio_dev *dev, size_t offset,
2689dde5e5cSDariusz Stojaczyk 		       void *dst, int length)
2699dde5e5cSDariusz Stojaczyk {
2709dde5e5cSDariusz Stojaczyk 	struct virtio_hw *hw = dev->ctx;
2719dde5e5cSDariusz Stojaczyk 	int i;
2729dde5e5cSDariusz Stojaczyk 	uint8_t *p;
2739dde5e5cSDariusz Stojaczyk 	uint8_t old_gen, new_gen;
2749dde5e5cSDariusz Stojaczyk 
2755ee049eeSJin Yu 	g_thread_virtio_hw = hw;
2769dde5e5cSDariusz Stojaczyk 	do {
2779dde5e5cSDariusz Stojaczyk 		old_gen = spdk_mmio_read_1(&hw->common_cfg->config_generation);
2789dde5e5cSDariusz Stojaczyk 
2799dde5e5cSDariusz Stojaczyk 		p = dst;
2809dde5e5cSDariusz Stojaczyk 		for (i = 0;  i < length; i++) {
2819dde5e5cSDariusz Stojaczyk 			*p++ = spdk_mmio_read_1((uint8_t *)hw->dev_cfg + offset + i);
2829dde5e5cSDariusz Stojaczyk 		}
2839dde5e5cSDariusz Stojaczyk 
2849dde5e5cSDariusz Stojaczyk 		new_gen = spdk_mmio_read_1(&hw->common_cfg->config_generation);
2859dde5e5cSDariusz Stojaczyk 	} while (old_gen != new_gen);
2865ee049eeSJin Yu 	g_thread_virtio_hw = NULL;
28762844ae3SDariusz Stojaczyk 
28862844ae3SDariusz Stojaczyk 	return 0;
2899dde5e5cSDariusz Stojaczyk }
2909dde5e5cSDariusz Stojaczyk 
29162844ae3SDariusz Stojaczyk static int
modern_write_dev_config(struct virtio_dev * dev,size_t offset,const void * src,int length)2929dde5e5cSDariusz Stojaczyk modern_write_dev_config(struct virtio_dev *dev, size_t offset,
2939dde5e5cSDariusz Stojaczyk 			const void *src, int length)
2949dde5e5cSDariusz Stojaczyk {
2959dde5e5cSDariusz Stojaczyk 	struct virtio_hw *hw = dev->ctx;
2969dde5e5cSDariusz Stojaczyk 	int i;
2979dde5e5cSDariusz Stojaczyk 	const uint8_t *p = src;
2989dde5e5cSDariusz Stojaczyk 
2995ee049eeSJin Yu 	g_thread_virtio_hw = hw;
3009dde5e5cSDariusz Stojaczyk 	for (i = 0;  i < length; i++) {
3019dde5e5cSDariusz Stojaczyk 		spdk_mmio_write_1(((uint8_t *)hw->dev_cfg) + offset + i, *p++);
3029dde5e5cSDariusz Stojaczyk 	}
3035ee049eeSJin Yu 	g_thread_virtio_hw = NULL;
30462844ae3SDariusz Stojaczyk 
30562844ae3SDariusz Stojaczyk 	return 0;
3069dde5e5cSDariusz Stojaczyk }
3079dde5e5cSDariusz Stojaczyk 
3089dde5e5cSDariusz Stojaczyk static uint64_t
modern_get_features(struct virtio_dev * dev)3099dde5e5cSDariusz Stojaczyk modern_get_features(struct virtio_dev *dev)
3109dde5e5cSDariusz Stojaczyk {
3119dde5e5cSDariusz Stojaczyk 	struct virtio_hw *hw = dev->ctx;
3129dde5e5cSDariusz Stojaczyk 	uint32_t features_lo, features_hi;
3139dde5e5cSDariusz Stojaczyk 
3145ee049eeSJin Yu 	g_thread_virtio_hw = hw;
3159dde5e5cSDariusz Stojaczyk 	spdk_mmio_write_4(&hw->common_cfg->device_feature_select, 0);
3169dde5e5cSDariusz Stojaczyk 	features_lo = spdk_mmio_read_4(&hw->common_cfg->device_feature);
3179dde5e5cSDariusz Stojaczyk 
3189dde5e5cSDariusz Stojaczyk 	spdk_mmio_write_4(&hw->common_cfg->device_feature_select, 1);
3199dde5e5cSDariusz Stojaczyk 	features_hi = spdk_mmio_read_4(&hw->common_cfg->device_feature);
3205ee049eeSJin Yu 	g_thread_virtio_hw = NULL;
3219dde5e5cSDariusz Stojaczyk 
3229dde5e5cSDariusz Stojaczyk 	return ((uint64_t)features_hi << 32) | features_lo;
3239dde5e5cSDariusz Stojaczyk }
3249dde5e5cSDariusz Stojaczyk 
3259dde5e5cSDariusz Stojaczyk static int
modern_set_features(struct virtio_dev * dev,uint64_t features)3269dde5e5cSDariusz Stojaczyk modern_set_features(struct virtio_dev *dev, uint64_t features)
3279dde5e5cSDariusz Stojaczyk {
3289dde5e5cSDariusz Stojaczyk 	struct virtio_hw *hw = dev->ctx;
3299dde5e5cSDariusz Stojaczyk 
3309dde5e5cSDariusz Stojaczyk 	if ((features & (1ULL << VIRTIO_F_VERSION_1)) == 0) {
3319dde5e5cSDariusz Stojaczyk 		SPDK_ERRLOG("VIRTIO_F_VERSION_1 feature is not enabled.\n");
332eeb1650cSDariusz Stojaczyk 		return -EINVAL;
3339dde5e5cSDariusz Stojaczyk 	}
3349dde5e5cSDariusz Stojaczyk 
3355ee049eeSJin Yu 	g_thread_virtio_hw = hw;
3369dde5e5cSDariusz Stojaczyk 	spdk_mmio_write_4(&hw->common_cfg->guest_feature_select, 0);
3379dde5e5cSDariusz Stojaczyk 	spdk_mmio_write_4(&hw->common_cfg->guest_feature, features & ((1ULL << 32) - 1));
3389dde5e5cSDariusz Stojaczyk 
3399dde5e5cSDariusz Stojaczyk 	spdk_mmio_write_4(&hw->common_cfg->guest_feature_select, 1);
3409dde5e5cSDariusz Stojaczyk 	spdk_mmio_write_4(&hw->common_cfg->guest_feature, features >> 32);
3415ee049eeSJin Yu 	g_thread_virtio_hw = NULL;
3429dde5e5cSDariusz Stojaczyk 
3439dde5e5cSDariusz Stojaczyk 	dev->negotiated_features = features;
3449dde5e5cSDariusz Stojaczyk 
3459dde5e5cSDariusz Stojaczyk 	return 0;
3469dde5e5cSDariusz Stojaczyk }
3479dde5e5cSDariusz Stojaczyk 
3489dde5e5cSDariusz Stojaczyk static void
modern_destruct_dev(struct virtio_dev * vdev)3499dde5e5cSDariusz Stojaczyk modern_destruct_dev(struct virtio_dev *vdev)
3509dde5e5cSDariusz Stojaczyk {
3519dde5e5cSDariusz Stojaczyk 	struct virtio_hw *hw = vdev->ctx;
352305d6458SSeth Howell 	struct spdk_pci_device *pci_dev;
3539dde5e5cSDariusz Stojaczyk 
35479c7744eSJin Yu 	if (hw != NULL) {
355ebea4dd6SJin Yu 		pthread_mutex_lock(&g_hw_mutex);
356ebea4dd6SJin Yu 		TAILQ_REMOVE(&g_virtio_hws, hw, tailq);
357ebea4dd6SJin Yu 		pthread_mutex_unlock(&g_hw_mutex);
358305d6458SSeth Howell 		pci_dev = hw->pci_dev;
3599dde5e5cSDariusz Stojaczyk 		free_virtio_hw(hw);
360305d6458SSeth Howell 		if (pci_dev) {
361305d6458SSeth Howell 			spdk_pci_device_detach(pci_dev);
362305d6458SSeth Howell 		}
36379c7744eSJin Yu 	}
3649dde5e5cSDariusz Stojaczyk }
3659dde5e5cSDariusz Stojaczyk 
3669dde5e5cSDariusz Stojaczyk static uint8_t
modern_get_status(struct virtio_dev * dev)3679dde5e5cSDariusz Stojaczyk modern_get_status(struct virtio_dev *dev)
3689dde5e5cSDariusz Stojaczyk {
3699dde5e5cSDariusz Stojaczyk 	struct virtio_hw *hw = dev->ctx;
3705ee049eeSJin Yu 	uint8_t ret;
3719dde5e5cSDariusz Stojaczyk 
3725ee049eeSJin Yu 	g_thread_virtio_hw = hw;
3735ee049eeSJin Yu 	ret = spdk_mmio_read_1(&hw->common_cfg->device_status);
3745ee049eeSJin Yu 	g_thread_virtio_hw = NULL;
3755ee049eeSJin Yu 
3765ee049eeSJin Yu 	return ret;
3779dde5e5cSDariusz Stojaczyk }
3789dde5e5cSDariusz Stojaczyk 
3799dde5e5cSDariusz Stojaczyk static void
modern_set_status(struct virtio_dev * dev,uint8_t status)3809dde5e5cSDariusz Stojaczyk modern_set_status(struct virtio_dev *dev, uint8_t status)
3819dde5e5cSDariusz Stojaczyk {
3829dde5e5cSDariusz Stojaczyk 	struct virtio_hw *hw = dev->ctx;
3839dde5e5cSDariusz Stojaczyk 
3845ee049eeSJin Yu 	g_thread_virtio_hw = hw;
3859dde5e5cSDariusz Stojaczyk 	spdk_mmio_write_1(&hw->common_cfg->device_status, status);
3865ee049eeSJin Yu 	g_thread_virtio_hw = NULL;
3879dde5e5cSDariusz Stojaczyk }
3889dde5e5cSDariusz Stojaczyk 
3899dde5e5cSDariusz Stojaczyk static uint16_t
modern_get_queue_size(struct virtio_dev * dev,uint16_t queue_id)3900a1a4ce1SDariusz Stojaczyk modern_get_queue_size(struct virtio_dev *dev, uint16_t queue_id)
3919dde5e5cSDariusz Stojaczyk {
3929dde5e5cSDariusz Stojaczyk 	struct virtio_hw *hw = dev->ctx;
3935ee049eeSJin Yu 	uint16_t ret;
3949dde5e5cSDariusz Stojaczyk 
3955ee049eeSJin Yu 	g_thread_virtio_hw = hw;
3969dde5e5cSDariusz Stojaczyk 	spdk_mmio_write_2(&hw->common_cfg->queue_select, queue_id);
3975ee049eeSJin Yu 	ret = spdk_mmio_read_2(&hw->common_cfg->queue_size);
3985ee049eeSJin Yu 	g_thread_virtio_hw = NULL;
3995ee049eeSJin Yu 
4005ee049eeSJin Yu 	return ret;
4019dde5e5cSDariusz Stojaczyk }
4029dde5e5cSDariusz Stojaczyk 
4039dde5e5cSDariusz Stojaczyk static int
modern_setup_queue(struct virtio_dev * dev,struct virtqueue * vq)4049dde5e5cSDariusz Stojaczyk modern_setup_queue(struct virtio_dev *dev, struct virtqueue *vq)
4059dde5e5cSDariusz Stojaczyk {
4069dde5e5cSDariusz Stojaczyk 	struct virtio_hw *hw = dev->ctx;
4079dde5e5cSDariusz Stojaczyk 	uint64_t desc_addr, avail_addr, used_addr;
4089dde5e5cSDariusz Stojaczyk 	uint16_t notify_off;
4093a15b49bSDariusz Stojaczyk 	void *queue_mem;
4103a15b49bSDariusz Stojaczyk 	uint64_t queue_mem_phys_addr;
4113a15b49bSDariusz Stojaczyk 
412e8762a41SDariusz Stojaczyk 	/* To ensure physical address contiguity we make the queue occupy
413e8762a41SDariusz Stojaczyk 	 * only a single hugepage (2MB). As of Virtio 1.0, the queue size
414e8762a41SDariusz Stojaczyk 	 * always falls within this limit.
415e8762a41SDariusz Stojaczyk 	 */
416eecc6dc8SDarek Stojaczyk 	if (vq->vq_ring_size > VALUE_2MB) {
417e8762a41SDariusz Stojaczyk 		return -ENOMEM;
418e8762a41SDariusz Stojaczyk 	}
419e8762a41SDariusz Stojaczyk 
42001103b2eSDarek Stojaczyk 	queue_mem = spdk_zmalloc(vq->vq_ring_size, VALUE_2MB, NULL,
42101103b2eSDarek Stojaczyk 				 SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
4223a15b49bSDariusz Stojaczyk 	if (queue_mem == NULL) {
4233a15b49bSDariusz Stojaczyk 		return -ENOMEM;
4243a15b49bSDariusz Stojaczyk 	}
4253a15b49bSDariusz Stojaczyk 
426a86029b9SDarek Stojaczyk 	queue_mem_phys_addr = spdk_vtophys(queue_mem, NULL);
427a86029b9SDarek Stojaczyk 	if (queue_mem_phys_addr == SPDK_VTOPHYS_ERROR) {
42801103b2eSDarek Stojaczyk 		spdk_free(queue_mem);
429a86029b9SDarek Stojaczyk 		return -EFAULT;
430a86029b9SDarek Stojaczyk 	}
431a86029b9SDarek Stojaczyk 
4323a15b49bSDariusz Stojaczyk 	vq->vq_ring_mem = queue_mem_phys_addr;
4333a15b49bSDariusz Stojaczyk 	vq->vq_ring_virt_mem = queue_mem;
4349dde5e5cSDariusz Stojaczyk 
4359dde5e5cSDariusz Stojaczyk 	if (!check_vq_phys_addr_ok(vq)) {
43601103b2eSDarek Stojaczyk 		spdk_free(queue_mem);
437eeb1650cSDariusz Stojaczyk 		return -ENOMEM;
4389dde5e5cSDariusz Stojaczyk 	}
4399dde5e5cSDariusz Stojaczyk 
4409dde5e5cSDariusz Stojaczyk 	desc_addr = vq->vq_ring_mem;
4419dde5e5cSDariusz Stojaczyk 	avail_addr = desc_addr + vq->vq_nentries * sizeof(struct vring_desc);
4429dde5e5cSDariusz Stojaczyk 	used_addr = (avail_addr + offsetof(struct vring_avail, ring[vq->vq_nentries])
4439dde5e5cSDariusz Stojaczyk 		     + VIRTIO_PCI_VRING_ALIGN - 1) & ~(VIRTIO_PCI_VRING_ALIGN - 1);
4449dde5e5cSDariusz Stojaczyk 
4455ee049eeSJin Yu 	g_thread_virtio_hw = hw;
4469dde5e5cSDariusz Stojaczyk 	spdk_mmio_write_2(&hw->common_cfg->queue_select, vq->vq_queue_index);
4479dde5e5cSDariusz Stojaczyk 
4489dde5e5cSDariusz Stojaczyk 	io_write64_twopart(desc_addr, &hw->common_cfg->queue_desc_lo,
4499dde5e5cSDariusz Stojaczyk 			   &hw->common_cfg->queue_desc_hi);
4509dde5e5cSDariusz Stojaczyk 	io_write64_twopart(avail_addr, &hw->common_cfg->queue_avail_lo,
4519dde5e5cSDariusz Stojaczyk 			   &hw->common_cfg->queue_avail_hi);
4529dde5e5cSDariusz Stojaczyk 	io_write64_twopart(used_addr, &hw->common_cfg->queue_used_lo,
4539dde5e5cSDariusz Stojaczyk 			   &hw->common_cfg->queue_used_hi);
4549dde5e5cSDariusz Stojaczyk 
4559dde5e5cSDariusz Stojaczyk 	notify_off = spdk_mmio_read_2(&hw->common_cfg->queue_notify_off);
4569dde5e5cSDariusz Stojaczyk 	vq->notify_addr = (void *)((uint8_t *)hw->notify_base +
4579dde5e5cSDariusz Stojaczyk 				   notify_off * hw->notify_off_multiplier);
4589dde5e5cSDariusz Stojaczyk 
4599dde5e5cSDariusz Stojaczyk 	spdk_mmio_write_2(&hw->common_cfg->queue_enable, 1);
4605ee049eeSJin Yu 	g_thread_virtio_hw = NULL;
4619dde5e5cSDariusz Stojaczyk 
4622172c432STomasz Zawadzki 	SPDK_DEBUGLOG(virtio_pci, "queue %"PRIu16" addresses:\n", vq->vq_queue_index);
4632172c432STomasz Zawadzki 	SPDK_DEBUGLOG(virtio_pci, "\t desc_addr: %" PRIx64 "\n", desc_addr);
4642172c432STomasz Zawadzki 	SPDK_DEBUGLOG(virtio_pci, "\t aval_addr: %" PRIx64 "\n", avail_addr);
4652172c432STomasz Zawadzki 	SPDK_DEBUGLOG(virtio_pci, "\t used_addr: %" PRIx64 "\n", used_addr);
4662172c432STomasz Zawadzki 	SPDK_DEBUGLOG(virtio_pci, "\t notify addr: %p (notify offset: %"PRIu16")\n",
4679dde5e5cSDariusz Stojaczyk 		      vq->notify_addr, notify_off);
4689dde5e5cSDariusz Stojaczyk 
4699dde5e5cSDariusz Stojaczyk 	return 0;
4709dde5e5cSDariusz Stojaczyk }
4719dde5e5cSDariusz Stojaczyk 
4729dde5e5cSDariusz Stojaczyk static void
modern_del_queue(struct virtio_dev * dev,struct virtqueue * vq)4739dde5e5cSDariusz Stojaczyk modern_del_queue(struct virtio_dev *dev, struct virtqueue *vq)
4749dde5e5cSDariusz Stojaczyk {
4759dde5e5cSDariusz Stojaczyk 	struct virtio_hw *hw = dev->ctx;
4769dde5e5cSDariusz Stojaczyk 
4775ee049eeSJin Yu 	g_thread_virtio_hw = hw;
4789dde5e5cSDariusz Stojaczyk 	spdk_mmio_write_2(&hw->common_cfg->queue_select, vq->vq_queue_index);
4799dde5e5cSDariusz Stojaczyk 
4809dde5e5cSDariusz Stojaczyk 	io_write64_twopart(0, &hw->common_cfg->queue_desc_lo,
4819dde5e5cSDariusz Stojaczyk 			   &hw->common_cfg->queue_desc_hi);
4829dde5e5cSDariusz Stojaczyk 	io_write64_twopart(0, &hw->common_cfg->queue_avail_lo,
4839dde5e5cSDariusz Stojaczyk 			   &hw->common_cfg->queue_avail_hi);
4849dde5e5cSDariusz Stojaczyk 	io_write64_twopart(0, &hw->common_cfg->queue_used_lo,
4859dde5e5cSDariusz Stojaczyk 			   &hw->common_cfg->queue_used_hi);
4869dde5e5cSDariusz Stojaczyk 
4879dde5e5cSDariusz Stojaczyk 	spdk_mmio_write_2(&hw->common_cfg->queue_enable, 0);
4885ee049eeSJin Yu 	g_thread_virtio_hw = NULL;
4893a15b49bSDariusz Stojaczyk 
49001103b2eSDarek Stojaczyk 	spdk_free(vq->vq_ring_virt_mem);
4919dde5e5cSDariusz Stojaczyk }
4929dde5e5cSDariusz Stojaczyk 
4939dde5e5cSDariusz Stojaczyk static void
modern_notify_queue(struct virtio_dev * dev,struct virtqueue * vq)4949dde5e5cSDariusz Stojaczyk modern_notify_queue(struct virtio_dev *dev, struct virtqueue *vq)
4959dde5e5cSDariusz Stojaczyk {
4965ee049eeSJin Yu 	g_thread_virtio_hw = dev->ctx;
4979dde5e5cSDariusz Stojaczyk 	spdk_mmio_write_2(vq->notify_addr, vq->vq_queue_index);
4985ee049eeSJin Yu 	g_thread_virtio_hw = NULL;
4999dde5e5cSDariusz Stojaczyk }
5009dde5e5cSDariusz Stojaczyk 
5019dde5e5cSDariusz Stojaczyk static const struct virtio_dev_ops modern_ops = {
5029dde5e5cSDariusz Stojaczyk 	.read_dev_cfg	= modern_read_dev_config,
5039dde5e5cSDariusz Stojaczyk 	.write_dev_cfg	= modern_write_dev_config,
5049dde5e5cSDariusz Stojaczyk 	.get_status	= modern_get_status,
5059dde5e5cSDariusz Stojaczyk 	.set_status	= modern_set_status,
5069dde5e5cSDariusz Stojaczyk 	.get_features	= modern_get_features,
5079dde5e5cSDariusz Stojaczyk 	.set_features	= modern_set_features,
5089dde5e5cSDariusz Stojaczyk 	.destruct_dev	= modern_destruct_dev,
5090a1a4ce1SDariusz Stojaczyk 	.get_queue_size	= modern_get_queue_size,
5109dde5e5cSDariusz Stojaczyk 	.setup_queue	= modern_setup_queue,
5119dde5e5cSDariusz Stojaczyk 	.del_queue	= modern_del_queue,
5129dde5e5cSDariusz Stojaczyk 	.notify_queue	= modern_notify_queue,
5131a6dac40SPawel Wodkowski 	.dump_json_info = pci_dump_json_info,
5141a6dac40SPawel Wodkowski 	.write_json_config = pci_write_json_config,
5159dde5e5cSDariusz Stojaczyk };
5169dde5e5cSDariusz Stojaczyk 
5179dde5e5cSDariusz Stojaczyk static void *
get_cfg_addr(struct virtio_hw * hw,struct virtio_pci_cap * cap)5189dde5e5cSDariusz Stojaczyk get_cfg_addr(struct virtio_hw *hw, struct virtio_pci_cap *cap)
5199dde5e5cSDariusz Stojaczyk {
5209dde5e5cSDariusz Stojaczyk 	uint8_t  bar    = cap->bar;
5219dde5e5cSDariusz Stojaczyk 	uint32_t length = cap->length;
5229dde5e5cSDariusz Stojaczyk 	uint32_t offset = cap->offset;
5239dde5e5cSDariusz Stojaczyk 
5249dde5e5cSDariusz Stojaczyk 	if (bar > 5) {
5259dde5e5cSDariusz Stojaczyk 		SPDK_ERRLOG("invalid bar: %"PRIu8"\n", bar);
5269dde5e5cSDariusz Stojaczyk 		return NULL;
5279dde5e5cSDariusz Stojaczyk 	}
5289dde5e5cSDariusz Stojaczyk 
5299dde5e5cSDariusz Stojaczyk 	if (offset + length < offset) {
5309dde5e5cSDariusz Stojaczyk 		SPDK_ERRLOG("offset(%"PRIu32") + length(%"PRIu32") overflows\n",
5319dde5e5cSDariusz Stojaczyk 			    offset, length);
5329dde5e5cSDariusz Stojaczyk 		return NULL;
5339dde5e5cSDariusz Stojaczyk 	}
5349dde5e5cSDariusz Stojaczyk 
5359dde5e5cSDariusz Stojaczyk 	if (offset + length > hw->pci_bar[bar].len) {
5369dde5e5cSDariusz Stojaczyk 		SPDK_ERRLOG("invalid cap: overflows bar space: %"PRIu32" > %"PRIu32"\n",
5379dde5e5cSDariusz Stojaczyk 			    offset + length, hw->pci_bar[bar].len);
5389dde5e5cSDariusz Stojaczyk 		return NULL;
5399dde5e5cSDariusz Stojaczyk 	}
5409dde5e5cSDariusz Stojaczyk 
5419dde5e5cSDariusz Stojaczyk 	if (hw->pci_bar[bar].vaddr == NULL) {
5429dde5e5cSDariusz Stojaczyk 		SPDK_ERRLOG("bar %"PRIu8" base addr is NULL\n", bar);
5439dde5e5cSDariusz Stojaczyk 		return NULL;
5449dde5e5cSDariusz Stojaczyk 	}
5459dde5e5cSDariusz Stojaczyk 
5469dde5e5cSDariusz Stojaczyk 	return hw->pci_bar[bar].vaddr + offset;
5479dde5e5cSDariusz Stojaczyk }
5489dde5e5cSDariusz Stojaczyk 
5499dde5e5cSDariusz Stojaczyk static int
virtio_read_caps(struct virtio_hw * hw)5509dde5e5cSDariusz Stojaczyk virtio_read_caps(struct virtio_hw *hw)
5519dde5e5cSDariusz Stojaczyk {
5529dde5e5cSDariusz Stojaczyk 	uint8_t pos;
5539dde5e5cSDariusz Stojaczyk 	struct virtio_pci_cap cap;
5549dde5e5cSDariusz Stojaczyk 	int ret;
5559dde5e5cSDariusz Stojaczyk 
5569dde5e5cSDariusz Stojaczyk 	ret = spdk_pci_device_cfg_read(hw->pci_dev, &pos, 1, PCI_CAPABILITY_LIST);
5579dde5e5cSDariusz Stojaczyk 	if (ret < 0) {
5582172c432STomasz Zawadzki 		SPDK_DEBUGLOG(virtio_pci, "failed to read pci capability list\n");
559eeb1650cSDariusz Stojaczyk 		return ret;
5609dde5e5cSDariusz Stojaczyk 	}
5619dde5e5cSDariusz Stojaczyk 
5629dde5e5cSDariusz Stojaczyk 	while (pos) {
5639dde5e5cSDariusz Stojaczyk 		ret = spdk_pci_device_cfg_read(hw->pci_dev, &cap, sizeof(cap), pos);
5649dde5e5cSDariusz Stojaczyk 		if (ret < 0) {
5659dde5e5cSDariusz Stojaczyk 			SPDK_ERRLOG("failed to read pci cap at pos: %"PRIx8"\n", pos);
5669dde5e5cSDariusz Stojaczyk 			break;
5679dde5e5cSDariusz Stojaczyk 		}
5689dde5e5cSDariusz Stojaczyk 
5699dde5e5cSDariusz Stojaczyk 		if (cap.cap_vndr == PCI_CAP_ID_MSIX) {
5709dde5e5cSDariusz Stojaczyk 			hw->use_msix = 1;
5719dde5e5cSDariusz Stojaczyk 		}
5729dde5e5cSDariusz Stojaczyk 
5739dde5e5cSDariusz Stojaczyk 		if (cap.cap_vndr != PCI_CAP_ID_VNDR) {
5742172c432STomasz Zawadzki 			SPDK_DEBUGLOG(virtio_pci,
5759dde5e5cSDariusz Stojaczyk 				      "[%2"PRIx8"] skipping non VNDR cap id: %02"PRIx8"\n",
5769dde5e5cSDariusz Stojaczyk 				      pos, cap.cap_vndr);
5779dde5e5cSDariusz Stojaczyk 			goto next;
5789dde5e5cSDariusz Stojaczyk 		}
5799dde5e5cSDariusz Stojaczyk 
5802172c432STomasz Zawadzki 		SPDK_DEBUGLOG(virtio_pci,
5819dde5e5cSDariusz Stojaczyk 			      "[%2"PRIx8"] cfg type: %"PRIu8", bar: %"PRIu8", offset: %04"PRIx32", len: %"PRIu32"\n",
5829dde5e5cSDariusz Stojaczyk 			      pos, cap.cfg_type, cap.bar, cap.offset, cap.length);
5839dde5e5cSDariusz Stojaczyk 
5849dde5e5cSDariusz Stojaczyk 		switch (cap.cfg_type) {
5859dde5e5cSDariusz Stojaczyk 		case VIRTIO_PCI_CAP_COMMON_CFG:
5869dde5e5cSDariusz Stojaczyk 			hw->common_cfg = get_cfg_addr(hw, &cap);
5879dde5e5cSDariusz Stojaczyk 			break;
5889dde5e5cSDariusz Stojaczyk 		case VIRTIO_PCI_CAP_NOTIFY_CFG:
5899dde5e5cSDariusz Stojaczyk 			spdk_pci_device_cfg_read(hw->pci_dev, &hw->notify_off_multiplier,
5909dde5e5cSDariusz Stojaczyk 						 4, pos + sizeof(cap));
5919dde5e5cSDariusz Stojaczyk 			hw->notify_base = get_cfg_addr(hw, &cap);
5929dde5e5cSDariusz Stojaczyk 			break;
5939dde5e5cSDariusz Stojaczyk 		case VIRTIO_PCI_CAP_DEVICE_CFG:
5949dde5e5cSDariusz Stojaczyk 			hw->dev_cfg = get_cfg_addr(hw, &cap);
5959dde5e5cSDariusz Stojaczyk 			break;
5969dde5e5cSDariusz Stojaczyk 		case VIRTIO_PCI_CAP_ISR_CFG:
5979dde5e5cSDariusz Stojaczyk 			hw->isr = get_cfg_addr(hw, &cap);
5989dde5e5cSDariusz Stojaczyk 			break;
5999dde5e5cSDariusz Stojaczyk 		}
6009dde5e5cSDariusz Stojaczyk 
6019dde5e5cSDariusz Stojaczyk next:
6029dde5e5cSDariusz Stojaczyk 		pos = cap.cap_next;
6039dde5e5cSDariusz Stojaczyk 	}
6049dde5e5cSDariusz Stojaczyk 
6059dde5e5cSDariusz Stojaczyk 	if (hw->common_cfg == NULL || hw->notify_base == NULL ||
6069dde5e5cSDariusz Stojaczyk 	    hw->dev_cfg == NULL    || hw->isr == NULL) {
6072172c432STomasz Zawadzki 		SPDK_DEBUGLOG(virtio_pci, "no modern virtio pci device found.\n");
608eeb1650cSDariusz Stojaczyk 		if (ret < 0) {
609eeb1650cSDariusz Stojaczyk 			return ret;
610eeb1650cSDariusz Stojaczyk 		} else {
611eeb1650cSDariusz Stojaczyk 			return -EINVAL;
612eeb1650cSDariusz Stojaczyk 		}
6139dde5e5cSDariusz Stojaczyk 	}
6149dde5e5cSDariusz Stojaczyk 
6152172c432STomasz Zawadzki 	SPDK_DEBUGLOG(virtio_pci, "found modern virtio pci device.\n");
6169dde5e5cSDariusz Stojaczyk 
6172172c432STomasz Zawadzki 	SPDK_DEBUGLOG(virtio_pci, "common cfg mapped at: %p\n", hw->common_cfg);
6182172c432STomasz Zawadzki 	SPDK_DEBUGLOG(virtio_pci, "device cfg mapped at: %p\n", hw->dev_cfg);
6192172c432STomasz Zawadzki 	SPDK_DEBUGLOG(virtio_pci, "isr cfg mapped at: %p\n", hw->isr);
6202172c432STomasz Zawadzki 	SPDK_DEBUGLOG(virtio_pci, "notify base: %p, notify off multiplier: %u\n",
6219dde5e5cSDariusz Stojaczyk 		      hw->notify_base, hw->notify_off_multiplier);
6229dde5e5cSDariusz Stojaczyk 
6239dde5e5cSDariusz Stojaczyk 	return 0;
6249dde5e5cSDariusz Stojaczyk }
6259dde5e5cSDariusz Stojaczyk 
6269dde5e5cSDariusz Stojaczyk static int
virtio_pci_dev_probe(struct spdk_pci_device * pci_dev,struct virtio_pci_probe_ctx * ctx)6275c0c104bSDariusz Stojaczyk virtio_pci_dev_probe(struct spdk_pci_device *pci_dev, struct virtio_pci_probe_ctx *ctx)
6289dde5e5cSDariusz Stojaczyk {
6299dde5e5cSDariusz Stojaczyk 	struct virtio_hw *hw;
6309dde5e5cSDariusz Stojaczyk 	uint8_t *bar_vaddr;
6319dde5e5cSDariusz Stojaczyk 	uint64_t bar_paddr, bar_len;
6329dde5e5cSDariusz Stojaczyk 	int rc;
6339dde5e5cSDariusz Stojaczyk 	unsigned i;
63448245f4bSDariusz Stojaczyk 	char bdf[32];
63548245f4bSDariusz Stojaczyk 	struct spdk_pci_addr addr;
63648245f4bSDariusz Stojaczyk 
63748245f4bSDariusz Stojaczyk 	addr = spdk_pci_device_get_addr(pci_dev);
63848245f4bSDariusz Stojaczyk 	rc = spdk_pci_addr_fmt(bdf, sizeof(bdf), &addr);
63948245f4bSDariusz Stojaczyk 	if (rc != 0) {
64048245f4bSDariusz Stojaczyk 		SPDK_ERRLOG("Ignoring a device with non-parseable PCI address\n");
64148245f4bSDariusz Stojaczyk 		return -1;
64248245f4bSDariusz Stojaczyk 	}
6439dde5e5cSDariusz Stojaczyk 
6449dde5e5cSDariusz Stojaczyk 	hw = calloc(1, sizeof(*hw));
6459dde5e5cSDariusz Stojaczyk 	if (hw == NULL) {
64648245f4bSDariusz Stojaczyk 		SPDK_ERRLOG("%s: calloc failed\n", bdf);
6479dde5e5cSDariusz Stojaczyk 		return -1;
6489dde5e5cSDariusz Stojaczyk 	}
6499dde5e5cSDariusz Stojaczyk 
6509dde5e5cSDariusz Stojaczyk 	hw->pci_dev = pci_dev;
6519dde5e5cSDariusz Stojaczyk 
6529dde5e5cSDariusz Stojaczyk 	for (i = 0; i < 6; ++i) {
6539dde5e5cSDariusz Stojaczyk 		rc = spdk_pci_device_map_bar(pci_dev, i, (void *) &bar_vaddr, &bar_paddr,
6549dde5e5cSDariusz Stojaczyk 					     &bar_len);
6559dde5e5cSDariusz Stojaczyk 		if (rc != 0) {
65648245f4bSDariusz Stojaczyk 			SPDK_ERRLOG("%s: failed to memmap PCI BAR %u\n", bdf, i);
6579dde5e5cSDariusz Stojaczyk 			free_virtio_hw(hw);
6589dde5e5cSDariusz Stojaczyk 			return -1;
6599dde5e5cSDariusz Stojaczyk 		}
6609dde5e5cSDariusz Stojaczyk 
6619dde5e5cSDariusz Stojaczyk 		hw->pci_bar[i].vaddr = bar_vaddr;
6629dde5e5cSDariusz Stojaczyk 		hw->pci_bar[i].len = bar_len;
6639dde5e5cSDariusz Stojaczyk 	}
6649dde5e5cSDariusz Stojaczyk 
6659dde5e5cSDariusz Stojaczyk 	/* Virtio PCI caps exist only on modern PCI devices.
6669dde5e5cSDariusz Stojaczyk 	 * Legacy devices are not supported.
6679dde5e5cSDariusz Stojaczyk 	 */
6689dde5e5cSDariusz Stojaczyk 	if (virtio_read_caps(hw) != 0) {
66948245f4bSDariusz Stojaczyk 		SPDK_NOTICELOG("Ignoring legacy PCI device at %s\n", bdf);
6709dde5e5cSDariusz Stojaczyk 		free_virtio_hw(hw);
6719dde5e5cSDariusz Stojaczyk 		return -1;
6729dde5e5cSDariusz Stojaczyk 	}
6739dde5e5cSDariusz Stojaczyk 
6745c0c104bSDariusz Stojaczyk 	rc = ctx->enum_cb((struct virtio_pci_ctx *)hw, ctx->enum_ctx);
6759dde5e5cSDariusz Stojaczyk 	if (rc != 0) {
6769dde5e5cSDariusz Stojaczyk 		free_virtio_hw(hw);
677ebea4dd6SJin Yu 		return rc;
6789dde5e5cSDariusz Stojaczyk 	}
6799dde5e5cSDariusz Stojaczyk 
6805ee049eeSJin Yu 	if (g_sigset != true) {
6815ee049eeSJin Yu 		spdk_pci_register_error_handler(virtio_pci_dev_sigbus_handler,
6825ee049eeSJin Yu 						NULL);
6835ee049eeSJin Yu 		g_sigset = true;
6845ee049eeSJin Yu 	}
6855ee049eeSJin Yu 
686ebea4dd6SJin Yu 	pthread_mutex_lock(&g_hw_mutex);
687ebea4dd6SJin Yu 	TAILQ_INSERT_TAIL(&g_virtio_hws, hw, tailq);
688ebea4dd6SJin Yu 	pthread_mutex_unlock(&g_hw_mutex);
689ebea4dd6SJin Yu 
690ebea4dd6SJin Yu 	return 0;
6919dde5e5cSDariusz Stojaczyk }
6929dde5e5cSDariusz Stojaczyk 
6939dde5e5cSDariusz Stojaczyk static int
virtio_pci_dev_probe_cb(void * probe_ctx,struct spdk_pci_device * pci_dev)694724c9aa7SDariusz Stojaczyk virtio_pci_dev_probe_cb(void *probe_ctx, struct spdk_pci_device *pci_dev)
6959dde5e5cSDariusz Stojaczyk {
696724c9aa7SDariusz Stojaczyk 	struct virtio_pci_probe_ctx *ctx = probe_ctx;
6979dde5e5cSDariusz Stojaczyk 	uint16_t pci_device_id = spdk_pci_device_get_device_id(pci_dev);
6984c890c31SJin Yu 	uint16_t device_id;
6999dde5e5cSDariusz Stojaczyk 
7004c890c31SJin Yu 	if (pci_device_id < 0x1000 || pci_device_id > 0x107f) {
7014c890c31SJin Yu 		SPDK_ERRLOG("Probe device is not a virtio device\n");
7024c890c31SJin Yu 		return 1;
7034c890c31SJin Yu 	}
7044c890c31SJin Yu 
7054c890c31SJin Yu 	if (pci_device_id < 0x1040) {
7064c890c31SJin Yu 		/* Transitional devices: use the PCI subsystem device id as
7074c890c31SJin Yu 		 * virtio device id, same as legacy driver always did.
7084c890c31SJin Yu 		 */
7094c890c31SJin Yu 		device_id = spdk_pci_device_get_subdevice_id(pci_dev);
7104c890c31SJin Yu 	} else {
7114c890c31SJin Yu 		/* Modern devices: simply use PCI device id, but start from 0x1040. */
7124c890c31SJin Yu 		device_id = pci_device_id - 0x1040;
7134c890c31SJin Yu 	}
7144c890c31SJin Yu 
7154c890c31SJin Yu 	if (device_id != ctx->device_id) {
7169dde5e5cSDariusz Stojaczyk 		return 1;
7179dde5e5cSDariusz Stojaczyk 	}
7189dde5e5cSDariusz Stojaczyk 
7195c0c104bSDariusz Stojaczyk 	return virtio_pci_dev_probe(pci_dev, ctx);
7209dde5e5cSDariusz Stojaczyk }
7219dde5e5cSDariusz Stojaczyk 
7229dde5e5cSDariusz Stojaczyk int
virtio_pci_dev_enumerate(virtio_pci_create_cb enum_cb,void * enum_ctx,uint16_t pci_device_id)7235c0c104bSDariusz Stojaczyk virtio_pci_dev_enumerate(virtio_pci_create_cb enum_cb, void *enum_ctx,
7245c0c104bSDariusz Stojaczyk 			 uint16_t pci_device_id)
7259dde5e5cSDariusz Stojaczyk {
726724c9aa7SDariusz Stojaczyk 	struct virtio_pci_probe_ctx ctx;
727724c9aa7SDariusz Stojaczyk 
7289dde5e5cSDariusz Stojaczyk 	if (!spdk_process_is_primary()) {
7299dde5e5cSDariusz Stojaczyk 		SPDK_WARNLOG("virtio_pci secondary process support is not implemented yet.\n");
7309dde5e5cSDariusz Stojaczyk 		return 0;
7319dde5e5cSDariusz Stojaczyk 	}
7329dde5e5cSDariusz Stojaczyk 
733724c9aa7SDariusz Stojaczyk 	ctx.enum_cb = enum_cb;
7345c0c104bSDariusz Stojaczyk 	ctx.enum_ctx = enum_ctx;
735724c9aa7SDariusz Stojaczyk 	ctx.device_id = pci_device_id;
736724c9aa7SDariusz Stojaczyk 
73776a7517cSDarek Stojaczyk 	return spdk_pci_enumerate(spdk_pci_virtio_get_driver(),
73876a7517cSDarek Stojaczyk 				  virtio_pci_dev_probe_cb, &ctx);
7399dde5e5cSDariusz Stojaczyk }
7409dde5e5cSDariusz Stojaczyk 
7419dde5e5cSDariusz Stojaczyk int
virtio_pci_dev_attach(virtio_pci_create_cb enum_cb,void * enum_ctx,uint16_t device_id,struct spdk_pci_addr * pci_address)74237fb6222SDariusz Stojaczyk virtio_pci_dev_attach(virtio_pci_create_cb enum_cb, void *enum_ctx,
7434c890c31SJin Yu 		      uint16_t device_id, struct spdk_pci_addr *pci_address)
74437fb6222SDariusz Stojaczyk {
74537fb6222SDariusz Stojaczyk 	struct virtio_pci_probe_ctx ctx;
74637fb6222SDariusz Stojaczyk 
74737fb6222SDariusz Stojaczyk 	if (!spdk_process_is_primary()) {
74837fb6222SDariusz Stojaczyk 		SPDK_WARNLOG("virtio_pci secondary process support is not implemented yet.\n");
74937fb6222SDariusz Stojaczyk 		return 0;
75037fb6222SDariusz Stojaczyk 	}
75137fb6222SDariusz Stojaczyk 
75237fb6222SDariusz Stojaczyk 	ctx.enum_cb = enum_cb;
75337fb6222SDariusz Stojaczyk 	ctx.enum_ctx = enum_ctx;
7544c890c31SJin Yu 	ctx.device_id = device_id;
75537fb6222SDariusz Stojaczyk 
75676a7517cSDarek Stojaczyk 	return spdk_pci_device_attach(spdk_pci_virtio_get_driver(),
75776a7517cSDarek Stojaczyk 				      virtio_pci_dev_probe_cb, &ctx, pci_address);
75837fb6222SDariusz Stojaczyk }
75937fb6222SDariusz Stojaczyk 
76037fb6222SDariusz Stojaczyk int
virtio_pci_dev_init(struct virtio_dev * vdev,const char * name,struct virtio_pci_ctx * pci_ctx)7619dde5e5cSDariusz Stojaczyk virtio_pci_dev_init(struct virtio_dev *vdev, const char *name,
7629dde5e5cSDariusz Stojaczyk 		    struct virtio_pci_ctx *pci_ctx)
7639dde5e5cSDariusz Stojaczyk {
7649dde5e5cSDariusz Stojaczyk 	int rc;
765ebea4dd6SJin Yu 	struct virtio_hw *hw = (struct virtio_hw *)pci_ctx;
7669dde5e5cSDariusz Stojaczyk 
767fb12bbecSDariusz Stojaczyk 	rc = virtio_dev_construct(vdev, name, &modern_ops, pci_ctx);
7689dde5e5cSDariusz Stojaczyk 	if (rc != 0) {
769eeb1650cSDariusz Stojaczyk 		return rc;
7709dde5e5cSDariusz Stojaczyk 	}
7719dde5e5cSDariusz Stojaczyk 
7729dde5e5cSDariusz Stojaczyk 	vdev->is_hw = 1;
7739dde5e5cSDariusz Stojaczyk 	vdev->modern = 1;
774ebea4dd6SJin Yu 	hw->vdev = vdev;
7759dde5e5cSDariusz Stojaczyk 
7769dde5e5cSDariusz Stojaczyk 	return 0;
7779dde5e5cSDariusz Stojaczyk }
7789dde5e5cSDariusz Stojaczyk 
7792172c432STomasz Zawadzki SPDK_LOG_REGISTER_COMPONENT(virtio_pci)
780