Skip to content

Commit f1f0c0c

Browse files
Alexandru EliseiMarc Zyngier
authored andcommitted
KVM: arm64: Don't BUG_ON() if emulated register table is unsorted
To emulate a register access, KVM uses a table of registers sorted by register encoding to speed up queries using binary search. When Linux boots, KVM checks that the table is sorted and uses a BUG_ON() statement to let the user know if it's not. The unfortunate side effect is that an unsorted sysreg table brings down the whole kernel, not just KVM, even though the rest of the kernel can function just fine without KVM. To make matters worse, on machines which lack a serial console, the user is left pondering why the machine is taking so long to boot. Improve this situation by returning an error from kvm_arch_init() if the sysreg tables are not in the correct order. The machine is still very much usable for the user, with the exception of virtualization, who can now easily determine what went wrong. A minor typo has also been corrected in the check_sysreg_table() function. Signed-off-by: Alexandru Elisei <alexandru.elisei@arm.com> Signed-off-by: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/r/20220428103405.70884-2-alexandru.elisei@arm.com
1 parent b2d229d commit f1f0c0c

3 files changed

Lines changed: 26 additions & 17 deletions

File tree

arch/arm64/include/asm/kvm_host.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -686,7 +686,7 @@ int kvm_handle_sys_reg(struct kvm_vcpu *vcpu);
686686

687687
void kvm_reset_sys_regs(struct kvm_vcpu *vcpu);
688688

689-
void kvm_sys_reg_table_init(void);
689+
int kvm_sys_reg_table_init(void);
690690

691691
/* MMIO helpers */
692692
void kvm_mmio_write_buf(void *buf, unsigned int len, unsigned long data);

arch/arm64/kvm/arm.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1761,8 +1761,6 @@ static int init_subsystems(void)
17611761

17621762
kvm_register_perf_callbacks(NULL);
17631763

1764-
kvm_sys_reg_table_init();
1765-
17661764
out:
17671765
if (err || !is_protected_kvm_enabled())
17681766
on_each_cpu(_kvm_arch_hardware_disable, NULL, 1);
@@ -2089,6 +2087,12 @@ int kvm_arch_init(void *opaque)
20892087
return -ENODEV;
20902088
}
20912089

2090+
err = kvm_sys_reg_table_init();
2091+
if (err) {
2092+
kvm_info("Error initializing system register tables");
2093+
return err;
2094+
}
2095+
20922096
in_hyp_mode = is_kernel_in_hyp_mode();
20932097

20942098
if (cpus_have_final_cap(ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE) ||

arch/arm64/kvm/sys_regs.c

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2187,25 +2187,24 @@ static const struct sys_reg_desc cp15_64_regs[] = {
21872187
{ SYS_DESC(SYS_AARCH32_CNTP_CVAL), access_arch_timer },
21882188
};
21892189

2190-
static int check_sysreg_table(const struct sys_reg_desc *table, unsigned int n,
2191-
bool is_32)
2190+
static bool check_sysreg_table(const struct sys_reg_desc *table, unsigned int n,
2191+
bool is_32)
21922192
{
21932193
unsigned int i;
21942194

21952195
for (i = 0; i < n; i++) {
21962196
if (!is_32 && table[i].reg && !table[i].reset) {
2197-
kvm_err("sys_reg table %p entry %d has lacks reset\n",
2198-
table, i);
2199-
return 1;
2197+
kvm_err("sys_reg table %p entry %d lacks reset\n", table, i);
2198+
return false;
22002199
}
22012200

22022201
if (i && cmp_sys_reg(&table[i-1], &table[i]) >= 0) {
22032202
kvm_err("sys_reg table %p out of order (%d)\n", table, i - 1);
2204-
return 1;
2203+
return false;
22052204
}
22062205
}
22072206

2208-
return 0;
2207+
return true;
22092208
}
22102209

22112210
int kvm_handle_cp14_load_store(struct kvm_vcpu *vcpu)
@@ -2860,18 +2859,22 @@ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
28602859
return write_demux_regids(uindices);
28612860
}
28622861

2863-
void kvm_sys_reg_table_init(void)
2862+
int kvm_sys_reg_table_init(void)
28642863
{
2864+
bool valid = true;
28652865
unsigned int i;
28662866
struct sys_reg_desc clidr;
28672867

28682868
/* Make sure tables are unique and in order. */
2869-
BUG_ON(check_sysreg_table(sys_reg_descs, ARRAY_SIZE(sys_reg_descs), false));
2870-
BUG_ON(check_sysreg_table(cp14_regs, ARRAY_SIZE(cp14_regs), true));
2871-
BUG_ON(check_sysreg_table(cp14_64_regs, ARRAY_SIZE(cp14_64_regs), true));
2872-
BUG_ON(check_sysreg_table(cp15_regs, ARRAY_SIZE(cp15_regs), true));
2873-
BUG_ON(check_sysreg_table(cp15_64_regs, ARRAY_SIZE(cp15_64_regs), true));
2874-
BUG_ON(check_sysreg_table(invariant_sys_regs, ARRAY_SIZE(invariant_sys_regs), false));
2869+
valid &= check_sysreg_table(sys_reg_descs, ARRAY_SIZE(sys_reg_descs), false);
2870+
valid &= check_sysreg_table(cp14_regs, ARRAY_SIZE(cp14_regs), true);
2871+
valid &= check_sysreg_table(cp14_64_regs, ARRAY_SIZE(cp14_64_regs), true);
2872+
valid &= check_sysreg_table(cp15_regs, ARRAY_SIZE(cp15_regs), true);
2873+
valid &= check_sysreg_table(cp15_64_regs, ARRAY_SIZE(cp15_64_regs), true);
2874+
valid &= check_sysreg_table(invariant_sys_regs, ARRAY_SIZE(invariant_sys_regs), false);
2875+
2876+
if (!valid)
2877+
return -EINVAL;
28752878

28762879
/* We abuse the reset function to overwrite the table itself. */
28772880
for (i = 0; i < ARRAY_SIZE(invariant_sys_regs); i++)
@@ -2894,4 +2897,6 @@ void kvm_sys_reg_table_init(void)
28942897
break;
28952898
/* Clear all higher bits. */
28962899
cache_levels &= (1 << (i*3))-1;
2900+
2901+
return 0;
28972902
}

0 commit comments

Comments
 (0)