Skip to content

Commit d300b01

Browse files
committed
Merge branch 'kvm-arm64/pv-cpuid' into kvmarm/next
* kvm-arm64/pv-cpuid: : Paravirtualized implementation ID, courtesy of Shameer Kolothum : : Big-little has historically been a pain in the ass to virtualize. The : implementation ID (MIDR, REVIDR, AIDR) of a vCPU can change at the whim : of vCPU scheduling. This can be particularly annoying when the guest : needs to know the underlying implementation to mitigate errata. : : "Hyperscalers" face a similar scheduling problem, where VMs may freely : migrate between hosts in a pool of heterogenous hardware. And yes, our : server-class friends are equally riddled with errata too. : : In absence of an architected solution to this wart on the ecosystem, : introduce support for paravirtualizing the implementation exposed : to a VM, allowing the VMM to describe the pool of implementations that a : VM may be exposed to due to scheduling/migration. : : Userspace is expected to intercept and handle these hypercalls using the : SMCCC filter UAPI, should it choose to do so. smccc: kvm_guest: Fix kernel builds for 32 bit arm KVM: selftests: Add test for KVM_REG_ARM_VENDOR_HYP_BMAP_2 smccc/kvm_guest: Enable errata based on implementation CPUs arm64: Make  _midr_in_range_list() an exported function KVM: arm64: Introduce KVM_REG_ARM_VENDOR_HYP_BMAP_2 KVM: arm64: Specify hypercall ABI for retrieving target implementations arm64: Modify _midr_range() functions to read MIDR/REVIDR internally Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
2 parents 13f64f6 + 44ff44c commit d300b01

File tree

20 files changed

+329
-62
lines changed

20 files changed

+329
-62
lines changed

Documentation/virt/kvm/arm/fw-pseudo-registers.rst

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ The pseudo-firmware bitmap register are as follows:
116116
ARM DEN0057A.
117117

118118
* KVM_REG_ARM_VENDOR_HYP_BMAP:
119-
Controls the bitmap of the Vendor specific Hypervisor Service Calls.
119+
Controls the bitmap of the Vendor specific Hypervisor Service Calls[0-63].
120120

121121
The following bits are accepted:
122122

@@ -127,6 +127,19 @@ The pseudo-firmware bitmap register are as follows:
127127
Bit-1: KVM_REG_ARM_VENDOR_HYP_BIT_PTP:
128128
The bit represents the Precision Time Protocol KVM service.
129129

130+
* KVM_REG_ARM_VENDOR_HYP_BMAP_2:
131+
Controls the bitmap of the Vendor specific Hypervisor Service Calls[64-127].
132+
133+
The following bits are accepted:
134+
135+
Bit-0: KVM_REG_ARM_VENDOR_HYP_BIT_DISCOVER_IMPL_VER
136+
This represents the ARM_SMCCC_VENDOR_HYP_KVM_DISCOVER_IMPL_VER_FUNC_ID
137+
function-id. This is reset to 0.
138+
139+
Bit-1: KVM_REG_ARM_VENDOR_HYP_BIT_DISCOVER_IMPL_CPUS
140+
This represents the ARM_SMCCC_VENDOR_HYP_KVM_DISCOVER_IMPL_CPUS_FUNC_ID
141+
function-id. This is reset to 0.
142+
130143
Errors:
131144

132145
======= =============================================================

Documentation/virt/kvm/arm/hypercalls.rst

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,62 @@ region is equal to the memory protection granule advertised by
142142
| | | +---------------------------------------------+
143143
| | | | ``INVALID_PARAMETER (-3)`` |
144144
+---------------------+----------+----+---------------------------------------------+
145+
146+
``ARM_SMCCC_VENDOR_HYP_KVM_DISCOVER_IMPL_VER_FUNC_ID``
147+
-------------------------------------------------------
148+
Request the target CPU implementation version information and the number of target
149+
implementations for the Guest VM.
150+
151+
+---------------------+-------------------------------------------------------------+
152+
| Presence: | Optional; KVM/ARM64 Guests only |
153+
+---------------------+-------------------------------------------------------------+
154+
| Calling convention: | HVC64 |
155+
+---------------------+----------+--------------------------------------------------+
156+
| Function ID: | (uint32) | 0xC6000040 |
157+
+---------------------+----------+--------------------------------------------------+
158+
| Arguments: | None |
159+
+---------------------+----------+----+---------------------------------------------+
160+
| Return Values: | (int64) | R0 | ``SUCCESS (0)`` |
161+
| | | +---------------------------------------------+
162+
| | | | ``NOT_SUPPORTED (-1)`` |
163+
| +----------+----+---------------------------------------------+
164+
| | (uint64) | R1 | Bits [63:32] Reserved/Must be zero |
165+
| | | +---------------------------------------------+
166+
| | | | Bits [31:16] Major version |
167+
| | | +---------------------------------------------+
168+
| | | | Bits [15:0] Minor version |
169+
| +----------+----+---------------------------------------------+
170+
| | (uint64) | R2 | Number of target implementations |
171+
| +----------+----+---------------------------------------------+
172+
| | (uint64) | R3 | Reserved / Must be zero |
173+
+---------------------+----------+----+---------------------------------------------+
174+
175+
``ARM_SMCCC_VENDOR_HYP_KVM_DISCOVER_IMPL_CPUS_FUNC_ID``
176+
-------------------------------------------------------
177+
178+
Request the target CPU implementation information for the Guest VM. The Guest kernel
179+
will use this information to enable the associated errata.
180+
181+
+---------------------+-------------------------------------------------------------+
182+
| Presence: | Optional; KVM/ARM64 Guests only |
183+
+---------------------+-------------------------------------------------------------+
184+
| Calling convention: | HVC64 |
185+
+---------------------+----------+--------------------------------------------------+
186+
| Function ID: | (uint32) | 0xC6000041 |
187+
+---------------------+----------+----+---------------------------------------------+
188+
| Arguments: | (uint64) | R1 | selected implementation index |
189+
| +----------+----+---------------------------------------------+
190+
| | (uint64) | R2 | Reserved / Must be zero |
191+
| +----------+----+---------------------------------------------+
192+
| | (uint64) | R3 | Reserved / Must be zero |
193+
+---------------------+----------+----+---------------------------------------------+
194+
| Return Values: | (int64) | R0 | ``SUCCESS (0)`` |
195+
| | | +---------------------------------------------+
196+
| | | | ``INVALID_PARAMETER (-3)`` |
197+
| +----------+----+---------------------------------------------+
198+
| | (uint64) | R1 | MIDR_EL1 of the selected implementation |
199+
| +----------+----+---------------------------------------------+
200+
| | (uint64) | R2 | REVIDR_EL1 of the selected implementation |
201+
| +----------+----+---------------------------------------------+
202+
| | (uint64) | R3 | AIDR_EL1 of the selected implementation |
203+
+---------------------+----------+----+---------------------------------------------+

arch/arm64/include/asm/cputype.h

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,16 @@
231231

232232
#define read_cpuid(reg) read_sysreg_s(SYS_ ## reg)
233233

234+
/*
235+
* The CPU ID never changes at run time, so we might as well tell the
236+
* compiler that it's constant. Use this function to read the CPU ID
237+
* rather than directly reading processor_id or read_cpuid() directly.
238+
*/
239+
static inline u32 __attribute_const__ read_cpuid_id(void)
240+
{
241+
return read_cpuid(MIDR_EL1);
242+
}
243+
234244
/*
235245
* Represent a range of MIDR values for a given CPU model and a
236246
* range of variant/revision values.
@@ -266,30 +276,14 @@ static inline bool midr_is_cpu_model_range(u32 midr, u32 model, u32 rv_min,
266276
return _model == model && rv >= rv_min && rv <= rv_max;
267277
}
268278

269-
static inline bool is_midr_in_range(u32 midr, struct midr_range const *range)
270-
{
271-
return midr_is_cpu_model_range(midr, range->model,
272-
range->rv_min, range->rv_max);
273-
}
274-
275-
static inline bool
276-
is_midr_in_range_list(u32 midr, struct midr_range const *ranges)
277-
{
278-
while (ranges->model)
279-
if (is_midr_in_range(midr, ranges++))
280-
return true;
281-
return false;
282-
}
279+
struct target_impl_cpu {
280+
u64 midr;
281+
u64 revidr;
282+
u64 aidr;
283+
};
283284

284-
/*
285-
* The CPU ID never changes at run time, so we might as well tell the
286-
* compiler that it's constant. Use this function to read the CPU ID
287-
* rather than directly reading processor_id or read_cpuid() directly.
288-
*/
289-
static inline u32 __attribute_const__ read_cpuid_id(void)
290-
{
291-
return read_cpuid(MIDR_EL1);
292-
}
285+
bool cpu_errata_set_target_impl(u64 num, void *impl_cpus);
286+
bool is_midr_in_range_list(struct midr_range const *ranges);
293287

294288
static inline u64 __attribute_const__ read_cpuid_mpidr(void)
295289
{

arch/arm64/include/asm/hypervisor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
void kvm_init_hyp_services(void);
88
bool kvm_arm_hyp_service_available(u32 func_id);
9+
void kvm_arm_target_impl_cpu_init(void);
910

1011
#ifdef CONFIG_ARM_PKVM_GUEST
1112
void pkvm_init_hyp_services(void);

arch/arm64/include/asm/kvm_host.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,8 @@ struct kvm_arch_memory_slot {
238238
struct kvm_smccc_features {
239239
unsigned long std_bmap;
240240
unsigned long std_hyp_bmap;
241-
unsigned long vendor_hyp_bmap;
241+
unsigned long vendor_hyp_bmap; /* Function numbers 0-63 */
242+
unsigned long vendor_hyp_bmap_2; /* Function numbers 64-127 */
242243
};
243244

244245
typedef unsigned int pkvm_handle_t;

arch/arm64/include/asm/mmu.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,7 @@ static inline bool kaslr_requires_kpti(void)
101101
if (IS_ENABLED(CONFIG_CAVIUM_ERRATUM_27456)) {
102102
extern const struct midr_range cavium_erratum_27456_cpus[];
103103

104-
if (is_midr_in_range_list(read_cpuid_id(),
105-
cavium_erratum_27456_cpus))
104+
if (is_midr_in_range_list(cavium_erratum_27456_cpus))
106105
return false;
107106
}
108107

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,7 @@ enum {
372372
#endif
373373
};
374374

375+
/* Vendor hyper call function numbers 0-63 */
375376
#define KVM_REG_ARM_VENDOR_HYP_BMAP KVM_REG_ARM_FW_FEAT_BMAP_REG(2)
376377

377378
enum {
@@ -382,6 +383,17 @@ enum {
382383
#endif
383384
};
384385

386+
/* Vendor hyper call function numbers 64-127 */
387+
#define KVM_REG_ARM_VENDOR_HYP_BMAP_2 KVM_REG_ARM_FW_FEAT_BMAP_REG(3)
388+
389+
enum {
390+
KVM_REG_ARM_VENDOR_HYP_BIT_DISCOVER_IMPL_VER = 0,
391+
KVM_REG_ARM_VENDOR_HYP_BIT_DISCOVER_IMPL_CPUS = 1,
392+
#ifdef __KERNEL__
393+
KVM_REG_ARM_VENDOR_HYP_BMAP_2_BIT_COUNT,
394+
#endif
395+
};
396+
385397
/* Device Control API on vm fd */
386398
#define KVM_ARM_VM_SMCCC_CTRL 0
387399
#define KVM_ARM_VM_SMCCC_FILTER 0

arch/arm64/kernel/cpu_errata.c

Lines changed: 63 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,31 +14,85 @@
1414
#include <asm/kvm_asm.h>
1515
#include <asm/smp_plat.h>
1616

17+
static u64 target_impl_cpu_num;
18+
static struct target_impl_cpu *target_impl_cpus;
19+
20+
bool cpu_errata_set_target_impl(u64 num, void *impl_cpus)
21+
{
22+
if (target_impl_cpu_num || !num || !impl_cpus)
23+
return false;
24+
25+
target_impl_cpu_num = num;
26+
target_impl_cpus = impl_cpus;
27+
return true;
28+
}
29+
30+
static inline bool is_midr_in_range(struct midr_range const *range)
31+
{
32+
int i;
33+
34+
if (!target_impl_cpu_num)
35+
return midr_is_cpu_model_range(read_cpuid_id(), range->model,
36+
range->rv_min, range->rv_max);
37+
38+
for (i = 0; i < target_impl_cpu_num; i++) {
39+
if (midr_is_cpu_model_range(target_impl_cpus[i].midr,
40+
range->model,
41+
range->rv_min, range->rv_max))
42+
return true;
43+
}
44+
return false;
45+
}
46+
47+
bool is_midr_in_range_list(struct midr_range const *ranges)
48+
{
49+
while (ranges->model)
50+
if (is_midr_in_range(ranges++))
51+
return true;
52+
return false;
53+
}
54+
EXPORT_SYMBOL_GPL(is_midr_in_range_list);
55+
1756
static bool __maybe_unused
18-
is_affected_midr_range(const struct arm64_cpu_capabilities *entry, int scope)
57+
__is_affected_midr_range(const struct arm64_cpu_capabilities *entry,
58+
u32 midr, u32 revidr)
1959
{
2060
const struct arm64_midr_revidr *fix;
21-
u32 midr = read_cpuid_id(), revidr;
22-
23-
WARN_ON(scope != SCOPE_LOCAL_CPU || preemptible());
24-
if (!is_midr_in_range(midr, &entry->midr_range))
61+
if (!is_midr_in_range(&entry->midr_range))
2562
return false;
2663

2764
midr &= MIDR_REVISION_MASK | MIDR_VARIANT_MASK;
28-
revidr = read_cpuid(REVIDR_EL1);
2965
for (fix = entry->fixed_revs; fix && fix->revidr_mask; fix++)
3066
if (midr == fix->midr_rv && (revidr & fix->revidr_mask))
3167
return false;
32-
3368
return true;
3469
}
3570

71+
static bool __maybe_unused
72+
is_affected_midr_range(const struct arm64_cpu_capabilities *entry, int scope)
73+
{
74+
int i;
75+
76+
if (!target_impl_cpu_num) {
77+
WARN_ON(scope != SCOPE_LOCAL_CPU || preemptible());
78+
return __is_affected_midr_range(entry, read_cpuid_id(),
79+
read_cpuid(REVIDR_EL1));
80+
}
81+
82+
for (i = 0; i < target_impl_cpu_num; i++) {
83+
if (__is_affected_midr_range(entry, target_impl_cpus[i].midr,
84+
target_impl_cpus[i].midr))
85+
return true;
86+
}
87+
return false;
88+
}
89+
3690
static bool __maybe_unused
3791
is_affected_midr_range_list(const struct arm64_cpu_capabilities *entry,
3892
int scope)
3993
{
4094
WARN_ON(scope != SCOPE_LOCAL_CPU || preemptible());
41-
return is_midr_in_range_list(read_cpuid_id(), entry->midr_range_list);
95+
return is_midr_in_range_list(entry->midr_range_list);
4296
}
4397

4498
static bool __maybe_unused
@@ -186,12 +240,11 @@ static bool __maybe_unused
186240
has_neoverse_n1_erratum_1542419(const struct arm64_cpu_capabilities *entry,
187241
int scope)
188242
{
189-
u32 midr = read_cpuid_id();
190243
bool has_dic = read_cpuid_cachetype() & BIT(CTR_EL0_DIC_SHIFT);
191244
const struct midr_range range = MIDR_ALL_VERSIONS(MIDR_NEOVERSE_N1);
192245

193246
WARN_ON(scope != SCOPE_LOCAL_CPU || preemptible());
194-
return is_midr_in_range(midr, &range) && has_dic;
247+
return is_midr_in_range(&range) && has_dic;
195248
}
196249

197250
#ifdef CONFIG_ARM64_WORKAROUND_REPEAT_TLBI

arch/arm64/kernel/cpufeature.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
#include <asm/kvm_host.h>
8787
#include <asm/mmu_context.h>
8888
#include <asm/mte.h>
89+
#include <asm/hypervisor.h>
8990
#include <asm/processor.h>
9091
#include <asm/smp.h>
9192
#include <asm/sysreg.h>
@@ -1793,7 +1794,7 @@ static bool unmap_kernel_at_el0(const struct arm64_cpu_capabilities *entry,
17931794
char const *str = "kpti command line option";
17941795
bool meltdown_safe;
17951796

1796-
meltdown_safe = is_midr_in_range_list(read_cpuid_id(), kpti_safe_list);
1797+
meltdown_safe = is_midr_in_range_list(kpti_safe_list);
17971798

17981799
/* Defer to CPU feature registers */
17991800
if (has_cpuid_feature(entry, scope))
@@ -1863,7 +1864,7 @@ static bool has_nv1(const struct arm64_cpu_capabilities *entry, int scope)
18631864

18641865
return (__system_matches_cap(ARM64_HAS_NESTED_VIRT) &&
18651866
!(has_cpuid_feature(entry, scope) ||
1866-
is_midr_in_range_list(read_cpuid_id(), nv1_ni_list)));
1867+
is_midr_in_range_list(nv1_ni_list)));
18671868
}
18681869

18691870
#if defined(ID_AA64MMFR0_EL1_TGRAN_LPA2) && defined(ID_AA64MMFR0_EL1_TGRAN_2_SUPPORTED_LPA2)
@@ -2046,7 +2047,7 @@ static bool cpu_has_broken_dbm(void)
20462047
{},
20472048
};
20482049

2049-
return is_midr_in_range_list(read_cpuid_id(), cpus);
2050+
return is_midr_in_range_list(cpus);
20502051
}
20512052

20522053
static bool cpu_can_use_dbm(const struct arm64_cpu_capabilities *cap)
@@ -3691,6 +3692,7 @@ unsigned long cpu_get_elf_hwcap3(void)
36913692

36923693
static void __init setup_boot_cpu_capabilities(void)
36933694
{
3695+
kvm_arm_target_impl_cpu_init();
36943696
/*
36953697
* The boot CPU's feature register values have been recorded. Detect
36963698
* boot cpucaps and local cpucaps for the boot CPU, then enable and

arch/arm64/kernel/image-vars.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ PROVIDE(__pi_arm64_sw_feature_override = arm64_sw_feature_override);
4949
PROVIDE(__pi_arm64_use_ng_mappings = arm64_use_ng_mappings);
5050
#ifdef CONFIG_CAVIUM_ERRATUM_27456
5151
PROVIDE(__pi_cavium_erratum_27456_cpus = cavium_erratum_27456_cpus);
52+
PROVIDE(__pi_is_midr_in_range_list = is_midr_in_range_list);
5253
#endif
5354
PROVIDE(__pi__ctype = _ctype);
5455
PROVIDE(__pi_memstart_offset_seed = memstart_offset_seed);

0 commit comments

Comments
 (0)