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