]> git.proxmox.com Git - mirror_edk2.git/blob - DynamicTablesPkg/Library/Acpi/Arm/AcpiSsdtPcieLibArm/SsdtPcieGenerator.c
DynamicTablesPkg: AcpiSsdtPcieLibArm: Allow use of segment number as UID
[mirror_edk2.git] / DynamicTablesPkg / Library / Acpi / Arm / AcpiSsdtPcieLibArm / SsdtPcieGenerator.c
1 /** @file
2 SSDT Pcie Table Generator.
3
4 Copyright (c) 2021 - 2022, Arm Limited. All rights reserved.<BR>
5
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7
8 @par Reference(s):
9 - PCI Firmware Specification - Revision 3.0
10 - ACPI 6.4 specification:
11 - s6.2.13 "_PRT (PCI Routing Table)"
12 - s6.1.1 "_ADR (Address)"
13 - linux kernel code
14 - Arm Base Boot Requirements v1.0
15 - Arm Base System Architecture v1.0
16 **/
17
18 #include <Library/AcpiLib.h>
19 #include <Library/BaseLib.h>
20 #include <Library/BaseMemoryLib.h>
21 #include <Library/DebugLib.h>
22 #include <Library/MemoryAllocationLib.h>
23 #include <Protocol/AcpiTable.h>
24
25 // Module specific include files.
26 #include <AcpiTableGenerator.h>
27 #include <ConfigurationManagerObject.h>
28 #include <ConfigurationManagerHelper.h>
29 #include <Library/AcpiHelperLib.h>
30 #include <Library/TableHelperLib.h>
31 #include <Library/AmlLib/AmlLib.h>
32 #include <Library/SsdtPcieSupportLib.h>
33 #include <Protocol/ConfigurationManagerProtocol.h>
34
35 #include "SsdtPcieGenerator.h"
36
37 #define PCI_MAX_DEVICE_COUNT_PER_BUS 32
38 #define PCI_MAX_FUNCTION_COUNT_PER_DEVICE 8
39
40 /** ARM standard SSDT Pcie Table Generator.
41
42 Requirements:
43 The following Configuration Manager Object(s) are required by
44 this Generator:
45 - EArmObjCmRef
46 - EArmObjPciConfigSpaceInfo
47 - EArmObjPciAddressMapInfo
48 - EArmObjPciInterruptMapInfo
49 */
50
51 /** This macro expands to a function that retrieves the cross-CM-object-
52 reference information from the Configuration Manager.
53 */
54 GET_OBJECT_LIST (
55 EObjNameSpaceArm,
56 EArmObjCmRef,
57 CM_ARM_OBJ_REF
58 );
59
60 /** This macro expands to a function that retrieves the Pci
61 Configuration Space Information from the Configuration Manager.
62 */
63 GET_OBJECT_LIST (
64 EObjNameSpaceArm,
65 EArmObjPciConfigSpaceInfo,
66 CM_ARM_PCI_CONFIG_SPACE_INFO
67 );
68
69 /** This macro expands to a function that retrieves the Pci
70 Address Mapping Information from the Configuration Manager.
71 */
72 GET_OBJECT_LIST (
73 EObjNameSpaceArm,
74 EArmObjPciAddressMapInfo,
75 CM_ARM_PCI_ADDRESS_MAP_INFO
76 );
77
78 /** This macro expands to a function that retrieves the Pci
79 Interrupt Mapping Information from the Configuration Manager.
80 */
81 GET_OBJECT_LIST (
82 EObjNameSpaceArm,
83 EArmObjPciInterruptMapInfo,
84 CM_ARM_PCI_INTERRUPT_MAP_INFO
85 );
86
87 /** Initialize the MappingTable.
88
89 @param [in] MappingTable The mapping table structure.
90 @param [in] Count Number of entries to allocate in the
91 MappingTable.
92
93 @retval EFI_SUCCESS Success.
94 @retval EFI_INVALID_PARAMETER Invalid parameter.
95 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
96 **/
97 STATIC
98 EFI_STATUS
99 EFIAPI
100 MappingTableInitialize (
101 IN MAPPING_TABLE *MappingTable,
102 IN UINT32 Count
103 )
104 {
105 UINT32 *Table;
106
107 if ((MappingTable == NULL) ||
108 (Count == 0))
109 {
110 ASSERT (0);
111 return EFI_INVALID_PARAMETER;
112 }
113
114 Table = AllocateZeroPool (sizeof (*Table) * Count);
115 if (Table == NULL) {
116 ASSERT (0);
117 return EFI_OUT_OF_RESOURCES;
118 }
119
120 MappingTable->Table = Table;
121 MappingTable->LastIndex = 0;
122 MappingTable->MaxIndex = Count;
123
124 return EFI_SUCCESS;
125 }
126
127 /** Free the MappingTable.
128
129 @param [in, out] MappingTable The mapping table structure.
130 **/
131 STATIC
132 VOID
133 EFIAPI
134 MappingTableFree (
135 IN OUT MAPPING_TABLE *MappingTable
136 )
137 {
138 ASSERT (MappingTable != NULL);
139 ASSERT (MappingTable->Table != NULL);
140
141 if (MappingTable->Table != NULL) {
142 FreePool (MappingTable->Table);
143 }
144 }
145
146 /** Add a new entry to the MappingTable and return its index.
147
148 If an entry with [Integer] is already available in the table,
149 return its index without adding a new entry.
150
151 @param [in] MappingTable The mapping table structure.
152 @param [in] Integer New Integer entry to add.
153
154 @retval The index of the Integer entry in the MappingTable.
155 **/
156 STATIC
157 UINT32
158 EFIAPI
159 MappingTableAdd (
160 IN MAPPING_TABLE *MappingTable,
161 IN UINT32 Integer
162 )
163 {
164 UINT32 *Table;
165 UINT32 Index;
166 UINT32 LastIndex;
167
168 ASSERT (MappingTable != NULL);
169 ASSERT (MappingTable->Table != NULL);
170
171 Table = MappingTable->Table;
172 LastIndex = MappingTable->LastIndex;
173
174 // Search if there is already an entry with this Integer.
175 for (Index = 0; Index < LastIndex; Index++) {
176 if (Table[Index] == Integer) {
177 return Index;
178 }
179 }
180
181 ASSERT (LastIndex < MappingTable->MaxIndex);
182
183 // If no, create a new entry.
184 Table[LastIndex] = Integer;
185
186 return MappingTable->LastIndex++;
187 }
188
189 /** Generate required Pci device information.
190
191 ASL code:
192 Name (_UID, <Uid>) // Uid of the Pci device
193 Name (_HID, EISAID ("PNP0A08")) // PCI Express Root Bridge
194 Name (_CID, EISAID ("PNP0A03")) // Compatible PCI Root Bridge
195 Name (_SEG, <Pci segment group>) // PCI Segment Group number
196 Name (_BBN, <Bus number>) // PCI Base Bus Number
197 Name (_CCA, 1) // Initially mark the PCI coherent
198
199 @param [in] PciInfo Pci device information.
200 @param [in] Uid Unique Id of the Pci device.
201 @param [in, out] PciNode Pci node to amend.
202
203 @retval EFI_SUCCESS Success.
204 @retval EFI_INVALID_PARAMETER Invalid parameter.
205 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
206 **/
207 STATIC
208 EFI_STATUS
209 EFIAPI
210 GeneratePciDeviceInfo (
211 IN CONST CM_ARM_PCI_CONFIG_SPACE_INFO *PciInfo,
212 IN UINT32 Uid,
213 IN OUT AML_OBJECT_NODE_HANDLE PciNode
214 )
215 {
216 EFI_STATUS Status;
217 UINT32 EisaId;
218
219 ASSERT (PciInfo != NULL);
220 ASSERT (PciNode != NULL);
221
222 // ASL: Name (_UID, <Uid>)
223 Status = AmlCodeGenNameInteger ("_UID", Uid, PciNode, NULL);
224 if (EFI_ERROR (Status)) {
225 ASSERT (0);
226 return Status;
227 }
228
229 // ASL: Name (_HID, EISAID ("PNP0A08"))
230 Status = AmlGetEisaIdFromString ("PNP0A08", &EisaId);
231 if (EFI_ERROR (Status)) {
232 ASSERT (0);
233 return Status;
234 }
235
236 Status = AmlCodeGenNameInteger ("_HID", EisaId, PciNode, NULL);
237 if (EFI_ERROR (Status)) {
238 ASSERT (0);
239 return Status;
240 }
241
242 // ASL: Name (_CID, EISAID ("PNP0A03"))
243 Status = AmlGetEisaIdFromString ("PNP0A03", &EisaId);
244 if (EFI_ERROR (Status)) {
245 ASSERT (0);
246 return Status;
247 }
248
249 Status = AmlCodeGenNameInteger ("_CID", EisaId, PciNode, NULL);
250 if (EFI_ERROR (Status)) {
251 ASSERT (0);
252 return Status;
253 }
254
255 // ASL: Name (_SEG, <Pci segment group>)
256 Status = AmlCodeGenNameInteger (
257 "_SEG",
258 PciInfo->PciSegmentGroupNumber,
259 PciNode,
260 NULL
261 );
262 if (EFI_ERROR (Status)) {
263 ASSERT (0);
264 return Status;
265 }
266
267 // ASL: Name (_BBN, <Bus number>)
268 Status = AmlCodeGenNameInteger (
269 "_BBN",
270 PciInfo->StartBusNumber,
271 PciNode,
272 NULL
273 );
274 if (EFI_ERROR (Status)) {
275 ASSERT (0);
276 return Status;
277 }
278
279 // ASL: Name (_CCA, 1)
280 // Must be aligned with the IORT CCA property in
281 // "Table 14 Memory access properties"
282 Status = AmlCodeGenNameInteger ("_CCA", 1, PciNode, NULL);
283 ASSERT_EFI_ERROR (Status);
284 return Status;
285 }
286
287 /** Generate a _PRT object (Pci Routing Table) for the Pci device.
288
289 Cf. ACPI 6.4 specification, s6.2.13 "_PRT (PCI Routing Table)"
290
291 The first model (defining a _CRS object) is used. This is necessary because
292 PCI legacy interrupts are active low and GICv2 SPI interrupts are active
293 high.
294 Even though PCI interrupts cannot be re-routed, only the first model allows
295 to specify the activation state (low/high).
296
297 @param [in] Generator The SSDT Pci generator.
298 @param [in] CfgMgrProtocol Pointer to the Configuration Manager
299 Protocol interface.
300 @param [in] PciInfo Pci device information.
301 @param [in] Uid Unique Id of the Pci device.
302 @param [in, out] PciNode Pci node to amend.
303
304 @retval EFI_SUCCESS The function completed successfully.
305 @retval EFI_INVALID_PARAMETER Invalid parameter.
306 @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
307 **/
308 STATIC
309 EFI_STATUS
310 EFIAPI
311 GeneratePrt (
312 IN ACPI_PCI_GENERATOR *Generator,
313 IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol,
314 IN CONST CM_ARM_PCI_CONFIG_SPACE_INFO *PciInfo,
315 IN UINT32 Uid,
316 IN OUT AML_OBJECT_NODE_HANDLE PciNode
317 )
318 {
319 EFI_STATUS Status;
320 INT32 Index;
321 AML_OBJECT_NODE_HANDLE PrtNode;
322 CM_ARM_OBJ_REF *RefInfo;
323 UINT32 RefCount;
324 CM_ARM_PCI_INTERRUPT_MAP_INFO *IrqMapInfo;
325
326 ASSERT (Generator != NULL);
327 ASSERT (CfgMgrProtocol != NULL);
328 ASSERT (PciInfo != NULL);
329 ASSERT (PciNode != NULL);
330
331 PrtNode = NULL;
332
333 // Get the array of CM_ARM_OBJ_REF referencing the
334 // CM_ARM_PCI_INTERRUPT_MAP_INFO objects.
335 Status = GetEArmObjCmRef (
336 CfgMgrProtocol,
337 PciInfo->InterruptMapToken,
338 &RefInfo,
339 &RefCount
340 );
341 if (EFI_ERROR (Status)) {
342 ASSERT (0);
343 return Status;
344 }
345
346 // Initialized DeviceTable.
347 Status = MappingTableInitialize (&Generator->DeviceTable, RefCount);
348 if (EFI_ERROR (Status)) {
349 ASSERT (0);
350 goto exit_handler0;
351 }
352
353 // ASL: Name (_PRT, Package () {})
354 Status = AmlCodeGenNamePackage ("_PRT", NULL, &PrtNode);
355 if (EFI_ERROR (Status)) {
356 ASSERT (0);
357 goto exit_handler;
358 }
359
360 for (Index = 0; Index < RefCount; Index++) {
361 // Get CM_ARM_PCI_INTERRUPT_MAP_INFO structures one by one.
362 Status = GetEArmObjPciInterruptMapInfo (
363 CfgMgrProtocol,
364 RefInfo[Index].ReferenceToken,
365 &IrqMapInfo,
366 NULL
367 );
368 if (EFI_ERROR (Status)) {
369 ASSERT (0);
370 goto exit_handler;
371 }
372
373 // Check that the interrupts flags are SPIs, level high.
374 // Cf. Arm BSA v1.0, sE.6 "Legacy interrupts"
375 if ((Index > 0) &&
376 (IrqMapInfo->IntcInterrupt.Interrupt >= 32) &&
377 (IrqMapInfo->IntcInterrupt.Interrupt < 1020) &&
378 ((IrqMapInfo->IntcInterrupt.Flags & 0xB) != 0))
379 {
380 Status = EFI_INVALID_PARAMETER;
381 ASSERT_EFI_ERROR (Status);
382 goto exit_handler;
383 }
384
385 // Add the device to the DeviceTable.
386 MappingTableAdd (&Generator->DeviceTable, IrqMapInfo->PciDevice);
387
388 /* Add a _PRT entry.
389 ASL
390 Name (_PRT, Package () {
391 <OldPrtEntries>,
392 <NewPrtEntry>
393 })
394
395 Address is set as:
396 ACPI 6.4 specification, Table 6.2: "ADR Object Address Encodings"
397 High word-Device #, Low word-Function #. (for example, device 3,
398 function 2 is 0x00030002). To refer to all the functions on a device #,
399 use a function number of FFFF).
400 */
401 Status = AmlAddPrtEntry (
402 (IrqMapInfo->PciDevice << 16) | 0xFFFF,
403 IrqMapInfo->PciInterrupt,
404 NULL,
405 IrqMapInfo->IntcInterrupt.Interrupt,
406 PrtNode
407 );
408 if (EFI_ERROR (Status)) {
409 ASSERT (0);
410 goto exit_handler;
411 }
412 } // for
413
414 // Attach the _PRT entry.
415 Status = AmlAttachNode (PciNode, PrtNode);
416 if (EFI_ERROR (Status)) {
417 ASSERT_EFI_ERROR (Status);
418 goto exit_handler;
419 }
420
421 PrtNode = NULL;
422
423 // Generate the Pci slots once all the device have been added.
424 Status = GeneratePciSlots (PciInfo, &Generator->DeviceTable, Uid, PciNode);
425 if (EFI_ERROR (Status)) {
426 ASSERT (0);
427 goto exit_handler;
428 }
429
430 exit_handler:
431 MappingTableFree (&Generator->DeviceTable);
432 exit_handler0:
433 if (PrtNode != NULL) {
434 AmlDeleteTree (PrtNode);
435 }
436
437 return Status;
438 }
439
440 /** Generate a _CRS method for the Pci device.
441
442 @param [in] Generator The SSDT Pci generator.
443 @param [in] CfgMgrProtocol Pointer to the Configuration Manager
444 Protocol interface.
445 @param [in] PciInfo Pci device information.
446 @param [in, out] PciNode Pci node to amend.
447
448 @retval EFI_SUCCESS The function completed successfully.
449 @retval EFI_INVALID_PARAMETER Invalid parameter.
450 @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
451 **/
452 STATIC
453 EFI_STATUS
454 EFIAPI
455 GeneratePciCrs (
456 IN ACPI_PCI_GENERATOR *Generator,
457 IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol,
458 IN CONST CM_ARM_PCI_CONFIG_SPACE_INFO *PciInfo,
459 IN OUT AML_OBJECT_NODE_HANDLE PciNode
460 )
461 {
462 EFI_STATUS Status;
463 BOOLEAN Translation;
464 UINT32 Index;
465 CM_ARM_OBJ_REF *RefInfo;
466 UINT32 RefCount;
467 CM_ARM_PCI_ADDRESS_MAP_INFO *AddrMapInfo;
468 AML_OBJECT_NODE_HANDLE CrsNode;
469 BOOLEAN IsPosDecode;
470
471 ASSERT (Generator != NULL);
472 ASSERT (CfgMgrProtocol != NULL);
473 ASSERT (PciInfo != NULL);
474 ASSERT (PciNode != NULL);
475
476 // ASL: Name (_CRS, ResourceTemplate () {})
477 Status = AmlCodeGenNameResourceTemplate ("_CRS", PciNode, &CrsNode);
478 if (EFI_ERROR (Status)) {
479 ASSERT (0);
480 return Status;
481 }
482
483 // ASL:
484 // WordBusNumber ( // Bus numbers assigned to this root
485 // ResourceProducer, MinFixed, MaxFixed, PosDecode,
486 // 0, // AddressGranularity
487 // <Start>, // AddressMinimum - Minimum Bus Number
488 // <End>, // AddressMaximum - Maximum Bus Number
489 // 0, // AddressTranslation - Set to 0
490 // <End> - <Start> + 1 // RangeLength - Number of Busses
491 // )
492 Status = AmlCodeGenRdWordBusNumber (
493 FALSE,
494 TRUE,
495 TRUE,
496 TRUE,
497 0,
498 PciInfo->StartBusNumber,
499 PciInfo->EndBusNumber,
500 0,
501 PciInfo->EndBusNumber - PciInfo->StartBusNumber + 1,
502 0,
503 NULL,
504 CrsNode,
505 NULL
506 );
507 if (EFI_ERROR (Status)) {
508 ASSERT (0);
509 return Status;
510 }
511
512 // Get the array of CM_ARM_OBJ_REF referencing the
513 // CM_ARM_PCI_ADDRESS_MAP_INFO objects.
514 Status = GetEArmObjCmRef (
515 CfgMgrProtocol,
516 PciInfo->AddressMapToken,
517 &RefInfo,
518 &RefCount
519 );
520 if (EFI_ERROR (Status)) {
521 ASSERT (0);
522 return Status;
523 }
524
525 for (Index = 0; Index < RefCount; Index++) {
526 // Get CM_ARM_PCI_ADDRESS_MAP_INFO structures one by one.
527 Status = GetEArmObjPciAddressMapInfo (
528 CfgMgrProtocol,
529 RefInfo[Index].ReferenceToken,
530 &AddrMapInfo,
531 NULL
532 );
533 if (EFI_ERROR (Status)) {
534 ASSERT (0);
535 return Status;
536 }
537
538 Translation = (AddrMapInfo->CpuAddress != AddrMapInfo->PciAddress);
539 if (AddrMapInfo->CpuAddress >= AddrMapInfo->PciAddress) {
540 IsPosDecode = TRUE;
541 } else {
542 IsPosDecode = FALSE;
543 }
544
545 switch (AddrMapInfo->SpaceCode) {
546 case PCI_SS_IO:
547 Status = AmlCodeGenRdDWordIo (
548 FALSE,
549 TRUE,
550 TRUE,
551 IsPosDecode,
552 3,
553 0,
554 AddrMapInfo->PciAddress,
555 AddrMapInfo->PciAddress + AddrMapInfo->AddressSize - 1,
556 Translation ? AddrMapInfo->CpuAddress - AddrMapInfo->PciAddress : 0,
557 AddrMapInfo->AddressSize,
558 0,
559 NULL,
560 TRUE,
561 FALSE,
562 CrsNode,
563 NULL
564 );
565 break;
566
567 case PCI_SS_M32:
568 Status = AmlCodeGenRdDWordMemory (
569 FALSE,
570 IsPosDecode,
571 TRUE,
572 TRUE,
573 TRUE,
574 TRUE,
575 0,
576 AddrMapInfo->PciAddress,
577 AddrMapInfo->PciAddress + AddrMapInfo->AddressSize - 1,
578 Translation ? AddrMapInfo->CpuAddress - AddrMapInfo->PciAddress : 0,
579 AddrMapInfo->AddressSize,
580 0,
581 NULL,
582 0,
583 TRUE,
584 CrsNode,
585 NULL
586 );
587 break;
588
589 case PCI_SS_M64:
590 Status = AmlCodeGenRdQWordMemory (
591 FALSE,
592 IsPosDecode,
593 TRUE,
594 TRUE,
595 TRUE,
596 TRUE,
597 0,
598 AddrMapInfo->PciAddress,
599 AddrMapInfo->PciAddress + AddrMapInfo->AddressSize - 1,
600 Translation ? AddrMapInfo->CpuAddress - AddrMapInfo->PciAddress : 0,
601 AddrMapInfo->AddressSize,
602 0,
603 NULL,
604 0,
605 TRUE,
606 CrsNode,
607 NULL
608 );
609 break;
610
611 default:
612 Status = EFI_INVALID_PARAMETER;
613 } // switch
614
615 if (EFI_ERROR (Status)) {
616 ASSERT (0);
617 return Status;
618 }
619 } // for
620
621 return Status;
622 }
623
624 /** Generate a RES0 device node to reserve PNP motherboard resources
625 for a given PCI node.
626
627 @param [in] PciNode Parent PCI node handle of the generated
628 resource object.
629 @param [out] CrsNode CRS node of the AML tree to populate.
630
631 @retval EFI_SUCCESS The function completed successfully.
632 @retval EFI_INVALID_PARAMETER Invalid input parameter.
633 @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
634 **/
635 STATIC
636 EFI_STATUS
637 EFIAPI
638 GenerateMotherboardDevice (
639 IN AML_OBJECT_NODE_HANDLE PciNode,
640 OUT AML_OBJECT_NODE_HANDLE *CrsNode
641 )
642 {
643 EFI_STATUS Status;
644 UINT32 EisaId;
645 AML_OBJECT_NODE_HANDLE ResNode;
646
647 if (CrsNode == NULL) {
648 ASSERT (0);
649 return EFI_INVALID_PARAMETER;
650 }
651
652 // ASL: Device (RES0) {}
653 Status = AmlCodeGenDevice ("RES0", PciNode, &ResNode);
654 if (EFI_ERROR (Status)) {
655 ASSERT (0);
656 return Status;
657 }
658
659 // ASL: Name (_HID, EISAID ("PNP0C02"))
660 Status = AmlGetEisaIdFromString ("PNP0C02", &EisaId); /* PNP Motherboard Resources */
661 if (EFI_ERROR (Status)) {
662 ASSERT (0);
663 return Status;
664 }
665
666 Status = AmlCodeGenNameInteger ("_HID", EisaId, ResNode, NULL);
667 if (EFI_ERROR (Status)) {
668 ASSERT (0);
669 return Status;
670 }
671
672 // ASL: Name (_CRS, ResourceTemplate () {})
673 Status = AmlCodeGenNameResourceTemplate ("_CRS", ResNode, CrsNode);
674 if (EFI_ERROR (Status)) {
675 ASSERT (0);
676 return Status;
677 }
678
679 return Status;
680 }
681
682 /** Reserves ECAM space for PCI config space
683
684 @param [in] Generator The SSDT Pci generator.
685 @param [in] CfgMgrProtocol Pointer to the Configuration Manager
686 Protocol interface.
687 @param [in] PciInfo Pci device information.
688 @param [in, out] PciNode RootNode of the AML tree to populate.
689
690 @retval EFI_SUCCESS The function completed successfully.
691 @retval EFI_INVALID_PARAMETER Invalid parameter.
692 @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
693 **/
694 STATIC
695 EFI_STATUS
696 EFIAPI
697 ReserveEcamSpace (
698 IN ACPI_PCI_GENERATOR *Generator,
699 IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol,
700 IN CONST CM_ARM_PCI_CONFIG_SPACE_INFO *PciInfo,
701 IN OUT AML_OBJECT_NODE_HANDLE PciNode
702 )
703 {
704 EFI_STATUS Status;
705 AML_OBJECT_NODE_HANDLE CrsNode;
706 UINT64 AddressMinimum;
707 UINT64 AddressMaximum;
708
709 Status = GenerateMotherboardDevice (PciNode, &CrsNode);
710 if (EFI_ERROR (Status)) {
711 ASSERT (0);
712 return Status;
713 }
714
715 AddressMinimum = PciInfo->BaseAddress + (PciInfo->StartBusNumber *
716 PCI_MAX_DEVICE_COUNT_PER_BUS * PCI_MAX_FUNCTION_COUNT_PER_DEVICE * SIZE_4KB);
717 AddressMaximum = PciInfo->BaseAddress + ((PciInfo->EndBusNumber + 1) *
718 PCI_MAX_DEVICE_COUNT_PER_BUS * PCI_MAX_FUNCTION_COUNT_PER_DEVICE * SIZE_4KB) - 1;
719
720 Status = AmlCodeGenRdQWordMemory (
721 FALSE,
722 TRUE,
723 TRUE,
724 TRUE,
725 FALSE, // non-cacheable
726 TRUE,
727 0,
728 AddressMinimum,
729 AddressMaximum,
730 0, // no translation
731 AddressMaximum - AddressMinimum + 1,
732 0,
733 NULL,
734 0,
735 TRUE,
736 CrsNode,
737 NULL
738 );
739
740 if (EFI_ERROR (Status)) {
741 ASSERT (0);
742 return Status;
743 }
744
745 return Status;
746 }
747
748 /** Generate a Pci device.
749
750 @param [in] Generator The SSDT Pci generator.
751 @param [in] CfgMgrProtocol Pointer to the Configuration Manager
752 Protocol interface.
753 @param [in] PciInfo Pci device information.
754 @param [in] Uid Unique Id of the Pci device.
755 @param [in, out] RootNode RootNode of the AML tree to populate.
756
757 @retval EFI_SUCCESS The function completed successfully.
758 @retval EFI_INVALID_PARAMETER Invalid parameter.
759 @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
760 **/
761 STATIC
762 EFI_STATUS
763 EFIAPI
764 GeneratePciDevice (
765 IN ACPI_PCI_GENERATOR *Generator,
766 IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol,
767 IN CONST CM_ARM_PCI_CONFIG_SPACE_INFO *PciInfo,
768 IN UINT32 Uid,
769 IN OUT AML_ROOT_NODE_HANDLE *RootNode
770 )
771 {
772 EFI_STATUS Status;
773
774 CHAR8 AslName[AML_NAME_SEG_SIZE + 1];
775 AML_OBJECT_NODE_HANDLE ScopeNode;
776 AML_OBJECT_NODE_HANDLE PciNode;
777
778 ASSERT (Generator != NULL);
779 ASSERT (CfgMgrProtocol != NULL);
780 ASSERT (PciInfo != NULL);
781 ASSERT (RootNode != NULL);
782
783 PciNode = NULL;
784
785 // ASL: Scope (\_SB) {}
786 Status = AmlCodeGenScope (SB_SCOPE, RootNode, &ScopeNode);
787 if (EFI_ERROR (Status)) {
788 ASSERT (0);
789 return Status;
790 }
791
792 // Write the name of the PCI device.
793 CopyMem (AslName, "PCIx", AML_NAME_SEG_SIZE + 1);
794 AslName[AML_NAME_SEG_SIZE - 1] = AsciiFromHex (Uid & 0xF);
795 if (Uid > 0xF) {
796 AslName[AML_NAME_SEG_SIZE - 2] = AsciiFromHex ((Uid >> 4) & 0xF);
797 }
798
799 // ASL: Device (PCIx) {}
800 Status = AmlCodeGenDevice (AslName, ScopeNode, &PciNode);
801 if (EFI_ERROR (Status)) {
802 ASSERT (0);
803 return Status;
804 }
805
806 // Populate the PCIx node with some Id values.
807 Status = GeneratePciDeviceInfo (PciInfo, Uid, PciNode);
808 if (EFI_ERROR (Status)) {
809 ASSERT (0);
810 return Status;
811 }
812
813 // Generate the Pci Routing Table (_PRT).
814 if (PciInfo->InterruptMapToken != CM_NULL_TOKEN) {
815 Status = GeneratePrt (
816 Generator,
817 CfgMgrProtocol,
818 PciInfo,
819 Uid,
820 PciNode
821 );
822 if (EFI_ERROR (Status)) {
823 ASSERT (0);
824 return Status;
825 }
826 }
827
828 // Generate the _CRS method.
829 Status = GeneratePciCrs (Generator, CfgMgrProtocol, PciInfo, PciNode);
830 if (EFI_ERROR (Status)) {
831 ASSERT (0);
832 return Status;
833 }
834
835 // Add the PNP Motherboard Resources Device to reserve ECAM space
836 Status = ReserveEcamSpace (Generator, CfgMgrProtocol, PciInfo, PciNode);
837 if (EFI_ERROR (Status)) {
838 ASSERT (0);
839 return Status;
840 }
841
842 // Add the template _OSC method.
843 Status = AddOscMethod (PciInfo, PciNode);
844 ASSERT_EFI_ERROR (Status);
845
846 return Status;
847 }
848
849 /** Build an Ssdt table describing a Pci device.
850
851 @param [in] Generator The SSDT Pci generator.
852 @param [in] CfgMgrProtocol Pointer to the Configuration Manager
853 Protocol interface.
854 @param [in] AcpiTableInfo Pointer to the ACPI table information.
855 @param [in] PciInfo Pci device information.
856 @param [in] Uid Unique Id of the Pci device.
857 @param [out] Table If success, contains the created SSDT table.
858
859 @retval EFI_SUCCESS The function completed successfully.
860 @retval EFI_INVALID_PARAMETER Invalid parameter.
861 @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
862 **/
863 STATIC
864 EFI_STATUS
865 EFIAPI
866 BuildSsdtPciTable (
867 IN ACPI_PCI_GENERATOR *Generator,
868 IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol,
869 IN CONST CM_STD_OBJ_ACPI_TABLE_INFO *CONST AcpiTableInfo,
870 IN CONST CM_ARM_PCI_CONFIG_SPACE_INFO *PciInfo,
871 IN UINT32 Uid,
872 OUT EFI_ACPI_DESCRIPTION_HEADER **Table
873 )
874 {
875 EFI_STATUS Status;
876 EFI_STATUS Status1;
877 AML_ROOT_NODE_HANDLE RootNode;
878
879 ASSERT (Generator != NULL);
880 ASSERT (CfgMgrProtocol != NULL);
881 ASSERT (PciInfo != NULL);
882 ASSERT (Table != NULL);
883
884 // Create a new Ssdt table.
885 Status = AddSsdtAcpiHeader (
886 CfgMgrProtocol,
887 &Generator->Header,
888 AcpiTableInfo,
889 &RootNode
890 );
891 if (EFI_ERROR (Status)) {
892 ASSERT (0);
893 return Status;
894 }
895
896 Status = GeneratePciDevice (
897 Generator,
898 CfgMgrProtocol,
899 PciInfo,
900 Uid,
901 RootNode
902 );
903 if (EFI_ERROR (Status)) {
904 ASSERT (0);
905 goto exit_handler;
906 }
907
908 // Serialize the tree.
909 Status = AmlSerializeDefinitionBlock (
910 RootNode,
911 Table
912 );
913 if (EFI_ERROR (Status)) {
914 DEBUG ((
915 DEBUG_ERROR,
916 "ERROR: SSDT-PCI: Failed to Serialize SSDT Table Data."
917 " Status = %r\n",
918 Status
919 ));
920 }
921
922 exit_handler:
923 // Cleanup
924 Status1 = AmlDeleteTree (RootNode);
925 if (EFI_ERROR (Status1)) {
926 DEBUG ((
927 DEBUG_ERROR,
928 "ERROR: SSDT-PCI: Failed to cleanup AML tree."
929 " Status = %r\n",
930 Status1
931 ));
932 // If Status was success but we failed to delete the AML Tree
933 // return Status1 else return the original error code, i.e. Status.
934 if (!EFI_ERROR (Status)) {
935 return Status1;
936 }
937 }
938
939 return Status;
940 }
941
942 /** Construct SSDT tables describing Pci root complexes.
943
944 This function invokes the Configuration Manager protocol interface
945 to get the required hardware information for generating the ACPI
946 table.
947
948 If this function allocates any resources then they must be freed
949 in the FreeXXXXTableResourcesEx function.
950
951 @param [in] This Pointer to the ACPI table generator.
952 @param [in] AcpiTableInfo Pointer to the ACPI table information.
953 @param [in] CfgMgrProtocol Pointer to the Configuration Manager
954 Protocol interface.
955 @param [out] Table Pointer to a list of generated ACPI table(s).
956 @param [out] TableCount Number of generated ACPI table(s).
957
958 @retval EFI_SUCCESS Table generated successfully.
959 @retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration
960 Manager is less than the Object size for
961 the requested object.
962 @retval EFI_INVALID_PARAMETER A parameter is invalid.
963 @retval EFI_NOT_FOUND Could not find information.
964 @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
965 @retval EFI_UNSUPPORTED Unsupported configuration.
966 **/
967 STATIC
968 EFI_STATUS
969 EFIAPI
970 BuildSsdtPciTableEx (
971 IN CONST ACPI_TABLE_GENERATOR *This,
972 IN CONST CM_STD_OBJ_ACPI_TABLE_INFO *CONST AcpiTableInfo,
973 IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol,
974 OUT EFI_ACPI_DESCRIPTION_HEADER ***Table,
975 OUT UINTN *CONST TableCount
976 )
977 {
978 EFI_STATUS Status;
979 CM_ARM_PCI_CONFIG_SPACE_INFO *PciInfo;
980 UINT32 PciCount;
981 UINTN Index;
982 EFI_ACPI_DESCRIPTION_HEADER **TableList;
983 ACPI_PCI_GENERATOR *Generator;
984 UINT32 Uid;
985
986 ASSERT (This != NULL);
987 ASSERT (AcpiTableInfo != NULL);
988 ASSERT (CfgMgrProtocol != NULL);
989 ASSERT (Table != NULL);
990 ASSERT (TableCount != NULL);
991 ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);
992 ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);
993
994 *TableCount = 0;
995 Generator = (ACPI_PCI_GENERATOR *)This;
996
997 Status = GetEArmObjPciConfigSpaceInfo (
998 CfgMgrProtocol,
999 CM_NULL_TOKEN,
1000 &PciInfo,
1001 &PciCount
1002 );
1003 if (EFI_ERROR (Status)) {
1004 ASSERT (0);
1005 return Status;
1006 }
1007
1008 if (PciCount > MAX_PCI_ROOT_COMPLEXES_SUPPORTED) {
1009 DEBUG ((
1010 DEBUG_ERROR,
1011 "ERROR: SSDT-PCI: Too many Pci root complexes: %d."
1012 " Maximum Pci root complexes supported = %d.\n",
1013 PciCount,
1014 MAX_PCI_ROOT_COMPLEXES_SUPPORTED
1015 ));
1016 return EFI_INVALID_PARAMETER;
1017 }
1018
1019 // Allocate a table to store pointers to the SSDT tables.
1020 TableList = (EFI_ACPI_DESCRIPTION_HEADER **)
1021 AllocateZeroPool (
1022 (sizeof (EFI_ACPI_DESCRIPTION_HEADER *) * PciCount)
1023 );
1024 if (TableList == NULL) {
1025 Status = EFI_OUT_OF_RESOURCES;
1026 DEBUG ((
1027 DEBUG_ERROR,
1028 "ERROR: SSDT-PCI: Failed to allocate memory for Table List."
1029 " Status = %r\n",
1030 Status
1031 ));
1032 return Status;
1033 }
1034
1035 // Setup the table list early so that appropriate cleanup
1036 // can be done in case of failure.
1037 *Table = TableList;
1038
1039 for (Index = 0; Index < PciCount; Index++) {
1040 if (PcdGetBool (PcdPciUseSegmentAsUid)) {
1041 Uid = PciInfo[Index].PciSegmentGroupNumber;
1042 if (Uid > MAX_PCI_ROOT_COMPLEXES_SUPPORTED) {
1043 DEBUG ((
1044 DEBUG_ERROR,
1045 "ERROR: SSDT-PCI: Pci root complexes segment number: %d."
1046 " Greater than maximum number of Pci root complexes supported = %d.\n",
1047 Uid,
1048 MAX_PCI_ROOT_COMPLEXES_SUPPORTED
1049 ));
1050 return EFI_INVALID_PARAMETER;
1051 }
1052 } else {
1053 Uid = Index;
1054 }
1055
1056 // Build a SSDT table describing the Pci devices.
1057 Status = BuildSsdtPciTable (
1058 Generator,
1059 CfgMgrProtocol,
1060 AcpiTableInfo,
1061 &PciInfo[Index],
1062 Uid,
1063 &TableList[Index]
1064 );
1065 if (EFI_ERROR (Status)) {
1066 DEBUG ((
1067 DEBUG_ERROR,
1068 "ERROR: SSDT-PCI: Failed to build associated SSDT table."
1069 " Status = %r\n",
1070 Status
1071 ));
1072 goto error_handler;
1073 }
1074
1075 *TableCount += 1;
1076 } // for
1077
1078 error_handler:
1079 // Note: Table list and Table count have been setup. The
1080 // error handler does nothing here as the framework will invoke
1081 // FreeSsdtPciTableEx () even on failure.
1082 return Status;
1083 }
1084
1085 /** Free any resources allocated for constructing the tables.
1086
1087 @param [in] This Pointer to the ACPI table generator.
1088 @param [in] AcpiTableInfo Pointer to the ACPI Table Info.
1089 @param [in] CfgMgrProtocol Pointer to the Configuration Manager
1090 Protocol Interface.
1091 @param [in, out] Table Pointer to an array of pointers
1092 to ACPI Table(s).
1093 @param [in] TableCount Number of ACPI table(s).
1094
1095 @retval EFI_SUCCESS The resources were freed successfully.
1096 @retval EFI_INVALID_PARAMETER The table pointer is NULL or invalid.
1097 **/
1098 STATIC
1099 EFI_STATUS
1100 EFIAPI
1101 FreeSsdtPciTableEx (
1102 IN CONST ACPI_TABLE_GENERATOR *CONST This,
1103 IN CONST CM_STD_OBJ_ACPI_TABLE_INFO *CONST AcpiTableInfo,
1104 IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL *CONST CfgMgrProtocol,
1105 IN OUT EFI_ACPI_DESCRIPTION_HEADER ***CONST Table,
1106 IN CONST UINTN TableCount
1107 )
1108 {
1109 EFI_ACPI_DESCRIPTION_HEADER **TableList;
1110 UINTN Index;
1111
1112 ASSERT (This != NULL);
1113 ASSERT (AcpiTableInfo != NULL);
1114 ASSERT (CfgMgrProtocol != NULL);
1115 ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);
1116 ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);
1117
1118 if ((Table == NULL) ||
1119 (*Table == NULL) ||
1120 (TableCount == 0))
1121 {
1122 DEBUG ((DEBUG_ERROR, "ERROR: SSDT-PCI: Invalid Table Pointer\n"));
1123 return EFI_INVALID_PARAMETER;
1124 }
1125
1126 TableList = *Table;
1127 for (Index = 0; Index < TableCount; Index++) {
1128 if ((TableList[Index] != NULL) &&
1129 (TableList[Index]->Signature ==
1130 EFI_ACPI_6_3_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE))
1131 {
1132 FreePool (TableList[Index]);
1133 } else {
1134 DEBUG ((
1135 DEBUG_ERROR,
1136 "ERROR: SSDT-PCI: Could not free SSDT table at index %d.",
1137 Index
1138 ));
1139 return EFI_INVALID_PARAMETER;
1140 }
1141 } // for
1142
1143 // Free the table list.
1144 FreePool (*Table);
1145
1146 return EFI_SUCCESS;
1147 }
1148
1149 /** This macro defines the SSDT Pci Table Generator revision.
1150 */
1151 #define SSDT_PCI_GENERATOR_REVISION CREATE_REVISION (1, 0)
1152
1153 /** The interface for the SSDT Pci Table Generator.
1154 */
1155 STATIC
1156 ACPI_PCI_GENERATOR SsdtPcieGenerator = {
1157 // ACPI table generator header
1158 {
1159 // Generator ID
1160 CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdSsdtPciExpress),
1161 // Generator Description
1162 L"ACPI.STD.SSDT.PCI.GENERATOR",
1163 // ACPI Table Signature
1164 EFI_ACPI_6_3_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE,
1165 // ACPI Table Revision - Unused
1166 0,
1167 // Minimum ACPI Table Revision - Unused
1168 0,
1169 // Creator ID
1170 TABLE_GENERATOR_CREATOR_ID_ARM,
1171 // Creator Revision
1172 SSDT_PCI_GENERATOR_REVISION,
1173 // Build table function. Use the extended version instead.
1174 NULL,
1175 // Free table function. Use the extended version instead.
1176 NULL,
1177 // Extended Build table function.
1178 BuildSsdtPciTableEx,
1179 // Extended free function.
1180 FreeSsdtPciTableEx
1181 },
1182
1183 // Private fields are defined from here.
1184
1185 // DeviceTable
1186 {
1187 // Table
1188 NULL,
1189 // LastIndex
1190 0,
1191 // MaxIndex
1192 0
1193 },
1194 };
1195
1196 /** Register the Generator with the ACPI Table Factory.
1197
1198 @param [in] ImageHandle The handle to the image.
1199 @param [in] SystemTable Pointer to the System Table.
1200
1201 @retval EFI_SUCCESS The Generator is registered.
1202 @retval EFI_INVALID_PARAMETER A parameter is invalid.
1203 @retval EFI_ALREADY_STARTED The Generator for the Table ID
1204 is already registered.
1205 **/
1206 EFI_STATUS
1207 EFIAPI
1208 AcpiSsdtPcieLibConstructor (
1209 IN EFI_HANDLE ImageHandle,
1210 IN EFI_SYSTEM_TABLE *SystemTable
1211 )
1212 {
1213 EFI_STATUS Status;
1214
1215 Status = RegisterAcpiTableGenerator (&SsdtPcieGenerator.Header);
1216 DEBUG ((
1217 DEBUG_INFO,
1218 "SSDT-PCI: Register Generator. Status = %r\n",
1219 Status
1220 ));
1221 ASSERT_EFI_ERROR (Status);
1222 return Status;
1223 }
1224
1225 /** Deregister the Generator from the ACPI Table Factory.
1226
1227 @param [in] ImageHandle The handle to the image.
1228 @param [in] SystemTable Pointer to the System Table.
1229
1230 @retval EFI_SUCCESS The Generator is deregistered.
1231 @retval EFI_INVALID_PARAMETER A parameter is invalid.
1232 @retval EFI_NOT_FOUND The Generator is not registered.
1233 **/
1234 EFI_STATUS
1235 EFIAPI
1236 AcpiSsdtPcieLibDestructor (
1237 IN EFI_HANDLE ImageHandle,
1238 IN EFI_SYSTEM_TABLE *SystemTable
1239 )
1240 {
1241 EFI_STATUS Status;
1242
1243 Status = DeregisterAcpiTableGenerator (&SsdtPcieGenerator.Header);
1244 DEBUG ((
1245 DEBUG_INFO,
1246 "SSDT-PCI: Deregister Generator. Status = %r\n",
1247 Status
1248 ));
1249 ASSERT_EFI_ERROR (Status);
1250 return Status;
1251 }