diff options
Diffstat (limited to 'arch/sh/kernel/traps_32.c')
-rw-r--r-- | arch/sh/kernel/traps_32.c | 181 |
1 files changed, 18 insertions, 163 deletions
diff --git a/arch/sh/kernel/traps_32.c b/arch/sh/kernel/traps_32.c index 86639beac3a2..c3d86fa71ddf 100644 --- a/arch/sh/kernel/traps_32.c +++ b/arch/sh/kernel/traps_32.c | |||
@@ -24,11 +24,10 @@ | |||
24 | #include <linux/kdebug.h> | 24 | #include <linux/kdebug.h> |
25 | #include <linux/kexec.h> | 25 | #include <linux/kexec.h> |
26 | #include <linux/limits.h> | 26 | #include <linux/limits.h> |
27 | #include <linux/proc_fs.h> | ||
28 | #include <linux/seq_file.h> | ||
29 | #include <linux/sysfs.h> | 27 | #include <linux/sysfs.h> |
28 | #include <linux/uaccess.h> | ||
30 | #include <asm/system.h> | 29 | #include <asm/system.h> |
31 | #include <asm/uaccess.h> | 30 | #include <asm/alignment.h> |
32 | #include <asm/fpu.h> | 31 | #include <asm/fpu.h> |
33 | #include <asm/kprobes.h> | 32 | #include <asm/kprobes.h> |
34 | 33 | ||
@@ -47,73 +46,6 @@ | |||
47 | #define TRAP_ILLEGAL_SLOT_INST 13 | 46 | #define TRAP_ILLEGAL_SLOT_INST 13 |
48 | #endif | 47 | #endif |
49 | 48 | ||
50 | static unsigned long se_user; | ||
51 | static unsigned long se_sys; | ||
52 | static unsigned long se_half; | ||
53 | static unsigned long se_word; | ||
54 | static unsigned long se_dword; | ||
55 | static unsigned long se_multi; | ||
56 | /* bitfield: 1: warn 2: fixup 4: signal -> combinations 2|4 && 1|2|4 are not | ||
57 | valid! */ | ||
58 | static int se_usermode = 3; | ||
59 | /* 0: no warning 1: print a warning message, disabled by default */ | ||
60 | static int se_kernmode_warn; | ||
61 | |||
62 | #ifdef CONFIG_PROC_FS | ||
63 | static const char *se_usermode_action[] = { | ||
64 | "ignored", | ||
65 | "warn", | ||
66 | "fixup", | ||
67 | "fixup+warn", | ||
68 | "signal", | ||
69 | "signal+warn" | ||
70 | }; | ||
71 | |||
72 | static int alignment_proc_show(struct seq_file *m, void *v) | ||
73 | { | ||
74 | seq_printf(m, "User:\t\t%lu\n", se_user); | ||
75 | seq_printf(m, "System:\t\t%lu\n", se_sys); | ||
76 | seq_printf(m, "Half:\t\t%lu\n", se_half); | ||
77 | seq_printf(m, "Word:\t\t%lu\n", se_word); | ||
78 | seq_printf(m, "DWord:\t\t%lu\n", se_dword); | ||
79 | seq_printf(m, "Multi:\t\t%lu\n", se_multi); | ||
80 | seq_printf(m, "User faults:\t%i (%s)\n", se_usermode, | ||
81 | se_usermode_action[se_usermode]); | ||
82 | seq_printf(m, "Kernel faults:\t%i (fixup%s)\n", se_kernmode_warn, | ||
83 | se_kernmode_warn ? "+warn" : ""); | ||
84 | return 0; | ||
85 | } | ||
86 | |||
87 | static int alignment_proc_open(struct inode *inode, struct file *file) | ||
88 | { | ||
89 | return single_open(file, alignment_proc_show, NULL); | ||
90 | } | ||
91 | |||
92 | static ssize_t alignment_proc_write(struct file *file, | ||
93 | const char __user *buffer, size_t count, loff_t *pos) | ||
94 | { | ||
95 | int *data = PDE(file->f_path.dentry->d_inode)->data; | ||
96 | char mode; | ||
97 | |||
98 | if (count > 0) { | ||
99 | if (get_user(mode, buffer)) | ||
100 | return -EFAULT; | ||
101 | if (mode >= '0' && mode <= '5') | ||
102 | *data = mode - '0'; | ||
103 | } | ||
104 | return count; | ||
105 | } | ||
106 | |||
107 | static const struct file_operations alignment_proc_fops = { | ||
108 | .owner = THIS_MODULE, | ||
109 | .open = alignment_proc_open, | ||
110 | .read = seq_read, | ||
111 | .llseek = seq_lseek, | ||
112 | .release = single_release, | ||
113 | .write = alignment_proc_write, | ||
114 | }; | ||
115 | #endif | ||
116 | |||
117 | static void dump_mem(const char *str, unsigned long bottom, unsigned long top) | 49 | static void dump_mem(const char *str, unsigned long bottom, unsigned long top) |
118 | { | 50 | { |
119 | unsigned long p; | 51 | unsigned long p; |
@@ -265,10 +197,10 @@ static int handle_unaligned_ins(insn_size_t instruction, struct pt_regs *regs, | |||
265 | count = 1<<(instruction&3); | 197 | count = 1<<(instruction&3); |
266 | 198 | ||
267 | switch (count) { | 199 | switch (count) { |
268 | case 1: se_half += 1; break; | 200 | case 1: inc_unaligned_byte_access(); break; |
269 | case 2: se_word += 1; break; | 201 | case 2: inc_unaligned_word_access(); break; |
270 | case 4: se_dword += 1; break; | 202 | case 4: inc_unaligned_dword_access(); break; |
271 | case 8: se_multi += 1; break; /* ??? */ | 203 | case 8: inc_unaligned_multi_access(); break; |
272 | } | 204 | } |
273 | 205 | ||
274 | ret = -EFAULT; | 206 | ret = -EFAULT; |
@@ -452,18 +384,8 @@ int handle_unaligned_access(insn_size_t instruction, struct pt_regs *regs, | |||
452 | rm = regs->regs[index]; | 384 | rm = regs->regs[index]; |
453 | 385 | ||
454 | /* shout about fixups */ | 386 | /* shout about fixups */ |
455 | if (!expected) { | 387 | if (!expected) |
456 | if (user_mode(regs) && (se_usermode & 1) && printk_ratelimit()) | 388 | unaligned_fixups_notify(current, instruction, regs); |
457 | pr_notice("Fixing up unaligned userspace access " | ||
458 | "in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n", | ||
459 | current->comm, task_pid_nr(current), | ||
460 | (void *)regs->pc, instruction); | ||
461 | else if (se_kernmode_warn && printk_ratelimit()) | ||
462 | pr_notice("Fixing up unaligned kernel access " | ||
463 | "in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n", | ||
464 | current->comm, task_pid_nr(current), | ||
465 | (void *)regs->pc, instruction); | ||
466 | } | ||
467 | 389 | ||
468 | ret = -EFAULT; | 390 | ret = -EFAULT; |
469 | switch (instruction&0xF000) { | 391 | switch (instruction&0xF000) { |
@@ -616,10 +538,10 @@ asmlinkage void do_address_error(struct pt_regs *regs, | |||
616 | 538 | ||
617 | if (user_mode(regs)) { | 539 | if (user_mode(regs)) { |
618 | int si_code = BUS_ADRERR; | 540 | int si_code = BUS_ADRERR; |
541 | unsigned int user_action; | ||
619 | 542 | ||
620 | local_irq_enable(); | 543 | local_irq_enable(); |
621 | 544 | inc_unaligned_user_access(); | |
622 | se_user += 1; | ||
623 | 545 | ||
624 | set_fs(USER_DS); | 546 | set_fs(USER_DS); |
625 | if (copy_from_user(&instruction, (insn_size_t *)(regs->pc & ~1), | 547 | if (copy_from_user(&instruction, (insn_size_t *)(regs->pc & ~1), |
@@ -630,16 +552,12 @@ asmlinkage void do_address_error(struct pt_regs *regs, | |||
630 | set_fs(oldfs); | 552 | set_fs(oldfs); |
631 | 553 | ||
632 | /* shout about userspace fixups */ | 554 | /* shout about userspace fixups */ |
633 | if (se_usermode & 1) | 555 | unaligned_fixups_notify(current, instruction, regs); |
634 | printk(KERN_NOTICE "Unaligned userspace access " | ||
635 | "in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n", | ||
636 | current->comm, current->pid, (void *)regs->pc, | ||
637 | instruction); | ||
638 | 556 | ||
639 | if (se_usermode & 2) | 557 | user_action = unaligned_user_action(); |
558 | if (user_action & UM_FIXUP) | ||
640 | goto fixup; | 559 | goto fixup; |
641 | 560 | if (user_action & UM_SIGNAL) | |
642 | if (se_usermode & 4) | ||
643 | goto uspace_segv; | 561 | goto uspace_segv; |
644 | else { | 562 | else { |
645 | /* ignore */ | 563 | /* ignore */ |
@@ -659,7 +577,7 @@ fixup: | |||
659 | &user_mem_access, 0); | 577 | &user_mem_access, 0); |
660 | set_fs(oldfs); | 578 | set_fs(oldfs); |
661 | 579 | ||
662 | if (tmp==0) | 580 | if (tmp == 0) |
663 | return; /* sorted */ | 581 | return; /* sorted */ |
664 | uspace_segv: | 582 | uspace_segv: |
665 | printk(KERN_NOTICE "Sending SIGBUS to \"%s\" due to unaligned " | 583 | printk(KERN_NOTICE "Sending SIGBUS to \"%s\" due to unaligned " |
@@ -672,7 +590,7 @@ uspace_segv: | |||
672 | info.si_addr = (void __user *)address; | 590 | info.si_addr = (void __user *)address; |
673 | force_sig_info(SIGBUS, &info, current); | 591 | force_sig_info(SIGBUS, &info, current); |
674 | } else { | 592 | } else { |
675 | se_sys += 1; | 593 | inc_unaligned_kernel_access(); |
676 | 594 | ||
677 | if (regs->pc & 1) | 595 | if (regs->pc & 1) |
678 | die("unaligned program counter", regs, error_code); | 596 | die("unaligned program counter", regs, error_code); |
@@ -687,11 +605,7 @@ uspace_segv: | |||
687 | die("insn faulting in do_address_error", regs, 0); | 605 | die("insn faulting in do_address_error", regs, 0); |
688 | } | 606 | } |
689 | 607 | ||
690 | if (se_kernmode_warn) | 608 | unaligned_fixups_notify(current, instruction, regs); |
691 | printk(KERN_NOTICE "Unaligned kernel access " | ||
692 | "on behalf of \"%s\" pid=%d pc=0x%p ins=0x%04hx\n", | ||
693 | current->comm, current->pid, (void *)regs->pc, | ||
694 | instruction); | ||
695 | 609 | ||
696 | handle_unaligned_access(instruction, regs, | 610 | handle_unaligned_access(instruction, regs, |
697 | &user_mem_access, 0); | 611 | &user_mem_access, 0); |
@@ -876,35 +790,10 @@ asmlinkage void do_exception_error(unsigned long r4, unsigned long r5, | |||
876 | die_if_kernel("exception", regs, ex); | 790 | die_if_kernel("exception", regs, ex); |
877 | } | 791 | } |
878 | 792 | ||
879 | #if defined(CONFIG_SH_STANDARD_BIOS) | ||
880 | void *gdb_vbr_vector; | ||
881 | |||
882 | static inline void __init gdb_vbr_init(void) | ||
883 | { | ||
884 | register unsigned long vbr; | ||
885 | |||
886 | /* | ||
887 | * Read the old value of the VBR register to initialise | ||
888 | * the vector through which debug and BIOS traps are | ||
889 | * delegated by the Linux trap handler. | ||
890 | */ | ||
891 | asm volatile("stc vbr, %0" : "=r" (vbr)); | ||
892 | |||
893 | gdb_vbr_vector = (void *)(vbr + 0x100); | ||
894 | printk("Setting GDB trap vector to 0x%08lx\n", | ||
895 | (unsigned long)gdb_vbr_vector); | ||
896 | } | ||
897 | #endif | ||
898 | |||
899 | void __cpuinit per_cpu_trap_init(void) | 793 | void __cpuinit per_cpu_trap_init(void) |
900 | { | 794 | { |
901 | extern void *vbr_base; | 795 | extern void *vbr_base; |
902 | 796 | ||
903 | #ifdef CONFIG_SH_STANDARD_BIOS | ||
904 | if (raw_smp_processor_id() == 0) | ||
905 | gdb_vbr_init(); | ||
906 | #endif | ||
907 | |||
908 | /* NOTE: The VBR value should be at P1 | 797 | /* NOTE: The VBR value should be at P1 |
909 | (or P2, virtural "fixed" address space). | 798 | (or P2, virtural "fixed" address space). |
910 | It's definitely should not in physical address. */ | 799 | It's definitely should not in physical address. */ |
@@ -956,11 +845,8 @@ void __init trap_init(void) | |||
956 | #endif | 845 | #endif |
957 | 846 | ||
958 | #ifdef TRAP_UBC | 847 | #ifdef TRAP_UBC |
959 | set_exception_table_vec(TRAP_UBC, break_point_trap); | 848 | set_exception_table_vec(TRAP_UBC, breakpoint_trap_handler); |
960 | #endif | 849 | #endif |
961 | |||
962 | /* Setup VBR for boot cpu */ | ||
963 | per_cpu_trap_init(); | ||
964 | } | 850 | } |
965 | 851 | ||
966 | void show_stack(struct task_struct *tsk, unsigned long *sp) | 852 | void show_stack(struct task_struct *tsk, unsigned long *sp) |
@@ -985,34 +871,3 @@ void dump_stack(void) | |||
985 | show_stack(NULL, NULL); | 871 | show_stack(NULL, NULL); |
986 | } | 872 | } |
987 | EXPORT_SYMBOL(dump_stack); | 873 | EXPORT_SYMBOL(dump_stack); |
988 | |||
989 | #ifdef CONFIG_PROC_FS | ||
990 | /* | ||
991 | * This needs to be done after sysctl_init, otherwise sys/ will be | ||
992 | * overwritten. Actually, this shouldn't be in sys/ at all since | ||
993 | * it isn't a sysctl, and it doesn't contain sysctl information. | ||
994 | * We now locate it in /proc/cpu/alignment instead. | ||
995 | */ | ||
996 | static int __init alignment_init(void) | ||
997 | { | ||
998 | struct proc_dir_entry *dir, *res; | ||
999 | |||
1000 | dir = proc_mkdir("cpu", NULL); | ||
1001 | if (!dir) | ||
1002 | return -ENOMEM; | ||
1003 | |||
1004 | res = proc_create_data("alignment", S_IWUSR | S_IRUGO, dir, | ||
1005 | &alignment_proc_fops, &se_usermode); | ||
1006 | if (!res) | ||
1007 | return -ENOMEM; | ||
1008 | |||
1009 | res = proc_create_data("kernel_alignment", S_IWUSR | S_IRUGO, dir, | ||
1010 | &alignment_proc_fops, &se_kernmode_warn); | ||
1011 | if (!res) | ||
1012 | return -ENOMEM; | ||
1013 | |||
1014 | return 0; | ||
1015 | } | ||
1016 | |||
1017 | fs_initcall(alignment_init); | ||
1018 | #endif | ||