Linux-ACPI Archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 0/4] Implement Platform Runtime Mechanism plumbing
@ 2021-03-22 22:37 Erik Kaneda
  2021-03-22 22:37 ` [PATCH v3 1/4] ACPICA: iASL: add disassembler support for PRMT Erik Kaneda
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Erik Kaneda @ 2021-03-22 22:37 UTC (permalink / raw
  To: Rafael J . Wysocki, ACPI Devel Maling List
  Cc: Erik Kaneda, Dan Williams, Ashok Raj, Rafael Wysocki

Platform Runtime Mechanism (PRM) is a firmware interface that exposes
a set of binary executables that can either be called from the AML
interpreter or device drivers by bypassing the AML interpreter.

This mechanism offers a more performant way to execute native code
than ASL and a less disruptive approach than ASL invoking SMI by writing
to the SMI command port.

This patch seies adds linux support to invoke PRM through a new ACPI
OperationRegion address type. The native code is ran as if it were a
UEFI runtime service by calling efi_call_virt_pointer to function
pointers exposed in the PRMT ACPI table.

According to the PRM specification, the PRM binary should only consist
of unprivleged instructions. Code that enables Linux to run PRM in CPL3
is currently in-progress and will be released in the future.

The code follows the PRM specification and has been tested on QEMU
running an OVMF image that was integrated with a sample implementation
in Michael Kubacki's EDK2 staging branch.

Links: 

PRM specification:
https://uefi.org/sites/default/files/resources/Platform%20Runtime%20Mechanism%20-%20with%20legal%20notice.pdf

The EDK2 staging branch:
https://github.com/tianocore/edk2-staging/tree/PlatformRuntimeMechanism/PrmPkg


Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Ashok Raj <ashok.raj@intel.com>
Cc: Rafael Wysocki <rafael.j.wysocki@intel.com>

Signed-off-by: Erik Kaneda <erik.kaneda@intel.com>


Erik Kaneda (4):
  ACPICA: iASL: add disassembler support for PRMT
  ACPICA: Add support for PlatformRtMechanism OperationRegion handler
  PRM: implement OperationRegion handler for the PlatformRtMechanism
    subtype
  ACPI: Add \_SB._OSC bit for PRM

 drivers/acpi/Kconfig           |   5 +
 drivers/acpi/Makefile          |   1 +
 drivers/acpi/acpica/acutils.h  |   2 +
 drivers/acpi/acpica/exfield.c  |   8 +-
 drivers/acpi/acpica/exserial.c |  12 ++
 drivers/acpi/acpica/utuuid.c   |  41 +++++
 drivers/acpi/bus.c             |   3 +
 drivers/acpi/prmt.c            | 302 +++++++++++++++++++++++++++++++++
 drivers/acpi/tables.c          |   9 +
 include/acpi/acconfig.h        |   2 +
 include/acpi/actbl2.h          |  40 +++++
 include/linux/acpi.h           |   2 +
 include/linux/prmt.h           |   7 +
 13 files changed, 432 insertions(+), 2 deletions(-)
 create mode 100644 drivers/acpi/prmt.c
 create mode 100644 include/linux/prmt.h

-- 
2.29.2


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v3 1/4] ACPICA: iASL: add disassembler support for PRMT
  2021-03-22 22:37 [PATCH v3 0/4] Implement Platform Runtime Mechanism plumbing Erik Kaneda
@ 2021-03-22 22:37 ` Erik Kaneda
  2021-03-22 22:37 ` [PATCH v3 2/4] ACPICA: Add support for PlatformRtMechanism OperationRegion handler Erik Kaneda
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 6+ messages in thread
From: Erik Kaneda @ 2021-03-22 22:37 UTC (permalink / raw
  To: Rafael J . Wysocki, ACPI Devel Maling List; +Cc: Erik Kaneda

Signed-off-by: Erik Kaneda <erik.kaneda@intel.com>
---
 include/acpi/actbl2.h | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h
index d6478c430c99..3b6434fc3f3c 100644
--- a/include/acpi/actbl2.h
+++ b/include/acpi/actbl2.h
@@ -38,6 +38,7 @@
 #define ACPI_SIG_PDTT           "PDTT"	/* Platform Debug Trigger Table */
 #define ACPI_SIG_PMTT           "PMTT"	/* Platform Memory Topology Table */
 #define ACPI_SIG_PPTT           "PPTT"	/* Processor Properties Topology Table */
+#define ACPI_SIG_PRMT           "PRMT"	/* Platform Runtime Mechanism Table */
 #define ACPI_SIG_RASF           "RASF"	/* RAS Feature table */
 #define ACPI_SIG_SBST           "SBST"	/* Smart Battery Specification Table */
 #define ACPI_SIG_SDEI           "SDEI"	/* Software Delegated Exception Interface Table */
@@ -1528,6 +1529,45 @@ struct acpi_pptt_id {
 	u16 spin_rev;
 };
 
+/*******************************************************************************
+ *
+ * PRMT - Platform Runtime Mechanism Table
+ *        Version 1
+ *
+ ******************************************************************************/
+
+struct acpi_table_prmt {
+	struct acpi_table_header header;
+	u8 platform_guid[16];
+	u32 module_offset;
+	u32 module_count;
+};
+
+struct acpi_prmt_module_header {
+	u16 revision;
+	u16 length;
+};
+
+struct acpi_prmt_module_info {
+	u16 revision;
+	u16 length;
+	u8 guid[16];
+	u16 major_rev;
+	u16 minor_rev;
+	u16 handler_count;
+	u32 handler_offset;
+	u64 mmio_list_pointer;
+};
+
+struct acpi_prmt_handler_info {
+	u16 revision;
+	u16 length;
+	u8 guid[16];
+	u64 address;
+	u64 static_data_buffer_address;
+	u64 acpi_param_buffer_address;
+};
+
 /*******************************************************************************
  *
  * RASF - RAS Feature Table (ACPI 5.0)
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v3 2/4] ACPICA: Add support for PlatformRtMechanism OperationRegion handler
  2021-03-22 22:37 [PATCH v3 0/4] Implement Platform Runtime Mechanism plumbing Erik Kaneda
  2021-03-22 22:37 ` [PATCH v3 1/4] ACPICA: iASL: add disassembler support for PRMT Erik Kaneda
@ 2021-03-22 22:37 ` Erik Kaneda
  2021-03-22 22:37 ` [PATCH v3 3/4] PRM: implement OperationRegion handler for the PlatformRtMechanism subtype Erik Kaneda
  2021-03-22 22:37 ` [PATCH v3 4/4] ACPI: Add \_SB._OSC bit for PRM Erik Kaneda
  3 siblings, 0 replies; 6+ messages in thread
From: Erik Kaneda @ 2021-03-22 22:37 UTC (permalink / raw
  To: Rafael J . Wysocki, ACPI Devel Maling List; +Cc: Erik Kaneda

Writing a buffer to a PlatformRtMechanism fieldunit invokes a
bidirectional transaction. The input buffer contains 26 bytes
containing 9 bytes of status, a command byte and a 16-byte UUID.
This change will will simply pass this incoming buffer to a handler
registered by the OS.

Signed-off-by: Erik Kaneda <erik.kaneda@intel.com>
---
 drivers/acpi/acpica/acutils.h  |  2 ++
 drivers/acpi/acpica/exfield.c  |  8 +++++--
 drivers/acpi/acpica/exserial.c | 12 ++++++++++
 drivers/acpi/acpica/utuuid.c   | 41 ++++++++++++++++++++++++++++++++++
 include/acpi/acconfig.h        |  2 ++
 5 files changed, 63 insertions(+), 2 deletions(-)

diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h
index be6de7149e67..dd72d589babe 100644
--- a/drivers/acpi/acpica/acutils.h
+++ b/drivers/acpi/acpica/acutils.h
@@ -736,6 +736,8 @@ const char *acpi_ah_match_uuid(u8 *data);
  */
 #if (defined ACPI_ASL_COMPILER || defined ACPI_EXEC_APP || defined ACPI_HELP_APP)
 void acpi_ut_convert_string_to_uuid(char *in_string, u8 *uuid_buffer);
+
+acpi_status acpi_ut_convert_uuid_to_string(char *uuid_buffer, char *out_string);
 #endif
 
 #endif				/* _ACUTILS_H */
diff --git a/drivers/acpi/acpica/exfield.c b/drivers/acpi/acpica/exfield.c
index 32f03ee81785..06f3c9df1e22 100644
--- a/drivers/acpi/acpica/exfield.c
+++ b/drivers/acpi/acpica/exfield.c
@@ -139,7 +139,9 @@ acpi_ex_read_data_from_field(struct acpi_walk_state *walk_state,
 		    || obj_desc->field.region_obj->region.space_id ==
 		    ACPI_ADR_SPACE_GSBUS
 		    || obj_desc->field.region_obj->region.space_id ==
-		    ACPI_ADR_SPACE_IPMI)) {
+		    ACPI_ADR_SPACE_IPMI
+		    || obj_desc->field.region_obj->region.space_id ==
+		    ACPI_ADR_SPACE_PLATFORM_RT)) {
 
 		/* SMBus, GSBus, IPMI serial */
 
@@ -301,7 +303,9 @@ acpi_ex_write_data_to_field(union acpi_operand_object *source_desc,
 		    || obj_desc->field.region_obj->region.space_id ==
 		    ACPI_ADR_SPACE_GSBUS
 		    || obj_desc->field.region_obj->region.space_id ==
-		    ACPI_ADR_SPACE_IPMI)) {
+		    ACPI_ADR_SPACE_IPMI
+		    || obj_desc->field.region_obj->region.space_id ==
+		    ACPI_ADR_SPACE_PLATFORM_RT)) {
 
 		/* SMBus, GSBus, IPMI serial */
 
diff --git a/drivers/acpi/acpica/exserial.c b/drivers/acpi/acpica/exserial.c
index 8e8d95f7947b..10d68a5f76a3 100644
--- a/drivers/acpi/acpica/exserial.c
+++ b/drivers/acpi/acpica/exserial.c
@@ -195,6 +195,12 @@ acpi_ex_read_serial_bus(union acpi_operand_object *obj_desc,
 		function = ACPI_READ | (accessor_type << 16);
 		break;
 
+	case ACPI_ADR_SPACE_PLATFORM_RT:
+
+		buffer_length = ACPI_PRM_INPUT_BUFFER_SIZE;
+		function = ACPI_READ;
+		break;
+
 	default:
 		return_ACPI_STATUS(AE_AML_INVALID_SPACE_ID);
 	}
@@ -311,6 +317,12 @@ acpi_ex_write_serial_bus(union acpi_operand_object *source_desc,
 		function = ACPI_WRITE | (accessor_type << 16);
 		break;
 
+	case ACPI_ADR_SPACE_PLATFORM_RT:
+
+		buffer_length = ACPI_PRM_INPUT_BUFFER_SIZE;
+		function = ACPI_WRITE;
+		break;
+
 	default:
 		return_ACPI_STATUS(AE_AML_INVALID_SPACE_ID);
 	}
diff --git a/drivers/acpi/acpica/utuuid.c b/drivers/acpi/acpica/utuuid.c
index 090e44b6b6c7..dca9061518ab 100644
--- a/drivers/acpi/acpica/utuuid.c
+++ b/drivers/acpi/acpica/utuuid.c
@@ -61,4 +61,45 @@ void acpi_ut_convert_string_to_uuid(char *in_string, u8 *uuid_buffer)
 					       1]);
 	}
 }
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_ut_convert_uuid_to_string
+ *
+ * PARAMETERS:  uuid_buffer         - 16-byte UUID buffer
+ *              out_string          - 36-byte formatted UUID string
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Convert 16-byte UUID buffer to 36-byte formatted UUID string
+ *              out_string must be 37 bytes to include null terminator.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_convert_uuid_to_string(char *uuid_buffer, char *out_string)
+{
+	u32 i;
+
+	if (!uuid_buffer || !out_string) {
+		return (AE_BAD_PARAMETER);
+	}
+
+	for (i = 0; i < UUID_BUFFER_LENGTH; i++) {
+		out_string[acpi_gbl_map_to_uuid_offset[i]] =
+		    acpi_ut_hex_to_ascii_char(uuid_buffer[i], 4);
+
+		out_string[acpi_gbl_map_to_uuid_offset[i] + 1] =
+		    acpi_ut_hex_to_ascii_char(uuid_buffer[i], 0);
+	}
+
+	/* Insert required hyphens (dashes) */
+
+	out_string[UUID_HYPHEN1_OFFSET] =
+	    out_string[UUID_HYPHEN2_OFFSET] =
+	    out_string[UUID_HYPHEN3_OFFSET] =
+	    out_string[UUID_HYPHEN4_OFFSET] = '-';
+
+	out_string[UUID_STRING_LENGTH] = 0;	/* Null terminate */
+	return (AE_OK);
+}
 #endif
diff --git a/include/acpi/acconfig.h b/include/acpi/acconfig.h
index e92f84fa8c68..0362cbb72359 100644
--- a/include/acpi/acconfig.h
+++ b/include/acpi/acconfig.h
@@ -188,6 +188,8 @@
 #define ACPI_MAX_GSBUS_DATA_SIZE        255
 #define ACPI_MAX_GSBUS_BUFFER_SIZE      ACPI_SERIAL_HEADER_SIZE + ACPI_MAX_GSBUS_DATA_SIZE
 
+#define ACPI_PRM_INPUT_BUFFER_SIZE      26
+
 /* _sx_d and _sx_w control methods */
 
 #define ACPI_NUM_sx_d_METHODS           4
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v3 3/4] PRM: implement OperationRegion handler for the PlatformRtMechanism subtype
  2021-03-22 22:37 [PATCH v3 0/4] Implement Platform Runtime Mechanism plumbing Erik Kaneda
  2021-03-22 22:37 ` [PATCH v3 1/4] ACPICA: iASL: add disassembler support for PRMT Erik Kaneda
  2021-03-22 22:37 ` [PATCH v3 2/4] ACPICA: Add support for PlatformRtMechanism OperationRegion handler Erik Kaneda
@ 2021-03-22 22:37 ` Erik Kaneda
  2021-06-09 14:56   ` Rafael J. Wysocki
  2021-03-22 22:37 ` [PATCH v3 4/4] ACPI: Add \_SB._OSC bit for PRM Erik Kaneda
  3 siblings, 1 reply; 6+ messages in thread
From: Erik Kaneda @ 2021-03-22 22:37 UTC (permalink / raw
  To: Rafael J . Wysocki, ACPI Devel Maling List
  Cc: Erik Kaneda, Dan Williams, Ashok Raj, Rafael Wysocki

Platform Runtime Mechanism (PRM) is a firmware interface that exposes
a set of binary executables that can either be called from the AML
interpreter or device drivers by bypassing the AML interpreter.
This change implements the AML interpreter path.

According to the specification [1], PRM services are listed in an
ACPI table called the PRMT. This patch parses module and handler
information listed in the PRMT and registers the PlatformRtMechanism
OpRegion handler before ACPI tables are loaded.

Each service is defined by a 16-byte GUID and called from writing a
26-byte ASL buffer containing the identifier to a FieldUnit object
defined inside a PlatformRtMechanism OperationRegion.

    OperationRegion (PRMR, PlatformRtMechanism, 0, 26)
    Field (PRMR, BufferAcc, NoLock, Preserve)
    {
        PRMF, 208 // Write to this field to invoke the OperationRegion Handler
    }

The 26-byte ASL buffer is defined as the following:

Byte Offset   Byte Length    Description
=============================================================
     0             1         PRM OperationRegion handler status
     1             8         PRM service status
     9             1         PRM command
    10            16         PRM handler GUID

The ASL caller fills out a 26-byte buffer containing the PRM command
and the PRM handler GUID like so:

    /* Local0 is the PRM data buffer */
    Local0 = buffer (26){}

    /* Create byte fields over the buffer */
    CreateByteField (Local0, 0x9, CMD)
    CreateField (Local0, 0x50, 0x80, GUID)

    /* Fill in the command and data fields of the data buffer */
    CMD = 0 // run command
    GUID = ToUUID("xxxx-xx-xxx-xxxx")

    /*
     * Invoke PRM service with an ID that matches GUID and save the
     * result.
     */
    Local0 = (\_SB.PRMT.PRMF = Local0)

Byte offset 0 - 8 are written by the handler as a status passed back to AML
and used by ASL like so:

    /* Create byte fields over the buffer */
    CreateByteField (Local0, 0x0, PSTA)
    CreateQWordField (Local0, 0x1, USTA)

In this ASL code, PSTA contains a status from the OperationRegion and
USTA contains a status from the PRM service.

The 26-byte buffer is recieved by acpi_platformrt_space_handler. This
handler will look at the command value and the handler guid and take
the approperiate actions.

Command value    Action
=====================================================================
    0            Run the PRM service indicated by the PRM handler
                 GUID (bytes 10-26)

    1            Prevent PRM runtime updates from happening to the
                 service's parent module

    2            Allow PRM updates from happening to the service's parent module

Note: PRM service updates will be implemented in future patches. This
patch enables command value 0.

[1] https://uefi.org/sites/default/files/resources/Platform%20Runtime%20Mechanism%20-%20with%20legal%20notice.pdf

Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Ashok Raj <ashok.raj@intel.com>
Cc: Rafael Wysocki <rafael.j.wysocki@intel.com>

Signed-off-by: Erik Kaneda <erik.kaneda@intel.com>
---
 drivers/acpi/Kconfig  |   5 +
 drivers/acpi/Makefile |   1 +
 drivers/acpi/bus.c    |   2 +
 drivers/acpi/prmt.c   | 302 ++++++++++++++++++++++++++++++++++++++++++
 drivers/acpi/tables.c |   9 ++
 include/linux/acpi.h  |   1 +
 include/linux/prmt.h  |   7 +
 7 files changed, 327 insertions(+)
 create mode 100644 drivers/acpi/prmt.c
 create mode 100644 include/linux/prmt.h

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index eedec61e3476..3972de7b7565 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -543,3 +543,8 @@ config X86_PM_TIMER
 
 	  You should nearly always say Y here because many modern
 	  systems require this timer.
+
+config ACPI_PRMT
+	bool "Platform Runtime Mechanism Support"
+	depends on EFI && X86_64
+	default y
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 700b41adf2db..efb0d1f64019 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -61,6 +61,7 @@ acpi-$(CONFIG_ACPI_FPDT)	+= acpi_fpdt.o
 acpi-$(CONFIG_ACPI_LPIT)	+= acpi_lpit.o
 acpi-$(CONFIG_ACPI_GENERIC_GSI) += irq.o
 acpi-$(CONFIG_ACPI_WATCHDOG)	+= acpi_watchdog.o
+acpi-$(CONFIG_ACPI_PRMT)	+= prmt.o
 
 # Address translation
 acpi-$(CONFIG_ACPI_ADXL)	+= acpi_adxl.o
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index be7da23fad76..3484497923d5 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -30,6 +30,7 @@
 #include <linux/pci.h>
 #include <acpi/apei.h>
 #include <linux/suspend.h>
+#include <linux/prmt.h>
 
 #include "internal.h"
 
@@ -1330,6 +1331,7 @@ static int __init acpi_init(void)
 		acpi_kobj = NULL;
 	}
 
+	init_prmt();
 	result = acpi_bus_init();
 	if (result) {
 		disable_acpi();
diff --git a/drivers/acpi/prmt.c b/drivers/acpi/prmt.c
new file mode 100644
index 000000000000..2bc28bdf0998
--- /dev/null
+++ b/drivers/acpi/prmt.c
@@ -0,0 +1,302 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Author: Erik Kaneda <erik.kaneda@intel.com>
+ * Copyright 2020 Intel Corporation
+ *
+ * prmt.c
+ *
+ * Each PRM service is an executable that is run in a restricted environment
+ * that is invoked by writing to the PlatformRtMechanism OperationRegion from
+ * AML bytecode.
+ *
+ * init_prmt initializes the Platform Runtime Mechanism (PRM) services by
+ * processing data in the PRMT as well as registering an ACPI OperationRegion
+ * handler for the PlatformRtMechanism subtype.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/efi.h>
+#include <linux/acpi.h>
+#include <linux/prmt.h>
+#include <asm/efi.h>
+
+#pragma pack(1)
+struct prm_mmio_addr_range {
+	u64 phys_addr;
+	u64 virt_addr;
+	u32 length;
+};
+
+struct prm_mmio_info {
+	u64 mmio_count;
+	struct prm_mmio_addr_range addr_ranges[];
+};
+
+struct prm_buffer {
+	u8 prm_status;
+	u64 efi_status;
+	u8 prm_cmd;
+	guid_t handler_guid;
+};
+
+struct prm_context_buffer {
+	char signature[ACPI_NAMESEG_SIZE];
+	u16 revision;
+	u16 reserved;
+	guid_t identifier;
+	u64 static_data_buffer;
+	struct prm_mmio_info *mmio_ranges;
+};
+#pragma pack()
+
+
+LIST_HEAD(prm_module_list);
+
+struct prm_handler_info {
+	guid_t guid;
+	u64 handler_addr;
+	u64 static_data_buffer_addr;
+	u64 acpi_param_buffer_addr;
+
+	struct list_head handler_list;
+};
+
+struct prm_module_info {
+	guid_t guid;
+	u16 major_rev;
+	u16 minor_rev;
+	u16 handler_count;
+	struct prm_mmio_info *mmio_info;
+	bool updatable;
+
+	struct list_head module_list;
+	struct prm_handler_info handlers[];
+};
+
+
+static u64 efi_pa_va_lookup(u64 pa)
+{
+	efi_memory_desc_t *md;
+	u64 pa_offset = pa & ~PAGE_MASK;
+	u64 page = pa & PAGE_MASK;
+
+	for_each_efi_memory_desc(md) {
+		if (md->phys_addr < pa && pa < md->phys_addr + PAGE_SIZE * md->num_pages)
+			return pa_offset + md->virt_addr + page - md->phys_addr;
+	}
+
+	return 0;
+}
+
+
+#define get_first_handler(a) ((struct acpi_prmt_handler_info *) ((char *) (a) + a->handler_offset))
+#define get_next_handler(a) ((struct acpi_prmt_handler_info *) (sizeof(struct acpi_prmt_handler_info) + (char *) a))
+
+static int __init
+acpi_parse_prmt(union acpi_subtable_headers *header, const unsigned long end)
+{
+	struct acpi_prmt_module_info *module_info;
+	struct acpi_prmt_handler_info *handler_info;
+	struct prm_handler_info *th;
+	struct prm_module_info *tm;
+	u64 mmio_count = 0;
+	u64 cur_handler = 0;
+	u32 module_info_size = 0;
+	u64 mmio_range_size = 0;
+	void *temp_mmio;
+
+	module_info = (struct acpi_prmt_module_info *) header;
+	module_info_size = struct_size(tm, handlers, module_info->handler_count);
+	tm = kmalloc(module_info_size, GFP_KERNEL);
+
+	guid_copy(&tm->guid, (guid_t *) module_info->guid);
+	tm->major_rev = module_info->major_rev;
+	tm->minor_rev = module_info->minor_rev;
+	tm->handler_count = module_info->handler_count;
+	tm->updatable = true;
+
+	if (module_info->mmio_list_pointer) {
+		/*
+		 * Each module is associated with a list of addr
+		 * ranges that it can use during the service
+		 */
+		mmio_count = *(u64 *) memremap(module_info->mmio_list_pointer, 8, MEMREMAP_WB);
+		mmio_range_size = struct_size(tm->mmio_info, addr_ranges, mmio_count);
+		tm->mmio_info = kmalloc(mmio_range_size, GFP_KERNEL);
+		temp_mmio = memremap(module_info->mmio_list_pointer, mmio_range_size, MEMREMAP_WB);
+		memmove(tm->mmio_info, temp_mmio, mmio_range_size);
+	} else {
+		mmio_range_size = struct_size(tm->mmio_info, addr_ranges, mmio_count);
+		tm->mmio_info = kmalloc(mmio_range_size, GFP_KERNEL);
+		tm->mmio_info->mmio_count = 0;
+	}
+
+	INIT_LIST_HEAD(&tm->module_list);
+	list_add(&tm->module_list, &prm_module_list);
+
+	handler_info = get_first_handler(module_info);
+	do {
+		th = &tm->handlers[cur_handler];
+
+		guid_copy(&th->guid, (guid_t *)handler_info->guid);
+		th->handler_addr = efi_pa_va_lookup(handler_info->address);
+		th->static_data_buffer_addr = efi_pa_va_lookup(handler_info->static_data_buffer_address);
+		th->acpi_param_buffer_addr = efi_pa_va_lookup(handler_info->acpi_param_buffer_address);
+	} while (++cur_handler < tm->handler_count && (handler_info = get_next_handler(handler_info)));
+
+	return 0;
+}
+
+#define GET_MODULE	0
+#define GET_HANDLER	1
+
+static void *find_guid_info(const guid_t *guid, u8 mode)
+{
+	struct prm_handler_info *cur_handler;
+	struct prm_module_info *cur_module;
+	int i = 0;
+
+	list_for_each_entry(cur_module, &prm_module_list, module_list) {
+		for (i = 0; i < cur_module->handler_count; ++i) {
+			cur_handler = &cur_module->handlers[i];
+			if (guid_equal(guid, &cur_handler->guid)) {
+				if (mode == GET_MODULE)
+					return (void *)cur_module;
+				else
+					return (void *)cur_handler;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+
+static struct prm_module_info *find_prm_module(const guid_t *guid)
+{
+	return (struct prm_module_info *)find_guid_info(guid, GET_MODULE);
+}
+
+static struct prm_handler_info *find_prm_handler(const guid_t *guid)
+{
+	return (struct prm_handler_info *) find_guid_info(guid, GET_HANDLER);
+}
+
+/* In-coming PRM commands */
+
+#define PRM_CMD_RUN_SERVICE		0
+#define PRM_CMD_START_TRANSACTION	1
+#define PRM_CMD_END_TRANSACTION		2
+
+/* statuses that can be passed back to ASL */
+
+#define PRM_HANDLER_SUCCESS 		0
+#define PRM_HANDLER_ERROR 		1
+#define INVALID_PRM_COMMAND 		2
+#define PRM_HANDLER_GUID_NOT_FOUND 	3
+#define UPDATE_LOCK_ALREADY_HELD 	4
+#define UPDATE_UNLOCK_WITHOUT_LOCK 	5
+
+/*
+ * This is the PlatformRtMechanism opregion space handler.
+ * @function: indicates the read/write. In fact as the PlatformRtMechanism
+ * message is driven by command, only write is meaningful.
+ *
+ * @addr   : not used
+ * @bits   : not used.
+ * @value  : it is an in/out parameter. It points to the PRM message buffer.
+ * @handler_context: not used
+ */
+static acpi_status acpi_platformrt_space_handler(u32 function,
+						 acpi_physical_address addr,
+						 u32 bits, acpi_integer *value,
+						 void *handler_context,
+						 void *region_context)
+{
+	struct prm_buffer *buffer = ACPI_CAST_PTR(struct prm_buffer, value);
+	struct prm_handler_info *handler;
+	struct prm_module_info *module;
+	efi_status_t status;
+	struct prm_context_buffer context;
+
+	/*
+	 * The returned acpi_status will always be AE_OK. Error values will be
+	 * saved in the first byte of the PRM message buffer to be used by ASL.
+	 */
+	switch (buffer->prm_cmd) {
+	case PRM_CMD_RUN_SERVICE:
+
+		handler = find_prm_handler(&buffer->handler_guid);
+		module = find_prm_module(&buffer->handler_guid);
+		if (!handler || !module)
+			goto invalid_guid;
+
+		ACPI_COPY_NAMESEG(context.signature, "PRMC");
+		context.revision = 0x0;
+		context.reserved = 0x0;
+		context.identifier = handler->guid;
+		context.static_data_buffer = handler->static_data_buffer_addr;
+		context.mmio_ranges = module->mmio_info;
+
+		status = efi_call_virt_pointer(handler, handler_addr,
+					       handler->acpi_param_buffer_addr,
+					       &context);
+		if (status == EFI_SUCCESS) {
+			buffer->prm_status = PRM_HANDLER_SUCCESS;
+		} else {
+			buffer->prm_status = PRM_HANDLER_ERROR;
+			buffer->efi_status = status;
+		}
+		break;
+
+	case PRM_CMD_START_TRANSACTION:
+
+		module = find_prm_module(&buffer->handler_guid);
+		if (!module)
+			goto invalid_guid;
+
+		if (module->updatable)
+			module->updatable = false;
+		else
+			buffer->prm_status = UPDATE_LOCK_ALREADY_HELD;
+		break;
+
+	case PRM_CMD_END_TRANSACTION:
+
+		module = find_prm_module(&buffer->handler_guid);
+		if (!module)
+			goto invalid_guid;
+
+		if (module->updatable)
+			buffer->prm_status = UPDATE_UNLOCK_WITHOUT_LOCK;
+		else
+			module->updatable = true;
+		break;
+
+	default:
+
+		buffer->prm_status = INVALID_PRM_COMMAND;
+		break;
+	}
+
+	return AE_OK;
+
+invalid_guid:
+	buffer->prm_status = PRM_HANDLER_GUID_NOT_FOUND;
+	return AE_OK;
+}
+
+void __init init_prmt(void)
+{
+	acpi_status status;
+	int mc = acpi_table_parse_entries(ACPI_SIG_PRMT, sizeof(struct acpi_table_prmt),
+					  0, acpi_parse_prmt, 0);
+	pr_info("PRM: found %u modules\n", mc);
+
+	status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
+						    ACPI_ADR_SPACE_PLATFORM_RT,
+						    &acpi_platformrt_space_handler,
+						    NULL, NULL);
+	if (ACPI_FAILURE(status))
+		pr_alert("PRM: OperationRegion handler could not be installed\n");
+}
diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c
index e48690a006a4..69a03d70740c 100644
--- a/drivers/acpi/tables.c
+++ b/drivers/acpi/tables.c
@@ -39,6 +39,7 @@ static int acpi_apic_instance __initdata;
 enum acpi_subtable_type {
 	ACPI_SUBTABLE_COMMON,
 	ACPI_SUBTABLE_HMAT,
+	ACPI_SUBTABLE_PRMT,
 };
 
 struct acpi_subtable_entry {
@@ -222,6 +223,8 @@ acpi_get_entry_type(struct acpi_subtable_entry *entry)
 		return entry->hdr->common.type;
 	case ACPI_SUBTABLE_HMAT:
 		return entry->hdr->hmat.type;
+	case ACPI_SUBTABLE_PRMT:
+		return 0;
 	}
 	return 0;
 }
@@ -234,6 +237,8 @@ acpi_get_entry_length(struct acpi_subtable_entry *entry)
 		return entry->hdr->common.length;
 	case ACPI_SUBTABLE_HMAT:
 		return entry->hdr->hmat.length;
+	case ACPI_SUBTABLE_PRMT:
+		return entry->hdr->prmt.length;
 	}
 	return 0;
 }
@@ -246,6 +251,8 @@ acpi_get_subtable_header_length(struct acpi_subtable_entry *entry)
 		return sizeof(entry->hdr->common);
 	case ACPI_SUBTABLE_HMAT:
 		return sizeof(entry->hdr->hmat);
+	case ACPI_SUBTABLE_PRMT:
+		return sizeof(entry->hdr->prmt);
 	}
 	return 0;
 }
@@ -255,6 +262,8 @@ acpi_get_subtable_type(char *id)
 {
 	if (strncmp(id, ACPI_SIG_HMAT, 4) == 0)
 		return ACPI_SUBTABLE_HMAT;
+	if (strncmp(id, ACPI_SIG_PRMT, 4) == 0)
+		return ACPI_SUBTABLE_PRMT;
 	return ACPI_SUBTABLE_COMMON;
 }
 
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 9f432411e988..6868b342cdc3 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -132,6 +132,7 @@ enum acpi_address_range_id {
 union acpi_subtable_headers {
 	struct acpi_subtable_header common;
 	struct acpi_hmat_structure hmat;
+	struct acpi_prmt_module_header prmt;
 };
 
 typedef int (*acpi_tbl_table_handler)(struct acpi_table_header *table);
diff --git a/include/linux/prmt.h b/include/linux/prmt.h
new file mode 100644
index 000000000000..24da8364b919
--- /dev/null
+++ b/include/linux/prmt.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifdef CONFIG_ACPI_PRMT
+void init_prmt(void);
+#else
+static inline void init_prmt(void) { }
+#endif
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v3 4/4] ACPI: Add \_SB._OSC bit for PRM
  2021-03-22 22:37 [PATCH v3 0/4] Implement Platform Runtime Mechanism plumbing Erik Kaneda
                   ` (2 preceding siblings ...)
  2021-03-22 22:37 ` [PATCH v3 3/4] PRM: implement OperationRegion handler for the PlatformRtMechanism subtype Erik Kaneda
@ 2021-03-22 22:37 ` Erik Kaneda
  3 siblings, 0 replies; 6+ messages in thread
From: Erik Kaneda @ 2021-03-22 22:37 UTC (permalink / raw
  To: Rafael J . Wysocki, ACPI Devel Maling List; +Cc: Erik Kaneda

Signed-off-by: Erik Kaneda <erik.kaneda@intel.com>
---
 drivers/acpi/bus.c   | 1 +
 include/linux/acpi.h | 1 +
 2 files changed, 2 insertions(+)

diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index 3484497923d5..e8119a9eca28 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -305,6 +305,7 @@ static void acpi_bus_osc_negotiate_platform_control(void)
 
 	capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_HOTPLUG_OST_SUPPORT;
 	capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PCLPI_SUPPORT;
+	capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PRM_SUPPORT;
 
 #ifdef CONFIG_ARM64
 	capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_GENERIC_INITIATOR_SUPPORT;
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 6868b342cdc3..fc50d2202446 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -547,6 +547,7 @@ acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context);
 #define OSC_SB_OSLPI_SUPPORT			0x00000100
 #define OSC_SB_CPC_DIVERSE_HIGH_SUPPORT		0x00001000
 #define OSC_SB_GENERIC_INITIATOR_SUPPORT	0x00002000
+#define OSC_SB_PRM_SUPPORT			0x00020000
 #define OSC_SB_NATIVE_USB4_SUPPORT		0x00040000
 
 extern bool osc_sb_apei_support_acked;
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH v3 3/4] PRM: implement OperationRegion handler for the PlatformRtMechanism subtype
  2021-03-22 22:37 ` [PATCH v3 3/4] PRM: implement OperationRegion handler for the PlatformRtMechanism subtype Erik Kaneda
@ 2021-06-09 14:56   ` Rafael J. Wysocki
  0 siblings, 0 replies; 6+ messages in thread
From: Rafael J. Wysocki @ 2021-06-09 14:56 UTC (permalink / raw
  To: Erik Kaneda
  Cc: Rafael J . Wysocki, ACPI Devel Maling List, Dan Williams,
	Ashok Raj, Rafael Wysocki

On Tue, Mar 23, 2021 at 12:09 AM Erik Kaneda <erik.kaneda@intel.com> wrote:
>
> Platform Runtime Mechanism (PRM) is a firmware interface that exposes
> a set of binary executables that can either be called from the AML
> interpreter or device drivers by bypassing the AML interpreter.
> This change implements the AML interpreter path.
>
> According to the specification [1], PRM services are listed in an
> ACPI table called the PRMT. This patch parses module and handler
> information listed in the PRMT and registers the PlatformRtMechanism
> OpRegion handler before ACPI tables are loaded.
>
> Each service is defined by a 16-byte GUID and called from writing a
> 26-byte ASL buffer containing the identifier to a FieldUnit object
> defined inside a PlatformRtMechanism OperationRegion.
>
>     OperationRegion (PRMR, PlatformRtMechanism, 0, 26)
>     Field (PRMR, BufferAcc, NoLock, Preserve)
>     {
>         PRMF, 208 // Write to this field to invoke the OperationRegion Handler
>     }
>
> The 26-byte ASL buffer is defined as the following:
>
> Byte Offset   Byte Length    Description
> =============================================================
>      0             1         PRM OperationRegion handler status
>      1             8         PRM service status
>      9             1         PRM command
>     10            16         PRM handler GUID
>
> The ASL caller fills out a 26-byte buffer containing the PRM command
> and the PRM handler GUID like so:
>
>     /* Local0 is the PRM data buffer */
>     Local0 = buffer (26){}
>
>     /* Create byte fields over the buffer */
>     CreateByteField (Local0, 0x9, CMD)
>     CreateField (Local0, 0x50, 0x80, GUID)
>
>     /* Fill in the command and data fields of the data buffer */
>     CMD = 0 // run command
>     GUID = ToUUID("xxxx-xx-xxx-xxxx")
>
>     /*
>      * Invoke PRM service with an ID that matches GUID and save the
>      * result.
>      */
>     Local0 = (\_SB.PRMT.PRMF = Local0)
>
> Byte offset 0 - 8 are written by the handler as a status passed back to AML
> and used by ASL like so:
>
>     /* Create byte fields over the buffer */
>     CreateByteField (Local0, 0x0, PSTA)
>     CreateQWordField (Local0, 0x1, USTA)
>
> In this ASL code, PSTA contains a status from the OperationRegion and
> USTA contains a status from the PRM service.
>
> The 26-byte buffer is recieved by acpi_platformrt_space_handler. This
> handler will look at the command value and the handler guid and take
> the approperiate actions.
>
> Command value    Action
> =====================================================================
>     0            Run the PRM service indicated by the PRM handler
>                  GUID (bytes 10-26)
>
>     1            Prevent PRM runtime updates from happening to the
>                  service's parent module
>
>     2            Allow PRM updates from happening to the service's parent module
>
> Note: PRM service updates will be implemented in future patches. This
> patch enables command value 0.
>
> [1] https://uefi.org/sites/default/files/resources/Platform%20Runtime%20Mechanism%20-%20with%20legal%20notice.pdf
>
> Cc: Dan Williams <dan.j.williams@intel.com>
> Cc: Ashok Raj <ashok.raj@intel.com>
> Cc: Rafael Wysocki <rafael.j.wysocki@intel.com>
>
> Signed-off-by: Erik Kaneda <erik.kaneda@intel.com>

Applied as 5.14 material along with the [4/4] (on top of the recent
ACPICA update) with some minor edits in the changelog and subject.

Thanks!

> ---
>  drivers/acpi/Kconfig  |   5 +
>  drivers/acpi/Makefile |   1 +
>  drivers/acpi/bus.c    |   2 +
>  drivers/acpi/prmt.c   | 302 ++++++++++++++++++++++++++++++++++++++++++
>  drivers/acpi/tables.c |   9 ++
>  include/linux/acpi.h  |   1 +
>  include/linux/prmt.h  |   7 +
>  7 files changed, 327 insertions(+)
>  create mode 100644 drivers/acpi/prmt.c
>  create mode 100644 include/linux/prmt.h
>
> diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
> index eedec61e3476..3972de7b7565 100644
> --- a/drivers/acpi/Kconfig
> +++ b/drivers/acpi/Kconfig
> @@ -543,3 +543,8 @@ config X86_PM_TIMER
>
>           You should nearly always say Y here because many modern
>           systems require this timer.
> +
> +config ACPI_PRMT
> +       bool "Platform Runtime Mechanism Support"
> +       depends on EFI && X86_64
> +       default y
> diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
> index 700b41adf2db..efb0d1f64019 100644
> --- a/drivers/acpi/Makefile
> +++ b/drivers/acpi/Makefile
> @@ -61,6 +61,7 @@ acpi-$(CONFIG_ACPI_FPDT)      += acpi_fpdt.o
>  acpi-$(CONFIG_ACPI_LPIT)       += acpi_lpit.o
>  acpi-$(CONFIG_ACPI_GENERIC_GSI) += irq.o
>  acpi-$(CONFIG_ACPI_WATCHDOG)   += acpi_watchdog.o
> +acpi-$(CONFIG_ACPI_PRMT)       += prmt.o
>
>  # Address translation
>  acpi-$(CONFIG_ACPI_ADXL)       += acpi_adxl.o
> diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
> index be7da23fad76..3484497923d5 100644
> --- a/drivers/acpi/bus.c
> +++ b/drivers/acpi/bus.c
> @@ -30,6 +30,7 @@
>  #include <linux/pci.h>
>  #include <acpi/apei.h>
>  #include <linux/suspend.h>
> +#include <linux/prmt.h>
>
>  #include "internal.h"
>
> @@ -1330,6 +1331,7 @@ static int __init acpi_init(void)
>                 acpi_kobj = NULL;
>         }
>
> +       init_prmt();
>         result = acpi_bus_init();
>         if (result) {
>                 disable_acpi();
> diff --git a/drivers/acpi/prmt.c b/drivers/acpi/prmt.c
> new file mode 100644
> index 000000000000..2bc28bdf0998
> --- /dev/null
> +++ b/drivers/acpi/prmt.c
> @@ -0,0 +1,302 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Author: Erik Kaneda <erik.kaneda@intel.com>
> + * Copyright 2020 Intel Corporation
> + *
> + * prmt.c
> + *
> + * Each PRM service is an executable that is run in a restricted environment
> + * that is invoked by writing to the PlatformRtMechanism OperationRegion from
> + * AML bytecode.
> + *
> + * init_prmt initializes the Platform Runtime Mechanism (PRM) services by
> + * processing data in the PRMT as well as registering an ACPI OperationRegion
> + * handler for the PlatformRtMechanism subtype.
> + *
> + */
> +#include <linux/kernel.h>
> +#include <linux/efi.h>
> +#include <linux/acpi.h>
> +#include <linux/prmt.h>
> +#include <asm/efi.h>
> +
> +#pragma pack(1)
> +struct prm_mmio_addr_range {
> +       u64 phys_addr;
> +       u64 virt_addr;
> +       u32 length;
> +};
> +
> +struct prm_mmio_info {
> +       u64 mmio_count;
> +       struct prm_mmio_addr_range addr_ranges[];
> +};
> +
> +struct prm_buffer {
> +       u8 prm_status;
> +       u64 efi_status;
> +       u8 prm_cmd;
> +       guid_t handler_guid;
> +};
> +
> +struct prm_context_buffer {
> +       char signature[ACPI_NAMESEG_SIZE];
> +       u16 revision;
> +       u16 reserved;
> +       guid_t identifier;
> +       u64 static_data_buffer;
> +       struct prm_mmio_info *mmio_ranges;
> +};
> +#pragma pack()
> +
> +
> +LIST_HEAD(prm_module_list);
> +
> +struct prm_handler_info {
> +       guid_t guid;
> +       u64 handler_addr;
> +       u64 static_data_buffer_addr;
> +       u64 acpi_param_buffer_addr;
> +
> +       struct list_head handler_list;
> +};
> +
> +struct prm_module_info {
> +       guid_t guid;
> +       u16 major_rev;
> +       u16 minor_rev;
> +       u16 handler_count;
> +       struct prm_mmio_info *mmio_info;
> +       bool updatable;
> +
> +       struct list_head module_list;
> +       struct prm_handler_info handlers[];
> +};
> +
> +
> +static u64 efi_pa_va_lookup(u64 pa)
> +{
> +       efi_memory_desc_t *md;
> +       u64 pa_offset = pa & ~PAGE_MASK;
> +       u64 page = pa & PAGE_MASK;
> +
> +       for_each_efi_memory_desc(md) {
> +               if (md->phys_addr < pa && pa < md->phys_addr + PAGE_SIZE * md->num_pages)
> +                       return pa_offset + md->virt_addr + page - md->phys_addr;
> +       }
> +
> +       return 0;
> +}
> +
> +
> +#define get_first_handler(a) ((struct acpi_prmt_handler_info *) ((char *) (a) + a->handler_offset))
> +#define get_next_handler(a) ((struct acpi_prmt_handler_info *) (sizeof(struct acpi_prmt_handler_info) + (char *) a))
> +
> +static int __init
> +acpi_parse_prmt(union acpi_subtable_headers *header, const unsigned long end)
> +{
> +       struct acpi_prmt_module_info *module_info;
> +       struct acpi_prmt_handler_info *handler_info;
> +       struct prm_handler_info *th;
> +       struct prm_module_info *tm;
> +       u64 mmio_count = 0;
> +       u64 cur_handler = 0;
> +       u32 module_info_size = 0;
> +       u64 mmio_range_size = 0;
> +       void *temp_mmio;
> +
> +       module_info = (struct acpi_prmt_module_info *) header;
> +       module_info_size = struct_size(tm, handlers, module_info->handler_count);
> +       tm = kmalloc(module_info_size, GFP_KERNEL);
> +
> +       guid_copy(&tm->guid, (guid_t *) module_info->guid);
> +       tm->major_rev = module_info->major_rev;
> +       tm->minor_rev = module_info->minor_rev;
> +       tm->handler_count = module_info->handler_count;
> +       tm->updatable = true;
> +
> +       if (module_info->mmio_list_pointer) {
> +               /*
> +                * Each module is associated with a list of addr
> +                * ranges that it can use during the service
> +                */
> +               mmio_count = *(u64 *) memremap(module_info->mmio_list_pointer, 8, MEMREMAP_WB);
> +               mmio_range_size = struct_size(tm->mmio_info, addr_ranges, mmio_count);
> +               tm->mmio_info = kmalloc(mmio_range_size, GFP_KERNEL);
> +               temp_mmio = memremap(module_info->mmio_list_pointer, mmio_range_size, MEMREMAP_WB);
> +               memmove(tm->mmio_info, temp_mmio, mmio_range_size);
> +       } else {
> +               mmio_range_size = struct_size(tm->mmio_info, addr_ranges, mmio_count);
> +               tm->mmio_info = kmalloc(mmio_range_size, GFP_KERNEL);
> +               tm->mmio_info->mmio_count = 0;
> +       }
> +
> +       INIT_LIST_HEAD(&tm->module_list);
> +       list_add(&tm->module_list, &prm_module_list);
> +
> +       handler_info = get_first_handler(module_info);
> +       do {
> +               th = &tm->handlers[cur_handler];
> +
> +               guid_copy(&th->guid, (guid_t *)handler_info->guid);
> +               th->handler_addr = efi_pa_va_lookup(handler_info->address);
> +               th->static_data_buffer_addr = efi_pa_va_lookup(handler_info->static_data_buffer_address);
> +               th->acpi_param_buffer_addr = efi_pa_va_lookup(handler_info->acpi_param_buffer_address);
> +       } while (++cur_handler < tm->handler_count && (handler_info = get_next_handler(handler_info)));
> +
> +       return 0;
> +}
> +
> +#define GET_MODULE     0
> +#define GET_HANDLER    1
> +
> +static void *find_guid_info(const guid_t *guid, u8 mode)
> +{
> +       struct prm_handler_info *cur_handler;
> +       struct prm_module_info *cur_module;
> +       int i = 0;
> +
> +       list_for_each_entry(cur_module, &prm_module_list, module_list) {
> +               for (i = 0; i < cur_module->handler_count; ++i) {
> +                       cur_handler = &cur_module->handlers[i];
> +                       if (guid_equal(guid, &cur_handler->guid)) {
> +                               if (mode == GET_MODULE)
> +                                       return (void *)cur_module;
> +                               else
> +                                       return (void *)cur_handler;
> +                       }
> +               }
> +       }
> +
> +       return NULL;
> +}
> +
> +
> +static struct prm_module_info *find_prm_module(const guid_t *guid)
> +{
> +       return (struct prm_module_info *)find_guid_info(guid, GET_MODULE);
> +}
> +
> +static struct prm_handler_info *find_prm_handler(const guid_t *guid)
> +{
> +       return (struct prm_handler_info *) find_guid_info(guid, GET_HANDLER);
> +}
> +
> +/* In-coming PRM commands */
> +
> +#define PRM_CMD_RUN_SERVICE            0
> +#define PRM_CMD_START_TRANSACTION      1
> +#define PRM_CMD_END_TRANSACTION                2
> +
> +/* statuses that can be passed back to ASL */
> +
> +#define PRM_HANDLER_SUCCESS            0
> +#define PRM_HANDLER_ERROR              1
> +#define INVALID_PRM_COMMAND            2
> +#define PRM_HANDLER_GUID_NOT_FOUND     3
> +#define UPDATE_LOCK_ALREADY_HELD       4
> +#define UPDATE_UNLOCK_WITHOUT_LOCK     5
> +
> +/*
> + * This is the PlatformRtMechanism opregion space handler.
> + * @function: indicates the read/write. In fact as the PlatformRtMechanism
> + * message is driven by command, only write is meaningful.
> + *
> + * @addr   : not used
> + * @bits   : not used.
> + * @value  : it is an in/out parameter. It points to the PRM message buffer.
> + * @handler_context: not used
> + */
> +static acpi_status acpi_platformrt_space_handler(u32 function,
> +                                                acpi_physical_address addr,
> +                                                u32 bits, acpi_integer *value,
> +                                                void *handler_context,
> +                                                void *region_context)
> +{
> +       struct prm_buffer *buffer = ACPI_CAST_PTR(struct prm_buffer, value);
> +       struct prm_handler_info *handler;
> +       struct prm_module_info *module;
> +       efi_status_t status;
> +       struct prm_context_buffer context;
> +
> +       /*
> +        * The returned acpi_status will always be AE_OK. Error values will be
> +        * saved in the first byte of the PRM message buffer to be used by ASL.
> +        */
> +       switch (buffer->prm_cmd) {
> +       case PRM_CMD_RUN_SERVICE:
> +
> +               handler = find_prm_handler(&buffer->handler_guid);
> +               module = find_prm_module(&buffer->handler_guid);
> +               if (!handler || !module)
> +                       goto invalid_guid;
> +
> +               ACPI_COPY_NAMESEG(context.signature, "PRMC");
> +               context.revision = 0x0;
> +               context.reserved = 0x0;
> +               context.identifier = handler->guid;
> +               context.static_data_buffer = handler->static_data_buffer_addr;
> +               context.mmio_ranges = module->mmio_info;
> +
> +               status = efi_call_virt_pointer(handler, handler_addr,
> +                                              handler->acpi_param_buffer_addr,
> +                                              &context);
> +               if (status == EFI_SUCCESS) {
> +                       buffer->prm_status = PRM_HANDLER_SUCCESS;
> +               } else {
> +                       buffer->prm_status = PRM_HANDLER_ERROR;
> +                       buffer->efi_status = status;
> +               }
> +               break;
> +
> +       case PRM_CMD_START_TRANSACTION:
> +
> +               module = find_prm_module(&buffer->handler_guid);
> +               if (!module)
> +                       goto invalid_guid;
> +
> +               if (module->updatable)
> +                       module->updatable = false;
> +               else
> +                       buffer->prm_status = UPDATE_LOCK_ALREADY_HELD;
> +               break;
> +
> +       case PRM_CMD_END_TRANSACTION:
> +
> +               module = find_prm_module(&buffer->handler_guid);
> +               if (!module)
> +                       goto invalid_guid;
> +
> +               if (module->updatable)
> +                       buffer->prm_status = UPDATE_UNLOCK_WITHOUT_LOCK;
> +               else
> +                       module->updatable = true;
> +               break;
> +
> +       default:
> +
> +               buffer->prm_status = INVALID_PRM_COMMAND;
> +               break;
> +       }
> +
> +       return AE_OK;
> +
> +invalid_guid:
> +       buffer->prm_status = PRM_HANDLER_GUID_NOT_FOUND;
> +       return AE_OK;
> +}
> +
> +void __init init_prmt(void)
> +{
> +       acpi_status status;
> +       int mc = acpi_table_parse_entries(ACPI_SIG_PRMT, sizeof(struct acpi_table_prmt),
> +                                         0, acpi_parse_prmt, 0);
> +       pr_info("PRM: found %u modules\n", mc);
> +
> +       status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
> +                                                   ACPI_ADR_SPACE_PLATFORM_RT,
> +                                                   &acpi_platformrt_space_handler,
> +                                                   NULL, NULL);
> +       if (ACPI_FAILURE(status))
> +               pr_alert("PRM: OperationRegion handler could not be installed\n");
> +}
> diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c
> index e48690a006a4..69a03d70740c 100644
> --- a/drivers/acpi/tables.c
> +++ b/drivers/acpi/tables.c
> @@ -39,6 +39,7 @@ static int acpi_apic_instance __initdata;
>  enum acpi_subtable_type {
>         ACPI_SUBTABLE_COMMON,
>         ACPI_SUBTABLE_HMAT,
> +       ACPI_SUBTABLE_PRMT,
>  };
>
>  struct acpi_subtable_entry {
> @@ -222,6 +223,8 @@ acpi_get_entry_type(struct acpi_subtable_entry *entry)
>                 return entry->hdr->common.type;
>         case ACPI_SUBTABLE_HMAT:
>                 return entry->hdr->hmat.type;
> +       case ACPI_SUBTABLE_PRMT:
> +               return 0;
>         }
>         return 0;
>  }
> @@ -234,6 +237,8 @@ acpi_get_entry_length(struct acpi_subtable_entry *entry)
>                 return entry->hdr->common.length;
>         case ACPI_SUBTABLE_HMAT:
>                 return entry->hdr->hmat.length;
> +       case ACPI_SUBTABLE_PRMT:
> +               return entry->hdr->prmt.length;
>         }
>         return 0;
>  }
> @@ -246,6 +251,8 @@ acpi_get_subtable_header_length(struct acpi_subtable_entry *entry)
>                 return sizeof(entry->hdr->common);
>         case ACPI_SUBTABLE_HMAT:
>                 return sizeof(entry->hdr->hmat);
> +       case ACPI_SUBTABLE_PRMT:
> +               return sizeof(entry->hdr->prmt);
>         }
>         return 0;
>  }
> @@ -255,6 +262,8 @@ acpi_get_subtable_type(char *id)
>  {
>         if (strncmp(id, ACPI_SIG_HMAT, 4) == 0)
>                 return ACPI_SUBTABLE_HMAT;
> +       if (strncmp(id, ACPI_SIG_PRMT, 4) == 0)
> +               return ACPI_SUBTABLE_PRMT;
>         return ACPI_SUBTABLE_COMMON;
>  }
>
> diff --git a/include/linux/acpi.h b/include/linux/acpi.h
> index 9f432411e988..6868b342cdc3 100644
> --- a/include/linux/acpi.h
> +++ b/include/linux/acpi.h
> @@ -132,6 +132,7 @@ enum acpi_address_range_id {
>  union acpi_subtable_headers {
>         struct acpi_subtable_header common;
>         struct acpi_hmat_structure hmat;
> +       struct acpi_prmt_module_header prmt;
>  };
>
>  typedef int (*acpi_tbl_table_handler)(struct acpi_table_header *table);
> diff --git a/include/linux/prmt.h b/include/linux/prmt.h
> new file mode 100644
> index 000000000000..24da8364b919
> --- /dev/null
> +++ b/include/linux/prmt.h
> @@ -0,0 +1,7 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +
> +#ifdef CONFIG_ACPI_PRMT
> +void init_prmt(void);
> +#else
> +static inline void init_prmt(void) { }
> +#endif
> --
> 2.29.2
>

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2021-06-09 14:57 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2021-03-22 22:37 [PATCH v3 0/4] Implement Platform Runtime Mechanism plumbing Erik Kaneda
2021-03-22 22:37 ` [PATCH v3 1/4] ACPICA: iASL: add disassembler support for PRMT Erik Kaneda
2021-03-22 22:37 ` [PATCH v3 2/4] ACPICA: Add support for PlatformRtMechanism OperationRegion handler Erik Kaneda
2021-03-22 22:37 ` [PATCH v3 3/4] PRM: implement OperationRegion handler for the PlatformRtMechanism subtype Erik Kaneda
2021-06-09 14:56   ` Rafael J. Wysocki
2021-03-22 22:37 ` [PATCH v3 4/4] ACPI: Add \_SB._OSC bit for PRM Erik Kaneda

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).