xref: /freebsd-14.2/sys/i386/pci/pci_cfgreg.c (revision 6a75d3b3)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 1997, Stefan Esser <[email protected]>
5  * Copyright (c) 2000, Michael Smith <[email protected]>
6  * Copyright (c) 2000, BSDi
7  * Copyright (c) 2004, Scott Long <[email protected]>
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice unmodified, this list of conditions, and the following
15  *    disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/bus.h>
36 #include <sys/lock.h>
37 #include <sys/kernel.h>
38 #include <sys/mutex.h>
39 #include <sys/malloc.h>
40 #include <sys/queue.h>
41 #include <sys/sysctl.h>
42 #include <dev/pci/pcivar.h>
43 #include <dev/pci/pcireg.h>
44 #include <machine/pci_cfgreg.h>
45 #include <machine/pc/bios.h>
46 
47 #include <vm/vm.h>
48 #include <vm/vm_param.h>
49 #include <vm/vm_kern.h>
50 #include <vm/vm_extern.h>
51 #include <vm/pmap.h>
52 
53 #define PRVERB(a) do {							\
54 	if (bootverbose)						\
55 		printf a ;						\
56 } while(0)
57 
58 struct pcie_mcfg_region {
59 	uint64_t base;
60 	uint16_t domain;
61 	uint8_t minbus;
62 	uint8_t maxbus;
63 };
64 
65 #define PCIE_CACHE 8
66 struct pcie_cfg_elem {
67 	TAILQ_ENTRY(pcie_cfg_elem)	elem;
68 	vm_offset_t	vapage;
69 	vm_paddr_t	papage;
70 };
71 
72 SYSCTL_DECL(_hw_pci);
73 
74 static struct pcie_mcfg_region *mcfg_regions;
75 static int mcfg_numregions;
76 static TAILQ_HEAD(pcie_cfg_list, pcie_cfg_elem) pcie_list[MAXCPU];
77 static int pcie_cache_initted;
78 static uint32_t pcie_badslots;
79 int cfgmech;
80 static int devmax;
81 static struct mtx pcicfg_mtx;
82 
83 static int mcfg_enable = 1;
84 SYSCTL_INT(_hw_pci, OID_AUTO, mcfg, CTLFLAG_RDTUN, &mcfg_enable, 0,
85     "Enable support for PCI-e memory mapped config access");
86 
87 static uint32_t	pci_docfgregread(int domain, int bus, int slot, int func,
88 		    int reg, int bytes);
89 static struct pcie_mcfg_region *pcie_lookup_region(int domain, int bus);
90 static int	pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
91 static void	pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
92 static int	pcireg_cfgopen(void);
93 static int	pciereg_cfgread(struct pcie_mcfg_region *region, int bus,
94 		    unsigned slot, unsigned func, unsigned reg, unsigned bytes);
95 static void	pciereg_cfgwrite(struct pcie_mcfg_region *region, int bus,
96 		    unsigned slot, unsigned func, unsigned reg, int data,
97 		    unsigned bytes);
98 
99 /*
100  * Some BIOS writers seem to want to ignore the spec and put
101  * 0 in the intline rather than 255 to indicate none.  Some use
102  * numbers in the range 128-254 to indicate something strange and
103  * apparently undocumented anywhere.  Assume these are completely bogus
104  * and map them to 255, which means "none".
105  */
106 static __inline int
pci_i386_map_intline(int line)107 pci_i386_map_intline(int line)
108 {
109 	if (line == 0 || line >= 128)
110 		return (PCI_INVALID_IRQ);
111 	return (line);
112 }
113 
114 static u_int16_t
pcibios_get_version(void)115 pcibios_get_version(void)
116 {
117 	struct bios_regs args;
118 
119 	if (PCIbios.ventry == 0) {
120 		PRVERB(("pcibios: No call entry point\n"));
121 		return (0);
122 	}
123 	args.eax = PCIBIOS_BIOS_PRESENT;
124 	if (bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL))) {
125 		PRVERB(("pcibios: BIOS_PRESENT call failed\n"));
126 		return (0);
127 	}
128 	if (args.edx != 0x20494350) {
129 		PRVERB(("pcibios: BIOS_PRESENT didn't return 'PCI ' in edx\n"));
130 		return (0);
131 	}
132 	return (args.ebx & 0xffff);
133 }
134 
135 /*
136  * Initialise access to PCI configuration space
137  */
138 int
pci_cfgregopen(void)139 pci_cfgregopen(void)
140 {
141 	uint16_t v;
142 	static int opened = 0;
143 
144 	if (opened)
145 		return (1);
146 
147 	if (cfgmech == CFGMECH_NONE && pcireg_cfgopen() == 0)
148 		return (0);
149 
150 	v = pcibios_get_version();
151 	if (v > 0)
152 		PRVERB(("pcibios: BIOS version %x.%02x\n", (v & 0xff00) >> 8,
153 		    v & 0xff));
154 	mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN);
155 	opened = 1;
156 
157 	/* $PIR requires PCI BIOS 2.10 or greater. */
158 	if (v >= 0x0210)
159 		pci_pir_open();
160 
161 	return (1);
162 }
163 
164 static struct pcie_mcfg_region *
pcie_lookup_region(int domain,int bus)165 pcie_lookup_region(int domain, int bus)
166 {
167 	for (int i = 0; i < mcfg_numregions; i++)
168 		if (mcfg_regions[i].domain == domain &&
169 		    bus >= mcfg_regions[i].minbus &&
170 		    bus <= mcfg_regions[i].maxbus)
171 			return (&mcfg_regions[i]);
172 	return (NULL);
173 }
174 
175 static uint32_t
pci_docfgregread(int domain,int bus,int slot,int func,int reg,int bytes)176 pci_docfgregread(int domain, int bus, int slot, int func, int reg, int bytes)
177 {
178 	if (domain == 0 && bus == 0 && (1 << slot & pcie_badslots) != 0)
179 		return (pcireg_cfgread(bus, slot, func, reg, bytes));
180 
181 	if (cfgmech == CFGMECH_PCIE) {
182 		struct pcie_mcfg_region *region;
183 
184 		region = pcie_lookup_region(domain, bus);
185 		if (region != NULL)
186 			return (pciereg_cfgread(region, bus, slot, func, reg,
187 			    bytes));
188 	}
189 
190 	if (domain == 0)
191 		return (pcireg_cfgread(bus, slot, func, reg, bytes));
192 	else
193 		return (-1);
194 }
195 
196 /*
197  * Read configuration space register
198  */
199 u_int32_t
pci_cfgregread(int domain,int bus,int slot,int func,int reg,int bytes)200 pci_cfgregread(int domain, int bus, int slot, int func, int reg, int bytes)
201 {
202 	uint32_t line;
203 
204 	/*
205 	 * Some BIOS writers seem to want to ignore the spec and put
206 	 * 0 in the intline rather than 255 to indicate none.  The rest of
207 	 * the code uses 255 as an invalid IRQ.
208 	 */
209 	if (reg == PCIR_INTLINE && bytes == 1) {
210 		line = pci_docfgregread(domain, bus, slot, func, PCIR_INTLINE,
211 		    1);
212 		return (pci_i386_map_intline(line));
213 	}
214 	return (pci_docfgregread(domain, bus, slot, func, reg, bytes));
215 }
216 
217 /*
218  * Write configuration space register
219  */
220 void
pci_cfgregwrite(int domain,int bus,int slot,int func,int reg,uint32_t data,int bytes)221 pci_cfgregwrite(int domain, int bus, int slot, int func, int reg, uint32_t data,
222     int bytes)
223 {
224 	if (domain == 0 && bus == 0 && (1 << slot & pcie_badslots) != 0) {
225 		pcireg_cfgwrite(bus, slot, func, reg, data, bytes);
226 		return;
227 	}
228 
229 	if (cfgmech == CFGMECH_PCIE) {
230 		struct pcie_mcfg_region *region;
231 
232 		region = pcie_lookup_region(domain, bus);
233 		if (region != NULL) {
234 			pciereg_cfgwrite(region, bus, slot, func, reg, data,
235 			    bytes);
236 			return;
237 		}
238 	}
239 
240 	if (domain == 0)
241 		pcireg_cfgwrite(bus, slot, func, reg, data, bytes);
242 }
243 
244 /*
245  * Configuration space access using direct register operations
246  */
247 
248 /* enable configuration space accesses and return data port address */
249 static int
pci_cfgenable(unsigned bus,unsigned slot,unsigned func,int reg,int bytes)250 pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
251 {
252 	int dataport = 0;
253 
254 	if (bus <= PCI_BUSMAX
255 	    && slot < devmax
256 	    && func <= PCI_FUNCMAX
257 	    && (unsigned)reg <= PCI_REGMAX
258 	    && bytes != 3
259 	    && (unsigned)bytes <= 4
260 	    && (reg & (bytes - 1)) == 0) {
261 		switch (cfgmech) {
262 		case CFGMECH_PCIE:
263 		case CFGMECH_1:
264 			outl(CONF1_ADDR_PORT, (1U << 31)
265 			    | (bus << 16) | (slot << 11)
266 			    | (func << 8) | (reg & ~0x03));
267 			dataport = CONF1_DATA_PORT + (reg & 0x03);
268 			break;
269 		case CFGMECH_2:
270 			outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1));
271 			outb(CONF2_FORWARD_PORT, bus);
272 			dataport = 0xc000 | (slot << 8) | reg;
273 			break;
274 		}
275 	}
276 	return (dataport);
277 }
278 
279 /* disable configuration space accesses */
280 static void
pci_cfgdisable(void)281 pci_cfgdisable(void)
282 {
283 	switch (cfgmech) {
284 	case CFGMECH_PCIE:
285 	case CFGMECH_1:
286 		/*
287 		 * Do nothing for the config mechanism 1 case.
288 		 * Writing a 0 to the address port can apparently
289 		 * confuse some bridges and cause spurious
290 		 * access failures.
291 		 */
292 		break;
293 	case CFGMECH_2:
294 		outb(CONF2_ENABLE_PORT, 0);
295 		break;
296 	}
297 }
298 
299 static int
pcireg_cfgread(int bus,int slot,int func,int reg,int bytes)300 pcireg_cfgread(int bus, int slot, int func, int reg, int bytes)
301 {
302 	int data = -1;
303 	int port;
304 
305 	mtx_lock_spin(&pcicfg_mtx);
306 	port = pci_cfgenable(bus, slot, func, reg, bytes);
307 	if (port != 0) {
308 		switch (bytes) {
309 		case 1:
310 			data = inb(port);
311 			break;
312 		case 2:
313 			data = inw(port);
314 			break;
315 		case 4:
316 			data = inl(port);
317 			break;
318 		}
319 		pci_cfgdisable();
320 	}
321 	mtx_unlock_spin(&pcicfg_mtx);
322 	return (data);
323 }
324 
325 static void
pcireg_cfgwrite(int bus,int slot,int func,int reg,int data,int bytes)326 pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
327 {
328 	int port;
329 
330 	mtx_lock_spin(&pcicfg_mtx);
331 	port = pci_cfgenable(bus, slot, func, reg, bytes);
332 	if (port != 0) {
333 		switch (bytes) {
334 		case 1:
335 			outb(port, data);
336 			break;
337 		case 2:
338 			outw(port, data);
339 			break;
340 		case 4:
341 			outl(port, data);
342 			break;
343 		}
344 		pci_cfgdisable();
345 	}
346 	mtx_unlock_spin(&pcicfg_mtx);
347 }
348 
349 /* check whether the configuration mechanism has been correctly identified */
350 static int
pci_cfgcheck(int maxdev)351 pci_cfgcheck(int maxdev)
352 {
353 	uint32_t id, class;
354 	uint8_t header;
355 	uint8_t device;
356 	int port;
357 
358 	if (bootverbose)
359 		printf("pci_cfgcheck:\tdevice ");
360 
361 	for (device = 0; device < maxdev; device++) {
362 		if (bootverbose)
363 			printf("%d ", device);
364 
365 		port = pci_cfgenable(0, device, 0, 0, 4);
366 		id = inl(port);
367 		if (id == 0 || id == 0xffffffff)
368 			continue;
369 
370 		port = pci_cfgenable(0, device, 0, 8, 4);
371 		class = inl(port) >> 8;
372 		if (bootverbose)
373 			printf("[class=%06x] ", class);
374 		if (class == 0 || (class & 0xf870ff) != 0)
375 			continue;
376 
377 		port = pci_cfgenable(0, device, 0, 14, 1);
378 		header = inb(port);
379 		if (bootverbose)
380 			printf("[hdr=%02x] ", header);
381 		if ((header & 0x7e) != 0)
382 			continue;
383 
384 		if (bootverbose)
385 			printf("is there (id=%08x)\n", id);
386 
387 		pci_cfgdisable();
388 		return (1);
389 	}
390 	if (bootverbose)
391 		printf("-- nothing found\n");
392 
393 	pci_cfgdisable();
394 	return (0);
395 }
396 
397 static int
pcireg_cfgopen(void)398 pcireg_cfgopen(void)
399 {
400 	uint32_t mode1res, oldval1;
401 	uint8_t mode2res, oldval2;
402 
403 	/* Check for type #1 first. */
404 	oldval1 = inl(CONF1_ADDR_PORT);
405 
406 	if (bootverbose) {
407 		printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08x\n",
408 		    oldval1);
409 	}
410 
411 	cfgmech = CFGMECH_1;
412 	devmax = 32;
413 
414 	outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
415 	DELAY(1);
416 	mode1res = inl(CONF1_ADDR_PORT);
417 	outl(CONF1_ADDR_PORT, oldval1);
418 
419 	if (bootverbose)
420 		printf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n",  mode1res,
421 		    CONF1_ENABLE_CHK);
422 
423 	if (mode1res) {
424 		if (pci_cfgcheck(32))
425 			return (cfgmech);
426 	}
427 
428 	outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
429 	mode1res = inl(CONF1_ADDR_PORT);
430 	outl(CONF1_ADDR_PORT, oldval1);
431 
432 	if (bootverbose)
433 		printf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n",  mode1res,
434 		    CONF1_ENABLE_CHK1);
435 
436 	if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
437 		if (pci_cfgcheck(32))
438 			return (cfgmech);
439 	}
440 
441 	/* Type #1 didn't work, so try type #2. */
442 	oldval2 = inb(CONF2_ENABLE_PORT);
443 
444 	if (bootverbose) {
445 		printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n",
446 		    oldval2);
447 	}
448 
449 	if ((oldval2 & 0xf0) == 0) {
450 		cfgmech = CFGMECH_2;
451 		devmax = 16;
452 
453 		outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
454 		mode2res = inb(CONF2_ENABLE_PORT);
455 		outb(CONF2_ENABLE_PORT, oldval2);
456 
457 		if (bootverbose)
458 			printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n",
459 			    mode2res, CONF2_ENABLE_CHK);
460 
461 		if (mode2res == CONF2_ENABLE_RES) {
462 			if (bootverbose)
463 				printf("pci_open(2a):\tnow trying mechanism 2\n");
464 
465 			if (pci_cfgcheck(16))
466 				return (cfgmech);
467 		}
468 	}
469 
470 	/* Nothing worked, so punt. */
471 	cfgmech = CFGMECH_NONE;
472 	devmax = 0;
473 	return (cfgmech);
474 }
475 
476 static bool
pcie_init_cache(void)477 pcie_init_cache(void)
478 {
479 	struct pcie_cfg_list *pcielist;
480 	struct pcie_cfg_elem *pcie_array, *elem;
481 #ifdef SMP
482 	struct pcpu *pc;
483 #endif
484 	vm_offset_t va;
485 	int i;
486 
487 #ifdef SMP
488 	STAILQ_FOREACH(pc, &cpuhead, pc_allcpu)
489 #endif
490 	{
491 		pcie_array = malloc(sizeof(struct pcie_cfg_elem) * PCIE_CACHE,
492 		    M_DEVBUF, M_NOWAIT);
493 		if (pcie_array == NULL)
494 			return (false);
495 
496 		va = kva_alloc(PCIE_CACHE * PAGE_SIZE);
497 		if (va == 0) {
498 			free(pcie_array, M_DEVBUF);
499 			return (false);
500 		}
501 
502 #ifdef SMP
503 		pcielist = &pcie_list[pc->pc_cpuid];
504 #else
505 		pcielist = &pcie_list[0];
506 #endif
507 		TAILQ_INIT(pcielist);
508 		for (i = 0; i < PCIE_CACHE; i++) {
509 			elem = &pcie_array[i];
510 			elem->vapage = va + (i * PAGE_SIZE);
511 			elem->papage = 0;
512 			TAILQ_INSERT_HEAD(pcielist, elem, elem);
513 		}
514 	}
515 	return (true);
516 }
517 
518 static void
pcie_init_badslots(struct pcie_mcfg_region * region)519 pcie_init_badslots(struct pcie_mcfg_region *region)
520 {
521 	uint32_t val1, val2;
522 	int slot;
523 
524 	/*
525 	 * On some AMD systems, some of the devices on bus 0 are
526 	 * inaccessible using memory-mapped PCI config access.  Walk
527 	 * bus 0 looking for such devices.  For these devices, we will
528 	 * fall back to using type 1 config access instead.
529 	 */
530 	if (pci_cfgregopen() != 0) {
531 		for (slot = 0; slot <= PCI_SLOTMAX; slot++) {
532 			val1 = pcireg_cfgread(0, slot, 0, 0, 4);
533 			if (val1 == 0xffffffff)
534 				continue;
535 
536 			val2 = pciereg_cfgread(region, 0, slot, 0, 0, 4);
537 			if (val2 != val1)
538 				pcie_badslots |= (1 << slot);
539 		}
540 	}
541 }
542 
543 int
pcie_cfgregopen(uint64_t base,uint16_t domain,uint8_t minbus,uint8_t maxbus)544 pcie_cfgregopen(uint64_t base, uint16_t domain, uint8_t minbus, uint8_t maxbus)
545 {
546 	struct pcie_mcfg_region *region;
547 
548 	if (!mcfg_enable)
549 		return (0);
550 
551 	if (!pae_mode && base >= 0x100000000) {
552 		if (bootverbose)
553 			printf(
554 	    "PCI: MCFG domain %u bus %u-%u base 0x%jx too high\n",
555 			domain, minbus, maxbus, (uintmax_t)base);
556 		return (0);
557 	}
558 
559 	if (bootverbose)
560 		printf("PCI: MCFG domain %u bus %u-%u base @ 0x%jx\n",
561 		    domain, minbus, maxbus, (uintmax_t)base);
562 
563 	if (pcie_cache_initted == 0) {
564 		if (!pcie_init_cache())
565 			pcie_cache_initted = -1;
566 		else
567 			pcie_cache_initted = 1;
568 	}
569 
570 	if (pcie_cache_initted == -1)
571 		return (0);
572 
573 	/* Resize the array. */
574 	mcfg_regions = realloc(mcfg_regions,
575 	    sizeof(*mcfg_regions) * (mcfg_numregions + 1), M_DEVBUF, M_WAITOK);
576 
577 	region = &mcfg_regions[mcfg_numregions];
578 	region->base = base + (minbus << 20);
579 	region->domain = domain;
580 	region->minbus = minbus;
581 	region->maxbus = maxbus;
582 	mcfg_numregions++;
583 
584 	cfgmech = CFGMECH_PCIE;
585 	devmax = 32;
586 
587 	if (domain == 0 && minbus == 0)
588 		pcie_init_badslots(region);
589 
590 	return (1);
591 }
592 
593 #define PCIE_PADDR(base, reg, bus, slot, func)	\
594 	((base)				+	\
595 	((((bus) & 0xff) << 20)		|	\
596 	(((slot) & 0x1f) << 15)		|	\
597 	(((func) & 0x7) << 12)		|	\
598 	((reg) & 0xfff)))
599 
600 static __inline vm_offset_t
pciereg_findaddr(struct pcie_mcfg_region * region,int bus,unsigned slot,unsigned func,unsigned reg)601 pciereg_findaddr(struct pcie_mcfg_region *region, int bus, unsigned slot,
602     unsigned func, unsigned reg)
603 {
604 	struct pcie_cfg_list *pcielist;
605 	struct pcie_cfg_elem *elem;
606 	vm_paddr_t pa, papage;
607 
608 	MPASS(bus >= region->minbus && bus <= region->maxbus);
609 
610 	pa = PCIE_PADDR(region->base, reg, bus - region->minbus, slot, func);
611 	papage = pa & ~PAGE_MASK;
612 
613 	/*
614 	 * Find an element in the cache that matches the physical page desired,
615 	 * or create a new mapping from the least recently used element.
616 	 * A very simple LRU algorithm is used here, does it need to be more
617 	 * efficient?
618 	 */
619 	pcielist = &pcie_list[PCPU_GET(cpuid)];
620 	TAILQ_FOREACH(elem, pcielist, elem) {
621 		if (elem->papage == papage)
622 			break;
623 	}
624 
625 	if (elem == NULL) {
626 		elem = TAILQ_LAST(pcielist, pcie_cfg_list);
627 		if (elem->papage != 0) {
628 			pmap_kremove(elem->vapage);
629 			invlpg(elem->vapage);
630 		}
631 		pmap_kenter(elem->vapage, papage);
632 		elem->papage = papage;
633 	}
634 
635 	if (elem != TAILQ_FIRST(pcielist)) {
636 		TAILQ_REMOVE(pcielist, elem, elem);
637 		TAILQ_INSERT_HEAD(pcielist, elem, elem);
638 	}
639 	return (elem->vapage | (pa & PAGE_MASK));
640 }
641 
642 /*
643  * AMD BIOS And Kernel Developer's Guides for CPU families starting with 10h
644  * have a requirement that all accesses to the memory mapped PCI configuration
645  * space are done using AX class of registers.
646  * Since other vendors do not currently have any contradicting requirements
647  * the AMD access pattern is applied universally.
648  */
649 
650 static int
pciereg_cfgread(struct pcie_mcfg_region * region,int bus,unsigned slot,unsigned func,unsigned reg,unsigned bytes)651 pciereg_cfgread(struct pcie_mcfg_region *region, int bus, unsigned slot,
652     unsigned func, unsigned reg, unsigned bytes)
653 {
654 	vm_offset_t va;
655 	int data = -1;
656 
657 	if (slot > PCI_SLOTMAX || func > PCI_FUNCMAX || reg > PCIE_REGMAX)
658 		return (-1);
659 
660 	critical_enter();
661 	va = pciereg_findaddr(region, bus, slot, func, reg);
662 
663 	switch (bytes) {
664 	case 4:
665 		__asm("movl %1, %0" : "=a" (data)
666 		    : "m" (*(volatile uint32_t *)va));
667 		break;
668 	case 2:
669 		__asm("movzwl %1, %0" : "=a" (data)
670 		    : "m" (*(volatile uint16_t *)va));
671 		break;
672 	case 1:
673 		__asm("movzbl %1, %0" : "=a" (data)
674 		    : "m" (*(volatile uint8_t *)va));
675 		break;
676 	}
677 
678 	critical_exit();
679 	return (data);
680 }
681 
682 static void
pciereg_cfgwrite(struct pcie_mcfg_region * region,int bus,unsigned slot,unsigned func,unsigned reg,int data,unsigned bytes)683 pciereg_cfgwrite(struct pcie_mcfg_region *region, int bus, unsigned slot,
684     unsigned func, unsigned reg, int data, unsigned bytes)
685 {
686 	vm_offset_t va;
687 
688 	if (slot > PCI_SLOTMAX || func > PCI_FUNCMAX || reg > PCIE_REGMAX)
689 		return;
690 
691 	critical_enter();
692 	va = pciereg_findaddr(region, bus, slot, func, reg);
693 
694 	switch (bytes) {
695 	case 4:
696 		__asm("movl %1, %0" : "=m" (*(volatile uint32_t *)va)
697 		    : "a" (data));
698 		break;
699 	case 2:
700 		__asm("movw %1, %0" : "=m" (*(volatile uint16_t *)va)
701 		    : "a" ((uint16_t)data));
702 		break;
703 	case 1:
704 		__asm("movb %1, %0" : "=m" (*(volatile uint8_t *)va)
705 		    : "a" ((uint8_t)data));
706 		break;
707 	}
708 
709 	critical_exit();
710 }
711 
712 /* ABI compatibility shims. */
713 #undef pci_cfgregread
714 #undef pci_cfgregwrite
715 
716 u_int32_t pci_cfgregread(int bus, int slot, int func, int reg, int bytes);
717 void	pci_cfgregwrite(int bus, int slot, int func, int reg, uint32_t data,
718     int bytes);
719 
720 u_int32_t
pci_cfgregread(int bus,int slot,int func,int reg,int bytes)721 pci_cfgregread(int bus, int slot, int func, int reg, int bytes)
722 {
723 	return (pci_cfgregread_domain(0, bus, slot, func, reg, bytes));
724 }
725 
726 void
pci_cfgregwrite(int bus,int slot,int func,int reg,uint32_t data,int bytes)727 pci_cfgregwrite(int bus, int slot, int func, int reg, uint32_t data, int bytes)
728 {
729 	return (pci_cfgregwrite_domain(0, bus, slot, func, reg, data, bytes));
730 }
731