This post is part 6 of the "SBSA Reference Platform in QEMU" series:
- Versioning of sbsa-ref machine
- SBSA Reference Platform update
- Testing *BSD on SBSA Reference Platform
- Running SBSA Reference Platform
- DT-free EDK2 on SBSA Reference Platform
- ConfigurationManager in EDK2: just say no
During my work on SBSA Reference Platform I have spent lot of time in firmware’s code. Which mostly meant Tianocore EDK2 as Trusted Firmware is quite small.
Writing all those ACPI tables by hand takes time. So I checked ConfigurationManager component which can do it for me.
Introduction
In 2018 Sami Mujawar from Arm contributed Dynamic Tables Framework to Tianocore EDK2 project. The goal was to have code which generates all ACPI tables from all those data structs describing hardware which EDK2 already has.
In 2023 I was writing code for IORT and GTDT tables to generate them from C. And started wondering about use of ConfigurationManager.
Mailed edk2-devel ML for pointers, documentation, hints. Got nothing in return, idea went to the shelf.
SBSA-Ref and multiple PCI Express buses
Last week I got SBSA-Ref system booting in NUMA configuration with three separate PCI Express buses. And started working on getting EDK2 firmware to recognize them as such.
Took me a day and pci
command listed cards properly:
Shell> pci
Seg Bus Dev Func
--- --- --- ----
00 00 00 00 ==> Bridge Device - Host/PCI bridge
Vendor 1B36 Device 0008 Prog Interface 0
00 00 01 00 ==> Network Controller - Ethernet controller
Vendor 8086 Device 10D3 Prog Interface 0
00 00 02 00 ==> Bridge Device - PCI/PCI bridge
Vendor 1B36 Device 000C Prog Interface 0
00 00 03 00 ==> Bridge Device - Host/PCI bridge
Vendor 1B36 Device 000B Prog Interface 0
00 00 04 00 ==> Bridge Device - Host/PCI bridge
Vendor 1B36 Device 000B Prog Interface 0
00 01 00 00 ==> Mass Storage Controller - Non-volatile memory subsystem
Vendor 1B36 Device 0010 Prog Interface 2
00 40 00 00 ==> Bridge Device - PCI/PCI bridge
Vendor 1B36 Device 000C Prog Interface 0
00 40 01 00 ==> Bridge Device - PCI/PCI bridge
Vendor 1B36 Device 000C Prog Interface 0
00 41 00 00 ==> Base System Peripherals - SD Host controller
Vendor 1B36 Device 0007 Prog Interface 1
00 42 00 00 ==> Display Controller - Other display controller
Vendor 1234 Device 1111 Prog Interface 0
00 80 00 00 ==> Bridge Device - PCI/PCI bridge
Vendor 1B36 Device 000E Prog Interface 0
00 80 01 00 ==> Bridge Device - PCI/PCI bridge
Vendor 1B36 Device 000C Prog Interface 0
00 81 09 00 ==> Multimedia Device - Audio device
Vendor 1274 Device 5000 Prog Interface 0
00 81 10 00 ==> Network Controller - Ethernet controller
Vendor 8086 Device 100E Prog Interface 0
00 82 00 00 ==> Mass Storage Controller - Serial ATA controller
Vendor 8086 Device 2922 Prog Interface 1
Three buses are: 0x00, 0x40 and 0x80. But then I had to tell Operating System about those. Which meant playing with ACPI tables code in C.
So idea came “what about trying ConfigurationManager?”.
Another try
Mailed edk2-devel ML again for pointers, documentation hints. And then looked at code written for N1SDP and started playing with ConfigurationManager…
ConfigurationManager.c has EDKII_PLATFORM_REPOSITORY_INFO struct with hundreds of lines of data (as another structs). From listing which ACPI tables I want to have (FADT, GTDT, APIC, SPCR, DBG2, IORT, MCFG, SRAT, DSDT, PPTT etc.) to listing all hardware details like GIC, PCIe, Timers, CPU and Memory information.
Then code for querying this struct. I thought that CM/DT (ConfigurationManager/DynamicTables) framework will have those already in EDK2 code but no — each platform has own set of functions. Another hundreds of lines to maintain.
Took some time to get it built, then started filling proper data and compared with ACPI tables I had previously. There were differences to sort out. But digging more and more into code I saw that I go deeper and deeper into rabbit hole…
Dynamic systems do not fit CM?
For platforms with dynamic hardware configuration (like SBSA-Ref) I needed to write code which would populate that struct with data on runtime. Check amount of cpu cores and write cpu information (with topology, cache etc), create all GIC structures and mappings. Then same for PCIe buses. Etc. Etc. etc…
STATIC
EFI_STATUS
EFIAPI
InitializePlatformRepository (
IN EDKII_PLATFORM_REPOSITORY_INFO * CONST PlatRepoInfo
)
{
GicInfo GicInfo;
CM_ARM_GIC_REDIST_INFO *GicRedistInfo;
CM_ARM_GIC_ITS_INFO *GicItsInfo;
CM_ARM_SMMUV3_NODE *SmmuV3Info;
GetGicDetails(&GicInfo);
PlatRepoInfo->GicDInfo.PhysicalBaseAddress = GicInfo.DistributorBase;
GicRedistInfo = &PlatRepoInfo->GicRedistInfo[0];
GicRedistInfo->DiscoveryRangeBaseAddress = GicInfo.RedistributorsBase;
GicItsInfo = &PlatRepoInfo->GicItsInfo[0];
GicItsInfo->PhysicalBaseAddress = GicInfo.ItsBase;
SmmuV3Info = &PlatRepoInfo->SmmuV3Info[0];
SmmuV3Info->BaseAddress = PcdGet64 (PcdSmmuBase);
return EFI_SUCCESS;
}
Which in my case can mean even more code written to populate CM struct of structs than it would take to generate ACPI tables by hand.
Summary
ConfigurationManager and DynamicTables frameworks look tempting. There may be systems where it can be used with success. I know that I do not want to touch it again. All those structs of structs may look good for someone familiar with LISP or JSON but not for me.