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 <errno.h>
27 #include <ipxe/uaccess.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_allocation tmp;
50  unsigned int best = 0;
51  unsigned int offset;
52  unsigned int count;
53  unsigned int index;
54  userptr_t mcfg;
57 
58  /* Return empty range on error */
59  range->count = 0;
60 
61  /* Locate MCFG table */
62  mcfg = acpi_table ( ECAM_SIGNATURE, 0 );
63  if ( ! mcfg ) {
64  DBGC ( &ecam, "ECAM found no MCFG table\n" );
65  return -ENOTSUP;
66  }
67 
68  /* Get length of table */
69  copy_from_user ( &length, mcfg,
70  offsetof ( struct ecam_table, acpi.length ),
71  sizeof ( length ) );
72 
73  /* Iterate over allocations */
74  for ( offset = offsetof ( struct ecam_table, alloc ) ;
75  ( offset + sizeof ( tmp ) ) <= le32_to_cpu ( length ) ;
76  offset += sizeof ( tmp ) ) {
77 
78  /* Read allocation */
79  copy_from_user ( &tmp, mcfg, offset, sizeof ( tmp ) );
80  DBGC2 ( &ecam, "ECAM %04x:[%02x-%02x] has base %08llx\n",
81  le16_to_cpu ( tmp.segment ), tmp.start, tmp.end,
82  ( ( unsigned long long ) le64_to_cpu ( tmp.base ) ) );
83  start = PCI_BUSDEVFN ( le16_to_cpu ( tmp.segment ),
84  tmp.start, 0, 0 );
85  count = PCI_BUSDEVFN ( 0, ( tmp.end - tmp.start + 1 ), 0, 0 );
86 
87  /* Check for a matching or new closest allocation */
88  index = ( busdevfn - start );
89  if ( ( index < count ) || ( index > best ) ) {
90  if ( alloc )
91  memcpy ( alloc, &tmp, sizeof ( *alloc ) );
92  range->start = start;
93  range->count = count;
94  best = index;
95  }
96 
97  /* Stop if this range contains the target bus:dev.fn address */
98  if ( index < count )
99  return 0;
100  }
101 
102  return ( best ? 0 : -ENOENT );
103 }
104 
105 /**
106  * Find next PCI bus:dev.fn address range in system
107  *
108  * @v busdevfn Starting PCI bus:dev.fn address
109  * @v range PCI bus:dev.fn address range to fill in
110  */
111 static void ecam_discover ( uint32_t busdevfn, struct pci_range *range ) {
112 
113  /* Find new range, if any */
115 }
116 
117 /**
118  * Access configuration space for PCI device
119  *
120  * @v pci PCI device
121  * @ret rc Return status code
122  */
123 static int ecam_access ( struct pci_device *pci ) {
124  uint64_t base;
125  size_t len;
126  int rc;
127 
128  /* Reuse mapping if possible */
129  if ( ( pci->busdevfn - ecam.range.start ) < ecam.range.count )
130  return ecam.rc;
131 
132  /* Clear any existing mapping */
133  if ( ecam.regs ) {
134  iounmap ( ecam.regs );
135  ecam.regs = NULL;
136  }
137 
138  /* Find allocation for this PCI device */
139  if ( ( rc = ecam_find ( pci->busdevfn, &ecam.range,
140  &ecam.alloc ) ) != 0 ) {
141  DBGC ( &ecam, "ECAM found no allocation for " PCI_FMT ": %s\n",
142  PCI_ARGS ( pci ), strerror ( rc ) );
143  goto err_find;
144  }
145  if ( ecam.range.start > pci->busdevfn ) {
146  DBGC ( &ecam, "ECAM found no allocation for " PCI_FMT "\n",
147  PCI_ARGS ( pci ) );
148  rc = -ENOENT;
149  goto err_find;
150  }
151 
152  /* Map configuration space for this allocation */
154  base += ( ecam.alloc.start * ECAM_SIZE * PCI_BUSDEVFN ( 0, 1, 0, 0 ) );
155  len = ( ecam.range.count * ECAM_SIZE );
156  if ( base != ( ( unsigned long ) base ) ) {
157  DBGC ( &ecam, "ECAM %04x:[%02x-%02x] could not map "
158  "[%08llx,%08llx) outside CPU range\n",
160  ecam.alloc.end, base, ( base + len ) );
161  rc = -ERANGE;
162  goto err_range;
163  }
164  ecam.regs = ioremap ( base, len );
165  if ( ! ecam.regs ) {
166  DBGC ( &ecam, "ECAM %04x:[%02x-%02x] could not map "
167  "[%08llx,%08llx)\n", le16_to_cpu ( ecam.alloc.segment ),
168  ecam.alloc.start, ecam.alloc.end, base, ( base + len ) );
169  rc = -ENODEV;
170  goto err_ioremap;
171  }
172 
173  /* Populate cached mapping */
174  DBGC ( &ecam, "ECAM %04x:[%02x-%02x] mapped [%08llx,%08llx) -> %p\n",
176  ecam.alloc.end, base, ( base + len ), ecam.regs );
177  ecam.rc = 0;
178  return 0;
179 
180  iounmap ( ecam.regs );
181  err_ioremap:
182  err_range:
183  err_find:
184  ecam.rc = rc;
185  return rc;
186 }
187 
188 /**
189  * Read from PCI configuration space
190  *
191  * @v pci PCI device
192  * @v location Offset and length within PCI configuration space
193  * @v value Value read
194  * @ret rc Return status code
195  */
196 int ecam_read ( struct pci_device *pci, unsigned int location, void *value ) {
197  unsigned int where = ECAM_WHERE ( location );
198  unsigned int len = ECAM_LEN ( location );
199  unsigned int index;
200  void *addr;
201  int rc;
202 
203  /* Return all-ones on error */
204  memset ( value, 0xff, len );
205 
206  /* Access configuration space */
207  if ( ( rc = ecam_access ( pci ) ) != 0 )
208  return rc;
209 
210  /* Read from address */
211  index = ( pci->busdevfn - ecam.range.start );
212  addr = ( ecam.regs + ( index * ECAM_SIZE ) + where );
213  switch ( len ) {
214  case 4:
215  *( ( uint32_t *) value ) = readl ( addr );
216  break;
217  case 2:
218  *( ( uint16_t *) value ) = readw ( addr );
219  break;
220  case 1:
221  *( ( uint8_t *) value ) = readb ( addr );
222  break;
223  default:
224  assert ( 0 );
225  }
226 
227  return 0;
228 }
229 
230 /**
231  * Write to PCI configuration space
232  *
233  * @v pci PCI device
234  * @v location Offset and length within PCI configuration space
235  * @v value Value to write
236  * @ret rc Return status code
237  */
238 int ecam_write ( struct pci_device *pci, unsigned int location,
239  unsigned long value ) {
240  unsigned int where = ECAM_WHERE ( location );
241  unsigned int len = ECAM_LEN ( location );
242  unsigned int index;
243  void *addr;
244  int rc;
245 
246  /* Access configuration space */
247  if ( ( rc = ecam_access ( pci ) ) != 0 )
248  return rc;
249 
250  /* Write to address */
251  index = ( pci->busdevfn - ecam.range.start );
252  addr = ( ecam.regs + ( index * ECAM_SIZE ) + where );
253  switch ( len ) {
254  case 4:
255  writel ( value, addr );
256  break;
257  case 2:
258  writew ( value, addr );
259  break;
260  case 1:
261  writeb ( value, addr );
262  break;
263  default:
264  assert ( 0 );
265  }
266 
267  /* Read from address, to guarantee completion of the write
268  *
269  * PCIe configuration space registers may not have read side
270  * effects. Reading back is therefore always safe to do, and
271  * guarantees that the write has reached the device.
272  */
273  mb();
274  ecam_read ( pci, location, &value );
275 
276  return 0;
277 }
278 
287 
static int ecam_access(struct pci_device *pci)
Access configuration space for PCI device.
Definition: ecam.c:123
u16 length
Definition: sky2.h:9
uint32_t start
Starting bus:dev.fn address.
Definition: pci_io.h:23
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.
static __always_inline void struct pci_range * range
Definition: efi_pci_api.h:43
struct pci_range range
PCI bus:dev.fn address range.
Definition: ecam.h:50
#define le32_to_cpu(value)
Definition: byteswap.h:113
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.
static __always_inline void copy_from_user(void *dest, userptr_t src, off_t src_off, size_t len)
Copy data from user buffer.
Definition: uaccess.h:337
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
userptr_t acpi_table(uint32_t signature, unsigned int index)
Locate ACPI table.
Definition: acpi.c:98
#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
int pci_read_config_word(struct pci_device *pci, unsigned int where, uint16_t *value)
Read 16-bit word from PCI configuration space.
#define offsetof(type, field)
Get offset of a field within a structure.
Definition: stddef.h:24
PROVIDE_PCIAPI_INLINE(ecam, pci_read_config_byte)
static const void * base
Base address.
Definition: crypto.h:335
Access to external ("user") memory.
struct pci_api ecam_api
Definition: ecam.c:288
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:53
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)
int pci_read_config_dword(struct pci_device *pci, unsigned int where, uint32_t *value)
Read 32-bit dword from PCI configuration space.
#define PCIAPI_RUNTIME(_subsys)
Provide a runtime selectable PCI I/O API.
Definition: pci_io.h:167
int ecam_read(struct pci_device *pci, unsigned int location, void *value)
Read from PCI configuration space.
Definition: ecam.c:196
static EFI_ACPI_TABLE_PROTOCOL * acpi
ACPI table protocol protocol.
Definition: efi_block.c:65
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:21
static userptr_t size_t offset
Offset of the first segment within the content.
Definition: deflate.h:259
uint64_t base
Base address.
Definition: ecam.h:24
pseudo_bit_t value[0x00020]
Definition: arbel.h:13
#define PCI_BUSDEVFN(segment, bus, slot, func)
Definition: pci_io.h:28
#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:307
A PCI device.
Definition: pci.h:206
A runtime selectable PCI I/O API.
Definition: pci_io.h:154
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
u32 addr
Definition: sky2.h:8
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:25
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:233
int ecam_write(struct pci_device *pci, unsigned int location, unsigned long value)
Write to PCI configuration space.
Definition: ecam.c:238
uint16_t segment
PCI segment number.
Definition: ecam.h:26
uint32_t len
Length.
Definition: ena.h:14
#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
uint16_t count
Number of entries.
Definition: ena.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:310
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.
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.
PROVIDE_PCIAPI(ecam, pci_discover, ecam_discover)
uint64_t index
Index of the first segment within the content.
Definition: pccrc.h:21
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
static __always_inline int unsigned int where
Definition: ecam_io.h:46
unsigned long userptr_t
A pointer to a user buffer.
Definition: uaccess.h:33
static void ecam_discover(uint32_t busdevfn, struct pci_range *range)
Find next PCI bus:dev.fn address range in system.
Definition: ecam.c:111
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.