| 
									
										
										
										
											2011-10-31 18:44:34 -05:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Kernel traps/events for Hexagon processor | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2013-03-28 20:45:40 -05:00
										 |  |  |  * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved. | 
					
						
							| 
									
										
										
										
											2011-10-31 18:44:34 -05:00
										 |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or modify | 
					
						
							|  |  |  |  * it under the terms of the GNU General Public License version 2 and | 
					
						
							|  |  |  |  * only version 2 as published by the Free Software Foundation. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  |  * GNU General Public License for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * You should have received a copy of the GNU General Public License | 
					
						
							|  |  |  |  * along with this program; if not, write to the Free Software | 
					
						
							|  |  |  |  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | 
					
						
							|  |  |  |  * 02110-1301, USA. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/init.h>
 | 
					
						
							|  |  |  | #include <linux/sched.h>
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | #include <linux/kallsyms.h>
 | 
					
						
							|  |  |  | #include <linux/kdebug.h>
 | 
					
						
							|  |  |  | #include <linux/syscalls.h>
 | 
					
						
							|  |  |  | #include <linux/signal.h>
 | 
					
						
							|  |  |  | #include <linux/tracehook.h>
 | 
					
						
							|  |  |  | #include <asm/traps.h>
 | 
					
						
							|  |  |  | #include <asm/vm_fault.h>
 | 
					
						
							|  |  |  | #include <asm/syscall.h>
 | 
					
						
							|  |  |  | #include <asm/registers.h>
 | 
					
						
							|  |  |  | #include <asm/unistd.h>
 | 
					
						
							|  |  |  | #include <asm/sections.h>
 | 
					
						
							|  |  |  | #ifdef CONFIG_KGDB
 | 
					
						
							|  |  |  | # include <linux/kgdb.h>
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define TRAP_SYSCALL	1
 | 
					
						
							|  |  |  | #define TRAP_DEBUG	0xdb
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void __init trap_init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_GENERIC_BUG
 | 
					
						
							|  |  |  | /* Maybe should resemble arch/sh/kernel/traps.c ?? */ | 
					
						
							|  |  |  | int is_valid_bugaddr(unsigned long addr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #endif /* CONFIG_GENERIC_BUG */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const char *ex_name(int ex) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	switch (ex) { | 
					
						
							|  |  |  | 	case HVM_GE_C_XPROT: | 
					
						
							|  |  |  | 	case HVM_GE_C_XUSER: | 
					
						
							|  |  |  | 		return "Execute protection fault"; | 
					
						
							|  |  |  | 	case HVM_GE_C_RPROT: | 
					
						
							|  |  |  | 	case HVM_GE_C_RUSER: | 
					
						
							|  |  |  | 		return "Read protection fault"; | 
					
						
							|  |  |  | 	case HVM_GE_C_WPROT: | 
					
						
							|  |  |  | 	case HVM_GE_C_WUSER: | 
					
						
							|  |  |  | 		return "Write protection fault"; | 
					
						
							|  |  |  | 	case HVM_GE_C_XMAL: | 
					
						
							|  |  |  | 		return "Misaligned instruction"; | 
					
						
							| 
									
										
										
										
											2012-10-28 19:54:37 -05:00
										 |  |  | 	case HVM_GE_C_WREG: | 
					
						
							|  |  |  | 		return "Multiple writes to same register in packet"; | 
					
						
							|  |  |  | 	case HVM_GE_C_PCAL: | 
					
						
							|  |  |  | 		return "Program counter values that are not properly aligned"; | 
					
						
							| 
									
										
										
										
											2011-10-31 18:44:34 -05:00
										 |  |  | 	case HVM_GE_C_RMAL: | 
					
						
							|  |  |  | 		return "Misaligned data load"; | 
					
						
							|  |  |  | 	case HVM_GE_C_WMAL: | 
					
						
							|  |  |  | 		return "Misaligned data store"; | 
					
						
							|  |  |  | 	case HVM_GE_C_INVI: | 
					
						
							|  |  |  | 	case HVM_GE_C_PRIVI: | 
					
						
							|  |  |  | 		return "Illegal instruction"; | 
					
						
							|  |  |  | 	case HVM_GE_C_BUS: | 
					
						
							|  |  |  | 		return "Precise bus error"; | 
					
						
							|  |  |  | 	case HVM_GE_C_CACHE: | 
					
						
							|  |  |  | 		return "Cache error"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case 0xdb: | 
					
						
							|  |  |  | 		return "Debugger trap"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return "Unrecognized exception"; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void do_show_stack(struct task_struct *task, unsigned long *fp, | 
					
						
							|  |  |  | 			  unsigned long ip) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int kstack_depth_to_print = 24; | 
					
						
							|  |  |  | 	unsigned long offset, size; | 
					
						
							|  |  |  | 	const char *name = NULL; | 
					
						
							|  |  |  | 	unsigned long *newfp; | 
					
						
							|  |  |  | 	unsigned long low, high; | 
					
						
							|  |  |  | 	char tmpstr[128]; | 
					
						
							|  |  |  | 	char *modname; | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (task == NULL) | 
					
						
							|  |  |  | 		task = current; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	printk(KERN_INFO "CPU#%d, %s/%d, Call Trace:\n", | 
					
						
							|  |  |  | 	       raw_smp_processor_id(), task->comm, | 
					
						
							|  |  |  | 	       task_pid_nr(task)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (fp == NULL) { | 
					
						
							|  |  |  | 		if (task == current) { | 
					
						
							|  |  |  | 			asm("%0 = r30" : "=r" (fp)); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			fp = (unsigned long *) | 
					
						
							|  |  |  | 			     ((struct hexagon_switch_stack *) | 
					
						
							|  |  |  | 			     task->thread.switch_sp)->fp; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if ((((unsigned long) fp) & 0x3) || ((unsigned long) fp < 0x1000)) { | 
					
						
							|  |  |  | 		printk(KERN_INFO "-- Corrupt frame pointer %p\n", fp); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Saved link reg is one word above FP */ | 
					
						
							|  |  |  | 	if (!ip) | 
					
						
							|  |  |  | 		ip = *(fp+1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Expect kernel stack to be in-bounds */ | 
					
						
							|  |  |  | 	low = (unsigned long)task_stack_page(task); | 
					
						
							|  |  |  | 	high = low + THREAD_SIZE - 8; | 
					
						
							|  |  |  | 	low += sizeof(struct thread_info); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < kstack_depth_to_print; i++) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		name = kallsyms_lookup(ip, &size, &offset, &modname, tmpstr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		printk(KERN_INFO "[%p] 0x%lx: %s + 0x%lx", fp, ip, name, | 
					
						
							|  |  |  | 			offset); | 
					
						
							|  |  |  | 		if (((unsigned long) fp < low) || (high < (unsigned long) fp)) | 
					
						
							|  |  |  | 			printk(KERN_CONT " (FP out of bounds!)"); | 
					
						
							|  |  |  | 		if (modname) | 
					
						
							|  |  |  | 			printk(KERN_CONT " [%s] ", modname); | 
					
						
							|  |  |  | 		printk(KERN_CONT "\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		newfp = (unsigned long *) *fp; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (((unsigned long) newfp) & 0x3) { | 
					
						
							|  |  |  | 			printk(KERN_INFO "-- Corrupt frame pointer %p\n", | 
					
						
							|  |  |  | 				newfp); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Attempt to continue past exception. */ | 
					
						
							|  |  |  | 		if (0 == newfp) { | 
					
						
							|  |  |  | 			struct pt_regs *regs = (struct pt_regs *) (((void *)fp) | 
					
						
							|  |  |  | 						+ 8); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (regs->syscall_nr != -1) { | 
					
						
							|  |  |  | 				printk(KERN_INFO "-- trap0 -- syscall_nr: %ld", | 
					
						
							|  |  |  | 					regs->syscall_nr); | 
					
						
							|  |  |  | 				printk(KERN_CONT "  psp: %lx  elr: %lx\n", | 
					
						
							|  |  |  | 					 pt_psp(regs), pt_elr(regs)); | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				/* really want to see more ... */ | 
					
						
							|  |  |  | 				kstack_depth_to_print += 6; | 
					
						
							|  |  |  | 				printk(KERN_INFO "-- %s (0x%lx)  badva: %lx\n", | 
					
						
							|  |  |  | 					ex_name(pt_cause(regs)), pt_cause(regs), | 
					
						
							|  |  |  | 					pt_badva(regs)); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			newfp = (unsigned long *) regs->r30; | 
					
						
							|  |  |  | 			ip = pt_elr(regs); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			ip = *(newfp + 1); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* If link reg is null, we are done. */ | 
					
						
							|  |  |  | 		if (ip == 0x0) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* If newfp isn't larger, we're tracing garbage. */ | 
					
						
							|  |  |  | 		if (newfp > fp) | 
					
						
							|  |  |  | 			fp = newfp; | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void show_stack(struct task_struct *task, unsigned long *fp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* Saved link reg is one word above FP */ | 
					
						
							|  |  |  | 	do_show_stack(task, fp, 0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int die(const char *str, struct pt_regs *regs, long err) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	static struct { | 
					
						
							|  |  |  | 		spinlock_t lock; | 
					
						
							|  |  |  | 		int counter; | 
					
						
							|  |  |  | 	} die = { | 
					
						
							|  |  |  | 		.lock = __SPIN_LOCK_UNLOCKED(die.lock), | 
					
						
							|  |  |  | 		.counter = 0 | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	console_verbose(); | 
					
						
							|  |  |  | 	oops_enter(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock_irq(&die.lock); | 
					
						
							|  |  |  | 	bust_spinlocks(1); | 
					
						
							|  |  |  | 	printk(KERN_EMERG "Oops: %s[#%d]:\n", str, ++die.counter); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (notify_die(DIE_OOPS, str, regs, err, pt_cause(regs), SIGSEGV) == | 
					
						
							|  |  |  | 	    NOTIFY_STOP) | 
					
						
							|  |  |  | 		return 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	print_modules(); | 
					
						
							|  |  |  | 	show_regs(regs); | 
					
						
							|  |  |  | 	do_show_stack(current, ®s->r30, pt_elr(regs)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bust_spinlocks(0); | 
					
						
							| 
									
										
										
										
											2013-01-21 17:17:39 +10:30
										 |  |  | 	add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); | 
					
						
							| 
									
										
										
										
											2011-10-31 18:44:34 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	spin_unlock_irq(&die.lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (in_interrupt()) | 
					
						
							|  |  |  | 		panic("Fatal exception in interrupt"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (panic_on_oops) | 
					
						
							|  |  |  | 		panic("Fatal exception"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	oops_exit(); | 
					
						
							|  |  |  | 	do_exit(err); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int die_if_kernel(char *str, struct pt_regs *regs, long err) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (!user_mode(regs)) | 
					
						
							|  |  |  | 		return die(str, regs, err); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * It's not clear that misaligned fetches are ever recoverable. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void misaligned_instruction(struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	die_if_kernel("Misaligned Instruction", regs, 0); | 
					
						
							|  |  |  | 	force_sig(SIGBUS, current); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Misaligned loads and stores, on the other hand, can be | 
					
						
							|  |  |  |  * emulated, and probably should be, some day.  But for now | 
					
						
							|  |  |  |  * they will be considered fatal. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void misaligned_data_load(struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	die_if_kernel("Misaligned Data Load", regs, 0); | 
					
						
							|  |  |  | 	force_sig(SIGBUS, current); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void misaligned_data_store(struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	die_if_kernel("Misaligned Data Store", regs, 0); | 
					
						
							|  |  |  | 	force_sig(SIGBUS, current); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void illegal_instruction(struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	die_if_kernel("Illegal Instruction", regs, 0); | 
					
						
							|  |  |  | 	force_sig(SIGILL, current); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Precise bus errors may be recoverable with a a retry, | 
					
						
							|  |  |  |  * but for now, treat them as irrecoverable. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void precise_bus_error(struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	die_if_kernel("Precise Bus Error", regs, 0); | 
					
						
							|  |  |  | 	force_sig(SIGBUS, current); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * If anything is to be done here other than panic, | 
					
						
							|  |  |  |  * it will probably be complex and migrate to another | 
					
						
							|  |  |  |  * source module.  For now, just die. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void cache_error(struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	die("Cache Error", regs, 0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * General exception handler | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void do_genex(struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Decode Cause and Dispatch | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	switch (pt_cause(regs)) { | 
					
						
							|  |  |  | 	case HVM_GE_C_XPROT: | 
					
						
							|  |  |  | 	case HVM_GE_C_XUSER: | 
					
						
							|  |  |  | 		execute_protection_fault(regs); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case HVM_GE_C_RPROT: | 
					
						
							|  |  |  | 	case HVM_GE_C_RUSER: | 
					
						
							|  |  |  | 		read_protection_fault(regs); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case HVM_GE_C_WPROT: | 
					
						
							|  |  |  | 	case HVM_GE_C_WUSER: | 
					
						
							|  |  |  | 		write_protection_fault(regs); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case HVM_GE_C_XMAL: | 
					
						
							|  |  |  | 		misaligned_instruction(regs); | 
					
						
							|  |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2012-10-28 19:54:37 -05:00
										 |  |  | 	case HVM_GE_C_WREG: | 
					
						
							|  |  |  | 		illegal_instruction(regs); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case HVM_GE_C_PCAL: | 
					
						
							|  |  |  | 		misaligned_instruction(regs); | 
					
						
							|  |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2011-10-31 18:44:34 -05:00
										 |  |  | 	case HVM_GE_C_RMAL: | 
					
						
							|  |  |  | 		misaligned_data_load(regs); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case HVM_GE_C_WMAL: | 
					
						
							|  |  |  | 		misaligned_data_store(regs); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case HVM_GE_C_INVI: | 
					
						
							|  |  |  | 	case HVM_GE_C_PRIVI: | 
					
						
							|  |  |  | 		illegal_instruction(regs); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case HVM_GE_C_BUS: | 
					
						
							|  |  |  | 		precise_bus_error(regs); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case HVM_GE_C_CACHE: | 
					
						
							|  |  |  | 		cache_error(regs); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		/* Halt and catch fire */ | 
					
						
							|  |  |  | 		panic("Unrecognized exception 0x%lx\n", pt_cause(regs)); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Indirect system call dispatch */ | 
					
						
							|  |  |  | long sys_syscall(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	printk(KERN_ERR "sys_syscall invoked!\n"); | 
					
						
							|  |  |  | 	return -ENOSYS; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void do_trap0(struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	syscall_fn syscall; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (pt_cause(regs)) { | 
					
						
							|  |  |  | 	case TRAP_SYSCALL: | 
					
						
							|  |  |  | 		/* System call is trap0 #1 */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* allow strace to catch syscall args  */ | 
					
						
							|  |  |  | 		if (unlikely(test_thread_flag(TIF_SYSCALL_TRACE) && | 
					
						
							|  |  |  | 			tracehook_report_syscall_entry(regs))) | 
					
						
							|  |  |  | 			return;  /*  return -ENOSYS somewhere?  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Interrupts should be re-enabled for syscall processing */ | 
					
						
							|  |  |  | 		__vmsetie(VM_INT_ENABLE); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * System call number is in r6, arguments in r0..r5. | 
					
						
							|  |  |  | 		 * Fortunately, no Linux syscall has more than 6 arguments, | 
					
						
							|  |  |  | 		 * and Hexagon ABI passes first 6 arguments in registers. | 
					
						
							|  |  |  | 		 * 64-bit arguments are passed in odd/even register pairs. | 
					
						
							|  |  |  | 		 * Fortunately, we have no system calls that take more | 
					
						
							|  |  |  | 		 * than three arguments with more than one 64-bit value. | 
					
						
							|  |  |  | 		 * Should that change, we'd need to redesign to copy | 
					
						
							|  |  |  | 		 * between user and kernel stacks. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		regs->syscall_nr = regs->r06; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * GPR R0 carries the first parameter, and is also used | 
					
						
							|  |  |  | 		 * to report the return value.  We need a backup of | 
					
						
							|  |  |  | 		 * the user's value in case we need to do a late restart | 
					
						
							|  |  |  | 		 * of the system call. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		regs->restart_r0 = regs->r00; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if ((unsigned long) regs->syscall_nr >= __NR_syscalls) { | 
					
						
							|  |  |  | 			regs->r00 = -1; | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			syscall = (syscall_fn) | 
					
						
							|  |  |  | 				  (sys_call_table[regs->syscall_nr]); | 
					
						
							| 
									
										
										
										
											2012-05-29 17:23:14 -05:00
										 |  |  | 			regs->r00 = syscall(regs->r00, regs->r01, | 
					
						
							| 
									
										
										
										
											2011-10-31 18:44:34 -05:00
										 |  |  | 				   regs->r02, regs->r03, | 
					
						
							|  |  |  | 				   regs->r04, regs->r05); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* allow strace to get the syscall return state  */ | 
					
						
							|  |  |  | 		if (unlikely(test_thread_flag(TIF_SYSCALL_TRACE))) | 
					
						
							|  |  |  | 			tracehook_report_syscall_exit(regs, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case TRAP_DEBUG: | 
					
						
							|  |  |  | 		/* Trap0 0xdb is debug breakpoint */ | 
					
						
							|  |  |  | 		if (user_mode(regs)) { | 
					
						
							|  |  |  | 			struct siginfo info; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			info.si_signo = SIGTRAP; | 
					
						
							|  |  |  | 			info.si_errno = 0; | 
					
						
							|  |  |  | 			/*
 | 
					
						
							|  |  |  | 			 * Some architecures add some per-thread state | 
					
						
							|  |  |  | 			 * to distinguish between breakpoint traps and | 
					
						
							|  |  |  | 			 * trace traps.  We may want to do that, and | 
					
						
							|  |  |  | 			 * set the si_code value appropriately, or we | 
					
						
							|  |  |  | 			 * may want to use a different trap0 flavor. | 
					
						
							|  |  |  | 			 */ | 
					
						
							|  |  |  | 			info.si_code = TRAP_BRKPT; | 
					
						
							|  |  |  | 			info.si_addr = (void __user *) pt_elr(regs); | 
					
						
							|  |  |  | 			send_sig_info(SIGTRAP, &info, current); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | #ifdef CONFIG_KGDB
 | 
					
						
							|  |  |  | 			kgdb_handle_exception(pt_cause(regs), SIGTRAP, | 
					
						
							|  |  |  | 					      TRAP_BRKPT, regs); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	/* Ignore other trap0 codes for now, especially 0 (Angel calls) */ | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Machine check exception handler | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void do_machcheck(struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* Halt and catch fire */ | 
					
						
							|  |  |  | 	__vmstop(); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2013-03-07 12:03:10 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Treat this like the old 0xdb trap. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void do_debug_exception(struct pt_regs *regs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	regs->hvmer.vmest &= ~HVM_VMEST_CAUSE_MSK; | 
					
						
							|  |  |  | 	regs->hvmer.vmest |= (TRAP_DEBUG << HVM_VMEST_CAUSE_SFT); | 
					
						
							|  |  |  | 	do_trap0(regs); | 
					
						
							|  |  |  | } |