Modify ACPI to only supply _EJ0 methods for PCI slots that support hotplug.
This is done by runtime patching: - Instrument ASL code with ACPI_EXTRACT directives tagging _EJ0 and _ADR fields. - At compile time, tools/acpi_extract.py looks for these methods in ASL source finds the matching AML, and stores the offsets of these methods in tables named aml_ej0_name and aml_adr_dword. - At run time, go over aml_ej0_name, use aml_adr_dword to get slot information and check which slots support hotplug.
If hotplug is disabled, we patch the _EJ0 NameString in ACPI table, replacing _EJ0 with EJ0_.
Note that this has the same checksum, but is ignored by OSPM.
Note: the method used is robust in that we don't need to change any offsets manually in case of ASL code changes. As all parsing is done at compile time, any unexpected input causes build failure, not a runtime failure.
Signed-off-by: Michael S. Tsirkin mst@redhat.com --- src/acpi-dsdt.dsl | 47 ++++++++++++++++++++++++++++++++++++++--------- src/acpi.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 9 deletions(-)
diff --git a/src/acpi-dsdt.dsl b/src/acpi-dsdt.dsl index 08412e2..440e315 100644 --- a/src/acpi-dsdt.dsl +++ b/src/acpi-dsdt.dsl @@ -16,6 +16,30 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* +Documentation of ACPI_EXTRACT_* directive tags: + +These directive tags are processed by tools/acpi_extract.py +to output offset information from AML for BIOS runtime table generation. +Each directive is of the form: +ACPI_EXTRACT_<TYPE> <array_name> <Operator> (...) +and causes the extractor to create an array +named <array_name> with offset, in the generated AML, +of an object of a given type from the following <Operator>. + +A directive and array name must fit on a single code line. + +Object type in AML is verified, a mismatch causes a build failure. + +Directives and operators currently supported are: +ACPI_EXTRACT_NAME_DWORD_CONST - extract a Dword Const object from Name() +ACPI_EXTRACT_METHOD_STRING - extract a NameString from Method() +ACPI_EXTRACT_NAME_STRING - extract a NameString from Name() + +ACPI_EXTRACT is not allowed anywhere else in code, except in comments. +*/ + DefinitionBlock ( "acpi-dsdt.aml", // Output Filename "DSDT", // Signature @@ -127,15 +151,20 @@ DefinitionBlock ( { PCRM, 32, } - -#define hotplug_slot(name, nr) \ - Device (S##name) { \ - Name (_ADR, nr##0000) \ - Method (_EJ0,1) { \ - Store(ShiftLeft(1, nr), B0EJ) \ - Return (0x0) \ - } \ - Name (_SUN, name) \ + // Method _EJ0 can be patched by BIOS to EJ0_ + // at runtime, if the slot is detected to not support hotplug. + // Extract the offset of the address dword and the + // _EJ0 name to allow this patching. +#define hotplug_slot(name, nr) \ + Device (S##name) { \ + ACPI_EXTRACT_NAME_DWORD_CONST aml_adr_dword \ + Name (_ADR, nr##0000) \ + ACPI_EXTRACT_METHOD_STRING aml_ej0_name \ + Method (_EJ0, 1) { \ + Store(ShiftLeft(1, nr), B0EJ) \ + Return (0x0) \ + } \ + Name (_SUN, name) \ }
hotplug_slot(1, 0x0001) diff --git a/src/acpi.c b/src/acpi.c index 6bb6ff6..f65f974 100644 --- a/src/acpi.c +++ b/src/acpi.c @@ -198,6 +198,8 @@ struct srat_memory_affinity u32 reserved3[2]; } PACKED;
+#define PCI_RMV_BASE 0xae0c + #include "acpi-dsdt.hex"
static void @@ -237,12 +239,16 @@ static const struct pci_device_id fadt_init_tbl[] = { PCI_DEVICE_END };
+extern void link_time_assertion(void); + static void * build_fadt(struct pci_device *pci) { struct fadt_descriptor_rev1 *fadt = malloc_high(sizeof(*fadt)); struct facs_descriptor_rev1 *facs = memalign_high(64, sizeof(*facs)); void *dsdt = malloc_high(sizeof(AmlCode)); + u32 rmvc_pcrm; + int i;
if (!fadt || !facs || !dsdt) { warn_noalloc(); @@ -257,6 +263,25 @@ build_fadt(struct pci_device *pci) /* DSDT */ memcpy(dsdt, AmlCode, sizeof(AmlCode));
+ /* Runtime patching of EJ0: to disable hotplug for a slot, + * replace the method name: _EJ0 by EJ0_. */ + if (ARRAY_SIZE(aml_ej0_name) != ARRAY_SIZE(aml_adr_dword)) { + link_time_assertion(); + } + rmvc_pcrm = inl(PCI_RMV_BASE); + for (i = 0; i < ARRAY_SIZE(aml_ej0_name); ++i) { + /* Slot is in byte 2 in _ADR */ + u8 slot = AmlCode[aml_adr_dword[i] + 2] & 0x1F; + /* Sanity check */ + if (memcmp(AmlCode + aml_ej0_name[i], "_EJ0", 4)) { + warn_internalerror(); + goto error; + } + if (!(rmvc_pcrm & (0x1 << slot))) { + memcpy(dsdt + aml_ej0_name[i], "EJ0_", 4); + } + } + /* FADT */ memset(fadt, 0, sizeof(*fadt)); fadt->firmware_ctrl = cpu_to_le32((u32)facs); @@ -281,6 +306,12 @@ build_fadt(struct pci_device *pci) build_header((void*)fadt, FACP_SIGNATURE, sizeof(*fadt), 1);
return fadt; + +error: + free(fadt); + free(facs); + free(dsdt); + return NULL; }
static void*