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