From nobody Thu Feb 12 00:24:55 2026 Received: from galois.linutronix.de (Galois.linutronix.de [193.142.43.55]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9D9B116A939; Tue, 18 Jun 2024 14:01:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=193.142.43.55 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718719310; cv=none; b=k82t7ZymFs5Bwjx+n0umqrzImV3QUdpEfShmMar718mQlxXuuAiIFXtaUgKQNApH1mV5F0z+UAyQJ0WKOJJP9nDfTG7G1Iafd4JMsveJuH7hUOqVlL7ipb4ZKN+bN3ZAzra9nbOT62XJ2gRIGdVJSjTxiVSTPkDFmVEvM42MShI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718719310; c=relaxed/simple; bh=joHn5dyU0Xk1I5UOydkfN2ISXUFABT3D8btMGbkUydU=; h=Date:From:To:Subject:Cc:In-Reply-To:References:MIME-Version: Message-ID:Content-Type; b=VKe4OqlsioVXB7nzJc9sll+2wpwSfmKrOFCY7sqPXO5CK8UMdyNiJQwalFLc6dIusMxSM2+NhxClP2X+PhEX1ORL+sBOUE1yj7hZNj8L98XQkRHDMIsOLmAdX5JVkohimRrGXfw7kwZpPQH2MMPLwIvgzL8xkYwnVCvKxuq1mPE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linutronix.de; spf=pass smtp.mailfrom=linutronix.de; dkim=pass (2048-bit key) header.d=linutronix.de header.i=@linutronix.de header.b=tl6IUbRc; dkim=permerror (0-bit key) header.d=linutronix.de header.i=@linutronix.de header.b=xe8VoVLK; arc=none smtp.client-ip=193.142.43.55 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linutronix.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linutronix.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linutronix.de header.i=@linutronix.de header.b="tl6IUbRc"; dkim=permerror (0-bit key) header.d=linutronix.de header.i=@linutronix.de header.b="xe8VoVLK" Date: Tue, 18 Jun 2024 14:01:38 -0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020; t=1718719298; h=from:from:sender:sender:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=HHkSd0nEVKUAQe91+u42GcJ1zyBA8e7raj2v1Ri4wGs=; b=tl6IUbRc+gh7ehZWaeXcHP+/YFLSr+OJYIGiGZmnG7EGdrJ3JbWcT5+ivbn4cnYp8TIA05 xYgZ5BrRvfFHT4m5bWN0nJ7B8TRQfkelB84au4WwOYChwm6dcVS8xEbzkAOCmlCuujWAT5 cgPCh3L6diCLEZmdhYC2e5CmElkRwWx04HGRcghFPvEw7fRPJWwgybuSEXBXfiAMoQFxsv qlueMXMeLkINY83J1lJugjqoeCjJMBElMatQkWvhXmcTuDqGC7ggYRGcK535LXmkSHRs3z FmF32H2bfl9plAyLI8uei+MHgb1IGHtCp0qxN1x5JsnFK0uQfe5tiYrBfbY62Q== DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020e; t=1718719298; h=from:from:sender:sender:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=HHkSd0nEVKUAQe91+u42GcJ1zyBA8e7raj2v1Ri4wGs=; b=xe8VoVLKhQJvdwkmbWNAnvX0VzIDEk7lkBCitAWzbTH+DVFm/7v5yU0mE2pIel+IjCI8bj HEOWhxyHLYfcFZAA== From: "tip-bot2 for Kirill A. Shutemov" Sender: tip-bot2@linutronix.de Reply-to: linux-kernel@vger.kernel.org To: linux-tip-commits@vger.kernel.org Subject: [tip: x86/cc] x86/acpi: Add support for CPU offlining for ACPI MADT wakeup method Cc: "Kirill A. Shutemov" , "Borislav Petkov (AMD)" , Kuppuswamy Sathyanarayanan , Thomas Gleixner , Kai Huang , "Rafael J. Wysocki" , Tao Liu , x86@kernel.org, linux-kernel@vger.kernel.org In-Reply-To: <13356251.uLZWGnKmhe@kreacher> References: <13356251.uLZWGnKmhe@kreacher> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-ID: <171871929835.10875.9378835130859177232.tip-bot2@tip-bot2> Robot-ID: Robot-Unsubscribe: Contact to get blacklisted from these emails Precedence: bulk Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable The following commit has been merged into the x86/cc branch of tip: Commit-ID: 1ceebe2e46720de02af4cf626dc847ecc4a263fd Gitweb: https://git.kernel.org/tip/1ceebe2e46720de02af4cf626dc847ecc= 4a263fd Author: Kirill A. Shutemov AuthorDate: Fri, 14 Jun 2024 12:59:03 +03:00 Committer: Borislav Petkov (AMD) CommitterDate: Mon, 17 Jun 2024 17:46:25 +02:00 x86/acpi: Add support for CPU offlining for ACPI MADT wakeup method MADT Multiprocessor Wakeup structure version 1 brings support for CPU offli= ning: BIOS provides a reset vector where the CPU has to jump to for offlining its= elf. The new TEST mailbox command can be used to test whether the CPU offlined i= tself which means the BIOS has control over the CPU and can online it again via t= he ACPI MADT wakeup method. Add CPU offlining support for the ACPI MADT wakeup method by implementing c= ustom cpu_die(), play_dead() and stop_this_cpu() SMP operations. CPU offlining makes it possible to hand over secondary CPUs over kexec, not limiting the second kernel to a single CPU. The change conforms to the approved ACPI spec change proposal. See the Link. Signed-off-by: Kirill A. Shutemov Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Kuppuswamy Sathyanarayanan Reviewed-by: Thomas Gleixner Acked-by: Kai Huang Acked-by: Rafael J. Wysocki Tested-by: Tao Liu Link: https://lore.kernel.org/all/13356251.uLZWGnKmhe@kreacher Link: https://lore.kernel.org/r/20240614095904.1345461-19-kirill.shutemov@l= inux.intel.com --- arch/x86/include/asm/acpi.h | 2 +- arch/x86/kernel/acpi/Makefile | 2 +- arch/x86/kernel/acpi/madt_playdead.S | 28 ++++- arch/x86/kernel/acpi/madt_wakeup.c | 184 +++++++++++++++++++++++++- include/acpi/actbl2.h | 15 +- 5 files changed, 227 insertions(+), 4 deletions(-) create mode 100644 arch/x86/kernel/acpi/madt_playdead.S diff --git a/arch/x86/include/asm/acpi.h b/arch/x86/include/asm/acpi.h index ceacac2..21bc53f 100644 --- a/arch/x86/include/asm/acpi.h +++ b/arch/x86/include/asm/acpi.h @@ -83,6 +83,8 @@ union acpi_subtable_headers; int __init acpi_parse_mp_wake(union acpi_subtable_headers *header, const unsigned long end); =20 +void asm_acpi_mp_play_dead(u64 reset_vector, u64 pgd_pa); + /* * Check if the CPU can handle C2 and deeper */ diff --git a/arch/x86/kernel/acpi/Makefile b/arch/x86/kernel/acpi/Makefile index 2feba72..842a5f4 100644 --- a/arch/x86/kernel/acpi/Makefile +++ b/arch/x86/kernel/acpi/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_ACPI) +=3D boot.o obj-$(CONFIG_ACPI_SLEEP) +=3D sleep.o wakeup_$(BITS).o obj-$(CONFIG_ACPI_APEI) +=3D apei.o obj-$(CONFIG_ACPI_CPPC_LIB) +=3D cppc.o -obj-$(CONFIG_ACPI_MADT_WAKEUP) +=3D madt_wakeup.o +obj-$(CONFIG_ACPI_MADT_WAKEUP) +=3D madt_wakeup.o madt_playdead.o =20 ifneq ($(CONFIG_ACPI_PROCESSOR),) obj-y +=3D cstate.o diff --git a/arch/x86/kernel/acpi/madt_playdead.S b/arch/x86/kernel/acpi/ma= dt_playdead.S new file mode 100644 index 0000000..4e498d2 --- /dev/null +++ b/arch/x86/kernel/acpi/madt_playdead.S @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include +#include +#include +#include + + .text + .align PAGE_SIZE + +/* + * asm_acpi_mp_play_dead() - Hand over control of the CPU to the BIOS + * + * rdi: Address of the ACPI MADT MPWK ResetVector + * rsi: PGD of the identity mapping + */ +SYM_FUNC_START(asm_acpi_mp_play_dead) + /* Turn off global entries. Following CR3 write will flush them. */ + movq %cr4, %rdx + andq $~(X86_CR4_PGE), %rdx + movq %rdx, %cr4 + + /* Switch to identity mapping */ + movq %rsi, %cr3 + + /* Jump to reset vector */ + ANNOTATE_RETPOLINE_SAFE + jmp *%rdi +SYM_FUNC_END(asm_acpi_mp_play_dead) diff --git a/arch/x86/kernel/acpi/madt_wakeup.c b/arch/x86/kernel/acpi/madt= _wakeup.c index 30820f9..6cfe762 100644 --- a/arch/x86/kernel/acpi/madt_wakeup.c +++ b/arch/x86/kernel/acpi/madt_wakeup.c @@ -1,10 +1,19 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include #include +#include #include +#include +#include +#include +#include #include #include +#include +#include +#include #include +#include =20 /* Physical address of the Multiprocessor Wakeup Structure mailbox */ static u64 acpi_mp_wake_mailbox_paddr __ro_after_init; @@ -12,6 +21,154 @@ static u64 acpi_mp_wake_mailbox_paddr __ro_after_init; /* Virtual address of the Multiprocessor Wakeup Structure mailbox */ static struct acpi_madt_multiproc_wakeup_mailbox *acpi_mp_wake_mailbox __r= o_after_init; =20 +static u64 acpi_mp_pgd __ro_after_init; +static u64 acpi_mp_reset_vector_paddr __ro_after_init; + +static void acpi_mp_stop_this_cpu(void) +{ + asm_acpi_mp_play_dead(acpi_mp_reset_vector_paddr, acpi_mp_pgd); +} + +static void acpi_mp_play_dead(void) +{ + play_dead_common(); + asm_acpi_mp_play_dead(acpi_mp_reset_vector_paddr, acpi_mp_pgd); +} + +static void acpi_mp_cpu_die(unsigned int cpu) +{ + u32 apicid =3D per_cpu(x86_cpu_to_apicid, cpu); + unsigned long timeout; + + /* + * Use TEST mailbox command to prove that BIOS got control over + * the CPU before declaring it dead. + * + * BIOS has to clear 'command' field of the mailbox. + */ + acpi_mp_wake_mailbox->apic_id =3D apicid; + smp_store_release(&acpi_mp_wake_mailbox->command, + ACPI_MP_WAKE_COMMAND_TEST); + + /* Don't wait longer than a second. */ + timeout =3D USEC_PER_SEC; + while (READ_ONCE(acpi_mp_wake_mailbox->command) && --timeout) + udelay(1); + + if (!timeout) + pr_err("Failed to hand over CPU %d to BIOS\n", cpu); +} + +/* The argument is required to match type of x86_mapping_info::alloc_pgt_p= age */ +static void __init *alloc_pgt_page(void *dummy) +{ + return memblock_alloc(PAGE_SIZE, PAGE_SIZE); +} + +static void __init free_pgt_page(void *pgt, void *dummy) +{ + return memblock_free(pgt, PAGE_SIZE); +} + +/* + * Make sure asm_acpi_mp_play_dead() is present in the identity mapping at + * the same place as in the kernel page tables. asm_acpi_mp_play_dead() sw= itches + * to the identity mapping and the function has be present at the same spo= t in + * the virtual address space before and after switching page tables. + */ +static int __init init_transition_pgtable(pgd_t *pgd) +{ + pgprot_t prot =3D PAGE_KERNEL_EXEC_NOENC; + unsigned long vaddr, paddr; + p4d_t *p4d; + pud_t *pud; + pmd_t *pmd; + pte_t *pte; + + vaddr =3D (unsigned long)asm_acpi_mp_play_dead; + pgd +=3D pgd_index(vaddr); + if (!pgd_present(*pgd)) { + p4d =3D (p4d_t *)alloc_pgt_page(NULL); + if (!p4d) + return -ENOMEM; + set_pgd(pgd, __pgd(__pa(p4d) | _KERNPG_TABLE)); + } + p4d =3D p4d_offset(pgd, vaddr); + if (!p4d_present(*p4d)) { + pud =3D (pud_t *)alloc_pgt_page(NULL); + if (!pud) + return -ENOMEM; + set_p4d(p4d, __p4d(__pa(pud) | _KERNPG_TABLE)); + } + pud =3D pud_offset(p4d, vaddr); + if (!pud_present(*pud)) { + pmd =3D (pmd_t *)alloc_pgt_page(NULL); + if (!pmd) + return -ENOMEM; + set_pud(pud, __pud(__pa(pmd) | _KERNPG_TABLE)); + } + pmd =3D pmd_offset(pud, vaddr); + if (!pmd_present(*pmd)) { + pte =3D (pte_t *)alloc_pgt_page(NULL); + if (!pte) + return -ENOMEM; + set_pmd(pmd, __pmd(__pa(pte) | _KERNPG_TABLE)); + } + pte =3D pte_offset_kernel(pmd, vaddr); + + paddr =3D __pa(vaddr); + set_pte(pte, pfn_pte(paddr >> PAGE_SHIFT, prot)); + + return 0; +} + +static int __init acpi_mp_setup_reset(u64 reset_vector) +{ + struct x86_mapping_info info =3D { + .alloc_pgt_page =3D alloc_pgt_page, + .free_pgt_page =3D free_pgt_page, + .page_flag =3D __PAGE_KERNEL_LARGE_EXEC, + .kernpg_flag =3D _KERNPG_TABLE_NOENC, + }; + pgd_t *pgd; + + pgd =3D alloc_pgt_page(NULL); + if (!pgd) + return -ENOMEM; + + for (int i =3D 0; i < nr_pfn_mapped; i++) { + unsigned long mstart, mend; + + mstart =3D pfn_mapped[i].start << PAGE_SHIFT; + mend =3D pfn_mapped[i].end << PAGE_SHIFT; + if (kernel_ident_mapping_init(&info, pgd, mstart, mend)) { + kernel_ident_mapping_free(&info, pgd); + return -ENOMEM; + } + } + + if (kernel_ident_mapping_init(&info, pgd, + PAGE_ALIGN_DOWN(reset_vector), + PAGE_ALIGN(reset_vector + 1))) { + kernel_ident_mapping_free(&info, pgd); + return -ENOMEM; + } + + if (init_transition_pgtable(pgd)) { + kernel_ident_mapping_free(&info, pgd); + return -ENOMEM; + } + + smp_ops.play_dead =3D acpi_mp_play_dead; + smp_ops.stop_this_cpu =3D acpi_mp_stop_this_cpu; + smp_ops.cpu_die =3D acpi_mp_cpu_die; + + acpi_mp_reset_vector_paddr =3D reset_vector; + acpi_mp_pgd =3D __pa(pgd); + + return 0; +} + static int acpi_wakeup_cpu(u32 apicid, unsigned long start_ip) { if (!acpi_mp_wake_mailbox_paddr) { @@ -97,14 +254,37 @@ int __init acpi_parse_mp_wake(union acpi_subtable_head= ers *header, struct acpi_madt_multiproc_wakeup *mp_wake; =20 mp_wake =3D (struct acpi_madt_multiproc_wakeup *)header; - if (BAD_MADT_ENTRY(mp_wake, end)) + + /* + * Cannot use the standard BAD_MADT_ENTRY() to sanity check the @mp_wake + * entry. 'sizeof (struct acpi_madt_multiproc_wakeup)' can be larger + * than the actual size of the MP wakeup entry in ACPI table because the + * 'reset_vector' is only available in the V1 MP wakeup structure. + */ + if (!mp_wake) + return -EINVAL; + if (end - (unsigned long)mp_wake < ACPI_MADT_MP_WAKEUP_SIZE_V0) + return -EINVAL; + if (mp_wake->header.length < ACPI_MADT_MP_WAKEUP_SIZE_V0) return -EINVAL; =20 acpi_table_print_madt_entry(&header->common); =20 acpi_mp_wake_mailbox_paddr =3D mp_wake->mailbox_address; =20 - acpi_mp_disable_offlining(mp_wake); + if (mp_wake->version >=3D ACPI_MADT_MP_WAKEUP_VERSION_V1 && + mp_wake->header.length >=3D ACPI_MADT_MP_WAKEUP_SIZE_V1) { + if (acpi_mp_setup_reset(mp_wake->reset_vector)) { + pr_warn("Failed to setup MADT reset vector\n"); + acpi_mp_disable_offlining(mp_wake); + } + } else { + /* + * CPU offlining requires version 1 of the ACPI MADT wakeup + * structure. + */ + acpi_mp_disable_offlining(mp_wake); + } =20 apic_update_callback(wakeup_secondary_cpu_64, acpi_wakeup_cpu); =20 diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h index fa63362..e27958e 100644 --- a/include/acpi/actbl2.h +++ b/include/acpi/actbl2.h @@ -1197,8 +1197,20 @@ struct acpi_madt_multiproc_wakeup { u16 version; u32 reserved; /* reserved - must be zero */ u64 mailbox_address; + u64 reset_vector; }; =20 +/* Values for Version field above */ + +enum acpi_madt_multiproc_wakeup_version { + ACPI_MADT_MP_WAKEUP_VERSION_NONE =3D 0, + ACPI_MADT_MP_WAKEUP_VERSION_V1 =3D 1, + ACPI_MADT_MP_WAKEUP_VERSION_RESERVED =3D 2, /* 2 and greater are reserved= */ +}; + +#define ACPI_MADT_MP_WAKEUP_SIZE_V0 16 +#define ACPI_MADT_MP_WAKEUP_SIZE_V1 24 + #define ACPI_MULTIPROC_WAKEUP_MB_OS_SIZE 2032 #define ACPI_MULTIPROC_WAKEUP_MB_FIRMWARE_SIZE 2048 =20 @@ -1211,7 +1223,8 @@ struct acpi_madt_multiproc_wakeup_mailbox { u8 reserved_firmware[ACPI_MULTIPROC_WAKEUP_MB_FIRMWARE_SIZE]; /* reserved= for firmware use */ }; =20 -#define ACPI_MP_WAKE_COMMAND_WAKEUP 1 +#define ACPI_MP_WAKE_COMMAND_WAKEUP 1 +#define ACPI_MP_WAKE_COMMAND_TEST 2 =20 /* 17: CPU Core Interrupt Controller (ACPI 6.5) */