[PATCH v3] ARM64: kernel: implement ACPI parking protocol
Lorenzo Pieralisi
lorenzo.pieralisi at arm.com
Wed Jan 27 03:46:34 PST 2016
On Wed, Jan 27, 2016 at 11:23:41AM +0100, Ard Biesheuvel wrote:
[...]
> > It is to report that this patch currently works on my AMD Seattle with
> > a v4.5-rc1 kernel whilst it was not working before owing to FW putting
> > the mailboxes in WB memory (that was part of linear mapping, I still have
> > to pinpoint the exact reason). I think it is related to recent changes in
> > EFI and memblock. The code in this patch is allowed to map mailboxes
> > through ioremap() (ie it was not before because IIUC pfn_valid was returning
> > true on the mailboxes addresses), which it looks like it changed with:
> >
> > 68709f45385a ("arm64: only consider memblocks with NOMAP cleared for
> > linear mapping")
> >
> > Basically we end up mapping a memory area with ioremap with attributes
> > that may mismatch EFI descriptors, on my AMD Seattle the mailboxes are
> > part of runtime services data that is WB (see below).
> >
>
> The '|WB|WT|WC|UC]' means it can be mapped in various ways, and since
> we dropped it from the linear mapping, it should tolerate being mapped
> as device memory, with the caveat that, since it is tagged as
> EFI_MEMORY_RUNTIME as well, it will be mapped cacheable during
> invocations to EFI runtime services.
>
> So the issue is not in the memory types it exposes, but in the fact
> that it is listed as a region that is subject to VA remapping.
Thanks for clarifying and yes, I agree it is a firmware bug, I just
flagged this up to check with you whether we want to do something
about it (ie it is not just related to this patch).
> > I think the only way I can prevent this is by looking up the
> > mailboxes addresses in the EFI memory map and bail out if the
> > memory descriptor is marked as WB (according to the ACPI parking
> > protocol the mailboxes must be Device-nGnRnE).
> >
>
> This is a firmware bug, and should be caught at the ACPI/UEFI
> validation level. I am not sure we have to special case this in the
> kernel.
I came to the same conclusion, I thought it was worth mentioning
it since with previous kernel versions this mismatch was caught in
the ioremap implementation and now it is not, if a warning has to be
put in place it has to be done separately, it is not an issue
specific to this patch only.
Thanks,
Lorenzo
>
> --
> Ard.
>
>
>
> > AMD Seattle snip boot log with this patch applied and some debug output
> > below (ie mailbox address falls within "Runtime Data").
> >
> > I will keep debugging, comments appreciated.
> >
> > Thanks,
> > Lorenzo
> >
> > [ 0.000000] Processing EFI memory map:
> >
> > <snip>
> >
> > [ 0.000000] 0x008028000000-0x0080280fffff [Runtime Data |RUN| | | | | | |WB|WT|WC|UC]*
> > [ 0.000000] acpi_parking_protocol_cpu_init: ACPI parked addr=80280c1000
> >
> >> - Moved Kconfig option under "Boot options"
> >>
> >> v2: http://lists.infradead.org/pipermail/linux-arm-kernel/2015-September/369121.html
> >>
> >> v1->v2
> >>
> >> - Rebased against v4.2
> >> - Removed SMP dependency (it was removed from arm64)
> >> - Updated some comments
> >> - Clarified 64k page mailbox alignment and requested UEFI specs update
> >>
> >> v1: http://lists.infradead.org/pipermail/linux-arm-kernel/2015-July/356750.html
> >>
> >> arch/arm64/Kconfig | 9 ++
> >> arch/arm64/include/asm/acpi.h | 19 +++-
> >> arch/arm64/include/asm/hardirq.h | 2 +-
> >> arch/arm64/include/asm/smp.h | 9 ++
> >> arch/arm64/kernel/Makefile | 1 +
> >> arch/arm64/kernel/acpi_parking_protocol.c | 153 ++++++++++++++++++++++++++++++
> >> arch/arm64/kernel/cpu_ops.c | 27 +++++-
> >> arch/arm64/kernel/smp.c | 22 +++++
> >> 8 files changed, 236 insertions(+), 6 deletions(-)
> >> create mode 100644 arch/arm64/kernel/acpi_parking_protocol.c
> >>
> >> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
> >> index 8cc6228..53e48a6 100644
> >> --- a/arch/arm64/Kconfig
> >> +++ b/arch/arm64/Kconfig
> >> @@ -756,6 +756,15 @@ endmenu
> >>
> >> menu "Boot options"
> >>
> >> +config ARM64_ACPI_PARKING_PROTOCOL
> >> + bool "Enable support for the ARM64 ACPI parking protocol"
> >> + depends on ACPI
> >> + help
> >> + Enable support for the ARM64 ACPI parking protocol. If disabled
> >> + the kernel will not allow booting through the ARM64 ACPI parking
> >> + protocol even if the corresponding data is present in the ACPI
> >> + MADT table.
> >> +
> >> config CMDLINE
> >> string "Default kernel command string"
> >> default ""
> >> diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h
> >> index caafd63..aee323b 100644
> >> --- a/arch/arm64/include/asm/acpi.h
> >> +++ b/arch/arm64/include/asm/acpi.h
> >> @@ -87,9 +87,26 @@ void __init acpi_init_cpus(void);
> >> static inline void acpi_init_cpus(void) { }
> >> #endif /* CONFIG_ACPI */
> >>
> >> +#ifdef CONFIG_ARM64_ACPI_PARKING_PROTOCOL
> >> +bool acpi_parking_protocol_valid(int cpu);
> >> +void __init
> >> +acpi_set_mailbox_entry(int cpu, struct acpi_madt_generic_interrupt *processor);
> >> +#else
> >> +static inline bool acpi_parking_protocol_valid(int cpu) { return false; }
> >> +static inline void
> >> +acpi_set_mailbox_entry(int cpu, struct acpi_madt_generic_interrupt *processor)
> >> +{}
> >> +#endif
> >> +
> >> static inline const char *acpi_get_enable_method(int cpu)
> >> {
> >> - return acpi_psci_present() ? "psci" : NULL;
> >> + if (acpi_psci_present())
> >> + return "psci";
> >> +
> >> + if (acpi_parking_protocol_valid(cpu))
> >> + return "parking-protocol";
> >> +
> >> + return NULL;
> >> }
> >>
> >> #ifdef CONFIG_ACPI_APEI
> >> diff --git a/arch/arm64/include/asm/hardirq.h b/arch/arm64/include/asm/hardirq.h
> >> index a57601f..8740297 100644
> >> --- a/arch/arm64/include/asm/hardirq.h
> >> +++ b/arch/arm64/include/asm/hardirq.h
> >> @@ -20,7 +20,7 @@
> >> #include <linux/threads.h>
> >> #include <asm/irq.h>
> >>
> >> -#define NR_IPI 5
> >> +#define NR_IPI 6
> >>
> >> typedef struct {
> >> unsigned int __softirq_pending;
> >> diff --git a/arch/arm64/include/asm/smp.h b/arch/arm64/include/asm/smp.h
> >> index d9c3d6a..2013a4d 100644
> >> --- a/arch/arm64/include/asm/smp.h
> >> +++ b/arch/arm64/include/asm/smp.h
> >> @@ -64,6 +64,15 @@ extern void secondary_entry(void);
> >> extern void arch_send_call_function_single_ipi(int cpu);
> >> extern void arch_send_call_function_ipi_mask(const struct cpumask *mask);
> >>
> >> +#ifdef CONFIG_ARM64_ACPI_PARKING_PROTOCOL
> >> +extern void arch_send_wakeup_ipi_mask(const struct cpumask *mask);
> >> +#else
> >> +static inline void arch_send_wakeup_ipi_mask(const struct cpumask *mask)
> >> +{
> >> + BUILD_BUG();
> >> +}
> >> +#endif
> >> +
> >> extern int __cpu_disable(void);
> >>
> >> extern void __cpu_die(unsigned int cpu);
> >> diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
> >> index 83cd7e6..8a9c65c 100644
> >> --- a/arch/arm64/kernel/Makefile
> >> +++ b/arch/arm64/kernel/Makefile
> >> @@ -41,6 +41,7 @@ arm64-obj-$(CONFIG_EFI) += efi.o efi-entry.stub.o
> >> arm64-obj-$(CONFIG_PCI) += pci.o
> >> arm64-obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o
> >> arm64-obj-$(CONFIG_ACPI) += acpi.o
> >> +arm64-obj-$(CONFIG_ARM64_ACPI_PARKING_PROTOCOL) += acpi_parking_protocol.o
> >> arm64-obj-$(CONFIG_PARAVIRT) += paravirt.o
> >>
> >> obj-y += $(arm64-obj-y) vdso/
> >> diff --git a/arch/arm64/kernel/acpi_parking_protocol.c b/arch/arm64/kernel/acpi_parking_protocol.c
> >> new file mode 100644
> >> index 0000000..531c3ad
> >> --- /dev/null
> >> +++ b/arch/arm64/kernel/acpi_parking_protocol.c
> >> @@ -0,0 +1,153 @@
> >> +/*
> >> + * ARM64 ACPI Parking Protocol implementation
> >> + *
> >> + * Authors: Lorenzo Pieralisi <lorenzo.pieralisi at arm.com>
> >> + * Mark Salter <msalter at redhat.com>
> >> + *
> >> + * This program is free software; you can redistribute it and/or modify
> >> + * it under the terms of the GNU General Public License version 2 as
> >> + * published by the Free Software Foundation.
> >> + *
> >> + * This program is distributed in the hope that it will be useful,
> >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> >> + * GNU General Public License for more details.
> >> + *
> >> + * You should have received a copy of the GNU General Public License
> >> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> >> + */
> >> +#include <linux/acpi.h>
> >> +#include <linux/types.h>
> >> +
> >> +#include <asm/cpu_ops.h>
> >> +
> >> +struct cpu_mailbox_entry {
> >> + phys_addr_t mailbox_addr;
> >> + u8 version;
> >> + u8 gic_cpu_id;
> >> +};
> >> +
> >> +static struct cpu_mailbox_entry cpu_mailbox_entries[NR_CPUS];
> >> +
> >> +void __init acpi_set_mailbox_entry(int cpu,
> >> + struct acpi_madt_generic_interrupt *p)
> >> +{
> >> + struct cpu_mailbox_entry *cpu_entry = &cpu_mailbox_entries[cpu];
> >> +
> >> + cpu_entry->mailbox_addr = p->parked_address;
> >> + cpu_entry->version = p->parking_version;
> >> + cpu_entry->gic_cpu_id = p->cpu_interface_number;
> >> +}
> >> +
> >> +bool __init acpi_parking_protocol_valid(int cpu)
> >> +{
> >> + struct cpu_mailbox_entry *cpu_entry = &cpu_mailbox_entries[cpu];
> >> +
> >> + return cpu_entry->mailbox_addr && cpu_entry->version;
> >> +}
> >> +
> >> +static int acpi_parking_protocol_cpu_init(unsigned int cpu)
> >> +{
> >> + pr_debug("%s: ACPI parked addr=%llx\n", __func__,
> >> + cpu_mailbox_entries[cpu].mailbox_addr);
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static int acpi_parking_protocol_cpu_prepare(unsigned int cpu)
> >> +{
> >> + return 0;
> >> +}
> >> +
> >> +struct parking_protocol_mailbox {
> >> + __le32 cpu_id;
> >> + __le32 reserved;
> >> + __le64 entry_point;
> >> +};
> >> +
> >> +static int acpi_parking_protocol_cpu_boot(unsigned int cpu)
> >> +{
> >> + struct cpu_mailbox_entry *cpu_entry = &cpu_mailbox_entries[cpu];
> >> + struct parking_protocol_mailbox __iomem *mailbox;
> >> + __le32 cpu_id;
> >> +
> >> + /*
> >> + * Map mailbox memory with attribute device nGnRE (ie ioremap -
> >> + * this deviates from the parking protocol specifications since
> >> + * the mailboxes are required to be mapped nGnRnE; the attribute
> >> + * discrepancy is harmless insofar as the protocol specification
> >> + * is concerned).
> >> + * If the mailbox is mistakenly allocated in the linear mapping
> >> + * by FW ioremap will fail since the mapping will be prevented
> >> + * by the kernel (it clashes with the linear mapping attributes
> >> + * specifications).
> >> + */
> >> + mailbox = ioremap(cpu_entry->mailbox_addr, sizeof(*mailbox));
> >> + if (!mailbox)
> >> + return -EIO;
> >> +
> >> + cpu_id = readl_relaxed(&mailbox->cpu_id);
> >> + /*
> >> + * Check if firmware has set-up the mailbox entry properly
> >> + * before kickstarting the respective cpu.
> >> + */
> >> + if (cpu_id != ~0U) {
> >> + iounmap(mailbox);
> >> + return -ENXIO;
> >> + }
> >> +
> >> + /*
> >> + * We write the entry point and cpu id as LE regardless of the
> >> + * native endianness of the kernel. Therefore, any boot-loaders
> >> + * that read this address need to convert this address to the
> >> + * Boot-Loader's endianness before jumping.
> >> + */
> >> + writeq_relaxed(__pa(secondary_entry), &mailbox->entry_point);
> >> + writel_relaxed(cpu_entry->gic_cpu_id, &mailbox->cpu_id);
> >> +
> >> + arch_send_wakeup_ipi_mask(cpumask_of(cpu));
> >> +
> >> + iounmap(mailbox);
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static void acpi_parking_protocol_cpu_postboot(void)
> >> +{
> >> + int cpu = smp_processor_id();
> >> + struct cpu_mailbox_entry *cpu_entry = &cpu_mailbox_entries[cpu];
> >> + struct parking_protocol_mailbox __iomem *mailbox;
> >> + __le64 entry_point;
> >> +
> >> + /*
> >> + * Map mailbox memory with attribute device nGnRE (ie ioremap -
> >> + * this deviates from the parking protocol specifications since
> >> + * the mailboxes are required to be mapped nGnRnE; the attribute
> >> + * discrepancy is harmless insofar as the protocol specification
> >> + * is concerned).
> >> + * If the mailbox is mistakenly allocated in the linear mapping
> >> + * by FW ioremap will fail since the mapping will be prevented
> >> + * by the kernel (it clashes with the linear mapping attributes
> >> + * specifications).
> >> + */
> >> + mailbox = ioremap(cpu_entry->mailbox_addr, sizeof(*mailbox));
> >> + if (!mailbox)
> >> + return;
> >> +
> >> + entry_point = readl_relaxed(&mailbox->entry_point);
> >> + /*
> >> + * Check if firmware has cleared the entry_point as expected
> >> + * by the protocol specification.
> >> + */
> >> + WARN_ON(entry_point);
> >> +
> >> + iounmap(mailbox);
> >> +}
> >> +
> >> +const struct cpu_operations acpi_parking_protocol_ops = {
> >> + .name = "parking-protocol",
> >> + .cpu_init = acpi_parking_protocol_cpu_init,
> >> + .cpu_prepare = acpi_parking_protocol_cpu_prepare,
> >> + .cpu_boot = acpi_parking_protocol_cpu_boot,
> >> + .cpu_postboot = acpi_parking_protocol_cpu_postboot
> >> +};
> >> diff --git a/arch/arm64/kernel/cpu_ops.c b/arch/arm64/kernel/cpu_ops.c
> >> index b6bd7d4..c7cfb8f 100644
> >> --- a/arch/arm64/kernel/cpu_ops.c
> >> +++ b/arch/arm64/kernel/cpu_ops.c
> >> @@ -25,19 +25,30 @@
> >> #include <asm/smp_plat.h>
> >>
> >> extern const struct cpu_operations smp_spin_table_ops;
> >> +extern const struct cpu_operations acpi_parking_protocol_ops;
> >> extern const struct cpu_operations cpu_psci_ops;
> >>
> >> const struct cpu_operations *cpu_ops[NR_CPUS];
> >>
> >> -static const struct cpu_operations *supported_cpu_ops[] __initconst = {
> >> +static const struct cpu_operations *dt_supported_cpu_ops[] __initconst = {
> >> &smp_spin_table_ops,
> >> &cpu_psci_ops,
> >> NULL,
> >> };
> >>
> >> +static const struct cpu_operations *acpi_supported_cpu_ops[] __initconst = {
> >> +#ifdef CONFIG_ARM64_ACPI_PARKING_PROTOCOL
> >> + &acpi_parking_protocol_ops,
> >> +#endif
> >> + &cpu_psci_ops,
> >> + NULL,
> >> +};
> >> +
> >> static const struct cpu_operations * __init cpu_get_ops(const char *name)
> >> {
> >> - const struct cpu_operations **ops = supported_cpu_ops;
> >> + const struct cpu_operations **ops;
> >> +
> >> + ops = acpi_disabled ? dt_supported_cpu_ops : acpi_supported_cpu_ops;
> >>
> >> while (*ops) {
> >> if (!strcmp(name, (*ops)->name))
> >> @@ -75,8 +86,16 @@ static const char *__init cpu_read_enable_method(int cpu)
> >> }
> >> } else {
> >> enable_method = acpi_get_enable_method(cpu);
> >> - if (!enable_method)
> >> - pr_err("Unsupported ACPI enable-method\n");
> >> + if (!enable_method) {
> >> + /*
> >> + * In ACPI systems the boot CPU does not require
> >> + * checking the enable method since for some
> >> + * boot protocol (ie parking protocol) it need not
> >> + * be initialized. Don't warn spuriously.
> >> + */
> >> + if (cpu != 0)
> >> + pr_err("Unsupported ACPI enable-method\n");
> >> + }
> >> }
> >>
> >> return enable_method;
> >> diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
> >> index b1adc51..6f608af 100644
> >> --- a/arch/arm64/kernel/smp.c
> >> +++ b/arch/arm64/kernel/smp.c
> >> @@ -70,6 +70,7 @@ enum ipi_msg_type {
> >> IPI_CPU_STOP,
> >> IPI_TIMER,
> >> IPI_IRQ_WORK,
> >> + IPI_WAKEUP
> >> };
> >>
> >> /*
> >> @@ -445,6 +446,17 @@ acpi_map_gic_cpu_interface(struct acpi_madt_generic_interrupt *processor)
> >> /* map the logical cpu id to cpu MPIDR */
> >> cpu_logical_map(cpu_count) = hwid;
> >>
> >> + /*
> >> + * Set-up the ACPI parking protocol cpu entries
> >> + * while initializing the cpu_logical_map to
> >> + * avoid parsing MADT entries multiple times for
> >> + * nothing (ie a valid cpu_logical_map entry should
> >> + * contain a valid parking protocol data set to
> >> + * initialize the cpu if the parking protocol is
> >> + * the only available enable method).
> >> + */
> >> + acpi_set_mailbox_entry(cpu_count, processor);
> >> +
> >> cpu_count++;
> >> }
> >>
> >> @@ -627,6 +639,7 @@ static const char *ipi_types[NR_IPI] __tracepoint_string = {
> >> S(IPI_CPU_STOP, "CPU stop interrupts"),
> >> S(IPI_TIMER, "Timer broadcast interrupts"),
> >> S(IPI_IRQ_WORK, "IRQ work interrupts"),
> >> + S(IPI_WAKEUP, "CPU wakeup interrupts"),
> >> };
> >>
> >> static void smp_cross_call(const struct cpumask *target, unsigned int ipinr)
> >> @@ -670,6 +683,13 @@ void arch_send_call_function_single_ipi(int cpu)
> >> smp_cross_call(cpumask_of(cpu), IPI_CALL_FUNC);
> >> }
> >>
> >> +#ifdef CONFIG_ARM64_ACPI_PARKING_PROTOCOL
> >> +void arch_send_wakeup_ipi_mask(const struct cpumask *mask)
> >> +{
> >> + smp_cross_call(mask, IPI_WAKEUP);
> >> +}
> >> +#endif
> >> +
> >> #ifdef CONFIG_IRQ_WORK
> >> void arch_irq_work_raise(void)
> >> {
> >> @@ -746,6 +766,8 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
> >> irq_exit();
> >> break;
> >> #endif
> >> + case IPI_WAKEUP:
> >> + break;
> >>
> >> default:
> >> pr_crit("CPU%u: Unknown IPI message 0x%x\n", cpu, ipinr);
> >> --
> >> 2.5.1
> >>
>
More information about the linux-arm-kernel
mailing list