Commit 1e1f2e5d authored by David Howells's avatar David Howells Committed by Linus Torvalds

[PATCH] FRV: Fujitsu FR-V CPU arch implementation part 6

The attached patches provides part 6 of an architecture implementation
for the Fujitsu FR-V CPU series, configurably as Linux or uClinux.
Signed-Off-By: default avatarDavid Howells <dhowells@redhat.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent e2aba90e
/*
* linux/arch/frvnommu/kernel/sys_frv.c
*
* This file contains various random system calls that
* have a non-standard calling sequence on the Linux/FRV
* platform.
*/
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/sem.h>
#include <linux/msg.h>
#include <linux/shm.h>
#include <linux/stat.h>
#include <linux/mman.h>
#include <linux/file.h>
#include <linux/utsname.h>
#include <linux/syscalls.h>
#include <asm/setup.h>
#include <asm/uaccess.h>
#include <asm/ipc.h>
/*
* sys_pipe() is the normal C calling standard for creating
* a pipe. It's not the way unix traditionally does this, though.
*/
asmlinkage long sys_pipe(unsigned long * fildes)
{
int fd[2];
int error;
error = do_pipe(fd);
if (!error) {
if (copy_to_user(fildes, fd, 2*sizeof(int)))
error = -EFAULT;
}
return error;
}
asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags,
unsigned long fd, unsigned long pgoff)
{
int error = -EBADF;
struct file * file = NULL;
flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
if (!(flags & MAP_ANONYMOUS)) {
file = fget(fd);
if (!file)
goto out;
}
/* As with sparc32, make sure the shift for mmap2 is constant
(12), no matter what PAGE_SIZE we have.... */
/* But unlike sparc32, don't just silently break if we're
trying to map something we can't */
if (pgoff & ((1<<(PAGE_SHIFT-12))-1))
return -EINVAL;
pgoff >>= (PAGE_SHIFT - 12);
down_write(&current->mm->mmap_sem);
error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
up_write(&current->mm->mmap_sem);
if (file)
fput(file);
out:
return error;
}
#if 0 /* DAVIDM - do we want this */
struct mmap_arg_struct64 {
__u32 addr;
__u32 len;
__u32 prot;
__u32 flags;
__u64 offset; /* 64 bits */
__u32 fd;
};
asmlinkage long sys_mmap64(struct mmap_arg_struct64 *arg)
{
int error = -EFAULT;
struct file * file = NULL;
struct mmap_arg_struct64 a;
unsigned long pgoff;
if (copy_from_user(&a, arg, sizeof(a)))
return -EFAULT;
if ((long)a.offset & ~PAGE_MASK)
return -EINVAL;
pgoff = a.offset >> PAGE_SHIFT;
if ((a.offset >> PAGE_SHIFT) != pgoff)
return -EINVAL;
if (!(a.flags & MAP_ANONYMOUS)) {
error = -EBADF;
file = fget(a.fd);
if (!file)
goto out;
}
a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
down_write(&current->mm->mmap_sem);
error = do_mmap_pgoff(file, a.addr, a.len, a.prot, a.flags, pgoff);
up_write(&current->mm->mmap_sem);
if (file)
fput(file);
out:
return error;
}
#endif
/*
* sys_ipc() is the de-multiplexer for the SysV IPC calls..
*
* This is really horribly ugly.
*/
asmlinkage long sys_ipc(unsigned long call,
unsigned long first,
unsigned long second,
unsigned long third,
void __user *ptr,
unsigned long fifth)
{
int version, ret;
version = call >> 16; /* hack for backward compatibility */
call &= 0xffff;
switch (call) {
case SEMOP:
return sys_semtimedop(first, (struct sembuf __user *)ptr, second, NULL);
case SEMTIMEDOP:
return sys_semtimedop(first, (struct sembuf __user *)ptr, second,
(const struct timespec __user *)fifth);
case SEMGET:
return sys_semget (first, second, third);
case SEMCTL: {
union semun fourth;
if (!ptr)
return -EINVAL;
if (get_user(fourth.__pad, (void * __user *) ptr))
return -EFAULT;
return sys_semctl (first, second, third, fourth);
}
case MSGSND:
return sys_msgsnd (first, (struct msgbuf __user *) ptr,
second, third);
case MSGRCV:
switch (version) {
case 0: {
struct ipc_kludge tmp;
if (!ptr)
return -EINVAL;
if (copy_from_user(&tmp,
(struct ipc_kludge __user *) ptr,
sizeof (tmp)))
return -EFAULT;
return sys_msgrcv (first, tmp.msgp, second,
tmp.msgtyp, third);
}
default:
return sys_msgrcv (first,
(struct msgbuf __user *) ptr,
second, fifth, third);
}
case MSGGET:
return sys_msgget ((key_t) first, second);
case MSGCTL:
return sys_msgctl (first, second, (struct msqid_ds __user *) ptr);
case SHMAT:
switch (version) {
default: {
ulong raddr;
ret = do_shmat (first, (char __user *) ptr, second, &raddr);
if (ret)
return ret;
return put_user (raddr, (ulong __user *) third);
}
case 1: /* iBCS2 emulator entry point */
if (!segment_eq(get_fs(), get_ds()))
return -EINVAL;
/* The "(ulong *) third" is valid _only_ because of the kernel segment thing */
return do_shmat (first, (char __user *) ptr, second, (ulong *) third);
}
case SHMDT:
return sys_shmdt ((char __user *)ptr);
case SHMGET:
return sys_shmget (first, second, third);
case SHMCTL:
return sys_shmctl (first, second,
(struct shmid_ds __user *) ptr);
default:
return -ENOSYS;
}
}
/* sysctl.c: implementation of /proc/sys files relating to FRV specifically
*
* Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/config.h>
#include <linux/slab.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
#include <asm/uaccess.h>
static const char frv_cache_wback[] = "wback";
static const char frv_cache_wthru[] = "wthru";
static void frv_change_dcache_mode(unsigned long newmode)
{
unsigned long flags, hsr0;
local_irq_save(flags);
hsr0 = __get_HSR(0);
hsr0 &= ~HSR0_DCE;
__set_HSR(0, hsr0);
asm volatile(" dcef @(gr0,gr0),#1 \n"
" membar \n"
: : : "memory"
);
hsr0 = (hsr0 & ~HSR0_CBM) | newmode;
__set_HSR(0, hsr0);
hsr0 |= HSR0_DCE;
__set_HSR(0, hsr0);
local_irq_restore(flags);
//printk("HSR0 now %08lx\n", hsr0);
}
/*****************************************************************************/
/*
* handle requests to dynamically switch the write caching mode delivered by /proc
*/
static int procctl_frv_cachemode(ctl_table *table, int write, struct file *filp,
void *buffer, size_t *lenp, loff_t *ppos)
{
unsigned long hsr0;
char buff[8];
int len;
len = *lenp;
if (write) {
/* potential state change */
if (len <= 1 || len > sizeof(buff) - 1)
return -EINVAL;
if (copy_from_user(buff, buffer, len) != 0)
return -EFAULT;
if (buff[len - 1] == '\n')
buff[len - 1] = '\0';
else
buff[len] = '\0';
if (strcmp(buff, frv_cache_wback) == 0) {
/* switch dcache into write-back mode */
frv_change_dcache_mode(HSR0_CBM_COPY_BACK);
return 0;
}
if (strcmp(buff, frv_cache_wthru) == 0) {
/* switch dcache into write-through mode */
frv_change_dcache_mode(HSR0_CBM_WRITE_THRU);
return 0;
}
return -EINVAL;
}
/* read the state */
if (filp->f_pos > 0) {
*lenp = 0;
return 0;
}
hsr0 = __get_HSR(0);
switch (hsr0 & HSR0_CBM) {
case HSR0_CBM_WRITE_THRU:
memcpy(buff, frv_cache_wthru, sizeof(frv_cache_wthru) - 1);
buff[sizeof(frv_cache_wthru) - 1] = '\n';
len = sizeof(frv_cache_wthru);
break;
default:
memcpy(buff, frv_cache_wback, sizeof(frv_cache_wback) - 1);
buff[sizeof(frv_cache_wback) - 1] = '\n';
len = sizeof(frv_cache_wback);
break;
}
if (len > *lenp)
len = *lenp;
if (copy_to_user(buffer, buff, len) != 0)
return -EFAULT;
*lenp = len;
filp->f_pos = len;
return 0;
} /* end procctl_frv_cachemode() */
/*****************************************************************************/
/*
* permit the mm_struct the nominated process is using have its MMU context ID pinned
*/
#ifdef CONFIG_MMU
static int procctl_frv_pin_cxnr(ctl_table *table, int write, struct file *filp,
void *buffer, size_t *lenp, loff_t *ppos)
{
pid_t pid;
char buff[16], *p;
int len;
len = *lenp;
if (write) {
/* potential state change */
if (len <= 1 || len > sizeof(buff) - 1)
return -EINVAL;
if (copy_from_user(buff, buffer, len) != 0)
return -EFAULT;
if (buff[len - 1] == '\n')
buff[len - 1] = '\0';
else
buff[len] = '\0';
pid = simple_strtoul(buff, &p, 10);
if (*p)
return -EINVAL;
return cxn_pin_by_pid(pid);
}
/* read the currently pinned CXN */
if (filp->f_pos > 0) {
*lenp = 0;
return 0;
}
len = snprintf(buff, sizeof(buff), "%d\n", cxn_pinned);
if (len > *lenp)
len = *lenp;
if (copy_to_user(buffer, buff, len) != 0)
return -EFAULT;
*lenp = len;
filp->f_pos = len;
return 0;
} /* end procctl_frv_pin_cxnr() */
#endif
/*
* FR-V specific sysctls
*/
static struct ctl_table frv_table[] =
{
{ 1, "cache-mode", NULL, 0, 0644, NULL, &procctl_frv_cachemode },
#ifdef CONFIG_MMU
{ 2, "pin-cxnr", NULL, 0, 0644, NULL, &procctl_frv_pin_cxnr },
#endif
{ 0 }
};
/*
* Use a temporary sysctl number. Horrid, but will be cleaned up in 2.6
* when all the PM interfaces exist nicely.
*/
#define CTL_FRV 9898
static struct ctl_table frv_dir_table[] =
{
{CTL_FRV, "frv", NULL, 0, 0555, frv_table},
{0}
};
/*
* Initialize power interface
*/
static int __init frv_sysctl_init(void)
{
register_sysctl_table(frv_dir_table, 1);
return 0;
}
__initcall(frv_sysctl_init);
/*
* linux/arch/m68k/kernel/time.c
*
* Copyright (C) 1991, 1992, 1995 Linus Torvalds
*
* This file contains the m68k-specific time handling details.
* Most of the stuff is located in the machine specific files.
*
* 1997-09-10 Updated NTP code according to technical memorandum Jan '96
* "A Kernel Model for Precision Timekeeping" by Dave Mills
*/
#include <linux/config.h> /* CONFIG_HEARTBEAT */
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/param.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/profile.h>
#include <linux/irq.h>
#include <linux/mm.h>
#include <asm/io.h>
#include <asm/timer-regs.h>
#include <asm/mb-regs.h>
#include <asm/mb86943a.h>
#include <asm/irq-routing.h>
#include <linux/timex.h>
#define TICK_SIZE (tick_nsec / 1000)
extern unsigned long wall_jiffies;
u64 jiffies_64 = INITIAL_JIFFIES;
EXPORT_SYMBOL(jiffies_64);
unsigned long __nongprelbss __clkin_clock_speed_HZ;
unsigned long __nongprelbss __ext_bus_clock_speed_HZ;
unsigned long __nongprelbss __res_bus_clock_speed_HZ;
unsigned long __nongprelbss __sdram_clock_speed_HZ;
unsigned long __nongprelbss __core_bus_clock_speed_HZ;
unsigned long __nongprelbss __core_clock_speed_HZ;
unsigned long __nongprelbss __dsu_clock_speed_HZ;
unsigned long __nongprelbss __serial_clock_speed_HZ;
unsigned long __delay_loops_MHz;
static irqreturn_t timer_interrupt(int irq, void *dummy, struct pt_regs *regs);
static struct irqaction timer_irq = {
timer_interrupt, SA_INTERRUPT, CPU_MASK_NONE, "timer", NULL, NULL
};
static inline int set_rtc_mmss(unsigned long nowtime)
{
return -1;
}
/*
* timer_interrupt() needs to keep up the real-time clock,
* as well as call the "do_timer()" routine every clocktick
*/
static irqreturn_t timer_interrupt(int irq, void *dummy, struct pt_regs * regs)
{
/* last time the cmos clock got updated */
static long last_rtc_update = 0;
/*
* Here we are in the timer irq handler. We just have irqs locally
* disabled but we don't know if the timer_bh is running on the other
* CPU. We need to avoid to SMP race with it. NOTE: we don' t need
* the irq version of write_lock because as just said we have irq
* locally disabled. -arca
*/
write_seqlock(&xtime_lock);
do_timer(regs);
update_process_times(user_mode(regs));
profile_tick(CPU_PROFILING, regs);
/*
* If we have an externally synchronized Linux clock, then update
* CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
* called as close as possible to 500 ms before the new second starts.
*/
if ((time_status & STA_UNSYNC) == 0 &&
xtime.tv_sec > last_rtc_update + 660 &&
(xtime.tv_nsec / 1000) >= 500000 - ((unsigned) TICK_SIZE) / 2 &&
(xtime.tv_nsec / 1000) <= 500000 + ((unsigned) TICK_SIZE) / 2
) {
if (set_rtc_mmss(xtime.tv_sec) == 0)
last_rtc_update = xtime.tv_sec;
else
last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
}
#ifdef CONFIG_HEARTBEAT
static unsigned short n;
n++;
__set_LEDS(n);
#endif /* CONFIG_HEARTBEAT */
write_sequnlock(&xtime_lock);
return IRQ_HANDLED;
}
void time_divisor_init(void)
{
unsigned short base, pre, prediv;
/* set the scheduling timer going */
pre = 1;
prediv = 4;
base = __res_bus_clock_speed_HZ / pre / HZ / (1 << prediv);
__set_TPRV(pre);
__set_TxCKSL_DATA(0, prediv);
__set_TCTR(TCTR_SC_CTR0 | TCTR_RL_RW_LH8 | TCTR_MODE_2);
__set_TCSR_DATA(0, base & 0xff);
__set_TCSR_DATA(0, base >> 8);
}
void time_init(void)
{
unsigned int year, mon, day, hour, min, sec;
extern void arch_gettod(int *year, int *mon, int *day, int *hour, int *min, int *sec);
/* FIX by dqg : Set to zero for platforms that don't have tod */
/* without this time is undefined and can overflow time_t, causing */
/* very stange errors */
year = 1980;
mon = day = 1;
hour = min = sec = 0;
arch_gettod (&year, &mon, &day, &hour, &min, &sec);
if ((year += 1900) < 1970)
year += 100;
xtime.tv_sec = mktime(year, mon, day, hour, min, sec);
xtime.tv_nsec = 0;
/* install scheduling interrupt handler */
setup_irq(IRQ_CPU_TIMER0, &timer_irq);
time_divisor_init();
}
/*
* This version of gettimeofday has near microsecond resolution.
*/
void do_gettimeofday(struct timeval *tv)
{
unsigned long seq;
unsigned long usec, sec;
unsigned long max_ntp_tick;
do {
unsigned long lost;
seq = read_seqbegin(&xtime_lock);
usec = 0;
lost = jiffies - wall_jiffies;
/*
* If time_adjust is negative then NTP is slowing the clock
* so make sure not to go into next possible interval.
* Better to lose some accuracy than have time go backwards..
*/
if (unlikely(time_adjust < 0)) {
max_ntp_tick = (USEC_PER_SEC / HZ) - tickadj;
usec = min(usec, max_ntp_tick);
if (lost)
usec += lost * max_ntp_tick;
}
else if (unlikely(lost))
usec += lost * (USEC_PER_SEC / HZ);
sec = xtime.tv_sec;
usec += (xtime.tv_nsec / 1000);
} while (read_seqretry(&xtime_lock, seq));
while (usec >= 1000000) {
usec -= 1000000;
sec++;
}
tv->tv_sec = sec;
tv->tv_usec = usec;
}
int do_settimeofday(struct timespec *tv)
{
time_t wtm_sec, sec = tv->tv_sec;
long wtm_nsec, nsec = tv->tv_nsec;
if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
return -EINVAL;
write_seqlock_irq(&xtime_lock);
/*
* This is revolting. We need to set "xtime" correctly. However, the
* value in this location is the value at the most recent update of
* wall time. Discover what correction gettimeofday() would have
* made, and then undo it!
*/
nsec -= 0 * NSEC_PER_USEC;
nsec -= (jiffies - wall_jiffies) * TICK_NSEC;
wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);
set_normalized_timespec(&xtime, sec, nsec);
set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
time_adjust = 0; /* stop active adjtime() */
time_status |= STA_UNSYNC;
time_maxerror = NTP_PHASE_LIMIT;
time_esterror = NTP_PHASE_LIMIT;
write_sequnlock_irq(&xtime_lock);
clock_was_set();
return 0;
}
/*
* Scheduler clock - returns current time in nanosec units.
*/
unsigned long long sched_clock(void)
{
return jiffies_64 * (1000000000 / HZ);
}
/* traps.c: high-level exception handler for FR-V
*
* Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/types.h>
#include <linux/user.h>
#include <linux/string.h>
#include <linux/linkage.h>
#include <linux/init.h>
#include <asm/setup.h>
#include <asm/fpu.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/siginfo.h>
#include <asm/unaligned.h>
void show_backtrace(struct pt_regs *, unsigned long);
extern asmlinkage void __break_hijack_kernel_event(void);
/*****************************************************************************/
/*
* instruction access error
*/
asmlinkage void insn_access_error(unsigned long esfr1, unsigned long epcr0, unsigned long esr0)
{
siginfo_t info;
die_if_kernel("-- Insn Access Error --\n"
"EPCR0 : %08lx\n"
"ESR0 : %08lx\n",
epcr0, esr0);
info.si_signo = SIGSEGV;
info.si_code = SEGV_ACCERR;
info.si_errno = 0;
info.si_addr = (void *) ((epcr0 & EPCR0_V) ? (epcr0 & EPCR0_PC) : __frame->pc);
force_sig_info(info.si_signo, &info, current);
} /* end insn_access_error() */
/*****************************************************************************/
/*
* handler for:
* - illegal instruction
* - privileged instruction
* - unsupported trap
* - debug exceptions
*/
asmlinkage void illegal_instruction(unsigned long esfr1, unsigned long epcr0, unsigned long esr0)
{
siginfo_t info;
die_if_kernel("-- Illegal Instruction --\n"
"EPCR0 : %08lx\n"
"ESR0 : %08lx\n"
"ESFR1 : %08lx\n",
epcr0, esr0, esfr1);
info.si_errno = 0;
info.si_addr = (void *) ((epcr0 & EPCR0_PC) ? (epcr0 & EPCR0_PC) : __frame->pc);
switch (__frame->tbr & TBR_TT) {
case TBR_TT_ILLEGAL_INSTR:
info.si_signo = SIGILL;
info.si_code = ILL_ILLOPC;
break;
case TBR_TT_PRIV_INSTR:
info.si_signo = SIGILL;
info.si_code = ILL_PRVOPC;
break;
case TBR_TT_TRAP2 ... TBR_TT_TRAP126:
info.si_signo = SIGILL;
info.si_code = ILL_ILLTRP;
break;
/* GDB uses "tira gr0, #1" as a breakpoint instruction. */
case TBR_TT_TRAP1:
case TBR_TT_BREAK:
info.si_signo = SIGTRAP;
info.si_code =
(__frame->__status & REG__STATUS_STEPPED) ? TRAP_TRACE : TRAP_BRKPT;
break;
}
force_sig_info(info.si_signo, &info, current);
} /* end illegal_instruction() */
/*****************************************************************************/
/*
*
*/
asmlinkage void media_exception(unsigned long msr0, unsigned long msr1)
{
siginfo_t info;
die_if_kernel("-- Media Exception --\n"
"MSR0 : %08lx\n"
"MSR1 : %08lx\n",
msr0, msr1);
info.si_signo = SIGFPE;
info.si_code = FPE_MDAOVF;
info.si_errno = 0;
info.si_addr = (void *) __frame->pc;
force_sig_info(info.si_signo, &info, current);
} /* end media_exception() */
/*****************************************************************************/
/*
* instruction or data access exception
*/
asmlinkage void memory_access_exception(unsigned long esr0,
unsigned long ear0,
unsigned long epcr0)
{
siginfo_t info;
#ifdef CONFIG_MMU
unsigned long fixup;
if ((esr0 & ESRx_EC) == ESRx_EC_DATA_ACCESS)
if (handle_misalignment(esr0, ear0, epcr0) == 0)
return;
if ((fixup = search_exception_table(__frame->pc)) != 0) {
__frame->pc = fixup;
return;
}
#endif
die_if_kernel("-- Memory Access Exception --\n"
"ESR0 : %08lx\n"
"EAR0 : %08lx\n"
"EPCR0 : %08lx\n",
esr0, ear0, epcr0);
info.si_signo = SIGSEGV;
info.si_code = SEGV_ACCERR;
info.si_errno = 0;
info.si_addr = NULL;
if ((esr0 & (ESRx_VALID | ESR0_EAV)) == (ESRx_VALID | ESR0_EAV))
info.si_addr = (void *) ear0;
force_sig_info(info.si_signo, &info, current);
} /* end memory_access_exception() */
/*****************************************************************************/
/*
* data access error
* - double-word data load from CPU control area (0xFExxxxxx)
* - read performed on inactive or self-refreshing SDRAM
* - error notification from slave device
* - misaligned address
* - access to out of bounds memory region
* - user mode accessing privileged memory region
* - write to R/O memory region
*/
asmlinkage void data_access_error(unsigned long esfr1, unsigned long esr15, unsigned long ear15)
{
siginfo_t info;
die_if_kernel("-- Data Access Error --\n"
"ESR15 : %08lx\n"
"EAR15 : %08lx\n",
esr15, ear15);
info.si_signo = SIGSEGV;
info.si_code = SEGV_ACCERR;
info.si_errno = 0;
info.si_addr = (void *)
(((esr15 & (ESRx_VALID|ESR15_EAV)) == (ESRx_VALID|ESR15_EAV)) ? ear15 : 0);
force_sig_info(info.si_signo, &info, current);
} /* end data_access_error() */
/*****************************************************************************/
/*
* data store error - should only happen if accessing inactive or self-refreshing SDRAM
*/
asmlinkage void data_store_error(unsigned long esfr1, unsigned long esr15)
{
die_if_kernel("-- Data Store Error --\n"
"ESR15 : %08lx\n",
esr15);
BUG();
} /* end data_store_error() */
/*****************************************************************************/
/*
*
*/
asmlinkage void division_exception(unsigned long esfr1, unsigned long esr0, unsigned long isr)
{
siginfo_t info;
die_if_kernel("-- Division Exception --\n"
"ESR0 : %08lx\n"
"ISR : %08lx\n",
esr0, isr);
info.si_signo = SIGFPE;
info.si_code = FPE_INTDIV;
info.si_errno = 0;
info.si_addr = (void *) __frame->pc;
force_sig_info(info.si_signo, &info, current);
} /* end division_exception() */
/*****************************************************************************/
/*
*
*/
asmlinkage void compound_exception(unsigned long esfr1,
unsigned long esr0, unsigned long esr14, unsigned long esr15,
unsigned long msr0, unsigned long msr1)
{
die_if_kernel("-- Compound Exception --\n"
"ESR0 : %08lx\n"
"ESR15 : %08lx\n"
"ESR15 : %08lx\n"
"MSR0 : %08lx\n"
"MSR1 : %08lx\n",
esr0, esr14, esr15, msr0, msr1);
BUG();
} /* end compound_exception() */
/*****************************************************************************/
/*
* The architecture-independent backtrace generator
*/
void dump_stack(void)
{
show_stack(NULL, NULL);
}
void show_stack(struct task_struct *task, unsigned long *sp)
{
}
void show_trace_task(struct task_struct *tsk)
{
printk("CONTEXT: stack=0x%lx frame=0x%p LR=0x%lx RET=0x%lx\n",
tsk->thread.sp, tsk->thread.frame, tsk->thread.lr, tsk->thread.sched_lr);
}
static const char *regnames[] = {
"PSR ", "ISR ", "CCR ", "CCCR",
"LR ", "LCR ", "PC ", "_stt",
"sys ", "GR8*", "GNE0", "GNE1",
"IACH", "IACL",
"TBR ", "SP ", "FP ", "GR3 ",
"GR4 ", "GR5 ", "GR6 ", "GR7 ",
"GR8 ", "GR9 ", "GR10", "GR11",
"GR12", "GR13", "GR14", "GR15",
"GR16", "GR17", "GR18", "GR19",
"GR20", "GR21", "GR22", "GR23",
"GR24", "GR25", "GR26", "GR27",
"EFRM", "CURR", "GR30", "BFRM"
};
void show_regs(struct pt_regs *regs)
{
uint32_t *reg;
int loop;
printk("\n");
printk("Frame: @%08x [%s]\n",
(uint32_t) regs,
regs->psr & PSR_S ? "kernel" : "user");
reg = (uint32_t *) regs;
for (loop = 0; loop < REG__END; loop++) {
printk("%s %08x", regnames[loop + 0], reg[loop + 0]);
if (loop == REG__END - 1 || loop % 5 == 4)
printk("\n");
else
printk(" | ");
}
printk("Process %s (pid: %d)\n", current->comm, current->pid);
}
void die_if_kernel(const char *str, ...)
{
char buffer[256];
va_list va;
if (user_mode(__frame))
return;
va_start(va, str);
vsprintf(buffer, str, va);
va_end(va);
console_verbose();
printk("\n===================================\n");
printk("%s\n", buffer);
show_backtrace(__frame, 0);
__break_hijack_kernel_event();
do_exit(SIGSEGV);
}
/*****************************************************************************/
/*
* dump the contents of an exception frame
*/
static void show_backtrace_regs(struct pt_regs *frame)
{
uint32_t *reg;
int loop;
/* print the registers for this frame */
printk("<-- %s Frame: @%p -->\n",
frame->psr & PSR_S ? "Kernel Mode" : "User Mode",
frame);
reg = (uint32_t *) frame;
for (loop = 0; loop < REG__END; loop++) {
printk("%s %08x", regnames[loop + 0], reg[loop + 0]);
if (loop == REG__END - 1 || loop % 5 == 4)
printk("\n");
else
printk(" | ");
}
printk("--------\n");
} /* end show_backtrace_regs() */
/*****************************************************************************/
/*
* generate a backtrace of the kernel stack
*/
void show_backtrace(struct pt_regs *frame, unsigned long sp)
{
struct pt_regs *frame0;
unsigned long tos = 0, stop = 0, base;
int format;
base = ((((unsigned long) frame) + 8191) & ~8191) - sizeof(struct user_context);
frame0 = (struct pt_regs *) base;
if (sp) {
tos = sp;
stop = (unsigned long) frame;
}
printk("\nProcess %s (pid: %d)\n\n", current->comm, current->pid);
for (;;) {
/* dump stack segment between frames */
//printk("%08lx -> %08lx\n", tos, stop);
format = 0;
while (tos < stop) {
if (format == 0)
printk(" %04lx :", tos & 0xffff);
printk(" %08lx", *(unsigned long *) tos);
tos += 4;
format++;
if (format == 8) {
printk("\n");
format = 0;
}
}
if (format > 0)
printk("\n");
/* dump frame 0 outside of the loop */
if (frame == frame0)
break;
tos = frame->sp;
if (((unsigned long) frame) + sizeof(*frame) != tos) {
printk("-- TOS %08lx does not follow frame %p --\n",
tos, frame);
break;
}
show_backtrace_regs(frame);
/* dump the stack between this frame and the next */
stop = (unsigned long) frame->next_frame;
if (stop != base &&
(stop < tos ||
stop > base ||
(stop < base && stop + sizeof(*frame) > base) ||
stop & 3)) {
printk("-- next_frame %08lx is invalid (range %08lx-%08lx) --\n",
stop, tos, base);
break;
}
/* move to next frame */
frame = frame->next_frame;
}
/* we can always dump frame 0, even if the rest of the stack is corrupt */
show_backtrace_regs(frame0);
} /* end show_backtrace() */
/*****************************************************************************/
/*
* initialise traps
*/
void __init trap_init (void)
{
} /* end trap_init() */
/* uaccess.c: userspace access functions
*
* Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/mm.h>
#include <asm/uaccess.h>
/*****************************************************************************/
/*
* copy a null terminated string from userspace
*/
long strncpy_from_user(char *dst, const char *src, long count)
{
unsigned long max;
char *p, ch;
long err = -EFAULT;
if (count < 0)
BUG();
p = dst;
#ifndef CONFIG_MMU
if ((unsigned long) src < memory_start)
goto error;
#endif
if ((unsigned long) src >= get_addr_limit())
goto error;
max = get_addr_limit() - (unsigned long) src;
if ((unsigned long) count > max) {
memset(dst + max, 0, count - max);
count = max;
}
err = 0;
for (; count > 0; count--, p++, src++) {
__get_user_asm(err, ch, src, "ub", "=r");
if (err < 0)
goto error;
if (!ch)
break;
*p = ch;
}
err = p - dst; /* return length excluding NUL */
error:
if (count > 0)
memset(p, 0, count); /* clear remainder of buffer [security] */
return err;
} /* end strncpy_from_user() */
/*****************************************************************************/
/*
* Return the size of a string (including the ending 0)
*
* Return 0 on exception, a value greater than N if too long
*/
long strnlen_user(const char *src, long count)
{
const char *p;
long err = 0;
char ch;
if (count < 0)
BUG();
#ifndef CONFIG_MMU
if ((unsigned long) src < memory_start)
return 0;
#endif
if ((unsigned long) src >= get_addr_limit())
return 0;
for (p = src; count > 0; count--, p++) {
__get_user_asm(err, ch, p, "ub", "=r");
if (err < 0)
return 0;
if (!ch)
break;
}
return p - src + 1; /* return length including NUL */
} /* end strnlen_user() */
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