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