Skip to content

Commit e8c2d99

Browse files
jaustinchazy
authored andcommitted
KVM: ARM: Add support for Cortex-A7
This patch adds support for running Cortex-A7 guests on Cortex-A7 hosts. As Cortex-A7 is architecturally compatible with A15, this patch is largely just generalising existing code. Areas where 'implementation defined' behaviour is identical for A7 and A15 is moved to allow it to be used by both cores. The check to ensure that coprocessor register tables are sorted correctly is also moved in to 'common' code to avoid each new cpu doing its own check (and possibly forgetting to do so!) Signed-off-by: Jonathan Austin <jonathan.austin@arm.com> Acked-by: Marc Zyngier <marc.zyngier@arm.com> Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
1 parent 5e49704 commit e8c2d99

File tree

8 files changed

+184
-124
lines changed

8 files changed

+184
-124
lines changed

arch/arm/include/asm/kvm_asm.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
#define c6_IFAR 17 /* Instruction Fault Address Register */
4040
#define c7_PAR 18 /* Physical Address Register */
4141
#define c7_PAR_high 19 /* PAR top 32 bits */
42-
#define c9_L2CTLR 20 /* Cortex A15 L2 Control Register */
42+
#define c9_L2CTLR 20 /* Cortex A15/A7 L2 Control Register */
4343
#define c10_PRRR 21 /* Primary Region Remap Register */
4444
#define c10_NMRR 22 /* Normal Memory Remap Register */
4545
#define c12_VBAR 23 /* Vector Base Address Register */

arch/arm/include/uapi/asm/kvm.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ struct kvm_regs {
6363

6464
/* Supported Processor Types */
6565
#define KVM_ARM_TARGET_CORTEX_A15 0
66-
#define KVM_ARM_NUM_TARGETS 1
66+
#define KVM_ARM_TARGET_CORTEX_A7 1
67+
#define KVM_ARM_NUM_TARGETS 2
6768

6869
/* KVM_ARM_SET_DEVICE_ADDR ioctl id encoding */
6970
#define KVM_ARM_DEVICE_TYPE_SHIFT 0

arch/arm/kvm/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@ kvm-arm-y = $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o
1919

2020
obj-y += kvm-arm.o init.o interrupts.o
2121
obj-y += arm.o handle_exit.o guest.o mmu.o emulate.o reset.o
22-
obj-y += coproc.o coproc_a15.o mmio.o psci.o perf.o
22+
obj-y += coproc.o coproc_a15.o coproc_a7.o mmio.o psci.o perf.o
2323
obj-$(CONFIG_KVM_ARM_VGIC) += $(KVM)/arm/vgic.o
2424
obj-$(CONFIG_KVM_ARM_TIMER) += $(KVM)/arm/arch_timer.o

arch/arm/kvm/coproc.c

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,92 @@ int kvm_handle_cp14_access(struct kvm_vcpu *vcpu, struct kvm_run *run)
7171
return 1;
7272
}
7373

74+
static void reset_mpidr(struct kvm_vcpu *vcpu, const struct coproc_reg *r)
75+
{
76+
/*
77+
* Compute guest MPIDR. No need to mess around with different clusters
78+
* but we read the 'U' bit from the underlying hardware directly.
79+
*/
80+
vcpu->arch.cp15[c0_MPIDR] = (read_cpuid_mpidr() & MPIDR_SMP_BITMASK)
81+
| vcpu->vcpu_id;
82+
}
83+
84+
/* TRM entries A7:4.3.31 A15:4.3.28 - RO WI */
85+
static bool access_actlr(struct kvm_vcpu *vcpu,
86+
const struct coproc_params *p,
87+
const struct coproc_reg *r)
88+
{
89+
if (p->is_write)
90+
return ignore_write(vcpu, p);
91+
92+
*vcpu_reg(vcpu, p->Rt1) = vcpu->arch.cp15[c1_ACTLR];
93+
return true;
94+
}
95+
96+
/* TRM entries A7:4.3.56, A15:4.3.60 - R/O. */
97+
static bool access_cbar(struct kvm_vcpu *vcpu,
98+
const struct coproc_params *p,
99+
const struct coproc_reg *r)
100+
{
101+
if (p->is_write)
102+
return write_to_read_only(vcpu, p);
103+
return read_zero(vcpu, p);
104+
}
105+
106+
/* TRM entries A7:4.3.49, A15:4.3.48 - R/O WI */
107+
static bool access_l2ctlr(struct kvm_vcpu *vcpu,
108+
const struct coproc_params *p,
109+
const struct coproc_reg *r)
110+
{
111+
if (p->is_write)
112+
return ignore_write(vcpu, p);
113+
114+
*vcpu_reg(vcpu, p->Rt1) = vcpu->arch.cp15[c9_L2CTLR];
115+
return true;
116+
}
117+
118+
static void reset_l2ctlr(struct kvm_vcpu *vcpu, const struct coproc_reg *r)
119+
{
120+
u32 l2ctlr, ncores;
121+
122+
asm volatile("mrc p15, 1, %0, c9, c0, 2\n" : "=r" (l2ctlr));
123+
l2ctlr &= ~(3 << 24);
124+
ncores = atomic_read(&vcpu->kvm->online_vcpus) - 1;
125+
l2ctlr |= (ncores & 3) << 24;
126+
127+
vcpu->arch.cp15[c9_L2CTLR] = l2ctlr;
128+
}
129+
130+
static void reset_actlr(struct kvm_vcpu *vcpu, const struct coproc_reg *r)
131+
{
132+
u32 actlr;
133+
134+
/* ACTLR contains SMP bit: make sure you create all cpus first! */
135+
asm volatile("mrc p15, 0, %0, c1, c0, 1\n" : "=r" (actlr));
136+
/* Make the SMP bit consistent with the guest configuration */
137+
if (atomic_read(&vcpu->kvm->online_vcpus) > 1)
138+
actlr |= 1U << 6;
139+
else
140+
actlr &= ~(1U << 6);
141+
142+
vcpu->arch.cp15[c1_ACTLR] = actlr;
143+
}
144+
145+
/*
146+
* TRM entries: A7:4.3.50, A15:4.3.49
147+
* R/O WI (even if NSACR.NS_L2ERR, a write of 1 is ignored).
148+
*/
149+
static bool access_l2ectlr(struct kvm_vcpu *vcpu,
150+
const struct coproc_params *p,
151+
const struct coproc_reg *r)
152+
{
153+
if (p->is_write)
154+
return ignore_write(vcpu, p);
155+
156+
*vcpu_reg(vcpu, p->Rt1) = 0;
157+
return true;
158+
}
159+
74160
/* See note at ARM ARM B1.14.4 */
75161
static bool access_dcsw(struct kvm_vcpu *vcpu,
76162
const struct coproc_params *p,
@@ -153,10 +239,22 @@ static bool pm_fake(struct kvm_vcpu *vcpu,
153239
* registers preceding 32-bit ones.
154240
*/
155241
static const struct coproc_reg cp15_regs[] = {
242+
/* MPIDR: we use VMPIDR for guest access. */
243+
{ CRn( 0), CRm( 0), Op1( 0), Op2( 5), is32,
244+
NULL, reset_mpidr, c0_MPIDR },
245+
156246
/* CSSELR: swapped by interrupt.S. */
157247
{ CRn( 0), CRm( 0), Op1( 2), Op2( 0), is32,
158248
NULL, reset_unknown, c0_CSSELR },
159249

250+
/* ACTLR: trapped by HCR.TAC bit. */
251+
{ CRn( 1), CRm( 0), Op1( 0), Op2( 1), is32,
252+
access_actlr, reset_actlr, c1_ACTLR },
253+
254+
/* CPACR: swapped by interrupt.S. */
255+
{ CRn( 1), CRm( 0), Op1( 0), Op2( 2), is32,
256+
NULL, reset_val, c1_CPACR, 0x00000000 },
257+
160258
/* TTBR0/TTBR1: swapped by interrupt.S. */
161259
{ CRm64( 2), Op1( 0), is64, NULL, reset_unknown64, c2_TTBR0 },
162260
{ CRm64( 2), Op1( 1), is64, NULL, reset_unknown64, c2_TTBR1 },
@@ -194,6 +292,13 @@ static const struct coproc_reg cp15_regs[] = {
194292
{ CRn( 7), CRm( 6), Op1( 0), Op2( 2), is32, access_dcsw},
195293
{ CRn( 7), CRm(10), Op1( 0), Op2( 2), is32, access_dcsw},
196294
{ CRn( 7), CRm(14), Op1( 0), Op2( 2), is32, access_dcsw},
295+
/*
296+
* L2CTLR access (guest wants to know #CPUs).
297+
*/
298+
{ CRn( 9), CRm( 0), Op1( 1), Op2( 2), is32,
299+
access_l2ctlr, reset_l2ctlr, c9_L2CTLR },
300+
{ CRn( 9), CRm( 0), Op1( 1), Op2( 3), is32, access_l2ectlr},
301+
197302
/*
198303
* Dummy performance monitor implementation.
199304
*/
@@ -234,13 +339,22 @@ static const struct coproc_reg cp15_regs[] = {
234339
/* CNTKCTL: swapped by interrupt.S. */
235340
{ CRn(14), CRm( 1), Op1( 0), Op2( 0), is32,
236341
NULL, reset_val, c14_CNTKCTL, 0x00000000 },
342+
343+
/* The Configuration Base Address Register. */
344+
{ CRn(15), CRm( 0), Op1( 4), Op2( 0), is32, access_cbar},
237345
};
238346

239347
/* Target specific emulation tables */
240348
static struct kvm_coproc_target_table *target_tables[KVM_ARM_NUM_TARGETS];
241349

242350
void kvm_register_target_coproc_table(struct kvm_coproc_target_table *table)
243351
{
352+
unsigned int i;
353+
354+
for (i = 1; i < table->num; i++)
355+
BUG_ON(cmp_reg(&table->table[i-1],
356+
&table->table[i]) >= 0);
357+
244358
target_tables[table->target] = table;
245359
}
246360

arch/arm/kvm/coproc_a15.c

Lines changed: 1 addition & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -17,98 +17,12 @@
1717
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
1818
*/
1919
#include <linux/kvm_host.h>
20-
#include <asm/cputype.h>
21-
#include <asm/kvm_arm.h>
22-
#include <asm/kvm_host.h>
23-
#include <asm/kvm_emulate.h>
2420
#include <asm/kvm_coproc.h>
21+
#include <asm/kvm_emulate.h>
2522
#include <linux/init.h>
2623

27-
static void reset_mpidr(struct kvm_vcpu *vcpu, const struct coproc_reg *r)
28-
{
29-
/*
30-
* Compute guest MPIDR. No need to mess around with different clusters
31-
* but we read the 'U' bit from the underlying hardware directly.
32-
*/
33-
vcpu->arch.cp15[c0_MPIDR] = (read_cpuid_mpidr() & MPIDR_SMP_BITMASK)
34-
| vcpu->vcpu_id;
35-
}
36-
3724
#include "coproc.h"
3825

39-
/* A15 TRM 4.3.28: RO WI */
40-
static bool access_actlr(struct kvm_vcpu *vcpu,
41-
const struct coproc_params *p,
42-
const struct coproc_reg *r)
43-
{
44-
if (p->is_write)
45-
return ignore_write(vcpu, p);
46-
47-
*vcpu_reg(vcpu, p->Rt1) = vcpu->arch.cp15[c1_ACTLR];
48-
return true;
49-
}
50-
51-
/* A15 TRM 4.3.60: R/O. */
52-
static bool access_cbar(struct kvm_vcpu *vcpu,
53-
const struct coproc_params *p,
54-
const struct coproc_reg *r)
55-
{
56-
if (p->is_write)
57-
return write_to_read_only(vcpu, p);
58-
return read_zero(vcpu, p);
59-
}
60-
61-
/* A15 TRM 4.3.48: R/O WI. */
62-
static bool access_l2ctlr(struct kvm_vcpu *vcpu,
63-
const struct coproc_params *p,
64-
const struct coproc_reg *r)
65-
{
66-
if (p->is_write)
67-
return ignore_write(vcpu, p);
68-
69-
*vcpu_reg(vcpu, p->Rt1) = vcpu->arch.cp15[c9_L2CTLR];
70-
return true;
71-
}
72-
73-
static void reset_l2ctlr(struct kvm_vcpu *vcpu, const struct coproc_reg *r)
74-
{
75-
u32 l2ctlr, ncores;
76-
77-
asm volatile("mrc p15, 1, %0, c9, c0, 2\n" : "=r" (l2ctlr));
78-
l2ctlr &= ~(3 << 24);
79-
ncores = atomic_read(&vcpu->kvm->online_vcpus) - 1;
80-
l2ctlr |= (ncores & 3) << 24;
81-
82-
vcpu->arch.cp15[c9_L2CTLR] = l2ctlr;
83-
}
84-
85-
static void reset_actlr(struct kvm_vcpu *vcpu, const struct coproc_reg *r)
86-
{
87-
u32 actlr;
88-
89-
/* ACTLR contains SMP bit: make sure you create all cpus first! */
90-
asm volatile("mrc p15, 0, %0, c1, c0, 1\n" : "=r" (actlr));
91-
/* Make the SMP bit consistent with the guest configuration */
92-
if (atomic_read(&vcpu->kvm->online_vcpus) > 1)
93-
actlr |= 1U << 6;
94-
else
95-
actlr &= ~(1U << 6);
96-
97-
vcpu->arch.cp15[c1_ACTLR] = actlr;
98-
}
99-
100-
/* A15 TRM 4.3.49: R/O WI (even if NSACR.NS_L2ERR, a write of 1 is ignored). */
101-
static bool access_l2ectlr(struct kvm_vcpu *vcpu,
102-
const struct coproc_params *p,
103-
const struct coproc_reg *r)
104-
{
105-
if (p->is_write)
106-
return ignore_write(vcpu, p);
107-
108-
*vcpu_reg(vcpu, p->Rt1) = 0;
109-
return true;
110-
}
111-
11226
/*
11327
* A15-specific CP15 registers.
11428
* CRn denotes the primary register number, but is copied to the CRm in the
@@ -118,29 +32,9 @@ static bool access_l2ectlr(struct kvm_vcpu *vcpu,
11832
* registers preceding 32-bit ones.
11933
*/
12034
static const struct coproc_reg a15_regs[] = {
121-
/* MPIDR: we use VMPIDR for guest access. */
122-
{ CRn( 0), CRm( 0), Op1( 0), Op2( 5), is32,
123-
NULL, reset_mpidr, c0_MPIDR },
124-
12535
/* SCTLR: swapped by interrupt.S. */
12636
{ CRn( 1), CRm( 0), Op1( 0), Op2( 0), is32,
12737
NULL, reset_val, c1_SCTLR, 0x00C50078 },
128-
/* ACTLR: trapped by HCR.TAC bit. */
129-
{ CRn( 1), CRm( 0), Op1( 0), Op2( 1), is32,
130-
access_actlr, reset_actlr, c1_ACTLR },
131-
/* CPACR: swapped by interrupt.S. */
132-
{ CRn( 1), CRm( 0), Op1( 0), Op2( 2), is32,
133-
NULL, reset_val, c1_CPACR, 0x00000000 },
134-
135-
/*
136-
* L2CTLR access (guest wants to know #CPUs).
137-
*/
138-
{ CRn( 9), CRm( 0), Op1( 1), Op2( 2), is32,
139-
access_l2ctlr, reset_l2ctlr, c9_L2CTLR },
140-
{ CRn( 9), CRm( 0), Op1( 1), Op2( 3), is32, access_l2ectlr},
141-
142-
/* The Configuration Base Address Register. */
143-
{ CRn(15), CRm( 0), Op1( 4), Op2( 0), is32, access_cbar},
14438
};
14539

14640
static struct kvm_coproc_target_table a15_target_table = {
@@ -151,12 +45,6 @@ static struct kvm_coproc_target_table a15_target_table = {
15145

15246
static int __init coproc_a15_init(void)
15347
{
154-
unsigned int i;
155-
156-
for (i = 1; i < ARRAY_SIZE(a15_regs); i++)
157-
BUG_ON(cmp_reg(&a15_regs[i-1],
158-
&a15_regs[i]) >= 0);
159-
16048
kvm_register_target_coproc_table(&a15_target_table);
16149
return 0;
16250
}

arch/arm/kvm/coproc_a7.c

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright (C) 2012 - Virtual Open Systems and Columbia University
3+
* Copyright (C) 2013 - ARM Ltd
4+
*
5+
* Authors: Rusty Russell <rusty@rustcorp.au>
6+
* Christoffer Dall <c.dall@virtualopensystems.com>
7+
* Jonathan Austin <jonathan.austin@arm.com>
8+
*
9+
* This program is free software; you can redistribute it and/or modify
10+
* it under the terms of the GNU General Public License, version 2, as
11+
* published by the Free Software Foundation.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU General Public License
19+
* along with this program; if not, write to the Free Software
20+
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21+
*/
22+
#include <linux/kvm_host.h>
23+
#include <asm/kvm_coproc.h>
24+
#include <asm/kvm_emulate.h>
25+
#include <linux/init.h>
26+
27+
#include "coproc.h"
28+
29+
/*
30+
* Cortex-A7 specific CP15 registers.
31+
* CRn denotes the primary register number, but is copied to the CRm in the
32+
* user space API for 64-bit register access in line with the terminology used
33+
* in the ARM ARM.
34+
* Important: Must be sorted ascending by CRn, CRM, Op1, Op2 and with 64-bit
35+
* registers preceding 32-bit ones.
36+
*/
37+
static const struct coproc_reg a7_regs[] = {
38+
/* SCTLR: swapped by interrupt.S. */
39+
{ CRn( 1), CRm( 0), Op1( 0), Op2( 0), is32,
40+
NULL, reset_val, c1_SCTLR, 0x00C50878 },
41+
};
42+
43+
static struct kvm_coproc_target_table a7_target_table = {
44+
.target = KVM_ARM_TARGET_CORTEX_A7,
45+
.table = a7_regs,
46+
.num = ARRAY_SIZE(a7_regs),
47+
};
48+
49+
static int __init coproc_a7_init(void)
50+
{
51+
kvm_register_target_coproc_table(&a7_target_table);
52+
return 0;
53+
}
54+
late_initcall(coproc_a7_init);

arch/arm/kvm/guest.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,8 @@ int __attribute_const__ kvm_target_cpu(void)
190190
return -EINVAL;
191191

192192
switch (part_number) {
193+
case ARM_CPU_PART_CORTEX_A7:
194+
return KVM_ARM_TARGET_CORTEX_A7;
193195
case ARM_CPU_PART_CORTEX_A15:
194196
return KVM_ARM_TARGET_CORTEX_A15;
195197
default:
@@ -202,7 +204,7 @@ int kvm_vcpu_set_target(struct kvm_vcpu *vcpu,
202204
{
203205
unsigned int i;
204206

205-
/* We can only do a cortex A15 for now. */
207+
/* We can only cope with guest==host and only on A15/A7 (for now). */
206208
if (init->target != kvm_target_cpu())
207209
return -EINVAL;
208210

0 commit comments

Comments
 (0)