* [RFC PATCH 07/16] arm64/sve: Enable SVE state tracking for non-task contexts
From: Dave Martin @ 2018-06-21 14:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1529593060-542-1-git-send-email-Dave.Martin@arm.com>
The current FPSIMD/SVE context handling support for non-task (i.e.,
KVM vcpu) contexts does not take SVE into account. This means that
only task contexts can safely use SVE at present.
In preparation for enabling KVM guests to use SVE, it is necessary
to keep track of SVE state for non-task contexts too.
This patch adds the necessary support, removing assumptions from
the context switch code about the location of the SVE context
storage.
When binding a vcpu context, its vector length is arbitrarily
specified as sve_max_vl for now. In any case, because TIF_SVE is
presently cleared at vcpu context bind time, the specified vector
length will not be used for anything yet. In later patches TIF_SVE
will be set here as appropriate, and the appropriate maximum vector
length for the vcpu will be passed when binding.
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
---
arch/arm64/include/asm/fpsimd.h | 3 ++-
arch/arm64/kernel/fpsimd.c | 20 +++++++++++++++-----
arch/arm64/kvm/fpsimd.c | 4 +++-
3 files changed, 20 insertions(+), 7 deletions(-)
diff --git a/arch/arm64/include/asm/fpsimd.h b/arch/arm64/include/asm/fpsimd.h
index 3ad4607..d575e59 100644
--- a/arch/arm64/include/asm/fpsimd.h
+++ b/arch/arm64/include/asm/fpsimd.h
@@ -54,7 +54,8 @@ extern void fpsimd_restore_current_state(void);
extern void fpsimd_update_current_state(struct user_fpsimd_state const *state);
extern void fpsimd_bind_task_to_cpu(void);
-extern void fpsimd_bind_state_to_cpu(struct user_fpsimd_state *state);
+extern void fpsimd_bind_state_to_cpu(struct user_fpsimd_state *state,
+ void *sve_state, unsigned int sve_vl);
extern void fpsimd_flush_task_state(struct task_struct *target);
extern void fpsimd_flush_cpu_state(void);
diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c
index 390afb4..8afc518 100644
--- a/arch/arm64/kernel/fpsimd.c
+++ b/arch/arm64/kernel/fpsimd.c
@@ -121,6 +121,8 @@
*/
struct fpsimd_last_state_struct {
struct user_fpsimd_state *st;
+ void *sve_state;
+ unsigned int sve_vl;
};
static DEFINE_PER_CPU(struct fpsimd_last_state_struct, fpsimd_last_state);
@@ -260,14 +262,15 @@ static void task_fpsimd_load(void)
*/
void fpsimd_save(void)
{
- struct user_fpsimd_state *st = __this_cpu_read(fpsimd_last_state.st);
+ struct fpsimd_last_state_struct const *last =
+ this_cpu_ptr(&fpsimd_last_state);
/* set by fpsimd_bind_task_to_cpu() or fpsimd_bind_state_to_cpu() */
WARN_ON(!in_softirq() && !irqs_disabled());
if (!test_thread_flag(TIF_FOREIGN_FPSTATE)) {
if (system_supports_sve() && test_thread_flag(TIF_SVE)) {
- if (WARN_ON(sve_get_vl() != current->thread.sve_vl)) {
+ if (WARN_ON(sve_get_vl() != last->sve_vl)) {
/*
* Can't save the user regs, so current would
* re-enter user with corrupt state.
@@ -277,9 +280,11 @@ void fpsimd_save(void)
return;
}
- sve_save_state(sve_pffr(¤t->thread), &st->fpsr);
+ sve_save_state((char *)last->sve_state +
+ sve_ffr_offset(last->sve_vl),
+ &last->st->fpsr);
} else
- fpsimd_save_state(st);
+ fpsimd_save_state(last->st);
}
}
@@ -1053,6 +1058,8 @@ void fpsimd_bind_task_to_cpu(void)
this_cpu_ptr(&fpsimd_last_state);
last->st = ¤t->thread.uw.fpsimd_state;
+ last->sve_state = current->thread.sve_state;
+ last->sve_vl = current->thread.sve_vl;
current->thread.fpsimd_cpu = smp_processor_id();
if (system_supports_sve()) {
@@ -1066,7 +1073,8 @@ void fpsimd_bind_task_to_cpu(void)
}
}
-void fpsimd_bind_state_to_cpu(struct user_fpsimd_state *st)
+void fpsimd_bind_state_to_cpu(struct user_fpsimd_state *st, void *sve_state,
+ unsigned int sve_vl)
{
struct fpsimd_last_state_struct *last =
this_cpu_ptr(&fpsimd_last_state);
@@ -1074,6 +1082,8 @@ void fpsimd_bind_state_to_cpu(struct user_fpsimd_state *st)
WARN_ON(!in_softirq() && !irqs_disabled());
last->st = st;
+ last->sve_state = sve_state;
+ last->sve_vl = sve_vl;
}
/*
diff --git a/arch/arm64/kvm/fpsimd.c b/arch/arm64/kvm/fpsimd.c
index 4aaf78e..872008c 100644
--- a/arch/arm64/kvm/fpsimd.c
+++ b/arch/arm64/kvm/fpsimd.c
@@ -85,7 +85,9 @@ void kvm_arch_vcpu_ctxsync_fp(struct kvm_vcpu *vcpu)
WARN_ON_ONCE(!irqs_disabled());
if (vcpu->arch.flags & KVM_ARM64_FP_ENABLED) {
- fpsimd_bind_state_to_cpu(&vcpu->arch.ctxt.gp_regs.fp_regs);
+ fpsimd_bind_state_to_cpu(&vcpu->arch.ctxt.gp_regs.fp_regs,
+ NULL, sve_max_vl);
+
clear_thread_flag(TIF_FOREIGN_FPSTATE);
clear_thread_flag(TIF_SVE);
}
--
2.1.4
^ permalink raw reply related
* [RFC PATCH 08/16] KVM: arm64: Support dynamically hideable system registers
From: Dave Martin @ 2018-06-21 14:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1529593060-542-1-git-send-email-Dave.Martin@arm.com>
Some system registers may or may not logically exist for a vcpu
depending on whether certain architectural features are enabled for
the vcpu.
In order to avoid spuriously emulating access to these registers
when they should not exist, or allowing the registers to be
spuriously enumerated or saved/restored through the ioctl
interface, a means is needed to allow registers to be hidden
depending on the vcpu configuration.
In order to support this in a flexible way, this patch adds a
check_present() method to struct sys_reg_desc, and updates the
generic system register access and enumeration code to be aware of
it: if check_present() returns false, the code behaves as if the
register did not exist.
For convenience, the complete check is wrapped up in a new helper
sys_reg_present().
An attempt has been made to hook the new check into the generic
accessors for trapped system registers. This should reduce the
potential for future surprises, although the redundant check will
add a small cost. No system register depends on this functionality
yet, and some paths needing the check may also need attention.
Naturally, this facility makes sense only for registers that are
trapped.
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
---
arch/arm64/kvm/sys_regs.c | 20 +++++++++++++++-----
arch/arm64/kvm/sys_regs.h | 11 +++++++++++
2 files changed, 26 insertions(+), 5 deletions(-)
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index a436373..31a351a 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -1840,7 +1840,7 @@ static int emulate_cp(struct kvm_vcpu *vcpu,
r = find_reg(params, table, num);
- if (r) {
+ if (likely(r) && sys_reg_present(vcpu, r)) {
perform_access(vcpu, params, r);
return 0;
}
@@ -2016,7 +2016,7 @@ static int emulate_sys_reg(struct kvm_vcpu *vcpu,
if (!r)
r = find_reg(params, sys_reg_descs, ARRAY_SIZE(sys_reg_descs));
- if (likely(r)) {
+ if (likely(r) && sys_reg_present(vcpu, r)) {
perform_access(vcpu, params, r);
} else {
kvm_err("Unsupported guest sys_reg access at: %lx\n",
@@ -2313,6 +2313,9 @@ int kvm_arm_sys_reg_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg
if (!r)
return get_invariant_sys_reg(reg->id, uaddr);
+ if (!sys_reg_present(vcpu, r))
+ return -ENOENT;
+
if (r->get_user)
return (r->get_user)(vcpu, r, reg, uaddr);
@@ -2334,6 +2337,9 @@ int kvm_arm_sys_reg_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg
if (!r)
return set_invariant_sys_reg(reg->id, uaddr);
+ if (!sys_reg_present(vcpu, r))
+ return -ENOENT;
+
if (r->set_user)
return (r->set_user)(vcpu, r, reg, uaddr);
@@ -2390,7 +2396,8 @@ static bool copy_reg_to_user(const struct sys_reg_desc *reg, u64 __user **uind)
return true;
}
-static int walk_one_sys_reg(const struct sys_reg_desc *rd,
+static int walk_one_sys_reg(struct kvm_vcpu *vcpu,
+ const struct sys_reg_desc *rd,
u64 __user **uind,
unsigned int *total)
{
@@ -2401,6 +2408,9 @@ static int walk_one_sys_reg(const struct sys_reg_desc *rd,
if (!(rd->reg || rd->get_user))
return 0;
+ if (!sys_reg_present(vcpu, rd))
+ return 0;
+
if (!copy_reg_to_user(rd, uind))
return -EFAULT;
@@ -2429,9 +2439,9 @@ static int walk_sys_regs(struct kvm_vcpu *vcpu, u64 __user *uind)
int cmp = cmp_sys_reg(i1, i2);
/* target-specific overrides generic entry. */
if (cmp <= 0)
- err = walk_one_sys_reg(i1, &uind, &total);
+ err = walk_one_sys_reg(vcpu, i1, &uind, &total);
else
- err = walk_one_sys_reg(i2, &uind, &total);
+ err = walk_one_sys_reg(vcpu, i2, &uind, &total);
if (err)
return err;
diff --git a/arch/arm64/kvm/sys_regs.h b/arch/arm64/kvm/sys_regs.h
index cd710f8..dfbb342 100644
--- a/arch/arm64/kvm/sys_regs.h
+++ b/arch/arm64/kvm/sys_regs.h
@@ -22,6 +22,9 @@
#ifndef __ARM64_KVM_SYS_REGS_LOCAL_H__
#define __ARM64_KVM_SYS_REGS_LOCAL_H__
+#include <linux/compiler.h>
+#include <linux/types.h>
+
struct sys_reg_params {
u8 Op0;
u8 Op1;
@@ -61,8 +64,16 @@ struct sys_reg_desc {
const struct kvm_one_reg *reg, void __user *uaddr);
int (*set_user)(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
const struct kvm_one_reg *reg, void __user *uaddr);
+ bool (*check_present)(const struct kvm_vcpu *vpcu,
+ const struct sys_reg_desc *rd);
};
+static inline bool sys_reg_present(const struct kvm_vcpu *vcpu,
+ const struct sys_reg_desc *rd)
+{
+ return likely(!rd->check_present) || rd->check_present(vcpu, rd);
+}
+
static inline void print_sys_reg_instr(const struct sys_reg_params *p)
{
/* Look, we even formatted it for you to paste into the table! */
--
2.1.4
^ permalink raw reply related
* [RFC PATCH 09/16] KVM: arm64: Allow ID registers to by dynamically read-as-zero
From: Dave Martin @ 2018-06-21 14:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1529593060-542-1-git-send-email-Dave.Martin@arm.com>
When a feature-dependent ID register is hidden from the guest, it
needs to exhibit read-as-zero behaviour as defined by the Arm
architecture, rather than appearing to be entirely absent.
This patch updates the ID register emulation logic to make use of
the new check_present() method to determine whether the register
should read as zero instead of yielding the host's sanitised
value. Because currently a false result from this method truncates
the trap call chain before the sysreg's emulate method() is called,
a flag is added to distinguish this special case, and helpers are
refactored appropriately.
This invloves some trivial updates to pass the vcpu pointer down
into the ID register emulation/access functions.
A new ID_SANITISED_IF() macro is defined for declaring
conditionally visible ID registers.
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
---
arch/arm64/kvm/sys_regs.c | 51 ++++++++++++++++++++++++++++++-----------------
arch/arm64/kvm/sys_regs.h | 11 ++++++++++
2 files changed, 44 insertions(+), 18 deletions(-)
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 31a351a..87d2468 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -987,11 +987,17 @@ static bool access_cntp_cval(struct kvm_vcpu *vcpu,
}
/* Read a sanitised cpufeature ID register by sys_reg_desc */
-static u64 read_id_reg(struct sys_reg_desc const *r, bool raz)
+static u64 read_id_reg(const struct kvm_vcpu *vcpu,
+ struct sys_reg_desc const *r, bool raz)
{
u32 id = sys_reg((u32)r->Op0, (u32)r->Op1,
(u32)r->CRn, (u32)r->CRm, (u32)r->Op2);
- u64 val = raz ? 0 : read_sanitised_ftr_reg(id);
+ u64 val;
+
+ if (raz || !sys_reg_present(vcpu, r))
+ val = 0;
+ else
+ val = read_sanitised_ftr_reg(id);
if (id == SYS_ID_AA64PFR0_EL1) {
if (val & (0xfUL << ID_AA64PFR0_SVE_SHIFT))
@@ -1018,7 +1024,7 @@ static bool __access_id_reg(struct kvm_vcpu *vcpu,
if (p->is_write)
return write_to_read_only(vcpu, p, r);
- p->regval = read_id_reg(r, raz);
+ p->regval = read_id_reg(vcpu, r, raz);
return true;
}
@@ -1047,16 +1053,18 @@ static u64 sys_reg_to_index(const struct sys_reg_desc *reg);
* are stored, and for set_id_reg() we don't allow the effective value
* to be changed.
*/
-static int __get_id_reg(const struct sys_reg_desc *rd, void __user *uaddr,
+static int __get_id_reg(const struct kvm_vcpu *vcpu,
+ const struct sys_reg_desc *rd, void __user *uaddr,
bool raz)
{
const u64 id = sys_reg_to_index(rd);
- const u64 val = read_id_reg(rd, raz);
+ const u64 val = read_id_reg(vcpu, rd, raz);
return reg_to_user(uaddr, &val, id);
}
-static int __set_id_reg(const struct sys_reg_desc *rd, void __user *uaddr,
+static int __set_id_reg(const struct kvm_vcpu *vcpu,
+ const struct sys_reg_desc *rd, void __user *uaddr,
bool raz)
{
const u64 id = sys_reg_to_index(rd);
@@ -1068,7 +1076,7 @@ static int __set_id_reg(const struct sys_reg_desc *rd, void __user *uaddr,
return err;
/* This is what we mean by invariant: you can't change it. */
- if (val != read_id_reg(rd, raz))
+ if (val != read_id_reg(vcpu, rd, raz))
return -EINVAL;
return 0;
@@ -1077,33 +1085,40 @@ static int __set_id_reg(const struct sys_reg_desc *rd, void __user *uaddr,
static int get_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
const struct kvm_one_reg *reg, void __user *uaddr)
{
- return __get_id_reg(rd, uaddr, false);
+ return __get_id_reg(vcpu, rd, uaddr, false);
}
static int set_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
const struct kvm_one_reg *reg, void __user *uaddr)
{
- return __set_id_reg(rd, uaddr, false);
+ return __set_id_reg(vcpu, rd, uaddr, false);
}
static int get_raz_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
const struct kvm_one_reg *reg, void __user *uaddr)
{
- return __get_id_reg(rd, uaddr, true);
+ return __get_id_reg(vcpu, rd, uaddr, true);
}
static int set_raz_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
const struct kvm_one_reg *reg, void __user *uaddr)
{
- return __set_id_reg(rd, uaddr, true);
+ return __set_id_reg(vcpu, rd, uaddr, true);
}
/* sys_reg_desc initialiser for known cpufeature ID registers */
-#define ID_SANITISED(name) { \
+#define __ID_SANITISED(name) \
SYS_DESC(SYS_##name), \
.access = access_id_reg, \
.get_user = get_id_reg, \
- .set_user = set_id_reg, \
+ .set_user = set_id_reg
+
+#define ID_SANITISED(name) { __ID_SANITISED(name) }
+
+#define ID_SANITISED_IF(name, check) { \
+ __ID_SANITISED(name), \
+ .check_present = check, \
+ .flags = SR_RAZ_IF_ABSENT, \
}
/*
@@ -1840,7 +1855,7 @@ static int emulate_cp(struct kvm_vcpu *vcpu,
r = find_reg(params, table, num);
- if (likely(r) && sys_reg_present(vcpu, r)) {
+ if (likely(r) && sys_reg_present_or_raz(vcpu, r)) {
perform_access(vcpu, params, r);
return 0;
}
@@ -2016,7 +2031,7 @@ static int emulate_sys_reg(struct kvm_vcpu *vcpu,
if (!r)
r = find_reg(params, sys_reg_descs, ARRAY_SIZE(sys_reg_descs));
- if (likely(r) && sys_reg_present(vcpu, r)) {
+ if (likely(r) && sys_reg_present_or_raz(vcpu, r)) {
perform_access(vcpu, params, r);
} else {
kvm_err("Unsupported guest sys_reg access at: %lx\n",
@@ -2313,7 +2328,7 @@ int kvm_arm_sys_reg_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg
if (!r)
return get_invariant_sys_reg(reg->id, uaddr);
- if (!sys_reg_present(vcpu, r))
+ if (!sys_reg_present_or_raz(vcpu, r))
return -ENOENT;
if (r->get_user)
@@ -2337,7 +2352,7 @@ int kvm_arm_sys_reg_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg
if (!r)
return set_invariant_sys_reg(reg->id, uaddr);
- if (!sys_reg_present(vcpu, r))
+ if (!sys_reg_present_or_raz(vcpu, r))
return -ENOENT;
if (r->set_user)
@@ -2408,7 +2423,7 @@ static int walk_one_sys_reg(struct kvm_vcpu *vcpu,
if (!(rd->reg || rd->get_user))
return 0;
- if (!sys_reg_present(vcpu, rd))
+ if (!sys_reg_present_or_raz(vcpu, rd))
return 0;
if (!copy_reg_to_user(rd, uind))
diff --git a/arch/arm64/kvm/sys_regs.h b/arch/arm64/kvm/sys_regs.h
index dfbb342..304928f 100644
--- a/arch/arm64/kvm/sys_regs.h
+++ b/arch/arm64/kvm/sys_regs.h
@@ -66,14 +66,25 @@ struct sys_reg_desc {
const struct kvm_one_reg *reg, void __user *uaddr);
bool (*check_present)(const struct kvm_vcpu *vpcu,
const struct sys_reg_desc *rd);
+
+ /* OR of SR_* flags */
+ unsigned int flags;
};
+#define SR_RAZ_IF_ABSENT (1 << 0)
+
static inline bool sys_reg_present(const struct kvm_vcpu *vcpu,
const struct sys_reg_desc *rd)
{
return likely(!rd->check_present) || rd->check_present(vcpu, rd);
}
+static inline bool sys_reg_present_or_raz(const struct kvm_vcpu *vcpu,
+ const struct sys_reg_desc *rd)
+{
+ return sys_reg_present(vcpu, rd) || (rd->flags & SR_RAZ_IF_ABSENT);
+}
+
static inline void print_sys_reg_instr(const struct sys_reg_params *p)
{
/* Look, we even formatted it for you to paste into the table! */
--
2.1.4
^ permalink raw reply related
* [RFC PATCH 10/16] KVM: arm64: Add a vcpu flag to control SVE visibility for the guest
From: Dave Martin @ 2018-06-21 14:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1529593060-542-1-git-send-email-Dave.Martin@arm.com>
Since SVE will be enabled or disabled on a per-vcpu basis, a flag
is needed in order to track which vcpus have it enabled.
This patch adds a suitable flag and a helper for checking it.
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
---
arch/arm64/include/asm/kvm_host.h | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 9671ddd..609d08b 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -308,6 +308,14 @@ struct kvm_vcpu_arch {
#define KVM_ARM64_FP_HOST (1 << 2) /* host FP regs loaded */
#define KVM_ARM64_HOST_SVE_IN_USE (1 << 3) /* backup for host TIF_SVE */
#define KVM_ARM64_HOST_SVE_ENABLED (1 << 4) /* SVE enabled for EL0 */
+#define KVM_ARM64_GUEST_HAS_SVE (1 << 5) /* SVE exposed to guest */
+
+static inline bool vcpu_has_sve(struct kvm_vcpu_arch const *vcpu_arch)
+{
+ return system_supports_sve() &&
+ (vcpu_arch->flags & KVM_ARM64_GUEST_HAS_SVE);
+
+}
#define vcpu_gp_regs(v) (&(v)->arch.ctxt.gp_regs)
--
2.1.4
^ permalink raw reply related
* [RFC PATCH 11/16] KVM: arm64/sve: System register context switch and access support
From: Dave Martin @ 2018-06-21 14:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1529593060-542-1-git-send-email-Dave.Martin@arm.com>
This patch adds the necessary support for context switching ZCR_EL1
for each vcpu.
The ID_AA64PFR0_EL1 emulation code is updated to expose the
presence of SVE to the guest if appropriate, and ioctl() access to
ZCR_EL1 is also added.
In the context switch code itself, ZCR_EL1 is context switched if
the host is SVE-capable, irrespectively for now of whether SVE is
exposed to the guest or not. Adding a dynamic vcpu_has_sve() check
may lose as much performance as would be gained in this simple
case.
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
---
arch/arm64/include/asm/kvm_host.h | 1 +
arch/arm64/include/asm/sysreg.h | 3 +++
arch/arm64/kvm/hyp/sysreg-sr.c | 5 +++++
arch/arm64/kvm/sys_regs.c | 14 +++++++++-----
4 files changed, 18 insertions(+), 5 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 609d08b..f331abf 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -111,6 +111,7 @@ enum vcpu_sysreg {
SCTLR_EL1, /* System Control Register */
ACTLR_EL1, /* Auxiliary Control Register */
CPACR_EL1, /* Coprocessor Access Control */
+ ZCR_EL1, /* SVE Control */
TTBR0_EL1, /* Translation Table Base Register 0 */
TTBR1_EL1, /* Translation Table Base Register 1 */
TCR_EL1, /* Translation Control Register */
diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
index a8f8481..6476dbd 100644
--- a/arch/arm64/include/asm/sysreg.h
+++ b/arch/arm64/include/asm/sysreg.h
@@ -416,6 +416,9 @@
#define SYS_ICH_LR14_EL2 __SYS__LR8_EL2(6)
#define SYS_ICH_LR15_EL2 __SYS__LR8_EL2(7)
+/* VHE encodings for architectural EL0/1 system registers */
+#define SYS_ZCR_EL12 sys_reg(3, 5, 1, 2, 0)
+
/* Common SCTLR_ELx flags. */
#define SCTLR_ELx_EE (1 << 25)
#define SCTLR_ELx_IESB (1 << 21)
diff --git a/arch/arm64/kvm/hyp/sysreg-sr.c b/arch/arm64/kvm/hyp/sysreg-sr.c
index 35bc168..0f4046a 100644
--- a/arch/arm64/kvm/hyp/sysreg-sr.c
+++ b/arch/arm64/kvm/hyp/sysreg-sr.c
@@ -21,6 +21,7 @@
#include <asm/kvm_asm.h>
#include <asm/kvm_emulate.h>
#include <asm/kvm_hyp.h>
+#include <asm/sysreg.h>
/*
* Non-VHE: Both host and guest must save everything.
@@ -57,6 +58,8 @@ static void __hyp_text __sysreg_save_el1_state(struct kvm_cpu_context *ctxt)
ctxt->sys_regs[SCTLR_EL1] = read_sysreg_el1(sctlr);
ctxt->sys_regs[ACTLR_EL1] = read_sysreg(actlr_el1);
ctxt->sys_regs[CPACR_EL1] = read_sysreg_el1(cpacr);
+ if (system_supports_sve()) /* implies has_vhe() */
+ ctxt->sys_regs[ZCR_EL1] = read_sysreg_s(SYS_ZCR_EL12);
ctxt->sys_regs[TTBR0_EL1] = read_sysreg_el1(ttbr0);
ctxt->sys_regs[TTBR1_EL1] = read_sysreg_el1(ttbr1);
ctxt->sys_regs[TCR_EL1] = read_sysreg_el1(tcr);
@@ -129,6 +132,8 @@ static void __hyp_text __sysreg_restore_el1_state(struct kvm_cpu_context *ctxt)
write_sysreg_el1(ctxt->sys_regs[SCTLR_EL1], sctlr);
write_sysreg(ctxt->sys_regs[ACTLR_EL1], actlr_el1);
write_sysreg_el1(ctxt->sys_regs[CPACR_EL1], cpacr);
+ if (system_supports_sve()) /* implies has_vhe() */
+ write_sysreg_s(ctxt->sys_regs[ZCR_EL1], SYS_ZCR_EL12);
write_sysreg_el1(ctxt->sys_regs[TTBR0_EL1], ttbr0);
write_sysreg_el1(ctxt->sys_regs[TTBR1_EL1], ttbr1);
write_sysreg_el1(ctxt->sys_regs[TCR_EL1], tcr);
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 87d2468..dcaf6e5 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -986,6 +986,12 @@ static bool access_cntp_cval(struct kvm_vcpu *vcpu,
return true;
}
+static bool sve_check_present(const struct kvm_vcpu *vcpu,
+ const struct sys_reg_desc *rd)
+{
+ return vcpu_has_sve(&vcpu->arch);
+}
+
/* Read a sanitised cpufeature ID register by sys_reg_desc */
static u64 read_id_reg(const struct kvm_vcpu *vcpu,
struct sys_reg_desc const *r, bool raz)
@@ -999,10 +1005,7 @@ static u64 read_id_reg(const struct kvm_vcpu *vcpu,
else
val = read_sanitised_ftr_reg(id);
- if (id == SYS_ID_AA64PFR0_EL1) {
- if (val & (0xfUL << ID_AA64PFR0_SVE_SHIFT))
- kvm_debug("SVE unsupported for guests, suppressing\n");
-
+ if (id == SYS_ID_AA64PFR0_EL1 && !vcpu_has_sve(&vcpu->arch)) {
val &= ~(0xfUL << ID_AA64PFR0_SVE_SHIFT);
} else if (id == SYS_ID_AA64MMFR1_EL1) {
if (val & (0xfUL << ID_AA64MMFR1_LOR_SHIFT))
@@ -1240,7 +1243,7 @@ static const struct sys_reg_desc sys_reg_descs[] = {
ID_SANITISED(ID_AA64PFR1_EL1),
ID_UNALLOCATED(4,2),
ID_UNALLOCATED(4,3),
- ID_UNALLOCATED(4,4),
+ ID_SANITISED_IF(ID_AA64ZFR0_EL1, sve_check_present),
ID_UNALLOCATED(4,5),
ID_UNALLOCATED(4,6),
ID_UNALLOCATED(4,7),
@@ -1277,6 +1280,7 @@ static const struct sys_reg_desc sys_reg_descs[] = {
{ SYS_DESC(SYS_SCTLR_EL1), access_vm_reg, reset_val, SCTLR_EL1, 0x00C50078 },
{ SYS_DESC(SYS_CPACR_EL1), NULL, reset_val, CPACR_EL1, 0 },
+ { SYS_DESC(SYS_ZCR_EL1), NULL, reset_val, ZCR_EL1, 0, .check_present = sve_check_present },
{ SYS_DESC(SYS_TTBR0_EL1), access_vm_reg, reset_unknown, TTBR0_EL1 },
{ SYS_DESC(SYS_TTBR1_EL1), access_vm_reg, reset_unknown, TTBR1_EL1 },
{ SYS_DESC(SYS_TCR_EL1), access_vm_reg, reset_val, TCR_EL1, 0 },
--
2.1.4
^ permalink raw reply related
* [RFC PATCH 12/16] KVM: arm64/sve: Context switch the SVE registers
From: Dave Martin @ 2018-06-21 14:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1529593060-542-1-git-send-email-Dave.Martin@arm.com>
In order to give each vcpu its own view of the SVE registers, this
patch adds context storage via a new sve_state pointer in struct
vcpu_arch. An additional member sve_max_vl is also added for each
vcpu, to determine the maximum vector length visible to the guest
and thus the value to be configured in ZCR_EL2.LEN while the is
active. This also determines the layout and size of the storage in
sve_state, which is read and written by the same backend functions
that are used for context-switching the SVE state for host tasks.
On SVE-enabled vcpus, SVE access traps are now handled by switching
in the vcpu's SVE context and disabling the trap before returning
to the guest. On other vcpus, the trap is not handled and an exit
back to the host occurs, where the handle_sve() fallback path
reflects an undefined instruction exception back to the guest,
consistently with the behaviour of non-SVE-capable hardware (as was
done unconditionally prior to this patch).
No SVE handling is added on non-VHE-only paths, since VHE is an
architectural and Kconfig prerequisite of SVE.
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
---
arch/arm64/include/asm/kvm_host.h | 2 ++
arch/arm64/kvm/fpsimd.c | 5 +++--
arch/arm64/kvm/hyp/switch.c | 43 ++++++++++++++++++++++++++++++---------
3 files changed, 38 insertions(+), 12 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index f331abf..d2084ae 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -211,6 +211,8 @@ typedef struct kvm_cpu_context kvm_cpu_context_t;
struct kvm_vcpu_arch {
struct kvm_cpu_context ctxt;
+ void *sve_state;
+ unsigned int sve_max_vl;
/* HYP configuration */
u64 hcr_el2;
diff --git a/arch/arm64/kvm/fpsimd.c b/arch/arm64/kvm/fpsimd.c
index 872008c..44cf783 100644
--- a/arch/arm64/kvm/fpsimd.c
+++ b/arch/arm64/kvm/fpsimd.c
@@ -86,10 +86,11 @@ void kvm_arch_vcpu_ctxsync_fp(struct kvm_vcpu *vcpu)
if (vcpu->arch.flags & KVM_ARM64_FP_ENABLED) {
fpsimd_bind_state_to_cpu(&vcpu->arch.ctxt.gp_regs.fp_regs,
- NULL, sve_max_vl);
+ vcpu->arch.sve_state,
+ vcpu->arch.sve_max_vl);
clear_thread_flag(TIF_FOREIGN_FPSTATE);
- clear_thread_flag(TIF_SVE);
+ update_thread_flag(TIF_SVE, vcpu_has_sve(&vcpu->arch));
}
}
diff --git a/arch/arm64/kvm/hyp/switch.c b/arch/arm64/kvm/hyp/switch.c
index d496ef5..98df5c1 100644
--- a/arch/arm64/kvm/hyp/switch.c
+++ b/arch/arm64/kvm/hyp/switch.c
@@ -98,8 +98,13 @@ static void activate_traps_vhe(struct kvm_vcpu *vcpu)
val = read_sysreg(cpacr_el1);
val |= CPACR_EL1_TTA;
val &= ~CPACR_EL1_ZEN;
- if (!update_fp_enabled(vcpu))
+
+ if (update_fp_enabled(vcpu)) {
+ if (vcpu_has_sve(&vcpu->arch))
+ val |= CPACR_EL1_ZEN;
+ } else {
val &= ~CPACR_EL1_FPEN;
+ }
write_sysreg(val, cpacr_el1);
@@ -114,6 +119,7 @@ static void __hyp_text __activate_traps_nvhe(struct kvm_vcpu *vcpu)
val = CPTR_EL2_DEFAULT;
val |= CPTR_EL2_TTA | CPTR_EL2_TZ;
+
if (!update_fp_enabled(vcpu))
val |= CPTR_EL2_TFP;
@@ -329,16 +335,22 @@ static bool __hyp_text __skip_instr(struct kvm_vcpu *vcpu)
}
}
-static bool __hyp_text __hyp_switch_fpsimd(struct kvm_vcpu *vcpu)
+static bool __hyp_text __hyp_switch_fpsimd(struct kvm_vcpu *vcpu,
+ bool guest_has_sve)
{
struct user_fpsimd_state *host_fpsimd = vcpu->arch.host_fpsimd_state;
- if (has_vhe())
- write_sysreg(read_sysreg(cpacr_el1) | CPACR_EL1_FPEN,
- cpacr_el1);
- else
+ if (has_vhe()) {
+ u64 reg = read_sysreg(cpacr_el1) | CPACR_EL1_FPEN;
+
+ if (system_supports_sve() && guest_has_sve)
+ reg |= CPACR_EL1_ZEN;
+
+ write_sysreg(reg, cpacr_el1);
+ } else {
write_sysreg(read_sysreg(cptr_el2) & ~(u64)CPTR_EL2_TFP,
cptr_el2);
+ }
isb();
@@ -361,7 +373,13 @@ static bool __hyp_text __hyp_switch_fpsimd(struct kvm_vcpu *vcpu)
vcpu->arch.flags &= ~KVM_ARM64_FP_HOST;
}
- __fpsimd_restore_state(&vcpu->arch.ctxt.gp_regs.fp_regs);
+ if (system_supports_sve() && guest_has_sve)
+ sve_load_state((char *)vcpu->arch.sve_state +
+ sve_ffr_offset(vcpu->arch.sve_max_vl),
+ &vcpu->arch.ctxt.gp_regs.fp_regs.fpsr,
+ sve_vq_from_vl(vcpu->arch.sve_max_vl) - 1);
+ else
+ __fpsimd_restore_state(&vcpu->arch.ctxt.gp_regs.fp_regs);
/* Skip restoring fpexc32 for AArch64 guests */
if (!(read_sysreg(hcr_el2) & HCR_RW))
@@ -380,6 +398,8 @@ static bool __hyp_text __hyp_switch_fpsimd(struct kvm_vcpu *vcpu)
*/
static bool __hyp_text fixup_guest_exit(struct kvm_vcpu *vcpu, u64 *exit_code)
{
+ bool guest_has_sve;
+
if (ARM_EXCEPTION_CODE(*exit_code) != ARM_EXCEPTION_IRQ)
vcpu->arch.fault.esr_el2 = read_sysreg_el2(esr);
@@ -397,10 +417,13 @@ static bool __hyp_text fixup_guest_exit(struct kvm_vcpu *vcpu, u64 *exit_code)
* and restore the guest context lazily.
* If FP/SIMD is not implemented, handle the trap and inject an
* undefined instruction exception to the guest.
+ * Similarly for trapped SVE accesses.
*/
- if (system_supports_fpsimd() &&
- kvm_vcpu_trap_get_class(vcpu) == ESR_ELx_EC_FP_ASIMD)
- return __hyp_switch_fpsimd(vcpu);
+ guest_has_sve = vcpu_has_sve(&vcpu->arch);
+ if ((system_supports_fpsimd() &&
+ kvm_vcpu_trap_get_class(vcpu) == ESR_ELx_EC_FP_ASIMD) ||
+ (guest_has_sve && kvm_vcpu_trap_get_class(vcpu) == ESR_ELx_EC_SVE))
+ return __hyp_switch_fpsimd(vcpu, guest_has_sve);
if (!__populate_fault_info(vcpu))
return true;
--
2.1.4
^ permalink raw reply related
* [RFC PATCH 13/16] KVM: Allow 2048-bit register access via KVM_{GET, SET}_ONE_REG
From: Dave Martin @ 2018-06-21 14:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1529593060-542-1-git-send-email-Dave.Martin@arm.com>
The Arm SVE architecture defines registers that are up to 2048 bits
in size (with some possibility of further future expansion).
In order to avoid the need for an excessively large number of
ioctls when saving and restoring a vcpu's registers, this patch
adds a #define to make support for individual 2048-bit registers
through the KVM_{GET,SET}_ONE_REG ioctl interface official. This
will allow each SVE register to be accessed in a single call.
There are sufficient spare bits in the register id size field for
this change, so there is no ABI impact providing that
KVM_GET_REG_LIST does not enumerate any 2048-bit register unless
userspace explicitly opts in to the relevant architecture-specific
features.
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
---
include/uapi/linux/kvm.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index b6270a3..345be88 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1106,6 +1106,7 @@ struct kvm_dirty_tlb {
#define KVM_REG_SIZE_U256 0x0050000000000000ULL
#define KVM_REG_SIZE_U512 0x0060000000000000ULL
#define KVM_REG_SIZE_U1024 0x0070000000000000ULL
+#define KVM_REG_SIZE_U2048 0x0080000000000000ULL
struct kvm_reg_list {
__u64 n; /* number of regs */
--
2.1.4
^ permalink raw reply related
* [RFC PATCH 14/16] KVM: arm64/sve: Add SVE support to register access ioctl interface
From: Dave Martin @ 2018-06-21 14:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1529593060-542-1-git-send-email-Dave.Martin@arm.com>
This patch adds the following registers for access via the
KVM_{GET,SET}_ONE_REG interface:
* KVM_REG_ARM64_SVE_ZREG(n, i) (n = 0..31) (in 2048-bit slices)
* KVM_REG_ARM64_SVE_PREG(n, i) (n = 0..15) (in 256-bit slices)
* KVM_REG_ARM64_SVE_FFR(i) (in 256-bit slices)
In order to adapt gracefully to future architectural extensions,
the registers are divided up into slices as noted above: the i
parameter denotes the slice index.
For simplicity, bits or slices that exceed the maximum vector
length supported for the vcpu are ignored for KVM_SET_ONE_REG, and
read as zero for KVM_GET_ONE_REG.
For the current architecture, only slice i = 0 is significant. The
interface design allows i to increase to up to 31 in the future if
required by future architectural amendments.
The registers are only visible for vcpus that have SVE enabled.
They are not enumerated by KVM_GET_REG_LIST on vcpus that do not
have SVE. In all cases, surplus slices are not enumerated by
KVM_GET_REG_LIST.
Accesses to the FPSIMD registers via KVM_REG_ARM_CORE are
redirected to access the underlying vcpu SVE register storage as
appropriate. In order to make this more straightforward, register
accesses that straddle register boundaries are no longer guaranteed
to succeed. (Support for such use was never deliberate, and
userspace does not currently seem to be relying on it.)
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
---
arch/arm64/include/uapi/asm/kvm.h | 10 ++
arch/arm64/kvm/guest.c | 219 +++++++++++++++++++++++++++++++++++---
2 files changed, 216 insertions(+), 13 deletions(-)
diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
index 4e76630..f54a9b0 100644
--- a/arch/arm64/include/uapi/asm/kvm.h
+++ b/arch/arm64/include/uapi/asm/kvm.h
@@ -213,6 +213,16 @@ struct kvm_arch_memory_slot {
KVM_REG_ARM_FW | ((r) & 0xffff))
#define KVM_REG_ARM_PSCI_VERSION KVM_REG_ARM_FW_REG(0)
+/* SVE registers */
+#define KVM_REG_ARM64_SVE (0x15 << KVM_REG_ARM_COPROC_SHIFT)
+#define KVM_REG_ARM64_SVE_ZREG(n, i) (KVM_REG_ARM64 | KVM_REG_ARM64_SVE | \
+ KVM_REG_SIZE_U2048 | \
+ ((n) << 5) | (i))
+#define KVM_REG_ARM64_SVE_PREG(n, i) (KVM_REG_ARM64 | KVM_REG_ARM64_SVE | \
+ KVM_REG_SIZE_U256 | \
+ ((n) << 5) | (i) | 0x400)
+#define KVM_REG_ARM64_SVE_FFR(i) KVM_REG_ARM64_SVE_PREG(16, i)
+
/* Device Control API: ARM VGIC */
#define KVM_DEV_ARM_VGIC_GRP_ADDR 0
#define KVM_DEV_ARM_VGIC_GRP_DIST_REGS 1
diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
index 4a9d77c..005394b 100644
--- a/arch/arm64/kvm/guest.c
+++ b/arch/arm64/kvm/guest.c
@@ -23,14 +23,19 @@
#include <linux/err.h>
#include <linux/kvm_host.h>
#include <linux/module.h>
+#include <linux/uaccess.h>
#include <linux/vmalloc.h>
#include <linux/fs.h>
+#include <linux/stddef.h>
#include <kvm/arm_psci.h>
#include <asm/cputype.h>
#include <linux/uaccess.h>
+#include <asm/fpsimd.h>
#include <asm/kvm.h>
#include <asm/kvm_emulate.h>
#include <asm/kvm_coproc.h>
+#include <asm/kvm_host.h>
+#include <asm/sigcontext.h>
#include "trace.h"
@@ -57,6 +62,106 @@ static u64 core_reg_offset_from_id(u64 id)
return id & ~(KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK | KVM_REG_ARM_CORE);
}
+static bool is_zreg(const struct kvm_one_reg *reg)
+{
+ return reg->id >= KVM_REG_ARM64_SVE_ZREG(0, 0) &&
+ reg->id <= KVM_REG_ARM64_SVE_ZREG(SVE_NUM_ZREGS, 0x1f);
+}
+
+static bool is_preg(const struct kvm_one_reg *reg)
+{
+ return reg->id >= KVM_REG_ARM64_SVE_PREG(0, 0) &&
+ reg->id <= KVM_REG_ARM64_SVE_FFR(0x1f);
+}
+
+static unsigned int sve_reg_num(const struct kvm_one_reg *reg)
+{
+ return (reg->id >> 5) & 0x1f;
+}
+
+static unsigned int sve_reg_index(const struct kvm_one_reg *reg)
+{
+ return reg->id & 0x1f;
+}
+
+struct reg_bounds_struct {
+ char *kptr;
+ size_t start_offset;
+ size_t copy_count;
+ size_t flush_count;
+};
+
+static int copy_bounded_reg_to_user(void __user *uptr,
+ const struct reg_bounds_struct *b)
+{
+ if (copy_to_user(uptr, b->kptr, b->copy_count) ||
+ clear_user((char __user *)uptr + b->copy_count, b->flush_count))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int copy_bounded_reg_from_user(const struct reg_bounds_struct *b,
+ const void __user *uptr)
+{
+ if (copy_from_user(b->kptr, uptr, b->copy_count))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int fpsimd_vreg_bounds(struct reg_bounds_struct *b,
+ struct kvm_vcpu *vcpu,
+ const struct kvm_one_reg *reg)
+{
+ const size_t stride = KVM_REG_ARM_CORE_REG(fp_regs.vregs[1]) -
+ KVM_REG_ARM_CORE_REG(fp_regs.vregs[0]);
+ const size_t start = KVM_REG_ARM_CORE_REG(fp_regs.vregs[0]);
+ const size_t limit = KVM_REG_ARM_CORE_REG(fp_regs.vregs[32]);
+
+ const u64 uoffset = core_reg_offset_from_id(reg->id);
+ size_t usize = KVM_REG_SIZE(reg->id);
+ size_t start_vreg, end_vreg;
+
+ if (WARN_ON((reg->id & KVM_REG_ARM_COPROC_MASK) != KVM_REG_ARM_CORE))
+ return -ENOENT;
+
+ if (usize % sizeof(u32))
+ return -EINVAL;
+
+ usize /= sizeof(u32);
+
+ if ((uoffset <= start && usize <= start - uoffset) ||
+ uoffset >= limit)
+ return -ENOENT; /* not a vreg */
+
+ BUILD_BUG_ON(uoffset > limit);
+ if (uoffset < start || usize > limit - uoffset)
+ return -EINVAL; /* overlaps vregs[] bounds */
+
+ start_vreg = (uoffset - start) / stride;
+ end_vreg = ((uoffset - start) + usize - 1) / stride;
+ if (start_vreg != end_vreg)
+ return -EINVAL; /* spans multiple vregs */
+
+ b->start_offset = ((uoffset - start) % stride) * sizeof(u32);
+ b->copy_count = usize * sizeof(u32);
+ b->flush_count = 0;
+
+ if (vcpu_has_sve(&vcpu->arch)) {
+ const unsigned int vq = sve_vq_from_vl(vcpu->arch.sve_max_vl);
+
+ b->kptr = vcpu->arch.sve_state;
+ b->kptr += (SVE_SIG_ZREG_OFFSET(vq, start_vreg) -
+ SVE_SIG_REGS_OFFSET);
+ } else {
+ b->kptr = (char *)&vcpu_gp_regs(vcpu)->fp_regs.vregs[
+ start_vreg];
+ }
+
+ return 0;
+}
+
static int get_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
{
/*
@@ -65,11 +170,20 @@ static int get_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
* array. Hence below, nr_regs is the number of entries, and
* off the index in the "array".
*/
+ int err;
+ struct reg_bounds_struct b;
__u32 __user *uaddr = (__u32 __user *)(unsigned long)reg->addr;
struct kvm_regs *regs = vcpu_gp_regs(vcpu);
int nr_regs = sizeof(*regs) / sizeof(__u32);
u32 off;
+ err = fpsimd_vreg_bounds(&b, vcpu, reg);
+ switch (err) {
+ case 0: return copy_bounded_reg_to_user(uaddr, &b);
+ case -ENOENT: break; /* not and FPSIMD vreg */
+ default: return err;
+ }
+
/* Our ID is an index into the kvm_regs struct. */
off = core_reg_offset_from_id(reg->id);
if (off >= nr_regs ||
@@ -84,14 +198,23 @@ static int get_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
static int set_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
{
+ int err;
+ struct reg_bounds_struct b;
__u32 __user *uaddr = (__u32 __user *)(unsigned long)reg->addr;
struct kvm_regs *regs = vcpu_gp_regs(vcpu);
int nr_regs = sizeof(*regs) / sizeof(__u32);
__uint128_t tmp;
void *valp = &tmp;
u64 off;
- int err = 0;
+ err = fpsimd_vreg_bounds(&b, vcpu, reg);
+ switch (err) {
+ case 0: return copy_bounded_reg_from_user(&b, uaddr);
+ case -ENOENT: break; /* not and FPSIMD vreg */
+ default: return err;
+ }
+
+ err = 0;
/* Our ID is an index into the kvm_regs struct. */
off = core_reg_offset_from_id(reg->id);
if (off >= nr_regs ||
@@ -130,6 +253,78 @@ static int set_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
return err;
}
+static int sve_reg_bounds(struct reg_bounds_struct *b,
+ const struct kvm_vcpu *vcpu,
+ const struct kvm_one_reg *reg)
+{
+ unsigned int n = sve_reg_num(reg);
+ unsigned int i = sve_reg_index(reg);
+ unsigned int vl = vcpu->arch.sve_max_vl;
+ unsigned int vq = sve_vq_from_vl(vl);
+ unsigned int start, copy_limit, limit;
+
+ b->kptr = vcpu->arch.sve_state;
+ if (is_zreg(reg)) {
+ b->kptr += SVE_SIG_ZREG_OFFSET(vq, n) - SVE_SIG_REGS_OFFSET;
+ start = i * 0x100;
+ limit = start + 0x100;
+ copy_limit = vl;
+ } else if (is_preg(reg)) {
+ b->kptr += SVE_SIG_PREG_OFFSET(vq, n) - SVE_SIG_REGS_OFFSET;
+ start = i * 0x20;
+ limit = start + 0x20;
+ copy_limit = vl / 8;
+ } else {
+ WARN_ON(1);
+ start = 0;
+ copy_limit = limit = 0;
+ }
+
+ b->kptr += start;
+
+ if (copy_limit < start)
+ copy_limit = start;
+ else if (copy_limit > limit)
+ copy_limit = limit;
+
+ b->copy_count = copy_limit - start;
+ b->flush_count = limit - copy_limit;
+
+ return 0;
+}
+
+static int get_sve_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
+{
+ int ret;
+ struct reg_bounds_struct b;
+ char __user *uptr = (char __user *)reg->addr;
+
+ if (!vcpu_has_sve(&vcpu->arch))
+ return -ENOENT;
+
+ ret = sve_reg_bounds(&b, vcpu, reg);
+ if (ret)
+ return ret;
+
+ return copy_bounded_reg_to_user(uptr, &b);
+}
+
+static int set_sve_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
+{
+ int ret;
+ struct reg_bounds_struct b;
+ char __user *uptr = (char __user *)reg->addr;
+
+ if (!vcpu_has_sve(&vcpu->arch))
+ return -ENOENT;
+
+ ret = sve_reg_bounds(&b, vcpu, reg);
+ if (ret)
+ return ret;
+
+ return copy_bounded_reg_from_user(&b, uptr);
+}
+
int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
{
return -EINVAL;
@@ -251,12 +446,11 @@ int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
if ((reg->id & ~KVM_REG_SIZE_MASK) >> 32 != KVM_REG_ARM64 >> 32)
return -EINVAL;
- /* Register group 16 means we want a core register. */
- if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE)
- return get_core_reg(vcpu, reg);
-
- if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_FW)
- return kvm_arm_get_fw_reg(vcpu, reg);
+ switch (reg->id & KVM_REG_ARM_COPROC_MASK) {
+ case KVM_REG_ARM_CORE: return get_core_reg(vcpu, reg);
+ case KVM_REG_ARM_FW: return kvm_arm_get_fw_reg(vcpu, reg);
+ case KVM_REG_ARM64_SVE: return get_sve_reg(vcpu, reg);
+ }
if (is_timer_reg(reg->id))
return get_timer_reg(vcpu, reg);
@@ -270,12 +464,11 @@ int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
if ((reg->id & ~KVM_REG_SIZE_MASK) >> 32 != KVM_REG_ARM64 >> 32)
return -EINVAL;
- /* Register group 16 means we set a core register. */
- if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE)
- return set_core_reg(vcpu, reg);
-
- if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_FW)
- return kvm_arm_set_fw_reg(vcpu, reg);
+ switch (reg->id & KVM_REG_ARM_COPROC_MASK) {
+ case KVM_REG_ARM_CORE: return set_core_reg(vcpu, reg);
+ case KVM_REG_ARM_FW: return kvm_arm_set_fw_reg(vcpu, reg);
+ case KVM_REG_ARM64_SVE: return set_sve_reg(vcpu, reg);
+ }
if (is_timer_reg(reg->id))
return set_timer_reg(vcpu, reg);
--
2.1.4
^ permalink raw reply related
* [RFC PATCH 15/16] KVM: arm64: Enumerate SVE register indices for KVM_GET_REG_LIST
From: Dave Martin @ 2018-06-21 14:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1529593060-542-1-git-send-email-Dave.Martin@arm.com>
This patch includes the SVE register IDs in the list returned by
KVM_GET_REG_LIST, as appropriate.
On a non-SVE-enabled vcpu, no extra IDs are added.
On an SVE-enabled vcpu, the appropriate number of slide IDs are
enumerated for each SVE register, depending on the maximum vector
length for the vcpu.
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
---
arch/arm64/kvm/guest.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 73 insertions(+)
diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
index 005394b..5152362 100644
--- a/arch/arm64/kvm/guest.c
+++ b/arch/arm64/kvm/guest.c
@@ -21,6 +21,7 @@
#include <linux/errno.h>
#include <linux/err.h>
+#include <linux/kernel.h>
#include <linux/kvm_host.h>
#include <linux/module.h>
#include <linux/uaccess.h>
@@ -253,6 +254,73 @@ static int set_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
return err;
}
+static void copy_reg_index_to_user(u64 __user **uind, int *total, int *cerr,
+ u64 id)
+{
+ int err;
+
+ if (*cerr)
+ return;
+
+ if (uind) {
+ err = put_user(id, *uind);
+ if (err) {
+ *cerr = err;
+ return;
+ }
+ }
+
+ ++*total;
+ if (uind)
+ ++*uind;
+}
+
+static int enumerate_sve_regs(const struct kvm_vcpu *vcpu, u64 __user **uind)
+{
+ unsigned int n, i;
+ int err = 0;
+ int total = 0;
+ unsigned int slices;
+
+ if (!vcpu_has_sve(&vcpu->arch))
+ return 0;
+
+ slices = DIV_ROUND_UP(vcpu->arch.sve_max_vl,
+ KVM_REG_SIZE(KVM_REG_ARM64_SVE_ZREG(0, 0)));
+
+ for (n = 0; n < SVE_NUM_ZREGS; ++n)
+ for (i = 0; i < slices; ++i)
+ copy_reg_index_to_user(uind, &total, &err,
+ KVM_REG_ARM64_SVE_ZREG(n, i));
+
+ for (n = 0; n < SVE_NUM_PREGS; ++n)
+ for (i = 0; i < slices; ++i)
+ copy_reg_index_to_user(uind, &total, &err,
+ KVM_REG_ARM64_SVE_PREG(n, i));
+
+ for (i = 0; i < slices; ++i)
+ copy_reg_index_to_user(uind, &total, &err,
+ KVM_REG_ARM64_SVE_FFR(i));
+
+ if (err)
+ return -EFAULT;
+
+ return total;
+}
+
+static unsigned long num_sve_regs(const struct kvm_vcpu *vcpu)
+{
+ return enumerate_sve_regs(vcpu, NULL);
+}
+
+static int copy_sve_reg_indices(const struct kvm_vcpu *vcpu, u64 __user **uind)
+{
+ int err;
+
+ err = enumerate_sve_regs(vcpu, uind);
+ return err < 0 ? err : 0;
+}
+
static int sve_reg_bounds(struct reg_bounds_struct *b,
const struct kvm_vcpu *vcpu,
const struct kvm_one_reg *reg)
@@ -403,6 +471,7 @@ unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu)
unsigned long res = 0;
res += num_core_regs();
+ res += num_sve_regs(vcpu);
res += kvm_arm_num_sys_reg_descs(vcpu);
res += kvm_arm_get_fw_num_regs(vcpu);
res += NUM_TIMER_REGS;
@@ -427,6 +496,10 @@ int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
uindices++;
}
+ ret = copy_sve_reg_indices(vcpu, &uindices);
+ if (ret)
+ return ret;
+
ret = kvm_arm_copy_fw_reg_indices(vcpu, uindices);
if (ret)
return ret;
--
2.1.4
^ permalink raw reply related
* [RFC PATCH 16/16] KVM: arm64/sve: Report and enable SVE API extensions for userspace
From: Dave Martin @ 2018-06-21 14:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1529593060-542-1-git-send-email-Dave.Martin@arm.com>
This patch reports the availability of KVM SVE support to userspace
via a new vcpu feature flag KVM_ARM_VCPU_SVE. This flag is
reported via the KVM_ARM_PREFERRED_TARGET ioctl.
Userspace can enable the feature by setting the flag for
KVM_ARM_VCPU_INIT. Without this flag set, SVE-related ioctls and
register access extensions are hidden, and SVE remains disabled
unconditionally for the guest. This ensures that non-SVE-aware KVM
userspace does not receive a vcpu that it does not understand how
to snapshot or restore correctly.
Storage is allocated for the SVE register state at vcpu init time,
sufficient for the maximum vector length to be exposed to the vcpu.
No attempt is made to allocate the storage lazily for now. Also,
no attempt is made to resize the storage dynamically, since the
effective vector length of the vcpu can change at each EL0/EL1
transition. The storage is freed at the vcpu uninit hook.
No particular attempt is made to prevent userspace from creating a
mix of vcpus some of which have SVE enabled and some of which have
it disabled. This may or may not be useful, but it reflects the
underlying architectural behaviour.
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
---
arch/arm64/include/asm/kvm_host.h | 6 +++---
arch/arm64/include/uapi/asm/kvm.h | 1 +
arch/arm64/kvm/guest.c | 19 +++++++++++++------
arch/arm64/kvm/reset.c | 14 ++++++++++++++
4 files changed, 31 insertions(+), 9 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index d2084ae..d956cf2 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -44,7 +44,7 @@
#define KVM_MAX_VCPUS VGIC_V3_MAX_CPUS
-#define KVM_VCPU_MAX_FEATURES 4
+#define KVM_VCPU_MAX_FEATURES 5
#define KVM_REQ_SLEEP \
KVM_ARCH_REQ_FLAGS(0, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP)
@@ -439,8 +439,8 @@ static inline void kvm_arch_sync_events(struct kvm *kvm) {}
static inline void kvm_arch_sched_in(struct kvm_vcpu *vcpu, int cpu) {}
static inline void kvm_arch_vcpu_block_finish(struct kvm_vcpu *vcpu) {}
-static inline int kvm_arm_arch_vcpu_init(struct kvm_vcpu *vcpu) { return 0; }
-static inline void kvm_arm_arch_vcpu_uninit(struct kvm_vcpu *vcpu) {}
+int kvm_arm_arch_vcpu_init(struct kvm_vcpu *vcpu);
+void kvm_arm_arch_vcpu_uninit(struct kvm_vcpu *vcpu);
void kvm_arm_init_debug(void);
void kvm_arm_setup_debug(struct kvm_vcpu *vcpu);
diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
index f54a9b0..6acf276 100644
--- a/arch/arm64/include/uapi/asm/kvm.h
+++ b/arch/arm64/include/uapi/asm/kvm.h
@@ -101,6 +101,7 @@ struct kvm_regs {
#define KVM_ARM_VCPU_EL1_32BIT 1 /* CPU running a 32bit VM */
#define KVM_ARM_VCPU_PSCI_0_2 2 /* CPU uses PSCI v0.2 */
#define KVM_ARM_VCPU_PMU_V3 3 /* Support guest PMUv3 */
+#define KVM_ARM_VCPU_SVE 4 /* Allow SVE for guest */
struct kvm_vcpu_init {
__u32 target;
diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
index 5152362..fb7f6aa 100644
--- a/arch/arm64/kvm/guest.c
+++ b/arch/arm64/kvm/guest.c
@@ -58,6 +58,16 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
return 0;
}
+int kvm_arm_arch_vcpu_init(struct kvm_vcpu *vcpu)
+{
+ return 0;
+}
+
+void kvm_arm_arch_vcpu_uninit(struct kvm_vcpu *vcpu)
+{
+ kfree(vcpu->arch.sve_state);
+}
+
static u64 core_reg_offset_from_id(u64 id)
{
return id & ~(KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK | KVM_REG_ARM_CORE);
@@ -600,12 +610,9 @@ int kvm_vcpu_preferred_target(struct kvm_vcpu_init *init)
memset(init, 0, sizeof(*init));
- /*
- * For now, we don't return any features.
- * In future, we might use features to return target
- * specific features available for the preferred
- * target type.
- */
+ /* KVM_ARM_VCPU_SVE understood by KVM_VCPU_INIT */
+ init->features[0] = 1 << KVM_ARM_VCPU_SVE;
+
init->target = (__u32)target;
return 0;
diff --git a/arch/arm64/kvm/reset.c b/arch/arm64/kvm/reset.c
index a74311b..f63a791 100644
--- a/arch/arm64/kvm/reset.c
+++ b/arch/arm64/kvm/reset.c
@@ -110,6 +110,20 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu)
cpu_reset = &default_regs_reset;
}
+ if (system_supports_sve() &&
+ test_bit(KVM_ARM_VCPU_SVE, vcpu->arch.features)) {
+ vcpu->arch.flags |= KVM_ARM64_GUEST_HAS_SVE;
+
+ vcpu->arch.sve_max_vl = sve_max_virtualisable_vl;
+
+ vcpu->arch.sve_state = kzalloc(
+ SVE_SIG_REGS_SIZE(
+ sve_vq_from_vl(vcpu->arch.sve_max_vl)),
+ GFP_KERNEL);
+ if (!vcpu->arch.sve_state)
+ return -ENOMEM;
+ }
+
break;
}
--
2.1.4
^ permalink raw reply related
* [PATCH 1/2] KVM: Enforce error in ioctl for compat tasks when !KVM_COMPAT
From: Radim Krčmář @ 2018-06-21 15:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <f4f2dcd6-9ef7-f7fa-8ec4-f320d4a649ac@arm.com>
2018-06-21 12:40+0100, Marc Zyngier:
> Radim, Paolo: are you happy with me taking this through the kvmarm tree?
Yes, please do.
Acked-by: Radim Kr?m?? <rkrcmar@redhat.com>
^ permalink raw reply
* [GIT PULL] Allwinner clock changes for 4.18
From: Stephen Boyd @ 2018-06-21 15:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAGb2v67gL8m+XorkC78PwcaDA9cz69acsMkOrcLc9dgBY7m1eQ@mail.gmail.com>
Quoting Chen-Yu Tsai (2018-06-19 19:14:26)
> Hi Stephen,
>
> On Tue, Jun 12, 2018 at 4:38 PM, Chen-Yu Tsai <wens@csie.org> wrote:
> > On Tue, Jun 12, 2018 at 4:00 PM, Stephen Boyd <sboyd@kernel.org> wrote:
> >> Quoting Chen-Yu Tsai (2018-06-11 00:53:41)
> >>>
> >>
> >> I can directly pick and send as fix for clk-fixes targeting rc2.
>
> This is a friendly reminder to pick up this patch for -rc2.
>
> > The patch can still be found in the sunxi/for-next branch of the
> > sunxi repo on kernel.org:
> >
> > https://git.kernel.org/pub/scm/linux/kernel/git/sunxi/linux.git/commit/?h=sunxi/for-next&id=d4a1496286b0c29fcace4e10c0047cca7cc79b25
>
> This is still available, but no longer referenced in any branch,
> as we've updated our sunxi/for-next branch for 4.19 stuff.
>
> > Or the original on patchwork:
> >
> > https://patchwork.kernel.org/patch/10348027/
>
> And
>
> Acked-by: Chen-Yu Tsai <wens@csie.org>
>
> if you pick directly from patchwork.
>
Got it from linux-next. Thanks!
^ permalink raw reply
* [PATCH 1/2] hwrng: msm: add a spinlock and support for blocking reads
From: Timur Tabi @ 2018-06-21 15:17 UTC (permalink / raw)
To: linux-arm-kernel
The hwrng.read callback includes a boolean parameter called 'wait'
which indicates whether the function should block and wait for
more data.
When 'wait' is true, the driver spins on the DATA_AVAIL bit or until
a reasonable timeout. The timeout can occur if there is a heavy load
on reading the PRNG.
The same code also needs a spinlock to protect against race conditions.
If multiple cores hammer on the PRNG, it's possible for a race
condition to occur between reading the status register and
reading the data register. Add a spinlock to protect against
that.
1. Core 1 reads status register, shows data is available.
2. Core 2 also reads status register, same result
3. Core 2 reads data register, depleting all entropy
4. Core 1 reads data register, which returns 0
Signed-off-by: Timur Tabi <timur@codeaurora.org>
---
drivers/char/hw_random/msm-rng.c | 57 +++++++++++++++++++++++++++++++++++-----
1 file changed, 50 insertions(+), 7 deletions(-)
diff --git a/drivers/char/hw_random/msm-rng.c b/drivers/char/hw_random/msm-rng.c
index 841fee845ec9..44580588b938 100644
--- a/drivers/char/hw_random/msm-rng.c
+++ b/drivers/char/hw_random/msm-rng.c
@@ -15,9 +15,11 @@
#include <linux/err.h>
#include <linux/hw_random.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/spinlock.h>
/* Device specific register offsets */
#define PRNG_DATA_OUT 0x0000
@@ -35,10 +37,22 @@
#define MAX_HW_FIFO_SIZE (MAX_HW_FIFO_DEPTH * 4)
#define WORD_SZ 4
+/*
+ * Normally, this would be the maximum time it takes to refill the FIFO,
+ * after a read. Under heavy load, tests show that this delay is either
+ * below 50us or above 2200us. The higher value is probably what happens
+ * when entropy is completely depleted.
+ *
+ * Since we don't want to wait 2ms in a spinlock, set the timeout to the
+ * lower value. Under extreme situations, that timeout can extend to 100us.
+ */
+#define TIMEOUT 50
+
struct msm_rng {
void __iomem *base;
struct clk *clk;
struct hwrng hwrng;
+ spinlock_t lock;
};
#define to_msm_rng(p) container_of(p, struct msm_rng, hwrng)
@@ -96,11 +110,39 @@ static int msm_rng_read(struct hwrng *hwrng, void *data, size_t max, bool wait)
/* read random data from hardware */
do {
- val = readl_relaxed(rng->base + PRNG_STATUS);
- if (!(val & PRNG_STATUS_DATA_AVAIL))
- break;
+ spin_lock(&rng->lock);
+
+ /*
+ * First check the status bit. If 'wait' is true, then wait
+ * up to TIMEOUT microseconds for data to be available.
+ */
+ if (wait) {
+ int ret;
+
+ ret = readl_poll_timeout_atomic(rng->base + PRNG_STATUS,
+ val, val & PRNG_STATUS_DATA_AVAIL, 0, TIMEOUT);
+ if (ret) {
+ /* Timed out */
+ spin_unlock(&rng->lock);
+ break;
+ }
+ } else {
+ val = readl_relaxed(rng->base + PRNG_STATUS);
+ if (!(val & PRNG_STATUS_DATA_AVAIL)) {
+ spin_unlock(&rng->lock);
+ break;
+ }
+ }
val = readl_relaxed(rng->base + PRNG_DATA_OUT);
+ spin_unlock(&rng->lock);
+
+ /*
+ * Zero is technically a valid random number, but it's also
+ * the value returned if the PRNG is not enabled properly.
+ * To avoid accidentally returning all zeros, treat it as
+ * invalid and just return what we've already read.
+ */
if (!val)
break;
@@ -148,10 +190,11 @@ static int msm_rng_probe(struct platform_device *pdev)
if (IS_ERR(rng->clk))
return PTR_ERR(rng->clk);
- rng->hwrng.name = KBUILD_MODNAME,
- rng->hwrng.init = msm_rng_init,
- rng->hwrng.cleanup = msm_rng_cleanup,
- rng->hwrng.read = msm_rng_read,
+ rng->hwrng.name = KBUILD_MODNAME;
+ rng->hwrng.init = msm_rng_init;
+ rng->hwrng.cleanup = msm_rng_cleanup;
+ rng->hwrng.read = msm_rng_read;
+ spin_lock_init(&rng->lock);
ret = devm_hwrng_register(&pdev->dev, &rng->hwrng);
if (ret) {
--
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm
Technologies, Inc. Qualcomm Technologies, Inc. is a member of the
Code Aurora Forum, a Linux Foundation Collaborative Project.
^ permalink raw reply related
* [PATCH 2/2] hwrng: msm: add ACPI support
From: Timur Tabi @ 2018-06-21 15:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1529594276-12210-1-git-send-email-timur@codeaurora.org>
Add support for probing on ACPI systems, with ACPI HID QCOM8160.
On ACPI systems, clocks are always enabled, the PRNG should
already be enabled, and the register region is read-only.
The driver only verifies that the hardware is already
enabled never tries to disable or configure it.
Signed-off-by: Timur Tabi <timur@codeaurora.org>
---
drivers/char/hw_random/msm-rng.c | 40 ++++++++++++++++++++++++++++++++++------
1 file changed, 34 insertions(+), 6 deletions(-)
diff --git a/drivers/char/hw_random/msm-rng.c b/drivers/char/hw_random/msm-rng.c
index 44580588b938..f34713d23d77 100644
--- a/drivers/char/hw_random/msm-rng.c
+++ b/drivers/char/hw_random/msm-rng.c
@@ -20,6 +20,7 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
+#include <linux/acpi.h>
/* Device specific register offsets */
#define PRNG_DATA_OUT 0x0000
@@ -186,16 +187,32 @@ static int msm_rng_probe(struct platform_device *pdev)
if (IS_ERR(rng->base))
return PTR_ERR(rng->base);
- rng->clk = devm_clk_get(&pdev->dev, "core");
- if (IS_ERR(rng->clk))
- return PTR_ERR(rng->clk);
-
rng->hwrng.name = KBUILD_MODNAME;
- rng->hwrng.init = msm_rng_init;
- rng->hwrng.cleanup = msm_rng_cleanup;
rng->hwrng.read = msm_rng_read;
spin_lock_init(&rng->lock);
+ /*
+ * ACPI systems have v2 hardware. The clocks are always enabled,
+ * the PRNG register space is read-only, and the PRNG should
+ * already be enabled.
+ */
+ if (has_acpi_companion(&pdev->dev)) {
+ u32 val;
+
+ val = readl(rng->base + PRNG_CONFIG);
+ if (!(val & PRNG_CONFIG_HW_ENABLE)) {
+ dev_err(&pdev->dev, "device is not enabled\n");
+ return -ENODEV;
+ }
+ } else {
+ rng->clk = devm_clk_get(&pdev->dev, "core");
+ if (IS_ERR(rng->clk))
+ return PTR_ERR(rng->clk);
+
+ rng->hwrng.init = msm_rng_init;
+ rng->hwrng.cleanup = msm_rng_cleanup;
+ }
+
ret = devm_hwrng_register(&pdev->dev, &rng->hwrng);
if (ret) {
dev_err(&pdev->dev, "failed to register hwrng\n");
@@ -211,11 +228,22 @@ static int msm_rng_probe(struct platform_device *pdev)
};
MODULE_DEVICE_TABLE(of, msm_rng_of_match);
+#if IS_ENABLED(CONFIG_ACPI)
+static const struct acpi_device_id msm_rng_acpi_match[] = {
+ {
+ .id = "QCOM8160", /* v2 PRNG */
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, msm_rng_acpi_match);
+#endif
+
static struct platform_driver msm_rng_driver = {
.probe = msm_rng_probe,
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = of_match_ptr(msm_rng_of_match),
+ .acpi_match_table = ACPI_PTR(msm_rng_acpi_match),
}
};
module_platform_driver(msm_rng_driver);
--
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm
Technologies, Inc. Qualcomm Technologies, Inc. is a member of the
Code Aurora Forum, a Linux Foundation Collaborative Project.
^ permalink raw reply related
* [PATCH 7/9] media: cedrus: Move IRQ maintainance to cedrus_dec_ops
From: Paul Kocialkowski @ 2018-06-21 15:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180613140714.1686-8-maxime.ripard@bootlin.com>
Hi,
On Wed, 2018-06-13 at 16:07 +0200, Maxime Ripard wrote:
> The IRQ handler up until now was hardcoding the use of the MPEG engine to
> read the interrupt status, clear it and disable the interrupts.
>
> Obviously, that won't work really well with the introduction of new codecs
> that use a separate engine with a separate register set.
>
> In order to make this more future proof, introduce new decodec operations
> to deal with the interrupt management. The only one missing is the one to
> enable the interrupts in the first place, but that's taken care of by the
> trigger hook for now.
Acked-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> ---
> .../sunxi/cedrus/sunxi_cedrus_common.h | 9 +++++
> .../platform/sunxi/cedrus/sunxi_cedrus_hw.c | 21 ++++++------
> .../sunxi/cedrus/sunxi_cedrus_mpeg2.c | 33 +++++++++++++++++++
> 3 files changed, 53 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/media/platform/sunxi/cedrus/sunxi_cedrus_common.h b/drivers/media/platform/sunxi/cedrus/sunxi_cedrus_common.h
> index c2e2c92d103b..a2a507eb9fc9 100644
> --- a/drivers/media/platform/sunxi/cedrus/sunxi_cedrus_common.h
> +++ b/drivers/media/platform/sunxi/cedrus/sunxi_cedrus_common.h
> @@ -108,7 +108,16 @@ struct sunxi_cedrus_buffer *vb2_to_cedrus_buffer(const struct vb2_buffer *p)
> return vb2_v4l2_to_cedrus_buffer(to_vb2_v4l2_buffer(p));
> }
>
> +enum sunxi_cedrus_irq_status {
> + SUNXI_CEDRUS_IRQ_NONE,
> + SUNXI_CEDRUS_IRQ_ERROR,
> + SUNXI_CEDRUS_IRQ_OK,
> +};
> +
> struct sunxi_cedrus_dec_ops {
> + void (*irq_clear)(struct sunxi_cedrus_ctx *ctx);
> + void (*irq_disable)(struct sunxi_cedrus_ctx *ctx);
> + enum sunxi_cedrus_irq_status (*irq_status)(struct sunxi_cedrus_ctx *ctx);
> void (*setup)(struct sunxi_cedrus_ctx *ctx,
> struct sunxi_cedrus_run *run);
> void (*trigger)(struct sunxi_cedrus_ctx *ctx);
> diff --git a/drivers/media/platform/sunxi/cedrus/sunxi_cedrus_hw.c b/drivers/media/platform/sunxi/cedrus/sunxi_cedrus_hw.c
> index bb46a01214e0..6b97cbd2834e 100644
> --- a/drivers/media/platform/sunxi/cedrus/sunxi_cedrus_hw.c
> +++ b/drivers/media/platform/sunxi/cedrus/sunxi_cedrus_hw.c
> @@ -77,27 +77,28 @@ static irqreturn_t sunxi_cedrus_ve_irq(int irq, void *dev_id)
> struct sunxi_cedrus_ctx *ctx;
> struct sunxi_cedrus_buffer *src_buffer, *dst_buffer;
> struct vb2_v4l2_buffer *src_vb, *dst_vb;
> + enum sunxi_cedrus_irq_status status;
> unsigned long flags;
> - unsigned int value, status;
>
> spin_lock_irqsave(&dev->irq_lock, flags);
>
> - /* Disable MPEG interrupts and stop the MPEG engine */
> - value = sunxi_cedrus_read(dev, VE_MPEG_CTRL);
> - sunxi_cedrus_write(dev, value & (~0xf), VE_MPEG_CTRL);
> -
> - status = sunxi_cedrus_read(dev, VE_MPEG_STATUS);
> - sunxi_cedrus_write(dev, 0x0000c00f, VE_MPEG_STATUS);
> - sunxi_cedrus_engine_disable(dev);
> -
> ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev);
> if (!ctx) {
> pr_err("Instance released before the end of transaction\n");
> spin_unlock_irqrestore(&dev->irq_lock, flags);
>
> - return IRQ_HANDLED;
> + return IRQ_NONE;
> }
>
> + status = dev->dec_ops[ctx->current_codec]->irq_status(ctx);
> + if (status == SUNXI_CEDRUS_IRQ_NONE) {
> + spin_unlock_irqrestore(&dev->irq_lock, flags);
> + return IRQ_NONE;
> + }
> +
> + dev->dec_ops[ctx->current_codec]->irq_disable(ctx);
> + dev->dec_ops[ctx->current_codec]->irq_clear(ctx);
> +
> src_vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
> dst_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
>
> diff --git a/drivers/media/platform/sunxi/cedrus/sunxi_cedrus_mpeg2.c b/drivers/media/platform/sunxi/cedrus/sunxi_cedrus_mpeg2.c
> index e25075bb5779..51fa0c0f9bf2 100644
> --- a/drivers/media/platform/sunxi/cedrus/sunxi_cedrus_mpeg2.c
> +++ b/drivers/media/platform/sunxi/cedrus/sunxi_cedrus_mpeg2.c
> @@ -52,6 +52,36 @@ static const u8 mpeg_default_non_intra_quant[64] = {
>
> #define m_niq(i) ((i << 8) | mpeg_default_non_intra_quant[i])
>
> +static enum sunxi_cedrus_irq_status
> +sunxi_cedrus_mpeg2_irq_status(struct sunxi_cedrus_ctx *ctx)
> +{
> + struct sunxi_cedrus_dev *dev = ctx->dev;
> + u32 reg = sunxi_cedrus_read(dev, VE_MPEG_STATUS) & 0x7;
> +
> + if (!reg)
> + return SUNXI_CEDRUS_IRQ_NONE;
> +
> + if (reg & (BIT(1) | BIT(2)))
> + return SUNXI_CEDRUS_IRQ_ERROR;
> +
> + return SUNXI_CEDRUS_IRQ_OK;
> +}
> +
> +static void sunxi_cedrus_mpeg2_irq_clear(struct sunxi_cedrus_ctx *ctx)
> +{
> + struct sunxi_cedrus_dev *dev = ctx->dev;
> +
> + sunxi_cedrus_write(dev, GENMASK(2, 0), VE_MPEG_STATUS);
> +}
> +
> +static void sunxi_cedrus_mpeg2_irq_disable(struct sunxi_cedrus_ctx *ctx)
> +{
> + struct sunxi_cedrus_dev *dev = ctx->dev;
> + u32 reg = sunxi_cedrus_read(dev, VE_MPEG_CTRL) & ~BIT(3);
> +
> + sunxi_cedrus_write(dev, reg, VE_MPEG_CTRL);
> +}
> +
> static void sunxi_cedrus_mpeg2_setup(struct sunxi_cedrus_ctx *ctx,
> struct sunxi_cedrus_run *run)
> {
> @@ -156,6 +186,9 @@ static void sunxi_cedrus_mpeg2_trigger(struct sunxi_cedrus_ctx *ctx)
> }
>
> struct sunxi_cedrus_dec_ops sunxi_cedrus_dec_ops_mpeg2 = {
> + .irq_clear = sunxi_cedrus_mpeg2_irq_clear,
> + .irq_disable = sunxi_cedrus_mpeg2_irq_disable,
> + .irq_status = sunxi_cedrus_mpeg2_irq_status,
> .setup = sunxi_cedrus_mpeg2_setup,
> .trigger = sunxi_cedrus_mpeg2_trigger,
> };
--
Paul Kocialkowski, Bootlin (formerly Free Electrons)
Embedded Linux and kernel engineering
https://bootlin.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 488 bytes
Desc: This is a digitally signed message part
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20180621/6142c67d/attachment.sig>
^ permalink raw reply
* [linux-sunxi] Re: [PATCH v2 11/27] drm/sun4i: tcon: Add support for tcon-top gate
From: Jernej Škrabec @ 2018-06-21 15:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAGb2v64iB8FP1Nf+vZM2wd6wzNLxWYrCP78D-rOUg881RPNpEA@mail.gmail.com>
Dne ?etrtek, 21. junij 2018 ob 03:23:27 CEST je Chen-Yu Tsai napisal(a):
> On Thu, Jun 21, 2018 at 3:37 AM, Jernej ?krabec <jernej.skrabec@siol.net>
wrote:
> > Dne sobota, 16. junij 2018 ob 07:48:38 CEST je Chen-Yu Tsai napisal(a):
> >> On Sat, Jun 16, 2018 at 1:33 AM, Jernej ?krabec <jernej.skrabec@siol.net>
> >
> > wrote:
> >> > Dne petek, 15. junij 2018 ob 19:13:17 CEST je Chen-Yu Tsai napisal(a):
> >> >> On Sat, Jun 16, 2018 at 12:41 AM, Jernej ?krabec
> >> >>
> >> >> <jernej.skrabec@siol.net> wrote:
> >> >> > Hi,
> >> >> >
> >> >> > Dne petek, 15. junij 2018 ob 10:31:10 CEST je Maxime Ripard
napisal(a):
> >> >> >> Hi,
> >> >> >>
> >> >> >> On Tue, Jun 12, 2018 at 10:00:20PM +0200, Jernej Skrabec wrote:
> >> >> >> > TV TCONs connected to TCON TOP have to enable additional gate in
> >> >> >> > order
> >> >> >> > to work.
> >> >> >> >
> >> >> >> > Add support for such TCONs.
> >> >> >> >
> >> >> >> > Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
> >> >> >> > ---
> >> >> >> >
> >> >> >> > drivers/gpu/drm/sun4i/sun4i_tcon.c | 11 +++++++++++
> >> >> >> > drivers/gpu/drm/sun4i/sun4i_tcon.h | 4 ++++
> >> >> >> > 2 files changed, 15 insertions(+)
> >> >> >> >
> >> >> >> > diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c
> >> >> >> > b/drivers/gpu/drm/sun4i/sun4i_tcon.c index
> >> >> >> > 08747fc3ee71..0afb5a94a414
> >> >> >> > 100644
> >> >> >> > --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
> >> >> >> > +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
> >> >> >> > @@ -688,6 +688,16 @@ static int sun4i_tcon_init_clocks(struct
> >> >> >> > device
> >> >> >> > *dev,
> >> >> >> >
> >> >> >> > dev_err(dev, "Couldn't get the TCON bus clock\n");
> >> >> >> > return PTR_ERR(tcon->clk);
> >> >> >> >
> >> >> >> > }
> >> >> >> >
> >> >> >> > +
> >> >> >> > + if (tcon->quirks->has_tcon_top_gate) {
> >> >> >> > + tcon->top_clk = devm_clk_get(dev, "tcon-top");
> >> >> >> > + if (IS_ERR(tcon->top_clk)) {
> >> >> >> > + dev_err(dev, "Couldn't get the TCON TOP bus
> >> >> >> > clock\n");
> >> >> >> > + return PTR_ERR(tcon->top_clk);
> >> >> >> > + }
> >> >> >> > + clk_prepare_enable(tcon->top_clk);
> >> >> >> > + }
> >> >> >> > +
> >> >> >>
> >> >> >> Is it required for the TCON itself to operate, or does the TCON
> >> >> >> requires the TCON TOP, which in turn requires that clock to be
> >> >> >> functional?
> >> >> >>
> >> >> >> I find it quite odd to have a clock that isn't meant for a
> >> >> >> particular
> >> >> >> device to actually be wired to another device. I'm not saying this
> >> >> >> isn't the case, but it would be a first.
> >> >> >
> >> >> > Documentation doesn't say much about that gate. I did few tests and
> >> >> > TCON
> >> >> > registers can be read and written even if TCON TOP TV TCON gate is
> >> >> > disabled. However, there is no image, as expected.
> >> >>
> >> >> The R40 manual does include it in the diagram, on page 504. There's
> >> >> also
> >> >> a
> >> >> mux to select whether the clock comes directly from the CCU or the TV
> >> >> encoder (a feedback mode?). I assume this is the gate you are
> >> >> referring
> >> >> to
> >> >> here, in which case it is not a bus clock, but rather the TCON module
> >> >> or
> >> >> channel clock, strangely routed.
> >> >>
> >> >> > More interestingly, I enabled test pattern directly in TCON to
> >> >> > eliminate
> >> >> > influence of the mixer. As soon as I disabled that gate, test
> >> >> > pattern
> >> >> > on
> >> >> > HDMI screen was gone, which suggest that this gate influences
> >> >> > something
> >> >> > inside TCON.
> >> >> >
> >> >> > Another test I did was that I moved enable/disable gate code to
> >> >> > sun4i_tcon_channel_set_status() and it worked just as well.
> >> >> >
> >> >> > I'll ask AW engineer what that gate actually does, but from what I
> >> >> > saw,
> >> >> > I
> >> >> > would say that most appropriate location to enable/disable TCON TOP
> >> >> > TV
> >> >> > TCON
> >> >> > gate is TCON driver. Alternatively, TCON TOP driver could check if
> >> >> > any
> >> >> > TV
> >> >> > TCON is in use and enable appropriate gate. However, that doesn't
> >> >> > sound
> >> >> > right to me for some reason.
> >> >>
> >> >> If what I said above it true, then yes, the appropriate location to
> >> >> enable
> >> >> it is the TCON driver, but moreover, the representation of the clock
> >> >> tree
> >> >> should be fixed such that the TCON takes the clock from the TCON TOP
> >> >> as
> >> >> its
> >> >> channel/ module clock instead. That way you don't need this patch, but
> >> >> you'd add another for all the clock routing.
> >> >
> >> > Can you be more specific? I not sure what you mean here.
> >>
> >> For clock related properties in the device tree:
> >>
> >> &tcon_top {
> >>
> >> clocks = <&ccu CLK_BUS_TCON_TOP>,
> >>
> >> <&ccu CLK_TCON_TV0>,
> >> <&tve0>,
> >> <&ccu CLK_TCON_TV1>,
> >> <&tve1>;
> >>
> >> clock-names = "bus", "tcon-tv0", "tve0", "tcon-tv1", "tve1";
> >> clock-output-names = "tcon-top-tv0", "tcon-top-tv1";
> >>
> >> };
> >>
> >> &tcon_tv0 {
> >>
> >> clocks = <&ccu CLK_BUS_TCON_TV0>, <&tcon_top 0>'
> >> clock-names = "ahb", "tcon-ch1";
> >>
> >> };
> >>
> >> A diagram would look like:
> >> | This part is TCON TOP |
> >>
> >> v v
> >>
> >> CCU CLK_TCON_TV0 --|----\ |
> >>
> >> | mux ---- gate ----|-- TCON_TV0
> >>
> >> TVE0 --------------|----/ |
> >>
> >> And the same goes for TCON_TV1 and TVE1.
> >>
> >> The user manual is a bit lacking on how TVE outputs a clock though.
> >
> > I didn't yet received any response on HW details from AW till now, but I
> > would like to post new version of patches soon.
> >
> > While chaining like you described could be implemented easily, I don't
> > think it really represents HW as it is. Tests showed that these two
> > clocks are independent, otherwise register writes/reads wouldn't be
> > possible with tcon- top gate disabled. I chose tcon-top bus clock as a
> > parent becase if it is not enabled, it simply won't work.
>
> AFAIK with the TCONs, even when the TCON channel clock (not the bus clock)
> is disabled, register accesses still work.
You're right, I just tested that.
> I'm saying that the TCON TOP
> gate is downstream from the TCON channel clock in the CCU. These are not
> related to the TCON bus clock in the CCU, which affects register access.
>
> Did Allwinner provide any information regarding the hierarchy of the clocks?
No reponse for now.
> > However, if everyone feels chaining is the best way to implement it, I'll
> > do it.
>
> I would like to get it right and match actual hardware. My proposal is
> based on my understanding from the diagrams in the user manual.
So for now, your explanation is the most reasonable. Should we go ahead and
implement your idea?
Please note that H6 has TCON-TOP too, but it has only one LCD TCON and one TV
TCON instead of two of each kind. That means we will have hole in indices
(tcon_lcd0 is 1, tcon_tv0 is 3, which is aligned with R40) and different TCON-
TOP binding (no tcon_tv1 channel clock), but setup is exactly the same.
Best regard,
Jernej
^ permalink raw reply
* [PATCH 8/9] media: cedrus: Add start and stop decoder operations
From: Paul Kocialkowski @ 2018-06-21 15:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180613140714.1686-9-maxime.ripard@bootlin.com>
Hi,
On Wed, 2018-06-13 at 16:07 +0200, Maxime Ripard wrote:
> Some codec needs to perform some additional task when a decoding is started
> and stopped, and not only at every frame.
>
> For example, the H264 decoding support needs to allocate buffers that will
> be used in the decoding process, but do not need to change over time, or at
> each frame.
>
> In order to allow that for codecs, introduce a start and stop hook that
> will be called if present at start_streaming and stop_streaming time.
>
> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> ---
> .../platform/sunxi/cedrus/sunxi_cedrus_common.h | 2 ++
> .../platform/sunxi/cedrus/sunxi_cedrus_video.c | 14 +++++++++++++-
> 2 files changed, 15 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/media/platform/sunxi/cedrus/sunxi_cedrus_common.h b/drivers/media/platform/sunxi/cedrus/sunxi_cedrus_common.h
> index a2a507eb9fc9..20c78ec1f037 100644
> --- a/drivers/media/platform/sunxi/cedrus/sunxi_cedrus_common.h
> +++ b/drivers/media/platform/sunxi/cedrus/sunxi_cedrus_common.h
> @@ -120,6 +120,8 @@ struct sunxi_cedrus_dec_ops {
> enum sunxi_cedrus_irq_status (*irq_status)(struct sunxi_cedrus_ctx *ctx);
> void (*setup)(struct sunxi_cedrus_ctx *ctx,
> struct sunxi_cedrus_run *run);
> + int (*start)(struct sunxi_cedrus_ctx *ctx);
> + void (*stop)(struct sunxi_cedrus_ctx *ctx);
> void (*trigger)(struct sunxi_cedrus_ctx *ctx);
> };
>
> diff --git a/drivers/media/platform/sunxi/cedrus/sunxi_cedrus_video.c b/drivers/media/platform/sunxi/cedrus/sunxi_cedrus_video.c
> index fb7b081a5bb7..d93461178857 100644
> --- a/drivers/media/platform/sunxi/cedrus/sunxi_cedrus_video.c
> +++ b/drivers/media/platform/sunxi/cedrus/sunxi_cedrus_video.c
> @@ -416,6 +416,8 @@ static int sunxi_cedrus_buf_prepare(struct vb2_buffer *vb)
> static int sunxi_cedrus_start_streaming(struct vb2_queue *q, unsigned int count)
> {
> struct sunxi_cedrus_ctx *ctx = vb2_get_drv_priv(q);
> + struct sunxi_cedrus_dev *dev = ctx->dev;
> + int ret = 0;
>
> switch (ctx->vpu_src_fmt->fourcc) {
> case V4L2_PIX_FMT_MPEG2_FRAME:
> @@ -425,16 +427,26 @@ static int sunxi_cedrus_start_streaming(struct vb2_queue *q, unsigned int count)
> return -EINVAL;
> }
>
> - return 0;
> + if (V4L2_TYPE_IS_OUTPUT(q->type) &&
I suppose this check was put in place to ensure that ->start is only
called once, but what if start_streaming is called multiple times on
output? Am I totally unsure about whether the API guarantees that we
only get one start_streaming call per buffer queue, regardless of how
many userspace issues.
If we don't have such a guarantee, we probably need an internal
mechanism to avoid having ->start called more than once.
> + dev->dec_ops[ctx->current_codec]->start)
> + ret = dev->dec_ops[ctx->current_codec]->start(ctx);
> +
> + return ret;
> }
>
> static void sunxi_cedrus_stop_streaming(struct vb2_queue *q)
> {
> struct sunxi_cedrus_ctx *ctx = vb2_get_drv_priv(q);
> + struct sunxi_cedrus_dev *dev = ctx->dev;
> struct vb2_v4l2_buffer *vbuf;
> unsigned long flags;
>
> flush_scheduled_work();
> +
> + if (V4L2_TYPE_IS_OUTPUT(q->type) &&
Ditto.
Cheers,
Paul
> + dev->dec_ops[ctx->current_codec]->stop)
> + dev->dec_ops[ctx->current_codec]->stop(ctx);
> +
> for (;;) {
> spin_lock_irqsave(&ctx->dev->irq_lock, flags);
>
--
Paul Kocialkowski, Bootlin (formerly Free Electrons)
Embedded Linux and kernel engineering
https://bootlin.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 488 bytes
Desc: This is a digitally signed message part
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20180621/ce39ae6b/attachment.sig>
^ permalink raw reply
* [PATCH] [RFC] arm: Replace "multiple platforms" by "common platform"
From: Geert Uytterhoeven @ 2018-06-21 15:59 UTC (permalink / raw)
To: linux-arm-kernel
"ARM multiplatform" has actually two meanings:
? 1. It groups platforms that follow the "ARM multiplatform" software
framework,
? 2. It allows to build a single kernel that can be booted on multiple
? ? ?platforms.
Currently support for XIP and/or NOMMU cannot be enabled on platforms
that follow the "ARM multiplatform" framework, without duplicating their
machine selection logic under a new Kconfig symbol. As (in theory) all
platforms can be used with XIP and/or NOMMU, this is not sustainable.
Hence clarify the meaning of ARCH_MULTIPLATFORM:
1. Replace "multiple platforms" by "common platform", as it allows to
select one or more platforms adhering to the common framework,
2. Note that a single kernel may not boot on all platforms if XIP or
NOMMU is enabled.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
References:
- "[PATCHv4 0/4] arm/versatile: no-MMU support"
(http://lists.infradead.org/pipermail/linux-arm-kernel/2018-June/584555.html)
- "[PATCH 0/2] ARM: ARMv7 (with MMU) XIP without ARCH_MULTIPLATFORM"
(http://lists.infradead.org/pipermail/linux-arm-kernel/2017-February/486835.html)
arch/arm/Kconfig | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 54eeb8d00bc62a9f..6b286c018cf748c7 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -329,7 +329,7 @@ choice
default ARCH_MULTIPLATFORM if MMU
config ARCH_MULTIPLATFORM
- bool "Allow multiple platforms to be selected"
+ bool "Common platforms (\"multiplatform\")"
depends on MMU
select ARM_HAS_SG_CHAIN
select ARM_PATCH_PHYS_VIRT
@@ -342,6 +342,13 @@ config ARCH_MULTIPLATFORM
select PCI_DOMAINS if PCI
select SPARSE_IRQ
select USE_OF
+ help
+ Support for systems implemented using the common "multiplatform"
+ framework.
+
+ Unless specialized options depending on intimate platform
+ details, like XIP or NOMMU, are enabled, this allows to build a
+ single kernel that boots on multiple platforms.
config ARM_SINGLE_ARMV7M
bool "ARMv7-M based platforms (Cortex-M0/M3/M4)"
--
2.17.1
^ permalink raw reply related
* [PATCHv4 4/4] ARM: versatile: support configuring versatile machine for no-MMU
From: Geert Uytterhoeven @ 2018-06-21 15:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180618141905.7789-5-gerg@linux-m68k.org>
Hi Greg,
CC Chris, Arnd, Nicolas
On Mon, Jun 18, 2018 at 4:21 PM Greg Ungerer <gerg@linux-m68k.org> wrote:
> Allow the arm versatile machine to be configured for no-MMU operation.
>
> Older kernels had the ability to build the versatile machine with the MMU
> disabled (!CONFIG_MMU). Recent changes to convert the versatile machine
> to device tree lost this ability. (Although older kernels could be built
> they did not run due to a bug in the IO_ADDRESS() mapping on this machine).
>
> The motivation for this is that the versatile machine is well supported
> in qemu. And this provides an excellent platform for development and
> testing no-MMU support on ARM in general.
>
> This patch adds a versatile platform selection in the upper level arm
> system type menu - where it appeared in older kernel versions - when
> configuring for the no-MMU case. There is no visible change to the way
> versatile is selected for the MMU enabled case.
>
> Signed-off-by: Greg Ungerer <gerg@linux-m68k.org>
Thanks for your patch! I'm happy to see nommu spreading ;-)
> ---
> arch/arm/Kconfig | 11 +++++++++++
> arch/arm/Kconfig.debug | 3 ++-
> arch/arm/mach-versatile/Kconfig | 5 +++--
> 3 files changed, 16 insertions(+), 3 deletions(-)
>
> v2: don't use a multiplatform setup, move versatile choice to top level menu
> v3: rebase on top of linux-4.10
> v3: rebase on top of linux-4.17
>
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index 54eeb8d00bc6..0608a6a32fac 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -356,6 +356,17 @@ config ARM_SINGLE_ARMV7M
> select SPARSE_IRQ
> select USE_OF
>
> +config ARM_SINGLE_ARCH_VERSATILE
> + bool "ARM Ltd. Versatile family"
> + depends on !MMU
> + select AUTO_ZRELADDR
> + select CLKSRC_OF
> + select COMMON_CLK
> + select GENERIC_CLOCKEVENTS
> + select GPIOLIB
> + select SPARSE_IRQ
> + select USE_OF
> +
I'm afraid adding (duplicating) machine entries for nommu support is not a
sustainable solutions. Any machine can run a nommu kernel, in theory.
I'm aware of the objection "but you cannot build a nommu kernel that can
boot on multiple systems". That's true (unless RAM/FLASH addresses are the
same). So don't do that.
All of the above is true for XIP, too, hence applies with s/nommu/XIP/.
One more reason not to have this duplication.
The current "ARM multiplatform" has actually two meanings:
1. It groups platforms that follow the "ARM multiplatform" framework,
2. It allows to build a single kernel that can be booted on multiple
platforms.
To avoid the duplication, I think 2 should be relaxed when specialized
options like XIP or NOMMU are selected.
I've just sent a patch to do that.
> config ARCH_EBSA110
> bool "EBSA-110"
> select ARCH_USES_GETTIMEOFFSET
> diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
> index 693f84392f1b..242a0f744c8a 100644
> --- a/arch/arm/Kconfig.debug
> +++ b/arch/arm/Kconfig.debug
> @@ -1836,7 +1836,8 @@ config DEBUG_UNCOMPRESS
> config UNCOMPRESS_INCLUDE
> string
> default "debug/uncompress.h" if ARCH_MULTIPLATFORM || ARCH_MSM || \
> - PLAT_SAMSUNG || ARM_SINGLE_ARMV7M
> + PLAT_SAMSUNG || ARM_SINGLE_ARMV7M || \
> + ARM_SINGLE_ARCH_VERSATILE
> default "mach/uncompress.h"
>
> config EARLY_PRINTK
> diff --git a/arch/arm/mach-versatile/Kconfig b/arch/arm/mach-versatile/Kconfig
> index f5c275434d6c..06ad999d5978 100644
> --- a/arch/arm/mach-versatile/Kconfig
> +++ b/arch/arm/mach-versatile/Kconfig
> @@ -1,7 +1,8 @@
> # SPDX-License-Identifier: GPL-2.0
> config ARCH_VERSATILE
> - bool "ARM Ltd. Versatile family"
> - depends on ARCH_MULTI_V5
> + bool "ARM Ltd. Versatile family" if ARCH_MULTI_V5
> + depends on ARCH_MULTI_V5 || ARM_SINGLE_ARCH_VERSATILE
> + default y if ARM_SINGLE_ARCH_VERSATILE
> select ARM_AMBA
> select ARM_TIMER_SP804
> select ARM_VIC
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert at linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* [PATCHv4 4/4] ARM: versatile: support configuring versatile machine for no-MMU
From: Nicolas Pitre @ 2018-06-21 16:24 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAMuHMdUyCEq0U_CJindwcSEF+0dR-SJc1v8zGT-CcUqVkdOeVw@mail.gmail.com>
[ adding linux-kbuild for their input ]
On Thu, 21 Jun 2018, Geert Uytterhoeven wrote:
> I'm afraid adding (duplicating) machine entries for nommu support is not a
> sustainable solutions. Any machine can run a nommu kernel, in theory.
>
> I'm aware of the objection "but you cannot build a nommu kernel that can
> boot on multiple systems". That's true (unless RAM/FLASH addresses are the
> same). So don't do that.
>
> All of the above is true for XIP, too, hence applies with s/nommu/XIP/.
> One more reason not to have this duplication.
>
> The current "ARM multiplatform" has actually two meanings:
> 1. It groups platforms that follow the "ARM multiplatform" framework,
> 2. It allows to build a single kernel that can be booted on multiple
> platforms.
> To avoid the duplication, I think 2 should be relaxed when specialized
> options like XIP or NOMMU are selected.
>
> I've just sent a patch to do that.
Your patch isn't wrong per se. But it is not enough. The issue here
would be easily fixed with some kconfig extension so that:
- If XIP or NOMMU is selected then only one target in the multiplatform
set can be selected, basically turning it into a choice menu.
- If more than one multiplatform targets is selected then XIP and NOMMU
can no longere be selected.
I don't know how this could be expressed but the implementation might
not be that hard.
Nicolas
^ permalink raw reply
* [PATCHv4 4/4] ARM: versatile: support configuring versatile machine for no-MMU
From: Chris Brandt @ 2018-06-21 16:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <nycvar.YSQ.7.76.1806211212010.16670@knanqh.ubzr>
On Thursday, June 21, 2018 1, Nicolas Pitre wrote:
> > I've just sent a patch to do that.
>
> Your patch isn't wrong per se. But it is not enough. The issue here
> would be easily fixed with some kconfig extension so that:
>
> - If XIP or NOMMU is selected then only one target in the multiplatform
> set can be selected, basically turning it into a choice menu.
>
> - If more than one multiplatform targets is selected then XIP and NOMMU
> can no longere be selected.
>
Over the years I tried to tackle this issue in multiple different ways,
while still staying in the limitations of kbuild.
Basically, everything was shot down for one reason or another (or the
only way to do it is really ugly...so I didn't even ask).
kbuild doesn't really have a mutually exclusive property that can be
dynamically set/cleared.
In the end, I saw only 2 paths for adding XIP to ARM MMU builds:
1) Modify kbuild to give the ability as stated above.
2) Make XIP builds somehow be able to run at multiple physical address
by modifying the binaries post-build (Arnd's suggestion).
What I have been doing is simply applying this patch to arch/arm/Kconifg
before every XIP KERNEL build:
config XIP_KERNEL
bool "Kernel Execute-In-Place from ROM"
- depends on !ARM_LPAE && !ARCH_MULTIPLATFORM
+ depends on !ARM_LPAE
help
Execute-In-Place allows the kernel to run from non-volatile storage
directly addressable by the CPU, such as NOR flash. This saves RAM
Chris
^ permalink raw reply
* [PATCH] ARM: ftrace: Only set kernel memory back to read-only after boot
From: Steven Rostedt @ 2018-06-21 16:47 UTC (permalink / raw)
To: linux-arm-kernel
From: Steven Rostedt (VMware) <rostedt@goodmis.org>
Dynamic ftrace requires modifying the code segments that are usually
set to read-only. To do this, a per arch function is called both before
and after the ftrace modifications are performed. The "before" function
will set kernel code text to read-write to allow for ftrace to make the
modifications, and the "after" function will set the kernel code text
back to "read-only" to keep the kernel code text protected.
The issue happens when dynamic ftrace is tested at boot up. The test is
done before the kernel code text has been set to read-only. But the
"before" and "after" calls are still performed. The "after" call will
change the kernel code text to read-only prematurely, and other boot
code that expects this code to be read-write will fail.
The solution is to add a variable that is set when the kernel code text
is expected to be converted to read-only, and make the ftrace "before"
and "after" calls do nothing if that variable is not yet set. This is
similar to the x86 solution from commit 162396309745 ("ftrace, x86:
make kernel text writable only for conversions").
Reported-by: Stefan Agner <stefan@agner.ch>
Tested-by: Stefan Agner <stefan@agner.ch>
Link: http://lkml.kernel.org/r/20180620212906.24b7b66e at vmware.local.home
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c
index c186474422f3..0cc8e04295a4 100644
--- a/arch/arm/mm/init.c
+++ b/arch/arm/mm/init.c
@@ -736,20 +736,29 @@ static int __mark_rodata_ro(void *unused)
return 0;
}
+static int kernel_set_to_readonly __read_mostly;
+
void mark_rodata_ro(void)
{
+ kernel_set_to_readonly = 1;
stop_machine(__mark_rodata_ro, NULL, NULL);
debug_checkwx();
}
void set_kernel_text_rw(void)
{
+ if (!kernel_set_to_readonly)
+ return;
+
set_section_perms(ro_perms, ARRAY_SIZE(ro_perms), false,
current->active_mm);
}
void set_kernel_text_ro(void)
{
+ if (!kernel_set_to_readonly)
+ return;
+
set_section_perms(ro_perms, ARRAY_SIZE(ro_perms), true,
current->active_mm);
}
^ permalink raw reply related
* [PATCH v3 0/2] Add support for a YUV 10bits pixel format
From: Randy Li @ 2018-06-21 16:47 UTC (permalink / raw)
To: linux-arm-kernel
In the last time, I got some feedback and not a clear guide on what
I should do. So just give more comment on describing this 10bits format.
Whether I should add bpp instead cpp in drm_format_info and update a
numbers of functions is up to you guys.
And I don't any other driver would request 10bits yuv format support,
so I can't add the pixel format they don't use as I did a year ago.
You would ignore those patches.
v3:
I put a code comment in a wrong commit in the previous commit,
move it back.
v2:
add more comment to describe this pixel format
Randy Li (2):
drm/fourcc: add a 10bits fully packed variant of NV12
drm/rockchip: Support 10 bits yuv format in vop
drivers/gpu/drm/drm_fourcc.c | 1 +
drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 27 +++++++++++++++++++++++++--
drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 1 +
drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 2 ++
include/uapi/drm/drm_fourcc.h | 8 ++++++++
5 files changed, 37 insertions(+), 2 deletions(-)
--
2.14.4
^ permalink raw reply
* [PATCH v3 1/2] drm/fourcc: add a 10bits fully packed variant of NV12
From: Randy Li @ 2018-06-21 16:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180621164737.23611-1-ayaka@soulik.info>
This pixel format is a fully packed and 10bits variant of NV12.
A luma pixel would take 10bits in memory, without any
filled bits between pixels in a stride.
Signed-off-by: Randy Li <ayaka@soulik.info>
---
drivers/gpu/drm/drm_fourcc.c | 1 +
include/uapi/drm/drm_fourcc.h | 8 ++++++++
2 files changed, 9 insertions(+)
diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c
index 5ca6395cd4d3..1f43967c4013 100644
--- a/drivers/gpu/drm/drm_fourcc.c
+++ b/drivers/gpu/drm/drm_fourcc.c
@@ -173,6 +173,7 @@ const struct drm_format_info *__drm_format_info(u32 format)
{ .format = DRM_FORMAT_UYVY, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 2, .vsub = 1 },
{ .format = DRM_FORMAT_VYUY, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 2, .vsub = 1 },
{ .format = DRM_FORMAT_AYUV, .depth = 0, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
+ { .format = DRM_FORMAT_NV12_10LE40, .depth = 0, .num_planes = 2, .cpp = { 1, 2, 0 }, .hsub = 2, .vsub = 2 },
};
unsigned int i;
diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h
index e04613d30a13..14bee94a12d9 100644
--- a/include/uapi/drm/drm_fourcc.h
+++ b/include/uapi/drm/drm_fourcc.h
@@ -140,6 +140,14 @@ extern "C" {
#define DRM_FORMAT_NV61 fourcc_code('N', 'V', '6', '1') /* 2x1 subsampled Cb:Cr plane */
#define DRM_FORMAT_NV24 fourcc_code('N', 'V', '2', '4') /* non-subsampled Cr:Cb plane */
#define DRM_FORMAT_NV42 fourcc_code('N', 'V', '4', '2') /* non-subsampled Cb:Cr plane */
+/*
+ * A fully packed 2 plane YCbCr
+ * Y1 0-9, Y2 10-19, Y3 20-29, Y4 20-39
+ * ....
+ * U1V1: 0-19, U2V2: 20-39
+ */
+#define DRM_FORMAT_NV12_10LE40 fourcc_code('R', 'K', '2', '0') /* 2x2 subsampled Cr:Cb plane */
+
/*
* 3 plane YCbCr
--
2.14.4
^ permalink raw reply related
* [PATCH v3 2/2] drm/rockchip: Support 10 bits yuv format in vop
From: Randy Li @ 2018-06-21 16:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180621164737.23611-1-ayaka@soulik.info>
The rockchip use fully packed pixel format variants
for YUV 10bits.
This patch only make the VOP accept this pixel format,
but it doesn't add the converting data path for
the color gamuts that the target display are supported.
Signed-off-by: Randy Li <ayaka@soulik.info>
---
drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 27 +++++++++++++++++++++++++--
drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 1 +
drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 2 ++
3 files changed, 28 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 2121345a61af..6a54b20501ac 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -232,6 +232,7 @@ static enum vop_data_format vop_convert_format(uint32_t format)
case DRM_FORMAT_BGR565:
return VOP_FMT_RGB565;
case DRM_FORMAT_NV12:
+ case DRM_FORMAT_NV12_10LE40:
return VOP_FMT_YUV420SP;
case DRM_FORMAT_NV16:
return VOP_FMT_YUV422SP;
@@ -249,6 +250,17 @@ static bool is_yuv_support(uint32_t format)
case DRM_FORMAT_NV12:
case DRM_FORMAT_NV16:
case DRM_FORMAT_NV24:
+ case DRM_FORMAT_NV12_10LE40:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool is_yuv_10bit(uint32_t format)
+{
+ switch (format) {
+ case DRM_FORMAT_NV12_10LE40:
return true;
default:
return false;
@@ -711,6 +723,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
dma_addr_t dma_addr;
uint32_t val;
bool rb_swap;
+ bool is_10_bits = false;
int win_index = VOP_WIN_TO_INDEX(vop_win);
int format;
@@ -728,6 +741,8 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
return;
}
+ is_10_bits = is_yuv_10bit(fb->format->format);
+
obj = rockchip_fb_get_gem_obj(fb, 0);
rk_obj = to_rockchip_obj(obj);
@@ -742,7 +757,11 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
dsp_sty = dest->y1 + crtc->mode.vtotal - crtc->mode.vsync_start;
dsp_st = dsp_sty << 16 | (dsp_stx & 0xffff);
- offset = (src->x1 >> 16) * fb->format->cpp[0];
+ if (is_10_bits)
+ offset = (src->x1 >> 16) * (fb->format->cpp[0] * 5 / 4);
+ else
+ offset = (src->x1 >> 16) * fb->format->cpp[0];
+
offset += (src->y1 >> 16) * fb->pitches[0];
dma_addr = rk_obj->dma_addr + offset + fb->offsets[0];
@@ -753,6 +772,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
VOP_WIN_SET(vop, win, format, format);
VOP_WIN_SET(vop, win, yrgb_vir, DIV_ROUND_UP(fb->pitches[0], 4));
VOP_WIN_SET(vop, win, yrgb_mst, dma_addr);
+ VOP_WIN_SET(vop, win, fmt_10, is_10_bits);
if (is_yuv_support(fb->format->format)) {
int hsub = drm_format_horz_chroma_subsampling(fb->format->format);
int vsub = drm_format_vert_chroma_subsampling(fb->format->format);
@@ -761,7 +781,10 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
uv_obj = rockchip_fb_get_gem_obj(fb, 1);
rk_uv_obj = to_rockchip_obj(uv_obj);
- offset = (src->x1 >> 16) * bpp / hsub;
+ if (is_10_bits)
+ offset = (src->x1 >> 16) * (bpp * 5 / 4) / hsub;
+ else
+ offset = (src->x1 >> 16) * bpp / hsub;
offset += (src->y1 >> 16) * fb->pitches[1] / vsub;
dma_addr = rk_uv_obj->dma_addr + offset + fb->offsets[1];
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index 084acdd0019a..d9ec993f420a 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -131,6 +131,7 @@ struct vop_win_phy {
struct vop_reg enable;
struct vop_reg gate;
struct vop_reg format;
+ struct vop_reg fmt_10;
struct vop_reg rb_swap;
struct vop_reg act_info;
struct vop_reg dsp_info;
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 08023d3ecb76..5393886ddd95 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -50,6 +50,7 @@ static const uint32_t formats_win_full[] = {
DRM_FORMAT_NV12,
DRM_FORMAT_NV16,
DRM_FORMAT_NV24,
+ DRM_FORMAT_NV12_10LE40,
};
static const uint32_t formats_win_lite[] = {
@@ -215,6 +216,7 @@ static const struct vop_win_phy rk3288_win01_data = {
.nformats = ARRAY_SIZE(formats_win_full),
.enable = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 0),
.format = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 1),
+ .fmt_10 = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 4),
.rb_swap = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 12),
.act_info = VOP_REG(RK3288_WIN0_ACT_INFO, 0x1fff1fff, 0),
.dsp_info = VOP_REG(RK3288_WIN0_DSP_INFO, 0x0fff0fff, 0),
--
2.14.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox