1 /*++ @file
2   This is the code that publishes the CPU I/O Protocol.
3   The intent herein is to have a single I/O service that can load
4   as early as possible, extend into runtime, and be layered upon by
5   the implementations of architectural protocols and the PCI Root
6   Bridge I/O Protocol.
7 
8 
9 Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.<BR>
10 Portions copyright (c) 2011, Apple Inc. All rights reserved.
11 SPDX-License-Identifier: BSD-2-Clause-Patent
12 
13 **/
14 
15 #include <PiDxe.h>
16 #include <Protocol/Cpu.h>
17 #include <Protocol/CpuIo2.h>
18 
19 #include <Library/BaseLib.h>
20 #include <Library/DebugLib.h>
21 #include <Library/HiiLib.h>
22 #include <Library/UefiLib.h>
23 #include <Library/UefiDriverEntryPoint.h>
24 #include <Library/BaseMemoryLib.h>
25 #include <Library/MemoryAllocationLib.h>
26 #include <Library/UefiBootServicesTableLib.h>
27 #include <CpuDriver.h>
28 
29 #define IA32_MAX_IO_ADDRESS   0xFFFF
30 #define IA32_MAX_MEM_ADDRESS  0xFFFFFFFF
31 
32 EFI_STATUS
33 CpuIoCheckAddressRange (
34   IN  EFI_CPU_IO_PROTOCOL_WIDTH         Width,
35   IN  UINT64                            Address,
36   IN  UINTN                             Count,
37   IN  VOID                              *Buffer,
38   IN  UINT64                            Limit
39   );
40 
41 EFI_STATUS
42 EFIAPI
CpuMemoryServiceRead(IN EFI_CPU_IO2_PROTOCOL * This,IN EFI_CPU_IO_PROTOCOL_WIDTH Width,IN UINT64 Address,IN UINTN Count,IN OUT VOID * Buffer)43 CpuMemoryServiceRead (
44   IN  EFI_CPU_IO2_PROTOCOL              *This,
45   IN  EFI_CPU_IO_PROTOCOL_WIDTH         Width,
46   IN  UINT64                            Address,
47   IN  UINTN                             Count,
48   IN  OUT VOID                          *Buffer
49   )
50 /*++
51 
52 Routine Description:
53 
54   Perform the Memory Access Read service for the CPU I/O Protocol
55 
56 Arguments:
57 
58   Pointer to an instance of the CPU I/O Protocol
59   Width of the Memory Access
60   Address of the Memory access
61   Count of the number of accesses to perform
62   Pointer to the buffer to read or write from memory
63 
64 Returns:
65 
66   Status
67 
68   EFI_SUCCESS             - The data was read from or written to the EFI
69                             System.
70   EFI_INVALID_PARAMETER   - Width is invalid for this EFI System.
71   EFI_INVALID_PARAMETER   - Buffer is NULL.
72   EFI_UNSUPPORTED         - The Buffer is not aligned for the given Width.
73   EFI_UNSUPPORTED         - The address range specified by Address, Width,
74                             and Count is not valid for this EFI System.
75 
76 **/
77 {
78   EFI_STATUS  Status;
79 
80   if (!Buffer) {
81     return EFI_INVALID_PARAMETER;
82   }
83 
84   Status = CpuIoCheckAddressRange (Width, Address, Count, Buffer, IA32_MAX_MEM_ADDRESS);
85   if (EFI_ERROR (Status)) {
86     return Status;
87   }
88 
89   //
90   // Do nothing for Nt32 version
91   //
92   return EFI_SUCCESS;
93 }
94 
95 EFI_STATUS
96 EFIAPI
CpuMemoryServiceWrite(IN EFI_CPU_IO2_PROTOCOL * This,IN EFI_CPU_IO_PROTOCOL_WIDTH Width,IN UINT64 Address,IN UINTN Count,IN OUT VOID * Buffer)97 CpuMemoryServiceWrite (
98   IN EFI_CPU_IO2_PROTOCOL               *This,
99   IN  EFI_CPU_IO_PROTOCOL_WIDTH         Width,
100   IN  UINT64                            Address,
101   IN  UINTN                             Count,
102   IN  OUT VOID                          *Buffer
103   )
104 /*++
105 
106 Routine Description:
107 
108   Perform the Memory Access Read service for the CPU I/O Protocol
109 
110 Arguments:
111 
112   Pointer to an instance of the CPU I/O Protocol
113   Width of the Memory Access
114   Address of the Memory access
115   Count of the number of accesses to perform
116   Pointer to the buffer to read or write from memory
117 
118 Returns:
119 
120   Status
121 
122   EFI_SUCCESS             - The data was read from or written to the EFI System.
123   EFI_INVALID_PARAMETER   - Width is invalid for this EFI System.
124   EFI_INVALID_PARAMETER   - Buffer is NULL.
125   EFI_UNSUPPORTED         - The Buffer is not aligned for the given Width.
126   EFI_UNSUPPORTED         - The address range specified by Address, Width, and
127                             Count is not valid for this EFI System.
128 
129 **/
130 {
131   EFI_STATUS  Status;
132 
133   if (!Buffer) {
134     return EFI_INVALID_PARAMETER;
135   }
136 
137   Status = CpuIoCheckAddressRange (Width, Address, Count, Buffer, IA32_MAX_MEM_ADDRESS);
138   if (EFI_ERROR (Status)) {
139     return Status;
140   }
141 
142   //
143   // Do nothing for Nt32 version
144   //
145   return EFI_SUCCESS;
146 }
147 
148 EFI_STATUS
149 EFIAPI
CpuIoServiceRead(IN EFI_CPU_IO2_PROTOCOL * This,IN EFI_CPU_IO_PROTOCOL_WIDTH Width,IN UINT64 UserAddress,IN UINTN Count,IN OUT VOID * UserBuffer)150 CpuIoServiceRead (
151   IN EFI_CPU_IO2_PROTOCOL               *This,
152   IN  EFI_CPU_IO_PROTOCOL_WIDTH         Width,
153   IN  UINT64                            UserAddress,
154   IN  UINTN                             Count,
155   IN  OUT VOID                          *UserBuffer
156   )
157 /*++
158 
159 Routine Description:
160 
161   This is the service that implements the I/O read
162 
163 Arguments:
164 
165   Pointer to an instance of the CPU I/O Protocol
166   Width of the Memory Access
167   Address of the I/O access
168   Count of the number of accesses to perform
169   Pointer to the buffer to read or write from I/O space
170 
171 Returns:
172 
173   Status
174   EFI_SUCCESS             - The data was read from or written to the EFI System.
175   EFI_INVALID_PARAMETER   - Width is invalid for this EFI System.
176   EFI_INVALID_PARAMETER   - Buffer is NULL.
177   EFI_UNSUPPORTED         - The Buffer is not aligned for the given Width.
178   EFI_UNSUPPORTED         - The address range specified by Address, Width, and
179                             Count is not valid for this EFI System.
180 **/
181 {
182   UINTN       Address;
183   EFI_STATUS  Status;
184 
185   if (!UserBuffer) {
186     return EFI_INVALID_PARAMETER;
187   }
188 
189   Address = (UINTN) UserAddress;
190 
191   if (Width >= EfiCpuIoWidthMaximum) {
192     return EFI_INVALID_PARAMETER;
193   }
194 
195   Status = CpuIoCheckAddressRange (Width, Address, Count, UserBuffer, IA32_MAX_IO_ADDRESS);
196   if (EFI_ERROR (Status)) {
197     return Status;
198   }
199 
200   //
201   // Do nothing for Nt32 version
202   //
203   return EFI_SUCCESS;
204 }
205 
206 EFI_STATUS
207 EFIAPI
CpuIoServiceWrite(IN EFI_CPU_IO2_PROTOCOL * This,IN EFI_CPU_IO_PROTOCOL_WIDTH Width,IN UINT64 UserAddress,IN UINTN Count,IN OUT VOID * UserBuffer)208 CpuIoServiceWrite (
209   IN EFI_CPU_IO2_PROTOCOL               *This,
210   IN  EFI_CPU_IO_PROTOCOL_WIDTH         Width,
211   IN  UINT64                            UserAddress,
212   IN  UINTN                             Count,
213   IN  OUT VOID                          *UserBuffer
214   )
215 /*++
216 
217 Routine Description:
218 
219 
220   This is the service that implements the I/O Write
221 
222 Arguments:
223 
224   Pointer to an instance of the CPU I/O Protocol
225   Width of the Memory Access
226   Address of the I/O access
227   Count of the number of accesses to perform
228   Pointer to the buffer to read or write from I/O space
229 
230 Returns:
231 
232   Status
233 
234   Status
235   EFI_SUCCESS             - The data was read from or written to the EFI System.
236   EFI_INVALID_PARAMETER   - Width is invalid for this EFI System.
237   EFI_INVALID_PARAMETER   - Buffer is NULL.
238   EFI_UNSUPPORTED         - The Buffer is not aligned for the given Width.
239   EFI_UNSUPPORTED         - The address range specified by Address, Width, and
240                             Count is not valid for this EFI System.
241 
242 **/
243 {
244   UINTN       Address;
245   EFI_STATUS  Status;
246 
247   if (!UserBuffer) {
248     return EFI_INVALID_PARAMETER;
249   }
250 
251   Address = (UINTN) UserAddress;
252 
253   if (Width >= EfiCpuIoWidthMaximum) {
254     return EFI_INVALID_PARAMETER;
255   }
256 
257   Status = CpuIoCheckAddressRange (Width, Address, Count, UserBuffer, IA32_MAX_IO_ADDRESS);
258   if (EFI_ERROR (Status)) {
259     return Status;
260   }
261 
262   //
263   // Do nothing for Nt32 version
264   //
265   return EFI_SUCCESS;
266 }
267 
268 
269 /*++
270 
271 Routine Description:
272 
273 Arguments:
274 
275   Width   - TODO: add argument description
276   Address - TODO: add argument description
277   Count   - TODO: add argument description
278   Buffer  - TODO: add argument description
279   Limit   - TODO: add argument description
280 
281 Returns:
282 
283   EFI_UNSUPPORTED - TODO: Add description for return value
284   EFI_UNSUPPORTED - TODO: Add description for return value
285   EFI_UNSUPPORTED - TODO: Add description for return value
286   EFI_SUCCESS - TODO: Add description for return value
287 
288 **/
289 EFI_STATUS
CpuIoCheckAddressRange(IN EFI_CPU_IO_PROTOCOL_WIDTH Width,IN UINT64 Address,IN UINTN Count,IN VOID * Buffer,IN UINT64 Limit)290 CpuIoCheckAddressRange (
291   IN  EFI_CPU_IO_PROTOCOL_WIDTH         Width,
292   IN  UINT64                            Address,
293   IN  UINTN                             Count,
294   IN  VOID                              *Buffer,
295   IN  UINT64                            Limit
296   )
297 {
298   UINTN AlignMask;
299 
300   if (Address > Limit) {
301     return EFI_UNSUPPORTED;
302   }
303 
304   //
305   // For FiFo type, the target address won't increase during the access, so treat count as 1
306   //
307   if (Width >= EfiCpuIoWidthFifoUint8 && Width <= EfiCpuIoWidthFifoUint64) {
308     Count = 1;
309   }
310 
311   Width = Width & 0x03;
312   if ((Address - 1 + LShiftU64 (Count, Width)) > Limit) {
313     return EFI_UNSUPPORTED;
314   }
315 
316   AlignMask = (1 << Width) - 1;
317   if ((UINTN) Buffer & AlignMask) {
318     return EFI_UNSUPPORTED;
319   }
320 
321   return EFI_SUCCESS;
322 }
323 
324 
325