Skip to content

Commit

Permalink
sh: Split out the unaligned counters and user bits.
Browse files Browse the repository at this point in the history
This splits out the unaligned access counters and userspace bits in to
their own generic interface, which will allow them to be wired up on sh64
too.

Signed-off-by: Paul Mundt <lethal@linux-sh.org>
  • Loading branch information
Paul Mundt committed Jan 12, 2010
1 parent 776258d commit a99eae5
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 135 deletions.
21 changes: 21 additions & 0 deletions arch/sh/include/asm/alignment.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#ifndef __ASM_SH_ALIGNMENT_H
#define __ASM_SH_ALIGNMENT_H

#include <linux/types.h>

extern void inc_unaligned_byte_access(void);
extern void inc_unaligned_word_access(void);
extern void inc_unaligned_dword_access(void);
extern void inc_unaligned_multi_access(void);
extern void inc_unaligned_user_access(void);
extern void inc_unaligned_kernel_access(void);

#define UM_WARN (1 << 0)
#define UM_FIXUP (1 << 1)
#define UM_SIGNAL (1 << 2)

extern unsigned int unaligned_user_action(void);

extern void unaligned_fixups_notify(struct task_struct *, insn_size_t, struct pt_regs *);

#endif /* __ASM_SH_ALIGNMENT_H */
151 changes: 17 additions & 134 deletions arch/sh/kernel/traps_32.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,10 @@
#include <linux/kdebug.h>
#include <linux/kexec.h>
#include <linux/limits.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/sysfs.h>
#include <linux/uaccess.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/alignment.h>
#include <asm/fpu.h>
#include <asm/kprobes.h>
#include <asm/sh_bios.h>
Expand All @@ -48,73 +47,6 @@
#define TRAP_ILLEGAL_SLOT_INST 13
#endif

static unsigned long se_user;
static unsigned long se_sys;
static unsigned long se_half;
static unsigned long se_word;
static unsigned long se_dword;
static unsigned long se_multi;
/* bitfield: 1: warn 2: fixup 4: signal -> combinations 2|4 && 1|2|4 are not
valid! */
static int se_usermode = 3;
/* 0: no warning 1: print a warning message, disabled by default */
static int se_kernmode_warn;

#ifdef CONFIG_PROC_FS
static const char *se_usermode_action[] = {
"ignored",
"warn",
"fixup",
"fixup+warn",
"signal",
"signal+warn"
};

static int alignment_proc_show(struct seq_file *m, void *v)
{
seq_printf(m, "User:\t\t%lu\n", se_user);
seq_printf(m, "System:\t\t%lu\n", se_sys);
seq_printf(m, "Half:\t\t%lu\n", se_half);
seq_printf(m, "Word:\t\t%lu\n", se_word);
seq_printf(m, "DWord:\t\t%lu\n", se_dword);
seq_printf(m, "Multi:\t\t%lu\n", se_multi);
seq_printf(m, "User faults:\t%i (%s)\n", se_usermode,
se_usermode_action[se_usermode]);
seq_printf(m, "Kernel faults:\t%i (fixup%s)\n", se_kernmode_warn,
se_kernmode_warn ? "+warn" : "");
return 0;
}

static int alignment_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, alignment_proc_show, NULL);
}

static ssize_t alignment_proc_write(struct file *file,
const char __user *buffer, size_t count, loff_t *pos)
{
int *data = PDE(file->f_path.dentry->d_inode)->data;
char mode;

if (count > 0) {
if (get_user(mode, buffer))
return -EFAULT;
if (mode >= '0' && mode <= '5')
*data = mode - '0';
}
return count;
}

static const struct file_operations alignment_proc_fops = {
.owner = THIS_MODULE,
.open = alignment_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.write = alignment_proc_write,
};
#endif

static void dump_mem(const char *str, unsigned long bottom, unsigned long top)
{
unsigned long p;
Expand Down Expand Up @@ -266,10 +198,10 @@ static int handle_unaligned_ins(insn_size_t instruction, struct pt_regs *regs,
count = 1<<(instruction&3);

switch (count) {
case 1: se_half += 1; break;
case 2: se_word += 1; break;
case 4: se_dword += 1; break;
case 8: se_multi += 1; break; /* ??? */
case 1: inc_unaligned_byte_access(); break;
case 2: inc_unaligned_word_access(); break;
case 4: inc_unaligned_dword_access(); break;
case 8: inc_unaligned_multi_access(); break;
}

ret = -EFAULT;
Expand Down Expand Up @@ -453,18 +385,8 @@ int handle_unaligned_access(insn_size_t instruction, struct pt_regs *regs,
rm = regs->regs[index];

/* shout about fixups */
if (!expected) {
if (user_mode(regs) && (se_usermode & 1) && printk_ratelimit())
pr_notice("Fixing up unaligned userspace access "
"in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n",
current->comm, task_pid_nr(current),
(void *)regs->pc, instruction);
else if (se_kernmode_warn && printk_ratelimit())
pr_notice("Fixing up unaligned kernel access "
"in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n",
current->comm, task_pid_nr(current),
(void *)regs->pc, instruction);
}
if (!expected)
unaligned_fixups_notify(current, instruction, regs);

ret = -EFAULT;
switch (instruction&0xF000) {
Expand Down Expand Up @@ -617,10 +539,10 @@ asmlinkage void do_address_error(struct pt_regs *regs,

if (user_mode(regs)) {
int si_code = BUS_ADRERR;
unsigned int user_action;

local_irq_enable();

se_user += 1;
inc_unaligned_user_access();

set_fs(USER_DS);
if (copy_from_user(&instruction, (insn_size_t *)(regs->pc & ~1),
Expand All @@ -631,16 +553,12 @@ asmlinkage void do_address_error(struct pt_regs *regs,
set_fs(oldfs);

/* shout about userspace fixups */
if (se_usermode & 1)
printk(KERN_NOTICE "Unaligned userspace access "
"in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n",
current->comm, current->pid, (void *)regs->pc,
instruction);
unaligned_fixups_notify(current, instruction, regs);

if (se_usermode & 2)
user_action = unaligned_user_action();
if (user_action & UM_FIXUP)
goto fixup;

if (se_usermode & 4)
if (user_action & UM_SIGNAL)
goto uspace_segv;
else {
/* ignore */
Expand All @@ -660,7 +578,7 @@ asmlinkage void do_address_error(struct pt_regs *regs,
&user_mem_access, 0);
set_fs(oldfs);

if (tmp==0)
if (tmp == 0)
return; /* sorted */
uspace_segv:
printk(KERN_NOTICE "Sending SIGBUS to \"%s\" due to unaligned "
Expand All @@ -673,7 +591,7 @@ asmlinkage void do_address_error(struct pt_regs *regs,
info.si_addr = (void __user *)address;
force_sig_info(SIGBUS, &info, current);
} else {
se_sys += 1;
inc_unaligned_kernel_access();

if (regs->pc & 1)
die("unaligned program counter", regs, error_code);
Expand All @@ -688,11 +606,7 @@ asmlinkage void do_address_error(struct pt_regs *regs,
die("insn faulting in do_address_error", regs, 0);
}

if (se_kernmode_warn)
printk(KERN_NOTICE "Unaligned kernel access "
"on behalf of \"%s\" pid=%d pc=0x%p ins=0x%04hx\n",
current->comm, current->pid, (void *)regs->pc,
instruction);
unaligned_fixups_notify(current, instruction, regs);

handle_unaligned_access(instruction, regs,
&user_mem_access, 0);
Expand Down Expand Up @@ -964,34 +878,3 @@ void dump_stack(void)
show_stack(NULL, NULL);
}
EXPORT_SYMBOL(dump_stack);

#ifdef CONFIG_PROC_FS
/*
* This needs to be done after sysctl_init, otherwise sys/ will be
* overwritten. Actually, this shouldn't be in sys/ at all since
* it isn't a sysctl, and it doesn't contain sysctl information.
* We now locate it in /proc/cpu/alignment instead.
*/
static int __init alignment_init(void)
{
struct proc_dir_entry *dir, *res;

dir = proc_mkdir("cpu", NULL);
if (!dir)
return -ENOMEM;

res = proc_create_data("alignment", S_IWUSR | S_IRUGO, dir,
&alignment_proc_fops, &se_usermode);
if (!res)
return -ENOMEM;

res = proc_create_data("kernel_alignment", S_IWUSR | S_IRUGO, dir,
&alignment_proc_fops, &se_kernmode_warn);
if (!res)
return -ENOMEM;

return 0;
}

fs_initcall(alignment_init);
#endif
2 changes: 1 addition & 1 deletion arch/sh/mm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Makefile for the Linux SuperH-specific parts of the memory manager.
#

obj-y := cache.o init.o consistent.o mmap.o
obj-y := alignment.o cache.o init.o consistent.o mmap.o

cacheops-$(CONFIG_CPU_SH2) := cache-sh2.o
cacheops-$(CONFIG_CPU_SH2A) := cache-sh2a.o
Expand Down
Loading

0 comments on commit a99eae5

Please sign in to comment.