diff options
Diffstat (limited to 'arch/arm/kvm/coproc_a15.c')
-rw-r--r-- | arch/arm/kvm/coproc_a15.c | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/arch/arm/kvm/coproc_a15.c b/arch/arm/kvm/coproc_a15.c new file mode 100644 index 000000000000..685063a6d0cf --- /dev/null +++ b/arch/arm/kvm/coproc_a15.c | |||
@@ -0,0 +1,162 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 - Virtual Open Systems and Columbia University | ||
3 | * Authors: Rusty Russell <rusty@rustcorp.au> | ||
4 | * Christoffer Dall <c.dall@virtualopensystems.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License, version 2, as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
18 | */ | ||
19 | #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> | ||
24 | #include <asm/kvm_coproc.h> | ||
25 | #include <linux/init.h> | ||
26 | |||
27 | static void reset_mpidr(struct kvm_vcpu *vcpu, const struct coproc_reg *r) | ||
28 | { | ||
29 | /* | ||
30 | * Compute guest MPIDR: | ||
31 | * (Even if we present only one VCPU to the guest on an SMP | ||
32 | * host we don't set the U bit in the MPIDR, or vice versa, as | ||
33 | * revealing the underlying hardware properties is likely to | ||
34 | * be the best choice). | ||
35 | */ | ||
36 | vcpu->arch.cp15[c0_MPIDR] = (read_cpuid_mpidr() & ~MPIDR_LEVEL_MASK) | ||
37 | | (vcpu->vcpu_id & MPIDR_LEVEL_MASK); | ||
38 | } | ||
39 | |||
40 | #include "coproc.h" | ||
41 | |||
42 | /* A15 TRM 4.3.28: RO WI */ | ||
43 | static bool access_actlr(struct kvm_vcpu *vcpu, | ||
44 | const struct coproc_params *p, | ||
45 | const struct coproc_reg *r) | ||
46 | { | ||
47 | if (p->is_write) | ||
48 | return ignore_write(vcpu, p); | ||
49 | |||
50 | *vcpu_reg(vcpu, p->Rt1) = vcpu->arch.cp15[c1_ACTLR]; | ||
51 | return true; | ||
52 | } | ||
53 | |||
54 | /* A15 TRM 4.3.60: R/O. */ | ||
55 | static bool access_cbar(struct kvm_vcpu *vcpu, | ||
56 | const struct coproc_params *p, | ||
57 | const struct coproc_reg *r) | ||
58 | { | ||
59 | if (p->is_write) | ||
60 | return write_to_read_only(vcpu, p); | ||
61 | return read_zero(vcpu, p); | ||
62 | } | ||
63 | |||
64 | /* A15 TRM 4.3.48: R/O WI. */ | ||
65 | static bool access_l2ctlr(struct kvm_vcpu *vcpu, | ||
66 | const struct coproc_params *p, | ||
67 | const struct coproc_reg *r) | ||
68 | { | ||
69 | if (p->is_write) | ||
70 | return ignore_write(vcpu, p); | ||
71 | |||
72 | *vcpu_reg(vcpu, p->Rt1) = vcpu->arch.cp15[c9_L2CTLR]; | ||
73 | return true; | ||
74 | } | ||
75 | |||
76 | static void reset_l2ctlr(struct kvm_vcpu *vcpu, const struct coproc_reg *r) | ||
77 | { | ||
78 | u32 l2ctlr, ncores; | ||
79 | |||
80 | asm volatile("mrc p15, 1, %0, c9, c0, 2\n" : "=r" (l2ctlr)); | ||
81 | l2ctlr &= ~(3 << 24); | ||
82 | ncores = atomic_read(&vcpu->kvm->online_vcpus) - 1; | ||
83 | l2ctlr |= (ncores & 3) << 24; | ||
84 | |||
85 | vcpu->arch.cp15[c9_L2CTLR] = l2ctlr; | ||
86 | } | ||
87 | |||
88 | static void reset_actlr(struct kvm_vcpu *vcpu, const struct coproc_reg *r) | ||
89 | { | ||
90 | u32 actlr; | ||
91 | |||
92 | /* ACTLR contains SMP bit: make sure you create all cpus first! */ | ||
93 | asm volatile("mrc p15, 0, %0, c1, c0, 1\n" : "=r" (actlr)); | ||
94 | /* Make the SMP bit consistent with the guest configuration */ | ||
95 | if (atomic_read(&vcpu->kvm->online_vcpus) > 1) | ||
96 | actlr |= 1U << 6; | ||
97 | else | ||
98 | actlr &= ~(1U << 6); | ||
99 | |||
100 | vcpu->arch.cp15[c1_ACTLR] = actlr; | ||
101 | } | ||
102 | |||
103 | /* A15 TRM 4.3.49: R/O WI (even if NSACR.NS_L2ERR, a write of 1 is ignored). */ | ||
104 | static bool access_l2ectlr(struct kvm_vcpu *vcpu, | ||
105 | const struct coproc_params *p, | ||
106 | const struct coproc_reg *r) | ||
107 | { | ||
108 | if (p->is_write) | ||
109 | return ignore_write(vcpu, p); | ||
110 | |||
111 | *vcpu_reg(vcpu, p->Rt1) = 0; | ||
112 | return true; | ||
113 | } | ||
114 | |||
115 | /* | ||
116 | * A15-specific CP15 registers. | ||
117 | * Important: Must be sorted ascending by CRn, CRM, Op1, Op2 | ||
118 | */ | ||
119 | static const struct coproc_reg a15_regs[] = { | ||
120 | /* MPIDR: we use VMPIDR for guest access. */ | ||
121 | { CRn( 0), CRm( 0), Op1( 0), Op2( 5), is32, | ||
122 | NULL, reset_mpidr, c0_MPIDR }, | ||
123 | |||
124 | /* SCTLR: swapped by interrupt.S. */ | ||
125 | { CRn( 1), CRm( 0), Op1( 0), Op2( 0), is32, | ||
126 | NULL, reset_val, c1_SCTLR, 0x00C50078 }, | ||
127 | /* ACTLR: trapped by HCR.TAC bit. */ | ||
128 | { CRn( 1), CRm( 0), Op1( 0), Op2( 1), is32, | ||
129 | access_actlr, reset_actlr, c1_ACTLR }, | ||
130 | /* CPACR: swapped by interrupt.S. */ | ||
131 | { CRn( 1), CRm( 0), Op1( 0), Op2( 2), is32, | ||
132 | NULL, reset_val, c1_CPACR, 0x00000000 }, | ||
133 | |||
134 | /* | ||
135 | * L2CTLR access (guest wants to know #CPUs). | ||
136 | */ | ||
137 | { CRn( 9), CRm( 0), Op1( 1), Op2( 2), is32, | ||
138 | access_l2ctlr, reset_l2ctlr, c9_L2CTLR }, | ||
139 | { CRn( 9), CRm( 0), Op1( 1), Op2( 3), is32, access_l2ectlr}, | ||
140 | |||
141 | /* The Configuration Base Address Register. */ | ||
142 | { CRn(15), CRm( 0), Op1( 4), Op2( 0), is32, access_cbar}, | ||
143 | }; | ||
144 | |||
145 | static struct kvm_coproc_target_table a15_target_table = { | ||
146 | .target = KVM_ARM_TARGET_CORTEX_A15, | ||
147 | .table = a15_regs, | ||
148 | .num = ARRAY_SIZE(a15_regs), | ||
149 | }; | ||
150 | |||
151 | static int __init coproc_a15_init(void) | ||
152 | { | ||
153 | unsigned int i; | ||
154 | |||
155 | for (i = 1; i < ARRAY_SIZE(a15_regs); i++) | ||
156 | BUG_ON(cmp_reg(&a15_regs[i-1], | ||
157 | &a15_regs[i]) >= 0); | ||
158 | |||
159 | kvm_register_target_coproc_table(&a15_target_table); | ||
160 | return 0; | ||
161 | } | ||
162 | late_initcall(coproc_a15_init); | ||