ARM: save and reset the address limit when entering an exception
When we enter an exception, the current address limit should not apply to the exception context: if the exception context wishes to access kernel space via the user accessors (eg, perf code), it must explicitly request such access. Acked-by: Will Deacon <will.deacon@arm.com> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
This commit is contained in:
parent
dd665be0e2
commit
e6978e4bf1
|
@ -22,7 +22,7 @@ struct pt_regs {
|
||||||
struct svc_pt_regs {
|
struct svc_pt_regs {
|
||||||
struct pt_regs regs;
|
struct pt_regs regs;
|
||||||
u32 dacr;
|
u32 dacr;
|
||||||
u32 unused;
|
u32 addr_limit;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define to_svc_pt_regs(r) container_of(r, struct svc_pt_regs, regs)
|
#define to_svc_pt_regs(r) container_of(r, struct svc_pt_regs, regs)
|
||||||
|
|
|
@ -109,6 +109,7 @@ int main(void)
|
||||||
DEFINE(S_OLD_R0, offsetof(struct pt_regs, ARM_ORIG_r0));
|
DEFINE(S_OLD_R0, offsetof(struct pt_regs, ARM_ORIG_r0));
|
||||||
DEFINE(PT_REGS_SIZE, sizeof(struct pt_regs));
|
DEFINE(PT_REGS_SIZE, sizeof(struct pt_regs));
|
||||||
DEFINE(SVC_DACR, offsetof(struct svc_pt_regs, dacr));
|
DEFINE(SVC_DACR, offsetof(struct svc_pt_regs, dacr));
|
||||||
|
DEFINE(SVC_ADDR_LIMIT, offsetof(struct svc_pt_regs, addr_limit));
|
||||||
DEFINE(SVC_REGS_SIZE, sizeof(struct svc_pt_regs));
|
DEFINE(SVC_REGS_SIZE, sizeof(struct svc_pt_regs));
|
||||||
BLANK();
|
BLANK();
|
||||||
#ifdef CONFIG_CACHE_L2X0
|
#ifdef CONFIG_CACHE_L2X0
|
||||||
|
|
|
@ -185,6 +185,12 @@ ENDPROC(__und_invalid)
|
||||||
@
|
@
|
||||||
stmia r7, {r2 - r6}
|
stmia r7, {r2 - r6}
|
||||||
|
|
||||||
|
get_thread_info tsk
|
||||||
|
ldr r0, [tsk, #TI_ADDR_LIMIT]
|
||||||
|
mov r1, #TASK_SIZE
|
||||||
|
str r1, [tsk, #TI_ADDR_LIMIT]
|
||||||
|
str r0, [sp, #SVC_ADDR_LIMIT]
|
||||||
|
|
||||||
uaccess_save r0
|
uaccess_save r0
|
||||||
.if \uaccess
|
.if \uaccess
|
||||||
uaccess_disable r0
|
uaccess_disable r0
|
||||||
|
@ -213,7 +219,6 @@ __irq_svc:
|
||||||
irq_handler
|
irq_handler
|
||||||
|
|
||||||
#ifdef CONFIG_PREEMPT
|
#ifdef CONFIG_PREEMPT
|
||||||
get_thread_info tsk
|
|
||||||
ldr r8, [tsk, #TI_PREEMPT] @ get preempt count
|
ldr r8, [tsk, #TI_PREEMPT] @ get preempt count
|
||||||
ldr r0, [tsk, #TI_FLAGS] @ get flags
|
ldr r0, [tsk, #TI_FLAGS] @ get flags
|
||||||
teq r8, #0 @ if preempt count != 0
|
teq r8, #0 @ if preempt count != 0
|
||||||
|
|
|
@ -215,7 +215,9 @@
|
||||||
blne trace_hardirqs_off
|
blne trace_hardirqs_off
|
||||||
#endif
|
#endif
|
||||||
.endif
|
.endif
|
||||||
|
ldr r1, [sp, #SVC_ADDR_LIMIT]
|
||||||
uaccess_restore
|
uaccess_restore
|
||||||
|
str r1, [tsk, #TI_ADDR_LIMIT]
|
||||||
|
|
||||||
#ifndef CONFIG_THUMB2_KERNEL
|
#ifndef CONFIG_THUMB2_KERNEL
|
||||||
@ ARM mode SVC restore
|
@ ARM mode SVC restore
|
||||||
|
@ -259,7 +261,9 @@
|
||||||
@ on the stack remains correct).
|
@ on the stack remains correct).
|
||||||
@
|
@
|
||||||
.macro svc_exit_via_fiq
|
.macro svc_exit_via_fiq
|
||||||
|
ldr r1, [sp, #SVC_ADDR_LIMIT]
|
||||||
uaccess_restore
|
uaccess_restore
|
||||||
|
str r1, [tsk, #TI_ADDR_LIMIT]
|
||||||
#ifndef CONFIG_THUMB2_KERNEL
|
#ifndef CONFIG_THUMB2_KERNEL
|
||||||
@ ARM mode restore
|
@ ARM mode restore
|
||||||
mov r0, sp
|
mov r0, sp
|
||||||
|
|
|
@ -96,19 +96,23 @@ void __show_regs(struct pt_regs *regs)
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
char buf[64];
|
char buf[64];
|
||||||
#ifndef CONFIG_CPU_V7M
|
#ifndef CONFIG_CPU_V7M
|
||||||
unsigned int domain;
|
unsigned int domain, fs;
|
||||||
#ifdef CONFIG_CPU_SW_DOMAIN_PAN
|
#ifdef CONFIG_CPU_SW_DOMAIN_PAN
|
||||||
/*
|
/*
|
||||||
* Get the domain register for the parent context. In user
|
* Get the domain register for the parent context. In user
|
||||||
* mode, we don't save the DACR, so lets use what it should
|
* mode, we don't save the DACR, so lets use what it should
|
||||||
* be. For other modes, we place it after the pt_regs struct.
|
* be. For other modes, we place it after the pt_regs struct.
|
||||||
*/
|
*/
|
||||||
if (user_mode(regs))
|
if (user_mode(regs)) {
|
||||||
domain = DACR_UACCESS_ENABLE;
|
domain = DACR_UACCESS_ENABLE;
|
||||||
else
|
fs = get_fs();
|
||||||
|
} else {
|
||||||
domain = to_svc_pt_regs(regs)->dacr;
|
domain = to_svc_pt_regs(regs)->dacr;
|
||||||
|
fs = to_svc_pt_regs(regs)->addr_limit;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
domain = get_domain();
|
domain = get_domain();
|
||||||
|
fs = get_fs();
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -144,7 +148,7 @@ void __show_regs(struct pt_regs *regs)
|
||||||
if ((domain & domain_mask(DOMAIN_USER)) ==
|
if ((domain & domain_mask(DOMAIN_USER)) ==
|
||||||
domain_val(DOMAIN_USER, DOMAIN_NOACCESS))
|
domain_val(DOMAIN_USER, DOMAIN_NOACCESS))
|
||||||
segment = "none";
|
segment = "none";
|
||||||
else if (get_fs() == get_ds())
|
else if (fs == get_ds())
|
||||||
segment = "kernel";
|
segment = "kernel";
|
||||||
else
|
else
|
||||||
segment = "user";
|
segment = "user";
|
||||||
|
|
Loading…
Reference in New Issue