Commit a035001d authored by Marc Zyngier's avatar Marc Zyngier
Browse files

arm64: Convert VTCR_EL2 to sysreg infratructure



Our definition of VTCR_EL2 is both partial (tons of fields are
missing) and totally inconsistent (some constants are shifted,
some are not). They are also expressed in terms of TCR, which is
rather inconvenient.

Replace the ad-hoc definitions with the the generated version.
This results in a bunch of additional changes to make the code
with the unshifted nature of generated enumerations.

The register data was extracted from the BSD licenced AARCHMRS
(AARCHMRS_OPENSOURCE_A_profile_FAT-2025-09_ASL0).

Reviewed-by: default avatarAlexandru Elisei <alexandru.elisei@arm.com>
Reviewed-by: default avatarFuad Tabba <tabba@google.com>
Tested-by: default avatarFuad Tabba <tabba@google.com>
Link: https://patch.msgid.link/20251210173024.561160-4-maz@kernel.org


Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
parent f1640174
Loading
Loading
Loading
Loading
+12 −40
Original line number Diff line number Diff line
@@ -124,37 +124,7 @@
#define TCR_EL2_MASK	(TCR_EL2_TG0_MASK | TCR_EL2_SH0_MASK | \
			 TCR_EL2_ORGN0_MASK | TCR_EL2_IRGN0_MASK)

/* VTCR_EL2 Registers bits */
#define VTCR_EL2_DS		TCR_EL2_DS
#define VTCR_EL2_RES1		(1U << 31)
#define VTCR_EL2_HD		(1 << 22)
#define VTCR_EL2_HA		(1 << 21)
#define VTCR_EL2_PS_SHIFT	TCR_EL2_PS_SHIFT
#define VTCR_EL2_PS_MASK	TCR_EL2_PS_MASK
#define VTCR_EL2_TG0_MASK	TCR_TG0_MASK
#define VTCR_EL2_TG0_4K		TCR_TG0_4K
#define VTCR_EL2_TG0_16K	TCR_TG0_16K
#define VTCR_EL2_TG0_64K	TCR_TG0_64K
#define VTCR_EL2_SH0_MASK	TCR_SH0_MASK
#define VTCR_EL2_SH0_INNER	TCR_SH0_INNER
#define VTCR_EL2_ORGN0_MASK	TCR_ORGN0_MASK
#define VTCR_EL2_ORGN0_WBWA	TCR_ORGN0_WBWA
#define VTCR_EL2_IRGN0_MASK	TCR_IRGN0_MASK
#define VTCR_EL2_IRGN0_WBWA	TCR_IRGN0_WBWA
#define VTCR_EL2_SL0_SHIFT	6
#define VTCR_EL2_SL0_MASK	(3 << VTCR_EL2_SL0_SHIFT)
#define VTCR_EL2_T0SZ_MASK	0x3f
#define VTCR_EL2_VS_SHIFT	19
#define VTCR_EL2_VS_8BIT	(0 << VTCR_EL2_VS_SHIFT)
#define VTCR_EL2_VS_16BIT	(1 << VTCR_EL2_VS_SHIFT)

#define VTCR_EL2_T0SZ(x)	TCR_T0SZ(x)

/*
 * We configure the Stage-2 page tables to always restrict the IPA space to be
 * 40 bits wide (T0SZ = 24).  Systems with a PARange smaller than 40 bits are
 * not known to exist and will break with this configuration.
 *
 * The VTCR_EL2 is configured per VM and is initialised in kvm_init_stage2_mmu.
 *
 * Note that when using 4K pages, we concatenate two first level page tables
@@ -162,9 +132,6 @@
 *
 */

#define VTCR_EL2_COMMON_BITS	(VTCR_EL2_SH0_INNER | VTCR_EL2_ORGN0_WBWA | \
				 VTCR_EL2_IRGN0_WBWA | VTCR_EL2_RES1)

/*
 * VTCR_EL2:SL0 indicates the entry level for Stage2 translation.
 * Interestingly, it depends on the page size.
@@ -196,30 +163,35 @@
 */
#ifdef CONFIG_ARM64_64K_PAGES

#define VTCR_EL2_TGRAN			VTCR_EL2_TG0_64K
#define VTCR_EL2_TGRAN			64K
#define VTCR_EL2_TGRAN_SL0_BASE		3UL

#elif defined(CONFIG_ARM64_16K_PAGES)

#define VTCR_EL2_TGRAN			VTCR_EL2_TG0_16K
#define VTCR_EL2_TGRAN			16K
#define VTCR_EL2_TGRAN_SL0_BASE		3UL

#else	/* 4K */

#define VTCR_EL2_TGRAN			VTCR_EL2_TG0_4K
#define VTCR_EL2_TGRAN			4K
#define VTCR_EL2_TGRAN_SL0_BASE		2UL

#endif

#define VTCR_EL2_LVLS_TO_SL0(levels)	\
	((VTCR_EL2_TGRAN_SL0_BASE - (4 - (levels))) << VTCR_EL2_SL0_SHIFT)
	FIELD_PREP(VTCR_EL2_SL0, (VTCR_EL2_TGRAN_SL0_BASE - (4 - (levels))))
#define VTCR_EL2_SL0_TO_LVLS(sl0)	\
	((sl0) + 4 - VTCR_EL2_TGRAN_SL0_BASE)
#define VTCR_EL2_LVLS(vtcr)		\
	VTCR_EL2_SL0_TO_LVLS(((vtcr) & VTCR_EL2_SL0_MASK) >> VTCR_EL2_SL0_SHIFT)
	VTCR_EL2_SL0_TO_LVLS(FIELD_GET(VTCR_EL2_SL0, (vtcr)))

#define VTCR_EL2_FLAGS	(SYS_FIELD_PREP_ENUM(VTCR_EL2, SH0, INNER)	    | \
			 SYS_FIELD_PREP_ENUM(VTCR_EL2, ORGN0, WBWA)	    | \
			 SYS_FIELD_PREP_ENUM(VTCR_EL2, IRGN0, WBWA)	    | \
			 SYS_FIELD_PREP_ENUM(VTCR_EL2, TG0, VTCR_EL2_TGRAN) | \
			 VTCR_EL2_RES1)

#define VTCR_EL2_FLAGS			(VTCR_EL2_COMMON_BITS | VTCR_EL2_TGRAN)
#define VTCR_EL2_IPA(vtcr)		(64 - ((vtcr) & VTCR_EL2_T0SZ_MASK))
#define VTCR_EL2_IPA(vtcr)		(64 - FIELD_GET(VTCR_EL2_T0SZ, (vtcr)))

/*
 * ARM VMSAv8-64 defines an algorithm for finding the translation table
+0 −1
Original line number Diff line number Diff line
@@ -517,7 +517,6 @@
#define SYS_TTBR1_EL2			sys_reg(3, 4, 2, 0, 1)
#define SYS_TCR_EL2			sys_reg(3, 4, 2, 0, 2)
#define SYS_VTTBR_EL2			sys_reg(3, 4, 2, 1, 0)
#define SYS_VTCR_EL2			sys_reg(3, 4, 2, 1, 2)

#define SYS_HAFGRTR_EL2			sys_reg(3, 4, 3, 1, 6)
#define SYS_SPSR_EL2			sys_reg(3, 4, 4, 0, 0)
+3 −5
Original line number Diff line number Diff line
@@ -583,8 +583,8 @@ u64 kvm_get_vtcr(u64 mmfr0, u64 mmfr1, u32 phys_shift)
	u64 vtcr = VTCR_EL2_FLAGS;
	s8 lvls;

	vtcr |= kvm_get_parange(mmfr0) << VTCR_EL2_PS_SHIFT;
	vtcr |= VTCR_EL2_T0SZ(phys_shift);
	vtcr |= FIELD_PREP(VTCR_EL2_PS, kvm_get_parange(mmfr0));
	vtcr |= FIELD_PREP(VTCR_EL2_T0SZ, (UL(64) - phys_shift));
	/*
	 * Use a minimum 2 level page table to prevent splitting
	 * host PMD huge pages at stage2.
@@ -624,9 +624,7 @@ u64 kvm_get_vtcr(u64 mmfr0, u64 mmfr1, u32 phys_shift)
		vtcr |= VTCR_EL2_DS;

	/* Set the vmid bits */
	vtcr |= (get_vmid_bits(mmfr1) == 16) ?
		VTCR_EL2_VS_16BIT :
		VTCR_EL2_VS_8BIT;
	vtcr |= (get_vmid_bits(mmfr1) == 16) ? VTCR_EL2_VS : 0;

	return vtcr;
}
+4 −4
Original line number Diff line number Diff line
@@ -377,7 +377,7 @@ static void vtcr_to_walk_info(u64 vtcr, struct s2_walk_info *wi)
{
	wi->t0sz = vtcr & TCR_EL2_T0SZ_MASK;

	switch (vtcr & VTCR_EL2_TG0_MASK) {
	switch (FIELD_GET(VTCR_EL2_TG0_MASK, vtcr)) {
	case VTCR_EL2_TG0_4K:
		wi->pgshift = 12;	 break;
	case VTCR_EL2_TG0_16K:
@@ -513,7 +513,7 @@ static u8 get_guest_mapping_ttl(struct kvm_s2_mmu *mmu, u64 addr)

	lockdep_assert_held_write(&kvm_s2_mmu_to_kvm(mmu)->mmu_lock);

	switch (vtcr & VTCR_EL2_TG0_MASK) {
	switch (FIELD_GET(VTCR_EL2_TG0_MASK, vtcr)) {
	case VTCR_EL2_TG0_4K:
		ttl = (TLBI_TTL_TG_4K << 2);
		break;
@@ -530,7 +530,7 @@ static u8 get_guest_mapping_ttl(struct kvm_s2_mmu *mmu, u64 addr)

again:
	/* Iteratively compute the block sizes for a particular granule size */
	switch (vtcr & VTCR_EL2_TG0_MASK) {
	switch (FIELD_GET(VTCR_EL2_TG0_MASK, vtcr)) {
	case VTCR_EL2_TG0_4K:
		if	(sz < SZ_4K)	sz = SZ_4K;
		else if (sz < SZ_2M)	sz = SZ_2M;
@@ -593,7 +593,7 @@ unsigned long compute_tlb_inval_range(struct kvm_s2_mmu *mmu, u64 val)

	if (!max_size) {
		/* Compute the maximum extent of the invalidation */
		switch (mmu->tlb_vtcr & VTCR_EL2_TG0_MASK) {
		switch (FIELD_GET(VTCR_EL2_TG0_MASK, mmu->tlb_vtcr)) {
		case VTCR_EL2_TG0_4K:
			max_size = SZ_1G;
			break;
+57 −0
Original line number Diff line number Diff line
@@ -4400,6 +4400,63 @@ Field 56:12 BADDR
Res0	11:0
EndSysreg

Sysreg	VTCR_EL2	3	4	2	1	2
Res0	63:46
Field	45	HDBSS
Field	44	HAFT
Res0	43:42
Field	41	TL0
Field	40	GCSH
Res0	39
Field	38	D128
Field	37	S2POE
Field	36	S2PIE
Field	35	TL1
Field	34	AssuredOnly
Field	33	SL2
Field	32	DS
Res1	31
Field	30	NSA
Field	29	NSW
Field	28	HWU62
Field	27	HWU61
Field	26	HWU60
Field	25	HWU59
Res0	24:23
Field	22	HD
Field	21	HA
Res0	20
Enum	19	VS
	0b0	8BIT
	0b1	16BIT
EndEnum
Field	18:16	PS
Enum	15:14	TG0
	0b00	4K
	0b01	64K
	0b10	16K
EndEnum
Enum	13:12	SH0
	0b00	NONE
	0b01	OUTER
	0b11	INNER
EndEnum
Enum	11:10	ORGN0
	0b00	NC
	0b01	WBWA
	0b10	WT
	0b11	WBnWA
EndEnum
Enum	9:8	IRGN0
	0b00	NC
	0b01	WBWA
	0b10	WT
	0b11	WBnWA
EndEnum
Field	7:6	SL0
Field	5:0	T0SZ
EndSysreg

Sysreg	GCSCR_EL2	3	4	2	5	0
Fields	GCSCR_ELx
EndSysreg