lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20251222094351.38792-9-tianruidong@linux.alibaba.com>
Date: Mon, 22 Dec 2025 17:43:41 +0800
From: Ruidong Tian <tianruidong@...ux.alibaba.com>
To: catalin.marinas@....com,
	will@...nel.org,
	lpieralisi@...nel.org,
	guohanjun@...wei.com,
	sudeep.holla@....com,
	xueshuai@...ux.alibaba.com,
	linux-kernel@...r.kernel.org,
	linux-acpi@...r.kernel.org,
	linux-arm-kernel@...ts.infradead.org,
	rafael@...nel.org,
	lenb@...nel.org,
	tony.luck@...el.com,
	bp@...en8.de,
	yazen.ghannam@....com,
	misono.tomohiro@...itsu.com
Cc: tianruidong@...ux.alibaba.com
Subject: [PATCH v4 08/17] ras: AEST: Enable and register IRQs

The interrupt numbers for certain error records may be explicitly
programmed into their configuration register.

And for PPIs, each core will maintains its own copy of the aest_device
structure.

Given that handling RAS errors entails complex processes such as EDAC
and memory_failure, all handling is deferred to and handled within a
bottom-half context.

Signed-off-by: Ruidong Tian <tianruidong@...ux.alibaba.com>
---
 arch/arm64/include/asm/ras.h |  36 +++
 drivers/ras/aest/aest-core.c | 531 ++++++++++++++++++++++++++++++++++-
 drivers/ras/aest/aest.h      |  56 ++++
 include/linux/acpi_aest.h    |   7 +
 include/linux/ras.h          |   8 +
 5 files changed, 637 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/include/asm/ras.h b/arch/arm64/include/asm/ras.h
index 6c51d27520c0..02cf15278d9f 100644
--- a/arch/arm64/include/asm/ras.h
+++ b/arch/arm64/include/asm/ras.h
@@ -2,6 +2,7 @@
 #ifndef __ASM_RAS_H
 #define __ASM_RAS_H
 
+#include <linux/bits.h>
 #include <linux/types.h>
 
 /* ERR<n>FR */
@@ -37,6 +38,41 @@
 #define ERR_MISC0_16B_OFR BIT(47)
 #define ERR_MISC0_16B_CECR GENMASK_ULL(46, 32)
 
+/* ERR<n>STATUS */
+#define ERR_STATUS_AV BIT(31)
+#define ERR_STATUS_V BIT(30)
+#define ERR_STATUS_UE BIT(29)
+#define ERR_STATUS_ER BIT(28)
+#define ERR_STATUS_OF BIT(27)
+#define ERR_STATUS_MV BIT(26)
+#define ERR_STATUS_CE (BIT(25) | BIT(24))
+#define ERR_STATUS_DE BIT(23)
+#define ERR_STATUS_PN BIT(22)
+#define ERR_STATUS_UET (BIT(21) | BIT(20))
+#define ERR_STATUS_CI BIT(19)
+#define ERR_STATUS_IERR GENMASK_ULL(15, 8)
+#define ERR_STATUS_SERR GENMASK_ULL(7, 0)
+
+/* Theses bits are	 write-one-to-clear */
+#define ERR_STATUS_W1TC                                                  \
+	(ERR_STATUS_AV | ERR_STATUS_V | ERR_STATUS_UE | ERR_STATUS_ER |  \
+	 ERR_STATUS_OF | ERR_STATUS_MV | ERR_STATUS_CE | ERR_STATUS_DE | \
+	 ERR_STATUS_PN | ERR_STATUS_UET | ERR_STATUS_CI)
+
+#define ERR_STATUS_UET_UC 0
+#define ERR_STATUS_UET_UEU 1
+#define ERR_STATUS_UET_UEO 2
+#define ERR_STATUS_UET_UER 3
+
+/* ERR<n>ADDR */
+#define ERR_ADDR_AI BIT(61)
+#define ERR_ADDR_PADDR GENMASK_ULL(55, 0)
+
+/* ERR<n>CTLR */
+#define ERR_CTLR_CFI BIT(8)
+#define ERR_CTLR_FI BIT(3)
+#define ERR_CTLR_UI BIT(2)
+
 /* ERRDEVARCH */
 #define ERRDEVARCH_REV GENMASK(19, 16)
 
diff --git a/drivers/ras/aest/aest-core.c b/drivers/ras/aest/aest-core.c
index 5cfe91a6d72a..5ec0ba38f51b 100644
--- a/drivers/ras/aest/aest-core.c
+++ b/drivers/ras/aest/aest-core.c
@@ -5,8 +5,11 @@
  * Copyright (c) 2025, Alibaba Group.
  */
 
+#include <linux/interrupt.h>
+#include <linux/panic.h>
 #include <linux/platform_device.h>
 #include <linux/xarray.h>
+#include <linux/genalloc.h>
 #include <linux/ras.h>
 
 #include "aest.h"
@@ -16,6 +19,439 @@ DEFINE_PER_CPU(struct aest_device, percpu_adev);
 #undef pr_fmt
 #define pr_fmt(fmt) "AEST: " fmt
 
+/*
+ * This memory pool is only to be used to save AEST node in AEST irq context.
+ * There can be 500 AEST node at most.
+ */
+#define AEST_NODE_ALLOCED_MAX 500
+
+#define AEST_LOG_PREFIX_BUFFER 64
+
+BLOCKING_NOTIFIER_HEAD(aest_decoder_chain);
+
+static void aest_print(struct aest_event *event)
+{
+	static atomic_t seqno = { 0 };
+	unsigned int curr_seqno;
+	char pfx_seq[AEST_LOG_PREFIX_BUFFER];
+	int index;
+	struct ras_ext_regs *regs;
+
+	curr_seqno = atomic_inc_return(&seqno);
+	snprintf(pfx_seq, sizeof(pfx_seq), "{%u}" HW_ERR, curr_seqno);
+	pr_info("%sHardware error from AEST %s\n", pfx_seq, event->node_name);
+
+	switch (event->type) {
+	case ACPI_AEST_PROCESSOR_ERROR_NODE:
+		pr_err("%s Error from CPU%d\n", pfx_seq, event->id0);
+		break;
+	case ACPI_AEST_MEMORY_ERROR_NODE:
+		pr_err("%s Error from memory at SRAT proximity domain %#x\n",
+		       pfx_seq, event->id0);
+		break;
+	case ACPI_AEST_SMMU_ERROR_NODE:
+		pr_err("%s Error from SMMU IORT node %#x subcomponent %#x\n",
+		       pfx_seq, event->id0, event->id1);
+		break;
+	case ACPI_AEST_VENDOR_ERROR_NODE:
+		pr_err("%s Error from vendor hid %8.8s uid %#x\n", pfx_seq,
+		       event->hid, event->id1);
+		break;
+	case ACPI_AEST_GIC_ERROR_NODE:
+		pr_err("%s Error from GIC type %#x instance %#x\n", pfx_seq,
+		       event->id0, event->id1);
+		break;
+	default:
+		pr_err("%s Unknown AEST node type\n", pfx_seq);
+		return;
+	}
+
+	index = event->index;
+	regs = &event->regs;
+
+	pr_err("%s  ERR%dFR: 0x%llx\n", pfx_seq, index, regs->err_fr);
+	pr_err("%s  ERR%dCTRL: 0x%llx\n", pfx_seq, index, regs->err_ctlr);
+	pr_err("%s  ERR%dSTATUS: 0x%llx\n", pfx_seq, index, regs->err_status);
+	if (regs->err_status & ERR_STATUS_AV)
+		pr_err("%s  ERR%dADDR: 0x%llx\n", pfx_seq, index,
+		       regs->err_addr);
+
+	if (regs->err_status & ERR_STATUS_MV) {
+		pr_err("%s  ERR%dMISC0: 0x%llx\n", pfx_seq, index,
+		       regs->err_misc[0]);
+		pr_err("%s  ERR%dMISC1: 0x%llx\n", pfx_seq, index,
+		       regs->err_misc[1]);
+		pr_err("%s  ERR%dMISC2: 0x%llx\n", pfx_seq, index,
+		       regs->err_misc[2]);
+		pr_err("%s  ERR%dMISC3: 0x%llx\n", pfx_seq, index,
+		       regs->err_misc[3]);
+	}
+}
+
+static void aest_handle_memory_failure(u64 addr)
+{
+	unsigned long pfn;
+
+	pfn = PHYS_PFN(addr);
+
+	if (!pfn_valid(pfn)) {
+		pr_warn(HW_ERR "Invalid physical address: %#llx\n", addr);
+		return;
+	}
+
+#ifdef CONFIG_MEMORY_FAILURE
+	memory_failure(pfn, 0);
+#endif
+}
+
+static void init_aest_event(struct aest_event *event,
+			    struct aest_record *record,
+			    struct ras_ext_regs *regs)
+{
+	struct aest_node *node = record->node;
+	struct acpi_aest_node *info = node->info;
+
+	event->type = node->type;
+	event->node_name = node->name;
+	switch (node->type) {
+	case ACPI_AEST_PROCESSOR_ERROR_NODE:
+		if (info->processor->flags &
+		    (ACPI_AEST_PROC_FLAG_SHARED | ACPI_AEST_PROC_FLAG_GLOBAL))
+			event->id0 = smp_processor_id();
+		else
+			event->id0 = get_cpu_for_acpi_id(
+				info->processor->processor_id);
+
+		event->id1 = info->processor->resource_type;
+		break;
+	case ACPI_AEST_MEMORY_ERROR_NODE:
+		event->id0 = info->memory->srat_proximity_domain;
+		break;
+	case ACPI_AEST_SMMU_ERROR_NODE:
+		event->id0 = info->smmu->iort_node_reference;
+		event->id1 = info->smmu->subcomponent_reference;
+		break;
+	case ACPI_AEST_VENDOR_ERROR_NODE:
+		event->id0 = 0;
+		event->id1 = info->vendor->acpi_uid;
+		event->hid = info->vendor->acpi_hid;
+		break;
+	case ACPI_AEST_GIC_ERROR_NODE:
+		event->id0 = info->gic->interface_type;
+		event->id1 = info->gic->instance_id;
+		break;
+	default:
+		event->id0 = 0;
+		event->id1 = 0;
+	}
+
+	memcpy(&event->regs, regs, sizeof(*regs));
+	event->index = record->index;
+	event->addressing_mode = record->addressing_mode;
+}
+
+static int aest_node_gen_pool_add(struct aest_device *adev,
+				  struct aest_record *record,
+				  struct ras_ext_regs *regs)
+{
+	struct aest_event *event;
+
+	if (!adev->pool)
+		return -EINVAL;
+
+	event = (void *)gen_pool_alloc(adev->pool, sizeof(*event));
+	if (!event)
+		return -ENOMEM;
+
+	init_aest_event(event, record, regs);
+	llist_add(&event->llnode, &adev->event_list);
+
+	return 0;
+}
+
+static void aest_log(struct aest_record *record, struct ras_ext_regs *regs)
+{
+	struct aest_device *adev = record->node->adev;
+
+	if (!aest_node_gen_pool_add(adev, record, regs))
+		schedule_work(&adev->aest_work);
+}
+
+void aest_register_decode_chain(struct notifier_block *nb)
+{
+	blocking_notifier_chain_register(&aest_decoder_chain, nb);
+}
+EXPORT_SYMBOL_GPL(aest_register_decode_chain);
+
+void aest_unregister_decode_chain(struct notifier_block *nb)
+{
+	blocking_notifier_chain_unregister(&aest_decoder_chain, nb);
+}
+EXPORT_SYMBOL_GPL(aest_unregister_decode_chain);
+
+static void aest_node_pool_process(struct work_struct *work)
+{
+	struct llist_node *head;
+	struct aest_event *event;
+	struct aest_device *adev =
+		container_of(work, struct aest_device, aest_work);
+	u64 status, addr;
+
+	head = llist_del_all(&adev->event_list);
+	if (!head)
+		return;
+
+	head = llist_reverse_order(head);
+	llist_for_each_entry(event, head, llnode) {
+		aest_print(event);
+
+		status = event->regs.err_status;
+		if (!(event->regs.err_addr & ERR_ADDR_AI) &&
+		    (status & (ERR_STATUS_UE | ERR_STATUS_DE))) {
+			if (event->addressing_mode == AEST_ADDREESS_SPA)
+				addr = event->regs.err_addr & PHYS_MASK;
+			aest_handle_memory_failure(addr);
+		}
+
+		blocking_notifier_call_chain(&aest_decoder_chain, 0, event);
+		gen_pool_free(adev->pool, (unsigned long)event, sizeof(*event));
+	}
+}
+
+static int aest_node_pool_init(struct aest_device *adev)
+{
+	unsigned long addr, size;
+
+	size = ilog2(sizeof(struct aest_event));
+	adev->pool =
+		devm_gen_pool_create(adev->dev, size, -1, dev_name(adev->dev));
+	if (!adev->pool)
+		return -ENOMEM;
+
+	size = PAGE_ALIGN(size * AEST_NODE_ALLOCED_MAX);
+	addr = (unsigned long)devm_kzalloc(adev->dev, size, GFP_KERNEL);
+	if (!addr)
+		return -ENOMEM;
+
+	return gen_pool_add(adev->pool, addr, size, -1);
+}
+
+static void aest_panic(struct aest_record *record, struct ras_ext_regs *regs,
+		       char *msg)
+{
+	struct aest_event event = { 0 };
+
+	init_aest_event(&event, record, regs);
+
+	aest_print(&event);
+
+	panic(msg);
+}
+
+static void aest_proc_record(struct aest_record *record, void *data)
+{
+	struct ras_ext_regs regs = { 0 };
+	int *count = data;
+	u64 ue;
+
+	regs.err_status = record_read(record, ERXSTATUS);
+	if (!(regs.err_status & ERR_STATUS_V))
+		return;
+
+	(*count)++;
+
+	if (regs.err_status & ERR_STATUS_AV)
+		regs.err_addr = record_read(record, ERXADDR);
+
+	regs.err_fr = record_read(record, ERXFR);
+	regs.err_ctlr = record_read(record, ERXCTLR);
+
+	if (regs.err_status & ERR_STATUS_MV) {
+		regs.err_misc[0] = record_read(record, ERXMISC0);
+		regs.err_misc[1] = record_read(record, ERXMISC1);
+		if (record->node->version >= ID_AA64PFR0_EL1_RAS_V1P1) {
+			regs.err_misc[2] = record_read(record, ERXMISC2);
+			regs.err_misc[3] = record_read(record, ERXMISC3);
+		}
+
+		if (record->node->info->interface_hdr->flags &
+		    AEST_XFACE_FLAG_CLEAR_MISC) {
+			record_write(record, ERXMISC0, 0);
+			record_write(record, ERXMISC1, 0);
+			if (record->node->version >= ID_AA64PFR0_EL1_RAS_V1P1) {
+				record_write(record, ERXMISC2, 0);
+				record_write(record, ERXMISC3, 0);
+			}
+			/* ce count is 0 if record do not support ce */
+		} else if (record->ce.count > 0)
+			record_write(record, ERXMISC0, record->ce.reg_val);
+	}
+
+	/* panic if unrecoverable and uncontainable error encountered */
+	ue = FIELD_GET(ERR_STATUS_UET, regs.err_status);
+	if ((regs.err_status & ERR_STATUS_UE) &&
+	    (ue == ERR_STATUS_UET_UC || ue == ERR_STATUS_UET_UEU))
+		aest_panic(record, &regs,
+			   "AEST: unrecoverable error encountered");
+
+	aest_log(record, &regs);
+
+	/* Write-one-to-clear the bits we've seen */
+	regs.err_status &= ERR_STATUS_W1TC;
+
+	/* Multi bit filed need to write all-ones to clear. */
+	if (regs.err_status & ERR_STATUS_CE)
+		regs.err_status |= ERR_STATUS_CE;
+
+	/* Multi bit filed need to write all-ones to clear. */
+	if (regs.err_status & ERR_STATUS_UET)
+		regs.err_status |= ERR_STATUS_UET;
+
+	record_write(record, ERXSTATUS, regs.err_status);
+}
+
+static void aest_node_foreach_record(void (*func)(struct aest_record *, void *),
+				     struct aest_node *node, void *data,
+				     unsigned long *bitmap)
+{
+	int i;
+
+	for_each_clear_bit(i, bitmap, node->record_count) {
+		aest_select_record(node, i);
+
+		func(&node->records[i], data);
+
+		aest_sync(node);
+	}
+}
+
+static int aest_proc(struct aest_node *node)
+{
+	int count = 0, i, j, size = node->record_count;
+	u64 err_group = 0;
+
+	aest_node_dbg(node, "Poll bitmap %*pb\n", size,
+		      node->record_implemented);
+	aest_node_foreach_record(aest_proc_record, node, &count,
+				 node->record_implemented);
+
+	if (!node->errgsr)
+		return count;
+
+	aest_node_dbg(node, "Report bitmap %*pb\n", size,
+		      node->status_reporting);
+	for (i = 0; i < BITS_TO_U64(size); i++) {
+		err_group = readq_relaxed((void *)node->errgsr + i * 8);
+		aest_node_dbg(node, "errgsr[%d]: 0x%llx\n", i, err_group);
+
+		for_each_set_bit(j, (unsigned long *)&err_group,
+				 BITS_PER_LONG) {
+			/*
+			 * Error group base is only valid in Memory Map node,
+			 * so driver do not need to write select register and
+			 * sync.
+			 */
+			if (test_bit(i * BITS_PER_LONG + j,
+				     node->status_reporting))
+				continue;
+			aest_proc_record(&node->records[j], &count);
+		}
+	}
+
+	return count;
+}
+
+static irqreturn_t aest_irq_func(int irq, void *input)
+{
+	struct aest_device *adev = input;
+	int i;
+
+	for (i = 0; i < adev->node_cnt; i++)
+		aest_proc(&adev->nodes[i]);
+
+	return IRQ_HANDLED;
+}
+
+static int aest_register_irq(struct aest_device *adev)
+{
+	int i, irq, ret;
+	char *irq_desc;
+
+	irq_desc = devm_kasprintf(adev->dev, GFP_KERNEL, "%s.%s.",
+				  dev_driver_string(adev->dev),
+				  dev_name(adev->dev));
+	if (!irq_desc)
+		return -ENOMEM;
+
+	for (i = 0; i < MAX_GSI_PER_NODE; i++) {
+		irq = adev->irq[i];
+
+		if (!irq)
+			continue;
+
+		if (irq_is_percpu_devid(irq)) {
+			ret = request_percpu_irq(irq, aest_irq_func, irq_desc,
+						 adev->adev_oncore);
+			if (ret)
+				goto free;
+		} else {
+			ret = devm_request_irq(adev->dev, irq, aest_irq_func, 0,
+					       irq_desc, adev);
+			if (ret)
+				return ret;
+		}
+	}
+	return 0;
+
+free:
+	for (; i >= 0; i--) {
+		irq = adev->irq[i];
+
+		if (irq_is_percpu_devid(irq))
+			free_percpu_irq(irq, adev->adev_oncore);
+	}
+
+	return ret;
+}
+
+static void aest_enable_irq(struct aest_record *record)
+{
+	u64 err_ctlr;
+	struct aest_device *adev = record->node->adev;
+
+	err_ctlr = record_read(record, ERXCTLR);
+
+	if (adev->irq[ACPI_AEST_NODE_FAULT_HANDLING])
+		err_ctlr |= (ERR_CTLR_FI | ERR_CTLR_CFI);
+	if (adev->irq[ACPI_AEST_NODE_ERROR_RECOVERY])
+		err_ctlr |= ERR_CTLR_UI;
+
+	record_write(record, ERXCTLR, err_ctlr);
+}
+
+static void aest_config_irq(struct aest_node *node)
+{
+	int i;
+	struct acpi_aest_node_interrupt_v2 *interrupt;
+
+	if (!node->irq_config)
+		return;
+
+	for (i = 0; i < node->info->interrupt_count; i++) {
+		interrupt = &node->info->interrupt[i];
+
+		if (interrupt->type == ACPI_AEST_NODE_FAULT_HANDLING)
+			writeq_relaxed(interrupt->gsiv, node->irq_config);
+
+		if (interrupt->type == ACPI_AEST_NODE_ERROR_RECOVERY)
+			writeq_relaxed(interrupt->gsiv, node->irq_config + 8);
+
+		aest_node_dbg(node, "config irq type %d gsiv %d at %llx",
+			      interrupt->type, interrupt->gsiv,
+			      (u64)node->irq_config);
+	}
+}
+
 static enum ras_ce_threshold aest_get_ce_threshold(struct aest_record *record)
 {
 	u64 err_fr, err_fr_cec, err_fr_rp = -1;
@@ -128,6 +564,7 @@ static int aest_init_record(struct aest_record *record, int i,
 	record->index = i;
 	record->node = node;
 	aest_set_ce_threshold(record);
+	aest_enable_irq(record);
 
 	aest_record_dbg(record, "base: %p, index: %d, address mode: %x\n",
 			record->regs_base, record->index,
@@ -232,6 +669,21 @@ static int aest_init_node(struct aest_device *adev, struct aest_node *node,
 		}
 	}
 
+	address = node->info->common->interrupt_config_register_base;
+	if ((flags & AEST_XFACE_FLAG_INT_CONFIG) && address) {
+		if (address - anode->interface_hdr->address < node->group->size)
+			node->irq_config =
+				node->base +
+				(address - anode->interface_hdr->address);
+		else {
+			node->irq_config =
+				devm_ioremap(adev->dev, address, PAGE_SIZE);
+			if (!node->irq_config)
+				return -ENOMEM;
+		}
+	}
+	aest_config_irq(node);
+
 	ret = aest_node_set_errgsr(adev, node);
 	if (ret)
 		return ret;
@@ -276,6 +728,66 @@ static int aest_init_nodes(struct aest_device *adev, struct aest_hnode *ahnode)
 	return 0;
 }
 
+static int __setup_ppi(struct aest_device *adev)
+{
+	int cpu, i;
+	struct aest_device *oncore_adev;
+	struct aest_node *oncore_node;
+	size_t size;
+
+	adev->adev_oncore = &percpu_adev;
+	for_each_possible_cpu(cpu) {
+		oncore_adev = per_cpu_ptr(&percpu_adev, cpu);
+		memcpy(oncore_adev, adev, sizeof(struct aest_device));
+
+		oncore_adev->nodes =
+			devm_kcalloc(adev->dev, oncore_adev->node_cnt,
+				     sizeof(struct aest_node), GFP_KERNEL);
+		if (!oncore_adev->nodes)
+			return -ENOMEM;
+
+		size = adev->node_cnt * sizeof(struct aest_node);
+		memcpy(oncore_adev->nodes, adev->nodes, size);
+		for (i = 0; i < oncore_adev->node_cnt; i++) {
+			oncore_node = &oncore_adev->nodes[i];
+			oncore_node->records = devm_kcalloc(
+				adev->dev, oncore_node->record_count,
+				sizeof(struct aest_record), GFP_KERNEL);
+			if (!oncore_node->records)
+				return -ENOMEM;
+
+			size = oncore_node->record_count *
+			       sizeof(struct aest_record);
+			memcpy(oncore_node->records, adev->nodes[i].records,
+			       size);
+		}
+
+		aest_dev_dbg(adev, "Init device on CPU%d.\n", cpu);
+	}
+
+	return 0;
+}
+
+static int aest_setup_irq(struct platform_device *pdev,
+			  struct aest_device *adev)
+{
+	int fhi_irq, eri_irq;
+
+	fhi_irq = platform_get_irq_byname_optional(pdev, AEST_FHI_NAME);
+	if (fhi_irq > 0)
+		adev->irq[0] = fhi_irq;
+
+	eri_irq = platform_get_irq_byname_optional(pdev, AEST_ERI_NAME);
+	if (eri_irq > 0)
+		adev->irq[1] = eri_irq;
+
+	/* Allocate and initialise the percpu device pointer for PPI */
+	if (irq_is_percpu(fhi_irq) || irq_is_percpu(eri_irq))
+		return __setup_ppi(adev);
+
+	return 0;
+}
+
 static int aest_device_probe(struct platform_device *pdev)
 {
 	int ret;
@@ -289,14 +801,31 @@ static int aest_device_probe(struct platform_device *pdev)
 	adev = devm_kzalloc(&pdev->dev, sizeof(*adev), GFP_KERNEL);
 	if (!adev)
 		return -ENOMEM;
-
 	adev->dev = &pdev->dev;
 	adev->id = pdev->id;
 	aest_set_name(adev, ahnode);
+
+	INIT_WORK(&adev->aest_work, aest_node_pool_process);
+	ret = aest_node_pool_init(adev);
+	if (ret) {
+		aest_dev_err(adev, "Failed init aest node pool.\n");
+		return ret;
+	}
+	init_llist_head(&adev->event_list);
+
 	ret = aest_init_nodes(adev, ahnode);
 	if (ret)
 		return ret;
 
+	ret = aest_setup_irq(pdev, adev);
+	if (ret)
+		return ret;
+
+	ret = aest_register_irq(adev);
+	if (ret) {
+		aest_dev_err(adev, "register irq failed\n");
+		return ret;
+	}
 	platform_set_drvdata(pdev, adev);
 
 	aest_dev_dbg(adev, "Node cnt: %x, id: %x\n", adev->node_cnt, adev->id);
diff --git a/drivers/ras/aest/aest.h b/drivers/ras/aest/aest.h
index 85eeed79bcbe..a5e43b2a2e90 100644
--- a/drivers/ras/aest/aest.h
+++ b/drivers/ras/aest/aest.h
@@ -67,6 +67,34 @@
 
 #define GIC_ERRDEVARCH 0xFFBC
 
+struct aest_event {
+	struct llist_node llnode;
+	char *node_name;
+	u32 type;
+	/*
+	 * Different nodes have different meanings:
+	 *   - Processor node	: processor number.
+	 *   - Memory node	: SRAT proximity domain.
+	 *   - SMMU node	: IORT proximity domain.
+	 *   - GIC node		: interface type.
+	 */
+	u32 id0;
+	/*
+	 * Different nodes have different meanings:
+	 *   - Processor node	: processor resource type.
+	 *   - Memory node	: Non.
+	 *   - SMMU node	: subcomponent reference.
+	 *   - Vendor node	: Unique ID.
+	 *   - GIC node		: instance identifier.
+	 */
+	u32 id1;
+	/* Vendor node	: hardware ID. */
+	char *hid;
+	u32 index;
+	int addressing_mode;
+	struct ras_ext_regs regs;
+};
+
 struct aest_access {
 	u64 (*read)(void *base, u32 offset);
 	void (*write)(void *base, u32 offset, u64 val);
@@ -141,6 +169,7 @@ struct aest_node {
 	void *errgsr;
 	void *base;
 	void *inj;
+	void *irq_config;
 
 	/*
 	 * This bitmap indicates which of the error records within this error
@@ -172,6 +201,7 @@ struct aest_node {
 
 	int record_count;
 	struct aest_record *records;
+	struct aest_node __percpu *oncore_node;
 };
 
 struct aest_device {
@@ -180,6 +210,12 @@ struct aest_device {
 	int node_cnt;
 	struct aest_node *nodes;
 	u32 id;
+	int irq[MAX_GSI_PER_NODE];
+
+	struct work_struct aest_work;
+	struct gen_pool *pool;
+	struct llist_head event_list;
+	struct aest_device __percpu *adev_oncore;
 };
 
 static const char *const aest_node_name[] = {
@@ -283,3 +319,23 @@ static const struct aest_access aest_access[] = {
 	},
 	{ }
 };
+
+/*
+ * Each PE may has multi error record, you must selects an error
+ * record to be accessed through the Error Record System
+ * registers.
+ */
+static inline void aest_select_record(struct aest_node *node, int index)
+{
+	if (node->type == ACPI_AEST_PROCESSOR_ERROR_NODE) {
+		write_sysreg_s(index, SYS_ERRSELR_EL1);
+		isb();
+	}
+}
+
+/* Ensure all writes has taken effect. */
+static inline void aest_sync(struct aest_node *node)
+{
+	if (node->type == ACPI_AEST_PROCESSOR_ERROR_NODE)
+		isb();
+}
diff --git a/include/linux/acpi_aest.h b/include/linux/acpi_aest.h
index a7898c643896..3a899f57f92f 100644
--- a/include/linux/acpi_aest.h
+++ b/include/linux/acpi_aest.h
@@ -10,6 +10,13 @@
 #define AEST_FHI_NAME "AEST:FHI"
 #define AEST_ERI_NAME "AEST:ERI"
 
+/* AEST component */
+#define ACPI_AEST_PROC_FLAG_GLOBAL	(1<<0)
+#define ACPI_AEST_PROC_FLAG_SHARED	(1<<1)
+
+#define AEST_ADDREESS_SPA	0
+#define AEST_ADDREESS_LA	1
+
 /* AEST interrupt */
 #define AEST_INTERRUPT_MODE BIT(0)
 
diff --git a/include/linux/ras.h b/include/linux/ras.h
index 468941bfe855..05096f049dac 100644
--- a/include/linux/ras.h
+++ b/include/linux/ras.h
@@ -63,4 +63,12 @@ amd_convert_umc_mca_addr_to_sys_addr(struct atl_err *err) { return -EINVAL; }
 #define GET_LOGICAL_INDEX(mpidr) -EINVAL
 #endif /* CONFIG_ARM || CONFIG_ARM64 */
 
+#if IS_ENABLED(CONFIG_AEST)
+void aest_register_decode_chain(struct notifier_block *nb);
+void aest_unregister_decode_chain(struct notifier_block *nb);
+#else
+static inline void aest_register_decode_chain(struct notifier_block *nb) {}
+static inline void aest_unregister_decode_chain(struct notifier_block *nb) {}
+#endif /* CONFIG_AEST */
+
 #endif /* __RAS_H__ */
-- 
2.51.2.612.gdc70283dfc


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ