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