mirror of
https://github.com/torvalds/linux.git
synced 2024-12-21 02:21:36 +00:00
0648505324
The ARMv8.1 architecture extensions introduce support for hardware updates of the access and dirty information in page table entries. With VTCR_EL2.HA enabled (bit 21), when the CPU accesses an IPA with the PTE_AF bit cleared in the stage 2 page table, instead of raising an Access Flag fault to EL2 the CPU sets the actual page table entry bit (10). To ensure that kernel modifications to the page table do not inadvertently revert a bit set by hardware updates, certain Stage 2 software pte/pmd operations must be performed atomically. The main user of the AF bit is the kvm_age_hva() mechanism. The kvm_age_hva_handler() function performs a "test and clear young" action on the pte/pmd. This needs to be atomic in respect of automatic hardware updates of the AF bit. Since the AF bit is in the same position for both Stage 1 and Stage 2, the patch reuses the existing ptep_test_and_clear_young() functionality if __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG is defined. Otherwise, the existing pte_young/pte_mkold mechanism is preserved. The kvm_set_s2pte_readonly() (and the corresponding pmd equivalent) have to perform atomic modifications in order to avoid a race with updates of the AF bit. The arm64 implementation has been re-written using exclusives. Currently, kvm_set_s2pte_writable() (and pmd equivalent) take a pointer argument and modify the pte/pmd in place. However, these functions are only used on local variables rather than actual page table entries, so it makes more sense to follow the pte_mkwrite() approach for stage 1 attributes. The change to kvm_s2pte_mkwrite() makes it clear that these functions do not modify the actual page table entries. The (pte|pmd)_mkyoung() uses on Stage 2 entries (setting the AF bit explicitly) do not need to be modified since hardware updates of the dirty status are not supported by KVM, so there is no possibility of losing such information. Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> Cc: Paolo Bonzini <pbonzini@redhat.com> Acked-by: Marc Zyngier <marc.zyngier@arm.com> Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org> Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
89 lines
2.2 KiB
C
89 lines
2.2 KiB
C
/*
|
|
* Copyright (C) 2016 - ARM Ltd
|
|
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <linux/types.h>
|
|
#include <asm/kvm_arm.h>
|
|
#include <asm/kvm_asm.h>
|
|
#include <asm/kvm_hyp.h>
|
|
|
|
u32 __hyp_text __init_stage2_translation(void)
|
|
{
|
|
u64 val = VTCR_EL2_FLAGS;
|
|
u64 parange;
|
|
u64 tmp;
|
|
|
|
/*
|
|
* Read the PARange bits from ID_AA64MMFR0_EL1 and set the PS
|
|
* bits in VTCR_EL2. Amusingly, the PARange is 4 bits, while
|
|
* PS is only 3. Fortunately, bit 19 is RES0 in VTCR_EL2...
|
|
*/
|
|
parange = read_sysreg(id_aa64mmfr0_el1) & 7;
|
|
val |= parange << 16;
|
|
|
|
/* Compute the actual PARange... */
|
|
switch (parange) {
|
|
case 0:
|
|
parange = 32;
|
|
break;
|
|
case 1:
|
|
parange = 36;
|
|
break;
|
|
case 2:
|
|
parange = 40;
|
|
break;
|
|
case 3:
|
|
parange = 42;
|
|
break;
|
|
case 4:
|
|
parange = 44;
|
|
break;
|
|
case 5:
|
|
default:
|
|
parange = 48;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* ... and clamp it to 40 bits, unless we have some braindead
|
|
* HW that implements less than that. In all cases, we'll
|
|
* return that value for the rest of the kernel to decide what
|
|
* to do.
|
|
*/
|
|
val |= 64 - (parange > 40 ? 40 : parange);
|
|
|
|
/*
|
|
* Check the availability of Hardware Access Flag / Dirty Bit
|
|
* Management in ID_AA64MMFR1_EL1 and enable the feature in VTCR_EL2.
|
|
*/
|
|
tmp = (read_sysreg(id_aa64mmfr1_el1) >> ID_AA64MMFR1_HADBS_SHIFT) & 0xf;
|
|
if (IS_ENABLED(CONFIG_ARM64_HW_AFDBM) && tmp)
|
|
val |= VTCR_EL2_HA;
|
|
|
|
/*
|
|
* Read the VMIDBits bits from ID_AA64MMFR1_EL1 and set the VS
|
|
* bit in VTCR_EL2.
|
|
*/
|
|
tmp = (read_sysreg(id_aa64mmfr1_el1) >> ID_AA64MMFR1_VMIDBITS_SHIFT) & 0xf;
|
|
val |= (tmp == ID_AA64MMFR1_VMIDBITS_16) ?
|
|
VTCR_EL2_VS_16BIT :
|
|
VTCR_EL2_VS_8BIT;
|
|
|
|
write_sysreg(val, vtcr_el2);
|
|
|
|
return parange;
|
|
}
|