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