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