patch-2.4.9 linux/arch/arm/kernel/traps.c

Next file: linux/arch/arm/kernel/via82c505.c
Previous file: linux/arch/arm/kernel/signal.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.8/linux/arch/arm/kernel/traps.c linux/arch/arm/kernel/traps.c
@@ -208,9 +208,16 @@
 
 asmlinkage void do_undefinstr(int address, struct pt_regs *regs, int mode)
 {
-	unsigned long addr = instruction_pointer(regs);
+	unsigned long addr;
 	siginfo_t info;
 
+	/*
+	 * According to the ARM ARM, PC is 2 or 4 bytes ahead, depending
+	 * whether we're in Thumb mode or not.
+	 */
+	regs->ARM_pc -= thumb_mode(regs) ? 2 : 4;
+	addr = instruction_pointer(regs);
+
 #ifdef CONFIG_DEBUG_USER
 	printk(KERN_INFO "%s (%d): undefined instruction: pc=%08lx\n",
 		current->comm, current->pid, addr);
@@ -230,6 +237,7 @@
 	die_if_kernel("Oops - undefined instruction", regs, mode);
 }
 
+#ifdef CONFIG_CPU_26
 asmlinkage void do_excpt(int address, struct pt_regs *regs, int mode)
 {
 	siginfo_t info;
@@ -252,6 +260,7 @@
 
 	die_if_kernel("Oops - address exception", regs, mode);
 }
+#endif
 
 asmlinkage void do_unexp_fiq (struct pt_regs *regs)
 {
@@ -269,33 +278,84 @@
  */
 asmlinkage void bad_mode(struct pt_regs *regs, int reason, int proc_mode)
 {
+	unsigned int vectors = vectors_base();
+	mm_segment_t fs;
+
 	console_verbose();
 
 	printk(KERN_CRIT "Bad mode in %s handler detected: mode %s\n",
 		handler[reason], processor_modes[proc_mode]);
 
 	/*
+	 * We need to switch to kernel mode so that we can
+	 * use __get_user to safely read from kernel space.
+	 * Note that we now dump the code first, just in case
+	 * the backtrace kills us.
+	 */
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+
+	/*
 	 * Dump out the vectors and stub routines.  Maybe a better solution
 	 * would be to dump them out only if we detect that they are corrupted.
 	 */
 	printk(KERN_CRIT "Vectors:\n");
-	dump_mem(0, 0x40);
+	dump_mem(vectors, 0x40);
 	printk(KERN_CRIT "Stubs:\n");
-	dump_mem(0x200, 0x4b8);
+	dump_mem(vectors + 0x200, 0x4b8);
+
+	set_fs(fs);
 
 	die("Oops", regs, 0);
 	cli();
 	panic("bad mode");
 }
 
+static int bad_syscall(int n, struct pt_regs *regs)
+{
+	siginfo_t info;
+
+	/* You might think just testing `handler' would be enough, but PER_LINUX
+	 * points it to no_lcall7 to catch undercover SVr4 binaries.  Gutted.
+	 */
+	if (current->personality != PER_LINUX && current->exec_domain->handler) {
+		/* Hand it off to iBCS.  The extra parameter and consequent type 
+		 * forcing is necessary because of the weird ARM calling convention.
+		 */
+		current->exec_domain->handler(n, regs);
+		return regs->ARM_r0;
+	}
+
+#ifdef CONFIG_DEBUG_USER
+	printk(KERN_ERR "[%d] %s: obsolete system call %08x.\n",
+		current->pid, current->comm, n);
+	dump_instr(regs);
+#endif
+
+	info.si_signo = SIGILL;
+	info.si_errno = 0;
+	info.si_code  = ILL_ILLTRP;
+	info.si_addr  = (void *)instruction_pointer(regs) -
+			 (thumb_mode(regs) ? 2 : 4);
+
+	force_sig_info(SIGILL, &info, current);
+	die_if_kernel("Oops", regs, n);
+	return regs->ARM_r0;
+}
+
 /*
- * Handle some more esoteric system calls
+ * Handle all unrecognised system calls.
+ *  0x9f0000 - 0x9fffff are some more esoteric system calls
  */
+#define NR(x) ((__ARM_NR_##x) - __ARM_NR_BASE)
 asmlinkage int arm_syscall(int no, struct pt_regs *regs)
 {
 	siginfo_t info;
 
-	switch (no) {
+	if ((no >> 16) != 0x9f)
+		return bad_syscall(no, regs);
+
+	switch (no & 0xffff) {
 	case 0: /* branch through 0 */
 		info.si_signo = SIGSEGV;
 		info.si_errno = 0;
@@ -305,9 +365,9 @@
 		force_sig_info(SIGSEGV, &info, current);
 
 		die_if_kernel("branch through zero", regs, 0);
-		break;
+		return 0;
 
-	case 1: /* SWI BREAK_POINT */
+	case NR(breakpoint): /* SWI BREAK_POINT */
 		/*
 		 * The PC is always left pointing at the next
 		 * instruction.  Fix this.
@@ -318,17 +378,50 @@
 		info.si_signo = SIGTRAP;
 		info.si_errno = 0;
 		info.si_code  = TRAP_BRKPT;
-		info.si_addr  = (void *)instruction_pointer(regs);
+		info.si_addr  = (void *)instruction_pointer(regs) -
+				 (thumb_mode(regs) ? 2 : 4);
 
 		force_sig_info(SIGTRAP, &info, current);
 		return regs->ARM_r0;
 
-	case 2:	/* sys_cacheflush */
 #ifdef CONFIG_CPU_32
-		/* r0 = start, r1 = end, r2 = flags */
+	/*
+	 * Flush a region from virtual address 'r0' to virtual address 'r1'
+	 * _inclusive_.  There is no alignment requirement on either address;
+	 * user space does not need to know the hardware cache layout.
+	 *
+	 * r2 contains flags.  It should ALWAYS be passed as ZERO until it
+	 * is defined to be something else.  For now we ignore it, but may
+	 * the fires of hell burn in your belly if you break this rule. ;)
+	 *
+	 * (at a later date, we may want to allow this call to not flush
+	 * various aspects of the cache.  Passing '0' will guarantee that
+	 * everything necessary gets flushed to maintain consistency in
+	 * the specified region).
+	 */
+	case NR(cacheflush):
 		cpu_cache_clean_invalidate_range(regs->ARM_r0, regs->ARM_r1, 1);
-#endif
+		return 0;
+
+	case NR(usr26):
+		if (!(elf_hwcap & HWCAP_26BIT))
+			break;
+		regs->ARM_cpsr &= ~0x10;
+		return regs->ARM_r0;
+
+	case NR(usr32):
+		if (!(elf_hwcap & HWCAP_26BIT))
+			break;
+		regs->ARM_cpsr |= 0x10;
+		return regs->ARM_r0;
+#else
+	case NR(cacheflush):
+		return 0;
+
+	case NR(usr26):
+	case NR(usr32):
 		break;
+#endif
 
 	default:
 		/* Calls 9f00xx..9f07ff are defined to return -ENOSYS
@@ -337,45 +430,29 @@
 		   a feature is supported.  */
 		if (no <= 0x7ff)
 			return -ENOSYS;
-#ifdef CONFIG_DEBUG_USER
-		/* experience shows that these seem to indicate that
-		 * something catastrophic has happened
-		 */
-		printk("[%d] %s: arm syscall %d\n", current->pid, current->comm, no);
-		dump_instr(regs);
-		if (user_mode(regs)) {
-			show_regs(regs);
-			c_backtrace(regs->ARM_fp, processor_mode(regs));
-		}
-#endif
-		force_sig(SIGILL, current);
-		die_if_kernel("Oops", regs, no);
 		break;
 	}
-	return 0;
-}
-
-asmlinkage void deferred(int n, struct pt_regs *regs)
-{
-	/* You might think just testing `handler' would be enough, but PER_LINUX
-	 * points it to no_lcall7 to catch undercover SVr4 binaries.  Gutted.
-	 */
-	if (current->personality != PER_LINUX && current->exec_domain->handler) {
-		/* Hand it off to iBCS.  The extra parameter and consequent type 
-		 * forcing is necessary because of the weird ARM calling convention.
-		 */
-		void (*handler)(int nr, struct pt_regs *regs) = (void *)current->exec_domain->handler;
-		(*handler)(n, regs);
-		return;
-	}
-
 #ifdef CONFIG_DEBUG_USER
-	printk(KERN_ERR "[%d] %s: obsolete system call %08x.\n",
-		current->pid, current->comm, n);
+	/*
+	 * experience shows that these seem to indicate that
+	 * something catastrophic has happened
+	 */
+	printk("[%d] %s: arm syscall %d\n", current->pid, current->comm, no);
 	dump_instr(regs);
+	if (user_mode(regs)) {
+		show_regs(regs);
+		c_backtrace(regs->ARM_fp, processor_mode(regs));
+	}
 #endif
-	force_sig(SIGILL, current);
-	die_if_kernel("Oops", regs, n);
+	info.si_signo = SIGILL;
+	info.si_errno = 0;
+	info.si_code  = ILL_ILLTRP;
+	info.si_addr  = (void *)instruction_pointer(regs) -
+			 (thumb_mode(regs) ? 2 : 4);
+
+	force_sig_info(SIGILL, &info, current);
+	die_if_kernel("Oops", regs, no);
+	return 0;
 }
 
 void __bad_xchg(volatile void *ptr, int size)
@@ -466,7 +543,7 @@
 
 	__trap_init((void *)vectors_base());
 	if (vectors_base() != 0)
-		printk("Relocating machine vectors to 0x%08x\n",
+		printk(KERN_DEBUG "Relocating machine vectors to 0x%08x\n",
 			vectors_base());
 #ifdef CONFIG_CPU_32
 	modify_domain(DOMAIN_USER, DOMAIN_CLIENT);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)