729f556e46a9cfb08cba0c08e047291764d0348c
[mirror_edk2.git] / CpuIo.c
1 /*++
2
3 Copyright (c) 2004 - 2007, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11
12 Module Name:
13 CpuIo.c
14
15 Abstract:
16
17 This is the code that publishes the CPU I/O Protocol.
18 The intent herein is to have a single I/O service that can load
19 as early as possible, extend into runtime, and be layered upon by
20 the implementations of architectural protocols and the PCI Root
21 Bridge I/O Protocol.
22
23 --*/
24
25 #include "CpuIo.h"
26
27 #define IA32_MAX_IO_ADDRESS 0xFFFF
28
29 EFI_CPU_IO_PROTOCOL mCpuIo = {
30 {
31 CpuMemoryServiceRead,
32 CpuMemoryServiceWrite
33 },
34 {
35 CpuIoServiceRead,
36 CpuIoServiceWrite
37 }
38 };
39
40 EFI_STATUS
41 CpuIoMemRW (
42 IN EFI_CPU_IO_PROTOCOL_WIDTH Width,
43 IN UINTN Count,
44 IN BOOLEAN DestinationStrideFlag,
45 OUT PTR Destination,
46 IN BOOLEAN SourceStrideFlag,
47 IN PTR Source
48 )
49 /*++
50
51 Routine Description:
52
53 Private service to perform memory mapped I/O read/write
54
55 Arguments:
56
57 Width - Width of the memory mapped I/O operation
58 Count - Count of the number of accesses to perform
59 DestinationStrideFlag - Boolean flag indicates if the destination is to be incremented
60 Destination - Destination of the memory mapped I/O operation
61 SourceStrideFlag - Boolean flag indicates if the source is to be incremented
62 Source - Source of the memory mapped I/O operation
63
64 Returns:
65
66 EFI_SUCCESS - Successful operation
67 EFI_INVALID_PARAMETER - Width is invalid
68
69 --*/
70 {
71 UINTN Stride;
72 UINTN DestinationStride;
73 UINTN SourceStride;
74
75 Width = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);
76 Stride = (UINTN)1 << Width;
77 DestinationStride = DestinationStrideFlag ? Stride : 0;
78 SourceStride = SourceStrideFlag ? Stride : 0;
79
80 //
81 // Loop for each iteration and move the data
82 //
83 switch (Width) {
84 case EfiCpuIoWidthUint8:
85 for (; Count > 0; Count--, Destination.buf += DestinationStride, Source.buf += SourceStride) {
86 MemoryFence();
87 *Destination.ui8 = *Source.ui8;
88 MemoryFence();
89 }
90 break;
91
92 case EfiCpuIoWidthUint16:
93 for (; Count > 0; Count--, Destination.buf += DestinationStride, Source.buf += SourceStride) {
94 MemoryFence ();
95 *Destination.ui16 = *Source.ui16;
96 MemoryFence ();
97 }
98 break;
99
100 case EfiCpuIoWidthUint32:
101 for (; Count > 0; Count--, Destination.buf += DestinationStride, Source.buf += SourceStride) {
102 MemoryFence ();
103 *Destination.ui32 = *Source.ui32;
104 MemoryFence ();
105 }
106 break;
107
108 case EfiCpuIoWidthUint64:
109 for (; Count > 0; Count--, Destination.buf += DestinationStride, Source.buf += SourceStride) {
110 MemoryFence ();
111 *Destination.ui64 = *Source.ui64;
112 MemoryFence ();
113 }
114 break;
115
116 default:
117 return EFI_INVALID_PARAMETER;
118 }
119
120 return EFI_SUCCESS;
121 }
122
123 EFI_STATUS
124 EFIAPI
125 CpuMemoryServiceRead (
126 IN EFI_CPU_IO_PROTOCOL *This,
127 IN EFI_CPU_IO_PROTOCOL_WIDTH Width,
128 IN UINT64 Address,
129 IN UINTN Count,
130 OUT VOID *Buffer
131 )
132 /*++
133
134 Routine Description:
135
136 Perform the memory mapped I/O read service
137
138 Arguments:
139
140 This - Pointer to an instance of the CPU I/O Protocol
141 Width - Width of the memory mapped I/O operation
142 Address - Base address of the memory mapped I/O operation
143 Count - Count of the number of accesses to perform
144 Buffer - Pointer to the destination buffer to store the results
145
146 Returns:
147
148 EFI_SUCCESS - The data was read.
149 EFI_INVALID_PARAMETER - Width is invalid.
150 EFI_INVALID_PARAMETER - Buffer is NULL.
151 EFI_UNSUPPORTED - The Buffer is not aligned for the given Width.
152 EFI_UNSUPPORTED - The address range specified by Address, Width,
153 and Count is not valid.
154
155 --*/
156 {
157 PTR Source;
158 PTR Destination;
159 EFI_STATUS Status;
160
161 Status = CpuIoCheckParameter (Width, Address, Count, Buffer, MAX_ADDRESS);
162 if (EFI_ERROR (Status)) {
163 return Status;
164 }
165
166 Destination.buf = Buffer;
167 Source.buf = (VOID *) (UINTN) Address;
168
169 if (Width >= EfiCpuIoWidthUint8 && Width <= EfiCpuIoWidthUint64) {
170 return CpuIoMemRW (Width, Count, TRUE, Destination, TRUE, Source);
171 }
172
173 if (Width >= EfiCpuIoWidthFifoUint8 && Width <= EfiCpuIoWidthFifoUint64) {
174 return CpuIoMemRW (Width, Count, TRUE, Destination, FALSE, Source);
175 }
176
177 if (Width >= EfiCpuIoWidthFillUint8 && Width <= EfiCpuIoWidthFillUint64) {
178 return CpuIoMemRW (Width, Count, FALSE, Destination, TRUE, Source);
179 }
180
181 return EFI_INVALID_PARAMETER;
182 }
183
184 EFI_STATUS
185 EFIAPI
186 CpuMemoryServiceWrite (
187 IN EFI_CPU_IO_PROTOCOL *This,
188 IN EFI_CPU_IO_PROTOCOL_WIDTH Width,
189 IN UINT64 Address,
190 IN UINTN Count,
191 IN VOID *Buffer
192 )
193 /*++
194
195 Routine Description:
196
197 Perform the memory mapped I/O write service
198
199 Arguments:
200
201 This - Pointer to an instance of the CPU I/O Protocol
202 Width - Width of the memory mapped I/O operation
203 Address - Base address of the memory mapped I/O operation
204 Count - Count of the number of accesses to perform
205 Buffer - Pointer to the source buffer from which to write data
206
207 Returns:
208
209 EFI_SUCCESS - The data was written.
210 EFI_INVALID_PARAMETER - Width is invalid.
211 EFI_INVALID_PARAMETER - Buffer is NULL.
212 EFI_UNSUPPORTED - The Buffer is not aligned for the given Width.
213 EFI_UNSUPPORTED - The address range specified by Address, Width,
214 and Count is not valid.
215
216 --*/
217 {
218 PTR Source;
219 PTR Destination;
220 EFI_STATUS Status;
221
222 Status = CpuIoCheckParameter (Width, Address, Count, Buffer, MAX_ADDRESS);
223 if (EFI_ERROR (Status)) {
224 return Status;
225 }
226
227 Destination.buf = (VOID *) (UINTN) Address;
228 Source.buf = Buffer;
229
230 if (Width >= EfiCpuIoWidthUint8 && Width <= EfiCpuIoWidthUint64) {
231 return CpuIoMemRW (Width, Count, TRUE, Destination, TRUE, Source);
232 }
233
234 if (Width >= EfiCpuIoWidthFifoUint8 && Width <= EfiCpuIoWidthFifoUint64) {
235 return CpuIoMemRW (Width, Count, FALSE, Destination, TRUE, Source);
236 }
237
238 if (Width >= EfiCpuIoWidthFillUint8 && Width <= EfiCpuIoWidthFillUint64) {
239 return CpuIoMemRW (Width, Count, TRUE, Destination, FALSE, Source);
240 }
241
242 return EFI_INVALID_PARAMETER;
243 }
244
245 EFI_STATUS
246 EFIAPI
247 CpuIoServiceRead (
248 IN EFI_CPU_IO_PROTOCOL *This,
249 IN EFI_CPU_IO_PROTOCOL_WIDTH Width,
250 IN UINT64 UserAddress,
251 IN UINTN Count,
252 OUT VOID *UserBuffer
253 )
254 /*++
255
256 Routine Description:
257
258 Perform the port I/O read service
259
260 Arguments:
261
262 This - Pointer to an instance of the CPU I/O Protocol
263 Width - Width of the port I/O operation
264 Address - Base address of the port I/O operation
265 Count - Count of the number of accesses to perform
266 Buffer - Pointer to the destination buffer to store the results
267
268 Returns:
269
270 EFI_SUCCESS - The data was read.
271 EFI_INVALID_PARAMETER - Width is invalid.
272 EFI_INVALID_PARAMETER - Buffer is NULL.
273 EFI_UNSUPPORTED - The Buffer is not aligned for the given Width.
274 EFI_UNSUPPORTED - The address range specified by Address, Width,
275 and Count is not valid.
276
277 --*/
278 {
279 UINTN InStride;
280 UINTN OutStride;
281 UINTN Address;
282 PTR Buffer;
283 EFI_STATUS Status;
284
285 Buffer.buf = (UINT8 *) UserBuffer;
286
287 if (Width >= EfiCpuIoWidthMaximum) {
288 return EFI_INVALID_PARAMETER;
289 }
290
291 Status = CpuIoCheckParameter (Width, UserAddress, Count, UserBuffer, IA32_MAX_IO_ADDRESS);
292 if (EFI_ERROR (Status)) {
293 return Status;
294 }
295
296 Address = (UINTN) UserAddress;
297 InStride = (UINTN)1 << (Width & 0x03);
298 OutStride = InStride;
299 if (Width >= EfiCpuIoWidthFifoUint8 && Width <= EfiCpuIoWidthFifoUint64) {
300 InStride = 0;
301 }
302
303 if (Width >= EfiCpuIoWidthFillUint8 && Width <= EfiCpuIoWidthFillUint64) {
304 OutStride = 0;
305 }
306
307 Width = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);
308
309 //
310 // Loop for each iteration and move the data
311 //
312 switch (Width) {
313 case EfiCpuIoWidthUint8:
314 for (; Count > 0; Count--, Buffer.buf += OutStride, Address += InStride) {
315 *Buffer.ui8 = IoRead8 ((UINTN) Address);
316 }
317 break;
318
319 case EfiCpuIoWidthUint16:
320 for (; Count > 0; Count--, Buffer.buf += OutStride, Address += InStride) {
321 *Buffer.ui16 = IoRead16 ((UINTN) Address);
322 }
323 break;
324
325 case EfiCpuIoWidthUint32:
326 for (; Count > 0; Count--, Buffer.buf += OutStride, Address += InStride) {
327 *Buffer.ui32 = IoRead32 ((UINTN) Address);
328 }
329 break;
330
331 default:
332 return EFI_INVALID_PARAMETER;
333 }
334
335 return EFI_SUCCESS;
336 }
337
338 EFI_STATUS
339 EFIAPI
340 CpuIoServiceWrite (
341 IN EFI_CPU_IO_PROTOCOL *This,
342 IN EFI_CPU_IO_PROTOCOL_WIDTH Width,
343 IN UINT64 UserAddress,
344 IN UINTN Count,
345 IN VOID *UserBuffer
346 )
347 /*++
348
349 Routine Description:
350
351 Perform the port I/O write service
352
353 Arguments:
354
355 This - Pointer to an instance of the CPU I/O Protocol
356 Width - Width of the port I/O operation
357 Address - Base address of the port I/O operation
358 Count - Count of the number of accesses to perform
359 Buffer - Pointer to the source buffer from which to write data
360
361 Returns:
362
363 EFI_SUCCESS - The data was written.
364 EFI_INVALID_PARAMETER - Width is invalid.
365 EFI_INVALID_PARAMETER - Buffer is NULL.
366 EFI_UNSUPPORTED - The Buffer is not aligned for the given Width.
367 EFI_UNSUPPORTED - The address range specified by Address, Width,
368 and Count is not valid.
369
370 --*/
371 {
372 UINTN InStride;
373 UINTN OutStride;
374 UINTN Address;
375 PTR Buffer;
376 EFI_STATUS Status;
377
378 Buffer.buf = (UINT8 *) UserBuffer;
379
380 if (Width >= EfiCpuIoWidthMaximum) {
381 return EFI_INVALID_PARAMETER;
382 }
383
384 Status = CpuIoCheckParameter (Width, UserAddress, Count, UserBuffer, IA32_MAX_IO_ADDRESS);
385 if (EFI_ERROR (Status)) {
386 return Status;
387 }
388
389 Address = (UINTN) UserAddress;
390 InStride = (UINTN)1 << (Width & 0x03);
391 OutStride = InStride;
392 if (Width >= EfiCpuIoWidthFifoUint8 && Width <= EfiCpuIoWidthFifoUint64) {
393 InStride = 0;
394 }
395
396 if (Width >= EfiCpuIoWidthFillUint8 && Width <= EfiCpuIoWidthFillUint64) {
397 OutStride = 0;
398 }
399
400 Width = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);
401
402 //
403 // Loop for each iteration and move the data
404 //
405 switch (Width) {
406 case EfiCpuIoWidthUint8:
407 for (; Count > 0; Count--, Buffer.buf += OutStride, Address += InStride) {
408 IoWrite8 ((UINTN) Address, *Buffer.ui8);
409 }
410 break;
411
412 case EfiCpuIoWidthUint16:
413 for (; Count > 0; Count--, Buffer.buf += OutStride, Address += InStride) {
414 IoWrite16 ((UINTN) Address, *Buffer.ui16);
415 }
416 break;
417
418 case EfiCpuIoWidthUint32:
419 for (; Count > 0; Count--, Buffer.buf += OutStride, Address += InStride) {
420 IoWrite32 ((UINTN) Address, *Buffer.ui32);
421 }
422 break;
423
424 default:
425 return EFI_INVALID_PARAMETER;
426 }
427
428 return EFI_SUCCESS;
429 }
430
431 EFI_STATUS
432 EFIAPI
433 CpuIoInitialize (
434 IN EFI_HANDLE ImageHandle,
435 IN EFI_SYSTEM_TABLE *SystemTable
436 )
437 /*++
438
439 Routine Description:
440
441 CpuIo driver entry point.
442
443 Arguments:
444
445 ImageHandle - The firmware allocated handle for the EFI image.
446 SystemTable - A pointer to the EFI System Table.
447
448 Returns:
449
450 EFI_SUCCESS - The driver was initialized.
451 EFI_OUT_OF_RESOURCES - The request could not be completed due to a lack of resources.
452
453 --*/
454 {
455 EFI_STATUS Status;
456 EFI_HANDLE Handle;
457
458 Handle = NULL;
459 Status = SystemTable->BootServices->InstallProtocolInterface (
460 &Handle,
461 &gEfiCpuIoProtocolGuid,
462 EFI_NATIVE_INTERFACE,
463 &mCpuIo
464 );
465 ASSERT_EFI_ERROR (Status);
466
467 return Status;
468 }
469
470 EFI_STATUS
471 CpuIoCheckParameter (
472 IN EFI_CPU_IO_PROTOCOL_WIDTH Width,
473 IN UINT64 Address,
474 IN UINTN Count,
475 IN VOID *Buffer,
476 IN UINT64 Limit
477 )
478 /*++
479
480 Routine Description:
481
482 Check the validation of parameters for CPU I/O interface functions.
483
484 Arguments:
485
486 Width - Width of the Memory Access
487 Address - Address of the Memory access
488 Count - Count of the number of accesses to perform
489 Buffer - Pointer to the buffer to read from memory
490 Buffer - Memory buffer for the I/O operation
491 Limit - Maximum address supported
492
493 Returns:
494
495 EFI_INVALID_PARAMETER - Buffer is NULL
496 EFI_UNSUPPORTED - The address range specified by Width, Address and Count is invalid
497 EFI_UNSUPPORTED - The memory buffer is not aligned
498 EFI_SUCCESS - Parameters are OK
499
500 --*/
501 {
502 UINTN AlignMask;
503
504 if (Buffer == NULL) {
505 return EFI_INVALID_PARAMETER;
506 }
507
508 if (Address > Limit) {
509 return EFI_UNSUPPORTED;
510 }
511
512 //
513 // For FiFo type, the target address won't increase during the access,
514 // so treat count as 1
515 //
516 if (Width >= EfiCpuIoWidthFifoUint8 && Width <= EfiCpuIoWidthFifoUint64) {
517 Count = 1;
518 }
519
520 Width = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03);
521 if (Address - 1 + ((UINTN)1 << Width) * Count > Limit) {
522 return EFI_UNSUPPORTED;
523 }
524
525 AlignMask = ((UINTN)1 << Width) - 1;
526 if ((UINTN) Buffer & AlignMask) {
527 return EFI_UNSUPPORTED;
528 }
529
530 return EFI_SUCCESS;
531 }