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 0;
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  goto err_find;
149  }
150 
151  /* Map configuration space for this allocation */
153  len = ( ecam.range.count * ECAM_SIZE );
154  ecam.regs = ioremap ( base, len );
155  if ( ! ecam.regs ) {
156  DBGC ( &ecam, "ECAM %04x:[%02x-%02x] could not map "
157  "[%08llx,%08llx)\n", le16_to_cpu ( ecam.alloc.segment ),
158  ecam.alloc.start, ecam.alloc.end, base, ( base + len ) );
159  rc = -ENODEV;
160  goto err_ioremap;
161  }
162 
163  /* Populate cached mapping */
164  DBGC ( &ecam, "ECAM %04x:[%02x-%02x] mapped [%08llx,%08llx) -> %p\n",
166  ecam.alloc.end, base, ( base + len ), ecam.regs );
167  return 0;
168 
169  iounmap ( ecam.regs );
170  err_ioremap:
171  err_find:
172  ecam.range.count = 0;
173  return rc;
174 }
175 
176 /**
177  * Read from PCI configuration space
178  *
179  * @v pci PCI device
180  * @v location Offset and length within PCI configuration space
181  * @v value Value read
182  * @ret rc Return status code
183  */
184 int ecam_read ( struct pci_device *pci, unsigned int location, void *value ) {
185  unsigned int where = ECAM_WHERE ( location );
186  unsigned int len = ECAM_LEN ( location );
187  unsigned int index;
188  void *addr;
189  int rc;
190 
191  /* Return all-ones on error */
192  memset ( value, 0xff, len );
193 
194  /* Access configuration space */
195  if ( ( rc = ecam_access ( pci ) ) != 0 )
196  return rc;
197 
198  /* Read from address */
199  index = ( pci->busdevfn - ecam.range.start );
200  addr = ( ecam.regs + ( index * ECAM_SIZE ) + where );
201  switch ( len ) {
202  case 4:
203  *( ( uint32_t *) value ) = readl ( addr );
204  break;
205  case 2:
206  *( ( uint16_t *) value ) = readw ( addr );
207  break;
208  case 1:
209  *( ( uint8_t *) value ) = readb ( addr );
210  break;
211  default:
212  assert ( 0 );
213  }
214 
215  return 0;
216 }
217 
218 /**
219  * Write to PCI configuration space
220  *
221  * @v pci PCI device
222  * @v location Offset and length within PCI configuration space
223  * @v value Value to write
224  * @ret rc Return status code
225  */
226 int ecam_write ( struct pci_device *pci, unsigned int location,
227  unsigned long value ) {
228  unsigned int where = ECAM_WHERE ( location );
229  unsigned int len = ECAM_LEN ( location );
230  unsigned int index;
231  void *addr;
232  int rc;
233 
234  /* Access configuration space */
235  if ( ( rc = ecam_access ( pci ) ) != 0 )
236  return rc;
237 
238  /* Read from address */
239  index = ( pci->busdevfn - ecam.range.start );
240  addr = ( ecam.regs + ( index * ECAM_SIZE ) + where );
241  switch ( len ) {
242  case 4:
243  writel ( value, addr );
244  break;
245  case 2:
246  writew ( value, addr );
247  break;
248  case 1:
249  writeb ( value, addr );
250  break;
251  default:
252  assert ( 0 );
253  }
254 
255  return 0;
256 }
257 
266 
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)
Access to external ("user") memory.
struct pci_api ecam_api
Definition: ecam.c:267
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:184
static EFI_ACPI_TABLE_PROTOCOL * acpi
ACPI table protocol protocol.
Definition: efi_block.c:62
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
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
#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
uint16_t base
Base address.
Definition: edd.h:14
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:226
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
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.