xref: /kvm-unit-tests/lib/pci.c (revision 647d2ab7c99b29e7ad6c48494a7322bbb09f4c80)
1456c55bcSAndrew Jones /*
2456c55bcSAndrew Jones  * Copyright (C) 2013, Red Hat Inc, Michael S. Tsirkin <mst@redhat.com>
3456c55bcSAndrew Jones  *
4456c55bcSAndrew Jones  * This work is licensed under the terms of the GNU LGPL, version 2.
5456c55bcSAndrew Jones  */
64932b58aSMichael S. Tsirkin #include <linux/pci_regs.h>
74932b58aSMichael S. Tsirkin #include "pci.h"
8456c55bcSAndrew Jones #include "asm/pci.h"
94932b58aSMichael S. Tsirkin 
10e1cad5c8SAlexander Gordeev bool pci_dev_exists(pcidevaddr_t dev)
11e1cad5c8SAlexander Gordeev {
12e1cad5c8SAlexander Gordeev 	return (pci_config_readw(dev, PCI_VENDOR_ID) != 0xffff &&
13e1cad5c8SAlexander Gordeev 		pci_config_readw(dev, PCI_DEVICE_ID) != 0xffff);
14e1cad5c8SAlexander Gordeev }
15e1cad5c8SAlexander Gordeev 
164932b58aSMichael S. Tsirkin /* Scan bus look for a specific device. Only bus 0 scanned for now. */
174932b58aSMichael S. Tsirkin pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id)
184932b58aSMichael S. Tsirkin {
19ebb58e7eSAlexander Gordeev 	pcidevaddr_t dev;
20ebb58e7eSAlexander Gordeev 
214932b58aSMichael S. Tsirkin 	for (dev = 0; dev < 256; ++dev) {
22d8369c77SAlexander Gordeev 		if (pci_config_readw(dev, PCI_VENDOR_ID) == vendor_id &&
23d8369c77SAlexander Gordeev 		    pci_config_readw(dev, PCI_DEVICE_ID) == device_id)
244932b58aSMichael S. Tsirkin 			return dev;
254932b58aSMichael S. Tsirkin 	}
26ebb58e7eSAlexander Gordeev 
274932b58aSMichael S. Tsirkin 	return PCIDEVADDR_INVALID;
284932b58aSMichael S. Tsirkin }
294932b58aSMichael S. Tsirkin 
3033d78b07SAlexander Gordeev uint32_t pci_bar_mask(uint32_t bar)
312455ef20SAlexander Gordeev {
322455ef20SAlexander Gordeev 	return (bar & PCI_BASE_ADDRESS_SPACE_IO) ?
332455ef20SAlexander Gordeev 		PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK;
342455ef20SAlexander Gordeev }
352455ef20SAlexander Gordeev 
3633d78b07SAlexander Gordeev uint32_t pci_bar_get(pcidevaddr_t dev, int bar_num)
377aa83307SAlexander Gordeev {
387aa83307SAlexander Gordeev 	return pci_config_readl(dev, PCI_BASE_ADDRESS_0 + bar_num * 4);
397aa83307SAlexander Gordeev }
407aa83307SAlexander Gordeev 
41647f92c7SAlexander Gordeev phys_addr_t pci_bar_get_addr(pcidevaddr_t dev, int bar_num)
424932b58aSMichael S. Tsirkin {
437aa83307SAlexander Gordeev 	uint32_t bar = pci_bar_get(dev, bar_num);
442455ef20SAlexander Gordeev 	uint32_t mask = pci_bar_mask(bar);
452455ef20SAlexander Gordeev 	uint64_t addr = bar & mask;
46*647d2ab7SAlexander Gordeev 	phys_addr_t phys_addr;
47ebb58e7eSAlexander Gordeev 
482455ef20SAlexander Gordeev 	if (pci_bar_is64(dev, bar_num))
492455ef20SAlexander Gordeev 		addr |= (uint64_t)pci_bar_get(dev, bar_num + 1) << 32;
502455ef20SAlexander Gordeev 
51*647d2ab7SAlexander Gordeev 	phys_addr = pci_translate_addr(dev, addr);
52*647d2ab7SAlexander Gordeev 	assert(phys_addr != INVALID_PHYS_ADDR);
53*647d2ab7SAlexander Gordeev 
54*647d2ab7SAlexander Gordeev 	return phys_addr;
552455ef20SAlexander Gordeev }
562455ef20SAlexander Gordeev 
57647f92c7SAlexander Gordeev void pci_bar_set_addr(pcidevaddr_t dev, int bar_num, phys_addr_t addr)
58647f92c7SAlexander Gordeev {
59647f92c7SAlexander Gordeev 	int off = PCI_BASE_ADDRESS_0 + bar_num * 4;
60647f92c7SAlexander Gordeev 
61647f92c7SAlexander Gordeev 	pci_config_writel(dev, off, (uint32_t)addr);
62647f92c7SAlexander Gordeev 
63647f92c7SAlexander Gordeev 	if (pci_bar_is64(dev, bar_num))
64647f92c7SAlexander Gordeev 		pci_config_writel(dev, off + 4, (uint32_t)(addr >> 32));
65647f92c7SAlexander Gordeev }
66647f92c7SAlexander Gordeev 
672455ef20SAlexander Gordeev /*
682455ef20SAlexander Gordeev  * To determine the amount of address space needed by a PCI device,
692455ef20SAlexander Gordeev  * one must save the original value of the BAR, write a value of
702455ef20SAlexander Gordeev  * all 1's to the register, and then read it back. The amount of
712455ef20SAlexander Gordeev  * memory can be then determined by masking the information bits,
722455ef20SAlexander Gordeev  * performing a bitwise NOT, and incrementing the value by 1.
732455ef20SAlexander Gordeev  *
742455ef20SAlexander Gordeev  * The following pci_bar_size_helper() and pci_bar_size() functions
752455ef20SAlexander Gordeev  * implement the algorithm.
762455ef20SAlexander Gordeev  */
772455ef20SAlexander Gordeev static uint32_t pci_bar_size_helper(pcidevaddr_t dev, int bar_num)
782455ef20SAlexander Gordeev {
792455ef20SAlexander Gordeev 	int off = PCI_BASE_ADDRESS_0 + bar_num * 4;
802455ef20SAlexander Gordeev 	uint32_t bar, val;
812455ef20SAlexander Gordeev 
822455ef20SAlexander Gordeev 	bar = pci_config_readl(dev, off);
832455ef20SAlexander Gordeev 	pci_config_writel(dev, off, ~0u);
842455ef20SAlexander Gordeev 	val = pci_config_readl(dev, off);
852455ef20SAlexander Gordeev 	pci_config_writel(dev, off, bar);
862455ef20SAlexander Gordeev 
872455ef20SAlexander Gordeev 	return val;
882455ef20SAlexander Gordeev }
892455ef20SAlexander Gordeev 
902455ef20SAlexander Gordeev phys_addr_t pci_bar_size(pcidevaddr_t dev, int bar_num)
912455ef20SAlexander Gordeev {
922455ef20SAlexander Gordeev 	uint32_t bar, size;
932455ef20SAlexander Gordeev 
942455ef20SAlexander Gordeev 	size = pci_bar_size_helper(dev, bar_num);
952455ef20SAlexander Gordeev 	if (!size)
962455ef20SAlexander Gordeev 		return 0;
972455ef20SAlexander Gordeev 
982455ef20SAlexander Gordeev 	bar = pci_bar_get(dev, bar_num);
992455ef20SAlexander Gordeev 	size &= pci_bar_mask(bar);
1002455ef20SAlexander Gordeev 
1012455ef20SAlexander Gordeev 	if (pci_bar_is64(dev, bar_num)) {
1022455ef20SAlexander Gordeev 		phys_addr_t size64 = pci_bar_size_helper(dev, bar_num + 1);
1032455ef20SAlexander Gordeev 		size64 = (size64 << 32) | size;
1042455ef20SAlexander Gordeev 
1052455ef20SAlexander Gordeev 		return ~size64 + 1;
1062455ef20SAlexander Gordeev 	} else {
1072455ef20SAlexander Gordeev 		return ~size + 1;
1082455ef20SAlexander Gordeev 	}
1094932b58aSMichael S. Tsirkin }
1104932b58aSMichael S. Tsirkin 
1114932b58aSMichael S. Tsirkin bool pci_bar_is_memory(pcidevaddr_t dev, int bar_num)
1124932b58aSMichael S. Tsirkin {
1137aa83307SAlexander Gordeev 	uint32_t bar = pci_bar_get(dev, bar_num);
114ebb58e7eSAlexander Gordeev 
1154932b58aSMichael S. Tsirkin 	return !(bar & PCI_BASE_ADDRESS_SPACE_IO);
1164932b58aSMichael S. Tsirkin }
1174932b58aSMichael S. Tsirkin 
1184932b58aSMichael S. Tsirkin bool pci_bar_is_valid(pcidevaddr_t dev, int bar_num)
1194932b58aSMichael S. Tsirkin {
1207aa83307SAlexander Gordeev 	return pci_bar_get(dev, bar_num);
1214932b58aSMichael S. Tsirkin }
1222455ef20SAlexander Gordeev 
1232455ef20SAlexander Gordeev bool pci_bar_is64(pcidevaddr_t dev, int bar_num)
1242455ef20SAlexander Gordeev {
1252455ef20SAlexander Gordeev 	uint32_t bar = pci_bar_get(dev, bar_num);
1262455ef20SAlexander Gordeev 
1272455ef20SAlexander Gordeev 	if (bar & PCI_BASE_ADDRESS_SPACE_IO)
1282455ef20SAlexander Gordeev 		return false;
1292455ef20SAlexander Gordeev 
1302455ef20SAlexander Gordeev 	return (bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
1312455ef20SAlexander Gordeev 		      PCI_BASE_ADDRESS_MEM_TYPE_64;
1322455ef20SAlexander Gordeev }
133e4611520SAlexander Gordeev 
13433d78b07SAlexander Gordeev void pci_bar_print(pcidevaddr_t dev, int bar_num)
135e4611520SAlexander Gordeev {
136e4611520SAlexander Gordeev 	phys_addr_t size, start, end;
137e4611520SAlexander Gordeev 	uint32_t bar;
138e4611520SAlexander Gordeev 
139e4611520SAlexander Gordeev 	size = pci_bar_size(dev, bar_num);
140e4611520SAlexander Gordeev 	if (!size)
141e4611520SAlexander Gordeev 		return;
142e4611520SAlexander Gordeev 
143e4611520SAlexander Gordeev 	bar = pci_bar_get(dev, bar_num);
144e4611520SAlexander Gordeev 	start = pci_bar_get_addr(dev, bar_num);
145e4611520SAlexander Gordeev 	end = start + size - 1;
146e4611520SAlexander Gordeev 
147e4611520SAlexander Gordeev 	if (pci_bar_is64(dev, bar_num)) {
148e4611520SAlexander Gordeev 		printf("BAR#%d,%d [%" PRIx64 "-%" PRIx64 " ",
149e4611520SAlexander Gordeev 		       bar_num, bar_num + 1, start, end);
150e4611520SAlexander Gordeev 	} else {
151e4611520SAlexander Gordeev 		printf("BAR#%d [%02x-%02x ",
152e4611520SAlexander Gordeev 		       bar_num, (uint32_t)start, (uint32_t)end);
153e4611520SAlexander Gordeev 	}
154e4611520SAlexander Gordeev 
155e4611520SAlexander Gordeev 	if (bar & PCI_BASE_ADDRESS_SPACE_IO) {
156e4611520SAlexander Gordeev 		printf("PIO");
157e4611520SAlexander Gordeev 	} else {
158e4611520SAlexander Gordeev 		printf("MEM");
159e4611520SAlexander Gordeev 		switch (bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK) {
160e4611520SAlexander Gordeev 		case PCI_BASE_ADDRESS_MEM_TYPE_32:
161e4611520SAlexander Gordeev 			printf("32");
162e4611520SAlexander Gordeev 			break;
163e4611520SAlexander Gordeev 		case PCI_BASE_ADDRESS_MEM_TYPE_1M:
164e4611520SAlexander Gordeev 			printf("1M");
165e4611520SAlexander Gordeev 			break;
166e4611520SAlexander Gordeev 		case PCI_BASE_ADDRESS_MEM_TYPE_64:
167e4611520SAlexander Gordeev 			printf("64");
168e4611520SAlexander Gordeev 			break;
169e4611520SAlexander Gordeev 		default:
170e4611520SAlexander Gordeev 			assert(0);
171e4611520SAlexander Gordeev 		}
172e4611520SAlexander Gordeev 	}
173e4611520SAlexander Gordeev 
174e4611520SAlexander Gordeev 	if (bar & PCI_BASE_ADDRESS_MEM_PREFETCH)
175e4611520SAlexander Gordeev 		printf("/p");
176e4611520SAlexander Gordeev 
177e4611520SAlexander Gordeev 	printf("]");
178e4611520SAlexander Gordeev }
179e4611520SAlexander Gordeev 
18033d78b07SAlexander Gordeev void pci_dev_print_id(pcidevaddr_t dev)
181e4611520SAlexander Gordeev {
182e4611520SAlexander Gordeev 	printf("00.%02x.%1x %04x:%04x", dev / 8, dev % 8,
183e4611520SAlexander Gordeev 		pci_config_readw(dev, PCI_VENDOR_ID),
184e4611520SAlexander Gordeev 		pci_config_readw(dev, PCI_DEVICE_ID));
185e4611520SAlexander Gordeev }
186e4611520SAlexander Gordeev 
187e4611520SAlexander Gordeev static void pci_dev_print(pcidevaddr_t dev)
188e4611520SAlexander Gordeev {
189e4611520SAlexander Gordeev 	uint8_t header = pci_config_readb(dev, PCI_HEADER_TYPE);
190e4611520SAlexander Gordeev 	uint8_t progif = pci_config_readb(dev, PCI_CLASS_PROG);
191e4611520SAlexander Gordeev 	uint8_t subclass = pci_config_readb(dev, PCI_CLASS_DEVICE);
192e4611520SAlexander Gordeev 	uint8_t class = pci_config_readb(dev, PCI_CLASS_DEVICE + 1);
193e4611520SAlexander Gordeev 	int i;
194e4611520SAlexander Gordeev 
195e4611520SAlexander Gordeev 	pci_dev_print_id(dev);
196e4611520SAlexander Gordeev 	printf(" type %02x progif %02x class %02x subclass %02x\n",
197e4611520SAlexander Gordeev 	       header, progif, class, subclass);
198e4611520SAlexander Gordeev 
199e4611520SAlexander Gordeev 	if ((header & PCI_HEADER_TYPE_MASK) != PCI_HEADER_TYPE_NORMAL)
200e4611520SAlexander Gordeev 		return;
201e4611520SAlexander Gordeev 
202e4611520SAlexander Gordeev 	for (i = 0; i < 6; i++) {
203e4611520SAlexander Gordeev 		if (pci_bar_size(dev, i)) {
204e4611520SAlexander Gordeev 			printf("\t");
205e4611520SAlexander Gordeev 			pci_bar_print(dev, i);
206e4611520SAlexander Gordeev 			printf("\n");
207e4611520SAlexander Gordeev 		}
208e4611520SAlexander Gordeev 		if (pci_bar_is64(dev, i))
209e4611520SAlexander Gordeev 			i++;
210e4611520SAlexander Gordeev 	}
211e4611520SAlexander Gordeev }
212e4611520SAlexander Gordeev 
213e4611520SAlexander Gordeev void pci_print(void)
214e4611520SAlexander Gordeev {
215e4611520SAlexander Gordeev 	pcidevaddr_t dev;
216e4611520SAlexander Gordeev 
217e4611520SAlexander Gordeev 	for (dev = 0; dev < 256; ++dev) {
218e4611520SAlexander Gordeev 		if (pci_dev_exists(dev))
219e4611520SAlexander Gordeev 			pci_dev_print(dev);
220e4611520SAlexander Gordeev 	}
221e4611520SAlexander Gordeev }
222