1 /******************************************************************************
2 *
3 * Module Name: aeregion - Handler for operation regions
4 *
5 *****************************************************************************/
6
7 /*
8 * Copyright (C) 2000 - 2023, 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 MERCHANTABILITY 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
AeRegionHandler(UINT32 Function,ACPI_PHYSICAL_ADDRESS Address,UINT32 BitWidth,UINT64 * Value,void * HandlerContext,void * RegionContext)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 char Uuid[ACPI_PRM_INPUT_BUFFER_SIZE + 1];
101
102
103 ACPI_FUNCTION_NAME (AeRegionHandler);
104
105
106 /* If the object is not a region, simply return */
107
108 if (RegionObject->Region.Type != ACPI_TYPE_REGION)
109 {
110 return (AE_OK);
111 }
112
113 /* Check that we actually got back our context parameter */
114
115 if (HandlerContext != &AeMyContext)
116 {
117 AcpiOsPrintf (
118 "Region handler received incorrect context %p, should be %p\n",
119 HandlerContext, &AeMyContext);
120 }
121
122 MyContext = ACPI_CAST_PTR (ACPI_CONNECTION_INFO, HandlerContext);
123
124 /*
125 * Find the region's address space and length before searching
126 * the linked list.
127 */
128 BaseAddress = RegionObject->Region.Address;
129 Length = RegionObject->Region.Length;
130 SpaceId = RegionObject->Region.SpaceId;
131
132 ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION,
133 "Operation Region request on %s at 0x%X, BitWidth 0x%X, RegionLength 0x%X\n",
134 AcpiUtGetRegionName (RegionObject->Region.SpaceId),
135 (UINT32) Address, BitWidth, (UINT32) Length));
136
137 /*
138 * Region support can be disabled with the -do option.
139 * We use this to support dynamically loaded tables where we pass a valid
140 * address to the AML.
141 */
142 if (AcpiGbl_DbOpt_NoRegionSupport)
143 {
144 BufferValue = ACPI_TO_POINTER (Address);
145 ByteWidth = (BitWidth / 8);
146
147 if (BitWidth % 8)
148 {
149 ByteWidth += 1;
150 }
151 goto DoFunction;
152 }
153
154 switch (SpaceId)
155 {
156 case ACPI_ADR_SPACE_SYSTEM_IO:
157 /*
158 * For I/O space, exercise the port validation
159 * Note: ReadPort currently always returns all ones, length=BitLength
160 */
161 switch (Function & ACPI_IO_MASK)
162 {
163 case ACPI_READ:
164
165 if (BitWidth == 64)
166 {
167 /* Split the 64-bit request into two 32-bit requests */
168
169 Status = AcpiHwReadPort (Address, &Value1, 32);
170 ACPI_CHECK_OK (AcpiHwReadPort, Status);
171 Status = AcpiHwReadPort (Address+4, &Value2, 32);
172 ACPI_CHECK_OK (AcpiHwReadPort, Status);
173
174 *Value = Value1 | ((UINT64) Value2 << 32);
175 }
176 else
177 {
178 Status = AcpiHwReadPort (Address, &Value1, BitWidth);
179 ACPI_CHECK_OK (AcpiHwReadPort, Status);
180 *Value = (UINT64) Value1;
181 }
182 break;
183
184 case ACPI_WRITE:
185
186 if (BitWidth == 64)
187 {
188 /* Split the 64-bit request into two 32-bit requests */
189
190 Status = AcpiHwWritePort (Address, ACPI_LODWORD (*Value), 32);
191 ACPI_CHECK_OK (AcpiHwWritePort, Status);
192 Status = AcpiHwWritePort (Address+4, ACPI_HIDWORD (*Value), 32);
193 ACPI_CHECK_OK (AcpiHwWritePort, Status);
194 }
195 else
196 {
197 Status = AcpiHwWritePort (Address, (UINT32) *Value, BitWidth);
198 ACPI_CHECK_OK (AcpiHwWritePort, Status);
199 }
200 break;
201
202 default:
203
204 Status = AE_BAD_PARAMETER;
205 break;
206 }
207
208 if (ACPI_FAILURE (Status))
209 {
210 return (Status);
211 }
212
213 /* Now go ahead and simulate the hardware */
214 break;
215
216 /*
217 * SMBus and GenericSerialBus support the various bidirectional
218 * protocols.
219 */
220 case ACPI_ADR_SPACE_SMBUS:
221 case ACPI_ADR_SPACE_GSBUS: /* ACPI 5.0 */
222
223 Status = AcpiExGetProtocolBufferLength ((Function >> 16), &Length);
224 if (ACPI_FAILURE (Status))
225 {
226 AcpiOsPrintf ("AcpiExec: Invalid SMbus/GSbus protocol ID: 0x%X\n",
227 (Function >> 16));
228 return (Status);
229 }
230
231 /* Adjust for fixed SMBus buffer size */
232
233 if ((SpaceId == ACPI_ADR_SPACE_SMBUS) &&
234 (Length > ACPI_SMBUS_DATA_SIZE))
235 {
236 Length = ACPI_SMBUS_DATA_SIZE; /* SMBus buffer is fixed-length */
237 }
238
239 if (AcpiGbl_DisplayRegionAccess)
240 {
241 AcpiOsPrintf ("AcpiExec: %s "
242 "%s: Attr %X Addr %.4X BaseAddr %.4X Length %.2X BitWidth %X BufLen %X\n",
243 AcpiUtGetRegionName (SpaceId),
244 (Function & ACPI_IO_MASK) ? "Write" : "Read ",
245 (UINT32) (Function >> 16),
246 (UINT32) Address, (UINT32) BaseAddress,
247 Length, BitWidth, Buffer[1]);
248
249 /* GenericSerialBus has a Connection() parameter */
250
251 if ((SpaceId == ACPI_ADR_SPACE_GSBUS) && MyContext)
252 {
253 Status = AcpiBufferToResource (MyContext->Connection,
254 MyContext->Length, &Resource);
255 if (ACPI_SUCCESS (Status))
256 {
257 ACPI_FREE (Resource);
258 }
259
260 AcpiOsPrintf (" [AccessLength %.2X Connection %p]",
261 MyContext->AccessLength, MyContext->Connection);
262 }
263
264 AcpiOsPrintf ("\n");
265 }
266
267 DataBuffer = &Buffer[2];
268 DataLength = (UINT8) Length;
269
270 /* Setup the return buffer. Note: ASLTS depends on these fill values */
271
272 if (Length == ACPI_MAX_GSBUS_DATA_SIZE)
273 {
274 DataLength = 0x20; /* For ASLTS only */
275 }
276
277 for (i = 0; i < Length; i++)
278 {
279 DataBuffer[i] = (UINT8) (0xA0 + i);
280 }
281
282 Buffer[0] = 0; /* Return Status, OK */
283 Buffer[1] = DataLength; /* Length of valid data */
284 return (AE_OK);
285
286 case ACPI_ADR_SPACE_IPMI: /* ACPI 4.0 */
287
288 if (AcpiGbl_DisplayRegionAccess)
289 {
290 AcpiOsPrintf ("AcpiExec: IPMI "
291 "%s: Attr %X Addr %.4X BaseAddr %.4X Len %.2X Width %X BufLen %X\n",
292 (Function & ACPI_IO_MASK) ? "Write" : "Read ",
293 (UINT32) (Function >> 16), (UINT32) Address, (UINT32) BaseAddress,
294 Length, BitWidth, Buffer[1]);
295 }
296
297 /*
298 * Regardless of a READ or WRITE, this handler is passed a 66-byte
299 * buffer in which to return the IPMI status/length/data.
300 *
301 * Return some example data to show use of the bidirectional buffer
302 */
303 Buffer[0] = 0; /* Status byte */
304 Buffer[1] = ACPI_IPMI_DATA_SIZE; /* Return buffer data length */
305 Buffer[2] = 0; /* Completion code */
306 Buffer[3] = 0; /* Reserved */
307
308 /*
309 * Fill the 66-byte buffer with the return data.
310 * Note: ASLTS depends on these fill values.
311 */
312 for (i = 4; i < ACPI_IPMI_BUFFER_SIZE; i++)
313 {
314 Buffer[i] = (UINT8) (i);
315 }
316 return (AE_OK);
317
318 /*
319 * GPIO has some special semantics:
320 * 1) Address is the pin number index into the Connection() pin list
321 * 2) BitWidth is the actual number of bits (pins) defined by the field
322 */
323 case ACPI_ADR_SPACE_GPIO: /* ACPI 5.0 */
324
325 if (AcpiGbl_DisplayRegionAccess)
326 {
327 AcpiOsPrintf ("AcpiExec: GPIO "
328 "%s: Address %.4X Length %X BitWidth %X Conn %p\n",
329 (Function & ACPI_IO_MASK) ? "Write" : "Read ",
330 (UINT32) Address, Length, BitWidth, MyContext->Connection);
331 }
332
333 /* Now perform the "normal" SystemMemory handling, for AcpiExec only */
334 break;
335
336 /*
337 * PCC operation region will write the entire subspace's data and expect
338 * a response from the hardware. For acpiexec, we'll fill the buffer with
339 * default values. Note: ASLTS will depend on these values.
340 */
341 case ACPI_ADR_SPACE_PLATFORM_COMM: /* ACPI 6.3 */
342
343 if (AcpiGbl_DisplayRegionAccess)
344 {
345 AcpiOsPrintf ("AcpiExec: PCC Write : Addr %.4X Width %X\n",
346 (UINT32) Address, BitWidth);
347 }
348 for (i = 0; i < Length; ++i)
349 {
350 Buffer[i] = (UINT8) i;
351 }
352 return (AE_OK);
353
354 case ACPI_ADR_SPACE_PLATFORM_RT:
355
356 AcpiOsPrintf ("Acpiexec: PRM %s invoked\n",
357 (Function & ACPI_IO_MASK) ? "Write" : "Read ");
358
359 if ((Function & ACPI_IO_MASK) == ACPI_WRITE)
360 {
361 AcpiUtConvertUuidToString((char *) Buffer + 10, Uuid);
362 AcpiOsPrintf ("Mode: %u GUID: %s\n", Buffer[0], Uuid);
363 }
364
365 /* Unpack the input buffer and print the contents for debug */
366
367 break;
368
369 default:
370 break;
371 }
372
373 /*
374 * Search through the linked list for this region's buffer
375 */
376 BufferExists = FALSE;
377 BufferResize = FALSE;
378 RegionElement = AeRegions.RegionList;
379
380 if (AeRegions.NumberOfRegions)
381 {
382 BaseAddressEnd = BaseAddress + Length - 1;
383 while (!BufferExists && RegionElement)
384 {
385 RegionAddress = RegionElement->Address;
386 RegionAddressEnd = RegionElement->Address + RegionElement->Length - 1;
387 RegionLength = RegionElement->Length;
388
389 /*
390 * Overlapping Region Support
391 *
392 * While searching through the region buffer list, determine if an
393 * overlap exists between the requested buffer space and the current
394 * RegionElement space. If there is an overlap then replace the old
395 * buffer with a new buffer of increased size before continuing to
396 * do the read or write
397 */
398 if (RegionElement->SpaceId != SpaceId ||
399 BaseAddressEnd < RegionAddress ||
400 BaseAddress > RegionAddressEnd)
401 {
402 /*
403 * Requested buffer is outside of the current RegionElement
404 * bounds
405 */
406 RegionElement = RegionElement->NextRegion;
407 }
408 else
409 {
410 /*
411 * Some amount of buffer space sharing exists. There are 4 cases
412 * to consider:
413 *
414 * 1. Right overlap
415 * 2. Left overlap
416 * 3. Left and right overlap
417 * 4. Fully contained - no resizing required
418 */
419 BufferExists = TRUE;
420
421 if ((BaseAddress >= RegionAddress) &&
422 (BaseAddress <= RegionAddressEnd) &&
423 (BaseAddressEnd > RegionAddressEnd))
424 {
425 /* Right overlap */
426
427 RegionElement->Length = (UINT32) (BaseAddress -
428 RegionAddress + Length);
429 BufferResize = TRUE;
430 }
431
432 else if ((BaseAddressEnd >= RegionAddress) &&
433 (BaseAddressEnd <= RegionAddressEnd) &&
434 (BaseAddress < RegionAddress))
435 {
436 /* Left overlap */
437
438 RegionElement->Address = BaseAddress;
439 RegionElement->Length = (UINT32) (RegionAddress -
440 BaseAddress + RegionElement->Length);
441 BufferResize = TRUE;
442 }
443
444 else if ((BaseAddress < RegionAddress) &&
445 (BaseAddressEnd > RegionAddressEnd))
446 {
447 /* Left and right overlap */
448
449 RegionElement->Address = BaseAddress;
450 RegionElement->Length = Length;
451 BufferResize = TRUE;
452 }
453
454 /*
455 * only remaining case is fully contained for which we don't
456 * need to do anything
457 */
458 if (BufferResize)
459 {
460 NewBuffer = AcpiOsAllocate (RegionElement->Length);
461 if (!NewBuffer)
462 {
463 return (AE_NO_MEMORY);
464 }
465
466 OldBuffer = RegionElement->Buffer;
467 RegionElement->Buffer = NewBuffer;
468 NewBuffer = NULL;
469
470 /* Initialize the region with the default fill value */
471
472 memset (RegionElement->Buffer,
473 AcpiGbl_RegionFillValue, RegionElement->Length);
474
475 /*
476 * Get BufferValue to point (within the new buffer) to the
477 * base address of the old buffer
478 */
479 BufferValue = (UINT8 *) RegionElement->Buffer +
480 (UINT64) RegionAddress -
481 (UINT64) RegionElement->Address;
482
483 /*
484 * Copy the old buffer to its same location within the new
485 * buffer
486 */
487 memcpy (BufferValue, OldBuffer, RegionLength);
488 AcpiOsFree (OldBuffer);
489 }
490 }
491 }
492 }
493
494 /*
495 * If the Region buffer does not exist, create it now
496 */
497 if (!BufferExists)
498 {
499 /* Do the memory allocations first */
500
501 RegionElement = AcpiOsAllocate (sizeof (AE_REGION));
502 if (!RegionElement)
503 {
504 return (AE_NO_MEMORY);
505 }
506
507 RegionElement->Buffer = AcpiOsAllocate (Length);
508 if (!RegionElement->Buffer)
509 {
510 AcpiOsFree (RegionElement);
511 return (AE_NO_MEMORY);
512 }
513
514 /* Initialize the region with the default fill value */
515
516 memset (RegionElement->Buffer, AcpiGbl_RegionFillValue, Length);
517
518 RegionElement->Address = BaseAddress;
519 RegionElement->Length = Length;
520 RegionElement->SpaceId = SpaceId;
521 RegionElement->NextRegion = NULL;
522
523 /*
524 * Increment the number of regions and put this one
525 * at the head of the list as it will probably get accessed
526 * more often anyway.
527 */
528 AeRegions.NumberOfRegions += 1;
529
530 if (AeRegions.RegionList)
531 {
532 RegionElement->NextRegion = AeRegions.RegionList;
533 }
534
535 AeRegions.RegionList = RegionElement;
536 }
537
538 /* Calculate the size of the memory copy */
539
540 ByteWidth = (BitWidth / 8);
541 if (BitWidth % 8)
542 {
543 ByteWidth += 1;
544 }
545
546 /*
547 * The buffer exists and is pointed to by RegionElement.
548 * We now need to verify the request is valid and perform the operation.
549 *
550 * NOTE: RegionElement->Length is in bytes, therefore it we compare against
551 * ByteWidth (see above)
552 */
553 if ((RegionObject->Region.SpaceId != ACPI_ADR_SPACE_GPIO) &&
554 ((UINT64) Address + ByteWidth) >
555 ((UINT64)(RegionElement->Address) + RegionElement->Length))
556 {
557 ACPI_WARNING ((AE_INFO,
558 "Request on [%4.4s] is beyond region limit "
559 "Req-0x%X+0x%X, Base=0x%X, Len-0x%X",
560 (RegionObject->Region.Node)->Name.Ascii, (UINT32) Address,
561 ByteWidth, (UINT32)(RegionElement->Address),
562 RegionElement->Length));
563
564 return (AE_AML_REGION_LIMIT);
565 }
566
567 /*
568 * Get BufferValue to point to the "address" in the buffer
569 */
570 BufferValue = ((UINT8 *) RegionElement->Buffer +
571 ((UINT64) Address - (UINT64) RegionElement->Address));
572
573 DoFunction:
574 /*
575 * Perform a read or write to the buffer space
576 */
577 switch (Function)
578 {
579 case ACPI_READ:
580 /*
581 * Set the pointer Value to whatever is in the buffer
582 */
583 memcpy (Value, BufferValue, ByteWidth);
584 break;
585
586 case ACPI_WRITE:
587 /*
588 * Write the contents of Value to the buffer
589 */
590 memcpy (BufferValue, Value, ByteWidth);
591 break;
592
593 default:
594
595 return (AE_BAD_PARAMETER);
596 }
597
598 if (AcpiGbl_DisplayRegionAccess)
599 {
600 switch (SpaceId)
601 {
602 case ACPI_ADR_SPACE_SYSTEM_MEMORY:
603
604 AcpiOsPrintf ("AcpiExec: SystemMemory "
605 "%s: Val %.8X Addr %.4X BitWidth %X [REGION: BaseAddr %.4X Len %.2X]\n",
606 (Function & ACPI_IO_MASK) ? "Write" : "Read ",
607 (UINT32) *Value, (UINT32) Address, BitWidth, (UINT32) BaseAddress, Length);
608 break;
609
610 case ACPI_ADR_SPACE_GSBUS:
611
612 AcpiOsPrintf ("AcpiExec: GenericSerialBus\n");
613 break;
614
615 case ACPI_ADR_SPACE_GPIO: /* ACPI 5.0 */
616
617 /* This space is required to always be ByteAcc */
618
619 Status = AcpiBufferToResource (MyContext->Connection,
620 MyContext->Length, &Resource);
621
622 AcpiOsPrintf ("AcpiExec: GeneralPurposeIo "
623 "%s: %.8X Addr %.4X BaseAddr %.4X Length %.2X "
624 "BitWidth %X AccLen %.2X Conn %p\n",
625 (Function & ACPI_IO_MASK) ? "Write" : "Read ", (UINT32) *Value,
626 (UINT32) Address, (UINT32) BaseAddress, Length, BitWidth,
627 MyContext->AccessLength, MyContext->Connection);
628 if (ACPI_SUCCESS (Status))
629 {
630 ACPI_FREE (Resource);
631 }
632 break;
633
634 default:
635
636 AcpiOsPrintf ("AcpiExec: Region access on SpaceId %2.2X\n", SpaceId);
637 break;
638 }
639 }
640
641 return (AE_OK);
642 }
643