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