aeregion.c revision 1.1.1.9 1 /******************************************************************************
2 *
3 * Module Name: aeregion - Handler for operation regions
4 *
5 *****************************************************************************/
6
7 /*
8 * Copyright (C) 2000 - 2018, Intel Corp.
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions, and the following disclaimer,
16 * without modification.
17 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
18 * substantially similar to the "NO WARRANTY" disclaimer below
19 * ("Disclaimer") and any redistribution must be conditioned upon
20 * including a substantially similar Disclaimer requirement for further
21 * binary redistribution.
22 * 3. Neither the names of the above-listed copyright holders nor the names
23 * of any contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * Alternatively, this software may be distributed under the terms of the
27 * GNU General Public License ("GPL") version 2 as published by the Free
28 * Software Foundation.
29 *
30 * NO WARRANTY
31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
34 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
35 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
37 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
40 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
41 * POSSIBILITY OF SUCH DAMAGES.
42 */
43
44 #include "aecommon.h"
45
46 #define _COMPONENT ACPI_TOOLS
47 ACPI_MODULE_NAME ("aeregion")
48
49
50 static AE_DEBUG_REGIONS AeRegions;
51
52
53 /******************************************************************************
54 *
55 * FUNCTION: AeRegionHandler
56 *
57 * PARAMETERS: Standard region handler parameters
58 *
59 * RETURN: Status
60 *
61 * DESCRIPTION: Test handler - Handles some dummy regions via memory that can
62 * be manipulated in Ring 3. Simulates actual reads and writes.
63 *
64 *****************************************************************************/
65
66 ACPI_STATUS
67 AeRegionHandler (
68 UINT32 Function,
69 ACPI_PHYSICAL_ADDRESS Address,
70 UINT32 BitWidth,
71 UINT64 *Value,
72 void *HandlerContext,
73 void *RegionContext)
74 {
75
76 ACPI_OPERAND_OBJECT *RegionObject = ACPI_CAST_PTR (ACPI_OPERAND_OBJECT, RegionContext);
77 UINT8 *Buffer = ACPI_CAST_PTR (UINT8, Value);
78 UINT8 *OldBuffer;
79 UINT8 *NewBuffer;
80 ACPI_PHYSICAL_ADDRESS BaseAddress;
81 ACPI_PHYSICAL_ADDRESS BaseAddressEnd;
82 ACPI_PHYSICAL_ADDRESS RegionAddress;
83 ACPI_PHYSICAL_ADDRESS RegionAddressEnd;
84 UINT32 Length;
85 UINT8 DataLength;
86 UINT8 *DataBuffer;
87 BOOLEAN BufferExists;
88 BOOLEAN BufferResize;
89 AE_REGION *RegionElement;
90 void *BufferValue;
91 ACPI_STATUS Status;
92 UINT32 ByteWidth;
93 UINT32 RegionLength;
94 UINT32 i;
95 UINT8 SpaceId;
96 ACPI_CONNECTION_INFO *MyContext;
97 UINT32 Value1;
98 UINT32 Value2;
99 ACPI_RESOURCE *Resource;
100
101
102 ACPI_FUNCTION_NAME (AeRegionHandler);
103
104
105 /* If the object is not a region, simply return */
106
107 if (RegionObject->Region.Type != ACPI_TYPE_REGION)
108 {
109 return (AE_OK);
110 }
111
112 /* Check that we actually got back our context parameter */
113
114 if (HandlerContext != &AeMyContext)
115 {
116 AcpiOsPrintf (
117 "Region handler received incorrect context %p, should be %p\n",
118 HandlerContext, &AeMyContext);
119 }
120
121 MyContext = ACPI_CAST_PTR (ACPI_CONNECTION_INFO, HandlerContext);
122
123 /*
124 * Find the region's address space and length before searching
125 * the linked list.
126 */
127 BaseAddress = RegionObject->Region.Address;
128 Length = RegionObject->Region.Length;
129 SpaceId = RegionObject->Region.SpaceId;
130
131 ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION,
132 "Operation Region request on %s at 0x%X, BitWidth 0x%X, RegionLength 0x%X\n",
133 AcpiUtGetRegionName (RegionObject->Region.SpaceId),
134 (UINT32) Address, BitWidth, (UINT32) Length));
135
136 /*
137 * Region support can be disabled with the -do option.
138 * We use this to support dynamically loaded tables where we pass a valid
139 * address to the AML.
140 */
141 if (AcpiGbl_DbOpt_NoRegionSupport)
142 {
143 BufferValue = ACPI_TO_POINTER (Address);
144 ByteWidth = (BitWidth / 8);
145
146 if (BitWidth % 8)
147 {
148 ByteWidth += 1;
149 }
150 goto DoFunction;
151 }
152
153 switch (SpaceId)
154 {
155 case ACPI_ADR_SPACE_SYSTEM_IO:
156 /*
157 * For I/O space, exercise the port validation
158 * Note: ReadPort currently always returns all ones, length=BitLength
159 */
160 switch (Function & ACPI_IO_MASK)
161 {
162 case ACPI_READ:
163
164 if (BitWidth == 64)
165 {
166 /* Split the 64-bit request into two 32-bit requests */
167
168 Status = AcpiHwReadPort (Address, &Value1, 32);
169 ACPI_CHECK_OK (AcpiHwReadPort, Status);
170 Status = AcpiHwReadPort (Address+4, &Value2, 32);
171 ACPI_CHECK_OK (AcpiHwReadPort, Status);
172
173 *Value = Value1 | ((UINT64) Value2 << 32);
174 }
175 else
176 {
177 Status = AcpiHwReadPort (Address, &Value1, BitWidth);
178 ACPI_CHECK_OK (AcpiHwReadPort, Status);
179 *Value = (UINT64) Value1;
180 }
181 break;
182
183 case ACPI_WRITE:
184
185 if (BitWidth == 64)
186 {
187 /* Split the 64-bit request into two 32-bit requests */
188
189 Status = AcpiHwWritePort (Address, ACPI_LODWORD (*Value), 32);
190 ACPI_CHECK_OK (AcpiHwWritePort, Status);
191 Status = AcpiHwWritePort (Address+4, ACPI_HIDWORD (*Value), 32);
192 ACPI_CHECK_OK (AcpiHwWritePort, Status);
193 }
194 else
195 {
196 Status = AcpiHwWritePort (Address, (UINT32) *Value, BitWidth);
197 ACPI_CHECK_OK (AcpiHwWritePort, Status);
198 }
199 break;
200
201 default:
202
203 Status = AE_BAD_PARAMETER;
204 break;
205 }
206
207 if (ACPI_FAILURE (Status))
208 {
209 return (Status);
210 }
211
212 /* Now go ahead and simulate the hardware */
213 break;
214
215 /*
216 * SMBus and GenericSerialBus support the various bidirectional
217 * protocols.
218 */
219 case ACPI_ADR_SPACE_SMBUS:
220 case ACPI_ADR_SPACE_GSBUS: /* ACPI 5.0 */
221
222 Status = AcpiExGetProtocolBufferLength ((Function >> 16), &Length);
223 if (ACPI_FAILURE (Status))
224 {
225 AcpiOsPrintf ("AcpiExec: Invalid SMbus/GSbus protocol ID: 0x%X\n",
226 (Function >> 16));
227 return (Status);
228 }
229
230 /* Adjust for fixed SMBus buffer size */
231
232 if ((SpaceId == ACPI_ADR_SPACE_SMBUS) &&
233 (Length > ACPI_SMBUS_DATA_SIZE))
234 {
235 Length = ACPI_SMBUS_DATA_SIZE; /* SMBus buffer is fixed-length */
236 }
237
238 if (AcpiGbl_DisplayRegionAccess)
239 {
240 AcpiOsPrintf ("AcpiExec: %s "
241 "%s: Attr %X Addr %.4X BaseAddr %.4X Length %.2X BitWidth %X BufLen %X\n",
242 AcpiUtGetRegionName (SpaceId),
243 (Function & ACPI_IO_MASK) ? "Write" : "Read ",
244 (UINT32) (Function >> 16),
245 (UINT32) Address, (UINT32) BaseAddress,
246 Length, BitWidth, Buffer[1]);
247
248 /* GenericSerialBus has a Connection() parameter */
249
250 if ((SpaceId == ACPI_ADR_SPACE_GSBUS) && MyContext)
251 {
252 Status = AcpiBufferToResource (MyContext->Connection,
253 MyContext->Length, &Resource);
254 if (ACPI_SUCCESS (Status))
255 {
256 ACPI_FREE (Resource);
257 }
258
259 AcpiOsPrintf (" [AccessLength %.2X Connnection %p]",
260 MyContext->AccessLength, MyContext->Connection);
261 }
262
263 AcpiOsPrintf ("\n");
264 }
265
266 DataBuffer = &Buffer[2];
267 DataLength = (UINT8) Length;
268
269 /* Setup the return buffer. Note: ASLTS depends on these fill values */
270
271 if (Length == ACPI_MAX_GSBUS_DATA_SIZE)
272 {
273 DataLength = 0x20; /* For ASLTS only */
274 }
275
276 for (i = 0; i < Length; i++)
277 {
278 DataBuffer[i] = (UINT8) (0xA0 + i);
279 }
280
281 Buffer[0] = 0; /* Return Status, OK */
282 Buffer[1] = DataLength; /* Length of valid data */
283 return (AE_OK);
284
285 case ACPI_ADR_SPACE_IPMI: /* ACPI 4.0 */
286
287 if (AcpiGbl_DisplayRegionAccess)
288 {
289 AcpiOsPrintf ("AcpiExec: IPMI "
290 "%s: Attr %X Addr %.4X BaseAddr %.4X Len %.2X Width %X BufLen %X\n",
291 (Function & ACPI_IO_MASK) ? "Write" : "Read ",
292 (UINT32) (Function >> 16), (UINT32) Address, (UINT32) BaseAddress,
293 Length, BitWidth, Buffer[1]);
294 }
295
296 /*
297 * Regardless of a READ or WRITE, this handler is passed a 66-byte
298 * buffer in which to return the IPMI status/length/data.
299 *
300 * Return some example data to show use of the bidirectional buffer
301 */
302 Buffer[0] = 0; /* Status byte */
303 Buffer[1] = ACPI_IPMI_DATA_SIZE; /* Return buffer data length */
304 Buffer[2] = 0; /* Completion code */
305 Buffer[3] = 0; /* Reserved */
306
307 /*
308 * Fill the 66-byte buffer with the return data.
309 * Note: ASLTS depends on these fill values.
310 */
311 for (i = 4; i < ACPI_IPMI_BUFFER_SIZE; i++)
312 {
313 Buffer[i] = (UINT8) (i);
314 }
315 return (AE_OK);
316
317 /*
318 * GPIO has some special semantics:
319 * 1) Address is the pin number index into the Connection() pin list
320 * 2) BitWidth is the actual number of bits (pins) defined by the field
321 */
322 case ACPI_ADR_SPACE_GPIO: /* ACPI 5.0 */
323
324 if (AcpiGbl_DisplayRegionAccess)
325 {
326 AcpiOsPrintf ("AcpiExec: GPIO "
327 "%s: Address %.4X Length %X BitWidth %X Conn %p\n",
328 (Function & ACPI_IO_MASK) ? "Write" : "Read ",
329 (UINT32) Address, Length, BitWidth, MyContext->Connection);
330 }
331
332 /* Now perform the "normal" SystemMemory handling, for AcpiExec only */
333 break;
334
335 default:
336 break;
337 }
338
339 /*
340 * Search through the linked list for this region's buffer
341 */
342 BufferExists = FALSE;
343 BufferResize = FALSE;
344 RegionElement = AeRegions.RegionList;
345
346 if (AeRegions.NumberOfRegions)
347 {
348 BaseAddressEnd = BaseAddress + Length - 1;
349 while (!BufferExists && RegionElement)
350 {
351 RegionAddress = RegionElement->Address;
352 RegionAddressEnd = RegionElement->Address + RegionElement->Length - 1;
353 RegionLength = RegionElement->Length;
354
355 /*
356 * Overlapping Region Support
357 *
358 * While searching through the region buffer list, determine if an
359 * overlap exists between the requested buffer space and the current
360 * RegionElement space. If there is an overlap then replace the old
361 * buffer with a new buffer of increased size before continuing to
362 * do the read or write
363 */
364 if (RegionElement->SpaceId != SpaceId ||
365 BaseAddressEnd < RegionAddress ||
366 BaseAddress > RegionAddressEnd)
367 {
368 /*
369 * Requested buffer is outside of the current RegionElement
370 * bounds
371 */
372 RegionElement = RegionElement->NextRegion;
373 }
374 else
375 {
376 /*
377 * Some amount of buffer space sharing exists. There are 4 cases
378 * to consider:
379 *
380 * 1. Right overlap
381 * 2. Left overlap
382 * 3. Left and right overlap
383 * 4. Fully contained - no resizing required
384 */
385 BufferExists = TRUE;
386
387 if ((BaseAddress >= RegionAddress) &&
388 (BaseAddress <= RegionAddressEnd) &&
389 (BaseAddressEnd > RegionAddressEnd))
390 {
391 /* Right overlap */
392
393 RegionElement->Length = (UINT32) (BaseAddress -
394 RegionAddress + Length);
395 BufferResize = TRUE;
396 }
397
398 else if ((BaseAddressEnd >= RegionAddress) &&
399 (BaseAddressEnd <= RegionAddressEnd) &&
400 (BaseAddress < RegionAddress))
401 {
402 /* Left overlap */
403
404 RegionElement->Address = BaseAddress;
405 RegionElement->Length = (UINT32) (RegionAddress -
406 BaseAddress + RegionElement->Length);
407 BufferResize = TRUE;
408 }
409
410 else if ((BaseAddress < RegionAddress) &&
411 (BaseAddressEnd > RegionAddressEnd))
412 {
413 /* Left and right overlap */
414
415 RegionElement->Address = BaseAddress;
416 RegionElement->Length = Length;
417 BufferResize = TRUE;
418 }
419
420 /*
421 * only remaining case is fully contained for which we don't
422 * need to do anything
423 */
424 if (BufferResize)
425 {
426 NewBuffer = AcpiOsAllocate (RegionElement->Length);
427 if (!NewBuffer)
428 {
429 return (AE_NO_MEMORY);
430 }
431
432 OldBuffer = RegionElement->Buffer;
433 RegionElement->Buffer = NewBuffer;
434 NewBuffer = NULL;
435
436 /* Initialize the region with the default fill value */
437
438 memset (RegionElement->Buffer,
439 AcpiGbl_RegionFillValue, RegionElement->Length);
440
441 /*
442 * Get BufferValue to point (within the new buffer) to the
443 * base address of the old buffer
444 */
445 BufferValue = (UINT8 *) RegionElement->Buffer +
446 (UINT64) RegionAddress -
447 (UINT64) RegionElement->Address;
448
449 /*
450 * Copy the old buffer to its same location within the new
451 * buffer
452 */
453 memcpy (BufferValue, OldBuffer, RegionLength);
454 AcpiOsFree (OldBuffer);
455 }
456 }
457 }
458 }
459
460 /*
461 * If the Region buffer does not exist, create it now
462 */
463 if (!BufferExists)
464 {
465 /* Do the memory allocations first */
466
467 RegionElement = AcpiOsAllocate (sizeof (AE_REGION));
468 if (!RegionElement)
469 {
470 return (AE_NO_MEMORY);
471 }
472
473 RegionElement->Buffer = AcpiOsAllocate (Length);
474 if (!RegionElement->Buffer)
475 {
476 AcpiOsFree (RegionElement);
477 return (AE_NO_MEMORY);
478 }
479
480 /* Initialize the region with the default fill value */
481
482 memset (RegionElement->Buffer, AcpiGbl_RegionFillValue, Length);
483
484 RegionElement->Address = BaseAddress;
485 RegionElement->Length = Length;
486 RegionElement->SpaceId = SpaceId;
487 RegionElement->NextRegion = NULL;
488
489 /*
490 * Increment the number of regions and put this one
491 * at the head of the list as it will probably get accessed
492 * more often anyway.
493 */
494 AeRegions.NumberOfRegions += 1;
495
496 if (AeRegions.RegionList)
497 {
498 RegionElement->NextRegion = AeRegions.RegionList;
499 }
500
501 AeRegions.RegionList = RegionElement;
502 }
503
504 /* Calculate the size of the memory copy */
505
506 ByteWidth = (BitWidth / 8);
507 if (BitWidth % 8)
508 {
509 ByteWidth += 1;
510 }
511
512 /*
513 * The buffer exists and is pointed to by RegionElement.
514 * We now need to verify the request is valid and perform the operation.
515 *
516 * NOTE: RegionElement->Length is in bytes, therefore it we compare against
517 * ByteWidth (see above)
518 */
519 if ((RegionObject->Region.SpaceId != ACPI_ADR_SPACE_GPIO) &&
520 ((UINT64) Address + ByteWidth) >
521 ((UINT64)(RegionElement->Address) + RegionElement->Length))
522 {
523 ACPI_WARNING ((AE_INFO,
524 "Request on [%4.4s] is beyond region limit "
525 "Req-0x%X+0x%X, Base=0x%X, Len-0x%X",
526 (RegionObject->Region.Node)->Name.Ascii, (UINT32) Address,
527 ByteWidth, (UINT32)(RegionElement->Address),
528 RegionElement->Length));
529
530 return (AE_AML_REGION_LIMIT);
531 }
532
533 /*
534 * Get BufferValue to point to the "address" in the buffer
535 */
536 BufferValue = ((UINT8 *) RegionElement->Buffer +
537 ((UINT64) Address - (UINT64) RegionElement->Address));
538
539 DoFunction:
540 /*
541 * Perform a read or write to the buffer space
542 */
543 switch (Function)
544 {
545 case ACPI_READ:
546 /*
547 * Set the pointer Value to whatever is in the buffer
548 */
549 memcpy (Value, BufferValue, ByteWidth);
550 break;
551
552 case ACPI_WRITE:
553 /*
554 * Write the contents of Value to the buffer
555 */
556 memcpy (BufferValue, Value, ByteWidth);
557 break;
558
559 default:
560
561 return (AE_BAD_PARAMETER);
562 }
563
564 if (AcpiGbl_DisplayRegionAccess)
565 {
566 switch (SpaceId)
567 {
568 case ACPI_ADR_SPACE_SYSTEM_MEMORY:
569
570 AcpiOsPrintf ("AcpiExec: SystemMemory "
571 "%s: Val %.8X Addr %.4X BitWidth %X [REGION: BaseAddr %.4X Len %.2X]\n",
572 (Function & ACPI_IO_MASK) ? "Write" : "Read ",
573 (UINT32) *Value, (UINT32) Address, BitWidth, (UINT32) BaseAddress, Length);
574 break;
575
576 case ACPI_ADR_SPACE_GSBUS:
577
578 AcpiOsPrintf ("AcpiExec: GenericSerialBus\n");
579 break;
580
581 case ACPI_ADR_SPACE_GPIO: /* ACPI 5.0 */
582
583 /* This space is required to always be ByteAcc */
584
585 Status = AcpiBufferToResource (MyContext->Connection,
586 MyContext->Length, &Resource);
587
588 AcpiOsPrintf ("AcpiExec: GeneralPurposeIo "
589 "%s: %.8X Addr %.4X BaseAddr %.4X Length %.2X "
590 "BitWidth %X AccLen %.2X Conn %p\n",
591 (Function & ACPI_IO_MASK) ? "Write" : "Read ", (UINT32) *Value,
592 (UINT32) Address, (UINT32) BaseAddress, Length, BitWidth,
593 MyContext->AccessLength, MyContext->Connection);
594 if (ACPI_SUCCESS (Status))
595 {
596 ACPI_FREE (Resource);
597 }
598 break;
599
600 default:
601
602 AcpiOsPrintf ("AcpiExec: Region access on SpaceId %2.2X\n", SpaceId);
603 break;
604 }
605 }
606
607 return (AE_OK);
608 }
609