diff options
Diffstat (limited to 'arch/arm/kvm/coproc.c')
-rw-r--r-- | arch/arm/kvm/coproc.c | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/arch/arm/kvm/coproc.c b/arch/arm/kvm/coproc.c index db9cf692d4dd..a629f2c1d0f9 100644 --- a/arch/arm/kvm/coproc.c +++ b/arch/arm/kvm/coproc.c | |||
@@ -71,6 +71,92 @@ int kvm_handle_cp14_access(struct kvm_vcpu *vcpu, struct kvm_run *run) | |||
71 | return 1; | 71 | return 1; |
72 | } | 72 | } |
73 | 73 | ||
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 | |||
74 | /* See note at ARM ARM B1.14.4 */ | 160 | /* See note at ARM ARM B1.14.4 */ |
75 | static bool access_dcsw(struct kvm_vcpu *vcpu, | 161 | static bool access_dcsw(struct kvm_vcpu *vcpu, |
76 | const struct coproc_params *p, | 162 | const struct coproc_params *p, |
@@ -153,10 +239,22 @@ static bool pm_fake(struct kvm_vcpu *vcpu, | |||
153 | * registers preceding 32-bit ones. | 239 | * registers preceding 32-bit ones. |
154 | */ | 240 | */ |
155 | static const struct coproc_reg cp15_regs[] = { | 241 | 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 | |||
156 | /* CSSELR: swapped by interrupt.S. */ | 246 | /* CSSELR: swapped by interrupt.S. */ |
157 | { CRn( 0), CRm( 0), Op1( 2), Op2( 0), is32, | 247 | { CRn( 0), CRm( 0), Op1( 2), Op2( 0), is32, |
158 | NULL, reset_unknown, c0_CSSELR }, | 248 | NULL, reset_unknown, c0_CSSELR }, |
159 | 249 | ||
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 | |||
160 | /* TTBR0/TTBR1: swapped by interrupt.S. */ | 258 | /* TTBR0/TTBR1: swapped by interrupt.S. */ |
161 | { CRm64( 2), Op1( 0), is64, NULL, reset_unknown64, c2_TTBR0 }, | 259 | { CRm64( 2), Op1( 0), is64, NULL, reset_unknown64, c2_TTBR0 }, |
162 | { CRm64( 2), Op1( 1), is64, NULL, reset_unknown64, c2_TTBR1 }, | 260 | { CRm64( 2), Op1( 1), is64, NULL, reset_unknown64, c2_TTBR1 }, |
@@ -195,6 +293,13 @@ static const struct coproc_reg cp15_regs[] = { | |||
195 | { CRn( 7), CRm(10), Op1( 0), Op2( 2), is32, access_dcsw}, | 293 | { CRn( 7), CRm(10), Op1( 0), Op2( 2), is32, access_dcsw}, |
196 | { CRn( 7), CRm(14), Op1( 0), Op2( 2), is32, access_dcsw}, | 294 | { CRn( 7), CRm(14), Op1( 0), Op2( 2), is32, access_dcsw}, |
197 | /* | 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 | |||
302 | /* | ||
198 | * Dummy performance monitor implementation. | 303 | * Dummy performance monitor implementation. |
199 | */ | 304 | */ |
200 | { CRn( 9), CRm(12), Op1( 0), Op2( 0), is32, access_pmcr}, | 305 | { CRn( 9), CRm(12), Op1( 0), Op2( 0), is32, access_pmcr}, |
@@ -234,6 +339,9 @@ static const struct coproc_reg cp15_regs[] = { | |||
234 | /* CNTKCTL: swapped by interrupt.S. */ | 339 | /* CNTKCTL: swapped by interrupt.S. */ |
235 | { CRn(14), CRm( 1), Op1( 0), Op2( 0), is32, | 340 | { CRn(14), CRm( 1), Op1( 0), Op2( 0), is32, |
236 | NULL, reset_val, c14_CNTKCTL, 0x00000000 }, | 341 | 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}, | ||
237 | }; | 345 | }; |
238 | 346 | ||
239 | /* Target specific emulation tables */ | 347 | /* Target specific emulation tables */ |
@@ -241,6 +349,12 @@ static struct kvm_coproc_target_table *target_tables[KVM_ARM_NUM_TARGETS]; | |||
241 | 349 | ||
242 | void kvm_register_target_coproc_table(struct kvm_coproc_target_table *table) | 350 | void kvm_register_target_coproc_table(struct kvm_coproc_target_table *table) |
243 | { | 351 | { |
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 | |||
244 | target_tables[table->target] = table; | 358 | target_tables[table->target] = table; |
245 | } | 359 | } |
246 | 360 | ||