CpuIo2Mm.c 13 KB

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