Re: [PATCH v11 01/13] vfio: VFIO_IOMMU_SET_PASID_TABLE

2020-11-16 Thread kernel test robot
Hi Eric,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on v5.10-rc4]
[also build test ERROR on next-20201116]
[cannot apply to vfio/next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:
https://github.com/0day-ci/linux/commits/Eric-Auger/SMMUv3-Nested-Stage-Setup-VFIO-part/20201116-190742
base:09162bc32c880a791c6c0668ce0745cf7958f576
config: x86_64-randconfig-s022-20201115 (attached as .config)
compiler: gcc-9 (Debian 9.3.0-15) 9.3.0
reproduce:
# apt-get install sparse
# sparse version: v0.6.3-107-gaf3512a6-dirty
# 
https://github.com/0day-ci/linux/commit/ce7c900f2d50c7e8cc62d54836369a3f3150adaf
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review 
Eric-Auger/SMMUv3-Nested-Stage-Setup-VFIO-part/20201116-190742
git checkout ce7c900f2d50c7e8cc62d54836369a3f3150adaf
# save the attached .config to linux build tree
make W=1 C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' ARCH=x86_64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot 

All errors (new ones prefixed by >>):

   In file included from :32:
>> ./usr/include/linux/vfio.h:1197:34: error: field 'config' has incomplete type
1197 |  struct iommu_pasid_table_config config; /* used on SET */
 |  ^~

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-...@lists.01.org


.config.gz
Description: application/gzip
___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v2 07/24] kvm: arm64: Refactor handle_trap to use a switch

2020-11-16 Thread David Brazdil
Small refactor so that nVHE's handle_trap uses a switch on the Exception
Class value of ESR_EL2 in preparation for adding a handler of SMC32/64.

Signed-off-by: David Brazdil 
---
 arch/arm64/kvm/hyp/nvhe/hyp-main.c | 15 ---
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c 
b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
index 411b0f652417..19332c20fcde 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
@@ -16,9 +16,9 @@
 
 DEFINE_PER_CPU(struct kvm_nvhe_init_params, kvm_init_params);
 
-static void handle_host_hcall(unsigned long func_id,
- struct kvm_cpu_context *host_ctxt)
+static void handle_host_hcall(struct kvm_cpu_context *host_ctxt)
 {
+   unsigned long func_id = host_ctxt->regs.regs[0];
unsigned long ret = 0;
 
switch (func_id) {
@@ -109,11 +109,12 @@ static void handle_host_hcall(unsigned long func_id,
 void handle_trap(struct kvm_cpu_context *host_ctxt)
 {
u64 esr = read_sysreg_el2(SYS_ESR);
-   unsigned long func_id;
 
-   if (ESR_ELx_EC(esr) != ESR_ELx_EC_HVC64)
+   switch (ESR_ELx_EC(esr)) {
+   case ESR_ELx_EC_HVC64:
+   handle_host_hcall(host_ctxt);
+   break;
+   default:
hyp_panic();
-
-   func_id = host_ctxt->regs.regs[0];
-   handle_host_hcall(func_id, host_ctxt);
+   }
 }
-- 
2.29.2.299.gdc1121823c-goog

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v2 12/24] kvm: arm64: Bootstrap PSCI SMC handler in nVHE EL2

2020-11-16 Thread David Brazdil
Add a handler of PSCI SMCs in nVHE hyp code. The handler is initialized
with the version used by the host's PSCI driver and the function IDs it
was configured with. If the SMC function ID matches one of the
configured PSCI calls (for v0.1) or falls into the PSCI function ID
range (for v0.2+), the SMC is handled by the PSCI handler. For now, all
SMCs return PSCI_RET_NOT_SUPPORTED.

Signed-off-by: David Brazdil 
---
 arch/arm64/include/asm/kvm_hyp.h |   4 ++
 arch/arm64/kvm/arm.c |  14 
 arch/arm64/kvm/hyp/nvhe/Makefile |   2 +-
 arch/arm64/kvm/hyp/nvhe/hyp-main.c   |   6 +-
 arch/arm64/kvm/hyp/nvhe/psci-relay.c | 104 +++
 5 files changed, 128 insertions(+), 2 deletions(-)
 create mode 100644 arch/arm64/kvm/hyp/nvhe/psci-relay.c

diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h
index a3289071f3d8..95a2bbbcc7e1 100644
--- a/arch/arm64/include/asm/kvm_hyp.h
+++ b/arch/arm64/include/asm/kvm_hyp.h
@@ -96,6 +96,10 @@ void deactivate_traps_vhe_put(void);
 
 u64 __guest_enter(struct kvm_vcpu *vcpu);
 
+#ifdef __KVM_NVHE_HYPERVISOR__
+bool kvm_host_psci_handler(struct kvm_cpu_context *host_ctxt);
+#endif
+
 void __noreturn hyp_panic(void);
 #ifdef __KVM_NVHE_HYPERVISOR__
 void __noreturn __hyp_do_panic(bool restore_host, u64 spsr, u64 elr, u64 par);
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index cdd7981ea560..7d2270eeecfb 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -19,6 +19,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 
 #define CREATE_TRACE_POINTS
@@ -1514,6 +1515,18 @@ static void init_cpu_logical_map(void)
CHOOSE_NVHE_SYM(__cpu_logical_map)[cpu] = cpu_logical_map(cpu);
 }
 
+static void init_psci_relay(void)
+{
+   extern u32 kvm_nvhe_sym(kvm_host_psci_version);
+   extern u32 kvm_nvhe_sym(kvm_host_psci_function_id)[PSCI_FN_MAX];
+   int i;
+
+   CHOOSE_NVHE_SYM(kvm_host_psci_version) = psci_ops.get_version
+   ? psci_ops.get_version() : PSCI_VERSION(0, 0);
+   for (i = 0; i < PSCI_FN_MAX; ++i)
+   CHOOSE_NVHE_SYM(kvm_host_psci_function_id)[i] = 
psci_get_function_id(i);
+}
+
 static int init_common_resources(void)
 {
return kvm_set_ipa_limit();
@@ -1693,6 +1706,7 @@ static int init_hyp_mode(void)
}
 
init_cpu_logical_map();
+   init_psci_relay();
 
return 0;
 
diff --git a/arch/arm64/kvm/hyp/nvhe/Makefile b/arch/arm64/kvm/hyp/nvhe/Makefile
index 2d842e009a40..bf62c8e42ab2 100644
--- a/arch/arm64/kvm/hyp/nvhe/Makefile
+++ b/arch/arm64/kvm/hyp/nvhe/Makefile
@@ -7,7 +7,7 @@ asflags-y := -D__KVM_NVHE_HYPERVISOR__
 ccflags-y := -D__KVM_NVHE_HYPERVISOR__
 
 obj-y := timer-sr.o sysreg-sr.o debug-sr.o switch.o tlb.o hyp-init.o host.o \
-hyp-main.o hyp-smp.o
+hyp-main.o hyp-smp.o psci-relay.o
 obj-y += ../vgic-v3-sr.o ../aarch32.o ../vgic-v2-cpuif-proxy.o ../entry.o \
 ../fpsimd.o ../hyp-entry.o
 
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c 
b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
index 71a17af05953..df4acb40dd39 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
@@ -120,7 +120,11 @@ static void skip_host_instruction(void)
 
 static void handle_host_smc(struct kvm_cpu_context *host_ctxt)
 {
-   default_host_smc_handler(host_ctxt);
+   bool handled;
+
+   handled = kvm_host_psci_handler(host_ctxt);
+   if (!handled)
+   default_host_smc_handler(host_ctxt);
 
/*
 * Unlike HVC, the return address of an SMC is the instruction's PC.
diff --git a/arch/arm64/kvm/hyp/nvhe/psci-relay.c 
b/arch/arm64/kvm/hyp/nvhe/psci-relay.c
new file mode 100644
index ..d75d3f896bfd
--- /dev/null
+++ b/arch/arm64/kvm/hyp/nvhe/psci-relay.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020 - Google LLC
+ * Author: David Brazdil 
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+/* Config options set by the host. */
+u32 __ro_after_init kvm_host_psci_version = PSCI_VERSION(0, 0);
+u32 __ro_after_init kvm_host_psci_function_id[PSCI_FN_MAX];
+
+static u64 get_psci_func_id(struct kvm_cpu_context *host_ctxt)
+{
+   return host_ctxt->regs.regs[0];
+}
+
+static bool is_psci_0_1_call(u64 func_id)
+{
+   unsigned int i;
+
+   for (i = 0; i < ARRAY_SIZE(kvm_host_psci_function_id); ++i) {
+   if (func_id == kvm_host_psci_function_id[i])
+   return true;
+   }
+   return false;
+}
+
+static bool is_psci_0_2_call(u64 func_id)
+{
+   /* SMCCC reserves IDs 0x00-1F with the given 32/64-bit base for PSCI. */
+   return (PSCI_0_2_FN(0) <= func_id && func_id <= PSCI_0_2_FN(31)) ||
+  (PSCI_0_2_FN64(0) <= func_id && func_id <= PSCI_0_2_FN64(31));
+}
+
+static bool is_psci_call(u64 func_id)
+{
+   switch (kvm_host_psci_version) {
+   case PSCI_VERSION(0, 0):
+   

[PATCH v2 19/24] kvm: arm64: Intercept host's PSCI_CPU_ON SMCs

2020-11-16 Thread David Brazdil
Add a handler of the CPU_ON PSCI call from host. When invoked, it looks
up the logical CPU ID corresponding to the provided MPIDR and populates
the state struct of the target CPU with the provided x0, pc. It then
calls CPU_ON itself, with an entry point in hyp that initializes EL2
state before returning ERET to the provided PC in EL1.

There is a simple atomic lock around the reset state struct. If it is
already locked, CPU_ON will return PENDING_ON error code.

Signed-off-by: David Brazdil 
---
 arch/arm64/include/asm/kvm_asm.h |   8 ++-
 arch/arm64/kvm/arm.c |   1 +
 arch/arm64/kvm/hyp/nvhe/psci-relay.c | 104 +++
 3 files changed, 110 insertions(+), 3 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h
index 109867fb76f6..2e36ba4be748 100644
--- a/arch/arm64/include/asm/kvm_asm.h
+++ b/arch/arm64/include/asm/kvm_asm.h
@@ -175,9 +175,11 @@ struct kvm_s2_mmu;
 DECLARE_KVM_NVHE_SYM(__kvm_hyp_init);
 DECLARE_KVM_NVHE_SYM(__kvm_hyp_host_vector);
 DECLARE_KVM_HYP_SYM(__kvm_hyp_vector);
-#define __kvm_hyp_init CHOOSE_NVHE_SYM(__kvm_hyp_init)
-#define __kvm_hyp_host_vector  CHOOSE_NVHE_SYM(__kvm_hyp_host_vector)
-#define __kvm_hyp_vector   CHOOSE_HYP_SYM(__kvm_hyp_vector)
+DECLARE_KVM_NVHE_SYM(__kvm_hyp_psci_cpu_entry);
+#define __kvm_hyp_init CHOOSE_NVHE_SYM(__kvm_hyp_init)
+#define __kvm_hyp_host_vector  CHOOSE_NVHE_SYM(__kvm_hyp_host_vector)
+#define __kvm_hyp_vector   CHOOSE_HYP_SYM(__kvm_hyp_vector)
+#define __kvm_hyp_psci_cpu_entry   
CHOOSE_NVHE_SYM(__kvm_hyp_psci_cpu_entry)
 
 extern unsigned long kvm_arm_hyp_percpu_base[NR_CPUS];
 DECLARE_KVM_NVHE_SYM(__per_cpu_start);
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 7d2270eeecfb..c76a8e5bd19c 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -1365,6 +1365,7 @@ static void cpu_init_hyp_mode(void)
 
params->vector_hyp_va = (unsigned 
long)kern_hyp_va(kvm_ksym_ref(__kvm_hyp_host_vector));
params->stack_hyp_va = 
kern_hyp_va(__this_cpu_read(kvm_arm_hyp_stack_page) + PAGE_SIZE);
+   params->entry_hyp_va = (unsigned 
long)kern_hyp_va(kvm_ksym_ref(__kvm_hyp_psci_cpu_entry));
params->pgd_pa = kvm_mmu_get_httbr();
 
/*
diff --git a/arch/arm64/kvm/hyp/nvhe/psci-relay.c 
b/arch/arm64/kvm/hyp/nvhe/psci-relay.c
index 7542de8bd679..2daf52b59846 100644
--- a/arch/arm64/kvm/hyp/nvhe/psci-relay.c
+++ b/arch/arm64/kvm/hyp/nvhe/psci-relay.c
@@ -9,10 +9,15 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
 
+#define INVALID_CPU_ID UINT_MAX
+
+extern char __kvm_hyp_cpu_entry[];
+
 /* Config options set by the host. */
 u32 __ro_after_init kvm_host_psci_version = PSCI_VERSION(0, 0);
 u32 __ro_after_init kvm_host_psci_function_id[PSCI_FN_MAX];
@@ -20,6 +25,14 @@ s64 __ro_after_init hyp_physvirt_offset;
 
 #define __hyp_pa(x) ((phys_addr_t)((x)) + hyp_physvirt_offset)
 
+struct kvm_host_psci_state {
+   atomic_t pending_on;
+   unsigned long pc;
+   unsigned long r0;
+};
+
+static DEFINE_PER_CPU(struct kvm_host_psci_state, kvm_host_psci_state);
+
 static u64 get_psci_func_id(struct kvm_cpu_context *host_ctxt)
 {
return host_ctxt->regs.regs[0];
@@ -76,10 +89,99 @@ static __noreturn unsigned long 
psci_forward_noreturn(struct kvm_cpu_context *ho
hyp_panic(); /* unreachable */
 }
 
+static unsigned int find_cpu_id(u64 mpidr)
+{
+   int i;
+
+   if (mpidr != INVALID_HWID) {
+   for (i = 0; i < NR_CPUS; i++) {
+   if (cpu_logical_map(i) == mpidr)
+   return i;
+   }
+   }
+
+   return INVALID_CPU_ID;
+}
+
+static bool try_acquire_reset_state(struct kvm_host_psci_state *cpu_state,
+   unsigned long pc, unsigned long r0)
+{
+   if (atomic_cmpxchg_acquire(&cpu_state->pending_on, 0, 1) != 0)
+   return false;
+
+   cpu_state->pc = pc;
+   cpu_state->r0 = r0;
+   wmb();
+
+   return true;
+}
+
+static void release_reset_state(struct kvm_host_psci_state *cpu_state)
+{
+   atomic_set_release(&cpu_state->pending_on, 0);
+}
+
+static int psci_cpu_on(u64 func_id, struct kvm_cpu_context *host_ctxt)
+{
+   u64 mpidr = host_ctxt->regs.regs[1];
+   unsigned long pc = host_ctxt->regs.regs[2];
+   unsigned long r0 = host_ctxt->regs.regs[3];
+   unsigned int cpu_id;
+   struct kvm_host_psci_state *cpu_state;
+   struct kvm_nvhe_init_params *cpu_params;
+   int ret;
+
+   /*
+* Find the logical CPU ID for the given MPIDR. The search set is
+* the set of CPUs that were online at the point of KVM initialization.
+* Booting other CPUs is rejected because their cpufeatures were not
+* checked against the finalized capabilities. This could be relaxed
+* by doing the feature checks in hyp.
+*/
+   cpu_id = find_c

[PATCH v2 22/24] kvm: arm64: Keep nVHE EL2 vector installed

2020-11-16 Thread David Brazdil
KVM by default keeps the stub vector installed and installs the nVHE
vector only briefly for init and later on demand. Change this policy
to install the vector at init and then never uninstall it if the kernel
was given the protected KVM command line parameter.

Signed-off-by: David Brazdil 
---
 arch/arm64/kvm/arm.c | 12 
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 49d2474f2a80..45bc7a6b9e0b 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -1443,7 +1443,8 @@ static void _kvm_arch_hardware_disable(void *discard)
 
 void kvm_arch_hardware_disable(void)
 {
-   _kvm_arch_hardware_disable(NULL);
+   if (!is_protected_kvm_enabled())
+   _kvm_arch_hardware_disable(NULL);
 }
 
 #ifdef CONFIG_CPU_PM
@@ -1486,11 +1487,13 @@ static struct notifier_block hyp_init_cpu_pm_nb = {
 
 static void __init hyp_cpu_pm_init(void)
 {
-   cpu_pm_register_notifier(&hyp_init_cpu_pm_nb);
+   if (!is_protected_kvm_enabled())
+   cpu_pm_register_notifier(&hyp_init_cpu_pm_nb);
 }
 static void __init hyp_cpu_pm_exit(void)
 {
-   cpu_pm_unregister_notifier(&hyp_init_cpu_pm_nb);
+   if (!is_protected_kvm_enabled())
+   cpu_pm_unregister_notifier(&hyp_init_cpu_pm_nb);
 }
 #else
 static inline void hyp_cpu_pm_init(void)
@@ -1575,7 +1578,8 @@ static int init_subsystems(void)
kvm_coproc_table_init();
 
 out:
-   on_each_cpu(_kvm_arch_hardware_disable, NULL, 1);
+   if (err || !is_protected_kvm_enabled())
+   on_each_cpu(_kvm_arch_hardware_disable, NULL, 1);
 
return err;
 }
-- 
2.29.2.299.gdc1121823c-goog

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v2 10/24] kvm: arm64: Support per_cpu_ptr in nVHE hyp code

2020-11-16 Thread David Brazdil
When compiling with __KVM_NVHE_HYPERVISOR__ redefine per_cpu_offset() to
__hyp_per_cpu_offset() which looks up the base of the nVHE per-CPU
region of the given cpu and computes its offset from the
.hyp.data..percpu section.

This enables use of per_cpu_ptr() helpers in nVHE hyp code. Until now
only this_cpu_ptr() was supported by setting TPIDR_EL2.

Signed-off-by: David Brazdil 
---
 arch/arm64/include/asm/percpu.h   |  6 ++
 arch/arm64/kernel/image-vars.h|  3 +++
 arch/arm64/kvm/hyp/nvhe/Makefile  |  3 ++-
 arch/arm64/kvm/hyp/nvhe/hyp-smp.c | 24 
 4 files changed, 35 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm64/kvm/hyp/nvhe/hyp-smp.c

diff --git a/arch/arm64/include/asm/percpu.h b/arch/arm64/include/asm/percpu.h
index 1599e17379d8..8f1661603b78 100644
--- a/arch/arm64/include/asm/percpu.h
+++ b/arch/arm64/include/asm/percpu.h
@@ -239,6 +239,12 @@ PERCPU_RET_OP(add, add, ldadd)
 #define this_cpu_cmpxchg_8(pcp, o, n)  \
_pcp_protect_return(cmpxchg_relaxed, pcp, o, n)
 
+#ifdef __KVM_NVHE_HYPERVISOR__
+extern unsigned long __hyp_per_cpu_offset(unsigned int cpu);
+#define __per_cpu_offset
+#define per_cpu_offset(cpu)__hyp_per_cpu_offset((cpu))
+#endif
+
 #include 
 
 /* Redefine macros for nVHE hyp under DEBUG_PREEMPT to avoid its dependencies. 
*/
diff --git a/arch/arm64/kernel/image-vars.h b/arch/arm64/kernel/image-vars.h
index c615b285ff5b..78a42a7cdb72 100644
--- a/arch/arm64/kernel/image-vars.h
+++ b/arch/arm64/kernel/image-vars.h
@@ -103,6 +103,9 @@ KVM_NVHE_ALIAS(gic_nonsecure_priorities);
 KVM_NVHE_ALIAS(__start___kvm_ex_table);
 KVM_NVHE_ALIAS(__stop___kvm_ex_table);
 
+/* Array containing bases of nVHE per-CPU memory regions. */
+KVM_NVHE_ALIAS(kvm_arm_hyp_percpu_base);
+
 #endif /* CONFIG_KVM */
 
 #endif /* __ARM64_KERNEL_IMAGE_VARS_H */
diff --git a/arch/arm64/kvm/hyp/nvhe/Makefile b/arch/arm64/kvm/hyp/nvhe/Makefile
index ddde15fe85f2..2d842e009a40 100644
--- a/arch/arm64/kvm/hyp/nvhe/Makefile
+++ b/arch/arm64/kvm/hyp/nvhe/Makefile
@@ -6,7 +6,8 @@
 asflags-y := -D__KVM_NVHE_HYPERVISOR__
 ccflags-y := -D__KVM_NVHE_HYPERVISOR__
 
-obj-y := timer-sr.o sysreg-sr.o debug-sr.o switch.o tlb.o hyp-init.o host.o 
hyp-main.o
+obj-y := timer-sr.o sysreg-sr.o debug-sr.o switch.o tlb.o hyp-init.o host.o \
+hyp-main.o hyp-smp.o
 obj-y += ../vgic-v3-sr.o ../aarch32.o ../vgic-v2-cpuif-proxy.o ../entry.o \
 ../fpsimd.o ../hyp-entry.o
 
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-smp.c 
b/arch/arm64/kvm/hyp/nvhe/hyp-smp.c
new file mode 100644
index ..7b0363b4857f
--- /dev/null
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-smp.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020 - Google LLC
+ * Author: David Brazdil 
+ */
+
+#include 
+#include 
+#include 
+
+unsigned long __hyp_per_cpu_offset(unsigned int cpu)
+{
+   unsigned long *cpu_base_array;
+   unsigned long this_cpu_base;
+   unsigned long elf_base;
+
+   if (cpu >= ARRAY_SIZE(kvm_arm_hyp_percpu_base))
+   hyp_panic();
+
+   cpu_base_array = (unsigned long 
*)hyp_symbol_addr(kvm_arm_hyp_percpu_base);
+   this_cpu_base = kern_hyp_va(cpu_base_array[cpu]);
+   elf_base = (unsigned long)hyp_symbol_addr(__per_cpu_start);
+   return this_cpu_base - elf_base;
+}
-- 
2.29.2.299.gdc1121823c-goog

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v2 09/24] kvm: arm64: Add .hyp.data..ro_after_init ELF section

2020-11-16 Thread David Brazdil
Add rules for renaming the .data..ro_after_init ELF section in KVM nVHE
object files to .hyp.data..ro_after_init, linking it into the kernel
and mapping it in hyp at runtime.

The section is RW to the host, then mapped RO in hyp. The expectation is
that the host populates the variables in the section and they are never
changed by hyp afterwards.

Signed-off-by: David Brazdil 
---
 arch/arm64/include/asm/sections.h |  1 +
 arch/arm64/kernel/vmlinux.lds.S   | 10 ++
 arch/arm64/kvm/arm.c  |  8 
 arch/arm64/kvm/hyp/nvhe/hyp.lds.S |  1 +
 4 files changed, 20 insertions(+)

diff --git a/arch/arm64/include/asm/sections.h 
b/arch/arm64/include/asm/sections.h
index 3994169985ef..8ff579361731 100644
--- a/arch/arm64/include/asm/sections.h
+++ b/arch/arm64/include/asm/sections.h
@@ -11,6 +11,7 @@ extern char __alt_instructions[], __alt_instructions_end[];
 extern char __hibernate_exit_text_start[], __hibernate_exit_text_end[];
 extern char __hyp_idmap_text_start[], __hyp_idmap_text_end[];
 extern char __hyp_text_start[], __hyp_text_end[];
+extern char __hyp_data_ro_after_init_start[], __hyp_data_ro_after_init_end[];
 extern char __idmap_text_start[], __idmap_text_end[];
 extern char __initdata_begin[], __initdata_end[];
 extern char __inittext_begin[], __inittext_end[];
diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S
index 1bda604f4c70..4382b5d0645d 100644
--- a/arch/arm64/kernel/vmlinux.lds.S
+++ b/arch/arm64/kernel/vmlinux.lds.S
@@ -30,6 +30,13 @@ jiffies = jiffies_64;
*(__kvm_ex_table)   \
__stop___kvm_ex_table = .;
 
+#define HYPERVISOR_DATA_SECTIONS   \
+   HYP_SECTION_NAME(.data..ro_after_init) : {  \
+   __hyp_data_ro_after_init_start = .; \
+   *(HYP_SECTION_NAME(.data..ro_after_init))   \
+   __hyp_data_ro_after_init_end = .;   \
+   }
+
 #define HYPERVISOR_PERCPU_SECTION  \
. = ALIGN(PAGE_SIZE);   \
HYP_SECTION_NAME(.data..percpu) : { \
@@ -37,6 +44,7 @@ jiffies = jiffies_64;
}
 #else /* CONFIG_KVM */
 #define HYPERVISOR_EXTABLE
+#define HYPERVISOR_DATA_SECTIONS
 #define HYPERVISOR_PERCPU_SECTION
 #endif
 
@@ -234,6 +242,8 @@ SECTIONS
_sdata = .;
RW_DATA(L1_CACHE_BYTES, PAGE_SIZE, THREAD_ALIGN)
 
+   HYPERVISOR_DATA_SECTIONS
+
/*
 * Data written with the MMU off but read with the MMU on requires
 * cache lines to be invalidated, discarding up to a Cache Writeback
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 4838556920fb..97af6c285f84 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -1620,6 +1620,14 @@ static int init_hyp_mode(void)
goto out_err;
}
 
+   err = create_hyp_mappings(kvm_ksym_ref(__hyp_data_ro_after_init_start),
+ kvm_ksym_ref(__hyp_data_ro_after_init_end),
+ PAGE_HYP_RO);
+   if (err) {
+   kvm_err("Cannot map .hyp.data..ro_after_init section\n");
+   goto out_err;
+   }
+
err = create_hyp_mappings(kvm_ksym_ref(__start_rodata),
  kvm_ksym_ref(__end_rodata), PAGE_HYP_RO);
if (err) {
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp.lds.S 
b/arch/arm64/kvm/hyp/nvhe/hyp.lds.S
index bb2d986ff696..5d76ff2ba63e 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp.lds.S
+++ b/arch/arm64/kvm/hyp/nvhe/hyp.lds.S
@@ -16,4 +16,5 @@ SECTIONS {
HYP_SECTION_NAME(.data..percpu) : {
PERCPU_INPUT(L1_CACHE_BYTES)
}
+   HYP_SECTION(.data..ro_after_init)
 }
-- 
2.29.2.299.gdc1121823c-goog

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v2 20/24] kvm: arm64: Intercept host's CPU_SUSPEND PSCI SMCs

2020-11-16 Thread David Brazdil
Add a handler of CPU_SUSPEND host PSCI SMCs. The SMC can either enter
a sleep state indistinguishable from a WFI or a deeper sleep state that
behaves like a CPU_OFF+CPU_ON.

The handler saves r0,pc of the host and makes the same call to EL3 with
the hyp CPU entry point. It either returns back to the handler and then
back to the host, or wakes up into the entry point and initializes EL2
state before dropping back to EL1.

There is a simple atomic lock around the reset state struct to protect
from races with CPU_ON. A well-behaved host should never run CPU_ON
against an already online core, and the kernel indeed does not allow
that, so if the core sees its reset state struct locked, it will return
a non-spec error code PENDING_ON. This protects the hypervisor state and
avoids the need for more complicated locking and/or tracking power state
of individual cores.

Signed-off-by: David Brazdil 
---
 arch/arm64/kvm/hyp/nvhe/psci-relay.c | 39 +++-
 1 file changed, 38 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/kvm/hyp/nvhe/psci-relay.c 
b/arch/arm64/kvm/hyp/nvhe/psci-relay.c
index 2daf52b59846..313ef42f0eab 100644
--- a/arch/arm64/kvm/hyp/nvhe/psci-relay.c
+++ b/arch/arm64/kvm/hyp/nvhe/psci-relay.c
@@ -121,6 +121,39 @@ static void release_reset_state(struct kvm_host_psci_state 
*cpu_state)
atomic_set_release(&cpu_state->pending_on, 0);
 }
 
+static int psci_cpu_suspend(u64 func_id, struct kvm_cpu_context *host_ctxt)
+{
+   u64 power_state = host_ctxt->regs.regs[1];
+   unsigned long pc = host_ctxt->regs.regs[2];
+   unsigned long r0 = host_ctxt->regs.regs[3];
+   struct kvm_host_psci_state *cpu_state;
+   struct kvm_nvhe_init_params *cpu_params;
+   int ret;
+
+   cpu_state = this_cpu_ptr(&kvm_host_psci_state);
+   cpu_params = this_cpu_ptr(&kvm_init_params);
+
+   /*
+* Lock the reset state struct. This fails if the host has concurrently
+* called CPU_ON with this CPU as target. The kernel keeps track of
+* online CPUs, so that should never happen. If it does anyway, return
+* a non-spec error. This avoids the need for spinlocks.
+*/
+   if (!try_acquire_reset_state(cpu_state, pc, r0))
+   return PSCI_RET_ALREADY_ON;
+
+   /*
+* Will either return if shallow sleep state, or wake up into the entry
+* point if it is a deep sleep state.
+*/
+   ret = psci_call(func_id, power_state,
+   __hyp_pa(hyp_symbol_addr(__kvm_hyp_cpu_entry)),
+   __hyp_pa(cpu_params));
+
+   release_reset_state(cpu_state);
+   return ret;
+}
+
 static int psci_cpu_on(u64 func_id, struct kvm_cpu_context *host_ctxt)
 {
u64 mpidr = host_ctxt->regs.regs[1];
@@ -178,7 +211,9 @@ asmlinkage void __noreturn __kvm_hyp_psci_cpu_entry(void)
 
 static unsigned long psci_0_1_handler(u64 func_id, struct kvm_cpu_context 
*host_ctxt)
 {
-   if (func_id == kvm_host_psci_function_id[PSCI_FN_CPU_OFF])
+   if (func_id == kvm_host_psci_function_id[PSCI_FN_CPU_SUSPEND])
+   return psci_cpu_suspend(func_id, host_ctxt);
+   else if (func_id == kvm_host_psci_function_id[PSCI_FN_CPU_OFF])
return psci_forward(host_ctxt);
else if (func_id == kvm_host_psci_function_id[PSCI_FN_CPU_ON])
return psci_cpu_on(func_id, host_ctxt);
@@ -202,6 +237,8 @@ static unsigned long psci_0_2_handler(u64 func_id, struct 
kvm_cpu_context *host_
case PSCI_0_2_FN_SYSTEM_RESET:
psci_forward_noreturn(host_ctxt);
unreachable();
+   case PSCI_0_2_FN64_CPU_SUSPEND:
+   return psci_cpu_suspend(func_id, host_ctxt);
case PSCI_0_2_FN64_CPU_ON:
return psci_cpu_on(func_id, host_ctxt);
default:
-- 
2.29.2.299.gdc1121823c-goog

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v2 08/24] kvm: arm64: Add SMC handler in nVHE EL2

2020-11-16 Thread David Brazdil
Add handler of host SMCs in KVM nVHE trap handler. Forward all SMCs to
EL3 and propagate the result back to EL1. This is done in preparation
for validating host SMCs in KVM nVHE protected mode.

The implementation assumes that firmware uses SMCCC v1.2 or older. That
means x0-x17 can be used both for arguments and results, other GPRs are
preserved.

Signed-off-by: David Brazdil 
---
 arch/arm64/kvm/hyp/nvhe/host.S | 38 ++
 arch/arm64/kvm/hyp/nvhe/hyp-main.c | 26 
 2 files changed, 64 insertions(+)

diff --git a/arch/arm64/kvm/hyp/nvhe/host.S b/arch/arm64/kvm/hyp/nvhe/host.S
index ed27f06a31ba..52dae5cd5a28 100644
--- a/arch/arm64/kvm/hyp/nvhe/host.S
+++ b/arch/arm64/kvm/hyp/nvhe/host.S
@@ -183,3 +183,41 @@ SYM_CODE_START(__kvm_hyp_host_vector)
invalid_host_el1_vect   // FIQ 32-bit EL1
invalid_host_el1_vect   // Error 32-bit EL1
 SYM_CODE_END(__kvm_hyp_host_vector)
+
+/*
+ * Forward SMC with arguments in struct kvm_cpu_context, and
+ * store the result into the same struct. Assumes SMCCC 1.2 or older.
+ *
+ * x0: struct kvm_cpu_context*
+ */
+SYM_CODE_START(__kvm_hyp_host_forward_smc)
+   /*
+* Use x18 to keep a pointer to the host context because x18
+* is callee-saved SMCCC but not in AAPCS64.
+*/
+   mov x18, x0
+
+   ldp x0, x1,   [x18, #CPU_XREG_OFFSET(0)]
+   ldp x2, x3,   [x18, #CPU_XREG_OFFSET(2)]
+   ldp x4, x5,   [x18, #CPU_XREG_OFFSET(4)]
+   ldp x6, x7,   [x18, #CPU_XREG_OFFSET(6)]
+   ldp x8, x9,   [x18, #CPU_XREG_OFFSET(8)]
+   ldp x10, x11, [x18, #CPU_XREG_OFFSET(10)]
+   ldp x12, x13, [x18, #CPU_XREG_OFFSET(12)]
+   ldp x14, x15, [x18, #CPU_XREG_OFFSET(14)]
+   ldp x16, x17, [x18, #CPU_XREG_OFFSET(16)]
+
+   smc #0
+
+   stp x0, x1,   [x18, #CPU_XREG_OFFSET(0)]
+   stp x2, x3,   [x18, #CPU_XREG_OFFSET(2)]
+   stp x4, x5,   [x18, #CPU_XREG_OFFSET(4)]
+   stp x6, x7,   [x18, #CPU_XREG_OFFSET(6)]
+   stp x8, x9,   [x18, #CPU_XREG_OFFSET(8)]
+   stp x10, x11, [x18, #CPU_XREG_OFFSET(10)]
+   stp x12, x13, [x18, #CPU_XREG_OFFSET(12)]
+   stp x14, x15, [x18, #CPU_XREG_OFFSET(14)]
+   stp x16, x17, [x18, #CPU_XREG_OFFSET(16)]
+
+   ret
+SYM_CODE_END(__kvm_hyp_host_forward_smc)
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c 
b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
index 19332c20fcde..71a17af05953 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
@@ -16,6 +16,8 @@
 
 DEFINE_PER_CPU(struct kvm_nvhe_init_params, kvm_init_params);
 
+extern void __kvm_hyp_host_forward_smc(struct kvm_cpu_context *host_ctxt);
+
 static void handle_host_hcall(struct kvm_cpu_context *host_ctxt)
 {
unsigned long func_id = host_ctxt->regs.regs[0];
@@ -106,6 +108,27 @@ static void handle_host_hcall(struct kvm_cpu_context 
*host_ctxt)
host_ctxt->regs.regs[1] = ret;
 }
 
+static void default_host_smc_handler(struct kvm_cpu_context *host_ctxt)
+{
+   __kvm_hyp_host_forward_smc(host_ctxt);
+}
+
+static void skip_host_instruction(void)
+{
+   write_sysreg_el2(read_sysreg_el2(SYS_ELR) + 4, SYS_ELR);
+}
+
+static void handle_host_smc(struct kvm_cpu_context *host_ctxt)
+{
+   default_host_smc_handler(host_ctxt);
+
+   /*
+* Unlike HVC, the return address of an SMC is the instruction's PC.
+* Move the return address past the instruction.
+*/
+   skip_host_instruction();
+}
+
 void handle_trap(struct kvm_cpu_context *host_ctxt)
 {
u64 esr = read_sysreg_el2(SYS_ESR);
@@ -114,6 +137,9 @@ void handle_trap(struct kvm_cpu_context *host_ctxt)
case ESR_ELx_EC_HVC64:
handle_host_hcall(host_ctxt);
break;
+   case ESR_ELx_EC_SMC64:
+   handle_host_smc(host_ctxt);
+   break;
default:
hyp_panic();
}
-- 
2.29.2.299.gdc1121823c-goog

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v2 24/24] kvm: arm64: Fix EL2 mode availability checks

2020-11-16 Thread David Brazdil
With protected nVHE hyp code interception host's PSCI CPU_ON/SUSPEND
SMCs, the host starts seeing new CPUs boot in EL1 instead of EL2. The
kernel logic that keeps track of the boot mode needs to be adjusted.

Add a static key enabled if KVM protected nVHE initialization is
successful.

When the key is enabled, is_hyp_mode_available continues to report
`true` because its users either treat it as a check whether KVM will be
/ was initialized, or whether stub HVCs can be made (eg. hibernate).

is_hyp_mode_mismatched is changed to report `false` when the key is
enabled. That's because all cores' modes matched at the point of KVM
init and KVM will not allow cores not present at init to boot. That
said, the function is never used after KVM is initialized.

Signed-off-by: David Brazdil 
---
 arch/arm64/include/asm/virt.h | 18 ++
 arch/arm64/kvm/arm.c  | 10 +++---
 2 files changed, 25 insertions(+), 3 deletions(-)

diff --git a/arch/arm64/include/asm/virt.h b/arch/arm64/include/asm/virt.h
index 2fde1186b962..f7cf3f0e5297 100644
--- a/arch/arm64/include/asm/virt.h
+++ b/arch/arm64/include/asm/virt.h
@@ -65,9 +65,19 @@ extern u32 __boot_cpu_mode[2];
 void __hyp_set_vectors(phys_addr_t phys_vector_base);
 void __hyp_reset_vectors(void);
 
+DECLARE_STATIC_KEY_FALSE(kvm_protected_mode_initialized);
+
 /* Reports the availability of HYP mode */
 static inline bool is_hyp_mode_available(void)
 {
+   /*
+* If KVM protected mode is initialized, all CPUs must have been booted
+* in EL2. Avoid checking __boot_cpu_mode as CPUs now come up in EL1.
+*/
+   if (IS_ENABLED(CONFIG_KVM) &&
+   static_branch_likely(&kvm_protected_mode_initialized))
+   return true;
+
return (__boot_cpu_mode[0] == BOOT_CPU_MODE_EL2 &&
__boot_cpu_mode[1] == BOOT_CPU_MODE_EL2);
 }
@@ -75,6 +85,14 @@ static inline bool is_hyp_mode_available(void)
 /* Check if the bootloader has booted CPUs in different modes */
 static inline bool is_hyp_mode_mismatched(void)
 {
+   /*
+* If KVM protected mode is initialized, all CPUs must have been booted
+* in EL2. Avoid checking __boot_cpu_mode as CPUs now come up in EL1.
+*/
+   if (IS_ENABLED(CONFIG_KVM) &&
+   static_branch_likely(&kvm_protected_mode_initialized))
+   return false;
+
return __boot_cpu_mode[0] != __boot_cpu_mode[1];
 }
 
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 45bc7a6b9e0b..b86d0b38f30b 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -47,6 +47,8 @@
 __asm__(".arch_extension   virt");
 #endif
 
+DEFINE_STATIC_KEY_FALSE(kvm_protected_mode_initialized);
+
 DECLARE_KVM_HYP_PER_CPU(unsigned long, kvm_hyp_vector);
 
 static DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_page);
@@ -1837,12 +1839,14 @@ int kvm_arch_init(void *opaque)
if (err)
goto out_hyp;
 
-   if (is_protected_kvm_enabled())
+   if (is_protected_kvm_enabled()) {
+   static_branch_enable(&kvm_protected_mode_initialized);
kvm_info("Protected nVHE mode initialized successfully\n");
-   else if (in_hyp_mode)
+   } else if (in_hyp_mode) {
kvm_info("VHE mode initialized successfully\n");
-   else
+   } else {
kvm_info("Hyp mode initialized successfully\n");
+   }
 
return 0;
 
-- 
2.29.2.299.gdc1121823c-goog

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v2 23/24] kvm: arm64: Trap host SMCs in protected mode.

2020-11-16 Thread David Brazdil
While protected nVHE KVM is installed, start trapping all host SMCs.
By default, these are simply forwarded to EL3, but PSCI SMCs are
validated first.

Create new constant HCR_HOST_NVHE_PROTECTED_FLAGS with the new set of HCR
flags to use while the nVHE vector is installed when the kernel was
booted with the protected flag enabled. Switch back to the default HCR
flags when switching back to the stub vector.

Signed-off-by: David Brazdil 
---
 arch/arm64/include/asm/kvm_arm.h   |  1 +
 arch/arm64/kvm/hyp/nvhe/hyp-init.S | 12 
 arch/arm64/kvm/hyp/nvhe/switch.c   |  5 -
 3 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h
index 64ce29378467..4e90c2debf70 100644
--- a/arch/arm64/include/asm/kvm_arm.h
+++ b/arch/arm64/include/asm/kvm_arm.h
@@ -80,6 +80,7 @@
 HCR_FMO | HCR_IMO | HCR_PTW )
 #define HCR_VIRT_EXCP_MASK (HCR_VSE | HCR_VI | HCR_VF)
 #define HCR_HOST_NVHE_FLAGS (HCR_RW | HCR_API | HCR_APK | HCR_ATA)
+#define HCR_HOST_NVHE_PROTECTED_FLAGS (HCR_HOST_NVHE_FLAGS | HCR_TSC)
 #define HCR_HOST_VHE_FLAGS (HCR_RW | HCR_TGE | HCR_E2H)
 
 /* TCR_EL2 Registers bits */
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-init.S 
b/arch/arm64/kvm/hyp/nvhe/hyp-init.S
index 6d8202d2bdfb..8f3602f320ac 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-init.S
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-init.S
@@ -88,6 +88,12 @@ SYM_CODE_END(__kvm_hyp_init)
  * x0: struct kvm_nvhe_init_params PA
  */
 SYM_CODE_START(___kvm_hyp_init)
+alternative_if ARM64_PROTECTED_KVM
+   mov_q   x1, HCR_HOST_NVHE_PROTECTED_FLAGS
+   msr hcr_el2, x1
+   isb
+alternative_else_nop_endif
+
ldr x1, [x0, #NVHE_INIT_TPIDR_EL2]
msr tpidr_el2, x1
 
@@ -224,6 +230,12 @@ reset:
msr sctlr_el2, x5
isb
 
+alternative_if ARM64_PROTECTED_KVM
+   mov_q   x5, HCR_HOST_NVHE_FLAGS
+   msr hcr_el2, x5
+   isb
+alternative_else_nop_endif
+
/* Install stub vectors */
adr_l   x5, __hyp_stub_vectors
msr vbar_el2, x5
diff --git a/arch/arm64/kvm/hyp/nvhe/switch.c b/arch/arm64/kvm/hyp/nvhe/switch.c
index 8ae8160bc93a..e1f8e0797144 100644
--- a/arch/arm64/kvm/hyp/nvhe/switch.c
+++ b/arch/arm64/kvm/hyp/nvhe/switch.c
@@ -96,7 +96,10 @@ static void __deactivate_traps(struct kvm_vcpu *vcpu)
mdcr_el2 |= MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT;
 
write_sysreg(mdcr_el2, mdcr_el2);
-   write_sysreg(HCR_HOST_NVHE_FLAGS, hcr_el2);
+   if (is_protected_kvm_enabled())
+   write_sysreg(HCR_HOST_NVHE_PROTECTED_FLAGS, hcr_el2);
+   else
+   write_sysreg(HCR_HOST_NVHE_FLAGS, hcr_el2);
write_sysreg(CPTR_EL2_DEFAULT, cptr_el2);
write_sysreg(__kvm_hyp_host_vector, vbar_el2);
 }
-- 
2.29.2.299.gdc1121823c-goog

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v2 17/24] kvm: arm64: Add CPU entry point in nVHE hyp

2020-11-16 Thread David Brazdil
When nVHE hyp starts interception host's PSCI CPU_ON SMCs, it will need
to install KVM on the newly booted CPU before returning to the host. Add
an entry point which expects the same kvm_nvhe_init_params struct as the
__kvm_hyp_init HVC in the CPU_ON context argument (x0).

The entry point initializes EL2 state with the same init_el2_state macro
used by the kernel's entry point. It then initializes KVM using the same
helper function used in the __kvm_hyp_init HVC.

When done, the entry point branches to a function provided in the init
params.

Signed-off-by: David Brazdil 
---
 arch/arm64/include/asm/kvm_asm.h   |  1 +
 arch/arm64/kernel/asm-offsets.c|  1 +
 arch/arm64/kvm/hyp/nvhe/hyp-init.S | 32 ++
 3 files changed, 34 insertions(+)

diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h
index 01904e88cead..109867fb76f6 100644
--- a/arch/arm64/include/asm/kvm_asm.h
+++ b/arch/arm64/include/asm/kvm_asm.h
@@ -154,6 +154,7 @@ struct kvm_nvhe_init_params {
unsigned long tpidr_el2;
unsigned long vector_hyp_va;
unsigned long stack_hyp_va;
+   unsigned long entry_hyp_va;
phys_addr_t pgd_pa;
 };
 
diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
index 4435ad8be938..9752100bf01f 100644
--- a/arch/arm64/kernel/asm-offsets.c
+++ b/arch/arm64/kernel/asm-offsets.c
@@ -113,6 +113,7 @@ int main(void)
   DEFINE(NVHE_INIT_TPIDR_EL2,  offsetof(struct kvm_nvhe_init_params, 
tpidr_el2));
   DEFINE(NVHE_INIT_VECTOR_HYP_VA,  offsetof(struct kvm_nvhe_init_params, 
vector_hyp_va));
   DEFINE(NVHE_INIT_STACK_HYP_VA,   offsetof(struct kvm_nvhe_init_params, 
stack_hyp_va));
+  DEFINE(NVHE_INIT_ENTRY_HYP_VA,   offsetof(struct kvm_nvhe_init_params, 
entry_hyp_va));
   DEFINE(NVHE_INIT_PGD_PA, offsetof(struct kvm_nvhe_init_params, pgd_pa));
 #endif
 #ifdef CONFIG_CPU_PM
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-init.S 
b/arch/arm64/kvm/hyp/nvhe/hyp-init.S
index d4a71ac34254..6d8202d2bdfb 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-init.S
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-init.S
@@ -9,6 +9,7 @@
 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -159,6 +160,37 @@ alternative_else_nop_endif
ret
 SYM_CODE_END(___kvm_hyp_init)
 
+SYM_CODE_START(__kvm_hyp_cpu_entry)
+   msr SPsel, #1   // We want to use SP_EL{1,2}
+
+   /* Check that the core was booted in EL2. */
+   mrs x1, CurrentEL
+   cmp x1, #CurrentEL_EL2
+   b.eq2f
+
+   /* The core booted in EL1. KVM cannot be initialized on it. */
+1: wfe
+   wfi
+   b   1b
+
+   /* Initialize EL2 CPU state to sane values. */
+2: mov x29, x0
+   init_el2_state nvhe
+   mov x0, x29
+
+   /*
+* Load hyp VA of C entry function. Must do so before switching on the
+* MMU because the struct pointer is PA and not identity-mapped in hyp.
+*/
+   ldr x29, [x0, #NVHE_INIT_ENTRY_HYP_VA]
+
+   /* Enable MMU, set vectors and stack. */
+   bl  ___kvm_hyp_init
+
+   /* Leave idmap. */
+   br  x29
+SYM_CODE_END(__kvm_hyp_cpu_entry)
+
 SYM_CODE_START(__kvm_handle_stub_hvc)
cmp x0, #HVC_SOFT_RESTART
b.ne1f
-- 
2.29.2.299.gdc1121823c-goog

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v2 21/24] kvm: arm64: Add kvm-arm.protected early kernel parameter

2020-11-16 Thread David Brazdil
Add an early parameter that allows users to opt into protected KVM mode
when using the nVHE hypervisor. In this mode, guest state will be kept
private from the host. This will primarily involve enabling stage-2
address translation for the host, restricting DMA to host memory, and
filtering host SMCs.

Capability ARM64_PROTECTED_KVM is set if the param is passed, CONFIG_KVM
is enabled and the kernel was not booted with VHE.

Signed-off-by: David Brazdil 
---
 arch/arm64/include/asm/cpucaps.h |  3 ++-
 arch/arm64/include/asm/virt.h|  8 
 arch/arm64/kernel/cpufeature.c   | 29 +
 arch/arm64/kvm/arm.c | 10 +-
 4 files changed, 48 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/include/asm/cpucaps.h b/arch/arm64/include/asm/cpucaps.h
index e7d98997c09c..ac075f70b2e4 100644
--- a/arch/arm64/include/asm/cpucaps.h
+++ b/arch/arm64/include/asm/cpucaps.h
@@ -66,7 +66,8 @@
 #define ARM64_HAS_TLB_RANGE56
 #define ARM64_MTE  57
 #define ARM64_WORKAROUND_1508412   58
+#define ARM64_PROTECTED_KVM59
 
-#define ARM64_NCAPS59
+#define ARM64_NCAPS60
 
 #endif /* __ASM_CPUCAPS_H */
diff --git a/arch/arm64/include/asm/virt.h b/arch/arm64/include/asm/virt.h
index 6069be50baf9..2fde1186b962 100644
--- a/arch/arm64/include/asm/virt.h
+++ b/arch/arm64/include/asm/virt.h
@@ -97,6 +97,14 @@ static __always_inline bool has_vhe(void)
return cpus_have_final_cap(ARM64_HAS_VIRT_HOST_EXTN);
 }
 
+static __always_inline bool is_protected_kvm_enabled(void)
+{
+   if (is_vhe_hyp_code())
+   return false;
+   else
+   return cpus_have_final_cap(ARM64_PROTECTED_KVM);
+}
+
 #endif /* __ASSEMBLY__ */
 
 #endif /* ! __ASM__VIRT_H */
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 6f36c4f62f69..dd5bc0f0cf0d 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -1709,6 +1709,29 @@ static void cpu_enable_mte(struct arm64_cpu_capabilities 
const *cap)
 }
 #endif /* CONFIG_ARM64_MTE */
 
+#ifdef CONFIG_KVM
+static bool enable_protected_kvm;
+
+static bool has_protected_kvm(const struct arm64_cpu_capabilities *entry, int 
__unused)
+{
+   if (!enable_protected_kvm)
+   return false;
+
+   if (is_kernel_in_hyp_mode()) {
+   pr_warn("Protected KVM not available with VHE\n");
+   return false;
+   }
+
+   return true;
+}
+
+static int __init early_protected_kvm_cfg(char *buf)
+{
+   return strtobool(buf, &enable_protected_kvm);
+}
+early_param("kvm-arm.protected", early_protected_kvm_cfg);
+#endif /* CONFIG_KVM */
+
 /* Internal helper functions to match cpu capability type */
 static bool
 cpucap_late_cpu_optional(const struct arm64_cpu_capabilities *cap)
@@ -1822,6 +1845,12 @@ static const struct arm64_cpu_capabilities 
arm64_features[] = {
.field_pos = ID_AA64PFR0_EL1_SHIFT,
.min_field_value = ID_AA64PFR0_EL1_32BIT_64BIT,
},
+   {
+   .desc = "Protected KVM",
+   .capability = ARM64_PROTECTED_KVM,
+   .type = ARM64_CPUCAP_SYSTEM_FEATURE,
+   .matches = has_protected_kvm,
+   },
 #endif
{
.desc = "Kernel page table isolation (KPTI)",
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index c76a8e5bd19c..49d2474f2a80 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -1796,6 +1796,12 @@ int kvm_arch_init(void *opaque)
return -ENODEV;
}
 
+   /* The PROTECTED_KVM cap should not have been enabled for VHE. */
+   if (in_hyp_mode && is_protected_kvm_enabled()) {
+   kvm_pr_unimpl("VHE protected mode unsupported, not 
initializing\n");
+   return -ENODEV;
+   }
+
if (cpus_have_final_cap(ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE) ||
cpus_have_final_cap(ARM64_WORKAROUND_1508412))
kvm_info("Guests without required CPU erratum workarounds can 
deadlock system!\n" \
@@ -1827,7 +1833,9 @@ int kvm_arch_init(void *opaque)
if (err)
goto out_hyp;
 
-   if (in_hyp_mode)
+   if (is_protected_kvm_enabled())
+   kvm_info("Protected nVHE mode initialized successfully\n");
+   else if (in_hyp_mode)
kvm_info("VHE mode initialized successfully\n");
else
kvm_info("Hyp mode initialized successfully\n");
-- 
2.29.2.299.gdc1121823c-goog

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v2 06/24] kvm: arm64: Move hyp-init params to a per-CPU struct

2020-11-16 Thread David Brazdil
Once we start initializing KVM on newly booted cores before the rest of
the kernel, parameters to __do_hyp_init will need to be provided by EL2
rather than EL1. At that point it will not be possible to pass its four
arguments directly because PSCI_CPU_ON only supports one context
argument.

Refactor __do_hyp_init to accept its parameters in a struct. This
prepares the code for KVM booting cores as well as removes any limits on
the number of __do_hyp_init arguments.

Signed-off-by: David Brazdil 
---
 arch/arm64/include/asm/kvm_asm.h   |  7 +++
 arch/arm64/include/asm/kvm_hyp.h   |  4 
 arch/arm64/kernel/asm-offsets.c|  4 
 arch/arm64/kvm/arm.c   | 26 ++
 arch/arm64/kvm/hyp/nvhe/hyp-init.S | 21 ++---
 arch/arm64/kvm/hyp/nvhe/hyp-main.c |  2 ++
 6 files changed, 41 insertions(+), 23 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h
index 54387ccd1ab2..01904e88cead 100644
--- a/arch/arm64/include/asm/kvm_asm.h
+++ b/arch/arm64/include/asm/kvm_asm.h
@@ -150,6 +150,13 @@ extern void *__vhe_undefined_symbol;
 
 #endif
 
+struct kvm_nvhe_init_params {
+   unsigned long tpidr_el2;
+   unsigned long vector_hyp_va;
+   unsigned long stack_hyp_va;
+   phys_addr_t pgd_pa;
+};
+
 /* Translate a kernel address @ptr into its equivalent linear mapping */
 #define kvm_ksym_ref(ptr)  \
({  \
diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h
index 6b664de5ec1f..a3289071f3d8 100644
--- a/arch/arm64/include/asm/kvm_hyp.h
+++ b/arch/arm64/include/asm/kvm_hyp.h
@@ -15,6 +15,10 @@
 DECLARE_PER_CPU(struct kvm_cpu_context, kvm_hyp_ctxt);
 DECLARE_PER_CPU(unsigned long, kvm_hyp_vector);
 
+#ifdef __KVM_NVHE_HYPERVISOR__
+DECLARE_PER_CPU(struct kvm_nvhe_init_params, kvm_init_params);
+#endif
+
 #define read_sysreg_elx(r,nvh,vh)  \
({  \
u64 reg;\
diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
index 7d32fc959b1a..4435ad8be938 100644
--- a/arch/arm64/kernel/asm-offsets.c
+++ b/arch/arm64/kernel/asm-offsets.c
@@ -110,6 +110,10 @@ int main(void)
   DEFINE(CPU_APGAKEYLO_EL1,offsetof(struct kvm_cpu_context, 
sys_regs[APGAKEYLO_EL1]));
   DEFINE(HOST_CONTEXT_VCPU,offsetof(struct kvm_cpu_context, 
__hyp_running_vcpu));
   DEFINE(HOST_DATA_CONTEXT,offsetof(struct kvm_host_data, host_ctxt));
+  DEFINE(NVHE_INIT_TPIDR_EL2,  offsetof(struct kvm_nvhe_init_params, 
tpidr_el2));
+  DEFINE(NVHE_INIT_VECTOR_HYP_VA,  offsetof(struct kvm_nvhe_init_params, 
vector_hyp_va));
+  DEFINE(NVHE_INIT_STACK_HYP_VA,   offsetof(struct kvm_nvhe_init_params, 
stack_hyp_va));
+  DEFINE(NVHE_INIT_PGD_PA, offsetof(struct kvm_nvhe_init_params, pgd_pa));
 #endif
 #ifdef CONFIG_CPU_PM
   DEFINE(CPU_CTX_SP,   offsetof(struct cpu_suspend_ctx, sp));
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index c0ffb019ca8b..4838556920fb 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -50,6 +50,7 @@ DECLARE_KVM_HYP_PER_CPU(unsigned long, kvm_hyp_vector);
 
 static DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_page);
 unsigned long kvm_arm_hyp_percpu_base[NR_CPUS];
+DECLARE_KVM_NVHE_PER_CPU(struct kvm_nvhe_init_params, kvm_init_params);
 
 /* The VMID used in the VTTBR */
 static atomic64_t kvm_vmid_gen = ATOMIC64_INIT(1);
@@ -1347,10 +1348,7 @@ static int kvm_map_vectors(void)
 
 static void cpu_init_hyp_mode(void)
 {
-   phys_addr_t pgd_ptr;
-   unsigned long hyp_stack_ptr;
-   unsigned long vector_ptr;
-   unsigned long tpidr_el2;
+   struct kvm_nvhe_init_params *params = 
this_cpu_ptr_nvhe_sym(kvm_init_params);
struct arm_smccc_res res;
 
/* Switch from the HYP stub to our own HYP init vector */
@@ -1361,13 +1359,18 @@ static void cpu_init_hyp_mode(void)
 * kernel's mapping to the linear mapping, and store it in tpidr_el2
 * so that we can use adr_l to access per-cpu variables in EL2.
 */
-   tpidr_el2 = (unsigned long)this_cpu_ptr_nvhe_sym(__per_cpu_start) -
-   (unsigned 
long)kvm_ksym_ref(CHOOSE_NVHE_SYM(__per_cpu_start));
+   params->tpidr_el2 = (unsigned 
long)this_cpu_ptr_nvhe_sym(__per_cpu_start) -
+   (unsigned 
long)kvm_ksym_ref(CHOOSE_NVHE_SYM(__per_cpu_start));
 
-   pgd_ptr = kvm_mmu_get_httbr();
-   hyp_stack_ptr = __this_cpu_read(kvm_arm_hyp_stack_page) + PAGE_SIZE;
-   hyp_stack_ptr = kern_hyp_va(hyp_stack_ptr);
-   vector_ptr = (unsigned 
long)kern_hyp_va(kvm_ksym_ref(__kvm_hyp_host_vector));
+   params->vector_hyp_va = (unsigned 
long)kern_hyp_va(kvm_ksym_ref(__kvm_hyp_host_vector));
+   params->stack_hyp_va = 
k

[PATCH v2 05/24] kvm: arm64: Initialize MAIR_EL2 using a constant

2020-11-16 Thread David Brazdil
MAIR_EL2 is currently initialized to the value of MAIR_EL1, which itself
is set to a constant MAIR_ELx_SET.

Initialize MAIR_EL2 to MAIR_ELx_SET directly in preparation of allowing
KVM to start CPU cores itself and not initializing itself before ERETing
to EL1. In that case, MAIR_EL2 will be initialized before MAIR_EL1.

Signed-off-by: David Brazdil 
---
 arch/arm64/kvm/hyp/nvhe/hyp-init.S | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-init.S 
b/arch/arm64/kvm/hyp/nvhe/hyp-init.S
index b11a9d7db677..17b58dbc3a2f 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-init.S
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-init.S
@@ -111,7 +111,7 @@ alternative_else_nop_endif
 
msr tcr_el2, x0
 
-   mrs x0, mair_el1
+   mov_q   x0, MAIR_ELx_SET
msr mair_el2, x0
isb
 
-- 
2.29.2.299.gdc1121823c-goog

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v2 13/24] kvm: arm64: Add offset for hyp VA <-> PA conversion

2020-11-16 Thread David Brazdil
Add a host-initialized constant to KVM nVHE hyp code for converting
between EL2 linear map virtual addresses and physical addresses.
Also add `__hyp_pa` macro that performs the conversion.

Signed-off-by: David Brazdil 
---
 arch/arm64/kvm/hyp/nvhe/psci-relay.c |  3 +++
 arch/arm64/kvm/va_layout.c   | 30 +---
 2 files changed, 30 insertions(+), 3 deletions(-)

diff --git a/arch/arm64/kvm/hyp/nvhe/psci-relay.c 
b/arch/arm64/kvm/hyp/nvhe/psci-relay.c
index d75d3f896bfd..1583b63322c4 100644
--- a/arch/arm64/kvm/hyp/nvhe/psci-relay.c
+++ b/arch/arm64/kvm/hyp/nvhe/psci-relay.c
@@ -16,6 +16,9 @@
 /* Config options set by the host. */
 u32 __ro_after_init kvm_host_psci_version = PSCI_VERSION(0, 0);
 u32 __ro_after_init kvm_host_psci_function_id[PSCI_FN_MAX];
+s64 __ro_after_init hyp_physvirt_offset;
+
+#define __hyp_pa(x) ((phys_addr_t)((x)) + hyp_physvirt_offset)
 
 static u64 get_psci_func_id(struct kvm_cpu_context *host_ctxt)
 {
diff --git a/arch/arm64/kvm/va_layout.c b/arch/arm64/kvm/va_layout.c
index e0404bcab019..744a0d998ad3 100644
--- a/arch/arm64/kvm/va_layout.c
+++ b/arch/arm64/kvm/va_layout.c
@@ -22,6 +22,30 @@ static u8 tag_lsb;
 static u64 tag_val;
 static u64 va_mask;
 
+/*
+ * Compute HYP VA by using the same computation as kern_hyp_va().
+ */
+static u64 __early_kern_hyp_va(u64 addr)
+{
+   addr &= va_mask;
+   addr |= tag_val << tag_lsb;
+   return addr;
+}
+
+/*
+ * Store a hyp VA <-> PA offset into a hyp-owned variable.
+ */
+static void init_hyp_physvirt_offset(void)
+{
+   extern s64 kvm_nvhe_sym(hyp_physvirt_offset);
+   u64 kern_va, hyp_va;
+
+   /* Compute the offset from the hyp VA and PA of a random symbol. */
+   kern_va = (u64)kvm_ksym_ref(__hyp_text_start);
+   hyp_va = __early_kern_hyp_va(kern_va);
+   CHOOSE_NVHE_SYM(hyp_physvirt_offset) = (s64)__pa(kern_va) - (s64)hyp_va;
+}
+
 /*
  * We want to generate a hyp VA with the following format (with V ==
  * vabits_actual):
@@ -53,6 +77,8 @@ __init void kvm_compute_layout(void)
tag_val |= get_random_long() & GENMASK_ULL(vabits_actual - 2, 
tag_lsb);
}
tag_val >>= tag_lsb;
+
+   init_hyp_physvirt_offset();
 }
 
 static u32 compute_instruction(int n, u32 rd, u32 rn)
@@ -150,9 +176,7 @@ void kvm_patch_vector_branch(struct alt_instr *alt,
/*
 * Compute HYP VA by using the same computation as kern_hyp_va()
 */
-   addr = (uintptr_t)kvm_ksym_ref(__kvm_hyp_vector);
-   addr &= va_mask;
-   addr |= tag_val << tag_lsb;
+   addr = __early_kern_hyp_va((u64)kvm_ksym_ref(__kvm_hyp_vector));
 
/* Use PC[10:7] to branch to the same vector in KVM */
addr |= ((u64)origptr & GENMASK_ULL(10, 7));
-- 
2.29.2.299.gdc1121823c-goog

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v2 15/24] kvm: arm64: Extract parts of el2_setup into a macro

2020-11-16 Thread David Brazdil
When the a CPU is booted in EL2, the kernel checks for VHE support and
initializes the CPU core accordingly. For nVHE it also installs the stub
vectors and drops down to EL1.

Once KVM gains the ability to boot cores without going through the
kernel entry point, it will need to initialize the CPU the same way.
Extract the relevant bits of el2_setup into an init_el2_state macro
with an argument specifying whether to initialize for VHE or nVHE.

No functional change. Size of el2_setup increased by 148 bytes due
to duplication.

Signed-off-by: David Brazdil 
---
 arch/arm64/include/asm/el2_setup.h | 185 +
 arch/arm64/kernel/head.S   | 144 +++---
 2 files changed, 201 insertions(+), 128 deletions(-)
 create mode 100644 arch/arm64/include/asm/el2_setup.h

diff --git a/arch/arm64/include/asm/el2_setup.h 
b/arch/arm64/include/asm/el2_setup.h
new file mode 100644
index ..e5026e0aa878
--- /dev/null
+++ b/arch/arm64/include/asm/el2_setup.h
@@ -0,0 +1,185 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2012,2013 - ARM Ltd
+ * Author: Marc Zyngier 
+ */
+
+#ifndef __ARM_KVM_INIT_H__
+#define __ARM_KVM_INIT_H__
+
+#ifndef __ASSEMBLY__
+#error Assembly-only header
+#endif
+
+#ifdef CONFIG_ARM_GIC_V3
+#include 
+#endif
+
+#include 
+#include 
+#include 
+
+.macro __init_el2_sctlr
+   mov_q   x0, (SCTLR_EL2_RES1 | ENDIAN_SET_EL2)
+   msr sctlr_el2, x0
+   isb
+.endm
+
+/*
+ * Allow Non-secure EL1 and EL0 to access physical timer and counter.
+ * This is not necessary for VHE, since the host kernel runs in EL2,
+ * and EL0 accesses are configured in the later stage of boot process.
+ * Note that when HCR_EL2.E2H == 1, CNTHCTL_EL2 has the same bit layout
+ * as CNTKCTL_EL1, and CNTKCTL_EL1 accessing instructions are redefined
+ * to access CNTHCTL_EL2. This allows the kernel designed to run at EL1
+ * to transparently mess with the EL0 bits via CNTKCTL_EL1 access in
+ * EL2.
+ */
+.macro __init_el2_timers mode
+.ifeqs "\mode", "nvhe"
+   mrs x0, cnthctl_el2
+   orr x0, x0, #3  // Enable EL1 physical timers
+   msr cnthctl_el2, x0
+.endif
+   msr cntvoff_el2, xzr// Clear virtual offset
+.endm
+
+.macro __init_el2_debug mode
+   mrs x1, id_aa64dfr0_el1
+   sbfxx0, x1, #ID_AA64DFR0_PMUVER_SHIFT, #4
+   cmp x0, #1
+   b.lt1f  // Skip if no PMU present
+   mrs x0, pmcr_el0// Disable debug access traps
+   ubfxx0, x0, #11, #5 // to EL2 and allow access to
+1:
+   cselx2, xzr, x0, lt // all PMU counters from EL1
+
+   /* Statistical profiling */
+   ubfxx0, x1, #ID_AA64DFR0_PMSVER_SHIFT, #4
+   cbz x0, 3f  // Skip if SPE not present
+
+.ifeqs "\mode", "nvhe"
+   mrs_s   x0, SYS_PMBIDR_EL1  // If SPE available at EL2,
+   and x0, x0, #(1 << SYS_PMBIDR_EL1_P_SHIFT)
+   cbnzx0, 2f  // then permit sampling of 
physical
+   mov x0, #(1 << SYS_PMSCR_EL2_PCT_SHIFT | \
+ 1 << SYS_PMSCR_EL2_PA_SHIFT)
+   msr_s   SYS_PMSCR_EL2, x0   // addresses and physical 
counter
+2:
+   mov x0, #(MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT)
+   orr x2, x2, x0  // If we don't have VHE, then
+   // use EL1&0 translation.
+.else
+   orr x2, x2, #MDCR_EL2_TPMS  // For VHE, use EL2 translation
+   // and disable access from EL1
+.endif
+
+3:
+   msr mdcr_el2, x2// Configure debug traps
+.endm
+
+/* LORegions */
+.macro __init_el2_lor
+   mrs x1, id_aa64mmfr1_el1
+   ubfxx0, x1, #ID_AA64MMFR1_LOR_SHIFT, 4
+   cbz x0, 1f
+   msr_s   SYS_LORC_EL1, xzr
+1:
+.endm
+
+/* Stage-2 translation */
+.macro __init_el2_stage2
+   msr vttbr_el2, xzr
+.endm
+
+/* GICv3 system register access */
+#ifdef CONFIG_ARM_GIC_V3
+.macro __init_el2_gicv3
+   mrs x0, id_aa64pfr0_el1
+   ubfxx0, x0, #ID_AA64PFR0_GIC_SHIFT, #4
+   cbz x0, 1f
+
+   mrs_s   x0, SYS_ICC_SRE_EL2
+   orr x0, x0, #ICC_SRE_EL2_SRE// Set ICC_SRE_EL2.SRE==1
+   orr x0, x0, #ICC_SRE_EL2_ENABLE // Set ICC_SRE_EL2.Enable==1
+   msr_s   SYS_ICC_SRE_EL2, x0
+   isb // Make sure SRE is now set
+   mrs_s   x0, SYS_ICC_SRE_EL2 // Read SRE back,
+   tbz x0, #0, 1f  // and check that it sticks
+   msr_s   SYS_ICH_HCR_EL2, xzr// Reset ICC_HCR_EL2 to defaults
+1:
+.endm
+#endif
+
+/* Virtual CPU ID registers */
+.macro __init_el2_nvhe_idregs
+   mrs x0, midr_el1
+   mrs x1, mpidr_el1
+   msr vpidr_el2,

[PATCH v2 16/24] kvm: arm64: Extract __do_hyp_init into a helper function

2020-11-16 Thread David Brazdil
In preparation for adding a CPU entry point in nVHE hyp code, extract
most of __do_hyp_init hypervisor initialization code into a common
helper function. This will be invoked by the entry point to install KVM
on the newly booted CPU.

Signed-off-by: David Brazdil 
---
 arch/arm64/kvm/hyp/nvhe/hyp-init.S | 39 +-
 1 file changed, 28 insertions(+), 11 deletions(-)

diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-init.S 
b/arch/arm64/kvm/hyp/nvhe/hyp-init.S
index 67342cc9930f..d4a71ac34254 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-init.S
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-init.S
@@ -68,16 +68,35 @@ __do_hyp_init:
mov x0, #SMCCC_RET_NOT_SUPPORTED
eret
 
-1: ldr x0, [x1, #NVHE_INIT_TPIDR_EL2]
-   msr tpidr_el2, x0
+1: mov x0, x1
+   mov x4, lr
+   bl  ___kvm_hyp_init
+   mov lr, x4
 
-   ldr x0, [x1, #NVHE_INIT_VECTOR_HYP_VA]
-   msr vbar_el2, x0
+   /* Hello, World! */
+   mov x0, #SMCCC_RET_SUCCESS
+   eret
+SYM_CODE_END(__kvm_hyp_init)
+
+/*
+ * Initialize the hypervisor in EL2.
+ *
+ * Only uses x0..x3 so as to not clobber callee-saved SMCCC registers
+ * and leave x4 for the caller.
+ *
+ * x0: struct kvm_nvhe_init_params PA
+ */
+SYM_CODE_START(___kvm_hyp_init)
+   ldr x1, [x0, #NVHE_INIT_TPIDR_EL2]
+   msr tpidr_el2, x1
+
+   ldr x1, [x0, #NVHE_INIT_VECTOR_HYP_VA]
+   msr vbar_el2, x1
 
-   ldr x0, [x1, #NVHE_INIT_STACK_HYP_VA]
-   mov sp, x0
+   ldr x1, [x0, #NVHE_INIT_STACK_HYP_VA]
+   mov sp, x1
 
-   ldr x1, [x1, #NVHE_INIT_PGD_PA]
+   ldr x1, [x0, #NVHE_INIT_PGD_PA]
phys_to_ttbr x0, x1
 alternative_if ARM64_HAS_CNP
orr x0, x0, #TTBR_CNP_BIT
@@ -137,10 +156,8 @@ alternative_else_nop_endif
msr sctlr_el2, x0
isb
 
-   /* Hello, World! */
-   mov x0, #SMCCC_RET_SUCCESS
-   eret
-SYM_CODE_END(__kvm_hyp_init)
+   ret
+SYM_CODE_END(___kvm_hyp_init)
 
 SYM_CODE_START(__kvm_handle_stub_hvc)
cmp x0, #HVC_SOFT_RESTART
-- 
2.29.2.299.gdc1121823c-goog

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v2 18/24] kvm: arm64: Add function to enter host from KVM nVHE hyp code

2020-11-16 Thread David Brazdil
All nVHE hyp code is currently executed as handlers of host's HVCs. This
will change as nVHE starts intercepting host's PSCI CPU_ON SMCs. The
newly booted CPU will need to initialize EL2 state and then enter the
host. Add __host_enter function that branches into the existing
host state-restoring code after the trap handler would have returned.

Signed-off-by: David Brazdil 
---
 arch/arm64/kvm/hyp/nvhe/host.S | 9 +
 1 file changed, 9 insertions(+)

diff --git a/arch/arm64/kvm/hyp/nvhe/host.S b/arch/arm64/kvm/hyp/nvhe/host.S
index 52dae5cd5a28..170d6f6376ae 100644
--- a/arch/arm64/kvm/hyp/nvhe/host.S
+++ b/arch/arm64/kvm/hyp/nvhe/host.S
@@ -41,6 +41,7 @@ SYM_FUNC_START(__host_exit)
bl  handle_trap
 
/* Restore host regs x0-x17 */
+__host_enter_restore_full:
ldp x0, x1,   [x29, #CPU_XREG_OFFSET(0)]
ldp x2, x3,   [x29, #CPU_XREG_OFFSET(2)]
ldp x4, x5,   [x29, #CPU_XREG_OFFSET(4)]
@@ -63,6 +64,14 @@ __host_enter_without_restoring:
sb
 SYM_FUNC_END(__host_exit)
 
+/*
+ * void __noreturn __host_enter(struct kvm_cpu_context *host_ctxt);
+ */
+SYM_FUNC_START(__host_enter)
+   mov x29, x0
+   b   __host_enter_restore_full
+SYM_FUNC_END(__host_enter)
+
 /*
  * void __noreturn __hyp_do_panic(bool restore_host, u64 spsr, u64 elr, u64 
par);
  */
-- 
2.29.2.299.gdc1121823c-goog

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v2 11/24] kvm: arm64: Create nVHE copy of cpu_logical_map

2020-11-16 Thread David Brazdil
When KVM starts validating host's PSCI requests, it will need to map
MPIDR back to the CPU ID. To this end, copy cpu_logical_map into nVHE
hyp memory when KVM is initialized.

Only copy the information for CPUs that are online at the point of KVM
initialization so that KVM rejects CPUs whose features were not checked
against the finalized capabilities.

Signed-off-by: David Brazdil 
---
 arch/arm64/kvm/arm.c  | 17 +
 arch/arm64/kvm/hyp/nvhe/hyp-smp.c | 16 
 2 files changed, 33 insertions(+)

diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 97af6c285f84..cdd7981ea560 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -1499,6 +1499,21 @@ static inline void hyp_cpu_pm_exit(void)
 }
 #endif
 
+static void init_cpu_logical_map(void)
+{
+   extern u64 kvm_nvhe_sym(__cpu_logical_map)[NR_CPUS];
+   int cpu;
+
+   /*
+* Copy the MPIDR <-> logical CPU ID mapping to hyp.
+* Only copy the set of online CPUs whose features have been chacked
+* against the finalized system capabilities. The hypervisor will not
+* allow any other CPUs from the `possible` set to boot.
+*/
+   for_each_online_cpu(cpu)
+   CHOOSE_NVHE_SYM(__cpu_logical_map)[cpu] = cpu_logical_map(cpu);
+}
+
 static int init_common_resources(void)
 {
return kvm_set_ipa_limit();
@@ -1677,6 +1692,8 @@ static int init_hyp_mode(void)
}
}
 
+   init_cpu_logical_map();
+
return 0;
 
 out_err:
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-smp.c 
b/arch/arm64/kvm/hyp/nvhe/hyp-smp.c
index 7b0363b4857f..cbab0c6246e2 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-smp.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-smp.c
@@ -8,6 +8,22 @@
 #include 
 #include 
 
+/*
+ * nVHE copy of data structures tracking available CPU cores.
+ * Only entries for CPUs that were online at KVM init are populated.
+ * Other CPUs should not be allowed to boot because their features were
+ * not checked against the finalized system capabilities.
+ */
+u64 __ro_after_init __cpu_logical_map[NR_CPUS] = { [0 ... NR_CPUS-1] = 
INVALID_HWID };
+
+u64 cpu_logical_map(unsigned int cpu)
+{
+   if (cpu >= ARRAY_SIZE(__cpu_logical_map))
+   hyp_panic();
+
+   return __cpu_logical_map[cpu];
+}
+
 unsigned long __hyp_per_cpu_offset(unsigned int cpu)
 {
unsigned long *cpu_base_array;
-- 
2.29.2.299.gdc1121823c-goog

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v2 14/24] kvm: arm64: Forward safe PSCI SMCs coming from host

2020-11-16 Thread David Brazdil
Forward the following PSCI SMCs issued by host to EL3 as they do not
require the hypervisor's intervention. This assumes that EL3 correctly
implements the PSCI specification.

Only function IDs implemented in Linux are included.

Where both 32-bit and 64-bit variants exist, it is assumed that the host
will always use the 64-bit variant.

 * SMCs that only return information about the system
   * PSCI_VERSION- PSCI version implemented by EL3
   * PSCI_FEATURES   - optional features supported by EL3
   * AFFINITY_INFO   - power state of core/cluster
   * MIGRATE_INFO_TYPE   - whether Trusted OS can be migrated
   * MIGRATE_INFO_UP_CPU - resident core of Trusted OS
 * operations which do not affect the hypervisor
   * MIGRATE - migrate Trusted OS to a different core
   * SET_SUSPEND_MODE- toggle OS-initiated mode
 * system shutdown/reset
   * SYSTEM_OFF
   * SYSTEM_RESET
   * SYSTEM_RESET2

Signed-off-by: David Brazdil 
---
 arch/arm64/kvm/hyp/nvhe/psci-relay.c | 43 +++-
 1 file changed, 42 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/kvm/hyp/nvhe/psci-relay.c 
b/arch/arm64/kvm/hyp/nvhe/psci-relay.c
index 1583b63322c4..7542de8bd679 100644
--- a/arch/arm64/kvm/hyp/nvhe/psci-relay.c
+++ b/arch/arm64/kvm/hyp/nvhe/psci-relay.c
@@ -55,14 +55,51 @@ static bool is_psci_call(u64 func_id)
}
 }
 
+static unsigned long psci_call(unsigned long fn, unsigned long arg0,
+  unsigned long arg1, unsigned long arg2)
+{
+   struct arm_smccc_res res;
+
+   arm_smccc_1_1_smc(fn, arg0, arg1, arg2, &res);
+   return res.a0;
+}
+
+static unsigned long psci_forward(struct kvm_cpu_context *host_ctxt)
+{
+   return psci_call(host_ctxt->regs.regs[0], host_ctxt->regs.regs[1],
+host_ctxt->regs.regs[2], host_ctxt->regs.regs[3]);
+}
+
+static __noreturn unsigned long psci_forward_noreturn(struct kvm_cpu_context 
*host_ctxt)
+{
+   psci_forward(host_ctxt);
+   hyp_panic(); /* unreachable */
+}
+
 static unsigned long psci_0_1_handler(u64 func_id, struct kvm_cpu_context 
*host_ctxt)
 {
-   return PSCI_RET_NOT_SUPPORTED;
+   if (func_id == kvm_host_psci_function_id[PSCI_FN_CPU_OFF])
+   return psci_forward(host_ctxt);
+   else if (func_id == kvm_host_psci_function_id[PSCI_FN_MIGRATE])
+   return psci_forward(host_ctxt);
+   else
+   return PSCI_RET_NOT_SUPPORTED;
 }
 
 static unsigned long psci_0_2_handler(u64 func_id, struct kvm_cpu_context 
*host_ctxt)
 {
switch (func_id) {
+   case PSCI_0_2_FN_PSCI_VERSION:
+   case PSCI_0_2_FN_CPU_OFF:
+   case PSCI_0_2_FN64_AFFINITY_INFO:
+   case PSCI_0_2_FN64_MIGRATE:
+   case PSCI_0_2_FN_MIGRATE_INFO_TYPE:
+   case PSCI_0_2_FN64_MIGRATE_INFO_UP_CPU:
+   return psci_forward(host_ctxt);
+   case PSCI_0_2_FN_SYSTEM_OFF:
+   case PSCI_0_2_FN_SYSTEM_RESET:
+   psci_forward_noreturn(host_ctxt);
+   unreachable();
default:
return PSCI_RET_NOT_SUPPORTED;
}
@@ -71,6 +108,10 @@ static unsigned long psci_0_2_handler(u64 func_id, struct 
kvm_cpu_context *host_
 static unsigned long psci_1_0_handler(u64 func_id, struct kvm_cpu_context 
*host_ctxt)
 {
switch (func_id) {
+   case PSCI_1_0_FN_PSCI_FEATURES:
+   case PSCI_1_0_FN_SET_SUSPEND_MODE:
+   case PSCI_1_1_FN64_SYSTEM_RESET2:
+   return psci_forward(host_ctxt);
default:
return psci_0_2_handler(func_id, host_ctxt);
}
-- 
2.29.2.299.gdc1121823c-goog

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v2 03/24] arm64: Make cpu_logical_map() take unsigned int

2020-11-16 Thread David Brazdil
CPU index should never be negative. Change the signature of
(set_)cpu_logical_map to take an unsigned int.

Signed-off-by: David Brazdil 
---
 arch/arm64/include/asm/smp.h | 4 ++--
 arch/arm64/kernel/setup.c| 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/arch/arm64/include/asm/smp.h b/arch/arm64/include/asm/smp.h
index 2e7f529ec5a6..bcb01ca15325 100644
--- a/arch/arm64/include/asm/smp.h
+++ b/arch/arm64/include/asm/smp.h
@@ -46,9 +46,9 @@ DECLARE_PER_CPU_READ_MOSTLY(int, cpu_number);
  * Logical CPU mapping.
  */
 extern u64 __cpu_logical_map[NR_CPUS];
-extern u64 cpu_logical_map(int cpu);
+extern u64 cpu_logical_map(unsigned int cpu);
 
-static inline void set_cpu_logical_map(int cpu, u64 hwid)
+static inline void set_cpu_logical_map(unsigned int cpu, u64 hwid)
 {
__cpu_logical_map[cpu] = hwid;
 }
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
index 133257ffd859..2f2973bc67c7 100644
--- a/arch/arm64/kernel/setup.c
+++ b/arch/arm64/kernel/setup.c
@@ -276,7 +276,7 @@ arch_initcall(reserve_memblock_reserved_regions);
 
 u64 __cpu_logical_map[NR_CPUS] = { [0 ... NR_CPUS-1] = INVALID_HWID };
 
-u64 cpu_logical_map(int cpu)
+u64 cpu_logical_map(unsigned int cpu)
 {
return __cpu_logical_map[cpu];
 }
-- 
2.29.2.299.gdc1121823c-goog

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v2 00/24] Opt-in always-on nVHE hypervisor

2020-11-16 Thread David Brazdil
As we progress towards being able to keep guest state private to the
host running nVHE hypervisor, this series allows the hypervisor to
install itself on newly booted CPUs before the host is allowed to run
on them.

All functionality described below is opt-in, guarded by an early param
'kvm-arm.protected'. Future patches specific to the new "protected" mode
should be hidden behind the same param.

The hypervisor starts trapping host SMCs and intercepting host's PSCI
CPU_ON/SUSPEND calls. It replaces the host's entry point with its own,
initializes the EL2 state of the new CPU and installs the nVHE hyp vector
before ERETing to the host's entry point.

The kernel checks new cores' features against the finalized system
capabilities. To avoid the need to move this code/data to EL2, the
implementation only allows to boot cores that were online at the time of
KVM initialization and therefore had been checked already.

Other PSCI SMCs are forwarded to EL3, though only the known set of SMCs
implemented in the kernel is allowed. Non-PSCI SMCs are also forwarded
to EL3. Future changes will need to ensure the safety of all SMCs wrt.
private guests.

The host is still allowed to reset EL2 back to the stub vector, eg. for
hibernation or kexec, but will not disable nVHE when there are no VMs.

Tested on Rock Pi 4b, based on 5.10-rc4.

changes since v1:
  * early param sets a capability instead of a static key
  * assume SMCCC v1.2 for host SMC forwarding
  * fix reserved SMC ID range for PSCI
  * split init_el2_state into smaller macros, move to el2_setup.h
  * many small cleanups

changes since RFC:
  * add early param to make features opt-in
  * simplify CPU_ON/SUSPEND implementation
  * replace spinlocks with CAS atomic
  * make cpu_logical_map ro_after_init

David Brazdil (24):
  psci: Support psci_ops.get_version for v0.1
  psci: Accessor for configured PSCI function IDs
  arm64: Make cpu_logical_map() take unsigned int
  arm64: Move MAIR_EL1_SET to asm/memory.h
  kvm: arm64: Initialize MAIR_EL2 using a constant
  kvm: arm64: Move hyp-init params to a per-CPU struct
  kvm: arm64: Refactor handle_trap to use a switch
  kvm: arm64: Add SMC handler in nVHE EL2
  kvm: arm64: Add .hyp.data..ro_after_init ELF section
  kvm: arm64: Support per_cpu_ptr in nVHE hyp code
  kvm: arm64: Create nVHE copy of cpu_logical_map
  kvm: arm64: Bootstrap PSCI SMC handler in nVHE EL2
  kvm: arm64: Add offset for hyp VA <-> PA conversion
  kvm: arm64: Forward safe PSCI SMCs coming from host
  kvm: arm64: Extract parts of el2_setup into a macro
  kvm: arm64: Extract __do_hyp_init into a helper function
  kvm: arm64: Add CPU entry point in nVHE hyp
  kvm: arm64: Add function to enter host from KVM nVHE hyp code
  kvm: arm64: Intercept host's PSCI_CPU_ON SMCs
  kvm: arm64: Intercept host's CPU_SUSPEND PSCI SMCs
  kvm: arm64: Add kvm-arm.protected early kernel parameter
  kvm: arm64: Keep nVHE EL2 vector installed
  kvm: arm64: Trap host SMCs in protected mode.
  kvm: arm64: Fix EL2 mode availability checks

 arch/arm64/include/asm/cpucaps.h |   3 +-
 arch/arm64/include/asm/el2_setup.h   | 185 +
 arch/arm64/include/asm/kvm_arm.h |   1 +
 arch/arm64/include/asm/kvm_asm.h |  16 +-
 arch/arm64/include/asm/kvm_hyp.h |   8 +
 arch/arm64/include/asm/memory.h  |  29 ++-
 arch/arm64/include/asm/percpu.h  |   6 +
 arch/arm64/include/asm/sections.h|   1 +
 arch/arm64/include/asm/smp.h |   4 +-
 arch/arm64/include/asm/sysreg.h  |  30 +++
 arch/arm64/include/asm/virt.h|  26 +++
 arch/arm64/kernel/asm-offsets.c  |   5 +
 arch/arm64/kernel/cpufeature.c   |  29 +++
 arch/arm64/kernel/head.S | 144 ++---
 arch/arm64/kernel/image-vars.h   |   3 +
 arch/arm64/kernel/setup.c|   2 +-
 arch/arm64/kernel/vmlinux.lds.S  |  10 +
 arch/arm64/kvm/arm.c |  94 +++--
 arch/arm64/kvm/hyp/nvhe/Makefile |   3 +-
 arch/arm64/kvm/hyp/nvhe/host.S   |  47 +
 arch/arm64/kvm/hyp/nvhe/hyp-init.S   |  90 +++--
 arch/arm64/kvm/hyp/nvhe/hyp-main.c   |  47 -
 arch/arm64/kvm/hyp/nvhe/hyp-smp.c|  40 
 arch/arm64/kvm/hyp/nvhe/hyp.lds.S|   1 +
 arch/arm64/kvm/hyp/nvhe/psci-relay.c | 289 +++
 arch/arm64/kvm/hyp/nvhe/switch.c |   5 +-
 arch/arm64/kvm/va_layout.c   |  30 ++-
 arch/arm64/mm/proc.S |  15 +-
 drivers/firmware/psci/psci.c |  21 +-
 include/linux/psci.h |  10 +
 30 files changed, 977 insertions(+), 217 deletions(-)
 create mode 100644 arch/arm64/include/asm/el2_setup.h
 create mode 100644 arch/arm64/kvm/hyp/nvhe/hyp-smp.c
 create mode 100644 arch/arm64/kvm/hyp/nvhe/psci-relay.c

--
2.29.2.299.gdc1121823c-goog
___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v2 04/24] arm64: Move MAIR_EL1_SET to asm/memory.h

2020-11-16 Thread David Brazdil
KVM currently initializes MAIR_EL2 to the value of MAIR_EL1. In
preparation for initializing MAIR_EL2 before MAIR_EL1, move the constant
into a shared header file. Since it is used for EL1 and EL2, rename to
MAIR_ELx_SET.

Signed-off-by: David Brazdil 
---
 arch/arm64/include/asm/memory.h | 29 ++---
 arch/arm64/include/asm/sysreg.h | 30 ++
 arch/arm64/mm/proc.S| 15 +--
 3 files changed, 45 insertions(+), 29 deletions(-)

diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h
index cd61239bae8c..8ae8fd883a0c 100644
--- a/arch/arm64/include/asm/memory.h
+++ b/arch/arm64/include/asm/memory.h
@@ -13,6 +13,7 @@
 #include 
 #include 
 #include 
+#include 
 
 /*
  * Size of the PCI I/O space. This must remain a power of two so that
@@ -124,21 +125,6 @@
  */
 #define SEGMENT_ALIGN  SZ_64K
 
-/*
- * Memory types available.
- *
- * IMPORTANT: MT_NORMAL must be index 0 since vm_get_page_prot() may 'or' in
- *   the MT_NORMAL_TAGGED memory type for PROT_MTE mappings. Note
- *   that protection_map[] only contains MT_NORMAL attributes.
- */
-#define MT_NORMAL  0
-#define MT_NORMAL_TAGGED   1
-#define MT_NORMAL_NC   2
-#define MT_NORMAL_WT   3
-#define MT_DEVICE_nGnRnE   4
-#define MT_DEVICE_nGnRE5
-#define MT_DEVICE_GRE  6
-
 /*
  * Memory types for Stage-2 translation
  */
@@ -152,6 +138,19 @@
 #define MT_S2_FWB_NORMAL   6
 #define MT_S2_FWB_DEVICE_nGnRE 1
 
+/*
+ * Default MAIR_EL1. MT_NORMAL_TAGGED is initially mapped as Normal memory and
+ * changed during __cpu_setup to Normal Tagged if the system supports MTE.
+ */
+#define MAIR_ELx_SET   \
+   (MAIR_ATTRIDX(MAIR_ATTR_DEVICE_nGnRnE, MT_DEVICE_nGnRnE) |  \
+MAIR_ATTRIDX(MAIR_ATTR_DEVICE_nGnRE, MT_DEVICE_nGnRE) |\
+MAIR_ATTRIDX(MAIR_ATTR_DEVICE_GRE, MT_DEVICE_GRE) |\
+MAIR_ATTRIDX(MAIR_ATTR_NORMAL_NC, MT_NORMAL_NC) |  \
+MAIR_ATTRIDX(MAIR_ATTR_NORMAL, MT_NORMAL) |\
+MAIR_ATTRIDX(MAIR_ATTR_NORMAL_WT, MT_NORMAL_WT) |  \
+MAIR_ATTRIDX(MAIR_ATTR_NORMAL, MT_NORMAL_TAGGED))
+
 #ifdef CONFIG_ARM64_4K_PAGES
 #define IOREMAP_MAX_ORDER  (PUD_SHIFT)
 #else
diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
index e2ef4c2edf06..24e773414cb4 100644
--- a/arch/arm64/include/asm/sysreg.h
+++ b/arch/arm64/include/asm/sysreg.h
@@ -635,6 +635,34 @@
 /* Position the attr at the correct index */
 #define MAIR_ATTRIDX(attr, idx)((attr) << ((idx) * 8))
 
+/*
+ * Memory types available.
+ *
+ * IMPORTANT: MT_NORMAL must be index 0 since vm_get_page_prot() may 'or' in
+ *   the MT_NORMAL_TAGGED memory type for PROT_MTE mappings. Note
+ *   that protection_map[] only contains MT_NORMAL attributes.
+ */
+#define MT_NORMAL  0
+#define MT_NORMAL_TAGGED   1
+#define MT_NORMAL_NC   2
+#define MT_NORMAL_WT   3
+#define MT_DEVICE_nGnRnE   4
+#define MT_DEVICE_nGnRE5
+#define MT_DEVICE_GRE  6
+
+/*
+ * Default MAIR_ELx. MT_NORMAL_TAGGED is initially mapped as Normal memory and
+ * changed during __cpu_setup to Normal Tagged if the system supports MTE.
+ */
+#define MAIR_ELx_SET   \
+   (MAIR_ATTRIDX(MAIR_ATTR_DEVICE_nGnRnE, MT_DEVICE_nGnRnE) |  \
+MAIR_ATTRIDX(MAIR_ATTR_DEVICE_nGnRE, MT_DEVICE_nGnRE) |\
+MAIR_ATTRIDX(MAIR_ATTR_DEVICE_GRE, MT_DEVICE_GRE) |\
+MAIR_ATTRIDX(MAIR_ATTR_NORMAL_NC, MT_NORMAL_NC) |  \
+MAIR_ATTRIDX(MAIR_ATTR_NORMAL, MT_NORMAL) |\
+MAIR_ATTRIDX(MAIR_ATTR_NORMAL_WT, MT_NORMAL_WT) |  \
+MAIR_ATTRIDX(MAIR_ATTR_NORMAL, MT_NORMAL_TAGGED))
+
 /* id_aa64isar0 */
 #define ID_AA64ISAR0_RNDR_SHIFT60
 #define ID_AA64ISAR0_TLB_SHIFT 56
@@ -992,6 +1020,7 @@
 /* Safe value for MPIDR_EL1: Bit31:RES1, Bit30:U:0, Bit24:MT:0 */
 #define SYS_MPIDR_SAFE_VAL (BIT(31))
 
+#ifndef LINKER_SCRIPT
 #ifdef __ASSEMBLY__
 
.irp
num,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30
@@ -1109,5 +1138,6 @@
 })
 
 #endif
+#endif /* LINKER_SCRIPT */
 
 #endif /* __ASM_SYSREG_H */
diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S
index 23c326a06b2d..e3b9aa372b96 100644
--- a/arch/arm64/mm/proc.S
+++ b/arch/arm64/mm/proc.S
@@ -45,19 +45,6 @@
 #define TCR_KASAN_FLAGS 0
 #endif
 
-/*
- * Default MAIR_EL1. MT_NORMAL_TAGGED is initially mapped as Normal memory and
- * changed during __cpu_setup to Normal Tagged if the system supports MTE.
- */
-#define MAIR_EL1_SET   \
-   (MAIR_ATTRIDX(MAIR_ATTR_DEVICE_nGnRnE, MT_DEVICE_nGnRnE) |  \
-MAI

[PATCH v2 01/24] psci: Support psci_ops.get_version for v0.1

2020-11-16 Thread David Brazdil
KVM's host PSCI SMC filter needs to be aware of the PSCI version of the
system but currently it is impossible to distinguish between v0.1 and
PSCI disabled because both have get_version == NULL.

Populate get_version for v0.1 with a function that returns a constant.

psci_opt.get_version is currently unused so this has no effect on
existing functionality.

Signed-off-by: David Brazdil 
---
 drivers/firmware/psci/psci.c | 7 +++
 1 file changed, 7 insertions(+)

diff --git a/drivers/firmware/psci/psci.c b/drivers/firmware/psci/psci.c
index 00af99b6f97c..213c68418a65 100644
--- a/drivers/firmware/psci/psci.c
+++ b/drivers/firmware/psci/psci.c
@@ -146,6 +146,11 @@ static int psci_to_linux_errno(int errno)
return -EINVAL;
 }
 
+static u32 psci_get_version_0_1(void)
+{
+   return PSCI_VERSION(0, 1);
+}
+
 static u32 psci_get_version(void)
 {
return invoke_psci_fn(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0);
@@ -514,6 +519,8 @@ static int __init psci_0_1_init(struct device_node *np)
 
pr_info("Using PSCI v0.1 Function IDs from DT\n");
 
+   psci_ops.get_version = psci_get_version_0_1;
+
if (!of_property_read_u32(np, "cpu_suspend", &id)) {
psci_function_id[PSCI_FN_CPU_SUSPEND] = id;
psci_ops.cpu_suspend = psci_cpu_suspend;
-- 
2.29.2.299.gdc1121823c-goog

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v2 02/24] psci: Accessor for configured PSCI function IDs

2020-11-16 Thread David Brazdil
Function IDs used by PSCI are configurable for v0.1 via DT/APCI. If the
host is using PSCI v0.1, KVM's host PSCI proxy needs to use the same IDs.
Expose the array holding the information with a read-only accessor.

Signed-off-by: David Brazdil 
---
 drivers/firmware/psci/psci.c | 14 ++
 include/linux/psci.h | 10 ++
 2 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/drivers/firmware/psci/psci.c b/drivers/firmware/psci/psci.c
index 213c68418a65..d835f3d8b121 100644
--- a/drivers/firmware/psci/psci.c
+++ b/drivers/firmware/psci/psci.c
@@ -58,16 +58,14 @@ typedef unsigned long (psci_fn)(unsigned long, unsigned 
long,
unsigned long, unsigned long);
 static psci_fn *invoke_psci_fn;
 
-enum psci_function {
-   PSCI_FN_CPU_SUSPEND,
-   PSCI_FN_CPU_ON,
-   PSCI_FN_CPU_OFF,
-   PSCI_FN_MIGRATE,
-   PSCI_FN_MAX,
-};
-
 static u32 psci_function_id[PSCI_FN_MAX];
 
+u32 psci_get_function_id(enum psci_function fn)
+{
+   WARN_ON(fn >= PSCI_FN_MAX);
+   return psci_function_id[fn];
+}
+
 #define PSCI_0_2_POWER_STATE_MASK  \
(PSCI_0_2_POWER_STATE_ID_MASK | \
PSCI_0_2_POWER_STATE_TYPE_MASK | \
diff --git a/include/linux/psci.h b/include/linux/psci.h
index 2a1bfb890e58..5b49a5c82d6f 100644
--- a/include/linux/psci.h
+++ b/include/linux/psci.h
@@ -21,6 +21,16 @@ bool psci_power_state_is_valid(u32 state);
 int psci_set_osi_mode(bool enable);
 bool psci_has_osi_support(void);
 
+enum psci_function {
+   PSCI_FN_CPU_SUSPEND,
+   PSCI_FN_CPU_ON,
+   PSCI_FN_CPU_OFF,
+   PSCI_FN_MIGRATE,
+   PSCI_FN_MAX,
+};
+
+u32 psci_get_function_id(enum psci_function fn);
+
 struct psci_operations {
u32 (*get_version)(void);
int (*cpu_suspend)(u32 state, unsigned long entry_point);
-- 
2.29.2.299.gdc1121823c-goog

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


Re: [PATCH v1 10/24] kvm: arm64: Extract parts of el2_setup into a macro

2020-11-16 Thread David Brazdil
Hi Marc,

> > +* Check for VHE being present. x2 being non-zero indicates that we
> > +* do have VHE, and that the kernel is intended to run at EL2.
> >  */
> > mrs x2, id_aa64mmfr1_el1
> > ubfxx2, x2, #ID_AA64MMFR1_VHE_SHIFT, #4
> > -#else
> > -   mov x2, xzr
> > -#endif
> > +   cbz x2, el2_setup_nvhe
> 
> What initialises x2 to zero when CONFIG_VHE is disabled?

There is no need for x2 anymore, the nVHE code just falls through. Basically:

el2_setup:
< check CurrentEL >
b.eq1f
ret
1:
#ifdef CONFIG_VHE
< check has VHE >
cbz el2_setup_nvhe
< VHE setup >
ret
#endif
el2_setup_nvhe:
< nVHE setup >
eret

-David
___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


Re: [PATCH v11 05/13] vfio/pci: Register an iommu fault handler

2020-11-16 Thread kernel test robot
Hi Eric,

I love your patch! Perhaps something to improve:

[auto build test WARNING on v5.10-rc4]
[also build test WARNING on next-20201116]
[cannot apply to vfio/next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:
https://github.com/0day-ci/linux/commits/Eric-Auger/SMMUv3-Nested-Stage-Setup-VFIO-part/20201116-190742
base:09162bc32c880a791c6c0668ce0745cf7958f576
config: x86_64-allyesconfig (attached as .config)
compiler: gcc-9 (Debian 9.3.0-15) 9.3.0
reproduce (this is a W=1 build):
# 
https://github.com/0day-ci/linux/commit/747ef402696e1192684908ca99f06f3d68466c04
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review 
Eric-Auger/SMMUv3-Nested-Stage-Setup-VFIO-part/20201116-190742
git checkout 747ef402696e1192684908ca99f06f3d68466c04
# save the attached .config to linux build tree
make W=1 ARCH=x86_64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot 

All warnings (new ones prefixed by >>):

   In file included from include/linux/vfio.h:16,
from drivers/vfio/pci/vfio_pci.c:26:
   include/uapi/linux/vfio.h:1231:34: error: field 'config' has incomplete type
1231 |  struct iommu_pasid_table_config config; /* used on SET */
 |  ^~
>> drivers/vfio/pci/vfio_pci.c:339:5: warning: no previous prototype for 
>> 'vfio_pci_iommu_dev_fault_handler' [-Wmissing-prototypes]
 339 | int vfio_pci_iommu_dev_fault_handler(struct iommu_fault *fault, void 
*data)
 | ^~~~

vim +/vfio_pci_iommu_dev_fault_handler +339 drivers/vfio/pci/vfio_pci.c

   338  
 > 339  int vfio_pci_iommu_dev_fault_handler(struct iommu_fault *fault, void 
 > *data)
   340  {
   341  struct vfio_pci_device *vdev = (struct vfio_pci_device *)data;
   342  struct vfio_region_dma_fault *reg =
   343  (struct vfio_region_dma_fault *)vdev->fault_pages;
   344  struct iommu_fault *new;
   345  u32 head, tail, size;
   346  int ret = -EINVAL;
   347  
   348  
   349  if (WARN_ON(!reg))
   350  return ret;
   351  
   352  mutex_lock(&vdev->fault_queue_lock);
   353  
   354  head = reg->head;
   355  tail = reg->tail;
   356  size = reg->nb_entries;
   357  
   358  new = (struct iommu_fault *)(vdev->fault_pages + reg->offset +
   359   head * reg->entry_size);
   360  
   361  if (CIRC_SPACE(head, tail, size) < 1) {
   362  ret = -ENOSPC;
   363  goto unlock;
   364  }
   365  
   366  *new = *fault;
   367  reg->head = (head + 1) % size;
   368  ret = 0;
   369  unlock:
   370  mutex_unlock(&vdev->fault_queue_lock);
   371  return ret;
   372  }
   373  

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-...@lists.01.org


.config.gz
Description: application/gzip
___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


Re: [PATCH] memory: Skip dirty tracking for un-migratable memory regions

2020-11-16 Thread Paolo Bonzini

On 16/11/20 14:22, Zenghui Yu wrote:

It makes no sense to track dirty pages for those un-migratable memory
regions (e.g., Memory BAR region of the VFIO PCI device) and doing so
will potentially lead to some unpleasant issues during migration [1].

Skip dirty tracking for those regions by evaluating if the region is
migratable before setting dirty_log_mask (DIRTY_MEMORY_MIGRATION).

[1] https://lists.gnu.org/archive/html/qemu-devel/2020-11/msg03757.html

Signed-off-by: Zenghui Yu 
---
  softmmu/memory.c | 5 -
  1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/softmmu/memory.c b/softmmu/memory.c
index 71951fe4dc..aa393f1bb0 100644
--- a/softmmu/memory.c
+++ b/softmmu/memory.c
@@ -1806,7 +1806,10 @@ bool memory_region_is_ram_device(MemoryRegion *mr)
  uint8_t memory_region_get_dirty_log_mask(MemoryRegion *mr)
  {
  uint8_t mask = mr->dirty_log_mask;
-if (global_dirty_log && (mr->ram_block || memory_region_is_iommu(mr))) {
+RAMBlock *rb = mr->ram_block;
+
+if (global_dirty_log && ((rb && qemu_ram_is_migratable(rb)) ||
+ memory_region_is_iommu(mr))) {
  mask |= (1 << DIRTY_MEMORY_MIGRATION);
  }
  return mask;



Queued, thanks.

Paolo

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


Re: [PATCH] memory: Skip dirty tracking for un-migratable memory regions

2020-11-16 Thread Cornelia Huck
On Mon, 16 Nov 2020 21:22:10 +0800
Zenghui Yu  wrote:

> It makes no sense to track dirty pages for those un-migratable memory
> regions (e.g., Memory BAR region of the VFIO PCI device) and doing so
> will potentially lead to some unpleasant issues during migration [1].
> 
> Skip dirty tracking for those regions by evaluating if the region is
> migratable before setting dirty_log_mask (DIRTY_MEMORY_MIGRATION).
> 
> [1] https://lists.gnu.org/archive/html/qemu-devel/2020-11/msg03757.html
> 
> Signed-off-by: Zenghui Yu 
> ---
>  softmmu/memory.c | 5 -
>  1 file changed, 4 insertions(+), 1 deletion(-)
> 
> diff --git a/softmmu/memory.c b/softmmu/memory.c
> index 71951fe4dc..aa393f1bb0 100644
> --- a/softmmu/memory.c
> +++ b/softmmu/memory.c
> @@ -1806,7 +1806,10 @@ bool memory_region_is_ram_device(MemoryRegion *mr)
>  uint8_t memory_region_get_dirty_log_mask(MemoryRegion *mr)
>  {
>  uint8_t mask = mr->dirty_log_mask;
> -if (global_dirty_log && (mr->ram_block || memory_region_is_iommu(mr))) {
> +RAMBlock *rb = mr->ram_block;
> +
> +if (global_dirty_log && ((rb && qemu_ram_is_migratable(rb)) ||
> + memory_region_is_iommu(mr))) {
>  mask |= (1 << DIRTY_MEMORY_MIGRATION);
>  }
>  return mask;

Looks reasonable.

Reviewed-by: Cornelia Huck 

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


Re: [PATCH v12 15/15] iommu/smmuv3: Add PASID cache invalidation per PASID

2020-11-16 Thread kernel test robot
Hi Eric,

I love your patch! Perhaps something to improve:

[auto build test WARNING on iommu/next]
[also build test WARNING on linus/master v5.10-rc4 next-20201116]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:
https://github.com/0day-ci/linux/commits/Eric-Auger/SMMUv3-Nested-Stage-Setup-IOMMU-part/20201116-185039
base:   https://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu.git next
config: arm64-randconfig-r034-20201115 (attached as .config)
compiler: clang version 12.0.0 (https://github.com/llvm/llvm-project 
c044709b8fbea2a9a375e4173a6bd735f6866c0c)
reproduce (this is a W=1 build):
wget 
https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O 
~/bin/make.cross
chmod +x ~/bin/make.cross
# install arm64 cross compiling tool for clang build
# apt-get install binutils-aarch64-linux-gnu
# 
https://github.com/0day-ci/linux/commit/95e4ccc61b7a7c06e1e0c6c01f362d590136ad3c
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review 
Eric-Auger/SMMUv3-Nested-Stage-Setup-IOMMU-part/20201116-185039
git checkout 95e4ccc61b7a7c06e1e0c6c01f362d590136ad3c
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=arm64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot 

All warnings (new ones prefixed by >>):

>> drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c:3010:8: warning: logical not is 
>> only applied to the left hand side of this bitwise operator 
>> [-Wlogical-not-parentheses]
   if (!info->flags & IOMMU_INV_PASID_FLAGS_PASID)
   ^~
   drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c:3010:8: note: add parentheses 
after the '!' to evaluate the bitwise operator first
   if (!info->flags & IOMMU_INV_PASID_FLAGS_PASID)
   ^
()
   drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c:3010:8: note: add parentheses 
around left hand side expression to silence this warning
   if (!info->flags & IOMMU_INV_PASID_FLAGS_PASID)
   ^
   (   )
   1 warning generated.

vim +3010 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c

  2960  
  2961  static int
  2962  arm_smmu_cache_invalidate(struct iommu_domain *domain, struct device 
*dev,
  2963struct iommu_cache_invalidate_info *inv_info)
  2964  {
  2965  struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
  2966  struct arm_smmu_device *smmu = smmu_domain->smmu;
  2967  
  2968  if (smmu_domain->stage != ARM_SMMU_DOMAIN_NESTED)
  2969  return -EINVAL;
  2970  
  2971  if (!smmu)
  2972  return -EINVAL;
  2973  
  2974  if (inv_info->version != IOMMU_CACHE_INVALIDATE_INFO_VERSION_1)
  2975  return -EINVAL;
  2976  
  2977  if (inv_info->cache & IOMMU_CACHE_INV_TYPE_IOTLB) {
  2978  if (inv_info->granularity == IOMMU_INV_GRANU_PASID) {
  2979  struct iommu_inv_pasid_info *info =
  2980  &inv_info->granu.pasid_info;
  2981  
  2982  if (!(info->flags & 
IOMMU_INV_PASID_FLAGS_ARCHID) ||
  2983   (info->flags & 
IOMMU_INV_PASID_FLAGS_PASID))
  2984  return -EINVAL;
  2985  
  2986  __arm_smmu_tlb_inv_context(smmu_domain, 
info->archid);
  2987  
  2988  } else if (inv_info->granularity == 
IOMMU_INV_GRANU_ADDR) {
  2989  struct iommu_inv_addr_info *info = 
&inv_info->granu.addr_info;
  2990  size_t size = info->nb_granules * 
info->granule_size;
  2991  bool leaf = info->flags & 
IOMMU_INV_ADDR_FLAGS_LEAF;
  2992  
  2993  if (!(info->flags & 
IOMMU_INV_ADDR_FLAGS_ARCHID) ||
  2994   (info->flags & IOMMU_INV_ADDR_FLAGS_PASID))
  2995  return -EINVAL;
  2996  
  2997  __arm_smmu_tlb_inv_range(info->addr, size,
  2998   info->granule_size, 
leaf,
  2999smmu_domain, 
info->archid);
  3000  
  3001  arm_smmu_cmdq_issue_sync(smmu);
  3002  } else {
 

Re: [PATCH v1 13/24] kvm: arm64: Add CPU entry point in nVHE hyp

2020-11-16 Thread David Brazdil
> >  #ifdef CONFIG_CPU_PM
> >DEFINE(CPU_CTX_SP,   offsetof(struct cpu_suspend_ctx, sp));
> > diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-init.S
> > b/arch/arm64/kvm/hyp/nvhe/hyp-init.S
> > index 1697d25756e9..f999a35b2c8c 100644
> > --- a/arch/arm64/kvm/hyp/nvhe/hyp-init.S
> > +++ b/arch/arm64/kvm/hyp/nvhe/hyp-init.S
> > @@ -6,6 +6,7 @@
> > 
> >  #include 
> >  #include 
> > +#include 
> 
> This should probably be included from the file that provides init_el2_state.

Agreed. This is a workaround for the fact that the arm-gic* headers don't play
nice with each other (define the same constants). Including arm-gic-v3.h in
kvm_asm.h will trigger macro redefine warnings in vgic*-v2.c because they
include arm-gic.h.

Another option is to create a header just for el2 init. Would that be
preferable? Other ideas?

> 
> > 
> >  #include 
> >  #include 
> > @@ -159,6 +160,35 @@ alternative_else_nop_endif
> > ret
> >  SYM_CODE_END(___kvm_hyp_init)
> > 
> > +SYM_CODE_START(__kvm_hyp_cpu_entry)
> > +   msr SPsel, #1   // We want to use SP_EL{1,2}
> > +
> > +   /*
> > +* Check that the core was booted in EL2. Loop indefinitely if not
> > +* because it cannot be safely given to the host without installing
> > KVM.
> > +*/
> > +   mrs x1, CurrentEL
> > +   cmp x1, #CurrentEL_EL2
> > +   b.ne.
> 
> This is a bit brutal. Consider using a WFE/WFI loop as we have in other
> places already (see __secondary_too_slow for example).

Ack

> > +
> > +   /* Initialize EL2 CPU state to sane values. */
> > +   mov x29, x0
> > +   init_el2_state nvhe
> > +   mov x0, x29
> > +
> > +   /*
> > +* Load hyp VA of C entry function. Must do so before switching on the
> > +* MMU because the struct pointer is PA and not identity-mapped in
> > hyp.
> > +*/
> > +   ldr x29, [x0, #NVHE_INIT_PSCI_CPU_ENTRY_FN]
> > +
> > +   /* Enable MMU, set vectors and stack. */
> > +   bl  ___kvm_hyp_init
> > +
> > +   /* Leave idmap. */
> > +   br  x29
> 
> To a point I made against an earlier patch: psci_cpu_entry_fn seems to be a
> HYP
> VA, and really needs to be documented as such, because this is pretty hard
> to
> follow otherwise.
> 
> > +SYM_CODE_END(__kvm_hyp_cpu_entry)
> > +
> >  SYM_CODE_START(__kvm_handle_stub_hvc)
> > cmp x0, #HVC_SOFT_RESTART
> > b.ne1f
> 
> Thanks,
> 
> M.
> -- 
> Jazz is not dead. It just smells funny...
___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


Re: [PATCH v12 01/15] iommu: Introduce attach/detach_pasid_table API

2020-11-16 Thread kernel test robot
Hi Eric,

I love your patch! Perhaps something to improve:

[auto build test WARNING on iommu/next]
[also build test WARNING on linus/master v5.10-rc4 next-20201116]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:
https://github.com/0day-ci/linux/commits/Eric-Auger/SMMUv3-Nested-Stage-Setup-IOMMU-part/20201116-185039
base:   https://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu.git next
config: arm64-randconfig-r034-20201115 (attached as .config)
compiler: clang version 12.0.0 (https://github.com/llvm/llvm-project 
c044709b8fbea2a9a375e4173a6bd735f6866c0c)
reproduce (this is a W=1 build):
wget 
https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O 
~/bin/make.cross
chmod +x ~/bin/make.cross
# install arm64 cross compiling tool for clang build
# apt-get install binutils-aarch64-linux-gnu
# 
https://github.com/0day-ci/linux/commit/54be9a9e014a566f9c7640da201c24cfb1eda06e
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review 
Eric-Auger/SMMUv3-Nested-Stage-Setup-IOMMU-part/20201116-185039
git checkout 54be9a9e014a566f9c7640da201c24cfb1eda06e
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=arm64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot 

All warnings (new ones prefixed by >>):

>> drivers/iommu/iommu.c:2225:34: warning: overlapping comparisons always 
>> evaluate to false [-Wtautological-overlap-compare]
   if (pasid_table_data.config < 1 && pasid_table_data.config > 3)
   ^~
   1 warning generated.

vim +2225 drivers/iommu/iommu.c

  2182  
  2183  int iommu_uapi_attach_pasid_table(struct iommu_domain *domain,
  2184void __user *uinfo)
  2185  {
  2186  struct iommu_pasid_table_config pasid_table_data = { 0 };
  2187  u32 minsz;
  2188  
  2189  if (unlikely(!domain->ops->attach_pasid_table))
  2190  return -ENODEV;
  2191  
  2192  /*
  2193   * No new spaces can be added before the variable sized union, 
the
  2194   * minimum size is the offset to the union.
  2195   */
  2196  minsz = offsetof(struct iommu_pasid_table_config, vendor_data);
  2197  
  2198  /* Copy minsz from user to get flags and argsz */
  2199  if (copy_from_user(&pasid_table_data, uinfo, minsz))
  2200  return -EFAULT;
  2201  
  2202  /* Fields before the variable size union are mandatory */
  2203  if (pasid_table_data.argsz < minsz)
  2204  return -EINVAL;
  2205  
  2206  /* PASID and address granu require additional info beyond minsz 
*/
  2207  if (pasid_table_data.version != PASID_TABLE_CFG_VERSION_1)
  2208  return -EINVAL;
  2209  if (pasid_table_data.format == IOMMU_PASID_FORMAT_SMMUV3 &&
  2210  pasid_table_data.argsz <
  2211  offsetofend(struct iommu_pasid_table_config, 
vendor_data.smmuv3))
  2212  return -EINVAL;
  2213  
  2214  /*
  2215   * User might be using a newer UAPI header which has a larger 
data
  2216   * size, we shall support the existing flags within the current
  2217   * size. Copy the remaining user data _after_ minsz but not more
  2218   * than the current kernel supported size.
  2219   */
  2220  if (copy_from_user((void *)&pasid_table_data + minsz, uinfo + 
minsz,
  2221 min_t(u32, pasid_table_data.argsz, 
sizeof(pasid_table_data)) - minsz))
    return -EFAULT;
  2223  
  2224  /* Now the argsz is validated, check the content */
> 2225  if (pasid_table_data.config < 1 && pasid_table_data.config > 3)
  2226  return -EINVAL;
  2227  
  2228  return domain->ops->attach_pasid_table(domain, 
&pasid_table_data);
  2229  }
  2230  EXPORT_SYMBOL_GPL(iommu_uapi_attach_pasid_table);
  2231  

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-...@lists.01.org


.config.gz
Description: application/gzip
___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


Re: [PATCH 1/2] KVM: arm64: vgic: Forbid invalid userspace Redistributor accesses

2020-11-16 Thread Zenghui Yu

Hi Marc,

On 2020/11/16 22:10, Marc Zyngier wrote:

My take is that only if the "[Re]Distributor base address" is specified
in the system memory map, will the user-provided kvm_device_attr.offset
make sense. And we can then handle the access to the register which is
defined by "base address + offset".


I'd tend to agree, but it is just that this is a large change at -rc4.
I'd rather have a quick fix for 5.10, and a more invasive change for 5.11,
spanning all the possible vgic devices.


So you prefer fixing it by "return a value that doesn't have the Last
bit set" for v5.10? I'm ok with it and can send v2 for it.

Btw, looking again at the way we handle the user-reading of GICR_TYPER

vgic_mmio_read_v3r_typer(vcpu, addr, len)

it seems that @addr is actually the *offset* of GICR_TYPER (0x0008) and
@addr is unlikely to be equal to last_rdist_typer, which is the *GPA* of
the last RD. Looks like the user-reading of GICR_TYPER.Last is always
broken?


Thanks,
Zenghui
___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


Re: [PATCH 1/2] KVM: arm64: vgic: Forbid invalid userspace Redistributor accesses

2020-11-16 Thread Marc Zyngier

On 2020-11-16 13:09, Zenghui Yu wrote:

Hi Marc,

On 2020/11/16 1:04, Marc Zyngier wrote:

Hi Zenghui,

On 2020-11-13 14:28, Zenghui Yu wrote:
It's expected that users will access registers in the redistributor 
*if*
the RD has been initialized properly. Unfortunately userspace can be 
bogus
enough to access registers before setting the RD base address, and 
KVM
implicitly allows it (we handle the access anyway, regardless of 
whether

the base address is set).

Bad thing happens when we're handling the user read of GICR_TYPER. We 
end

up with an oops when deferencing the unset rdreg...

gpa_t last_rdist_typer = rdreg->base + GICR_TYPER +
    (rdreg->free_index - 1) * KVM_VGIC_V3_REDIST_SIZE;

Fix this issue by informing userspace what had gone wrong (-ENXIO).


I'm worried about the "implicit" aspect of the access that this patch
now forbids.

The problem is that the existing documentation doesn't cover this 
case, > and -ENXIO's "Getting or setting this register is not yet 
supported"

is way too vague.


Indeed. How about changing to

-ENXIO  Getting or setting this register is not yet supported
or VGIC not properly configured (e.g., [Re]Distributor base
address is unknown)


Looks OK to me.




There is a precedent with the ITS, but that's undocumented
as well. Also, how about v2? If that's the wasy we are going to fix 
this,

we also nned to beef up the documentation.


Sure, I plan to do so and hope it won't break the existing userspace.


Well, at this stage we can only hope.



Of course, the other horrible way to address the issue is to return a 
value
that doesn't have the Last bit set, since we can't synthetise it. It 
doesn't
change the userspace API, and I can even find some (admittedly  
twisted)

logic to it (since there is no base address, there is no last RD...).


I'm fine with it. But I'm afraid that there might be other issues due 
to
the "unexpected" accesses since I haven't tested with all registers 
from

userspace.


I have had a look at the weekend, and couldn't see any other other GICR
register that would suffer from rdreg being NULL. I haven't looked at
GICD, but I don't anticipate anything bad on that front.


My take is that only if the "[Re]Distributor base address" is specified
in the system memory map, will the user-provided kvm_device_attr.offset
make sense. And we can then handle the access to the register which is
defined by "base address + offset".


I'd tend to agree, but it is just that this is a large change at -rc4.
I'd rather have a quick fix for 5.10, and a more invasive change for 
5.11,

spanning all the possible vgic devices.

M.
--
Jazz is not dead. It just smells funny...
___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


Re: [PATCH v11 05/13] vfio/pci: Register an iommu fault handler

2020-11-16 Thread kernel test robot
Hi Eric,

I love your patch! Perhaps something to improve:

[auto build test WARNING on v5.10-rc4]
[also build test WARNING on next-20201116]
[cannot apply to vfio/next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:
https://github.com/0day-ci/linux/commits/Eric-Auger/SMMUv3-Nested-Stage-Setup-VFIO-part/20201116-190742
base:09162bc32c880a791c6c0668ce0745cf7958f576
config: powerpc64-randconfig-r026-20201116 (attached as .config)
compiler: clang version 12.0.0 (https://github.com/llvm/llvm-project 
c044709b8fbea2a9a375e4173a6bd735f6866c0c)
reproduce (this is a W=1 build):
wget 
https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O 
~/bin/make.cross
chmod +x ~/bin/make.cross
# install powerpc64 cross compiling tool for clang build
# apt-get install binutils-powerpc64-linux-gnu
# 
https://github.com/0day-ci/linux/commit/747ef402696e1192684908ca99f06f3d68466c04
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review 
Eric-Auger/SMMUv3-Nested-Stage-Setup-VFIO-part/20201116-190742
git checkout 747ef402696e1192684908ca99f06f3d68466c04
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross 
ARCH=powerpc64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot 

All warnings (new ones prefixed by >>):

   In file included from drivers/vfio/pci/vfio_pci.c:26:
   In file included from include/linux/vfio.h:16:
   include/uapi/linux/vfio.h:1231:34: error: field has incomplete type 'struct 
iommu_pasid_table_config'
   struct iommu_pasid_table_config config; /* used on SET */
   ^
   include/uapi/linux/vfio.h:1231:9: note: forward declaration of 'struct 
iommu_pasid_table_config'
   struct iommu_pasid_table_config config; /* used on SET */
  ^
>> drivers/vfio/pci/vfio_pci.c:339:5: warning: no previous prototype for 
>> function 'vfio_pci_iommu_dev_fault_handler' [-Wmissing-prototypes]
   int vfio_pci_iommu_dev_fault_handler(struct iommu_fault *fault, void *data)
   ^
   drivers/vfio/pci/vfio_pci.c:339:1: note: declare 'static' if the function is 
not intended to be used outside of this translation unit
   int vfio_pci_iommu_dev_fault_handler(struct iommu_fault *fault, void *data)
   ^
   static 
   1 warning and 1 error generated.

vim +/vfio_pci_iommu_dev_fault_handler +339 drivers/vfio/pci/vfio_pci.c

   338  
 > 339  int vfio_pci_iommu_dev_fault_handler(struct iommu_fault *fault, void 
 > *data)
   340  {
   341  struct vfio_pci_device *vdev = (struct vfio_pci_device *)data;
   342  struct vfio_region_dma_fault *reg =
   343  (struct vfio_region_dma_fault *)vdev->fault_pages;
   344  struct iommu_fault *new;
   345  u32 head, tail, size;
   346  int ret = -EINVAL;
   347  
   348  
   349  if (WARN_ON(!reg))
   350  return ret;
   351  
   352  mutex_lock(&vdev->fault_queue_lock);
   353  
   354  head = reg->head;
   355  tail = reg->tail;
   356  size = reg->nb_entries;
   357  
   358  new = (struct iommu_fault *)(vdev->fault_pages + reg->offset +
   359   head * reg->entry_size);
   360  
   361  if (CIRC_SPACE(head, tail, size) < 1) {
   362  ret = -ENOSPC;
   363  goto unlock;
   364  }
   365  
   366  *new = *fault;
   367  reg->head = (head + 1) % size;
   368  ret = 0;
   369  unlock:
   370  mutex_unlock(&vdev->fault_queue_lock);
   371  return ret;
   372  }
   373  

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-...@lists.01.org


.config.gz
Description: application/gzip
___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH] memory: Skip dirty tracking for un-migratable memory regions

2020-11-16 Thread Zenghui Yu
It makes no sense to track dirty pages for those un-migratable memory
regions (e.g., Memory BAR region of the VFIO PCI device) and doing so
will potentially lead to some unpleasant issues during migration [1].

Skip dirty tracking for those regions by evaluating if the region is
migratable before setting dirty_log_mask (DIRTY_MEMORY_MIGRATION).

[1] https://lists.gnu.org/archive/html/qemu-devel/2020-11/msg03757.html

Signed-off-by: Zenghui Yu 
---
 softmmu/memory.c | 5 -
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/softmmu/memory.c b/softmmu/memory.c
index 71951fe4dc..aa393f1bb0 100644
--- a/softmmu/memory.c
+++ b/softmmu/memory.c
@@ -1806,7 +1806,10 @@ bool memory_region_is_ram_device(MemoryRegion *mr)
 uint8_t memory_region_get_dirty_log_mask(MemoryRegion *mr)
 {
 uint8_t mask = mr->dirty_log_mask;
-if (global_dirty_log && (mr->ram_block || memory_region_is_iommu(mr))) {
+RAMBlock *rb = mr->ram_block;
+
+if (global_dirty_log && ((rb && qemu_ram_is_migratable(rb)) ||
+ memory_region_is_iommu(mr))) {
 mask |= (1 << DIRTY_MEMORY_MIGRATION);
 }
 return mask;
-- 
2.19.1

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


Re: [PATCH 1/2] KVM: arm64: vgic: Forbid invalid userspace Redistributor accesses

2020-11-16 Thread Zenghui Yu

Hi Marc,

On 2020/11/16 1:04, Marc Zyngier wrote:

Hi Zenghui,

On 2020-11-13 14:28, Zenghui Yu wrote:

It's expected that users will access registers in the redistributor *if*
the RD has been initialized properly. Unfortunately userspace can be 
bogus

enough to access registers before setting the RD base address, and KVM
implicitly allows it (we handle the access anyway, regardless of whether
the base address is set).

Bad thing happens when we're handling the user read of GICR_TYPER. We end
up with an oops when deferencing the unset rdreg...

gpa_t last_rdist_typer = rdreg->base + GICR_TYPER +
    (rdreg->free_index - 1) * KVM_VGIC_V3_REDIST_SIZE;

Fix this issue by informing userspace what had gone wrong (-ENXIO).


I'm worried about the "implicit" aspect of the access that this patch
now forbids.

The problem is that the existing documentation doesn't cover this case, > and -ENXIO's 
"Getting or setting this register is not yet supported"
is way too vague.


Indeed. How about changing to

-ENXIO  Getting or setting this register is not yet supported
or VGIC not properly configured (e.g., [Re]Distributor base
address is unknown)

There is a precedent with the ITS, but that's 
undocumented

as well. Also, how about v2? If that's the wasy we are going to fix this,
we also nned to beef up the documentation.


Sure, I plan to do so and hope it won't break the existing userspace.


Of course, the other horrible way to address the issue is to return a value
that doesn't have the Last bit set, since we can't synthetise it. It 
doesn't

change the userspace API, and I can even find some (admittedly  twisted)
logic to it (since there is no base address, there is no last RD...).


I'm fine with it. But I'm afraid that there might be other issues due to
the "unexpected" accesses since I haven't tested with all registers from
userspace.

My take is that only if the "[Re]Distributor base address" is specified
in the system memory map, will the user-provided kvm_device_attr.offset
make sense. And we can then handle the access to the register which is
defined by "base address + offset".


Thanks,
Zenghui
___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


Re: [PATCH v1 13/24] kvm: arm64: Add CPU entry point in nVHE hyp

2020-11-16 Thread Marc Zyngier

On 2020-11-16 11:49, David Brazdil wrote:

>  #ifdef CONFIG_CPU_PM
>DEFINE(CPU_CTX_SP,  offsetof(struct cpu_suspend_ctx, sp));
> diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-init.S
> b/arch/arm64/kvm/hyp/nvhe/hyp-init.S
> index 1697d25756e9..f999a35b2c8c 100644
> --- a/arch/arm64/kvm/hyp/nvhe/hyp-init.S
> +++ b/arch/arm64/kvm/hyp/nvhe/hyp-init.S
> @@ -6,6 +6,7 @@
>
>  #include 
>  #include 
> +#include 

This should probably be included from the file that provides 
init_el2_state.


Agreed. This is a workaround for the fact that the arm-gic* headers 
don't play

nice with each other (define the same constants).


Ah, that...


Including arm-gic-v3.h in
kvm_asm.h will trigger macro redefine warnings in vgic*-v2.c because 
they

include arm-gic.h.


Boo.


Another option is to create a header just for el2 init. Would that be
preferable? Other ideas?


Having an asm/el2_setup.h file feels like a better option. After all, it
is in no way KVM specific, and having the macros hanging around in
asm/kvm_asm.h feels odd.

M.
--
Jazz is not dead. It just smells funny...
___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v11 12/13] vfio/pci: Register a DMA fault response region

2020-11-16 Thread Eric Auger
In preparation for vSVA, let's register a DMA fault response region,
where the userspace will push the page responses and increment the
head of the buffer. The kernel will pop those responses and inject them
on iommu side.

Signed-off-by: Eric Auger 
---
 drivers/vfio/pci/vfio_pci.c | 114 +---
 drivers/vfio/pci/vfio_pci_private.h |   5 ++
 drivers/vfio/pci/vfio_pci_rdwr.c|  39 ++
 include/uapi/linux/vfio.h   |  32 
 4 files changed, 181 insertions(+), 9 deletions(-)

diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index 65a83fd0e8c0..e9a904ce3f0d 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -318,9 +318,20 @@ static void vfio_pci_dma_fault_release(struct 
vfio_pci_device *vdev,
kfree(vdev->fault_pages);
 }
 
-static int vfio_pci_dma_fault_mmap(struct vfio_pci_device *vdev,
-  struct vfio_pci_region *region,
-  struct vm_area_struct *vma)
+static void
+vfio_pci_dma_fault_response_release(struct vfio_pci_device *vdev,
+   struct vfio_pci_region *region)
+{
+   if (vdev->dma_fault_response_wq)
+   destroy_workqueue(vdev->dma_fault_response_wq);
+   kfree(vdev->fault_response_pages);
+   vdev->fault_response_pages = NULL;
+}
+
+static int __vfio_pci_dma_fault_mmap(struct vfio_pci_device *vdev,
+struct vfio_pci_region *region,
+struct vm_area_struct *vma,
+u8 *pages)
 {
u64 phys_len, req_len, pgoff, req_start;
unsigned long long addr;
@@ -333,14 +344,14 @@ static int vfio_pci_dma_fault_mmap(struct vfio_pci_device 
*vdev,
((1U << (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT)) - 1);
req_start = pgoff << PAGE_SHIFT;
 
-   /* only the second page of the producer fault region is mmappable */
+   /* only the second page of the fault region is mmappable */
if (req_start < PAGE_SIZE)
return -EINVAL;
 
if (req_start + req_len > phys_len)
return -EINVAL;
 
-   addr = virt_to_phys(vdev->fault_pages);
+   addr = virt_to_phys(pages);
vma->vm_private_data = vdev;
vma->vm_pgoff = (addr >> PAGE_SHIFT) + pgoff;
 
@@ -349,13 +360,29 @@ static int vfio_pci_dma_fault_mmap(struct vfio_pci_device 
*vdev,
return ret;
 }
 
-static int vfio_pci_dma_fault_add_capability(struct vfio_pci_device *vdev,
-struct vfio_pci_region *region,
-struct vfio_info_cap *caps)
+static int vfio_pci_dma_fault_mmap(struct vfio_pci_device *vdev,
+  struct vfio_pci_region *region,
+  struct vm_area_struct *vma)
+{
+   return __vfio_pci_dma_fault_mmap(vdev, region, vma, vdev->fault_pages);
+}
+
+static int
+vfio_pci_dma_fault_response_mmap(struct vfio_pci_device *vdev,
+   struct vfio_pci_region *region,
+   struct vm_area_struct *vma)
+{
+   return __vfio_pci_dma_fault_mmap(vdev, region, vma, 
vdev->fault_response_pages);
+}
+
+static int __vfio_pci_dma_fault_add_capability(struct vfio_pci_device *vdev,
+  struct vfio_pci_region *region,
+  struct vfio_info_cap *caps,
+  u32 cap_id)
 {
struct vfio_region_info_cap_sparse_mmap *sparse = NULL;
struct vfio_region_info_cap_fault cap = {
-   .header.id = VFIO_REGION_INFO_CAP_DMA_FAULT,
+   .header.id = cap_id,
.header.version = 1,
.version = 1,
};
@@ -383,6 +410,14 @@ static int vfio_pci_dma_fault_add_capability(struct 
vfio_pci_device *vdev,
return ret;
 }
 
+static int vfio_pci_dma_fault_add_capability(struct vfio_pci_device *vdev,
+struct vfio_pci_region *region,
+struct vfio_info_cap *caps)
+{
+   return __vfio_pci_dma_fault_add_capability(vdev, region, caps,
+  
VFIO_REGION_INFO_CAP_DMA_FAULT);
+}
+
 static const struct vfio_pci_regops vfio_pci_dma_fault_regops = {
.rw = vfio_pci_dma_fault_rw,
.release= vfio_pci_dma_fault_release,
@@ -390,6 +425,13 @@ static const struct vfio_pci_regops 
vfio_pci_dma_fault_regops = {
.add_capability = vfio_pci_dma_fault_add_capability,
 };
 
+static const struct vfio_pci_regops vfio_pci_dma_fault_response_regops = {
+   .rw = vfio_pci_dma_fault_response_rw,
+   .release= vfio_pci_dma_fault_response_release,
+   .mmap   = vfio_pci_dma_fault_response_mmap,
+   .add_

[PATCH v11 13/13] vfio/pci: Inject page response upon response region fill

2020-11-16 Thread Eric Auger
When the userspace increments the head of the page response
buffer ring, let's push the response into the iommu layer.
This is done through a workqueue that pops the responses from
the ring buffer and increment the tail.

Signed-off-by: Eric Auger 
---
 drivers/vfio/pci/vfio_pci.c | 40 +
 drivers/vfio/pci/vfio_pci_private.h |  8 ++
 drivers/vfio/pci/vfio_pci_rdwr.c|  1 +
 3 files changed, 49 insertions(+)

diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index e9a904ce3f0d..beea70d70151 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -542,6 +542,32 @@ static int vfio_pci_dma_fault_init(struct vfio_pci_device 
*vdev)
return ret;
 }
 
+static void dma_response_inject(struct work_struct *work)
+{
+   struct vfio_pci_dma_fault_response_work *rwork =
+   container_of(work, struct vfio_pci_dma_fault_response_work, 
inject);
+   struct vfio_region_dma_fault_response *header = rwork->header;
+   struct vfio_pci_device *vdev = rwork->vdev;
+   struct iommu_page_response *resp;
+   u32 tail, head, size;
+
+   mutex_lock(&vdev->fault_response_queue_lock);
+
+   tail = header->tail;
+   head = header->head;
+   size = header->nb_entries;
+
+   while (CIRC_CNT(head, tail, size) >= 1) {
+   resp = (struct iommu_page_response 
*)(vdev->fault_response_pages + header->offset +
+   tail * header->entry_size);
+
+   /* TODO: properly handle the return value */
+   iommu_page_response(&vdev->pdev->dev, resp);
+   header->tail = tail = (tail + 1) % size;
+   }
+   mutex_unlock(&vdev->fault_response_queue_lock);
+}
+
 #define DMA_FAULT_RESPONSE_RING_LENGTH 512
 
 static int vfio_pci_dma_fault_response_init(struct vfio_pci_device *vdev)
@@ -585,8 +611,22 @@ static int vfio_pci_dma_fault_response_init(struct 
vfio_pci_device *vdev)
header->nb_entries = DMA_FAULT_RESPONSE_RING_LENGTH;
header->offset = PAGE_SIZE;
 
+   vdev->response_work = kzalloc(sizeof(*vdev->response_work), GFP_KERNEL);
+   if (!vdev->response_work)
+   goto out;
+   vdev->response_work->header = header;
+   vdev->response_work->vdev = vdev;
+
+   /* launch the thread that will extract the response */
+   INIT_WORK(&vdev->response_work->inject, dma_response_inject);
+   vdev->dma_fault_response_wq =
+   create_singlethread_workqueue("vfio-dma-fault-response");
+   if (!vdev->dma_fault_response_wq)
+   return -ENOMEM;
+
return 0;
 out:
+   kfree(vdev->fault_response_pages);
vdev->fault_response_pages = NULL;
return ret;
 }
diff --git a/drivers/vfio/pci/vfio_pci_private.h 
b/drivers/vfio/pci/vfio_pci_private.h
index 035634521cd0..5944f96ced0c 100644
--- a/drivers/vfio/pci/vfio_pci_private.h
+++ b/drivers/vfio/pci/vfio_pci_private.h
@@ -52,6 +52,12 @@ struct vfio_pci_irq_ctx {
struct irq_bypass_producer  producer;
 };
 
+struct vfio_pci_dma_fault_response_work {
+   struct work_struct inject;
+   struct vfio_region_dma_fault_response *header;
+   struct vfio_pci_device *vdev;
+};
+
 struct vfio_pci_device;
 struct vfio_pci_region;
 
@@ -145,6 +151,8 @@ struct vfio_pci_device {
struct eventfd_ctx  *req_trigger;
u8  *fault_pages;
u8  *fault_response_pages;
+   struct workqueue_struct *dma_fault_response_wq;
+   struct vfio_pci_dma_fault_response_work *response_work;
struct mutexfault_queue_lock;
struct mutexfault_response_queue_lock;
struct list_headdummy_resources_list;
diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c
index efde0793360b..78c494fe35cc 100644
--- a/drivers/vfio/pci/vfio_pci_rdwr.c
+++ b/drivers/vfio/pci/vfio_pci_rdwr.c
@@ -430,6 +430,7 @@ size_t vfio_pci_dma_fault_response_rw(struct 
vfio_pci_device *vdev, char __user
mutex_lock(&vdev->fault_response_queue_lock);
header->head = new_head;
mutex_unlock(&vdev->fault_response_queue_lock);
+   queue_work(vdev->dma_fault_response_wq, 
&vdev->response_work->inject);
} else {
if (copy_to_user(buf, base + pos, count))
return -EFAULT;
-- 
2.21.3

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v11 07/13] vfio: Use capability chains to handle device specific irq

2020-11-16 Thread Eric Auger
From: Tina Zhang 

Caps the number of irqs with fixed indexes and uses capability chains
to chain device specific irqs.

Signed-off-by: Tina Zhang 
Signed-off-by: Eric Auger 
[Eric: Put cap_offset at the end of the vfio_irq_info struct,
remove GFX IRQ at the moment and remove any reference to this latter
in the commit message]

---
---
 include/uapi/linux/vfio.h | 19 ++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
index 629dfb38d9e7..0e2bfbeccd08 100644
--- a/include/uapi/linux/vfio.h
+++ b/include/uapi/linux/vfio.h
@@ -701,11 +701,27 @@ struct vfio_irq_info {
 #define VFIO_IRQ_INFO_MASKABLE (1 << 1)
 #define VFIO_IRQ_INFO_AUTOMASKED   (1 << 2)
 #define VFIO_IRQ_INFO_NORESIZE (1 << 3)
+#define VFIO_IRQ_INFO_FLAG_CAPS(1 << 4) /* Info supports caps 
*/
__u32   index;  /* IRQ index */
__u32   count;  /* Number of IRQs within this index */
+   __u32   cap_offset; /* Offset within info struct of first cap */
 };
 #define VFIO_DEVICE_GET_IRQ_INFO   _IO(VFIO_TYPE, VFIO_BASE + 9)
 
+/*
+ * The irq type capability allows IRQs unique to a specific device or
+ * class of devices to be exposed.
+ *
+ * The structures below define version 1 of this capability.
+ */
+#define VFIO_IRQ_INFO_CAP_TYPE  3
+
+struct vfio_irq_info_cap_type {
+   struct vfio_info_cap_header header;
+   __u32 type; /* global per bus driver */
+   __u32 subtype;  /* type specific */
+};
+
 /**
  * VFIO_DEVICE_SET_IRQS - _IOW(VFIO_TYPE, VFIO_BASE + 10, struct vfio_irq_set)
  *
@@ -807,7 +823,8 @@ enum {
VFIO_PCI_MSIX_IRQ_INDEX,
VFIO_PCI_ERR_IRQ_INDEX,
VFIO_PCI_REQ_IRQ_INDEX,
-   VFIO_PCI_NUM_IRQS
+   VFIO_PCI_NUM_IRQS = 5   /* Fixed user ABI, IRQ indexes >=5 use   */
+   /* device specific cap to define content */
 };
 
 /*
-- 
2.21.3

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v11 05/13] vfio/pci: Register an iommu fault handler

2020-11-16 Thread Eric Auger
Register an IOMMU fault handler which records faults in
the DMA FAULT region ring buffer. In a subsequent patch, we
will add the signaling of a specific eventfd to allow the
userspace to be notified whenever a new fault as shown up.

Signed-off-by: Eric Auger 

---
v11 -> v12:
- take the fault_queue_lock before reading header (Zenghui)
- also record recoverable errors

v10 -> v11:
- move iommu_unregister_device_fault_handler into
  vfio_pci_disable
- check fault_pages != 0

v8 -> v9:
- handler now takes an iommu_fault handle
- eventfd signaling moved to a subsequent patch
- check the fault type and return an error if != UNRECOV
- still the fault handler registration can fail. We need to
  reach an agreement about how to deal with the situation

v3 -> v4:
- move iommu_unregister_device_fault_handler to vfio_pci_release
---
 drivers/vfio/pci/vfio_pci.c | 45 +
 1 file changed, 45 insertions(+)

diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index 7546a81e7fb6..b39d6ed66c71 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -27,6 +27,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #include "vfio_pci_private.h"
 
@@ -335,6 +336,41 @@ static const struct vfio_pci_regops 
vfio_pci_dma_fault_regops = {
.add_capability = vfio_pci_dma_fault_add_capability,
 };
 
+int vfio_pci_iommu_dev_fault_handler(struct iommu_fault *fault, void *data)
+{
+   struct vfio_pci_device *vdev = (struct vfio_pci_device *)data;
+   struct vfio_region_dma_fault *reg =
+   (struct vfio_region_dma_fault *)vdev->fault_pages;
+   struct iommu_fault *new;
+   u32 head, tail, size;
+   int ret = -EINVAL;
+
+
+   if (WARN_ON(!reg))
+   return ret;
+
+   mutex_lock(&vdev->fault_queue_lock);
+
+   head = reg->head;
+   tail = reg->tail;
+   size = reg->nb_entries;
+
+   new = (struct iommu_fault *)(vdev->fault_pages + reg->offset +
+head * reg->entry_size);
+
+   if (CIRC_SPACE(head, tail, size) < 1) {
+   ret = -ENOSPC;
+   goto unlock;
+   }
+
+   *new = *fault;
+   reg->head = (head + 1) % size;
+   ret = 0;
+unlock:
+   mutex_unlock(&vdev->fault_queue_lock);
+   return ret;
+}
+
 #define DMA_FAULT_RING_LENGTH 512
 
 static int vfio_pci_dma_fault_init(struct vfio_pci_device *vdev)
@@ -376,6 +412,13 @@ static int vfio_pci_dma_fault_init(struct vfio_pci_device 
*vdev)
header->entry_size = sizeof(struct iommu_fault);
header->nb_entries = DMA_FAULT_RING_LENGTH;
header->offset = sizeof(struct vfio_region_dma_fault);
+
+   ret = iommu_register_device_fault_handler(&vdev->pdev->dev,
+   vfio_pci_iommu_dev_fault_handler,
+   vdev);
+   if (ret) /* the dma fault region is freed in vfio_pci_disable() */
+   goto out;
+
return 0;
 out:
kfree(vdev->fault_pages);
@@ -508,6 +551,8 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev)
VFIO_IRQ_SET_ACTION_TRIGGER,
vdev->irq_type, 0, 0, NULL);
 
+   WARN_ON(iommu_unregister_device_fault_handler(&vdev->pdev->dev));
+
/* Device closed, don't need mutex here */
list_for_each_entry_safe(ioeventfd, ioeventfd_tmp,
 &vdev->ioeventfds_list, next) {
-- 
2.21.3

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v11 08/13] vfio/pci: Add framework for custom interrupt indices

2020-11-16 Thread Eric Auger
Implement IRQ capability chain infrastructure. All interrupt
indexes beyond VFIO_PCI_NUM_IRQS are handled as extended
interrupts. They are registered with a specific type/subtype
and supported flags.

Signed-off-by: Eric Auger 
---
 drivers/vfio/pci/vfio_pci.c | 99 +++--
 drivers/vfio/pci/vfio_pci_intrs.c   | 62 ++
 drivers/vfio/pci/vfio_pci_private.h | 14 
 3 files changed, 157 insertions(+), 18 deletions(-)

diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index 2a6cc1a87323..93e03a4a5f32 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -608,6 +608,14 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev)
 
WARN_ON(iommu_unregister_device_fault_handler(&vdev->pdev->dev));
 
+   for (i = 0; i < vdev->num_ext_irqs; i++)
+   vfio_pci_set_irqs_ioctl(vdev, VFIO_IRQ_SET_DATA_NONE |
+   VFIO_IRQ_SET_ACTION_TRIGGER,
+   VFIO_PCI_NUM_IRQS + i, 0, 0, NULL);
+   vdev->num_ext_irqs = 0;
+   kfree(vdev->ext_irqs);
+   vdev->ext_irqs = NULL;
+
/* Device closed, don't need mutex here */
list_for_each_entry_safe(ioeventfd, ioeventfd_tmp,
 &vdev->ioeventfds_list, next) {
@@ -823,6 +831,9 @@ static int vfio_pci_get_irq_count(struct vfio_pci_device 
*vdev, int irq_type)
return 1;
} else if (irq_type == VFIO_PCI_REQ_IRQ_INDEX) {
return 1;
+   } else if (irq_type >= VFIO_PCI_NUM_IRQS &&
+  irq_type < VFIO_PCI_NUM_IRQS + vdev->num_ext_irqs) {
+   return 1;
}
 
return 0;
@@ -1008,7 +1019,7 @@ static long vfio_pci_ioctl(void *device_data,
info.flags |= VFIO_DEVICE_FLAGS_RESET;
 
info.num_regions = VFIO_PCI_NUM_REGIONS + vdev->num_regions;
-   info.num_irqs = VFIO_PCI_NUM_IRQS;
+   info.num_irqs = VFIO_PCI_NUM_IRQS + vdev->num_ext_irqs;
 
if (IS_ENABLED(CONFIG_VFIO_PCI_ZDEV)) {
int ret = vfio_pci_info_zdev_add_caps(vdev, &caps);
@@ -1187,36 +1198,87 @@ static long vfio_pci_ioctl(void *device_data,
 
} else if (cmd == VFIO_DEVICE_GET_IRQ_INFO) {
struct vfio_irq_info info;
+   struct vfio_info_cap caps = { .buf = NULL, .size = 0 };
+   unsigned long capsz;
 
minsz = offsetofend(struct vfio_irq_info, count);
 
+   /* For backward compatibility, cannot require this */
+   capsz = offsetofend(struct vfio_irq_info, cap_offset);
+
if (copy_from_user(&info, (void __user *)arg, minsz))
return -EFAULT;
 
-   if (info.argsz < minsz || info.index >= VFIO_PCI_NUM_IRQS)
+   if (info.argsz < minsz ||
+   info.index >= VFIO_PCI_NUM_IRQS + vdev->num_ext_irqs)
return -EINVAL;
 
-   switch (info.index) {
-   case VFIO_PCI_INTX_IRQ_INDEX ... VFIO_PCI_MSIX_IRQ_INDEX:
-   case VFIO_PCI_REQ_IRQ_INDEX:
-   break;
-   case VFIO_PCI_ERR_IRQ_INDEX:
-   if (pci_is_pcie(vdev->pdev))
-   break;
-   fallthrough;
-   default:
-   return -EINVAL;
-   }
+   if (info.argsz >= capsz)
+   minsz = capsz;
 
info.flags = VFIO_IRQ_INFO_EVENTFD;
 
-   info.count = vfio_pci_get_irq_count(vdev, info.index);
-
-   if (info.index == VFIO_PCI_INTX_IRQ_INDEX)
+   switch (info.index) {
+   case VFIO_PCI_INTX_IRQ_INDEX:
info.flags |= (VFIO_IRQ_INFO_MASKABLE |
   VFIO_IRQ_INFO_AUTOMASKED);
-   else
+   break;
+   case VFIO_PCI_MSI_IRQ_INDEX ... VFIO_PCI_MSIX_IRQ_INDEX:
+   case VFIO_PCI_REQ_IRQ_INDEX:
info.flags |= VFIO_IRQ_INFO_NORESIZE;
+   break;
+   case VFIO_PCI_ERR_IRQ_INDEX:
+   info.flags |= VFIO_IRQ_INFO_NORESIZE;
+   if (!pci_is_pcie(vdev->pdev))
+   return -EINVAL;
+   break;
+   default:
+   {
+   struct vfio_irq_info_cap_type cap_type = {
+   .header.id = VFIO_IRQ_INFO_CAP_TYPE,
+   .header.version = 1 };
+   int ret, i;
+
+   if (info.index >= VFIO_PCI_NUM_IRQS +
+   vdev->num_ext_irqs)
+   return -EINVAL;
+   info.index = array_index_nospec(info.index,
+

[PATCH v11 06/13] vfio/pci: Allow to mmap the fault queue

2020-11-16 Thread Eric Auger
The DMA FAULT region contains the fault ring buffer.
There is benefit to let the userspace mmap this area.
Expose this mmappable area through a sparse mmap entry
and implement the mmap operation.

Signed-off-by: Eric Auger 

---

v8 -> v9:
- remove unused index local variable in vfio_pci_fault_mmap
---
 drivers/vfio/pci/vfio_pci.c | 61 +++--
 1 file changed, 58 insertions(+), 3 deletions(-)

diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index b39d6ed66c71..2a6cc1a87323 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -318,21 +318,75 @@ static void vfio_pci_dma_fault_release(struct 
vfio_pci_device *vdev,
kfree(vdev->fault_pages);
 }
 
+static int vfio_pci_dma_fault_mmap(struct vfio_pci_device *vdev,
+  struct vfio_pci_region *region,
+  struct vm_area_struct *vma)
+{
+   u64 phys_len, req_len, pgoff, req_start;
+   unsigned long long addr;
+   unsigned int ret;
+
+   phys_len = region->size;
+
+   req_len = vma->vm_end - vma->vm_start;
+   pgoff = vma->vm_pgoff &
+   ((1U << (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT)) - 1);
+   req_start = pgoff << PAGE_SHIFT;
+
+   /* only the second page of the producer fault region is mmappable */
+   if (req_start < PAGE_SIZE)
+   return -EINVAL;
+
+   if (req_start + req_len > phys_len)
+   return -EINVAL;
+
+   addr = virt_to_phys(vdev->fault_pages);
+   vma->vm_private_data = vdev;
+   vma->vm_pgoff = (addr >> PAGE_SHIFT) + pgoff;
+
+   ret = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+ req_len, vma->vm_page_prot);
+   return ret;
+}
+
 static int vfio_pci_dma_fault_add_capability(struct vfio_pci_device *vdev,
 struct vfio_pci_region *region,
 struct vfio_info_cap *caps)
 {
+   struct vfio_region_info_cap_sparse_mmap *sparse = NULL;
struct vfio_region_info_cap_fault cap = {
.header.id = VFIO_REGION_INFO_CAP_DMA_FAULT,
.header.version = 1,
.version = 1,
};
-   return vfio_info_add_capability(caps, &cap.header, sizeof(cap));
+   size_t size = sizeof(*sparse) + sizeof(*sparse->areas);
+   int ret;
+
+   ret = vfio_info_add_capability(caps, &cap.header, sizeof(cap));
+   if (ret)
+   return ret;
+
+   sparse = kzalloc(size, GFP_KERNEL);
+   if (!sparse)
+   return -ENOMEM;
+
+   sparse->header.id = VFIO_REGION_INFO_CAP_SPARSE_MMAP;
+   sparse->header.version = 1;
+   sparse->nr_areas = 1;
+   sparse->areas[0].offset = PAGE_SIZE;
+   sparse->areas[0].size = region->size - PAGE_SIZE;
+
+   ret = vfio_info_add_capability(caps, &sparse->header, size);
+   if (ret)
+   kfree(sparse);
+
+   return ret;
 }
 
 static const struct vfio_pci_regops vfio_pci_dma_fault_regops = {
.rw = vfio_pci_dma_fault_rw,
.release= vfio_pci_dma_fault_release,
+   .mmap   = vfio_pci_dma_fault_mmap,
.add_capability = vfio_pci_dma_fault_add_capability,
 };
 
@@ -403,7 +457,8 @@ static int vfio_pci_dma_fault_init(struct vfio_pci_device 
*vdev)
VFIO_REGION_TYPE_NESTED,
VFIO_REGION_SUBTYPE_NESTED_DMA_FAULT,
&vfio_pci_dma_fault_regops, size,
-   VFIO_REGION_INFO_FLAG_READ | VFIO_REGION_INFO_FLAG_WRITE,
+   VFIO_REGION_INFO_FLAG_READ | VFIO_REGION_INFO_FLAG_WRITE |
+   VFIO_REGION_INFO_FLAG_MMAP,
vdev->fault_pages);
if (ret)
goto out;
@@ -411,7 +466,7 @@ static int vfio_pci_dma_fault_init(struct vfio_pci_device 
*vdev)
header = (struct vfio_region_dma_fault *)vdev->fault_pages;
header->entry_size = sizeof(struct iommu_fault);
header->nb_entries = DMA_FAULT_RING_LENGTH;
-   header->offset = sizeof(struct vfio_region_dma_fault);
+   header->offset = PAGE_SIZE;
 
ret = iommu_register_device_fault_handler(&vdev->pdev->dev,
vfio_pci_iommu_dev_fault_handler,
-- 
2.21.3

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v11 04/13] vfio/pci: Add VFIO_REGION_TYPE_NESTED region type

2020-11-16 Thread Eric Auger
Add a new specific DMA_FAULT region aiming to exposed nested mode
translation faults. This region only is exposed if the device
is attached to a nested domain.

The region has a ring buffer that contains the actual fault
records plus a header allowing to handle it (tail/head indices,
max capacity, entry size). At the moment the region is dimensionned
for 512 fault records.

Signed-off-by: Eric Auger 

---
v11 -> v12:
- set fault_pages to NULL after free
- check new_tail >= header->nb_entries (Zenghui)

v10 -> v11:
- rename vfio_pci_init_dma_fault_region into
  vfio_pci_dma_fault_init
- free fault_pages in vfio_pci_dma_fault_release
- only register the region if the device is attached
  to a nested domain

v8 -> v9:
- Use a single region instead of a prod/cons region

v4 -> v5
- check cons is not null in vfio_pci_check_cons_fault

v3 -> v4:
- use 2 separate regions, respectively in read and write modes
- add the version capability
---
 drivers/vfio/pci/vfio_pci.c | 76 +
 drivers/vfio/pci/vfio_pci_private.h |  6 +++
 drivers/vfio/pci/vfio_pci_rdwr.c| 44 +
 include/uapi/linux/vfio.h   | 34 +
 4 files changed, 160 insertions(+)

diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index e6190173482c..7546a81e7fb6 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -311,6 +311,78 @@ int vfio_pci_set_power_state(struct vfio_pci_device *vdev, 
pci_power_t state)
return ret;
 }
 
+static void vfio_pci_dma_fault_release(struct vfio_pci_device *vdev,
+  struct vfio_pci_region *region)
+{
+   kfree(vdev->fault_pages);
+}
+
+static int vfio_pci_dma_fault_add_capability(struct vfio_pci_device *vdev,
+struct vfio_pci_region *region,
+struct vfio_info_cap *caps)
+{
+   struct vfio_region_info_cap_fault cap = {
+   .header.id = VFIO_REGION_INFO_CAP_DMA_FAULT,
+   .header.version = 1,
+   .version = 1,
+   };
+   return vfio_info_add_capability(caps, &cap.header, sizeof(cap));
+}
+
+static const struct vfio_pci_regops vfio_pci_dma_fault_regops = {
+   .rw = vfio_pci_dma_fault_rw,
+   .release= vfio_pci_dma_fault_release,
+   .add_capability = vfio_pci_dma_fault_add_capability,
+};
+
+#define DMA_FAULT_RING_LENGTH 512
+
+static int vfio_pci_dma_fault_init(struct vfio_pci_device *vdev)
+{
+   struct vfio_region_dma_fault *header;
+   struct iommu_domain *domain;
+   size_t size;
+   bool nested;
+   int ret;
+
+   domain = iommu_get_domain_for_dev(&vdev->pdev->dev);
+   ret = iommu_domain_get_attr(domain, DOMAIN_ATTR_NESTING, &nested);
+   if (ret || !nested)
+   return ret;
+
+   mutex_init(&vdev->fault_queue_lock);
+
+   /*
+* We provision 1 page for the header and space for
+* DMA_FAULT_RING_LENGTH fault records in the ring buffer.
+*/
+   size = ALIGN(sizeof(struct iommu_fault) *
+DMA_FAULT_RING_LENGTH, PAGE_SIZE) + PAGE_SIZE;
+
+   vdev->fault_pages = kzalloc(size, GFP_KERNEL);
+   if (!vdev->fault_pages)
+   return -ENOMEM;
+
+   ret = vfio_pci_register_dev_region(vdev,
+   VFIO_REGION_TYPE_NESTED,
+   VFIO_REGION_SUBTYPE_NESTED_DMA_FAULT,
+   &vfio_pci_dma_fault_regops, size,
+   VFIO_REGION_INFO_FLAG_READ | VFIO_REGION_INFO_FLAG_WRITE,
+   vdev->fault_pages);
+   if (ret)
+   goto out;
+
+   header = (struct vfio_region_dma_fault *)vdev->fault_pages;
+   header->entry_size = sizeof(struct iommu_fault);
+   header->nb_entries = DMA_FAULT_RING_LENGTH;
+   header->offset = sizeof(struct vfio_region_dma_fault);
+   return 0;
+out:
+   kfree(vdev->fault_pages);
+   vdev->fault_pages = NULL;
+   return ret;
+}
+
 static int vfio_pci_enable(struct vfio_pci_device *vdev)
 {
struct pci_dev *pdev = vdev->pdev;
@@ -409,6 +481,10 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev)
}
}
 
+   ret = vfio_pci_dma_fault_init(vdev);
+   if (ret)
+   goto disable_exit;
+
vfio_pci_probe_mmaps(vdev);
 
return 0;
diff --git a/drivers/vfio/pci/vfio_pci_private.h 
b/drivers/vfio/pci/vfio_pci_private.h
index 5c90e560c5c7..1d9b0f648133 100644
--- a/drivers/vfio/pci/vfio_pci_private.h
+++ b/drivers/vfio/pci/vfio_pci_private.h
@@ -134,6 +134,8 @@ struct vfio_pci_device {
int ioeventfds_nr;
struct eventfd_ctx  *err_trigger;
struct eventfd_ctx  *req_trigger;
+   u8  *fault_pages;
+   struct mutexfault_queue_lock;
struct list_headdummy_resources_list;
struct mutexioeventfds_lock;
st

[PATCH v11 10/13] vfio/pci: Register and allow DMA FAULT IRQ signaling

2020-11-16 Thread Eric Auger
Register the VFIO_IRQ_TYPE_NESTED/VFIO_IRQ_SUBTYPE_DMA_FAULT
IRQ that allows to signal a nested mode DMA fault.

Signed-off-by: Eric Auger 

---

v10 -> v11:
- the irq now is registered in vfio_pci_dma_fault_init()
  in case the domain is nested
---
 drivers/vfio/pci/vfio_pci.c | 21 -
 1 file changed, 20 insertions(+), 1 deletion(-)

diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index 93e03a4a5f32..65a83fd0e8c0 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -397,6 +397,7 @@ int vfio_pci_iommu_dev_fault_handler(struct iommu_fault 
*fault, void *data)
(struct vfio_region_dma_fault *)vdev->fault_pages;
struct iommu_fault *new;
u32 head, tail, size;
+   int ext_irq_index;
int ret = -EINVAL;
 
 
@@ -422,7 +423,19 @@ int vfio_pci_iommu_dev_fault_handler(struct iommu_fault 
*fault, void *data)
ret = 0;
 unlock:
mutex_unlock(&vdev->fault_queue_lock);
-   return ret;
+   if (ret)
+   return ret;
+
+   ext_irq_index = vfio_pci_get_ext_irq_index(vdev, VFIO_IRQ_TYPE_NESTED,
+  VFIO_IRQ_SUBTYPE_DMA_FAULT);
+   if (ext_irq_index < 0)
+   return -EINVAL;
+
+   mutex_lock(&vdev->igate);
+   if (vdev->ext_irqs[ext_irq_index].trigger)
+   eventfd_signal(vdev->ext_irqs[ext_irq_index].trigger, 1);
+   mutex_unlock(&vdev->igate);
+   return 0;
 }
 
 #define DMA_FAULT_RING_LENGTH 512
@@ -474,6 +487,12 @@ static int vfio_pci_dma_fault_init(struct vfio_pci_device 
*vdev)
if (ret) /* the dma fault region is freed in vfio_pci_disable() */
goto out;
 
+   ret = vfio_pci_register_irq(vdev, VFIO_IRQ_TYPE_NESTED,
+   VFIO_IRQ_SUBTYPE_DMA_FAULT,
+   VFIO_IRQ_INFO_EVENTFD);
+   if (ret) /* the fault handler is also freed in vfio_pci_disable() */
+   goto out;
+
return 0;
 out:
kfree(vdev->fault_pages);
-- 
2.21.3

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v11 09/13] vfio: Add new IRQ for DMA fault reporting

2020-11-16 Thread Eric Auger
Add a new IRQ type/subtype to get notification on nested
stage DMA faults.

Signed-off-by: Eric Auger 
---
 include/uapi/linux/vfio.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
index 0e2bfbeccd08..1e5c82f9d14d 100644
--- a/include/uapi/linux/vfio.h
+++ b/include/uapi/linux/vfio.h
@@ -722,6 +722,9 @@ struct vfio_irq_info_cap_type {
__u32 subtype;  /* type specific */
 };
 
+#define VFIO_IRQ_TYPE_NESTED   (1)
+#define VFIO_IRQ_SUBTYPE_DMA_FAULT (1)
+
 /**
  * VFIO_DEVICE_SET_IRQS - _IOW(VFIO_TYPE, VFIO_BASE + 10, struct vfio_irq_set)
  *
-- 
2.21.3

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v11 11/13] vfio: Document nested stage control

2020-11-16 Thread Eric Auger
The VFIO API was enhanced to support nested stage control: a bunch of
new iotcls, one DMA FAULT region and an associated specific IRQ.

Let's document the process to follow to set up nested mode.

Signed-off-by: Eric Auger 

---

v11 -> v12:
s/VFIO_REGION_INFO_CAP_PRODUCER_FAULT/VFIO_REGION_INFO_CAP_DMA_FAULT

v8 -> v9:
- new names for SET_MSI_BINDING and SET_PASID_TABLE
- new layout for the DMA FAULT memory region and specific IRQ

v2 -> v3:
- document the new fault API

v1 -> v2:
- use the new ioctl names
- add doc related to fault handling
---
 Documentation/driver-api/vfio.rst | 77 +++
 1 file changed, 77 insertions(+)

diff --git a/Documentation/driver-api/vfio.rst 
b/Documentation/driver-api/vfio.rst
index f1a4d3c3ba0b..14e41324237d 100644
--- a/Documentation/driver-api/vfio.rst
+++ b/Documentation/driver-api/vfio.rst
@@ -239,6 +239,83 @@ group and can access them as follows::
/* Gratuitous device reset and go... */
ioctl(device, VFIO_DEVICE_RESET);
 
+IOMMU Dual Stage Control
+
+
+Some IOMMUs support 2 stages/levels of translation. "Stage" corresponds to
+the ARM terminology while "level" corresponds to Intel's VTD terminology. In
+the following text we use either without distinction.
+
+This is useful when the guest is exposed with a virtual IOMMU and some
+devices are assigned to the guest through VFIO. Then the guest OS can use
+stage 1 (IOVA -> GPA), while the hypervisor uses stage 2 for VM isolation
+(GPA -> HPA).
+
+The guest gets ownership of the stage 1 page tables and also owns stage 1
+configuration structures. The hypervisor owns the root configuration structure
+(for security reason), including stage 2 configuration. This works as long
+configuration structures and page table format are compatible between the
+virtual IOMMU and the physical IOMMU.
+
+Assuming the HW supports it, this nested mode is selected by choosing the
+VFIO_TYPE1_NESTING_IOMMU type through:
+
+ioctl(container, VFIO_SET_IOMMU, VFIO_TYPE1_NESTING_IOMMU);
+
+This forces the hypervisor to use the stage 2, leaving stage 1 available for
+guest usage.
+
+Once groups are attached to the container, the guest stage 1 translation
+configuration data can be passed to VFIO by using
+
+ioctl(container, VFIO_IOMMU_SET_PASID_TABLE, &pasid_table_info);
+
+This allows to combine the guest stage 1 configuration structure along with
+the hypervisor stage 2 configuration structure. Stage 1 configuration
+structures are dependent on the IOMMU type.
+
+As the stage 1 translation is fully delegated to the HW, translation faults
+encountered during the translation process need to be propagated up to
+the virtualizer and re-injected into the guest.
+
+The userspace must be prepared to receive faults. The VFIO-PCI device
+exposes one dedicated DMA FAULT region: it contains a ring buffer and
+its header that allows to manage the head/tail indices. The region is
+identified by the following index/subindex:
+- VFIO_REGION_TYPE_NESTED/VFIO_REGION_SUBTYPE_NESTED_DMA_FAULT
+
+The DMA FAULT region exposes a VFIO_REGION_INFO_CAP_DMA_FAULT
+region capability that allows the userspace to retrieve the ABI version
+of the fault records filled by the host.
+
+On top of that region, the userspace can be notified whenever a fault
+occurs at the physical level. It can use the VFIO_IRQ_TYPE_NESTED/
+VFIO_IRQ_SUBTYPE_DMA_FAULT specific IRQ to attach the eventfd to be
+signalled.
+
+The ring buffer containing the fault records can be mmapped. When
+the userspace consumes a fault in the queue, it should increment
+the consumer index to allow new fault records to replace the used ones.
+
+The queue size and the entry size can be retrieved in the header.
+The tail index should never overshoot the producer index as in any
+other circular buffer scheme. Also it must be less than the queue size
+otherwise the change fails.
+
+When the guest invalidates stage 1 related caches, invalidations must be
+forwarded to the host through
+ioctl(container, VFIO_IOMMU_CACHE_INVALIDATE, &inv_data);
+Those invalidations can happen at various granularity levels, page, context, 
...
+
+The ARM SMMU specification introduces another challenge: MSIs are translated by
+both the virtual SMMU and the physical SMMU. To build a nested mapping for the
+IOVA programmed into the assigned device, the guest needs to pass its IOVA/MSI
+doorbell GPA binding to the host. Then the hypervisor can build a nested stage 
2
+binding eventually translating into the physical MSI doorbell.
+
+This is achieved by calling
+ioctl(container, VFIO_IOMMU_SET_MSI_BINDING, &guest_binding);
+
 VFIO User API
 ---
 
-- 
2.21.3

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v11 00/13] SMMUv3 Nested Stage Setup (VFIO part)

2020-11-16 Thread Eric Auger
This series brings the VFIO part of HW nested paging support
in the SMMUv3.

This is a rebase on top of v5.10-rc4

The series depends on:
[PATCH v12 00/15] SMMUv3 Nested Stage Setup (IOMMU part)

3 new IOCTLs are introduced that allow the userspace to
1) pass the guest stage 1 configuration
2) pass stage 1 MSI bindings
3) invalidate stage 1 related caches

They map onto the related new IOMMU API functions.

We introduce the capability to register specific interrupt
indexes (see [1]). A new DMA_FAULT interrupt index allows to register
an eventfd to be signaled whenever a stage 1 related fault
is detected at physical level. Also two specific regions allow to
- expose the fault records to the user space and
- inject page responses.

This latter functionality is not exercised in this series
but is provided as a POC for further vSVA activities (Shameer's input).

Best Regards

Eric

This series can be found at:
https://github.com/eauger/linux/tree/5.10-rc4-2stage-v12

The series series includes Tina's patch steming from
[1] "[RFC PATCH v2 1/3] vfio: Use capability chains to handle device
specific irq" plus patches originally contributed by Yi.

History:

v10 -> v11:
- rebase on top of v5.10-rc4
- adapt to changes on the IOMMU API (compliant with the doc
  written by Jacob/Yi)
- addition of the page response region
- Took into account Zenghui's comments
- In this version I have kept the ioctl separate. Since
  Yi's series [2] is currently stalled, I've just rebased here.

[2] [PATCH v7 00/16] vfio: expose virtual Shared Virtual Addressing
to VMs

v9 -> v10
- rebase on top of 5.6.0-rc3 (no change versus v9)

v8 -> v9:
- introduce specific irq framework
- single fault region
- iommu_unregister_device_fault_handler failure case not handled
  yet.

v7 -> v8:
- rebase on top of v5.2-rc1 and especially
  8be39a1a04c1  iommu/arm-smmu-v3: Add a master->domain pointer
- dynamic alloc of s1_cfg/s2_cfg
- __arm_smmu_tlb_inv_asid/s1_range_nosync
- check there is no HW MSI regions
- asid invalidation using pasid extended struct (change in the uapi)
- add s1_live/s2_live checks
- move check about support of nested stages in domain finalise
- fixes in error reporting according to the discussion with Robin
- reordered the patches to have first iommu/smmuv3 patches and then
  VFIO patches

v6 -> v7:
- removed device handle from bind/unbind_guest_msi
- added "iommu/smmuv3: Nested mode single MSI doorbell per domain
  enforcement"
- added few uapi comments as suggested by Jean, Jacop and Alex

v5 -> v6:
- Fix compilation issue when CONFIG_IOMMU_API is unset

v4 -> v5:
- fix bug reported by Vincent: fault handler unregistration now happens in
  vfio_pci_release
- IOMMU_FAULT_PERM_* moved outside of struct definition + small
  uapi changes suggested by Kean-Philippe (except fetch_addr)
- iommu: introduce device fault report API: removed the PRI part.
- see individual logs for more details
- reset the ste abort flag on detach

v3 -> v4:
- took into account Alex, jean-Philippe and Robin's comments on v3
- rework of the smmuv3 driver integration
- add tear down ops for msi binding and PASID table binding
- fix S1 fault propagation
- put fault reporting patches at the beginning of the series following
  Jean-Philippe's request
- update of the cache invalidate and fault API uapis
- VFIO fault reporting rework with 2 separate regions and one mmappable
  segment for the fault queue
- moved to PATCH

v2 -> v3:
- When registering the S1 MSI binding we now store the device handle. This
  addresses Robin's comment about discimination of devices beonging to
  different S1 groups and using different physical MSI doorbells.
- Change the fault reporting API: use VFIO_PCI_DMA_FAULT_IRQ_INDEX to
  set the eventfd and expose the faults through an mmappable fault region

v1 -> v2:
- Added the fault reporting capability
- asid properly passed on invalidation (fix assignment of multiple
  devices)
- see individual change logs for more info


Eric Auger (10):
  vfio: VFIO_IOMMU_SET_MSI_BINDING
  vfio/pci: Add VFIO_REGION_TYPE_NESTED region type
  vfio/pci: Register an iommu fault handler
  vfio/pci: Allow to mmap the fault queue
  vfio/pci: Add framework for custom interrupt indices
  vfio: Add new IRQ for DMA fault reporting
  vfio/pci: Register and allow DMA FAULT IRQ signaling
  vfio: Document nested stage control
  vfio/pci: Register a DMA fault response region
  vfio/pci: Inject page response upon response region fill

Liu, Yi L (2):
  vfio: VFIO_IOMMU_SET_PASID_TABLE
  vfio: VFIO_IOMMU_CACHE_INVALIDATE

Tina Zhang (1):
  vfio: Use capability chains to handle device specific irq

 Documentation/driver-api/vfio.rst   |  77 +
 drivers/vfio/pci/vfio_pci.c | 430 ++--
 drivers/vfio/pci/vfio_pci_intrs.c   |  62 
 drivers/vfio/pci/vfio_pci_private.h |  33 +++
 drivers/vfio/pci/vfio_pci_rdwr.c|  84 ++
 drivers/vfio/vfio_iommu_type1.c | 186 
 include/uapi/linux/vfio.h   | 140 -
 7

[PATCH v11 03/13] vfio: VFIO_IOMMU_SET_MSI_BINDING

2020-11-16 Thread Eric Auger
This patch adds the VFIO_IOMMU_SET_MSI_BINDING ioctl which aim
to (un)register the guest MSI binding to the host. This latter
then can use those stage 1 bindings to build a nested stage
binding targeting the physical MSIs.

Signed-off-by: Eric Auger 

---

v10 -> v11:
- renamed ustruct into msi_binding
- return 0 on unbind

v8 -> v9:
- merge VFIO_IOMMU_BIND_MSI/VFIO_IOMMU_UNBIND_MSI into a single
  VFIO_IOMMU_SET_MSI_BINDING ioctl
- ioctl id changed

v6 -> v7:
- removed the dev arg

v3 -> v4:
- add UNBIND
- unwind on BIND error

v2 -> v3:
- adapt to new proto of bind_guest_msi
- directly use vfio_iommu_for_each_dev

v1 -> v2:
- s/vfio_iommu_type1_guest_msi_binding/vfio_iommu_type1_bind_guest_msi
---
 drivers/vfio/vfio_iommu_type1.c | 63 +
 include/uapi/linux/vfio.h   | 20 +++
 2 files changed, 83 insertions(+)

diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index 966909f542f1..bb2bc0971fb0 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -2657,6 +2657,41 @@ static int vfio_cache_inv_fn(struct device *dev, void 
*data)
return iommu_uapi_cache_invalidate(dc->domain, dev, (void __user *)arg);
 }
 
+static int
+vfio_bind_msi(struct vfio_iommu *iommu,
+ dma_addr_t giova, phys_addr_t gpa, size_t size)
+{
+   struct vfio_domain *d;
+   int ret = 0;
+
+   mutex_lock(&iommu->lock);
+
+   list_for_each_entry(d, &iommu->domain_list, next) {
+   ret = iommu_bind_guest_msi(d->domain, giova, gpa, size);
+   if (ret)
+   goto unwind;
+   }
+   goto unlock;
+unwind:
+   list_for_each_entry_continue_reverse(d, &iommu->domain_list, next) {
+   iommu_unbind_guest_msi(d->domain, giova);
+   }
+unlock:
+   mutex_unlock(&iommu->lock);
+   return ret;
+}
+
+static void
+vfio_unbind_msi(struct vfio_iommu *iommu, dma_addr_t giova)
+{
+   struct vfio_domain *d;
+
+   mutex_lock(&iommu->lock);
+   list_for_each_entry(d, &iommu->domain_list, next)
+   iommu_unbind_guest_msi(d->domain, giova);
+   mutex_unlock(&iommu->lock);
+}
+
 static int vfio_iommu_migration_build_caps(struct vfio_iommu *iommu,
   struct vfio_info_cap *caps)
 {
@@ -2866,6 +2901,32 @@ static int vfio_iommu_type1_cache_invalidate(struct 
vfio_iommu *iommu,
return ret;
 }
 
+static int vfio_iommu_type1_set_msi_binding(struct vfio_iommu *iommu,
+   unsigned long arg)
+{
+   struct vfio_iommu_type1_set_msi_binding msi_binding;
+   unsigned long minsz;
+   int ret = -EINVAL;
+
+   minsz = offsetofend(struct vfio_iommu_type1_set_msi_binding,
+   size);
+
+   if (copy_from_user(&msi_binding, (void __user *)arg, minsz))
+   return -EFAULT;
+
+   if (msi_binding.argsz < minsz)
+   return -EINVAL;
+
+   if (msi_binding.flags == VFIO_IOMMU_UNBIND_MSI) {
+   vfio_unbind_msi(iommu, msi_binding.iova);
+   ret = 0;
+   } else if (msi_binding.flags == VFIO_IOMMU_BIND_MSI) {
+   ret = vfio_bind_msi(iommu, msi_binding.iova,
+   msi_binding.gpa, msi_binding.size);
+   }
+   return ret;
+}
+
 static int vfio_iommu_type1_dirty_pages(struct vfio_iommu *iommu,
unsigned long arg)
 {
@@ -2990,6 +3051,8 @@ static long vfio_iommu_type1_ioctl(void *iommu_data,
return vfio_iommu_type1_set_pasid_table(iommu, arg);
case VFIO_IOMMU_CACHE_INVALIDATE:
return vfio_iommu_type1_cache_invalidate(iommu, arg);
+   case VFIO_IOMMU_SET_MSI_BINDING:
+   return vfio_iommu_type1_set_msi_binding(iommu, arg);
default:
return -ENOTTY;
}
diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
index 0e6d94cc2ba4..b352e76cfb71 100644
--- a/include/uapi/linux/vfio.h
+++ b/include/uapi/linux/vfio.h
@@ -1212,6 +1212,26 @@ struct vfio_iommu_type1_cache_invalidate {
 };
 #define VFIO_IOMMU_CACHE_INVALIDATE  _IO(VFIO_TYPE, VFIO_BASE + 23)
 
+/**
+ * VFIO_IOMMU_SET_MSI_BINDING - _IOWR(VFIO_TYPE, VFIO_BASE + 24,
+ * struct vfio_iommu_type1_set_msi_binding)
+ *
+ * Pass a stage 1 MSI doorbell mapping to the host so that this
+ * latter can build a nested stage2 mapping. Or conversely tear
+ * down a previously bound stage 1 MSI binding.
+ */
+struct vfio_iommu_type1_set_msi_binding {
+   __u32   argsz;
+   __u32   flags;
+#define VFIO_IOMMU_BIND_MSI(1 << 0)
+#define VFIO_IOMMU_UNBIND_MSI  (1 << 1)
+   __u64   iova;   /* MSI guest IOVA */
+   /* Fields below are used on BIND */
+   __u64   gpa;/* MSI guest physical address */
+   __u64   size;   /* size of stage1 mapping (bytes) */
+};
+#define VFIO_IOMMU_SET_MSI_BINDING  _IO(VFIO_TYPE, V

[PATCH v11 01/13] vfio: VFIO_IOMMU_SET_PASID_TABLE

2020-11-16 Thread Eric Auger
From: "Liu, Yi L" 

This patch adds an VFIO_IOMMU_SET_PASID_TABLE ioctl
which aims to pass the virtual iommu guest configuration
to the host. This latter takes the form of the so-called
PASID table.

Signed-off-by: Jacob Pan 
Signed-off-by: Liu, Yi L 
Signed-off-by: Eric Auger 

---
v11 -> v12:
- use iommu_uapi_set_pasid_table
- check SET and UNSET are not set simultaneously (Zenghui)

v8 -> v9:
- Merge VFIO_IOMMU_ATTACH/DETACH_PASID_TABLE into a single
  VFIO_IOMMU_SET_PASID_TABLE ioctl.

v6 -> v7:
- add a comment related to VFIO_IOMMU_DETACH_PASID_TABLE

v3 -> v4:
- restore ATTACH/DETACH
- add unwind on failure

v2 -> v3:
- s/BIND_PASID_TABLE/SET_PASID_TABLE

v1 -> v2:
- s/BIND_GUEST_STAGE/BIND_PASID_TABLE
- remove the struct device arg
---
 drivers/vfio/vfio_iommu_type1.c | 65 +
 include/uapi/linux/vfio.h   | 19 ++
 2 files changed, 84 insertions(+)

diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index 67e827638995..87ddd9e882dc 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -2587,6 +2587,41 @@ static int vfio_iommu_iova_build_caps(struct vfio_iommu 
*iommu,
return ret;
 }
 
+static void
+vfio_detach_pasid_table(struct vfio_iommu *iommu)
+{
+   struct vfio_domain *d;
+
+   mutex_lock(&iommu->lock);
+   list_for_each_entry(d, &iommu->domain_list, next)
+   iommu_detach_pasid_table(d->domain);
+
+   mutex_unlock(&iommu->lock);
+}
+
+static int
+vfio_attach_pasid_table(struct vfio_iommu *iommu, unsigned long arg)
+{
+   struct vfio_domain *d;
+   int ret = 0;
+
+   mutex_lock(&iommu->lock);
+
+   list_for_each_entry(d, &iommu->domain_list, next) {
+   ret = iommu_uapi_attach_pasid_table(d->domain, (void __user 
*)arg);
+   if (ret)
+   goto unwind;
+   }
+   goto unlock;
+unwind:
+   list_for_each_entry_continue_reverse(d, &iommu->domain_list, next) {
+   iommu_detach_pasid_table(d->domain);
+   }
+unlock:
+   mutex_unlock(&iommu->lock);
+   return ret;
+}
+
 static int vfio_iommu_migration_build_caps(struct vfio_iommu *iommu,
   struct vfio_info_cap *caps)
 {
@@ -2747,6 +2782,34 @@ static int vfio_iommu_type1_unmap_dma(struct vfio_iommu 
*iommu,
-EFAULT : 0;
 }
 
+static int vfio_iommu_type1_set_pasid_table(struct vfio_iommu *iommu,
+   unsigned long arg)
+{
+   struct vfio_iommu_type1_set_pasid_table spt;
+   unsigned long minsz;
+   int ret = -EINVAL;
+
+   minsz = offsetofend(struct vfio_iommu_type1_set_pasid_table, flags);
+
+   if (copy_from_user(&spt, (void __user *)arg, minsz))
+   return -EFAULT;
+
+   if (spt.argsz < minsz)
+   return -EINVAL;
+
+   if (spt.flags & VFIO_PASID_TABLE_FLAG_SET &&
+   spt.flags & VFIO_PASID_TABLE_FLAG_UNSET)
+   return -EINVAL;
+
+   if (spt.flags & VFIO_PASID_TABLE_FLAG_SET)
+   ret = vfio_attach_pasid_table(iommu, arg + minsz);
+   else if (spt.flags & VFIO_PASID_TABLE_FLAG_UNSET) {
+   vfio_detach_pasid_table(iommu);
+   ret = 0;
+   }
+   return ret;
+}
+
 static int vfio_iommu_type1_dirty_pages(struct vfio_iommu *iommu,
unsigned long arg)
 {
@@ -2867,6 +2930,8 @@ static long vfio_iommu_type1_ioctl(void *iommu_data,
return vfio_iommu_type1_unmap_dma(iommu, arg);
case VFIO_IOMMU_DIRTY_PAGES:
return vfio_iommu_type1_dirty_pages(iommu, arg);
+   case VFIO_IOMMU_SET_PASID_TABLE:
+   return vfio_iommu_type1_set_pasid_table(iommu, arg);
default:
return -ENOTTY;
}
diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
index 2f313a238a8f..78ce3ce6c331 100644
--- a/include/uapi/linux/vfio.h
+++ b/include/uapi/linux/vfio.h
@@ -14,6 +14,7 @@
 
 #include 
 #include 
+#include 
 
 #define VFIO_API_VERSION   0
 
@@ -1180,6 +1181,24 @@ struct vfio_iommu_type1_dirty_bitmap_get {
 
 #define VFIO_IOMMU_DIRTY_PAGES _IO(VFIO_TYPE, VFIO_BASE + 17)
 
+/*
+ * VFIO_IOMMU_SET_PASID_TABLE - _IOWR(VFIO_TYPE, VFIO_BASE + 22,
+ * struct vfio_iommu_type1_set_pasid_table)
+ *
+ * The SET operation passes a PASID table to the host while the
+ * UNSET operation detaches the one currently programmed. Setting
+ * a table while another is already programmed replaces the old table.
+ */
+struct vfio_iommu_type1_set_pasid_table {
+   __u32   argsz;
+   __u32   flags;
+#define VFIO_PASID_TABLE_FLAG_SET  (1 << 0)
+#define VFIO_PASID_TABLE_FLAG_UNSET(1 << 1)
+   struct iommu_pasid_table_config config; /* used on SET */
+};
+
+#define VFIO_IOMMU_SET_PASID_TABLE _IO(VFIO_TYPE, VFIO_BASE + 22)
+
 /*  Additional API for SPAPR TCE (Server

[PATCH v11 02/13] vfio: VFIO_IOMMU_CACHE_INVALIDATE

2020-11-16 Thread Eric Auger
From: "Liu, Yi L" 

When the guest "owns" the stage 1 translation structures,  the host
IOMMU driver has no knowledge of caching structure updates unless
the guest invalidation requests are trapped and passed down to the
host.

This patch adds the VFIO_IOMMU_CACHE_INVALIDATE ioctl with aims
at propagating guest stage1 IOMMU cache invalidations to the host.

Signed-off-by: Liu, Yi L 
Signed-off-by: Eric Auger 

---
v10 -> v11:
- renamed ustruct into cache_inv

v8 -> v9:
- change the ioctl ID

v6 -> v7:
- Use iommu_capsule struct
- renamed vfio_iommu_for_each_dev into vfio_iommu_lookup_dev
  due to checkpatch error related to for_each_dev suffix

v2 -> v3:
- introduce vfio_iommu_for_each_dev back in this patch

v1 -> v2:
- s/TLB/CACHE
- remove vfio_iommu_task usage
- commit message rewording
---
 drivers/vfio/vfio_iommu_type1.c | 58 +
 include/uapi/linux/vfio.h   | 13 
 2 files changed, 71 insertions(+)

diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index 87ddd9e882dc..966909f542f1 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -143,6 +143,34 @@ struct vfio_regions {
 #define DIRTY_BITMAP_PAGES_MAX  ((u64)INT_MAX)
 #define DIRTY_BITMAP_SIZE_MAX   DIRTY_BITMAP_BYTES(DIRTY_BITMAP_PAGES_MAX)
 
+struct domain_capsule {
+   struct iommu_domain *domain;
+   void *data;
+};
+
+/* iommu->lock must be held */
+static int
+vfio_iommu_lookup_dev(struct vfio_iommu *iommu,
+ int (*fn)(struct device *dev, void *data),
+ unsigned long arg)
+{
+   struct domain_capsule dc = {.data = &arg};
+   struct vfio_domain *d;
+   struct vfio_group *g;
+   int ret = 0;
+
+   list_for_each_entry(d, &iommu->domain_list, next) {
+   dc.domain = d->domain;
+   list_for_each_entry(g, &d->group_list, next) {
+   ret = iommu_group_for_each_dev(g->iommu_group,
+  &dc, fn);
+   if (ret)
+   break;
+   }
+   }
+   return ret;
+}
+
 static int put_pfn(unsigned long pfn, int prot);
 
 static struct vfio_group *vfio_iommu_find_iommu_group(struct vfio_iommu *iommu,
@@ -2621,6 +2649,13 @@ vfio_attach_pasid_table(struct vfio_iommu *iommu, 
unsigned long arg)
mutex_unlock(&iommu->lock);
return ret;
 }
+static int vfio_cache_inv_fn(struct device *dev, void *data)
+{
+   struct domain_capsule *dc = (struct domain_capsule *)data;
+   unsigned long arg = *(unsigned long *)dc->data;
+
+   return iommu_uapi_cache_invalidate(dc->domain, dev, (void __user *)arg);
+}
 
 static int vfio_iommu_migration_build_caps(struct vfio_iommu *iommu,
   struct vfio_info_cap *caps)
@@ -2810,6 +2845,27 @@ static int vfio_iommu_type1_set_pasid_table(struct 
vfio_iommu *iommu,
return ret;
 }
 
+static int vfio_iommu_type1_cache_invalidate(struct vfio_iommu *iommu,
+   unsigned long arg)
+{
+   struct vfio_iommu_type1_cache_invalidate cache_inv;
+   unsigned long minsz;
+   int ret;
+
+   minsz = offsetofend(struct vfio_iommu_type1_cache_invalidate, flags);
+
+   if (copy_from_user(&cache_inv, (void __user *)arg, minsz))
+   return -EFAULT;
+
+   if (cache_inv.argsz < minsz || cache_inv.flags)
+   return -EINVAL;
+
+   mutex_lock(&iommu->lock);
+   ret = vfio_iommu_lookup_dev(iommu, vfio_cache_inv_fn, arg + minsz);
+   mutex_unlock(&iommu->lock);
+   return ret;
+}
+
 static int vfio_iommu_type1_dirty_pages(struct vfio_iommu *iommu,
unsigned long arg)
 {
@@ -2932,6 +2988,8 @@ static long vfio_iommu_type1_ioctl(void *iommu_data,
return vfio_iommu_type1_dirty_pages(iommu, arg);
case VFIO_IOMMU_SET_PASID_TABLE:
return vfio_iommu_type1_set_pasid_table(iommu, arg);
+   case VFIO_IOMMU_CACHE_INVALIDATE:
+   return vfio_iommu_type1_cache_invalidate(iommu, arg);
default:
return -ENOTTY;
}
diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
index 78ce3ce6c331..0e6d94cc2ba4 100644
--- a/include/uapi/linux/vfio.h
+++ b/include/uapi/linux/vfio.h
@@ -1199,6 +1199,19 @@ struct vfio_iommu_type1_set_pasid_table {
 
 #define VFIO_IOMMU_SET_PASID_TABLE _IO(VFIO_TYPE, VFIO_BASE + 22)
 
+/**
+ * VFIO_IOMMU_CACHE_INVALIDATE - _IOWR(VFIO_TYPE, VFIO_BASE + 23,
+ * struct vfio_iommu_type1_cache_invalidate)
+ *
+ * Propagate guest IOMMU cache invalidation to the host.
+ */
+struct vfio_iommu_type1_cache_invalidate {
+   __u32   argsz;
+   __u32   flags;
+   struct iommu_cache_invalidate_info info;
+};
+#define VFIO_IOMMU_CACHE_INVALIDATE  _IO(VFIO_TYPE, VFIO_BASE + 23)
+
 /*  Additional API for SP

[PATCH v12 15/15] iommu/smmuv3: Add PASID cache invalidation per PASID

2020-11-16 Thread Eric Auger
In order to cascade guest CFGI_CD, let's add PASID cache invalidation
per PASID.

Signed-off-by: Eric Auger 
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 16 +---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c 
b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 6549c3ee6af6..eb0e09936803 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -3002,9 +3002,19 @@ arm_smmu_cache_invalidate(struct iommu_domain *domain, 
struct device *dev,
} else {
return -EINVAL;
}
-   }
-   if (inv_info->cache & IOMMU_CACHE_INV_TYPE_PASID ||
-   inv_info->cache & IOMMU_CACHE_INV_TYPE_DEV_IOTLB) {
+   } else if (inv_info->cache & IOMMU_CACHE_INV_TYPE_PASID) {
+   if (inv_info->granularity == IOMMU_INV_GRANU_PASID) {
+   struct iommu_inv_pasid_info *info =
+   &inv_info->granu.pasid_info;
+
+   if (!info->flags & IOMMU_INV_PASID_FLAGS_PASID)
+   return -EINVAL;
+
+   arm_smmu_sync_cd(smmu_domain, info->pasid, true);
+   } else {
+   return -ENOENT;
+   }
+   } else { /* IOMMU_CACHE_INV_TYPE_DEV_IOTLB */
return -ENOENT;
}
return 0;
-- 
2.21.3

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v12 09/15] dma-iommu: Implement NESTED_MSI cookie

2020-11-16 Thread Eric Auger
Up to now, when the type was UNMANAGED, we used to
allocate IOVA pages within a reserved IOVA MSI range.

If both the host and the guest are exposed with SMMUs, each
would allocate an IOVA. The guest allocates an IOVA (gIOVA)
to map onto the guest MSI doorbell (gDB). The Host allocates
another IOVA (hIOVA) to map onto the physical doorbell (hDB).

So we end up with 2 unrelated mappings, at S1 and S2:
 S1 S2
gIOVA-> gDB
   hIOVA->hDB

The PCI device would be programmed with hIOVA.
No stage 1 mapping would existing, causing the MSIs to fault.

iommu_dma_bind_guest_msi() allows to pass gIOVA/gDB
to the host so that gIOVA can be used by the host instead of
re-allocating a new hIOVA.

 S1   S2
gIOVA->gDB->hDB

this time, the PCI device can be programmed with the gIOVA MSI
doorbell which is correctly mapped through both stages.

Nested mode is not compatible with HW MSI regions as in that
case gDB and hDB should have a 1-1 mapping. This check will
be done when attaching each device to the IOMMU domain.

Signed-off-by: Eric Auger 

---

v10 -> v11:
- fix compilation if !CONFIG_IOMMU_DMA

v7 -> v8:
- correct iommu_dma_(un)bind_guest_msi when
  !CONFIG_IOMMU_DMA
- Mentioned nested mode is not compatible with HW MSI regions
  in commit message
- protect with msi_lock on unbind

v6 -> v7:
- removed device handle

v3 -> v4:
- change function names; add unregister
- protect with msi_lock

v2 -> v3:
- also store the device handle on S1 mapping registration.
  This garantees we associate the associated S2 mapping binds
  to the correct physical MSI controller.

v1 -> v2:
- unmap stage2 on put()
---
 drivers/iommu/dma-iommu.c | 142 +-
 include/linux/dma-iommu.h |  16 +
 2 files changed, 155 insertions(+), 3 deletions(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 0cbcd3fc3e7e..a14ecad6b79b 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -19,6 +19,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -27,12 +28,15 @@
 struct iommu_dma_msi_page {
struct list_headlist;
dma_addr_t  iova;
+   dma_addr_t  gpa;
phys_addr_t phys;
+   size_t  s1_granule;
 };
 
 enum iommu_dma_cookie_type {
IOMMU_DMA_IOVA_COOKIE,
IOMMU_DMA_MSI_COOKIE,
+   IOMMU_DMA_NESTED_MSI_COOKIE,
 };
 
 struct iommu_dma_cookie {
@@ -44,6 +48,7 @@ struct iommu_dma_cookie {
dma_addr_t  msi_iova;
};
struct list_headmsi_page_list;
+   spinlock_t  msi_lock;
 
/* Domain for flush queue callback; NULL if flush queue not in use */
struct iommu_domain *fq_domain;
@@ -62,6 +67,7 @@ static struct iommu_dma_cookie *cookie_alloc(enum 
iommu_dma_cookie_type type)
 
cookie = kzalloc(sizeof(*cookie), GFP_KERNEL);
if (cookie) {
+   spin_lock_init(&cookie->msi_lock);
INIT_LIST_HEAD(&cookie->msi_page_list);
cookie->type = type;
}
@@ -95,14 +101,17 @@ EXPORT_SYMBOL(iommu_get_dma_cookie);
  *
  * Users who manage their own IOVA allocation and do not want DMA API support,
  * but would still like to take advantage of automatic MSI remapping, can use
- * this to initialise their own domain appropriately. Users should reserve a
+ * this to initialise their own domain appropriately. Users may reserve a
  * contiguous IOVA region, starting at @base, large enough to accommodate the
  * number of PAGE_SIZE mappings necessary to cover every MSI doorbell address
- * used by the devices attached to @domain.
+ * used by the devices attached to @domain. The other way round is to provide
+ * usable iova pages through the iommu_dma_bind_doorbell API (nested stages
+ * use case)
  */
 int iommu_get_msi_cookie(struct iommu_domain *domain, dma_addr_t base)
 {
struct iommu_dma_cookie *cookie;
+   int nesting, ret;
 
if (domain->type != IOMMU_DOMAIN_UNMANAGED)
return -EINVAL;
@@ -110,7 +119,12 @@ int iommu_get_msi_cookie(struct iommu_domain *domain, 
dma_addr_t base)
if (domain->iova_cookie)
return -EEXIST;
 
-   cookie = cookie_alloc(IOMMU_DMA_MSI_COOKIE);
+   ret =  iommu_domain_get_attr(domain, DOMAIN_ATTR_NESTING, &nesting);
+   if (!ret && nesting)
+   cookie = cookie_alloc(IOMMU_DMA_NESTED_MSI_COOKIE);
+   else
+   cookie = cookie_alloc(IOMMU_DMA_MSI_COOKIE);
+
if (!cookie)
return -ENOMEM;
 
@@ -131,6 +145,7 @@ void iommu_put_dma_cookie(struct iommu_domain *domain)
 {
struct iommu_dma_cookie *cookie = domain->iova_cookie;
struct iommu_dma_msi_page *msi, *tmp;
+   bool s2_unmap = false;
 
if (!cookie)
return;
@@ -138,7 +153,15 @@ void iommu_

[PATCH v12 11/15] iommu/smmuv3: Enforce incompatibility between nested mode and HW MSI regions

2020-11-16 Thread Eric Auger
Nested mode currently is not compatible with HW MSI reserved regions.
Indeed MSI transactions targeting this MSI doorbells bypass the SMMU.

Let's check nested mode is not attempted in such configuration.

Signed-off-by: Eric Auger 
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 23 +++--
 1 file changed, 21 insertions(+), 2 deletions(-)

diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c 
b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index de03ac111f76..0c5ab4005f76 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -2296,6 +2296,23 @@ static bool arm_smmu_share_msi_domain(struct 
iommu_domain *domain,
return share;
 }
 
+static bool arm_smmu_has_hw_msi_resv_region(struct device *dev)
+{
+   struct iommu_resv_region *region;
+   bool has_msi_resv_region = false;
+   LIST_HEAD(resv_regions);
+
+   iommu_get_resv_regions(dev, &resv_regions);
+   list_for_each_entry(region, &resv_regions, list) {
+   if (region->type == IOMMU_RESV_MSI) {
+   has_msi_resv_region = true;
+   break;
+   }
+   }
+   iommu_put_resv_regions(dev, &resv_regions);
+   return has_msi_resv_region;
+}
+
 static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
 {
int ret = 0;
@@ -2350,10 +2367,12 @@ static int arm_smmu_attach_dev(struct iommu_domain 
*domain, struct device *dev)
/*
 * In nested mode we must check all devices belonging to the
 * domain share the same physical MSI doorbell. Otherwise nested
-* stage MSI binding is not supported.
+* stage MSI binding is not supported. Also nested mode is not
+* compatible with MSI HW reserved regions.
 */
if (smmu_domain->stage == ARM_SMMU_DOMAIN_NESTED &&
-   !arm_smmu_share_msi_domain(domain, dev)) {
+   (!arm_smmu_share_msi_domain(domain, dev) ||
+arm_smmu_has_hw_msi_resv_region(dev))) {
ret = -EINVAL;
goto out_unlock;
}
-- 
2.21.3

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v12 08/15] iommu/smmuv3: Implement cache_invalidate

2020-11-16 Thread Eric Auger
Implement domain-selective and page-selective IOTLB invalidations.

Signed-off-by: Eric Auger 

---
v7 -> v8:
- ASID based invalidation using iommu_inv_pasid_info
- check ARCHID/PASID flags in addr based invalidation
- use __arm_smmu_tlb_inv_context and __arm_smmu_tlb_inv_range_nosync

v6 -> v7
- check the uapi version

v3 -> v4:
- adapt to changes in the uapi
- add support for leaf parameter
- do not use arm_smmu_tlb_inv_range_nosync or arm_smmu_tlb_inv_context
  anymore

v2 -> v3:
- replace __arm_smmu_tlb_sync by arm_smmu_cmdq_issue_sync

v1 -> v2:
- properly pass the asid
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 53 +
 1 file changed, 53 insertions(+)

diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c 
b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 73f7a56101dd..4b796693d697 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -2774,6 +2774,58 @@ static void arm_smmu_detach_pasid_table(struct 
iommu_domain *domain)
mutex_unlock(&smmu_domain->init_mutex);
 }
 
+static int
+arm_smmu_cache_invalidate(struct iommu_domain *domain, struct device *dev,
+ struct iommu_cache_invalidate_info *inv_info)
+{
+   struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+   struct arm_smmu_device *smmu = smmu_domain->smmu;
+
+   if (smmu_domain->stage != ARM_SMMU_DOMAIN_NESTED)
+   return -EINVAL;
+
+   if (!smmu)
+   return -EINVAL;
+
+   if (inv_info->version != IOMMU_CACHE_INVALIDATE_INFO_VERSION_1)
+   return -EINVAL;
+
+   if (inv_info->cache & IOMMU_CACHE_INV_TYPE_IOTLB) {
+   if (inv_info->granularity == IOMMU_INV_GRANU_PASID) {
+   struct iommu_inv_pasid_info *info =
+   &inv_info->granu.pasid_info;
+
+   if (!(info->flags & IOMMU_INV_PASID_FLAGS_ARCHID) ||
+(info->flags & IOMMU_INV_PASID_FLAGS_PASID))
+   return -EINVAL;
+
+   __arm_smmu_tlb_inv_context(smmu_domain, info->archid);
+
+   } else if (inv_info->granularity == IOMMU_INV_GRANU_ADDR) {
+   struct iommu_inv_addr_info *info = 
&inv_info->granu.addr_info;
+   size_t size = info->nb_granules * info->granule_size;
+   bool leaf = info->flags & IOMMU_INV_ADDR_FLAGS_LEAF;
+
+   if (!(info->flags & IOMMU_INV_ADDR_FLAGS_ARCHID) ||
+(info->flags & IOMMU_INV_ADDR_FLAGS_PASID))
+   return -EINVAL;
+
+   __arm_smmu_tlb_inv_range(info->addr, size,
+info->granule_size, leaf,
+ smmu_domain, info->archid);
+
+   arm_smmu_cmdq_issue_sync(smmu);
+   } else {
+   return -EINVAL;
+   }
+   }
+   if (inv_info->cache & IOMMU_CACHE_INV_TYPE_PASID ||
+   inv_info->cache & IOMMU_CACHE_INV_TYPE_DEV_IOTLB) {
+   return -ENOENT;
+   }
+   return 0;
+}
+
 static bool arm_smmu_dev_has_feature(struct device *dev,
 enum iommu_dev_features feat)
 {
@@ -2857,6 +2909,7 @@ static struct iommu_ops arm_smmu_ops = {
.put_resv_regions   = generic_iommu_put_resv_regions,
.attach_pasid_table = arm_smmu_attach_pasid_table,
.detach_pasid_table = arm_smmu_detach_pasid_table,
+   .cache_invalidate   = arm_smmu_cache_invalidate,
.dev_has_feat   = arm_smmu_dev_has_feature,
.dev_feat_enabled   = arm_smmu_dev_feature_enabled,
.dev_enable_feat= arm_smmu_dev_enable_feature,
-- 
2.21.3

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v12 14/15] iommu/smmuv3: Accept configs with more than one context descriptor

2020-11-16 Thread Eric Auger
In preparation for vSVA, let's accept userspace provided configs
with more than one CD. We check the max CD against the host iommu
capability and also the format (linear versus 2 level).

Signed-off-by: Eric Auger 
Signed-off-by: Shameer Kolothum 
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 13 -
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c 
b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 31a2500bde32..6549c3ee6af6 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -2901,11 +2901,12 @@ static int arm_smmu_attach_pasid_table(struct 
iommu_domain *domain,
if (smmu_domain->s1_cfg)
goto out;
 
-   /*
-* we currently support a single CD so s1fmt and s1dss
-* fields are also ignored
-*/
-   if (cfg->pasid_bits)
+   list_for_each_entry(master, &smmu_domain->devices, domain_head) 
{
+   if (cfg->pasid_bits > master->ssid_bits)
+   goto out;
+   }
+   if (cfg->vendor_data.smmuv3.s1fmt == STRTAB_STE_0_S1FMT_64K_L2 
&&
+   !(smmu->features & ARM_SMMU_FEAT_2_LVL_CDTAB))
goto out;
 
smmu_domain->s1_cfg = kzalloc(sizeof(*smmu_domain->s1_cfg),
@@ -2916,6 +2917,8 @@ static int arm_smmu_attach_pasid_table(struct 
iommu_domain *domain,
}
 
smmu_domain->s1_cfg->cdcfg.cdtab_dma = cfg->base_ptr;
+   smmu_domain->s1_cfg->s1cdmax = cfg->pasid_bits;
+   smmu_domain->s1_cfg->s1fmt = cfg->vendor_data.smmuv3.s1fmt;
smmu_domain->abort = false;
break;
default:
-- 
2.21.3

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v12 13/15] iommu/smmuv3: Report non recoverable faults

2020-11-16 Thread Eric Auger
When a stage 1 related fault event is read from the event queue,
let's propagate it to potential external fault listeners, ie. users
who registered a fault handler.

Signed-off-by: Eric Auger 

---
v8 -> v9:
- adapt to the removal of IOMMU_FAULT_UNRECOV_PERM_VALID:
  only look at IOMMU_FAULT_UNRECOV_ADDR_VALID which comes with
  perm
- do not advertise IOMMU_FAULT_UNRECOV_PASID_VALID faults for
  translation faults
- trace errors if !master
- test nested before calling iommu_report_device_fault
- call the fault handler unconditionnally in non nested mode

v4 -> v5:
- s/IOMMU_FAULT_PERM_INST/IOMMU_FAULT_PERM_EXEC
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 102 +---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h |  80 +++
 2 files changed, 171 insertions(+), 11 deletions(-)

diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c 
b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 5aa9e0e747fa..31a2500bde32 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -1379,7 +1379,6 @@ static int arm_smmu_init_l2_strtab(struct arm_smmu_device 
*smmu, u32 sid)
return 0;
 }
 
-__maybe_unused
 static struct arm_smmu_master *
 arm_smmu_find_master(struct arm_smmu_device *smmu, u32 sid)
 {
@@ -1405,25 +1404,106 @@ arm_smmu_find_master(struct arm_smmu_device *smmu, u32 
sid)
return master;
 }
 
+/* Populates the record fields according to the input SMMU event */
+static bool arm_smmu_transcode_fault(u64 *evt, u8 type,
+struct iommu_fault_unrecoverable *record)
+{
+   const struct arm_smmu_fault_propagation_data *data;
+   u32 fields;
+
+   if (type >= ARRAY_SIZE(fault_propagation))
+   return false;
+
+   data = &fault_propagation[type];
+   if (!data->reason)
+   return false;
+
+   fields = data->fields;
+
+   if (data->s1_check & FIELD_GET(EVTQ_1_S2, evt[1]))
+   return false; /* S2 related fault, don't propagate */
+
+   if (fields & IOMMU_FAULT_UNRECOV_PASID_VALID)
+   record->pasid = FIELD_GET(EVTQ_0_SUBSTREAMID, evt[0]);
+   else {
+   /* all other transcoded errors have SSV */
+   if (FIELD_GET(EVTQ_0_SSV, evt[0])) {
+   record->pasid = FIELD_GET(EVTQ_0_SUBSTREAMID, evt[0]);
+   fields |= IOMMU_FAULT_UNRECOV_PASID_VALID;
+   }
+   }
+
+   if (fields & IOMMU_FAULT_UNRECOV_ADDR_VALID) {
+   if (FIELD_GET(EVTQ_1_RNW, evt[1]))
+   record->perm = IOMMU_FAULT_PERM_READ;
+   else
+   record->perm = IOMMU_FAULT_PERM_WRITE;
+   if (FIELD_GET(EVTQ_1_PNU, evt[1]))
+   record->perm |= IOMMU_FAULT_PERM_PRIV;
+   if (FIELD_GET(EVTQ_1_IND, evt[1]))
+   record->perm |= IOMMU_FAULT_PERM_EXEC;
+   record->addr = evt[2];
+   }
+
+   if (fields & IOMMU_FAULT_UNRECOV_FETCH_ADDR_VALID)
+   record->fetch_addr = FIELD_GET(EVTQ_3_FETCH_ADDR, evt[3]);
+
+   record->flags = fields;
+   record->reason = data->reason;
+   return true;
+}
+
+static void arm_smmu_report_event(struct arm_smmu_device *smmu, u64 *evt)
+{
+   u32 sid = FIELD_GET(EVTQ_0_STREAMID, evt[0]);
+   u8 type = FIELD_GET(EVTQ_0_ID, evt[0]);
+   struct arm_smmu_master *master;
+   struct iommu_fault_event event = {};
+   bool nested;
+   int i;
+
+   master = arm_smmu_find_master(smmu, sid);
+   if (!master || !master->domain)
+   goto out;
+
+   event.fault.type = IOMMU_FAULT_DMA_UNRECOV;
+
+   nested = (master->domain->stage == ARM_SMMU_DOMAIN_NESTED);
+
+   if (nested) {
+   if (arm_smmu_transcode_fault(evt, type, &event.fault.event)) {
+   /*
+* Only S1 related faults should be reported to the
+* guest and must not flood the host log.
+* Also a fault handler should have been registered
+* to guarantee the full nested functionality
+*/
+   WARN_ON_ONCE(iommu_report_device_fault(master->dev,
+  &event));
+   return;
+   }
+   } else {
+   iommu_report_device_fault(master->dev, &event);
+   }
+out:
+   dev_info(smmu->dev, "event 0x%02x received:\n", type);
+   for (i = 0; i < EVTQ_ENT_DWORDS; ++i) {
+   dev_info(smmu->dev, "\t0x%016llx\n",
+(unsigned long long)evt[i]);
+   }
+}
+
 /* IRQ and event handlers */
 static irqreturn_t arm_smmu_evtq_thread(int irq, void *dev)
 {
-   int i;
struct arm_smmu_device *smmu = dev;
struct arm_smmu_queue *q = &smmu->evtq.q;
struct arm_smmu_ll_queue *llq = &

[PATCH v12 06/15] iommu/smmuv3: Implement attach/detach_pasid_table

2020-11-16 Thread Eric Auger
On attach_pasid_table() we program STE S1 related info set
by the guest into the actual physical STEs. At minimum
we need to program the context descriptor GPA and compute
whether the stage1 is translated/bypassed or aborted.

Signed-off-by: Eric Auger 

---
v7 -> v8:
- remove smmu->features check, now done on domain finalize

v6 -> v7:
- check versions and comment the fact we don't need to take
  into account s1dss and s1fmt
v3 -> v4:
- adapt to changes in iommu_pasid_table_config
- different programming convention at s1_cfg/s2_cfg/ste.abort

v2 -> v3:
- callback now is named set_pasid_table and struct fields
  are laid out differently.

v1 -> v2:
- invalidate the STE before changing them
- hold init_mutex
- handle new fields
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 98 +
 1 file changed, 98 insertions(+)

diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c 
b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 9580090bd0c9..08ab0dd81049 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -2655,6 +2655,102 @@ static void arm_smmu_get_resv_regions(struct device 
*dev,
iommu_dma_get_resv_regions(dev, head);
 }
 
+static int arm_smmu_attach_pasid_table(struct iommu_domain *domain,
+  struct iommu_pasid_table_config *cfg)
+{
+   struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+   struct arm_smmu_master *master;
+   struct arm_smmu_device *smmu;
+   unsigned long flags;
+   int ret = -EINVAL;
+
+   if (cfg->format != IOMMU_PASID_FORMAT_SMMUV3)
+   return -EINVAL;
+
+   if (cfg->version != PASID_TABLE_CFG_VERSION_1 ||
+   cfg->vendor_data.smmuv3.version != PASID_TABLE_SMMUV3_CFG_VERSION_1)
+   return -EINVAL;
+
+   mutex_lock(&smmu_domain->init_mutex);
+
+   smmu = smmu_domain->smmu;
+
+   if (!smmu)
+   goto out;
+
+   if (smmu_domain->stage != ARM_SMMU_DOMAIN_NESTED)
+   goto out;
+
+   switch (cfg->config) {
+   case IOMMU_PASID_CONFIG_ABORT:
+   kfree(smmu_domain->s1_cfg);
+   smmu_domain->s1_cfg = NULL;
+   smmu_domain->abort = true;
+   break;
+   case IOMMU_PASID_CONFIG_BYPASS:
+   kfree(smmu_domain->s1_cfg);
+   smmu_domain->s1_cfg = NULL;
+   smmu_domain->abort = false;
+   break;
+   case IOMMU_PASID_CONFIG_TRANSLATE:
+   /* we do not support S1 <-> S1 transitions */
+   if (smmu_domain->s1_cfg)
+   goto out;
+
+   /*
+* we currently support a single CD so s1fmt and s1dss
+* fields are also ignored
+*/
+   if (cfg->pasid_bits)
+   goto out;
+
+   smmu_domain->s1_cfg = kzalloc(sizeof(*smmu_domain->s1_cfg),
+ GFP_KERNEL);
+   if (!smmu_domain->s1_cfg) {
+   ret = -ENOMEM;
+   goto out;
+   }
+
+   smmu_domain->s1_cfg->cdcfg.cdtab_dma = cfg->base_ptr;
+   smmu_domain->abort = false;
+   break;
+   default:
+   goto out;
+   }
+   spin_lock_irqsave(&smmu_domain->devices_lock, flags);
+   list_for_each_entry(master, &smmu_domain->devices, domain_head)
+   arm_smmu_install_ste_for_dev(master);
+   spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);
+   ret = 0;
+out:
+   mutex_unlock(&smmu_domain->init_mutex);
+   return ret;
+}
+
+static void arm_smmu_detach_pasid_table(struct iommu_domain *domain)
+{
+   struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+   struct arm_smmu_master *master;
+   unsigned long flags;
+
+   mutex_lock(&smmu_domain->init_mutex);
+
+   if (smmu_domain->stage != ARM_SMMU_DOMAIN_NESTED)
+   goto unlock;
+
+   kfree(smmu_domain->s1_cfg);
+   smmu_domain->s1_cfg = NULL;
+   smmu_domain->abort = true;
+
+   spin_lock_irqsave(&smmu_domain->devices_lock, flags);
+   list_for_each_entry(master, &smmu_domain->devices, domain_head)
+   arm_smmu_install_ste_for_dev(master);
+   spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);
+
+unlock:
+   mutex_unlock(&smmu_domain->init_mutex);
+}
+
 static bool arm_smmu_dev_has_feature(struct device *dev,
 enum iommu_dev_features feat)
 {
@@ -2736,6 +2832,8 @@ static struct iommu_ops arm_smmu_ops = {
.of_xlate   = arm_smmu_of_xlate,
.get_resv_regions   = arm_smmu_get_resv_regions,
.put_resv_regions   = generic_iommu_put_resv_regions,
+   .attach_pasid_table = arm_smmu_attach_pasid_table,
+   .detach_pasid_table = arm_smmu_detach_pasid_table,
.dev_has_feat   

[PATCH v12 07/15] iommu/smmuv3: Allow stage 1 invalidation with unmanaged ASIDs

2020-11-16 Thread Eric Auger
With nested stage support, soon we will need to invalidate
S1 contexts and ranges tagged with an unmanaged asid, this
latter being managed by the guest. So let's introduce 2 helpers
that allow to invalidate with externally managed ASIDs

Signed-off-by: Eric Auger 
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 35 +
 1 file changed, 29 insertions(+), 6 deletions(-)

diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c 
b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 08ab0dd81049..73f7a56101dd 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -1679,9 +1679,9 @@ static int arm_smmu_atc_inv_domain(struct arm_smmu_domain 
*smmu_domain,
 }
 
 /* IO_PGTABLE API */
-static void arm_smmu_tlb_inv_context(void *cookie)
+static void __arm_smmu_tlb_inv_context(struct arm_smmu_domain *smmu_domain,
+  int ext_asid)
 {
-   struct arm_smmu_domain *smmu_domain = cookie;
struct arm_smmu_device *smmu = smmu_domain->smmu;
struct arm_smmu_cmdq_ent cmd;
 
@@ -1692,7 +1692,11 @@ static void arm_smmu_tlb_inv_context(void *cookie)
 * insertion to guarantee those are observed before the TLBI. Do be
 * careful, 007.
 */
-   if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
+   if (ext_asid >= 0) { /* guest stage 1 invalidation */
+   cmd.opcode  = CMDQ_OP_TLBI_NH_ASID;
+   cmd.tlbi.asid   = ext_asid;
+   cmd.tlbi.vmid   = smmu_domain->s2_cfg->vmid;
+   } else if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
arm_smmu_tlb_inv_asid(smmu, smmu_domain->s1_cfg->cd.asid);
} else {
cmd.opcode  = CMDQ_OP_TLBI_S12_VMALL;
@@ -1703,9 +1707,17 @@ static void arm_smmu_tlb_inv_context(void *cookie)
arm_smmu_atc_inv_domain(smmu_domain, 0, 0, 0);
 }
 
-static void arm_smmu_tlb_inv_range(unsigned long iova, size_t size,
+static void arm_smmu_tlb_inv_context(void *cookie)
+{
+   struct arm_smmu_domain *smmu_domain = cookie;
+
+   __arm_smmu_tlb_inv_context(smmu_domain, -1);
+}
+
+static void __arm_smmu_tlb_inv_range(unsigned long iova, size_t size,
   size_t granule, bool leaf,
-  struct arm_smmu_domain *smmu_domain)
+  struct arm_smmu_domain *smmu_domain,
+  int ext_asid)
 {
struct arm_smmu_device *smmu = smmu_domain->smmu;
unsigned long start = iova, end = iova + size, num_pages = 0, tg = 0;
@@ -1720,7 +1732,11 @@ static void arm_smmu_tlb_inv_range(unsigned long iova, 
size_t size,
if (!size)
return;
 
-   if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
+   if (ext_asid >= 0) {  /* guest stage 1 invalidation */
+   cmd.opcode  = CMDQ_OP_TLBI_NH_VA;
+   cmd.tlbi.asid   = ext_asid;
+   cmd.tlbi.vmid   = smmu_domain->s2_cfg->vmid;
+   } else if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
cmd.opcode  = CMDQ_OP_TLBI_NH_VA;
cmd.tlbi.asid   = smmu_domain->s1_cfg->cd.asid;
} else {
@@ -1780,6 +1796,13 @@ static void arm_smmu_tlb_inv_range(unsigned long iova, 
size_t size,
arm_smmu_atc_inv_domain(smmu_domain, 0, start, size);
 }
 
+static void arm_smmu_tlb_inv_range(unsigned long iova, size_t size,
+  size_t granule, bool leaf,
+  struct arm_smmu_domain *smmu_domain)
+{
+   __arm_smmu_tlb_inv_range(iova, size, granule, leaf, smmu_domain, -1);
+}
+
 static void arm_smmu_tlb_inv_page_nosync(struct iommu_iotlb_gather *gather,
 unsigned long iova, size_t granule,
 void *cookie)
-- 
2.21.3

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v12 10/15] iommu/smmuv3: Nested mode single MSI doorbell per domain enforcement

2020-11-16 Thread Eric Auger
In nested mode we enforce the rule that all devices belonging
to the same iommu_domain share the same msi_domain.

Indeed if there were several physical MSI doorbells being used
within a single iommu_domain, it becomes really difficult to
resolve the nested stage mapping translating into the correct
physical doorbell. So let's forbid this situation.

Signed-off-by: Eric Auger 
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 41 +
 1 file changed, 41 insertions(+)

diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c 
b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 4b796693d697..de03ac111f76 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -2265,6 +2265,37 @@ static void arm_smmu_detach_dev(struct arm_smmu_master 
*master)
arm_smmu_install_ste_for_dev(master);
 }
 
+static bool arm_smmu_share_msi_domain(struct iommu_domain *domain,
+ struct device *dev)
+{
+   struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+   struct irq_domain *irqd = dev_get_msi_domain(dev);
+   struct arm_smmu_master *master;
+   unsigned long flags;
+   bool share = false;
+
+   if (!irqd)
+   return true;
+
+   spin_lock_irqsave(&smmu_domain->devices_lock, flags);
+   list_for_each_entry(master, &smmu_domain->devices, domain_head) {
+   struct irq_domain *d = dev_get_msi_domain(master->dev);
+
+   if (!d)
+   continue;
+   if (irqd != d) {
+   dev_info(dev, "Nested mode forbids to attach devices "
+"using different physical MSI doorbells "
+"to the same iommu_domain");
+   goto unlock;
+   }
+   }
+   share = true;
+unlock:
+   spin_unlock_irqrestore(&smmu_domain->devices_lock, flags);
+   return share;
+}
+
 static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
 {
int ret = 0;
@@ -2316,6 +2347,16 @@ static int arm_smmu_attach_dev(struct iommu_domain 
*domain, struct device *dev)
ret = -EINVAL;
goto out_unlock;
}
+   /*
+* In nested mode we must check all devices belonging to the
+* domain share the same physical MSI doorbell. Otherwise nested
+* stage MSI binding is not supported.
+*/
+   if (smmu_domain->stage == ARM_SMMU_DOMAIN_NESTED &&
+   !arm_smmu_share_msi_domain(domain, dev)) {
+   ret = -EINVAL;
+   goto out_unlock;
+   }
 
master->domain = smmu_domain;
 
-- 
2.21.3

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v12 12/15] iommu/smmuv3: Implement bind/unbind_guest_msi

2020-11-16 Thread Eric Auger
The bind/unbind_guest_msi() callbacks check the domain
is NESTED and redirect to the dma-iommu implementation.

Signed-off-by: Eric Auger 

---

v6 -> v7:
- remove device handle argument
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 43 +
 1 file changed, 43 insertions(+)

diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c 
b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 0c5ab4005f76..5aa9e0e747fa 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -2738,6 +2738,47 @@ static void arm_smmu_get_resv_regions(struct device *dev,
iommu_dma_get_resv_regions(dev, head);
 }
 
+static int
+arm_smmu_bind_guest_msi(struct iommu_domain *domain,
+   dma_addr_t giova, phys_addr_t gpa, size_t size)
+{
+   struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+   struct arm_smmu_device *smmu;
+   int ret = -EINVAL;
+
+   mutex_lock(&smmu_domain->init_mutex);
+   smmu = smmu_domain->smmu;
+   if (!smmu)
+   goto out;
+
+   if (smmu_domain->stage != ARM_SMMU_DOMAIN_NESTED)
+   goto out;
+
+   ret = iommu_dma_bind_guest_msi(domain, giova, gpa, size);
+out:
+   mutex_unlock(&smmu_domain->init_mutex);
+   return ret;
+}
+
+static void
+arm_smmu_unbind_guest_msi(struct iommu_domain *domain, dma_addr_t giova)
+{
+   struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+   struct arm_smmu_device *smmu;
+
+   mutex_lock(&smmu_domain->init_mutex);
+   smmu = smmu_domain->smmu;
+   if (!smmu)
+   goto unlock;
+
+   if (smmu_domain->stage != ARM_SMMU_DOMAIN_NESTED)
+   goto unlock;
+
+   iommu_dma_unbind_guest_msi(domain, giova);
+unlock:
+   mutex_unlock(&smmu_domain->init_mutex);
+}
+
 static int arm_smmu_attach_pasid_table(struct iommu_domain *domain,
   struct iommu_pasid_table_config *cfg)
 {
@@ -2970,6 +3011,8 @@ static struct iommu_ops arm_smmu_ops = {
.attach_pasid_table = arm_smmu_attach_pasid_table,
.detach_pasid_table = arm_smmu_detach_pasid_table,
.cache_invalidate   = arm_smmu_cache_invalidate,
+   .bind_guest_msi = arm_smmu_bind_guest_msi,
+   .unbind_guest_msi   = arm_smmu_unbind_guest_msi,
.dev_has_feat   = arm_smmu_dev_has_feature,
.dev_feat_enabled   = arm_smmu_dev_feature_enabled,
.dev_enable_feat= arm_smmu_dev_enable_feature,
-- 
2.21.3

___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm


[PATCH v12 00/15] SMMUv3 Nested Stage Setup (IOMMU part)

2020-11-16 Thread Eric Auger
This series brings the IOMMU part of HW nested paging support
in the SMMUv3. The VFIO part is submitted separately.

The IOMMU API is extended to support 2 new API functionalities:
1) pass the guest stage 1 configuration
2) pass stage 1 MSI bindings

Then those capabilities gets implemented in the SMMUv3 driver.

The virtualizer passes information through the VFIO user API
which cascades them to the iommu subsystem. This allows the guest
to own stage 1 tables and context descriptors (so-called PASID
table) while the host owns stage 2 tables and main configuration
structures (STE).

Best Regards

Eric

This series can be found at:
https://github.com/eauger/linux/tree/5.10-rc4-2stage-v12
(including the VFIO part)

The series includes a patch from Jean-Philippe. It is better to
review the original patch:
[PATCH v8 2/9] iommu/arm-smmu-v3: Maintain a SID->device structure

The VFIO series is sent separately.

History:

v11 -> v12:
- rebase on top of v5.10-rc4

Two new patches paving the way for vSVA/ARM (Shameer's input)
- iommu/smmuv3: Accept configs with more than one context descriptor
- iommu/smmuv3: Add PASID cache invalidation per PASID

v10 -> v11:
- S2TTB reset when S2 is off
- fix compil issue when CONFIG_IOMMU_DMA is not set

v9 -> v10:
- rebase on top of 5.6.0-rc3

v8 -> v9:
- rebase on 5.3
- split iommu/vfio parts

v6 -> v8:
- Implement VFIO-PCI device specific interrupt framework

v7 -> v8:
- rebase on top of v5.2-rc1 and especially
  8be39a1a04c1  iommu/arm-smmu-v3: Add a master->domain pointer
- dynamic alloc of s1_cfg/s2_cfg
- __arm_smmu_tlb_inv_asid/s1_range_nosync
- check there is no HW MSI regions
- asid invalidation using pasid extended struct (change in the uapi)
- add s1_live/s2_live checks
- move check about support of nested stages in domain finalise
- fixes in error reporting according to the discussion with Robin
- reordered the patches to have first iommu/smmuv3 patches and then
  VFIO patches

v6 -> v7:
- removed device handle from bind/unbind_guest_msi
- added "iommu/smmuv3: Nested mode single MSI doorbell per domain
  enforcement"
- added few uapi comments as suggested by Jean, Jacop and Alex

v5 -> v6:
- Fix compilation issue when CONFIG_IOMMU_API is unset

v4 -> v5:
- fix bug reported by Vincent: fault handler unregistration now happens in
  vfio_pci_release
- IOMMU_FAULT_PERM_* moved outside of struct definition + small
  uapi changes suggested by Kean-Philippe (except fetch_addr)
- iommu: introduce device fault report API: removed the PRI part.
- see individual logs for more details
- reset the ste abort flag on detach

v3 -> v4:
- took into account Alex, jean-Philippe and Robin's comments on v3
- rework of the smmuv3 driver integration
- add tear down ops for msi binding and PASID table binding
- fix S1 fault propagation
- put fault reporting patches at the beginning of the series following
  Jean-Philippe's request
- update of the cache invalidate and fault API uapis
- VFIO fault reporting rework with 2 separate regions and one mmappable
  segment for the fault queue
- moved to PATCH

v2 -> v3:
- When registering the S1 MSI binding we now store the device handle. This
  addresses Robin's comment about discimination of devices beonging to
  different S1 groups and using different physical MSI doorbells.
- Change the fault reporting API: use VFIO_PCI_DMA_FAULT_IRQ_INDEX to
  set the eventfd and expose the faults through an mmappable fault region

v1 -> v2:
- Added the fault reporting capability
- asid properly passed on invalidation (fix assignment of multiple
  devices)
- see individual change logs for more info


Eric Auger (15):
  iommu: Introduce attach/detach_pasid_table API
  iommu: Introduce bind/unbind_guest_msi
  iommu/arm-smmu-v3: Maintain a SID->device structure
  iommu/smmuv3: Dynamically allocate s1_cfg and s2_cfg
  iommu/smmuv3: Get prepared for nested stage support
  iommu/smmuv3: Implement attach/detach_pasid_table
  iommu/smmuv3: Allow stage 1 invalidation with unmanaged ASIDs
  iommu/smmuv3: Implement cache_invalidate
  dma-iommu: Implement NESTED_MSI cookie
  iommu/smmuv3: Nested mode single MSI doorbell per domain enforcement
  iommu/smmuv3: Enforce incompatibility between nested mode and HW MSI
regions
  iommu/smmuv3: Implement bind/unbind_guest_msi
  iommu/smmuv3: Report non recoverable faults
  iommu/smmuv3: Accept configs with more than one context descriptor
  iommu/smmuv3: Add PASID cache invalidation per PASID

 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 650 ++--
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h |  98 ++-
 drivers/iommu/dma-iommu.c   | 142 -
 drivers/iommu/iommu.c   | 104 
 include/linux/dma-iommu.h   |  16 +
 include/linux/iommu.h   |  41 ++
 include/uapi/linux/iommu.h  |  54 ++
 7 files changed, 1035 insertions(+), 70 deletions(-)

-- 
2.21.3

___
kvmarm mailing list
kvmarm@lis

[PATCH v12 01/15] iommu: Introduce attach/detach_pasid_table API

2020-11-16 Thread Eric Auger
In virtualization use case, when a guest is assigned
a PCI host device, protected by a virtual IOMMU on the guest,
the physical IOMMU must be programmed to be consistent with
the guest mappings. If the physical IOMMU supports two
translation stages it makes sense to program guest mappings
onto the first stage/level (ARM/Intel terminology) while the host
owns the stage/level 2.

In that case, it is mandated to trap on guest configuration
settings and pass those to the physical iommu driver.

This patch adds a new API to the iommu subsystem that allows
to set/unset the pasid table information.

A generic iommu_pasid_table_config struct is introduced in
a new iommu.h uapi header. This is going to be used by the VFIO
user API.

Signed-off-by: Jean-Philippe Brucker 
Signed-off-by: Liu, Yi L 
Signed-off-by: Ashok Raj 
Signed-off-by: Jacob Pan 
Signed-off-by: Eric Auger 

---

v11 -> v12:
- add argsz, name the union
---
 drivers/iommu/iommu.c  | 67 ++
 include/linux/iommu.h  | 21 
 include/uapi/linux/iommu.h | 54 ++
 3 files changed, 142 insertions(+)

diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index b53446bb8c6b..b061bf4c3bb2 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -2171,6 +2171,73 @@ int iommu_uapi_sva_unbind_gpasid(struct iommu_domain 
*domain, struct device *dev
 }
 EXPORT_SYMBOL_GPL(iommu_uapi_sva_unbind_gpasid);
 
+int iommu_attach_pasid_table(struct iommu_domain *domain,
+struct iommu_pasid_table_config *cfg)
+{
+   if (unlikely(!domain->ops->attach_pasid_table))
+   return -ENODEV;
+
+   return domain->ops->attach_pasid_table(domain, cfg);
+}
+
+int iommu_uapi_attach_pasid_table(struct iommu_domain *domain,
+ void __user *uinfo)
+{
+   struct iommu_pasid_table_config pasid_table_data = { 0 };
+   u32 minsz;
+
+   if (unlikely(!domain->ops->attach_pasid_table))
+   return -ENODEV;
+
+   /*
+* No new spaces can be added before the variable sized union, the
+* minimum size is the offset to the union.
+*/
+   minsz = offsetof(struct iommu_pasid_table_config, vendor_data);
+
+   /* Copy minsz from user to get flags and argsz */
+   if (copy_from_user(&pasid_table_data, uinfo, minsz))
+   return -EFAULT;
+
+   /* Fields before the variable size union are mandatory */
+   if (pasid_table_data.argsz < minsz)
+   return -EINVAL;
+
+   /* PASID and address granu require additional info beyond minsz */
+   if (pasid_table_data.version != PASID_TABLE_CFG_VERSION_1)
+   return -EINVAL;
+   if (pasid_table_data.format == IOMMU_PASID_FORMAT_SMMUV3 &&
+   pasid_table_data.argsz <
+   offsetofend(struct iommu_pasid_table_config, 
vendor_data.smmuv3))
+   return -EINVAL;
+
+   /*
+* User might be using a newer UAPI header which has a larger data
+* size, we shall support the existing flags within the current
+* size. Copy the remaining user data _after_ minsz but not more
+* than the current kernel supported size.
+*/
+   if (copy_from_user((void *)&pasid_table_data + minsz, uinfo + minsz,
+  min_t(u32, pasid_table_data.argsz, 
sizeof(pasid_table_data)) - minsz))
+   return -EFAULT;
+
+   /* Now the argsz is validated, check the content */
+   if (pasid_table_data.config < 1 && pasid_table_data.config > 3)
+   return -EINVAL;
+
+   return domain->ops->attach_pasid_table(domain, &pasid_table_data);
+}
+EXPORT_SYMBOL_GPL(iommu_uapi_attach_pasid_table);
+
+void iommu_detach_pasid_table(struct iommu_domain *domain)
+{
+   if (unlikely(!domain->ops->detach_pasid_table))
+   return;
+
+   domain->ops->detach_pasid_table(domain);
+}
+EXPORT_SYMBOL_GPL(iommu_detach_pasid_table);
+
 static void __iommu_detach_device(struct iommu_domain *domain,
  struct device *dev)
 {
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index b95a6f8db6ff..464fcbecf841 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -223,6 +223,8 @@ struct iommu_iotlb_gather {
  * @cache_invalidate: invalidate translation caches
  * @sva_bind_gpasid: bind guest pasid and mm
  * @sva_unbind_gpasid: unbind guest pasid and mm
+ * @attach_pasid_table: attach a pasid table
+ * @detach_pasid_table: detach the pasid table
  * @def_domain_type: device default domain type, return value:
  * - IOMMU_DOMAIN_IDENTITY: must use an identity domain
  * - IOMMU_DOMAIN_DMA: must use a dma domain
@@ -287,6 +289,9 @@ struct iommu_ops {
  void *drvdata);
void (*sva_unbind)(struct iommu_sva *handle);
u32 (*sva_get_pasid)(struct iommu_sva *handle);
+   int (*attach_pasid_tabl

[PATCH v12 03/15] iommu/arm-smmu-v3: Maintain a SID->device structure

2020-11-16 Thread Eric Auger
When handling faults from the event or PRI queue, we need to find the
struct device associated to a SID. Add a rb_tree to keep track of SIDs.

Signed-off-by: Eric Auger 
Signed-off-by: Jean-Philippe Brucker 
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 99 +
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 10 +++
 2 files changed, 109 insertions(+)

diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c 
b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index e634bbe60573..d828d6cbeb0e 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -1350,6 +1350,32 @@ static int arm_smmu_init_l2_strtab(struct 
arm_smmu_device *smmu, u32 sid)
return 0;
 }
 
+__maybe_unused
+static struct arm_smmu_master *
+arm_smmu_find_master(struct arm_smmu_device *smmu, u32 sid)
+{
+   struct rb_node *node;
+   struct arm_smmu_stream *stream;
+   struct arm_smmu_master *master = NULL;
+
+   mutex_lock(&smmu->streams_mutex);
+   node = smmu->streams.rb_node;
+   while (node) {
+   stream = rb_entry(node, struct arm_smmu_stream, node);
+   if (stream->id < sid) {
+   node = node->rb_right;
+   } else if (stream->id > sid) {
+   node = node->rb_left;
+   } else {
+   master = stream->master;
+   break;
+   }
+   }
+   mutex_unlock(&smmu->streams_mutex);
+
+   return master;
+}
+
 /* IRQ and event handlers */
 static irqreturn_t arm_smmu_evtq_thread(int irq, void *dev)
 {
@@ -2306,6 +2332,69 @@ static bool arm_smmu_sid_in_range(struct arm_smmu_device 
*smmu, u32 sid)
return sid < limit;
 }
 
+static int arm_smmu_insert_master(struct arm_smmu_device *smmu,
+ struct arm_smmu_master *master)
+{
+   int i;
+   int ret = 0;
+   struct arm_smmu_stream *new_stream, *cur_stream;
+   struct rb_node **new_node, *parent_node = NULL;
+
+   master->streams = kcalloc(master->num_sids,
+ sizeof(struct arm_smmu_stream), GFP_KERNEL);
+   if (!master->streams)
+   return -ENOMEM;
+
+   mutex_lock(&smmu->streams_mutex);
+   for (i = 0; i < master->num_sids && !ret; i++) {
+   new_stream = &master->streams[i];
+   new_stream->id = master->sids[i];
+   new_stream->master = master;
+
+   new_node = &(smmu->streams.rb_node);
+   while (*new_node) {
+   cur_stream = rb_entry(*new_node, struct arm_smmu_stream,
+ node);
+   parent_node = *new_node;
+   if (cur_stream->id > new_stream->id) {
+   new_node = &((*new_node)->rb_left);
+   } else if (cur_stream->id < new_stream->id) {
+   new_node = &((*new_node)->rb_right);
+   } else {
+   dev_warn(master->dev,
+"stream %u already in tree\n",
+cur_stream->id);
+   ret = -EINVAL;
+   break;
+   }
+   }
+
+   if (!ret) {
+   rb_link_node(&new_stream->node, parent_node, new_node);
+   rb_insert_color(&new_stream->node, &smmu->streams);
+   }
+   }
+   mutex_unlock(&smmu->streams_mutex);
+
+   return ret;
+}
+
+static void arm_smmu_remove_master(struct arm_smmu_device *smmu,
+  struct arm_smmu_master *master)
+{
+   int i;
+
+   if (!master->streams)
+   return;
+
+   mutex_lock(&smmu->streams_mutex);
+   for (i = 0; i < master->num_sids; i++)
+   rb_erase(&master->streams[i].node, &smmu->streams);
+   mutex_unlock(&smmu->streams_mutex);
+
+   kfree(master->streams);
+}
+
 static struct iommu_ops arm_smmu_ops;
 
 static struct iommu_device *arm_smmu_probe_device(struct device *dev)
@@ -2369,6 +2458,10 @@ static struct iommu_device *arm_smmu_probe_device(struct 
device *dev)
master->ssid_bits = min_t(u8, master->ssid_bits,
  CTXDESC_LINEAR_CDMAX);
 
+   ret = arm_smmu_insert_master(smmu, master);
+   if (ret)
+   goto err_free_master;
+
return &smmu->iommu;
 
 err_free_master:
@@ -2381,14 +2474,17 @@ static void arm_smmu_release_device(struct device *dev)
 {
struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
struct arm_smmu_master *master;
+   struct arm_smmu_device *smmu;
 
if (!fwspec || fwspec->ops != &arm_smmu_ops)
return;
 
master = dev_iommu_priv_get(dev);
+   smmu = master->smmu;
WARN_ON(arm

[PATCH v12 02/15] iommu: Introduce bind/unbind_guest_msi

2020-11-16 Thread Eric Auger
On ARM, MSI are translated by the SMMU. An IOVA is allocated
for each MSI doorbell. If both the host and the guest are exposed
with SMMUs, we end up with 2 different IOVAs allocated by each.
guest allocates an IOVA (gIOVA) to map onto the guest MSI
doorbell (gDB). The Host allocates another IOVA (hIOVA) to map
onto the physical doorbell (hDB).

So we end up with 2 untied mappings:
 S1S2
gIOVA->gDB
  hIOVA->hDB

Currently the PCI device is programmed by the host with hIOVA
as MSI doorbell. So this does not work.

This patch introduces an API to pass gIOVA/gDB to the host so
that gIOVA can be reused by the host instead of re-allocating
a new IOVA. So the goal is to create the following nested mapping:

 S1S2
gIOVA->gDB ->hDB

and program the PCI device with gIOVA MSI doorbell.

In case we have several devices attached to this nested domain
(devices belonging to the same group), they cannot be isolated
on guest side either. So they should also end up in the same domain
on guest side. We will enforce that all the devices attached to
the host iommu domain use the same physical doorbell and similarly
a single virtual doorbell mapping gets registered (1 single
virtual doorbell is used on guest as well).

Signed-off-by: Eric Auger 

---
v7 -> v8:
- dummy iommu_unbind_guest_msi turned into a void function

v6 -> v7:
- remove the device handle parameter.
- Add comments saying there can only be a single MSI binding
  registered per iommu_domain
v5 -> v6:
-fix compile issue when IOMMU_API is not set

v3 -> v4:
- add unbind

v2 -> v3:
- add a struct device handle
---
 drivers/iommu/iommu.c | 37 +
 include/linux/iommu.h | 20 
 2 files changed, 57 insertions(+)

diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index b061bf4c3bb2..3f311e25d6e2 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -2251,6 +2251,43 @@ static void __iommu_detach_device(struct iommu_domain 
*domain,
trace_detach_device_from_domain(dev);
 }
 
+/**
+ * iommu_bind_guest_msi - Passes the stage1 GIOVA/GPA mapping of a
+ * virtual doorbell
+ *
+ * @domain: iommu domain the stage 1 mapping will be attached to
+ * @iova: iova allocated by the guest
+ * @gpa: guest physical address of the virtual doorbell
+ * @size: granule size used for the mapping
+ *
+ * The associated IOVA can be reused by the host to create a nested
+ * stage2 binding mapping translating into the physical doorbell used
+ * by the devices attached to the domain.
+ *
+ * All devices within the domain must share the same physical doorbell.
+ * A single MSI GIOVA/GPA mapping can be attached to an iommu_domain.
+ */
+
+int iommu_bind_guest_msi(struct iommu_domain *domain,
+dma_addr_t giova, phys_addr_t gpa, size_t size)
+{
+   if (unlikely(!domain->ops->bind_guest_msi))
+   return -ENODEV;
+
+   return domain->ops->bind_guest_msi(domain, giova, gpa, size);
+}
+EXPORT_SYMBOL_GPL(iommu_bind_guest_msi);
+
+void iommu_unbind_guest_msi(struct iommu_domain *domain,
+   dma_addr_t iova)
+{
+   if (unlikely(!domain->ops->unbind_guest_msi))
+   return;
+
+   domain->ops->unbind_guest_msi(domain, iova);
+}
+EXPORT_SYMBOL_GPL(iommu_unbind_guest_msi);
+
 void iommu_detach_device(struct iommu_domain *domain, struct device *dev)
 {
struct iommu_group *group;
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index 464fcbecf841..35819bff03bc 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -225,6 +225,8 @@ struct iommu_iotlb_gather {
  * @sva_unbind_gpasid: unbind guest pasid and mm
  * @attach_pasid_table: attach a pasid table
  * @detach_pasid_table: detach the pasid table
+ * @bind_guest_msi: provides a stage1 giova/gpa MSI doorbell mapping
+ * @unbind_guest_msi: withdraw a stage1 giova/gpa MSI doorbell mapping
  * @def_domain_type: device default domain type, return value:
  * - IOMMU_DOMAIN_IDENTITY: must use an identity domain
  * - IOMMU_DOMAIN_DMA: must use a dma domain
@@ -305,6 +307,10 @@ struct iommu_ops {
 
int (*def_domain_type)(struct device *dev);
 
+   int (*bind_guest_msi)(struct iommu_domain *domain,
+ dma_addr_t giova, phys_addr_t gpa, size_t size);
+   void (*unbind_guest_msi)(struct iommu_domain *domain, dma_addr_t giova);
+
unsigned long pgsize_bitmap;
struct module *owner;
 };
@@ -444,6 +450,10 @@ extern int iommu_attach_pasid_table(struct iommu_domain 
*domain,
 extern int iommu_uapi_attach_pasid_table(struct iommu_domain *domain,
 void __user *udata);
 extern void iommu_detach_pasid_table(struct iommu_domain *domain);
+extern int iommu_bind_guest_msi(struct iommu_domain *domain,
+   dma_addr_t giova, phys_addr_t gpa, size_t size);
+extern v

[PATCH v12 05/15] iommu/smmuv3: Get prepared for nested stage support

2020-11-16 Thread Eric Auger
When nested stage translation is setup, both s1_cfg and
s2_cfg are allocated.

We introduce a new smmu domain abort field that will be set
upon guest stage1 configuration passing.

arm_smmu_write_strtab_ent() is modified to write both stage
fields in the STE and deal with the abort field.

In nested mode, only stage 2 is "finalized" as the host does
not own/configure the stage 1 context descriptor; guest does.

Signed-off-by: Eric Auger 

---
v10 -> v11:
- Fix an issue reported by Shameer when switching from with vSMMU
  to without vSMMU. Despite the spec does not seem to mention it
  seems to be needed to reset the 2 high 64b when switching from
  S1+S2 cfg to S1 only. Especially dst[3] needs to be reset (S2TTB).
  On some implementations, if the S2TTB is not reset, this causes
  a C_BAD_STE error
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 66 +
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h |  2 +
 2 files changed, 58 insertions(+), 10 deletions(-)

diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c 
b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 4baf9fafe462..9580090bd0c9 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -1181,8 +1181,10 @@ static void arm_smmu_write_strtab_ent(struct 
arm_smmu_master *master, u32 sid,
 * three cases at the moment:
 *
 * 1. Invalid (all zero) -> bypass/fault (init)
-* 2. Bypass/fault -> translation/bypass (attach)
-* 3. Translation/bypass -> bypass/fault (detach)
+* 2. Bypass/fault -> single stage translation/bypass (attach)
+* 3. Single or nested stage Translation/bypass -> bypass/fault (detach)
+* 4. S2 -> S1 + S2 (attach_pasid_table)
+* 5. S1 + S2 -> S2 (detach_pasid_table)
 *
 * Given that we can't update the STE atomically and the SMMU
 * doesn't read the thing in a defined order, that leaves us
@@ -1193,7 +1195,8 @@ static void arm_smmu_write_strtab_ent(struct 
arm_smmu_master *master, u32 sid,
 * 3. Update Config, sync
 */
u64 val = le64_to_cpu(dst[0]);
-   bool ste_live = false;
+   bool abort, translate, s1_live = false, s2_live = false, ste_live;
+   bool nested = false;
struct arm_smmu_device *smmu = NULL;
struct arm_smmu_s1_cfg *s1_cfg = NULL;
struct arm_smmu_s2_cfg *s2_cfg = NULL;
@@ -1213,6 +1216,7 @@ static void arm_smmu_write_strtab_ent(struct 
arm_smmu_master *master, u32 sid,
if (smmu_domain) {
s1_cfg = smmu_domain->s1_cfg;
s2_cfg = smmu_domain->s2_cfg;
+   nested = (smmu_domain->stage == ARM_SMMU_DOMAIN_NESTED);
}
 
if (val & STRTAB_STE_0_V) {
@@ -1220,23 +1224,37 @@ static void arm_smmu_write_strtab_ent(struct 
arm_smmu_master *master, u32 sid,
case STRTAB_STE_0_CFG_BYPASS:
break;
case STRTAB_STE_0_CFG_S1_TRANS:
+   s1_live = true;
+   break;
case STRTAB_STE_0_CFG_S2_TRANS:
-   ste_live = true;
+   s2_live = true;
+   break;
+   case STRTAB_STE_0_CFG_NESTED:
+   s1_live = true;
+   s2_live = true;
break;
case STRTAB_STE_0_CFG_ABORT:
-   BUG_ON(!disable_bypass);
break;
default:
BUG(); /* STE corruption */
}
}
 
+   ste_live = s1_live || s2_live;
+
/* Nuke the existing STE_0 value, as we're going to rewrite it */
val = STRTAB_STE_0_V;
 
/* Bypass/fault */
-   if (!smmu_domain || !(s1_cfg || s2_cfg)) {
-   if (!smmu_domain && disable_bypass)
+
+   if (!smmu_domain)
+   abort = disable_bypass;
+   else
+   abort = smmu_domain->abort;
+   translate = s1_cfg || s2_cfg;
+
+   if (abort || !translate) {
+   if (abort)
val |= FIELD_PREP(STRTAB_STE_0_CFG, 
STRTAB_STE_0_CFG_ABORT);
else
val |= FIELD_PREP(STRTAB_STE_0_CFG, 
STRTAB_STE_0_CFG_BYPASS);
@@ -1254,8 +1272,18 @@ static void arm_smmu_write_strtab_ent(struct 
arm_smmu_master *master, u32 sid,
return;
}
 
+   /* S1 or S2 translation */
+
+   BUG_ON(ste_live && !nested);
+
+   if (ste_live) {
+   /* First invalidate the live STE */
+   dst[0] = cpu_to_le64(STRTAB_STE_0_CFG_ABORT);
+   arm_smmu_sync_ste_for_sid(smmu, sid);
+   }
+
if (s1_cfg) {
-   BUG_ON(ste_live);
+   BUG_ON(s1_live);
dst[1] = cpu_to_le64(
 FIELD_PREP(STRTAB_STE_1_S1DSS, 
STRTAB_STE_1_S1DSS_SSID0) |
 FIELD_PREP(STRTAB_STE_1_S1CIR, 
ST

[PATCH v12 04/15] iommu/smmuv3: Dynamically allocate s1_cfg and s2_cfg

2020-11-16 Thread Eric Auger
In preparation for the introduction of nested stages
let's turn s1_cfg and s2_cfg fields into pointers which are
dynamically allocated depending on the smmu_domain stage.

In nested mode, both stages will coexist and s1_cfg will
be allocated when the guest configuration gets passed.

Signed-off-by: Eric Auger 
---
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 83 -
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h |  6 +-
 2 files changed, 48 insertions(+), 41 deletions(-)

diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c 
b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index d828d6cbeb0e..4baf9fafe462 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -953,9 +953,9 @@ static __le64 *arm_smmu_get_cd_ptr(struct arm_smmu_domain 
*smmu_domain,
unsigned int idx;
struct arm_smmu_l1_ctx_desc *l1_desc;
struct arm_smmu_device *smmu = smmu_domain->smmu;
-   struct arm_smmu_ctx_desc_cfg *cdcfg = &smmu_domain->s1_cfg.cdcfg;
+   struct arm_smmu_ctx_desc_cfg *cdcfg = &smmu_domain->s1_cfg->cdcfg;
 
-   if (smmu_domain->s1_cfg.s1fmt == STRTAB_STE_0_S1FMT_LINEAR)
+   if (smmu_domain->s1_cfg->s1fmt == STRTAB_STE_0_S1FMT_LINEAR)
return cdcfg->cdtab + ssid * CTXDESC_CD_DWORDS;
 
idx = ssid >> CTXDESC_SPLIT;
@@ -990,7 +990,7 @@ int arm_smmu_write_ctx_desc(struct arm_smmu_domain 
*smmu_domain, int ssid,
__le64 *cdptr;
struct arm_smmu_device *smmu = smmu_domain->smmu;
 
-   if (WARN_ON(ssid >= (1 << smmu_domain->s1_cfg.s1cdmax)))
+   if (WARN_ON(ssid >= (1 << smmu_domain->s1_cfg->s1cdmax)))
return -E2BIG;
 
cdptr = arm_smmu_get_cd_ptr(smmu_domain, ssid);
@@ -1056,7 +1056,7 @@ static int arm_smmu_alloc_cd_tables(struct 
arm_smmu_domain *smmu_domain)
size_t l1size;
size_t max_contexts;
struct arm_smmu_device *smmu = smmu_domain->smmu;
-   struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg;
+   struct arm_smmu_s1_cfg *cfg = smmu_domain->s1_cfg;
struct arm_smmu_ctx_desc_cfg *cdcfg = &cfg->cdcfg;
 
max_contexts = 1 << cfg->s1cdmax;
@@ -1104,7 +1104,7 @@ static void arm_smmu_free_cd_tables(struct 
arm_smmu_domain *smmu_domain)
int i;
size_t size, l1size;
struct arm_smmu_device *smmu = smmu_domain->smmu;
-   struct arm_smmu_ctx_desc_cfg *cdcfg = &smmu_domain->s1_cfg.cdcfg;
+   struct arm_smmu_ctx_desc_cfg *cdcfg = &smmu_domain->s1_cfg->cdcfg;
 
if (cdcfg->l1_desc) {
size = CTXDESC_L2_ENTRIES * (CTXDESC_CD_DWORDS << 3);
@@ -1211,17 +1211,8 @@ static void arm_smmu_write_strtab_ent(struct 
arm_smmu_master *master, u32 sid,
}
 
if (smmu_domain) {
-   switch (smmu_domain->stage) {
-   case ARM_SMMU_DOMAIN_S1:
-   s1_cfg = &smmu_domain->s1_cfg;
-   break;
-   case ARM_SMMU_DOMAIN_S2:
-   case ARM_SMMU_DOMAIN_NESTED:
-   s2_cfg = &smmu_domain->s2_cfg;
-   break;
-   default:
-   break;
-   }
+   s1_cfg = smmu_domain->s1_cfg;
+   s2_cfg = smmu_domain->s2_cfg;
}
 
if (val & STRTAB_STE_0_V) {
@@ -1664,10 +1655,10 @@ static void arm_smmu_tlb_inv_context(void *cookie)
 * careful, 007.
 */
if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
-   arm_smmu_tlb_inv_asid(smmu, smmu_domain->s1_cfg.cd.asid);
+   arm_smmu_tlb_inv_asid(smmu, smmu_domain->s1_cfg->cd.asid);
} else {
cmd.opcode  = CMDQ_OP_TLBI_S12_VMALL;
-   cmd.tlbi.vmid   = smmu_domain->s2_cfg.vmid;
+   cmd.tlbi.vmid   = smmu_domain->s2_cfg->vmid;
arm_smmu_cmdq_issue_cmd(smmu, &cmd);
arm_smmu_cmdq_issue_sync(smmu);
}
@@ -1693,10 +1684,10 @@ static void arm_smmu_tlb_inv_range(unsigned long iova, 
size_t size,
 
if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
cmd.opcode  = CMDQ_OP_TLBI_NH_VA;
-   cmd.tlbi.asid   = smmu_domain->s1_cfg.cd.asid;
+   cmd.tlbi.asid   = smmu_domain->s1_cfg->cd.asid;
} else {
cmd.opcode  = CMDQ_OP_TLBI_S2_IPA;
-   cmd.tlbi.vmid   = smmu_domain->s2_cfg.vmid;
+   cmd.tlbi.vmid   = smmu_domain->s2_cfg->vmid;
}
 
if (smmu->features & ARM_SMMU_FEAT_RANGE_INV) {
@@ -1846,24 +1837,25 @@ static void arm_smmu_domain_free(struct iommu_domain 
*domain)
 {
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
struct arm_smmu_device *smmu = smmu_domain->smmu;
+   struct arm_smmu_s1_cfg *s1_cfg = smmu_domain->s1_cfg;
+   struct arm_smmu_s2_cfg *s2_cfg = smmu_domain->s2_cfg;
 
iommu_put_dma_cookie(domain);
free_io_pgtable_ops(smmu_domain->pgtbl_ops);
 
/* Free 

Re: [PATCH] irqchip/gic-v4.1: Optimize the wait for the completion of the analysis of the VPT

2020-11-16 Thread Shenming Lu
Hi Marc,

Friendly ping, it is some time since I sent this patch according to your last 
advice...

Besides, recently we found that the mmio delay on GICv4.1 system is about 10 
times higher
than that on GICv4.0 system in kvm-unit-tests (the specific data is as 
follows). By the
way, HiSilicon GICv4.1 has already been implemented and will be released with 
our
next-generation server, which is almost the only implementation of GICv4.1 at 
present.

|   GICv4.1 emulator   |  GICv4.0 emulator
mmio_read_user (ns) |12811 |1598

After analysis, this is mainly caused by the 10 us delay in 
its_wait_vpt_parse_complete()
(the above difference is just about 10 us)...

What's your opinion about this?

Thanks,
Shenming

On 2020/9/23 14:35, Shenming Lu wrote:
> Right after a vPE is made resident, the code starts polling the
> GICR_VPENDBASER.Dirty bit until it becomes 0, where the delay_us
> is set to 10. But in our measurement, it takes only hundreds of
> nanoseconds, or 1~2 microseconds, to finish parsing the VPT in most
> cases. And we also measured the time from vcpu_load() (include it)
> to __guest_enter() on Kunpeng 920. On average, it takes 2.55 microseconds
> (not first run && the VPT is empty). So 10 microseconds delay might
> really hurt performance.
> 
> To avoid this, we can set the delay_us to 1, which is more appropriate
> in this situation and universal. Besides, we can delay the execution
> of its_wait_vpt_parse_complete() (call it from kvm_vgic_flush_hwstate()
> corresponding to vPE resident), giving the GIC a chance to work in
> parallel with the CPU on the entry path.
> 
> Signed-off-by: Shenming Lu 
> ---
>  arch/arm64/kvm/vgic/vgic-v4.c  | 18 ++
>  arch/arm64/kvm/vgic/vgic.c |  2 ++
>  drivers/irqchip/irq-gic-v3-its.c   | 14 +++---
>  drivers/irqchip/irq-gic-v4.c   | 11 +++
>  include/kvm/arm_vgic.h |  3 +++
>  include/linux/irqchip/arm-gic-v4.h |  4 
>  6 files changed, 49 insertions(+), 3 deletions(-)
> 
> diff --git a/arch/arm64/kvm/vgic/vgic-v4.c b/arch/arm64/kvm/vgic/vgic-v4.c
> index b5fa73c9fd35..1d5d2d6894d3 100644
> --- a/arch/arm64/kvm/vgic/vgic-v4.c
> +++ b/arch/arm64/kvm/vgic/vgic-v4.c
> @@ -353,6 +353,24 @@ int vgic_v4_load(struct kvm_vcpu *vcpu)
>   return err;
>  }
>  
> +void vgic_v4_wait_vpt(struct kvm_vcpu *vcpu)
> +{
> + struct its_vpe *vpe;
> +
> + if (kvm_vgic_global_state.type == VGIC_V2 || 
> !vgic_supports_direct_msis(vcpu->kvm))
> + return;
> +
> + vpe = &vcpu->arch.vgic_cpu.vgic_v3.its_vpe;
> +
> + if (vpe->vpt_ready)
> + return;
> +
> + if (its_wait_vpt(vpe))
> + return;
> +
> + vpe->vpt_ready = true;
> +}
> +
>  static struct vgic_its *vgic_get_its(struct kvm *kvm,
>struct kvm_kernel_irq_routing_entry 
> *irq_entry)
>  {
> diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c
> index c3643b7f101b..ed810a80cda2 100644
> --- a/arch/arm64/kvm/vgic/vgic.c
> +++ b/arch/arm64/kvm/vgic/vgic.c
> @@ -915,6 +915,8 @@ void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu)
>  
>   if (can_access_vgic_from_kernel())
>   vgic_restore_state(vcpu);
> +
> + vgic_v4_wait_vpt(vcpu);
>  }
>  
>  void kvm_vgic_load(struct kvm_vcpu *vcpu)
> diff --git a/drivers/irqchip/irq-gic-v3-its.c 
> b/drivers/irqchip/irq-gic-v3-its.c
> index 548de7538632..b7cbc9bcab9d 100644
> --- a/drivers/irqchip/irq-gic-v3-its.c
> +++ b/drivers/irqchip/irq-gic-v3-its.c
> @@ -3803,7 +3803,7 @@ static void its_wait_vpt_parse_complete(void)
>   WARN_ON_ONCE(readq_relaxed_poll_timeout_atomic(vlpi_base + 
> GICR_VPENDBASER,
>  val,
>  !(val & 
> GICR_VPENDBASER_Dirty),
> -10, 500));
> +1, 500));
>  }
>  
>  static void its_vpe_schedule(struct its_vpe *vpe)
> @@ -3837,7 +3837,7 @@ static void its_vpe_schedule(struct its_vpe *vpe)
>   val |= GICR_VPENDBASER_Valid;
>   gicr_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER);
>  
> - its_wait_vpt_parse_complete();
> + vpe->vpt_ready = false;
>  }
>  
>  static void its_vpe_deschedule(struct its_vpe *vpe)
> @@ -3881,6 +3881,10 @@ static int its_vpe_set_vcpu_affinity(struct irq_data 
> *d, void *vcpu_info)
>   its_vpe_schedule(vpe);
>   return 0;
>  
> + case WAIT_VPT:
> + its_wait_vpt_parse_complete();
> + return 0;
> +
>   case DESCHEDULE_VPE:
>   its_vpe_deschedule(vpe);
>   return 0;
> @@ -4047,7 +4051,7 @@ static void its_vpe_4_1_schedule(struct its_vpe *vpe,
>  
>   gicr_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER);
>  
> - its_wait_vpt_parse_complete();
> + vpe->vpt_ready = false;
>  }
>  
>  static void its_

Re: [RFC] vfio-pci/migration: Dirty logging of the Memory BAR region?

2020-11-16 Thread Zenghui Yu

On 2020/11/15 23:03, Paolo Bonzini wrote:

On 15/11/20 15:31, Zenghui Yu wrote:

diff --git a/softmmu/memory.c b/softmmu/memory.c
index 71951fe4dc..0958db1a08 100644
--- a/softmmu/memory.c
+++ b/softmmu/memory.c
@@ -1806,7 +1806,10 @@ bool memory_region_is_ram_device(MemoryRegion *mr)
 uint8_t memory_region_get_dirty_log_mask(MemoryRegion *mr)
 {
 uint8_t mask = mr->dirty_log_mask;
-if (global_dirty_log && (mr->ram_block || 
memory_region_is_iommu(mr))) {

+RAMBlock *rb = mr->ram_block;
+
+if (global_dirty_log && ((rb && qemu_ram_is_migratable(rb)) ||
+ memory_region_is_iommu(mr))) {
 mask |= (1 << DIRTY_MEMORY_MIGRATION);
 }
 return mask;


Yes, this makes sense.  Please send it as a patch, thanks!


Sure, I'm going to write a commit message for it.  Thanks for your
review.


Zenghui
___
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm