]> git.proxmox.com Git - mirror_edk2.git/blob - UefiCpuPkg/CpuIo2Smm/CpuIo2Smm.c
da470a044aec3d63a873fbde3f6ff35516395444
[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
5 All rights reserved. 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 Width Signifies the width of the I/O or Memory operation.
103 @param Address The base address of the I/O or Memory operation.
104 @param Count The number of I/O or Memory operations to perform.
105 The number of bytes moved is Width size * Count, starting at Address.
106 @param Buffer For read operations, the destination buffer to store the results.
107 For write operations, the source buffer from which to write data.
108 @param MmioOperation TRUE for an MMIO operation, FALSE for I/O Port operation.
109
110 @retval EFI_SUCCESS The parameters for this request pass the checks.
111 @retval EFI_INVALID_PARAMETER Buffer is NULL or Width is not valid.
112 @retval EFI_UNSUPPORTED The address range specified by Address, Width, and Count exceeds Limit.
113 The Buffer is not aligned for the given Width.
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 > 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 oveflow conditions.
160 //
161 // The follwing 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 @param This A pointer to the EFI_SMM_CPU_IO2_PROTOCOL instance.
193 @param Width Signifies the width of the I/O or Memory operation.
194 @param Address The base address of the I/O or Memoryoperation.
195 @param Count The number of I/O or Memory operations to perform.
196 The number of bytes moved is Width size * Count, starting at Address.
197 @param Buffer For read operations, the destination buffer to store the results.
198 For write operations, the source buffer from which to write data.
199
200 @retval EFI_SUCCESS The data was read from or written to the EFI system.
201 @retval EFI_INVALID_PARAMETER Width is invalid for this EFI system.Or Buffer is NULL.
202 @retval EFI_UNSUPPORTED The Buffer is not aligned for the given Width.
203 Or,The address range specified by Address, Width, and Count is not valid for this EFI system.
204
205 **/
206 EFI_STATUS
207 EFIAPI
208 CpuMemoryServiceRead (
209 IN CONST EFI_SMM_CPU_IO2_PROTOCOL *This,
210 IN EFI_SMM_IO_WIDTH Width,
211 IN UINT64 Address,
212 IN UINTN Count,
213 OUT VOID *Buffer
214 )
215 {
216 EFI_STATUS Status;
217 UINT8 Stride;
218 UINT8 *Uint8Buffer;
219
220 Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer);
221 if (EFI_ERROR (Status)) {
222 return Status;
223 }
224
225 //
226 // Select loop based on the width of the transfer
227 //
228 Stride = mStride[Width];
229 for (Uint8Buffer = Buffer; Count > 0; Address += Stride, Uint8Buffer += Stride, Count--) {
230 if (Width == SMM_IO_UINT8) {
231 *Uint8Buffer = MmioRead8 ((UINTN)Address);
232 } else if (Width == SMM_IO_UINT16) {
233 *((UINT16 *)Uint8Buffer) = MmioRead16 ((UINTN)Address);
234 } else if (Width == SMM_IO_UINT32) {
235 *((UINT32 *)Uint8Buffer) = MmioRead32 ((UINTN)Address);
236 } else if (Width == SMM_IO_UINT64) {
237 *((UINT64 *)Uint8Buffer) = MmioRead64 ((UINTN)Address);
238 }
239 }
240 return EFI_SUCCESS;
241 }
242
243 /**
244 Writes memory-mapped registers.
245
246 @param This A pointer to the EFI_SMM_CPU_IO2_PROTOCOL instance.
247 @param Width Signifies the width of the I/O or Memory operation.
248 @param Address The base address of the I/O or Memoryoperation.
249 @param Count The number of I/O or Memory operations to perform.
250 The number of bytes moved is Width size * Count, starting at Address.
251 @param Buffer For read operations, the destination buffer to store the results.
252 For write operations, the source buffer from which to write data.
253
254 @retval EFI_SUCCESS The data was read from or written to the EFI system.
255 @retval EFI_INVALID_PARAMETER Width is invalid for this EFI system.Or Buffer is NULL.
256 @retval EFI_UNSUPPORTED The Buffer is not aligned for the given Width.
257 Or,The address range specified by Address, Width, and Count is not valid for this EFI system.
258
259 **/
260 EFI_STATUS
261 EFIAPI
262 CpuMemoryServiceWrite (
263 IN CONST EFI_SMM_CPU_IO2_PROTOCOL *This,
264 IN EFI_SMM_IO_WIDTH Width,
265 IN UINT64 Address,
266 IN UINTN Count,
267 IN VOID *Buffer
268 )
269 {
270 EFI_STATUS Status;
271 UINT8 Stride;
272 UINT8 *Uint8Buffer;
273
274 Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer);
275 if (EFI_ERROR (Status)) {
276 return Status;
277 }
278
279 //
280 // Select loop based on the width of the transfer
281 //
282 Stride = mStride[Width];
283 for (Uint8Buffer = Buffer; Count > 0; Address += Stride, Uint8Buffer += Stride, Count--) {
284 if (Width == SMM_IO_UINT8) {
285 MmioWrite8 ((UINTN)Address, *Uint8Buffer);
286 } else if (Width == SMM_IO_UINT16) {
287 MmioWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer));
288 } else if (Width == SMM_IO_UINT32) {
289 MmioWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer));
290 } else if (Width == SMM_IO_UINT64) {
291 MmioWrite64 ((UINTN)Address, *((UINT64 *)Uint8Buffer));
292 }
293 }
294 return EFI_SUCCESS;
295 }
296
297 /**
298 Reads I/O registers.
299
300 @param This A pointer to the EFI_SMM_CPU_IO2_PROTOCOL instance.
301 @param Width Signifies the width of the I/O or Memory operation.
302 @param Address The base address of the I/O or Memoryoperation.
303 @param Count The number of I/O or Memory operations to perform.
304 The number of bytes moved is Width size * Count, starting at Address.
305 @param Buffer For read operations, the destination buffer to store the results.
306 For write operations, the source buffer from which to write data.
307
308 @retval EFI_SUCCESS The data was read from or written to the EFI system.
309 @retval EFI_INVALID_PARAMETER Width is invalid for this EFI system.Or Buffer is NULL.
310 @retval EFI_UNSUPPORTED The Buffer is not aligned for the given Width.
311 Or,The address range specified by Address, Width, and Count is not valid for this EFI system.
312
313 **/
314 EFI_STATUS
315 EFIAPI
316 CpuIoServiceRead (
317 IN CONST EFI_SMM_CPU_IO2_PROTOCOL *This,
318 IN EFI_SMM_IO_WIDTH Width,
319 IN UINT64 Address,
320 IN UINTN Count,
321 OUT VOID *Buffer
322 )
323 {
324 EFI_STATUS Status;
325 UINT8 Stride;
326 UINT8 *Uint8Buffer;
327
328 Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer);
329 if (EFI_ERROR (Status)) {
330 return Status;
331 }
332
333 //
334 // Select loop based on the width of the transfer
335 //
336 Stride = mStride[Width];
337 for (Uint8Buffer = Buffer; Count > 0; Address += Stride, Uint8Buffer += Stride, Count--) {
338 if (Width == SMM_IO_UINT8) {
339 *Uint8Buffer = IoRead8 ((UINTN)Address);
340 } else if (Width == SMM_IO_UINT16) {
341 *((UINT16 *)Uint8Buffer) = IoRead16 ((UINTN)Address);
342 } else if (Width == SMM_IO_UINT32) {
343 *((UINT32 *)Uint8Buffer) = IoRead32 ((UINTN)Address);
344 }
345 }
346
347 return EFI_SUCCESS;
348 }
349
350 /**
351 Write I/O registers.
352
353 @param This A pointer to the EFI_SMM_CPU_IO2_PROTOCOL instance.
354 @param Width Signifies the width of the I/O or Memory operation.
355 @param Address The base address of the I/O or Memoryoperation.
356 @param Count The number of I/O or Memory operations to perform.
357 The number of bytes moved is Width size * Count, starting at Address.
358 @param Buffer For read operations, the destination buffer to store the results.
359 For write operations, the source buffer from which to write data.
360
361 @retval EFI_SUCCESS The data was read from or written to the EFI system.
362 @retval EFI_INVALID_PARAMETER Width is invalid for this EFI system.Or Buffer is NULL.
363 @retval EFI_UNSUPPORTED The Buffer is not aligned for the given Width.
364 Or,The address range specified by Address, Width, and Count is not valid for this EFI system.
365
366 **/
367 EFI_STATUS
368 EFIAPI
369 CpuIoServiceWrite (
370 IN CONST EFI_SMM_CPU_IO2_PROTOCOL *This,
371 IN EFI_SMM_IO_WIDTH Width,
372 IN UINT64 Address,
373 IN UINTN Count,
374 IN VOID *Buffer
375 )
376 {
377 EFI_STATUS Status;
378 UINT8 Stride;
379 UINT8 *Uint8Buffer;
380
381 //
382 // Make sure the parameters are valid
383 //
384 Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer);
385 if (EFI_ERROR (Status)) {
386 return Status;
387 }
388
389 //
390 // Select loop based on the width of the transfer
391 //
392 Stride = mStride[Width];
393 for (Uint8Buffer = (UINT8 *)Buffer; Count > 0; Address += Stride, Uint8Buffer += Stride, Count--) {
394 if (Width == SMM_IO_UINT8) {
395 IoWrite8 ((UINTN)Address, *Uint8Buffer);
396 } else if (Width == SMM_IO_UINT16) {
397 IoWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer));
398 } else if (Width == SMM_IO_UINT32) {
399 IoWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer));
400 }
401 }
402
403 return EFI_SUCCESS;
404 }
405
406 /**
407 The module Entry Point SmmCpuIoProtocol driver
408
409 @param ImageHandle The firmware allocated handle for the EFI image.
410 @param SystemTable A pointer to the EFI System Table.
411
412 @retval EFI_SUCCESS The entry point is executed successfully.
413 @retval Other Some error occurs when executing this entry point.
414
415 **/
416 EFI_STATUS
417 EFIAPI
418 SmmCpuIo2Initialize (
419 IN EFI_HANDLE ImageHandle,
420 IN EFI_SYSTEM_TABLE *SystemTable
421 )
422 {
423 EFI_STATUS Status;
424
425 //
426 // Copy the SMM CPU I/O Protocol instance into the System Management System Table
427 //
428 CopyMem (&gSmst->SmmIo, &mSmmCpuIo2, sizeof (mSmmCpuIo2));
429
430 //
431 // Install the SMM CPU I/O Protocol into the SMM protocol database
432 //
433 Status = gSmst->SmmInstallProtocolInterface (
434 &mHandle,
435 &gEfiSmmCpuIo2ProtocolGuid,
436 EFI_NATIVE_INTERFACE,
437 &mSmmCpuIo2
438 );
439 ASSERT_EFI_ERROR (Status);
440
441 return Status;
442 }