1 /** @file
2   Plug a PciSegmentLib backend into PciCapLib, for config space access.
3 
4   Copyright (C) 2018, Red Hat, Inc.
5 
6   SPDX-License-Identifier: BSD-2-Clause-Patent
7 **/
8 
9 #include <IndustryStandard/Pci23.h>
10 
11 #include <Library/BaseLib.h>
12 #include <Library/MemoryAllocationLib.h>
13 #include <Library/PciSegmentLib.h>
14 
15 #include "BasePciCapPciSegmentLib.h"
16 
17 
18 /**
19   Read the config space of a given PCI device (both normal and extended).
20 
21   SegmentDevReadConfig() performs as few config space accesses as possible
22   (without attempting 64-bit wide accesses).
23 
24   @param[in] PciDevice           Implementation-specific unique representation
25                                  of the PCI device in the PCI hierarchy.
26 
27   @param[in] SourceOffset        Source offset in the config space of the PCI
28                                  device to start reading from.
29 
30   @param[out] DestinationBuffer  Buffer to store the read data to.
31 
32   @param[in] Size                The number of bytes to transfer.
33 
34   @retval RETURN_SUCCESS      Size bytes have been transferred from config
35                               space to DestinationBuffer.
36 
37   @retval RETURN_UNSUPPORTED  Accessing Size bytes from SourceOffset exceeds
38                               the config space limit of the PCI device.
39                               Although PCI_CAP_DEV_READ_CONFIG allows reading
40                               fewer than Size bytes in this case,
41                               SegmentDevReadConfig() will read none.
42 **/
43 STATIC
44 RETURN_STATUS
45 EFIAPI
SegmentDevReadConfig(IN PCI_CAP_DEV * PciDevice,IN UINT16 SourceOffset,OUT VOID * DestinationBuffer,IN UINT16 Size)46 SegmentDevReadConfig (
47   IN  PCI_CAP_DEV *PciDevice,
48   IN  UINT16      SourceOffset,
49   OUT VOID        *DestinationBuffer,
50   IN  UINT16      Size
51   )
52 {
53   SEGMENT_DEV *SegmentDev;
54   UINT16      ConfigSpaceSize;
55   UINT64      SourceAddress;
56 
57   SegmentDev = SEGMENT_DEV_FROM_PCI_CAP_DEV (PciDevice);
58   ConfigSpaceSize = (SegmentDev->MaxDomain == PciCapNormal ?
59                      PCI_MAX_CONFIG_OFFSET : PCI_EXP_MAX_CONFIG_OFFSET);
60   //
61   // Note that all UINT16 variables below are promoted to INT32, and the
62   // addition and the comparison is carried out in INT32.
63   //
64   if (SourceOffset + Size > ConfigSpaceSize) {
65     return RETURN_UNSUPPORTED;
66   }
67   SourceAddress = PCI_SEGMENT_LIB_ADDRESS (SegmentDev->SegmentNr,
68                     SegmentDev->BusNr, SegmentDev->DeviceNr,
69                     SegmentDev->FunctionNr, SourceOffset);
70   PciSegmentReadBuffer (SourceAddress, Size, DestinationBuffer);
71   return RETURN_SUCCESS;
72 }
73 
74 
75 /**
76   Write the config space of a given PCI device (both normal and extended).
77 
78   SegmentDevWriteConfig() performs as few config space accesses as possible
79   (without attempting 64-bit wide accesses).
80 
81   @param[in] PciDevice          Implementation-specific unique representation
82                                 of the PCI device in the PCI hierarchy.
83 
84   @param[in] DestinationOffset  Destination offset in the config space of the
85                                 PCI device to start writing at.
86 
87   @param[in] SourceBuffer       Buffer to read the data to be stored from.
88 
89   @param[in] Size               The number of bytes to transfer.
90 
91   @retval RETURN_SUCCESS      Size bytes have been transferred from
92                               SourceBuffer to config space.
93 
94   @retval RETURN_UNSUPPORTED  Accessing Size bytes at DestinationOffset exceeds
95                               the config space limit of the PCI device.
96                               Although PCI_CAP_DEV_WRITE_CONFIG allows writing
97                               fewer than Size bytes in this case,
98                               SegmentDevWriteConfig() will write none.
99 **/
100 STATIC
101 RETURN_STATUS
102 EFIAPI
SegmentDevWriteConfig(IN PCI_CAP_DEV * PciDevice,IN UINT16 DestinationOffset,IN VOID * SourceBuffer,IN UINT16 Size)103 SegmentDevWriteConfig (
104   IN PCI_CAP_DEV *PciDevice,
105   IN UINT16      DestinationOffset,
106   IN VOID        *SourceBuffer,
107   IN UINT16      Size
108   )
109 {
110   SEGMENT_DEV *SegmentDev;
111   UINT16      ConfigSpaceSize;
112   UINT64      DestinationAddress;
113 
114   SegmentDev = SEGMENT_DEV_FROM_PCI_CAP_DEV (PciDevice);
115   ConfigSpaceSize = (SegmentDev->MaxDomain == PciCapNormal ?
116                      PCI_MAX_CONFIG_OFFSET : PCI_EXP_MAX_CONFIG_OFFSET);
117   //
118   // Note that all UINT16 variables below are promoted to INT32, and the
119   // addition and the comparison is carried out in INT32.
120   //
121   if (DestinationOffset + Size > ConfigSpaceSize) {
122     return RETURN_UNSUPPORTED;
123   }
124   DestinationAddress = PCI_SEGMENT_LIB_ADDRESS (SegmentDev->SegmentNr,
125                          SegmentDev->BusNr, SegmentDev->DeviceNr,
126                          SegmentDev->FunctionNr, DestinationOffset);
127   PciSegmentWriteBuffer (DestinationAddress, Size, SourceBuffer);
128   return RETURN_SUCCESS;
129 }
130 
131 
132 /**
133   Create a PCI_CAP_DEV object from the PCI Segment:Bus:Device.Function
134   quadruplet. The config space accessors are based upon PciSegmentLib.
135 
136   @param[in] MaxDomain   If MaxDomain is PciCapExtended, then
137                          PciDevice->ReadConfig() and PciDevice->WriteConfig()
138                          will delegate extended config space accesses too to
139                          PciSegmentReadBuffer() and PciSegmentWriteBuffer(),
140                          respectively. Otherwise, PciDevice->ReadConfig() and
141                          PciDevice->WriteConfig() will reject accesses to
142                          extended config space with RETURN_UNSUPPORTED, without
143                          calling PciSegmentReadBuffer() or
144                          PciSegmentWriteBuffer(). By setting MaxDomain to
145                          PciCapNormal, the platform can prevent undefined
146                          PciSegmentLib behavior when the PCI root bridge under
147                          the PCI device at Segment:Bus:Device.Function doesn't
148                          support extended config space.
149 
150   @param[in] Segment     16-bit wide segment number.
151 
152   @param[in] Bus         8-bit wide bus number.
153 
154   @param[in] Device      5-bit wide device number.
155 
156   @param[in] Function    3-bit wide function number.
157 
158   @param[out] PciDevice  The PCI_CAP_DEV object constructed as described above.
159                          PciDevice can be passed to the PciCapLib APIs.
160 
161   @retval RETURN_SUCCESS            PciDevice has been constructed and output.
162 
163   @retval RETURN_INVALID_PARAMETER  Device or Function does not fit in the
164                                     permitted number of bits.
165 
166   @retval RETURN_OUT_OF_RESOURCES   Memory allocation failed.
167 **/
168 RETURN_STATUS
169 EFIAPI
PciCapPciSegmentDeviceInit(IN PCI_CAP_DOMAIN MaxDomain,IN UINT16 Segment,IN UINT8 Bus,IN UINT8 Device,IN UINT8 Function,OUT PCI_CAP_DEV ** PciDevice)170 PciCapPciSegmentDeviceInit (
171   IN  PCI_CAP_DOMAIN MaxDomain,
172   IN  UINT16         Segment,
173   IN  UINT8          Bus,
174   IN  UINT8          Device,
175   IN  UINT8          Function,
176   OUT PCI_CAP_DEV    **PciDevice
177   )
178 {
179   SEGMENT_DEV *SegmentDev;
180 
181   if (Device > PCI_MAX_DEVICE || Function > PCI_MAX_FUNC) {
182     return RETURN_INVALID_PARAMETER;
183   }
184 
185   SegmentDev = AllocatePool (sizeof *SegmentDev);
186   if (SegmentDev == NULL) {
187     return RETURN_OUT_OF_RESOURCES;
188   }
189 
190   SegmentDev->Signature              = SEGMENT_DEV_SIG;
191   SegmentDev->MaxDomain              = MaxDomain;
192   SegmentDev->SegmentNr              = Segment;
193   SegmentDev->BusNr                  = Bus;
194   SegmentDev->DeviceNr               = Device;
195   SegmentDev->FunctionNr             = Function;
196   SegmentDev->BaseDevice.ReadConfig  = SegmentDevReadConfig;
197   SegmentDev->BaseDevice.WriteConfig = SegmentDevWriteConfig;
198 
199   *PciDevice = &SegmentDev->BaseDevice;
200   return RETURN_SUCCESS;
201 }
202 
203 
204 /**
205   Free the resources used by PciDevice.
206 
207   @param[in] PciDevice  The PCI_CAP_DEV object to free, originally produced by
208                         PciCapPciSegmentDeviceInit().
209 **/
210 VOID
211 EFIAPI
PciCapPciSegmentDeviceUninit(IN PCI_CAP_DEV * PciDevice)212 PciCapPciSegmentDeviceUninit (
213   IN PCI_CAP_DEV *PciDevice
214   )
215 {
216   SEGMENT_DEV *SegmentDev;
217 
218   SegmentDev = SEGMENT_DEV_FROM_PCI_CAP_DEV (PciDevice);
219   FreePool (SegmentDev);
220 }
221