Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9141300a authored by Catalin Marinas's avatar Catalin Marinas
Browse files

arm64: Provide read/write fault information in compat signal handlers



For AArch32, bit 11 (WnR) of the FSR/ESR register is set when the fault
was caused by a write access and applications like Qemu rely on such
information being provided in sigcontext. This patch introduces the
ESR_EL1 tracking for the arm64 kernel faults and sets bit 11 accordingly
in compat sigcontext.

Signed-off-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
parent 64001113
Loading
Loading
Loading
Loading
+4 −2
Original line number Original line Diff line number Diff line
@@ -18,9 +18,11 @@
#ifndef __ASM_ESR_H
#ifndef __ASM_ESR_H
#define __ASM_ESR_H
#define __ASM_ESR_H


#define ESR_EL1_EC_SHIFT	(26)
#define ESR_EL1_WRITE		(1 << 6)
#define ESR_EL1_IL		(1U << 25)
#define ESR_EL1_CM		(1 << 8)
#define ESR_EL1_IL		(1 << 25)


#define ESR_EL1_EC_SHIFT	(26)
#define ESR_EL1_EC_UNKNOWN	(0x00)
#define ESR_EL1_EC_UNKNOWN	(0x00)
#define ESR_EL1_EC_WFI		(0x01)
#define ESR_EL1_EC_WFI		(0x01)
#define ESR_EL1_EC_CP15_32	(0x03)
#define ESR_EL1_EC_CP15_32	(0x03)
+1 −0
Original line number Original line Diff line number Diff line
@@ -79,6 +79,7 @@ struct thread_struct {
	unsigned long		tp_value;
	unsigned long		tp_value;
	struct fpsimd_state	fpsimd_state;
	struct fpsimd_state	fpsimd_state;
	unsigned long		fault_address;	/* fault info */
	unsigned long		fault_address;	/* fault info */
	unsigned long		fault_code;	/* ESR_EL1 value */
	struct debug_info	debug;		/* debugging */
	struct debug_info	debug;		/* debugging */
};
};


+6 −1
Original line number Original line Diff line number Diff line
@@ -23,6 +23,7 @@
#include <linux/syscalls.h>
#include <linux/syscalls.h>
#include <linux/ratelimit.h>
#include <linux/ratelimit.h>


#include <asm/esr.h>
#include <asm/fpsimd.h>
#include <asm/fpsimd.h>
#include <asm/signal32.h>
#include <asm/signal32.h>
#include <asm/uaccess.h>
#include <asm/uaccess.h>
@@ -81,6 +82,8 @@ struct compat_vfp_sigframe {
#define VFP_MAGIC		0x56465001
#define VFP_MAGIC		0x56465001
#define VFP_STORAGE_SIZE	sizeof(struct compat_vfp_sigframe)
#define VFP_STORAGE_SIZE	sizeof(struct compat_vfp_sigframe)


#define FSR_WRITE_SHIFT		(11)

struct compat_aux_sigframe {
struct compat_aux_sigframe {
	struct compat_vfp_sigframe	vfp;
	struct compat_vfp_sigframe	vfp;


@@ -500,7 +503,9 @@ static int compat_setup_sigframe(struct compat_sigframe __user *sf,
	__put_user_error(regs->pstate, &sf->uc.uc_mcontext.arm_cpsr, err);
	__put_user_error(regs->pstate, &sf->uc.uc_mcontext.arm_cpsr, err);


	__put_user_error((compat_ulong_t)0, &sf->uc.uc_mcontext.trap_no, err);
	__put_user_error((compat_ulong_t)0, &sf->uc.uc_mcontext.trap_no, err);
	__put_user_error((compat_ulong_t)0, &sf->uc.uc_mcontext.error_code, err);
	/* set the compat FSR WnR */
	__put_user_error(!!(current->thread.fault_code & ESR_EL1_WRITE) <<
			 FSR_WRITE_SHIFT, &sf->uc.uc_mcontext.error_code, err);
	__put_user_error(current->thread.fault_address, &sf->uc.uc_mcontext.fault_address, err);
	__put_user_error(current->thread.fault_address, &sf->uc.uc_mcontext.fault_address, err);
	__put_user_error(set->sig[0], &sf->uc.uc_mcontext.oldmask, err);
	__put_user_error(set->sig[0], &sf->uc.uc_mcontext.oldmask, err);


+5 −2
Original line number Original line Diff line number Diff line
@@ -251,11 +251,14 @@ void die(const char *str, struct pt_regs *regs, int err)
void arm64_notify_die(const char *str, struct pt_regs *regs,
void arm64_notify_die(const char *str, struct pt_regs *regs,
		      struct siginfo *info, int err)
		      struct siginfo *info, int err)
{
{
	if (user_mode(regs))
	if (user_mode(regs)) {
		current->thread.fault_address = 0;
		current->thread.fault_code = err;
		force_sig_info(info->si_signo, info, current);
		force_sig_info(info->si_signo, info, current);
	else
	} else {
		die(str, regs, err);
		die(str, regs, err);
	}
	}
}


asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
{
{
+4 −4
Original line number Original line Diff line number Diff line
@@ -32,6 +32,7 @@


#include <asm/exception.h>
#include <asm/exception.h>
#include <asm/debug-monitors.h>
#include <asm/debug-monitors.h>
#include <asm/esr.h>
#include <asm/system_misc.h>
#include <asm/system_misc.h>
#include <asm/pgtable.h>
#include <asm/pgtable.h>
#include <asm/tlbflush.h>
#include <asm/tlbflush.h>
@@ -123,6 +124,7 @@ static void __do_user_fault(struct task_struct *tsk, unsigned long addr,
	}
	}


	tsk->thread.fault_address = addr;
	tsk->thread.fault_address = addr;
	tsk->thread.fault_code = esr;
	si.si_signo = sig;
	si.si_signo = sig;
	si.si_errno = 0;
	si.si_errno = 0;
	si.si_code = code;
	si.si_code = code;
@@ -148,8 +150,6 @@ static void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *re
#define VM_FAULT_BADMAP		0x010000
#define VM_FAULT_BADMAP		0x010000
#define VM_FAULT_BADACCESS	0x020000
#define VM_FAULT_BADACCESS	0x020000


#define ESR_WRITE		(1 << 6)
#define ESR_CM			(1 << 8)
#define ESR_LNX_EXEC		(1 << 24)
#define ESR_LNX_EXEC		(1 << 24)


static int __do_page_fault(struct mm_struct *mm, unsigned long addr,
static int __do_page_fault(struct mm_struct *mm, unsigned long addr,
@@ -218,7 +218,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,


	if (esr & ESR_LNX_EXEC) {
	if (esr & ESR_LNX_EXEC) {
		vm_flags = VM_EXEC;
		vm_flags = VM_EXEC;
	} else if ((esr & ESR_WRITE) && !(esr & ESR_CM)) {
	} else if ((esr & ESR_EL1_WRITE) && !(esr & ESR_EL1_CM)) {
		vm_flags = VM_WRITE;
		vm_flags = VM_WRITE;
		mm_flags |= FAULT_FLAG_WRITE;
		mm_flags |= FAULT_FLAG_WRITE;
	}
	}
@@ -525,7 +525,7 @@ asmlinkage int __exception do_debug_exception(unsigned long addr,
	info.si_errno = 0;
	info.si_errno = 0;
	info.si_code  = inf->code;
	info.si_code  = inf->code;
	info.si_addr  = (void __user *)addr;
	info.si_addr  = (void __user *)addr;
	arm64_notify_die("", regs, &info, esr);
	arm64_notify_die("", regs, &info, 0);


	return 0;
	return 0;
}
}