diff mbox series

acpi: method: extend _CPC test for ACPI 6.6 (mantis 2353)

Message ID 20251113061026.309283-1-ivan.hu@canonical.com
State Accepted
Headers show
Series acpi: method: extend _CPC test for ACPI 6.6 (mantis 2353) | expand

Commit Message

Ivan Hu Nov. 13, 2025, 6:10 a.m. UTC
BugLink: https://bugs.launchpad.net/fwts/+bug/2131190

The ACPI 6.6 introduces two new elements (OSPM nominal performance\nregister and
a ResourcePriorityRegisters package) when _CPC reports revision 4. Extend the
existing method test to accept revision 4, verify the full 25-entry package
layout, and validate each resource priority descriptor.

Signed-off-by: Ivan Hu <ivan.hu@canonical.com>
---
 src/acpi/method/method.c | 185 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 184 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/src/acpi/method/method.c b/src/acpi/method/method.c
index 52c39359..9e832c16 100644
--- a/src/acpi/method/method.c
+++ b/src/acpi/method/method.c
@@ -1698,6 +1698,154 @@  static int method_test_SWS(fwts_framework *fw)
 /*
  * Section 8.4 Declaring Processors
  */
+
+static bool method_test_CPC_int_or_buffer(
+	fwts_framework *fw,
+	const char *name,
+	const ACPI_OBJECT *obj,
+	const char *field_name,
+	uint32_t index)
+{
+	if (obj->Type == ACPI_TYPE_INTEGER || obj->Type == ACPI_TYPE_BUFFER)
+		return true;
+
+	fwts_failed(fw, LOG_LEVEL_HIGH,
+		"Method_CPCResourcePriorityBadType",
+		"%s Resource Priority entry %" PRIu32 " field %s returned "
+		"an unexpected object type (%u). Expected an integer or buffer.",
+		name, index, field_name, obj->Type);
+	return false;
+}
+
+static bool method_test_CPC_resource_priority_descriptor(
+	fwts_framework *fw,
+	const char *name,
+	const ACPI_OBJECT *descriptor,
+	uint32_t index)
+{
+	static const uint32_t expected_elements = 5;
+	const ACPI_OBJECT *controlled_resources;
+	const ACPI_OBJECT *enable_value;
+	const ACPI_OBJECT *enable_register;
+	const ACPI_OBJECT *priority_count;
+	const ACPI_OBJECT *priority_register;
+	bool passed = true;
+
+	if (descriptor->Type != ACPI_TYPE_PACKAGE) {
+		fwts_failed(fw, LOG_LEVEL_HIGH,
+			"Method_CPCResourcePriorityDescriptorType",
+			"%s Resource Priority entry %" PRIu32
+			" is type %u, expected a package.",
+			name, index, descriptor->Type);
+		return false;
+	}
+
+	if (descriptor->Package.Count != expected_elements) {
+		fwts_failed(fw, LOG_LEVEL_HIGH,
+			"Method_CPCResourcePriorityDescriptorCount",
+			"%s Resource Priority entry %" PRIu32
+			" should contain %" PRIu32 " elements, got %" PRIu32 ".",
+			name, index, expected_elements, descriptor->Package.Count);
+		return false;
+	}
+
+	controlled_resources = &descriptor->Package.Elements[0];
+	enable_value = &descriptor->Package.Elements[1];
+	enable_register = &descriptor->Package.Elements[2];
+	priority_count = &descriptor->Package.Elements[3];
+	priority_register = &descriptor->Package.Elements[4];
+
+	if (controlled_resources->Type != ACPI_TYPE_PACKAGE) {
+		fwts_failed(fw, LOG_LEVEL_HIGH,
+			"Method_CPCResourcePriorityControlledResourcesType",
+			"%s Resource Priority entry %" PRIu32
+			" first element must be a package of resource IDs.",
+			name, index);
+		passed = false;
+	} else if (controlled_resources->Package.Count == 0) {
+		fwts_failed(fw, LOG_LEVEL_HIGH,
+			"Method_CPCResourcePriorityControlledResourcesEmpty",
+			"%s Resource Priority entry %" PRIu32
+			" must describe at least one resource type.",
+			name, index);
+		passed = false;
+	} else {
+		uint32_t i;
+
+		for (i = 0; i < controlled_resources->Package.Count; i++) {
+			if (controlled_resources->Package.Elements[i].Type != ACPI_TYPE_INTEGER) {
+				fwts_failed(fw, LOG_LEVEL_HIGH,
+					"Method_CPCResourcePriorityControlledResourcesType",
+					"%s Resource Priority entry %" PRIu32
+					" resource list item %" PRIu32
+					" is not an integer.",
+					name, index, i);
+				passed = false;
+			}
+		}
+	}
+
+	if (!method_test_CPC_int_or_buffer(fw, name, enable_value, "EnableValue", index))
+		passed = false;
+
+	if (enable_register->Type != ACPI_TYPE_BUFFER) {
+		fwts_failed(fw, LOG_LEVEL_HIGH,
+			"Method_CPCResourcePriorityEnableRegisterType",
+			"%s Resource Priority entry %" PRIu32
+			" EnableRegister must be a buffer containing a Register() descriptor.",
+			name, index);
+		passed = false;
+	}
+
+	if (!method_test_CPC_int_or_buffer(fw, name, priority_count, "PriorityCount", index))
+		passed = false;
+	else if (priority_count->Type == ACPI_TYPE_INTEGER &&
+		 priority_count->Integer.Value < 2) {
+		fwts_failed(fw, LOG_LEVEL_HIGH,
+			"Method_CPCResourcePriorityCountValue",
+			"%s Resource Priority entry %" PRIu32
+			" PriorityCount must be >= 2, got %" PRIu64 ".",
+			name, index, (uint64_t)priority_count->Integer.Value);
+		passed = false;
+	}
+
+	if (priority_register->Type != ACPI_TYPE_BUFFER) {
+		fwts_failed(fw, LOG_LEVEL_HIGH,
+			"Method_CPCResourcePriorityRegisterType",
+			"%s Resource Priority entry %" PRIu32
+			" PriorityRegister must be a buffer containing a Register() descriptor.",
+			name, index);
+		passed = false;
+	}
+
+	return passed;
+}
+
+static bool method_test_CPC_resource_priority_registers(
+	fwts_framework *fw,
+	const char *name,
+	const ACPI_OBJECT *pkg)
+{
+	bool passed = true;
+	uint32_t i;
+
+	if (pkg->Type != ACPI_TYPE_PACKAGE) {
+		fwts_failed(fw, LOG_LEVEL_HIGH,
+			"Method_CPCResourcePriorityType",
+			"%s ResourcePriorityRegisters element is not a package.",
+			name);
+		return false;
+	}
+
+	for (i = 0; i < pkg->Package.Count; i++) {
+		if (!method_test_CPC_resource_priority_descriptor(fw, name,
+				&pkg->Package.Elements[i], i))
+			passed = false;
+	}
+
+	return passed;
+}
+
 static void method_test_CPC_return(
 	fwts_framework *fw,
 	char *name,
@@ -1777,6 +1925,34 @@  static void method_test_CPC_return(
 		{ ACPI_TYPE_INTBUF,	"Nominal Frequency" }
 	};
 
+	static const fwts_package_element elementsv4[] = {
+		{ ACPI_TYPE_INTEGER,	"Number of Entries" },
+		{ ACPI_TYPE_INTEGER,	"Revision" },
+		{ ACPI_TYPE_INTBUF,	"Highest Performance" },
+		{ ACPI_TYPE_INTBUF,	"Nominal Performance" },
+		{ ACPI_TYPE_INTBUF,	"Lowest Non Linear Performance" },
+		{ ACPI_TYPE_INTBUF,	"Lowest Performance" },
+		{ ACPI_TYPE_BUFFER,	"Guaranteed Performance Register" },
+		{ ACPI_TYPE_BUFFER,	"Desired Performance Register" },
+		{ ACPI_TYPE_BUFFER,	"Minimum Performance Register" },
+		{ ACPI_TYPE_BUFFER,	"Maximum Performance Register" },
+		{ ACPI_TYPE_BUFFER,	"Performance Reduction Tolerance Register" },
+		{ ACPI_TYPE_BUFFER,	"Timed Window Register" },
+		{ ACPI_TYPE_INTBUF,	"Counter Wraparound Time" },
+		{ ACPI_TYPE_BUFFER,	"Reference Performance Counter Register" },
+		{ ACPI_TYPE_BUFFER,	"Delivered Performance Counter Register" },
+		{ ACPI_TYPE_BUFFER,	"Performance Limited Register" },
+		{ ACPI_TYPE_BUFFER,	"CPPC Enable Register" },
+		{ ACPI_TYPE_INTBUF,	"Autonomous Selection Enable" },
+		{ ACPI_TYPE_BUFFER,	"Autonomous Activity Window Register" },
+		{ ACPI_TYPE_BUFFER,	"Energy Performance Preference Register" },
+		{ ACPI_TYPE_INTBUF,	"Reference Performance" },
+		{ ACPI_TYPE_INTBUF,	"Lowest Frequency" },
+		{ ACPI_TYPE_INTBUF,	"Nominal Frequency" },
+		{ ACPI_TYPE_BUFFER,	"OSPM Nominal Performance Register" },
+		{ ACPI_TYPE_PACKAGE,	"Resource Priority Registers" }
+	};
+
 	FWTS_UNUSED(private);
 
 	if (fwts_method_check_type(fw, name, buf, ACPI_TYPE_PACKAGE) != FWTS_OK)
@@ -1796,11 +1972,18 @@  static void method_test_CPC_return(
 		/* For now, just check types */
 		if (fwts_method_package_elements_type(fw, name, obj, elementsv3) != FWTS_OK)
 			return;
+	} else if (revision == 4) {	// CPPC V4
+		if (fwts_method_package_elements_type(fw, name, obj, elementsv4) != FWTS_OK)
+			return;
+
+		if (!method_test_CPC_resource_priority_registers(fw, name,
+				&obj->Package.Elements[24]))
+			return;
 	} else {
 		fwts_failed(fw, LOG_LEVEL_HIGH,
 			"Method_CPCBadRevision",
 			"_CPC's revision is incorrect, "
-			"expecting 1, 2 or 3, got 0x%" PRIx8 , revision);
+			"expecting 1, 2, 3 or 4, got 0x%" PRIx8 , revision);
 
 		return;
 	}