Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/amd64/pci/pci_cfgreg.c
39536 views
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
* All rights reserved.
8
*
9
* Redistribution and use in source and binary forms, with or without
10
* modification, are permitted provided that the following conditions
11
* are met:
12
* 1. Redistributions of source code must retain the above copyright
13
* notice unmodified, this list of conditions, and the following
14
* disclaimer.
15
* 2. Redistributions in binary form must reproduce the above copyright
16
* notice, this list of conditions and the following disclaimer in the
17
* documentation and/or other materials provided with the distribution.
18
*
19
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
*/
30
31
#include <sys/param.h>
32
#include <sys/systm.h>
33
#include <sys/bus.h>
34
#include <sys/lock.h>
35
#include <sys/kernel.h>
36
#include <sys/malloc.h>
37
#include <sys/mutex.h>
38
#include <sys/sysctl.h>
39
#include <dev/pci/pcivar.h>
40
#include <dev/pci/pcireg.h>
41
#include <vm/vm.h>
42
#include <vm/pmap.h>
43
#include <machine/pci_cfgreg.h>
44
45
struct pcie_mcfg_region {
46
char *base;
47
uint16_t domain;
48
uint8_t minbus;
49
uint8_t maxbus;
50
};
51
52
static uint32_t pci_docfgregread(int domain, int bus, int slot, int func,
53
int reg, int bytes);
54
static struct pcie_mcfg_region *pcie_lookup_region(int domain, int bus);
55
static int pciereg_cfgread(struct pcie_mcfg_region *region, int bus,
56
unsigned slot, unsigned func, unsigned reg, unsigned bytes);
57
static void pciereg_cfgwrite(struct pcie_mcfg_region *region, int bus,
58
unsigned slot, unsigned func, unsigned reg, int data,
59
unsigned bytes);
60
static int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
61
static void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
62
63
SYSCTL_DECL(_hw_pci);
64
65
/*
66
* For amd64 we assume that type 1 I/O port-based access always works.
67
* If an ACPI MCFG table exists, pcie_cfgregopen() will be called to
68
* switch to memory-mapped access.
69
*/
70
int cfgmech = CFGMECH_1;
71
72
static struct pcie_mcfg_region *mcfg_regions;
73
static int mcfg_numregions;
74
static uint32_t pcie_badslots;
75
static struct mtx pcicfg_mtx;
76
MTX_SYSINIT(pcicfg_mtx, &pcicfg_mtx, "pcicfg_mtx", MTX_SPIN);
77
78
static int mcfg_enable = 1;
79
SYSCTL_INT(_hw_pci, OID_AUTO, mcfg, CTLFLAG_RDTUN, &mcfg_enable, 0,
80
"Enable support for PCI-e memory mapped config access");
81
82
int
83
pci_cfgregopen(void)
84
{
85
86
return (1);
87
}
88
89
static struct pcie_mcfg_region *
90
pcie_lookup_region(int domain, int bus)
91
{
92
for (int i = 0; i < mcfg_numregions; i++)
93
if (mcfg_regions[i].domain == domain &&
94
bus >= mcfg_regions[i].minbus &&
95
bus <= mcfg_regions[i].maxbus)
96
return (&mcfg_regions[i]);
97
return (NULL);
98
}
99
100
static uint32_t
101
pci_docfgregread(int domain, int bus, int slot, int func, int reg, int bytes)
102
{
103
if (domain == 0 && bus == 0 && (1 << slot & pcie_badslots) != 0)
104
return (pcireg_cfgread(bus, slot, func, reg, bytes));
105
106
if (cfgmech == CFGMECH_PCIE) {
107
struct pcie_mcfg_region *region;
108
109
region = pcie_lookup_region(domain, bus);
110
if (region != NULL)
111
return (pciereg_cfgread(region, bus, slot, func, reg,
112
bytes));
113
}
114
115
if (domain == 0)
116
return (pcireg_cfgread(bus, slot, func, reg, bytes));
117
else
118
return (-1);
119
}
120
121
/*
122
* Read configuration space register
123
*/
124
u_int32_t
125
pci_cfgregread(int domain, int bus, int slot, int func, int reg, int bytes)
126
{
127
uint32_t line;
128
129
/*
130
* Some BIOS writers seem to want to ignore the spec and put
131
* 0 in the intline rather than 255 to indicate none. Some use
132
* numbers in the range 128-254 to indicate something strange and
133
* apparently undocumented anywhere. Assume these are completely bogus
134
* and map them to 255, which the rest of the PCI code recognizes as
135
* as an invalid IRQ.
136
*/
137
if (reg == PCIR_INTLINE && bytes == 1) {
138
line = pci_docfgregread(domain, bus, slot, func, PCIR_INTLINE,
139
1);
140
if (line == 0 || line >= 128)
141
line = PCI_INVALID_IRQ;
142
return (line);
143
}
144
return (pci_docfgregread(domain, bus, slot, func, reg, bytes));
145
}
146
147
/*
148
* Write configuration space register
149
*/
150
void
151
pci_cfgregwrite(int domain, int bus, int slot, int func, int reg, uint32_t data,
152
int bytes)
153
{
154
if (domain == 0 && bus == 0 && (1 << slot & pcie_badslots) != 0) {
155
pcireg_cfgwrite(bus, slot, func, reg, data, bytes);
156
return;
157
}
158
159
if (cfgmech == CFGMECH_PCIE) {
160
struct pcie_mcfg_region *region;
161
162
region = pcie_lookup_region(domain, bus);
163
if (region != NULL) {
164
pciereg_cfgwrite(region, bus, slot, func, reg, data,
165
bytes);
166
return;
167
}
168
}
169
170
if (domain == 0)
171
pcireg_cfgwrite(bus, slot, func, reg, data, bytes);
172
}
173
174
/*
175
* Configuration space access using direct register operations
176
*/
177
178
/* enable configuration space accesses and return data port address */
179
static int
180
pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
181
{
182
int dataport = 0;
183
184
if (bus <= PCI_BUSMAX && slot <= PCI_SLOTMAX && func <= PCI_FUNCMAX &&
185
(unsigned)reg <= PCI_REGMAX && bytes != 3 &&
186
(unsigned)bytes <= 4 && (reg & (bytes - 1)) == 0) {
187
outl(CONF1_ADDR_PORT, (1U << 31) | (bus << 16) | (slot << 11)
188
| (func << 8) | (reg & ~0x03));
189
dataport = CONF1_DATA_PORT + (reg & 0x03);
190
}
191
return (dataport);
192
}
193
194
/* disable configuration space accesses */
195
static void
196
pci_cfgdisable(void)
197
{
198
199
/*
200
* Do nothing. Writing a 0 to the address port can apparently
201
* confuse some bridges and cause spurious access failures.
202
*/
203
}
204
205
static int
206
pcireg_cfgread(int bus, int slot, int func, int reg, int bytes)
207
{
208
int data = -1;
209
int port;
210
211
mtx_lock_spin(&pcicfg_mtx);
212
port = pci_cfgenable(bus, slot, func, reg, bytes);
213
if (port != 0) {
214
switch (bytes) {
215
case 1:
216
data = inb(port);
217
break;
218
case 2:
219
data = inw(port);
220
break;
221
case 4:
222
data = inl(port);
223
break;
224
}
225
pci_cfgdisable();
226
}
227
mtx_unlock_spin(&pcicfg_mtx);
228
return (data);
229
}
230
231
static void
232
pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
233
{
234
int port;
235
236
mtx_lock_spin(&pcicfg_mtx);
237
port = pci_cfgenable(bus, slot, func, reg, bytes);
238
if (port != 0) {
239
switch (bytes) {
240
case 1:
241
outb(port, data);
242
break;
243
case 2:
244
outw(port, data);
245
break;
246
case 4:
247
outl(port, data);
248
break;
249
}
250
pci_cfgdisable();
251
}
252
mtx_unlock_spin(&pcicfg_mtx);
253
}
254
255
static void
256
pcie_init_badslots(struct pcie_mcfg_region *region)
257
{
258
uint32_t val1, val2;
259
int slot;
260
261
/*
262
* On some AMD systems, some of the devices on bus 0 are
263
* inaccessible using memory-mapped PCI config access. Walk
264
* bus 0 looking for such devices. For these devices, we will
265
* fall back to using type 1 config access instead.
266
*/
267
if (pci_cfgregopen() != 0) {
268
for (slot = 0; slot <= PCI_SLOTMAX; slot++) {
269
val1 = pcireg_cfgread(0, slot, 0, 0, 4);
270
if (val1 == 0xffffffff)
271
continue;
272
273
val2 = pciereg_cfgread(region, 0, slot, 0, 0, 4);
274
if (val2 != val1)
275
pcie_badslots |= (1 << slot);
276
}
277
}
278
}
279
280
int
281
pcie_cfgregopen(uint64_t base, uint16_t domain, uint8_t minbus, uint8_t maxbus)
282
{
283
struct pcie_mcfg_region *region;
284
285
if (!mcfg_enable)
286
return (0);
287
288
if (bootverbose)
289
printf("PCI: MCFG domain %u bus %u-%u base @ 0x%lx\n",
290
domain, minbus, maxbus, base);
291
292
/* Resize the array. */
293
mcfg_regions = realloc(mcfg_regions,
294
sizeof(*mcfg_regions) * (mcfg_numregions + 1), M_DEVBUF, M_WAITOK);
295
296
region = &mcfg_regions[mcfg_numregions];
297
298
/* XXX: We should make sure this really fits into the direct map. */
299
region->base = pmap_mapdev_pciecfg(base + (minbus << 20), (maxbus + 1 - minbus) << 20);
300
region->domain = domain;
301
region->minbus = minbus;
302
region->maxbus = maxbus;
303
mcfg_numregions++;
304
305
cfgmech = CFGMECH_PCIE;
306
307
if (domain == 0 && minbus == 0)
308
pcie_init_badslots(region);
309
310
return (1);
311
}
312
313
#define PCIE_VADDR(base, reg, bus, slot, func) \
314
((base) + \
315
((((bus) & 0xff) << 20) | \
316
(((slot) & 0x1f) << 15) | \
317
(((func) & 0x7) << 12) | \
318
((reg) & 0xfff)))
319
320
/*
321
* AMD BIOS And Kernel Developer's Guides for CPU families starting with 10h
322
* have a requirement that all accesses to the memory mapped PCI configuration
323
* space are done using AX class of registers.
324
* Since other vendors do not currently have any contradicting requirements
325
* the AMD access pattern is applied universally.
326
*/
327
328
static int
329
pciereg_cfgread(struct pcie_mcfg_region *region, int bus, unsigned slot,
330
unsigned func, unsigned reg, unsigned bytes)
331
{
332
char *va;
333
int data = -1;
334
335
MPASS(bus >= region->minbus && bus <= region->maxbus);
336
337
if (slot > PCI_SLOTMAX || func > PCI_FUNCMAX || reg > PCIE_REGMAX)
338
return (-1);
339
340
va = PCIE_VADDR(region->base, reg, bus - region->minbus, slot, func);
341
342
switch (bytes) {
343
case 4:
344
__asm("movl %1, %0" : "=a" (data)
345
: "m" (*(volatile uint32_t *)va));
346
break;
347
case 2:
348
__asm("movzwl %1, %0" : "=a" (data)
349
: "m" (*(volatile uint16_t *)va));
350
break;
351
case 1:
352
__asm("movzbl %1, %0" : "=a" (data)
353
: "m" (*(volatile uint8_t *)va));
354
break;
355
}
356
357
return (data);
358
}
359
360
static void
361
pciereg_cfgwrite(struct pcie_mcfg_region *region, int bus, unsigned slot,
362
unsigned func, unsigned reg, int data, unsigned bytes)
363
{
364
char *va;
365
366
MPASS(bus >= region->minbus && bus <= region->maxbus);
367
368
if (slot > PCI_SLOTMAX || func > PCI_FUNCMAX || reg > PCIE_REGMAX)
369
return;
370
371
va = PCIE_VADDR(region->base, reg, bus - region->minbus, slot, func);
372
373
switch (bytes) {
374
case 4:
375
__asm("movl %1, %0" : "=m" (*(volatile uint32_t *)va)
376
: "a" (data));
377
break;
378
case 2:
379
__asm("movw %1, %0" : "=m" (*(volatile uint16_t *)va)
380
: "a" ((uint16_t)data));
381
break;
382
case 1:
383
__asm("movb %1, %0" : "=m" (*(volatile uint8_t *)va)
384
: "a" ((uint8_t)data));
385
break;
386
}
387
}
388
389