Skip to content

Commit 579d7eb

Browse files
Ryan RobertsMarc Zyngier
authored andcommitted
KVM: arm64: Fix kvm init failure when mode!=vhe and VA_BITS=52.
For nvhe and protected modes, the hyp stage 1 page-tables were previously configured to have the same number of VA bits as the kernel's idmap. However, for kernel configs with VA_BITS=52 and where the kernel is loaded in physical memory below 48 bits, the idmap VA bits is actually smaller than the kernel's normal stage 1 VA bits. This can lead to kernel addresses that can't be mapped into the hypervisor, leading to kvm initialization failure during boot: kvm [1]: IPA Size Limit: 48 bits kvm [1]: Cannot map world-switch code kvm [1]: error initializing Hyp mode: -34 Fix this by ensuring that the hyp stage 1 VA size is the maximum of what's used for the idmap and the regular kernel stage 1. At the same time, refactor the code so that the hyp VA bits is only calculated in one place. Prior to 7ba8f2b, the idmap was always 52 bits for a 52 VA bits kernel and therefore the hyp stage1 was also always 52 bits. Fixes: 7ba8f2b ("arm64: mm: use a 48-bit ID map when possible on 52-bit VA builds") Signed-off-by: Ryan Roberts <[email protected]> [maz: commit message fixes] Signed-off-by: Marc Zyngier <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 30a0b95 commit 579d7eb

File tree

2 files changed

+30
-18
lines changed

2 files changed

+30
-18
lines changed

arch/arm64/kvm/arm.c

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1518,7 +1518,7 @@ static int kvm_init_vector_slots(void)
15181518
return 0;
15191519
}
15201520

1521-
static void cpu_prepare_hyp_mode(int cpu)
1521+
static void cpu_prepare_hyp_mode(int cpu, u32 hyp_va_bits)
15221522
{
15231523
struct kvm_nvhe_init_params *params = per_cpu_ptr_nvhe_sym(kvm_init_params, cpu);
15241524
unsigned long tcr;
@@ -1534,23 +1534,9 @@ static void cpu_prepare_hyp_mode(int cpu)
15341534

15351535
params->mair_el2 = read_sysreg(mair_el1);
15361536

1537-
/*
1538-
* The ID map may be configured to use an extended virtual address
1539-
* range. This is only the case if system RAM is out of range for the
1540-
* currently configured page size and VA_BITS, in which case we will
1541-
* also need the extended virtual range for the HYP ID map, or we won't
1542-
* be able to enable the EL2 MMU.
1543-
*
1544-
* However, at EL2, there is only one TTBR register, and we can't switch
1545-
* between translation tables *and* update TCR_EL2.T0SZ at the same
1546-
* time. Bottom line: we need to use the extended range with *both* our
1547-
* translation tables.
1548-
*
1549-
* So use the same T0SZ value we use for the ID map.
1550-
*/
15511537
tcr = (read_sysreg(tcr_el1) & TCR_EL2_MASK) | TCR_EL2_RES1;
15521538
tcr &= ~TCR_T0SZ_MASK;
1553-
tcr |= (idmap_t0sz & GENMASK(TCR_TxSZ_WIDTH - 1, 0)) << TCR_T0SZ_OFFSET;
1539+
tcr |= TCR_T0SZ(hyp_va_bits);
15541540
params->tcr_el2 = tcr;
15551541

15561542
params->pgd_pa = kvm_mmu_get_httbr();
@@ -2054,7 +2040,7 @@ static int init_hyp_mode(void)
20542040
}
20552041

20562042
/* Prepare the CPU initialization parameters */
2057-
cpu_prepare_hyp_mode(cpu);
2043+
cpu_prepare_hyp_mode(cpu, hyp_va_bits);
20582044
}
20592045

20602046
if (is_protected_kvm_enabled()) {

arch/arm64/kvm/mmu.c

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1618,6 +1618,8 @@ static struct kvm_pgtable_mm_ops kvm_hyp_mm_ops = {
16181618
int kvm_mmu_init(u32 *hyp_va_bits)
16191619
{
16201620
int err;
1621+
u32 idmap_bits;
1622+
u32 kernel_bits;
16211623

16221624
hyp_idmap_start = __pa_symbol(__hyp_idmap_text_start);
16231625
hyp_idmap_start = ALIGN_DOWN(hyp_idmap_start, PAGE_SIZE);
@@ -1631,7 +1633,31 @@ int kvm_mmu_init(u32 *hyp_va_bits)
16311633
*/
16321634
BUG_ON((hyp_idmap_start ^ (hyp_idmap_end - 1)) & PAGE_MASK);
16331635

1634-
*hyp_va_bits = 64 - ((idmap_t0sz & TCR_T0SZ_MASK) >> TCR_T0SZ_OFFSET);
1636+
/*
1637+
* The ID map may be configured to use an extended virtual address
1638+
* range. This is only the case if system RAM is out of range for the
1639+
* currently configured page size and VA_BITS_MIN, in which case we will
1640+
* also need the extended virtual range for the HYP ID map, or we won't
1641+
* be able to enable the EL2 MMU.
1642+
*
1643+
* However, in some cases the ID map may be configured for fewer than
1644+
* the number of VA bits used by the regular kernel stage 1. This
1645+
* happens when VA_BITS=52 and the kernel image is placed in PA space
1646+
* below 48 bits.
1647+
*
1648+
* At EL2, there is only one TTBR register, and we can't switch between
1649+
* translation tables *and* update TCR_EL2.T0SZ at the same time. Bottom
1650+
* line: we need to use the extended range with *both* our translation
1651+
* tables.
1652+
*
1653+
* So use the maximum of the idmap VA bits and the regular kernel stage
1654+
* 1 VA bits to assure that the hypervisor can both ID map its code page
1655+
* and map any kernel memory.
1656+
*/
1657+
idmap_bits = 64 - ((idmap_t0sz & TCR_T0SZ_MASK) >> TCR_T0SZ_OFFSET);
1658+
kernel_bits = vabits_actual;
1659+
*hyp_va_bits = max(idmap_bits, kernel_bits);
1660+
16351661
kvm_debug("Using %u-bit virtual addresses at EL2\n", *hyp_va_bits);
16361662
kvm_debug("IDMAP page: %lx\n", hyp_idmap_start);
16371663
kvm_debug("HYP VA range: %lx:%lx\n",

0 commit comments

Comments
 (0)