All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/11] arm64/firmware: Software Delegated Exception Interface
@ 2017-05-15 17:43 ` James Morse
  0 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-05-15 17:43 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: devicetree, Marc Zyngier, Catalin Marinas, Will Deacon,
	Rob Herring, kvmarm

Hello!

The Software Delegated Exception Interface (SDEI) is an ARM specification
for registering callbacks from the platform firmware into the OS.
This is intended to be used to implement firmware-first RAS notifications.

The document is here:
http://infocenter.arm.com/help/topic/com.arm.doc.den0054a/ARM_DEN0054A_Software_Delegated_Exception_Interface.pdf

This series (juggles some registers with KVM+VHE, then) adds a DT binding to
trigger probing of the interface and support for the SDEI API.
A future version of the ACPI spec should add the necessary parts to enable this
to be used as a GHES notification.


SDEI runs between adjacent exception levels, so events will always be delivered
to EL2 if firmware is at EL3. For VHE hosts we run the SDEI event handler
behind KVM's back with all exceptions masked. Once the handler has done its
work we return to the appropriate vbar+irq entry. This allows KVM to
world-switch and deliver any signals sent by the handler to Qemu/kvmtool. We
do the same thing if we interrupt host EL0. If we interrupted code with
interrupts masked, we use a different API call to return to the interrupted
context.

What about non-VHE KVM? If you don't have VHE support and boot at EL2, the
kernel drops to EL1. This driver will print an error message then give up. This
is because events would still be delivered to EL2 hitting either KVM, or the
hyp-stub. Supporting this is complicated, but because the main use-case is
RAS, and ARM v8.2's RAS extensions imply v8.1's Virtual Host Extensions, we
can assume all platforms with SDEI will support VHE too. (I have some ideas
on how to support non-VHE if it turns out to be needed).


Running the event handler behind VHE-KVM's back has some side effects: The
event handler will blindly use any registers that are shared between the host
and guest. The two that I think matter are TPIDR_EL1, and the debug state. The
guest may have set MDSCR_EL1 so debug exceptions must remain masked. The
guest's TPIDR_EL1 will be used by the event handler if it accesses per-cpu
variables. This needs fixing. The first part of this series juggles KVMs use
of TPIDR_EL2 so that we share it with the host on VHE systems. An equivalent
change for 32bit is on my todo list. (one alternative to this is to have a
parody world switch in the SDEI event handler, but this would mean special
casing interrupted guests, and be an ABI link to KVM.)

Causing a synchronous exception from an event handler will cause KVM to
hyp-panic, but may silently succeed if the event didn't interrupt a guest.
(I may WARN_ON() if this happens in a later patch). You because of this you
should not kprobe anything that handles SDE events. Once we've called
nmi_enter(), ftrace and friends should be safe.

Is this another begins-with-S RAS mechanism for arm64? Yes.
Why? Any notification delivered as an exception will overwrite the exception
registers. This is fatal for the running thread if it happens during entry.S's
kernel_enter or kernel_exit. Instead of adding masking and routing controls,
events are delivered to a registered address at a fixed exception level and
don't change the exception registers when delivered.

This series can be retrieved from:
git://linux-arm.org/linux-jm.git -b sdei/v1/base

Questions and contradictions welcome!


James Morse (11):
  KVM: arm64: Store vcpu on the stack during __guest_enter()
  KVM: arm/arm64: Convert kvm_host_cpu_state to a static per-cpu
    allocation
  KVM: arm64: Change hyp_panic()s dependency on tpidr_el2
  arm64: alternatives: use tpidr_el2 on VHE hosts
  arm64: KVM: Stop save/restoring host tpidr_el1 on VHE
  dt-bindings: add devicetree binding for describing arm64 SDEI firmware
  firmware: arm_sdei: Add driver for Software Delegated Exceptions
  arm64: kernel: Add arch-specific SDEI entry code and CPU masking
  firmware: arm_sdei: Add support for CPU and system power states
  firmware: arm_sdei: add support for CPU private events
  KVM: arm64: Delegate support for SDEI to userspace

 Documentation/devicetree/bindings/arm/sdei.txt |   37 +
 arch/arm64/Kconfig                             |    1 +
 arch/arm64/include/asm/assembler.h             |    8 +
 arch/arm64/include/asm/kvm_host.h              |    2 +
 arch/arm64/include/asm/percpu.h                |   11 +-
 arch/arm64/include/asm/processor.h             |    1 +
 arch/arm64/include/asm/sdei.h                  |   45 +
 arch/arm64/kernel/Makefile                     |    1 +
 arch/arm64/kernel/asm-offsets.c                |    2 +
 arch/arm64/kernel/cpufeature.c                 |   22 +
 arch/arm64/kernel/entry.S                      |   68 ++
 arch/arm64/kernel/sdei.c                       |  106 +++
 arch/arm64/kernel/smp.c                        |    7 +
 arch/arm64/kvm/handle_exit.c                   |   10 +-
 arch/arm64/kvm/hyp-init.S                      |    4 +
 arch/arm64/kvm/hyp/entry.S                     |   12 +-
 arch/arm64/kvm/hyp/hyp-entry.S                 |   13 +-
 arch/arm64/kvm/hyp/switch.c                    |   25 +-
 arch/arm64/kvm/hyp/sysreg-sr.c                 |   16 +-
 arch/arm64/mm/proc.S                           |    8 +
 drivers/firmware/Kconfig                       |    8 +
 drivers/firmware/Makefile                      |    1 +
 drivers/firmware/arm_sdei.c                    | 1055 ++++++++++++++++++++++++
 include/linux/cpuhotplug.h                     |    1 +
 include/linux/sdei.h                           |  102 +++
 include/uapi/linux/kvm.h                       |    1 +
 include/uapi/linux/sdei.h                      |   91 ++
 virt/kvm/arm/arm.c                             |   23 +-
 28 files changed, 1634 insertions(+), 47 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/arm/sdei.txt
 create mode 100644 arch/arm64/include/asm/sdei.h
 create mode 100644 arch/arm64/kernel/sdei.c
 create mode 100644 drivers/firmware/arm_sdei.c
 create mode 100644 include/linux/sdei.h
 create mode 100644 include/uapi/linux/sdei.h

-- 
2.10.1

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

* [PATCH 00/11] arm64/firmware: Software Delegated Exception Interface
@ 2017-05-15 17:43 ` James Morse
  0 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-05-15 17:43 UTC (permalink / raw)
  To: linux-arm-kernel

Hello!

The Software Delegated Exception Interface (SDEI) is an ARM specification
for registering callbacks from the platform firmware into the OS.
This is intended to be used to implement firmware-first RAS notifications.

The document is here:
http://infocenter.arm.com/help/topic/com.arm.doc.den0054a/ARM_DEN0054A_Software_Delegated_Exception_Interface.pdf

This series (juggles some registers with KVM+VHE, then) adds a DT binding to
trigger probing of the interface and support for the SDEI API.
A future version of the ACPI spec should add the necessary parts to enable this
to be used as a GHES notification.


SDEI runs between adjacent exception levels, so events will always be delivered
to EL2 if firmware is at EL3. For VHE hosts we run the SDEI event handler
behind KVM's back with all exceptions masked. Once the handler has done its
work we return to the appropriate vbar+irq entry. This allows KVM to
world-switch and deliver any signals sent by the handler to Qemu/kvmtool. We
do the same thing if we interrupt host EL0. If we interrupted code with
interrupts masked, we use a different API call to return to the interrupted
context.

What about non-VHE KVM? If you don't have VHE support and boot at EL2, the
kernel drops to EL1. This driver will print an error message then give up. This
is because events would still be delivered to EL2 hitting either KVM, or the
hyp-stub. Supporting this is complicated, but because the main use-case is
RAS, and ARM v8.2's RAS extensions imply v8.1's Virtual Host Extensions, we
can assume all platforms with SDEI will support VHE too. (I have some ideas
on how to support non-VHE if it turns out to be needed).


Running the event handler behind VHE-KVM's back has some side effects: The
event handler will blindly use any registers that are shared between the host
and guest. The two that I think matter are TPIDR_EL1, and the debug state. The
guest may have set MDSCR_EL1 so debug exceptions must remain masked. The
guest's TPIDR_EL1 will be used by the event handler if it accesses per-cpu
variables. This needs fixing. The first part of this series juggles KVMs use
of TPIDR_EL2 so that we share it with the host on VHE systems. An equivalent
change for 32bit is on my todo list. (one alternative to this is to have a
parody world switch in the SDEI event handler, but this would mean special
casing interrupted guests, and be an ABI link to KVM.)

Causing a synchronous exception from an event handler will cause KVM to
hyp-panic, but may silently succeed if the event didn't interrupt a guest.
(I may WARN_ON() if this happens in a later patch). You because of this you
should not kprobe anything that handles SDE events. Once we've called
nmi_enter(), ftrace and friends should be safe.

Is this another begins-with-S RAS mechanism for arm64? Yes.
Why? Any notification delivered as an exception will overwrite the exception
registers. This is fatal for the running thread if it happens during entry.S's
kernel_enter or kernel_exit. Instead of adding masking and routing controls,
events are delivered to a registered address at a fixed exception level and
don't change the exception registers when delivered.

This series can be retrieved from:
git://linux-arm.org/linux-jm.git -b sdei/v1/base

Questions and contradictions welcome!


James Morse (11):
  KVM: arm64: Store vcpu on the stack during __guest_enter()
  KVM: arm/arm64: Convert kvm_host_cpu_state to a static per-cpu
    allocation
  KVM: arm64: Change hyp_panic()s dependency on tpidr_el2
  arm64: alternatives: use tpidr_el2 on VHE hosts
  arm64: KVM: Stop save/restoring host tpidr_el1 on VHE
  dt-bindings: add devicetree binding for describing arm64 SDEI firmware
  firmware: arm_sdei: Add driver for Software Delegated Exceptions
  arm64: kernel: Add arch-specific SDEI entry code and CPU masking
  firmware: arm_sdei: Add support for CPU and system power states
  firmware: arm_sdei: add support for CPU private events
  KVM: arm64: Delegate support for SDEI to userspace

 Documentation/devicetree/bindings/arm/sdei.txt |   37 +
 arch/arm64/Kconfig                             |    1 +
 arch/arm64/include/asm/assembler.h             |    8 +
 arch/arm64/include/asm/kvm_host.h              |    2 +
 arch/arm64/include/asm/percpu.h                |   11 +-
 arch/arm64/include/asm/processor.h             |    1 +
 arch/arm64/include/asm/sdei.h                  |   45 +
 arch/arm64/kernel/Makefile                     |    1 +
 arch/arm64/kernel/asm-offsets.c                |    2 +
 arch/arm64/kernel/cpufeature.c                 |   22 +
 arch/arm64/kernel/entry.S                      |   68 ++
 arch/arm64/kernel/sdei.c                       |  106 +++
 arch/arm64/kernel/smp.c                        |    7 +
 arch/arm64/kvm/handle_exit.c                   |   10 +-
 arch/arm64/kvm/hyp-init.S                      |    4 +
 arch/arm64/kvm/hyp/entry.S                     |   12 +-
 arch/arm64/kvm/hyp/hyp-entry.S                 |   13 +-
 arch/arm64/kvm/hyp/switch.c                    |   25 +-
 arch/arm64/kvm/hyp/sysreg-sr.c                 |   16 +-
 arch/arm64/mm/proc.S                           |    8 +
 drivers/firmware/Kconfig                       |    8 +
 drivers/firmware/Makefile                      |    1 +
 drivers/firmware/arm_sdei.c                    | 1055 ++++++++++++++++++++++++
 include/linux/cpuhotplug.h                     |    1 +
 include/linux/sdei.h                           |  102 +++
 include/uapi/linux/kvm.h                       |    1 +
 include/uapi/linux/sdei.h                      |   91 ++
 virt/kvm/arm/arm.c                             |   23 +-
 28 files changed, 1634 insertions(+), 47 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/arm/sdei.txt
 create mode 100644 arch/arm64/include/asm/sdei.h
 create mode 100644 arch/arm64/kernel/sdei.c
 create mode 100644 drivers/firmware/arm_sdei.c
 create mode 100644 include/linux/sdei.h
 create mode 100644 include/uapi/linux/sdei.h

-- 
2.10.1

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

* [PATCH 01/11] KVM: arm64: Store vcpu on the stack during __guest_enter()
  2017-05-15 17:43 ` James Morse
@ 2017-05-15 17:43   ` James Morse
  -1 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-05-15 17:43 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: devicetree, Marc Zyngier, Catalin Marinas, Will Deacon,
	Rob Herring, kvmarm

KVM uses tpidr_el2 as its private vcpu register, which makes sense for
non-vhe world switch as only KVM can access this register. This means
vhe Linux has to use tpidr_el1, which KVM has to save/restore as part
of the host context.

__guest_enter() stores the host_ctxt on the stack, do the same with
the vcpu.

Signed-off-by: James Morse <james.morse@arm.com>
---
 arch/arm64/kvm/hyp/entry.S     | 12 ++++++++----
 arch/arm64/kvm/hyp/hyp-entry.S |  6 +++---
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/arch/arm64/kvm/hyp/entry.S b/arch/arm64/kvm/hyp/entry.S
index 12ee62d6d410..113735df7d01 100644
--- a/arch/arm64/kvm/hyp/entry.S
+++ b/arch/arm64/kvm/hyp/entry.S
@@ -62,8 +62,8 @@ ENTRY(__guest_enter)
 	// Store the host regs
 	save_callee_saved_regs x1
 
-	// Store the host_ctxt for use at exit time
-	str	x1, [sp, #-16]!
+	// Store host_ctxt and vcpu for use at exit time
+	stp	x1, x0, [sp, #-16]!
 
 	add	x18, x0, #VCPU_CONTEXT
 
@@ -159,9 +159,15 @@ abort_guest_exit_end:
 ENDPROC(__guest_exit)
 
 ENTRY(__fpsimd_guest_restore)
+	// x0: esr
+	// x1: vcpu
+	// x2-x29,lr: vcpu regs
+	// vcpu x0-x1 on the stack
 	stp	x2, x3, [sp, #-16]!
 	stp	x4, lr, [sp, #-16]!
 
+	mov	x3, x1
+
 alternative_if_not ARM64_HAS_VIRT_HOST_EXTN
 	mrs	x2, cptr_el2
 	bic	x2, x2, #CPTR_EL2_TFP
@@ -173,8 +179,6 @@ alternative_else
 alternative_endif
 	isb
 
-	mrs	x3, tpidr_el2
-
 	ldr	x0, [x3, #VCPU_HOST_CONTEXT]
 	kern_hyp_va x0
 	add	x0, x0, #CPU_GP_REG_OFFSET(CPU_FP_REGS)
diff --git a/arch/arm64/kvm/hyp/hyp-entry.S b/arch/arm64/kvm/hyp/hyp-entry.S
index 5170ce1021da..fce7cc507e0a 100644
--- a/arch/arm64/kvm/hyp/hyp-entry.S
+++ b/arch/arm64/kvm/hyp/hyp-entry.S
@@ -104,6 +104,7 @@ el1_trap:
 	/*
 	 * x0: ESR_EC
 	 */
+	ldr	x1, [sp, #16 + 8]	// vcpu stored by __guest_enter
 
 	/*
 	 * We trap the first access to the FP/SIMD to save the host context
@@ -116,19 +117,18 @@ alternative_if_not ARM64_HAS_NO_FPSIMD
 	b.eq	__fpsimd_guest_restore
 alternative_else_nop_endif
 
-	mrs	x1, tpidr_el2
 	mov	x0, #ARM_EXCEPTION_TRAP
 	b	__guest_exit
 
 el1_irq:
 	stp     x0, x1, [sp, #-16]!
-	mrs	x1, tpidr_el2
+	ldr	x1, [sp, #16 + 8]
 	mov	x0, #ARM_EXCEPTION_IRQ
 	b	__guest_exit
 
 el1_error:
 	stp     x0, x1, [sp, #-16]!
-	mrs	x1, tpidr_el2
+	ldr	x1, [sp, #16 + 8]
 	mov	x0, #ARM_EXCEPTION_EL1_SERROR
 	b	__guest_exit
 
-- 
2.10.1

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

* [PATCH 01/11] KVM: arm64: Store vcpu on the stack during __guest_enter()
@ 2017-05-15 17:43   ` James Morse
  0 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-05-15 17:43 UTC (permalink / raw)
  To: linux-arm-kernel

KVM uses tpidr_el2 as its private vcpu register, which makes sense for
non-vhe world switch as only KVM can access this register. This means
vhe Linux has to use tpidr_el1, which KVM has to save/restore as part
of the host context.

__guest_enter() stores the host_ctxt on the stack, do the same with
the vcpu.

Signed-off-by: James Morse <james.morse@arm.com>
---
 arch/arm64/kvm/hyp/entry.S     | 12 ++++++++----
 arch/arm64/kvm/hyp/hyp-entry.S |  6 +++---
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/arch/arm64/kvm/hyp/entry.S b/arch/arm64/kvm/hyp/entry.S
index 12ee62d6d410..113735df7d01 100644
--- a/arch/arm64/kvm/hyp/entry.S
+++ b/arch/arm64/kvm/hyp/entry.S
@@ -62,8 +62,8 @@ ENTRY(__guest_enter)
 	// Store the host regs
 	save_callee_saved_regs x1
 
-	// Store the host_ctxt for use at exit time
-	str	x1, [sp, #-16]!
+	// Store host_ctxt and vcpu for use at exit time
+	stp	x1, x0, [sp, #-16]!
 
 	add	x18, x0, #VCPU_CONTEXT
 
@@ -159,9 +159,15 @@ abort_guest_exit_end:
 ENDPROC(__guest_exit)
 
 ENTRY(__fpsimd_guest_restore)
+	// x0: esr
+	// x1: vcpu
+	// x2-x29,lr: vcpu regs
+	// vcpu x0-x1 on the stack
 	stp	x2, x3, [sp, #-16]!
 	stp	x4, lr, [sp, #-16]!
 
+	mov	x3, x1
+
 alternative_if_not ARM64_HAS_VIRT_HOST_EXTN
 	mrs	x2, cptr_el2
 	bic	x2, x2, #CPTR_EL2_TFP
@@ -173,8 +179,6 @@ alternative_else
 alternative_endif
 	isb
 
-	mrs	x3, tpidr_el2
-
 	ldr	x0, [x3, #VCPU_HOST_CONTEXT]
 	kern_hyp_va x0
 	add	x0, x0, #CPU_GP_REG_OFFSET(CPU_FP_REGS)
diff --git a/arch/arm64/kvm/hyp/hyp-entry.S b/arch/arm64/kvm/hyp/hyp-entry.S
index 5170ce1021da..fce7cc507e0a 100644
--- a/arch/arm64/kvm/hyp/hyp-entry.S
+++ b/arch/arm64/kvm/hyp/hyp-entry.S
@@ -104,6 +104,7 @@ el1_trap:
 	/*
 	 * x0: ESR_EC
 	 */
+	ldr	x1, [sp, #16 + 8]	// vcpu stored by __guest_enter
 
 	/*
 	 * We trap the first access to the FP/SIMD to save the host context
@@ -116,19 +117,18 @@ alternative_if_not ARM64_HAS_NO_FPSIMD
 	b.eq	__fpsimd_guest_restore
 alternative_else_nop_endif
 
-	mrs	x1, tpidr_el2
 	mov	x0, #ARM_EXCEPTION_TRAP
 	b	__guest_exit
 
 el1_irq:
 	stp     x0, x1, [sp, #-16]!
-	mrs	x1, tpidr_el2
+	ldr	x1, [sp, #16 + 8]
 	mov	x0, #ARM_EXCEPTION_IRQ
 	b	__guest_exit
 
 el1_error:
 	stp     x0, x1, [sp, #-16]!
-	mrs	x1, tpidr_el2
+	ldr	x1, [sp, #16 + 8]
 	mov	x0, #ARM_EXCEPTION_EL1_SERROR
 	b	__guest_exit
 
-- 
2.10.1

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

* [PATCH 02/11] KVM: arm/arm64: Convert kvm_host_cpu_state to a static per-cpu allocation
  2017-05-15 17:43 ` James Morse
@ 2017-05-15 17:43     ` James Morse
  -1 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-05-15 17:43 UTC (permalink / raw)
  To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Will Deacon, Catalin Marinas,
	kvmarm-FPEHb7Xf0XXUo1n7N8X6UoWGPAHP3yOg, Marc Zyngier,
	Christoffer Dall, Rob Herring, Mark Rutland,
	devicetree-u79uwXL29TY76Z2rM5mHXA

kvm_host_cpu_state is a per-cpu allocation made from kvm_arch_init()
used to store the host EL1 registers when KVM switches to a guest.

Make it easier for ASM to generate pointers into this per-cpu memory
by making it a static allocation.

Signed-off-by: James Morse <james.morse-5wv7dgnIgG8@public.gmane.org>
---
 virt/kvm/arm/arm.c | 18 +++---------------
 1 file changed, 3 insertions(+), 15 deletions(-)

diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c
index 3417e184c8e1..3a776ec99181 100644
--- a/virt/kvm/arm/arm.c
+++ b/virt/kvm/arm/arm.c
@@ -51,8 +51,8 @@
 __asm__(".arch_extension	virt");
 #endif
 
+DEFINE_PER_CPU(kvm_cpu_context_t, kvm_host_cpu_state);
 static DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_page);
-static kvm_cpu_context_t __percpu *kvm_host_cpu_state;
 
 /* Per-CPU variable containing the currently running vcpu. */
 static DEFINE_PER_CPU(struct kvm_vcpu *, kvm_arm_running_vcpu);
@@ -351,7 +351,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
 	}
 
 	vcpu->cpu = cpu;
-	vcpu->arch.host_cpu_context = this_cpu_ptr(kvm_host_cpu_state);
+	vcpu->arch.host_cpu_context = this_cpu_ptr(&kvm_host_cpu_state);
 
 	kvm_arm_set_running_vcpu(vcpu);
 
@@ -1226,19 +1226,8 @@ static inline void hyp_cpu_pm_exit(void)
 }
 #endif
 
-static void teardown_common_resources(void)
-{
-	free_percpu(kvm_host_cpu_state);
-}
-
 static int init_common_resources(void)
 {
-	kvm_host_cpu_state = alloc_percpu(kvm_cpu_context_t);
-	if (!kvm_host_cpu_state) {
-		kvm_err("Cannot allocate host CPU state\n");
-		return -ENOMEM;
-	}
-
 	/* set size of VMID supported by CPU */
 	kvm_vmid_bits = kvm_get_vmid_bits();
 	kvm_info("%d-bit VMID\n", kvm_vmid_bits);
@@ -1383,7 +1372,7 @@ static int init_hyp_mode(void)
 	for_each_possible_cpu(cpu) {
 		kvm_cpu_context_t *cpu_ctxt;
 
-		cpu_ctxt = per_cpu_ptr(kvm_host_cpu_state, cpu);
+		cpu_ctxt = per_cpu_ptr(&kvm_host_cpu_state, cpu);
 		err = create_hyp_mappings(cpu_ctxt, cpu_ctxt + 1, PAGE_HYP);
 
 		if (err) {
@@ -1461,7 +1450,6 @@ int kvm_arch_init(void *opaque)
 out_hyp:
 	teardown_hyp_mode();
 out_err:
-	teardown_common_resources();
 	return err;
 }
 
-- 
2.10.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 02/11] KVM: arm/arm64: Convert kvm_host_cpu_state to a static per-cpu allocation
@ 2017-05-15 17:43     ` James Morse
  0 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-05-15 17:43 UTC (permalink / raw)
  To: linux-arm-kernel

kvm_host_cpu_state is a per-cpu allocation made from kvm_arch_init()
used to store the host EL1 registers when KVM switches to a guest.

Make it easier for ASM to generate pointers into this per-cpu memory
by making it a static allocation.

Signed-off-by: James Morse <james.morse@arm.com>
---
 virt/kvm/arm/arm.c | 18 +++---------------
 1 file changed, 3 insertions(+), 15 deletions(-)

diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c
index 3417e184c8e1..3a776ec99181 100644
--- a/virt/kvm/arm/arm.c
+++ b/virt/kvm/arm/arm.c
@@ -51,8 +51,8 @@
 __asm__(".arch_extension	virt");
 #endif
 
+DEFINE_PER_CPU(kvm_cpu_context_t, kvm_host_cpu_state);
 static DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_page);
-static kvm_cpu_context_t __percpu *kvm_host_cpu_state;
 
 /* Per-CPU variable containing the currently running vcpu. */
 static DEFINE_PER_CPU(struct kvm_vcpu *, kvm_arm_running_vcpu);
@@ -351,7 +351,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
 	}
 
 	vcpu->cpu = cpu;
-	vcpu->arch.host_cpu_context = this_cpu_ptr(kvm_host_cpu_state);
+	vcpu->arch.host_cpu_context = this_cpu_ptr(&kvm_host_cpu_state);
 
 	kvm_arm_set_running_vcpu(vcpu);
 
@@ -1226,19 +1226,8 @@ static inline void hyp_cpu_pm_exit(void)
 }
 #endif
 
-static void teardown_common_resources(void)
-{
-	free_percpu(kvm_host_cpu_state);
-}
-
 static int init_common_resources(void)
 {
-	kvm_host_cpu_state = alloc_percpu(kvm_cpu_context_t);
-	if (!kvm_host_cpu_state) {
-		kvm_err("Cannot allocate host CPU state\n");
-		return -ENOMEM;
-	}
-
 	/* set size of VMID supported by CPU */
 	kvm_vmid_bits = kvm_get_vmid_bits();
 	kvm_info("%d-bit VMID\n", kvm_vmid_bits);
@@ -1383,7 +1372,7 @@ static int init_hyp_mode(void)
 	for_each_possible_cpu(cpu) {
 		kvm_cpu_context_t *cpu_ctxt;
 
-		cpu_ctxt = per_cpu_ptr(kvm_host_cpu_state, cpu);
+		cpu_ctxt = per_cpu_ptr(&kvm_host_cpu_state, cpu);
 		err = create_hyp_mappings(cpu_ctxt, cpu_ctxt + 1, PAGE_HYP);
 
 		if (err) {
@@ -1461,7 +1450,6 @@ int kvm_arch_init(void *opaque)
 out_hyp:
 	teardown_hyp_mode();
 out_err:
-	teardown_common_resources();
 	return err;
 }
 
-- 
2.10.1

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

* [PATCH 03/11] KVM: arm64: Change hyp_panic()s dependency on tpidr_el2
  2017-05-15 17:43 ` James Morse
@ 2017-05-15 17:43     ` James Morse
  -1 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-05-15 17:43 UTC (permalink / raw)
  To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Will Deacon, Catalin Marinas,
	kvmarm-FPEHb7Xf0XXUo1n7N8X6UoWGPAHP3yOg, Marc Zyngier,
	Christoffer Dall, Rob Herring, Mark Rutland,
	devicetree-u79uwXL29TY76Z2rM5mHXA

KVM calls hyp_panic() when anything unexpected happens. This may occur
while a guest owns the EL1 registers. KVM stashes the vcpu pointer in
tpidr_el2, which it uses to find the host context in order to restore
the host EL1 registers before parachuting into the host's panic().

The host context is a struct kvm_cpu_context allocated in the per-cpu
area, and mapped to hyp. Given the per-cpu offset for this CPU, this is
easy to find. Change hyp_panic() to take a pointer to the
struct kvm_cpu_context. Wrap these calls with an asm function that
retrieves the struct kvm_cpu_context from the host's per-cpu area.

Copy the per-cpu offset from the hosts tpidr_el1 into tpidr_el2 during
kvm init. (Later patches will make this unnecessary for VHE hosts)

We print out the vcpu pointer as part of the panic message. Add a back
reference to the 'running vcpu' in the host cpu context to preserve this.

Signed-off-by: James Morse <james.morse-5wv7dgnIgG8@public.gmane.org>
---
 arch/arm64/include/asm/kvm_host.h |  2 ++
 arch/arm64/kvm/hyp/hyp-entry.S    |  7 +++++++
 arch/arm64/kvm/hyp/s2-setup.c     |  3 +++
 arch/arm64/kvm/hyp/switch.c       | 25 +++++++++++++------------
 4 files changed, 25 insertions(+), 12 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 5e19165c5fa8..1407bfc81e9e 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -188,6 +188,8 @@ struct kvm_cpu_context {
 		u64 sys_regs[NR_SYS_REGS];
 		u32 copro[NR_COPRO_REGS];
 	};
+
+	struct kvm_vcpu *__hyp_running_vcpu;
 };
 
 typedef struct kvm_cpu_context kvm_cpu_context_t;
diff --git a/arch/arm64/kvm/hyp/hyp-entry.S b/arch/arm64/kvm/hyp/hyp-entry.S
index fce7cc507e0a..0f83e7e402ad 100644
--- a/arch/arm64/kvm/hyp/hyp-entry.S
+++ b/arch/arm64/kvm/hyp/hyp-entry.S
@@ -163,6 +163,13 @@ ENTRY(__hyp_do_panic)
 	eret
 ENDPROC(__hyp_do_panic)
 
+ENTRY(__hyp_panic)
+	ldr	x0, =kvm_host_cpu_state
+	mrs	x1, tpidr_el2
+	add	x0, x0, x1
+	b	hyp_panic
+ENDPROC(__hyp_panic)
+
 .macro invalid_vector	label, target = __hyp_panic
 	.align	2
 \label:
diff --git a/arch/arm64/kvm/hyp/s2-setup.c b/arch/arm64/kvm/hyp/s2-setup.c
index b81f4091c909..eb401dbb285e 100644
--- a/arch/arm64/kvm/hyp/s2-setup.c
+++ b/arch/arm64/kvm/hyp/s2-setup.c
@@ -84,5 +84,8 @@ u32 __hyp_text __init_stage2_translation(void)
 
 	write_sysreg(val, vtcr_el2);
 
+	/* copy tpidr_el1 into tpidr_el2 for use by HYP */
+	write_sysreg(read_sysreg(tpidr_el1), tpidr_el2);
+
 	return parange;
 }
diff --git a/arch/arm64/kvm/hyp/switch.c b/arch/arm64/kvm/hyp/switch.c
index aede1658aeda..34f39c1ade34 100644
--- a/arch/arm64/kvm/hyp/switch.c
+++ b/arch/arm64/kvm/hyp/switch.c
@@ -286,9 +286,9 @@ int __hyp_text __kvm_vcpu_run(struct kvm_vcpu *vcpu)
 	u64 exit_code;
 
 	vcpu = kern_hyp_va(vcpu);
-	write_sysreg(vcpu, tpidr_el2);
 
 	host_ctxt = kern_hyp_va(vcpu->arch.host_cpu_context);
+	host_ctxt->__hyp_running_vcpu = vcpu;
 	guest_ctxt = &vcpu->arch.ctxt;
 
 	__sysreg_save_host_state(host_ctxt);
@@ -379,7 +379,8 @@ int __hyp_text __kvm_vcpu_run(struct kvm_vcpu *vcpu)
 
 static const char __hyp_panic_string[] = "HYP panic:\nPS:%08llx PC:%016llx ESR:%08llx\nFAR:%016llx HPFAR:%016llx PAR:%016llx\nVCPU:%p\n";
 
-static void __hyp_text __hyp_call_panic_nvhe(u64 spsr, u64 elr, u64 par)
+static void __hyp_text __hyp_call_panic_nvhe(u64 spsr, u64 elr, u64 par,
+					     struct kvm_vcpu *vcpu)
 {
 	unsigned long str_va;
 
@@ -393,42 +394,42 @@ static void __hyp_text __hyp_call_panic_nvhe(u64 spsr, u64 elr, u64 par)
 	__hyp_do_panic(str_va,
 		       spsr,  elr,
 		       read_sysreg(esr_el2),   read_sysreg_el2(far),
-		       read_sysreg(hpfar_el2), par,
-		       (void *)read_sysreg(tpidr_el2));
+		       read_sysreg(hpfar_el2), par, vcpu);
 }
 
-static void __hyp_text __hyp_call_panic_vhe(u64 spsr, u64 elr, u64 par)
+static void __hyp_text __hyp_call_panic_vhe(u64 spsr, u64 elr, u64 par,
+					    struct kvm_vcpu *vcpu)
 {
 	panic(__hyp_panic_string,
 	      spsr,  elr,
 	      read_sysreg_el2(esr),   read_sysreg_el2(far),
-	      read_sysreg(hpfar_el2), par,
-	      (void *)read_sysreg(tpidr_el2));
+	      read_sysreg(hpfar_el2), par, vcpu);
 }
 
 static hyp_alternate_select(__hyp_call_panic,
 			    __hyp_call_panic_nvhe, __hyp_call_panic_vhe,
 			    ARM64_HAS_VIRT_HOST_EXTN);
 
-void __hyp_text __noreturn __hyp_panic(void)
+void __hyp_text __noreturn hyp_panic(struct kvm_cpu_context *__host_ctxt)
 {
+	struct kvm_vcpu *vcpu = NULL;
+
 	u64 spsr = read_sysreg_el2(spsr);
 	u64 elr = read_sysreg_el2(elr);
 	u64 par = read_sysreg(par_el1);
 
 	if (read_sysreg(vttbr_el2)) {
-		struct kvm_vcpu *vcpu;
 		struct kvm_cpu_context *host_ctxt;
 
-		vcpu = (struct kvm_vcpu *)read_sysreg(tpidr_el2);
-		host_ctxt = kern_hyp_va(vcpu->arch.host_cpu_context);
+		host_ctxt = kern_hyp_va(__host_ctxt);
+		vcpu = host_ctxt->__hyp_running_vcpu;
 		__deactivate_traps(vcpu);
 		__deactivate_vm(vcpu);
 		__sysreg_restore_host_state(host_ctxt);
 	}
 
 	/* Call panic for real */
-	__hyp_call_panic()(spsr, elr, par);
+	__hyp_call_panic()(spsr, elr, par, vcpu);
 
 	unreachable();
 }
-- 
2.10.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 03/11] KVM: arm64: Change hyp_panic()s dependency on tpidr_el2
@ 2017-05-15 17:43     ` James Morse
  0 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-05-15 17:43 UTC (permalink / raw)
  To: linux-arm-kernel

KVM calls hyp_panic() when anything unexpected happens. This may occur
while a guest owns the EL1 registers. KVM stashes the vcpu pointer in
tpidr_el2, which it uses to find the host context in order to restore
the host EL1 registers before parachuting into the host's panic().

The host context is a struct kvm_cpu_context allocated in the per-cpu
area, and mapped to hyp. Given the per-cpu offset for this CPU, this is
easy to find. Change hyp_panic() to take a pointer to the
struct kvm_cpu_context. Wrap these calls with an asm function that
retrieves the struct kvm_cpu_context from the host's per-cpu area.

Copy the per-cpu offset from the hosts tpidr_el1 into tpidr_el2 during
kvm init. (Later patches will make this unnecessary for VHE hosts)

We print out the vcpu pointer as part of the panic message. Add a back
reference to the 'running vcpu' in the host cpu context to preserve this.

Signed-off-by: James Morse <james.morse@arm.com>
---
 arch/arm64/include/asm/kvm_host.h |  2 ++
 arch/arm64/kvm/hyp/hyp-entry.S    |  7 +++++++
 arch/arm64/kvm/hyp/s2-setup.c     |  3 +++
 arch/arm64/kvm/hyp/switch.c       | 25 +++++++++++++------------
 4 files changed, 25 insertions(+), 12 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 5e19165c5fa8..1407bfc81e9e 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -188,6 +188,8 @@ struct kvm_cpu_context {
 		u64 sys_regs[NR_SYS_REGS];
 		u32 copro[NR_COPRO_REGS];
 	};
+
+	struct kvm_vcpu *__hyp_running_vcpu;
 };
 
 typedef struct kvm_cpu_context kvm_cpu_context_t;
diff --git a/arch/arm64/kvm/hyp/hyp-entry.S b/arch/arm64/kvm/hyp/hyp-entry.S
index fce7cc507e0a..0f83e7e402ad 100644
--- a/arch/arm64/kvm/hyp/hyp-entry.S
+++ b/arch/arm64/kvm/hyp/hyp-entry.S
@@ -163,6 +163,13 @@ ENTRY(__hyp_do_panic)
 	eret
 ENDPROC(__hyp_do_panic)
 
+ENTRY(__hyp_panic)
+	ldr	x0, =kvm_host_cpu_state
+	mrs	x1, tpidr_el2
+	add	x0, x0, x1
+	b	hyp_panic
+ENDPROC(__hyp_panic)
+
 .macro invalid_vector	label, target = __hyp_panic
 	.align	2
 \label:
diff --git a/arch/arm64/kvm/hyp/s2-setup.c b/arch/arm64/kvm/hyp/s2-setup.c
index b81f4091c909..eb401dbb285e 100644
--- a/arch/arm64/kvm/hyp/s2-setup.c
+++ b/arch/arm64/kvm/hyp/s2-setup.c
@@ -84,5 +84,8 @@ u32 __hyp_text __init_stage2_translation(void)
 
 	write_sysreg(val, vtcr_el2);
 
+	/* copy tpidr_el1 into tpidr_el2 for use by HYP */
+	write_sysreg(read_sysreg(tpidr_el1), tpidr_el2);
+
 	return parange;
 }
diff --git a/arch/arm64/kvm/hyp/switch.c b/arch/arm64/kvm/hyp/switch.c
index aede1658aeda..34f39c1ade34 100644
--- a/arch/arm64/kvm/hyp/switch.c
+++ b/arch/arm64/kvm/hyp/switch.c
@@ -286,9 +286,9 @@ int __hyp_text __kvm_vcpu_run(struct kvm_vcpu *vcpu)
 	u64 exit_code;
 
 	vcpu = kern_hyp_va(vcpu);
-	write_sysreg(vcpu, tpidr_el2);
 
 	host_ctxt = kern_hyp_va(vcpu->arch.host_cpu_context);
+	host_ctxt->__hyp_running_vcpu = vcpu;
 	guest_ctxt = &vcpu->arch.ctxt;
 
 	__sysreg_save_host_state(host_ctxt);
@@ -379,7 +379,8 @@ int __hyp_text __kvm_vcpu_run(struct kvm_vcpu *vcpu)
 
 static const char __hyp_panic_string[] = "HYP panic:\nPS:%08llx PC:%016llx ESR:%08llx\nFAR:%016llx HPFAR:%016llx PAR:%016llx\nVCPU:%p\n";
 
-static void __hyp_text __hyp_call_panic_nvhe(u64 spsr, u64 elr, u64 par)
+static void __hyp_text __hyp_call_panic_nvhe(u64 spsr, u64 elr, u64 par,
+					     struct kvm_vcpu *vcpu)
 {
 	unsigned long str_va;
 
@@ -393,42 +394,42 @@ static void __hyp_text __hyp_call_panic_nvhe(u64 spsr, u64 elr, u64 par)
 	__hyp_do_panic(str_va,
 		       spsr,  elr,
 		       read_sysreg(esr_el2),   read_sysreg_el2(far),
-		       read_sysreg(hpfar_el2), par,
-		       (void *)read_sysreg(tpidr_el2));
+		       read_sysreg(hpfar_el2), par, vcpu);
 }
 
-static void __hyp_text __hyp_call_panic_vhe(u64 spsr, u64 elr, u64 par)
+static void __hyp_text __hyp_call_panic_vhe(u64 spsr, u64 elr, u64 par,
+					    struct kvm_vcpu *vcpu)
 {
 	panic(__hyp_panic_string,
 	      spsr,  elr,
 	      read_sysreg_el2(esr),   read_sysreg_el2(far),
-	      read_sysreg(hpfar_el2), par,
-	      (void *)read_sysreg(tpidr_el2));
+	      read_sysreg(hpfar_el2), par, vcpu);
 }
 
 static hyp_alternate_select(__hyp_call_panic,
 			    __hyp_call_panic_nvhe, __hyp_call_panic_vhe,
 			    ARM64_HAS_VIRT_HOST_EXTN);
 
-void __hyp_text __noreturn __hyp_panic(void)
+void __hyp_text __noreturn hyp_panic(struct kvm_cpu_context *__host_ctxt)
 {
+	struct kvm_vcpu *vcpu = NULL;
+
 	u64 spsr = read_sysreg_el2(spsr);
 	u64 elr = read_sysreg_el2(elr);
 	u64 par = read_sysreg(par_el1);
 
 	if (read_sysreg(vttbr_el2)) {
-		struct kvm_vcpu *vcpu;
 		struct kvm_cpu_context *host_ctxt;
 
-		vcpu = (struct kvm_vcpu *)read_sysreg(tpidr_el2);
-		host_ctxt = kern_hyp_va(vcpu->arch.host_cpu_context);
+		host_ctxt = kern_hyp_va(__host_ctxt);
+		vcpu = host_ctxt->__hyp_running_vcpu;
 		__deactivate_traps(vcpu);
 		__deactivate_vm(vcpu);
 		__sysreg_restore_host_state(host_ctxt);
 	}
 
 	/* Call panic for real */
-	__hyp_call_panic()(spsr, elr, par);
+	__hyp_call_panic()(spsr, elr, par, vcpu);
 
 	unreachable();
 }
-- 
2.10.1

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

* [PATCH 04/11] arm64: alternatives: use tpidr_el2 on VHE hosts
  2017-05-15 17:43 ` James Morse
@ 2017-05-15 17:43     ` James Morse
  -1 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-05-15 17:43 UTC (permalink / raw)
  To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Will Deacon, Catalin Marinas,
	kvmarm-FPEHb7Xf0XXUo1n7N8X6UoWGPAHP3yOg, Marc Zyngier,
	Christoffer Dall, Rob Herring, Mark Rutland,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Now that KVM uses tpidr_el2 in the same way as Linux's cpu_offset in
tpidr_el1, merge the two. This saves KVM from save/restoring tpidr_el1
on VHE hosts, and allows future code to blindly access per-cpu variables
without triggering world-switch.

Signed-off-by: James Morse <james.morse-5wv7dgnIgG8@public.gmane.org>
---
 arch/arm64/include/asm/assembler.h |  8 ++++++++
 arch/arm64/include/asm/percpu.h    | 11 +++++++++--
 arch/arm64/include/asm/processor.h |  1 +
 arch/arm64/kernel/cpufeature.c     | 22 ++++++++++++++++++++++
 arch/arm64/mm/proc.S               |  8 ++++++++
 5 files changed, 48 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h
index 1b67c3782d00..06252602901e 100644
--- a/arch/arm64/include/asm/assembler.h
+++ b/arch/arm64/include/asm/assembler.h
@@ -236,7 +236,11 @@ lr	.req	x30		// link register
 	 */
 	.macro adr_this_cpu, dst, sym, tmp
 	adr_l	\dst, \sym
+alternative_if_not ARM64_HAS_VIRT_HOST_EXTN
 	mrs	\tmp, tpidr_el1
+alternative_else
+	mrs	\tmp, tpidr_el2
+alternative_endif
 	add	\dst, \dst, \tmp
 	.endm
 
@@ -247,7 +251,11 @@ lr	.req	x30		// link register
 	 */
 	.macro ldr_this_cpu dst, sym, tmp
 	adr_l	\dst, \sym
+alternative_if_not ARM64_HAS_VIRT_HOST_EXTN
 	mrs	\tmp, tpidr_el1
+alternative_else
+	mrs	\tmp, tpidr_el2
+alternative_endif
 	ldr	\dst, [\dst, \tmp]
 	.endm
 
diff --git a/arch/arm64/include/asm/percpu.h b/arch/arm64/include/asm/percpu.h
index 3bd498e4de4c..43393208229e 100644
--- a/arch/arm64/include/asm/percpu.h
+++ b/arch/arm64/include/asm/percpu.h
@@ -16,11 +16,15 @@
 #ifndef __ASM_PERCPU_H
 #define __ASM_PERCPU_H
 
+#include <asm/alternative.h>
 #include <asm/stack_pointer.h>
 
 static inline void set_my_cpu_offset(unsigned long off)
 {
-	asm volatile("msr tpidr_el1, %0" :: "r" (off) : "memory");
+	asm volatile(ALTERNATIVE("msr tpidr_el1, %0",
+				 "msr tpidr_el2, %0",
+				 ARM64_HAS_VIRT_HOST_EXTN)
+			:: "r" (off) : "memory");
 }
 
 static inline unsigned long __my_cpu_offset(void)
@@ -31,7 +35,10 @@ static inline unsigned long __my_cpu_offset(void)
 	 * We want to allow caching the value, so avoid using volatile and
 	 * instead use a fake stack read to hazard against barrier().
 	 */
-	asm("mrs %0, tpidr_el1" : "=r" (off) :
+	asm(ALTERNATIVE("mrs %0, tpidr_el1",
+			"mrs %0, tpidr_el2",
+			ARM64_HAS_VIRT_HOST_EXTN)
+		: "=r" (off) :
 		"Q" (*(const unsigned long *)current_stack_pointer));
 
 	return off;
diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h
index 9428b93fefb2..211ee60feeae 100644
--- a/arch/arm64/include/asm/processor.h
+++ b/arch/arm64/include/asm/processor.h
@@ -190,5 +190,6 @@ static inline void spin_lock_prefetch(const void *ptr)
 
 int cpu_enable_pan(void *__unused);
 int cpu_enable_cache_maint_trap(void *__unused);
+int cpu_copy_el2regs(void *__unused);
 
 #endif /* __ASM_PROCESSOR_H */
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 94b8f7fc3310..e965639ff08b 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -843,6 +843,7 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
 		.capability = ARM64_HAS_VIRT_HOST_EXTN,
 		.def_scope = SCOPE_SYSTEM,
 		.matches = runs_at_el2,
+		.enable = cpu_copy_el2regs,
 	},
 	{
 		.desc = "32-bit EL0 Support",
@@ -1255,3 +1256,24 @@ static int __init enable_mrs_emulation(void)
 }
 
 late_initcall(enable_mrs_emulation);
+
+int cpu_copy_el2regs(void *__unused)
+{
+	int do_copyregs = 0;
+
+	/*
+	 * Copy register values that aren't redirected by hardware.
+	 *
+	 * Before code patching, we only set tpidr_el1, all CPUs need to copy
+	 * this value to tpidr_el2 before we patch the code. Once we've done
+	 * that, freshly-onlined CPUs will set tpidr_el2, so we don't need to
+	 * do anything here.
+	 */
+	asm volatile(ALTERNATIVE("mov %0, #1", "nop", ARM64_HAS_VIRT_HOST_EXTN)
+		    : "=r" (do_copyregs) : : );
+
+	if (do_copyregs)
+		write_sysreg(read_sysreg(tpidr_el1), tpidr_el2);
+
+	return 0;
+}
diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S
index 877d42fb0df6..109d43a15aaf 100644
--- a/arch/arm64/mm/proc.S
+++ b/arch/arm64/mm/proc.S
@@ -70,7 +70,11 @@ ENTRY(cpu_do_suspend)
 	mrs	x8, mdscr_el1
 	mrs	x9, oslsr_el1
 	mrs	x10, sctlr_el1
+alternative_if_not ARM64_HAS_VIRT_HOST_EXTN
 	mrs	x11, tpidr_el1
+alternative_else
+	mrs	x11, tpidr_el2
+alternative_endif
 	mrs	x12, sp_el0
 	stp	x2, x3, [x0]
 	stp	x4, xzr, [x0, #16]
@@ -116,7 +120,11 @@ ENTRY(cpu_do_resume)
 	msr	mdscr_el1, x10
 
 	msr	sctlr_el1, x12
+alternative_if_not ARM64_HAS_VIRT_HOST_EXTN
 	msr	tpidr_el1, x13
+alternative_else
+	msr	tpidr_el2, x13
+alternative_endif
 	msr	sp_el0, x14
 	/*
 	 * Restore oslsr_el1 by writing oslar_el1
-- 
2.10.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 04/11] arm64: alternatives: use tpidr_el2 on VHE hosts
@ 2017-05-15 17:43     ` James Morse
  0 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-05-15 17:43 UTC (permalink / raw)
  To: linux-arm-kernel

Now that KVM uses tpidr_el2 in the same way as Linux's cpu_offset in
tpidr_el1, merge the two. This saves KVM from save/restoring tpidr_el1
on VHE hosts, and allows future code to blindly access per-cpu variables
without triggering world-switch.

Signed-off-by: James Morse <james.morse@arm.com>
---
 arch/arm64/include/asm/assembler.h |  8 ++++++++
 arch/arm64/include/asm/percpu.h    | 11 +++++++++--
 arch/arm64/include/asm/processor.h |  1 +
 arch/arm64/kernel/cpufeature.c     | 22 ++++++++++++++++++++++
 arch/arm64/mm/proc.S               |  8 ++++++++
 5 files changed, 48 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h
index 1b67c3782d00..06252602901e 100644
--- a/arch/arm64/include/asm/assembler.h
+++ b/arch/arm64/include/asm/assembler.h
@@ -236,7 +236,11 @@ lr	.req	x30		// link register
 	 */
 	.macro adr_this_cpu, dst, sym, tmp
 	adr_l	\dst, \sym
+alternative_if_not ARM64_HAS_VIRT_HOST_EXTN
 	mrs	\tmp, tpidr_el1
+alternative_else
+	mrs	\tmp, tpidr_el2
+alternative_endif
 	add	\dst, \dst, \tmp
 	.endm
 
@@ -247,7 +251,11 @@ lr	.req	x30		// link register
 	 */
 	.macro ldr_this_cpu dst, sym, tmp
 	adr_l	\dst, \sym
+alternative_if_not ARM64_HAS_VIRT_HOST_EXTN
 	mrs	\tmp, tpidr_el1
+alternative_else
+	mrs	\tmp, tpidr_el2
+alternative_endif
 	ldr	\dst, [\dst, \tmp]
 	.endm
 
diff --git a/arch/arm64/include/asm/percpu.h b/arch/arm64/include/asm/percpu.h
index 3bd498e4de4c..43393208229e 100644
--- a/arch/arm64/include/asm/percpu.h
+++ b/arch/arm64/include/asm/percpu.h
@@ -16,11 +16,15 @@
 #ifndef __ASM_PERCPU_H
 #define __ASM_PERCPU_H
 
+#include <asm/alternative.h>
 #include <asm/stack_pointer.h>
 
 static inline void set_my_cpu_offset(unsigned long off)
 {
-	asm volatile("msr tpidr_el1, %0" :: "r" (off) : "memory");
+	asm volatile(ALTERNATIVE("msr tpidr_el1, %0",
+				 "msr tpidr_el2, %0",
+				 ARM64_HAS_VIRT_HOST_EXTN)
+			:: "r" (off) : "memory");
 }
 
 static inline unsigned long __my_cpu_offset(void)
@@ -31,7 +35,10 @@ static inline unsigned long __my_cpu_offset(void)
 	 * We want to allow caching the value, so avoid using volatile and
 	 * instead use a fake stack read to hazard against barrier().
 	 */
-	asm("mrs %0, tpidr_el1" : "=r" (off) :
+	asm(ALTERNATIVE("mrs %0, tpidr_el1",
+			"mrs %0, tpidr_el2",
+			ARM64_HAS_VIRT_HOST_EXTN)
+		: "=r" (off) :
 		"Q" (*(const unsigned long *)current_stack_pointer));
 
 	return off;
diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h
index 9428b93fefb2..211ee60feeae 100644
--- a/arch/arm64/include/asm/processor.h
+++ b/arch/arm64/include/asm/processor.h
@@ -190,5 +190,6 @@ static inline void spin_lock_prefetch(const void *ptr)
 
 int cpu_enable_pan(void *__unused);
 int cpu_enable_cache_maint_trap(void *__unused);
+int cpu_copy_el2regs(void *__unused);
 
 #endif /* __ASM_PROCESSOR_H */
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 94b8f7fc3310..e965639ff08b 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -843,6 +843,7 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
 		.capability = ARM64_HAS_VIRT_HOST_EXTN,
 		.def_scope = SCOPE_SYSTEM,
 		.matches = runs_at_el2,
+		.enable = cpu_copy_el2regs,
 	},
 	{
 		.desc = "32-bit EL0 Support",
@@ -1255,3 +1256,24 @@ static int __init enable_mrs_emulation(void)
 }
 
 late_initcall(enable_mrs_emulation);
+
+int cpu_copy_el2regs(void *__unused)
+{
+	int do_copyregs = 0;
+
+	/*
+	 * Copy register values that aren't redirected by hardware.
+	 *
+	 * Before code patching, we only set tpidr_el1, all CPUs need to copy
+	 * this value to tpidr_el2 before we patch the code. Once we've done
+	 * that, freshly-onlined CPUs will set tpidr_el2, so we don't need to
+	 * do anything here.
+	 */
+	asm volatile(ALTERNATIVE("mov %0, #1", "nop", ARM64_HAS_VIRT_HOST_EXTN)
+		    : "=r" (do_copyregs) : : );
+
+	if (do_copyregs)
+		write_sysreg(read_sysreg(tpidr_el1), tpidr_el2);
+
+	return 0;
+}
diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S
index 877d42fb0df6..109d43a15aaf 100644
--- a/arch/arm64/mm/proc.S
+++ b/arch/arm64/mm/proc.S
@@ -70,7 +70,11 @@ ENTRY(cpu_do_suspend)
 	mrs	x8, mdscr_el1
 	mrs	x9, oslsr_el1
 	mrs	x10, sctlr_el1
+alternative_if_not ARM64_HAS_VIRT_HOST_EXTN
 	mrs	x11, tpidr_el1
+alternative_else
+	mrs	x11, tpidr_el2
+alternative_endif
 	mrs	x12, sp_el0
 	stp	x2, x3, [x0]
 	stp	x4, xzr, [x0, #16]
@@ -116,7 +120,11 @@ ENTRY(cpu_do_resume)
 	msr	mdscr_el1, x10
 
 	msr	sctlr_el1, x12
+alternative_if_not ARM64_HAS_VIRT_HOST_EXTN
 	msr	tpidr_el1, x13
+alternative_else
+	msr	tpidr_el2, x13
+alternative_endif
 	msr	sp_el0, x14
 	/*
 	 * Restore oslsr_el1 by writing oslar_el1
-- 
2.10.1

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

* [PATCH 05/11] arm64: KVM: Stop save/restoring host tpidr_el1 on VHE
  2017-05-15 17:43 ` James Morse
@ 2017-05-15 17:43   ` James Morse
  -1 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-05-15 17:43 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: devicetree, Marc Zyngier, Catalin Marinas, Will Deacon,
	Rob Herring, kvmarm

Now that a VHE host uses tpidr_el2 for the cpu offset we no longer
need KVM to save/restore tpidr_el1. Move this from the 'common' code
into the non-vhe code. While we're at it, on VHE we don't need to
save the ELR or SPSR as kernel_entry in entry.S will have pushed these
onto the kernel stack, and will restore them from there. Move these
to the non-vhe code as we need them to get back to the host.

Finally remove the always-copy-tpidr we hid in the stage2 setup
code, cpufeature's enable callback will do this for VHE, we only
need KVM to do it for non-vhe. Add the copy into kvm-init instead.

Signed-off-by: James Morse <james.morse@arm.com>
---
 arch/arm64/kvm/hyp-init.S      |  4 ++++
 arch/arm64/kvm/hyp/s2-setup.c  |  3 ---
 arch/arm64/kvm/hyp/sysreg-sr.c | 16 ++++++++--------
 3 files changed, 12 insertions(+), 11 deletions(-)

diff --git a/arch/arm64/kvm/hyp-init.S b/arch/arm64/kvm/hyp-init.S
index 839425c24b1c..b8219801bb28 100644
--- a/arch/arm64/kvm/hyp-init.S
+++ b/arch/arm64/kvm/hyp-init.S
@@ -119,6 +119,10 @@ __do_hyp_init:
 	kern_hyp_va	x2
 	msr	vbar_el2, x2
 
+	/* copy tpidr_el1 into tpidr_el2 for use by HYP */
+	mrs	x1, tpidr_el1
+	msr	tpidr_el2, x1
+
 	/* Hello, World! */
 	eret
 ENDPROC(__kvm_hyp_init)
diff --git a/arch/arm64/kvm/hyp/s2-setup.c b/arch/arm64/kvm/hyp/s2-setup.c
index eb401dbb285e..b81f4091c909 100644
--- a/arch/arm64/kvm/hyp/s2-setup.c
+++ b/arch/arm64/kvm/hyp/s2-setup.c
@@ -84,8 +84,5 @@ u32 __hyp_text __init_stage2_translation(void)
 
 	write_sysreg(val, vtcr_el2);
 
-	/* copy tpidr_el1 into tpidr_el2 for use by HYP */
-	write_sysreg(read_sysreg(tpidr_el1), tpidr_el2);
-
 	return parange;
 }
diff --git a/arch/arm64/kvm/hyp/sysreg-sr.c b/arch/arm64/kvm/hyp/sysreg-sr.c
index 934137647837..c54cc2afb92b 100644
--- a/arch/arm64/kvm/hyp/sysreg-sr.c
+++ b/arch/arm64/kvm/hyp/sysreg-sr.c
@@ -27,8 +27,8 @@ static void __hyp_text __sysreg_do_nothing(struct kvm_cpu_context *ctxt) { }
 /*
  * Non-VHE: Both host and guest must save everything.
  *
- * VHE: Host must save tpidr*_el[01], actlr_el1, mdscr_el1, sp0, pc,
- * pstate, and guest must save everything.
+ * VHE: Host must save tpidr*_el0, actlr_el1, mdscr_el1, sp_el0,
+ * and guest must save everything.
  */
 
 static void __hyp_text __sysreg_save_common_state(struct kvm_cpu_context *ctxt)
@@ -36,11 +36,8 @@ static void __hyp_text __sysreg_save_common_state(struct kvm_cpu_context *ctxt)
 	ctxt->sys_regs[ACTLR_EL1]	= read_sysreg(actlr_el1);
 	ctxt->sys_regs[TPIDR_EL0]	= read_sysreg(tpidr_el0);
 	ctxt->sys_regs[TPIDRRO_EL0]	= read_sysreg(tpidrro_el0);
-	ctxt->sys_regs[TPIDR_EL1]	= read_sysreg(tpidr_el1);
 	ctxt->sys_regs[MDSCR_EL1]	= read_sysreg(mdscr_el1);
 	ctxt->gp_regs.regs.sp		= read_sysreg(sp_el0);
-	ctxt->gp_regs.regs.pc		= read_sysreg_el2(elr);
-	ctxt->gp_regs.regs.pstate	= read_sysreg_el2(spsr);
 }
 
 static void __hyp_text __sysreg_save_state(struct kvm_cpu_context *ctxt)
@@ -62,10 +59,13 @@ static void __hyp_text __sysreg_save_state(struct kvm_cpu_context *ctxt)
 	ctxt->sys_regs[AMAIR_EL1]	= read_sysreg_el1(amair);
 	ctxt->sys_regs[CNTKCTL_EL1]	= read_sysreg_el1(cntkctl);
 	ctxt->sys_regs[PAR_EL1]		= read_sysreg(par_el1);
+	ctxt->sys_regs[TPIDR_EL1]	= read_sysreg(tpidr_el1);
 
 	ctxt->gp_regs.sp_el1		= read_sysreg(sp_el1);
 	ctxt->gp_regs.elr_el1		= read_sysreg_el1(elr);
 	ctxt->gp_regs.spsr[KVM_SPSR_EL1]= read_sysreg_el1(spsr);
+	ctxt->gp_regs.regs.pc		= read_sysreg_el2(elr);
+	ctxt->gp_regs.regs.pstate	= read_sysreg_el2(spsr);
 }
 
 static hyp_alternate_select(__sysreg_call_save_host_state,
@@ -89,11 +89,8 @@ static void __hyp_text __sysreg_restore_common_state(struct kvm_cpu_context *ctx
 	write_sysreg(ctxt->sys_regs[ACTLR_EL1],	  actlr_el1);
 	write_sysreg(ctxt->sys_regs[TPIDR_EL0],	  tpidr_el0);
 	write_sysreg(ctxt->sys_regs[TPIDRRO_EL0], tpidrro_el0);
-	write_sysreg(ctxt->sys_regs[TPIDR_EL1],	  tpidr_el1);
 	write_sysreg(ctxt->sys_regs[MDSCR_EL1],	  mdscr_el1);
 	write_sysreg(ctxt->gp_regs.regs.sp,	  sp_el0);
-	write_sysreg_el2(ctxt->gp_regs.regs.pc,	  elr);
-	write_sysreg_el2(ctxt->gp_regs.regs.pstate, spsr);
 }
 
 static void __hyp_text __sysreg_restore_state(struct kvm_cpu_context *ctxt)
@@ -115,10 +112,13 @@ static void __hyp_text __sysreg_restore_state(struct kvm_cpu_context *ctxt)
 	write_sysreg_el1(ctxt->sys_regs[AMAIR_EL1],	amair);
 	write_sysreg_el1(ctxt->sys_regs[CNTKCTL_EL1], 	cntkctl);
 	write_sysreg(ctxt->sys_regs[PAR_EL1],		par_el1);
+	write_sysreg(ctxt->sys_regs[TPIDR_EL1],		tpidr_el1);
 
 	write_sysreg(ctxt->gp_regs.sp_el1,		sp_el1);
 	write_sysreg_el1(ctxt->gp_regs.elr_el1,		elr);
 	write_sysreg_el1(ctxt->gp_regs.spsr[KVM_SPSR_EL1],spsr);
+	write_sysreg_el2(ctxt->gp_regs.regs.pc,		elr);
+	write_sysreg_el2(ctxt->gp_regs.regs.pstate,	spsr);
 }
 
 static hyp_alternate_select(__sysreg_call_restore_host_state,
-- 
2.10.1

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

* [PATCH 05/11] arm64: KVM: Stop save/restoring host tpidr_el1 on VHE
@ 2017-05-15 17:43   ` James Morse
  0 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-05-15 17:43 UTC (permalink / raw)
  To: linux-arm-kernel

Now that a VHE host uses tpidr_el2 for the cpu offset we no longer
need KVM to save/restore tpidr_el1. Move this from the 'common' code
into the non-vhe code. While we're at it, on VHE we don't need to
save the ELR or SPSR as kernel_entry in entry.S will have pushed these
onto the kernel stack, and will restore them from there. Move these
to the non-vhe code as we need them to get back to the host.

Finally remove the always-copy-tpidr we hid in the stage2 setup
code, cpufeature's enable callback will do this for VHE, we only
need KVM to do it for non-vhe. Add the copy into kvm-init instead.

Signed-off-by: James Morse <james.morse@arm.com>
---
 arch/arm64/kvm/hyp-init.S      |  4 ++++
 arch/arm64/kvm/hyp/s2-setup.c  |  3 ---
 arch/arm64/kvm/hyp/sysreg-sr.c | 16 ++++++++--------
 3 files changed, 12 insertions(+), 11 deletions(-)

diff --git a/arch/arm64/kvm/hyp-init.S b/arch/arm64/kvm/hyp-init.S
index 839425c24b1c..b8219801bb28 100644
--- a/arch/arm64/kvm/hyp-init.S
+++ b/arch/arm64/kvm/hyp-init.S
@@ -119,6 +119,10 @@ __do_hyp_init:
 	kern_hyp_va	x2
 	msr	vbar_el2, x2
 
+	/* copy tpidr_el1 into tpidr_el2 for use by HYP */
+	mrs	x1, tpidr_el1
+	msr	tpidr_el2, x1
+
 	/* Hello, World! */
 	eret
 ENDPROC(__kvm_hyp_init)
diff --git a/arch/arm64/kvm/hyp/s2-setup.c b/arch/arm64/kvm/hyp/s2-setup.c
index eb401dbb285e..b81f4091c909 100644
--- a/arch/arm64/kvm/hyp/s2-setup.c
+++ b/arch/arm64/kvm/hyp/s2-setup.c
@@ -84,8 +84,5 @@ u32 __hyp_text __init_stage2_translation(void)
 
 	write_sysreg(val, vtcr_el2);
 
-	/* copy tpidr_el1 into tpidr_el2 for use by HYP */
-	write_sysreg(read_sysreg(tpidr_el1), tpidr_el2);
-
 	return parange;
 }
diff --git a/arch/arm64/kvm/hyp/sysreg-sr.c b/arch/arm64/kvm/hyp/sysreg-sr.c
index 934137647837..c54cc2afb92b 100644
--- a/arch/arm64/kvm/hyp/sysreg-sr.c
+++ b/arch/arm64/kvm/hyp/sysreg-sr.c
@@ -27,8 +27,8 @@ static void __hyp_text __sysreg_do_nothing(struct kvm_cpu_context *ctxt) { }
 /*
  * Non-VHE: Both host and guest must save everything.
  *
- * VHE: Host must save tpidr*_el[01], actlr_el1, mdscr_el1, sp0, pc,
- * pstate, and guest must save everything.
+ * VHE: Host must save tpidr*_el0, actlr_el1, mdscr_el1, sp_el0,
+ * and guest must save everything.
  */
 
 static void __hyp_text __sysreg_save_common_state(struct kvm_cpu_context *ctxt)
@@ -36,11 +36,8 @@ static void __hyp_text __sysreg_save_common_state(struct kvm_cpu_context *ctxt)
 	ctxt->sys_regs[ACTLR_EL1]	= read_sysreg(actlr_el1);
 	ctxt->sys_regs[TPIDR_EL0]	= read_sysreg(tpidr_el0);
 	ctxt->sys_regs[TPIDRRO_EL0]	= read_sysreg(tpidrro_el0);
-	ctxt->sys_regs[TPIDR_EL1]	= read_sysreg(tpidr_el1);
 	ctxt->sys_regs[MDSCR_EL1]	= read_sysreg(mdscr_el1);
 	ctxt->gp_regs.regs.sp		= read_sysreg(sp_el0);
-	ctxt->gp_regs.regs.pc		= read_sysreg_el2(elr);
-	ctxt->gp_regs.regs.pstate	= read_sysreg_el2(spsr);
 }
 
 static void __hyp_text __sysreg_save_state(struct kvm_cpu_context *ctxt)
@@ -62,10 +59,13 @@ static void __hyp_text __sysreg_save_state(struct kvm_cpu_context *ctxt)
 	ctxt->sys_regs[AMAIR_EL1]	= read_sysreg_el1(amair);
 	ctxt->sys_regs[CNTKCTL_EL1]	= read_sysreg_el1(cntkctl);
 	ctxt->sys_regs[PAR_EL1]		= read_sysreg(par_el1);
+	ctxt->sys_regs[TPIDR_EL1]	= read_sysreg(tpidr_el1);
 
 	ctxt->gp_regs.sp_el1		= read_sysreg(sp_el1);
 	ctxt->gp_regs.elr_el1		= read_sysreg_el1(elr);
 	ctxt->gp_regs.spsr[KVM_SPSR_EL1]= read_sysreg_el1(spsr);
+	ctxt->gp_regs.regs.pc		= read_sysreg_el2(elr);
+	ctxt->gp_regs.regs.pstate	= read_sysreg_el2(spsr);
 }
 
 static hyp_alternate_select(__sysreg_call_save_host_state,
@@ -89,11 +89,8 @@ static void __hyp_text __sysreg_restore_common_state(struct kvm_cpu_context *ctx
 	write_sysreg(ctxt->sys_regs[ACTLR_EL1],	  actlr_el1);
 	write_sysreg(ctxt->sys_regs[TPIDR_EL0],	  tpidr_el0);
 	write_sysreg(ctxt->sys_regs[TPIDRRO_EL0], tpidrro_el0);
-	write_sysreg(ctxt->sys_regs[TPIDR_EL1],	  tpidr_el1);
 	write_sysreg(ctxt->sys_regs[MDSCR_EL1],	  mdscr_el1);
 	write_sysreg(ctxt->gp_regs.regs.sp,	  sp_el0);
-	write_sysreg_el2(ctxt->gp_regs.regs.pc,	  elr);
-	write_sysreg_el2(ctxt->gp_regs.regs.pstate, spsr);
 }
 
 static void __hyp_text __sysreg_restore_state(struct kvm_cpu_context *ctxt)
@@ -115,10 +112,13 @@ static void __hyp_text __sysreg_restore_state(struct kvm_cpu_context *ctxt)
 	write_sysreg_el1(ctxt->sys_regs[AMAIR_EL1],	amair);
 	write_sysreg_el1(ctxt->sys_regs[CNTKCTL_EL1], 	cntkctl);
 	write_sysreg(ctxt->sys_regs[PAR_EL1],		par_el1);
+	write_sysreg(ctxt->sys_regs[TPIDR_EL1],		tpidr_el1);
 
 	write_sysreg(ctxt->gp_regs.sp_el1,		sp_el1);
 	write_sysreg_el1(ctxt->gp_regs.elr_el1,		elr);
 	write_sysreg_el1(ctxt->gp_regs.spsr[KVM_SPSR_EL1],spsr);
+	write_sysreg_el2(ctxt->gp_regs.regs.pc,		elr);
+	write_sysreg_el2(ctxt->gp_regs.regs.pstate,	spsr);
 }
 
 static hyp_alternate_select(__sysreg_call_restore_host_state,
-- 
2.10.1

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

* [PATCH 06/11] dt-bindings: add devicetree binding for describing arm64 SDEI firmware
  2017-05-15 17:43 ` James Morse
@ 2017-05-15 17:43     ` James Morse
  -1 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-05-15 17:43 UTC (permalink / raw)
  To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Will Deacon, Catalin Marinas,
	kvmarm-FPEHb7Xf0XXUo1n7N8X6UoWGPAHP3yOg, Marc Zyngier,
	Christoffer Dall, Rob Herring, Mark Rutland,
	devicetree-u79uwXL29TY76Z2rM5mHXA

The Software Delegated Exception Interface (SDEI) is an ARM standard
for registering callbacks from the platform firmware into the OS.
This is typically used to implement RAS notifications.

Add a new devicetree binding to describe the SDE firmware interface.

Signed-off-by: James Morse <james.morse-5wv7dgnIgG8@public.gmane.org>
---
 Documentation/devicetree/bindings/arm/sdei.txt | 37 ++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/sdei.txt

diff --git a/Documentation/devicetree/bindings/arm/sdei.txt b/Documentation/devicetree/bindings/arm/sdei.txt
new file mode 100644
index 000000000000..69220d995286
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/sdei.txt
@@ -0,0 +1,37 @@
+* Software Delegated Exception Interface (SDEI)
+
+Firmware implementing the SDEI functions described in ARM document number
+ARM DEN 0054A ("Software Delegated Exception Interface") can be used by
+Linux to receive notification of events such as those generated by
+firmware-first error handling.
+
+The interface provides a number of API functions for registering callbacks
+and enabling/disabling events. Functions are invoked by trapping to the
+privilege level of the SDEI firmware (specified as part of the binding
+below) and passing arguments in a manner similar to that specified by AAPCS:
+
+	 r0		=> 32-bit Function ID / return value
+	{r1 - r3}	=> Parameters
+
+Note that the immediate field of the trapping instruction must be set
+to #0.
+
+The SDEI_EVENT_REGISTER function registers a callback in the kernel
+text to handle the specified event number.
+
+Main node required properties:
+
+ - compatible    : should contain:
+	* "arm,sdei-1.0" : For implementations complying to SDEI version 1.x.
+
+ - method        : The method of calling the SDEI firmware. Permitted
+                   values are:
+	* "smc" : SMC #0, with the register assignments specified in this
+	          binding.
+	* "hvc" : HVC #0, with the register assignments specified in this
+	          binding.
+Example:
+	sdei {
+		compatible	= "arm,sdei-1.0";
+		method		= "smc";
+	};
-- 
2.10.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 06/11] dt-bindings: add devicetree binding for describing arm64 SDEI firmware
@ 2017-05-15 17:43     ` James Morse
  0 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-05-15 17:43 UTC (permalink / raw)
  To: linux-arm-kernel

The Software Delegated Exception Interface (SDEI) is an ARM standard
for registering callbacks from the platform firmware into the OS.
This is typically used to implement RAS notifications.

Add a new devicetree binding to describe the SDE firmware interface.

Signed-off-by: James Morse <james.morse@arm.com>
---
 Documentation/devicetree/bindings/arm/sdei.txt | 37 ++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/sdei.txt

diff --git a/Documentation/devicetree/bindings/arm/sdei.txt b/Documentation/devicetree/bindings/arm/sdei.txt
new file mode 100644
index 000000000000..69220d995286
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/sdei.txt
@@ -0,0 +1,37 @@
+* Software Delegated Exception Interface (SDEI)
+
+Firmware implementing the SDEI functions described in ARM document number
+ARM DEN 0054A ("Software Delegated Exception Interface") can be used by
+Linux to receive notification of events such as those generated by
+firmware-first error handling.
+
+The interface provides a number of API functions for registering callbacks
+and enabling/disabling events. Functions are invoked by trapping to the
+privilege level of the SDEI firmware (specified as part of the binding
+below) and passing arguments in a manner similar to that specified by AAPCS:
+
+	 r0		=> 32-bit Function ID / return value
+	{r1 - r3}	=> Parameters
+
+Note that the immediate field of the trapping instruction must be set
+to #0.
+
+The SDEI_EVENT_REGISTER function registers a callback in the kernel
+text to handle the specified event number.
+
+Main node required properties:
+
+ - compatible    : should contain:
+	* "arm,sdei-1.0" : For implementations complying to SDEI version 1.x.
+
+ - method        : The method of calling the SDEI firmware. Permitted
+                   values are:
+	* "smc" : SMC #0, with the register assignments specified in this
+	          binding.
+	* "hvc" : HVC #0, with the register assignments specified in this
+	          binding.
+Example:
+	sdei {
+		compatible	= "arm,sdei-1.0";
+		method		= "smc";
+	};
-- 
2.10.1

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

* [PATCH 07/11] firmware: arm_sdei: Add driver for Software Delegated Exceptions
  2017-05-15 17:43 ` James Morse
@ 2017-05-15 17:43   ` James Morse
  -1 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-05-15 17:43 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: devicetree, Marc Zyngier, Catalin Marinas, Will Deacon,
	Rob Herring, kvmarm

The Software Delegated Exception Interface (SDEI) is an ARM standard
for registering callbacks from the platform firmware into the OS.
This is typically used to implement RAS notifications.

Add the code for detecting the SDEI version and the framework for
registering and unregistering events. Subsequent patches will add the
arch-specific backend code and the necessary power management hooks.

Currently only shared events are supported.

Signed-off-by: James Morse <james.morse@arm.com>
---
 drivers/firmware/Kconfig    |   7 +
 drivers/firmware/Makefile   |   1 +
 drivers/firmware/arm_sdei.c | 606 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/sdei.h        |  98 +++++++
 include/uapi/linux/sdei.h   |  91 +++++++
 5 files changed, 803 insertions(+)
 create mode 100644 drivers/firmware/arm_sdei.c
 create mode 100644 include/linux/sdei.h
 create mode 100644 include/uapi/linux/sdei.h

diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 6e4ed5a9c6fd..d8a9859f6102 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -48,6 +48,13 @@ config ARM_SCPI_POWER_DOMAIN
 	  This enables support for the SCPI power domains which can be
 	  enabled or disabled via the SCP firmware
 
+config ARM_SDE_INTERFACE
+	bool "ARM Software Delegated Exception Interface (SDEI)"
+	help
+	  The Software Delegated Exception Interface (SDEI) is an ARM
+	  standard for registering callbacks from the platform firmware
+	  into the OS. This is typically used to implement RAS notifications.
+
 config EDD
 	tristate "BIOS Enhanced Disk Drive calls determine boot disk"
 	depends on X86
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index a37f12e8d137..229984feca4a 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -5,6 +5,7 @@ obj-$(CONFIG_ARM_PSCI_FW)	+= psci.o
 obj-$(CONFIG_ARM_PSCI_CHECKER)	+= psci_checker.o
 obj-$(CONFIG_ARM_SCPI_PROTOCOL)	+= arm_scpi.o
 obj-$(CONFIG_ARM_SCPI_POWER_DOMAIN) += scpi_pm_domain.o
+obj-$(CONFIG_ARM_SDE_INTERFACE)	+= arm_sdei.o
 obj-$(CONFIG_DMI)		+= dmi_scan.o
 obj-$(CONFIG_DMI_SYSFS)		+= dmi-sysfs.o
 obj-$(CONFIG_EDD)		+= edd.o
diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c
new file mode 100644
index 000000000000..d22dda5e0fed
--- /dev/null
+++ b/drivers/firmware/arm_sdei.c
@@ -0,0 +1,606 @@
+/*
+ * Software Delegated Exception Interface (SDEI) driver code.
+ *
+ * Copyright (C) 2017 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#define pr_fmt(fmt) "sdei: " fmt
+
+#include <linux/acpi.h>
+#include <linux/arm-smccc.h>
+#include <linux/bitops.h>
+#include <linux/compiler.h>
+#include <linux/errno.h>
+#include <linux/hardirq.h>
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+#include <linux/kvm_host.h>
+#include <linux/list.h>
+#include <linux/of.h>
+#include <linux/percpu.h>
+#include <linux/platform_device.h>
+#include <linux/ptrace.h>
+#include <linux/preempt.h>
+#include <linux/sdei.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <uapi/linux/sdei.h>
+
+/*
+ * The call to use to reach the firmware.
+ */
+static asmlinkage void (*sdei_firmware_call)(unsigned long function_id,
+		      unsigned long arg0, unsigned long arg1,
+		      unsigned long arg2, unsigned long arg3,
+		      unsigned long arg4, struct arm_smccc_res *res);
+
+/* entry point from firmware to arch asm code */
+static void (*sdei_entry_point)(unsigned long, unsigned long);
+
+struct sdei_event {
+	struct list_head	list;
+	u32			event_num;
+	u64			type;
+	u64			priority;
+
+	/* This pointer is handed to firmware as the event argument. */
+	struct sdei_registered_event *registered;
+};
+
+static LIST_HEAD(sdei_events);
+static DEFINE_SPINLOCK(sdei_events_lock);
+
+static int sdei_to_linux_errno(unsigned long sdei_err)
+{
+	switch (sdei_err) {
+	case SDEI_NOT_SUPPORTED:
+		return -EOPNOTSUPP;
+	case SDEI_INVALID_PARAMETERS:
+		return -EINVAL;
+	case SDEI_DENIED:
+		return -EPERM;
+	case SDEI_PENDING:
+		return -EINPROGRESS;
+	case SDEI_OUT_OF_RESOURCE:
+		return -ENOMEM;
+	}
+
+	/* Not an error value ... */
+	return sdei_err;
+}
+
+/*
+ * If x0 is any of these values, then the call failed, use sdei_to_linux_errno()
+ * to translate.
+ */
+static int sdei_is_err(struct arm_smccc_res *res)
+{
+	switch (res->a0) {
+	case SDEI_NOT_SUPPORTED:
+	case SDEI_INVALID_PARAMETERS:
+	case SDEI_DENIED:
+	case SDEI_PENDING:
+	case SDEI_OUT_OF_RESOURCE:
+		return true;
+	}
+
+	return false;
+}
+
+static int invoke_sdei_fn(struct arm_smccc_res *res, unsigned long function_id,
+			  unsigned long arg0, unsigned long arg1,
+			  unsigned long arg2, unsigned long arg3,
+			  unsigned long arg4)
+{
+	int err = 0;
+
+	if (sdei_firmware_call) {
+		sdei_firmware_call(function_id, arg0, arg1, arg2, arg3, arg4, res);
+		if (sdei_is_err(res))
+			err = sdei_to_linux_errno(res->a0);
+	} else {
+		/*
+		 * !sdei_firmware_call means we failed to probe or called
+		 * sdei_mark_interface_broken(). -EIO is not an error returned
+		 * by sdei_to_linux_errno() and is used to suppress messages
+		 * from this driver.
+		 */
+		err = -EIO;
+		res->a0 = SDEI_NOT_SUPPORTED;
+	}
+
+	return err;
+}
+
+static struct sdei_event *sdei_event_find(u32 event_num)
+{
+	struct sdei_event *e, *found = NULL;
+
+	WARN_ON(!spin_is_locked(&sdei_events_lock));
+
+	list_for_each_entry(e, &sdei_events, list) {
+		if (e->event_num == event_num) {
+			found = e;
+			break;
+		}
+	}
+
+	return found;
+}
+
+int sdei_api_event_context(u32 query, u64 *result)
+{
+	int err;
+	struct arm_smccc_res res;
+
+	err = invoke_sdei_fn(&res, SDEI_1_0_FN_SDEI_EVENT_CONTEXT, query, 0, 0,
+			     0, 0);
+	*result = res.a0;
+	return err;
+}
+
+static int sdei_api_event_get_info(u32 event, u32 info, u64 *result)
+{
+	int err;
+	struct arm_smccc_res res;
+
+	err = invoke_sdei_fn(&res, SDEI_1_0_FN_SDEI_EVENT_GET_INFO, event, info,
+			     0, 0, 0);
+	*result = res.a0;
+	return err;
+}
+
+static struct sdei_event *sdei_event_create(u32 event_num,
+					    sdei_event_callback *cb,
+					    void *cb_arg)
+{
+	int err;
+	struct sdei_event *event;
+	struct sdei_registered_event *reg;
+
+	WARN_ON(!spin_is_locked(&sdei_events_lock));
+
+	event = kzalloc(sizeof(*event), GFP_KERNEL);
+	if (!event)
+		return ERR_PTR(-ENOMEM);
+
+	INIT_LIST_HEAD(&event->list);
+	event->event_num = event_num;
+
+	err = sdei_api_event_get_info(event_num, SDEI_EVENT_INFO_EV_PRIORITY,
+					&event->priority);
+	if (err) {
+		kfree(event);
+		return ERR_PTR(err);
+	}
+
+	err = sdei_api_event_get_info(event_num, SDEI_EVENT_INFO_EV_TYPE,
+					&event->type);
+	if (err) {
+		kfree(event);
+		return ERR_PTR(err);
+	}
+
+	if (event->type == SDEI_EVENT_TYPE_SHARED) {
+		reg = kzalloc(sizeof(*reg), GFP_KERNEL);
+		if (!reg) {
+			kfree(event);
+			return ERR_PTR(-ENOMEM);
+		}
+
+		reg->event_num = event_num;
+
+		reg->callback = cb;
+		reg->callback_arg = cb_arg;
+		event->registered = reg;
+	}
+
+	if (sdei_event_find(event_num)) {
+		kfree(event->registered);
+		kfree(event);
+		event = ERR_PTR(-EBUSY);
+	} else {
+		list_add(&event->list, &sdei_events);
+	}
+
+	return event;
+}
+
+static void sdei_event_destroy(struct sdei_event *event)
+{
+	WARN_ON(!spin_is_locked(&sdei_events_lock));
+
+	list_del(&event->list);
+
+	if (event->type == SDEI_EVENT_TYPE_SHARED)
+		kfree(event->registered);
+
+	kfree(event);
+}
+
+static int sdei_api_get_version(u64 *version)
+{
+	int err;
+	struct arm_smccc_res res;
+
+	err = invoke_sdei_fn(&res, SDEI_1_0_FN_SDEI_VERSION, 0, 0, 0, 0, 0);
+	if (err)
+		return err;
+
+	*version = res.a0;
+	return 0;
+}
+
+int sdei_mask_local_cpu(unsigned int cpu)
+{
+	int err;
+	struct arm_smccc_res res;
+
+	WARN_ON(preemptible());
+	err = invoke_sdei_fn(&res, SDEI_1_0_FN_SDEI_PE_MASK, 0, 0, 0, 0, 0);
+
+	if (err && err != -EIO) {
+		pr_warn_once("failed to mask CPU[%u]: %d\n",
+			      smp_processor_id(), err);
+		return err;
+	}
+
+	return 0;
+}
+
+static void _ipi_mask_cpu(void *ignored)
+{
+	sdei_mask_local_cpu(smp_processor_id());
+}
+
+int sdei_unmask_local_cpu(unsigned int ignored)
+{
+	int err;
+	struct arm_smccc_res res;
+
+	WARN_ON(preemptible());
+	err = invoke_sdei_fn(&res, SDEI_1_0_FN_SDEI_PE_UNMASK, 0, 0, 0, 0, 0);
+
+	if (err && err != -EIO) {
+		pr_warn_once("failed to unmask CPU[%u]: %d\n",
+			     smp_processor_id(), err);
+		return err;
+	}
+
+	return 0;
+}
+
+static void _ipi_unmask_cpu(void *ignored)
+{
+	sdei_unmask_local_cpu(smp_processor_id());
+}
+
+static void _ipi_private_reset(void *ignored)
+{
+	int err;
+	struct arm_smccc_res res;
+
+	err = invoke_sdei_fn(&res, SDEI_1_0_FN_SDEI_PRIVATE_RESET, 0, 0, 0, 0,
+			     0);
+
+	if (err && err != -EIO)
+		pr_warn_once("failed to reset CPU[%u]: %d\n",
+			     smp_processor_id(), err);
+}
+
+static int sdei_api_shared_reset(void)
+{
+	struct arm_smccc_res res;
+
+	return invoke_sdei_fn(&res, SDEI_1_0_FN_SDEI_SHARED_RESET, 0, 0, 0, 0,
+			      0);
+}
+
+static void sdei_mark_interface_broken(void)
+{
+	pr_err("disabling SDEI firmware interface\n");
+	on_each_cpu(&_ipi_mask_cpu, NULL, true);
+	sdei_firmware_call = NULL;
+}
+
+static int sdei_platform_reset(void)
+{
+	int err;
+
+	on_each_cpu(&_ipi_private_reset, NULL, true);
+	err = sdei_api_shared_reset();
+	if (err) {
+		pr_err("Failed to reset platform: %d\n", err);
+		sdei_mark_interface_broken();
+	}
+
+	return err;
+}
+
+static int sdei_api_event_enable(u32 event_num)
+{
+	struct arm_smccc_res res;
+
+	return invoke_sdei_fn(&res, SDEI_1_0_FN_SDEI_EVENT_ENABLE, event_num, 0,
+			      0, 0, 0);
+}
+
+int sdei_event_enable(u32 event_num)
+{
+	int err = -EINVAL;
+	struct sdei_event *event;
+
+	spin_lock(&sdei_events_lock);
+	event = sdei_event_find(event_num);
+	if (!event)
+		err = -ENOENT;
+	else if (event->type == SDEI_EVENT_TYPE_SHARED)
+		err = sdei_api_event_enable(event->event_num);
+	spin_unlock(&sdei_events_lock);
+
+	return err;
+}
+EXPORT_SYMBOL(sdei_event_enable);
+
+static int sdei_api_event_disable(u32 event_num)
+{
+	int err;
+	struct arm_smccc_res res;
+
+	err = invoke_sdei_fn(&res, SDEI_1_0_FN_SDEI_EVENT_DISABLE, event_num, 0,
+				0, 0, 0);
+
+	return err;
+}
+
+int sdei_event_disable(u32 event_num)
+{
+	int err = -EINVAL;
+	struct sdei_event *event;
+
+	spin_lock(&sdei_events_lock);
+	event = sdei_event_find(event_num);
+	if (!event)
+		err = -ENOENT;
+	else if (event->type == SDEI_EVENT_TYPE_SHARED)
+		err = sdei_api_event_disable(event->event_num);
+	spin_unlock(&sdei_events_lock);
+
+	return err;
+}
+EXPORT_SYMBOL(sdei_event_disable);
+
+static int sdei_api_event_unregister(u32 event_num)
+{
+	struct arm_smccc_res res;
+
+	return invoke_sdei_fn(&res, SDEI_1_0_FN_SDEI_EVENT_UNREGISTER,
+			      event_num, 0, 0, 0, 0);
+}
+
+static int _sdei_event_unregister(struct sdei_event *event)
+{
+	if (event->type == SDEI_EVENT_TYPE_SHARED)
+		return sdei_api_event_unregister(event->event_num);
+
+	return -EINVAL;
+}
+
+int sdei_event_unregister(u32 event_num)
+{
+	int err;
+	struct sdei_event *event;
+
+	WARN_ON(in_nmi());
+
+	spin_lock(&sdei_events_lock);
+	event = sdei_event_find(event_num);
+	do {
+		if (!event) {
+			pr_err("Event %u not registered\n", event_num);
+			err = -ENOENT;
+			break;
+		}
+
+		err = _sdei_event_unregister(event);
+		if (err)
+			break;
+
+		sdei_event_destroy(event);
+	} while (0);
+	spin_unlock(&sdei_events_lock);
+
+	return err;
+}
+EXPORT_SYMBOL(sdei_event_unregister);
+
+static int sdei_api_event_register(u32 event_num, void *entry_point, void *arg,
+			       u64 flags, u64 affinity)
+{
+	struct arm_smccc_res res;
+
+	return invoke_sdei_fn(&res, SDEI_1_0_FN_SDEI_EVENT_REGISTER, event_num,
+			      (unsigned long)entry_point, (unsigned long)arg,
+			      flags, affinity);
+}
+
+static int _sdei_event_register(struct sdei_event *event)
+{
+	if (event->type == SDEI_EVENT_TYPE_SHARED)
+		return sdei_api_event_register(event->event_num,
+					       sdei_entry_point,
+					       event->registered,
+					       SDEI_EVENT_REGISTER_RM_ANY, 0);
+
+	return -EINVAL;
+}
+
+int sdei_event_register(u32 event_num, sdei_event_callback *cb, void *arg)
+{
+	int err;
+	struct sdei_event *event;
+
+	WARN_ON(in_nmi());
+
+	spin_lock(&sdei_events_lock);
+	do {
+		if (sdei_event_find(event_num)) {
+			pr_err("Event %u already registered\n", event_num);
+			err = -EBUSY;
+			break;
+		}
+
+		event = sdei_event_create(event_num, cb, arg);
+		if (IS_ERR(event)) {
+			err = PTR_ERR(event);
+			pr_err("Failed to create event %u: %d\n", event_num, err);
+			break;
+		}
+
+		err = _sdei_event_register(event);
+		if (err) {
+			sdei_event_destroy(event);
+			pr_err("Failed to register event %u: %d\n", event_num, err);
+		}
+	} while (0);
+	spin_unlock(&sdei_events_lock);
+
+	return err;
+}
+EXPORT_SYMBOL(sdei_event_register);
+
+static void sdei_smccc_smc(unsigned long function_id,
+			   unsigned long arg0, unsigned long arg1,
+			   unsigned long arg2, unsigned long arg3,
+			   unsigned long arg4, struct arm_smccc_res *res)
+{
+	arm_smccc_smc(function_id, arg0, arg1, arg2, arg3, arg4, 0, 0, res);
+}
+
+static void sdei_smccc_hvc(unsigned long function_id,
+			   unsigned long arg0, unsigned long arg1,
+			   unsigned long arg2, unsigned long arg3,
+			   unsigned long arg4, struct arm_smccc_res *res)
+{
+	arm_smccc_hvc(function_id, arg0, arg1, arg2, arg3, arg4, 0, 0, res);
+}
+
+static int sdei_get_conduit(struct platform_device *pdev)
+{
+	const char *method;
+	struct device_node *np = pdev->dev.of_node;
+
+	sdei_firmware_call = NULL;
+	if (np) {
+		if (of_property_read_string(np, "method", &method)) {
+			pr_warn("missing \"method\" property\n");
+			return CONDUIT_INVALID;
+		}
+
+		if (!strcmp("hvc", method)) {
+			sdei_firmware_call = &sdei_smccc_hvc;
+			return CONDUIT_HVC;
+		} else if (!strcmp("smc", method)) {
+			sdei_firmware_call = &sdei_smccc_smc;
+			return CONDUIT_SMC;
+		}
+
+		pr_warn("invalid \"method\" property: %s\n", method);
+	}
+
+	return CONDUIT_INVALID;
+}
+
+static int sdei_probe(struct platform_device *pdev)
+{
+	int err;
+	u64 ver = 0;
+	int conduit;
+
+	conduit = sdei_get_conduit(pdev);
+	if (!sdei_firmware_call)
+		return 0;
+
+	err = sdei_api_get_version(&ver);
+	if (err == -EOPNOTSUPP)
+		pr_err("No SDEI implementation in platform firmware\n");
+	if (err) {
+		pr_err("Failed to get SDEI version: %d\n", err);
+		sdei_mark_interface_broken();
+		return err;
+	}
+
+	pr_info("SDEIv%d.%d (0x%x) detected in firmware.\n",
+		(int)SDEI_VERSION_MAJOR(ver), (int)SDEI_VERSION_MINOR(ver),
+		(int)SDEI_VERSION_VENDOR(ver));
+
+	if (SDEI_VERSION_MAJOR(ver) != 1) {
+		pr_err("Conflicting SDEI version detected.\n");
+		sdei_mark_interface_broken();
+		return -EINVAL;
+	}
+
+	err = sdei_platform_reset();
+	if (err)
+		return err;
+
+	sdei_entry_point = sdei_arch_get_entry_point(conduit);
+	if (!sdei_entry_point) {
+		/* Not supported due to hardware or boot configuration */
+		sdei_mark_interface_broken();
+		return 0;
+	}
+
+	on_each_cpu(&_ipi_unmask_cpu, NULL, false);
+
+	return 0;
+}
+
+static const struct of_device_id sdei_of_match[] = {
+	{ .compatible = "arm,sdei-1.0" },
+	{}
+};
+
+static struct platform_driver sdei_driver = {
+	.driver		= {
+		.name			= "sdei",
+		.of_match_table		= sdei_of_match,
+	},
+	.probe		= sdei_probe,
+};
+builtin_platform_driver(sdei_driver);
+
+int sdei_event_handler(struct pt_regs *regs,
+			       struct sdei_registered_event *arg)
+{
+	int err;
+	mm_segment_t orig_addr_limit;
+	u32 event_num = arg->event_num;
+
+	orig_addr_limit = get_fs();
+	set_fs(USER_DS);
+
+	err = arg->callback(event_num, regs, arg->callback_arg);
+	if (err)
+		pr_err("event %u on CPU %u failed with error: %d\n", event_num,
+		       smp_processor_id(), err);
+
+	set_fs(orig_addr_limit);
+
+	return err;
+}
+NOKPROBE_SYMBOL(sdei_event_handler);
diff --git a/include/linux/sdei.h b/include/linux/sdei.h
new file mode 100644
index 000000000000..504e8bf8bc07
--- /dev/null
+++ b/include/linux/sdei.h
@@ -0,0 +1,98 @@
+/*
+ * Software Delegated Exception Interface (SDEI) API
+ *
+ * Copyright (C) 2017 ARM Ltd.
+ *
+ * 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/>.
+ */
+
+#ifndef __LINUX_SDEI_H
+#define __LINUX_SDEI_H
+
+enum sdei_conduit_types {
+	CONDUIT_INVALID = 0,
+	CONDUIT_SMC,
+	CONDUIT_HVC,
+};
+
+#include <uapi/linux/sdei.h>
+
+/* Arch code should override this to set the entry point from firmware... */
+#ifndef sdei_arch_get_entry_point
+#define sdei_arch_get_entry_point(conduit)	(NULL)
+#endif
+
+/*
+ * When an event occurs sdei_event_handler() will call a user-provided callback
+ * like this in NMI context on the CPU that received the event.
+ */
+typedef int (sdei_event_callback)(u32 event, struct pt_regs *regs, void *arg);
+
+/*
+ * Register your callback to claim an event. The event must be described
+ * by firmware.
+ */
+int sdei_event_register(u32 event_num, sdei_event_callback *cb, void *arg);
+
+/*
+ * Calls to sdei_event_unregister() may return EINPROGRESS. Keep calling
+ * it until it succeeds.
+ */
+int sdei_event_unregister(u32 event_num);
+
+int sdei_event_enable(u32 event_num);
+int sdei_event_disable(u32 event_num);
+
+#ifdef CONFIG_ARM_SDE_INTERFACE
+/* For use by arch code when CPU hotplug notifiers are not appropriate. */
+int sdei_mask_local_cpu(unsigned int cpu);
+int sdei_unmask_local_cpu(unsigned int cpu);
+
+/* kvm needs to know if hvcs are for SDEI or PSCI */
+#define IS_SDEI_CALL(x)		((x & SDEI_1_0_MASK) == SDEI_1_0_FN_BASE)
+
+#else
+static inline int sdei_mask_local_cpu(unsigned int ignored) { return 0; }
+static inline int sdei_unmask_local_cpu(unsigned int ignored) { return 0; }
+
+#define IS_SDEI_CALL(x)		0
+#endif /* CONFIG_ARM_SDE_INTERFACE */
+
+
+
+/*
+ * This struct represents an event that has been registered. The driver
+ * maintains a list of all events, and which ones are registered. A pointer to
+ * this struct is passed to firmware, and back to the event handler. The event
+ * handler can then use this to invoke the registered callback, without having
+ * to walk the list.
+ *
+ * For CPU private events, this structure is per-cpu.
+ */
+struct sdei_registered_event {
+	/* For use by arch code: */
+	struct pt_regs          interrupted_regs;
+
+	sdei_event_callback	*callback;
+	void			*callback_arg;
+	u32			 event_num;
+};
+
+/* The arch code entry point should then call this when an event arrives. */
+int notrace sdei_event_handler(struct pt_regs *regs,
+			       struct sdei_registered_event *arg);
+
+/* arch code may use this to retrieve the extra registers. */
+int sdei_api_event_context(u32 query, u64 *result);
+
+#endif /* __LINUX_SDEI_H */
diff --git a/include/uapi/linux/sdei.h b/include/uapi/linux/sdei.h
new file mode 100644
index 000000000000..535c9d8d14cc
--- /dev/null
+++ b/include/uapi/linux/sdei.h
@@ -0,0 +1,91 @@
+/*
+ *Software Delegated Exception Interface (SDEI) Firmware API
+ *
+ * This header holds common SDEI defines and macros shared
+ * by: ARM64 kernel, KVM and user space.
+ *
+ * Copyright (C) 2017 ARM Ltd.
+ *
+ * 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/>.
+ */
+#ifndef _UAPI_LINUX_SDEI_H
+#define _UAPI_LINUX_SDEI_H
+
+#define SDEI_1_0_FN_BASE			0xC4000020
+#define SDEI_1_0_MASK				0xFFFFFFE0
+#define SDEI_1_0_FN(n)				(SDEI_1_0_FN_BASE + (n))
+
+#define SDEI_1_0_FN_SDEI_VERSION			SDEI_1_0_FN(0x00)
+#define SDEI_1_0_FN_SDEI_EVENT_REGISTER			SDEI_1_0_FN(0x01)
+#define SDEI_1_0_FN_SDEI_EVENT_ENABLE			SDEI_1_0_FN(0x02)
+#define SDEI_1_0_FN_SDEI_EVENT_DISABLE			SDEI_1_0_FN(0x03)
+#define SDEI_1_0_FN_SDEI_EVENT_CONTEXT			SDEI_1_0_FN(0x04)
+#define SDEI_1_0_FN_SDEI_EVENT_COMPLETE			SDEI_1_0_FN(0x05)
+#define SDEI_1_0_FN_SDEI_EVENT_COMPLETE_AND_RESUME	SDEI_1_0_FN(0x06)
+#define SDEI_1_0_FN_SDEI_EVENT_UNREGISTER		SDEI_1_0_FN(0x07)
+#define SDEI_1_0_FN_SDEI_EVENT_STATUS			SDEI_1_0_FN(0x08)
+#define SDEI_1_0_FN_SDEI_EVENT_GET_INFO			SDEI_1_0_FN(0x09)
+#define SDEI_1_0_FN_SDEI_EVENT_ROUTING_SET		SDEI_1_0_FN(0x0A)
+#define SDEI_1_0_FN_SDEI_PE_MASK			SDEI_1_0_FN(0x0B)
+#define SDEI_1_0_FN_SDEI_PE_UNMASK			SDEI_1_0_FN(0x0C)
+#define SDEI_1_0_FN_SDEI_INTERRUPT_BIND			SDEI_1_0_FN(0x0D)
+#define SDEI_1_0_FN_SDEI_INTERRUPT_RELEASE		SDEI_1_0_FN(0x0E)
+#define SDEI_1_0_FN_SDEI_PRIVATE_RESET			SDEI_1_0_FN(0x11)
+#define SDEI_1_0_FN_SDEI_SHARED_RESET			SDEI_1_0_FN(0x12)
+
+#define SDEI_VERSION_MAJOR_SHIFT			48
+#define SDEI_VERSION_MAJOR_MASK				0x7fff
+#define SDEI_VERSION_MINOR_SHIFT			32
+#define SDEI_VERSION_MINOR_MASK				0xffff
+#define SDEI_VERSION_VENDOR_SHIFT			0
+#define SDEI_VERSION_VENDOR_MASK			0xffffffff
+
+#define SDEI_VERSION_MAJOR(x)	(x>>SDEI_VERSION_MAJOR_SHIFT & SDEI_VERSION_MAJOR_MASK)
+#define SDEI_VERSION_MINOR(x)	(x>>SDEI_VERSION_MINOR_SHIFT & SDEI_VERSION_MINOR_MASK)
+#define SDEI_VERSION_VENDOR(x)	(x>>SDEI_VERSION_VENDOR_SHIFT & SDEI_VERSION_VENDOR_MASK)
+
+/* SDEI return values */
+#define SDEI_SUCCESS		0
+#define SDEI_NOT_SUPPORTED	-1
+#define SDEI_INVALID_PARAMETERS	-2
+#define SDEI_DENIED		-3
+#define SDEI_PENDING		-5
+#define SDEI_OUT_OF_RESOURCE	-10
+
+/* EVENT_REGISTER flags */
+#define SDEI_EVENT_REGISTER_RM_ANY	0
+#define SDEI_EVENT_REGISTER_RM_PE	1
+
+/* EVENT_STATUS return value bits */
+#define SDEI_EVENT_STATUS_RUNNING	2
+#define SDEI_EVENT_STATUS_ENABLED	1
+#define SDEI_EVENT_STATUS_REGISTERED	0
+
+/* EVENT_COMPLETE status values */
+#define SDEI_EV_HANDLED	0
+#define SDEI_EV_FAILED	1
+
+/* GET_INFO values */
+#define SDEI_EVENT_INFO_EV_TYPE			0
+#define SDEI_EVENT_INFO_EV_SIGNALED		1
+#define SDEI_EVENT_INFO_EV_PRIORITY		2
+#define SDEI_EVENT_INFO_EV_ROUTING_MODE		3
+#define SDEI_EVENT_INFO_EV_ROUTING_AFF		4
+
+/* and their results */
+#define SDEI_EVENT_TYPE_PRIVATE			0
+#define SDEI_EVENT_TYPE_SHARED			1
+#define SDEI_EVENT_PRIORITY_NORMAL		0
+#define SDEI_EVENT_PRIORITY_CRITICAL		1
+
+#endif /* _UAPI_LINUX_SDEI_H */
-- 
2.10.1

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

* [PATCH 07/11] firmware: arm_sdei: Add driver for Software Delegated Exceptions
@ 2017-05-15 17:43   ` James Morse
  0 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-05-15 17:43 UTC (permalink / raw)
  To: linux-arm-kernel

The Software Delegated Exception Interface (SDEI) is an ARM standard
for registering callbacks from the platform firmware into the OS.
This is typically used to implement RAS notifications.

Add the code for detecting the SDEI version and the framework for
registering and unregistering events. Subsequent patches will add the
arch-specific backend code and the necessary power management hooks.

Currently only shared events are supported.

Signed-off-by: James Morse <james.morse@arm.com>
---
 drivers/firmware/Kconfig    |   7 +
 drivers/firmware/Makefile   |   1 +
 drivers/firmware/arm_sdei.c | 606 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/sdei.h        |  98 +++++++
 include/uapi/linux/sdei.h   |  91 +++++++
 5 files changed, 803 insertions(+)
 create mode 100644 drivers/firmware/arm_sdei.c
 create mode 100644 include/linux/sdei.h
 create mode 100644 include/uapi/linux/sdei.h

diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 6e4ed5a9c6fd..d8a9859f6102 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -48,6 +48,13 @@ config ARM_SCPI_POWER_DOMAIN
 	  This enables support for the SCPI power domains which can be
 	  enabled or disabled via the SCP firmware
 
+config ARM_SDE_INTERFACE
+	bool "ARM Software Delegated Exception Interface (SDEI)"
+	help
+	  The Software Delegated Exception Interface (SDEI) is an ARM
+	  standard for registering callbacks from the platform firmware
+	  into the OS. This is typically used to implement RAS notifications.
+
 config EDD
 	tristate "BIOS Enhanced Disk Drive calls determine boot disk"
 	depends on X86
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index a37f12e8d137..229984feca4a 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -5,6 +5,7 @@ obj-$(CONFIG_ARM_PSCI_FW)	+= psci.o
 obj-$(CONFIG_ARM_PSCI_CHECKER)	+= psci_checker.o
 obj-$(CONFIG_ARM_SCPI_PROTOCOL)	+= arm_scpi.o
 obj-$(CONFIG_ARM_SCPI_POWER_DOMAIN) += scpi_pm_domain.o
+obj-$(CONFIG_ARM_SDE_INTERFACE)	+= arm_sdei.o
 obj-$(CONFIG_DMI)		+= dmi_scan.o
 obj-$(CONFIG_DMI_SYSFS)		+= dmi-sysfs.o
 obj-$(CONFIG_EDD)		+= edd.o
diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c
new file mode 100644
index 000000000000..d22dda5e0fed
--- /dev/null
+++ b/drivers/firmware/arm_sdei.c
@@ -0,0 +1,606 @@
+/*
+ * Software Delegated Exception Interface (SDEI) driver code.
+ *
+ * Copyright (C) 2017 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#define pr_fmt(fmt) "sdei: " fmt
+
+#include <linux/acpi.h>
+#include <linux/arm-smccc.h>
+#include <linux/bitops.h>
+#include <linux/compiler.h>
+#include <linux/errno.h>
+#include <linux/hardirq.h>
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+#include <linux/kvm_host.h>
+#include <linux/list.h>
+#include <linux/of.h>
+#include <linux/percpu.h>
+#include <linux/platform_device.h>
+#include <linux/ptrace.h>
+#include <linux/preempt.h>
+#include <linux/sdei.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <uapi/linux/sdei.h>
+
+/*
+ * The call to use to reach the firmware.
+ */
+static asmlinkage void (*sdei_firmware_call)(unsigned long function_id,
+		      unsigned long arg0, unsigned long arg1,
+		      unsigned long arg2, unsigned long arg3,
+		      unsigned long arg4, struct arm_smccc_res *res);
+
+/* entry point from firmware to arch asm code */
+static void (*sdei_entry_point)(unsigned long, unsigned long);
+
+struct sdei_event {
+	struct list_head	list;
+	u32			event_num;
+	u64			type;
+	u64			priority;
+
+	/* This pointer is handed to firmware as the event argument. */
+	struct sdei_registered_event *registered;
+};
+
+static LIST_HEAD(sdei_events);
+static DEFINE_SPINLOCK(sdei_events_lock);
+
+static int sdei_to_linux_errno(unsigned long sdei_err)
+{
+	switch (sdei_err) {
+	case SDEI_NOT_SUPPORTED:
+		return -EOPNOTSUPP;
+	case SDEI_INVALID_PARAMETERS:
+		return -EINVAL;
+	case SDEI_DENIED:
+		return -EPERM;
+	case SDEI_PENDING:
+		return -EINPROGRESS;
+	case SDEI_OUT_OF_RESOURCE:
+		return -ENOMEM;
+	}
+
+	/* Not an error value ... */
+	return sdei_err;
+}
+
+/*
+ * If x0 is any of these values, then the call failed, use sdei_to_linux_errno()
+ * to translate.
+ */
+static int sdei_is_err(struct arm_smccc_res *res)
+{
+	switch (res->a0) {
+	case SDEI_NOT_SUPPORTED:
+	case SDEI_INVALID_PARAMETERS:
+	case SDEI_DENIED:
+	case SDEI_PENDING:
+	case SDEI_OUT_OF_RESOURCE:
+		return true;
+	}
+
+	return false;
+}
+
+static int invoke_sdei_fn(struct arm_smccc_res *res, unsigned long function_id,
+			  unsigned long arg0, unsigned long arg1,
+			  unsigned long arg2, unsigned long arg3,
+			  unsigned long arg4)
+{
+	int err = 0;
+
+	if (sdei_firmware_call) {
+		sdei_firmware_call(function_id, arg0, arg1, arg2, arg3, arg4, res);
+		if (sdei_is_err(res))
+			err = sdei_to_linux_errno(res->a0);
+	} else {
+		/*
+		 * !sdei_firmware_call means we failed to probe or called
+		 * sdei_mark_interface_broken(). -EIO is not an error returned
+		 * by sdei_to_linux_errno() and is used to suppress messages
+		 * from this driver.
+		 */
+		err = -EIO;
+		res->a0 = SDEI_NOT_SUPPORTED;
+	}
+
+	return err;
+}
+
+static struct sdei_event *sdei_event_find(u32 event_num)
+{
+	struct sdei_event *e, *found = NULL;
+
+	WARN_ON(!spin_is_locked(&sdei_events_lock));
+
+	list_for_each_entry(e, &sdei_events, list) {
+		if (e->event_num == event_num) {
+			found = e;
+			break;
+		}
+	}
+
+	return found;
+}
+
+int sdei_api_event_context(u32 query, u64 *result)
+{
+	int err;
+	struct arm_smccc_res res;
+
+	err = invoke_sdei_fn(&res, SDEI_1_0_FN_SDEI_EVENT_CONTEXT, query, 0, 0,
+			     0, 0);
+	*result = res.a0;
+	return err;
+}
+
+static int sdei_api_event_get_info(u32 event, u32 info, u64 *result)
+{
+	int err;
+	struct arm_smccc_res res;
+
+	err = invoke_sdei_fn(&res, SDEI_1_0_FN_SDEI_EVENT_GET_INFO, event, info,
+			     0, 0, 0);
+	*result = res.a0;
+	return err;
+}
+
+static struct sdei_event *sdei_event_create(u32 event_num,
+					    sdei_event_callback *cb,
+					    void *cb_arg)
+{
+	int err;
+	struct sdei_event *event;
+	struct sdei_registered_event *reg;
+
+	WARN_ON(!spin_is_locked(&sdei_events_lock));
+
+	event = kzalloc(sizeof(*event), GFP_KERNEL);
+	if (!event)
+		return ERR_PTR(-ENOMEM);
+
+	INIT_LIST_HEAD(&event->list);
+	event->event_num = event_num;
+
+	err = sdei_api_event_get_info(event_num, SDEI_EVENT_INFO_EV_PRIORITY,
+					&event->priority);
+	if (err) {
+		kfree(event);
+		return ERR_PTR(err);
+	}
+
+	err = sdei_api_event_get_info(event_num, SDEI_EVENT_INFO_EV_TYPE,
+					&event->type);
+	if (err) {
+		kfree(event);
+		return ERR_PTR(err);
+	}
+
+	if (event->type == SDEI_EVENT_TYPE_SHARED) {
+		reg = kzalloc(sizeof(*reg), GFP_KERNEL);
+		if (!reg) {
+			kfree(event);
+			return ERR_PTR(-ENOMEM);
+		}
+
+		reg->event_num = event_num;
+
+		reg->callback = cb;
+		reg->callback_arg = cb_arg;
+		event->registered = reg;
+	}
+
+	if (sdei_event_find(event_num)) {
+		kfree(event->registered);
+		kfree(event);
+		event = ERR_PTR(-EBUSY);
+	} else {
+		list_add(&event->list, &sdei_events);
+	}
+
+	return event;
+}
+
+static void sdei_event_destroy(struct sdei_event *event)
+{
+	WARN_ON(!spin_is_locked(&sdei_events_lock));
+
+	list_del(&event->list);
+
+	if (event->type == SDEI_EVENT_TYPE_SHARED)
+		kfree(event->registered);
+
+	kfree(event);
+}
+
+static int sdei_api_get_version(u64 *version)
+{
+	int err;
+	struct arm_smccc_res res;
+
+	err = invoke_sdei_fn(&res, SDEI_1_0_FN_SDEI_VERSION, 0, 0, 0, 0, 0);
+	if (err)
+		return err;
+
+	*version = res.a0;
+	return 0;
+}
+
+int sdei_mask_local_cpu(unsigned int cpu)
+{
+	int err;
+	struct arm_smccc_res res;
+
+	WARN_ON(preemptible());
+	err = invoke_sdei_fn(&res, SDEI_1_0_FN_SDEI_PE_MASK, 0, 0, 0, 0, 0);
+
+	if (err && err != -EIO) {
+		pr_warn_once("failed to mask CPU[%u]: %d\n",
+			      smp_processor_id(), err);
+		return err;
+	}
+
+	return 0;
+}
+
+static void _ipi_mask_cpu(void *ignored)
+{
+	sdei_mask_local_cpu(smp_processor_id());
+}
+
+int sdei_unmask_local_cpu(unsigned int ignored)
+{
+	int err;
+	struct arm_smccc_res res;
+
+	WARN_ON(preemptible());
+	err = invoke_sdei_fn(&res, SDEI_1_0_FN_SDEI_PE_UNMASK, 0, 0, 0, 0, 0);
+
+	if (err && err != -EIO) {
+		pr_warn_once("failed to unmask CPU[%u]: %d\n",
+			     smp_processor_id(), err);
+		return err;
+	}
+
+	return 0;
+}
+
+static void _ipi_unmask_cpu(void *ignored)
+{
+	sdei_unmask_local_cpu(smp_processor_id());
+}
+
+static void _ipi_private_reset(void *ignored)
+{
+	int err;
+	struct arm_smccc_res res;
+
+	err = invoke_sdei_fn(&res, SDEI_1_0_FN_SDEI_PRIVATE_RESET, 0, 0, 0, 0,
+			     0);
+
+	if (err && err != -EIO)
+		pr_warn_once("failed to reset CPU[%u]: %d\n",
+			     smp_processor_id(), err);
+}
+
+static int sdei_api_shared_reset(void)
+{
+	struct arm_smccc_res res;
+
+	return invoke_sdei_fn(&res, SDEI_1_0_FN_SDEI_SHARED_RESET, 0, 0, 0, 0,
+			      0);
+}
+
+static void sdei_mark_interface_broken(void)
+{
+	pr_err("disabling SDEI firmware interface\n");
+	on_each_cpu(&_ipi_mask_cpu, NULL, true);
+	sdei_firmware_call = NULL;
+}
+
+static int sdei_platform_reset(void)
+{
+	int err;
+
+	on_each_cpu(&_ipi_private_reset, NULL, true);
+	err = sdei_api_shared_reset();
+	if (err) {
+		pr_err("Failed to reset platform: %d\n", err);
+		sdei_mark_interface_broken();
+	}
+
+	return err;
+}
+
+static int sdei_api_event_enable(u32 event_num)
+{
+	struct arm_smccc_res res;
+
+	return invoke_sdei_fn(&res, SDEI_1_0_FN_SDEI_EVENT_ENABLE, event_num, 0,
+			      0, 0, 0);
+}
+
+int sdei_event_enable(u32 event_num)
+{
+	int err = -EINVAL;
+	struct sdei_event *event;
+
+	spin_lock(&sdei_events_lock);
+	event = sdei_event_find(event_num);
+	if (!event)
+		err = -ENOENT;
+	else if (event->type == SDEI_EVENT_TYPE_SHARED)
+		err = sdei_api_event_enable(event->event_num);
+	spin_unlock(&sdei_events_lock);
+
+	return err;
+}
+EXPORT_SYMBOL(sdei_event_enable);
+
+static int sdei_api_event_disable(u32 event_num)
+{
+	int err;
+	struct arm_smccc_res res;
+
+	err = invoke_sdei_fn(&res, SDEI_1_0_FN_SDEI_EVENT_DISABLE, event_num, 0,
+				0, 0, 0);
+
+	return err;
+}
+
+int sdei_event_disable(u32 event_num)
+{
+	int err = -EINVAL;
+	struct sdei_event *event;
+
+	spin_lock(&sdei_events_lock);
+	event = sdei_event_find(event_num);
+	if (!event)
+		err = -ENOENT;
+	else if (event->type == SDEI_EVENT_TYPE_SHARED)
+		err = sdei_api_event_disable(event->event_num);
+	spin_unlock(&sdei_events_lock);
+
+	return err;
+}
+EXPORT_SYMBOL(sdei_event_disable);
+
+static int sdei_api_event_unregister(u32 event_num)
+{
+	struct arm_smccc_res res;
+
+	return invoke_sdei_fn(&res, SDEI_1_0_FN_SDEI_EVENT_UNREGISTER,
+			      event_num, 0, 0, 0, 0);
+}
+
+static int _sdei_event_unregister(struct sdei_event *event)
+{
+	if (event->type == SDEI_EVENT_TYPE_SHARED)
+		return sdei_api_event_unregister(event->event_num);
+
+	return -EINVAL;
+}
+
+int sdei_event_unregister(u32 event_num)
+{
+	int err;
+	struct sdei_event *event;
+
+	WARN_ON(in_nmi());
+
+	spin_lock(&sdei_events_lock);
+	event = sdei_event_find(event_num);
+	do {
+		if (!event) {
+			pr_err("Event %u not registered\n", event_num);
+			err = -ENOENT;
+			break;
+		}
+
+		err = _sdei_event_unregister(event);
+		if (err)
+			break;
+
+		sdei_event_destroy(event);
+	} while (0);
+	spin_unlock(&sdei_events_lock);
+
+	return err;
+}
+EXPORT_SYMBOL(sdei_event_unregister);
+
+static int sdei_api_event_register(u32 event_num, void *entry_point, void *arg,
+			       u64 flags, u64 affinity)
+{
+	struct arm_smccc_res res;
+
+	return invoke_sdei_fn(&res, SDEI_1_0_FN_SDEI_EVENT_REGISTER, event_num,
+			      (unsigned long)entry_point, (unsigned long)arg,
+			      flags, affinity);
+}
+
+static int _sdei_event_register(struct sdei_event *event)
+{
+	if (event->type == SDEI_EVENT_TYPE_SHARED)
+		return sdei_api_event_register(event->event_num,
+					       sdei_entry_point,
+					       event->registered,
+					       SDEI_EVENT_REGISTER_RM_ANY, 0);
+
+	return -EINVAL;
+}
+
+int sdei_event_register(u32 event_num, sdei_event_callback *cb, void *arg)
+{
+	int err;
+	struct sdei_event *event;
+
+	WARN_ON(in_nmi());
+
+	spin_lock(&sdei_events_lock);
+	do {
+		if (sdei_event_find(event_num)) {
+			pr_err("Event %u already registered\n", event_num);
+			err = -EBUSY;
+			break;
+		}
+
+		event = sdei_event_create(event_num, cb, arg);
+		if (IS_ERR(event)) {
+			err = PTR_ERR(event);
+			pr_err("Failed to create event %u: %d\n", event_num, err);
+			break;
+		}
+
+		err = _sdei_event_register(event);
+		if (err) {
+			sdei_event_destroy(event);
+			pr_err("Failed to register event %u: %d\n", event_num, err);
+		}
+	} while (0);
+	spin_unlock(&sdei_events_lock);
+
+	return err;
+}
+EXPORT_SYMBOL(sdei_event_register);
+
+static void sdei_smccc_smc(unsigned long function_id,
+			   unsigned long arg0, unsigned long arg1,
+			   unsigned long arg2, unsigned long arg3,
+			   unsigned long arg4, struct arm_smccc_res *res)
+{
+	arm_smccc_smc(function_id, arg0, arg1, arg2, arg3, arg4, 0, 0, res);
+}
+
+static void sdei_smccc_hvc(unsigned long function_id,
+			   unsigned long arg0, unsigned long arg1,
+			   unsigned long arg2, unsigned long arg3,
+			   unsigned long arg4, struct arm_smccc_res *res)
+{
+	arm_smccc_hvc(function_id, arg0, arg1, arg2, arg3, arg4, 0, 0, res);
+}
+
+static int sdei_get_conduit(struct platform_device *pdev)
+{
+	const char *method;
+	struct device_node *np = pdev->dev.of_node;
+
+	sdei_firmware_call = NULL;
+	if (np) {
+		if (of_property_read_string(np, "method", &method)) {
+			pr_warn("missing \"method\" property\n");
+			return CONDUIT_INVALID;
+		}
+
+		if (!strcmp("hvc", method)) {
+			sdei_firmware_call = &sdei_smccc_hvc;
+			return CONDUIT_HVC;
+		} else if (!strcmp("smc", method)) {
+			sdei_firmware_call = &sdei_smccc_smc;
+			return CONDUIT_SMC;
+		}
+
+		pr_warn("invalid \"method\" property: %s\n", method);
+	}
+
+	return CONDUIT_INVALID;
+}
+
+static int sdei_probe(struct platform_device *pdev)
+{
+	int err;
+	u64 ver = 0;
+	int conduit;
+
+	conduit = sdei_get_conduit(pdev);
+	if (!sdei_firmware_call)
+		return 0;
+
+	err = sdei_api_get_version(&ver);
+	if (err == -EOPNOTSUPP)
+		pr_err("No SDEI implementation in platform firmware\n");
+	if (err) {
+		pr_err("Failed to get SDEI version: %d\n", err);
+		sdei_mark_interface_broken();
+		return err;
+	}
+
+	pr_info("SDEIv%d.%d (0x%x) detected in firmware.\n",
+		(int)SDEI_VERSION_MAJOR(ver), (int)SDEI_VERSION_MINOR(ver),
+		(int)SDEI_VERSION_VENDOR(ver));
+
+	if (SDEI_VERSION_MAJOR(ver) != 1) {
+		pr_err("Conflicting SDEI version detected.\n");
+		sdei_mark_interface_broken();
+		return -EINVAL;
+	}
+
+	err = sdei_platform_reset();
+	if (err)
+		return err;
+
+	sdei_entry_point = sdei_arch_get_entry_point(conduit);
+	if (!sdei_entry_point) {
+		/* Not supported due to hardware or boot configuration */
+		sdei_mark_interface_broken();
+		return 0;
+	}
+
+	on_each_cpu(&_ipi_unmask_cpu, NULL, false);
+
+	return 0;
+}
+
+static const struct of_device_id sdei_of_match[] = {
+	{ .compatible = "arm,sdei-1.0" },
+	{}
+};
+
+static struct platform_driver sdei_driver = {
+	.driver		= {
+		.name			= "sdei",
+		.of_match_table		= sdei_of_match,
+	},
+	.probe		= sdei_probe,
+};
+builtin_platform_driver(sdei_driver);
+
+int sdei_event_handler(struct pt_regs *regs,
+			       struct sdei_registered_event *arg)
+{
+	int err;
+	mm_segment_t orig_addr_limit;
+	u32 event_num = arg->event_num;
+
+	orig_addr_limit = get_fs();
+	set_fs(USER_DS);
+
+	err = arg->callback(event_num, regs, arg->callback_arg);
+	if (err)
+		pr_err("event %u on CPU %u failed with error: %d\n", event_num,
+		       smp_processor_id(), err);
+
+	set_fs(orig_addr_limit);
+
+	return err;
+}
+NOKPROBE_SYMBOL(sdei_event_handler);
diff --git a/include/linux/sdei.h b/include/linux/sdei.h
new file mode 100644
index 000000000000..504e8bf8bc07
--- /dev/null
+++ b/include/linux/sdei.h
@@ -0,0 +1,98 @@
+/*
+ * Software Delegated Exception Interface (SDEI) API
+ *
+ * Copyright (C) 2017 ARM Ltd.
+ *
+ * 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/>.
+ */
+
+#ifndef __LINUX_SDEI_H
+#define __LINUX_SDEI_H
+
+enum sdei_conduit_types {
+	CONDUIT_INVALID = 0,
+	CONDUIT_SMC,
+	CONDUIT_HVC,
+};
+
+#include <uapi/linux/sdei.h>
+
+/* Arch code should override this to set the entry point from firmware... */
+#ifndef sdei_arch_get_entry_point
+#define sdei_arch_get_entry_point(conduit)	(NULL)
+#endif
+
+/*
+ * When an event occurs sdei_event_handler() will call a user-provided callback
+ * like this in NMI context on the CPU that received the event.
+ */
+typedef int (sdei_event_callback)(u32 event, struct pt_regs *regs, void *arg);
+
+/*
+ * Register your callback to claim an event. The event must be described
+ * by firmware.
+ */
+int sdei_event_register(u32 event_num, sdei_event_callback *cb, void *arg);
+
+/*
+ * Calls to sdei_event_unregister() may return EINPROGRESS. Keep calling
+ * it until it succeeds.
+ */
+int sdei_event_unregister(u32 event_num);
+
+int sdei_event_enable(u32 event_num);
+int sdei_event_disable(u32 event_num);
+
+#ifdef CONFIG_ARM_SDE_INTERFACE
+/* For use by arch code when CPU hotplug notifiers are not appropriate. */
+int sdei_mask_local_cpu(unsigned int cpu);
+int sdei_unmask_local_cpu(unsigned int cpu);
+
+/* kvm needs to know if hvcs are for SDEI or PSCI */
+#define IS_SDEI_CALL(x)		((x & SDEI_1_0_MASK) == SDEI_1_0_FN_BASE)
+
+#else
+static inline int sdei_mask_local_cpu(unsigned int ignored) { return 0; }
+static inline int sdei_unmask_local_cpu(unsigned int ignored) { return 0; }
+
+#define IS_SDEI_CALL(x)		0
+#endif /* CONFIG_ARM_SDE_INTERFACE */
+
+
+
+/*
+ * This struct represents an event that has been registered. The driver
+ * maintains a list of all events, and which ones are registered. A pointer to
+ * this struct is passed to firmware, and back to the event handler. The event
+ * handler can then use this to invoke the registered callback, without having
+ * to walk the list.
+ *
+ * For CPU private events, this structure is per-cpu.
+ */
+struct sdei_registered_event {
+	/* For use by arch code: */
+	struct pt_regs          interrupted_regs;
+
+	sdei_event_callback	*callback;
+	void			*callback_arg;
+	u32			 event_num;
+};
+
+/* The arch code entry point should then call this when an event arrives. */
+int notrace sdei_event_handler(struct pt_regs *regs,
+			       struct sdei_registered_event *arg);
+
+/* arch code may use this to retrieve the extra registers. */
+int sdei_api_event_context(u32 query, u64 *result);
+
+#endif /* __LINUX_SDEI_H */
diff --git a/include/uapi/linux/sdei.h b/include/uapi/linux/sdei.h
new file mode 100644
index 000000000000..535c9d8d14cc
--- /dev/null
+++ b/include/uapi/linux/sdei.h
@@ -0,0 +1,91 @@
+/*
+ *Software Delegated Exception Interface (SDEI) Firmware API
+ *
+ * This header holds common SDEI defines and macros shared
+ * by: ARM64 kernel, KVM and user space.
+ *
+ * Copyright (C) 2017 ARM Ltd.
+ *
+ * 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/>.
+ */
+#ifndef _UAPI_LINUX_SDEI_H
+#define _UAPI_LINUX_SDEI_H
+
+#define SDEI_1_0_FN_BASE			0xC4000020
+#define SDEI_1_0_MASK				0xFFFFFFE0
+#define SDEI_1_0_FN(n)				(SDEI_1_0_FN_BASE + (n))
+
+#define SDEI_1_0_FN_SDEI_VERSION			SDEI_1_0_FN(0x00)
+#define SDEI_1_0_FN_SDEI_EVENT_REGISTER			SDEI_1_0_FN(0x01)
+#define SDEI_1_0_FN_SDEI_EVENT_ENABLE			SDEI_1_0_FN(0x02)
+#define SDEI_1_0_FN_SDEI_EVENT_DISABLE			SDEI_1_0_FN(0x03)
+#define SDEI_1_0_FN_SDEI_EVENT_CONTEXT			SDEI_1_0_FN(0x04)
+#define SDEI_1_0_FN_SDEI_EVENT_COMPLETE			SDEI_1_0_FN(0x05)
+#define SDEI_1_0_FN_SDEI_EVENT_COMPLETE_AND_RESUME	SDEI_1_0_FN(0x06)
+#define SDEI_1_0_FN_SDEI_EVENT_UNREGISTER		SDEI_1_0_FN(0x07)
+#define SDEI_1_0_FN_SDEI_EVENT_STATUS			SDEI_1_0_FN(0x08)
+#define SDEI_1_0_FN_SDEI_EVENT_GET_INFO			SDEI_1_0_FN(0x09)
+#define SDEI_1_0_FN_SDEI_EVENT_ROUTING_SET		SDEI_1_0_FN(0x0A)
+#define SDEI_1_0_FN_SDEI_PE_MASK			SDEI_1_0_FN(0x0B)
+#define SDEI_1_0_FN_SDEI_PE_UNMASK			SDEI_1_0_FN(0x0C)
+#define SDEI_1_0_FN_SDEI_INTERRUPT_BIND			SDEI_1_0_FN(0x0D)
+#define SDEI_1_0_FN_SDEI_INTERRUPT_RELEASE		SDEI_1_0_FN(0x0E)
+#define SDEI_1_0_FN_SDEI_PRIVATE_RESET			SDEI_1_0_FN(0x11)
+#define SDEI_1_0_FN_SDEI_SHARED_RESET			SDEI_1_0_FN(0x12)
+
+#define SDEI_VERSION_MAJOR_SHIFT			48
+#define SDEI_VERSION_MAJOR_MASK				0x7fff
+#define SDEI_VERSION_MINOR_SHIFT			32
+#define SDEI_VERSION_MINOR_MASK				0xffff
+#define SDEI_VERSION_VENDOR_SHIFT			0
+#define SDEI_VERSION_VENDOR_MASK			0xffffffff
+
+#define SDEI_VERSION_MAJOR(x)	(x>>SDEI_VERSION_MAJOR_SHIFT & SDEI_VERSION_MAJOR_MASK)
+#define SDEI_VERSION_MINOR(x)	(x>>SDEI_VERSION_MINOR_SHIFT & SDEI_VERSION_MINOR_MASK)
+#define SDEI_VERSION_VENDOR(x)	(x>>SDEI_VERSION_VENDOR_SHIFT & SDEI_VERSION_VENDOR_MASK)
+
+/* SDEI return values */
+#define SDEI_SUCCESS		0
+#define SDEI_NOT_SUPPORTED	-1
+#define SDEI_INVALID_PARAMETERS	-2
+#define SDEI_DENIED		-3
+#define SDEI_PENDING		-5
+#define SDEI_OUT_OF_RESOURCE	-10
+
+/* EVENT_REGISTER flags */
+#define SDEI_EVENT_REGISTER_RM_ANY	0
+#define SDEI_EVENT_REGISTER_RM_PE	1
+
+/* EVENT_STATUS return value bits */
+#define SDEI_EVENT_STATUS_RUNNING	2
+#define SDEI_EVENT_STATUS_ENABLED	1
+#define SDEI_EVENT_STATUS_REGISTERED	0
+
+/* EVENT_COMPLETE status values */
+#define SDEI_EV_HANDLED	0
+#define SDEI_EV_FAILED	1
+
+/* GET_INFO values */
+#define SDEI_EVENT_INFO_EV_TYPE			0
+#define SDEI_EVENT_INFO_EV_SIGNALED		1
+#define SDEI_EVENT_INFO_EV_PRIORITY		2
+#define SDEI_EVENT_INFO_EV_ROUTING_MODE		3
+#define SDEI_EVENT_INFO_EV_ROUTING_AFF		4
+
+/* and their results */
+#define SDEI_EVENT_TYPE_PRIVATE			0
+#define SDEI_EVENT_TYPE_SHARED			1
+#define SDEI_EVENT_PRIORITY_NORMAL		0
+#define SDEI_EVENT_PRIORITY_CRITICAL		1
+
+#endif /* _UAPI_LINUX_SDEI_H */
-- 
2.10.1

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

* [PATCH 08/11] arm64: kernel: Add arch-specific SDEI entry code and CPU masking
  2017-05-15 17:43 ` James Morse
@ 2017-05-15 17:43     ` James Morse
  -1 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-05-15 17:43 UTC (permalink / raw)
  To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Will Deacon, Catalin Marinas,
	kvmarm-FPEHb7Xf0XXUo1n7N8X6UoWGPAHP3yOg, Marc Zyngier,
	Christoffer Dall, Rob Herring, Mark Rutland,
	devicetree-u79uwXL29TY76Z2rM5mHXA

The Software Delegated Exception Interface (SDEI) is an ARM standard
for registering callbacks from the platform firmware into the OS.
This is typically used to implement RAS notifications.

Such notifications enter the kernel at the registered entry-point
with the register values of the interrupted CPU context. Because this
is not a CPU exception, it cannot reuse the existing entry code.
(crucially we don't immediately know which exception level we interrupted),

Add entry.S asm to set us up for calling into C code. If the event
interrupted code that had interrutps masked, we always return to that
location. Otherwise we pretend this was an IRQ, and use SDEI's
complete_and_resume call to return to vbar_el1 + offset.

This allows the kernel to deliver signals to user space processes. For
KVM this triggers the world switch, a quick spin round vcpu_run, then
back into the guest, unless there are pending signals.

Add sdei_mask_local_cpu() calls to the smp_send_stop() code, this covers
the panic() code-path, which doesn't invoke cpuhotplug notifiers, and
only calls the panic notifiers if there is no kdump kernel registered.

Because we can interrupt entry-from/exit-to EL0, we can't trust the value
in sp_el0 even if we interrupted the kernel, in this case the code in
entry.S will save/restore sp_el0 and use the value in __entry_task.

Signed-off-by: James Morse <james.morse-5wv7dgnIgG8@public.gmane.org>
---
 arch/arm64/Kconfig              |   1 +
 arch/arm64/include/asm/sdei.h   |  45 +++++++++++++++++
 arch/arm64/kernel/Makefile      |   1 +
 arch/arm64/kernel/asm-offsets.c |   2 +
 arch/arm64/kernel/entry.S       |  68 ++++++++++++++++++++++++++
 arch/arm64/kernel/sdei.c        | 106 ++++++++++++++++++++++++++++++++++++++++
 arch/arm64/kernel/smp.c         |   7 +++
 drivers/firmware/Kconfig        |   1 +
 drivers/firmware/arm_sdei.c     |   1 +
 include/linux/sdei.h            |   1 +
 10 files changed, 233 insertions(+)
 create mode 100644 arch/arm64/include/asm/sdei.h
 create mode 100644 arch/arm64/kernel/sdei.c

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 3dcd7ec69bca..5813ecc8a3b9 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -92,6 +92,7 @@ config ARM64
 	select HAVE_IRQ_TIME_ACCOUNTING
 	select HAVE_MEMBLOCK
 	select HAVE_MEMBLOCK_NODE_MAP if NUMA
+	select HAVE_NMI
 	select HAVE_PATA_PLATFORM
 	select HAVE_PERF_EVENTS
 	select HAVE_PERF_REGS
diff --git a/arch/arm64/include/asm/sdei.h b/arch/arm64/include/asm/sdei.h
new file mode 100644
index 000000000000..5d74582be162
--- /dev/null
+++ b/arch/arm64/include/asm/sdei.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 ARM Ltd.
+ *
+ * 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/>.
+ */
+#ifndef __ASM_SDEI_H
+#define __ASM_SDEI_H
+
+#include <asm/virt.h>
+
+extern unsigned long sdei_use_hvc;
+
+/* Software Delegated Exception entry point from firmware*/
+void __sdei_asm_handler(unsigned long event_num, unsigned long arg);
+
+static inline void *sdei_arch_get_entry_point(int conduit)
+{
+	/*
+	 * SDEI works between adjacent exception levels. If we booted at EL1 we
+	 * assume a hypervisor is marshalling events. If we booted at EL2 and
+	 * dropped to EL1 because we don't support VHE, then we can't support
+	 * SDEI.
+	 */
+	if (is_hyp_mode_available() && !is_kernel_in_hyp_mode()) {
+		pr_err("Not supported on this hardware/boot configuration\n");
+		return NULL;
+	}
+
+	sdei_use_hvc = (conduit == CONDUIT_HVC) ? 1 : 0;
+
+	return __sdei_asm_handler;
+}
+#define sdei_arch_get_entry_point(x)	sdei_arch_get_entry_point(x)
+
+#endif	/* __ASM_SDEI_H */
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 1dcb69d3d0e5..4f524d7852bf 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -53,6 +53,7 @@ arm64-obj-$(CONFIG_KEXEC)		+= machine_kexec.o relocate_kernel.o	\
 arm64-obj-$(CONFIG_ARM64_RELOC_TEST)	+= arm64-reloc-test.o
 arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o
 arm64-obj-$(CONFIG_CRASH_DUMP)		+= crash_dump.o
+arm64-obj-$(CONFIG_ARM_SDE_INTERFACE)	+= sdei.o
 
 obj-y					+= $(arm64-obj-y) vdso/ probes/
 obj-m					+= $(arm64-obj-m)
diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
index b3bb7ef97bc8..2dd036407046 100644
--- a/arch/arm64/kernel/asm-offsets.c
+++ b/arch/arm64/kernel/asm-offsets.c
@@ -22,6 +22,7 @@
 #include <linux/mm.h>
 #include <linux/dma-mapping.h>
 #include <linux/kvm_host.h>
+#include <linux/sdei.h>
 #include <linux/suspend.h>
 #include <asm/cpufeature.h>
 #include <asm/thread_info.h>
@@ -153,5 +154,6 @@ int main(void)
   DEFINE(HIBERN_PBE_ADDR,	offsetof(struct pbe, address));
   DEFINE(HIBERN_PBE_NEXT,	offsetof(struct pbe, next));
   DEFINE(ARM64_FTR_SYSVAL,	offsetof(struct arm64_ftr_reg, sys_val));
+  DEFINE(SDEI_EVENT_INTREGS,	offsetof(struct sdei_registered_event, interrupted_regs));
   return 0;
 }
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
index b738880350f9..1709063a54a3 100644
--- a/arch/arm64/kernel/entry.S
+++ b/arch/arm64/kernel/entry.S
@@ -18,6 +18,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/err.h>
 #include <linux/init.h>
 #include <linux/linkage.h>
 
@@ -33,6 +34,7 @@
 #include <asm/thread_info.h>
 #include <asm/asm-uaccess.h>
 #include <asm/unistd.h>
+#include <uapi/linux/sdei.h>
 
 /*
  * Context tracking subsystem.  Used to instrument transitions
@@ -865,3 +867,69 @@ ENTRY(sys_rt_sigreturn_wrapper)
 	mov	x0, sp
 	b	sys_rt_sigreturn
 ENDPROC(sys_rt_sigreturn_wrapper)
+
+#ifdef CONFIG_ARM_SDE_INTERFACE
+/*
+ * Software Delegated Exception entry point.
+ *
+ * x0: Event number
+ * x1: struct sdei_registered_event argument from registration time.
+ * x2: interrupted PC
+ * x3: interrupted PSTATE
+ *
+ * Firmware has preserved x0->x17 for us, we must save/restore all other regs.
+ */
+ENTRY(__sdei_asm_handler)
+	stp     x2, x3, [x1, #SDEI_EVENT_INTREGS + S_PC]
+	stp     x4, x5, [x1, #SDEI_EVENT_INTREGS + 16 * 2]
+	stp     x6, x7, [x1, #SDEI_EVENT_INTREGS + 16 * 3]
+	stp     x8, x9, [x1, #SDEI_EVENT_INTREGS + 16 * 4]
+	stp     x10, x11, [x1, #SDEI_EVENT_INTREGS + 16 * 5]
+	stp     x12, x13, [x1, #SDEI_EVENT_INTREGS + 16 * 6]
+	stp     x14, x15, [x1, #SDEI_EVENT_INTREGS + 16 * 7]
+	stp     x16, x17, [x1, #SDEI_EVENT_INTREGS + 16 * 8]
+	stp     x18, x19, [x1, #SDEI_EVENT_INTREGS + 16 * 9]
+	stp     x20, x21, [x1, #SDEI_EVENT_INTREGS + 16 * 10]
+	stp     x22, x23, [x1, #SDEI_EVENT_INTREGS + 16 * 11]
+	stp     x24, x25, [x1, #SDEI_EVENT_INTREGS + 16 * 12]
+	stp     x26, x27, [x1, #SDEI_EVENT_INTREGS + 16 * 13]
+	stp     x28, x29, [x1, #SDEI_EVENT_INTREGS + 16 * 14]
+
+	mov	x19, x1
+
+	/*
+	 * We may have interrupted userspace, or a guest. Restore sp_el0 from
+	 * __entry_task.
+	 */
+	mrs	x0, sp_el0
+	stp     lr, x0, [x19, #SDEI_EVENT_INTREGS + S_LR]
+	ldr_this_cpu	dst=x0, sym=__entry_task, tmp=x1
+	msr	sp_el0, x0
+
+	add	x0, x19, #SDEI_EVENT_INTREGS
+	mov	x1, x19
+	bl	__sdei_handler
+
+	/* restore regs >x17 that we clobbered */
+	ldp     x28, x29, [x19, #SDEI_EVENT_INTREGS + 16 * 14]
+	ldp     lr, x1, [x19, #SDEI_EVENT_INTREGS + S_LR]
+	msr	sp_el0, x1
+	ldp     x18, x19, [x19, #SDEI_EVENT_INTREGS + 16 * 9]
+
+	mov	x1, x0			// address to complete_and_resume
+	/* x0 = (x0 <= 1) ? EVENT_COMPLETE:EVENT_COMPLETE_AND_RESUME */
+	cmp	x0, #1
+	mov_q	x2, SDEI_1_0_FN_SDEI_EVENT_COMPLETE
+	mov_q	x3, SDEI_1_0_FN_SDEI_EVENT_COMPLETE_AND_RESUME
+	csel	x0, x2, x3, ls
+
+	/* On success, this call never returns... */
+	ldr_l	x2, sdei_use_hvc
+	cbnz	x2, 1f
+	smc	#0
+	b	.
+1:	hvc	#0
+	b	.
+ENDPROC(__sdei_asm_handler)
+
+#endif /* CONFIG_ARM_SDE_INTERFACE */
diff --git a/arch/arm64/kernel/sdei.c b/arch/arm64/kernel/sdei.c
new file mode 100644
index 000000000000..c31ccc6fcc0d
--- /dev/null
+++ b/arch/arm64/kernel/sdei.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2017 ARM Ltd.
+ *
+ * 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/sched/task_stack.h>
+#include <linux/irqflags.h>
+#include <linux/hardirq.h>
+#include <linux/sdei.h>
+
+#include <asm/kprobes.h>
+#include <asm/ptrace.h>
+#include <asm/sysreg.h>
+
+unsigned long sdei_use_hvc;
+
+/*
+ * __sdei_handler() returns one of:
+ *  SDEI_EV_HANDLED -  success, return to the interrupted context.
+ *  SDEI_EV_FAILED  -  failure, return this error code to firmare.
+ *  virtual-address -  success, return to this address.
+ */
+static unsigned long _sdei_handler(struct pt_regs *regs,
+				   struct sdei_registered_event *arg)
+{
+	u32 mode;
+	int i, err = 0;
+	u32 kernel_mode = read_sysreg(CurrentEL) | 1;	/* +SPSel */
+	unsigned long vbar = read_sysreg(vbar_el1);
+
+	/* Retrieve the missing registers values */
+	for (i = 0; i < 4; i++) {
+		err = sdei_api_event_context(i, &regs->regs[i]);
+		if (err)
+			break;
+	}
+	if (err) {
+		pr_err("event %u on CPU %u: Failed to retrieve context register %u from firmware\n",
+			arg->event_num, smp_processor_id(), i);
+		return SDEI_EV_FAILED;
+	}
+
+	err = sdei_event_handler(regs, arg);
+	if (err)
+		return SDEI_EV_FAILED;
+
+	mode = regs->pstate & (PSR_MODE32_BIT | PSR_MODE_MASK);
+
+	/*
+	 * If we interrupted the kernel with interrupts masked, we always go
+	 * back to wherever we came from.
+	 */
+	if (mode == kernel_mode && !interrupts_enabled(regs))
+		return SDEI_EV_HANDLED;
+
+	/*
+	 * Otherwise, we pretend this was an IRQ. This lets user space tasks
+	 * receive signals before we return to them, and KVM to invoke it's
+	 * world switch to do the same.
+	 *
+	 * See DDI0487B.a Table D1-7 'Vector offsets from vector table base
+	 * address'.
+	 */
+	if (mode == kernel_mode)
+		return vbar + 0x280;
+	else if (mode & PSR_MODE32_BIT)
+		return vbar + 0x680;
+
+	return vbar + 0x480;
+}
+
+
+asmlinkage __kprobes notrace unsigned long
+__sdei_handler(struct pt_regs *regs, struct sdei_registered_event *arg)
+{
+	unsigned long ret;
+	bool do_nmi_exit = false;
+
+	/*
+	 * nmi_enter() deals with printk() re-entrance and use of RCU when
+	 * RCU believed this CPU was idle. Because critical events can
+	 * interrupt normal events, we may already be in_nmi().
+	 */
+	if (!in_nmi()) {
+		nmi_enter();
+		do_nmi_exit = true;
+	}
+
+	ret = _sdei_handler(regs, arg);
+
+	if (do_nmi_exit)
+		nmi_exit();
+
+	return ret;
+}
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 6e0e16a3a7d4..ceaf0a67f563 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -31,6 +31,7 @@
 #include <linux/mm.h>
 #include <linux/err.h>
 #include <linux/cpu.h>
+#include <linux/sdei.h>
 #include <linux/smp.h>
 #include <linux/seq_file.h>
 #include <linux/irq.h>
@@ -838,6 +839,7 @@ static void ipi_cpu_stop(unsigned int cpu)
 	set_cpu_online(cpu, false);
 
 	local_irq_disable();
+	sdei_mask_local_cpu(smp_processor_id());
 
 	while (1)
 		cpu_relax();
@@ -855,6 +857,7 @@ static void ipi_cpu_crash_stop(unsigned int cpu, struct pt_regs *regs)
 	atomic_dec(&waiting_for_crash_ipi);
 
 	local_irq_disable();
+	sdei_mask_local_cpu(smp_processor_id());
 
 #ifdef CONFIG_HOTPLUG_CPU
 	if (cpu_ops[cpu]->cpu_die)
@@ -975,6 +978,8 @@ void smp_send_stop(void)
 	if (num_online_cpus() > 1)
 		pr_warning("SMP: failed to stop secondary CPUs %*pbl\n",
 			   cpumask_pr_args(cpu_online_mask));
+
+	sdei_mask_local_cpu(smp_processor_id());
 }
 
 #ifdef CONFIG_KEXEC_CORE
@@ -1002,6 +1007,8 @@ void smp_send_crash_stop(void)
 	if (atomic_read(&waiting_for_crash_ipi) > 0)
 		pr_warning("SMP: failed to stop secondary CPUs %*pbl\n",
 			   cpumask_pr_args(&mask));
+
+	sdei_mask_local_cpu(smp_processor_id());
 }
 
 bool smp_crash_stop_failed(void)
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index d8a9859f6102..1f6633b337aa 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -50,6 +50,7 @@ config ARM_SCPI_POWER_DOMAIN
 
 config ARM_SDE_INTERFACE
 	bool "ARM Software Delegated Exception Interface (SDEI)"
+	depends on ARM64
 	help
 	  The Software Delegated Exception Interface (SDEI) is an ARM
 	  standard for registering callbacks from the platform firmware
diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c
index d22dda5e0fed..afd9499a0e2d 100644
--- a/drivers/firmware/arm_sdei.c
+++ b/drivers/firmware/arm_sdei.c
@@ -152,6 +152,7 @@ int sdei_api_event_context(u32 query, u64 *result)
 	*result = res.a0;
 	return err;
 }
+NOKPROBE_SYMBOL(sdei_api_event_context);
 
 static int sdei_api_event_get_info(u32 event, u32 info, u64 *result)
 {
diff --git a/include/linux/sdei.h b/include/linux/sdei.h
index 504e8bf8bc07..6138582faf4b 100644
--- a/include/linux/sdei.h
+++ b/include/linux/sdei.h
@@ -25,6 +25,7 @@ enum sdei_conduit_types {
 	CONDUIT_HVC,
 };
 
+#include <asm/sdei.h>
 #include <uapi/linux/sdei.h>
 
 /* Arch code should override this to set the entry point from firmware... */
-- 
2.10.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 08/11] arm64: kernel: Add arch-specific SDEI entry code and CPU masking
@ 2017-05-15 17:43     ` James Morse
  0 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-05-15 17:43 UTC (permalink / raw)
  To: linux-arm-kernel

The Software Delegated Exception Interface (SDEI) is an ARM standard
for registering callbacks from the platform firmware into the OS.
This is typically used to implement RAS notifications.

Such notifications enter the kernel at the registered entry-point
with the register values of the interrupted CPU context. Because this
is not a CPU exception, it cannot reuse the existing entry code.
(crucially we don't immediately know which exception level we interrupted),

Add entry.S asm to set us up for calling into C code. If the event
interrupted code that had interrutps masked, we always return to that
location. Otherwise we pretend this was an IRQ, and use SDEI's
complete_and_resume call to return to vbar_el1 + offset.

This allows the kernel to deliver signals to user space processes. For
KVM this triggers the world switch, a quick spin round vcpu_run, then
back into the guest, unless there are pending signals.

Add sdei_mask_local_cpu() calls to the smp_send_stop() code, this covers
the panic() code-path, which doesn't invoke cpuhotplug notifiers, and
only calls the panic notifiers if there is no kdump kernel registered.

Because we can interrupt entry-from/exit-to EL0, we can't trust the value
in sp_el0 even if we interrupted the kernel, in this case the code in
entry.S will save/restore sp_el0 and use the value in __entry_task.

Signed-off-by: James Morse <james.morse@arm.com>
---
 arch/arm64/Kconfig              |   1 +
 arch/arm64/include/asm/sdei.h   |  45 +++++++++++++++++
 arch/arm64/kernel/Makefile      |   1 +
 arch/arm64/kernel/asm-offsets.c |   2 +
 arch/arm64/kernel/entry.S       |  68 ++++++++++++++++++++++++++
 arch/arm64/kernel/sdei.c        | 106 ++++++++++++++++++++++++++++++++++++++++
 arch/arm64/kernel/smp.c         |   7 +++
 drivers/firmware/Kconfig        |   1 +
 drivers/firmware/arm_sdei.c     |   1 +
 include/linux/sdei.h            |   1 +
 10 files changed, 233 insertions(+)
 create mode 100644 arch/arm64/include/asm/sdei.h
 create mode 100644 arch/arm64/kernel/sdei.c

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 3dcd7ec69bca..5813ecc8a3b9 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -92,6 +92,7 @@ config ARM64
 	select HAVE_IRQ_TIME_ACCOUNTING
 	select HAVE_MEMBLOCK
 	select HAVE_MEMBLOCK_NODE_MAP if NUMA
+	select HAVE_NMI
 	select HAVE_PATA_PLATFORM
 	select HAVE_PERF_EVENTS
 	select HAVE_PERF_REGS
diff --git a/arch/arm64/include/asm/sdei.h b/arch/arm64/include/asm/sdei.h
new file mode 100644
index 000000000000..5d74582be162
--- /dev/null
+++ b/arch/arm64/include/asm/sdei.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 ARM Ltd.
+ *
+ * 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/>.
+ */
+#ifndef __ASM_SDEI_H
+#define __ASM_SDEI_H
+
+#include <asm/virt.h>
+
+extern unsigned long sdei_use_hvc;
+
+/* Software Delegated Exception entry point from firmware*/
+void __sdei_asm_handler(unsigned long event_num, unsigned long arg);
+
+static inline void *sdei_arch_get_entry_point(int conduit)
+{
+	/*
+	 * SDEI works between adjacent exception levels. If we booted at EL1 we
+	 * assume a hypervisor is marshalling events. If we booted at EL2 and
+	 * dropped to EL1 because we don't support VHE, then we can't support
+	 * SDEI.
+	 */
+	if (is_hyp_mode_available() && !is_kernel_in_hyp_mode()) {
+		pr_err("Not supported on this hardware/boot configuration\n");
+		return NULL;
+	}
+
+	sdei_use_hvc = (conduit == CONDUIT_HVC) ? 1 : 0;
+
+	return __sdei_asm_handler;
+}
+#define sdei_arch_get_entry_point(x)	sdei_arch_get_entry_point(x)
+
+#endif	/* __ASM_SDEI_H */
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 1dcb69d3d0e5..4f524d7852bf 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -53,6 +53,7 @@ arm64-obj-$(CONFIG_KEXEC)		+= machine_kexec.o relocate_kernel.o	\
 arm64-obj-$(CONFIG_ARM64_RELOC_TEST)	+= arm64-reloc-test.o
 arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o
 arm64-obj-$(CONFIG_CRASH_DUMP)		+= crash_dump.o
+arm64-obj-$(CONFIG_ARM_SDE_INTERFACE)	+= sdei.o
 
 obj-y					+= $(arm64-obj-y) vdso/ probes/
 obj-m					+= $(arm64-obj-m)
diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
index b3bb7ef97bc8..2dd036407046 100644
--- a/arch/arm64/kernel/asm-offsets.c
+++ b/arch/arm64/kernel/asm-offsets.c
@@ -22,6 +22,7 @@
 #include <linux/mm.h>
 #include <linux/dma-mapping.h>
 #include <linux/kvm_host.h>
+#include <linux/sdei.h>
 #include <linux/suspend.h>
 #include <asm/cpufeature.h>
 #include <asm/thread_info.h>
@@ -153,5 +154,6 @@ int main(void)
   DEFINE(HIBERN_PBE_ADDR,	offsetof(struct pbe, address));
   DEFINE(HIBERN_PBE_NEXT,	offsetof(struct pbe, next));
   DEFINE(ARM64_FTR_SYSVAL,	offsetof(struct arm64_ftr_reg, sys_val));
+  DEFINE(SDEI_EVENT_INTREGS,	offsetof(struct sdei_registered_event, interrupted_regs));
   return 0;
 }
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
index b738880350f9..1709063a54a3 100644
--- a/arch/arm64/kernel/entry.S
+++ b/arch/arm64/kernel/entry.S
@@ -18,6 +18,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/err.h>
 #include <linux/init.h>
 #include <linux/linkage.h>
 
@@ -33,6 +34,7 @@
 #include <asm/thread_info.h>
 #include <asm/asm-uaccess.h>
 #include <asm/unistd.h>
+#include <uapi/linux/sdei.h>
 
 /*
  * Context tracking subsystem.  Used to instrument transitions
@@ -865,3 +867,69 @@ ENTRY(sys_rt_sigreturn_wrapper)
 	mov	x0, sp
 	b	sys_rt_sigreturn
 ENDPROC(sys_rt_sigreturn_wrapper)
+
+#ifdef CONFIG_ARM_SDE_INTERFACE
+/*
+ * Software Delegated Exception entry point.
+ *
+ * x0: Event number
+ * x1: struct sdei_registered_event argument from registration time.
+ * x2: interrupted PC
+ * x3: interrupted PSTATE
+ *
+ * Firmware has preserved x0->x17 for us, we must save/restore all other regs.
+ */
+ENTRY(__sdei_asm_handler)
+	stp     x2, x3, [x1, #SDEI_EVENT_INTREGS + S_PC]
+	stp     x4, x5, [x1, #SDEI_EVENT_INTREGS + 16 * 2]
+	stp     x6, x7, [x1, #SDEI_EVENT_INTREGS + 16 * 3]
+	stp     x8, x9, [x1, #SDEI_EVENT_INTREGS + 16 * 4]
+	stp     x10, x11, [x1, #SDEI_EVENT_INTREGS + 16 * 5]
+	stp     x12, x13, [x1, #SDEI_EVENT_INTREGS + 16 * 6]
+	stp     x14, x15, [x1, #SDEI_EVENT_INTREGS + 16 * 7]
+	stp     x16, x17, [x1, #SDEI_EVENT_INTREGS + 16 * 8]
+	stp     x18, x19, [x1, #SDEI_EVENT_INTREGS + 16 * 9]
+	stp     x20, x21, [x1, #SDEI_EVENT_INTREGS + 16 * 10]
+	stp     x22, x23, [x1, #SDEI_EVENT_INTREGS + 16 * 11]
+	stp     x24, x25, [x1, #SDEI_EVENT_INTREGS + 16 * 12]
+	stp     x26, x27, [x1, #SDEI_EVENT_INTREGS + 16 * 13]
+	stp     x28, x29, [x1, #SDEI_EVENT_INTREGS + 16 * 14]
+
+	mov	x19, x1
+
+	/*
+	 * We may have interrupted userspace, or a guest. Restore sp_el0 from
+	 * __entry_task.
+	 */
+	mrs	x0, sp_el0
+	stp     lr, x0, [x19, #SDEI_EVENT_INTREGS + S_LR]
+	ldr_this_cpu	dst=x0, sym=__entry_task, tmp=x1
+	msr	sp_el0, x0
+
+	add	x0, x19, #SDEI_EVENT_INTREGS
+	mov	x1, x19
+	bl	__sdei_handler
+
+	/* restore regs >x17 that we clobbered */
+	ldp     x28, x29, [x19, #SDEI_EVENT_INTREGS + 16 * 14]
+	ldp     lr, x1, [x19, #SDEI_EVENT_INTREGS + S_LR]
+	msr	sp_el0, x1
+	ldp     x18, x19, [x19, #SDEI_EVENT_INTREGS + 16 * 9]
+
+	mov	x1, x0			// address to complete_and_resume
+	/* x0 = (x0 <= 1) ? EVENT_COMPLETE:EVENT_COMPLETE_AND_RESUME */
+	cmp	x0, #1
+	mov_q	x2, SDEI_1_0_FN_SDEI_EVENT_COMPLETE
+	mov_q	x3, SDEI_1_0_FN_SDEI_EVENT_COMPLETE_AND_RESUME
+	csel	x0, x2, x3, ls
+
+	/* On success, this call never returns... */
+	ldr_l	x2, sdei_use_hvc
+	cbnz	x2, 1f
+	smc	#0
+	b	.
+1:	hvc	#0
+	b	.
+ENDPROC(__sdei_asm_handler)
+
+#endif /* CONFIG_ARM_SDE_INTERFACE */
diff --git a/arch/arm64/kernel/sdei.c b/arch/arm64/kernel/sdei.c
new file mode 100644
index 000000000000..c31ccc6fcc0d
--- /dev/null
+++ b/arch/arm64/kernel/sdei.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2017 ARM Ltd.
+ *
+ * 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/sched/task_stack.h>
+#include <linux/irqflags.h>
+#include <linux/hardirq.h>
+#include <linux/sdei.h>
+
+#include <asm/kprobes.h>
+#include <asm/ptrace.h>
+#include <asm/sysreg.h>
+
+unsigned long sdei_use_hvc;
+
+/*
+ * __sdei_handler() returns one of:
+ *  SDEI_EV_HANDLED -  success, return to the interrupted context.
+ *  SDEI_EV_FAILED  -  failure, return this error code to firmare.
+ *  virtual-address -  success, return to this address.
+ */
+static unsigned long _sdei_handler(struct pt_regs *regs,
+				   struct sdei_registered_event *arg)
+{
+	u32 mode;
+	int i, err = 0;
+	u32 kernel_mode = read_sysreg(CurrentEL) | 1;	/* +SPSel */
+	unsigned long vbar = read_sysreg(vbar_el1);
+
+	/* Retrieve the missing registers values */
+	for (i = 0; i < 4; i++) {
+		err = sdei_api_event_context(i, &regs->regs[i]);
+		if (err)
+			break;
+	}
+	if (err) {
+		pr_err("event %u on CPU %u: Failed to retrieve context register %u from firmware\n",
+			arg->event_num, smp_processor_id(), i);
+		return SDEI_EV_FAILED;
+	}
+
+	err = sdei_event_handler(regs, arg);
+	if (err)
+		return SDEI_EV_FAILED;
+
+	mode = regs->pstate & (PSR_MODE32_BIT | PSR_MODE_MASK);
+
+	/*
+	 * If we interrupted the kernel with interrupts masked, we always go
+	 * back to wherever we came from.
+	 */
+	if (mode == kernel_mode && !interrupts_enabled(regs))
+		return SDEI_EV_HANDLED;
+
+	/*
+	 * Otherwise, we pretend this was an IRQ. This lets user space tasks
+	 * receive signals before we return to them, and KVM to invoke it's
+	 * world switch to do the same.
+	 *
+	 * See DDI0487B.a Table D1-7 'Vector offsets from vector table base
+	 * address'.
+	 */
+	if (mode == kernel_mode)
+		return vbar + 0x280;
+	else if (mode & PSR_MODE32_BIT)
+		return vbar + 0x680;
+
+	return vbar + 0x480;
+}
+
+
+asmlinkage __kprobes notrace unsigned long
+__sdei_handler(struct pt_regs *regs, struct sdei_registered_event *arg)
+{
+	unsigned long ret;
+	bool do_nmi_exit = false;
+
+	/*
+	 * nmi_enter() deals with printk() re-entrance and use of RCU when
+	 * RCU believed this CPU was idle. Because critical events can
+	 * interrupt normal events, we may already be in_nmi().
+	 */
+	if (!in_nmi()) {
+		nmi_enter();
+		do_nmi_exit = true;
+	}
+
+	ret = _sdei_handler(regs, arg);
+
+	if (do_nmi_exit)
+		nmi_exit();
+
+	return ret;
+}
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 6e0e16a3a7d4..ceaf0a67f563 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -31,6 +31,7 @@
 #include <linux/mm.h>
 #include <linux/err.h>
 #include <linux/cpu.h>
+#include <linux/sdei.h>
 #include <linux/smp.h>
 #include <linux/seq_file.h>
 #include <linux/irq.h>
@@ -838,6 +839,7 @@ static void ipi_cpu_stop(unsigned int cpu)
 	set_cpu_online(cpu, false);
 
 	local_irq_disable();
+	sdei_mask_local_cpu(smp_processor_id());
 
 	while (1)
 		cpu_relax();
@@ -855,6 +857,7 @@ static void ipi_cpu_crash_stop(unsigned int cpu, struct pt_regs *regs)
 	atomic_dec(&waiting_for_crash_ipi);
 
 	local_irq_disable();
+	sdei_mask_local_cpu(smp_processor_id());
 
 #ifdef CONFIG_HOTPLUG_CPU
 	if (cpu_ops[cpu]->cpu_die)
@@ -975,6 +978,8 @@ void smp_send_stop(void)
 	if (num_online_cpus() > 1)
 		pr_warning("SMP: failed to stop secondary CPUs %*pbl\n",
 			   cpumask_pr_args(cpu_online_mask));
+
+	sdei_mask_local_cpu(smp_processor_id());
 }
 
 #ifdef CONFIG_KEXEC_CORE
@@ -1002,6 +1007,8 @@ void smp_send_crash_stop(void)
 	if (atomic_read(&waiting_for_crash_ipi) > 0)
 		pr_warning("SMP: failed to stop secondary CPUs %*pbl\n",
 			   cpumask_pr_args(&mask));
+
+	sdei_mask_local_cpu(smp_processor_id());
 }
 
 bool smp_crash_stop_failed(void)
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index d8a9859f6102..1f6633b337aa 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -50,6 +50,7 @@ config ARM_SCPI_POWER_DOMAIN
 
 config ARM_SDE_INTERFACE
 	bool "ARM Software Delegated Exception Interface (SDEI)"
+	depends on ARM64
 	help
 	  The Software Delegated Exception Interface (SDEI) is an ARM
 	  standard for registering callbacks from the platform firmware
diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c
index d22dda5e0fed..afd9499a0e2d 100644
--- a/drivers/firmware/arm_sdei.c
+++ b/drivers/firmware/arm_sdei.c
@@ -152,6 +152,7 @@ int sdei_api_event_context(u32 query, u64 *result)
 	*result = res.a0;
 	return err;
 }
+NOKPROBE_SYMBOL(sdei_api_event_context);
 
 static int sdei_api_event_get_info(u32 event, u32 info, u64 *result)
 {
diff --git a/include/linux/sdei.h b/include/linux/sdei.h
index 504e8bf8bc07..6138582faf4b 100644
--- a/include/linux/sdei.h
+++ b/include/linux/sdei.h
@@ -25,6 +25,7 @@ enum sdei_conduit_types {
 	CONDUIT_HVC,
 };
 
+#include <asm/sdei.h>
 #include <uapi/linux/sdei.h>
 
 /* Arch code should override this to set the entry point from firmware... */
-- 
2.10.1

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

* [PATCH 09/11] firmware: arm_sdei: Add support for CPU and system power states
  2017-05-15 17:43 ` James Morse
@ 2017-05-15 17:43     ` James Morse
  -1 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-05-15 17:43 UTC (permalink / raw)
  To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Will Deacon, Catalin Marinas,
	kvmarm-FPEHb7Xf0XXUo1n7N8X6UoWGPAHP3yOg, Marc Zyngier,
	Christoffer Dall, Rob Herring, Mark Rutland,
	devicetree-u79uwXL29TY76Z2rM5mHXA

When a CPU enters an idle lower-power state or is powering off, we
need to mask SDE events so that no events can be delivered while we
are messing with the MMU as the registered entry points won't be valid.

If the system reboots, we want to unregister all events and mask the CPUs.
For kexec this allows us to hand a clean slate to the next kernel
instead of relying on it to call sdei_{private,system}_data_reset().

For hibernate we unregister all events and re-register them on restore,
in case we restored with the SDE code loaded at a different address.
(e.g. KASLR).

Add all the notifiers necessary to do this. We only support shared events
so all events are left registered and enabled over CPU hotplug.

Signed-off-by: James Morse <james.morse-5wv7dgnIgG8@public.gmane.org>
---
 drivers/firmware/arm_sdei.c | 223 +++++++++++++++++++++++++++++++++++++++++++-
 include/linux/cpuhotplug.h  |   1 +
 include/linux/sdei.h        |   3 +
 3 files changed, 226 insertions(+), 1 deletion(-)

diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c
index afd9499a0e2d..f9912d2ba2d3 100644
--- a/drivers/firmware/arm_sdei.c
+++ b/drivers/firmware/arm_sdei.c
@@ -22,17 +22,22 @@
 #include <linux/arm-smccc.h>
 #include <linux/bitops.h>
 #include <linux/compiler.h>
+#include <linux/cpuhotplug.h>
+#include <linux/cpu_pm.h>
 #include <linux/errno.h>
 #include <linux/hardirq.h>
 #include <linux/kernel.h>
 #include <linux/kprobes.h>
 #include <linux/kvm_host.h>
 #include <linux/list.h>
+#include <linux/notifier.h>
 #include <linux/of.h>
 #include <linux/percpu.h>
 #include <linux/platform_device.h>
+#include <linux/pm.h>
 #include <linux/ptrace.h>
 #include <linux/preempt.h>
+#include <linux/reboot.h>
 #include <linux/sdei.h>
 #include <linux/slab.h>
 #include <linux/smp.h>
@@ -332,6 +337,17 @@ static int sdei_platform_reset(void)
 	return err;
 }
 
+static int sdei_api_event_status(u32 event_num, u64 *result)
+{
+	int err;
+	struct arm_smccc_res res;
+
+	err = invoke_sdei_fn(&res, SDEI_1_0_FN_SDEI_EVENT_STATUS, event_num, 0,
+				0, 0, 0);
+	*result = res.a0;
+	return err;
+}
+
 static int sdei_api_event_enable(u32 event_num)
 {
 	struct arm_smccc_res res;
@@ -395,8 +411,23 @@ static int sdei_api_event_unregister(u32 event_num)
 
 static int _sdei_event_unregister(struct sdei_event *event)
 {
-	if (event->type == SDEI_EVENT_TYPE_SHARED)
+	int err;
+	u64 result;
+	bool enabled;
+
+	if (event->type == SDEI_EVENT_TYPE_SHARED) {
+		/*
+		 * We re-register events after power-management, remember if
+		 * this event was previously enabled.
+		 */
+		err = sdei_api_event_status(event->event_num, &result);
+		if (err)
+			return err;
+		enabled = !!(result & BIT(SDEI_EVENT_STATUS_ENABLED));
+		event->registered->was_enabled = enabled;
+
 		return sdei_api_event_unregister(event->event_num);
+	}
 
 	return -EINVAL;
 }
@@ -429,6 +460,26 @@ int sdei_event_unregister(u32 event_num)
 }
 EXPORT_SYMBOL(sdei_event_unregister);
 
+/*
+ * unregister events, but don't destroy them as they are re-registered by
+ * sdei_reregister_events().
+ */
+static int sdei_event_unregister_all(void)
+{
+	int err = 0;
+	struct sdei_event *event;
+
+	spin_lock(&sdei_events_lock);
+	list_for_each_entry(event, &sdei_events, list) {
+		err = _sdei_event_unregister(event);
+		if (err)
+			break;
+	}
+	spin_unlock(&sdei_events_lock);
+
+	return err;
+}
+
 static int sdei_api_event_register(u32 event_num, void *entry_point, void *arg,
 			       u64 flags, u64 affinity)
 {
@@ -484,6 +535,148 @@ int sdei_event_register(u32 event_num, sdei_event_callback *cb, void *arg)
 }
 EXPORT_SYMBOL(sdei_event_register);
 
+static int sdei_reregister_event(struct sdei_event *event)
+{
+	int err;
+
+	WARN_ON(!spin_is_locked(&sdei_events_lock));
+
+	err = _sdei_event_register(event);
+	if (err) {
+		pr_err("Failed to re-register event %u\n", event->event_num);
+		sdei_event_destroy(event);
+		return err;
+	}
+
+	if (event->type == SDEI_EVENT_TYPE_SHARED) {
+		if (event->registered->was_enabled)
+			err = sdei_api_event_enable(event->event_num);
+	}
+
+	if (err)
+		pr_err("Failed to re-enable event %u\n", event->event_num);
+
+	return err;
+}
+
+static int sdei_reregister_events(void)
+{
+	int err = 0;
+	struct sdei_event *event;
+
+	spin_lock(&sdei_events_lock);
+	list_for_each_entry(event, &sdei_events, list) {
+		err = sdei_reregister_event(event);
+		if (err)
+			break;
+	}
+	spin_unlock(&sdei_events_lock);
+
+	return err;
+}
+
+/* When entering idle, mask/unmask events for this cpu */
+static int sdei_pm_notifier(struct notifier_block *nb, unsigned long action,
+			    void *data)
+{
+	int rv;
+
+	switch (action) {
+	case CPU_PM_ENTER:
+		rv = sdei_mask_local_cpu(smp_processor_id());
+		break;
+	case CPU_PM_EXIT:
+		rv = sdei_unmask_local_cpu(smp_processor_id());
+		break;
+	default:
+		return NOTIFY_DONE;
+	}
+
+	if (rv)
+		return notifier_from_errno(rv);
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block sdei_pm_nb = {
+	.notifier_call = sdei_pm_notifier,
+};
+
+static int sdei_device_suspend(struct device *dev)
+{
+	on_each_cpu(_ipi_mask_cpu, NULL, true);
+
+	return 0;
+}
+
+static int sdei_device_resume(struct device *dev)
+{
+	on_each_cpu(_ipi_unmask_cpu, NULL, true);
+
+	return 0;
+}
+
+/*
+ * We need all events to be reregistered when we resume from hibernate.
+ *
+ * The sequence is freeze->thaw. Reboot. freeze->restore. We unregister
+ * events during freeze and save the enabled state, then re-register and
+ * enable them during thaw and restore.
+ */
+static int sdei_device_freeze(struct device *dev)
+{
+	int err;
+
+	err = sdei_event_unregister_all();
+	if (err)
+		return err;
+
+	return sdei_device_suspend(dev);
+}
+
+static int sdei_device_thaw(struct device *dev)
+{
+	sdei_device_resume(dev);
+
+	return sdei_reregister_events();
+}
+
+static int sdei_device_restore(struct device *dev)
+{
+	int err;
+
+	err = sdei_platform_reset();
+	if (err)
+		return err;
+
+	return sdei_device_thaw(dev);
+}
+
+static const struct dev_pm_ops sdei_pm_ops = {
+	.suspend = sdei_device_suspend,
+	.resume = sdei_device_resume,
+	.freeze = sdei_device_freeze,
+	.thaw = sdei_device_thaw,
+	.restore = sdei_device_restore,
+};
+
+/*
+ * Mask all CPUs and unregister all events on panic, reboot or kexec.
+ */
+static int sdei_reboot_notifier(struct notifier_block *nb, unsigned long action,
+				void *data)
+{
+	on_each_cpu(&_ipi_mask_cpu, NULL, true);
+
+	sdei_platform_reset();
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block sdei_reboot_nb = {
+	.notifier_call = sdei_reboot_notifier,
+};
+
 static void sdei_smccc_smc(unsigned long function_id,
 			   unsigned long arg0, unsigned long arg1,
 			   unsigned long arg2, unsigned long arg3,
@@ -566,9 +759,36 @@ static int sdei_probe(struct platform_device *pdev)
 		return 0;
 	}
 
+	err = cpuhp_setup_state_nocalls(CPUHP_AP_SDEI_STARTING, "SDEI",
+				&sdei_unmask_local_cpu, &sdei_mask_local_cpu);
+	if (err) {
+		pr_warn("Failed to register CPU hotplug notifier...\n");
+		return err;
+	}
+
+	err = cpu_pm_register_notifier(&sdei_pm_nb);
+	if (err) {
+		pr_warn("Failed to register CPU PM notifier...\n");
+		goto remove_cpuhp;
+	}
+
+	err = register_reboot_notifier(&sdei_reboot_nb);
+	if (err) {
+		pr_warn("Failed to register reboot notifier...\n");
+		goto remove_cpupm;
+	}
+
 	on_each_cpu(&_ipi_unmask_cpu, NULL, false);
 
 	return 0;
+
+remove_cpupm:
+	cpu_pm_unregister_notifier(&sdei_pm_nb);
+
+remove_cpuhp:
+	cpuhp_remove_state_nocalls(CPUHP_AP_SDEI_STARTING);
+
+	return err;
 }
 
 static const struct of_device_id sdei_of_match[] = {
@@ -579,6 +799,7 @@ static const struct of_device_id sdei_of_match[] = {
 static struct platform_driver sdei_driver = {
 	.driver		= {
 		.name			= "sdei",
+		.pm			= &sdei_pm_ops,
 		.of_match_table		= sdei_of_match,
 	},
 	.probe		= sdei_probe,
diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
index 0f2a80377520..5c5012c211a8 100644
--- a/include/linux/cpuhotplug.h
+++ b/include/linux/cpuhotplug.h
@@ -91,6 +91,7 @@ enum cpuhp_state {
 	CPUHP_AP_PERF_XTENSA_STARTING,
 	CPUHP_AP_PERF_METAG_STARTING,
 	CPUHP_AP_MIPS_OP_LOONGSON3_STARTING,
+	CPUHP_AP_SDEI_STARTING,
 	CPUHP_AP_ARM_VFP_STARTING,
 	CPUHP_AP_ARM64_DEBUG_MONITORS_STARTING,
 	CPUHP_AP_PERF_ARM_HW_BREAKPOINT_STARTING,
diff --git a/include/linux/sdei.h b/include/linux/sdei.h
index 6138582faf4b..3b933d37a87d 100644
--- a/include/linux/sdei.h
+++ b/include/linux/sdei.h
@@ -87,6 +87,9 @@ struct sdei_registered_event {
 	sdei_event_callback	*callback;
 	void			*callback_arg;
 	u32			 event_num;
+
+	/* Only valid over power management events. */
+	bool			 was_enabled;
 };
 
 /* The arch code entry point should then call this when an event arrives. */
-- 
2.10.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 09/11] firmware: arm_sdei: Add support for CPU and system power states
@ 2017-05-15 17:43     ` James Morse
  0 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-05-15 17:43 UTC (permalink / raw)
  To: linux-arm-kernel

When a CPU enters an idle lower-power state or is powering off, we
need to mask SDE events so that no events can be delivered while we
are messing with the MMU as the registered entry points won't be valid.

If the system reboots, we want to unregister all events and mask the CPUs.
For kexec this allows us to hand a clean slate to the next kernel
instead of relying on it to call sdei_{private,system}_data_reset().

For hibernate we unregister all events and re-register them on restore,
in case we restored with the SDE code loaded at a different address.
(e.g. KASLR).

Add all the notifiers necessary to do this. We only support shared events
so all events are left registered and enabled over CPU hotplug.

Signed-off-by: James Morse <james.morse@arm.com>
---
 drivers/firmware/arm_sdei.c | 223 +++++++++++++++++++++++++++++++++++++++++++-
 include/linux/cpuhotplug.h  |   1 +
 include/linux/sdei.h        |   3 +
 3 files changed, 226 insertions(+), 1 deletion(-)

diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c
index afd9499a0e2d..f9912d2ba2d3 100644
--- a/drivers/firmware/arm_sdei.c
+++ b/drivers/firmware/arm_sdei.c
@@ -22,17 +22,22 @@
 #include <linux/arm-smccc.h>
 #include <linux/bitops.h>
 #include <linux/compiler.h>
+#include <linux/cpuhotplug.h>
+#include <linux/cpu_pm.h>
 #include <linux/errno.h>
 #include <linux/hardirq.h>
 #include <linux/kernel.h>
 #include <linux/kprobes.h>
 #include <linux/kvm_host.h>
 #include <linux/list.h>
+#include <linux/notifier.h>
 #include <linux/of.h>
 #include <linux/percpu.h>
 #include <linux/platform_device.h>
+#include <linux/pm.h>
 #include <linux/ptrace.h>
 #include <linux/preempt.h>
+#include <linux/reboot.h>
 #include <linux/sdei.h>
 #include <linux/slab.h>
 #include <linux/smp.h>
@@ -332,6 +337,17 @@ static int sdei_platform_reset(void)
 	return err;
 }
 
+static int sdei_api_event_status(u32 event_num, u64 *result)
+{
+	int err;
+	struct arm_smccc_res res;
+
+	err = invoke_sdei_fn(&res, SDEI_1_0_FN_SDEI_EVENT_STATUS, event_num, 0,
+				0, 0, 0);
+	*result = res.a0;
+	return err;
+}
+
 static int sdei_api_event_enable(u32 event_num)
 {
 	struct arm_smccc_res res;
@@ -395,8 +411,23 @@ static int sdei_api_event_unregister(u32 event_num)
 
 static int _sdei_event_unregister(struct sdei_event *event)
 {
-	if (event->type == SDEI_EVENT_TYPE_SHARED)
+	int err;
+	u64 result;
+	bool enabled;
+
+	if (event->type == SDEI_EVENT_TYPE_SHARED) {
+		/*
+		 * We re-register events after power-management, remember if
+		 * this event was previously enabled.
+		 */
+		err = sdei_api_event_status(event->event_num, &result);
+		if (err)
+			return err;
+		enabled = !!(result & BIT(SDEI_EVENT_STATUS_ENABLED));
+		event->registered->was_enabled = enabled;
+
 		return sdei_api_event_unregister(event->event_num);
+	}
 
 	return -EINVAL;
 }
@@ -429,6 +460,26 @@ int sdei_event_unregister(u32 event_num)
 }
 EXPORT_SYMBOL(sdei_event_unregister);
 
+/*
+ * unregister events, but don't destroy them as they are re-registered by
+ * sdei_reregister_events().
+ */
+static int sdei_event_unregister_all(void)
+{
+	int err = 0;
+	struct sdei_event *event;
+
+	spin_lock(&sdei_events_lock);
+	list_for_each_entry(event, &sdei_events, list) {
+		err = _sdei_event_unregister(event);
+		if (err)
+			break;
+	}
+	spin_unlock(&sdei_events_lock);
+
+	return err;
+}
+
 static int sdei_api_event_register(u32 event_num, void *entry_point, void *arg,
 			       u64 flags, u64 affinity)
 {
@@ -484,6 +535,148 @@ int sdei_event_register(u32 event_num, sdei_event_callback *cb, void *arg)
 }
 EXPORT_SYMBOL(sdei_event_register);
 
+static int sdei_reregister_event(struct sdei_event *event)
+{
+	int err;
+
+	WARN_ON(!spin_is_locked(&sdei_events_lock));
+
+	err = _sdei_event_register(event);
+	if (err) {
+		pr_err("Failed to re-register event %u\n", event->event_num);
+		sdei_event_destroy(event);
+		return err;
+	}
+
+	if (event->type == SDEI_EVENT_TYPE_SHARED) {
+		if (event->registered->was_enabled)
+			err = sdei_api_event_enable(event->event_num);
+	}
+
+	if (err)
+		pr_err("Failed to re-enable event %u\n", event->event_num);
+
+	return err;
+}
+
+static int sdei_reregister_events(void)
+{
+	int err = 0;
+	struct sdei_event *event;
+
+	spin_lock(&sdei_events_lock);
+	list_for_each_entry(event, &sdei_events, list) {
+		err = sdei_reregister_event(event);
+		if (err)
+			break;
+	}
+	spin_unlock(&sdei_events_lock);
+
+	return err;
+}
+
+/* When entering idle, mask/unmask events for this cpu */
+static int sdei_pm_notifier(struct notifier_block *nb, unsigned long action,
+			    void *data)
+{
+	int rv;
+
+	switch (action) {
+	case CPU_PM_ENTER:
+		rv = sdei_mask_local_cpu(smp_processor_id());
+		break;
+	case CPU_PM_EXIT:
+		rv = sdei_unmask_local_cpu(smp_processor_id());
+		break;
+	default:
+		return NOTIFY_DONE;
+	}
+
+	if (rv)
+		return notifier_from_errno(rv);
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block sdei_pm_nb = {
+	.notifier_call = sdei_pm_notifier,
+};
+
+static int sdei_device_suspend(struct device *dev)
+{
+	on_each_cpu(_ipi_mask_cpu, NULL, true);
+
+	return 0;
+}
+
+static int sdei_device_resume(struct device *dev)
+{
+	on_each_cpu(_ipi_unmask_cpu, NULL, true);
+
+	return 0;
+}
+
+/*
+ * We need all events to be reregistered when we resume from hibernate.
+ *
+ * The sequence is freeze->thaw. Reboot. freeze->restore. We unregister
+ * events during freeze and save the enabled state, then re-register and
+ * enable them during thaw and restore.
+ */
+static int sdei_device_freeze(struct device *dev)
+{
+	int err;
+
+	err = sdei_event_unregister_all();
+	if (err)
+		return err;
+
+	return sdei_device_suspend(dev);
+}
+
+static int sdei_device_thaw(struct device *dev)
+{
+	sdei_device_resume(dev);
+
+	return sdei_reregister_events();
+}
+
+static int sdei_device_restore(struct device *dev)
+{
+	int err;
+
+	err = sdei_platform_reset();
+	if (err)
+		return err;
+
+	return sdei_device_thaw(dev);
+}
+
+static const struct dev_pm_ops sdei_pm_ops = {
+	.suspend = sdei_device_suspend,
+	.resume = sdei_device_resume,
+	.freeze = sdei_device_freeze,
+	.thaw = sdei_device_thaw,
+	.restore = sdei_device_restore,
+};
+
+/*
+ * Mask all CPUs and unregister all events on panic, reboot or kexec.
+ */
+static int sdei_reboot_notifier(struct notifier_block *nb, unsigned long action,
+				void *data)
+{
+	on_each_cpu(&_ipi_mask_cpu, NULL, true);
+
+	sdei_platform_reset();
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block sdei_reboot_nb = {
+	.notifier_call = sdei_reboot_notifier,
+};
+
 static void sdei_smccc_smc(unsigned long function_id,
 			   unsigned long arg0, unsigned long arg1,
 			   unsigned long arg2, unsigned long arg3,
@@ -566,9 +759,36 @@ static int sdei_probe(struct platform_device *pdev)
 		return 0;
 	}
 
+	err = cpuhp_setup_state_nocalls(CPUHP_AP_SDEI_STARTING, "SDEI",
+				&sdei_unmask_local_cpu, &sdei_mask_local_cpu);
+	if (err) {
+		pr_warn("Failed to register CPU hotplug notifier...\n");
+		return err;
+	}
+
+	err = cpu_pm_register_notifier(&sdei_pm_nb);
+	if (err) {
+		pr_warn("Failed to register CPU PM notifier...\n");
+		goto remove_cpuhp;
+	}
+
+	err = register_reboot_notifier(&sdei_reboot_nb);
+	if (err) {
+		pr_warn("Failed to register reboot notifier...\n");
+		goto remove_cpupm;
+	}
+
 	on_each_cpu(&_ipi_unmask_cpu, NULL, false);
 
 	return 0;
+
+remove_cpupm:
+	cpu_pm_unregister_notifier(&sdei_pm_nb);
+
+remove_cpuhp:
+	cpuhp_remove_state_nocalls(CPUHP_AP_SDEI_STARTING);
+
+	return err;
 }
 
 static const struct of_device_id sdei_of_match[] = {
@@ -579,6 +799,7 @@ static const struct of_device_id sdei_of_match[] = {
 static struct platform_driver sdei_driver = {
 	.driver		= {
 		.name			= "sdei",
+		.pm			= &sdei_pm_ops,
 		.of_match_table		= sdei_of_match,
 	},
 	.probe		= sdei_probe,
diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
index 0f2a80377520..5c5012c211a8 100644
--- a/include/linux/cpuhotplug.h
+++ b/include/linux/cpuhotplug.h
@@ -91,6 +91,7 @@ enum cpuhp_state {
 	CPUHP_AP_PERF_XTENSA_STARTING,
 	CPUHP_AP_PERF_METAG_STARTING,
 	CPUHP_AP_MIPS_OP_LOONGSON3_STARTING,
+	CPUHP_AP_SDEI_STARTING,
 	CPUHP_AP_ARM_VFP_STARTING,
 	CPUHP_AP_ARM64_DEBUG_MONITORS_STARTING,
 	CPUHP_AP_PERF_ARM_HW_BREAKPOINT_STARTING,
diff --git a/include/linux/sdei.h b/include/linux/sdei.h
index 6138582faf4b..3b933d37a87d 100644
--- a/include/linux/sdei.h
+++ b/include/linux/sdei.h
@@ -87,6 +87,9 @@ struct sdei_registered_event {
 	sdei_event_callback	*callback;
 	void			*callback_arg;
 	u32			 event_num;
+
+	/* Only valid over power management events. */
+	bool			 was_enabled;
 };
 
 /* The arch code entry point should then call this when an event arrives. */
-- 
2.10.1

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

* [PATCH 10/11] firmware: arm_sdei: add support for CPU private events
  2017-05-15 17:43 ` James Morse
@ 2017-05-15 17:43     ` James Morse
  -1 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-05-15 17:43 UTC (permalink / raw)
  To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Will Deacon, Catalin Marinas,
	kvmarm-FPEHb7Xf0XXUo1n7N8X6UoWGPAHP3yOg, Marc Zyngier,
	Christoffer Dall, Rob Herring, Mark Rutland,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Private SDE events are per-cpu, and need to be registered and enabled
on each CPU.

Hide this detail from the caller by adapting our {,un}register and
{en,dis}able calls to send an IPI to each CPU if the event is private.

CPU private events are unregistered when the CPU is powered-off, and
re-registered when the CPU is brought back online. This saves bringing
secondary cores back online to call private_reset() on shutdown, kexec
and resume from hibernate.

Signed-off-by: James Morse <james.morse-5wv7dgnIgG8@public.gmane.org>
---
 drivers/firmware/arm_sdei.c | 237 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 232 insertions(+), 5 deletions(-)

diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c
index f9912d2ba2d3..6f4a43cb1672 100644
--- a/drivers/firmware/arm_sdei.c
+++ b/drivers/firmware/arm_sdei.c
@@ -20,9 +20,11 @@
 
 #include <linux/acpi.h>
 #include <linux/arm-smccc.h>
+#include <linux/atomic.h>
 #include <linux/bitops.h>
 #include <linux/compiler.h>
 #include <linux/cpuhotplug.h>
+#include <linux/cpu.h>
 #include <linux/cpu_pm.h>
 #include <linux/errno.h>
 #include <linux/hardirq.h>
@@ -63,12 +65,49 @@ struct sdei_event {
 	u64			priority;
 
 	/* This pointer is handed to firmware as the event argument. */
-	struct sdei_registered_event *registered;
+	union {
+		/* Shared events */
+		struct sdei_registered_event *registered;
+
+		/* CPU private events */
+		struct sdei_registered_event __percpu *private_registered;
+	};
 };
 
 static LIST_HEAD(sdei_events);
 static DEFINE_SPINLOCK(sdei_events_lock);
 
+/* When frozen, cpu-hotplug notifiers shouldn't unregister/re-register events */
+static bool frozen;
+
+/* Private events are registered/enabled via IPI passing one of these */
+struct sdei_crosscall_args {
+	struct sdei_event *event;
+	atomic_t errors;
+	int first_error;
+};
+
+#define CROSSCALL_INIT(arg, event)	(arg.event = event, \
+					 arg.first_error = 0, \
+					 atomic_set(&arg.errors, 0))
+
+static inline int sdei_do_cross_call(void *fn, struct sdei_event * event)
+{
+	struct sdei_crosscall_args arg;
+
+	CROSSCALL_INIT(arg, event);
+	on_each_cpu(fn, &arg, true);
+
+	return arg.first_error;
+}
+
+static inline void
+sdei_cross_call_return(struct sdei_crosscall_args *arg, int err)
+{
+	if (err && (atomic_inc_return(&arg->errors) == 1))
+		arg->first_error = err;
+}
+
 static int sdei_to_linux_errno(unsigned long sdei_err)
 {
 	switch (sdei_err) {
@@ -213,6 +252,25 @@ static struct sdei_event *sdei_event_create(u32 event_num,
 		reg->callback = cb;
 		reg->callback_arg = cb_arg;
 		event->registered = reg;
+	} else {
+		int cpu;
+		struct sdei_registered_event __percpu *regs;
+
+		regs = alloc_percpu(struct sdei_registered_event);
+		if (!regs) {
+			kfree(event);
+			return ERR_PTR(-ENOMEM);
+		}
+
+		for_each_possible_cpu(cpu) {
+			reg = per_cpu_ptr(regs, cpu);
+
+			reg->event_num = event->event_num;
+			reg->callback = cb;
+			reg->callback_arg = cb_arg;
+		}
+
+		event->private_registered = regs;
 	}
 
 	if (sdei_event_find(event_num)) {
@@ -234,6 +292,8 @@ static void sdei_event_destroy(struct sdei_event *event)
 
 	if (event->type == SDEI_EVENT_TYPE_SHARED)
 		kfree(event->registered);
+	else
+		free_percpu(event->private_registered);
 
 	kfree(event);
 }
@@ -356,6 +416,16 @@ static int sdei_api_event_enable(u32 event_num)
 			      0, 0, 0);
 }
 
+static void _ipi_event_enable(void *data)
+{
+	int err;
+	struct sdei_crosscall_args *arg = data;
+
+	err = sdei_api_event_enable(arg->event->event_num);
+
+	sdei_cross_call_return(arg, err);
+}
+
 int sdei_event_enable(u32 event_num)
 {
 	int err = -EINVAL;
@@ -367,6 +437,8 @@ int sdei_event_enable(u32 event_num)
 		err = -ENOENT;
 	else if (event->type == SDEI_EVENT_TYPE_SHARED)
 		err = sdei_api_event_enable(event->event_num);
+	else
+		err = sdei_do_cross_call(_ipi_event_enable, event);
 	spin_unlock(&sdei_events_lock);
 
 	return err;
@@ -384,6 +456,16 @@ static int sdei_api_event_disable(u32 event_num)
 	return err;
 }
 
+static void _ipi_event_disable(void *data)
+{
+	int err;
+	struct sdei_crosscall_args *arg = data;
+
+	err = sdei_api_event_disable(arg->event->event_num);
+
+	sdei_cross_call_return(arg, err);
+}
+
 int sdei_event_disable(u32 event_num)
 {
 	int err = -EINVAL;
@@ -395,6 +477,9 @@ int sdei_event_disable(u32 event_num)
 		err = -ENOENT;
 	else if (event->type == SDEI_EVENT_TYPE_SHARED)
 		err = sdei_api_event_disable(event->event_num);
+	else
+		err = sdei_do_cross_call(_ipi_event_disable, event);
+
 	spin_unlock(&sdei_events_lock);
 
 	return err;
@@ -409,6 +494,27 @@ static int sdei_api_event_unregister(u32 event_num)
 			      event_num, 0, 0, 0, 0);
 }
 
+static void _local_event_unregister(void *data)
+{
+	int err;
+	u64 result;
+	struct sdei_registered_event *reg;
+	struct sdei_crosscall_args *arg = data;
+
+	WARN_ON_ONCE(preemptible());
+	WARN_ON(!spin_is_locked(&sdei_events_lock));
+
+	reg = per_cpu_ptr(arg->event->private_registered, smp_processor_id());
+	err = sdei_api_event_status(reg->event_num, &result);
+	if (!err) {
+		reg->was_enabled = !!(result & BIT(SDEI_EVENT_STATUS_ENABLED));
+
+		err = sdei_api_event_unregister(arg->event->event_num);
+	}
+
+	sdei_cross_call_return(arg, err);
+}
+
 static int _sdei_event_unregister(struct sdei_event *event)
 {
 	int err;
@@ -429,7 +535,7 @@ static int _sdei_event_unregister(struct sdei_event *event)
 		return sdei_api_event_unregister(event->event_num);
 	}
 
-	return -EINVAL;
+	return sdei_do_cross_call(_local_event_unregister, event);
 }
 
 int sdei_event_unregister(u32 event_num)
@@ -490,15 +596,41 @@ static int sdei_api_event_register(u32 event_num, void *entry_point, void *arg,
 			      flags, affinity);
 }
 
+static void _local_event_register(void *data)
+{
+	int err;
+	struct sdei_registered_event *reg;
+	struct sdei_crosscall_args *arg = data;
+
+	WARN_ON(preemptible());
+	WARN_ON(!spin_is_locked(&sdei_events_lock));
+
+	reg = per_cpu_ptr(arg->event->private_registered, smp_processor_id());
+	err = sdei_api_event_register(arg->event->event_num, sdei_entry_point,
+				      reg, 0, 0);
+
+	sdei_cross_call_return(arg, err);
+}
+
 static int _sdei_event_register(struct sdei_event *event)
 {
+	int err;
+
 	if (event->type == SDEI_EVENT_TYPE_SHARED)
 		return sdei_api_event_register(event->event_num,
 					       sdei_entry_point,
 					       event->registered,
 					       SDEI_EVENT_REGISTER_RM_ANY, 0);
 
-	return -EINVAL;
+	get_online_cpus();
+
+	err = sdei_do_cross_call(_local_event_register, event);
+	if (err)
+		sdei_do_cross_call(_local_event_unregister, event);
+
+	put_online_cpus();
+
+	return err;
 }
 
 int sdei_event_register(u32 event_num, sdei_event_callback *cb, void *arg)
@@ -535,6 +667,22 @@ int sdei_event_register(u32 event_num, sdei_event_callback *cb, void *arg)
 }
 EXPORT_SYMBOL(sdei_event_register);
 
+static void _local_event_reenable(void *data)
+{
+	int err = 0;
+	struct sdei_registered_event *reg;
+	struct sdei_crosscall_args *arg = data;
+
+	WARN_ON_ONCE(preemptible());
+	WARN_ON(!spin_is_locked(&sdei_events_lock));
+
+	reg = per_cpu_ptr(arg->event->private_registered, smp_processor_id());
+	if (reg->was_enabled)
+		err = sdei_api_event_enable(arg->event->event_num);
+
+	sdei_cross_call_return(arg, err);
+}
+
 static int sdei_reregister_event(struct sdei_event *event)
 {
 	int err;
@@ -551,6 +699,8 @@ static int sdei_reregister_event(struct sdei_event *event)
 	if (event->type == SDEI_EVENT_TYPE_SHARED) {
 		if (event->registered->was_enabled)
 			err = sdei_api_event_enable(event->event_num);
+	} else {
+		err = sdei_do_cross_call(_local_event_reenable, event);
 	}
 
 	if (err)
@@ -575,6 +725,72 @@ static int sdei_reregister_events(void)
 	return err;
 }
 
+static int sdei_cpuhp_down(unsigned int cpu)
+{
+	int err = 0;
+	struct sdei_event *event;
+	struct sdei_crosscall_args arg;
+
+	if (frozen)
+		return 0;
+
+	/* un-register private events */
+	spin_lock(&sdei_events_lock);
+	list_for_each_entry(event, &sdei_events, list) {
+		if (event->type == SDEI_EVENT_TYPE_SHARED)
+			continue;
+
+		CROSSCALL_INIT(arg, event);
+		/* call the cross-call function locally... */
+		_local_event_unregister(&arg);
+		err = arg.first_error;
+		if (err)
+			break;
+	}
+	spin_unlock(&sdei_events_lock);
+
+	if (err)
+		return err;
+
+	return sdei_mask_local_cpu(cpu);
+}
+
+static int sdei_cpuhp_up(unsigned int cpu)
+{
+	int err = 0;
+	struct sdei_event *event;
+	struct sdei_crosscall_args arg;
+
+	if (frozen)
+		return 0;
+
+	/* re-register/enable private events */
+	spin_lock(&sdei_events_lock);
+	list_for_each_entry(event, &sdei_events, list) {
+		if (event->type == SDEI_EVENT_TYPE_SHARED)
+			continue;
+
+		CROSSCALL_INIT(arg, event);
+		/* call the cross-call function locally... */
+		_local_event_register(&arg);
+		err = arg.first_error;
+		if (err)
+			break;
+
+		CROSSCALL_INIT(arg, event);
+		_local_event_reenable(&arg);
+		err = arg.first_error;
+		if (err)
+			break;
+	}
+	spin_unlock(&sdei_events_lock);
+
+	if (err)
+		return err;
+
+	return sdei_unmask_local_cpu(cpu);
+}
+
 /* When entering idle, mask/unmask events for this cpu */
 static int sdei_pm_notifier(struct notifier_block *nb, unsigned long action,
 			    void *data)
@@ -627,6 +843,7 @@ static int sdei_device_freeze(struct device *dev)
 {
 	int err;
 
+	frozen = true;
 	err = sdei_event_unregister_all();
 	if (err)
 		return err;
@@ -636,9 +853,13 @@ static int sdei_device_freeze(struct device *dev)
 
 static int sdei_device_thaw(struct device *dev)
 {
+	int err;
+
 	sdei_device_resume(dev);
 
-	return sdei_reregister_events();
+	err = sdei_reregister_events();
+	frozen = false;
+	return err;
 }
 
 static int sdei_device_restore(struct device *dev)
@@ -670,6 +891,12 @@ static int sdei_reboot_notifier(struct notifier_block *nb, unsigned long action,
 
 	sdei_platform_reset();
 
+	/*
+	 * There is now no point trying to unregister private events if we go on
+	 * to take CPUs offline.
+	 */
+	frozen = true;
+
 	return NOTIFY_OK;
 }
 
@@ -760,7 +987,7 @@ static int sdei_probe(struct platform_device *pdev)
 	}
 
 	err = cpuhp_setup_state_nocalls(CPUHP_AP_SDEI_STARTING, "SDEI",
-				&sdei_unmask_local_cpu, &sdei_mask_local_cpu);
+				&sdei_cpuhp_up, &sdei_cpuhp_down);
 	if (err) {
 		pr_warn("Failed to register CPU hotplug notifier...\n");
 		return err;
-- 
2.10.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 10/11] firmware: arm_sdei: add support for CPU private events
@ 2017-05-15 17:43     ` James Morse
  0 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-05-15 17:43 UTC (permalink / raw)
  To: linux-arm-kernel

Private SDE events are per-cpu, and need to be registered and enabled
on each CPU.

Hide this detail from the caller by adapting our {,un}register and
{en,dis}able calls to send an IPI to each CPU if the event is private.

CPU private events are unregistered when the CPU is powered-off, and
re-registered when the CPU is brought back online. This saves bringing
secondary cores back online to call private_reset() on shutdown, kexec
and resume from hibernate.

Signed-off-by: James Morse <james.morse@arm.com>
---
 drivers/firmware/arm_sdei.c | 237 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 232 insertions(+), 5 deletions(-)

diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c
index f9912d2ba2d3..6f4a43cb1672 100644
--- a/drivers/firmware/arm_sdei.c
+++ b/drivers/firmware/arm_sdei.c
@@ -20,9 +20,11 @@
 
 #include <linux/acpi.h>
 #include <linux/arm-smccc.h>
+#include <linux/atomic.h>
 #include <linux/bitops.h>
 #include <linux/compiler.h>
 #include <linux/cpuhotplug.h>
+#include <linux/cpu.h>
 #include <linux/cpu_pm.h>
 #include <linux/errno.h>
 #include <linux/hardirq.h>
@@ -63,12 +65,49 @@ struct sdei_event {
 	u64			priority;
 
 	/* This pointer is handed to firmware as the event argument. */
-	struct sdei_registered_event *registered;
+	union {
+		/* Shared events */
+		struct sdei_registered_event *registered;
+
+		/* CPU private events */
+		struct sdei_registered_event __percpu *private_registered;
+	};
 };
 
 static LIST_HEAD(sdei_events);
 static DEFINE_SPINLOCK(sdei_events_lock);
 
+/* When frozen, cpu-hotplug notifiers shouldn't unregister/re-register events */
+static bool frozen;
+
+/* Private events are registered/enabled via IPI passing one of these */
+struct sdei_crosscall_args {
+	struct sdei_event *event;
+	atomic_t errors;
+	int first_error;
+};
+
+#define CROSSCALL_INIT(arg, event)	(arg.event = event, \
+					 arg.first_error = 0, \
+					 atomic_set(&arg.errors, 0))
+
+static inline int sdei_do_cross_call(void *fn, struct sdei_event * event)
+{
+	struct sdei_crosscall_args arg;
+
+	CROSSCALL_INIT(arg, event);
+	on_each_cpu(fn, &arg, true);
+
+	return arg.first_error;
+}
+
+static inline void
+sdei_cross_call_return(struct sdei_crosscall_args *arg, int err)
+{
+	if (err && (atomic_inc_return(&arg->errors) == 1))
+		arg->first_error = err;
+}
+
 static int sdei_to_linux_errno(unsigned long sdei_err)
 {
 	switch (sdei_err) {
@@ -213,6 +252,25 @@ static struct sdei_event *sdei_event_create(u32 event_num,
 		reg->callback = cb;
 		reg->callback_arg = cb_arg;
 		event->registered = reg;
+	} else {
+		int cpu;
+		struct sdei_registered_event __percpu *regs;
+
+		regs = alloc_percpu(struct sdei_registered_event);
+		if (!regs) {
+			kfree(event);
+			return ERR_PTR(-ENOMEM);
+		}
+
+		for_each_possible_cpu(cpu) {
+			reg = per_cpu_ptr(regs, cpu);
+
+			reg->event_num = event->event_num;
+			reg->callback = cb;
+			reg->callback_arg = cb_arg;
+		}
+
+		event->private_registered = regs;
 	}
 
 	if (sdei_event_find(event_num)) {
@@ -234,6 +292,8 @@ static void sdei_event_destroy(struct sdei_event *event)
 
 	if (event->type == SDEI_EVENT_TYPE_SHARED)
 		kfree(event->registered);
+	else
+		free_percpu(event->private_registered);
 
 	kfree(event);
 }
@@ -356,6 +416,16 @@ static int sdei_api_event_enable(u32 event_num)
 			      0, 0, 0);
 }
 
+static void _ipi_event_enable(void *data)
+{
+	int err;
+	struct sdei_crosscall_args *arg = data;
+
+	err = sdei_api_event_enable(arg->event->event_num);
+
+	sdei_cross_call_return(arg, err);
+}
+
 int sdei_event_enable(u32 event_num)
 {
 	int err = -EINVAL;
@@ -367,6 +437,8 @@ int sdei_event_enable(u32 event_num)
 		err = -ENOENT;
 	else if (event->type == SDEI_EVENT_TYPE_SHARED)
 		err = sdei_api_event_enable(event->event_num);
+	else
+		err = sdei_do_cross_call(_ipi_event_enable, event);
 	spin_unlock(&sdei_events_lock);
 
 	return err;
@@ -384,6 +456,16 @@ static int sdei_api_event_disable(u32 event_num)
 	return err;
 }
 
+static void _ipi_event_disable(void *data)
+{
+	int err;
+	struct sdei_crosscall_args *arg = data;
+
+	err = sdei_api_event_disable(arg->event->event_num);
+
+	sdei_cross_call_return(arg, err);
+}
+
 int sdei_event_disable(u32 event_num)
 {
 	int err = -EINVAL;
@@ -395,6 +477,9 @@ int sdei_event_disable(u32 event_num)
 		err = -ENOENT;
 	else if (event->type == SDEI_EVENT_TYPE_SHARED)
 		err = sdei_api_event_disable(event->event_num);
+	else
+		err = sdei_do_cross_call(_ipi_event_disable, event);
+
 	spin_unlock(&sdei_events_lock);
 
 	return err;
@@ -409,6 +494,27 @@ static int sdei_api_event_unregister(u32 event_num)
 			      event_num, 0, 0, 0, 0);
 }
 
+static void _local_event_unregister(void *data)
+{
+	int err;
+	u64 result;
+	struct sdei_registered_event *reg;
+	struct sdei_crosscall_args *arg = data;
+
+	WARN_ON_ONCE(preemptible());
+	WARN_ON(!spin_is_locked(&sdei_events_lock));
+
+	reg = per_cpu_ptr(arg->event->private_registered, smp_processor_id());
+	err = sdei_api_event_status(reg->event_num, &result);
+	if (!err) {
+		reg->was_enabled = !!(result & BIT(SDEI_EVENT_STATUS_ENABLED));
+
+		err = sdei_api_event_unregister(arg->event->event_num);
+	}
+
+	sdei_cross_call_return(arg, err);
+}
+
 static int _sdei_event_unregister(struct sdei_event *event)
 {
 	int err;
@@ -429,7 +535,7 @@ static int _sdei_event_unregister(struct sdei_event *event)
 		return sdei_api_event_unregister(event->event_num);
 	}
 
-	return -EINVAL;
+	return sdei_do_cross_call(_local_event_unregister, event);
 }
 
 int sdei_event_unregister(u32 event_num)
@@ -490,15 +596,41 @@ static int sdei_api_event_register(u32 event_num, void *entry_point, void *arg,
 			      flags, affinity);
 }
 
+static void _local_event_register(void *data)
+{
+	int err;
+	struct sdei_registered_event *reg;
+	struct sdei_crosscall_args *arg = data;
+
+	WARN_ON(preemptible());
+	WARN_ON(!spin_is_locked(&sdei_events_lock));
+
+	reg = per_cpu_ptr(arg->event->private_registered, smp_processor_id());
+	err = sdei_api_event_register(arg->event->event_num, sdei_entry_point,
+				      reg, 0, 0);
+
+	sdei_cross_call_return(arg, err);
+}
+
 static int _sdei_event_register(struct sdei_event *event)
 {
+	int err;
+
 	if (event->type == SDEI_EVENT_TYPE_SHARED)
 		return sdei_api_event_register(event->event_num,
 					       sdei_entry_point,
 					       event->registered,
 					       SDEI_EVENT_REGISTER_RM_ANY, 0);
 
-	return -EINVAL;
+	get_online_cpus();
+
+	err = sdei_do_cross_call(_local_event_register, event);
+	if (err)
+		sdei_do_cross_call(_local_event_unregister, event);
+
+	put_online_cpus();
+
+	return err;
 }
 
 int sdei_event_register(u32 event_num, sdei_event_callback *cb, void *arg)
@@ -535,6 +667,22 @@ int sdei_event_register(u32 event_num, sdei_event_callback *cb, void *arg)
 }
 EXPORT_SYMBOL(sdei_event_register);
 
+static void _local_event_reenable(void *data)
+{
+	int err = 0;
+	struct sdei_registered_event *reg;
+	struct sdei_crosscall_args *arg = data;
+
+	WARN_ON_ONCE(preemptible());
+	WARN_ON(!spin_is_locked(&sdei_events_lock));
+
+	reg = per_cpu_ptr(arg->event->private_registered, smp_processor_id());
+	if (reg->was_enabled)
+		err = sdei_api_event_enable(arg->event->event_num);
+
+	sdei_cross_call_return(arg, err);
+}
+
 static int sdei_reregister_event(struct sdei_event *event)
 {
 	int err;
@@ -551,6 +699,8 @@ static int sdei_reregister_event(struct sdei_event *event)
 	if (event->type == SDEI_EVENT_TYPE_SHARED) {
 		if (event->registered->was_enabled)
 			err = sdei_api_event_enable(event->event_num);
+	} else {
+		err = sdei_do_cross_call(_local_event_reenable, event);
 	}
 
 	if (err)
@@ -575,6 +725,72 @@ static int sdei_reregister_events(void)
 	return err;
 }
 
+static int sdei_cpuhp_down(unsigned int cpu)
+{
+	int err = 0;
+	struct sdei_event *event;
+	struct sdei_crosscall_args arg;
+
+	if (frozen)
+		return 0;
+
+	/* un-register private events */
+	spin_lock(&sdei_events_lock);
+	list_for_each_entry(event, &sdei_events, list) {
+		if (event->type == SDEI_EVENT_TYPE_SHARED)
+			continue;
+
+		CROSSCALL_INIT(arg, event);
+		/* call the cross-call function locally... */
+		_local_event_unregister(&arg);
+		err = arg.first_error;
+		if (err)
+			break;
+	}
+	spin_unlock(&sdei_events_lock);
+
+	if (err)
+		return err;
+
+	return sdei_mask_local_cpu(cpu);
+}
+
+static int sdei_cpuhp_up(unsigned int cpu)
+{
+	int err = 0;
+	struct sdei_event *event;
+	struct sdei_crosscall_args arg;
+
+	if (frozen)
+		return 0;
+
+	/* re-register/enable private events */
+	spin_lock(&sdei_events_lock);
+	list_for_each_entry(event, &sdei_events, list) {
+		if (event->type == SDEI_EVENT_TYPE_SHARED)
+			continue;
+
+		CROSSCALL_INIT(arg, event);
+		/* call the cross-call function locally... */
+		_local_event_register(&arg);
+		err = arg.first_error;
+		if (err)
+			break;
+
+		CROSSCALL_INIT(arg, event);
+		_local_event_reenable(&arg);
+		err = arg.first_error;
+		if (err)
+			break;
+	}
+	spin_unlock(&sdei_events_lock);
+
+	if (err)
+		return err;
+
+	return sdei_unmask_local_cpu(cpu);
+}
+
 /* When entering idle, mask/unmask events for this cpu */
 static int sdei_pm_notifier(struct notifier_block *nb, unsigned long action,
 			    void *data)
@@ -627,6 +843,7 @@ static int sdei_device_freeze(struct device *dev)
 {
 	int err;
 
+	frozen = true;
 	err = sdei_event_unregister_all();
 	if (err)
 		return err;
@@ -636,9 +853,13 @@ static int sdei_device_freeze(struct device *dev)
 
 static int sdei_device_thaw(struct device *dev)
 {
+	int err;
+
 	sdei_device_resume(dev);
 
-	return sdei_reregister_events();
+	err = sdei_reregister_events();
+	frozen = false;
+	return err;
 }
 
 static int sdei_device_restore(struct device *dev)
@@ -670,6 +891,12 @@ static int sdei_reboot_notifier(struct notifier_block *nb, unsigned long action,
 
 	sdei_platform_reset();
 
+	/*
+	 * There is now no point trying to unregister private events if we go on
+	 * to take CPUs offline.
+	 */
+	frozen = true;
+
 	return NOTIFY_OK;
 }
 
@@ -760,7 +987,7 @@ static int sdei_probe(struct platform_device *pdev)
 	}
 
 	err = cpuhp_setup_state_nocalls(CPUHP_AP_SDEI_STARTING, "SDEI",
-				&sdei_unmask_local_cpu, &sdei_mask_local_cpu);
+				&sdei_cpuhp_up, &sdei_cpuhp_down);
 	if (err) {
 		pr_warn("Failed to register CPU hotplug notifier...\n");
 		return err;
-- 
2.10.1

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

* [PATCH 11/11] KVM: arm64: Delegate support for SDEI to userspace
  2017-05-15 17:43 ` James Morse
@ 2017-05-15 17:43     ` James Morse
  -1 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-05-15 17:43 UTC (permalink / raw)
  To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Will Deacon, Catalin Marinas,
	kvmarm-FPEHb7Xf0XXUo1n7N8X6UoWGPAHP3yOg, Marc Zyngier,
	Christoffer Dall, Rob Herring, Mark Rutland,
	devicetree-u79uwXL29TY76Z2rM5mHXA

The Software Delegated Exception Interface allows firmware to notify
the OS of system events by returning into registered handlers, even
if the OS has interrupts masked.

While we could support this in KVM, we would need to expose an API for
the user space hypervisor to inject events, (and decide what to do it
the event isn't registered or all the CPUs have SDE events masked). We
already have an API for guest 'hypercalls', so use this to push the
problem onto userspace.

Advertise a new capability 'KVM_CAP_ARM_SDEI_1_0' and when any SDEI
call comes in, exit to userspace with exit_reason = KVM_EXIT_HYPERCALL.

N.B. There is no enable/feature bit for SDEI exits as telling the guest
the interface exists via DT/ACPI should be sufficient.

Signed-off-by: James Morse <james.morse-5wv7dgnIgG8@public.gmane.org>

---
While I'm in here, why does KVM_CAP_ARM_SET_DEVICE_ADDR have a separate
entry for r=1;break?

 arch/arm64/kvm/handle_exit.c | 10 +++++++++-
 include/uapi/linux/kvm.h     |  1 +
 virt/kvm/arm/arm.c           |  5 +++--
 3 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/arch/arm64/kvm/handle_exit.c b/arch/arm64/kvm/handle_exit.c
index fa1b18e364fc..2bed62fbdc00 100644
--- a/arch/arm64/kvm/handle_exit.c
+++ b/arch/arm64/kvm/handle_exit.c
@@ -21,6 +21,7 @@
 
 #include <linux/kvm.h>
 #include <linux/kvm_host.h>
+#include <linux/sdei.h>
 
 #include <asm/esr.h>
 #include <asm/kvm_asm.h>
@@ -42,7 +43,14 @@ static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run)
 			    kvm_vcpu_hvc_get_imm(vcpu));
 	vcpu->stat.hvc_exit_stat++;
 
-	ret = kvm_psci_call(vcpu);
+	if (IS_SDEI_CALL(vcpu_get_reg(vcpu, 0))) {
+		/* SDEI is handled by userspace */
+		run->exit_reason = KVM_EXIT_HYPERCALL;
+		ret = 0;
+	} else {
+		ret = kvm_psci_call(vcpu);
+	}
+
 	if (ret < 0) {
 		kvm_inject_undefined(vcpu);
 		return 1;
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 577429a95ad8..e9ebfed9d624 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -895,6 +895,7 @@ struct kvm_ppc_resize_hpt {
 #define KVM_CAP_SPAPR_TCE_VFIO 142
 #define KVM_CAP_X86_GUEST_MWAIT 143
 #define KVM_CAP_ARM_USER_IRQ 144
+#define KVM_CAP_ARM_SDEI_1_0 145
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c
index 3a776ec99181..0bf2d923483c 100644
--- a/virt/kvm/arm/arm.c
+++ b/virt/kvm/arm/arm.c
@@ -206,8 +206,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
 	case KVM_CAP_READONLY_MEM:
 	case KVM_CAP_MP_STATE:
 	case KVM_CAP_IMMEDIATE_EXIT:
-		r = 1;
-		break;
+#ifdef CONFIG_ARM_SDE_INTERFACE
+	case KVM_CAP_ARM_SDEI_1_0:
+#endif
 	case KVM_CAP_ARM_SET_DEVICE_ADDR:
 		r = 1;
 		break;
-- 
2.10.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 11/11] KVM: arm64: Delegate support for SDEI to userspace
@ 2017-05-15 17:43     ` James Morse
  0 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-05-15 17:43 UTC (permalink / raw)
  To: linux-arm-kernel

The Software Delegated Exception Interface allows firmware to notify
the OS of system events by returning into registered handlers, even
if the OS has interrupts masked.

While we could support this in KVM, we would need to expose an API for
the user space hypervisor to inject events, (and decide what to do it
the event isn't registered or all the CPUs have SDE events masked). We
already have an API for guest 'hypercalls', so use this to push the
problem onto userspace.

Advertise a new capability 'KVM_CAP_ARM_SDEI_1_0' and when any SDEI
call comes in, exit to userspace with exit_reason = KVM_EXIT_HYPERCALL.

N.B. There is no enable/feature bit for SDEI exits as telling the guest
the interface exists via DT/ACPI should be sufficient.

Signed-off-by: James Morse <james.morse@arm.com>

---
While I'm in here, why does KVM_CAP_ARM_SET_DEVICE_ADDR have a separate
entry for r=1;break?

 arch/arm64/kvm/handle_exit.c | 10 +++++++++-
 include/uapi/linux/kvm.h     |  1 +
 virt/kvm/arm/arm.c           |  5 +++--
 3 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/arch/arm64/kvm/handle_exit.c b/arch/arm64/kvm/handle_exit.c
index fa1b18e364fc..2bed62fbdc00 100644
--- a/arch/arm64/kvm/handle_exit.c
+++ b/arch/arm64/kvm/handle_exit.c
@@ -21,6 +21,7 @@
 
 #include <linux/kvm.h>
 #include <linux/kvm_host.h>
+#include <linux/sdei.h>
 
 #include <asm/esr.h>
 #include <asm/kvm_asm.h>
@@ -42,7 +43,14 @@ static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run)
 			    kvm_vcpu_hvc_get_imm(vcpu));
 	vcpu->stat.hvc_exit_stat++;
 
-	ret = kvm_psci_call(vcpu);
+	if (IS_SDEI_CALL(vcpu_get_reg(vcpu, 0))) {
+		/* SDEI is handled by userspace */
+		run->exit_reason = KVM_EXIT_HYPERCALL;
+		ret = 0;
+	} else {
+		ret = kvm_psci_call(vcpu);
+	}
+
 	if (ret < 0) {
 		kvm_inject_undefined(vcpu);
 		return 1;
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 577429a95ad8..e9ebfed9d624 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -895,6 +895,7 @@ struct kvm_ppc_resize_hpt {
 #define KVM_CAP_SPAPR_TCE_VFIO 142
 #define KVM_CAP_X86_GUEST_MWAIT 143
 #define KVM_CAP_ARM_USER_IRQ 144
+#define KVM_CAP_ARM_SDEI_1_0 145
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c
index 3a776ec99181..0bf2d923483c 100644
--- a/virt/kvm/arm/arm.c
+++ b/virt/kvm/arm/arm.c
@@ -206,8 +206,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
 	case KVM_CAP_READONLY_MEM:
 	case KVM_CAP_MP_STATE:
 	case KVM_CAP_IMMEDIATE_EXIT:
-		r = 1;
-		break;
+#ifdef CONFIG_ARM_SDE_INTERFACE
+	case KVM_CAP_ARM_SDEI_1_0:
+#endif
 	case KVM_CAP_ARM_SET_DEVICE_ADDR:
 		r = 1;
 		break;
-- 
2.10.1

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

* Re: [PATCH 06/11] dt-bindings: add devicetree binding for describing arm64 SDEI firmware
  2017-05-15 17:43     ` James Morse
@ 2017-05-19  1:48       ` Rob Herring
  -1 siblings, 0 replies; 60+ messages in thread
From: Rob Herring @ 2017-05-19  1:48 UTC (permalink / raw)
  To: James Morse
  Cc: devicetree, Marc Zyngier, Catalin Marinas, Will Deacon,
	linux-arm-kernel, kvmarm

On Mon, May 15, 2017 at 06:43:54PM +0100, James Morse wrote:
> The Software Delegated Exception Interface (SDEI) is an ARM standard
> for registering callbacks from the platform firmware into the OS.
> This is typically used to implement RAS notifications.
> 
> Add a new devicetree binding to describe the SDE firmware interface.
> 
> Signed-off-by: James Morse <james.morse@arm.com>
> ---
>  Documentation/devicetree/bindings/arm/sdei.txt | 37 ++++++++++++++++++++++++++
>  1 file changed, 37 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/arm/sdei.txt
> 
> diff --git a/Documentation/devicetree/bindings/arm/sdei.txt b/Documentation/devicetree/bindings/arm/sdei.txt
> new file mode 100644
> index 000000000000..69220d995286
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/arm/sdei.txt
> @@ -0,0 +1,37 @@
> +* Software Delegated Exception Interface (SDEI)
> +
> +Firmware implementing the SDEI functions described in ARM document number
> +ARM DEN 0054A ("Software Delegated Exception Interface") can be used by
> +Linux to receive notification of events such as those generated by
> +firmware-first error handling.

Why am I reviewing bindings for RAS firmware-first events which is a 
server thing? Just curious.

> +
> +The interface provides a number of API functions for registering callbacks
> +and enabling/disabling events. Functions are invoked by trapping to the
> +privilege level of the SDEI firmware (specified as part of the binding
> +below) and passing arguments in a manner similar to that specified by AAPCS:

Shouldn't it be the ARM secure monitor call convention doc (forgot the 
name) that you are following?

> +
> +	 r0		=> 32-bit Function ID / return value
> +	{r1 - r3}	=> Parameters
> +
> +Note that the immediate field of the trapping instruction must be set
> +to #0.
> +
> +The SDEI_EVENT_REGISTER function registers a callback in the kernel
> +text to handle the specified event number.
> +
> +Main node required properties:
> +
> + - compatible    : should contain:
> +	* "arm,sdei-1.0" : For implementations complying to SDEI version 1.x.
> +
> + - method        : The method of calling the SDEI firmware. Permitted
> +                   values are:
> +	* "smc" : SMC #0, with the register assignments specified in this
> +	          binding.
> +	* "hvc" : HVC #0, with the register assignments specified in this
> +	          binding.

When is ARM going to define a firmware interface to determine what 
firmware interfaces are available?

> +Example:
> +	sdei {

Please specify this should be a child node of /firmware node.

> +		compatible	= "arm,sdei-1.0";
> +		method		= "smc";
> +	};
> -- 
> 2.10.1
> 

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

* [PATCH 06/11] dt-bindings: add devicetree binding for describing arm64 SDEI firmware
@ 2017-05-19  1:48       ` Rob Herring
  0 siblings, 0 replies; 60+ messages in thread
From: Rob Herring @ 2017-05-19  1:48 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, May 15, 2017 at 06:43:54PM +0100, James Morse wrote:
> The Software Delegated Exception Interface (SDEI) is an ARM standard
> for registering callbacks from the platform firmware into the OS.
> This is typically used to implement RAS notifications.
> 
> Add a new devicetree binding to describe the SDE firmware interface.
> 
> Signed-off-by: James Morse <james.morse@arm.com>
> ---
>  Documentation/devicetree/bindings/arm/sdei.txt | 37 ++++++++++++++++++++++++++
>  1 file changed, 37 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/arm/sdei.txt
> 
> diff --git a/Documentation/devicetree/bindings/arm/sdei.txt b/Documentation/devicetree/bindings/arm/sdei.txt
> new file mode 100644
> index 000000000000..69220d995286
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/arm/sdei.txt
> @@ -0,0 +1,37 @@
> +* Software Delegated Exception Interface (SDEI)
> +
> +Firmware implementing the SDEI functions described in ARM document number
> +ARM DEN 0054A ("Software Delegated Exception Interface") can be used by
> +Linux to receive notification of events such as those generated by
> +firmware-first error handling.

Why am I reviewing bindings for RAS firmware-first events which is a 
server thing? Just curious.

> +
> +The interface provides a number of API functions for registering callbacks
> +and enabling/disabling events. Functions are invoked by trapping to the
> +privilege level of the SDEI firmware (specified as part of the binding
> +below) and passing arguments in a manner similar to that specified by AAPCS:

Shouldn't it be the ARM secure monitor call convention doc (forgot the 
name) that you are following?

> +
> +	 r0		=> 32-bit Function ID / return value
> +	{r1 - r3}	=> Parameters
> +
> +Note that the immediate field of the trapping instruction must be set
> +to #0.
> +
> +The SDEI_EVENT_REGISTER function registers a callback in the kernel
> +text to handle the specified event number.
> +
> +Main node required properties:
> +
> + - compatible    : should contain:
> +	* "arm,sdei-1.0" : For implementations complying to SDEI version 1.x.
> +
> + - method        : The method of calling the SDEI firmware. Permitted
> +                   values are:
> +	* "smc" : SMC #0, with the register assignments specified in this
> +	          binding.
> +	* "hvc" : HVC #0, with the register assignments specified in this
> +	          binding.

When is ARM going to define a firmware interface to determine what 
firmware interfaces are available?

> +Example:
> +	sdei {

Please specify this should be a child node of /firmware node.

> +		compatible	= "arm,sdei-1.0";
> +		method		= "smc";
> +	};
> -- 
> 2.10.1
> 

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

* Re: [PATCH 03/11] KVM: arm64: Change hyp_panic()s dependency on tpidr_el2
  2017-05-15 17:43     ` James Morse
@ 2017-06-06 19:45       ` Christoffer Dall
  -1 siblings, 0 replies; 60+ messages in thread
From: Christoffer Dall @ 2017-06-06 19:45 UTC (permalink / raw)
  To: James Morse
  Cc: devicetree, Marc Zyngier, Catalin Marinas, Will Deacon,
	Rob Herring, linux-arm-kernel, kvmarm

On Mon, May 15, 2017 at 06:43:51PM +0100, James Morse wrote:
> KVM calls hyp_panic() when anything unexpected happens. This may occur
> while a guest owns the EL1 registers. KVM stashes the vcpu pointer in
> tpidr_el2, which it uses to find the host context in order to restore
> the host EL1 registers before parachuting into the host's panic().
> 
> The host context is a struct kvm_cpu_context allocated in the per-cpu
> area, and mapped to hyp. Given the per-cpu offset for this CPU, this is
> easy to find. Change hyp_panic() to take a pointer to the
> struct kvm_cpu_context. Wrap these calls with an asm function that
> retrieves the struct kvm_cpu_context from the host's per-cpu area.
> 
> Copy the per-cpu offset from the hosts tpidr_el1 into tpidr_el2 during
> kvm init. (Later patches will make this unnecessary for VHE hosts)
> 
> We print out the vcpu pointer as part of the panic message. Add a back
> reference to the 'running vcpu' in the host cpu context to preserve this.
> 
> Signed-off-by: James Morse <james.morse@arm.com>
> ---
>  arch/arm64/include/asm/kvm_host.h |  2 ++
>  arch/arm64/kvm/hyp/hyp-entry.S    |  7 +++++++
>  arch/arm64/kvm/hyp/s2-setup.c     |  3 +++
>  arch/arm64/kvm/hyp/switch.c       | 25 +++++++++++++------------
>  4 files changed, 25 insertions(+), 12 deletions(-)
> 
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index 5e19165c5fa8..1407bfc81e9e 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -188,6 +188,8 @@ struct kvm_cpu_context {
>  		u64 sys_regs[NR_SYS_REGS];
>  		u32 copro[NR_COPRO_REGS];
>  	};
> +
> +	struct kvm_vcpu *__hyp_running_vcpu;
>  };
>  
>  typedef struct kvm_cpu_context kvm_cpu_context_t;
> diff --git a/arch/arm64/kvm/hyp/hyp-entry.S b/arch/arm64/kvm/hyp/hyp-entry.S
> index fce7cc507e0a..0f83e7e402ad 100644
> --- a/arch/arm64/kvm/hyp/hyp-entry.S
> +++ b/arch/arm64/kvm/hyp/hyp-entry.S
> @@ -163,6 +163,13 @@ ENTRY(__hyp_do_panic)
>  	eret
>  ENDPROC(__hyp_do_panic)
>  
> +ENTRY(__hyp_panic)
> +	ldr	x0, =kvm_host_cpu_state

Why is this guaranteed to give us a valid EL2 address?

> +	mrs	x1, tpidr_el2
> +	add	x0, x0, x1
> +	b	hyp_panic
> +ENDPROC(__hyp_panic)
> +

Thanks,
-Christoffer

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

* [PATCH 03/11] KVM: arm64: Change hyp_panic()s dependency on tpidr_el2
@ 2017-06-06 19:45       ` Christoffer Dall
  0 siblings, 0 replies; 60+ messages in thread
From: Christoffer Dall @ 2017-06-06 19:45 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, May 15, 2017 at 06:43:51PM +0100, James Morse wrote:
> KVM calls hyp_panic() when anything unexpected happens. This may occur
> while a guest owns the EL1 registers. KVM stashes the vcpu pointer in
> tpidr_el2, which it uses to find the host context in order to restore
> the host EL1 registers before parachuting into the host's panic().
> 
> The host context is a struct kvm_cpu_context allocated in the per-cpu
> area, and mapped to hyp. Given the per-cpu offset for this CPU, this is
> easy to find. Change hyp_panic() to take a pointer to the
> struct kvm_cpu_context. Wrap these calls with an asm function that
> retrieves the struct kvm_cpu_context from the host's per-cpu area.
> 
> Copy the per-cpu offset from the hosts tpidr_el1 into tpidr_el2 during
> kvm init. (Later patches will make this unnecessary for VHE hosts)
> 
> We print out the vcpu pointer as part of the panic message. Add a back
> reference to the 'running vcpu' in the host cpu context to preserve this.
> 
> Signed-off-by: James Morse <james.morse@arm.com>
> ---
>  arch/arm64/include/asm/kvm_host.h |  2 ++
>  arch/arm64/kvm/hyp/hyp-entry.S    |  7 +++++++
>  arch/arm64/kvm/hyp/s2-setup.c     |  3 +++
>  arch/arm64/kvm/hyp/switch.c       | 25 +++++++++++++------------
>  4 files changed, 25 insertions(+), 12 deletions(-)
> 
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index 5e19165c5fa8..1407bfc81e9e 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -188,6 +188,8 @@ struct kvm_cpu_context {
>  		u64 sys_regs[NR_SYS_REGS];
>  		u32 copro[NR_COPRO_REGS];
>  	};
> +
> +	struct kvm_vcpu *__hyp_running_vcpu;
>  };
>  
>  typedef struct kvm_cpu_context kvm_cpu_context_t;
> diff --git a/arch/arm64/kvm/hyp/hyp-entry.S b/arch/arm64/kvm/hyp/hyp-entry.S
> index fce7cc507e0a..0f83e7e402ad 100644
> --- a/arch/arm64/kvm/hyp/hyp-entry.S
> +++ b/arch/arm64/kvm/hyp/hyp-entry.S
> @@ -163,6 +163,13 @@ ENTRY(__hyp_do_panic)
>  	eret
>  ENDPROC(__hyp_do_panic)
>  
> +ENTRY(__hyp_panic)
> +	ldr	x0, =kvm_host_cpu_state

Why is this guaranteed to give us a valid EL2 address?

> +	mrs	x1, tpidr_el2
> +	add	x0, x0, x1
> +	b	hyp_panic
> +ENDPROC(__hyp_panic)
> +

Thanks,
-Christoffer

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

* Re: [PATCH 11/11] KVM: arm64: Delegate support for SDEI to userspace
  2017-05-15 17:43     ` James Morse
@ 2017-06-06 19:58         ` Christoffer Dall
  -1 siblings, 0 replies; 60+ messages in thread
From: Christoffer Dall @ 2017-06-06 19:58 UTC (permalink / raw)
  To: James Morse
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Will Deacon,
	Catalin Marinas, kvmarm-FPEHb7Xf0XXUo1n7N8X6UoWGPAHP3yOg,
	Marc Zyngier, Christoffer Dall, Rob Herring, Mark Rutland,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Hi James,

On Mon, May 15, 2017 at 06:43:59PM +0100, James Morse wrote:
> The Software Delegated Exception Interface allows firmware to notify
> the OS of system events by returning into registered handlers, even
> if the OS has interrupts masked.
> 
> While we could support this in KVM, we would need to expose an API for
> the user space hypervisor to inject events, (and decide what to do it

'the user space hypervisor' ?

s/it/if/

> the event isn't registered or all the CPUs have SDE events masked). We
> already have an API for guest 'hypercalls', so use this to push the
> problem onto userspace.
> 
> Advertise a new capability 'KVM_CAP_ARM_SDEI_1_0' and when any SDEI
> call comes in, exit to userspace with exit_reason = KVM_EXIT_HYPERCALL.

Documentation/virtual/kvm/api.txt says this is unused.

We should add something there to say that this is now used for arm64,
and the api doc also suggests that the hypercall struct in kvm_run has
some meaningful data for this exit.

Have we checked that the guest can't provoke QEMU to do something weird
by causing this exit on arm64 currently (given that we always enable
this handling of SDEI calls)?

> 
> N.B. There is no enable/feature bit for SDEI exits as telling the guest
> the interface exists via DT/ACPI should be sufficient.
> 
> Signed-off-by: James Morse <james.morse-5wv7dgnIgG8@public.gmane.org>
> 
> ---
> While I'm in here, why does KVM_CAP_ARM_SET_DEVICE_ADDR have a separate
> entry for r=1;break?
> 
>  arch/arm64/kvm/handle_exit.c | 10 +++++++++-
>  include/uapi/linux/kvm.h     |  1 +
>  virt/kvm/arm/arm.c           |  5 +++--
>  3 files changed, 13 insertions(+), 3 deletions(-)
> 
> diff --git a/arch/arm64/kvm/handle_exit.c b/arch/arm64/kvm/handle_exit.c
> index fa1b18e364fc..2bed62fbdc00 100644
> --- a/arch/arm64/kvm/handle_exit.c
> +++ b/arch/arm64/kvm/handle_exit.c
> @@ -21,6 +21,7 @@
>  
>  #include <linux/kvm.h>
>  #include <linux/kvm_host.h>
> +#include <linux/sdei.h>
>  
>  #include <asm/esr.h>
>  #include <asm/kvm_asm.h>
> @@ -42,7 +43,14 @@ static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run)
>  			    kvm_vcpu_hvc_get_imm(vcpu));
>  	vcpu->stat.hvc_exit_stat++;
>  
> -	ret = kvm_psci_call(vcpu);
> +	if (IS_SDEI_CALL(vcpu_get_reg(vcpu, 0))) {
> +		/* SDEI is handled by userspace */
> +		run->exit_reason = KVM_EXIT_HYPERCALL;
> +		ret = 0;
> +	} else {
> +		ret = kvm_psci_call(vcpu);
> +	}
> +
>  	if (ret < 0) {
>  		kvm_inject_undefined(vcpu);
>  		return 1;
> diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
> index 577429a95ad8..e9ebfed9d624 100644
> --- a/include/uapi/linux/kvm.h
> +++ b/include/uapi/linux/kvm.h
> @@ -895,6 +895,7 @@ struct kvm_ppc_resize_hpt {
>  #define KVM_CAP_SPAPR_TCE_VFIO 142
>  #define KVM_CAP_X86_GUEST_MWAIT 143
>  #define KVM_CAP_ARM_USER_IRQ 144
> +#define KVM_CAP_ARM_SDEI_1_0 145
>  
>  #ifdef KVM_CAP_IRQ_ROUTING
>  
> diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c
> index 3a776ec99181..0bf2d923483c 100644
> --- a/virt/kvm/arm/arm.c
> +++ b/virt/kvm/arm/arm.c
> @@ -206,8 +206,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
>  	case KVM_CAP_READONLY_MEM:
>  	case KVM_CAP_MP_STATE:
>  	case KVM_CAP_IMMEDIATE_EXIT:
> -		r = 1;
> -		break;
> +#ifdef CONFIG_ARM_SDE_INTERFACE
> +	case KVM_CAP_ARM_SDEI_1_0:
> +#endif

What's the point of conditionally supporting this based on the config
option when the rest of the KVM functionality does not depend on the
CONFIG_ARM_SDE_INTERFACE functionality?

Could a user want to play with SDEI calls in a VM without the host
having the proper support, or is that never relevant?


>  	case KVM_CAP_ARM_SET_DEVICE_ADDR:
>  		r = 1;
>  		break;
> -- 
> 2.10.1
> 

Thanks,
-Christoffer

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 11/11] KVM: arm64: Delegate support for SDEI to userspace
@ 2017-06-06 19:58         ` Christoffer Dall
  0 siblings, 0 replies; 60+ messages in thread
From: Christoffer Dall @ 2017-06-06 19:58 UTC (permalink / raw)
  To: linux-arm-kernel

Hi James,

On Mon, May 15, 2017 at 06:43:59PM +0100, James Morse wrote:
> The Software Delegated Exception Interface allows firmware to notify
> the OS of system events by returning into registered handlers, even
> if the OS has interrupts masked.
> 
> While we could support this in KVM, we would need to expose an API for
> the user space hypervisor to inject events, (and decide what to do it

'the user space hypervisor' ?

s/it/if/

> the event isn't registered or all the CPUs have SDE events masked). We
> already have an API for guest 'hypercalls', so use this to push the
> problem onto userspace.
> 
> Advertise a new capability 'KVM_CAP_ARM_SDEI_1_0' and when any SDEI
> call comes in, exit to userspace with exit_reason = KVM_EXIT_HYPERCALL.

Documentation/virtual/kvm/api.txt says this is unused.

We should add something there to say that this is now used for arm64,
and the api doc also suggests that the hypercall struct in kvm_run has
some meaningful data for this exit.

Have we checked that the guest can't provoke QEMU to do something weird
by causing this exit on arm64 currently (given that we always enable
this handling of SDEI calls)?

> 
> N.B. There is no enable/feature bit for SDEI exits as telling the guest
> the interface exists via DT/ACPI should be sufficient.
> 
> Signed-off-by: James Morse <james.morse@arm.com>
> 
> ---
> While I'm in here, why does KVM_CAP_ARM_SET_DEVICE_ADDR have a separate
> entry for r=1;break?
> 
>  arch/arm64/kvm/handle_exit.c | 10 +++++++++-
>  include/uapi/linux/kvm.h     |  1 +
>  virt/kvm/arm/arm.c           |  5 +++--
>  3 files changed, 13 insertions(+), 3 deletions(-)
> 
> diff --git a/arch/arm64/kvm/handle_exit.c b/arch/arm64/kvm/handle_exit.c
> index fa1b18e364fc..2bed62fbdc00 100644
> --- a/arch/arm64/kvm/handle_exit.c
> +++ b/arch/arm64/kvm/handle_exit.c
> @@ -21,6 +21,7 @@
>  
>  #include <linux/kvm.h>
>  #include <linux/kvm_host.h>
> +#include <linux/sdei.h>
>  
>  #include <asm/esr.h>
>  #include <asm/kvm_asm.h>
> @@ -42,7 +43,14 @@ static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run)
>  			    kvm_vcpu_hvc_get_imm(vcpu));
>  	vcpu->stat.hvc_exit_stat++;
>  
> -	ret = kvm_psci_call(vcpu);
> +	if (IS_SDEI_CALL(vcpu_get_reg(vcpu, 0))) {
> +		/* SDEI is handled by userspace */
> +		run->exit_reason = KVM_EXIT_HYPERCALL;
> +		ret = 0;
> +	} else {
> +		ret = kvm_psci_call(vcpu);
> +	}
> +
>  	if (ret < 0) {
>  		kvm_inject_undefined(vcpu);
>  		return 1;
> diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
> index 577429a95ad8..e9ebfed9d624 100644
> --- a/include/uapi/linux/kvm.h
> +++ b/include/uapi/linux/kvm.h
> @@ -895,6 +895,7 @@ struct kvm_ppc_resize_hpt {
>  #define KVM_CAP_SPAPR_TCE_VFIO 142
>  #define KVM_CAP_X86_GUEST_MWAIT 143
>  #define KVM_CAP_ARM_USER_IRQ 144
> +#define KVM_CAP_ARM_SDEI_1_0 145
>  
>  #ifdef KVM_CAP_IRQ_ROUTING
>  
> diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c
> index 3a776ec99181..0bf2d923483c 100644
> --- a/virt/kvm/arm/arm.c
> +++ b/virt/kvm/arm/arm.c
> @@ -206,8 +206,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
>  	case KVM_CAP_READONLY_MEM:
>  	case KVM_CAP_MP_STATE:
>  	case KVM_CAP_IMMEDIATE_EXIT:
> -		r = 1;
> -		break;
> +#ifdef CONFIG_ARM_SDE_INTERFACE
> +	case KVM_CAP_ARM_SDEI_1_0:
> +#endif

What's the point of conditionally supporting this based on the config
option when the rest of the KVM functionality does not depend on the
CONFIG_ARM_SDE_INTERFACE functionality?

Could a user want to play with SDEI calls in a VM without the host
having the proper support, or is that never relevant?


>  	case KVM_CAP_ARM_SET_DEVICE_ADDR:
>  		r = 1;
>  		break;
> -- 
> 2.10.1
> 

Thanks,
-Christoffer

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

* Re: [PATCH 00/11] arm64/firmware: Software Delegated Exception Interface
  2017-05-15 17:43 ` James Morse
@ 2017-06-06 19:59     ` Christoffer Dall
  -1 siblings, 0 replies; 60+ messages in thread
From: Christoffer Dall @ 2017-06-06 19:59 UTC (permalink / raw)
  To: James Morse
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Will Deacon,
	Catalin Marinas, kvmarm-FPEHb7Xf0XXUo1n7N8X6UoWGPAHP3yOg,
	Marc Zyngier, Christoffer Dall, Rob Herring, Mark Rutland,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Hi James,

On Mon, May 15, 2017 at 06:43:48PM +0100, James Morse wrote:
> Hello!
> 
> The Software Delegated Exception Interface (SDEI) is an ARM specification
> for registering callbacks from the platform firmware into the OS.
> This is intended to be used to implement firmware-first RAS notifications.
> 
> The document is here:
> http://infocenter.arm.com/help/topic/com.arm.doc.den0054a/ARM_DEN0054A_Software_Delegated_Exception_Interface.pdf
> 
> This series (juggles some registers with KVM+VHE, then) adds a DT binding to
> trigger probing of the interface and support for the SDEI API.
> A future version of the ACPI spec should add the necessary parts to enable this
> to be used as a GHES notification.
> 
> 
> SDEI runs between adjacent exception levels, so events will always be delivered
> to EL2 if firmware is at EL3. For VHE hosts we run the SDEI event handler
> behind KVM's back with all exceptions masked. Once the handler has done its
> work we return to the appropriate vbar+irq entry. This allows KVM to
> world-switch and deliver any signals sent by the handler to Qemu/kvmtool. We
> do the same thing if we interrupt host EL0. If we interrupted code with
> interrupts masked, we use a different API call to return to the interrupted
> context.
> 
> What about non-VHE KVM? If you don't have VHE support and boot at EL2, the
> kernel drops to EL1. This driver will print an error message then give up. This
> is because events would still be delivered to EL2 hitting either KVM, or the
> hyp-stub. Supporting this is complicated, but because the main use-case is
> RAS, and ARM v8.2's RAS extensions imply v8.1's Virtual Host Extensions, we
> can assume all platforms with SDEI will support VHE too. (I have some ideas
> on how to support non-VHE if it turns out to be needed).
> 
> 
> Running the event handler behind VHE-KVM's back has some side effects: The
> event handler will blindly use any registers that are shared between the host
> and guest. The two that I think matter are TPIDR_EL1, and the debug state. The
> guest may have set MDSCR_EL1 so debug exceptions must remain masked. The
> guest's TPIDR_EL1 will be used by the event handler if it accesses per-cpu
> variables. This needs fixing. The first part of this series juggles KVMs use
> of TPIDR_EL2 so that we share it with the host on VHE systems. An equivalent
> change for 32bit is on my todo list. (one alternative to this is to have a
> parody world switch in the SDEI event handler, but this would mean special
> casing interrupted guests, and be an ABI link to KVM.)
> 
> Causing a synchronous exception from an event handler will cause KVM to
> hyp-panic, but may silently succeed if the event didn't interrupt a guest.
> (I may WARN_ON() if this happens in a later patch). You because of this you

The last sentence here doesn't make much sense to me.

> should not kprobe anything that handles SDE events. Once we've called
> nmi_enter(), ftrace and friends should be safe.
> 
> Is this another begins-with-S RAS mechanism for arm64? Yes.
> Why? Any notification delivered as an exception will overwrite the exception
> registers. This is fatal for the running thread if it happens during entry.S's
> kernel_enter or kernel_exit. Instead of adding masking and routing controls,
> events are delivered to a registered address at a fixed exception level and
> don't change the exception registers when delivered.
> 
> This series can be retrieved from:
> git://linux-arm.org/linux-jm.git -b sdei/v1/base
> 
> Questions and contradictions welcome!
> 

For the rest of the KVM part it looks mostly good to me, besides the
points I raised in the individual patches.

Thanks,
-Christoffer

> 
> James Morse (11):
>   KVM: arm64: Store vcpu on the stack during __guest_enter()
>   KVM: arm/arm64: Convert kvm_host_cpu_state to a static per-cpu
>     allocation
>   KVM: arm64: Change hyp_panic()s dependency on tpidr_el2
>   arm64: alternatives: use tpidr_el2 on VHE hosts
>   arm64: KVM: Stop save/restoring host tpidr_el1 on VHE
>   dt-bindings: add devicetree binding for describing arm64 SDEI firmware
>   firmware: arm_sdei: Add driver for Software Delegated Exceptions
>   arm64: kernel: Add arch-specific SDEI entry code and CPU masking
>   firmware: arm_sdei: Add support for CPU and system power states
>   firmware: arm_sdei: add support for CPU private events
>   KVM: arm64: Delegate support for SDEI to userspace
> 
>  Documentation/devicetree/bindings/arm/sdei.txt |   37 +
>  arch/arm64/Kconfig                             |    1 +
>  arch/arm64/include/asm/assembler.h             |    8 +
>  arch/arm64/include/asm/kvm_host.h              |    2 +
>  arch/arm64/include/asm/percpu.h                |   11 +-
>  arch/arm64/include/asm/processor.h             |    1 +
>  arch/arm64/include/asm/sdei.h                  |   45 +
>  arch/arm64/kernel/Makefile                     |    1 +
>  arch/arm64/kernel/asm-offsets.c                |    2 +
>  arch/arm64/kernel/cpufeature.c                 |   22 +
>  arch/arm64/kernel/entry.S                      |   68 ++
>  arch/arm64/kernel/sdei.c                       |  106 +++
>  arch/arm64/kernel/smp.c                        |    7 +
>  arch/arm64/kvm/handle_exit.c                   |   10 +-
>  arch/arm64/kvm/hyp-init.S                      |    4 +
>  arch/arm64/kvm/hyp/entry.S                     |   12 +-
>  arch/arm64/kvm/hyp/hyp-entry.S                 |   13 +-
>  arch/arm64/kvm/hyp/switch.c                    |   25 +-
>  arch/arm64/kvm/hyp/sysreg-sr.c                 |   16 +-
>  arch/arm64/mm/proc.S                           |    8 +
>  drivers/firmware/Kconfig                       |    8 +
>  drivers/firmware/Makefile                      |    1 +
>  drivers/firmware/arm_sdei.c                    | 1055 ++++++++++++++++++++++++
>  include/linux/cpuhotplug.h                     |    1 +
>  include/linux/sdei.h                           |  102 +++
>  include/uapi/linux/kvm.h                       |    1 +
>  include/uapi/linux/sdei.h                      |   91 ++
>  virt/kvm/arm/arm.c                             |   23 +-
>  28 files changed, 1634 insertions(+), 47 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/arm/sdei.txt
>  create mode 100644 arch/arm64/include/asm/sdei.h
>  create mode 100644 arch/arm64/kernel/sdei.c
>  create mode 100644 drivers/firmware/arm_sdei.c
>  create mode 100644 include/linux/sdei.h
>  create mode 100644 include/uapi/linux/sdei.h
> 
> -- 
> 2.10.1
> 
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 00/11] arm64/firmware: Software Delegated Exception Interface
@ 2017-06-06 19:59     ` Christoffer Dall
  0 siblings, 0 replies; 60+ messages in thread
From: Christoffer Dall @ 2017-06-06 19:59 UTC (permalink / raw)
  To: linux-arm-kernel

Hi James,

On Mon, May 15, 2017 at 06:43:48PM +0100, James Morse wrote:
> Hello!
> 
> The Software Delegated Exception Interface (SDEI) is an ARM specification
> for registering callbacks from the platform firmware into the OS.
> This is intended to be used to implement firmware-first RAS notifications.
> 
> The document is here:
> http://infocenter.arm.com/help/topic/com.arm.doc.den0054a/ARM_DEN0054A_Software_Delegated_Exception_Interface.pdf
> 
> This series (juggles some registers with KVM+VHE, then) adds a DT binding to
> trigger probing of the interface and support for the SDEI API.
> A future version of the ACPI spec should add the necessary parts to enable this
> to be used as a GHES notification.
> 
> 
> SDEI runs between adjacent exception levels, so events will always be delivered
> to EL2 if firmware is at EL3. For VHE hosts we run the SDEI event handler
> behind KVM's back with all exceptions masked. Once the handler has done its
> work we return to the appropriate vbar+irq entry. This allows KVM to
> world-switch and deliver any signals sent by the handler to Qemu/kvmtool. We
> do the same thing if we interrupt host EL0. If we interrupted code with
> interrupts masked, we use a different API call to return to the interrupted
> context.
> 
> What about non-VHE KVM? If you don't have VHE support and boot at EL2, the
> kernel drops to EL1. This driver will print an error message then give up. This
> is because events would still be delivered to EL2 hitting either KVM, or the
> hyp-stub. Supporting this is complicated, but because the main use-case is
> RAS, and ARM v8.2's RAS extensions imply v8.1's Virtual Host Extensions, we
> can assume all platforms with SDEI will support VHE too. (I have some ideas
> on how to support non-VHE if it turns out to be needed).
> 
> 
> Running the event handler behind VHE-KVM's back has some side effects: The
> event handler will blindly use any registers that are shared between the host
> and guest. The two that I think matter are TPIDR_EL1, and the debug state. The
> guest may have set MDSCR_EL1 so debug exceptions must remain masked. The
> guest's TPIDR_EL1 will be used by the event handler if it accesses per-cpu
> variables. This needs fixing. The first part of this series juggles KVMs use
> of TPIDR_EL2 so that we share it with the host on VHE systems. An equivalent
> change for 32bit is on my todo list. (one alternative to this is to have a
> parody world switch in the SDEI event handler, but this would mean special
> casing interrupted guests, and be an ABI link to KVM.)
> 
> Causing a synchronous exception from an event handler will cause KVM to
> hyp-panic, but may silently succeed if the event didn't interrupt a guest.
> (I may WARN_ON() if this happens in a later patch). You because of this you

The last sentence here doesn't make much sense to me.

> should not kprobe anything that handles SDE events. Once we've called
> nmi_enter(), ftrace and friends should be safe.
> 
> Is this another begins-with-S RAS mechanism for arm64? Yes.
> Why? Any notification delivered as an exception will overwrite the exception
> registers. This is fatal for the running thread if it happens during entry.S's
> kernel_enter or kernel_exit. Instead of adding masking and routing controls,
> events are delivered to a registered address at a fixed exception level and
> don't change the exception registers when delivered.
> 
> This series can be retrieved from:
> git://linux-arm.org/linux-jm.git -b sdei/v1/base
> 
> Questions and contradictions welcome!
> 

For the rest of the KVM part it looks mostly good to me, besides the
points I raised in the individual patches.

Thanks,
-Christoffer

> 
> James Morse (11):
>   KVM: arm64: Store vcpu on the stack during __guest_enter()
>   KVM: arm/arm64: Convert kvm_host_cpu_state to a static per-cpu
>     allocation
>   KVM: arm64: Change hyp_panic()s dependency on tpidr_el2
>   arm64: alternatives: use tpidr_el2 on VHE hosts
>   arm64: KVM: Stop save/restoring host tpidr_el1 on VHE
>   dt-bindings: add devicetree binding for describing arm64 SDEI firmware
>   firmware: arm_sdei: Add driver for Software Delegated Exceptions
>   arm64: kernel: Add arch-specific SDEI entry code and CPU masking
>   firmware: arm_sdei: Add support for CPU and system power states
>   firmware: arm_sdei: add support for CPU private events
>   KVM: arm64: Delegate support for SDEI to userspace
> 
>  Documentation/devicetree/bindings/arm/sdei.txt |   37 +
>  arch/arm64/Kconfig                             |    1 +
>  arch/arm64/include/asm/assembler.h             |    8 +
>  arch/arm64/include/asm/kvm_host.h              |    2 +
>  arch/arm64/include/asm/percpu.h                |   11 +-
>  arch/arm64/include/asm/processor.h             |    1 +
>  arch/arm64/include/asm/sdei.h                  |   45 +
>  arch/arm64/kernel/Makefile                     |    1 +
>  arch/arm64/kernel/asm-offsets.c                |    2 +
>  arch/arm64/kernel/cpufeature.c                 |   22 +
>  arch/arm64/kernel/entry.S                      |   68 ++
>  arch/arm64/kernel/sdei.c                       |  106 +++
>  arch/arm64/kernel/smp.c                        |    7 +
>  arch/arm64/kvm/handle_exit.c                   |   10 +-
>  arch/arm64/kvm/hyp-init.S                      |    4 +
>  arch/arm64/kvm/hyp/entry.S                     |   12 +-
>  arch/arm64/kvm/hyp/hyp-entry.S                 |   13 +-
>  arch/arm64/kvm/hyp/switch.c                    |   25 +-
>  arch/arm64/kvm/hyp/sysreg-sr.c                 |   16 +-
>  arch/arm64/mm/proc.S                           |    8 +
>  drivers/firmware/Kconfig                       |    8 +
>  drivers/firmware/Makefile                      |    1 +
>  drivers/firmware/arm_sdei.c                    | 1055 ++++++++++++++++++++++++
>  include/linux/cpuhotplug.h                     |    1 +
>  include/linux/sdei.h                           |  102 +++
>  include/uapi/linux/kvm.h                       |    1 +
>  include/uapi/linux/sdei.h                      |   91 ++
>  virt/kvm/arm/arm.c                             |   23 +-
>  28 files changed, 1634 insertions(+), 47 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/arm/sdei.txt
>  create mode 100644 arch/arm64/include/asm/sdei.h
>  create mode 100644 arch/arm64/kernel/sdei.c
>  create mode 100644 drivers/firmware/arm_sdei.c
>  create mode 100644 include/linux/sdei.h
>  create mode 100644 include/uapi/linux/sdei.h
> 
> -- 
> 2.10.1
> 

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

* Re: [PATCH 01/11] KVM: arm64: Store vcpu on the stack during __guest_enter()
  2017-05-15 17:43   ` James Morse
@ 2017-06-06 19:59       ` Christoffer Dall
  -1 siblings, 0 replies; 60+ messages in thread
From: Christoffer Dall @ 2017-06-06 19:59 UTC (permalink / raw)
  To: James Morse
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Will Deacon,
	Catalin Marinas, kvmarm-FPEHb7Xf0XXUo1n7N8X6UoWGPAHP3yOg,
	Marc Zyngier, Christoffer Dall, Rob Herring, Mark Rutland,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On Mon, May 15, 2017 at 06:43:49PM +0100, James Morse wrote:
> KVM uses tpidr_el2 as its private vcpu register, which makes sense for
> non-vhe world switch as only KVM can access this register. This means
> vhe Linux has to use tpidr_el1, which KVM has to save/restore as part
> of the host context.
> 
> __guest_enter() stores the host_ctxt on the stack, do the same with
> the vcpu.
> 
> Signed-off-by: James Morse <james.morse-5wv7dgnIgG8@public.gmane.org>
> ---
>  arch/arm64/kvm/hyp/entry.S     | 12 ++++++++----
>  arch/arm64/kvm/hyp/hyp-entry.S |  6 +++---
>  2 files changed, 11 insertions(+), 7 deletions(-)
> 
> diff --git a/arch/arm64/kvm/hyp/entry.S b/arch/arm64/kvm/hyp/entry.S
> index 12ee62d6d410..113735df7d01 100644
> --- a/arch/arm64/kvm/hyp/entry.S
> +++ b/arch/arm64/kvm/hyp/entry.S
> @@ -62,8 +62,8 @@ ENTRY(__guest_enter)
>  	// Store the host regs
>  	save_callee_saved_regs x1
>  
> -	// Store the host_ctxt for use at exit time
> -	str	x1, [sp, #-16]!
> +	// Store host_ctxt and vcpu for use at exit time
> +	stp	x1, x0, [sp, #-16]!
>  
>  	add	x18, x0, #VCPU_CONTEXT
>  
> @@ -159,9 +159,15 @@ abort_guest_exit_end:
>  ENDPROC(__guest_exit)
>  
>  ENTRY(__fpsimd_guest_restore)
> +	// x0: esr
> +	// x1: vcpu
> +	// x2-x29,lr: vcpu regs
> +	// vcpu x0-x1 on the stack
>  	stp	x2, x3, [sp, #-16]!
>  	stp	x4, lr, [sp, #-16]!
>  
> +	mov	x3, x1
> +

nit: can you avoid this by using x1 for the vcpu pointer in this routine
instead?

>  alternative_if_not ARM64_HAS_VIRT_HOST_EXTN
>  	mrs	x2, cptr_el2
>  	bic	x2, x2, #CPTR_EL2_TFP
> @@ -173,8 +179,6 @@ alternative_else
>  alternative_endif
>  	isb
>  
> -	mrs	x3, tpidr_el2
> -
>  	ldr	x0, [x3, #VCPU_HOST_CONTEXT]
>  	kern_hyp_va x0
>  	add	x0, x0, #CPU_GP_REG_OFFSET(CPU_FP_REGS)
> diff --git a/arch/arm64/kvm/hyp/hyp-entry.S b/arch/arm64/kvm/hyp/hyp-entry.S
> index 5170ce1021da..fce7cc507e0a 100644
> --- a/arch/arm64/kvm/hyp/hyp-entry.S
> +++ b/arch/arm64/kvm/hyp/hyp-entry.S
> @@ -104,6 +104,7 @@ el1_trap:
>  	/*
>  	 * x0: ESR_EC
>  	 */
> +	ldr	x1, [sp, #16 + 8]	// vcpu stored by __guest_enter
>  
>  	/*
>  	 * We trap the first access to the FP/SIMD to save the host context
> @@ -116,19 +117,18 @@ alternative_if_not ARM64_HAS_NO_FPSIMD
>  	b.eq	__fpsimd_guest_restore
>  alternative_else_nop_endif
>  
> -	mrs	x1, tpidr_el2
>  	mov	x0, #ARM_EXCEPTION_TRAP
>  	b	__guest_exit
>  
>  el1_irq:
>  	stp     x0, x1, [sp, #-16]!
> -	mrs	x1, tpidr_el2
> +	ldr	x1, [sp, #16 + 8]
>  	mov	x0, #ARM_EXCEPTION_IRQ
>  	b	__guest_exit
>  
>  el1_error:
>  	stp     x0, x1, [sp, #-16]!
> -	mrs	x1, tpidr_el2
> +	ldr	x1, [sp, #16 + 8]
>  	mov	x0, #ARM_EXCEPTION_EL1_SERROR
>  	b	__guest_exit
>  
> -- 
> 2.10.1
> 

Otherwise:
Reviewed-by: Christoffer Dall <cdall-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 01/11] KVM: arm64: Store vcpu on the stack during __guest_enter()
@ 2017-06-06 19:59       ` Christoffer Dall
  0 siblings, 0 replies; 60+ messages in thread
From: Christoffer Dall @ 2017-06-06 19:59 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, May 15, 2017 at 06:43:49PM +0100, James Morse wrote:
> KVM uses tpidr_el2 as its private vcpu register, which makes sense for
> non-vhe world switch as only KVM can access this register. This means
> vhe Linux has to use tpidr_el1, which KVM has to save/restore as part
> of the host context.
> 
> __guest_enter() stores the host_ctxt on the stack, do the same with
> the vcpu.
> 
> Signed-off-by: James Morse <james.morse@arm.com>
> ---
>  arch/arm64/kvm/hyp/entry.S     | 12 ++++++++----
>  arch/arm64/kvm/hyp/hyp-entry.S |  6 +++---
>  2 files changed, 11 insertions(+), 7 deletions(-)
> 
> diff --git a/arch/arm64/kvm/hyp/entry.S b/arch/arm64/kvm/hyp/entry.S
> index 12ee62d6d410..113735df7d01 100644
> --- a/arch/arm64/kvm/hyp/entry.S
> +++ b/arch/arm64/kvm/hyp/entry.S
> @@ -62,8 +62,8 @@ ENTRY(__guest_enter)
>  	// Store the host regs
>  	save_callee_saved_regs x1
>  
> -	// Store the host_ctxt for use at exit time
> -	str	x1, [sp, #-16]!
> +	// Store host_ctxt and vcpu for use at exit time
> +	stp	x1, x0, [sp, #-16]!
>  
>  	add	x18, x0, #VCPU_CONTEXT
>  
> @@ -159,9 +159,15 @@ abort_guest_exit_end:
>  ENDPROC(__guest_exit)
>  
>  ENTRY(__fpsimd_guest_restore)
> +	// x0: esr
> +	// x1: vcpu
> +	// x2-x29,lr: vcpu regs
> +	// vcpu x0-x1 on the stack
>  	stp	x2, x3, [sp, #-16]!
>  	stp	x4, lr, [sp, #-16]!
>  
> +	mov	x3, x1
> +

nit: can you avoid this by using x1 for the vcpu pointer in this routine
instead?

>  alternative_if_not ARM64_HAS_VIRT_HOST_EXTN
>  	mrs	x2, cptr_el2
>  	bic	x2, x2, #CPTR_EL2_TFP
> @@ -173,8 +179,6 @@ alternative_else
>  alternative_endif
>  	isb
>  
> -	mrs	x3, tpidr_el2
> -
>  	ldr	x0, [x3, #VCPU_HOST_CONTEXT]
>  	kern_hyp_va x0
>  	add	x0, x0, #CPU_GP_REG_OFFSET(CPU_FP_REGS)
> diff --git a/arch/arm64/kvm/hyp/hyp-entry.S b/arch/arm64/kvm/hyp/hyp-entry.S
> index 5170ce1021da..fce7cc507e0a 100644
> --- a/arch/arm64/kvm/hyp/hyp-entry.S
> +++ b/arch/arm64/kvm/hyp/hyp-entry.S
> @@ -104,6 +104,7 @@ el1_trap:
>  	/*
>  	 * x0: ESR_EC
>  	 */
> +	ldr	x1, [sp, #16 + 8]	// vcpu stored by __guest_enter
>  
>  	/*
>  	 * We trap the first access to the FP/SIMD to save the host context
> @@ -116,19 +117,18 @@ alternative_if_not ARM64_HAS_NO_FPSIMD
>  	b.eq	__fpsimd_guest_restore
>  alternative_else_nop_endif
>  
> -	mrs	x1, tpidr_el2
>  	mov	x0, #ARM_EXCEPTION_TRAP
>  	b	__guest_exit
>  
>  el1_irq:
>  	stp     x0, x1, [sp, #-16]!
> -	mrs	x1, tpidr_el2
> +	ldr	x1, [sp, #16 + 8]
>  	mov	x0, #ARM_EXCEPTION_IRQ
>  	b	__guest_exit
>  
>  el1_error:
>  	stp     x0, x1, [sp, #-16]!
> -	mrs	x1, tpidr_el2
> +	ldr	x1, [sp, #16 + 8]
>  	mov	x0, #ARM_EXCEPTION_EL1_SERROR
>  	b	__guest_exit
>  
> -- 
> 2.10.1
> 

Otherwise:
Reviewed-by: Christoffer Dall <cdall@linaro.org>

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

* Re: [PATCH 02/11] KVM: arm/arm64: Convert kvm_host_cpu_state to a static per-cpu allocation
  2017-05-15 17:43     ` James Morse
@ 2017-06-06 19:59         ` Christoffer Dall
  -1 siblings, 0 replies; 60+ messages in thread
From: Christoffer Dall @ 2017-06-06 19:59 UTC (permalink / raw)
  To: James Morse
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Will Deacon,
	Catalin Marinas, kvmarm-FPEHb7Xf0XXUo1n7N8X6UoWGPAHP3yOg,
	Marc Zyngier, Christoffer Dall, Rob Herring, Mark Rutland,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On Mon, May 15, 2017 at 06:43:50PM +0100, James Morse wrote:
> kvm_host_cpu_state is a per-cpu allocation made from kvm_arch_init()
> used to store the host EL1 registers when KVM switches to a guest.
> 
> Make it easier for ASM to generate pointers into this per-cpu memory
> by making it a static allocation.
> 

Acked-by: Christoffer Dall <cdall-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

> Signed-off-by: James Morse <james.morse-5wv7dgnIgG8@public.gmane.org>
> ---
>  virt/kvm/arm/arm.c | 18 +++---------------
>  1 file changed, 3 insertions(+), 15 deletions(-)
> 
> diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c
> index 3417e184c8e1..3a776ec99181 100644
> --- a/virt/kvm/arm/arm.c
> +++ b/virt/kvm/arm/arm.c
> @@ -51,8 +51,8 @@
>  __asm__(".arch_extension	virt");
>  #endif
>  
> +DEFINE_PER_CPU(kvm_cpu_context_t, kvm_host_cpu_state);
>  static DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_page);
> -static kvm_cpu_context_t __percpu *kvm_host_cpu_state;
>  
>  /* Per-CPU variable containing the currently running vcpu. */
>  static DEFINE_PER_CPU(struct kvm_vcpu *, kvm_arm_running_vcpu);
> @@ -351,7 +351,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
>  	}
>  
>  	vcpu->cpu = cpu;
> -	vcpu->arch.host_cpu_context = this_cpu_ptr(kvm_host_cpu_state);
> +	vcpu->arch.host_cpu_context = this_cpu_ptr(&kvm_host_cpu_state);
>  
>  	kvm_arm_set_running_vcpu(vcpu);
>  
> @@ -1226,19 +1226,8 @@ static inline void hyp_cpu_pm_exit(void)
>  }
>  #endif
>  
> -static void teardown_common_resources(void)
> -{
> -	free_percpu(kvm_host_cpu_state);
> -}
> -
>  static int init_common_resources(void)
>  {
> -	kvm_host_cpu_state = alloc_percpu(kvm_cpu_context_t);
> -	if (!kvm_host_cpu_state) {
> -		kvm_err("Cannot allocate host CPU state\n");
> -		return -ENOMEM;
> -	}
> -
>  	/* set size of VMID supported by CPU */
>  	kvm_vmid_bits = kvm_get_vmid_bits();
>  	kvm_info("%d-bit VMID\n", kvm_vmid_bits);
> @@ -1383,7 +1372,7 @@ static int init_hyp_mode(void)
>  	for_each_possible_cpu(cpu) {
>  		kvm_cpu_context_t *cpu_ctxt;
>  
> -		cpu_ctxt = per_cpu_ptr(kvm_host_cpu_state, cpu);
> +		cpu_ctxt = per_cpu_ptr(&kvm_host_cpu_state, cpu);
>  		err = create_hyp_mappings(cpu_ctxt, cpu_ctxt + 1, PAGE_HYP);
>  
>  		if (err) {
> @@ -1461,7 +1450,6 @@ int kvm_arch_init(void *opaque)
>  out_hyp:
>  	teardown_hyp_mode();
>  out_err:
> -	teardown_common_resources();
>  	return err;
>  }
>  
> -- 
> 2.10.1
> 
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 02/11] KVM: arm/arm64: Convert kvm_host_cpu_state to a static per-cpu allocation
@ 2017-06-06 19:59         ` Christoffer Dall
  0 siblings, 0 replies; 60+ messages in thread
From: Christoffer Dall @ 2017-06-06 19:59 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, May 15, 2017 at 06:43:50PM +0100, James Morse wrote:
> kvm_host_cpu_state is a per-cpu allocation made from kvm_arch_init()
> used to store the host EL1 registers when KVM switches to a guest.
> 
> Make it easier for ASM to generate pointers into this per-cpu memory
> by making it a static allocation.
> 

Acked-by: Christoffer Dall <cdall@linaro.org>

> Signed-off-by: James Morse <james.morse@arm.com>
> ---
>  virt/kvm/arm/arm.c | 18 +++---------------
>  1 file changed, 3 insertions(+), 15 deletions(-)
> 
> diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c
> index 3417e184c8e1..3a776ec99181 100644
> --- a/virt/kvm/arm/arm.c
> +++ b/virt/kvm/arm/arm.c
> @@ -51,8 +51,8 @@
>  __asm__(".arch_extension	virt");
>  #endif
>  
> +DEFINE_PER_CPU(kvm_cpu_context_t, kvm_host_cpu_state);
>  static DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_page);
> -static kvm_cpu_context_t __percpu *kvm_host_cpu_state;
>  
>  /* Per-CPU variable containing the currently running vcpu. */
>  static DEFINE_PER_CPU(struct kvm_vcpu *, kvm_arm_running_vcpu);
> @@ -351,7 +351,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
>  	}
>  
>  	vcpu->cpu = cpu;
> -	vcpu->arch.host_cpu_context = this_cpu_ptr(kvm_host_cpu_state);
> +	vcpu->arch.host_cpu_context = this_cpu_ptr(&kvm_host_cpu_state);
>  
>  	kvm_arm_set_running_vcpu(vcpu);
>  
> @@ -1226,19 +1226,8 @@ static inline void hyp_cpu_pm_exit(void)
>  }
>  #endif
>  
> -static void teardown_common_resources(void)
> -{
> -	free_percpu(kvm_host_cpu_state);
> -}
> -
>  static int init_common_resources(void)
>  {
> -	kvm_host_cpu_state = alloc_percpu(kvm_cpu_context_t);
> -	if (!kvm_host_cpu_state) {
> -		kvm_err("Cannot allocate host CPU state\n");
> -		return -ENOMEM;
> -	}
> -
>  	/* set size of VMID supported by CPU */
>  	kvm_vmid_bits = kvm_get_vmid_bits();
>  	kvm_info("%d-bit VMID\n", kvm_vmid_bits);
> @@ -1383,7 +1372,7 @@ static int init_hyp_mode(void)
>  	for_each_possible_cpu(cpu) {
>  		kvm_cpu_context_t *cpu_ctxt;
>  
> -		cpu_ctxt = per_cpu_ptr(kvm_host_cpu_state, cpu);
> +		cpu_ctxt = per_cpu_ptr(&kvm_host_cpu_state, cpu);
>  		err = create_hyp_mappings(cpu_ctxt, cpu_ctxt + 1, PAGE_HYP);
>  
>  		if (err) {
> @@ -1461,7 +1450,6 @@ int kvm_arch_init(void *opaque)
>  out_hyp:
>  	teardown_hyp_mode();
>  out_err:
> -	teardown_common_resources();
>  	return err;
>  }
>  
> -- 
> 2.10.1
> 

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

* Re: [PATCH 05/11] arm64: KVM: Stop save/restoring host tpidr_el1 on VHE
  2017-05-15 17:43   ` James Morse
@ 2017-06-06 20:00       ` Christoffer Dall
  -1 siblings, 0 replies; 60+ messages in thread
From: Christoffer Dall @ 2017-06-06 20:00 UTC (permalink / raw)
  To: James Morse
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Will Deacon,
	Catalin Marinas, kvmarm-FPEHb7Xf0XXUo1n7N8X6UoWGPAHP3yOg,
	Marc Zyngier, Christoffer Dall, Rob Herring, Mark Rutland,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On Mon, May 15, 2017 at 06:43:53PM +0100, James Morse wrote:
> Now that a VHE host uses tpidr_el2 for the cpu offset we no longer
> need KVM to save/restore tpidr_el1. Move this from the 'common' code
> into the non-vhe code. While we're at it, on VHE we don't need to
> save the ELR or SPSR as kernel_entry in entry.S will have pushed these
> onto the kernel stack, and will restore them from there. Move these
> to the non-vhe code as we need them to get back to the host.
> 
> Finally remove the always-copy-tpidr we hid in the stage2 setup
> code, cpufeature's enable callback will do this for VHE, we only
> need KVM to do it for non-vhe. Add the copy into kvm-init instead.
> 

Reviewed-by: Christoffer Dall <cdall-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

> Signed-off-by: James Morse <james.morse-5wv7dgnIgG8@public.gmane.org>
> ---
>  arch/arm64/kvm/hyp-init.S      |  4 ++++
>  arch/arm64/kvm/hyp/s2-setup.c  |  3 ---
>  arch/arm64/kvm/hyp/sysreg-sr.c | 16 ++++++++--------
>  3 files changed, 12 insertions(+), 11 deletions(-)
> 
> diff --git a/arch/arm64/kvm/hyp-init.S b/arch/arm64/kvm/hyp-init.S
> index 839425c24b1c..b8219801bb28 100644
> --- a/arch/arm64/kvm/hyp-init.S
> +++ b/arch/arm64/kvm/hyp-init.S
> @@ -119,6 +119,10 @@ __do_hyp_init:
>  	kern_hyp_va	x2
>  	msr	vbar_el2, x2
>  
> +	/* copy tpidr_el1 into tpidr_el2 for use by HYP */
> +	mrs	x1, tpidr_el1
> +	msr	tpidr_el2, x1
> +
>  	/* Hello, World! */
>  	eret
>  ENDPROC(__kvm_hyp_init)
> diff --git a/arch/arm64/kvm/hyp/s2-setup.c b/arch/arm64/kvm/hyp/s2-setup.c
> index eb401dbb285e..b81f4091c909 100644
> --- a/arch/arm64/kvm/hyp/s2-setup.c
> +++ b/arch/arm64/kvm/hyp/s2-setup.c
> @@ -84,8 +84,5 @@ u32 __hyp_text __init_stage2_translation(void)
>  
>  	write_sysreg(val, vtcr_el2);
>  
> -	/* copy tpidr_el1 into tpidr_el2 for use by HYP */
> -	write_sysreg(read_sysreg(tpidr_el1), tpidr_el2);
> -
>  	return parange;
>  }
> diff --git a/arch/arm64/kvm/hyp/sysreg-sr.c b/arch/arm64/kvm/hyp/sysreg-sr.c
> index 934137647837..c54cc2afb92b 100644
> --- a/arch/arm64/kvm/hyp/sysreg-sr.c
> +++ b/arch/arm64/kvm/hyp/sysreg-sr.c
> @@ -27,8 +27,8 @@ static void __hyp_text __sysreg_do_nothing(struct kvm_cpu_context *ctxt) { }
>  /*
>   * Non-VHE: Both host and guest must save everything.
>   *
> - * VHE: Host must save tpidr*_el[01], actlr_el1, mdscr_el1, sp0, pc,
> - * pstate, and guest must save everything.
> + * VHE: Host must save tpidr*_el0, actlr_el1, mdscr_el1, sp_el0,
> + * and guest must save everything.
>   */
>  
>  static void __hyp_text __sysreg_save_common_state(struct kvm_cpu_context *ctxt)
> @@ -36,11 +36,8 @@ static void __hyp_text __sysreg_save_common_state(struct kvm_cpu_context *ctxt)
>  	ctxt->sys_regs[ACTLR_EL1]	= read_sysreg(actlr_el1);
>  	ctxt->sys_regs[TPIDR_EL0]	= read_sysreg(tpidr_el0);
>  	ctxt->sys_regs[TPIDRRO_EL0]	= read_sysreg(tpidrro_el0);
> -	ctxt->sys_regs[TPIDR_EL1]	= read_sysreg(tpidr_el1);
>  	ctxt->sys_regs[MDSCR_EL1]	= read_sysreg(mdscr_el1);
>  	ctxt->gp_regs.regs.sp		= read_sysreg(sp_el0);
> -	ctxt->gp_regs.regs.pc		= read_sysreg_el2(elr);
> -	ctxt->gp_regs.regs.pstate	= read_sysreg_el2(spsr);
>  }
>  
>  static void __hyp_text __sysreg_save_state(struct kvm_cpu_context *ctxt)
> @@ -62,10 +59,13 @@ static void __hyp_text __sysreg_save_state(struct kvm_cpu_context *ctxt)
>  	ctxt->sys_regs[AMAIR_EL1]	= read_sysreg_el1(amair);
>  	ctxt->sys_regs[CNTKCTL_EL1]	= read_sysreg_el1(cntkctl);
>  	ctxt->sys_regs[PAR_EL1]		= read_sysreg(par_el1);
> +	ctxt->sys_regs[TPIDR_EL1]	= read_sysreg(tpidr_el1);
>  
>  	ctxt->gp_regs.sp_el1		= read_sysreg(sp_el1);
>  	ctxt->gp_regs.elr_el1		= read_sysreg_el1(elr);
>  	ctxt->gp_regs.spsr[KVM_SPSR_EL1]= read_sysreg_el1(spsr);
> +	ctxt->gp_regs.regs.pc		= read_sysreg_el2(elr);
> +	ctxt->gp_regs.regs.pstate	= read_sysreg_el2(spsr);
>  }
>  
>  static hyp_alternate_select(__sysreg_call_save_host_state,
> @@ -89,11 +89,8 @@ static void __hyp_text __sysreg_restore_common_state(struct kvm_cpu_context *ctx
>  	write_sysreg(ctxt->sys_regs[ACTLR_EL1],	  actlr_el1);
>  	write_sysreg(ctxt->sys_regs[TPIDR_EL0],	  tpidr_el0);
>  	write_sysreg(ctxt->sys_regs[TPIDRRO_EL0], tpidrro_el0);
> -	write_sysreg(ctxt->sys_regs[TPIDR_EL1],	  tpidr_el1);
>  	write_sysreg(ctxt->sys_regs[MDSCR_EL1],	  mdscr_el1);
>  	write_sysreg(ctxt->gp_regs.regs.sp,	  sp_el0);
> -	write_sysreg_el2(ctxt->gp_regs.regs.pc,	  elr);
> -	write_sysreg_el2(ctxt->gp_regs.regs.pstate, spsr);
>  }
>  
>  static void __hyp_text __sysreg_restore_state(struct kvm_cpu_context *ctxt)
> @@ -115,10 +112,13 @@ static void __hyp_text __sysreg_restore_state(struct kvm_cpu_context *ctxt)
>  	write_sysreg_el1(ctxt->sys_regs[AMAIR_EL1],	amair);
>  	write_sysreg_el1(ctxt->sys_regs[CNTKCTL_EL1], 	cntkctl);
>  	write_sysreg(ctxt->sys_regs[PAR_EL1],		par_el1);
> +	write_sysreg(ctxt->sys_regs[TPIDR_EL1],		tpidr_el1);
>  
>  	write_sysreg(ctxt->gp_regs.sp_el1,		sp_el1);
>  	write_sysreg_el1(ctxt->gp_regs.elr_el1,		elr);
>  	write_sysreg_el1(ctxt->gp_regs.spsr[KVM_SPSR_EL1],spsr);
> +	write_sysreg_el2(ctxt->gp_regs.regs.pc,		elr);
> +	write_sysreg_el2(ctxt->gp_regs.regs.pstate,	spsr);
>  }
>  
>  static hyp_alternate_select(__sysreg_call_restore_host_state,
> -- 
> 2.10.1
> 
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 05/11] arm64: KVM: Stop save/restoring host tpidr_el1 on VHE
@ 2017-06-06 20:00       ` Christoffer Dall
  0 siblings, 0 replies; 60+ messages in thread
From: Christoffer Dall @ 2017-06-06 20:00 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, May 15, 2017 at 06:43:53PM +0100, James Morse wrote:
> Now that a VHE host uses tpidr_el2 for the cpu offset we no longer
> need KVM to save/restore tpidr_el1. Move this from the 'common' code
> into the non-vhe code. While we're at it, on VHE we don't need to
> save the ELR or SPSR as kernel_entry in entry.S will have pushed these
> onto the kernel stack, and will restore them from there. Move these
> to the non-vhe code as we need them to get back to the host.
> 
> Finally remove the always-copy-tpidr we hid in the stage2 setup
> code, cpufeature's enable callback will do this for VHE, we only
> need KVM to do it for non-vhe. Add the copy into kvm-init instead.
> 

Reviewed-by: Christoffer Dall <cdall@linaro.org>

> Signed-off-by: James Morse <james.morse@arm.com>
> ---
>  arch/arm64/kvm/hyp-init.S      |  4 ++++
>  arch/arm64/kvm/hyp/s2-setup.c  |  3 ---
>  arch/arm64/kvm/hyp/sysreg-sr.c | 16 ++++++++--------
>  3 files changed, 12 insertions(+), 11 deletions(-)
> 
> diff --git a/arch/arm64/kvm/hyp-init.S b/arch/arm64/kvm/hyp-init.S
> index 839425c24b1c..b8219801bb28 100644
> --- a/arch/arm64/kvm/hyp-init.S
> +++ b/arch/arm64/kvm/hyp-init.S
> @@ -119,6 +119,10 @@ __do_hyp_init:
>  	kern_hyp_va	x2
>  	msr	vbar_el2, x2
>  
> +	/* copy tpidr_el1 into tpidr_el2 for use by HYP */
> +	mrs	x1, tpidr_el1
> +	msr	tpidr_el2, x1
> +
>  	/* Hello, World! */
>  	eret
>  ENDPROC(__kvm_hyp_init)
> diff --git a/arch/arm64/kvm/hyp/s2-setup.c b/arch/arm64/kvm/hyp/s2-setup.c
> index eb401dbb285e..b81f4091c909 100644
> --- a/arch/arm64/kvm/hyp/s2-setup.c
> +++ b/arch/arm64/kvm/hyp/s2-setup.c
> @@ -84,8 +84,5 @@ u32 __hyp_text __init_stage2_translation(void)
>  
>  	write_sysreg(val, vtcr_el2);
>  
> -	/* copy tpidr_el1 into tpidr_el2 for use by HYP */
> -	write_sysreg(read_sysreg(tpidr_el1), tpidr_el2);
> -
>  	return parange;
>  }
> diff --git a/arch/arm64/kvm/hyp/sysreg-sr.c b/arch/arm64/kvm/hyp/sysreg-sr.c
> index 934137647837..c54cc2afb92b 100644
> --- a/arch/arm64/kvm/hyp/sysreg-sr.c
> +++ b/arch/arm64/kvm/hyp/sysreg-sr.c
> @@ -27,8 +27,8 @@ static void __hyp_text __sysreg_do_nothing(struct kvm_cpu_context *ctxt) { }
>  /*
>   * Non-VHE: Both host and guest must save everything.
>   *
> - * VHE: Host must save tpidr*_el[01], actlr_el1, mdscr_el1, sp0, pc,
> - * pstate, and guest must save everything.
> + * VHE: Host must save tpidr*_el0, actlr_el1, mdscr_el1, sp_el0,
> + * and guest must save everything.
>   */
>  
>  static void __hyp_text __sysreg_save_common_state(struct kvm_cpu_context *ctxt)
> @@ -36,11 +36,8 @@ static void __hyp_text __sysreg_save_common_state(struct kvm_cpu_context *ctxt)
>  	ctxt->sys_regs[ACTLR_EL1]	= read_sysreg(actlr_el1);
>  	ctxt->sys_regs[TPIDR_EL0]	= read_sysreg(tpidr_el0);
>  	ctxt->sys_regs[TPIDRRO_EL0]	= read_sysreg(tpidrro_el0);
> -	ctxt->sys_regs[TPIDR_EL1]	= read_sysreg(tpidr_el1);
>  	ctxt->sys_regs[MDSCR_EL1]	= read_sysreg(mdscr_el1);
>  	ctxt->gp_regs.regs.sp		= read_sysreg(sp_el0);
> -	ctxt->gp_regs.regs.pc		= read_sysreg_el2(elr);
> -	ctxt->gp_regs.regs.pstate	= read_sysreg_el2(spsr);
>  }
>  
>  static void __hyp_text __sysreg_save_state(struct kvm_cpu_context *ctxt)
> @@ -62,10 +59,13 @@ static void __hyp_text __sysreg_save_state(struct kvm_cpu_context *ctxt)
>  	ctxt->sys_regs[AMAIR_EL1]	= read_sysreg_el1(amair);
>  	ctxt->sys_regs[CNTKCTL_EL1]	= read_sysreg_el1(cntkctl);
>  	ctxt->sys_regs[PAR_EL1]		= read_sysreg(par_el1);
> +	ctxt->sys_regs[TPIDR_EL1]	= read_sysreg(tpidr_el1);
>  
>  	ctxt->gp_regs.sp_el1		= read_sysreg(sp_el1);
>  	ctxt->gp_regs.elr_el1		= read_sysreg_el1(elr);
>  	ctxt->gp_regs.spsr[KVM_SPSR_EL1]= read_sysreg_el1(spsr);
> +	ctxt->gp_regs.regs.pc		= read_sysreg_el2(elr);
> +	ctxt->gp_regs.regs.pstate	= read_sysreg_el2(spsr);
>  }
>  
>  static hyp_alternate_select(__sysreg_call_save_host_state,
> @@ -89,11 +89,8 @@ static void __hyp_text __sysreg_restore_common_state(struct kvm_cpu_context *ctx
>  	write_sysreg(ctxt->sys_regs[ACTLR_EL1],	  actlr_el1);
>  	write_sysreg(ctxt->sys_regs[TPIDR_EL0],	  tpidr_el0);
>  	write_sysreg(ctxt->sys_regs[TPIDRRO_EL0], tpidrro_el0);
> -	write_sysreg(ctxt->sys_regs[TPIDR_EL1],	  tpidr_el1);
>  	write_sysreg(ctxt->sys_regs[MDSCR_EL1],	  mdscr_el1);
>  	write_sysreg(ctxt->gp_regs.regs.sp,	  sp_el0);
> -	write_sysreg_el2(ctxt->gp_regs.regs.pc,	  elr);
> -	write_sysreg_el2(ctxt->gp_regs.regs.pstate, spsr);
>  }
>  
>  static void __hyp_text __sysreg_restore_state(struct kvm_cpu_context *ctxt)
> @@ -115,10 +112,13 @@ static void __hyp_text __sysreg_restore_state(struct kvm_cpu_context *ctxt)
>  	write_sysreg_el1(ctxt->sys_regs[AMAIR_EL1],	amair);
>  	write_sysreg_el1(ctxt->sys_regs[CNTKCTL_EL1], 	cntkctl);
>  	write_sysreg(ctxt->sys_regs[PAR_EL1],		par_el1);
> +	write_sysreg(ctxt->sys_regs[TPIDR_EL1],		tpidr_el1);
>  
>  	write_sysreg(ctxt->gp_regs.sp_el1,		sp_el1);
>  	write_sysreg_el1(ctxt->gp_regs.elr_el1,		elr);
>  	write_sysreg_el1(ctxt->gp_regs.spsr[KVM_SPSR_EL1],spsr);
> +	write_sysreg_el2(ctxt->gp_regs.regs.pc,		elr);
> +	write_sysreg_el2(ctxt->gp_regs.regs.pstate,	spsr);
>  }
>  
>  static hyp_alternate_select(__sysreg_call_restore_host_state,
> -- 
> 2.10.1
> 

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

* Re: [PATCH 06/11] dt-bindings: add devicetree binding for describing arm64 SDEI firmware
  2017-05-19  1:48       ` Rob Herring
@ 2017-06-07  8:28         ` James Morse
  -1 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-06-07  8:28 UTC (permalink / raw)
  To: Rob Herring
  Cc: Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA, Marc Zyngier,
	Catalin Marinas, Will Deacon,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	kvmarm-FPEHb7Xf0XXUo1n7N8X6UoWGPAHP3yOg, Christoffer Dall

Hi Rob,

On 19/05/17 02:48, Rob Herring wrote:
> On Mon, May 15, 2017 at 06:43:54PM +0100, James Morse wrote:
>> The Software Delegated Exception Interface (SDEI) is an ARM standard
>> for registering callbacks from the platform firmware into the OS.
>> This is typically used to implement RAS notifications.
>>
>> Add a new devicetree binding to describe the SDE firmware interface.

>> diff --git a/Documentation/devicetree/bindings/arm/sdei.txt b/Documentation/devicetree/bindings/arm/sdei.txt
>> new file mode 100644
>> index 000000000000..69220d995286
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/arm/sdei.txt
>> @@ -0,0 +1,37 @@
>> +* Software Delegated Exception Interface (SDEI)
>> +
>> +Firmware implementing the SDEI functions described in ARM document number
>> +ARM DEN 0054A ("Software Delegated Exception Interface") can be used by
>> +Linux to receive notification of events such as those generated by
>> +firmware-first error handling.
> 
> Why am I reviewing bindings for RAS firmware-first events which is a 
> server thing? Just curious.

I did a bad job of explaining it: SDEI ended up being a general-purpose event
notification mechanism, one of the API calls in the spec lets an IRQ be 'bound'
to an event so its delivered to firmware, then via SDEI possibly interrupting
code that has IRQs masked.

(I will add some similar text...)

>> +
>> +The interface provides a number of API functions for registering callbacks
>> +and enabling/disabling events. Functions are invoked by trapping to the
>> +privilege level of the SDEI firmware (specified as part of the binding
>> +below) and passing arguments in a manner similar to that specified by AAPCS:
> 
> Shouldn't it be the ARM secure monitor call convention doc (forgot the 
> name) that you are following?

SMCCC... yes, that would make more sense, I just copied the PSCI binding.

'similar to .. AAPCS' should be come 'specified by SMCCC'.


>> +
>> +	 r0		=> 32-bit Function ID / return value
>> +	{r1 - r3}	=> Parameters
>> +
>> +Note that the immediate field of the trapping instruction must be set
>> +to #0.
>> +
>> +The SDEI_EVENT_REGISTER function registers a callback in the kernel
>> +text to handle the specified event number.
>> +
>> +Main node required properties:
>> +
>> + - compatible    : should contain:
>> +	* "arm,sdei-1.0" : For implementations complying to SDEI version 1.x.
>> +
>> + - method        : The method of calling the SDEI firmware. Permitted
>> +                   values are:
>> +	* "smc" : SMC #0, with the register assignments specified in this
>> +	          binding.
>> +	* "hvc" : HVC #0, with the register assignments specified in this
>> +	          binding.
> 
> When is ARM going to define a firmware interface to determine what 
> firmware interfaces are available?

Ideally the _version() calls could be probed, but SMCCC has:
> The Unknown Function Identifier must not be used to discover the presence, or
> lack of, an SMC or HVC Function.

So I think we're stuck with doing it like this...


>> +Example:
>> +	sdei {
> 
> Please specify this should be a child node of /firmware node.

Sure,


Thanks,

James
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 06/11] dt-bindings: add devicetree binding for describing arm64 SDEI firmware
@ 2017-06-07  8:28         ` James Morse
  0 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-06-07  8:28 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Rob,

On 19/05/17 02:48, Rob Herring wrote:
> On Mon, May 15, 2017 at 06:43:54PM +0100, James Morse wrote:
>> The Software Delegated Exception Interface (SDEI) is an ARM standard
>> for registering callbacks from the platform firmware into the OS.
>> This is typically used to implement RAS notifications.
>>
>> Add a new devicetree binding to describe the SDE firmware interface.

>> diff --git a/Documentation/devicetree/bindings/arm/sdei.txt b/Documentation/devicetree/bindings/arm/sdei.txt
>> new file mode 100644
>> index 000000000000..69220d995286
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/arm/sdei.txt
>> @@ -0,0 +1,37 @@
>> +* Software Delegated Exception Interface (SDEI)
>> +
>> +Firmware implementing the SDEI functions described in ARM document number
>> +ARM DEN 0054A ("Software Delegated Exception Interface") can be used by
>> +Linux to receive notification of events such as those generated by
>> +firmware-first error handling.
> 
> Why am I reviewing bindings for RAS firmware-first events which is a 
> server thing? Just curious.

I did a bad job of explaining it: SDEI ended up being a general-purpose event
notification mechanism, one of the API calls in the spec lets an IRQ be 'bound'
to an event so its delivered to firmware, then via SDEI possibly interrupting
code that has IRQs masked.

(I will add some similar text...)

>> +
>> +The interface provides a number of API functions for registering callbacks
>> +and enabling/disabling events. Functions are invoked by trapping to the
>> +privilege level of the SDEI firmware (specified as part of the binding
>> +below) and passing arguments in a manner similar to that specified by AAPCS:
> 
> Shouldn't it be the ARM secure monitor call convention doc (forgot the 
> name) that you are following?

SMCCC... yes, that would make more sense, I just copied the PSCI binding.

'similar to .. AAPCS' should be come 'specified by SMCCC'.


>> +
>> +	 r0		=> 32-bit Function ID / return value
>> +	{r1 - r3}	=> Parameters
>> +
>> +Note that the immediate field of the trapping instruction must be set
>> +to #0.
>> +
>> +The SDEI_EVENT_REGISTER function registers a callback in the kernel
>> +text to handle the specified event number.
>> +
>> +Main node required properties:
>> +
>> + - compatible    : should contain:
>> +	* "arm,sdei-1.0" : For implementations complying to SDEI version 1.x.
>> +
>> + - method        : The method of calling the SDEI firmware. Permitted
>> +                   values are:
>> +	* "smc" : SMC #0, with the register assignments specified in this
>> +	          binding.
>> +	* "hvc" : HVC #0, with the register assignments specified in this
>> +	          binding.
> 
> When is ARM going to define a firmware interface to determine what 
> firmware interfaces are available?

Ideally the _version() calls could be probed, but SMCCC has:
> The Unknown Function Identifier must not be used to discover the presence, or
> lack of, an SMC or HVC Function.

So I think we're stuck with doing it like this...


>> +Example:
>> +	sdei {
> 
> Please specify this should be a child node of /firmware node.

Sure,


Thanks,

James

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

* Re: [PATCH 00/11] arm64/firmware: Software Delegated Exception Interface
  2017-06-06 19:59     ` Christoffer Dall
@ 2017-06-07  9:45       ` James Morse
  -1 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-06-07  9:45 UTC (permalink / raw)
  To: Christoffer Dall
  Cc: devicetree, Marc Zyngier, Catalin Marinas, Will Deacon,
	Rob Herring, linux-arm-kernel, kvmarm

Hi Christoffer,

On 06/06/17 20:59, Christoffer Dall wrote:
> On Mon, May 15, 2017 at 06:43:48PM +0100, James Morse wrote:
>> Running the event handler behind VHE-KVM's back has some side effects: The
>> event handler will blindly use any registers that are shared between the host
>> and guest. The two that I think matter are TPIDR_EL1, and the debug state. The
>> guest may have set MDSCR_EL1 so debug exceptions must remain masked. The
>> guest's TPIDR_EL1 will be used by the event handler if it accesses per-cpu
>> variables. This needs fixing. The first part of this series juggles KVMs use
>> of TPIDR_EL2 so that we share it with the host on VHE systems. An equivalent
>> change for 32bit is on my todo list. (one alternative to this is to have a
>> parody world switch in the SDEI event handler, but this would mean special
>> casing interrupted guests, and be an ABI link to KVM.)
>>
>> Causing a synchronous exception from an event handler will cause KVM to
>> hyp-panic, but may silently succeed if the event didn't interrupt a guest.
>> (I may WARN_ON() if this happens in a later patch). You because of this you
> 
> The last sentence here doesn't make much sense to me.

If this interrupts a VHE-guest, KVM's hyp-vectors remain in vbar_el2. If we then
take a synchronous exception, KVM will assume this happened during world switch
and panic.
In contrast if you didn't interrupt a guest, the kernel vectors are in vbar_el2,
so the fault will be handled.

This 'silently succeed' only occurred to me when writing the cover-letter,
(hence its probably worded badly). I plan to catch it by save-restoring the
exception registers, and warning if they've changed as this would hyp-panic if
you interrupted a guest.


> For the rest of the KVM part it looks mostly good to me, besides the
> points I raised in the individual patches.

Thanks for taking a look!


Thanks,

James

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

* [PATCH 00/11] arm64/firmware: Software Delegated Exception Interface
@ 2017-06-07  9:45       ` James Morse
  0 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-06-07  9:45 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Christoffer,

On 06/06/17 20:59, Christoffer Dall wrote:
> On Mon, May 15, 2017 at 06:43:48PM +0100, James Morse wrote:
>> Running the event handler behind VHE-KVM's back has some side effects: The
>> event handler will blindly use any registers that are shared between the host
>> and guest. The two that I think matter are TPIDR_EL1, and the debug state. The
>> guest may have set MDSCR_EL1 so debug exceptions must remain masked. The
>> guest's TPIDR_EL1 will be used by the event handler if it accesses per-cpu
>> variables. This needs fixing. The first part of this series juggles KVMs use
>> of TPIDR_EL2 so that we share it with the host on VHE systems. An equivalent
>> change for 32bit is on my todo list. (one alternative to this is to have a
>> parody world switch in the SDEI event handler, but this would mean special
>> casing interrupted guests, and be an ABI link to KVM.)
>>
>> Causing a synchronous exception from an event handler will cause KVM to
>> hyp-panic, but may silently succeed if the event didn't interrupt a guest.
>> (I may WARN_ON() if this happens in a later patch). You because of this you
> 
> The last sentence here doesn't make much sense to me.

If this interrupts a VHE-guest, KVM's hyp-vectors remain in vbar_el2. If we then
take a synchronous exception, KVM will assume this happened during world switch
and panic.
In contrast if you didn't interrupt a guest, the kernel vectors are in vbar_el2,
so the fault will be handled.

This 'silently succeed' only occurred to me when writing the cover-letter,
(hence its probably worded badly). I plan to catch it by save-restoring the
exception registers, and warning if they've changed as this would hyp-panic if
you interrupted a guest.


> For the rest of the KVM part it looks mostly good to me, besides the
> points I raised in the individual patches.

Thanks for taking a look!


Thanks,

James

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

* Re: [PATCH 00/11] arm64/firmware: Software Delegated Exception Interface
  2017-06-07  9:45       ` James Morse
@ 2017-06-07  9:53         ` Christoffer Dall
  -1 siblings, 0 replies; 60+ messages in thread
From: Christoffer Dall @ 2017-06-07  9:53 UTC (permalink / raw)
  To: James Morse
  Cc: devicetree, Marc Zyngier, Catalin Marinas, Will Deacon,
	Rob Herring, linux-arm-kernel, kvmarm

On Wed, Jun 07, 2017 at 10:45:32AM +0100, James Morse wrote:
> Hi Christoffer,
> 
> On 06/06/17 20:59, Christoffer Dall wrote:
> > On Mon, May 15, 2017 at 06:43:48PM +0100, James Morse wrote:
> >> Running the event handler behind VHE-KVM's back has some side effects: The
> >> event handler will blindly use any registers that are shared between the host
> >> and guest. The two that I think matter are TPIDR_EL1, and the debug state. The
> >> guest may have set MDSCR_EL1 so debug exceptions must remain masked. The
> >> guest's TPIDR_EL1 will be used by the event handler if it accesses per-cpu
> >> variables. This needs fixing. The first part of this series juggles KVMs use
> >> of TPIDR_EL2 so that we share it with the host on VHE systems. An equivalent
> >> change for 32bit is on my todo list. (one alternative to this is to have a
> >> parody world switch in the SDEI event handler, but this would mean special
> >> casing interrupted guests, and be an ABI link to KVM.)
> >>
> >> Causing a synchronous exception from an event handler will cause KVM to
> >> hyp-panic, but may silently succeed if the event didn't interrupt a guest.
> >> (I may WARN_ON() if this happens in a later patch). You because of this you
> > 
> > The last sentence here doesn't make much sense to me.
> 
> If this interrupts a VHE-guest, KVM's hyp-vectors remain in vbar_el2. If we then
> take a synchronous exception, KVM will assume this happened during world switch
> and panic.
> In contrast if you didn't interrupt a guest, the kernel vectors are in vbar_el2,
> so the fault will be handled.

Sorry, I actually just meant the grammar... "You because of this you..."

> 
> This 'silently succeed' only occurred to me when writing the cover-letter,
> (hence its probably worded badly). I plan to catch it by save-restoring the
> exception registers, and warning if they've changed as this would hyp-panic if
> you interrupted a guest.
> 

But I didn't understand that before, and didn't realize that 'silently
succeed' may not be what you want.

Thanks for the explanation.

-Christoffer

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

* [PATCH 00/11] arm64/firmware: Software Delegated Exception Interface
@ 2017-06-07  9:53         ` Christoffer Dall
  0 siblings, 0 replies; 60+ messages in thread
From: Christoffer Dall @ 2017-06-07  9:53 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Jun 07, 2017 at 10:45:32AM +0100, James Morse wrote:
> Hi Christoffer,
> 
> On 06/06/17 20:59, Christoffer Dall wrote:
> > On Mon, May 15, 2017 at 06:43:48PM +0100, James Morse wrote:
> >> Running the event handler behind VHE-KVM's back has some side effects: The
> >> event handler will blindly use any registers that are shared between the host
> >> and guest. The two that I think matter are TPIDR_EL1, and the debug state. The
> >> guest may have set MDSCR_EL1 so debug exceptions must remain masked. The
> >> guest's TPIDR_EL1 will be used by the event handler if it accesses per-cpu
> >> variables. This needs fixing. The first part of this series juggles KVMs use
> >> of TPIDR_EL2 so that we share it with the host on VHE systems. An equivalent
> >> change for 32bit is on my todo list. (one alternative to this is to have a
> >> parody world switch in the SDEI event handler, but this would mean special
> >> casing interrupted guests, and be an ABI link to KVM.)
> >>
> >> Causing a synchronous exception from an event handler will cause KVM to
> >> hyp-panic, but may silently succeed if the event didn't interrupt a guest.
> >> (I may WARN_ON() if this happens in a later patch). You because of this you
> > 
> > The last sentence here doesn't make much sense to me.
> 
> If this interrupts a VHE-guest, KVM's hyp-vectors remain in vbar_el2. If we then
> take a synchronous exception, KVM will assume this happened during world switch
> and panic.
> In contrast if you didn't interrupt a guest, the kernel vectors are in vbar_el2,
> so the fault will be handled.

Sorry, I actually just meant the grammar... "You because of this you..."

> 
> This 'silently succeed' only occurred to me when writing the cover-letter,
> (hence its probably worded badly). I plan to catch it by save-restoring the
> exception registers, and warning if they've changed as this would hyp-panic if
> you interrupted a guest.
> 

But I didn't understand that before, and didn't realize that 'silently
succeed' may not be what you want.

Thanks for the explanation.

-Christoffer

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

* Re: [PATCH 03/11] KVM: arm64: Change hyp_panic()s dependency on tpidr_el2
  2017-06-06 19:45       ` Christoffer Dall
@ 2017-06-08 10:23         ` James Morse
  -1 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-06-08 10:23 UTC (permalink / raw)
  To: Christoffer Dall
  Cc: devicetree, Marc Zyngier, Catalin Marinas, Will Deacon,
	Rob Herring, linux-arm-kernel, kvmarm

Hi Christoffer,

On 06/06/17 20:45, Christoffer Dall wrote:
> On Mon, May 15, 2017 at 06:43:51PM +0100, James Morse wrote:
>> KVM calls hyp_panic() when anything unexpected happens. This may occur
>> while a guest owns the EL1 registers. KVM stashes the vcpu pointer in
>> tpidr_el2, which it uses to find the host context in order to restore
>> the host EL1 registers before parachuting into the host's panic().
>>
>> The host context is a struct kvm_cpu_context allocated in the per-cpu
>> area, and mapped to hyp. Given the per-cpu offset for this CPU, this is
>> easy to find. Change hyp_panic() to take a pointer to the
>> struct kvm_cpu_context. Wrap these calls with an asm function that
>> retrieves the struct kvm_cpu_context from the host's per-cpu area.
>>
>> Copy the per-cpu offset from the hosts tpidr_el1 into tpidr_el2 during
>> kvm init. (Later patches will make this unnecessary for VHE hosts)
>>
>> We print out the vcpu pointer as part of the panic message. Add a back
>> reference to the 'running vcpu' in the host cpu context to preserve this.

>> diff --git a/arch/arm64/kvm/hyp/hyp-entry.S b/arch/arm64/kvm/hyp/hyp-entry.S
>> index fce7cc507e0a..0f83e7e402ad 100644
>> --- a/arch/arm64/kvm/hyp/hyp-entry.S
>> +++ b/arch/arm64/kvm/hyp/hyp-entry.S
>> @@ -163,6 +163,13 @@ ENTRY(__hyp_do_panic)
>>  	eret
>>  ENDPROC(__hyp_do_panic)
>>  
>> +ENTRY(__hyp_panic)
>> +	ldr	x0, =kvm_host_cpu_state
> 
> Why is this guaranteed to give us a valid EL2 address?

... it doesn't. Its a host address from the constant pool which is used by
hyp-panic as __host_ctxt:
> void __hyp_text __noreturn hyp_panic(struct kvm_cpu_context *__host_ctxt);

and fixed up with:
> host_ctxt = kern_hyp_va(__host_ctxt);

to give us the EL2 address before we access it.
For VHE the last step is a nop as the host address can be accessed directly.

I will add a comment here explaining this.


Thanks!

James

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

* [PATCH 03/11] KVM: arm64: Change hyp_panic()s dependency on tpidr_el2
@ 2017-06-08 10:23         ` James Morse
  0 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-06-08 10:23 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Christoffer,

On 06/06/17 20:45, Christoffer Dall wrote:
> On Mon, May 15, 2017 at 06:43:51PM +0100, James Morse wrote:
>> KVM calls hyp_panic() when anything unexpected happens. This may occur
>> while a guest owns the EL1 registers. KVM stashes the vcpu pointer in
>> tpidr_el2, which it uses to find the host context in order to restore
>> the host EL1 registers before parachuting into the host's panic().
>>
>> The host context is a struct kvm_cpu_context allocated in the per-cpu
>> area, and mapped to hyp. Given the per-cpu offset for this CPU, this is
>> easy to find. Change hyp_panic() to take a pointer to the
>> struct kvm_cpu_context. Wrap these calls with an asm function that
>> retrieves the struct kvm_cpu_context from the host's per-cpu area.
>>
>> Copy the per-cpu offset from the hosts tpidr_el1 into tpidr_el2 during
>> kvm init. (Later patches will make this unnecessary for VHE hosts)
>>
>> We print out the vcpu pointer as part of the panic message. Add a back
>> reference to the 'running vcpu' in the host cpu context to preserve this.

>> diff --git a/arch/arm64/kvm/hyp/hyp-entry.S b/arch/arm64/kvm/hyp/hyp-entry.S
>> index fce7cc507e0a..0f83e7e402ad 100644
>> --- a/arch/arm64/kvm/hyp/hyp-entry.S
>> +++ b/arch/arm64/kvm/hyp/hyp-entry.S
>> @@ -163,6 +163,13 @@ ENTRY(__hyp_do_panic)
>>  	eret
>>  ENDPROC(__hyp_do_panic)
>>  
>> +ENTRY(__hyp_panic)
>> +	ldr	x0, =kvm_host_cpu_state
> 
> Why is this guaranteed to give us a valid EL2 address?

... it doesn't. Its a host address from the constant pool which is used by
hyp-panic as __host_ctxt:
> void __hyp_text __noreturn hyp_panic(struct kvm_cpu_context *__host_ctxt);

and fixed up with:
> host_ctxt = kern_hyp_va(__host_ctxt);

to give us the EL2 address before we access it.
For VHE the last step is a nop as the host address can be accessed directly.

I will add a comment here explaining this.


Thanks!

James

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

* Re: [PATCH 03/11] KVM: arm64: Change hyp_panic()s dependency on tpidr_el2
  2017-06-08 10:23         ` James Morse
@ 2017-06-08 10:34             ` Christoffer Dall
  -1 siblings, 0 replies; 60+ messages in thread
From: Christoffer Dall @ 2017-06-08 10:34 UTC (permalink / raw)
  To: James Morse
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Will Deacon,
	Catalin Marinas, kvmarm-FPEHb7Xf0XXUo1n7N8X6UoWGPAHP3yOg,
	Marc Zyngier, Christoffer Dall, Rob Herring, Mark Rutland,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On Thu, Jun 08, 2017 at 11:23:55AM +0100, James Morse wrote:
> Hi Christoffer,
> 
> On 06/06/17 20:45, Christoffer Dall wrote:
> > On Mon, May 15, 2017 at 06:43:51PM +0100, James Morse wrote:
> >> KVM calls hyp_panic() when anything unexpected happens. This may occur
> >> while a guest owns the EL1 registers. KVM stashes the vcpu pointer in
> >> tpidr_el2, which it uses to find the host context in order to restore
> >> the host EL1 registers before parachuting into the host's panic().
> >>
> >> The host context is a struct kvm_cpu_context allocated in the per-cpu
> >> area, and mapped to hyp. Given the per-cpu offset for this CPU, this is
> >> easy to find. Change hyp_panic() to take a pointer to the
> >> struct kvm_cpu_context. Wrap these calls with an asm function that
> >> retrieves the struct kvm_cpu_context from the host's per-cpu area.
> >>
> >> Copy the per-cpu offset from the hosts tpidr_el1 into tpidr_el2 during
> >> kvm init. (Later patches will make this unnecessary for VHE hosts)
> >>
> >> We print out the vcpu pointer as part of the panic message. Add a back
> >> reference to the 'running vcpu' in the host cpu context to preserve this.
> 
> >> diff --git a/arch/arm64/kvm/hyp/hyp-entry.S b/arch/arm64/kvm/hyp/hyp-entry.S
> >> index fce7cc507e0a..0f83e7e402ad 100644
> >> --- a/arch/arm64/kvm/hyp/hyp-entry.S
> >> +++ b/arch/arm64/kvm/hyp/hyp-entry.S
> >> @@ -163,6 +163,13 @@ ENTRY(__hyp_do_panic)
> >>  	eret
> >>  ENDPROC(__hyp_do_panic)
> >>  
> >> +ENTRY(__hyp_panic)
> >> +	ldr	x0, =kvm_host_cpu_state
> > 
> > Why is this guaranteed to give us a valid EL2 address?
> 
> ... it doesn't. Its a host address from the constant pool which is used by
> hyp-panic as __host_ctxt:
> > void __hyp_text __noreturn hyp_panic(struct kvm_cpu_context *__host_ctxt);
> 
> and fixed up with:
> > host_ctxt = kern_hyp_va(__host_ctxt);
> 

Ah, right, we only dereference the pointer inside the function, I missed
that.

> to give us the EL2 address before we access it.
> For VHE the last step is a nop as the host address can be accessed directly.
> 
> I will add a comment here explaining this.
> 
> 

Thanks!
-Christoffer
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 03/11] KVM: arm64: Change hyp_panic()s dependency on tpidr_el2
@ 2017-06-08 10:34             ` Christoffer Dall
  0 siblings, 0 replies; 60+ messages in thread
From: Christoffer Dall @ 2017-06-08 10:34 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Jun 08, 2017 at 11:23:55AM +0100, James Morse wrote:
> Hi Christoffer,
> 
> On 06/06/17 20:45, Christoffer Dall wrote:
> > On Mon, May 15, 2017 at 06:43:51PM +0100, James Morse wrote:
> >> KVM calls hyp_panic() when anything unexpected happens. This may occur
> >> while a guest owns the EL1 registers. KVM stashes the vcpu pointer in
> >> tpidr_el2, which it uses to find the host context in order to restore
> >> the host EL1 registers before parachuting into the host's panic().
> >>
> >> The host context is a struct kvm_cpu_context allocated in the per-cpu
> >> area, and mapped to hyp. Given the per-cpu offset for this CPU, this is
> >> easy to find. Change hyp_panic() to take a pointer to the
> >> struct kvm_cpu_context. Wrap these calls with an asm function that
> >> retrieves the struct kvm_cpu_context from the host's per-cpu area.
> >>
> >> Copy the per-cpu offset from the hosts tpidr_el1 into tpidr_el2 during
> >> kvm init. (Later patches will make this unnecessary for VHE hosts)
> >>
> >> We print out the vcpu pointer as part of the panic message. Add a back
> >> reference to the 'running vcpu' in the host cpu context to preserve this.
> 
> >> diff --git a/arch/arm64/kvm/hyp/hyp-entry.S b/arch/arm64/kvm/hyp/hyp-entry.S
> >> index fce7cc507e0a..0f83e7e402ad 100644
> >> --- a/arch/arm64/kvm/hyp/hyp-entry.S
> >> +++ b/arch/arm64/kvm/hyp/hyp-entry.S
> >> @@ -163,6 +163,13 @@ ENTRY(__hyp_do_panic)
> >>  	eret
> >>  ENDPROC(__hyp_do_panic)
> >>  
> >> +ENTRY(__hyp_panic)
> >> +	ldr	x0, =kvm_host_cpu_state
> > 
> > Why is this guaranteed to give us a valid EL2 address?
> 
> ... it doesn't. Its a host address from the constant pool which is used by
> hyp-panic as __host_ctxt:
> > void __hyp_text __noreturn hyp_panic(struct kvm_cpu_context *__host_ctxt);
> 
> and fixed up with:
> > host_ctxt = kern_hyp_va(__host_ctxt);
> 

Ah, right, we only dereference the pointer inside the function, I missed
that.

> to give us the EL2 address before we access it.
> For VHE the last step is a nop as the host address can be accessed directly.
> 
> I will add a comment here explaining this.
> 
> 

Thanks!
-Christoffer

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

* Re: [PATCH 07/11] firmware: arm_sdei: Add driver for Software Delegated Exceptions
  2017-05-15 17:43   ` James Morse
@ 2017-07-19 13:52       ` Dave Martin
  -1 siblings, 0 replies; 60+ messages in thread
From: Dave Martin @ 2017-07-19 13:52 UTC (permalink / raw)
  To: James Morse
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Rutland,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Marc Zyngier, Catalin Marinas,
	Will Deacon, Rob Herring,
	kvmarm-FPEHb7Xf0XXUo1n7N8X6UoWGPAHP3yOg, Christoffer Dall

On Mon, May 15, 2017 at 06:43:55PM +0100, James Morse wrote:
> The Software Delegated Exception Interface (SDEI) is an ARM standard
> for registering callbacks from the platform firmware into the OS.
> This is typically used to implement RAS notifications.
> 
> Add the code for detecting the SDEI version and the framework for
> registering and unregistering events. Subsequent patches will add the
> arch-specific backend code and the necessary power management hooks.
> 
> Currently only shared events are supported.
> 
> Signed-off-by: James Morse <james.morse-5wv7dgnIgG8@public.gmane.org>
> ---
>  drivers/firmware/Kconfig    |   7 +
>  drivers/firmware/Makefile   |   1 +
>  drivers/firmware/arm_sdei.c | 606 ++++++++++++++++++++++++++++++++++++++++++++
>  include/linux/sdei.h        |  98 +++++++
>  include/uapi/linux/sdei.h   |  91 +++++++
>  5 files changed, 803 insertions(+)
>  create mode 100644 drivers/firmware/arm_sdei.c
>  create mode 100644 include/linux/sdei.h
>  create mode 100644 include/uapi/linux/sdei.h

[...]

> diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c
> new file mode 100644
> index 000000000000..d22dda5e0fed
> --- /dev/null
> +++ b/drivers/firmware/arm_sdei.c
> @@ -0,0 +1,606 @@

[...]

> +int sdei_mask_local_cpu(unsigned int cpu)
> +{
> +	int err;
> +	struct arm_smccc_res res;
> +
> +	WARN_ON(preemptible());
> +	err = invoke_sdei_fn(&res, SDEI_1_0_FN_SDEI_PE_MASK, 0, 0, 0, 0, 0);

Randomly responding to an old patch here...


It seems awkward to have to declare res repeatedly when it's basically
unused.  Several callsites seem to do this.  Out of context, this looks
bug-like (though it's not a bug).

Could we make it explicit that the results other than x0 are unwanted by
passing NULL instead?

invoke_sdei_fn (or some downstream function) could declare its own res
for this case, but at least we'd only have to do that in one place.
arm_smccc_smc() and friends (at least the C interface in the headers)
could do something similar.

Cheers
---Dave
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 07/11] firmware: arm_sdei: Add driver for Software Delegated Exceptions
@ 2017-07-19 13:52       ` Dave Martin
  0 siblings, 0 replies; 60+ messages in thread
From: Dave Martin @ 2017-07-19 13:52 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, May 15, 2017 at 06:43:55PM +0100, James Morse wrote:
> The Software Delegated Exception Interface (SDEI) is an ARM standard
> for registering callbacks from the platform firmware into the OS.
> This is typically used to implement RAS notifications.
> 
> Add the code for detecting the SDEI version and the framework for
> registering and unregistering events. Subsequent patches will add the
> arch-specific backend code and the necessary power management hooks.
> 
> Currently only shared events are supported.
> 
> Signed-off-by: James Morse <james.morse@arm.com>
> ---
>  drivers/firmware/Kconfig    |   7 +
>  drivers/firmware/Makefile   |   1 +
>  drivers/firmware/arm_sdei.c | 606 ++++++++++++++++++++++++++++++++++++++++++++
>  include/linux/sdei.h        |  98 +++++++
>  include/uapi/linux/sdei.h   |  91 +++++++
>  5 files changed, 803 insertions(+)
>  create mode 100644 drivers/firmware/arm_sdei.c
>  create mode 100644 include/linux/sdei.h
>  create mode 100644 include/uapi/linux/sdei.h

[...]

> diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c
> new file mode 100644
> index 000000000000..d22dda5e0fed
> --- /dev/null
> +++ b/drivers/firmware/arm_sdei.c
> @@ -0,0 +1,606 @@

[...]

> +int sdei_mask_local_cpu(unsigned int cpu)
> +{
> +	int err;
> +	struct arm_smccc_res res;
> +
> +	WARN_ON(preemptible());
> +	err = invoke_sdei_fn(&res, SDEI_1_0_FN_SDEI_PE_MASK, 0, 0, 0, 0, 0);

Randomly responding to an old patch here...


It seems awkward to have to declare res repeatedly when it's basically
unused.  Several callsites seem to do this.  Out of context, this looks
bug-like (though it's not a bug).

Could we make it explicit that the results other than x0 are unwanted by
passing NULL instead?

invoke_sdei_fn (or some downstream function) could declare its own res
for this case, but at least we'd only have to do that in one place.
arm_smccc_smc() and friends (at least the C interface in the headers)
could do something similar.

Cheers
---Dave

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

* Re: [PATCH 11/11] KVM: arm64: Delegate support for SDEI to userspace
  2017-06-06 19:58         ` Christoffer Dall
@ 2017-07-26 17:00           ` James Morse
  -1 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-07-26 17:00 UTC (permalink / raw)
  To: Christoffer Dall
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Will Deacon,
	Catalin Marinas, kvmarm-FPEHb7Xf0XXUo1n7N8X6UoWGPAHP3yOg,
	Marc Zyngier, Christoffer Dall, Rob Herring, Mark Rutland,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Hi Christoffer,

(looks like I forgot to send this ...)

On 06/06/17 20:58, Christoffer Dall wrote:
> On Mon, May 15, 2017 at 06:43:59PM +0100, James Morse wrote:
>> The Software Delegated Exception Interface allows firmware to notify
>> the OS of system events by returning into registered handlers, even
>> if the OS has interrupts masked.
>>
>> While we could support this in KVM, we would need to expose an API for
>> the user space hypervisor to inject events, (and decide what to do it
> 
> 'the user space hypervisor' ?

Qemu or kvmtool. I never know what generic term to use for these.
virtual-machine-monitor?


> s/it/if/
> 
>> the event isn't registered or all the CPUs have SDE events masked). We
>> already have an API for guest 'hypercalls', so use this to push the
>> problem onto userspace.
>>
>> Advertise a new capability 'KVM_CAP_ARM_SDEI_1_0' and when any SDEI
>> call comes in, exit to userspace with exit_reason = KVM_EXIT_HYPERCALL.

> Documentation/virtual/kvm/api.txt says this is unused.
> 
> We should add something there to say that this is now used for arm64,
> and the api doc also suggests that the hypercall struct in kvm_run has
> some meaningful data for this exit.

Yes, good point.

I was expecting this patch to provoke some wider discussion on how to delegate
SMCCC/HVC calls to user space. Do we want per-API KVM_CAP's, or one that dumps
the whole range on user-space when enabled. It came up (as a tangent) on another
thread:

Marc Zyngier wrote[0]:
> Eventually, we want to be able to handle the full spectrum of the SMCCC
> and forward things to an actual TEE if available. There is no real
> reason why PSCI shouldn't be handled in userspace the same way (and we
> already offload reset and halt to QEMU).


> Have we checked that the guest can't provoke QEMU to do something weird
> by causing this exit on arm64 currently (given that we always enable
> this handling of SDEI calls)?

Qemu 2.2.0 in ubuntu 15.04 ignores the 'sdei_version' hvc/hypercall-exit and
re-enters the guest with the registers unmodified. I think this is 'weird', I
assumed it would exit.


>> N.B. There is no enable/feature bit for SDEI exits as telling the guest
>> the interface exists via DT/ACPI should be sufficient.

I'm probably being too trusting here. Today an unknown HVC will cause KVM to
inject an undef, whereas with this change it might get handled by user-space if
the kernel recognises the range, and user-space might just skip the HVC and
carry on...

I will change this to support KVM_CAP_ENABLE_CAP_VM to enable the SDEI CAP and
pass that HVC range through to user-space using KVM_EXIT_HYPERCALL and
populating as much of that structure as makes sense...


>> diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c
>> index 3a776ec99181..0bf2d923483c 100644
>> --- a/virt/kvm/arm/arm.c
>> +++ b/virt/kvm/arm/arm.c
>> @@ -206,8 +206,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
>>  	case KVM_CAP_READONLY_MEM:
>>  	case KVM_CAP_MP_STATE:
>>  	case KVM_CAP_IMMEDIATE_EXIT:
>> -		r = 1;
>> -		break;
>> +#ifdef CONFIG_ARM_SDE_INTERFACE
>> +	case KVM_CAP_ARM_SDEI_1_0:
>> +#endif
> 
> What's the point of conditionally supporting this based on the config
> option when the rest of the KVM functionality does not depend on the
> CONFIG_ARM_SDE_INTERFACE functionality?

You're right it doesn't depend on anything in KVM, but adding it unconditionally
here will enable it on 32bit too, and the spec says this is aarch64 only. So
#ifdef ARM64 would have been better.


> Could a user want to play with SDEI calls in a VM without the host
> having the proper support, or is that never relevant?

That works fine (its how it was developed!).

'Virtual machine monitors' should be able to pick a RAS notification method for
guests independently of what the host is using (if anything). If this doesn't
work it means we've accidentally created some ABI.


Thanks,

James

[0] http://lists.infradead.org/pipermail/linux-arm-kernel/2017-March/495861.html
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 11/11] KVM: arm64: Delegate support for SDEI to userspace
@ 2017-07-26 17:00           ` James Morse
  0 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-07-26 17:00 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Christoffer,

(looks like I forgot to send this ...)

On 06/06/17 20:58, Christoffer Dall wrote:
> On Mon, May 15, 2017 at 06:43:59PM +0100, James Morse wrote:
>> The Software Delegated Exception Interface allows firmware to notify
>> the OS of system events by returning into registered handlers, even
>> if the OS has interrupts masked.
>>
>> While we could support this in KVM, we would need to expose an API for
>> the user space hypervisor to inject events, (and decide what to do it
> 
> 'the user space hypervisor' ?

Qemu or kvmtool. I never know what generic term to use for these.
virtual-machine-monitor?


> s/it/if/
> 
>> the event isn't registered or all the CPUs have SDE events masked). We
>> already have an API for guest 'hypercalls', so use this to push the
>> problem onto userspace.
>>
>> Advertise a new capability 'KVM_CAP_ARM_SDEI_1_0' and when any SDEI
>> call comes in, exit to userspace with exit_reason = KVM_EXIT_HYPERCALL.

> Documentation/virtual/kvm/api.txt says this is unused.
> 
> We should add something there to say that this is now used for arm64,
> and the api doc also suggests that the hypercall struct in kvm_run has
> some meaningful data for this exit.

Yes, good point.

I was expecting this patch to provoke some wider discussion on how to delegate
SMCCC/HVC calls to user space. Do we want per-API KVM_CAP's, or one that dumps
the whole range on user-space when enabled. It came up (as a tangent) on another
thread:

Marc Zyngier wrote[0]:
> Eventually, we want to be able to handle the full spectrum of the SMCCC
> and forward things to an actual TEE if available. There is no real
> reason why PSCI shouldn't be handled in userspace the same way (and we
> already offload reset and halt to QEMU).


> Have we checked that the guest can't provoke QEMU to do something weird
> by causing this exit on arm64 currently (given that we always enable
> this handling of SDEI calls)?

Qemu 2.2.0 in ubuntu 15.04 ignores the 'sdei_version' hvc/hypercall-exit and
re-enters the guest with the registers unmodified. I think this is 'weird', I
assumed it would exit.


>> N.B. There is no enable/feature bit for SDEI exits as telling the guest
>> the interface exists via DT/ACPI should be sufficient.

I'm probably being too trusting here. Today an unknown HVC will cause KVM to
inject an undef, whereas with this change it might get handled by user-space if
the kernel recognises the range, and user-space might just skip the HVC and
carry on...

I will change this to support KVM_CAP_ENABLE_CAP_VM to enable the SDEI CAP and
pass that HVC range through to user-space using KVM_EXIT_HYPERCALL and
populating as much of that structure as makes sense...


>> diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c
>> index 3a776ec99181..0bf2d923483c 100644
>> --- a/virt/kvm/arm/arm.c
>> +++ b/virt/kvm/arm/arm.c
>> @@ -206,8 +206,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
>>  	case KVM_CAP_READONLY_MEM:
>>  	case KVM_CAP_MP_STATE:
>>  	case KVM_CAP_IMMEDIATE_EXIT:
>> -		r = 1;
>> -		break;
>> +#ifdef CONFIG_ARM_SDE_INTERFACE
>> +	case KVM_CAP_ARM_SDEI_1_0:
>> +#endif
> 
> What's the point of conditionally supporting this based on the config
> option when the rest of the KVM functionality does not depend on the
> CONFIG_ARM_SDE_INTERFACE functionality?

You're right it doesn't depend on anything in KVM, but adding it unconditionally
here will enable it on 32bit too, and the spec says this is aarch64 only. So
#ifdef ARM64 would have been better.


> Could a user want to play with SDEI calls in a VM without the host
> having the proper support, or is that never relevant?

That works fine (its how it was developed!).

'Virtual machine monitors' should be able to pick a RAS notification method for
guests independently of what the host is using (if anything). If this doesn't
work it means we've accidentally created some ABI.


Thanks,

James

[0] http://lists.infradead.org/pipermail/linux-arm-kernel/2017-March/495861.html

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

* Re: [PATCH 11/11] KVM: arm64: Delegate support for SDEI to userspace
  2017-07-26 17:00           ` James Morse
@ 2017-07-27  7:49               ` Christoffer Dall
  -1 siblings, 0 replies; 60+ messages in thread
From: Christoffer Dall @ 2017-07-27  7:49 UTC (permalink / raw)
  To: James Morse
  Cc: Christoffer Dall,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Will Deacon,
	Catalin Marinas, kvmarm-FPEHb7Xf0XXUo1n7N8X6UoWGPAHP3yOg,
	Marc Zyngier, Rob Herring, Mark Rutland,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Hi James,

On Wed, Jul 26, 2017 at 06:00:03PM +0100, James Morse wrote:
> Hi Christoffer,
> 
> (looks like I forgot to send this ...)
> 
> On 06/06/17 20:58, Christoffer Dall wrote:
> > On Mon, May 15, 2017 at 06:43:59PM +0100, James Morse wrote:
> >> The Software Delegated Exception Interface allows firmware to notify
> >> the OS of system events by returning into registered handlers, even
> >> if the OS has interrupts masked.
> >>
> >> While we could support this in KVM, we would need to expose an API for
> >> the user space hypervisor to inject events, (and decide what to do it
> > 
> > 'the user space hypervisor' ?
> 
> Qemu or kvmtool. I never know what generic term to use for these.
> virtual-machine-monitor?
> 

Ah, I also struggle with that aspect.  Here I was confused if you meant
QEMU TCG or something like that, and didn't quite understand the
connection.

I usually get away with saying simply user space, or the user space
driver (because user space drives KVM VMs), but I'm not aware of a fixed
unambiguous term.

VMM is probably not a good choice as most virt people think of
hypervisor==VMM.

> 
> > s/it/if/
> > 
> >> the event isn't registered or all the CPUs have SDE events masked). We
> >> already have an API for guest 'hypercalls', so use this to push the
> >> problem onto userspace.
> >>
> >> Advertise a new capability 'KVM_CAP_ARM_SDEI_1_0' and when any SDEI
> >> call comes in, exit to userspace with exit_reason = KVM_EXIT_HYPERCALL.
> 
> > Documentation/virtual/kvm/api.txt says this is unused.
> > 
> > We should add something there to say that this is now used for arm64,
> > and the api doc also suggests that the hypercall struct in kvm_run has
> > some meaningful data for this exit.
> 
> Yes, good point.
> 
> I was expecting this patch to provoke some wider discussion on how to delegate
> SMCCC/HVC calls to user space. Do we want per-API KVM_CAP's, or one that dumps
> the whole range on user-space when enabled. It came up (as a tangent) on another
> thread:
> 
> Marc Zyngier wrote[0]:
> > Eventually, we want to be able to handle the full spectrum of the SMCCC
> > and forward things to an actual TEE if available. There is no real
> > reason why PSCI shouldn't be handled in userspace the same way (and we
> > already offload reset and halt to QEMU).
> 

If implementing PSCI in userspace is not a big deal, then I lean towards
having a CAP and a feature, which simply moves all SMC/HVC calls to QEMU
and lets QEMU handle things.  On the other hand, if we ever want to
support known hypercalls that KVM must service directly, then we'd have
to split things up into different APIs for different types of calls.

If you need something short-term, I suspect only forwarding a limited
set of APIs to user space is the safest way to go, and we can always
include that with PSCI if moving everything to user space.


> 
> > Have we checked that the guest can't provoke QEMU to do something weird
> > by causing this exit on arm64 currently (given that we always enable
> > this handling of SDEI calls)?
> 
> Qemu 2.2.0 in ubuntu 15.04 ignores the 'sdei_version' hvc/hypercall-exit and
> re-enters the guest with the registers unmodified. I think this is 'weird', I
> assumed it would exit.
> 

IIRC, the arch-specific part of the QEMU run loop that calls into KVM,
specifically checks for the things it cares about on exit, and if it
doesn't see anything alarming, it just carries on.

It's a bit borderline to depend on this behavior, given that other
people could have modified QEMU versions or other user space drivers for
KVM deployed, but from a practical point of view, we'll probably be
ok...

> 
> >> N.B. There is no enable/feature bit for SDEI exits as telling the guest
> >> the interface exists via DT/ACPI should be sufficient.
> 
> I'm probably being too trusting here. Today an unknown HVC will cause KVM to
> inject an undef, whereas with this change it might get handled by user-space if
> the kernel recognises the range, and user-space might just skip the HVC and
> carry on...
> 
> I will change this to support KVM_CAP_ENABLE_CAP_VM to enable the SDEI CAP and
> pass that HVC range through to user-space using KVM_EXIT_HYPERCALL and
> populating as much of that structure as makes sense...
> 

I think the key is that the feature is only allowed if user space tells
KVM to notify it, because then we can assume user space also knows how
to deal with the exit code, so sounds good.

> 
> >> diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c
> >> index 3a776ec99181..0bf2d923483c 100644
> >> --- a/virt/kvm/arm/arm.c
> >> +++ b/virt/kvm/arm/arm.c
> >> @@ -206,8 +206,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
> >>  	case KVM_CAP_READONLY_MEM:
> >>  	case KVM_CAP_MP_STATE:
> >>  	case KVM_CAP_IMMEDIATE_EXIT:
> >> -		r = 1;
> >> -		break;
> >> +#ifdef CONFIG_ARM_SDE_INTERFACE
> >> +	case KVM_CAP_ARM_SDEI_1_0:
> >> +#endif
> > 
> > What's the point of conditionally supporting this based on the config
> > option when the rest of the KVM functionality does not depend on the
> > CONFIG_ARM_SDE_INTERFACE functionality?
> 
> You're right it doesn't depend on anything in KVM, but adding it unconditionally
> here will enable it on 32bit too, and the spec says this is aarch64 only. So
> #ifdef ARM64 would have been better.

Ah, I missed that.  Makes sense.

> 
> 
> > Could a user want to play with SDEI calls in a VM without the host
> > having the proper support, or is that never relevant?
> 
> That works fine (its how it was developed!).
> 
> 'Virtual machine monitors' should be able to pick a RAS notification method for
> guests independently of what the host is using (if anything). If this doesn't
> work it means we've accidentally created some ABI.
> 

Right, my point was if we wanted to support this in KVM even if the host
kernel didn't have CONFIG_ARM_SDE_INTERFACE, but it looks like we've
already addressed this.

Thanks,
-Christoffer
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 11/11] KVM: arm64: Delegate support for SDEI to userspace
@ 2017-07-27  7:49               ` Christoffer Dall
  0 siblings, 0 replies; 60+ messages in thread
From: Christoffer Dall @ 2017-07-27  7:49 UTC (permalink / raw)
  To: linux-arm-kernel

Hi James,

On Wed, Jul 26, 2017 at 06:00:03PM +0100, James Morse wrote:
> Hi Christoffer,
> 
> (looks like I forgot to send this ...)
> 
> On 06/06/17 20:58, Christoffer Dall wrote:
> > On Mon, May 15, 2017 at 06:43:59PM +0100, James Morse wrote:
> >> The Software Delegated Exception Interface allows firmware to notify
> >> the OS of system events by returning into registered handlers, even
> >> if the OS has interrupts masked.
> >>
> >> While we could support this in KVM, we would need to expose an API for
> >> the user space hypervisor to inject events, (and decide what to do it
> > 
> > 'the user space hypervisor' ?
> 
> Qemu or kvmtool. I never know what generic term to use for these.
> virtual-machine-monitor?
> 

Ah, I also struggle with that aspect.  Here I was confused if you meant
QEMU TCG or something like that, and didn't quite understand the
connection.

I usually get away with saying simply user space, or the user space
driver (because user space drives KVM VMs), but I'm not aware of a fixed
unambiguous term.

VMM is probably not a good choice as most virt people think of
hypervisor==VMM.

> 
> > s/it/if/
> > 
> >> the event isn't registered or all the CPUs have SDE events masked). We
> >> already have an API for guest 'hypercalls', so use this to push the
> >> problem onto userspace.
> >>
> >> Advertise a new capability 'KVM_CAP_ARM_SDEI_1_0' and when any SDEI
> >> call comes in, exit to userspace with exit_reason = KVM_EXIT_HYPERCALL.
> 
> > Documentation/virtual/kvm/api.txt says this is unused.
> > 
> > We should add something there to say that this is now used for arm64,
> > and the api doc also suggests that the hypercall struct in kvm_run has
> > some meaningful data for this exit.
> 
> Yes, good point.
> 
> I was expecting this patch to provoke some wider discussion on how to delegate
> SMCCC/HVC calls to user space. Do we want per-API KVM_CAP's, or one that dumps
> the whole range on user-space when enabled. It came up (as a tangent) on another
> thread:
> 
> Marc Zyngier wrote[0]:
> > Eventually, we want to be able to handle the full spectrum of the SMCCC
> > and forward things to an actual TEE if available. There is no real
> > reason why PSCI shouldn't be handled in userspace the same way (and we
> > already offload reset and halt to QEMU).
> 

If implementing PSCI in userspace is not a big deal, then I lean towards
having a CAP and a feature, which simply moves all SMC/HVC calls to QEMU
and lets QEMU handle things.  On the other hand, if we ever want to
support known hypercalls that KVM must service directly, then we'd have
to split things up into different APIs for different types of calls.

If you need something short-term, I suspect only forwarding a limited
set of APIs to user space is the safest way to go, and we can always
include that with PSCI if moving everything to user space.


> 
> > Have we checked that the guest can't provoke QEMU to do something weird
> > by causing this exit on arm64 currently (given that we always enable
> > this handling of SDEI calls)?
> 
> Qemu 2.2.0 in ubuntu 15.04 ignores the 'sdei_version' hvc/hypercall-exit and
> re-enters the guest with the registers unmodified. I think this is 'weird', I
> assumed it would exit.
> 

IIRC, the arch-specific part of the QEMU run loop that calls into KVM,
specifically checks for the things it cares about on exit, and if it
doesn't see anything alarming, it just carries on.

It's a bit borderline to depend on this behavior, given that other
people could have modified QEMU versions or other user space drivers for
KVM deployed, but from a practical point of view, we'll probably be
ok...

> 
> >> N.B. There is no enable/feature bit for SDEI exits as telling the guest
> >> the interface exists via DT/ACPI should be sufficient.
> 
> I'm probably being too trusting here. Today an unknown HVC will cause KVM to
> inject an undef, whereas with this change it might get handled by user-space if
> the kernel recognises the range, and user-space might just skip the HVC and
> carry on...
> 
> I will change this to support KVM_CAP_ENABLE_CAP_VM to enable the SDEI CAP and
> pass that HVC range through to user-space using KVM_EXIT_HYPERCALL and
> populating as much of that structure as makes sense...
> 

I think the key is that the feature is only allowed if user space tells
KVM to notify it, because then we can assume user space also knows how
to deal with the exit code, so sounds good.

> 
> >> diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c
> >> index 3a776ec99181..0bf2d923483c 100644
> >> --- a/virt/kvm/arm/arm.c
> >> +++ b/virt/kvm/arm/arm.c
> >> @@ -206,8 +206,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
> >>  	case KVM_CAP_READONLY_MEM:
> >>  	case KVM_CAP_MP_STATE:
> >>  	case KVM_CAP_IMMEDIATE_EXIT:
> >> -		r = 1;
> >> -		break;
> >> +#ifdef CONFIG_ARM_SDE_INTERFACE
> >> +	case KVM_CAP_ARM_SDEI_1_0:
> >> +#endif
> > 
> > What's the point of conditionally supporting this based on the config
> > option when the rest of the KVM functionality does not depend on the
> > CONFIG_ARM_SDE_INTERFACE functionality?
> 
> You're right it doesn't depend on anything in KVM, but adding it unconditionally
> here will enable it on 32bit too, and the spec says this is aarch64 only. So
> #ifdef ARM64 would have been better.

Ah, I missed that.  Makes sense.

> 
> 
> > Could a user want to play with SDEI calls in a VM without the host
> > having the proper support, or is that never relevant?
> 
> That works fine (its how it was developed!).
> 
> 'Virtual machine monitors' should be able to pick a RAS notification method for
> guests independently of what the host is using (if anything). If this doesn't
> work it means we've accidentally created some ABI.
> 

Right, my point was if we wanted to support this in KVM even if the host
kernel didn't have CONFIG_ARM_SDE_INTERFACE, but it looks like we've
already addressed this.

Thanks,
-Christoffer

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

* Re: [PATCH 07/11] firmware: arm_sdei: Add driver for Software Delegated Exceptions
  2017-07-19 13:52       ` Dave Martin
@ 2017-08-08 16:48           ` James Morse
  -1 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-08-08 16:48 UTC (permalink / raw)
  To: Dave Martin
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Rutland,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Marc Zyngier, Catalin Marinas,
	Will Deacon, Rob Herring,
	kvmarm-FPEHb7Xf0XXUo1n7N8X6UoWGPAHP3yOg, Christoffer Dall

Hi Dave,

On 19/07/17 14:52, Dave Martin wrote:
> On Mon, May 15, 2017 at 06:43:55PM +0100, James Morse wrote:
>> The Software Delegated Exception Interface (SDEI) is an ARM standard
>> for registering callbacks from the platform firmware into the OS.
>> This is typically used to implement RAS notifications.
>>
>> Add the code for detecting the SDEI version and the framework for
>> registering and unregistering events. Subsequent patches will add the
>> arch-specific backend code and the necessary power management hooks.
>>
>> Currently only shared events are supported.

>> diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c
>> new file mode 100644
>> index 000000000000..d22dda5e0fed
>> --- /dev/null
>> +++ b/drivers/firmware/arm_sdei.c
>> +int sdei_mask_local_cpu(unsigned int cpu)
>> +{
>> +	int err;
>> +	struct arm_smccc_res res;
>> +
>> +	WARN_ON(preemptible());
>> +	err = invoke_sdei_fn(&res, SDEI_1_0_FN_SDEI_PE_MASK, 0, 0, 0, 0, 0);

> It seems awkward to have to declare res repeatedly when it's basically
> unused.  Several callsites seem to do this.  Out of context, this looks
> bug-like (though it's not a bug).

After a quick trawl, only the 'qcom' and 'optee' callers use regs other than x0.


> Could we make it explicit that the results other than x0 are unwanted by
> passing NULL instead?

Looks straightforward, the quirk buffer has the same behaviour. I will put
something together.


> invoke_sdei_fn (or some downstream function) could declare its own res
> for this case, but at least we'd only have to do that in one place.
> arm_smccc_smc() and friends (at least the C interface in the headers)
> could do something similar.

Yes, for SDEI I can reduce this weirdness by moving smccc_res up a level.


Thanks,

James
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 07/11] firmware: arm_sdei: Add driver for Software Delegated Exceptions
@ 2017-08-08 16:48           ` James Morse
  0 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-08-08 16:48 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Dave,

On 19/07/17 14:52, Dave Martin wrote:
> On Mon, May 15, 2017 at 06:43:55PM +0100, James Morse wrote:
>> The Software Delegated Exception Interface (SDEI) is an ARM standard
>> for registering callbacks from the platform firmware into the OS.
>> This is typically used to implement RAS notifications.
>>
>> Add the code for detecting the SDEI version and the framework for
>> registering and unregistering events. Subsequent patches will add the
>> arch-specific backend code and the necessary power management hooks.
>>
>> Currently only shared events are supported.

>> diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c
>> new file mode 100644
>> index 000000000000..d22dda5e0fed
>> --- /dev/null
>> +++ b/drivers/firmware/arm_sdei.c
>> +int sdei_mask_local_cpu(unsigned int cpu)
>> +{
>> +	int err;
>> +	struct arm_smccc_res res;
>> +
>> +	WARN_ON(preemptible());
>> +	err = invoke_sdei_fn(&res, SDEI_1_0_FN_SDEI_PE_MASK, 0, 0, 0, 0, 0);

> It seems awkward to have to declare res repeatedly when it's basically
> unused.  Several callsites seem to do this.  Out of context, this looks
> bug-like (though it's not a bug).

After a quick trawl, only the 'qcom' and 'optee' callers use regs other than x0.


> Could we make it explicit that the results other than x0 are unwanted by
> passing NULL instead?

Looks straightforward, the quirk buffer has the same behaviour. I will put
something together.


> invoke_sdei_fn (or some downstream function) could declare its own res
> for this case, but at least we'd only have to do that in one place.
> arm_smccc_smc() and friends (at least the C interface in the headers)
> could do something similar.

Yes, for SDEI I can reduce this weirdness by moving smccc_res up a level.


Thanks,

James

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

* Re: [PATCH 01/11] KVM: arm64: Store vcpu on the stack during __guest_enter()
  2017-06-06 19:59       ` Christoffer Dall
@ 2017-08-08 16:48         ` James Morse
  -1 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-08-08 16:48 UTC (permalink / raw)
  To: Christoffer Dall
  Cc: devicetree, Marc Zyngier, Catalin Marinas, Will Deacon,
	Rob Herring, linux-arm-kernel, kvmarm

Hi Christoffer,

On 06/06/17 20:59, Christoffer Dall wrote:
> On Mon, May 15, 2017 at 06:43:49PM +0100, James Morse wrote:
>> KVM uses tpidr_el2 as its private vcpu register, which makes sense for
>> non-vhe world switch as only KVM can access this register. This means
>> vhe Linux has to use tpidr_el1, which KVM has to save/restore as part
>> of the host context.
>>
>> __guest_enter() stores the host_ctxt on the stack, do the same with
>> the vcpu.

>> diff --git a/arch/arm64/kvm/hyp/entry.S b/arch/arm64/kvm/hyp/entry.S
>> index 12ee62d6d410..113735df7d01 100644
>> --- a/arch/arm64/kvm/hyp/entry.S
>> +++ b/arch/arm64/kvm/hyp/entry.S
>> @@ -159,9 +159,15 @@ abort_guest_exit_end:
>>  ENDPROC(__guest_exit)
>>  
>>  ENTRY(__fpsimd_guest_restore)
>> +	// x0: esr
>> +	// x1: vcpu
>> +	// x2-x29,lr: vcpu regs
>> +	// vcpu x0-x1 on the stack
>>  	stp	x2, x3, [sp, #-16]!
>>  	stp	x4, lr, [sp, #-16]!
>>  
>> +	mov	x3, x1
>> +
> 
> nit: can you avoid this by using x1 for the vcpu pointer in this routine
> instead?

Unfortunately x1 is clobbered by the __fpsimd_{save,restore}_state() macros that
are called further down this function.

(its a bit obscure:
> fpsimd_save	x0, 1
that '1' is used to generate 'x1' or 'w1' in includes/asm/fpsimdmacros.h)



Thanks,

James

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

* [PATCH 01/11] KVM: arm64: Store vcpu on the stack during __guest_enter()
@ 2017-08-08 16:48         ` James Morse
  0 siblings, 0 replies; 60+ messages in thread
From: James Morse @ 2017-08-08 16:48 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Christoffer,

On 06/06/17 20:59, Christoffer Dall wrote:
> On Mon, May 15, 2017 at 06:43:49PM +0100, James Morse wrote:
>> KVM uses tpidr_el2 as its private vcpu register, which makes sense for
>> non-vhe world switch as only KVM can access this register. This means
>> vhe Linux has to use tpidr_el1, which KVM has to save/restore as part
>> of the host context.
>>
>> __guest_enter() stores the host_ctxt on the stack, do the same with
>> the vcpu.

>> diff --git a/arch/arm64/kvm/hyp/entry.S b/arch/arm64/kvm/hyp/entry.S
>> index 12ee62d6d410..113735df7d01 100644
>> --- a/arch/arm64/kvm/hyp/entry.S
>> +++ b/arch/arm64/kvm/hyp/entry.S
>> @@ -159,9 +159,15 @@ abort_guest_exit_end:
>>  ENDPROC(__guest_exit)
>>  
>>  ENTRY(__fpsimd_guest_restore)
>> +	// x0: esr
>> +	// x1: vcpu
>> +	// x2-x29,lr: vcpu regs
>> +	// vcpu x0-x1 on the stack
>>  	stp	x2, x3, [sp, #-16]!
>>  	stp	x4, lr, [sp, #-16]!
>>  
>> +	mov	x3, x1
>> +
> 
> nit: can you avoid this by using x1 for the vcpu pointer in this routine
> instead?

Unfortunately x1 is clobbered by the __fpsimd_{save,restore}_state() macros that
are called further down this function.

(its a bit obscure:
> fpsimd_save	x0, 1
that '1' is used to generate 'x1' or 'w1' in includes/asm/fpsimdmacros.h)



Thanks,

James

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

* Re: [PATCH 01/11] KVM: arm64: Store vcpu on the stack during __guest_enter()
  2017-08-08 16:48         ` James Morse
@ 2017-08-09  8:48             ` Christoffer Dall
  -1 siblings, 0 replies; 60+ messages in thread
From: Christoffer Dall @ 2017-08-09  8:48 UTC (permalink / raw)
  To: James Morse
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Will Deacon,
	Catalin Marinas, kvmarm-FPEHb7Xf0XXUo1n7N8X6UoWGPAHP3yOg,
	Marc Zyngier, Christoffer Dall, Rob Herring, Mark Rutland,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On Tue, Aug 08, 2017 at 05:48:29PM +0100, James Morse wrote:
> Hi Christoffer,
> 
> On 06/06/17 20:59, Christoffer Dall wrote:
> > On Mon, May 15, 2017 at 06:43:49PM +0100, James Morse wrote:
> >> KVM uses tpidr_el2 as its private vcpu register, which makes sense for
> >> non-vhe world switch as only KVM can access this register. This means
> >> vhe Linux has to use tpidr_el1, which KVM has to save/restore as part
> >> of the host context.
> >>
> >> __guest_enter() stores the host_ctxt on the stack, do the same with
> >> the vcpu.
> 
> >> diff --git a/arch/arm64/kvm/hyp/entry.S b/arch/arm64/kvm/hyp/entry.S
> >> index 12ee62d6d410..113735df7d01 100644
> >> --- a/arch/arm64/kvm/hyp/entry.S
> >> +++ b/arch/arm64/kvm/hyp/entry.S
> >> @@ -159,9 +159,15 @@ abort_guest_exit_end:
> >>  ENDPROC(__guest_exit)
> >>  
> >>  ENTRY(__fpsimd_guest_restore)
> >> +	// x0: esr
> >> +	// x1: vcpu
> >> +	// x2-x29,lr: vcpu regs
> >> +	// vcpu x0-x1 on the stack
> >>  	stp	x2, x3, [sp, #-16]!
> >>  	stp	x4, lr, [sp, #-16]!
> >>  
> >> +	mov	x3, x1
> >> +
> > 
> > nit: can you avoid this by using x1 for the vcpu pointer in this routine
> > instead?
> 
> Unfortunately x1 is clobbered by the __fpsimd_{save,restore}_state() macros that
> are called further down this function.
> 
> (its a bit obscure:
> > fpsimd_save	x0, 1
> that '1' is used to generate 'x1' or 'w1' in includes/asm/fpsimdmacros.h)
> 

Ah, I guess I missed that.

Thanks,
-Christoffer
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 01/11] KVM: arm64: Store vcpu on the stack during __guest_enter()
@ 2017-08-09  8:48             ` Christoffer Dall
  0 siblings, 0 replies; 60+ messages in thread
From: Christoffer Dall @ 2017-08-09  8:48 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Aug 08, 2017 at 05:48:29PM +0100, James Morse wrote:
> Hi Christoffer,
> 
> On 06/06/17 20:59, Christoffer Dall wrote:
> > On Mon, May 15, 2017 at 06:43:49PM +0100, James Morse wrote:
> >> KVM uses tpidr_el2 as its private vcpu register, which makes sense for
> >> non-vhe world switch as only KVM can access this register. This means
> >> vhe Linux has to use tpidr_el1, which KVM has to save/restore as part
> >> of the host context.
> >>
> >> __guest_enter() stores the host_ctxt on the stack, do the same with
> >> the vcpu.
> 
> >> diff --git a/arch/arm64/kvm/hyp/entry.S b/arch/arm64/kvm/hyp/entry.S
> >> index 12ee62d6d410..113735df7d01 100644
> >> --- a/arch/arm64/kvm/hyp/entry.S
> >> +++ b/arch/arm64/kvm/hyp/entry.S
> >> @@ -159,9 +159,15 @@ abort_guest_exit_end:
> >>  ENDPROC(__guest_exit)
> >>  
> >>  ENTRY(__fpsimd_guest_restore)
> >> +	// x0: esr
> >> +	// x1: vcpu
> >> +	// x2-x29,lr: vcpu regs
> >> +	// vcpu x0-x1 on the stack
> >>  	stp	x2, x3, [sp, #-16]!
> >>  	stp	x4, lr, [sp, #-16]!
> >>  
> >> +	mov	x3, x1
> >> +
> > 
> > nit: can you avoid this by using x1 for the vcpu pointer in this routine
> > instead?
> 
> Unfortunately x1 is clobbered by the __fpsimd_{save,restore}_state() macros that
> are called further down this function.
> 
> (its a bit obscure:
> > fpsimd_save	x0, 1
> that '1' is used to generate 'x1' or 'w1' in includes/asm/fpsimdmacros.h)
> 

Ah, I guess I missed that.

Thanks,
-Christoffer

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

end of thread, other threads:[~2017-08-09  8:48 UTC | newest]

Thread overview: 60+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-05-15 17:43 [PATCH 00/11] arm64/firmware: Software Delegated Exception Interface James Morse
2017-05-15 17:43 ` James Morse
2017-05-15 17:43 ` [PATCH 01/11] KVM: arm64: Store vcpu on the stack during __guest_enter() James Morse
2017-05-15 17:43   ` James Morse
     [not found]   ` <20170515174400.29735-2-james.morse-5wv7dgnIgG8@public.gmane.org>
2017-06-06 19:59     ` Christoffer Dall
2017-06-06 19:59       ` Christoffer Dall
2017-08-08 16:48       ` James Morse
2017-08-08 16:48         ` James Morse
     [not found]         ` <5989EB5D.6-5wv7dgnIgG8@public.gmane.org>
2017-08-09  8:48           ` Christoffer Dall
2017-08-09  8:48             ` Christoffer Dall
     [not found] ` <20170515174400.29735-1-james.morse-5wv7dgnIgG8@public.gmane.org>
2017-05-15 17:43   ` [PATCH 02/11] KVM: arm/arm64: Convert kvm_host_cpu_state to a static per-cpu allocation James Morse
2017-05-15 17:43     ` James Morse
     [not found]     ` <20170515174400.29735-3-james.morse-5wv7dgnIgG8@public.gmane.org>
2017-06-06 19:59       ` Christoffer Dall
2017-06-06 19:59         ` Christoffer Dall
2017-05-15 17:43   ` [PATCH 03/11] KVM: arm64: Change hyp_panic()s dependency on tpidr_el2 James Morse
2017-05-15 17:43     ` James Morse
2017-06-06 19:45     ` Christoffer Dall
2017-06-06 19:45       ` Christoffer Dall
2017-06-08 10:23       ` James Morse
2017-06-08 10:23         ` James Morse
     [not found]         ` <593925BB.30503-5wv7dgnIgG8@public.gmane.org>
2017-06-08 10:34           ` Christoffer Dall
2017-06-08 10:34             ` Christoffer Dall
2017-05-15 17:43   ` [PATCH 04/11] arm64: alternatives: use tpidr_el2 on VHE hosts James Morse
2017-05-15 17:43     ` James Morse
2017-05-15 17:43   ` [PATCH 06/11] dt-bindings: add devicetree binding for describing arm64 SDEI firmware James Morse
2017-05-15 17:43     ` James Morse
2017-05-19  1:48     ` Rob Herring
2017-05-19  1:48       ` Rob Herring
2017-06-07  8:28       ` James Morse
2017-06-07  8:28         ` James Morse
2017-05-15 17:43   ` [PATCH 08/11] arm64: kernel: Add arch-specific SDEI entry code and CPU masking James Morse
2017-05-15 17:43     ` James Morse
2017-05-15 17:43   ` [PATCH 09/11] firmware: arm_sdei: Add support for CPU and system power states James Morse
2017-05-15 17:43     ` James Morse
2017-05-15 17:43   ` [PATCH 10/11] firmware: arm_sdei: add support for CPU private events James Morse
2017-05-15 17:43     ` James Morse
2017-05-15 17:43   ` [PATCH 11/11] KVM: arm64: Delegate support for SDEI to userspace James Morse
2017-05-15 17:43     ` James Morse
     [not found]     ` <20170515174400.29735-12-james.morse-5wv7dgnIgG8@public.gmane.org>
2017-06-06 19:58       ` Christoffer Dall
2017-06-06 19:58         ` Christoffer Dall
2017-07-26 17:00         ` James Morse
2017-07-26 17:00           ` James Morse
     [not found]           ` <5978CA93.5090600-5wv7dgnIgG8@public.gmane.org>
2017-07-27  7:49             ` Christoffer Dall
2017-07-27  7:49               ` Christoffer Dall
2017-06-06 19:59   ` [PATCH 00/11] arm64/firmware: Software Delegated Exception Interface Christoffer Dall
2017-06-06 19:59     ` Christoffer Dall
2017-06-07  9:45     ` James Morse
2017-06-07  9:45       ` James Morse
2017-06-07  9:53       ` Christoffer Dall
2017-06-07  9:53         ` Christoffer Dall
2017-05-15 17:43 ` [PATCH 05/11] arm64: KVM: Stop save/restoring host tpidr_el1 on VHE James Morse
2017-05-15 17:43   ` James Morse
     [not found]   ` <20170515174400.29735-6-james.morse-5wv7dgnIgG8@public.gmane.org>
2017-06-06 20:00     ` Christoffer Dall
2017-06-06 20:00       ` Christoffer Dall
2017-05-15 17:43 ` [PATCH 07/11] firmware: arm_sdei: Add driver for Software Delegated Exceptions James Morse
2017-05-15 17:43   ` James Morse
     [not found]   ` <20170515174400.29735-8-james.morse-5wv7dgnIgG8@public.gmane.org>
2017-07-19 13:52     ` Dave Martin
2017-07-19 13:52       ` Dave Martin
     [not found]       ` <20170719135213.GA1538-M5GwZQ6tE7x5pKCnmE3YQBJ8xKzm50AiAL8bYrjMMd8@public.gmane.org>
2017-08-08 16:48         ` James Morse
2017-08-08 16:48           ` James Morse

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.