* [PATCH v2 15/19] arm64; insn: Add encoder for the EXTR instruction
From: Marc Zyngier @ 2017-12-11 14:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171211144937.4537-1-marc.zyngier@arm.com>
Add an encoder for the EXTR instruction, which also implements the ROR
variant (where Rn == Rm).
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
arch/arm64/include/asm/insn.h | 6 ++++++
arch/arm64/kernel/insn.c | 32 ++++++++++++++++++++++++++++++++
2 files changed, 38 insertions(+)
diff --git a/arch/arm64/include/asm/insn.h b/arch/arm64/include/asm/insn.h
index 815b35bc53ed..f62c56b1793f 100644
--- a/arch/arm64/include/asm/insn.h
+++ b/arch/arm64/include/asm/insn.h
@@ -319,6 +319,7 @@ __AARCH64_INSN_FUNCS(and_imm, 0x7F800000, 0x12000000)
__AARCH64_INSN_FUNCS(orr_imm, 0x7F800000, 0x32000000)
__AARCH64_INSN_FUNCS(eor_imm, 0x7F800000, 0x52000000)
__AARCH64_INSN_FUNCS(ands_imm, 0x7F800000, 0x72000000)
+__AARCH64_INSN_FUNCS(extr, 0x7FA00000, 0x13800000)
__AARCH64_INSN_FUNCS(b, 0xFC000000, 0x14000000)
__AARCH64_INSN_FUNCS(bl, 0xFC000000, 0x94000000)
__AARCH64_INSN_FUNCS(cbz, 0x7F000000, 0x34000000)
@@ -433,6 +434,11 @@ u32 aarch64_insn_gen_logical_immediate(enum aarch64_insn_logic_type type,
enum aarch64_insn_register Rn,
enum aarch64_insn_register Rd,
u64 imm);
+u32 aarch64_insn_gen_extr(enum aarch64_insn_variant variant,
+ enum aarch64_insn_register Rm,
+ enum aarch64_insn_register Rn,
+ enum aarch64_insn_register Rd,
+ u8 lsb);
u32 aarch64_insn_gen_prefetch(enum aarch64_insn_register base,
enum aarch64_insn_prfm_type type,
enum aarch64_insn_prfm_target target,
diff --git a/arch/arm64/kernel/insn.c b/arch/arm64/kernel/insn.c
index 326b17016485..af29fc3e09a9 100644
--- a/arch/arm64/kernel/insn.c
+++ b/arch/arm64/kernel/insn.c
@@ -1622,3 +1622,35 @@ u32 aarch64_insn_gen_logical_immediate(enum aarch64_insn_logic_type type,
insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, Rn);
return aarch64_encode_immediate(imm, variant, insn);
}
+
+u32 aarch64_insn_gen_extr(enum aarch64_insn_variant variant,
+ enum aarch64_insn_register Rm,
+ enum aarch64_insn_register Rn,
+ enum aarch64_insn_register Rd,
+ u8 lsb)
+{
+ u32 insn;
+
+ insn = aarch64_insn_get_extr_value();
+
+ switch (variant) {
+ case AARCH64_INSN_VARIANT_32BIT:
+ if (lsb > 31)
+ return AARCH64_BREAK_FAULT;
+ break;
+ case AARCH64_INSN_VARIANT_64BIT:
+ if (lsb > 63)
+ return AARCH64_BREAK_FAULT;
+ insn |= AARCH64_INSN_SF_BIT;
+ insn = aarch64_insn_encode_immediate(AARCH64_INSN_IMM_N, insn, 1);
+ break;
+ default:
+ pr_err("%s: unknown variant encoding %d\n", __func__, variant);
+ return AARCH64_BREAK_FAULT;
+ }
+
+ insn = aarch64_insn_encode_immediate(AARCH64_INSN_IMM_S, insn, lsb);
+ insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, Rd);
+ insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, Rn);
+ return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RM, insn, Rm);
+}
--
2.14.2
^ permalink raw reply related
* [PATCH v2 14/19] KVM: arm/arm64: Move HYP IO VAs to the "idmap" range
From: Marc Zyngier @ 2017-12-11 14:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171211144937.4537-1-marc.zyngier@arm.com>
We so far mapped our HYP IO (which is essencially the GICv2 control
registers) using the same method as for memory. It recently appeared
that is a bit unsafe:
we compute the HYP VA using the kern_hyp_va helper, but that helper
is only designed to deal with kernel VAs coming from the linear map,
and not from the vmalloc region... This could in turn cause some bad
aliasing between the two, amplified by the new VA randomisation.
A solution is to come up with our very own basic VA allocator for
MMIO. Since half of the HYP address space only contains a single
page (the idmap), we have plenty to borrow from. Let's use the idmap
as a base, and allocate downwards from it. GICv2 now lives on the
other side of the great VA barrier.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
virt/kvm/arm/mmu.c | 40 ++++++++++++++++++++++++++++------------
1 file changed, 28 insertions(+), 12 deletions(-)
diff --git a/virt/kvm/arm/mmu.c b/virt/kvm/arm/mmu.c
index 6192d45d1e1a..0597c9846f1a 100644
--- a/virt/kvm/arm/mmu.c
+++ b/virt/kvm/arm/mmu.c
@@ -43,6 +43,9 @@ static unsigned long hyp_idmap_start;
static unsigned long hyp_idmap_end;
static phys_addr_t hyp_idmap_vector;
+static DEFINE_MUTEX(io_map_lock);
+static unsigned long io_map_base;
+
#define S2_PGD_SIZE (PTRS_PER_S2_PGD * sizeof(pgd_t))
#define hyp_pgd_order get_order(PTRS_PER_PGD * sizeof(pgd_t))
@@ -502,27 +505,31 @@ static void unmap_hyp_range(pgd_t *pgdp, phys_addr_t start, u64 size)
*
* Assumes hyp_pgd is a page table used strictly in Hyp-mode and
* therefore contains either mappings in the kernel memory area (above
- * PAGE_OFFSET), or device mappings in the vmalloc range (from
- * VMALLOC_START to VMALLOC_END).
+ * PAGE_OFFSET), or device mappings in the idmap range.
*
- * boot_hyp_pgd should only map two pages for the init code.
+ * boot_hyp_pgd should only map the idmap range, and is only used in
+ * the extended idmap case.
*/
void free_hyp_pgds(void)
{
+ pgd_t *id_pgd;
+
mutex_lock(&kvm_hyp_pgd_mutex);
+ id_pgd = boot_hyp_pgd ? boot_hyp_pgd : hyp_pgd;
+
+ if (id_pgd)
+ unmap_hyp_range(id_pgd, io_map_base,
+ hyp_idmap_start + PAGE_SIZE - io_map_base);
+
if (boot_hyp_pgd) {
- unmap_hyp_range(boot_hyp_pgd, hyp_idmap_start, PAGE_SIZE);
free_pages((unsigned long)boot_hyp_pgd, hyp_pgd_order);
boot_hyp_pgd = NULL;
}
if (hyp_pgd) {
- unmap_hyp_range(hyp_pgd, hyp_idmap_start, PAGE_SIZE);
unmap_hyp_range(hyp_pgd, kern_hyp_va(PAGE_OFFSET),
(uintptr_t)high_memory - PAGE_OFFSET);
- unmap_hyp_range(hyp_pgd, kern_hyp_va(VMALLOC_START),
- VMALLOC_END - VMALLOC_START);
free_pages((unsigned long)hyp_pgd, hyp_pgd_order);
hyp_pgd = NULL;
@@ -721,7 +728,8 @@ int create_hyp_io_mappings(phys_addr_t phys_addr, size_t size,
void __iomem **kaddr,
void __iomem **haddr)
{
- unsigned long start, end;
+ pgd_t *pgd = hyp_pgd;
+ unsigned long base;
int ret;
*kaddr = ioremap(phys_addr, size);
@@ -733,19 +741,26 @@ int create_hyp_io_mappings(phys_addr_t phys_addr, size_t size,
return 0;
}
+ mutex_lock(&io_map_lock);
+
+ base = io_map_base - size;
+ base &= ~(size - 1);
+
+ if (__kvm_cpu_uses_extended_idmap())
+ pgd = boot_hyp_pgd;
- start = kern_hyp_va((unsigned long)*kaddr);
- end = kern_hyp_va((unsigned long)*kaddr + size);
- ret = __create_hyp_mappings(hyp_pgd, start, end,
+ ret = __create_hyp_mappings(pgd, base, base + size,
__phys_to_pfn(phys_addr), PAGE_HYP_DEVICE);
if (ret) {
iounmap(*kaddr);
*kaddr = NULL;
} else {
- *haddr = (void __iomem *)start;
+ *haddr = (void __iomem *)base;
+ io_map_base = base;
}
+ mutex_unlock(&io_map_lock);
return ret;
}
@@ -1826,6 +1841,7 @@ int kvm_mmu_init(void)
goto out;
}
+ io_map_base = hyp_idmap_start;
return 0;
out:
free_hyp_pgds();
--
2.14.2
^ permalink raw reply related
* [PATCH v2 13/19] KVM: arm/arm64: Keep GICv2 HYP VAs in kvm_vgic_global_state
From: Marc Zyngier @ 2017-12-11 14:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171211144937.4537-1-marc.zyngier@arm.com>
As we're about to change the way we map devices at HYP, we need
to move away from kern_hyp_va on an IO address.
One way of achieving this is to store the VAs in kvm_vgic_global_state,
and use that directly from the HYP code. This requires a small change
to create_hyp_io_mappings so that it can also return a HYP VA.
We take this opportunity to nuke the vctrl_base field in the emulated
distributor, as it is not used anymore.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
arch/arm/include/asm/kvm_mmu.h | 3 ++-
arch/arm64/include/asm/kvm_mmu.h | 3 ++-
include/kvm/arm_vgic.h | 12 ++++++------
virt/kvm/arm/hyp/vgic-v2-sr.c | 10 +++-------
virt/kvm/arm/mmu.c | 20 ++++++++++++++++----
virt/kvm/arm/vgic/vgic-init.c | 6 ------
virt/kvm/arm/vgic/vgic-v2.c | 13 +++++++------
7 files changed, 36 insertions(+), 31 deletions(-)
diff --git a/arch/arm/include/asm/kvm_mmu.h b/arch/arm/include/asm/kvm_mmu.h
index cb3bef71ec9b..feff24b34506 100644
--- a/arch/arm/include/asm/kvm_mmu.h
+++ b/arch/arm/include/asm/kvm_mmu.h
@@ -42,7 +42,8 @@
int create_hyp_mappings(void *from, void *to, pgprot_t prot);
int create_hyp_io_mappings(phys_addr_t phys_addr, size_t size,
- void __iomem **kaddr);
+ void __iomem **kaddr,
+ void __iomem **haddr);
void free_hyp_pgds(void);
void stage2_unmap_vm(struct kvm *kvm);
diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h
index 5553c1abf1d5..d30e83df5ccb 100644
--- a/arch/arm64/include/asm/kvm_mmu.h
+++ b/arch/arm64/include/asm/kvm_mmu.h
@@ -119,7 +119,8 @@ static inline unsigned long __kern_hyp_va(unsigned long v)
int create_hyp_mappings(void *from, void *to, pgprot_t prot);
int create_hyp_io_mappings(phys_addr_t phys_addr, size_t size,
- void __iomem **kaddr);
+ void __iomem **kaddr,
+ void __iomem **haddr);
void free_hyp_pgds(void);
void stage2_unmap_vm(struct kvm *kvm);
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index 8c896540a72c..8b3fbc03293b 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -57,11 +57,15 @@ struct vgic_global {
/* Physical address of vgic virtual cpu interface */
phys_addr_t vcpu_base;
- /* GICV mapping */
+ /* GICV mapping, kernel VA */
void __iomem *vcpu_base_va;
+ /* GICV mapping, HYP VA */
+ void __iomem *vcpu_hyp_va;
- /* virtual control interface mapping */
+ /* virtual control interface mapping, kernel VA */
void __iomem *vctrl_base;
+ /* virtual control interface mapping, HYP VA */
+ void __iomem *vctrl_hyp;
/* Number of implemented list registers */
int nr_lr;
@@ -198,10 +202,6 @@ struct vgic_dist {
int nr_spis;
- /* TODO: Consider moving to global state */
- /* Virtual control interface mapping */
- void __iomem *vctrl_base;
-
/* base addresses in guest physical address space: */
gpa_t vgic_dist_base; /* distributor */
union {
diff --git a/virt/kvm/arm/hyp/vgic-v2-sr.c b/virt/kvm/arm/hyp/vgic-v2-sr.c
index 19f63fbf3682..a3b224e09f74 100644
--- a/virt/kvm/arm/hyp/vgic-v2-sr.c
+++ b/virt/kvm/arm/hyp/vgic-v2-sr.c
@@ -60,10 +60,8 @@ static void __hyp_text save_lrs(struct kvm_vcpu *vcpu, void __iomem *base)
/* vcpu is already in the HYP VA space */
void __hyp_text __vgic_v2_save_state(struct kvm_vcpu *vcpu)
{
- struct kvm *kvm = kern_hyp_va(vcpu->kvm);
struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
- struct vgic_dist *vgic = &kvm->arch.vgic;
- void __iomem *base = kern_hyp_va(vgic->vctrl_base);
+ void __iomem *base = hyp_symbol_addr(kvm_vgic_global_state)->vctrl_hyp;
u64 used_lrs = vcpu->arch.vgic_cpu.used_lrs;
if (!base)
@@ -85,10 +83,8 @@ void __hyp_text __vgic_v2_save_state(struct kvm_vcpu *vcpu)
/* vcpu is already in the HYP VA space */
void __hyp_text __vgic_v2_restore_state(struct kvm_vcpu *vcpu)
{
- struct kvm *kvm = kern_hyp_va(vcpu->kvm);
struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
- struct vgic_dist *vgic = &kvm->arch.vgic;
- void __iomem *base = kern_hyp_va(vgic->vctrl_base);
+ void __iomem *base = hyp_symbol_addr(kvm_vgic_global_state)->vctrl_hyp;
int i;
u64 used_lrs = vcpu->arch.vgic_cpu.used_lrs;
@@ -143,7 +139,7 @@ int __hyp_text __vgic_v2_perform_cpuif_access(struct kvm_vcpu *vcpu)
return -1;
rd = kvm_vcpu_dabt_get_rd(vcpu);
- addr = kern_hyp_va(hyp_symbol_addr(kvm_vgic_global_state)->vcpu_base_va);
+ addr = hyp_symbol_addr(kvm_vgic_global_state)->vcpu_hyp_va;
addr += fault_ipa - vgic->vgic_cpu_base;
if (kvm_vcpu_dabt_iswrite(vcpu)) {
diff --git a/virt/kvm/arm/mmu.c b/virt/kvm/arm/mmu.c
index 38adbe0a016c..6192d45d1e1a 100644
--- a/virt/kvm/arm/mmu.c
+++ b/virt/kvm/arm/mmu.c
@@ -713,28 +713,40 @@ int create_hyp_mappings(void *from, void *to, pgprot_t prot)
* @phys_addr: The physical start address which gets mapped
* @size: Size of the region being mapped
* @kaddr: Kernel VA for this mapping
+ * @haddr: HYP VA for this mapping
*
- * The resulting HYP VA is the same as the kernel VA, modulo
- * HYP_PAGE_OFFSET.
+ * The resulting HYP VA is completely unrelated to the kernel VA.
*/
int create_hyp_io_mappings(phys_addr_t phys_addr, size_t size,
- void __iomem **kaddr)
+ void __iomem **kaddr,
+ void __iomem **haddr)
{
unsigned long start, end;
+ int ret;
*kaddr = ioremap(phys_addr, size);
if (!*kaddr)
return -ENOMEM;
if (is_kernel_in_hyp_mode()) {
+ *haddr = *kaddr;
return 0;
}
start = kern_hyp_va((unsigned long)*kaddr);
end = kern_hyp_va((unsigned long)*kaddr + size);
- return __create_hyp_mappings(hyp_pgd, start, end,
+ ret = __create_hyp_mappings(hyp_pgd, start, end,
__phys_to_pfn(phys_addr), PAGE_HYP_DEVICE);
+
+ if (ret) {
+ iounmap(*kaddr);
+ *kaddr = NULL;
+ } else {
+ *haddr = (void __iomem *)start;
+ }
+
+ return ret;
}
/**
diff --git a/virt/kvm/arm/vgic/vgic-init.c b/virt/kvm/arm/vgic/vgic-init.c
index 62310122ee78..3f01b5975055 100644
--- a/virt/kvm/arm/vgic/vgic-init.c
+++ b/virt/kvm/arm/vgic/vgic-init.c
@@ -166,12 +166,6 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)
kvm->arch.vgic.in_kernel = true;
kvm->arch.vgic.vgic_model = type;
- /*
- * kvm_vgic_global_state.vctrl_base is set on vgic probe (kvm_arch_init)
- * it is stored in distributor struct for asm save/restore purpose
- */
- kvm->arch.vgic.vctrl_base = kvm_vgic_global_state.vctrl_base;
-
kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF;
kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF;
kvm->arch.vgic.vgic_redist_base = VGIC_ADDR_UNDEF;
diff --git a/virt/kvm/arm/vgic/vgic-v2.c b/virt/kvm/arm/vgic/vgic-v2.c
index bc49d702f9f0..f0f566e4494e 100644
--- a/virt/kvm/arm/vgic/vgic-v2.c
+++ b/virt/kvm/arm/vgic/vgic-v2.c
@@ -335,7 +335,8 @@ int vgic_v2_probe(const struct gic_kvm_info *info)
ret = create_hyp_io_mappings(info->vcpu.start,
resource_size(&info->vcpu),
- &kvm_vgic_global_state.vcpu_base_va);
+ &kvm_vgic_global_state.vcpu_base_va,
+ &kvm_vgic_global_state.vcpu_hyp_va);
if (ret) {
kvm_err("Cannot map GICV into hyp\n");
goto out;
@@ -346,7 +347,8 @@ int vgic_v2_probe(const struct gic_kvm_info *info)
ret = create_hyp_io_mappings(info->vctrl.start,
resource_size(&info->vctrl),
- &kvm_vgic_global_state.vctrl_base);
+ &kvm_vgic_global_state.vctrl_base,
+ &kvm_vgic_global_state.vctrl_hyp);
if (ret) {
kvm_err("Cannot map VCTRL into hyp\n");
goto out;
@@ -381,15 +383,14 @@ int vgic_v2_probe(const struct gic_kvm_info *info)
void vgic_v2_load(struct kvm_vcpu *vcpu)
{
struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
- struct vgic_dist *vgic = &vcpu->kvm->arch.vgic;
- writel_relaxed(cpu_if->vgic_vmcr, vgic->vctrl_base + GICH_VMCR);
+ writel_relaxed(cpu_if->vgic_vmcr,
+ kvm_vgic_global_state.vctrl_base + GICH_VMCR);
}
void vgic_v2_put(struct kvm_vcpu *vcpu)
{
struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
- struct vgic_dist *vgic = &vcpu->kvm->arch.vgic;
- cpu_if->vgic_vmcr = readl_relaxed(vgic->vctrl_base + GICH_VMCR);
+ cpu_if->vgic_vmcr = readl_relaxed(kvm_vgic_global_state.vctrl_base + GICH_VMCR);
}
--
2.14.2
^ permalink raw reply related
* [PATCH v2 12/19] KVM: arm/arm64: Move ioremap calls to create_hyp_io_mappings
From: Marc Zyngier @ 2017-12-11 14:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171211144937.4537-1-marc.zyngier@arm.com>
Both HYP io mappings call ioremap, followed by create_hyp_io_mappings.
Let's move the ioremap call into create_hyp_io_mappings itself, which
simplifies the code a bit and allows for further refactoring.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
arch/arm/include/asm/kvm_mmu.h | 3 ++-
arch/arm64/include/asm/kvm_mmu.h | 3 ++-
virt/kvm/arm/mmu.c | 24 ++++++++++++++----------
virt/kvm/arm/vgic/vgic-v2.c | 31 ++++++++-----------------------
4 files changed, 26 insertions(+), 35 deletions(-)
diff --git a/arch/arm/include/asm/kvm_mmu.h b/arch/arm/include/asm/kvm_mmu.h
index fa6f2174276b..cb3bef71ec9b 100644
--- a/arch/arm/include/asm/kvm_mmu.h
+++ b/arch/arm/include/asm/kvm_mmu.h
@@ -41,7 +41,8 @@
#include <asm/stage2_pgtable.h>
int create_hyp_mappings(void *from, void *to, pgprot_t prot);
-int create_hyp_io_mappings(void *from, void *to, phys_addr_t);
+int create_hyp_io_mappings(phys_addr_t phys_addr, size_t size,
+ void __iomem **kaddr);
void free_hyp_pgds(void);
void stage2_unmap_vm(struct kvm *kvm);
diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h
index d03eb75f1704..5553c1abf1d5 100644
--- a/arch/arm64/include/asm/kvm_mmu.h
+++ b/arch/arm64/include/asm/kvm_mmu.h
@@ -118,7 +118,8 @@ static inline unsigned long __kern_hyp_va(unsigned long v)
#include <asm/stage2_pgtable.h>
int create_hyp_mappings(void *from, void *to, pgprot_t prot);
-int create_hyp_io_mappings(void *from, void *to, phys_addr_t);
+int create_hyp_io_mappings(phys_addr_t phys_addr, size_t size,
+ void __iomem **kaddr);
void free_hyp_pgds(void);
void stage2_unmap_vm(struct kvm *kvm);
diff --git a/virt/kvm/arm/mmu.c b/virt/kvm/arm/mmu.c
index 84d09f1a44d4..38adbe0a016c 100644
--- a/virt/kvm/arm/mmu.c
+++ b/virt/kvm/arm/mmu.c
@@ -709,26 +709,30 @@ int create_hyp_mappings(void *from, void *to, pgprot_t prot)
}
/**
- * create_hyp_io_mappings - duplicate a kernel IO mapping into Hyp mode
- * @from: The kernel start VA of the range
- * @to: The kernel end VA of the range (exclusive)
+ * create_hyp_io_mappings - Map IO into both kernel and HYP
* @phys_addr: The physical start address which gets mapped
+ * @size: Size of the region being mapped
+ * @kaddr: Kernel VA for this mapping
*
* The resulting HYP VA is the same as the kernel VA, modulo
* HYP_PAGE_OFFSET.
*/
-int create_hyp_io_mappings(void *from, void *to, phys_addr_t phys_addr)
+int create_hyp_io_mappings(phys_addr_t phys_addr, size_t size,
+ void __iomem **kaddr)
{
- unsigned long start = kern_hyp_va((unsigned long)from);
- unsigned long end = kern_hyp_va((unsigned long)to);
+ unsigned long start, end;
- if (is_kernel_in_hyp_mode())
+ *kaddr = ioremap(phys_addr, size);
+ if (!*kaddr)
+ return -ENOMEM;
+
+ if (is_kernel_in_hyp_mode()) {
return 0;
+ }
- /* Check for a valid kernel IO mapping */
- if (!is_vmalloc_addr(from) || !is_vmalloc_addr(to - 1))
- return -EINVAL;
+ start = kern_hyp_va((unsigned long)*kaddr);
+ end = kern_hyp_va((unsigned long)*kaddr + size);
return __create_hyp_mappings(hyp_pgd, start, end,
__phys_to_pfn(phys_addr), PAGE_HYP_DEVICE);
}
diff --git a/virt/kvm/arm/vgic/vgic-v2.c b/virt/kvm/arm/vgic/vgic-v2.c
index 80897102da26..bc49d702f9f0 100644
--- a/virt/kvm/arm/vgic/vgic-v2.c
+++ b/virt/kvm/arm/vgic/vgic-v2.c
@@ -332,16 +332,10 @@ int vgic_v2_probe(const struct gic_kvm_info *info)
if (!PAGE_ALIGNED(info->vcpu.start) ||
!PAGE_ALIGNED(resource_size(&info->vcpu))) {
kvm_info("GICV region size/alignment is unsafe, using trapping (reduced performance)\n");
- kvm_vgic_global_state.vcpu_base_va = ioremap(info->vcpu.start,
- resource_size(&info->vcpu));
- if (!kvm_vgic_global_state.vcpu_base_va) {
- kvm_err("Cannot ioremap GICV\n");
- return -ENOMEM;
- }
- ret = create_hyp_io_mappings(kvm_vgic_global_state.vcpu_base_va,
- kvm_vgic_global_state.vcpu_base_va + resource_size(&info->vcpu),
- info->vcpu.start);
+ ret = create_hyp_io_mappings(info->vcpu.start,
+ resource_size(&info->vcpu),
+ &kvm_vgic_global_state.vcpu_base_va);
if (ret) {
kvm_err("Cannot map GICV into hyp\n");
goto out;
@@ -350,26 +344,17 @@ int vgic_v2_probe(const struct gic_kvm_info *info)
static_branch_enable(&vgic_v2_cpuif_trap);
}
- kvm_vgic_global_state.vctrl_base = ioremap(info->vctrl.start,
- resource_size(&info->vctrl));
- if (!kvm_vgic_global_state.vctrl_base) {
- kvm_err("Cannot ioremap GICH\n");
- ret = -ENOMEM;
+ ret = create_hyp_io_mappings(info->vctrl.start,
+ resource_size(&info->vctrl),
+ &kvm_vgic_global_state.vctrl_base);
+ if (ret) {
+ kvm_err("Cannot map VCTRL into hyp\n");
goto out;
}
vtr = readl_relaxed(kvm_vgic_global_state.vctrl_base + GICH_VTR);
kvm_vgic_global_state.nr_lr = (vtr & 0x3f) + 1;
- ret = create_hyp_io_mappings(kvm_vgic_global_state.vctrl_base,
- kvm_vgic_global_state.vctrl_base +
- resource_size(&info->vctrl),
- info->vctrl.start);
- if (ret) {
- kvm_err("Cannot map VCTRL into hyp\n");
- goto out;
- }
-
ret = kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V2);
if (ret) {
kvm_err("Cannot register GICv2 KVM device\n");
--
2.14.2
^ permalink raw reply related
* [PATCH v2 11/19] KVM: arm/arm64: Demote HYP VA range display to being a debug feature
From: Marc Zyngier @ 2017-12-11 14:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171211144937.4537-1-marc.zyngier@arm.com>
Displaying the HYP VA information is slightly counterproductive when
using VA randomization. Turn it into a debug feature only, and adjust
the last displayed value to reflect the top of RAM instead of ~0.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
virt/kvm/arm/mmu.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/virt/kvm/arm/mmu.c b/virt/kvm/arm/mmu.c
index b4b69c2d1012..84d09f1a44d4 100644
--- a/virt/kvm/arm/mmu.c
+++ b/virt/kvm/arm/mmu.c
@@ -1760,9 +1760,10 @@ int kvm_mmu_init(void)
*/
BUG_ON((hyp_idmap_start ^ (hyp_idmap_end - 1)) & PAGE_MASK);
- kvm_info("IDMAP page: %lx\n", hyp_idmap_start);
- kvm_info("HYP VA range: %lx:%lx\n",
- kern_hyp_va(PAGE_OFFSET), kern_hyp_va(~0UL));
+ kvm_debug("IDMAP page: %lx\n", hyp_idmap_start);
+ kvm_debug("HYP VA range: %lx:%lx\n",
+ kern_hyp_va(PAGE_OFFSET),
+ kern_hyp_va((unsigned long)high_memory - 1));
if (hyp_idmap_start >= kern_hyp_va(PAGE_OFFSET) &&
hyp_idmap_start < kern_hyp_va(~0UL) &&
--
2.14.2
^ permalink raw reply related
* [PATCH v2 10/19] KVM: arm/arm64: Do not use kern_hyp_va() with kvm_vgic_global_state
From: Marc Zyngier @ 2017-12-11 14:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171211144937.4537-1-marc.zyngier@arm.com>
kvm_vgic_global_state is part of the read-only section, and is
usually accessed using a PC-relative address generation (adrp + add).
It is thus useless to use kern_hyp_va() on it, and actively problematic
if kern_hyp_va() becomes non-idempotent. On the other hand, there is
no way that the compiler is going to guarantee that such access is
always be PC relative.
So let's bite the bullet and provide our own accessor.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
arch/arm/include/asm/kvm_hyp.h | 6 ++++++
arch/arm64/include/asm/kvm_hyp.h | 9 +++++++++
virt/kvm/arm/hyp/vgic-v2-sr.c | 4 ++--
3 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/arch/arm/include/asm/kvm_hyp.h b/arch/arm/include/asm/kvm_hyp.h
index ab20ffa8b9e7..1d42d0aa2feb 100644
--- a/arch/arm/include/asm/kvm_hyp.h
+++ b/arch/arm/include/asm/kvm_hyp.h
@@ -26,6 +26,12 @@
#define __hyp_text __section(.hyp.text) notrace
+#define hyp_symbol_addr(s) \
+ ({ \
+ typeof(s) *addr = &(s); \
+ addr; \
+ })
+
#define __ACCESS_VFP(CRn) \
"mrc", "mcr", __stringify(p10, 7, %0, CRn, cr0, 0), u32
diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h
index 08d3bb66c8b7..a2d98c539023 100644
--- a/arch/arm64/include/asm/kvm_hyp.h
+++ b/arch/arm64/include/asm/kvm_hyp.h
@@ -25,6 +25,15 @@
#define __hyp_text __section(.hyp.text) notrace
+#define hyp_symbol_addr(s) \
+ ({ \
+ typeof(s) *addr; \
+ asm volatile("adrp %0, %1\n" \
+ "add %0, %0, :lo12:%1\n" \
+ : "=r" (addr) : "S" (&s)); \
+ addr; \
+ })
+
#define read_sysreg_elx(r,nvh,vh) \
({ \
u64 reg; \
diff --git a/virt/kvm/arm/hyp/vgic-v2-sr.c b/virt/kvm/arm/hyp/vgic-v2-sr.c
index a3f18d362366..19f63fbf3682 100644
--- a/virt/kvm/arm/hyp/vgic-v2-sr.c
+++ b/virt/kvm/arm/hyp/vgic-v2-sr.c
@@ -25,7 +25,7 @@
static void __hyp_text save_elrsr(struct kvm_vcpu *vcpu, void __iomem *base)
{
struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
- int nr_lr = (kern_hyp_va(&kvm_vgic_global_state))->nr_lr;
+ int nr_lr = hyp_symbol_addr(kvm_vgic_global_state)->nr_lr;
u32 elrsr0, elrsr1;
elrsr0 = readl_relaxed(base + GICH_ELRSR0);
@@ -143,7 +143,7 @@ int __hyp_text __vgic_v2_perform_cpuif_access(struct kvm_vcpu *vcpu)
return -1;
rd = kvm_vcpu_dabt_get_rd(vcpu);
- addr = kern_hyp_va((kern_hyp_va(&kvm_vgic_global_state))->vcpu_base_va);
+ addr = kern_hyp_va(hyp_symbol_addr(kvm_vgic_global_state)->vcpu_base_va);
addr += fault_ipa - vgic->vgic_cpu_base;
if (kvm_vcpu_dabt_iswrite(vcpu)) {
--
2.14.2
^ permalink raw reply related
* [PATCH v2 09/19] arm64: cpufeatures: Drop the ARM64_HYP_OFFSET_LOW feature flag
From: Marc Zyngier @ 2017-12-11 14:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171211144937.4537-1-marc.zyngier@arm.com>
Now that we can dynamically compute the kernek/hyp VA mask, there
is need for a feature flag to trigger the alternative patching.
Let's drop the flag and everything that depends on it.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
arch/arm64/include/asm/cpucaps.h | 2 +-
arch/arm64/kernel/cpufeature.c | 19 -------------------
2 files changed, 1 insertion(+), 20 deletions(-)
diff --git a/arch/arm64/include/asm/cpucaps.h b/arch/arm64/include/asm/cpucaps.h
index 2ff7c5e8efab..f130f35dca3c 100644
--- a/arch/arm64/include/asm/cpucaps.h
+++ b/arch/arm64/include/asm/cpucaps.h
@@ -32,7 +32,7 @@
#define ARM64_HAS_VIRT_HOST_EXTN 11
#define ARM64_WORKAROUND_CAVIUM_27456 12
#define ARM64_HAS_32BIT_EL0 13
-#define ARM64_HYP_OFFSET_LOW 14
+/* #define ARM64_UNALLOCATED_ENTRY 14 */
#define ARM64_MISMATCHED_CACHE_LINE_SIZE 15
#define ARM64_HAS_NO_FPSIMD 16
#define ARM64_WORKAROUND_REPEAT_TLBI 17
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index c5ba0097887f..9eabceaaf5fb 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -824,19 +824,6 @@ static bool runs_at_el2(const struct arm64_cpu_capabilities *entry, int __unused
return is_kernel_in_hyp_mode();
}
-static bool hyp_offset_low(const struct arm64_cpu_capabilities *entry,
- int __unused)
-{
- phys_addr_t idmap_addr = __pa_symbol(__hyp_idmap_text_start);
-
- /*
- * Activate the lower HYP offset only if:
- * - the idmap doesn't clash with it,
- * - the kernel is not running at EL2.
- */
- return idmap_addr > GENMASK(VA_BITS - 2, 0) && !is_kernel_in_hyp_mode();
-}
-
static bool has_no_fpsimd(const struct arm64_cpu_capabilities *entry, int __unused)
{
u64 pfr0 = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
@@ -925,12 +912,6 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
.field_pos = ID_AA64PFR0_EL0_SHIFT,
.min_field_value = ID_AA64PFR0_EL0_32BIT_64BIT,
},
- {
- .desc = "Reduced HYP mapping offset",
- .capability = ARM64_HYP_OFFSET_LOW,
- .def_scope = SCOPE_SYSTEM,
- .matches = hyp_offset_low,
- },
{
/* FP/SIMD is not implemented */
.capability = ARM64_HAS_NO_FPSIMD,
--
2.14.2
^ permalink raw reply related
* [PATCH v2 08/19] arm64: KVM: Dynamically patch the kernel/hyp VA mask
From: Marc Zyngier @ 2017-12-11 14:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171211144937.4537-1-marc.zyngier@arm.com>
So far, we're using a complicated sequence of alternatives to
patch the kernel/hyp VA mask on non-VHE, and NOP out the
masking altogether when on VHE.
THe newly introduced dynamic patching gives us the opportunity
to simplify that code by patching a single instruction with
the correct mask (instead of the mind bending cummulative masking
we have at the moment) or even a single NOP on VHE.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
arch/arm64/include/asm/kvm_mmu.h | 42 ++++++-----------------
arch/arm64/kvm/Makefile | 2 +-
arch/arm64/kvm/haslr.c | 74 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 85 insertions(+), 33 deletions(-)
create mode 100644 arch/arm64/kvm/haslr.c
diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h
index 672c8684d5c2..d03eb75f1704 100644
--- a/arch/arm64/include/asm/kvm_mmu.h
+++ b/arch/arm64/include/asm/kvm_mmu.h
@@ -69,9 +69,6 @@
* mappings, and none of this applies in that case.
*/
-#define HYP_PAGE_OFFSET_HIGH_MASK ((UL(1) << VA_BITS) - 1)
-#define HYP_PAGE_OFFSET_LOW_MASK ((UL(1) << (VA_BITS - 1)) - 1)
-
#ifdef __ASSEMBLY__
#include <asm/alternative.h>
@@ -81,27 +78,13 @@
* Convert a kernel VA into a HYP VA.
* reg: VA to be converted.
*
- * This generates the following sequences:
- * - High mask:
- * and x0, x0, #HYP_PAGE_OFFSET_HIGH_MASK
- * nop
- * - Low mask:
- * and x0, x0, #HYP_PAGE_OFFSET_HIGH_MASK
- * and x0, x0, #HYP_PAGE_OFFSET_LOW_MASK
- * - VHE:
- * nop
- * nop
- *
- * The "low mask" version works because the mask is a strict subset of
- * the "high mask", hence performing the first mask for nothing.
- * Should be completely invisible on any viable CPU.
+ * The actual code generation takes place in kvm_update_va_mask, and
+ * the instructions below are only there to reserve the space and
+ * perform the register allocation.
*/
.macro kern_hyp_va reg
-alternative_if_not ARM64_HAS_VIRT_HOST_EXTN
- and \reg, \reg, #HYP_PAGE_OFFSET_HIGH_MASK
-alternative_else_nop_endif
-alternative_if ARM64_HYP_OFFSET_LOW
- and \reg, \reg, #HYP_PAGE_OFFSET_LOW_MASK
+alternative_cb kvm_update_va_mask
+ and \reg, \reg, #1
alternative_else_nop_endif
.endm
@@ -113,18 +96,13 @@ alternative_else_nop_endif
#include <asm/mmu_context.h>
#include <asm/pgtable.h>
+u32 kvm_update_va_mask(struct alt_instr *alt, int index, u32 oinsn);
+
static inline unsigned long __kern_hyp_va(unsigned long v)
{
- asm volatile(ALTERNATIVE("and %0, %0, %1",
- "nop",
- ARM64_HAS_VIRT_HOST_EXTN)
- : "+r" (v)
- : "i" (HYP_PAGE_OFFSET_HIGH_MASK));
- asm volatile(ALTERNATIVE("nop",
- "and %0, %0, %1",
- ARM64_HYP_OFFSET_LOW)
- : "+r" (v)
- : "i" (HYP_PAGE_OFFSET_LOW_MASK));
+ asm volatile(ALTERNATIVE_CB("and %0, %0, #1\n",
+ kvm_update_va_mask)
+ : "+r" (v));
return v;
}
diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
index 87c4f7ae24de..baba030ee29e 100644
--- a/arch/arm64/kvm/Makefile
+++ b/arch/arm64/kvm/Makefile
@@ -16,7 +16,7 @@ kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o $(KVM)/e
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/arm.o $(KVM)/arm/mmu.o $(KVM)/arm/mmio.o
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/psci.o $(KVM)/arm/perf.o
-kvm-$(CONFIG_KVM_ARM_HOST) += inject_fault.o regmap.o
+kvm-$(CONFIG_KVM_ARM_HOST) += inject_fault.o regmap.o haslr.o
kvm-$(CONFIG_KVM_ARM_HOST) += hyp.o hyp-init.o handle_exit.o
kvm-$(CONFIG_KVM_ARM_HOST) += guest.o debug.o reset.o sys_regs.o sys_regs_generic_v8.o
kvm-$(CONFIG_KVM_ARM_HOST) += vgic-sys-reg-v3.o
diff --git a/arch/arm64/kvm/haslr.c b/arch/arm64/kvm/haslr.c
new file mode 100644
index 000000000000..5e1643a4e7bf
--- /dev/null
+++ b/arch/arm64/kvm/haslr.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2017 ARM Ltd.
+ * Author: Marc Zyngier <marc.zyngier@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kvm_host.h>
+#include <asm/alternative.h>
+#include <asm/debug-monitors.h>
+#include <asm/insn.h>
+#include <asm/kvm_mmu.h>
+
+#define HYP_PAGE_OFFSET_HIGH_MASK ((UL(1) << VA_BITS) - 1)
+#define HYP_PAGE_OFFSET_LOW_MASK ((UL(1) << (VA_BITS - 1)) - 1)
+
+static unsigned long get_hyp_va_mask(void)
+{
+ phys_addr_t idmap_addr = __pa_symbol(__hyp_idmap_text_start);
+ unsigned long mask = HYP_PAGE_OFFSET_HIGH_MASK;
+
+ /*
+ * Activate the lower HYP offset only if the idmap doesn't
+ * clash with it,
+ */
+ if (idmap_addr > HYP_PAGE_OFFSET_LOW_MASK)
+ mask = HYP_PAGE_OFFSET_HIGH_MASK;
+
+ return mask;
+}
+
+u32 __init kvm_update_va_mask(struct alt_instr *alt, int index, u32 oinsn)
+{
+ u32 rd, rn, insn;
+ u64 imm;
+
+ /* We only expect a 1 instruction sequence */
+ BUG_ON((alt->alt_len / sizeof(insn)) != 1);
+
+ /* VHE doesn't need any address translation, let's NOP everything */
+ if (has_vhe())
+ return aarch64_insn_gen_nop();
+
+ rd = aarch64_insn_decode_register(AARCH64_INSN_REGTYPE_RD, oinsn);
+ rn = aarch64_insn_decode_register(AARCH64_INSN_REGTYPE_RN, oinsn);
+
+ switch (index) {
+ default:
+ /* Something went wrong... */
+ insn = AARCH64_BREAK_FAULT;
+ break;
+
+ case 0:
+ imm = get_hyp_va_mask();
+ insn = aarch64_insn_gen_logical_immediate(AARCH64_INSN_LOGIC_AND,
+ AARCH64_INSN_VARIANT_64BIT,
+ rn, rd, imm);
+ break;
+ }
+
+ BUG_ON(insn == AARCH64_BREAK_FAULT);
+
+ return insn;
+}
--
2.14.2
^ permalink raw reply related
* [PATCH v2 07/19] arm64: insn: Add encoder for bitwise operations using litterals
From: Marc Zyngier @ 2017-12-11 14:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171211144937.4537-1-marc.zyngier@arm.com>
We lack a way to encode operations such as AND, ORR, EOR that take
an immediate value. Doing so is quite involved, and is all about
reverse engineering the decoding algorithm described in the
pseudocode function DecodeBitMasks().
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
arch/arm64/include/asm/insn.h | 9 +++
arch/arm64/kernel/insn.c | 137 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 146 insertions(+)
diff --git a/arch/arm64/include/asm/insn.h b/arch/arm64/include/asm/insn.h
index 21fffdd290a3..815b35bc53ed 100644
--- a/arch/arm64/include/asm/insn.h
+++ b/arch/arm64/include/asm/insn.h
@@ -315,6 +315,10 @@ __AARCH64_INSN_FUNCS(eor, 0x7F200000, 0x4A000000)
__AARCH64_INSN_FUNCS(eon, 0x7F200000, 0x4A200000)
__AARCH64_INSN_FUNCS(ands, 0x7F200000, 0x6A000000)
__AARCH64_INSN_FUNCS(bics, 0x7F200000, 0x6A200000)
+__AARCH64_INSN_FUNCS(and_imm, 0x7F800000, 0x12000000)
+__AARCH64_INSN_FUNCS(orr_imm, 0x7F800000, 0x32000000)
+__AARCH64_INSN_FUNCS(eor_imm, 0x7F800000, 0x52000000)
+__AARCH64_INSN_FUNCS(ands_imm, 0x7F800000, 0x72000000)
__AARCH64_INSN_FUNCS(b, 0xFC000000, 0x14000000)
__AARCH64_INSN_FUNCS(bl, 0xFC000000, 0x94000000)
__AARCH64_INSN_FUNCS(cbz, 0x7F000000, 0x34000000)
@@ -424,6 +428,11 @@ u32 aarch64_insn_gen_logical_shifted_reg(enum aarch64_insn_register dst,
int shift,
enum aarch64_insn_variant variant,
enum aarch64_insn_logic_type type);
+u32 aarch64_insn_gen_logical_immediate(enum aarch64_insn_logic_type type,
+ enum aarch64_insn_variant variant,
+ enum aarch64_insn_register Rn,
+ enum aarch64_insn_register Rd,
+ u64 imm);
u32 aarch64_insn_gen_prefetch(enum aarch64_insn_register base,
enum aarch64_insn_prfm_type type,
enum aarch64_insn_prfm_target target,
diff --git a/arch/arm64/kernel/insn.c b/arch/arm64/kernel/insn.c
index 7e432662d454..326b17016485 100644
--- a/arch/arm64/kernel/insn.c
+++ b/arch/arm64/kernel/insn.c
@@ -1485,3 +1485,140 @@ pstate_check_t * const aarch32_opcode_cond_checks[16] = {
__check_hi, __check_ls, __check_ge, __check_lt,
__check_gt, __check_le, __check_al, __check_al
};
+
+static bool range_of_ones(u64 val)
+{
+ /* Doesn't handle full ones or full zeroes */
+ int x = __ffs64(val) - 1;
+ u64 sval = val >> x;
+
+ /* One of Sean Eron Anderson's bithack tricks */
+ return ((sval + 1) & (sval)) == 0;
+}
+
+static u32 aarch64_encode_immediate(u64 imm,
+ enum aarch64_insn_variant variant,
+ u32 insn)
+{
+ unsigned int immr, imms, n, ones, ror, esz, tmp;
+ u64 mask;
+
+ /* Can't encode full zeroes or full ones */
+ if (!imm || !~imm)
+ return AARCH64_BREAK_FAULT;
+
+ switch (variant) {
+ case AARCH64_INSN_VARIANT_32BIT:
+ if (upper_32_bits(imm))
+ return AARCH64_BREAK_FAULT;
+ esz = 32;
+ break;
+ case AARCH64_INSN_VARIANT_64BIT:
+ insn |= AARCH64_INSN_SF_BIT;
+ esz = 64;
+ break;
+ default:
+ pr_err("%s: unknown variant encoding %d\n", __func__, variant);
+ return AARCH64_BREAK_FAULT;
+ }
+
+ /*
+ * Inverse of Replicate(). Try to spot a repeating pattern
+ * with a pow2 stride.
+ */
+ for (tmp = esz; tmp > 2; tmp /= 2) {
+ u64 emask = BIT(tmp / 2) - 1;
+
+ if ((imm & emask) != ((imm >> (tmp / 2)) & emask))
+ break;
+
+ esz = tmp;
+ }
+
+ /* N is only set if we're encoding a 64bit value */
+ n = esz == 64;
+
+ /* Trim imm to the element size */
+ mask = BIT(esz - 1) - 1;
+ imm &= mask;
+
+ /* That's how many ones we need to encode */
+ ones = hweight64(imm);
+
+ /*
+ * imms is set to (ones - 1), prefixed with a string of ones
+ * and a zero if they fit. Cap it to 6 bits.
+ */
+ imms = ones - 1;
+ imms |= 0xf << ffs(esz);
+ imms &= BIT(6) - 1;
+
+ /* Compute the rotation */
+ if (range_of_ones(imm)) {
+ /*
+ * Pattern: 0..01..10..0
+ *
+ * Compute how many rotate we need to align it right
+ */
+ ror = ffs(imm) - 1;
+ } else {
+ /*
+ * Pattern: 0..01..10..01..1
+ *
+ * Fill the unused top bits with ones, and check if
+ * the result is a valid immediate (all ones with a
+ * contiguous ranges of zeroes).
+ */
+ imm |= ~mask;
+ if (!range_of_ones(~imm))
+ return AARCH64_BREAK_FAULT;
+
+ /*
+ * Compute the rotation to get a continuous set of
+ * ones, with the first bit set@position 0
+ */
+ ror = fls(~imm);
+ }
+
+ /*
+ * immr is the number of bits we need to rotate back to the
+ * original set of ones. Note that this is relative to the
+ * element size...
+ */
+ immr = (esz - ror) & (esz - 1);
+
+ insn = aarch64_insn_encode_immediate(AARCH64_INSN_IMM_N, insn, n);
+ insn = aarch64_insn_encode_immediate(AARCH64_INSN_IMM_R, insn, immr);
+ return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_S, insn, imms);
+}
+
+u32 aarch64_insn_gen_logical_immediate(enum aarch64_insn_logic_type type,
+ enum aarch64_insn_variant variant,
+ enum aarch64_insn_register Rn,
+ enum aarch64_insn_register Rd,
+ u64 imm)
+{
+ u32 insn;
+
+ switch (type) {
+ case AARCH64_INSN_LOGIC_AND:
+ insn = aarch64_insn_get_and_imm_value();
+ break;
+ case AARCH64_INSN_LOGIC_ORR:
+ insn = aarch64_insn_get_orr_imm_value();
+ break;
+ case AARCH64_INSN_LOGIC_EOR:
+ insn = aarch64_insn_get_eor_imm_value();
+ break;
+ case AARCH64_INSN_LOGIC_AND_SETFLAGS:
+ insn = aarch64_insn_get_ands_imm_value();
+ break;
+ default:
+ pr_err("%s: unknown logical encoding %d\n", __func__, type);
+ return AARCH64_BREAK_FAULT;
+ }
+
+ insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, Rd);
+ insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, Rn);
+ return aarch64_encode_immediate(imm, variant, insn);
+}
--
2.14.2
^ permalink raw reply related
* [PATCH v2 06/19] arm64: insn: Add N immediate encoding
From: Marc Zyngier @ 2017-12-11 14:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171211144937.4537-1-marc.zyngier@arm.com>
We're missing the a way to generate the encoding of the N immediate,
which is only a single bit used in a number of instruction that take
an immediate.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
arch/arm64/include/asm/insn.h | 1 +
arch/arm64/kernel/insn.c | 4 ++++
2 files changed, 5 insertions(+)
diff --git a/arch/arm64/include/asm/insn.h b/arch/arm64/include/asm/insn.h
index 4214c38d016b..21fffdd290a3 100644
--- a/arch/arm64/include/asm/insn.h
+++ b/arch/arm64/include/asm/insn.h
@@ -70,6 +70,7 @@ enum aarch64_insn_imm_type {
AARCH64_INSN_IMM_6,
AARCH64_INSN_IMM_S,
AARCH64_INSN_IMM_R,
+ AARCH64_INSN_IMM_N,
AARCH64_INSN_IMM_MAX
};
diff --git a/arch/arm64/kernel/insn.c b/arch/arm64/kernel/insn.c
index 2718a77da165..7e432662d454 100644
--- a/arch/arm64/kernel/insn.c
+++ b/arch/arm64/kernel/insn.c
@@ -343,6 +343,10 @@ static int __kprobes aarch64_get_imm_shift_mask(enum aarch64_insn_imm_type type,
mask = BIT(6) - 1;
shift = 16;
break;
+ case AARCH64_INSN_IMM_N:
+ mask = 1;
+ shift = 22;
+ break;
default:
return -EINVAL;
}
--
2.14.2
^ permalink raw reply related
* [PATCH v2 05/19] arm64: alternatives: Add dynamic patching feature
From: Marc Zyngier @ 2017-12-11 14:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171211144937.4537-1-marc.zyngier@arm.com>
We've so far relied on a patching infrastructure that only gave us
a single alternative, without any way to finely control what gets
patched. For a single feature, this is an all or nothing thing.
It would be interesting to have a more fine grained way of patching
the kernel though, where we could dynamically tune the code that gets
injected.
In order to achive this, let's introduce a new form of alternative
that is associated with a callback. This callback gets the instruction
sequence number and the old instruction as a parameter, and returns
the new instruction. This callback is always called, as the patching
decision is now done at runtime (not patching is equivalent to returning
the same instruction).
Patching with a callback is declared with the new ALTERNATIVE_CB
and alternative_cb directives:
asm volatile(ALTERNATIVE_CB("mov %0, #0\n", callback)
: "r" (v));
or
alternative_cb callback
mov x0, #0
alternative_else_nop_endif
where callback is the C function computing the alternative.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
arch/arm64/include/asm/alternative.h | 40 ++++++++++++++++++++++--------
arch/arm64/include/asm/alternative_types.h | 3 +++
arch/arm64/kernel/alternative.c | 14 +++++++++--
3 files changed, 45 insertions(+), 12 deletions(-)
diff --git a/arch/arm64/include/asm/alternative.h b/arch/arm64/include/asm/alternative.h
index 395befde7595..ce612e10a2c9 100644
--- a/arch/arm64/include/asm/alternative.h
+++ b/arch/arm64/include/asm/alternative.h
@@ -18,10 +18,14 @@
void __init apply_alternatives_all(void);
void apply_alternatives(void *start, size_t length);
-#define ALTINSTR_ENTRY(feature) \
+#define ALTINSTR_ENTRY(feature,cb) \
" .align " __stringify(ALTINSTR_ALIGN) "\n" \
" .word 661b - .\n" /* label */ \
+ " .if " __stringify(cb) " == 0\n" \
" .word 663f - .\n" /* new instruction */ \
+ " .else\n" \
+ " .word " __stringify(cb) "- .\n" /* callback */ \
+ " .endif\n" \
" .hword " __stringify(feature) "\n" /* feature bit */ \
" .byte 662b-661b\n" /* source len */ \
" .byte 664f-663f\n" /* replacement len */
@@ -40,13 +44,13 @@ void apply_alternatives(void *start, size_t length);
* be fixed in a binutils release posterior to 2.25.51.0.2 (anything
* containing commit 4e4d08cf7399b606 or c1baaddf8861).
*/
-#define __ALTERNATIVE_CFG(oldinstr, newinstr, feature, cfg_enabled) \
+#define __ALTERNATIVE_CFG(oldinstr, newinstr, feature, cfg_enabled, cb) \
".if "__stringify(cfg_enabled)" == 1\n" \
"661:\n\t" \
oldinstr "\n" \
"662:\n" \
".pushsection .altinstructions,\"a\"\n" \
- ALTINSTR_ENTRY(feature) \
+ ALTINSTR_ENTRY(feature,cb) \
".popsection\n" \
".pushsection .altinstr_replacement, \"a\"\n" \
"663:\n\t" \
@@ -58,26 +62,32 @@ void apply_alternatives(void *start, size_t length);
".endif\n"
#define _ALTERNATIVE_CFG(oldinstr, newinstr, feature, cfg, ...) \
- __ALTERNATIVE_CFG(oldinstr, newinstr, feature, IS_ENABLED(cfg))
+ __ALTERNATIVE_CFG(oldinstr, newinstr, feature, IS_ENABLED(cfg), 0)
+#define _ALTERNATIVE_CB(oldinstr, cb, ...) \
+ __ALTERNATIVE_CFG(oldinstr, oldinstr, ARM64_NCAPS, 1, cb)
#else
#include <asm/assembler.h>
-.macro altinstruction_entry orig_offset alt_offset feature orig_len alt_len
+.macro altinstruction_entry orig_offset, alt_offset, feature, orig_len, alt_len, cb = 0
.align ALTINSTR_ALIGN
.word \orig_offset - .
+ .if \cb == 0
.word \alt_offset - .
+ .else
+ .word \cb - .
+ .endif
.hword \feature
.byte \orig_len
.byte \alt_len
.endm
-.macro alternative_insn insn1, insn2, cap, enable = 1
+.macro alternative_insn insn1, insn2, cap, enable = 1, cb = 0
.if \enable
661: \insn1
662: .pushsection .altinstructions, "a"
- altinstruction_entry 661b, 663f, \cap, 662b-661b, 664f-663f
+ altinstruction_entry 661b, 663f, \cap, 662b-661b, 664f-663f, \cb
.popsection
.pushsection .altinstr_replacement, "ax"
663: \insn2
@@ -109,10 +119,10 @@ void apply_alternatives(void *start, size_t length);
/*
* Begin an alternative code sequence.
*/
-.macro alternative_if_not cap
+.macro alternative_if_not cap, cb = 0
.set .Lasm_alt_mode, 0
.pushsection .altinstructions, "a"
- altinstruction_entry 661f, 663f, \cap, 662f-661f, 664f-663f
+ altinstruction_entry 661f, 663f, \cap, 662f-661f, 664f-663f, \cb
.popsection
661:
.endm
@@ -120,13 +130,17 @@ void apply_alternatives(void *start, size_t length);
.macro alternative_if cap
.set .Lasm_alt_mode, 1
.pushsection .altinstructions, "a"
- altinstruction_entry 663f, 661f, \cap, 664f-663f, 662f-661f
+ altinstruction_entry 663f, 661f, \cap, 664f-663f, 662f-661f, 0
.popsection
.pushsection .altinstr_replacement, "ax"
.align 2 /* So GAS knows label 661 is suitably aligned */
661:
.endm
+.macro alternative_cb cb
+ alternative_if_not ARM64_NCAPS, \cb
+.endm
+
/*
* Provide the other half of the alternative code sequence.
*/
@@ -166,6 +180,9 @@ alternative_endif
#define _ALTERNATIVE_CFG(insn1, insn2, cap, cfg, ...) \
alternative_insn insn1, insn2, cap, IS_ENABLED(cfg)
+#define _ALTERNATIVE_CB(insn1, cb, ...) \
+ alternative_insn insn1, insn1, ARM64_NCAPS, 1, cb
+
.macro user_alt, label, oldinstr, newinstr, cond
9999: alternative_insn "\oldinstr", "\newinstr", \cond
_ASM_EXTABLE 9999b, \label
@@ -242,4 +259,7 @@ alternative_endif
#define ALTERNATIVE(oldinstr, newinstr, ...) \
_ALTERNATIVE_CFG(oldinstr, newinstr, __VA_ARGS__, 1)
+#define ALTERNATIVE_CB(oldinstr, cb, ...) \
+ _ALTERNATIVE_CB(oldinstr, cb)
+
#endif /* __ASM_ALTERNATIVE_H */
diff --git a/arch/arm64/include/asm/alternative_types.h b/arch/arm64/include/asm/alternative_types.h
index 26cf76167f2d..513f3985d455 100644
--- a/arch/arm64/include/asm/alternative_types.h
+++ b/arch/arm64/include/asm/alternative_types.h
@@ -2,6 +2,9 @@
#ifndef __ASM_ALTERNATIVE_TYPES_H
#define __ASM_ALTERNATIVE_TYPES_H
+struct alt_instr;
+typedef u32 (*alternative_cb_t)(struct alt_instr *alt, int index, u32 new_insn);
+
struct alt_instr {
s32 orig_offset; /* offset to original instruction */
s32 alt_offset; /* offset to replacement instruction */
diff --git a/arch/arm64/kernel/alternative.c b/arch/arm64/kernel/alternative.c
index 6dd0a3a3e5c9..279c103ea801 100644
--- a/arch/arm64/kernel/alternative.c
+++ b/arch/arm64/kernel/alternative.c
@@ -110,12 +110,15 @@ static void __apply_alternatives(void *alt_region, bool use_linear_alias)
struct alt_instr *alt;
struct alt_region *region = alt_region;
__le32 *origptr, *replptr, *updptr;
+ alternative_cb_t alt_cb;
for (alt = region->begin; alt < region->end; alt++) {
u32 insn;
int i, nr_inst;
- if (!cpus_have_cap(alt->cpufeature))
+ /* Use ARM64_NCAPS as an unconditional patch */
+ if (alt->cpufeature != ARM64_NCAPS &&
+ !cpus_have_cap(alt->cpufeature))
continue;
BUG_ON(alt->alt_len != alt->orig_len);
@@ -124,11 +127,18 @@ static void __apply_alternatives(void *alt_region, bool use_linear_alias)
origptr = ALT_ORIG_PTR(alt);
replptr = ALT_REPL_PTR(alt);
+ alt_cb = ALT_REPL_PTR(alt);
updptr = use_linear_alias ? lm_alias(origptr) : origptr;
nr_inst = alt->alt_len / sizeof(insn);
for (i = 0; i < nr_inst; i++) {
- insn = get_alt_insn(alt, origptr + i, replptr + i);
+ if (alt->cpufeature == ARM64_NCAPS) {
+ insn = le32_to_cpu(updptr[i]);
+ insn = alt_cb(alt, i, insn);
+ } else {
+ insn = get_alt_insn(alt, origptr + i,
+ replptr + i);
+ }
updptr[i] = cpu_to_le32(insn);
}
--
2.14.2
^ permalink raw reply related
* [PATCH v2 04/19] arm64: alternatives: Enforce alignment of struct alt_instr
From: Marc Zyngier @ 2017-12-11 14:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171211144937.4537-1-marc.zyngier@arm.com>
We're playing a dangerous game with struct alt_instr, as we produce
it using assembly tricks, but parse them using the C structure.
We just assume that the respective alignments of the two will
be the same.
But as we add more fields to this structure, the alignment requirements
of the structure may change, and lead to all kind of funky bugs.
TO solve this, let's move the definition of struct alt_instr to its
own file, and use this to generate the alignment constraint from
asm-offsets.c. The various macros are then patched to take the
alignment into account.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
arch/arm64/include/asm/alternative.h | 13 +++++--------
arch/arm64/include/asm/alternative_types.h | 13 +++++++++++++
arch/arm64/kernel/asm-offsets.c | 4 ++++
3 files changed, 22 insertions(+), 8 deletions(-)
create mode 100644 arch/arm64/include/asm/alternative_types.h
diff --git a/arch/arm64/include/asm/alternative.h b/arch/arm64/include/asm/alternative.h
index 4a85c6952a22..395befde7595 100644
--- a/arch/arm64/include/asm/alternative.h
+++ b/arch/arm64/include/asm/alternative.h
@@ -2,28 +2,24 @@
#ifndef __ASM_ALTERNATIVE_H
#define __ASM_ALTERNATIVE_H
+#include <asm/asm-offsets.h>
#include <asm/cpucaps.h>
#include <asm/insn.h>
#ifndef __ASSEMBLY__
+#include <asm/alternative_types.h>
+
#include <linux/init.h>
#include <linux/types.h>
#include <linux/stddef.h>
#include <linux/stringify.h>
-struct alt_instr {
- s32 orig_offset; /* offset to original instruction */
- s32 alt_offset; /* offset to replacement instruction */
- u16 cpufeature; /* cpufeature bit set for replacement */
- u8 orig_len; /* size of original instruction(s) */
- u8 alt_len; /* size of new instruction(s), <= orig_len */
-};
-
void __init apply_alternatives_all(void);
void apply_alternatives(void *start, size_t length);
#define ALTINSTR_ENTRY(feature) \
+ " .align " __stringify(ALTINSTR_ALIGN) "\n" \
" .word 661b - .\n" /* label */ \
" .word 663f - .\n" /* new instruction */ \
" .hword " __stringify(feature) "\n" /* feature bit */ \
@@ -69,6 +65,7 @@ void apply_alternatives(void *start, size_t length);
#include <asm/assembler.h>
.macro altinstruction_entry orig_offset alt_offset feature orig_len alt_len
+ .align ALTINSTR_ALIGN
.word \orig_offset - .
.word \alt_offset - .
.hword \feature
diff --git a/arch/arm64/include/asm/alternative_types.h b/arch/arm64/include/asm/alternative_types.h
new file mode 100644
index 000000000000..26cf76167f2d
--- /dev/null
+++ b/arch/arm64/include/asm/alternative_types.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ASM_ALTERNATIVE_TYPES_H
+#define __ASM_ALTERNATIVE_TYPES_H
+
+struct alt_instr {
+ s32 orig_offset; /* offset to original instruction */
+ s32 alt_offset; /* offset to replacement instruction */
+ u16 cpufeature; /* cpufeature bit set for replacement */
+ u8 orig_len; /* size of original instruction(s) */
+ u8 alt_len; /* size of new instruction(s), <= orig_len */
+};
+
+#endif
diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
index 74b9a26a84b5..652165c8655a 100644
--- a/arch/arm64/kernel/asm-offsets.c
+++ b/arch/arm64/kernel/asm-offsets.c
@@ -25,6 +25,7 @@
#include <linux/dma-mapping.h>
#include <linux/kvm_host.h>
#include <linux/suspend.h>
+#include <asm/alternative_types.h>
#include <asm/cpufeature.h>
#include <asm/thread_info.h>
#include <asm/memory.h>
@@ -151,5 +152,8 @@ int main(void)
DEFINE(HIBERN_PBE_ADDR, offsetof(struct pbe, address));
DEFINE(HIBERN_PBE_NEXT, offsetof(struct pbe, next));
DEFINE(ARM64_FTR_SYSVAL, offsetof(struct arm64_ftr_reg, sys_val));
+ BLANK();
+ DEFINE(ALTINSTR_ALIGN, (63 - __builtin_clzl(__alignof__(struct alt_instr))));
+
return 0;
}
--
2.14.2
^ permalink raw reply related
* [PATCH v2 03/19] arm64: asm-offsets: Remove potential circular dependency
From: Marc Zyngier @ 2017-12-11 14:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171211144937.4537-1-marc.zyngier@arm.com>
So far, we've been lucky enough that none of the include files
that asm-offsets.c requires do include asm-offsets.h. This is
about to change, and would introduce a nasty circular dependency...
Let's now guard the inclusion of asm-offsets.h so that it never
gets pulled from asm-offsets.c.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
arch/arm64/include/asm/asm-offsets.h | 2 ++
arch/arm64/kernel/asm-offsets.c | 2 ++
2 files changed, 4 insertions(+)
diff --git a/arch/arm64/include/asm/asm-offsets.h b/arch/arm64/include/asm/asm-offsets.h
index d370ee36a182..ed8df3a9c95a 100644
--- a/arch/arm64/include/asm/asm-offsets.h
+++ b/arch/arm64/include/asm/asm-offsets.h
@@ -1 +1,3 @@
+#ifndef IN_ASM_OFFSET_GENERATOR
#include <generated/asm-offsets.h>
+#endif
diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
index 742887330101..74b9a26a84b5 100644
--- a/arch/arm64/kernel/asm-offsets.c
+++ b/arch/arm64/kernel/asm-offsets.c
@@ -18,6 +18,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#define IN_ASM_OFFSET_GENERATOR 1
+
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/dma-mapping.h>
--
2.14.2
^ permalink raw reply related
* [PATCH v2 02/19] arm64: asm-offsets: Remove unused definitions
From: Marc Zyngier @ 2017-12-11 14:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171211144937.4537-1-marc.zyngier@arm.com>
asm-offsets.h contains a number of definitions that are not used
at all, and in some cases conflict with other definitions (such as
NSEC_PER_SEC).
Spring clean-up time.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
arch/arm64/kernel/asm-offsets.c | 5 -----
1 file changed, 5 deletions(-)
diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
index 7e8be0c22ce0..742887330101 100644
--- a/arch/arm64/kernel/asm-offsets.c
+++ b/arch/arm64/kernel/asm-offsets.c
@@ -83,10 +83,6 @@ int main(void)
DEFINE(VMA_VM_MM, offsetof(struct vm_area_struct, vm_mm));
DEFINE(VMA_VM_FLAGS, offsetof(struct vm_area_struct, vm_flags));
BLANK();
- DEFINE(VM_EXEC, VM_EXEC);
- BLANK();
- DEFINE(PAGE_SZ, PAGE_SIZE);
- BLANK();
DEFINE(__DMA_BIDIRECTIONAL, DMA_BIDIRECTIONAL);
DEFINE(__DMA_TO_DEVICE, DMA_TO_DEVICE);
DEFINE(__DMA_FROM_DEVICE, DMA_FROM_DEVICE);
@@ -98,7 +94,6 @@ int main(void)
DEFINE(CLOCK_REALTIME_COARSE, CLOCK_REALTIME_COARSE);
DEFINE(CLOCK_MONOTONIC_COARSE,CLOCK_MONOTONIC_COARSE);
DEFINE(CLOCK_COARSE_RES, LOW_RES_NSEC);
- DEFINE(NSEC_PER_SEC, NSEC_PER_SEC);
BLANK();
DEFINE(VDSO_CS_CYCLE_LAST, offsetof(struct vdso_data, cs_cycle_last));
DEFINE(VDSO_RAW_TIME_SEC, offsetof(struct vdso_data, raw_time_sec));
--
2.14.2
^ permalink raw reply related
* [PATCH v2 01/19] arm64: asm-offsets: Avoid clashing DMA definitions
From: Marc Zyngier @ 2017-12-11 14:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171211144937.4537-1-marc.zyngier@arm.com>
asm-offsets.h contains a few DMA related definitions that have
the exact same name than the enum members they are derived from.
While this is not a problem so far, it will become an issue if
both asm-offsets.h and include/linux/dma-direction.h: are pulled
by the same file.
Let's sidestep the issue by renaming the asm-offsets.h constants.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
arch/arm64/kernel/asm-offsets.c | 6 +++---
arch/arm64/mm/cache.S | 4 ++--
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
index 71bf088f1e4b..7e8be0c22ce0 100644
--- a/arch/arm64/kernel/asm-offsets.c
+++ b/arch/arm64/kernel/asm-offsets.c
@@ -87,9 +87,9 @@ int main(void)
BLANK();
DEFINE(PAGE_SZ, PAGE_SIZE);
BLANK();
- DEFINE(DMA_BIDIRECTIONAL, DMA_BIDIRECTIONAL);
- DEFINE(DMA_TO_DEVICE, DMA_TO_DEVICE);
- DEFINE(DMA_FROM_DEVICE, DMA_FROM_DEVICE);
+ DEFINE(__DMA_BIDIRECTIONAL, DMA_BIDIRECTIONAL);
+ DEFINE(__DMA_TO_DEVICE, DMA_TO_DEVICE);
+ DEFINE(__DMA_FROM_DEVICE, DMA_FROM_DEVICE);
BLANK();
DEFINE(CLOCK_REALTIME, CLOCK_REALTIME);
DEFINE(CLOCK_MONOTONIC, CLOCK_MONOTONIC);
diff --git a/arch/arm64/mm/cache.S b/arch/arm64/mm/cache.S
index 7f1dbe962cf5..c1336be085eb 100644
--- a/arch/arm64/mm/cache.S
+++ b/arch/arm64/mm/cache.S
@@ -205,7 +205,7 @@ ENDPIPROC(__dma_flush_area)
* - dir - DMA direction
*/
ENTRY(__dma_map_area)
- cmp w2, #DMA_FROM_DEVICE
+ cmp w2, #__DMA_FROM_DEVICE
b.eq __dma_inv_area
b __dma_clean_area
ENDPIPROC(__dma_map_area)
@@ -217,7 +217,7 @@ ENDPIPROC(__dma_map_area)
* - dir - DMA direction
*/
ENTRY(__dma_unmap_area)
- cmp w2, #DMA_TO_DEVICE
+ cmp w2, #__DMA_TO_DEVICE
b.ne __dma_inv_area
ret
ENDPIPROC(__dma_unmap_area)
--
2.14.2
^ permalink raw reply related
* [PATCH v2 00/19] KVM/arm64: Randomise EL2 mappings
From: Marc Zyngier @ 2017-12-11 14:49 UTC (permalink / raw)
To: linux-arm-kernel
Whilst KVM benefits from the kernel randomisation via KASLR, there is
no additional randomisation when the kernel is running at EL1, as we
directly use a fixed offset from the linear mapping. This is not
necessarily a problem, but we could do a bit better by independently
randomizing the HYP placement.
This series proposes to randomise the offset by inserting a few random
bits between the MSB of the RAM linear mapping and the top of the HYP
VA (VA_BITS - 2). That's not a lot of random bits (on my Mustang, I
get 13 bits), but that's better than nothing.
In order to achieve this, we need to be able to patch dynamic values
in the kernel text. This results in a bunch of changes to the
alternative framework, the insn library, and a few more hacks in KVM
itself (we get a new way to map the GIC at EL2). This series used to
depend on a number of cleanups in asm-offsets, which is not the case
anymore. I'm still including them as I think they are still pretty
useful.
This has been tested on the FVP model, Seattle (both 39 and 48bit VA),
Mustang and Thunder-X. I've also done a sanity check on 32bit (which
is only impacted by the HYP IO VA stuff).
Thanks,
M.
* From v1:
- Now works correctly with KASLR
- Dropped the callback field from alt_instr, and reuse one of the
existing fields to store an offset to the callback
- Fix HYP teardown path (depends on fixes previously posted)
- Dropped the VA offset macros
Marc Zyngier (19):
arm64: asm-offsets: Avoid clashing DMA definitions
arm64: asm-offsets: Remove unused definitions
arm64: asm-offsets: Remove potential circular dependency
arm64: alternatives: Enforce alignment of struct alt_instr
arm64: alternatives: Add dynamic patching feature
arm64: insn: Add N immediate encoding
arm64: insn: Add encoder for bitwise operations using litterals
arm64: KVM: Dynamically patch the kernel/hyp VA mask
arm64: cpufeatures: Drop the ARM64_HYP_OFFSET_LOW feature flag
KVM: arm/arm64: Do not use kern_hyp_va() with kvm_vgic_global_state
KVM: arm/arm64: Demote HYP VA range display to being a debug feature
KVM: arm/arm64: Move ioremap calls to create_hyp_io_mappings
KVM: arm/arm64: Keep GICv2 HYP VAs in kvm_vgic_global_state
KVM: arm/arm64: Move HYP IO VAs to the "idmap" range
arm64; insn: Add encoder for the EXTR instruction
arm64: insn: Allow ADD/SUB (immediate) with LSL #12
arm64: KVM: Dynamically compute the HYP VA mask
arm64: KVM: Introduce EL2 VA randomisation
arm64: Update the KVM memory map documentation
Documentation/arm64/memory.txt | 8 +-
arch/arm/include/asm/kvm_hyp.h | 6 +
arch/arm/include/asm/kvm_mmu.h | 4 +-
arch/arm64/include/asm/alternative.h | 53 +++++---
arch/arm64/include/asm/alternative_types.h | 16 +++
arch/arm64/include/asm/asm-offsets.h | 2 +
arch/arm64/include/asm/cpucaps.h | 2 +-
arch/arm64/include/asm/insn.h | 16 +++
arch/arm64/include/asm/kvm_hyp.h | 9 ++
arch/arm64/include/asm/kvm_mmu.h | 54 ++++----
arch/arm64/kernel/alternative.c | 14 ++-
arch/arm64/kernel/asm-offsets.c | 17 +--
arch/arm64/kernel/cpufeature.c | 19 ---
arch/arm64/kernel/insn.c | 191 ++++++++++++++++++++++++++++-
arch/arm64/kvm/Makefile | 2 +-
arch/arm64/kvm/haslr.c | 135 ++++++++++++++++++++
arch/arm64/mm/cache.S | 4 +-
include/kvm/arm_vgic.h | 12 +-
virt/kvm/arm/hyp/vgic-v2-sr.c | 12 +-
virt/kvm/arm/mmu.c | 81 ++++++++----
virt/kvm/arm/vgic/vgic-init.c | 6 -
virt/kvm/arm/vgic/vgic-v2.c | 40 ++----
22 files changed, 542 insertions(+), 161 deletions(-)
create mode 100644 arch/arm64/include/asm/alternative_types.h
create mode 100644 arch/arm64/kvm/haslr.c
--
2.14.2
^ permalink raw reply
* [PATCH v10 1/3] ACPI/IORT: Add msi address regions reservation helper
From: Lorenzo Pieralisi @ 2017-12-11 14:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171129141449.120316-2-shameerali.kolothum.thodi@huawei.com>
On Wed, Nov 29, 2017 at 02:14:47PM +0000, Shameer Kolothum wrote:
> On some platforms msi parent address regions have to be excluded from
> normal IOVA allocation in that they are detected and decoded in a HW
> specific way by system components and so they cannot be considered normal
> IOVA address space.
>
> Add a helper function that retrieves ITS address regions - the msi
> parent - through IORT device <-> ITS mappings and reserves it so that
> these regions will not be translated by IOMMU and will be excluded from
> IOVA allocations. The function checks for the smmu model number and
> only applies the msi reservation if the platform requires it.
>
> Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> ---
> drivers/acpi/arm64/iort.c | 133 ++++++++++++++++++++++++++++++++++++++-
> drivers/irqchip/irq-gic-v3-its.c | 3 +-
> include/linux/acpi_iort.h | 7 ++-
> 3 files changed, 138 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
> index 95255ec..1c5fc36 100644
> --- a/drivers/acpi/arm64/iort.c
> +++ b/drivers/acpi/arm64/iort.c
> @@ -39,6 +39,7 @@
> struct iort_its_msi_chip {
> struct list_head list;
> struct fwnode_handle *fw_node;
> + phys_addr_t base_addr;
> u32 translation_id;
> };
>
> @@ -161,14 +162,16 @@ typedef acpi_status (*iort_find_node_callback)
> static DEFINE_SPINLOCK(iort_msi_chip_lock);
>
> /**
> - * iort_register_domain_token() - register domain token and related ITS ID
> - * to the list from where we can get it back later on.
> + * iort_register_domain_token() - register domain token along with related
> + * ITS ID and base address to the list from where we can get it back later on.
> * @trans_id: ITS ID.
> + * @base: ITS base address.
> * @fw_node: Domain token.
> *
> * Returns: 0 on success, -ENOMEM if no memory when allocating list element
> */
> -int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node)
> +int iort_register_domain_token(int trans_id, phys_addr_t base,
> + struct fwnode_handle *fw_node)
> {
> struct iort_its_msi_chip *its_msi_chip;
>
> @@ -178,6 +181,7 @@ int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node)
>
> its_msi_chip->fw_node = fw_node;
> its_msi_chip->translation_id = trans_id;
> + its_msi_chip->base_addr = base;
>
> spin_lock(&iort_msi_chip_lock);
> list_add(&its_msi_chip->list, &iort_msi_chip_list);
> @@ -581,6 +585,24 @@ int iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id)
> return -ENODEV;
> }
>
> +static int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base)
> +{
> + struct iort_its_msi_chip *its_msi_chip;
> + bool match = false;
> +
> + spin_lock(&iort_msi_chip_lock);
> + list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) {
> + if (its_msi_chip->translation_id == its_id) {
> + *base = its_msi_chip->base_addr;
> + match = true;
> + break;
> + }
> + }
> + spin_unlock(&iort_msi_chip_lock);
> +
> + return match ? 0 : -ENODEV;
Nit: if you need to return an int, use it as the "match" variable,
there is no point in using a bool.
int ret = -ENODEV;
if (its_msi_chip->translation_id == its_id) {
...
ret = 0;
break;
}
return ret;
> +}
> +
> /**
> * iort_dev_find_its_id() - Find the ITS identifier for a device
> * @dev: The device.
> @@ -740,6 +762,38 @@ static int __maybe_unused __get_pci_rid(struct pci_dev *pdev, u16 alias,
> return 0;
> }
>
> +static bool __maybe_unused iort_hw_msi_resv_enable(struct device *dev,
> + struct acpi_iort_node *node)
> +{
> + struct iort_fwnode *curr;
> + struct acpi_iort_node *iommu = NULL;
> + struct iommu_fwspec *fwspec = dev->iommu_fwspec;
> +
> + if (WARN_ON(!fwspec || !fwspec->iommu_fwnode))
Remove WARN_ON() here, it seems excessive.
> + return false;
> +
> + spin_lock(&iort_fwnode_lock);
> + list_for_each_entry(curr, &iort_fwnode_list, list) {
> + if (curr->fwnode == fwspec->iommu_fwnode) {
> + iommu = curr->iort_node;
> + break;
> + }
> + }
> + spin_unlock(&iort_fwnode_lock);
iort_get_iort_node() ?
> + if (iommu && (iommu->type == ACPI_IORT_NODE_SMMU_V3)) {
> + struct acpi_iort_smmu_v3 *smmu;
> +
> + smmu = (struct acpi_iort_smmu_v3 *)iommu->node_data;
> + if (smmu->model == ACPI_IORT_SMMU_V3_HISILICON_HI161X) {
> + dev_notice(dev, "Enabling HiSilicon erratum 161010801\n");
> + return true;
> + }
> + }
> +
> + return false;
> +}
> +
> static int arm_smmu_iort_xlate(struct device *dev, u32 streamid,
> struct fwnode_handle *fwnode,
> const struct iommu_ops *ops)
> @@ -782,6 +836,77 @@ static inline int iort_add_device_replay(const struct iommu_ops *ops,
>
> return err;
> }
> +
> +/**
> + * iort_iommu_msi_get_resv_regions - Reserved region driver helper
> + * @dev: Device from iommu_get_resv_regions()
> + * @head: Reserved region list from iommu_get_resv_regions()
> + *
> + * Returns: Number of msi reserved regions on success (0 if platform
> + * doesn't require the reservation or no associated msi regions),
> + * appropriate error value otherwise. The ITS interrupt translation
> + * space (ITS_base + 0x010000) associated with the device are the
> + * msi reserved regions.
> + */
> +int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head)
> +{
> + struct acpi_iort_its_group *its;
> + struct acpi_iort_node *node, *its_node = NULL;
> + int i, resv = 0;
> +
> + node = iort_find_dev_node(dev);
> + if (!node)
> + return -ENODEV;
You may not need node, see below.
> + if (!iort_hw_msi_resv_enable(dev, node))
node is passed but effectively not used by the function.
> + return 0;
> +
> + /*
> + * Current logic to reserve ITS regions relies on HW topologies
> + * where a given PCI or named component maps its IDs to only one
> + * ITS group; if a PCI or named component can map its IDs to
> + * different ITS groups through IORT mappings this function has
> + * to be reworked to ensure we reserve regions for all ITS groups
> + * a given PCI or named component may map IDs to.
> + */
> + if (dev_is_pci(dev)) {
> + u32 rid;
> +
> + pci_for_each_dma_alias(to_pci_dev(dev), __get_pci_rid, &rid);
> + its_node = iort_node_map_id(node, rid, NULL, IORT_MSI_TYPE);
> + } else {
> + for (i = 0; i < node->mapping_count; i++) {
> + its_node = iort_node_map_platform_id(node, NULL,
> + IORT_MSI_TYPE, i);
> + if (its_node)
> + break;
> + }
> + }
Ok. So, how about doing:
for (i = 0; i < dev->iommu_fwspec->num_ids; i++) {
its_node = iort_node_map_id(iommu_iort_node, dev->iommu_fwspec->ids[i],
NULL, IORT_MSI_TYPE);
if (its_node)
break;
}
which removes the need for dev_is_pci() check and simplifies a bit, is
there any issue with that ?
iommu_iort_node can be returned by iort_hw_msi_resv_enable(), that
you can rename accordingly.
Lorenzo
> +
> + if (!its_node)
> + return 0;
> +
> + /* Move to ITS specific data */
> + its = (struct acpi_iort_its_group *)its_node->node_data;
> +
> + for (i = 0; i < its->its_count; i++) {
> + phys_addr_t base;
> +
> + if (!iort_find_its_base(its->identifiers[i], &base)) {
> + int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
> + struct iommu_resv_region *region;
> +
> + region = iommu_alloc_resv_region(base + SZ_64K, SZ_64K,
> + prot, IOMMU_RESV_MSI);
> + if (region) {
> + list_add_tail(®ion->list, head);
> + resv++;
> + }
> + }
> + }
> +
> + return (resv == its->its_count) ? resv : -ENODEV;
> +}
> #else
> static inline const struct iommu_ops *iort_fwspec_iommu_ops(
> struct iommu_fwspec *fwspec)
> @@ -789,6 +914,8 @@ static inline const struct iommu_ops *iort_fwspec_iommu_ops(
> static inline int iort_add_device_replay(const struct iommu_ops *ops,
> struct device *dev)
> { return 0; }
> +int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head)
> +{ return -ENODEV; }
> #endif
>
> static int iort_iommu_xlate(struct device *dev, struct acpi_iort_node *node,
> diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
> index 4039e64..d4cff12 100644
> --- a/drivers/irqchip/irq-gic-v3-its.c
> +++ b/drivers/irqchip/irq-gic-v3-its.c
> @@ -3450,7 +3450,8 @@ static int __init gic_acpi_parse_madt_its(struct acpi_subtable_header *header,
> return -ENOMEM;
> }
>
> - err = iort_register_domain_token(its_entry->translation_id, dom_handle);
> + err = iort_register_domain_token(its_entry->translation_id, res.start,
> + dom_handle);
> if (err) {
> pr_err("ITS@%pa: Unable to register GICv3 ITS domain token (ITS ID %d) to IORT\n",
> &res.start, its_entry->translation_id);
> diff --git a/include/linux/acpi_iort.h b/include/linux/acpi_iort.h
> index 2f7a292..38cd77b 100644
> --- a/include/linux/acpi_iort.h
> +++ b/include/linux/acpi_iort.h
> @@ -26,7 +26,8 @@
> #define IORT_IRQ_MASK(irq) (irq & 0xffffffffULL)
> #define IORT_IRQ_TRIGGER_MASK(irq) ((irq >> 32) & 0xffffffffULL)
>
> -int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node);
> +int iort_register_domain_token(int trans_id, phys_addr_t base,
> + struct fwnode_handle *fw_node);
> void iort_deregister_domain_token(int trans_id);
> struct fwnode_handle *iort_find_domain_token(int trans_id);
> #ifdef CONFIG_ACPI_IORT
> @@ -38,6 +39,7 @@
> /* IOMMU interface */
> void iort_dma_setup(struct device *dev, u64 *dma_addr, u64 *size);
> const struct iommu_ops *iort_iommu_configure(struct device *dev);
> +int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head);
> #else
> static inline void acpi_iort_init(void) { }
> static inline u32 iort_msi_map_rid(struct device *dev, u32 req_id)
> @@ -52,6 +54,9 @@ static inline void iort_dma_setup(struct device *dev, u64 *dma_addr,
> static inline const struct iommu_ops *iort_iommu_configure(
> struct device *dev)
> { return NULL; }
> +static inline
> +int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head)
> +{ return 0; }
> #endif
>
> #endif /* __ACPI_IORT_H__ */
> --
> 1.9.1
>
>
^ permalink raw reply
* [PATCH v2 00/36] Optimize KVM/ARM for VHE systems
From: Yury Norov @ 2017-12-11 14:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171207170630.592-1-christoffer.dall@linaro.org>
Hi Christoffer,
On Thu, Dec 07, 2017 at 06:05:54PM +0100, Christoffer Dall wrote:
> This series redesigns parts of KVM/ARM to optimize the performance on
> VHE systems. The general approach is to try to do as little work as
> possible when transitioning between the VM and the hypervisor. This has
> the benefit of lower latency when waiting for interrupts and delivering
> virtual interrupts, and reduces the overhead of emulating behavior and
> I/O in the host kernel.
>
> Patches 01 through 04 are not VHE specific, but rework parts of KVM/ARM
> that can be generally improved. We then add infrastructure to move more
> logic into vcpu_load and vcpu_put, we improve handling of VFP and debug
> registers.
>
> We then introduce a new world-switch function for VHE systems, which we
> can tweak and optimize for VHE systems. To do that, we rework a lot of
> the system register save/restore handling and emulation code that may
> need access to system registers, so that we can defer as many system
> register save/restore operations to vcpu_load and vcpu_put, and move
> this logic out of the VHE world switch function.
>
> We then optimize the configuration of traps. On non-VHE systems, both
> the host and VM kernels run in EL1, but because the host kernel should
> have full access to the underlying hardware, but the VM kernel should
> not, we essentially make the host kernel more privileged than the VM
> kernel despite them both running at the same privilege level by enabling
> VE traps when entering the VM and disabling those traps when exiting the
> VM. On VHE systems, the host kernel runs in EL2 and has full access to
> the hardware (as much as allowed by secure side software), and is
> unaffected by the trap configuration. That means we can configure the
> traps for VMs running in EL1 once, and don't have to switch them on and
> off for every entry/exit to/from the VM.
>
> Finally, we improve our VGIC handling by moving all save/restore logic
> out of the VHE world-switch, and we make it possible to truly only
> evaluate if the AP list is empty and not do *any* VGIC work if that is
> the case, and only do the minimal amount of work required in the course
> of the VGIC processing when we have virtual interrupts in flight.
>
> The patches are based on v4.15-rc1 plus the fixes sent for v4.15-rc3
> [1], the level-triggered mapped interrupts support series [2], and the
> first five patches of James' SDEI series [3], a single SVE patch that
> moves the CPU ID reg trap setup out of the world-switch path, and v3 of
> my vcpu load/put series [4].
>
> I've given the patches a fair amount of testing on Thunder-X, Mustang,
> Seattle, and TC2 (32-bit) for non-VHE testing, and tested VHE
> functionality on the Foundation model, running both 64-bit VMs and
> 32-bit VMs side-by-side and using both GICv3-on-GICv3 and
> GICv2-on-GICv3.
>
> The patches are also available in the vhe-optimize-v2 branch on my
> kernel.org repository [5].
>
> Changes since v1:
> - Rebased on v4.15-rc1 and newer versions of other dependencies,
> including the vcpu load/put approach taken for KVM.
> - Addressed review comments from v1 (detailed changelogs are in the
> individual patches).
>
> Thanks,
> -Christoffer
>
> [1]: git://git.kernel.org/pub/scm/linux/kernel/git/kvmarm/kvmarm kvm-arm-fixes-for-v4.15-1
> [2]: git://git.kernel.org/pub/scm/linux/kernel/git/cdall/linux.git level-mapped-v6
> [3]: git://linux-arm.org/linux-jm.git sdei/v5/base
> [4]: git://git.kernel.org/pub/scm/linux/kernel/git/cdall/linux.git vcpu-load-put-v3
> [5]: git://git.kernel.org/pub/scm/linux/kernel/git/cdall/linux.git vhe-optimize-v2
I just submitted the benchmark I used to test your v1 and v2 series':
https://lkml.org/lkml/2017/12/11/364
On ThunderX2, 112 online CPUs test results for v1 are like this:
Host, v4.14:
Dry-run: 0 1
Self-IPI: 9 18
Normal IPI: 81 110
Broadcast IPI: 0 2106
Guest, v4.14:
Dry-run: 0 1
Self-IPI: 10 18
Normal IPI: 305 525
Broadcast IPI: 0 9729
Guest, v4.14 + VHE:
Dry-run: 0 1
Self-IPI: 9 18
Normal IPI: 176 343
Broadcast IPI: 0 9885
And for v2.
Host, v4.15:
Dry-run: 0 1
Self-IPI: 9 18
Normal IPI: 79 108
Broadcast IPI: 0 2102
Guest, v4.15-rc:
Dry-run: 0 1
Self-IPI: 9 18
Normal IPI: 291 526
Broadcast IPI: 0 10439
Guest, v4.15-rc + VHE:
Dry-run: 0 2
Self-IPI: 14 28
Normal IPI: 370 569
Broadcast IPI: 0 11688
All times are normalized to v1 host dry-run time. Smaller - better.
Results for v1 and v2 may vary because kernel version is changed.
What makes us worry is slowing down the "Normal IPI" test observed in
v2 series.
Nevertheless, if you find test relevant, for v1 and v2,
Tested-by: Yury Norov <ynorov@caviumnetworks.com>
^ permalink raw reply
* [PATCH 0/2] acpi, x86: Add SPCR table support
From: Ingo Molnar @ 2017-12-11 14:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <f078ada7-1e30-41cc-28dc-077ed2a5986d@redhat.com>
* Prarit Bhargava <prarit@redhat.com> wrote:
> If I disable "Serial Port Console Debug" in my BIOS I still see the SPCR configured:
>
> [root at prarit-lab ~]# dmesg | grep SPCR
> [ 0.000000] ACPI: SPCR 0x0000000069031000 000050 (v01
> 00000000 00000000)
>
> AFAICT the SPCR is always enabled on some systems.
Fair enough.
> > If so then we should pick up that serial console configuration and activate it,
> > regardless of any kernel boot options!
>
> I'm worried about someone who doesn't want a console on ttyS0 suddenly ending up
> with one. The SPCR could contain incorrect data, etc.
Yeah, that's not good.
Thanks,
Ingo
^ permalink raw reply
* [PATCH net-next v5 2/2] net: ethernet: socionext: add AVE ethernet driver
From: David Miller @ 2017-12-11 14:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1512979049-15930-3-git-send-email-hayashi.kunihiko@socionext.com>
From: Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
Date: Mon, 11 Dec 2017 16:57:29 +0900
> +static inline u32 ave_desc_read(struct net_device *ndev, enum desc_id id,
> + int entry, int offset)
Do not use inline functions in foo.c files, let the compiler decide what
to do.
Thank you.
^ permalink raw reply
* [PATCH] IPI performance benchmark
From: Christian Borntraeger @ 2017-12-11 14:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171211141600.24401-1-ynorov@caviumnetworks.com>
On 12/11/2017 03:16 PM, Yury Norov wrote:
> This benchmark sends many IPIs in different modes and measures
> time for IPI delivery (first column), and total time, ie including
> time to acknowledge the receive by sender (second column).
>
> The scenarios are:
> Dry-run: do everything except actually sending IPI. Useful
> to estimate system overhead.
> Self-IPI: Send IPI to self CPU.
> Normal IPI: Send IPI to some other CPU.
> Broadcast IPI: Send broadcast IPI to all online CPUs.
>
> For virtualized guests, sending and reveiving IPIs causes guest exit.
> I used this test to measure performance impact on KVM subsystem of
> Christoffer Dall's series "Optimize KVM/ARM for VHE systems".
>
> https://www.spinics.net/lists/kvm/msg156755.html
>
> Test machine is ThunderX2, 112 online CPUs. Below the results normalized
> to host dry-run time. Smaller - better.
>
> Host, v4.14:
> Dry-run: 0 1
> Self-IPI: 9 18
> Normal IPI: 81 110
> Broadcast IPI: 0 2106
>
> Guest, v4.14:
> Dry-run: 0 1
> Self-IPI: 10 18
> Normal IPI: 305 525
> Broadcast IPI: 0 9729
>
> Guest, v4.14 + VHE:
> Dry-run: 0 1
> Self-IPI: 9 18
> Normal IPI: 176 343
> Broadcast IPI: 0 9885
>
> CC: Andrew Morton <akpm@linux-foundation.org>
> CC: Ashish Kalra <Ashish.Kalra@cavium.com>
> CC: Christoffer Dall <christoffer.dall@linaro.org>
> CC: Geert Uytterhoeven <geert@linux-m68k.org>
> CC: Linu Cherian <Linu.Cherian@cavium.com>
> CC: Sunil Goutham <Sunil.Goutham@cavium.com>
> Signed-off-by: Yury Norov <ynorov@caviumnetworks.com>
> ---
> arch/Kconfig | 10 ++++
> kernel/Makefile | 1 +
> kernel/ipi_benchmark.c | 134 +++++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 145 insertions(+)
> create mode 100644 kernel/ipi_benchmark.c
> diff --git a/arch/Kconfig b/arch/Kconfig
> index 057370a0ac4e..80d6ef439199 100644
> --- a/arch/Kconfig
> +++ b/arch/Kconfig
> @@ -82,6 +82,16 @@ config JUMP_LABEL
> ( On 32-bit x86, the necessary options added to the compiler
> flags may increase the size of the kernel slightly. )
>
> +config IPI_BENCHMARK
> + tristate "Test IPI performance on SMP systems"
> + depends on SMP
> + help
> + Test IPI performance on SMP systems. If system has only one online
> + CPU, sending IPI to other CPU is obviously not possible, and ENOENT
> + is returned for corresponding test.
> +
> + If unsure, say N.
> +
> config STATIC_KEYS_SELFTEST
> bool "Static key selftest"
> depends on JUMP_LABEL
> diff --git a/kernel/Makefile b/kernel/Makefile
> index 172d151d429c..04e550e1990c 100644
> --- a/kernel/Makefile
> +++ b/kernel/Makefile
> @@ -101,6 +101,7 @@ obj-$(CONFIG_TRACEPOINTS) += trace/
> obj-$(CONFIG_IRQ_WORK) += irq_work.o
> obj-$(CONFIG_CPU_PM) += cpu_pm.o
> obj-$(CONFIG_BPF) += bpf/
> +obj-$(CONFIG_IPI_BENCHMARK) += ipi_benchmark.o
>
> obj-$(CONFIG_PERF_EVENTS) += events/
>
> diff --git a/kernel/ipi_benchmark.c b/kernel/ipi_benchmark.c
> new file mode 100644
> index 000000000000..35f1f7598c36
> --- /dev/null
> +++ b/kernel/ipi_benchmark.c
> @@ -0,0 +1,134 @@
> +/*
> + * Performance test for IPI on SMP machines.
> + *
> + * Copyright (c) 2017 Cavium Networks.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of version 2 of the GNU General Public
> + * License as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * General Public License for more details.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/ktime.h>
> +
> +#define NTIMES 100000
> +
> +#define POKE_ANY 0
> +#define DRY_RUN 1
> +#define POKE_SELF 2
> +#define POKE_ALL 3
> +
> +static void __init handle_ipi(void *t)
> +{
> + ktime_t *time = (ktime_t *) t;
> +
> + if (time)
> + *time = ktime_get() - *time;
> +}
> +
> +static ktime_t __init send_ipi(int flags)
> +{
> + ktime_t time;
> + unsigned int cpu = get_cpu();
> +
> + switch (flags) {
> + case POKE_ALL:
> + /* If broadcasting, don't force all CPUs to update time. */
> + smp_call_function_many(cpu_online_mask, handle_ipi, NULL, 1);
> + /* Fall thru */
> + case DRY_RUN:
> + /* Do everything except actually sending IPI. */
> + time = 0;
> + break;
> + case POKE_ANY:
> + cpu = cpumask_any_but(cpu_online_mask, cpu);
> + if (cpu >= nr_cpu_ids) {
> + time = -ENOENT;
> + break;
> + }
> + /* Fall thru */
> + case POKE_SELF:
> + time = ktime_get();
> + smp_call_function_single(cpu, handle_ipi, &time, 1);
> + break;
> + default:
> + time = -EINVAL;
> + }
> +
> + put_cpu();
> + return time;
> +}
> +
> +static int __init __bench_ipi(unsigned long i, ktime_t *time, int flags)
> +{
> + ktime_t t;
> +
> + *time = 0;
> + while (i--) {
> + t = send_ipi(flags);
> + if ((int) t < 0)
> + return (int) t;
> +
> + *time += t;
> + }
> +
> + return 0;
> +}
> +
> +static int __init bench_ipi(unsigned long times, int flags,
> + ktime_t *ipi, ktime_t *total)
> +{
> + int ret;
> +
> + *total = ktime_get();
> + ret = __bench_ipi(times, ipi, flags);
> + if (unlikely(ret))
> + return ret;
> +
> + *total = ktime_get() - *total;
> +
> + return 0;
> +}
> +
> +static int __init init_bench_ipi(void)
> +{
> + ktime_t ipi, total;
> + int ret;
> +
> + ret = bench_ipi(NTIMES, DRY_RUN, &ipi, &total);
> + if (ret)
> + pr_err("Dry-run FAILED: %d\n", ret);
> + else
> + pr_err("Dry-run: %18llu, %18llu ns\n", ipi, total);
you do not use NTIMES here to calculate the average value. Is that intended?
^ permalink raw reply
* [PATCH net-next v5 0/2] net: thunderx: add support for PTP clock
From: Philippe Ombredanne @ 2017-12-11 14:33 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171211141435.2915-1-aleksey.makarov@cavium.com>
Aleksey,
On Mon, Dec 11, 2017 at 3:14 PM, Aleksey Makarov
<aleksey.makarov@cavium.com> wrote:
> This series adds support for IEEE 1588 Precision Time Protocol
> to Cavium ethernet driver.
>
> The first patch adds support for the Precision Time Protocol Clocks and
> Timestamping coprocessor (PTP) found on Cavium processors.
> It registers a new PTP clock in the PTP core and provides functions
> to use the counter in BGX, TNS, GTI, and NIC blocks.
>
> The second patch introduces support for the PTP protocol to the
> Cavium ThunderX ethernet driver.
>
> v5:
> - fix the file headers (add SPDX tags, remove advertisment) (Philippe Ombredanne)
Thank you.
Acked-by: Philippe Ombredanne <pombredanne@nexb.com>
--
Cordially
Philippe Ombredanne
^ permalink raw reply
* [PATCH] crypto: stm32: fix modular build
From: Fabien DESSENNE @ 2017-12-11 14:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171211114752.3637710-1-arnd@arndb.de>
Hi Arnd,
This issue was reported a few days ago, and Herbert applied the patch 2
minutes before you send the mail (see
[https://www.mail-archive.com/linux-crypto at vger.kernel.org/msg29762.html]).
Anyway, thank you for reporting this.
Fabien
On 11/12/17 12:47, Arnd Bergmann wrote:
> Building the stm32 crypto suport as a loadable module causes a build
> failure from a simple typo:
>
> drivers/crypto/stm32/stm32-cryp.c:1035:25: error: 'sti_dt_ids' undeclared here (not in a function); did you mean 'stm32_dt_ids'?
>
> This renames the reference to point to the correct symbol.
>
> Fixes: 9e054ec21ef8 ("crypto: stm32 - Support for STM32 CRYP crypto module")
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
> ---
> drivers/crypto/stm32/stm32-cryp.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/crypto/stm32/stm32-cryp.c b/drivers/crypto/stm32/stm32-cryp.c
> index 459755940632..cf1dddbeaa2c 100644
> --- a/drivers/crypto/stm32/stm32-cryp.c
> +++ b/drivers/crypto/stm32/stm32-cryp.c
> @@ -1032,7 +1032,7 @@ static const struct of_device_id stm32_dt_ids[] = {
> { .compatible = "st,stm32f756-cryp", },
> {},
> };
> -MODULE_DEVICE_TABLE(of, sti_dt_ids);
> +MODULE_DEVICE_TABLE(of, stm32_dt_ids);
>
> static int stm32_cryp_probe(struct platform_device *pdev)
> {
^ permalink raw reply
* [PATCH 1/6] ARM: stm32: prepare stm32 family to welcome armv7 architecture
From: Ludovic BARRE @ 2017-12-11 14:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAK8P3a2rrpFuwET8r1H0YWVABbCZr5c2ySrKCgA4mfoZPfWp6Q@mail.gmail.com>
On 12/11/2017 02:40 PM, Arnd Bergmann wrote:
> On Mon, Dec 11, 2017 at 11:25 AM, Linus Walleij
> <linus.walleij@linaro.org> wrote:
>> On Fri, Dec 8, 2017 at 3:11 PM, Ludovic Barre <ludovic.Barre@st.com> wrote:
>>
>>> From: Ludovic Barre <ludovic.barre@st.com>
>>>
>>> This patch prepares the STM32 machine for the integration of Cortex-A
>>> based microprocessor (MPU), on top of the existing Cortex-M
>>> microcontroller family (MCU). Since both MCUs and MPUs are sharing
>>> common hardware blocks we can keep using ARCH_STM32 flag for most of
>>> them. If a hardware block is specific to one family we can use either
>>> ARCH_STM32_MCU or ARCH_STM32_MPU flag.
>>>
>>> Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
>
> To what degree do we need to treat them as separate families
> at all then? I wonder if the MCU/MPU distinction is always that
> clear along the Cortex-M/Cortex-A separation, especially if
> we ever get to a chip that has both types of cores. What
> exactly would we miss if we do away with the ARCH_STM32_MCU
> symbol here?
This patch series extends the existing STM32 microcontrollers (MCUs)
family to microprocessors (MPUs). Now, ARCH_STM32 groups STM32 chips
with Cortex-M or Cortex-A cores. But each core has different
infrastructure mpu vs mmu; nvic vs gic; systick vs arch_timer ...
So, ARCH_STM32_MCU/ARCH_STM32_MPU allow to define these specific blocks.
br
Ludo
>
>> So yesterdays application processors are todays MCU processors.
>>
>> I said this on a lecture for control systems a while back and
>> stated it as a reason I think RTOSes are not really seeing a bright
>> future compared to Linux.
>>
>> It happened quicker than I thought though, interesting.
>
> I think there is still lots of room for smaller RTOS in the long run,
> but it's likely that the 'MPU + external DRAM' design point will
> shift further to Linux, as there isn't really a benefit in squeezing
> in anything smaller when the minimum is 32MB or 128MB of
> RAM, depending on the interface.
>
> For on-chip eDRAM or SRAM based MPUs, that doesn't hold
> true, the memory size is what drives the cost here.
>
> Arnd
>
^ permalink raw reply
* [PATCH v2 28/36] KVM: arm64: Configure FPSIMD traps on vcpu load/put for VHE
From: Marc Zyngier @ 2017-12-11 14:18 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171207170630.592-29-christoffer.dall@linaro.org>
On 07/12/17 17:06, Christoffer Dall wrote:
> There is no need to enable/disable traps to FP registers on every switch
> to/from the VM, because the host kernel does not use this resource
> without calling vcpu_put. We can therefore move things around enough
> that we still always write FPEXC32_EL2 before programming CPTR_EL2 but
> only program these during vcpu load/put.
Same as the 32bit registers. I don't see the point in limiting this to
be VHE only. Or am I missing something?
Thanks,
M.
>
> Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
> ---
> arch/arm64/include/asm/kvm_hyp.h | 3 +++
> arch/arm64/kvm/hyp/switch.c | 34 ++++++++++++++++++++++++----------
> arch/arm64/kvm/hyp/sysreg-sr.c | 4 ++++
> 3 files changed, 31 insertions(+), 10 deletions(-)
>
> diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h
> index 3f54c55f77a1..28d5f3cb4001 100644
> --- a/arch/arm64/include/asm/kvm_hyp.h
> +++ b/arch/arm64/include/asm/kvm_hyp.h
> @@ -148,6 +148,9 @@ void __fpsimd_save_state(struct user_fpsimd_state *fp_regs);
> void __fpsimd_restore_state(struct user_fpsimd_state *fp_regs);
> bool __fpsimd_enabled(void);
>
> +void activate_traps_vhe_load(struct kvm_vcpu *vcpu);
> +void deactivate_traps_vhe_put(void);
> +
> u64 __guest_enter(struct kvm_vcpu *vcpu, struct kvm_cpu_context *host_ctxt);
> void __noreturn __hyp_do_panic(unsigned long, ...);
>
> diff --git a/arch/arm64/kvm/hyp/switch.c b/arch/arm64/kvm/hyp/switch.c
> index c01bcfc3fb52..44aae69a7fec 100644
> --- a/arch/arm64/kvm/hyp/switch.c
> +++ b/arch/arm64/kvm/hyp/switch.c
> @@ -24,22 +24,25 @@
> #include <asm/fpsimd.h>
> #include <asm/debug-monitors.h>
>
> -static void __hyp_text __activate_traps_common(struct kvm_vcpu *vcpu)
> +static void __hyp_text __activate_traps_fpsimd32(struct kvm_vcpu *vcpu)
> {
> /*
> - * We are about to set CPTR_EL2.TFP to trap all floating point
> - * register accesses to EL2, however, the ARM ARM clearly states that
> - * traps are only taken to EL2 if the operation would not otherwise
> - * trap to EL1. Therefore, always make sure that for 32-bit guests,
> - * we set FPEXC.EN to prevent traps to EL1, when setting the TFP bit.
> - * If FP/ASIMD is not implemented, FPEXC is UNDEFINED and any access to
> - * it will cause an exception.
> + * We are about to trap all floating point register accesses to EL2,
> + * however, traps are only taken to EL2 if the operation would not
> + * otherwise trap to EL1. Therefore, always make sure that for 32-bit
> + * guests, we set FPEXC.EN to prevent traps to EL1, when setting the
> + * TFP bit. If FP/ASIMD is not implemented, FPEXC is UNDEFINED and
> + * any access to it will cause an exception.
> */
> if (vcpu_el1_is_32bit(vcpu) && system_supports_fpsimd() &&
> !vcpu->arch.guest_vfp_loaded) {
> write_sysreg(1 << 30, fpexc32_el2);
> isb();
> }
> +}
> +
> +static void __hyp_text __activate_traps_common(struct kvm_vcpu *vcpu)
> +{
> write_sysreg(vcpu->arch.hcr_el2, hcr_el2);
>
> /* Trap on AArch32 cp15 c15 (impdef sysregs) accesses (EL1 or EL0) */
> @@ -61,10 +64,12 @@ static void __hyp_text __deactivate_traps_common(void)
> write_sysreg(0, pmuserenr_el0);
> }
>
> -static void __hyp_text __activate_traps_vhe(struct kvm_vcpu *vcpu)
> +void activate_traps_vhe_load(struct kvm_vcpu *vcpu)
> {
> u64 val;
>
> + __activate_traps_fpsimd32(vcpu);
> +
> val = read_sysreg(cpacr_el1);
> val |= CPACR_EL1_TTA;
> val &= ~CPACR_EL1_ZEN;
> @@ -73,7 +78,15 @@ static void __hyp_text __activate_traps_vhe(struct kvm_vcpu *vcpu)
> else
> val &= ~CPACR_EL1_FPEN;
> write_sysreg(val, cpacr_el1);
> +}
>
> +void deactivate_traps_vhe_put(void)
> +{
> + write_sysreg(CPACR_EL1_DEFAULT, cpacr_el1);
> +}
> +
> +static void __hyp_text __activate_traps_vhe(struct kvm_vcpu *vcpu)
> +{
> write_sysreg(__kvm_hyp_vector, vbar_el1);
> }
>
> @@ -81,6 +94,8 @@ static void __hyp_text __activate_traps_nvhe(struct kvm_vcpu *vcpu)
> {
> u64 val;
>
> + __activate_traps_fpsimd32(vcpu);
> +
> val = CPTR_EL2_DEFAULT;
> val |= CPTR_EL2_TTA | CPTR_EL2_TZ;
> if (vcpu->arch.guest_vfp_loaded)
> @@ -111,7 +126,6 @@ static void __hyp_text __deactivate_traps_vhe(void)
>
> write_sysreg(mdcr_el2, mdcr_el2);
> write_sysreg(HCR_HOST_VHE_FLAGS, hcr_el2);
> - write_sysreg(CPACR_EL1_DEFAULT, cpacr_el1);
> write_sysreg(vectors, vbar_el1);
> }
>
> diff --git a/arch/arm64/kvm/hyp/sysreg-sr.c b/arch/arm64/kvm/hyp/sysreg-sr.c
> index 65abf1aeba59..b647fea93fdc 100644
> --- a/arch/arm64/kvm/hyp/sysreg-sr.c
> +++ b/arch/arm64/kvm/hyp/sysreg-sr.c
> @@ -254,6 +254,8 @@ void kvm_vcpu_load_sysregs(struct kvm_vcpu *vcpu)
> __sysreg_restore_el1_state(guest_ctxt);
>
> vcpu->arch.sysregs_loaded_on_cpu = true;
> +
> + activate_traps_vhe_load(vcpu);
> }
>
> /**
> @@ -286,6 +288,8 @@ void kvm_vcpu_put_sysregs(struct kvm_vcpu *vcpu)
> if (!has_vhe())
> return;
>
> + deactivate_traps_vhe_put();
> +
> __sysreg_save_el1_state(guest_ctxt);
> __sysreg_save_user_state(guest_ctxt);
> __sysreg32_save_state(vcpu);
>
--
Jazz is not dead. It just smells funny...
^ permalink raw reply
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