iPXE
ecam.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 2022 Michael Brown <mbrown@fensystems.co.uk>.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 *
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
22 */
23
24FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26#include <string.h>
27#include <errno.h>
28#include <ipxe/ecam.h>
29
30/** @file
31 *
32 * PCI Enhanced Configuration Access Mechanism (ECAM)
33 *
34 */
35
36/** Cached mapped ECAM allocation */
37static struct ecam_mapping ecam;
38
39/**
40 * Find lowest ECAM allocation not below a given PCI bus:dev.fn address
41 *
42 * @v busdevfn PCI bus:dev.fn address
43 * @v range PCI device address range to fill in
44 * @v alloc ECAM allocation to fill in, or NULL
45 * @ret rc Return status code
46 */
48 struct ecam_allocation *alloc ) {
49 struct ecam_table *mcfg;
50 struct ecam_allocation *tmp;
51 unsigned int best = 0;
52 unsigned int count;
53 unsigned int index;
54 unsigned int i;
56
57 /* Return empty range on error */
58 range->count = 0;
59
60 /* Locate MCFG table */
62 struct ecam_table, acpi );
63 if ( ! mcfg ) {
64 DBGC ( &ecam, "ECAM found no MCFG table\n" );
65 return -ENOTSUP;
66 }
67
68 /* Iterate over allocations */
69 for ( i = 0 ; ( offsetof ( typeof ( *mcfg ), alloc[ i + 1 ] ) <=
70 le32_to_cpu ( mcfg->acpi.length ) ) ; i++ ) {
71
72 /* Read allocation */
73 tmp = &mcfg->alloc[i];
74 DBGC2 ( &ecam, "ECAM %04x:[%02x-%02x] has base %08llx\n",
75 le16_to_cpu ( tmp->segment ), tmp->start, tmp->end,
76 ( ( unsigned long long ) le64_to_cpu ( tmp->base ) ) );
77 start = PCI_BUSDEVFN ( le16_to_cpu ( tmp->segment ),
78 tmp->start, 0, 0 );
79 count = PCI_BUSDEVFN ( 0, ( tmp->end - tmp->start + 1 ), 0, 0 );
80
81 /* Check for a matching or new closest allocation */
82 index = ( busdevfn - start );
83 if ( ( index < count ) || ( index > best ) ) {
84 if ( alloc )
85 memcpy ( alloc, tmp, sizeof ( *alloc ) );
86 range->start = start;
87 range->count = count;
88 best = index;
89 }
90
91 /* Stop if this range contains the target bus:dev.fn address */
92 if ( index < count )
93 return 0;
94 }
95
96 return ( best ? 0 : -ENOENT );
97}
98
99/**
100 * Find next PCI bus:dev.fn address range in system
101 *
102 * @v busdevfn Starting PCI bus:dev.fn address
103 * @v range PCI bus:dev.fn address range to fill in
104 */
106
107 /* Find new range, if any */
109}
110
111/**
112 * Access configuration space for PCI device
113 *
114 * @v pci PCI device
115 * @ret rc Return status code
116 */
117static int ecam_access ( struct pci_device *pci ) {
119 size_t len;
120 int rc;
121
122 /* Reuse mapping if possible */
123 if ( ( pci->busdevfn - ecam.range.start ) < ecam.range.count )
124 return ecam.rc;
125
126 /* Clear any existing mapping */
127 if ( ecam.regs ) {
128 iounmap ( ecam.regs );
129 ecam.regs = NULL;
130 }
131
132 /* Find allocation for this PCI device */
133 if ( ( rc = ecam_find ( pci->busdevfn, &ecam.range,
134 &ecam.alloc ) ) != 0 ) {
135 DBGC ( &ecam, "ECAM found no allocation for " PCI_FMT ": %s\n",
136 PCI_ARGS ( pci ), strerror ( rc ) );
137 goto err_find;
138 }
139 if ( ecam.range.start > pci->busdevfn ) {
140 DBGC ( &ecam, "ECAM found no allocation for " PCI_FMT "\n",
141 PCI_ARGS ( pci ) );
142 rc = -ENOENT;
143 goto err_find;
144 }
145
146 /* Map configuration space for this allocation */
147 base = le64_to_cpu ( ecam.alloc.base );
148 base += ( ecam.alloc.start * ECAM_SIZE * PCI_BUSDEVFN ( 0, 1, 0, 0 ) );
149 len = ( ecam.range.count * ECAM_SIZE );
150 if ( base != ( ( unsigned long ) base ) ) {
151 DBGC ( &ecam, "ECAM %04x:[%02x-%02x] could not map "
152 "[%08llx,%08llx) outside CPU range\n",
153 le16_to_cpu ( ecam.alloc.segment ), ecam.alloc.start,
154 ecam.alloc.end, base, ( base + len ) );
155 rc = -ERANGE;
156 goto err_range;
157 }
158 ecam.regs = ioremap ( base, len );
159 if ( ! ecam.regs ) {
160 DBGC ( &ecam, "ECAM %04x:[%02x-%02x] could not map "
161 "[%08llx,%08llx)\n", le16_to_cpu ( ecam.alloc.segment ),
162 ecam.alloc.start, ecam.alloc.end, base, ( base + len ) );
163 rc = -ENODEV;
164 goto err_ioremap;
165 }
166
167 /* Populate cached mapping */
168 DBGC ( &ecam, "ECAM %04x:[%02x-%02x] mapped [%08llx,%08llx) -> %p\n",
169 le16_to_cpu ( ecam.alloc.segment ), ecam.alloc.start,
170 ecam.alloc.end, base, ( base + len ), ecam.regs );
171 ecam.rc = 0;
172 return 0;
173
174 iounmap ( ecam.regs );
175 err_ioremap:
176 err_range:
177 err_find:
178 ecam.rc = rc;
179 return rc;
180}
181
182/**
183 * Read from PCI configuration space
184 *
185 * @v pci PCI device
186 * @v location Offset and length within PCI configuration space
187 * @v value Value read
188 * @ret rc Return status code
189 */
190int ecam_read ( struct pci_device *pci, unsigned int location, void *value ) {
191 unsigned int where = ECAM_WHERE ( location );
192 unsigned int len = ECAM_LEN ( location );
193 unsigned int index;
194 void *addr;
195 int rc;
196
197 /* Return all-ones on error */
198 memset ( value, 0xff, len );
199
200 /* Access configuration space */
201 if ( ( rc = ecam_access ( pci ) ) != 0 )
202 return rc;
203
204 /* Read from address */
205 index = ( pci->busdevfn - ecam.range.start );
206 addr = ( ecam.regs + ( index * ECAM_SIZE ) + where );
207 switch ( len ) {
208 case 4:
209 *( ( uint32_t *) value ) = readl ( addr );
210 break;
211 case 2:
212 *( ( uint16_t *) value ) = readw ( addr );
213 break;
214 case 1:
215 *( ( uint8_t *) value ) = readb ( addr );
216 break;
217 default:
218 assert ( 0 );
219 }
220
221 return 0;
222}
223
224/**
225 * Write to PCI configuration space
226 *
227 * @v pci PCI device
228 * @v location Offset and length within PCI configuration space
229 * @v value Value to write
230 * @ret rc Return status code
231 */
232int ecam_write ( struct pci_device *pci, unsigned int location,
233 unsigned long value ) {
234 unsigned int where = ECAM_WHERE ( location );
235 unsigned int len = ECAM_LEN ( location );
236 unsigned int index;
237 void *addr;
238 int rc;
239
240 /* Access configuration space */
241 if ( ( rc = ecam_access ( pci ) ) != 0 )
242 return rc;
243
244 /* Write to address */
245 index = ( pci->busdevfn - ecam.range.start );
246 addr = ( ecam.regs + ( index * ECAM_SIZE ) + where );
247 switch ( len ) {
248 case 4:
249 writel ( value, addr );
250 break;
251 case 2:
252 writew ( value, addr );
253 break;
254 case 1:
255 writeb ( value, addr );
256 break;
257 default:
258 assert ( 0 );
259 }
260
261 /* Read from address, to guarantee completion of the write
262 *
263 * PCIe configuration space registers may not have read side
264 * effects. Reading back is therefore always safe to do, and
265 * guarantees that the write has reached the device.
266 */
267 mb();
268 ecam_read ( pci, location, &value );
269
270 return 0;
271}
272
#define NULL
NULL pointer (VOID *)
Definition Base.h:322
const struct acpi_header * acpi_table(uint32_t signature, unsigned int index)
Locate ACPI table.
Definition acpi.c:93
typeof(acpi_finder=acpi_find)
ACPI table finder.
Definition acpi.c:48
struct arbelprm_rc_send_wqe rc
Definition arbel.h:3
pseudo_bit_t value[0x00020]
Definition arbel.h:2
unsigned short uint16_t
Definition stdint.h:11
unsigned int uint32_t
Definition stdint.h:12
unsigned long long uint64_t
Definition stdint.h:13
unsigned char uint8_t
Definition stdint.h:10
long index
Definition bigint.h:65
#define assert(condition)
Assert a condition at run-time.
Definition assert.h:50
ring len
Length.
Definition dwmac.h:226
uint32_t addr
Buffer address.
Definition dwmac.h:9
static int ecam_find(uint32_t busdevfn, struct pci_range *range, struct ecam_allocation *alloc)
Find lowest ECAM allocation not below a given PCI bus:dev.fn address.
Definition ecam.c:47
static int ecam_access(struct pci_device *pci)
Access configuration space for PCI device.
Definition ecam.c:117
static void ecam_discover(uint32_t busdevfn, struct pci_range *range)
Find next PCI bus:dev.fn address range in system.
Definition ecam.c:105
int ecam_write(struct pci_device *pci, unsigned int location, unsigned long value)
Write to PCI configuration space.
Definition ecam.c:232
int ecam_read(struct pci_device *pci, unsigned int location, void *value)
Read from PCI configuration space.
Definition ecam.c:190
static struct ecam_mapping ecam
Cached mapped ECAM allocation.
Definition ecam.c:37
PCI I/O API for Enhanced Configuration Access Mechanism (ECAM)
#define ECAM_SIGNATURE
Enhanced Configuration Access Mechanism table signature.
Definition ecam.h:19
#define ECAM_SIZE
Enhanced Configuration Access Mechanism per-device size.
Definition ecam.h:16
#define ECAM_WHERE(location)
Extract offset from ECAM location.
Definition ecam_io.h:27
#define ECAM_LEN(location)
Extract length from ECAM location.
Definition ecam_io.h:30
static EFI_ACPI_TABLE_PROTOCOL * acpi
ACPI table protocol protocol.
Definition efi_block.c:67
uint16_t busdevfn
PCI bus:dev.fn address.
Definition ena.h:17
Error codes.
#define DBGC2(...)
Definition compiler.h:522
#define DBGC(...)
Definition compiler.h:505
uint32_t start
Starting offset.
Definition netvsc.h:1
static unsigned int count
Number of entries.
Definition dwmac.h:220
#define FILE_LICENCE(_licence)
Declare a particular licence as applying to a file.
Definition compiler.h:896
#define ENOENT
No such file or directory.
Definition errno.h:515
#define ENOTSUP
Operation not supported.
Definition errno.h:590
#define ERANGE
Result too large.
Definition errno.h:640
#define ENODEV
No such device.
Definition errno.h:510
#define le16_to_cpu(value)
Definition byteswap.h:113
#define le64_to_cpu(value)
Definition byteswap.h:115
#define le32_to_cpu(value)
Definition byteswap.h:114
void mb(void)
Memory barrier.
void * ioremap(unsigned long bus_addr, size_t len)
Map bus address as an I/O address.
void iounmap(volatile const void *io_addr)
Unmap I/O address.
int pci_read_config_dword(struct pci_device *pci, unsigned int where, uint32_t *value)
Read 32-bit dword from PCI configuration space.
int pci_can_probe(struct pci_device *pci)
Check if PCI bus probing is allowed.
void pci_discover(uint32_t busdevfn, struct pci_range *range)
Find next PCI bus:dev.fn address range in system.
int pci_read_config_word(struct pci_device *pci, unsigned int where, uint16_t *value)
Read 16-bit word from PCI configuration space.
void * pci_ioremap(struct pci_device *pci, unsigned long bus_addr, size_t len)
Map PCI bus address as an I/O address.
#define PCI_BUSDEVFN(segment, bus, slot, func)
Definition pci_io.h:30
#define PCIAPI_PRIORITY_ECAM
ACPI ECAM.
Definition pci_io.h:197
#define PROVIDE_PCIAPI_RUNTIME(subsys, priority)
Provide a runtime selectable PCI I/O API.
Definition pci_io.h:202
int pci_write_config_byte(struct pci_device *pci, unsigned int where, uint8_t value)
Write byte to PCI configuration space.
int pci_write_config_word(struct pci_device *pci, unsigned int where, uint16_t value)
Write 16-bit word to PCI configuration space.
int pci_read_config_byte(struct pci_device *pci, unsigned int where, uint8_t *value)
Read byte from PCI configuration space.
#define PROVIDE_PCIAPI(_subsys, _api_func, _func)
Provide a PCI I/O API implementation.
Definition pci_io.h:51
int pci_write_config_dword(struct pci_device *pci, unsigned int where, uint32_t value)
Write 32-bit dword to PCI configuration space.
#define PROVIDE_PCIAPI_INLINE(_subsys, _api_func)
Provide a static inline PCI I/O API implementation.
Definition pci_io.h:60
String functions.
void * memcpy(void *dest, const void *src, size_t len) __nonnull
void * memset(void *dest, int character, size_t len) __nonnull
uint32_t base
Base.
Definition librm.h:3
unsigned long tmp
Definition linux_pci.h:65
#define PCI_FMT
PCI device debug message format.
Definition pci.h:312
#define PCI_ARGS(pci)
PCI device debug message arguments.
Definition pci.h:315
static __always_inline int unsigned int where
Definition pcibios.h:57
struct pci_range range
PCI bus:dev.fn address range.
Definition pcicloud.c:40
#define offsetof(type, field)
Get offset of a field within a structure.
Definition stddef.h:25
#define container_of(ptr, type, field)
Get containing structure.
Definition stddef.h:36
char * strerror(int errno)
Retrieve string representation of error number.
Definition strerror.c:79
uint32_t length
Length of table, in bytes, including header.
Definition acpi.h:184
An Enhanced Configuration Access Mechanism allocation.
Definition ecam.h:22
A mapped Enhanced Configuration Access Mechanism allocation.
Definition ecam.h:46
struct ecam_allocation alloc
Allocation.
Definition ecam.h:48
An Enhanced Configuration Access Mechanism table.
Definition ecam.h:36
struct acpi_header acpi
ACPI header.
Definition ecam.h:38
struct ecam_allocation alloc[0]
Allocation structures.
Definition ecam.h:42
A PCI device.
Definition pci.h:211
uint32_t busdevfn
Segment, bus, device, and function (bus:dev.fn) number.
Definition pci.h:238
A PCI bus:dev.fn address range.
Definition pci_io.h:23
#define readl
Definition w89c840.c:157
#define writel
Definition w89c840.c:160
#define writew
Definition w89c840.c:159
#define readb
Definition w89c840.c:155
#define writeb
Definition w89c840.c:158
#define readw
Definition w89c840.c:156