Commit 25ed6267 authored by David Mosberger's avatar David Mosberger

ia64: Make asynchronous signal delivery work properly during fsys-mode execution.

	Add workaround for McKinley Erratum 7.
parent 50d5299d
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
----------------------------------- -----------------------------------
Started: 13-Jan-2002 Started: 13-Jan-2002
Last update: 14-Jan-2002 Last update: 15-Jan-2002
David Mosberger-Tang David Mosberger-Tang
<davidm@hpl.hp.com> <davidm@hpl.hp.com>
...@@ -14,23 +14,22 @@ execution to the ia64 linux kernel. We call this mode the ...@@ -14,23 +14,22 @@ execution to the ia64 linux kernel. We call this mode the
"fsys-mode". To recap, the normal states of execution are: "fsys-mode". To recap, the normal states of execution are:
- kernel mode: - kernel mode:
Both the register stack and the kernel stack have been Both the register stack and the memory stack have been
switched over to the kernel stack. The user-level state switched over to kernel memory. The user-level state is saved
is saved in a pt-regs structure at the top of the kernel in a pt-regs structure at the top of the kernel memory stack.
memory stack.
- user mode: - user mode:
Both the register stack and the kernel stack are in Both the register stack and the kernel stack are in
user land. The user-level state is contained in the user memory. The user-level state is contained in the
CPU registers. CPU registers.
- bank 0 interruption-handling mode: - bank 0 interruption-handling mode:
This is the non-interruptible state in that all This is the non-interruptible state which all
interruption-handlers start executing in. The user-level interruption-handlers start execution in. The user-level
state remains in the CPU registers and some kernel state may state remains in the CPU registers and some kernel state may
be stored in bank 0 of registers r16-r31. be stored in bank 0 of registers r16-r31.
Fsys-mode has the following special properties: In contrast, fsys-mode has the following special properties:
- execution is at privilege level 0 (most-privileged) - execution is at privilege level 0 (most-privileged)
...@@ -61,18 +60,19 @@ yet. For convenience, the header file <asm-ia64/ptrace.h> provides ...@@ -61,18 +60,19 @@ yet. For convenience, the header file <asm-ia64/ptrace.h> provides
three macros: three macros:
user_mode(regs) user_mode(regs)
user_stack(regs) user_stack(task,regs)
fsys_mode(regs) fsys_mode(task,regs)
The "regs" argument is a pointer to a pt_regs structure. user_mode() The "regs" argument is a pointer to a pt_regs structure. The "task"
returns TRUE if the CPU state pointed to by "regs" was executing in argument is a pointer to the task structure to which the "regs"
user mode (privilege level 3). user_stack() returns TRUE if the state pointer belongs to. user_mode() returns TRUE if the CPU state pointed
pointed to by "regs" was executing on the user-level stack(s). to by "regs" was executing in user mode (privilege level 3).
Finally, fsys_mode() returns TRUE if the CPU state pointed to by user_stack() returns TRUE if the state pointed to by "regs" was
"regs" was executing in fsys-mode. The fsys_mode() macro corresponds executing on the user-level stack(s). Finally, fsys_mode() returns
exactly to the expression: TRUE if the CPU state pointed to by "regs" was executing in fsys-mode.
The fsys_mode() macro is equivalent to the expression:
!user_mode(regs) && user_stack(regs) !user_mode(regs) && user_stack(task,regs)
* How to write an fsyscall handler * How to write an fsyscall handler
...@@ -155,6 +155,17 @@ fast system call execution (while fully preserving system call ...@@ -155,6 +155,17 @@ fast system call execution (while fully preserving system call
semantics), but there is also a lot of flexibility in handling more semantics), but there is also a lot of flexibility in handling more
complicated cases. complicated cases.
* Signal handling
The delivery of (asynchronous) signals must be delayed until fsys-mode
is exited. This is acomplished with the help of the lower-privilege
transfer trap: arch/ia64/kernel/process.c:do_notify_resume_user()
checks whether the interrupted task was in fsys-mode and, if so, sets
PSR.lp and returns immediately. When fsys-mode is exited via the
"br.ret" instruction that lowers the privilege level, a trap will
occur. The trap handler clears PSR.lp again and returns immediately.
The kernel exit path then checks for and delivers any pending signals.
* PSR Handling * PSR Handling
The "epc" instruction doesn't change the contents of PSR at all. This The "epc" instruction doesn't change the contents of PSR at all. This
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
ENTRY(fsys_ni_syscall) ENTRY(fsys_ni_syscall)
mov r8=ENOSYS mov r8=ENOSYS
mov r10=-1 mov r10=-1
MCKINLEY_E7_WORKAROUND
br.ret.sptk.many b6 br.ret.sptk.many b6
END(fsys_ni_syscall) END(fsys_ni_syscall)
...@@ -27,6 +28,7 @@ ENTRY(fsys_getpid) ...@@ -27,6 +28,7 @@ ENTRY(fsys_getpid)
;; ;;
cmp.ne p8,p0=0,r9 cmp.ne p8,p0=0,r9
(p8) br.spnt.many fsys_fallback_syscall (p8) br.spnt.many fsys_fallback_syscall
MCKINLEY_E7_WORKAROUND
br.ret.sptk.many b6 br.ret.sptk.many b6
END(fsys_getpid) END(fsys_getpid)
......
...@@ -66,6 +66,7 @@ GLOBAL_ENTRY(syscall_via_epc) ...@@ -66,6 +66,7 @@ GLOBAL_ENTRY(syscall_via_epc)
mov r10=-1 mov r10=-1
mov r8=ENOSYS mov r8=ENOSYS
MCKINLEY_E7_WORKAROUND
br.ret.sptk.many b6 br.ret.sptk.many b6
END(syscall_via_epc) END(syscall_via_epc)
...@@ -88,6 +89,7 @@ GLOBAL_ENTRY(fsys_fallback_syscall) ...@@ -88,6 +89,7 @@ GLOBAL_ENTRY(fsys_fallback_syscall)
*/ */
movl r2=(syscall_via_break - .start_gate) + GATE_ADDR movl r2=(syscall_via_break - .start_gate) + GATE_ADDR
;; ;;
MCKINLEY_E7_WORKAROUND
mov b7=r2 mov b7=r2
br.ret.sptk.many b7 br.ret.sptk.many b7
END(fsys_fallback_syscall) END(fsys_fallback_syscall)
......
/* /*
* Architecture-specific setup. * Architecture-specific setup.
* *
* Copyright (C) 1998-2002 Hewlett-Packard Co * Copyright (C) 1998-2003 Hewlett-Packard Co
* David Mosberger-Tang <davidm@hpl.hp.com> * David Mosberger-Tang <davidm@hpl.hp.com>
*/ */
#define __KERNEL_SYSCALLS__ /* see <asm/unistd.h> */ #define __KERNEL_SYSCALLS__ /* see <asm/unistd.h> */
...@@ -144,6 +144,13 @@ show_regs (struct pt_regs *regs) ...@@ -144,6 +144,13 @@ show_regs (struct pt_regs *regs)
void void
do_notify_resume_user (sigset_t *oldset, struct sigscratch *scr, long in_syscall) do_notify_resume_user (sigset_t *oldset, struct sigscratch *scr, long in_syscall)
{ {
if (fsys_mode(current, &scr->pt)) {
/* defer signal-handling etc. until we return to privilege-level 0. */
if (!ia64_psr(&scr->pt)->lp)
ia64_psr(&scr->pt)->lp = 1;
return;
}
#ifdef CONFIG_PERFMON #ifdef CONFIG_PERFMON
if (current->thread.pfm_ovfl_block_reset) if (current->thread.pfm_ovfl_block_reset)
pfm_ovfl_block_reset(); pfm_ovfl_block_reset();
......
/* /*
* Architecture-specific trap handling. * Architecture-specific trap handling.
* *
* Copyright (C) 1998-2002 Hewlett-Packard Co * Copyright (C) 1998-2003 Hewlett-Packard Co
* David Mosberger-Tang <davidm@hpl.hp.com> * David Mosberger-Tang <davidm@hpl.hp.com>
* *
* 05/12/00 grao <goutham.rao@intel.com> : added isr in siginfo for SIGFPE * 05/12/00 grao <goutham.rao@intel.com> : added isr in siginfo for SIGFPE
...@@ -524,7 +524,7 @@ ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa, ...@@ -524,7 +524,7 @@ ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa,
case 29: /* Debug */ case 29: /* Debug */
case 35: /* Taken Branch Trap */ case 35: /* Taken Branch Trap */
case 36: /* Single Step Trap */ case 36: /* Single Step Trap */
if (fsys_mode(regs)) { if (fsys_mode(current, regs)) {
extern char syscall_via_break[], __start_gate_section[]; extern char syscall_via_break[], __start_gate_section[];
/* /*
* Got a trap in fsys-mode: Taken Branch Trap and Single Step trap * Got a trap in fsys-mode: Taken Branch Trap and Single Step trap
...@@ -580,19 +580,31 @@ ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa, ...@@ -580,19 +580,31 @@ ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa,
} }
return; return;
case 34: /* Unimplemented Instruction Address Trap */ case 34:
if (user_mode(regs)) { if (isr & 0x2) {
siginfo.si_signo = SIGILL; /* Lower-Privilege Transfer Trap */
siginfo.si_code = ILL_BADIADDR; /*
siginfo.si_errno = 0; * Just clear PSR.lp and then return immediately: all the
siginfo.si_flags = 0; * interesting work (e.g., signal delivery is done in the kernel
siginfo.si_isr = 0; * exit path).
siginfo.si_imm = 0; */
siginfo.si_addr = (void *) (regs->cr_iip + ia64_psr(regs)->ri); ia64_psr(regs)->lp = 0;
force_sig_info(SIGILL, &siginfo, current);
return; return;
} else {
/* Unimplemented Instr. Address Trap */
if (user_mode(regs)) {
siginfo.si_signo = SIGILL;
siginfo.si_code = ILL_BADIADDR;
siginfo.si_errno = 0;
siginfo.si_flags = 0;
siginfo.si_isr = 0;
siginfo.si_imm = 0;
siginfo.si_addr = (void *) (regs->cr_iip + ia64_psr(regs)->ri);
force_sig_info(SIGILL, &siginfo, current);
return;
}
sprintf(buf, "Unimplemented Instruction Address fault");
} }
sprintf(buf, "Unimplemented Instruction Address fault");
break; break;
case 45: case 45:
......
...@@ -331,7 +331,7 @@ set_rse_reg (struct pt_regs *regs, unsigned long r1, unsigned long val, int nat) ...@@ -331,7 +331,7 @@ set_rse_reg (struct pt_regs *regs, unsigned long r1, unsigned long val, int nat)
return; return;
} }
if (!user_stack(regs)) { if (!user_stack(current, regs)) {
DPRINT("ignoring kernel write to r%lu; register isn't on the kernel RBS!", r1); DPRINT("ignoring kernel write to r%lu; register isn't on the kernel RBS!", r1);
return; return;
} }
...@@ -402,7 +402,7 @@ get_rse_reg (struct pt_regs *regs, unsigned long r1, unsigned long *val, int *na ...@@ -402,7 +402,7 @@ get_rse_reg (struct pt_regs *regs, unsigned long r1, unsigned long *val, int *na
return; return;
} }
if (!user_stack(regs)) { if (!user_stack(current, regs)) {
DPRINT("ignoring kernel read of r%lu; register isn't on the RBS!", r1); DPRINT("ignoring kernel read of r%lu; register isn't on the RBS!", r1);
goto fail; goto fail;
} }
......
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
* David Mosberger-Tang <davidm@hpl.hp.com> * David Mosberger-Tang <davidm@hpl.hp.com>
*/ */
#include <linux/config.h>
#define ENTRY(name) \ #define ENTRY(name) \
.align 32; \ .align 32; \
.proc name; \ .proc name; \
...@@ -57,4 +59,13 @@ ...@@ -57,4 +59,13 @@
99: x 99: x
#endif #endif
#ifdef CONFIG_MCKINLEY
/* workaround for Itanium 2 Errata 7: */
# define MCKINLEY_E7_WORKAROUND \
br.call.sptk.many b7=1f;; \
1:
#else
# define MCKINLEY_E7_WORKAROUND
#endif
#endif /* _ASM_IA64_ASMMACRO_H */ #endif /* _ASM_IA64_ASMMACRO_H */
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
#define _ASM_IA64_PTRACE_H #define _ASM_IA64_PTRACE_H
/* /*
* Copyright (C) 1998-2002 Hewlett-Packard Co * Copyright (C) 1998-2003 Hewlett-Packard Co
* David Mosberger-Tang <davidm@hpl.hp.com> * David Mosberger-Tang <davidm@hpl.hp.com>
* Stephane Eranian <eranian@hpl.hp.com> * Stephane Eranian <eranian@hpl.hp.com>
* *
...@@ -218,8 +218,13 @@ struct switch_stack { ...@@ -218,8 +218,13 @@ struct switch_stack {
# define ia64_task_regs(t) (((struct pt_regs *) ((char *) (t) + IA64_STK_OFFSET)) - 1) # define ia64_task_regs(t) (((struct pt_regs *) ((char *) (t) + IA64_STK_OFFSET)) - 1)
# define ia64_psr(regs) ((struct ia64_psr *) &(regs)->cr_ipsr) # define ia64_psr(regs) ((struct ia64_psr *) &(regs)->cr_ipsr)
# define user_mode(regs) (((struct ia64_psr *) &(regs)->cr_ipsr)->cpl != 0) # define user_mode(regs) (((struct ia64_psr *) &(regs)->cr_ipsr)->cpl != 0)
# define user_stack(regs) (current->thread.on_ustack != 0) # define user_stack(task,regs) ((long) regs - (long) task == IA64_STK_OFFSET - sizeof(*regs))
# define fsys_mode(regs) (!user_mode(regs) && user_stack(regs)) # define fsys_mode(task,regs) \
({ \
struct task_struct *_task = (task); \
struct pt_regs *_regs = (regs); \
!user_mode(regs) && user_stack(task, regs); \
})
struct task_struct; /* forward decl */ struct task_struct; /* forward decl */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment