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 
24 FILE_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 */
37 static 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  */
47 static int ecam_find ( uint32_t busdevfn, struct pci_range *range,
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 */
61  mcfg = container_of ( acpi_table ( ECAM_SIGNATURE, 0 ),
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  */
105 static void ecam_discover ( uint32_t busdevfn, struct pci_range *range ) {
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  */
117 static int ecam_access ( struct pci_device *pci ) {
118  uint64_t base;
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 */
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",
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",
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  */
190 int 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  */
232 int 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 
static int ecam_access(struct pci_device *pci)
Access configuration space for PCI device.
Definition: ecam.c:117
uint32_t base
Base.
Definition: librm.h:138
uint32_t start
Starting bus:dev.fn address.
Definition: pci_io.h:24
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
unsigned short uint16_t
Definition: stdint.h:11
struct ecam_allocation alloc
Allocation.
Definition: ecam.h:48
uint8_t readb(volatile uint8_t *io_addr)
Read byte from memory-mapped device.
struct pci_range range
PCI bus:dev.fn address range.
Definition: pcicloud.c:40
struct pci_range range
PCI bus:dev.fn address range.
Definition: ecam.h:50
#define le32_to_cpu(value)
Definition: byteswap.h:113
int pci_can_probe(struct pci_device *pci)
Check if PCI bus probing is allowed.
Error codes.
uint16_t readw(volatile uint16_t *io_addr)
Read 16-bit word from memory-mapped device.
int pci_write_config_word(struct pci_device *pci, unsigned int where, uint16_t value)
Write 16-bit word to PCI configuration space.
A mapped Enhanced Configuration Access Mechanism allocation.
Definition: ecam.h:46
uint32_t readl(volatile uint32_t *io_addr)
Read 32-bit dword from memory-mapped device.
#define ECAM_SIGNATURE
Enhanced Configuration Access Mechanism table signature.
Definition: ecam.h:19
#define DBGC(...)
Definition: compiler.h:505
long index
Definition: bigint.h:62
#define ENOENT
No such file or directory.
Definition: errno.h:514
static struct ecam_mapping ecam
Cached mapped ECAM allocation.
Definition: ecam.c:37
unsigned long long uint64_t
Definition: stdint.h:13
PROVIDE_PCIAPI_INLINE(ecam, pci_can_probe)
int pci_read_config_word(struct pci_device *pci, unsigned int where, uint16_t *value)
Read 16-bit word from PCI configuration space.
const struct acpi_header * acpi_table(uint32_t signature, unsigned int index)
Locate ACPI table.
Definition: acpi.c:92
#define offsetof(type, field)
Get offset of a field within a structure.
Definition: stddef.h:24
void writeb(uint8_t data, volatile uint8_t *io_addr)
Write byte to memory-mapped device.
#define ENOTSUP
Operation not supported.
Definition: errno.h:589
uint32_t start
Starting offset.
Definition: netvsc.h:12
unsigned long tmp
Definition: linux_pci.h:64
uint16_t busdevfn
PCI bus:dev.fn address.
Definition: ena.h:28
void * memcpy(void *dest, const void *src, size_t len) __nonnull
assert((readw(&hdr->flags) &(GTF_reading|GTF_writing))==0)
#define container_of(ptr, type, field)
Get containing structure.
Definition: stddef.h:35
int pci_read_config_dword(struct pci_device *pci, unsigned int where, uint32_t *value)
Read 32-bit dword from PCI configuration space.
uint32_t length
Length of table, in bytes, including header.
Definition: acpi.h:183
pseudo_bit_t value[0x00020]
Definition: arbel.h:13
int ecam_read(struct pci_device *pci, unsigned int location, void *value)
Read from PCI configuration space.
Definition: ecam.c:190
static EFI_ACPI_TABLE_PROTOCOL * acpi
ACPI table protocol protocol.
Definition: efi_block.c:66
ring len
Length.
Definition: dwmac.h:231
void writel(uint32_t data, volatile uint32_t *io_addr)
Write 32-bit dword to memory-mapped device.
A PCI bus:dev.fn address range.
Definition: pci_io.h:22
static unsigned int count
Number of entries.
Definition: dwmac.h:225
#define PCIAPI_PRIORITY_ECAM
ACPI ECAM.
Definition: pci_io.h:196
struct acpi_header acpi
ACPI header.
Definition: ecam.h:38
uint64_t base
Base address.
Definition: ecam.h:24
#define PCI_BUSDEVFN(segment, bus, slot, func)
Definition: pci_io.h:29
#define ERANGE
Result too large.
Definition: errno.h:639
char * strerror(int errno)
Retrieve string representation of error number.
Definition: strerror.c:78
int pci_write_config_byte(struct pci_device *pci, unsigned int where, uint8_t value)
Write byte to PCI configuration space.
#define PCI_FMT
PCI device debug message format.
Definition: pci.h:311
A PCI device.
Definition: pci.h:210
uint32_t addr
Buffer address.
Definition: dwmac.h:20
PCI I/O API for Enhanced Configuration Access Mechanism (ECAM)
#define ENODEV
No such device.
Definition: errno.h:509
void * regs
MMIO base address.
Definition: ecam.h:52
unsigned char uint8_t
Definition: stdint.h:10
int rc
Mapping result.
Definition: ecam.h:54
#define ECAM_LEN(location)
Extract length from ECAM location.
Definition: ecam_io.h:29
#define le16_to_cpu(value)
Definition: byteswap.h:112
unsigned int uint32_t
Definition: stdint.h:12
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
unsigned int count
Number of bus:dev.fn addresses within this range.
Definition: pci_io.h:26
void pci_discover(uint32_t busdevfn, struct pci_range *range)
Find next PCI bus:dev.fn address range in system.
uint32_t busdevfn
Segment, bus, device, and function (bus:dev.fn) number.
Definition: pci.h:237
int ecam_write(struct pci_device *pci, unsigned int location, unsigned long value)
Write to PCI configuration space.
Definition: ecam.c:232
uint16_t segment
PCI segment number.
Definition: ecam.h:26
PROVIDE_PCIAPI_RUNTIME(ecam, PCIAPI_PRIORITY_ECAM)
#define DBGC2(...)
Definition: compiler.h:522
#define writew
Definition: w89c840.c:159
#define ECAM_SIZE
Enhanced Configuration Access Mechanism per-device size.
Definition: ecam.h:16
An Enhanced Configuration Access Mechanism allocation.
Definition: ecam.h:22
uint8_t start
Start PCI bus number.
Definition: ecam.h:28
#define PCI_ARGS(pci)
PCI device debug message arguments.
Definition: pci.h:314
int pci_write_config_dword(struct pci_device *pci, unsigned int where, uint32_t value)
Write 32-bit dword to PCI configuration space.
void iounmap(volatile const void *io_addr)
Unmap I/O address.
static __always_inline int unsigned int where
Definition: pcibios.h:56
uint8_t end
End PCI bus number.
Definition: ecam.h:30
An Enhanced Configuration Access Mechanism table.
Definition: ecam.h:36
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
void mb(void)
Memory barrier.
typeof(acpi_finder=acpi_find)
ACPI table finder.
Definition: acpi.c:47
PROVIDE_PCIAPI(ecam, pci_discover, ecam_discover)
void * ioremap(unsigned long bus_addr, size_t len)
Map bus address as an I/O address.
void * pci_ioremap(struct pci_device *pci, unsigned long bus_addr, size_t len)
Map PCI bus address as an I/O address.
#define ECAM_WHERE(location)
Extract offset from ECAM location.
Definition: ecam_io.h:26
#define le64_to_cpu(value)
Definition: byteswap.h:114
#define NULL
NULL pointer (VOID *)
Definition: Base.h:321
String functions.
struct ecam_allocation alloc[0]
Allocation structures.
Definition: ecam.h:42
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
void * memset(void *dest, int character, size_t len) __nonnull
int pci_read_config_byte(struct pci_device *pci, unsigned int where, uint8_t *value)
Read byte from PCI configuration space.