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

Merge branch kvm-arm64/s1ptw-write-fault into kvmarm-master/fixes



* kvm-arm64/s1ptw-write-fault:
  : .
  : Fix S1PTW fault handling that was until then always taken
  : as a write. From the cover letter:
  :
  : `Recent developments on the EFI front have resulted in guests that
  : simply won't boot if the page tables are in a read-only memslot and
  : that you're a bit unlucky in the way S2 gets paged in... The core
  : issue is related to the fact that we treat a S1PTW as a write, which
  : is close enough to what needs to be done. Until to get to RO memslots.
  :
  : The first patch fixes this and is definitely a stable candidate. It
  : splits the faulting of page tables in two steps (RO translation fault,
  : followed by a writable permission fault -- should it even happen).
  : The second one documents the slightly odd behaviour of PTW writes to
  : RO memslot, which do not result in a KVM_MMIO exit. The last patch is
  : totally optional, only tangentially related, and randomly repainting
  : stuff (maybe that's contagious, who knows)."
  :
  : .
  KVM: arm64: Convert FSC_* over to ESR_ELx_FSC_*
  KVM: arm64: Document the behaviour of S1PTW faults on RO memslots
  KVM: arm64: Fix S1PTW handling on RO memslots

Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
parents d5b4d07b b0803ba7
Loading
Loading
Loading
Loading
+8 −0
Original line number Original line Diff line number Diff line
@@ -1354,6 +1354,14 @@ the memory region are automatically reflected into the guest. For example, an
mmap() that affects the region will be made visible immediately.  Another
mmap() that affects the region will be made visible immediately.  Another
example is madvise(MADV_DROP).
example is madvise(MADV_DROP).


Note: On arm64, a write generated by the page-table walker (to update
the Access and Dirty flags, for example) never results in a
KVM_EXIT_MMIO exit when the slot has the KVM_MEM_READONLY flag. This
is because KVM cannot provide the data that would be written by the
page-table walker, making it impossible to emulate the access.
Instead, an abort (data abort if the cause of the page-table update
was a load or a store, instruction abort if it was an instruction
fetch) is injected in the guest.


4.36 KVM_SET_TSS_ADDR
4.36 KVM_SET_TSS_ADDR
---------------------
---------------------
+9 −0
Original line number Original line Diff line number Diff line
@@ -114,6 +114,15 @@
#define ESR_ELx_FSC_ACCESS	(0x08)
#define ESR_ELx_FSC_ACCESS	(0x08)
#define ESR_ELx_FSC_FAULT	(0x04)
#define ESR_ELx_FSC_FAULT	(0x04)
#define ESR_ELx_FSC_PERM	(0x0C)
#define ESR_ELx_FSC_PERM	(0x0C)
#define ESR_ELx_FSC_SEA_TTW0	(0x14)
#define ESR_ELx_FSC_SEA_TTW1	(0x15)
#define ESR_ELx_FSC_SEA_TTW2	(0x16)
#define ESR_ELx_FSC_SEA_TTW3	(0x17)
#define ESR_ELx_FSC_SECC	(0x18)
#define ESR_ELx_FSC_SECC_TTW0	(0x1c)
#define ESR_ELx_FSC_SECC_TTW1	(0x1d)
#define ESR_ELx_FSC_SECC_TTW2	(0x1e)
#define ESR_ELx_FSC_SECC_TTW3	(0x1f)


/* ISS field definitions for Data Aborts */
/* ISS field definitions for Data Aborts */
#define ESR_ELx_ISV_SHIFT	(24)
#define ESR_ELx_ISV_SHIFT	(24)
+0 −15
Original line number Original line Diff line number Diff line
@@ -319,21 +319,6 @@
				 BIT(18) |		\
				 BIT(18) |		\
				 GENMASK(16, 15))
				 GENMASK(16, 15))


/* For compatibility with fault code shared with 32-bit */
#define FSC_FAULT	ESR_ELx_FSC_FAULT
#define FSC_ACCESS	ESR_ELx_FSC_ACCESS
#define FSC_PERM	ESR_ELx_FSC_PERM
#define FSC_SEA		ESR_ELx_FSC_EXTABT
#define FSC_SEA_TTW0	(0x14)
#define FSC_SEA_TTW1	(0x15)
#define FSC_SEA_TTW2	(0x16)
#define FSC_SEA_TTW3	(0x17)
#define FSC_SECC	(0x18)
#define FSC_SECC_TTW0	(0x1c)
#define FSC_SECC_TTW1	(0x1d)
#define FSC_SECC_TTW2	(0x1e)
#define FSC_SECC_TTW3	(0x1f)

/* Hyp Prefetch Fault Address Register (HPFAR/HDFAR) */
/* Hyp Prefetch Fault Address Register (HPFAR/HDFAR) */
#define HPFAR_MASK	(~UL(0xf))
#define HPFAR_MASK	(~UL(0xf))
/*
/*
+30 −12
Original line number Original line Diff line number Diff line
@@ -349,16 +349,16 @@ static __always_inline u8 kvm_vcpu_trap_get_fault_level(const struct kvm_vcpu *v
static __always_inline bool kvm_vcpu_abt_issea(const struct kvm_vcpu *vcpu)
static __always_inline bool kvm_vcpu_abt_issea(const struct kvm_vcpu *vcpu)
{
{
	switch (kvm_vcpu_trap_get_fault(vcpu)) {
	switch (kvm_vcpu_trap_get_fault(vcpu)) {
	case FSC_SEA:
	case ESR_ELx_FSC_EXTABT:
	case FSC_SEA_TTW0:
	case ESR_ELx_FSC_SEA_TTW0:
	case FSC_SEA_TTW1:
	case ESR_ELx_FSC_SEA_TTW1:
	case FSC_SEA_TTW2:
	case ESR_ELx_FSC_SEA_TTW2:
	case FSC_SEA_TTW3:
	case ESR_ELx_FSC_SEA_TTW3:
	case FSC_SECC:
	case ESR_ELx_FSC_SECC:
	case FSC_SECC_TTW0:
	case ESR_ELx_FSC_SECC_TTW0:
	case FSC_SECC_TTW1:
	case ESR_ELx_FSC_SECC_TTW1:
	case FSC_SECC_TTW2:
	case ESR_ELx_FSC_SECC_TTW2:
	case FSC_SECC_TTW3:
	case ESR_ELx_FSC_SECC_TTW3:
		return true;
		return true;
	default:
	default:
		return false;
		return false;
@@ -373,8 +373,26 @@ static __always_inline int kvm_vcpu_sys_get_rt(struct kvm_vcpu *vcpu)


static inline bool kvm_is_write_fault(struct kvm_vcpu *vcpu)
static inline bool kvm_is_write_fault(struct kvm_vcpu *vcpu)
{
{
	if (kvm_vcpu_abt_iss1tw(vcpu))
	if (kvm_vcpu_abt_iss1tw(vcpu)) {
		/*
		 * Only a permission fault on a S1PTW should be
		 * considered as a write. Otherwise, page tables baked
		 * in a read-only memslot will result in an exception
		 * being delivered in the guest.
		 *
		 * The drawback is that we end-up faulting twice if the
		 * guest is using any of HW AF/DB: a translation fault
		 * to map the page containing the PT (read only at
		 * first), then a permission fault to allow the flags
		 * to be set.
		 */
		switch (kvm_vcpu_trap_get_fault_type(vcpu)) {
		case ESR_ELx_FSC_PERM:
			return true;
			return true;
		default:
			return false;
		}
	}


	if (kvm_vcpu_trap_is_iabt(vcpu))
	if (kvm_vcpu_trap_is_iabt(vcpu))
		return false;
		return false;
+1 −1
Original line number Original line Diff line number Diff line
@@ -60,7 +60,7 @@ static inline bool __get_fault_info(u64 esr, struct kvm_vcpu_fault_info *fault)
	 */
	 */
	if (!(esr & ESR_ELx_S1PTW) &&
	if (!(esr & ESR_ELx_S1PTW) &&
	    (cpus_have_final_cap(ARM64_WORKAROUND_834220) ||
	    (cpus_have_final_cap(ARM64_WORKAROUND_834220) ||
	     (esr & ESR_ELx_FSC_TYPE) == FSC_PERM)) {
	     (esr & ESR_ELx_FSC_TYPE) == ESR_ELx_FSC_PERM)) {
		if (!__translate_far_to_hpfar(far, &hpfar))
		if (!__translate_far_to_hpfar(far, &hpfar))
			return false;
			return false;
	} else {
	} else {
Loading