1 /** @file
2   Produces the SMM CPU I/O Protocol.
3 
4 Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 **/
8 
9 #include "CpuIo2Smm.h"
10 
11 //
12 // Handle for the SMM CPU I/O Protocol
13 //
14 EFI_HANDLE  mHandle = NULL;
15 
16 //
17 // SMM CPU I/O Protocol instance
18 //
19 EFI_SMM_CPU_IO2_PROTOCOL mSmmCpuIo2 = {
20   {
21     CpuMemoryServiceRead,
22     CpuMemoryServiceWrite
23   },
24   {
25     CpuIoServiceRead,
26     CpuIoServiceWrite
27   }
28 };
29 
30 //
31 // Lookup table for increment values based on transfer widths
32 //
33 UINT8 mStride[] = {
34   1, // SMM_IO_UINT8
35   2, // SMM_IO_UINT16
36   4, // SMM_IO_UINT32
37   8  // SMM_IO_UINT64
38 };
39 
40 /**
41   Check parameters to a SMM CPU I/O Protocol service request.
42 
43   @param[in]  MmioOperation  TRUE for an MMIO operation, FALSE for I/O Port operation.
44   @param[in]  Width          Signifies the width of the I/O operations.
45   @param[in]  Address        The base address of the I/O operations.  The caller is
46                              responsible for aligning the Address if required.
47   @param[in]  Count          The number of I/O operations to perform.
48   @param[in]  Buffer         For read operations, the destination buffer to store
49                              the results.  For write operations, the source buffer
50                              from which to write data.
51 
52   @retval EFI_SUCCESS            The data was read from or written to the device.
53   @retval EFI_UNSUPPORTED        The Address is not valid for this system.
54   @retval EFI_INVALID_PARAMETER  Width or Count, or both, were invalid.
55 
56 **/
57 EFI_STATUS
CpuIoCheckParameter(IN BOOLEAN MmioOperation,IN EFI_SMM_IO_WIDTH Width,IN UINT64 Address,IN UINTN Count,IN VOID * Buffer)58 CpuIoCheckParameter (
59   IN BOOLEAN           MmioOperation,
60   IN EFI_SMM_IO_WIDTH  Width,
61   IN UINT64            Address,
62   IN UINTN             Count,
63   IN VOID              *Buffer
64   )
65 {
66   UINT64  MaxCount;
67   UINT64  Limit;
68 
69   //
70   // Check to see if Buffer is NULL
71   //
72   if (Buffer == NULL) {
73     return EFI_INVALID_PARAMETER;
74   }
75 
76   //
77   // Check to see if Width is in the valid range
78   //
79   if ((UINT32)Width > SMM_IO_UINT64) {
80     return EFI_INVALID_PARAMETER;
81   }
82 
83   //
84   // Check to see if Width is in the valid range for I/O Port operations
85   //
86   if (!MmioOperation && (Width == SMM_IO_UINT64)) {
87     return EFI_INVALID_PARAMETER;
88   }
89 
90   //
91   // Check to see if any address associated with this transfer exceeds the maximum
92   // allowed address.  The maximum address implied by the parameters passed in is
93   // Address + Size * Count.  If the following condition is met, then the transfer
94   // is not supported.
95   //
96   //    Address + Size * Count > (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS) + 1
97   //
98   // Since MAX_ADDRESS can be the maximum integer value supported by the CPU and Count
99   // can also be the maximum integer value supported by the CPU, this range
100   // check must be adjusted to avoid all overflow conditions.
101   //
102   // The following form of the range check is equivalent but assumes that
103   // MAX_ADDRESS and MAX_IO_PORT_ADDRESS are of the form (2^n - 1).
104   //
105   Limit = (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS);
106   if (Count == 0) {
107     if (Address > Limit) {
108       return EFI_UNSUPPORTED;
109     }
110   } else {
111     MaxCount = RShiftU64 (Limit, Width);
112     if (MaxCount < (Count - 1)) {
113       return EFI_UNSUPPORTED;
114     }
115     if (Address > LShiftU64 (MaxCount - Count + 1, Width)) {
116       return EFI_UNSUPPORTED;
117     }
118   }
119 
120   //
121   // Check to see if Address is aligned
122   //
123   if ((Address & ((UINT64)mStride[Width] - 1)) != 0) {
124     return EFI_UNSUPPORTED;
125   }
126 
127   return EFI_SUCCESS;
128 }
129 
130 /**
131   Reads memory-mapped registers.
132 
133   The I/O operations are carried out exactly as requested.  The caller is
134   responsible for any alignment and I/O width issues that the bus, device,
135   platform, or type of I/O might require.
136 
137   @param[in]  This     The EFI_SMM_CPU_IO2_PROTOCOL instance.
138   @param[in]  Width    Signifies the width of the I/O operations.
139   @param[in]  Address  The base address of the I/O operations.  The caller is
140                        responsible for aligning the Address if required.
141   @param[in]  Count    The number of I/O operations to perform.
142   @param[out] Buffer   For read operations, the destination buffer to store
143                        the results.  For write operations, the source buffer
144                        from which to write data.
145 
146   @retval EFI_SUCCESS            The data was read from or written to the device.
147   @retval EFI_UNSUPPORTED        The Address is not valid for this system.
148   @retval EFI_INVALID_PARAMETER  Width or Count, or both, were invalid.
149   @retval EFI_OUT_OF_RESOURCES   The request could not be completed due to a
150                                  lack of resources
151 
152 **/
153 EFI_STATUS
154 EFIAPI
CpuMemoryServiceRead(IN CONST EFI_SMM_CPU_IO2_PROTOCOL * This,IN EFI_SMM_IO_WIDTH Width,IN UINT64 Address,IN UINTN Count,OUT VOID * Buffer)155 CpuMemoryServiceRead (
156   IN  CONST EFI_SMM_CPU_IO2_PROTOCOL  *This,
157   IN  EFI_SMM_IO_WIDTH                Width,
158   IN  UINT64                          Address,
159   IN  UINTN                           Count,
160   OUT VOID                            *Buffer
161   )
162 {
163   EFI_STATUS  Status;
164   UINT8       Stride;
165   UINT8       *Uint8Buffer;
166 
167   Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer);
168   if (EFI_ERROR (Status)) {
169     return Status;
170   }
171 
172   //
173   // Select loop based on the width of the transfer
174   //
175   Stride = mStride[Width];
176   for (Uint8Buffer = Buffer; Count > 0; Address += Stride, Uint8Buffer += Stride, Count--) {
177     if (Width == SMM_IO_UINT8) {
178       *Uint8Buffer = MmioRead8 ((UINTN)Address);
179     } else if (Width == SMM_IO_UINT16) {
180       *((UINT16 *)Uint8Buffer) = MmioRead16 ((UINTN)Address);
181     } else if (Width == SMM_IO_UINT32) {
182       *((UINT32 *)Uint8Buffer) = MmioRead32 ((UINTN)Address);
183     } else if (Width == SMM_IO_UINT64) {
184       *((UINT64 *)Uint8Buffer) = MmioRead64 ((UINTN)Address);
185     }
186   }
187   return EFI_SUCCESS;
188 }
189 
190 /**
191   Writes memory-mapped registers.
192 
193   The I/O operations are carried out exactly as requested.  The caller is
194   responsible for any alignment and I/O width issues that the bus, device,
195   platform, or type of I/O might require.
196 
197   @param[in]  This     The EFI_SMM_CPU_IO2_PROTOCOL instance.
198   @param[in]  Width    Signifies the width of the I/O operations.
199   @param[in]  Address  The base address of the I/O operations.  The caller is
200                        responsible for aligning the Address if required.
201   @param[in]  Count    The number of I/O operations to perform.
202   @param[in]  Buffer   For read operations, the destination buffer to store
203                        the results.  For write operations, the source buffer
204                        from which to write data.
205 
206   @retval EFI_SUCCESS            The data was read from or written to the device.
207   @retval EFI_UNSUPPORTED        The Address is not valid for this system.
208   @retval EFI_INVALID_PARAMETER  Width or Count, or both, were invalid.
209   @retval EFI_OUT_OF_RESOURCES   The request could not be completed due to a
210                                  lack of resources
211 
212 **/
213 EFI_STATUS
214 EFIAPI
CpuMemoryServiceWrite(IN CONST EFI_SMM_CPU_IO2_PROTOCOL * This,IN EFI_SMM_IO_WIDTH Width,IN UINT64 Address,IN UINTN Count,IN VOID * Buffer)215 CpuMemoryServiceWrite (
216   IN CONST EFI_SMM_CPU_IO2_PROTOCOL  *This,
217   IN EFI_SMM_IO_WIDTH                Width,
218   IN UINT64                          Address,
219   IN UINTN                           Count,
220   IN VOID                            *Buffer
221   )
222 {
223   EFI_STATUS  Status;
224   UINT8       Stride;
225   UINT8       *Uint8Buffer;
226 
227   Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer);
228   if (EFI_ERROR (Status)) {
229     return Status;
230   }
231 
232   //
233   // Select loop based on the width of the transfer
234   //
235   Stride = mStride[Width];
236   for (Uint8Buffer = Buffer; Count > 0; Address += Stride, Uint8Buffer += Stride, Count--) {
237     if (Width == SMM_IO_UINT8) {
238       MmioWrite8 ((UINTN)Address, *Uint8Buffer);
239     } else if (Width == SMM_IO_UINT16) {
240       MmioWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer));
241     } else if (Width == SMM_IO_UINT32) {
242       MmioWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer));
243     } else if (Width == SMM_IO_UINT64) {
244       MmioWrite64 ((UINTN)Address, *((UINT64 *)Uint8Buffer));
245     }
246   }
247   return EFI_SUCCESS;
248 }
249 
250 /**
251   Reads I/O registers.
252 
253   The I/O operations are carried out exactly as requested.  The caller is
254   responsible for any alignment and I/O width issues that the bus, device,
255   platform, or type of I/O might require.
256 
257   @param[in]  This     The EFI_SMM_CPU_IO2_PROTOCOL instance.
258   @param[in]  Width    Signifies the width of the I/O operations.
259   @param[in]  Address  The base address of the I/O operations.  The caller is
260                        responsible for aligning the Address if required.
261   @param[in]  Count    The number of I/O operations to perform.
262   @param[out] Buffer   For read operations, the destination buffer to store
263                        the results.  For write operations, the source buffer
264                        from which to write data.
265 
266   @retval EFI_SUCCESS            The data was read from or written to the device.
267   @retval EFI_UNSUPPORTED        The Address is not valid for this system.
268   @retval EFI_INVALID_PARAMETER  Width or Count, or both, were invalid.
269   @retval EFI_OUT_OF_RESOURCES   The request could not be completed due to a
270                                  lack of resources
271 
272 **/
273 EFI_STATUS
274 EFIAPI
CpuIoServiceRead(IN CONST EFI_SMM_CPU_IO2_PROTOCOL * This,IN EFI_SMM_IO_WIDTH Width,IN UINT64 Address,IN UINTN Count,OUT VOID * Buffer)275 CpuIoServiceRead (
276   IN  CONST EFI_SMM_CPU_IO2_PROTOCOL  *This,
277   IN  EFI_SMM_IO_WIDTH                Width,
278   IN  UINT64                          Address,
279   IN  UINTN                           Count,
280   OUT VOID                            *Buffer
281   )
282 {
283   EFI_STATUS  Status;
284   UINT8       Stride;
285   UINT8       *Uint8Buffer;
286 
287   Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer);
288   if (EFI_ERROR (Status)) {
289     return Status;
290   }
291 
292   //
293   // Select loop based on the width of the transfer
294   //
295   Stride = mStride[Width];
296   for (Uint8Buffer = Buffer; Count > 0; Address += Stride, Uint8Buffer += Stride, Count--) {
297     if (Width == SMM_IO_UINT8) {
298       *Uint8Buffer = IoRead8 ((UINTN)Address);
299     } else if (Width == SMM_IO_UINT16) {
300       *((UINT16 *)Uint8Buffer) = IoRead16 ((UINTN)Address);
301     } else if (Width == SMM_IO_UINT32) {
302       *((UINT32 *)Uint8Buffer) = IoRead32 ((UINTN)Address);
303     }
304   }
305 
306   return EFI_SUCCESS;
307 }
308 
309 /**
310   Write I/O registers.
311 
312   The I/O operations are carried out exactly as requested.  The caller is
313   responsible for any alignment and I/O width issues that the bus, device,
314   platform, or type of I/O might require.
315 
316   @param[in]  This     The EFI_SMM_CPU_IO2_PROTOCOL instance.
317   @param[in]  Width    Signifies the width of the I/O operations.
318   @param[in]  Address  The base address of the I/O operations.  The caller is
319                        responsible for aligning the Address if required.
320   @param[in]  Count    The number of I/O operations to perform.
321   @param[in]  Buffer   For read operations, the destination buffer to store
322                        the results.  For write operations, the source buffer
323                        from which to write data.
324 
325   @retval EFI_SUCCESS            The data was read from or written to the device.
326   @retval EFI_UNSUPPORTED        The Address is not valid for this system.
327   @retval EFI_INVALID_PARAMETER  Width or Count, or both, were invalid.
328   @retval EFI_OUT_OF_RESOURCES   The request could not be completed due to a
329                                  lack of resources
330 
331 **/
332 EFI_STATUS
333 EFIAPI
CpuIoServiceWrite(IN CONST EFI_SMM_CPU_IO2_PROTOCOL * This,IN EFI_SMM_IO_WIDTH Width,IN UINT64 Address,IN UINTN Count,IN VOID * Buffer)334 CpuIoServiceWrite (
335   IN CONST EFI_SMM_CPU_IO2_PROTOCOL  *This,
336   IN EFI_SMM_IO_WIDTH                Width,
337   IN UINT64                          Address,
338   IN UINTN                           Count,
339   IN VOID                            *Buffer
340   )
341 {
342   EFI_STATUS  Status;
343   UINT8       Stride;
344   UINT8       *Uint8Buffer;
345 
346   //
347   // Make sure the parameters are valid
348   //
349   Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer);
350   if (EFI_ERROR (Status)) {
351     return Status;
352   }
353 
354   //
355   // Select loop based on the width of the transfer
356   //
357   Stride = mStride[Width];
358   for (Uint8Buffer = (UINT8 *)Buffer; Count > 0; Address += Stride, Uint8Buffer += Stride, Count--) {
359     if (Width == SMM_IO_UINT8) {
360       IoWrite8 ((UINTN)Address, *Uint8Buffer);
361     } else if (Width == SMM_IO_UINT16) {
362       IoWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer));
363     } else if (Width == SMM_IO_UINT32) {
364       IoWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer));
365     }
366   }
367 
368   return EFI_SUCCESS;
369 }
370 
371 /**
372   The module Entry Point SmmCpuIoProtocol driver
373 
374   @param[in] ImageHandle  The firmware allocated handle for the EFI image.
375   @param[in] SystemTable  A pointer to the EFI System Table.
376 
377   @retval EFI_SUCCESS  The entry point is executed successfully.
378   @retval Other        Some error occurs when executing this entry point.
379 
380 **/
381 EFI_STATUS
382 EFIAPI
SmmCpuIo2Initialize(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)383 SmmCpuIo2Initialize (
384   IN EFI_HANDLE        ImageHandle,
385   IN EFI_SYSTEM_TABLE  *SystemTable
386   )
387 {
388   EFI_STATUS  Status;
389 
390   //
391   // Copy the SMM CPU I/O Protocol instance into the System Management System Table
392   //
393   CopyMem (&gSmst->SmmIo, &mSmmCpuIo2, sizeof (mSmmCpuIo2));
394 
395   //
396   // Install the SMM CPU I/O Protocol into the SMM protocol database
397   //
398   Status = gSmst->SmmInstallProtocolInterface (
399                     &mHandle,
400                     &gEfiSmmCpuIo2ProtocolGuid,
401                     EFI_NATIVE_INTERFACE,
402                     &mSmmCpuIo2
403                     );
404   ASSERT_EFI_ERROR (Status);
405 
406   return Status;
407 }
408