From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id EE766CA5FB3 for ; Thu, 1 Oct 2026 11:08:08 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help :List-Post:List-Archive:List-Unsubscribe:List-Id:Content-Transfer-Encoding: Content-Type:MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:Cc: To:From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=jXIXwmmoKqSLt5Cci1IiJy9vld5xZla7NXSHpWPX9sA=; b=M8z4PSjHPlVayvZsdY+wO6zS0K zXnFd7HPg+LIxLgJygZuTjGErwiP7djmE7CXvv5UNoYmlrRRi/qo9dcV2cwUAMXAIzSrTgdauXOq1 duTFz1vlwHceWQpDq68pUFCJ8hGwUnIDmKFm9LlOCnGXvkwq8vFSjbPVu3lZEd7v3QGK8AEp5e/uH UCUPUlSm3SsWkUMFcZlsF7wBXizeBiV3v95A86NuXwhxQawD2hdy7GQ7ML6vyizatXVTjB6jBT/bu 5rBucuxNmObVe2HEfy8SqjUx1QQeLOcPvROpk//We0evL/EyeF0c6d14DgQNwXwzomDH6OQHZHJ2M k3A5ndRQ==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.99.1 #2 (Red Hat Linux)) id 1xCEdu-00000008bLu-3DC3; Thu, 01 Oct 2026 11:08:02 +0000 Received: from foss.arm.com ([217.140.110.172]) by bombadil.infradead.org with esmtp (Exim 4.99.1 #2 (Red Hat Linux)) id 1xCEds-00000008bJR-1l6X for linux-arm-kernel@lists.infradead.org; Thu, 01 Oct 2026 11:08:01 +0000 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id BB75D497; Thu, 1 Oct 2026 04:07:52 -0700 (PDT) Received: from e142392.cambridge.arm.com (usa-sjc-imap-foss1.foss.arm.com [10.121.207.14]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 920643F86F; Thu, 1 Oct 2026 04:07:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1790852876; bh=j+uQKqTVanjvf4slAELPAurZMlnXXqOg6QOnGPE5FgI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Zow3GMjlhNUsMykAZZ1UomKuSGJjjQb9lvsCfoIsGuvylhDJ0bhKfYZRxSHtcN0yV QO+YpAHaCoXmOrMw0TNSzkMNTNKvjXY7BDq0GLV48VXoSKzayLbuwKY3yTGTdQ49vA gVc4hqS+SuK0e81WGEoXV/3a1nV8OHZjj57gPHEo= From: =?UTF-8?q?Kristina=20Mart=C5=A1enko?= To: linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, kvmarm@lists.linux.dev, linux-efi@vger.kernel.org Cc: Catalin Marinas , Will Deacon , Mark Rutland , Ryan Roberts , David Hildenbrand , Lorenzo Stoakes , Linu Cherian , Anshuman Khandual , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Marc Zyngier , Oliver Upton , Fuad Tabba , Joey Gouly , Steffen Eiden , Suzuki K Poulose , Zenghui Yu , Ard Biesheuvel , Ilias Apalodimas Subject: [RFC 06/12] ACPI: TLBI: Parse domains from table Date: Thu, 1 Oct 2026 12:06:49 +0100 Message-ID: <20261001110655.461473-7-kristina.martsenko@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20261001110655.461473-1-kristina.martsenko@arm.com> References: <20261001110655.461473-1-kristina.martsenko@arm.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.9.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20261001_040800_618833_DD3D5CCE X-CRM114-Status: GOOD ( 25.98 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org Parse the Arm TLBI ACPI table. The table contains a list of TLBI domains. Each domain is identified by a domain number (which can be passed to a TLBI instruction). Each domain contains a list of descendants, which are CPUs, SMMUs and subdomains. CPUs are identified by their ACPI Processor UID in GICC MADT, SMMUs by their Identifier in IORT, and subdomains by their offset in the TLBI table itself. This allows to describe a hierarchy of domains and the CPUs and SMMUs that are part of each domain. Parse the domains from the table. For each domain, build a cpumask of the CPUs that are part of it. Only add CPUs that have not been disabled. Parse the domains recursively. Use an arbitrary depth limit of 10 to prevent infinite recursion in case of firmware bugs. For simplicity each subdomain hierarchy may be parsed multiple times. Validate that domain 0 (the fallback broadcast-all domain) always exists and contains all CPUs. Also check that domain IDs fit into the architectural limit of 16 bits and what the CPUs support in TLBIDIDR. Sanity check the table itself (i.e. table sizes), as usually done in ACPI parsing. The list of domains and their CPUs will be used in the next patch to set up domain management in arm64 code. Place common structures in the arm64 ACPI header. The ARM64_TLBID kconfig symbol will be used later to detect compiler support and disable all TLBID code. Assisted-by: LLM Signed-off-by: Kristina Martšenko --- arch/arm64/Kconfig | 5 + arch/arm64/include/asm/acpi.h | 9 ++ drivers/acpi/arm64/Kconfig | 3 + drivers/acpi/arm64/Makefile | 1 + drivers/acpi/arm64/init.c | 2 + drivers/acpi/arm64/init.h | 1 + drivers/acpi/arm64/tlbi.c | 188 ++++++++++++++++++++++++++++++++++ 7 files changed, 209 insertions(+) create mode 100644 drivers/acpi/arm64/tlbi.c diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index b5a51b0ef944..46701449fef2 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -2329,6 +2329,11 @@ config ARM64_LSUI endmenu # "ARMv9.6 architectural feature" +config ARM64_TLBID + def_bool y + depends on ACPI + select ACPI_TLBI + config ARM64_SVE bool "ARM Scalable Vector Extension support" default y diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h index 8a54ca6ba602..fb0bddd3b7ef 100644 --- a/arch/arm64/include/asm/acpi.h +++ b/arch/arm64/include/asm/acpi.h @@ -10,6 +10,7 @@ #define _ASM_ACPI_H #include +#include #include #include #include @@ -171,6 +172,14 @@ static inline int acpi_numa_get_nid(unsigned int cpu) { return NUMA_NO_NODE; } static inline void acpi_map_cpus_to_nodes(void) { } #endif /* CONFIG_ACPI_NUMA */ +#ifdef CONFIG_ARM64_TLBID +#define MAX_TLBI_DOMAINS (1UL << 16) +struct tlbi_domain { + u16 id; + cpumask_var_t cpus; +}; +#endif + #define ACPI_TABLE_UPGRADE_MAX_PHYS MEMBLOCK_ALLOC_ACCESSIBLE #endif /*_ASM_ACPI_H*/ diff --git a/drivers/acpi/arm64/Kconfig b/drivers/acpi/arm64/Kconfig index f2fd79f22e7d..d7ba9add438e 100644 --- a/drivers/acpi/arm64/Kconfig +++ b/drivers/acpi/arm64/Kconfig @@ -24,3 +24,6 @@ config ACPI_APMT config ACPI_MPAM bool + +config ACPI_TLBI + bool diff --git a/drivers/acpi/arm64/Makefile b/drivers/acpi/arm64/Makefile index 9390b57cb564..9cee81ce834b 100644 --- a/drivers/acpi/arm64/Makefile +++ b/drivers/acpi/arm64/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_ACPI_FFH) += ffh.o obj-$(CONFIG_ACPI_GTDT) += gtdt.o obj-$(CONFIG_ACPI_IORT) += iort.o obj-$(CONFIG_ACPI_MPAM) += mpam.o +obj-$(CONFIG_ACPI_TLBI) += tlbi.o obj-$(CONFIG_ACPI_PROCESSOR_IDLE) += cpuidle.o obj-$(CONFIG_ARM_AMBA) += amba.o obj-y += dma.o init.o diff --git a/drivers/acpi/arm64/init.c b/drivers/acpi/arm64/init.c index 7a47d8095a7d..3affc7abf156 100644 --- a/drivers/acpi/arm64/init.c +++ b/drivers/acpi/arm64/init.c @@ -10,6 +10,8 @@ void __init acpi_arch_init(void) acpi_apmt_init(); if (IS_ENABLED(CONFIG_ACPI_IORT)) acpi_iort_init(); + if (IS_ENABLED(CONFIG_ACPI_TLBI)) + acpi_tlbi_init(); if (IS_ENABLED(CONFIG_ARM_AMBA)) acpi_amba_init(); } diff --git a/drivers/acpi/arm64/init.h b/drivers/acpi/arm64/init.h index dcc277977194..c036e2622a2f 100644 --- a/drivers/acpi/arm64/init.h +++ b/drivers/acpi/arm64/init.h @@ -5,3 +5,4 @@ void __init acpi_agdi_init(void); void __init acpi_apmt_init(void); void __init acpi_iort_init(void); void __init acpi_amba_init(void); +void __init acpi_tlbi_init(void); diff --git a/drivers/acpi/arm64/tlbi.c b/drivers/acpi/arm64/tlbi.c new file mode 100644 index 000000000000..b705a5b19842 --- /dev/null +++ b/drivers/acpi/arm64/tlbi.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Arm TLBI Domain Table (TLBI) support + * + * Copyright (C) 2026 Arm Ltd. + * Author: Kristina Martsenko + */ + +#define pr_fmt(fmt) "ACPI: TLBI: " fmt + +#include +#include +#include +#include + +#include "init.h" + +#define MAX_HIERARCHY_DEPTH 10 + +struct acpi_table_tlbi *tlbi; +static struct tlbi_domain *domains; +static int domain_count; + +static int __init validate_tlbi_domains(void) +{ + u64 tlbididr = read_sanitised_ftr_reg(SYS_TLBIDIDR_EL1); + int domain_bits = FIELD_GET(TLBIDIDR_EL1_NIS, tlbididr); + int domain0_idx = -1; + int cpu; + + if (domain_bits > 16) + return -EINVAL; + + for (int i = 0; i < domain_count; i++) { + u16 max_domain_id = (1 << domain_bits) - 1; + + if (domains[i].id > max_domain_id && + !cpumask_empty(domains[i].cpus)) + return -EINVAL; + + if (!domains[i].id) + domain0_idx = i; + } + + if (domain0_idx == -1) + return -EINVAL; + + for_each_possible_cpu(cpu) { + if (!cpumask_test_cpu(cpu, domains[domain0_idx].cpus)) + return -EINVAL; + } + + return 0; +} + +static struct acpi_tlbi_descendant *__init tlbi_get_descendant(u32 offset) +{ + struct acpi_tlbi_descendant *descendant; + + descendant = ACPI_ADD_PTR(struct acpi_tlbi_descendant, tlbi, offset); + if (descendant->length < sizeof(*descendant) || + offset + descendant->length > tlbi->header.length) + return NULL; + + return descendant; +} + +static struct acpi_tlbi_domain *__init tlbi_get_domain(u32 offset) +{ + struct acpi_tlbi_domain *domain; + + domain = ACPI_ADD_PTR(struct acpi_tlbi_domain, tlbi, offset); + if (domain->length < sizeof(*domain) || + offset + domain->length > tlbi->header.length) + return NULL; + + return domain; +} + +static int __init parse_tlbi_domain(struct acpi_tlbi_domain *domain, + struct cpumask *cpus, int depth) +{ + u32 offset = domain->descendants_offset; + + if (depth > MAX_HIERARCHY_DEPTH) + return -EINVAL; + + for (int i = 0; i < domain->descendant_count; i++) { + struct acpi_tlbi_descendant *desc = tlbi_get_descendant(offset); + + if (!desc) + return -EINVAL; + + switch (desc->type) { + case ACPI_TLBI_DESCENDANT_DOMAIN: { + struct acpi_tlbi_domain *child; + int ret; + + child = tlbi_get_domain(desc->identifier); + if (!child) + return -EINVAL; + + ret = parse_tlbi_domain(child, cpus, depth + 1); + if (ret) + return ret; + break; + } + case ACPI_TLBI_DESCENDANT_CPU: { + int cpu = get_cpu_for_acpi_id(desc->identifier); + + if (cpu >= 0 && cpu_possible(cpu)) + cpumask_set_cpu(cpu, cpus); + break; + } + case ACPI_TLBI_DESCENDANT_SMMU: + break; + default: + return -EINVAL; + } + + offset += desc->length; + } + + return 0; +} + +static int __init parse_tlbi_table(void) +{ + struct acpi_table_header *table __free(acpi_put_table) = + acpi_get_table_pointer(ACPI_SIG_TLBI, 0); + u32 offset; + int ret; + + if (IS_ERR(table)) + return PTR_ERR(table); + + tlbi = (struct acpi_table_tlbi *)table; + if (tlbi->header.length < sizeof(*tlbi) || + tlbi->domain_count > MAX_TLBI_DOMAINS) + return -EINVAL; + if (!tlbi->domain_count) + return -ENOENT; + + domain_count = tlbi->domain_count; + domains = kcalloc(domain_count, sizeof(*domains), GFP_KERNEL); + if (!domains) + return -ENOMEM; + + offset = tlbi->domains_offset; + + for (int i = 0; i < domain_count; i++) { + struct acpi_tlbi_domain *domain = tlbi_get_domain(offset); + + if (!domain || domain->id > U16_MAX) + return -EINVAL; + + domains[i].id = domain->id; + if (!zalloc_cpumask_var(&domains[i].cpus, GFP_KERNEL)) + return -ENOMEM; + + ret = parse_tlbi_domain(domain, domains[i].cpus, 0); + if (ret) + return ret; + + offset += domain->length; + } + + ret = validate_tlbi_domains(); + if (ret) + return ret; + + return 0; +} + +void __init acpi_tlbi_init(void) +{ + int ret; + + if (!cpus_have_final_cap(ARM64_HAS_TLBID)) + return; + + ret = parse_tlbi_table(); + if (ret) { + if (ret != -ENOENT) + pr_err("Failed to parse ACPI table\n"); + return; + } +} -- 2.43.0