xref: /kvm-unit-tests/lib/pci.c (revision f1071c6d5fc454957e2501934fea6d9e592da8a5)
1 /*
2  * Copyright (C) 2013, Red Hat Inc, Michael S. Tsirkin <mst@redhat.com>
3  *
4  * This work is licensed under the terms of the GNU LGPL, version 2.
5  */
6 #include <linux/pci_regs.h>
7 #include "pci.h"
8 #include "asm/pci.h"
9 
10 bool pci_dev_exists(pcidevaddr_t dev)
11 {
12 	return (pci_config_readw(dev, PCI_VENDOR_ID) != 0xffff &&
13 		pci_config_readw(dev, PCI_DEVICE_ID) != 0xffff);
14 }
15 
16 /* Scan bus look for a specific device. Only bus 0 scanned for now. */
17 pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id)
18 {
19 	pcidevaddr_t dev;
20 
21 	for (dev = 0; dev < 256; ++dev) {
22 		if (pci_config_readw(dev, PCI_VENDOR_ID) == vendor_id &&
23 		    pci_config_readw(dev, PCI_DEVICE_ID) == device_id)
24 			return dev;
25 	}
26 
27 	return PCIDEVADDR_INVALID;
28 }
29 
30 uint32_t pci_bar_mask(uint32_t bar)
31 {
32 	return (bar & PCI_BASE_ADDRESS_SPACE_IO) ?
33 		PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK;
34 }
35 
36 uint32_t pci_bar_get(pcidevaddr_t dev, int bar_num)
37 {
38 	return pci_config_readl(dev, PCI_BASE_ADDRESS_0 + bar_num * 4);
39 }
40 
41 phys_addr_t pci_bar_get_addr(pcidevaddr_t dev, int bar_num)
42 {
43 	uint32_t bar = pci_bar_get(dev, bar_num);
44 	uint32_t mask = pci_bar_mask(bar);
45 	uint64_t addr = bar & mask;
46 	phys_addr_t phys_addr;
47 
48 	if (pci_bar_is64(dev, bar_num))
49 		addr |= (uint64_t)pci_bar_get(dev, bar_num + 1) << 32;
50 
51 	phys_addr = pci_translate_addr(dev, addr);
52 	assert(phys_addr != INVALID_PHYS_ADDR);
53 
54 	return phys_addr;
55 }
56 
57 void pci_bar_set_addr(pcidevaddr_t dev, int bar_num, phys_addr_t addr)
58 {
59 	int off = PCI_BASE_ADDRESS_0 + bar_num * 4;
60 
61 	pci_config_writel(dev, off, (uint32_t)addr);
62 
63 	if (pci_bar_is64(dev, bar_num))
64 		pci_config_writel(dev, off + 4, (uint32_t)(addr >> 32));
65 }
66 
67 /*
68  * To determine the amount of address space needed by a PCI device,
69  * one must save the original value of the BAR, write a value of
70  * all 1's to the register, and then read it back. The amount of
71  * memory can be then determined by masking the information bits,
72  * performing a bitwise NOT, and incrementing the value by 1.
73  *
74  * The following pci_bar_size_helper() and pci_bar_size() functions
75  * implement the algorithm.
76  */
77 static uint32_t pci_bar_size_helper(pcidevaddr_t dev, int bar_num)
78 {
79 	int off = PCI_BASE_ADDRESS_0 + bar_num * 4;
80 	uint32_t bar, val;
81 
82 	bar = pci_config_readl(dev, off);
83 	pci_config_writel(dev, off, ~0u);
84 	val = pci_config_readl(dev, off);
85 	pci_config_writel(dev, off, bar);
86 
87 	return val;
88 }
89 
90 phys_addr_t pci_bar_size(pcidevaddr_t dev, int bar_num)
91 {
92 	uint32_t bar, size;
93 
94 	size = pci_bar_size_helper(dev, bar_num);
95 	if (!size)
96 		return 0;
97 
98 	bar = pci_bar_get(dev, bar_num);
99 	size &= pci_bar_mask(bar);
100 
101 	if (pci_bar_is64(dev, bar_num)) {
102 		phys_addr_t size64 = pci_bar_size_helper(dev, bar_num + 1);
103 		size64 = (size64 << 32) | size;
104 
105 		return ~size64 + 1;
106 	} else {
107 		return ~size + 1;
108 	}
109 }
110 
111 bool pci_bar_is_memory(pcidevaddr_t dev, int bar_num)
112 {
113 	uint32_t bar = pci_bar_get(dev, bar_num);
114 
115 	return !(bar & PCI_BASE_ADDRESS_SPACE_IO);
116 }
117 
118 bool pci_bar_is_valid(pcidevaddr_t dev, int bar_num)
119 {
120 	return pci_bar_get(dev, bar_num);
121 }
122 
123 bool pci_bar_is64(pcidevaddr_t dev, int bar_num)
124 {
125 	uint32_t bar = pci_bar_get(dev, bar_num);
126 
127 	if (bar & PCI_BASE_ADDRESS_SPACE_IO)
128 		return false;
129 
130 	return (bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
131 		      PCI_BASE_ADDRESS_MEM_TYPE_64;
132 }
133 
134 void pci_bar_print(pcidevaddr_t dev, int bar_num)
135 {
136 	phys_addr_t size, start, end;
137 	uint32_t bar;
138 
139 	size = pci_bar_size(dev, bar_num);
140 	if (!size)
141 		return;
142 
143 	bar = pci_bar_get(dev, bar_num);
144 	start = pci_bar_get_addr(dev, bar_num);
145 	end = start + size - 1;
146 
147 	if (pci_bar_is64(dev, bar_num)) {
148 		printf("BAR#%d,%d [%" PRIx64 "-%" PRIx64 " ",
149 		       bar_num, bar_num + 1, start, end);
150 	} else {
151 		printf("BAR#%d [%02x-%02x ",
152 		       bar_num, (uint32_t)start, (uint32_t)end);
153 	}
154 
155 	if (bar & PCI_BASE_ADDRESS_SPACE_IO) {
156 		printf("PIO");
157 	} else {
158 		printf("MEM");
159 		switch (bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK) {
160 		case PCI_BASE_ADDRESS_MEM_TYPE_32:
161 			printf("32");
162 			break;
163 		case PCI_BASE_ADDRESS_MEM_TYPE_1M:
164 			printf("1M");
165 			break;
166 		case PCI_BASE_ADDRESS_MEM_TYPE_64:
167 			printf("64");
168 			break;
169 		default:
170 			assert(0);
171 		}
172 	}
173 
174 	if (bar & PCI_BASE_ADDRESS_MEM_PREFETCH)
175 		printf("/p");
176 
177 	printf("]");
178 }
179 
180 void pci_dev_print_id(pcidevaddr_t dev)
181 {
182 	printf("00.%02x.%1x %04x:%04x", dev / 8, dev % 8,
183 		pci_config_readw(dev, PCI_VENDOR_ID),
184 		pci_config_readw(dev, PCI_DEVICE_ID));
185 }
186 
187 static void pci_dev_print(pcidevaddr_t dev)
188 {
189 	uint8_t header = pci_config_readb(dev, PCI_HEADER_TYPE);
190 	uint8_t progif = pci_config_readb(dev, PCI_CLASS_PROG);
191 	uint8_t subclass = pci_config_readb(dev, PCI_CLASS_DEVICE);
192 	uint8_t class = pci_config_readb(dev, PCI_CLASS_DEVICE + 1);
193 	int i;
194 
195 	pci_dev_print_id(dev);
196 	printf(" type %02x progif %02x class %02x subclass %02x\n",
197 	       header, progif, class, subclass);
198 
199 	if ((header & PCI_HEADER_TYPE_MASK) != PCI_HEADER_TYPE_NORMAL)
200 		return;
201 
202 	for (i = 0; i < 6; i++) {
203 		if (pci_bar_size(dev, i)) {
204 			printf("\t");
205 			pci_bar_print(dev, i);
206 			printf("\n");
207 		}
208 		if (pci_bar_is64(dev, i))
209 			i++;
210 	}
211 }
212 
213 void pci_print(void)
214 {
215 	pcidevaddr_t dev;
216 
217 	for (dev = 0; dev < 256; ++dev) {
218 		if (pci_dev_exists(dev))
219 			pci_dev_print(dev);
220 	}
221 }
222