Initial commit; kernel source import

This commit is contained in:
Nathan
2025-04-06 23:50:55 -05:00
commit 25c6d769f4
45093 changed files with 18199410 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
#
# Makefile for the MN10300-specific core kernel code
#
extra-y := head.o vmlinux.lds
fpu-obj-y := fpu-nofpu.o fpu-nofpu-low.o
fpu-obj-$(CONFIG_FPU) := fpu.o fpu-low.o
obj-y := process.o signal.o entry.o traps.o irq.o \
ptrace.o setup.o time.o sys_mn10300.o io.o \
switch_to.o mn10300_ksyms.o $(fpu-obj-y) \
csrc-mn10300.o cevt-mn10300.o
obj-$(CONFIG_SMP) += smp.o smp-low.o
obj-$(CONFIG_MN10300_WD_TIMER) += mn10300-watchdog.o mn10300-watchdog-low.o
obj-$(CONFIG_MN10300_TTYSM) += mn10300-serial.o mn10300-serial-low.o \
mn10300-debug.o
obj-$(CONFIG_GDBSTUB) += gdb-stub.o gdb-low.o
obj-$(CONFIG_GDBSTUB_ON_TTYSx) += gdb-io-serial.o gdb-io-serial-low.o
obj-$(CONFIG_GDBSTUB_ON_TTYSMx) += gdb-io-ttysm.o gdb-io-ttysm-low.o
obj-$(CONFIG_MN10300_RTC) += rtc.o
obj-$(CONFIG_PROFILE) += profile.o profile-low.o
obj-$(CONFIG_MODULES) += module.o
obj-$(CONFIG_KPROBES) += kprobes.o
obj-$(CONFIG_KGDB) += kgdb.o

View File

@@ -0,0 +1,110 @@
/*
* Generate definitions needed by assembly language modules.
* This code generates raw asm output which is post-processed
* to extract and format the required data.
*/
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/personality.h>
#include <linux/kbuild.h>
#include <asm/ucontext.h>
#include <asm/processor.h>
#include <asm/thread_info.h>
#include <asm/ptrace.h>
#include "sigframe.h"
#include "mn10300-serial.h"
void foo(void)
{
OFFSET(SIGCONTEXT_d0, sigcontext, d0);
OFFSET(SIGCONTEXT_d1, sigcontext, d1);
BLANK();
OFFSET(TI_task, thread_info, task);
OFFSET(TI_exec_domain, thread_info, exec_domain);
OFFSET(TI_frame, thread_info, frame);
OFFSET(TI_flags, thread_info, flags);
OFFSET(TI_cpu, thread_info, cpu);
OFFSET(TI_preempt_count, thread_info, preempt_count);
OFFSET(TI_addr_limit, thread_info, addr_limit);
OFFSET(TI_restart_block, thread_info, restart_block);
BLANK();
OFFSET(REG_D0, pt_regs, d0);
OFFSET(REG_D1, pt_regs, d1);
OFFSET(REG_D2, pt_regs, d2);
OFFSET(REG_D3, pt_regs, d3);
OFFSET(REG_A0, pt_regs, a0);
OFFSET(REG_A1, pt_regs, a1);
OFFSET(REG_A2, pt_regs, a2);
OFFSET(REG_A3, pt_regs, a3);
OFFSET(REG_E0, pt_regs, e0);
OFFSET(REG_E1, pt_regs, e1);
OFFSET(REG_E2, pt_regs, e2);
OFFSET(REG_E3, pt_regs, e3);
OFFSET(REG_E4, pt_regs, e4);
OFFSET(REG_E5, pt_regs, e5);
OFFSET(REG_E6, pt_regs, e6);
OFFSET(REG_E7, pt_regs, e7);
OFFSET(REG_SP, pt_regs, sp);
OFFSET(REG_EPSW, pt_regs, epsw);
OFFSET(REG_PC, pt_regs, pc);
OFFSET(REG_LAR, pt_regs, lar);
OFFSET(REG_LIR, pt_regs, lir);
OFFSET(REG_MDR, pt_regs, mdr);
OFFSET(REG_MCVF, pt_regs, mcvf);
OFFSET(REG_MCRL, pt_regs, mcrl);
OFFSET(REG_MCRH, pt_regs, mcrh);
OFFSET(REG_MDRQ, pt_regs, mdrq);
OFFSET(REG_ORIG_D0, pt_regs, orig_d0);
OFFSET(REG_NEXT, pt_regs, next);
DEFINE(REG__END, sizeof(struct pt_regs));
BLANK();
OFFSET(THREAD_UREGS, thread_struct, uregs);
OFFSET(THREAD_PC, thread_struct, pc);
OFFSET(THREAD_SP, thread_struct, sp);
OFFSET(THREAD_A3, thread_struct, a3);
OFFSET(THREAD_USP, thread_struct, usp);
#ifdef CONFIG_FPU
OFFSET(THREAD_FPU_FLAGS, thread_struct, fpu_flags);
OFFSET(THREAD_FPU_STATE, thread_struct, fpu_state);
DEFINE(__THREAD_USING_FPU, THREAD_USING_FPU);
DEFINE(__THREAD_HAS_FPU, THREAD_HAS_FPU);
#endif /* CONFIG_FPU */
BLANK();
OFFSET(TASK_THREAD, task_struct, thread);
BLANK();
DEFINE(CLONE_VM_asm, CLONE_VM);
DEFINE(CLONE_FS_asm, CLONE_FS);
DEFINE(CLONE_FILES_asm, CLONE_FILES);
DEFINE(CLONE_SIGHAND_asm, CLONE_SIGHAND);
DEFINE(CLONE_UNTRACED_asm, CLONE_UNTRACED);
DEFINE(SIGCHLD_asm, SIGCHLD);
BLANK();
OFFSET(EXEC_DOMAIN_handler, exec_domain, handler);
OFFSET(RT_SIGFRAME_sigcontext, rt_sigframe, uc.uc_mcontext);
DEFINE(PAGE_SIZE_asm, PAGE_SIZE);
OFFSET(__rx_buffer, mn10300_serial_port, rx_buffer);
OFFSET(__rx_inp, mn10300_serial_port, rx_inp);
OFFSET(__rx_outp, mn10300_serial_port, rx_outp);
OFFSET(__uart_state, mn10300_serial_port, uart.state);
OFFSET(__tx_xchar, mn10300_serial_port, tx_xchar);
OFFSET(__tx_flags, mn10300_serial_port, tx_flags);
OFFSET(__intr_flags, mn10300_serial_port, intr_flags);
OFFSET(__rx_icr, mn10300_serial_port, rx_icr);
OFFSET(__tx_icr, mn10300_serial_port, tx_icr);
OFFSET(__tm_icr, mn10300_serial_port, _tmicr);
OFFSET(__iobase, mn10300_serial_port, _iobase);
DEFINE(__UART_XMIT_SIZE, UART_XMIT_SIZE);
OFFSET(__xmit_buffer, uart_state, xmit.buf);
OFFSET(__xmit_head, uart_state, xmit.head);
OFFSET(__xmit_tail, uart_state, xmit.tail);
}

View File

@@ -0,0 +1,142 @@
/* MN10300 clockevents
*
* Copyright (C) 2010 Red Hat, Inc. All Rights Reserved.
* Written by Mark Salter (msalter@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/percpu.h>
#include <linux/smp.h>
#include <asm/timex.h>
#include "internal.h"
#ifdef CONFIG_SMP
#if (CONFIG_NR_CPUS > 2) && !defined(CONFIG_GEENERIC_CLOCKEVENTS_BROADCAST)
#error "This doesn't scale well! Need per-core local timers."
#endif
#else /* CONFIG_SMP */
#define stop_jiffies_counter1()
#define reload_jiffies_counter1(x)
#define TMJC1IRQ TMJCIRQ
#endif
static int next_event(unsigned long delta,
struct clock_event_device *evt)
{
unsigned int cpu = smp_processor_id();
if (cpu == 0) {
stop_jiffies_counter();
reload_jiffies_counter(delta - 1);
} else {
stop_jiffies_counter1();
reload_jiffies_counter1(delta - 1);
}
return 0;
}
static void set_clock_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
/* Nothing to do ... */
}
static DEFINE_PER_CPU(struct clock_event_device, mn10300_clockevent_device);
static DEFINE_PER_CPU(struct irqaction, timer_irq);
static irqreturn_t timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *cd;
unsigned int cpu = smp_processor_id();
if (cpu == 0)
stop_jiffies_counter();
else
stop_jiffies_counter1();
cd = &per_cpu(mn10300_clockevent_device, cpu);
cd->event_handler(cd);
return IRQ_HANDLED;
}
static void event_handler(struct clock_event_device *dev)
{
}
static inline void setup_jiffies_interrupt(int irq,
struct irqaction *action)
{
u16 tmp;
setup_irq(irq, action);
set_intr_level(irq, NUM2GxICR_LEVEL(CONFIG_TIMER_IRQ_LEVEL));
GxICR(irq) |= GxICR_ENABLE | GxICR_DETECT | GxICR_REQUEST;
tmp = GxICR(irq);
}
int __init init_clockevents(void)
{
struct clock_event_device *cd;
struct irqaction *iact;
unsigned int cpu = smp_processor_id();
cd = &per_cpu(mn10300_clockevent_device, cpu);
if (cpu == 0) {
stop_jiffies_counter();
cd->irq = TMJCIRQ;
} else {
stop_jiffies_counter1();
cd->irq = TMJC1IRQ;
}
cd->name = "Timestamp";
cd->features = CLOCK_EVT_FEAT_ONESHOT;
/* Calculate shift/mult. We want to spawn at least 1 second */
clockevents_calc_mult_shift(cd, MN10300_JCCLK, 1);
/* Calculate the min / max delta */
cd->max_delta_ns = clockevent_delta2ns(TMJCBR_MAX, cd);
cd->min_delta_ns = clockevent_delta2ns(100, cd);
cd->rating = 200;
cd->cpumask = cpumask_of(smp_processor_id());
cd->set_mode = set_clock_mode;
cd->event_handler = event_handler;
cd->set_next_event = next_event;
iact = &per_cpu(timer_irq, cpu);
iact->flags = IRQF_DISABLED | IRQF_SHARED | IRQF_TIMER;
iact->handler = timer_interrupt;
clockevents_register_device(cd);
#if defined(CONFIG_SMP) && !defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST)
/* setup timer irq affinity so it only runs on this cpu */
{
struct irq_data *data;
data = irq_get_irq_data(cd->irq);
cpumask_copy(data->affinity, cpumask_of(cpu));
iact->flags |= IRQF_NOBALANCING;
}
#endif
if (cpu == 0) {
reload_jiffies_counter(MN10300_JC_PER_HZ - 1);
iact->name = "CPU0 Timer";
} else {
reload_jiffies_counter1(MN10300_JC_PER_HZ - 1);
iact->name = "CPU1 Timer";
}
setup_jiffies_interrupt(cd->irq, iact);
return 0;
}

View File

@@ -0,0 +1,34 @@
/* MN10300 clocksource
*
* Copyright (C) 2010 Red Hat, Inc. All Rights Reserved.
* Written by Mark Salter (msalter@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/clocksource.h>
#include <linux/init.h>
#include <asm/timex.h>
#include "internal.h"
static cycle_t mn10300_read(struct clocksource *cs)
{
return read_timestamp_counter();
}
static struct clocksource clocksource_mn10300 = {
.name = "TSC",
.rating = 200,
.read = mn10300_read,
.mask = CLOCKSOURCE_MASK(32),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
int __init init_clocksource(void)
{
startup_timestamp_counter();
clocksource_register_hz(&clocksource_mn10300, MN10300_TSCCLK);
return 0;
}

770
arch/mn10300/kernel/entry.S Normal file
View File

@@ -0,0 +1,770 @@
###############################################################################
#
# MN10300 Exception and interrupt entry points
#
# Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd.
# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
# Modified 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 Licence
# as published by the Free Software Foundation; either version
# 2 of the Licence, or (at your option) any later version.
#
###############################################################################
#include <linux/sys.h>
#include <linux/linkage.h>
#include <asm/smp.h>
#include <asm/irqflags.h>
#include <asm/thread_info.h>
#include <asm/intctl-regs.h>
#include <asm/busctl-regs.h>
#include <asm/timer-regs.h>
#include <unit/leds.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/errno.h>
#include <asm/asm-offsets.h>
#include <asm/frame.inc>
#if defined(CONFIG_SMP) && defined(CONFIG_GDBSTUB)
#include <asm/gdb-stub.h>
#endif /* CONFIG_SMP && CONFIG_GDBSTUB */
#ifdef CONFIG_PREEMPT
#define preempt_stop LOCAL_IRQ_DISABLE
#else
#define preempt_stop
#define resume_kernel restore_all
#endif
.am33_2
###############################################################################
#
# the return path for a forked child
# - on entry, D0 holds the address of the previous task to run
#
###############################################################################
ENTRY(ret_from_fork)
call schedule_tail[],0
GET_THREAD_INFO a2
# return 0 to indicate child process
clr d0
mov d0,(REG_D0,fp)
jmp syscall_exit
ENTRY(ret_from_kernel_thread)
call schedule_tail[],0
mov (REG_D0,fp),d0
mov (REG_A0,fp),a0
calls (a0)
GET_THREAD_INFO a2 # A2 must be set on return from sys_exit()
clr d0
mov d0,(REG_D0,fp)
jmp syscall_exit
###############################################################################
#
# system call handler
#
###############################################################################
ENTRY(system_call)
add -4,sp
SAVE_ALL
mov d0,(REG_ORIG_D0,fp)
GET_THREAD_INFO a2
cmp nr_syscalls,d0
bcc syscall_badsys
btst _TIF_SYSCALL_TRACE,(TI_flags,a2)
bne syscall_entry_trace
syscall_call:
add d0,d0,a1
add a1,a1
mov (REG_A0,fp),d0
mov (sys_call_table,a1),a0
calls (a0)
mov d0,(REG_D0,fp)
syscall_exit:
# make sure we don't miss an interrupt setting need_resched or
# sigpending between sampling and the rti
LOCAL_IRQ_DISABLE
mov (TI_flags,a2),d2
btst _TIF_ALLWORK_MASK,d2
bne syscall_exit_work
restore_all:
RESTORE_ALL
###############################################################################
#
# perform work that needs to be done immediately before resumption and syscall
# tracing
#
###############################################################################
ALIGN
syscall_exit_work:
mov (REG_EPSW,fp),d0
and EPSW_nSL,d0
beq resume_kernel # returning to supervisor mode
LOCAL_IRQ_ENABLE # could let syscall_trace_exit() call
# schedule() instead
btst _TIF_SYSCALL_TRACE,d2
beq work_pending
mov fp,d0
call syscall_trace_exit[],0 # do_syscall_trace(regs)
jmp resume_userspace
ALIGN
work_pending:
btst _TIF_NEED_RESCHED,d2
beq work_notifysig
work_resched:
call schedule[],0
resume_userspace:
# make sure we don't miss an interrupt setting need_resched or
# sigpending between sampling and the rti
LOCAL_IRQ_DISABLE
# is there any work to be done other than syscall tracing?
mov (TI_flags,a2),d2
btst _TIF_WORK_MASK,d2
beq restore_all
LOCAL_IRQ_ENABLE
btst _TIF_NEED_RESCHED,d2
bne work_resched
# deal with pending signals and notify-resume requests
work_notifysig:
mov fp,d0
mov d2,d1
call do_notify_resume[],0
jmp resume_userspace
# perform syscall entry tracing
syscall_entry_trace:
mov -ENOSYS,d0
mov d0,(REG_D0,fp)
mov fp,d0
call syscall_trace_entry[],0 # returns the syscall number to actually use
mov (REG_D1,fp),d1
cmp nr_syscalls,d0
bcs syscall_call
jmp syscall_exit
syscall_badsys:
mov -ENOSYS,d0
mov d0,(REG_D0,fp)
jmp resume_userspace
# userspace resumption stub bypassing syscall exit tracing
.globl ret_from_exception, ret_from_intr
ALIGN
ret_from_exception:
preempt_stop
ret_from_intr:
GET_THREAD_INFO a2
mov (REG_EPSW,fp),d0 # need to deliver signals before
# returning to userspace
and EPSW_nSL,d0
beq resume_kernel # returning to supervisor mode
#ifdef CONFIG_PREEMPT
ENTRY(resume_kernel)
LOCAL_IRQ_DISABLE
mov (TI_preempt_count,a2),d0 # non-zero preempt_count ?
cmp 0,d0
bne restore_all
need_resched:
btst _TIF_NEED_RESCHED,(TI_flags,a2)
beq restore_all
mov (REG_EPSW,fp),d0
and EPSW_IM,d0
cmp EPSW_IM_7,d0 # interrupts off (exception path) ?
bne restore_all
call preempt_schedule_irq[],0
jmp need_resched
#endif
###############################################################################
#
# IRQ handler entry point
# - intended to be entered at multiple priorities
#
###############################################################################
ENTRY(irq_handler)
add -4,sp
SAVE_ALL
# it's not a syscall
mov 0xffffffff,d0
mov d0,(REG_ORIG_D0,fp)
mov fp,d0
call do_IRQ[],0 # do_IRQ(regs)
jmp ret_from_intr
###############################################################################
#
# Double Fault handler entry point
# - note that there will not be a stack, D0/A0 will hold EPSW/PC as were
#
###############################################################################
.section .bss
.balign THREAD_SIZE
.space THREAD_SIZE
__df_stack:
.previous
ENTRY(double_fault)
mov a0,(__df_stack-4) # PC as was
mov d0,(__df_stack-8) # EPSW as was
mn10300_set_dbfleds # display 'db-f' on the LEDs
mov 0xaa55aa55,d0
mov d0,(__df_stack-12) # no ORIG_D0
mov sp,a0 # save corrupted SP
mov __df_stack-12,sp # emergency supervisor stack
SAVE_ALL
mov a0,(REG_A0,fp) # save corrupted SP as A0 (which got
# clobbered by the CPU)
mov fp,d0
calls do_double_fault
double_fault_loop:
bra double_fault_loop
###############################################################################
#
# Bus Error handler entry point
# - handle external (async) bus errors separately
#
###############################################################################
ENTRY(raw_bus_error)
add -4,sp
mov d0,(sp)
#if defined(CONFIG_ERRATUM_NEED_TO_RELOAD_MMUCTR)
mov (MMUCTR),d0
mov d0,(MMUCTR)
#endif
mov (BCBERR),d0 # what
btst BCBERR_BEMR_DMA,d0 # see if it was an external bus error
beq __common_exception_aux # it wasn't
SAVE_ALL
mov (BCBEAR),d1 # destination of erroneous access
mov (REG_ORIG_D0,fp),d2
mov d2,(REG_D0,fp)
mov -1,d2
mov d2,(REG_ORIG_D0,fp)
add -4,sp
mov fp,(12,sp) # frame pointer
call io_bus_error[],0
jmp restore_all
###############################################################################
#
# NMI exception entry points
#
# This is used by ordinary interrupt channels that have the GxICR_NMI bit set
# in addition to the main NMI and Watchdog channels. SMP NMI IPIs use this
# facility.
#
###############################################################################
ENTRY(nmi_handler)
add -4,sp
mov d0,(sp)
mov (TBR),d0
#ifdef CONFIG_SMP
add -4,sp
mov d0,(sp) # save d0(TBR)
movhu (NMIAGR),d0
and NMIAGR_GN,d0
lsr 0x2,d0
cmp CALL_FUNCTION_NMI_IPI,d0
bne nmi_not_smp_callfunc # if not call function, jump
# function call nmi ipi
add 4,sp # no need to store TBR
mov GxICR_DETECT,d0 # clear NMI request
movbu d0,(GxICR(CALL_FUNCTION_NMI_IPI))
movhu (GxICR(CALL_FUNCTION_NMI_IPI)),d0
and ~EPSW_NMID,epsw # enable NMI
mov (sp),d0 # restore d0
SAVE_ALL
call smp_nmi_call_function_interrupt[],0
RESTORE_ALL
nmi_not_smp_callfunc:
#ifdef CONFIG_KERNEL_DEBUGGER
cmp DEBUGGER_NMI_IPI,d0
bne nmi_not_debugger # if not kernel debugger NMI IPI, jump
# kernel debugger NMI IPI
add 4,sp # no need to store TBR
mov GxICR_DETECT,d0 # clear NMI
movbu d0,(GxICR(DEBUGGER_NMI_IPI))
movhu (GxICR(DEBUGGER_NMI_IPI)),d0
and ~EPSW_NMID,epsw # enable NMI
mov (sp),d0
SAVE_ALL
mov fp,d0 # arg 0: stacked register file
mov a2,d1 # arg 1: exception number
call debugger_nmi_interrupt[],0
RESTORE_ALL
nmi_not_debugger:
#endif /* CONFIG_KERNEL_DEBUGGER */
mov (sp),d0 # restore TBR to d0
add 4,sp
#endif /* CONFIG_SMP */
bra __common_exception_nonmi
###############################################################################
#
# General exception entry point
#
###############################################################################
ENTRY(__common_exception)
add -4,sp
mov d0,(sp)
#if defined(CONFIG_ERRATUM_NEED_TO_RELOAD_MMUCTR)
mov (MMUCTR),d0
mov d0,(MMUCTR)
#endif
__common_exception_aux:
mov (TBR),d0
and ~EPSW_NMID,epsw # turn NMIs back on if not NMI
or EPSW_IE,epsw
__common_exception_nonmi:
and 0x0000FFFF,d0 # turn the exception code into a vector
# table index
btst 0x00000007,d0
bne 1f
cmp 0x00000400,d0
bge 1f
SAVE_ALL # build the stack frame
mov (REG_D0,fp),a2 # get the exception number
mov (REG_ORIG_D0,fp),d0
mov d0,(REG_D0,fp)
mov -1,d0
mov d0,(REG_ORIG_D0,fp)
#ifdef CONFIG_GDBSTUB
#ifdef CONFIG_SMP
call gdbstub_busy_check[],0
and d0,d0 # check return value
beq 2f
#else /* CONFIG_SMP */
btst 0x01,(gdbstub_busy)
beq 2f
#endif /* CONFIG_SMP */
and ~EPSW_IE,epsw
mov fp,d0
mov a2,d1
call gdbstub_exception[],0 # gdbstub itself caused an exception
bra restore_all
2:
#endif /* CONFIG_GDBSTUB */
mov fp,d0 # arg 0: stacked register file
mov a2,d1 # arg 1: exception number
lsr 1,a2
mov (exception_table,a2),a2
calls (a2)
jmp ret_from_exception
1: pi # BUG() equivalent
###############################################################################
#
# Exception handler functions table
#
###############################################################################
.data
ENTRY(exception_table)
.rept 0x400>>1
.long uninitialised_exception
.endr
.previous
###############################################################################
#
# Change an entry in the exception table
# - D0 exception code, D1 handler
#
###############################################################################
ENTRY(set_excp_vector)
lsr 1,d0
add exception_table,d0
mov d1,(d0)
mov 4,d1
ret [],0
###############################################################################
#
# System call table
#
###############################################################################
.data
ENTRY(sys_call_table)
.long sys_restart_syscall /* 0 */
.long sys_exit
.long sys_fork
.long sys_read
.long sys_write
.long sys_open /* 5 */
.long sys_close
.long sys_waitpid
.long sys_creat
.long sys_link
.long sys_unlink /* 10 */
.long sys_execve
.long sys_chdir
.long sys_time
.long sys_mknod
.long sys_chmod /* 15 */
.long sys_lchown16
.long sys_ni_syscall /* old break syscall holder */
.long sys_stat
.long sys_lseek
.long sys_getpid /* 20 */
.long sys_mount
.long sys_oldumount
.long sys_setuid16
.long sys_getuid16
.long sys_stime /* 25 */
.long sys_ptrace
.long sys_alarm
.long sys_fstat
.long sys_pause
.long sys_utime /* 30 */
.long sys_ni_syscall /* old stty syscall holder */
.long sys_ni_syscall /* old gtty syscall holder */
.long sys_access
.long sys_nice
.long sys_ni_syscall /* 35 - old ftime syscall holder */
.long sys_sync
.long sys_kill
.long sys_rename
.long sys_mkdir
.long sys_rmdir /* 40 */
.long sys_dup
.long sys_pipe
.long sys_times
.long sys_ni_syscall /* old prof syscall holder */
.long sys_brk /* 45 */
.long sys_setgid16
.long sys_getgid16
.long sys_signal
.long sys_geteuid16
.long sys_getegid16 /* 50 */
.long sys_acct
.long sys_umount /* recycled never used phys() */
.long sys_ni_syscall /* old lock syscall holder */
.long sys_ioctl
.long sys_fcntl /* 55 */
.long sys_ni_syscall /* old mpx syscall holder */
.long sys_setpgid
.long sys_ni_syscall /* old ulimit syscall holder */
.long sys_ni_syscall /* old sys_olduname */
.long sys_umask /* 60 */
.long sys_chroot
.long sys_ustat
.long sys_dup2
.long sys_getppid
.long sys_getpgrp /* 65 */
.long sys_setsid
.long sys_sigaction
.long sys_sgetmask
.long sys_ssetmask
.long sys_setreuid16 /* 70 */
.long sys_setregid16
.long sys_sigsuspend
.long sys_sigpending
.long sys_sethostname
.long sys_setrlimit /* 75 */
.long sys_old_getrlimit
.long sys_getrusage
.long sys_gettimeofday
.long sys_settimeofday
.long sys_getgroups16 /* 80 */
.long sys_setgroups16
.long sys_old_select
.long sys_symlink
.long sys_lstat
.long sys_readlink /* 85 */
.long sys_uselib
.long sys_swapon
.long sys_reboot
.long sys_old_readdir
.long old_mmap /* 90 */
.long sys_munmap
.long sys_truncate
.long sys_ftruncate
.long sys_fchmod
.long sys_fchown16 /* 95 */
.long sys_getpriority
.long sys_setpriority
.long sys_ni_syscall /* old profil syscall holder */
.long sys_statfs
.long sys_fstatfs /* 100 */
.long sys_ni_syscall /* ioperm */
.long sys_socketcall
.long sys_syslog
.long sys_setitimer
.long sys_getitimer /* 105 */
.long sys_newstat
.long sys_newlstat
.long sys_newfstat
.long sys_ni_syscall /* old sys_uname */
.long sys_ni_syscall /* 110 - iopl */
.long sys_vhangup
.long sys_ni_syscall /* old "idle" system call */
.long sys_ni_syscall /* vm86old */
.long sys_wait4
.long sys_swapoff /* 115 */
.long sys_sysinfo
.long sys_ipc
.long sys_fsync
.long sys_sigreturn
.long sys_clone /* 120 */
.long sys_setdomainname
.long sys_newuname
.long sys_ni_syscall /* modify_ldt */
.long sys_adjtimex
.long sys_mprotect /* 125 */
.long sys_sigprocmask
.long sys_ni_syscall /* old "create_module" */
.long sys_init_module
.long sys_delete_module
.long sys_ni_syscall /* 130: old "get_kernel_syms" */
.long sys_quotactl
.long sys_getpgid
.long sys_fchdir
.long sys_bdflush
.long sys_sysfs /* 135 */
.long sys_personality
.long sys_ni_syscall /* reserved for afs_syscall */
.long sys_setfsuid16
.long sys_setfsgid16
.long sys_llseek /* 140 */
.long sys_getdents
.long sys_select
.long sys_flock
.long sys_msync
.long sys_readv /* 145 */
.long sys_writev
.long sys_getsid
.long sys_fdatasync
.long sys_sysctl
.long sys_mlock /* 150 */
.long sys_munlock
.long sys_mlockall
.long sys_munlockall
.long sys_sched_setparam
.long sys_sched_getparam /* 155 */
.long sys_sched_setscheduler
.long sys_sched_getscheduler
.long sys_sched_yield
.long sys_sched_get_priority_max
.long sys_sched_get_priority_min /* 160 */
.long sys_sched_rr_get_interval
.long sys_nanosleep
.long sys_mremap
.long sys_setresuid16
.long sys_getresuid16 /* 165 */
.long sys_ni_syscall /* vm86 */
.long sys_ni_syscall /* Old sys_query_module */
.long sys_poll
.long sys_ni_syscall /* was nfsservctl */
.long sys_setresgid16 /* 170 */
.long sys_getresgid16
.long sys_prctl
.long sys_rt_sigreturn
.long sys_rt_sigaction
.long sys_rt_sigprocmask /* 175 */
.long sys_rt_sigpending
.long sys_rt_sigtimedwait
.long sys_rt_sigqueueinfo
.long sys_rt_sigsuspend
.long sys_pread64 /* 180 */
.long sys_pwrite64
.long sys_chown16
.long sys_getcwd
.long sys_capget
.long sys_capset /* 185 */
.long sys_sigaltstack
.long sys_sendfile
.long sys_ni_syscall /* reserved for streams1 */
.long sys_ni_syscall /* reserved for streams2 */
.long sys_vfork /* 190 */
.long sys_getrlimit
.long sys_mmap_pgoff
.long sys_truncate64
.long sys_ftruncate64
.long sys_stat64 /* 195 */
.long sys_lstat64
.long sys_fstat64
.long sys_lchown
.long sys_getuid
.long sys_getgid /* 200 */
.long sys_geteuid
.long sys_getegid
.long sys_setreuid
.long sys_setregid
.long sys_getgroups /* 205 */
.long sys_setgroups
.long sys_fchown
.long sys_setresuid
.long sys_getresuid
.long sys_setresgid /* 210 */
.long sys_getresgid
.long sys_chown
.long sys_setuid
.long sys_setgid
.long sys_setfsuid /* 215 */
.long sys_setfsgid
.long sys_pivot_root
.long sys_mincore
.long sys_madvise
.long sys_getdents64 /* 220 */
.long sys_fcntl64
.long sys_ni_syscall /* reserved for TUX */
.long sys_ni_syscall
.long sys_gettid
.long sys_readahead /* 225 */
.long sys_setxattr
.long sys_lsetxattr
.long sys_fsetxattr
.long sys_getxattr
.long sys_lgetxattr /* 230 */
.long sys_fgetxattr
.long sys_listxattr
.long sys_llistxattr
.long sys_flistxattr
.long sys_removexattr /* 235 */
.long sys_lremovexattr
.long sys_fremovexattr
.long sys_tkill
.long sys_sendfile64
.long sys_futex /* 240 */
.long sys_sched_setaffinity
.long sys_sched_getaffinity
.long sys_ni_syscall /* sys_set_thread_area */
.long sys_ni_syscall /* sys_get_thread_area */
.long sys_io_setup /* 245 */
.long sys_io_destroy
.long sys_io_getevents
.long sys_io_submit
.long sys_io_cancel
.long sys_fadvise64 /* 250 */
.long sys_ni_syscall
.long sys_exit_group
.long sys_lookup_dcookie
.long sys_epoll_create
.long sys_epoll_ctl /* 255 */
.long sys_epoll_wait
.long sys_remap_file_pages
.long sys_set_tid_address
.long sys_timer_create
.long sys_timer_settime /* 260 */
.long sys_timer_gettime
.long sys_timer_getoverrun
.long sys_timer_delete
.long sys_clock_settime
.long sys_clock_gettime /* 265 */
.long sys_clock_getres
.long sys_clock_nanosleep
.long sys_statfs64
.long sys_fstatfs64
.long sys_tgkill /* 270 */
.long sys_utimes
.long sys_fadvise64_64
.long sys_ni_syscall /* sys_vserver */
.long sys_mbind
.long sys_get_mempolicy /* 275 */
.long sys_set_mempolicy
.long sys_mq_open
.long sys_mq_unlink
.long sys_mq_timedsend
.long sys_mq_timedreceive /* 280 */
.long sys_mq_notify
.long sys_mq_getsetattr
.long sys_kexec_load
.long sys_waitid
.long sys_ni_syscall /* 285 */ /* available */
.long sys_add_key
.long sys_request_key
.long sys_keyctl
.long sys_cacheflush
.long sys_ioprio_set /* 290 */
.long sys_ioprio_get
.long sys_inotify_init
.long sys_inotify_add_watch
.long sys_inotify_rm_watch
.long sys_migrate_pages /* 295 */
.long sys_openat
.long sys_mkdirat
.long sys_mknodat
.long sys_fchownat
.long sys_futimesat /* 300 */
.long sys_fstatat64
.long sys_unlinkat
.long sys_renameat
.long sys_linkat
.long sys_symlinkat /* 305 */
.long sys_readlinkat
.long sys_fchmodat
.long sys_faccessat
.long sys_pselect6
.long sys_ppoll /* 310 */
.long sys_unshare
.long sys_set_robust_list
.long sys_get_robust_list
.long sys_splice
.long sys_sync_file_range /* 315 */
.long sys_tee
.long sys_vmsplice
.long sys_move_pages
.long sys_getcpu
.long sys_epoll_pwait /* 320 */
.long sys_utimensat
.long sys_signalfd
.long sys_timerfd_create
.long sys_eventfd
.long sys_fallocate /* 325 */
.long sys_timerfd_settime
.long sys_timerfd_gettime
.long sys_signalfd4
.long sys_eventfd2
.long sys_epoll_create1 /* 330 */
.long sys_dup3
.long sys_pipe2
.long sys_inotify_init1
.long sys_preadv
.long sys_pwritev /* 335 */
.long sys_rt_tgsigqueueinfo
.long sys_perf_event_open
.long sys_recvmmsg
.long sys_setns
nr_syscalls=(.-sys_call_table)/4

View File

@@ -0,0 +1,258 @@
/* MN10300 Low level FPU management operations
*
* Copyright (C) 2007 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 Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/linkage.h>
#include <asm/cpu-regs.h>
#include <asm/smp.h>
#include <asm/thread_info.h>
#include <asm/asm-offsets.h>
#include <asm/frame.inc>
.macro FPU_INIT_STATE_ALL
fmov 0,fs0
fmov fs0,fs1
fmov fs0,fs2
fmov fs0,fs3
fmov fs0,fs4
fmov fs0,fs5
fmov fs0,fs6
fmov fs0,fs7
fmov fs0,fs8
fmov fs0,fs9
fmov fs0,fs10
fmov fs0,fs11
fmov fs0,fs12
fmov fs0,fs13
fmov fs0,fs14
fmov fs0,fs15
fmov fs0,fs16
fmov fs0,fs17
fmov fs0,fs18
fmov fs0,fs19
fmov fs0,fs20
fmov fs0,fs21
fmov fs0,fs22
fmov fs0,fs23
fmov fs0,fs24
fmov fs0,fs25
fmov fs0,fs26
fmov fs0,fs27
fmov fs0,fs28
fmov fs0,fs29
fmov fs0,fs30
fmov fs0,fs31
fmov FPCR_INIT,fpcr
.endm
.macro FPU_SAVE_ALL areg,dreg
fmov fs0,(\areg+)
fmov fs1,(\areg+)
fmov fs2,(\areg+)
fmov fs3,(\areg+)
fmov fs4,(\areg+)
fmov fs5,(\areg+)
fmov fs6,(\areg+)
fmov fs7,(\areg+)
fmov fs8,(\areg+)
fmov fs9,(\areg+)
fmov fs10,(\areg+)
fmov fs11,(\areg+)
fmov fs12,(\areg+)
fmov fs13,(\areg+)
fmov fs14,(\areg+)
fmov fs15,(\areg+)
fmov fs16,(\areg+)
fmov fs17,(\areg+)
fmov fs18,(\areg+)
fmov fs19,(\areg+)
fmov fs20,(\areg+)
fmov fs21,(\areg+)
fmov fs22,(\areg+)
fmov fs23,(\areg+)
fmov fs24,(\areg+)
fmov fs25,(\areg+)
fmov fs26,(\areg+)
fmov fs27,(\areg+)
fmov fs28,(\areg+)
fmov fs29,(\areg+)
fmov fs30,(\areg+)
fmov fs31,(\areg+)
fmov fpcr,\dreg
mov \dreg,(\areg)
.endm
.macro FPU_RESTORE_ALL areg,dreg
fmov (\areg+),fs0
fmov (\areg+),fs1
fmov (\areg+),fs2
fmov (\areg+),fs3
fmov (\areg+),fs4
fmov (\areg+),fs5
fmov (\areg+),fs6
fmov (\areg+),fs7
fmov (\areg+),fs8
fmov (\areg+),fs9
fmov (\areg+),fs10
fmov (\areg+),fs11
fmov (\areg+),fs12
fmov (\areg+),fs13
fmov (\areg+),fs14
fmov (\areg+),fs15
fmov (\areg+),fs16
fmov (\areg+),fs17
fmov (\areg+),fs18
fmov (\areg+),fs19
fmov (\areg+),fs20
fmov (\areg+),fs21
fmov (\areg+),fs22
fmov (\areg+),fs23
fmov (\areg+),fs24
fmov (\areg+),fs25
fmov (\areg+),fs26
fmov (\areg+),fs27
fmov (\areg+),fs28
fmov (\areg+),fs29
fmov (\areg+),fs30
fmov (\areg+),fs31
mov (\areg),\dreg
fmov \dreg,fpcr
.endm
###############################################################################
#
# void fpu_init_state(void)
# - initialise the FPU
#
###############################################################################
.globl fpu_init_state
.type fpu_init_state,@function
fpu_init_state:
mov epsw,d0
or EPSW_FE,epsw
#ifdef CONFIG_MN10300_PROC_MN103E010
nop
nop
nop
#endif
FPU_INIT_STATE_ALL
#ifdef CONFIG_MN10300_PROC_MN103E010
nop
nop
nop
#endif
mov d0,epsw
ret [],0
.size fpu_init_state,.-fpu_init_state
###############################################################################
#
# void fpu_save(struct fpu_state_struct *)
# - save the fpu state
# - note that an FPU Operational exception might occur during this process
#
###############################################################################
.globl fpu_save
.type fpu_save,@function
fpu_save:
mov epsw,d1
or EPSW_FE,epsw /* enable the FPU so we can access it */
#ifdef CONFIG_MN10300_PROC_MN103E010
nop
nop
#endif
mov d0,a0
FPU_SAVE_ALL a0,d0
#ifdef CONFIG_MN10300_PROC_MN103E010
nop
nop
#endif
mov d1,epsw
ret [],0
.size fpu_save,.-fpu_save
###############################################################################
#
# void fpu_disabled(void)
# - handle an exception due to the FPU being disabled
# when CONFIG_FPU is enabled
#
###############################################################################
.type fpu_disabled,@function
.globl fpu_disabled
fpu_disabled:
or EPSW_nAR|EPSW_FE,epsw
nop
nop
nop
mov sp,a1
mov (a1),d1 /* get epsw of user context */
and ~(THREAD_SIZE-1),a1 /* a1: (thread_info *ti) */
mov (TI_task,a1),a2 /* a2: (task_struct *tsk) */
btst EPSW_nSL,d1
beq fpu_used_in_kernel
or EPSW_FE,d1
mov d1,(sp)
mov (TASK_THREAD+THREAD_FPU_FLAGS,a2),d1
#ifndef CONFIG_LAZY_SAVE_FPU
or __THREAD_HAS_FPU,d1
mov d1,(TASK_THREAD+THREAD_FPU_FLAGS,a2)
#else /* !CONFIG_LAZY_SAVE_FPU */
mov (fpu_state_owner),a0
cmp 0,a0
beq fpu_regs_save_end
mov (TASK_THREAD+THREAD_UREGS,a0),a1
add TASK_THREAD+THREAD_FPU_STATE,a0
FPU_SAVE_ALL a0,d0
mov (REG_EPSW,a1),d0
and ~EPSW_FE,d0
mov d0,(REG_EPSW,a1)
fpu_regs_save_end:
mov a2,(fpu_state_owner)
#endif /* !CONFIG_LAZY_SAVE_FPU */
btst __THREAD_USING_FPU,d1
beq fpu_regs_init
add TASK_THREAD+THREAD_FPU_STATE,a2
FPU_RESTORE_ALL a2,d0
rti
fpu_regs_init:
FPU_INIT_STATE_ALL
add TASK_THREAD+THREAD_FPU_FLAGS,a2
bset __THREAD_USING_FPU,(0,a2)
rti
fpu_used_in_kernel:
and ~(EPSW_nAR|EPSW_FE),epsw
nop
nop
add -4,sp
SAVE_ALL
mov -1,d0
mov d0,(REG_ORIG_D0,fp)
and ~EPSW_NMID,epsw
mov fp,d0
call fpu_disabled_in_kernel[],0
jmp ret_from_exception
.size fpu_disabled,.-fpu_disabled

View File

@@ -0,0 +1,39 @@
/* MN10300 Low level FPU management operations
*
* Copyright (C) 2007 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 Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/linkage.h>
#include <asm/cpu-regs.h>
#include <asm/smp.h>
#include <asm/thread_info.h>
#include <asm/asm-offsets.h>
#include <asm/frame.inc>
###############################################################################
#
# void fpu_disabled(void)
# - handle an exception due to the FPU being disabled
# when CONFIG_FPU is disabled
#
###############################################################################
.type fpu_disabled,@function
.globl fpu_disabled
fpu_disabled:
add -4,sp
SAVE_ALL
mov -1,d0
mov d0,(REG_ORIG_D0,fp)
and ~EPSW_NMID,epsw
mov fp,d0
call unexpected_fpu_exception[],0
jmp ret_from_exception
.size fpu_disabled,.-fpu_disabled

View File

@@ -0,0 +1,30 @@
/* MN10300 FPU management
*
* Copyright (C) 2007 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 Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <asm/fpu.h>
/*
* handle an FPU operational exception
* - there's a possibility that if the FPU is asynchronous, the signal might
* be meant for a process other than the current one
*/
asmlinkage
void unexpected_fpu_exception(struct pt_regs *regs, enum exception_code code)
{
panic("An FPU exception was received, but there's no FPU enabled.");
}
/*
* fill in the FPU structure for a core dump
*/
int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpreg)
{
return 0; /* not valid */
}

175
arch/mn10300/kernel/fpu.c Normal file
View File

@@ -0,0 +1,175 @@
/* MN10300 FPU management
*
* Copyright (C) 2007 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 Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <asm/uaccess.h>
#include <asm/fpu.h>
#include <asm/elf.h>
#include <asm/exceptions.h>
#ifdef CONFIG_LAZY_SAVE_FPU
struct task_struct *fpu_state_owner;
#endif
/*
* error functions in FPU disabled exception
*/
asmlinkage void fpu_disabled_in_kernel(struct pt_regs *regs)
{
die_if_no_fixup("An FPU Disabled exception happened in kernel space\n",
regs, EXCEP_FPU_DISABLED);
}
/*
* handle an FPU operational exception
* - there's a possibility that if the FPU is asynchronous, the signal might
* be meant for a process other than the current one
*/
asmlinkage void fpu_exception(struct pt_regs *regs, enum exception_code code)
{
struct task_struct *tsk = current;
siginfo_t info;
u32 fpcr;
if (!user_mode(regs))
die_if_no_fixup("An FPU Operation exception happened in"
" kernel space\n",
regs, code);
if (!is_using_fpu(tsk))
die_if_no_fixup("An FPU Operation exception happened,"
" but the FPU is not in use",
regs, code);
info.si_signo = SIGFPE;
info.si_errno = 0;
info.si_addr = (void *) tsk->thread.uregs->pc;
info.si_code = FPE_FLTINV;
unlazy_fpu(tsk);
fpcr = tsk->thread.fpu_state.fpcr;
if (fpcr & FPCR_EC_Z)
info.si_code = FPE_FLTDIV;
else if (fpcr & FPCR_EC_O)
info.si_code = FPE_FLTOVF;
else if (fpcr & FPCR_EC_U)
info.si_code = FPE_FLTUND;
else if (fpcr & FPCR_EC_I)
info.si_code = FPE_FLTRES;
force_sig_info(SIGFPE, &info, tsk);
}
/*
* save the FPU state to a signal context
*/
int fpu_setup_sigcontext(struct fpucontext *fpucontext)
{
struct task_struct *tsk = current;
if (!is_using_fpu(tsk))
return 0;
/* transfer the current FPU state to memory and cause fpu_init() to be
* triggered by the next attempted FPU operation by the current
* process.
*/
preempt_disable();
#ifndef CONFIG_LAZY_SAVE_FPU
if (tsk->thread.fpu_flags & THREAD_HAS_FPU) {
fpu_save(&tsk->thread.fpu_state);
tsk->thread.uregs->epsw &= ~EPSW_FE;
tsk->thread.fpu_flags &= ~THREAD_HAS_FPU;
}
#else /* !CONFIG_LAZY_SAVE_FPU */
if (fpu_state_owner == tsk) {
fpu_save(&tsk->thread.fpu_state);
fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE;
fpu_state_owner = NULL;
}
#endif /* !CONFIG_LAZY_SAVE_FPU */
preempt_enable();
/* we no longer have a valid current FPU state */
clear_using_fpu(tsk);
/* transfer the saved FPU state onto the userspace stack */
if (copy_to_user(fpucontext,
&tsk->thread.fpu_state,
min(sizeof(struct fpu_state_struct),
sizeof(struct fpucontext))))
return -1;
return 1;
}
/*
* kill a process's FPU state during restoration after signal handling
*/
void fpu_kill_state(struct task_struct *tsk)
{
/* disown anything left in the FPU */
preempt_disable();
#ifndef CONFIG_LAZY_SAVE_FPU
if (tsk->thread.fpu_flags & THREAD_HAS_FPU) {
tsk->thread.uregs->epsw &= ~EPSW_FE;
tsk->thread.fpu_flags &= ~THREAD_HAS_FPU;
}
#else /* !CONFIG_LAZY_SAVE_FPU */
if (fpu_state_owner == tsk) {
fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE;
fpu_state_owner = NULL;
}
#endif /* !CONFIG_LAZY_SAVE_FPU */
preempt_enable();
/* we no longer have a valid current FPU state */
clear_using_fpu(tsk);
}
/*
* restore the FPU state from a signal context
*/
int fpu_restore_sigcontext(struct fpucontext *fpucontext)
{
struct task_struct *tsk = current;
int ret;
/* load up the old FPU state */
ret = copy_from_user(&tsk->thread.fpu_state, fpucontext,
min(sizeof(struct fpu_state_struct),
sizeof(struct fpucontext)));
if (!ret)
set_using_fpu(tsk);
return ret;
}
/*
* fill in the FPU structure for a core dump
*/
int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpreg)
{
struct task_struct *tsk = current;
int fpvalid;
fpvalid = is_using_fpu(tsk);
if (fpvalid) {
unlazy_fpu(tsk);
memcpy(fpreg, &tsk->thread.fpu_state, sizeof(*fpreg));
}
return fpvalid;
}

View File

@@ -0,0 +1,91 @@
###############################################################################
#
# 16550 serial Rx interrupt handler for gdbstub I/O
#
# Copyright (C) 2007 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 Licence
# as published by the Free Software Foundation; either version
# 2 of the Licence, or (at your option) any later version.
#
###############################################################################
#include <linux/sys.h>
#include <linux/linkage.h>
#include <asm/smp.h>
#include <asm/cpu-regs.h>
#include <asm/thread_info.h>
#include <asm/frame.inc>
#include <asm/intctl-regs.h>
#include <asm/irqflags.h>
#include <unit/serial.h>
.text
###############################################################################
#
# GDB stub serial receive interrupt entry point
# - intended to run at interrupt priority 0
#
###############################################################################
.globl gdbstub_io_rx_handler
.type gdbstub_io_rx_handler,@function
gdbstub_io_rx_handler:
movm [d2,d3,a2,a3],(sp)
#if 1
movbu (GDBPORT_SERIAL_IIR),d2
#endif
mov (gdbstub_rx_inp),a3
gdbstub_io_rx_more:
mov a3,a2
add 2,a3
and 0x00000fff,a3
mov (gdbstub_rx_outp),d3
cmp a3,d3
beq gdbstub_io_rx_overflow
movbu (GDBPORT_SERIAL_LSR),d3
btst UART_LSR_DR,d3
beq gdbstub_io_rx_done
movbu (GDBPORT_SERIAL_RX),d2
movbu d3,(gdbstub_rx_buffer+1,a2)
movbu d2,(gdbstub_rx_buffer,a2)
mov a3,(gdbstub_rx_inp)
bra gdbstub_io_rx_more
gdbstub_io_rx_done:
mov GxICR_DETECT,d2
movbu d2,(XIRQxICR(GDBPORT_SERIAL_IRQ)) # ACK the interrupt
movhu (XIRQxICR(GDBPORT_SERIAL_IRQ)),d2 # flush
movm (sp),[d2,d3,a2,a3]
bset 0x01,(gdbstub_busy)
beq gdbstub_io_rx_enter
rti
gdbstub_io_rx_overflow:
bset 0x01,(gdbstub_rx_overflow)
bra gdbstub_io_rx_done
gdbstub_io_rx_enter:
LOCAL_CHANGE_INTR_MASK_LEVEL(NUM2EPSW_IM(CONFIG_GDBSTUB_IRQ_LEVEL+1))
add -4,sp
SAVE_ALL
mov 0xffffffff,d0
mov d0,(REG_ORIG_D0,fp)
mov 0x280,d1
mov fp,d0
call gdbstub_rx_irq[],0 # gdbstub_rx_irq(regs,excep)
LOCAL_CLI
bclr 0x01,(gdbstub_busy)
.globl gdbstub_return
gdbstub_return:
RESTORE_ALL
.size gdbstub_io_rx_handler,.-gdbstub_io_rx_handler

View File

@@ -0,0 +1,174 @@
/* 16550 serial driver for gdbstub I/O
*
* Copyright (C) 2007 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 Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/console.h>
#include <linux/init.h>
#include <linux/nmi.h>
#include <asm/pgtable.h>
#include <asm/gdb-stub.h>
#include <asm/exceptions.h>
#include <asm/serial-regs.h>
#include <unit/serial.h>
#include <asm/smp.h>
/*
* initialise the GDB stub
*/
void gdbstub_io_init(void)
{
u16 tmp;
/* set up the serial port */
GDBPORT_SERIAL_LCR = UART_LCR_WLEN8; /* 1N8 */
GDBPORT_SERIAL_FCR = (UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR |
UART_FCR_CLEAR_XMIT);
FLOWCTL_CLEAR(DTR);
FLOWCTL_SET(RTS);
gdbstub_io_set_baud(115200);
/* we want to get serial receive interrupts */
XIRQxICR(GDBPORT_SERIAL_IRQ) = 0;
tmp = XIRQxICR(GDBPORT_SERIAL_IRQ);
#if CONFIG_GDBSTUB_IRQ_LEVEL == 0
IVAR0 = EXCEP_IRQ_LEVEL0;
#elif CONFIG_GDBSTUB_IRQ_LEVEL == 1
IVAR1 = EXCEP_IRQ_LEVEL1;
#elif CONFIG_GDBSTUB_IRQ_LEVEL == 2
IVAR2 = EXCEP_IRQ_LEVEL2;
#elif CONFIG_GDBSTUB_IRQ_LEVEL == 3
IVAR3 = EXCEP_IRQ_LEVEL3;
#elif CONFIG_GDBSTUB_IRQ_LEVEL == 4
IVAR4 = EXCEP_IRQ_LEVEL4;
#elif CONFIG_GDBSTUB_IRQ_LEVEL == 5
IVAR5 = EXCEP_IRQ_LEVEL5;
#else
#error "Unknown irq level for gdbstub."
#endif
set_intr_stub(NUM2EXCEP_IRQ_LEVEL(CONFIG_GDBSTUB_IRQ_LEVEL),
gdbstub_io_rx_handler);
XIRQxICR(GDBPORT_SERIAL_IRQ) &= ~GxICR_REQUEST;
XIRQxICR(GDBPORT_SERIAL_IRQ) =
GxICR_ENABLE | NUM2GxICR_LEVEL(CONFIG_GDBSTUB_IRQ_LEVEL);
tmp = XIRQxICR(GDBPORT_SERIAL_IRQ);
GDBPORT_SERIAL_IER = UART_IER_RDI | UART_IER_RLSI;
/* permit level 0 IRQs to take place */
arch_local_change_intr_mask_level(
NUM2EPSW_IM(CONFIG_GDBSTUB_IRQ_LEVEL + 1));
}
/*
* set up the GDB stub serial port baud rate timers
*/
void gdbstub_io_set_baud(unsigned baud)
{
unsigned value;
u8 lcr;
value = 18432000 / 16 / baud;
lcr = GDBPORT_SERIAL_LCR;
GDBPORT_SERIAL_LCR |= UART_LCR_DLAB;
GDBPORT_SERIAL_DLL = value & 0xff;
GDBPORT_SERIAL_DLM = (value >> 8) & 0xff;
GDBPORT_SERIAL_LCR = lcr;
}
/*
* wait for a character to come from the debugger
*/
int gdbstub_io_rx_char(unsigned char *_ch, int nonblock)
{
unsigned ix;
u8 ch, st;
#if defined(CONFIG_MN10300_WD_TIMER)
int cpu;
#endif
*_ch = 0xff;
if (gdbstub_rx_unget) {
*_ch = gdbstub_rx_unget;
gdbstub_rx_unget = 0;
return 0;
}
try_again:
/* pull chars out of the buffer */
ix = gdbstub_rx_outp;
barrier();
if (ix == gdbstub_rx_inp) {
if (nonblock)
return -EAGAIN;
#ifdef CONFIG_MN10300_WD_TIMER
for (cpu = 0; cpu < NR_CPUS; cpu++)
watchdog_alert_counter[cpu] = 0;
#endif
goto try_again;
}
ch = gdbstub_rx_buffer[ix++];
st = gdbstub_rx_buffer[ix++];
barrier();
gdbstub_rx_outp = ix & 0x00000fff;
if (st & UART_LSR_BI) {
gdbstub_proto("### GDB Rx Break Detected ###\n");
return -EINTR;
} else if (st & (UART_LSR_FE | UART_LSR_OE | UART_LSR_PE)) {
gdbstub_proto("### GDB Rx Error (st=%02x) ###\n", st);
return -EIO;
} else {
gdbstub_proto("### GDB Rx %02x (st=%02x) ###\n", ch, st);
*_ch = ch & 0x7f;
return 0;
}
}
/*
* send a character to the debugger
*/
void gdbstub_io_tx_char(unsigned char ch)
{
FLOWCTL_SET(DTR);
LSR_WAIT_FOR(THRE);
/* FLOWCTL_WAIT_FOR(CTS); */
if (ch == 0x0a) {
GDBPORT_SERIAL_TX = 0x0d;
LSR_WAIT_FOR(THRE);
/* FLOWCTL_WAIT_FOR(CTS); */
}
GDBPORT_SERIAL_TX = ch;
FLOWCTL_CLEAR(DTR);
}
/*
* send a character to the debugger
*/
void gdbstub_io_tx_flush(void)
{
LSR_WAIT_FOR(TEMT);
LSR_WAIT_FOR(THRE);
FLOWCTL_CLEAR(DTR);
}

View File

@@ -0,0 +1,93 @@
###############################################################################
#
# MN10300 On-chip serial Rx interrupt handler for GDB stub I/O
#
# Copyright (C) 2007 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 Licence
# as published by the Free Software Foundation; either version
# 2 of the Licence, or (at your option) any later version.
#
###############################################################################
#include <linux/sys.h>
#include <linux/linkage.h>
#include <asm/smp.h>
#include <asm/thread_info.h>
#include <asm/cpu-regs.h>
#include <asm/frame.inc>
#include <asm/intctl-regs.h>
#include <unit/serial.h>
#include "mn10300-serial.h"
.text
###############################################################################
#
# GDB stub serial receive interrupt entry point
# - intended to run at interrupt priority 0
#
###############################################################################
.globl gdbstub_io_rx_handler
.type gdbstub_io_rx_handler,@function
gdbstub_io_rx_handler:
movm [d2,d3,a2,a3],(sp)
mov (gdbstub_rx_inp),a3
gdbstub_io_rx_more:
mov a3,a2
add 2,a3
and PAGE_SIZE_asm-1,a3
mov (gdbstub_rx_outp),d3
cmp a3,d3
beq gdbstub_io_rx_overflow
movbu (SCgSTR),d3
btst SC01STR_RBF,d3
beq gdbstub_io_rx_done
movbu (SCgRXB),d2
movbu d3,(gdbstub_rx_buffer+1,a2)
movbu d2,(gdbstub_rx_buffer,a2)
mov a3,(gdbstub_rx_inp)
bra gdbstub_io_rx_more
gdbstub_io_rx_done:
mov GxICR_DETECT,d2
movbu d2,(GxICR(SCgRXIRQ)) # ACK the interrupt
movhu (GxICR(SCgRXIRQ)),d2 # flush
movm (sp),[d2,d3,a2,a3]
bset 0x01,(gdbstub_busy)
beq gdbstub_io_rx_enter
rti
gdbstub_io_rx_overflow:
bset 0x01,(gdbstub_rx_overflow)
bra gdbstub_io_rx_done
###############################################################################
#
# debugging interrupt - enter the GDB stub proper
#
###############################################################################
gdbstub_io_rx_enter:
or EPSW_IE|EPSW_IM_1,epsw
add -4,sp
SAVE_ALL
mov 0xffffffff,d0
mov d0,(REG_ORIG_D0,fp)
mov 0x280,d1
mov fp,d0
call gdbstub_rx_irq[],0 # gdbstub_io_rx_irq(regs,excep)
and ~EPSW_IE,epsw
bclr 0x01,(gdbstub_busy)
.globl gdbstub_return
gdbstub_return:
RESTORE_ALL
.size gdbstub_io_rx_handler,.-gdbstub_io_rx_handler

View File

@@ -0,0 +1,303 @@
/* MN10300 On-chip serial driver for gdbstub I/O
*
* Copyright (C) 2007 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 Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/console.h>
#include <linux/init.h>
#include <linux/tty.h>
#include <asm/pgtable.h>
#include <asm/gdb-stub.h>
#include <asm/exceptions.h>
#include <unit/clock.h>
#include "mn10300-serial.h"
#if defined(CONFIG_GDBSTUB_ON_TTYSM0)
struct mn10300_serial_port *const gdbstub_port = &mn10300_serial_port_sif0;
#elif defined(CONFIG_GDBSTUB_ON_TTYSM1)
struct mn10300_serial_port *const gdbstub_port = &mn10300_serial_port_sif1;
#else
struct mn10300_serial_port *const gdbstub_port = &mn10300_serial_port_sif2;
#endif
/*
* initialise the GDB stub I/O routines
*/
void __init gdbstub_io_init(void)
{
uint16_t scxctr;
int tmp;
switch (gdbstub_port->clock_src) {
case MNSCx_CLOCK_SRC_IOCLK:
gdbstub_port->ioclk = MN10300_IOCLK;
break;
#ifdef MN10300_IOBCLK
case MNSCx_CLOCK_SRC_IOBCLK:
gdbstub_port->ioclk = MN10300_IOBCLK;
break;
#endif
default:
BUG();
}
/* set up the serial port */
gdbstub_io_set_baud(115200);
/* we want to get serial receive interrupts */
set_intr_level(gdbstub_port->rx_irq,
NUM2GxICR_LEVEL(CONFIG_DEBUGGER_IRQ_LEVEL));
set_intr_level(gdbstub_port->tx_irq,
NUM2GxICR_LEVEL(CONFIG_DEBUGGER_IRQ_LEVEL));
set_intr_stub(NUM2EXCEP_IRQ_LEVEL(CONFIG_DEBUGGER_IRQ_LEVEL),
gdbstub_io_rx_handler);
*gdbstub_port->rx_icr |= GxICR_ENABLE;
tmp = *gdbstub_port->rx_icr;
/* enable the device */
scxctr = SC01CTR_CLN_8BIT; /* 1N8 */
switch (gdbstub_port->div_timer) {
case MNSCx_DIV_TIMER_16BIT:
scxctr |= SC0CTR_CK_TM8UFLOW_8; /* == SC1CTR_CK_TM9UFLOW_8
== SC2CTR_CK_TM10UFLOW_8 */
break;
case MNSCx_DIV_TIMER_8BIT:
scxctr |= SC0CTR_CK_TM2UFLOW_8;
break;
}
scxctr |= SC01CTR_TXE | SC01CTR_RXE;
*gdbstub_port->_control = scxctr;
tmp = *gdbstub_port->_control;
/* permit level 0 IRQs only */
arch_local_change_intr_mask_level(
NUM2EPSW_IM(CONFIG_DEBUGGER_IRQ_LEVEL + 1));
}
/*
* set up the GDB stub serial port baud rate timers
*/
void gdbstub_io_set_baud(unsigned baud)
{
const unsigned bits = 10; /* 1 [start] + 8 [data] + 0 [parity] +
* 1 [stop] */
unsigned long ioclk = gdbstub_port->ioclk;
unsigned xdiv, tmp;
uint16_t tmxbr;
uint8_t tmxmd;
if (!baud) {
baud = 9600;
} else if (baud == 134) {
baud = 269; /* 134 is really 134.5 */
xdiv = 2;
}
try_alternative:
xdiv = 1;
switch (gdbstub_port->div_timer) {
case MNSCx_DIV_TIMER_16BIT:
tmxmd = TM8MD_SRC_IOCLK;
tmxbr = tmp = (ioclk / (baud * xdiv) + 4) / 8 - 1;
if (tmp > 0 && tmp <= 65535)
goto timer_okay;
tmxmd = TM8MD_SRC_IOCLK_8;
tmxbr = tmp = (ioclk / (baud * 8 * xdiv) + 4) / 8 - 1;
if (tmp > 0 && tmp <= 65535)
goto timer_okay;
tmxmd = TM8MD_SRC_IOCLK_32;
tmxbr = tmp = (ioclk / (baud * 32 * xdiv) + 4) / 8 - 1;
if (tmp > 0 && tmp <= 65535)
goto timer_okay;
break;
case MNSCx_DIV_TIMER_8BIT:
tmxmd = TM2MD_SRC_IOCLK;
tmxbr = tmp = (ioclk / (baud * xdiv) + 4) / 8 - 1;
if (tmp > 0 && tmp <= 255)
goto timer_okay;
tmxmd = TM2MD_SRC_IOCLK_8;
tmxbr = tmp = (ioclk / (baud * 8 * xdiv) + 4) / 8 - 1;
if (tmp > 0 && tmp <= 255)
goto timer_okay;
tmxmd = TM2MD_SRC_IOCLK_32;
tmxbr = tmp = (ioclk / (baud * 32 * xdiv) + 4) / 8 - 1;
if (tmp > 0 && tmp <= 255)
goto timer_okay;
break;
}
/* as a last resort, if the quotient is zero, default to 9600 bps */
baud = 9600;
goto try_alternative;
timer_okay:
gdbstub_port->uart.timeout = (2 * bits * HZ) / baud;
gdbstub_port->uart.timeout += HZ / 50;
/* set the timer to produce the required baud rate */
switch (gdbstub_port->div_timer) {
case MNSCx_DIV_TIMER_16BIT:
*gdbstub_port->_tmxmd = 0;
*gdbstub_port->_tmxbr = tmxbr;
*gdbstub_port->_tmxmd = TM8MD_INIT_COUNTER;
*gdbstub_port->_tmxmd = tmxmd | TM8MD_COUNT_ENABLE;
break;
case MNSCx_DIV_TIMER_8BIT:
*gdbstub_port->_tmxmd = 0;
*(volatile u8 *) gdbstub_port->_tmxbr = (u8)tmxbr;
*gdbstub_port->_tmxmd = TM2MD_INIT_COUNTER;
*gdbstub_port->_tmxmd = tmxmd | TM2MD_COUNT_ENABLE;
break;
}
}
/*
* wait for a character to come from the debugger
*/
int gdbstub_io_rx_char(unsigned char *_ch, int nonblock)
{
unsigned ix;
u8 ch, st;
#if defined(CONFIG_MN10300_WD_TIMER)
int cpu;
#endif
*_ch = 0xff;
if (gdbstub_rx_unget) {
*_ch = gdbstub_rx_unget;
gdbstub_rx_unget = 0;
return 0;
}
try_again:
/* pull chars out of the buffer */
ix = gdbstub_rx_outp;
barrier();
if (ix == gdbstub_rx_inp) {
if (nonblock)
return -EAGAIN;
#ifdef CONFIG_MN10300_WD_TIMER
for (cpu = 0; cpu < NR_CPUS; cpu++)
watchdog_alert_counter[cpu] = 0;
#endif
goto try_again;
}
ch = gdbstub_rx_buffer[ix++];
st = gdbstub_rx_buffer[ix++];
barrier();
gdbstub_rx_outp = ix & (PAGE_SIZE - 1);
st &= SC01STR_RXF | SC01STR_RBF | SC01STR_FEF | SC01STR_PEF |
SC01STR_OEF;
/* deal with what we've got
* - note that the UART doesn't do BREAK-detection for us
*/
if (st & SC01STR_FEF && ch == 0) {
switch (gdbstub_port->rx_brk) {
case 0: gdbstub_port->rx_brk = 1; goto try_again;
case 1: gdbstub_port->rx_brk = 2; goto try_again;
case 2:
gdbstub_port->rx_brk = 3;
gdbstub_proto("### GDB MNSERIAL Rx Break Detected"
" ###\n");
return -EINTR;
default:
goto try_again;
}
} else if (st & SC01STR_FEF) {
if (gdbstub_port->rx_brk)
goto try_again;
gdbstub_proto("### GDB MNSERIAL Framing Error ###\n");
return -EIO;
} else if (st & SC01STR_OEF) {
if (gdbstub_port->rx_brk)
goto try_again;
gdbstub_proto("### GDB MNSERIAL Overrun Error ###\n");
return -EIO;
} else if (st & SC01STR_PEF) {
if (gdbstub_port->rx_brk)
goto try_again;
gdbstub_proto("### GDB MNSERIAL Parity Error ###\n");
return -EIO;
} else {
/* look for the tail-end char on a break run */
if (gdbstub_port->rx_brk == 3) {
switch (ch) {
case 0xFF:
case 0xFE:
case 0xFC:
case 0xF8:
case 0xF0:
case 0xE0:
case 0xC0:
case 0x80:
case 0x00:
gdbstub_port->rx_brk = 0;
goto try_again;
default:
break;
}
}
gdbstub_port->rx_brk = 0;
gdbstub_io("### GDB Rx %02x (st=%02x) ###\n", ch, st);
*_ch = ch & 0x7f;
return 0;
}
}
/*
* send a character to the debugger
*/
void gdbstub_io_tx_char(unsigned char ch)
{
while (*gdbstub_port->_status & SC01STR_TBF)
continue;
if (ch == 0x0a) {
*(u8 *) gdbstub_port->_txb = 0x0d;
while (*gdbstub_port->_status & SC01STR_TBF)
continue;
}
*(u8 *) gdbstub_port->_txb = ch;
}
/*
* flush the transmission buffers
*/
void gdbstub_io_tx_flush(void)
{
while (*gdbstub_port->_status & (SC01STR_TBF | SC01STR_TXF))
continue;
}

View File

@@ -0,0 +1,115 @@
###############################################################################
#
# MN10300 Low-level gdbstub routines
#
# Copyright (C) 2007 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 Licence
# as published by the Free Software Foundation; either version
# 2 of the Licence, or (at your option) any later version.
#
###############################################################################
#include <linux/sys.h>
#include <linux/linkage.h>
#include <asm/smp.h>
#include <asm/cache.h>
#include <asm/cpu-regs.h>
#include <asm/exceptions.h>
#include <asm/frame.inc>
#include <asm/serial-regs.h>
.text
###############################################################################
#
# GDB stub read memory with guard
# - D0 holds the memory address to read
# - D1 holds the address to store the byte into
#
###############################################################################
.globl gdbstub_read_byte_guard
.globl gdbstub_read_byte_cont
ENTRY(gdbstub_read_byte)
mov d0,a0
mov d1,a1
clr d0
gdbstub_read_byte_guard:
movbu (a0),d1
gdbstub_read_byte_cont:
movbu d1,(a1)
ret [],0
.globl gdbstub_read_word_guard
.globl gdbstub_read_word_cont
ENTRY(gdbstub_read_word)
mov d0,a0
mov d1,a1
clr d0
gdbstub_read_word_guard:
movhu (a0),d1
gdbstub_read_word_cont:
movhu d1,(a1)
ret [],0
.globl gdbstub_read_dword_guard
.globl gdbstub_read_dword_cont
ENTRY(gdbstub_read_dword)
mov d0,a0
mov d1,a1
clr d0
gdbstub_read_dword_guard:
mov (a0),d1
gdbstub_read_dword_cont:
mov d1,(a1)
ret [],0
###############################################################################
#
# GDB stub write memory with guard
# - D0 holds the byte to store
# - D1 holds the memory address to write
#
###############################################################################
.globl gdbstub_write_byte_guard
.globl gdbstub_write_byte_cont
ENTRY(gdbstub_write_byte)
mov d0,a0
mov d1,a1
clr d0
gdbstub_write_byte_guard:
movbu a0,(a1)
gdbstub_write_byte_cont:
ret [],0
.globl gdbstub_write_word_guard
.globl gdbstub_write_word_cont
ENTRY(gdbstub_write_word)
mov d0,a0
mov d1,a1
clr d0
gdbstub_write_word_guard:
movhu a0,(a1)
gdbstub_write_word_cont:
ret [],0
.globl gdbstub_write_dword_guard
.globl gdbstub_write_dword_cont
ENTRY(gdbstub_write_dword)
mov d0,a0
mov d1,a1
clr d0
gdbstub_write_dword_guard:
mov a0,(a1)
gdbstub_write_dword_cont:
ret [],0
###############################################################################
#
# GDB stub BUG() trap
#
###############################################################################
ENTRY(__gdbstub_bug_trap)
.byte 0xF7,0xF7 # don't use 0xFF as the JTAG unit preempts that
ret [],0

File diff suppressed because it is too large Load Diff

450
arch/mn10300/kernel/head.S Normal file
View File

@@ -0,0 +1,450 @@
/* Boot entry point for MN10300 kernel
*
* Copyright (C) 2005 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 Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/init.h>
#include <linux/threads.h>
#include <linux/linkage.h>
#include <linux/serial_reg.h>
#include <asm/thread_info.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/frame.inc>
#include <asm/param.h>
#include <unit/serial.h>
#ifdef CONFIG_SMP
#include <asm/smp.h>
#include <asm/intctl-regs.h>
#include <asm/cpu-regs.h>
#include <proc/smp-regs.h>
#endif /* CONFIG_SMP */
__HEAD
###############################################################################
#
# bootloader entry point
#
###############################################################################
.globl _start
.type _start,@function
_start:
#ifdef CONFIG_SMP
#
# If this is a secondary CPU (AP), then deal with that elsewhere
#
mov (CPUID),d3
and CPUID_MASK,d3
bne startup_secondary
#
# We're dealing with the primary CPU (BP) here, then.
# Keep BP's D0,D1,D2 register for boot check.
#
# Set up the Boot IPI for each secondary CPU
mov 0x1,a0
loop_set_secondary_icr:
mov a0,a1
asl CROSS_ICR_CPU_SHIFT,a1
add CROSS_GxICR(SMP_BOOT_IRQ,0),a1
movhu (a1),d3
or GxICR_ENABLE|GxICR_LEVEL_0,d3
movhu d3,(a1)
movhu (a1),d3 # flush
inc a0
cmp NR_CPUS,a0
bne loop_set_secondary_icr
#endif /* CONFIG_SMP */
# save commandline pointer
mov d0,a3
# preload the PGD pointer register
mov swapper_pg_dir,d0
mov d0,(PTBR)
clr d0
movbu d0,(PIDR)
# turn on the TLBs
mov MMUCTR_IIV|MMUCTR_DIV,d0
mov d0,(MMUCTR)
#ifdef CONFIG_AM34_2
mov MMUCTR_ITE|MMUCTR_DTE|MMUCTR_CE|MMUCTR_WTE,d0
#else
mov MMUCTR_ITE|MMUCTR_DTE|MMUCTR_CE,d0
#endif
mov d0,(MMUCTR)
# turn on AM33v2 exception handling mode and set the trap table base
movhu (CPUP),d0
or CPUP_EXM_AM33V2,d0
movhu d0,(CPUP)
mov CONFIG_INTERRUPT_VECTOR_BASE,d0
mov d0,(TBR)
# invalidate and enable both of the caches
#ifdef CONFIG_SMP
mov ECHCTR,a0
clr d0
mov d0,(a0)
#endif
mov CHCTR,a0
clr d0
movhu d0,(a0) # turn off first
mov CHCTR_ICINV|CHCTR_DCINV,d0
movhu d0,(a0)
setlb
mov (a0),d0
btst CHCTR_ICBUSY|CHCTR_DCBUSY,d0 # wait till not busy
lne
#ifdef CONFIG_MN10300_CACHE_ENABLED
#ifdef CONFIG_MN10300_CACHE_WBACK
#ifndef CONFIG_MN10300_CACHE_WBACK_NOWRALLOC
mov CHCTR_ICEN|CHCTR_DCEN|CHCTR_DCWTMD_WRBACK,d0
#else
mov CHCTR_ICEN|CHCTR_DCEN|CHCTR_DCWTMD_WRBACK|CHCTR_DCALMD,d0
#endif /* NOWRALLOC */
#else
mov CHCTR_ICEN|CHCTR_DCEN|CHCTR_DCWTMD_WRTHROUGH,d0
#endif /* WBACK */
movhu d0,(a0) # enable
#endif /* ENABLED */
# turn on RTS on the debug serial port if applicable
#ifdef CONFIG_MN10300_UNIT_ASB2305
bset UART_MCR_RTS,(ASB2305_DEBUG_MCR)
#endif
# clear the BSS area
mov __bss_start,a0
mov __bss_stop,a1
clr d0
bssclear:
cmp a1,a0
bge bssclear_end
mov d0,(a0)
inc4 a0
bra bssclear
bssclear_end:
# retrieve the parameters (including command line) before we overwrite
# them
cmp 0xabadcafe,d1
bne __no_parameters
__copy_parameters:
mov redboot_command_line,a0
mov a0,a1
add COMMAND_LINE_SIZE,a1
1:
movbu (a3),d0
inc a3
movbu d0,(a0)
inc a0
cmp a1,a0
blt 1b
mov redboot_platform_name,a0
mov a0,a1
add COMMAND_LINE_SIZE,a1
mov d2,a3
1:
movbu (a3),d0
inc a3
movbu d0,(a0)
inc a0
cmp a1,a0
blt 1b
__no_parameters:
# set up the registers with recognisable rubbish in them
mov init_thread_union+THREAD_SIZE-12,sp
mov 0xea01eaea,d0
mov d0,(4,sp) # EPSW save area
mov 0xea02eaea,d0
mov d0,(8,sp) # PC save area
mov 0xeb0060ed,d0
mov d0,mdr
mov 0xeb0061ed,d0
mov d0,mdrq
mov 0xeb0062ed,d0
mov d0,mcrh
mov 0xeb0063ed,d0
mov d0,mcrl
mov 0xeb0064ed,d0
mov d0,mcvf
mov 0xed0065ed,a3
mov a3,usp
mov 0xed00e0ed,e0
mov 0xed00e1ed,e1
mov 0xed00e2ed,e2
mov 0xed00e3ed,e3
mov 0xed00e4ed,e4
mov 0xed00e5ed,e5
mov 0xed00e6ed,e6
mov 0xed00e7ed,e7
mov 0xed00d0ed,d0
mov 0xed00d1ed,d1
mov 0xed00d2ed,d2
mov 0xed00d3ed,d3
mov 0xed00a0ed,a0
mov 0xed00a1ed,a1
mov 0xed00a2ed,a2
mov 0,a3
# set up the initial kernel stack
SAVE_ALL
mov 0xffffffff,d0
mov d0,(REG_ORIG_D0,fp)
# put different recognisable rubbish in the regs
mov 0xfb0060ed,d0
mov d0,mdr
mov 0xfb0061ed,d0
mov d0,mdrq
mov 0xfb0062ed,d0
mov d0,mcrh
mov 0xfb0063ed,d0
mov d0,mcrl
mov 0xfb0064ed,d0
mov d0,mcvf
mov 0xfd0065ed,a0
mov a0,usp
mov 0xfd00e0ed,e0
mov 0xfd00e1ed,e1
mov 0xfd00e2ed,e2
mov 0xfd00e3ed,e3
mov 0xfd00e4ed,e4
mov 0xfd00e5ed,e5
mov 0xfd00e6ed,e6
mov 0xfd00e7ed,e7
mov 0xfd00d0ed,d0
mov 0xfd00d1ed,d1
mov 0xfd00d2ed,d2
mov 0xfd00d3ed,d3
mov 0xfd00a0ed,a0
mov 0xfd00a1ed,a1
mov 0xfd00a2ed,a2
# we may be holding current in E2
#ifdef CONFIG_MN10300_CURRENT_IN_E2
mov init_task,e2
#endif
# initialise the processor and the unit
call processor_init[],0
call unit_init[],0
#ifdef CONFIG_SMP
# mark the primary CPU in cpu_boot_map
mov cpu_boot_map,a0
mov 0x1,d0
mov d0,(a0)
# signal each secondary CPU to begin booting
mov 0x1,d2 # CPU ID
loop_request_boot_secondary:
mov d2,a0
# send SMP_BOOT_IPI to secondary CPU
asl CROSS_ICR_CPU_SHIFT,a0
add CROSS_GxICR(SMP_BOOT_IRQ,0),a0
movhu (a0),d0
or GxICR_REQUEST|GxICR_DETECT,d0
movhu d0,(a0)
movhu (a0),d0 # flush
# wait up to 100ms for AP's IPI to be received
clr d3
wait_on_secondary_boot:
mov DELAY_TIME_BOOT_IPI,d0
call __delay[],0
inc d3
mov cpu_boot_map,a0
mov (a0),d0
lsr d2,d0
btst 0x1,d0
bne 1f
cmp TIME_OUT_COUNT_BOOT_IPI,d3
bne wait_on_secondary_boot
1:
inc d2
cmp NR_CPUS,d2
bne loop_request_boot_secondary
#endif /* CONFIG_SMP */
#ifdef CONFIG_GDBSTUB
call gdbstub_init[],0
#ifdef CONFIG_GDBSTUB_IMMEDIATE
.globl __gdbstub_pause
__gdbstub_pause:
bra __gdbstub_pause
#endif
#endif
jmp start_kernel
.size _start,.-_start
###############################################################################
#
# Secondary CPU boot point
#
###############################################################################
#ifdef CONFIG_SMP
startup_secondary:
# preload the PGD pointer register
mov swapper_pg_dir,d0
mov d0,(PTBR)
clr d0
movbu d0,(PIDR)
# turn on the TLBs
mov MMUCTR_IIV|MMUCTR_DIV,d0
mov d0,(MMUCTR)
#ifdef CONFIG_AM34_2
mov MMUCTR_ITE|MMUCTR_DTE|MMUCTR_CE|MMUCTR_WTE,d0
#else
mov MMUCTR_ITE|MMUCTR_DTE|MMUCTR_CE,d0
#endif
mov d0,(MMUCTR)
# turn on AM33v2 exception handling mode and set the trap table base
movhu (CPUP),d0
or CPUP_EXM_AM33V2,d0
movhu d0,(CPUP)
# set the interrupt vector table
mov CONFIG_INTERRUPT_VECTOR_BASE,d0
mov d0,(TBR)
# invalidate and enable both of the caches
mov ECHCTR,a0
clr d0
mov d0,(a0)
mov CHCTR,a0
clr d0
movhu d0,(a0) # turn off first
mov CHCTR_ICINV|CHCTR_DCINV,d0
movhu d0,(a0)
setlb
mov (a0),d0
btst CHCTR_ICBUSY|CHCTR_DCBUSY,d0 # wait till not busy (use CPU loop buffer)
lne
#ifdef CONFIG_MN10300_CACHE_ENABLED
#ifdef CONFIG_MN10300_CACHE_WBACK
#ifndef CONFIG_MN10300_CACHE_WBACK_NOWRALLOC
mov CHCTR_ICEN|CHCTR_DCEN|CHCTR_DCWTMD_WRBACK,d0
#else
mov CHCTR_ICEN|CHCTR_DCEN|CHCTR_DCWTMD_WRBACK|CHCTR_DCALMD,d0
#endif /* !NOWRALLOC */
#else
mov CHCTR_ICEN|CHCTR_DCEN|CHCTR_DCWTMD_WRTHROUGH,d0
#endif /* WBACK */
movhu d0,(a0) # enable
#endif /* ENABLED */
# Clear the boot IPI interrupt for this CPU
movhu (GxICR(SMP_BOOT_IRQ)),d0
and ~GxICR_REQUEST,d0
movhu d0,(GxICR(SMP_BOOT_IRQ))
movhu (GxICR(SMP_BOOT_IRQ)),d0 # flush
/* get stack */
mov CONFIG_INTERRUPT_VECTOR_BASE + CONFIG_BOOT_STACK_OFFSET,a0
mov (CPUID),d0
and CPUID_MASK,d0
mulu CONFIG_BOOT_STACK_SIZE,d0
sub d0,a0
mov a0,sp
# init interrupt for AP
call smp_prepare_cpu_init[],0
# mark this secondary CPU in cpu_boot_map
mov (CPUID),d0
mov 0x1,d1
asl d0,d1
mov cpu_boot_map,a0
bset d1,(a0)
or EPSW_IE|EPSW_IM_1,epsw # permit level 0 interrupts
nop
nop
#ifdef CONFIG_MN10300_CACHE_WBACK
# flush the local cache if it's in writeback mode
call mn10300_local_dcache_flush_inv[],0
setlb
mov (CHCTR),d0
btst CHCTR_DCBUSY,d0 # wait till not busy (use CPU loop buffer)
lne
#endif
# now sleep waiting for further instructions
secondary_sleep:
mov CPUM_SLEEP,d0
movhu d0,(CPUM)
nop
nop
bra secondary_sleep
.size startup_secondary,.-startup_secondary
#endif /* CONFIG_SMP */
###############################################################################
#
#
#
###############################################################################
ENTRY(__head_end)
/*
* This is initialized to disallow all access to the low 2G region
* - the high 2G region is managed directly by the MMU
* - range 0x70000000-0x7C000000 are initialised for use by VMALLOC
*/
.section .bss
.balign PAGE_SIZE
ENTRY(swapper_pg_dir)
.space PTRS_PER_PGD*4
/*
* The page tables are initialized to only 8MB here - the final page
* tables are set up later depending on memory size.
*/
.balign PAGE_SIZE
ENTRY(empty_zero_page)
.space PAGE_SIZE
.balign PAGE_SIZE
ENTRY(empty_bad_page)
.space PAGE_SIZE
.balign PAGE_SIZE
ENTRY(empty_bad_pte_table)
.space PAGE_SIZE
.balign PAGE_SIZE
ENTRY(large_page_table)
.space PAGE_SIZE
.balign PAGE_SIZE
ENTRY(kernel_vmalloc_ptes)
.space ((VMALLOC_END-VMALLOC_START)/PAGE_SIZE)*4

View File

@@ -0,0 +1,40 @@
/* Internal definitions for the arch part of the core kernel
*
* Copyright (C) 2007 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 Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/irqreturn.h>
struct clocksource;
struct clock_event_device;
/*
* entry.S
*/
extern void ret_from_fork(struct task_struct *) __attribute__((noreturn));
extern void ret_from_kernel_thread(struct task_struct *) __attribute__((noreturn));
/*
* smp-low.S
*/
#ifdef CONFIG_SMP
extern void mn10300_low_ipi_handler(void);
#endif
/*
* smp.c
*/
#ifdef CONFIG_SMP
extern void smp_jump_to_debugger(void);
#endif
/*
* time.c
*/
extern irqreturn_t local_timer_interrupt(void);

30
arch/mn10300/kernel/io.c Normal file
View File

@@ -0,0 +1,30 @@
/* MN10300 Misaligned multibyte-word I/O
*
* Copyright (C) 2007 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 Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/module.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <asm/io.h>
/*
* output data from a potentially misaligned buffer
*/
void __outsl(unsigned long addr, const void *buffer, int count)
{
const unsigned char *buf = buffer;
unsigned long val;
while (count--) {
memcpy(&val, buf, 4);
outl(val, addr);
buf += 4;
}
}
EXPORT_SYMBOL(__outsl);

355
arch/mn10300/kernel/irq.c Normal file
View File

@@ -0,0 +1,355 @@
/* MN10300 Arch-specific interrupt handling
*
* Copyright (C) 2007 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 Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
#include <linux/seq_file.h>
#include <linux/cpumask.h>
#include <asm/setup.h>
#include <asm/serial-regs.h>
unsigned long __mn10300_irq_enabled_epsw[NR_CPUS] __cacheline_aligned_in_smp = {
[0 ... NR_CPUS - 1] = EPSW_IE | EPSW_IM_7
};
EXPORT_SYMBOL(__mn10300_irq_enabled_epsw);
#ifdef CONFIG_SMP
static char irq_affinity_online[NR_IRQS] = {
[0 ... NR_IRQS - 1] = 0
};
#define NR_IRQ_WORDS ((NR_IRQS + 31) / 32)
static unsigned long irq_affinity_request[NR_IRQ_WORDS] = {
[0 ... NR_IRQ_WORDS - 1] = 0
};
#endif /* CONFIG_SMP */
atomic_t irq_err_count;
/*
* MN10300 interrupt controller operations
*/
static void mn10300_cpupic_ack(struct irq_data *d)
{
unsigned int irq = d->irq;
unsigned long flags;
u16 tmp;
flags = arch_local_cli_save();
GxICR_u8(irq) = GxICR_DETECT;
tmp = GxICR(irq);
arch_local_irq_restore(flags);
}
static void __mask_and_set_icr(unsigned int irq,
unsigned int mask, unsigned int set)
{
unsigned long flags;
u16 tmp;
flags = arch_local_cli_save();
tmp = GxICR(irq);
GxICR(irq) = (tmp & mask) | set;
tmp = GxICR(irq);
arch_local_irq_restore(flags);
}
static void mn10300_cpupic_mask(struct irq_data *d)
{
__mask_and_set_icr(d->irq, GxICR_LEVEL, 0);
}
static void mn10300_cpupic_mask_ack(struct irq_data *d)
{
unsigned int irq = d->irq;
#ifdef CONFIG_SMP
unsigned long flags;
u16 tmp;
flags = arch_local_cli_save();
if (!test_and_clear_bit(irq, irq_affinity_request)) {
tmp = GxICR(irq);
GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_DETECT;
tmp = GxICR(irq);
} else {
u16 tmp2;
tmp = GxICR(irq);
GxICR(irq) = (tmp & GxICR_LEVEL);
tmp2 = GxICR(irq);
irq_affinity_online[irq] =
cpumask_any_and(d->affinity, cpu_online_mask);
CROSS_GxICR(irq, irq_affinity_online[irq]) =
(tmp & (GxICR_LEVEL | GxICR_ENABLE)) | GxICR_DETECT;
tmp = CROSS_GxICR(irq, irq_affinity_online[irq]);
}
arch_local_irq_restore(flags);
#else /* CONFIG_SMP */
__mask_and_set_icr(irq, GxICR_LEVEL, GxICR_DETECT);
#endif /* CONFIG_SMP */
}
static void mn10300_cpupic_unmask(struct irq_data *d)
{
__mask_and_set_icr(d->irq, GxICR_LEVEL, GxICR_ENABLE);
}
static void mn10300_cpupic_unmask_clear(struct irq_data *d)
{
unsigned int irq = d->irq;
/* the MN10300 PIC latches its interrupt request bit, even after the
* device has ceased to assert its interrupt line and the interrupt
* channel has been disabled in the PIC, so for level-triggered
* interrupts we need to clear the request bit when we re-enable */
#ifdef CONFIG_SMP
unsigned long flags;
u16 tmp;
flags = arch_local_cli_save();
if (!test_and_clear_bit(irq, irq_affinity_request)) {
tmp = GxICR(irq);
GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_ENABLE | GxICR_DETECT;
tmp = GxICR(irq);
} else {
tmp = GxICR(irq);
irq_affinity_online[irq] = cpumask_any_and(d->affinity,
cpu_online_mask);
CROSS_GxICR(irq, irq_affinity_online[irq]) = (tmp & GxICR_LEVEL) | GxICR_ENABLE | GxICR_DETECT;
tmp = CROSS_GxICR(irq, irq_affinity_online[irq]);
}
arch_local_irq_restore(flags);
#else /* CONFIG_SMP */
__mask_and_set_icr(irq, GxICR_LEVEL, GxICR_ENABLE | GxICR_DETECT);
#endif /* CONFIG_SMP */
}
#ifdef CONFIG_SMP
static int
mn10300_cpupic_setaffinity(struct irq_data *d, const struct cpumask *mask,
bool force)
{
unsigned long flags;
flags = arch_local_cli_save();
set_bit(d->irq, irq_affinity_request);
arch_local_irq_restore(flags);
return 0;
}
#endif /* CONFIG_SMP */
/*
* MN10300 PIC level-triggered IRQ handling.
*
* The PIC has no 'ACK' function per se. It is possible to clear individual
* channel latches, but each latch relatches whether or not the channel is
* masked, so we need to clear the latch when we unmask the channel.
*
* Also for this reason, we don't supply an ack() op (it's unused anyway if
* mask_ack() is provided), and mask_ack() just masks.
*/
static struct irq_chip mn10300_cpu_pic_level = {
.name = "cpu_l",
.irq_disable = mn10300_cpupic_mask,
.irq_enable = mn10300_cpupic_unmask_clear,
.irq_ack = NULL,
.irq_mask = mn10300_cpupic_mask,
.irq_mask_ack = mn10300_cpupic_mask,
.irq_unmask = mn10300_cpupic_unmask_clear,
#ifdef CONFIG_SMP
.irq_set_affinity = mn10300_cpupic_setaffinity,
#endif
};
/*
* MN10300 PIC edge-triggered IRQ handling.
*
* We use the latch clearing function of the PIC as the 'ACK' function.
*/
static struct irq_chip mn10300_cpu_pic_edge = {
.name = "cpu_e",
.irq_disable = mn10300_cpupic_mask,
.irq_enable = mn10300_cpupic_unmask,
.irq_ack = mn10300_cpupic_ack,
.irq_mask = mn10300_cpupic_mask,
.irq_mask_ack = mn10300_cpupic_mask_ack,
.irq_unmask = mn10300_cpupic_unmask,
#ifdef CONFIG_SMP
.irq_set_affinity = mn10300_cpupic_setaffinity,
#endif
};
/*
* 'what should we do if we get a hw irq event on an illegal vector'.
* each architecture has to answer this themselves.
*/
void ack_bad_irq(int irq)
{
printk(KERN_WARNING "unexpected IRQ trap at vector %02x\n", irq);
}
/*
* change the level at which an IRQ executes
* - must not be called whilst interrupts are being processed!
*/
void set_intr_level(int irq, u16 level)
{
BUG_ON(in_interrupt());
__mask_and_set_icr(irq, GxICR_ENABLE, level);
}
/*
* mark an interrupt to be ACK'd after interrupt handlers have been run rather
* than before
*/
void mn10300_set_lateack_irq_type(int irq)
{
irq_set_chip_and_handler(irq, &mn10300_cpu_pic_level,
handle_level_irq);
}
/*
* initialise the interrupt system
*/
void __init init_IRQ(void)
{
int irq;
for (irq = 0; irq < NR_IRQS; irq++)
if (irq_get_chip(irq) == &no_irq_chip)
/* due to the PIC latching interrupt requests, even
* when the IRQ is disabled, IRQ_PENDING is superfluous
* and we can use handle_level_irq() for edge-triggered
* interrupts */
irq_set_chip_and_handler(irq, &mn10300_cpu_pic_edge,
handle_level_irq);
unit_init_IRQ();
}
/*
* handle normal device IRQs
*/
asmlinkage void do_IRQ(void)
{
unsigned long sp, epsw, irq_disabled_epsw, old_irq_enabled_epsw;
unsigned int cpu_id = smp_processor_id();
int irq;
sp = current_stack_pointer();
BUG_ON(sp - (sp & ~(THREAD_SIZE - 1)) < STACK_WARN);
/* make sure local_irq_enable() doesn't muck up the interrupt priority
* setting in EPSW */
old_irq_enabled_epsw = __mn10300_irq_enabled_epsw[cpu_id];
local_save_flags(epsw);
__mn10300_irq_enabled_epsw[cpu_id] = EPSW_IE | (EPSW_IM & epsw);
irq_disabled_epsw = EPSW_IE | MN10300_CLI_LEVEL;
#ifdef CONFIG_MN10300_WD_TIMER
__IRQ_STAT(cpu_id, __irq_count)++;
#endif
irq_enter();
for (;;) {
/* ask the interrupt controller for the next IRQ to process
* - the result we get depends on EPSW.IM
*/
irq = IAGR & IAGR_GN;
if (!irq)
break;
local_irq_restore(irq_disabled_epsw);
generic_handle_irq(irq >> 2);
/* restore IRQ controls for IAGR access */
local_irq_restore(epsw);
}
__mn10300_irq_enabled_epsw[cpu_id] = old_irq_enabled_epsw;
irq_exit();
}
/*
* Display interrupt management information through /proc/interrupts
*/
int arch_show_interrupts(struct seq_file *p, int prec)
{
#ifdef CONFIG_MN10300_WD_TIMER
int j;
seq_printf(p, "%*s: ", prec, "NMI");
for (j = 0; j < NR_CPUS; j++)
if (cpu_online(j))
seq_printf(p, "%10u ", nmi_count(j));
seq_putc(p, '\n');
#endif
seq_printf(p, "%*s: ", prec, "ERR");
seq_printf(p, "%10u\n", atomic_read(&irq_err_count));
return 0;
}
#ifdef CONFIG_HOTPLUG_CPU
void migrate_irqs(void)
{
int irq;
unsigned int self, new;
unsigned long flags;
self = smp_processor_id();
for (irq = 0; irq < NR_IRQS; irq++) {
struct irq_data *data = irq_get_irq_data(irq);
if (irqd_is_per_cpu(data))
continue;
if (cpumask_test_cpu(self, &data->affinity) &&
!cpumask_intersects(&irq_affinity[irq], cpu_online_mask)) {
int cpu_id;
cpu_id = cpumask_first(cpu_online_mask);
cpumask_set_cpu(cpu_id, &data->affinity);
}
/* We need to operate irq_affinity_online atomically. */
arch_local_cli_save(flags);
if (irq_affinity_online[irq] == self) {
u16 x, tmp;
x = GxICR(irq);
GxICR(irq) = x & GxICR_LEVEL;
tmp = GxICR(irq);
new = cpumask_any_and(&data->affinity,
cpu_online_mask);
irq_affinity_online[irq] = new;
CROSS_GxICR(irq, new) =
(x & GxICR_LEVEL) | GxICR_DETECT;
tmp = CROSS_GxICR(irq, new);
x &= GxICR_LEVEL | GxICR_ENABLE;
if (GxICR(irq) & GxICR_REQUEST)
x |= GxICR_REQUEST | GxICR_DETECT;
CROSS_GxICR(irq, new) = x;
tmp = CROSS_GxICR(irq, new);
}
arch_local_irq_restore(flags);
}
}
#endif /* CONFIG_HOTPLUG_CPU */

501
arch/mn10300/kernel/kgdb.c Normal file
View File

@@ -0,0 +1,501 @@
/* kgdb support for MN10300
*
* Copyright (C) 2010 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 Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/slab.h>
#include <linux/ptrace.h>
#include <linux/kgdb.h>
#include <linux/uaccess.h>
#include <unit/leds.h>
#include <unit/serial.h>
#include <asm/debugger.h>
#include <asm/serial-regs.h>
#include "internal.h"
/*
* Software single-stepping breakpoint save (used by __switch_to())
*/
static struct thread_info *kgdb_sstep_thread;
u8 *kgdb_sstep_bp_addr[2];
u8 kgdb_sstep_bp[2];
/*
* Copy kernel exception frame registers to the GDB register file
*/
void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
{
unsigned long ssp = (unsigned long) (regs + 1);
gdb_regs[GDB_FR_D0] = regs->d0;
gdb_regs[GDB_FR_D1] = regs->d1;
gdb_regs[GDB_FR_D2] = regs->d2;
gdb_regs[GDB_FR_D3] = regs->d3;
gdb_regs[GDB_FR_A0] = regs->a0;
gdb_regs[GDB_FR_A1] = regs->a1;
gdb_regs[GDB_FR_A2] = regs->a2;
gdb_regs[GDB_FR_A3] = regs->a3;
gdb_regs[GDB_FR_SP] = (regs->epsw & EPSW_nSL) ? regs->sp : ssp;
gdb_regs[GDB_FR_PC] = regs->pc;
gdb_regs[GDB_FR_MDR] = regs->mdr;
gdb_regs[GDB_FR_EPSW] = regs->epsw;
gdb_regs[GDB_FR_LIR] = regs->lir;
gdb_regs[GDB_FR_LAR] = regs->lar;
gdb_regs[GDB_FR_MDRQ] = regs->mdrq;
gdb_regs[GDB_FR_E0] = regs->e0;
gdb_regs[GDB_FR_E1] = regs->e1;
gdb_regs[GDB_FR_E2] = regs->e2;
gdb_regs[GDB_FR_E3] = regs->e3;
gdb_regs[GDB_FR_E4] = regs->e4;
gdb_regs[GDB_FR_E5] = regs->e5;
gdb_regs[GDB_FR_E6] = regs->e6;
gdb_regs[GDB_FR_E7] = regs->e7;
gdb_regs[GDB_FR_SSP] = ssp;
gdb_regs[GDB_FR_MSP] = 0;
gdb_regs[GDB_FR_USP] = regs->sp;
gdb_regs[GDB_FR_MCRH] = regs->mcrh;
gdb_regs[GDB_FR_MCRL] = regs->mcrl;
gdb_regs[GDB_FR_MCVF] = regs->mcvf;
gdb_regs[GDB_FR_DUMMY0] = 0;
gdb_regs[GDB_FR_DUMMY1] = 0;
gdb_regs[GDB_FR_FS0] = 0;
}
/*
* Extracts kernel SP/PC values understandable by gdb from the values
* saved by switch_to().
*/
void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
{
gdb_regs[GDB_FR_SSP] = p->thread.sp;
gdb_regs[GDB_FR_PC] = p->thread.pc;
gdb_regs[GDB_FR_A3] = p->thread.a3;
gdb_regs[GDB_FR_USP] = p->thread.usp;
gdb_regs[GDB_FR_FPCR] = p->thread.fpu_state.fpcr;
}
/*
* Fill kernel exception frame registers from the GDB register file
*/
void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs)
{
regs->d0 = gdb_regs[GDB_FR_D0];
regs->d1 = gdb_regs[GDB_FR_D1];
regs->d2 = gdb_regs[GDB_FR_D2];
regs->d3 = gdb_regs[GDB_FR_D3];
regs->a0 = gdb_regs[GDB_FR_A0];
regs->a1 = gdb_regs[GDB_FR_A1];
regs->a2 = gdb_regs[GDB_FR_A2];
regs->a3 = gdb_regs[GDB_FR_A3];
regs->sp = gdb_regs[GDB_FR_SP];
regs->pc = gdb_regs[GDB_FR_PC];
regs->mdr = gdb_regs[GDB_FR_MDR];
regs->epsw = gdb_regs[GDB_FR_EPSW];
regs->lir = gdb_regs[GDB_FR_LIR];
regs->lar = gdb_regs[GDB_FR_LAR];
regs->mdrq = gdb_regs[GDB_FR_MDRQ];
regs->e0 = gdb_regs[GDB_FR_E0];
regs->e1 = gdb_regs[GDB_FR_E1];
regs->e2 = gdb_regs[GDB_FR_E2];
regs->e3 = gdb_regs[GDB_FR_E3];
regs->e4 = gdb_regs[GDB_FR_E4];
regs->e5 = gdb_regs[GDB_FR_E5];
regs->e6 = gdb_regs[GDB_FR_E6];
regs->e7 = gdb_regs[GDB_FR_E7];
regs->sp = gdb_regs[GDB_FR_SSP];
/* gdb_regs[GDB_FR_MSP]; */
// regs->usp = gdb_regs[GDB_FR_USP];
regs->mcrh = gdb_regs[GDB_FR_MCRH];
regs->mcrl = gdb_regs[GDB_FR_MCRL];
regs->mcvf = gdb_regs[GDB_FR_MCVF];
/* gdb_regs[GDB_FR_DUMMY0]; */
/* gdb_regs[GDB_FR_DUMMY1]; */
// regs->fpcr = gdb_regs[GDB_FR_FPCR];
// regs->fs0 = gdb_regs[GDB_FR_FS0];
}
struct kgdb_arch arch_kgdb_ops = {
.gdb_bpt_instr = { 0xff },
.flags = KGDB_HW_BREAKPOINT,
};
static const unsigned char mn10300_kgdb_insn_sizes[256] =
{
/* 1 2 3 4 5 6 7 8 9 a b c d e f */
1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, /* 0 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 1 */
2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, /* 2 */
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, /* 3 */
1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, /* 4 */
1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, /* 5 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 7 */
2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 8 */
2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 9 */
2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* a */
2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* b */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, /* c */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* d */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* e */
0, 2, 2, 2, 2, 2, 2, 4, 0, 3, 0, 4, 0, 6, 7, 1 /* f */
};
/*
* Attempt to emulate single stepping by means of breakpoint instructions.
* Although there is a single-step trace flag in EPSW, its use is not
* sufficiently documented and is only intended for use with the JTAG debugger.
*/
static int kgdb_arch_do_singlestep(struct pt_regs *regs)
{
unsigned long arg;
unsigned size;
u8 *pc = (u8 *)regs->pc, *sp = (u8 *)(regs + 1), cur;
u8 *x = NULL, *y = NULL;
int ret;
ret = probe_kernel_read(&cur, pc, 1);
if (ret < 0)
return ret;
size = mn10300_kgdb_insn_sizes[cur];
if (size > 0) {
x = pc + size;
goto set_x;
}
switch (cur) {
/* Bxx (d8,PC) */
case 0xc0 ... 0xca:
ret = probe_kernel_read(&arg, pc + 1, 1);
if (ret < 0)
return ret;
x = pc + 2;
if (arg >= 0 && arg <= 2)
goto set_x;
y = pc + (s8)arg;
goto set_x_and_y;
/* LXX (d8,PC) */
case 0xd0 ... 0xda:
x = pc + 1;
if (regs->pc == regs->lar)
goto set_x;
y = (u8 *)regs->lar;
goto set_x_and_y;
/* SETLB - loads the next four bytes into the LIR register
* (which mustn't include a breakpoint instruction) */
case 0xdb:
x = pc + 5;
goto set_x;
/* JMP (d16,PC) or CALL (d16,PC) */
case 0xcc:
case 0xcd:
ret = probe_kernel_read(&arg, pc + 1, 2);
if (ret < 0)
return ret;
x = pc + (s16)arg;
goto set_x;
/* JMP (d32,PC) or CALL (d32,PC) */
case 0xdc:
case 0xdd:
ret = probe_kernel_read(&arg, pc + 1, 4);
if (ret < 0)
return ret;
x = pc + (s32)arg;
goto set_x;
/* RETF */
case 0xde:
x = (u8 *)regs->mdr;
goto set_x;
/* RET */
case 0xdf:
ret = probe_kernel_read(&arg, pc + 2, 1);
if (ret < 0)
return ret;
ret = probe_kernel_read(&x, sp + (s8)arg, 4);
if (ret < 0)
return ret;
goto set_x;
case 0xf0:
ret = probe_kernel_read(&cur, pc + 1, 1);
if (ret < 0)
return ret;
if (cur >= 0xf0 && cur <= 0xf7) {
/* JMP (An) / CALLS (An) */
switch (cur & 3) {
case 0: x = (u8 *)regs->a0; break;
case 1: x = (u8 *)regs->a1; break;
case 2: x = (u8 *)regs->a2; break;
case 3: x = (u8 *)regs->a3; break;
}
goto set_x;
} else if (cur == 0xfc) {
/* RETS */
ret = probe_kernel_read(&x, sp, 4);
if (ret < 0)
return ret;
goto set_x;
} else if (cur == 0xfd) {
/* RTI */
ret = probe_kernel_read(&x, sp + 4, 4);
if (ret < 0)
return ret;
goto set_x;
} else {
x = pc + 2;
goto set_x;
}
break;
/* potential 3-byte conditional branches */
case 0xf8:
ret = probe_kernel_read(&cur, pc + 1, 1);
if (ret < 0)
return ret;
x = pc + 3;
if (cur >= 0xe8 && cur <= 0xeb) {
ret = probe_kernel_read(&arg, pc + 2, 1);
if (ret < 0)
return ret;
if (arg >= 0 && arg <= 3)
goto set_x;
y = pc + (s8)arg;
goto set_x_and_y;
}
goto set_x;
case 0xfa:
ret = probe_kernel_read(&cur, pc + 1, 1);
if (ret < 0)
return ret;
if (cur == 0xff) {
/* CALLS (d16,PC) */
ret = probe_kernel_read(&arg, pc + 2, 2);
if (ret < 0)
return ret;
x = pc + (s16)arg;
goto set_x;
}
x = pc + 4;
goto set_x;
case 0xfc:
ret = probe_kernel_read(&cur, pc + 1, 1);
if (ret < 0)
return ret;
if (cur == 0xff) {
/* CALLS (d32,PC) */
ret = probe_kernel_read(&arg, pc + 2, 4);
if (ret < 0)
return ret;
x = pc + (s32)arg;
goto set_x;
}
x = pc + 6;
goto set_x;
}
return 0;
set_x:
kgdb_sstep_bp_addr[0] = x;
kgdb_sstep_bp_addr[1] = NULL;
ret = probe_kernel_read(&kgdb_sstep_bp[0], x, 1);
if (ret < 0)
return ret;
ret = probe_kernel_write(x, &arch_kgdb_ops.gdb_bpt_instr, 1);
if (ret < 0)
return ret;
kgdb_sstep_thread = current_thread_info();
debugger_local_cache_flushinv_one(x);
return ret;
set_x_and_y:
kgdb_sstep_bp_addr[0] = x;
kgdb_sstep_bp_addr[1] = y;
ret = probe_kernel_read(&kgdb_sstep_bp[0], x, 1);
if (ret < 0)
return ret;
ret = probe_kernel_read(&kgdb_sstep_bp[1], y, 1);
if (ret < 0)
return ret;
ret = probe_kernel_write(x, &arch_kgdb_ops.gdb_bpt_instr, 1);
if (ret < 0)
return ret;
ret = probe_kernel_write(y, &arch_kgdb_ops.gdb_bpt_instr, 1);
if (ret < 0) {
probe_kernel_write(kgdb_sstep_bp_addr[0],
&kgdb_sstep_bp[0], 1);
} else {
kgdb_sstep_thread = current_thread_info();
}
debugger_local_cache_flushinv_one(x);
debugger_local_cache_flushinv_one(y);
return ret;
}
/*
* Remove emplaced single-step breakpoints, returning true if we hit one of
* them.
*/
static bool kgdb_arch_undo_singlestep(struct pt_regs *regs)
{
bool hit = false;
u8 *x = kgdb_sstep_bp_addr[0], *y = kgdb_sstep_bp_addr[1];
u8 opcode;
if (kgdb_sstep_thread == current_thread_info()) {
if (x) {
if (x == (u8 *)regs->pc)
hit = true;
if (probe_kernel_read(&opcode, x,
1) < 0 ||
opcode != 0xff)
BUG();
probe_kernel_write(x, &kgdb_sstep_bp[0], 1);
debugger_local_cache_flushinv_one(x);
}
if (y) {
if (y == (u8 *)regs->pc)
hit = true;
if (probe_kernel_read(&opcode, y,
1) < 0 ||
opcode != 0xff)
BUG();
probe_kernel_write(y, &kgdb_sstep_bp[1], 1);
debugger_local_cache_flushinv_one(y);
}
}
kgdb_sstep_bp_addr[0] = NULL;
kgdb_sstep_bp_addr[1] = NULL;
kgdb_sstep_thread = NULL;
return hit;
}
/*
* Catch a single-step-pending thread being deleted and make sure the global
* single-step state is cleared. At this point the breakpoints should have
* been removed by __switch_to().
*/
void arch_release_thread_info(struct thread_info *ti)
{
if (kgdb_sstep_thread == ti) {
kgdb_sstep_thread = NULL;
/* However, we may now be running in degraded mode, with most
* of the CPUs disabled until such a time as KGDB is reentered,
* so force immediate reentry */
kgdb_breakpoint();
}
}
/*
* Handle unknown packets and [CcsDk] packets
* - at this point breakpoints have been installed
*/
int kgdb_arch_handle_exception(int vector, int signo, int err_code,
char *remcom_in_buffer, char *remcom_out_buffer,
struct pt_regs *regs)
{
long addr;
char *ptr;
switch (remcom_in_buffer[0]) {
case 'c':
case 's':
/* try to read optional parameter, pc unchanged if no parm */
ptr = &remcom_in_buffer[1];
if (kgdb_hex2long(&ptr, &addr))
regs->pc = addr;
case 'D':
case 'k':
atomic_set(&kgdb_cpu_doing_single_step, -1);
if (remcom_in_buffer[0] == 's') {
kgdb_arch_do_singlestep(regs);
kgdb_single_step = 1;
atomic_set(&kgdb_cpu_doing_single_step,
raw_smp_processor_id());
}
return 0;
}
return -1; /* this means that we do not want to exit from the handler */
}
/*
* Handle event interception
* - returns 0 if the exception should be skipped, -ERROR otherwise.
*/
int debugger_intercept(enum exception_code excep, int signo, int si_code,
struct pt_regs *regs)
{
int ret;
if (kgdb_arch_undo_singlestep(regs)) {
excep = EXCEP_TRAP;
signo = SIGTRAP;
si_code = TRAP_TRACE;
}
ret = kgdb_handle_exception(excep, signo, si_code, regs);
debugger_local_cache_flushinv();
return ret;
}
/*
* Determine if we've hit a debugger special breakpoint
*/
int at_debugger_breakpoint(struct pt_regs *regs)
{
return regs->pc == (unsigned long)&__arch_kgdb_breakpoint;
}
/*
* Initialise kgdb
*/
int kgdb_arch_init(void)
{
return 0;
}
/*
* Do something, perhaps, but don't know what.
*/
void kgdb_arch_exit(void)
{
}
#ifdef CONFIG_SMP
void debugger_nmi_interrupt(struct pt_regs *regs, enum exception_code code)
{
kgdb_nmicallback(arch_smp_processor_id(), regs);
debugger_local_cache_flushinv();
}
void kgdb_roundup_cpus(unsigned long flags)
{
smp_jump_to_debugger();
}
#endif

View File

@@ -0,0 +1,656 @@
/* MN10300 Kernel probes implementation
*
* Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
* Written by Mark Salter (msalter@redhat.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public Licence as published by
* the Free Software Foundation; either version 2 of the Licence, or
* (at your option) any later version.
*
* 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 Licence for more details.
*
* You should have received a copy of the GNU General Public Licence
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/kprobes.h>
#include <linux/ptrace.h>
#include <linux/spinlock.h>
#include <linux/preempt.h>
#include <linux/kdebug.h>
#include <asm/cacheflush.h>
struct kretprobe_blackpoint kretprobe_blacklist[] = { { NULL, NULL } };
const int kretprobe_blacklist_size = ARRAY_SIZE(kretprobe_blacklist);
/* kprobe_status settings */
#define KPROBE_HIT_ACTIVE 0x00000001
#define KPROBE_HIT_SS 0x00000002
static struct kprobe *cur_kprobe;
static unsigned long cur_kprobe_orig_pc;
static unsigned long cur_kprobe_next_pc;
static int cur_kprobe_ss_flags;
static unsigned long kprobe_status;
static kprobe_opcode_t cur_kprobe_ss_buf[MAX_INSN_SIZE + 2];
static unsigned long cur_kprobe_bp_addr;
DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
/* singlestep flag bits */
#define SINGLESTEP_BRANCH 1
#define SINGLESTEP_PCREL 2
#define READ_BYTE(p, valp) \
do { *(u8 *)(valp) = *(u8 *)(p); } while (0)
#define READ_WORD16(p, valp) \
do { \
READ_BYTE((p), (valp)); \
READ_BYTE((u8 *)(p) + 1, (u8 *)(valp) + 1); \
} while (0)
#define READ_WORD32(p, valp) \
do { \
READ_BYTE((p), (valp)); \
READ_BYTE((u8 *)(p) + 1, (u8 *)(valp) + 1); \
READ_BYTE((u8 *)(p) + 2, (u8 *)(valp) + 2); \
READ_BYTE((u8 *)(p) + 3, (u8 *)(valp) + 3); \
} while (0)
static const u8 mn10300_insn_sizes[256] =
{
/* 1 2 3 4 5 6 7 8 9 a b c d e f */
1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, /* 0 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 1 */
2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, /* 2 */
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, /* 3 */
1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, /* 4 */
1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, /* 5 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 7 */
2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 8 */
2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 9 */
2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* a */
2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* b */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, /* c */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* d */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* e */
0, 2, 2, 2, 2, 2, 2, 4, 0, 3, 0, 4, 0, 6, 7, 1 /* f */
};
#define LT (1 << 0)
#define GT (1 << 1)
#define GE (1 << 2)
#define LE (1 << 3)
#define CS (1 << 4)
#define HI (1 << 5)
#define CC (1 << 6)
#define LS (1 << 7)
#define EQ (1 << 8)
#define NE (1 << 9)
#define RA (1 << 10)
#define VC (1 << 11)
#define VS (1 << 12)
#define NC (1 << 13)
#define NS (1 << 14)
static const u16 cond_table[] = {
/* V C N Z */
/* 0 0 0 0 */ (NE | NC | CC | VC | GE | GT | HI),
/* 0 0 0 1 */ (EQ | NC | CC | VC | GE | LE | LS),
/* 0 0 1 0 */ (NE | NS | CC | VC | LT | LE | HI),
/* 0 0 1 1 */ (EQ | NS | CC | VC | LT | LE | LS),
/* 0 1 0 0 */ (NE | NC | CS | VC | GE | GT | LS),
/* 0 1 0 1 */ (EQ | NC | CS | VC | GE | LE | LS),
/* 0 1 1 0 */ (NE | NS | CS | VC | LT | LE | LS),
/* 0 1 1 1 */ (EQ | NS | CS | VC | LT | LE | LS),
/* 1 0 0 0 */ (NE | NC | CC | VS | LT | LE | HI),
/* 1 0 0 1 */ (EQ | NC | CC | VS | LT | LE | LS),
/* 1 0 1 0 */ (NE | NS | CC | VS | GE | GT | HI),
/* 1 0 1 1 */ (EQ | NS | CC | VS | GE | LE | LS),
/* 1 1 0 0 */ (NE | NC | CS | VS | LT | LE | LS),
/* 1 1 0 1 */ (EQ | NC | CS | VS | LT | LE | LS),
/* 1 1 1 0 */ (NE | NS | CS | VS | GE | GT | LS),
/* 1 1 1 1 */ (EQ | NS | CS | VS | GE | LE | LS),
};
/*
* Calculate what the PC will be after executing next instruction
*/
static unsigned find_nextpc(struct pt_regs *regs, int *flags)
{
unsigned size;
s8 x8;
s16 x16;
s32 x32;
u8 opc, *pc, *sp, *next;
next = 0;
*flags = SINGLESTEP_PCREL;
pc = (u8 *) regs->pc;
sp = (u8 *) (regs + 1);
opc = *pc;
size = mn10300_insn_sizes[opc];
if (size > 0) {
next = pc + size;
} else {
switch (opc) {
/* Bxx (d8,PC) */
case 0xc0 ... 0xca:
x8 = 2;
if (cond_table[regs->epsw & 0xf] & (1 << (opc & 0xf)))
x8 = (s8)pc[1];
next = pc + x8;
*flags |= SINGLESTEP_BRANCH;
break;
/* JMP (d16,PC) or CALL (d16,PC) */
case 0xcc:
case 0xcd:
READ_WORD16(pc + 1, &x16);
next = pc + x16;
*flags |= SINGLESTEP_BRANCH;
break;
/* JMP (d32,PC) or CALL (d32,PC) */
case 0xdc:
case 0xdd:
READ_WORD32(pc + 1, &x32);
next = pc + x32;
*flags |= SINGLESTEP_BRANCH;
break;
/* RETF */
case 0xde:
next = (u8 *)regs->mdr;
*flags &= ~SINGLESTEP_PCREL;
*flags |= SINGLESTEP_BRANCH;
break;
/* RET */
case 0xdf:
sp += pc[2];
READ_WORD32(sp, &x32);
next = (u8 *)x32;
*flags &= ~SINGLESTEP_PCREL;
*flags |= SINGLESTEP_BRANCH;
break;
case 0xf0:
next = pc + 2;
opc = pc[1];
if (opc >= 0xf0 && opc <= 0xf7) {
/* JMP (An) / CALLS (An) */
switch (opc & 3) {
case 0:
next = (u8 *)regs->a0;
break;
case 1:
next = (u8 *)regs->a1;
break;
case 2:
next = (u8 *)regs->a2;
break;
case 3:
next = (u8 *)regs->a3;
break;
}
*flags &= ~SINGLESTEP_PCREL;
*flags |= SINGLESTEP_BRANCH;
} else if (opc == 0xfc) {
/* RETS */
READ_WORD32(sp, &x32);
next = (u8 *)x32;
*flags &= ~SINGLESTEP_PCREL;
*flags |= SINGLESTEP_BRANCH;
} else if (opc == 0xfd) {
/* RTI */
READ_WORD32(sp + 4, &x32);
next = (u8 *)x32;
*flags &= ~SINGLESTEP_PCREL;
*flags |= SINGLESTEP_BRANCH;
}
break;
/* potential 3-byte conditional branches */
case 0xf8:
next = pc + 3;
opc = pc[1];
if (opc >= 0xe8 && opc <= 0xeb &&
(cond_table[regs->epsw & 0xf] &
(1 << ((opc & 0xf) + 3)))
) {
READ_BYTE(pc+2, &x8);
next = pc + x8;
*flags |= SINGLESTEP_BRANCH;
}
break;
case 0xfa:
if (pc[1] == 0xff) {
/* CALLS (d16,PC) */
READ_WORD16(pc + 2, &x16);
next = pc + x16;
} else
next = pc + 4;
*flags |= SINGLESTEP_BRANCH;
break;
case 0xfc:
x32 = 6;
if (pc[1] == 0xff) {
/* CALLS (d32,PC) */
READ_WORD32(pc + 2, &x32);
}
next = pc + x32;
*flags |= SINGLESTEP_BRANCH;
break;
/* LXX (d8,PC) */
/* SETLB - loads the next four bytes into the LIR reg */
case 0xd0 ... 0xda:
case 0xdb:
panic("Can't singlestep Lxx/SETLB\n");
break;
}
}
return (unsigned)next;
}
/*
* set up out of place singlestep of some branching instructions
*/
static unsigned __kprobes singlestep_branch_setup(struct pt_regs *regs)
{
u8 opc, *pc, *sp, *next;
next = NULL;
pc = (u8 *) regs->pc;
sp = (u8 *) (regs + 1);
switch (pc[0]) {
case 0xc0 ... 0xca: /* Bxx (d8,PC) */
case 0xcc: /* JMP (d16,PC) */
case 0xdc: /* JMP (d32,PC) */
case 0xf8: /* Bxx (d8,PC) 3-byte version */
/* don't really need to do anything except cause trap */
next = pc;
break;
case 0xcd: /* CALL (d16,PC) */
pc[1] = 5;
pc[2] = 0;
next = pc + 5;
break;
case 0xdd: /* CALL (d32,PC) */
pc[1] = 7;
pc[2] = 0;
pc[3] = 0;
pc[4] = 0;
next = pc + 7;
break;
case 0xde: /* RETF */
next = pc + 3;
regs->mdr = (unsigned) next;
break;
case 0xdf: /* RET */
sp += pc[2];
next = pc + 3;
*(unsigned *)sp = (unsigned) next;
break;
case 0xf0:
next = pc + 2;
opc = pc[1];
if (opc >= 0xf0 && opc <= 0xf3) {
/* CALLS (An) */
/* use CALLS (d16,PC) to avoid mucking with An */
pc[0] = 0xfa;
pc[1] = 0xff;
pc[2] = 4;
pc[3] = 0;
next = pc + 4;
} else if (opc >= 0xf4 && opc <= 0xf7) {
/* JMP (An) */
next = pc;
} else if (opc == 0xfc) {
/* RETS */
next = pc + 2;
*(unsigned *) sp = (unsigned) next;
} else if (opc == 0xfd) {
/* RTI */
next = pc + 2;
*(unsigned *)(sp + 4) = (unsigned) next;
}
break;
case 0xfa: /* CALLS (d16,PC) */
pc[2] = 4;
pc[3] = 0;
next = pc + 4;
break;
case 0xfc: /* CALLS (d32,PC) */
pc[2] = 6;
pc[3] = 0;
pc[4] = 0;
pc[5] = 0;
next = pc + 6;
break;
case 0xd0 ... 0xda: /* LXX (d8,PC) */
case 0xdb: /* SETLB */
panic("Can't singlestep Lxx/SETLB\n");
}
return (unsigned) next;
}
int __kprobes arch_prepare_kprobe(struct kprobe *p)
{
return 0;
}
void __kprobes arch_copy_kprobe(struct kprobe *p)
{
memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE);
}
void __kprobes arch_arm_kprobe(struct kprobe *p)
{
*p->addr = BREAKPOINT_INSTRUCTION;
flush_icache_range((unsigned long) p->addr,
(unsigned long) p->addr + sizeof(kprobe_opcode_t));
}
void __kprobes arch_disarm_kprobe(struct kprobe *p)
{
#ifndef CONFIG_MN10300_CACHE_SNOOP
mn10300_dcache_flush();
mn10300_icache_inv();
#endif
}
void arch_remove_kprobe(struct kprobe *p)
{
}
static inline
void __kprobes disarm_kprobe(struct kprobe *p, struct pt_regs *regs)
{
*p->addr = p->opcode;
regs->pc = (unsigned long) p->addr;
#ifndef CONFIG_MN10300_CACHE_SNOOP
mn10300_dcache_flush();
mn10300_icache_inv();
#endif
}
static inline
void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
{
unsigned long nextpc;
cur_kprobe_orig_pc = regs->pc;
memcpy(cur_kprobe_ss_buf, &p->ainsn.insn[0], MAX_INSN_SIZE);
regs->pc = (unsigned long) cur_kprobe_ss_buf;
nextpc = find_nextpc(regs, &cur_kprobe_ss_flags);
if (cur_kprobe_ss_flags & SINGLESTEP_PCREL)
cur_kprobe_next_pc = cur_kprobe_orig_pc + (nextpc - regs->pc);
else
cur_kprobe_next_pc = nextpc;
/* branching instructions need special handling */
if (cur_kprobe_ss_flags & SINGLESTEP_BRANCH)
nextpc = singlestep_branch_setup(regs);
cur_kprobe_bp_addr = nextpc;
*(u8 *) nextpc = BREAKPOINT_INSTRUCTION;
mn10300_dcache_flush_range2((unsigned) cur_kprobe_ss_buf,
sizeof(cur_kprobe_ss_buf));
mn10300_icache_inv();
}
static inline int __kprobes kprobe_handler(struct pt_regs *regs)
{
struct kprobe *p;
int ret = 0;
unsigned int *addr = (unsigned int *) regs->pc;
/* We're in an interrupt, but this is clear and BUG()-safe. */
preempt_disable();
/* Check we're not actually recursing */
if (kprobe_running()) {
/* We *are* holding lock here, so this is safe.
Disarm the probe we just hit, and ignore it. */
p = get_kprobe(addr);
if (p) {
disarm_kprobe(p, regs);
ret = 1;
} else {
p = cur_kprobe;
if (p->break_handler && p->break_handler(p, regs))
goto ss_probe;
}
/* If it's not ours, can't be delete race, (we hold lock). */
goto no_kprobe;
}
p = get_kprobe(addr);
if (!p) {
if (*addr != BREAKPOINT_INSTRUCTION) {
/* The breakpoint instruction was removed right after
* we hit it. Another cpu has removed either a
* probepoint or a debugger breakpoint at this address.
* In either case, no further handling of this
* interrupt is appropriate.
*/
ret = 1;
}
/* Not one of ours: let kernel handle it */
goto no_kprobe;
}
kprobe_status = KPROBE_HIT_ACTIVE;
cur_kprobe = p;
if (p->pre_handler(p, regs)) {
/* handler has already set things up, so skip ss setup */
return 1;
}
ss_probe:
prepare_singlestep(p, regs);
kprobe_status = KPROBE_HIT_SS;
return 1;
no_kprobe:
preempt_enable_no_resched();
return ret;
}
/*
* Called after single-stepping. p->addr is the address of the
* instruction whose first byte has been replaced by the "breakpoint"
* instruction. To avoid the SMP problems that can occur when we
* temporarily put back the original opcode to single-step, we
* single-stepped a copy of the instruction. The address of this
* copy is p->ainsn.insn.
*/
static void __kprobes resume_execution(struct kprobe *p, struct pt_regs *regs)
{
/* we may need to fixup regs/stack after singlestepping a call insn */
if (cur_kprobe_ss_flags & SINGLESTEP_BRANCH) {
regs->pc = cur_kprobe_orig_pc;
switch (p->ainsn.insn[0]) {
case 0xcd: /* CALL (d16,PC) */
*(unsigned *) regs->sp = regs->mdr = regs->pc + 5;
break;
case 0xdd: /* CALL (d32,PC) */
/* fixup mdr and return address on stack */
*(unsigned *) regs->sp = regs->mdr = regs->pc + 7;
break;
case 0xf0:
if (p->ainsn.insn[1] >= 0xf0 &&
p->ainsn.insn[1] <= 0xf3) {
/* CALLS (An) */
/* fixup MDR and return address on stack */
regs->mdr = regs->pc + 2;
*(unsigned *) regs->sp = regs->mdr;
}
break;
case 0xfa: /* CALLS (d16,PC) */
/* fixup MDR and return address on stack */
*(unsigned *) regs->sp = regs->mdr = regs->pc + 4;
break;
case 0xfc: /* CALLS (d32,PC) */
/* fixup MDR and return address on stack */
*(unsigned *) regs->sp = regs->mdr = regs->pc + 6;
break;
}
}
regs->pc = cur_kprobe_next_pc;
cur_kprobe_bp_addr = 0;
}
static inline int __kprobes post_kprobe_handler(struct pt_regs *regs)
{
if (!kprobe_running())
return 0;
if (cur_kprobe->post_handler)
cur_kprobe->post_handler(cur_kprobe, regs, 0);
resume_execution(cur_kprobe, regs);
reset_current_kprobe();
preempt_enable_no_resched();
return 1;
}
/* Interrupts disabled, kprobe_lock held. */
static inline
int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr)
{
if (cur_kprobe->fault_handler &&
cur_kprobe->fault_handler(cur_kprobe, regs, trapnr))
return 1;
if (kprobe_status & KPROBE_HIT_SS) {
resume_execution(cur_kprobe, regs);
reset_current_kprobe();
preempt_enable_no_resched();
}
return 0;
}
/*
* Wrapper routine to for handling exceptions.
*/
int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
unsigned long val, void *data)
{
struct die_args *args = data;
switch (val) {
case DIE_BREAKPOINT:
if (cur_kprobe_bp_addr != args->regs->pc) {
if (kprobe_handler(args->regs))
return NOTIFY_STOP;
} else {
if (post_kprobe_handler(args->regs))
return NOTIFY_STOP;
}
break;
case DIE_GPF:
if (kprobe_running() &&
kprobe_fault_handler(args->regs, args->trapnr))
return NOTIFY_STOP;
break;
default:
break;
}
return NOTIFY_DONE;
}
/* Jprobes support. */
static struct pt_regs jprobe_saved_regs;
static struct pt_regs *jprobe_saved_regs_location;
static kprobe_opcode_t jprobe_saved_stack[MAX_STACK_SIZE];
int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
{
struct jprobe *jp = container_of(p, struct jprobe, kp);
jprobe_saved_regs_location = regs;
memcpy(&jprobe_saved_regs, regs, sizeof(struct pt_regs));
/* Save a whole stack frame, this gets arguments
* pushed onto the stack after using up all the
* arg registers.
*/
memcpy(&jprobe_saved_stack, regs + 1, sizeof(jprobe_saved_stack));
/* setup return addr to the jprobe handler routine */
regs->pc = (unsigned long) jp->entry;
return 1;
}
void __kprobes jprobe_return(void)
{
void *orig_sp = jprobe_saved_regs_location + 1;
preempt_enable_no_resched();
asm volatile(" mov %0,sp\n"
".globl jprobe_return_bp_addr\n"
"jprobe_return_bp_addr:\n\t"
" .byte 0xff\n"
: : "d" (orig_sp));
}
extern void jprobe_return_bp_addr(void);
int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
{
u8 *addr = (u8 *) regs->pc;
if (addr == (u8 *) jprobe_return_bp_addr) {
if (jprobe_saved_regs_location != regs) {
printk(KERN_ERR"JPROBE:"
" Current regs (%p) does not match saved regs"
" (%p).\n",
regs, jprobe_saved_regs_location);
BUG();
}
/* Restore old register state.
*/
memcpy(regs, &jprobe_saved_regs, sizeof(struct pt_regs));
memcpy(regs + 1, &jprobe_saved_stack,
sizeof(jprobe_saved_stack));
return 1;
}
return 0;
}
int __init arch_init_kprobes(void)
{
return 0;
}

View File

@@ -0,0 +1,58 @@
/* Debugging stuff for the MN10300-based processors
*
* Copyright (C) 2007 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 Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/sched.h>
#include <asm/serial-regs.h>
#undef MN10300_CONSOLE_ON_SERIO
/*
* write a string directly through one of the serial ports on-board the MN10300
*/
#ifdef MN10300_CONSOLE_ON_SERIO
void debug_to_serial_mnser(const char *p, int n)
{
char ch;
for (; n > 0; n--) {
ch = *p++;
#if MN10300_CONSOLE_ON_SERIO == 0
while (SC0STR & (SC01STR_TBF)) continue;
SC0TXB = ch;
while (SC0STR & (SC01STR_TBF)) continue;
if (ch == 0x0a) {
SC0TXB = 0x0d;
while (SC0STR & (SC01STR_TBF)) continue;
}
#elif MN10300_CONSOLE_ON_SERIO == 1
while (SC1STR & (SC01STR_TBF)) continue;
SC1TXB = ch;
while (SC1STR & (SC01STR_TBF)) continue;
if (ch == 0x0a) {
SC1TXB = 0x0d;
while (SC1STR & (SC01STR_TBF)) continue;
}
#elif MN10300_CONSOLE_ON_SERIO == 2
while (SC2STR & (SC2STR_TBF)) continue;
SC2TXB = ch;
while (SC2STR & (SC2STR_TBF)) continue;
if (ch == 0x0a) {
SC2TXB = 0x0d;
while (SC2STR & (SC2STR_TBF)) continue;
}
#endif
}
}
#endif

View File

@@ -0,0 +1,194 @@
###############################################################################
#
# Virtual DMA driver for MN10300 serial ports
#
# Copyright (C) 2007 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 Licence
# as published by the Free Software Foundation; either version
# 2 of the Licence, or (at your option) any later version.
#
###############################################################################
#include <linux/sys.h>
#include <linux/linkage.h>
#include <asm/page.h>
#include <asm/smp.h>
#include <asm/cpu-regs.h>
#include <asm/frame.inc>
#include <asm/timer-regs.h>
#include <proc/cache.h>
#include <unit/timex.h>
#include "mn10300-serial.h"
#define SCxCTR 0x00
#define SCxICR 0x04
#define SCxTXB 0x08
#define SCxRXB 0x09
#define SCxSTR 0x0c
#define SCxTIM 0x0d
.text
###############################################################################
#
# serial port interrupt virtual DMA entry point
# - intended to run at interrupt priority 1 (not affected by local_irq_disable)
#
###############################################################################
.balign L1_CACHE_BYTES
ENTRY(mn10300_serial_vdma_interrupt)
# or EPSW_IE,psw # permit overriding by
# debugging interrupts
movm [d2,d3,a2,a3,exreg0],(sp)
movhu (IAGR),a2 # see if which interrupt is
# pending
and IAGR_GN,a2
add a2,a2
add mn10300_serial_int_tbl,a2
mov (a2+),a3
mov (__iobase,a3),e2
mov (a2),a2
jmp (a2)
###############################################################################
#
# serial port receive interrupt virtual DMA entry point
# - intended to run at interrupt priority 1 (not affected by local_irq_disable)
# - stores data/status byte pairs in the ring buffer
# - induces a scheduler tick timer interrupt when done, which we then subvert
# on entry:
# A3 struct mn10300_serial_port *
# E2 I/O port base
#
###############################################################################
ENTRY(mn10300_serial_vdma_rx_handler)
mov (__rx_icr,a3),e3
mov GxICR_DETECT,d2
movbu d2,(e3) # ACK the interrupt
movhu (e3),d2 # flush
mov (__rx_inp,a3),d3
mov d3,a2
add 2,d3
and MNSC_BUFFER_SIZE-1,d3
mov (__rx_outp,a3),d2
cmp d3,d2
beq mnsc_vdma_rx_overflow
mov (__rx_buffer,a3),d2
add d2,a2
movhu (SCxSTR,e2),d2
movbu d2,(1,a2)
movbu (SCxRXB,e2),d2
movbu d2,(a2)
mov d3,(__rx_inp,a3)
bset MNSCx_RX_AVAIL,(__intr_flags,a3)
mnsc_vdma_rx_done:
mov (__tm_icr,a3),a2
mov GxICR_LEVEL_6|GxICR_ENABLE|GxICR_REQUEST|GxICR_DETECT,d2
movhu d2,(a2) # request a slow interrupt
movhu (a2),d2 # flush
movm (sp),[d2,d3,a2,a3,exreg0]
rti
mnsc_vdma_rx_overflow:
bset MNSCx_RX_OVERF,(__intr_flags,a3)
bra mnsc_vdma_rx_done
###############################################################################
#
# serial port transmit interrupt virtual DMA entry point
# - intended to run at interrupt priority 1 (not affected by local_irq_disable)
# - retrieves data bytes from the ring buffer and passes them to the serial port
# - induces a scheduler tick timer interrupt when done, which we then subvert
# A3 struct mn10300_serial_port *
# E2 I/O port base
#
###############################################################################
.balign L1_CACHE_BYTES
ENTRY(mn10300_serial_vdma_tx_handler)
mov (__tx_icr,a3),e3
mov GxICR_DETECT,d2
movbu d2,(e3) # ACK the interrupt
movhu (e3),d2 # flush
btst 0xFF,(__tx_flags,a3) # handle transmit flags
bne mnsc_vdma_tx_flags
movbu (SCxSTR,e2),d2 # don't try and transmit a char if the
# buffer is not empty
btst SC01STR_TBF,d2 # (may have tried to jumpstart)
bne mnsc_vdma_tx_noint
movbu (__tx_xchar,a3),d2 # handle hi-pri XON/XOFF
or d2,d2
bne mnsc_vdma_tx_xchar
mov (__uart_state,a3),a2 # see if the TTY Tx queue has anything in it
mov (__xmit_tail,a2),d3
mov (__xmit_head,a2),d2
cmp d3,d2
beq mnsc_vdma_tx_empty
mov (__xmit_buffer,a2),d2 # get a char from the buffer and
# transmit it
movbu (d3,d2),d2
movbu d2,(SCxTXB,e2) # Tx
inc d3 # advance the buffer pointer
and __UART_XMIT_SIZE-1,d3
mov (__xmit_head,a2),d2
mov d3,(__xmit_tail,a2)
sub d3,d2 # see if we've written everything
beq mnsc_vdma_tx_empty
and __UART_XMIT_SIZE-1,d2 # see if we just made a hole
cmp __UART_XMIT_SIZE-2,d2
beq mnsc_vdma_tx_made_hole
mnsc_vdma_tx_done:
mov (__tm_icr,a3),a2
mov GxICR_LEVEL_6|GxICR_ENABLE|GxICR_REQUEST|GxICR_DETECT,d2
movhu d2,(a2) # request a slow interrupt
movhu (a2),d2 # flush
mnsc_vdma_tx_noint:
movm (sp),[d2,d3,a2,a3,exreg0]
rti
mnsc_vdma_tx_empty:
mov +(NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL)|GxICR_DETECT),d2
movhu d2,(e3) # disable the interrupt
movhu (e3),d2 # flush
bset MNSCx_TX_EMPTY,(__intr_flags,a3)
bra mnsc_vdma_tx_done
mnsc_vdma_tx_flags:
btst MNSCx_TX_STOP,(__tx_flags,a3)
bne mnsc_vdma_tx_stop
movhu (SCxCTR,e2),d2 # turn on break mode
or SC01CTR_BKE,d2
movhu d2,(SCxCTR,e2)
mnsc_vdma_tx_stop:
mov +(NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL)|GxICR_DETECT),d2
movhu d2,(e3) # disable transmit interrupts on this
# channel
movhu (e3),d2 # flush
bra mnsc_vdma_tx_noint
mnsc_vdma_tx_xchar:
bclr 0xff,(__tx_xchar,a3)
movbu d2,(SCxTXB,e2)
bra mnsc_vdma_tx_done
mnsc_vdma_tx_made_hole:
bset MNSCx_TX_SPACE,(__intr_flags,a3)
bra mnsc_vdma_tx_done

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,130 @@
/* MN10300 On-chip serial port driver definitions
*
* Copyright (C) 2007 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 Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#ifndef _MN10300_SERIAL_H
#define _MN10300_SERIAL_H
#ifndef __ASSEMBLY__
#include <linux/serial_core.h>
#include <linux/termios.h>
#endif
#include <asm/page.h>
#include <asm/serial-regs.h>
#define NR_PORTS 3 /* should be set 3 or 9 or 16 */
#define MNSC_BUFFER_SIZE +(PAGE_SIZE / 2)
/* intr_flags bits */
#define MNSCx_RX_AVAIL 0x01
#define MNSCx_RX_OVERF 0x02
#define MNSCx_TX_SPACE 0x04
#define MNSCx_TX_EMPTY 0x08
/* tx_flags bits */
#define MNSCx_TX_BREAK 0x01
#define MNSCx_TX_STOP 0x02
#ifndef __ASSEMBLY__
struct mn10300_serial_port {
char *rx_buffer; /* reception buffer base */
unsigned rx_inp; /* pointer to rx input offset */
unsigned rx_outp; /* pointer to rx output offset */
u8 tx_xchar; /* high-priority XON/XOFF buffer */
u8 tx_flags; /* transmit break/stop request */
u8 intr_flags; /* interrupt flags */
volatile u16 *rx_icr; /* Rx interrupt control register */
volatile u16 *tx_icr; /* Tx interrupt control register */
int rx_irq; /* reception IRQ */
int tx_irq; /* transmission IRQ */
int tm_irq; /* timer IRQ */
const char *name; /* name of serial port */
const char *rx_name; /* Rx interrupt handler name of serial port */
const char *tx_name; /* Tx interrupt handler name of serial port */
const char *tm_name; /* Timer interrupt handler name */
unsigned short type; /* type of serial port */
unsigned char isconsole; /* T if it's a console */
volatile void *_iobase; /* pointer to base of I/O control regs */
volatile u16 *_control; /* control register pointer */
volatile u8 *_status; /* status register pointer */
volatile u8 *_intr; /* interrupt register pointer */
volatile u8 *_rxb; /* receive buffer register pointer */
volatile u8 *_txb; /* transmit buffer register pointer */
volatile u16 *_tmicr; /* timer interrupt control register */
volatile u8 *_tmxmd; /* baud rate timer mode register */
volatile u16 *_tmxbr; /* baud rate timer base register */
/* this must come down here so that assembly can use BSET to access the
* above fields */
struct uart_port uart;
unsigned short rx_brk; /* current break reception status */
u16 tx_cts; /* current CTS status */
int gdbstub; /* preemptively stolen by GDB stub */
u8 clock_src; /* clock source */
#define MNSCx_CLOCK_SRC_IOCLK 0
#define MNSCx_CLOCK_SRC_IOBCLK 1
u8 div_timer; /* timer used as divisor */
#define MNSCx_DIV_TIMER_16BIT 0
#define MNSCx_DIV_TIMER_8BIT 1
u16 options; /* options */
#define MNSCx_OPT_CTS 0x0001
unsigned long ioclk; /* base clock rate */
};
#ifdef CONFIG_MN10300_TTYSM0
extern struct mn10300_serial_port mn10300_serial_port_sif0;
#endif
#ifdef CONFIG_MN10300_TTYSM1
extern struct mn10300_serial_port mn10300_serial_port_sif1;
#endif
#ifdef CONFIG_MN10300_TTYSM2
extern struct mn10300_serial_port mn10300_serial_port_sif2;
#endif
extern struct mn10300_serial_port *mn10300_serial_ports[];
struct mn10300_serial_int {
struct mn10300_serial_port *port;
asmlinkage void (*vdma)(void);
};
extern struct mn10300_serial_int mn10300_serial_int_tbl[];
extern asmlinkage void mn10300_serial_vdma_interrupt(void);
extern asmlinkage void mn10300_serial_vdma_rx_handler(void);
extern asmlinkage void mn10300_serial_vdma_tx_handler(void);
#endif /* __ASSEMBLY__ */
#if defined(CONFIG_GDBSTUB_ON_TTYSM0)
#define SCgSTR SC0STR
#define SCgRXB SC0RXB
#define SCgRXIRQ SC0RXIRQ
#elif defined(CONFIG_GDBSTUB_ON_TTYSM1)
#define SCgSTR SC1STR
#define SCgRXB SC1RXB
#define SCgRXIRQ SC1RXIRQ
#elif defined(CONFIG_GDBSTUB_ON_TTYSM2)
#define SCgSTR SC2STR
#define SCgRXB SC2RXB
#define SCgRXIRQ SC2RXIRQ
#endif
#endif /* _MN10300_SERIAL_H */

View File

@@ -0,0 +1,66 @@
###############################################################################
#
# MN10300 Watchdog interrupt handler
#
# Copyright (C) 2007 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 Licence
# as published by the Free Software Foundation; either version
# 2 of the Licence, or (at your option) any later version.
#
###############################################################################
#include <linux/sys.h>
#include <linux/linkage.h>
#include <asm/intctl-regs.h>
#include <asm/timer-regs.h>
#include <asm/frame.inc>
#include <linux/threads.h>
.text
###############################################################################
#
# Watchdog handler entry point
# - special non-maskable interrupt
#
###############################################################################
.globl watchdog_handler
.type watchdog_handler,@function
watchdog_handler:
add -4,sp
SAVE_ALL
mov 0xffffffff,d0
mov d0,(REG_ORIG_D0,fp)
mov fp,d0
lsr 2,d1
call watchdog_interrupt[],0 # watchdog_interrupt(regs,irq)
jmp ret_from_intr
.size watchdog_handler,.-watchdog_handler
###############################################################################
#
# Watchdog touch entry point
# - kept to absolute minimum (unfortunately, it's prototyped in linux/nmi.h so
# we can't inline it)
#
###############################################################################
.globl touch_nmi_watchdog
.type touch_nmi_watchdog,@function
touch_nmi_watchdog:
clr d0
clr d1
mov watchdog_alert_counter, a0
setlb
mov d0, (a0+)
inc d1
cmp NR_CPUS, d1
lne
ret [],0
.size touch_nmi_watchdog,.-touch_nmi_watchdog

View File

@@ -0,0 +1,205 @@
/* MN10300 Watchdog timer
*
* Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
* - Derived from arch/i386/kernel/nmi.c
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
#include <linux/nmi.h>
#include <asm/processor.h>
#include <linux/atomic.h>
#include <asm/intctl-regs.h>
#include <asm/rtc-regs.h>
#include <asm/div64.h>
#include <asm/smp.h>
#include <asm/gdb-stub.h>
#include <proc/clock.h>
static DEFINE_SPINLOCK(watchdog_print_lock);
static unsigned int watchdog;
static unsigned int watchdog_hz = 1;
unsigned int watchdog_alert_counter[NR_CPUS];
EXPORT_SYMBOL(touch_nmi_watchdog);
/*
* the best way to detect whether a CPU has a 'hard lockup' problem
* is to check its timer makes IRQ counts. If they are not
* changing then that CPU has some problem.
*
* since NMIs dont listen to _any_ locks, we have to be extremely
* careful not to rely on unsafe variables. The printk might lock
* up though, so we have to break up any console locks first ...
* [when there will be more tty-related locks, break them up
* here too!]
*/
static unsigned int last_irq_sums[NR_CPUS];
int __init check_watchdog(void)
{
irq_cpustat_t tmp[1];
printk(KERN_INFO "Testing Watchdog... ");
memcpy(tmp, irq_stat, sizeof(tmp));
local_irq_enable();
mdelay((10 * 1000) / watchdog_hz); /* wait 10 ticks */
local_irq_disable();
if (nmi_count(0) - tmp[0].__nmi_count <= 5) {
printk(KERN_WARNING "CPU#%d: Watchdog appears to be stuck!\n",
0);
return -1;
}
printk(KERN_INFO "OK.\n");
/* now that we know it works we can reduce NMI frequency to something
* more reasonable; makes a difference in some configs
*/
watchdog_hz = 1;
return 0;
}
static int __init setup_watchdog(char *str)
{
unsigned tmp;
int opt;
u8 ctr;
get_option(&str, &opt);
if (opt != 1)
return 0;
watchdog = opt;
if (watchdog) {
set_intr_stub(EXCEP_WDT, watchdog_handler);
ctr = WDCTR_WDCK_65536th;
WDCTR = WDCTR_WDRST | ctr;
WDCTR = ctr;
tmp = WDCTR;
tmp = __muldiv64u(1 << (16 + ctr * 2), 1000000, MN10300_WDCLK);
tmp = 1000000000 / tmp;
watchdog_hz = (tmp + 500) / 1000;
}
return 1;
}
__setup("watchdog=", setup_watchdog);
void __init watchdog_go(void)
{
u8 wdt;
if (watchdog) {
printk(KERN_INFO "Watchdog: running at %uHz\n", watchdog_hz);
wdt = WDCTR & ~WDCTR_WDCNE;
WDCTR = wdt | WDCTR_WDRST;
wdt = WDCTR;
WDCTR = wdt | WDCTR_WDCNE;
wdt = WDCTR;
check_watchdog();
}
}
#ifdef CONFIG_SMP
static void watchdog_dump_register(void *dummy)
{
printk(KERN_ERR "--- Register Dump (CPU%d) ---\n", CPUID);
show_registers(current_frame());
}
#endif
asmlinkage
void watchdog_interrupt(struct pt_regs *regs, enum exception_code excep)
{
/*
* Since current-> is always on the stack, and we always switch
* the stack NMI-atomically, it's safe to use smp_processor_id().
*/
int sum, cpu;
int irq = NMIIRQ;
u8 wdt, tmp;
wdt = WDCTR & ~WDCTR_WDCNE;
WDCTR = wdt;
tmp = WDCTR;
NMICR = NMICR_WDIF;
nmi_count(smp_processor_id())++;
kstat_incr_irqs_this_cpu(irq, irq_to_desc(irq));
for_each_online_cpu(cpu) {
sum = irq_stat[cpu].__irq_count;
if ((last_irq_sums[cpu] == sum)
#if defined(CONFIG_GDBSTUB) && defined(CONFIG_SMP)
&& !(CHK_GDBSTUB_BUSY()
|| atomic_read(&cpu_doing_single_step))
#endif
) {
/*
* Ayiee, looks like this CPU is stuck ...
* wait a few IRQs (5 seconds) before doing the oops ...
*/
watchdog_alert_counter[cpu]++;
if (watchdog_alert_counter[cpu] == 5 * watchdog_hz) {
spin_lock(&watchdog_print_lock);
/*
* We are in trouble anyway, lets at least try
* to get a message out.
*/
bust_spinlocks(1);
printk(KERN_ERR
"NMI Watchdog detected LOCKUP on CPU%d,"
" pc %08lx, registers:\n",
cpu, regs->pc);
#ifdef CONFIG_SMP
printk(KERN_ERR
"--- Register Dump (CPU%d) ---\n",
CPUID);
#endif
show_registers(regs);
#ifdef CONFIG_SMP
smp_nmi_call_function(watchdog_dump_register,
NULL, 1);
#endif
printk(KERN_NOTICE "console shuts up ...\n");
console_silent();
spin_unlock(&watchdog_print_lock);
bust_spinlocks(0);
#ifdef CONFIG_GDBSTUB
if (CHK_GDBSTUB_BUSY_AND_ACTIVE())
gdbstub_exception(regs, excep);
else
gdbstub_intercept(regs, excep);
#endif
do_exit(SIGSEGV);
}
} else {
last_irq_sums[cpu] = sum;
watchdog_alert_counter[cpu] = 0;
}
}
WDCTR = wdt | WDCTR_WDRST;
tmp = WDCTR;
WDCTR = wdt | WDCTR_WDCNE;
tmp = WDCTR;
}

View File

@@ -0,0 +1,42 @@
/* MN10300 Miscellaneous and library kernel exports
*
* Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd.
* Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/module.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
EXPORT_SYMBOL(empty_zero_page);
EXPORT_SYMBOL(change_bit);
EXPORT_SYMBOL(test_and_change_bit);
EXPORT_SYMBOL(memcpy);
EXPORT_SYMBOL(memmove);
EXPORT_SYMBOL(memset);
EXPORT_SYMBOL(strncpy_from_user);
EXPORT_SYMBOL(__strncpy_from_user);
EXPORT_SYMBOL(clear_user);
EXPORT_SYMBOL(__clear_user);
EXPORT_SYMBOL(__generic_copy_from_user);
EXPORT_SYMBOL(__generic_copy_to_user);
EXPORT_SYMBOL(strnlen_user);
extern u64 __ashrdi3(u64, unsigned);
extern u64 __ashldi3(u64, unsigned);
extern u64 __lshrdi3(u64, unsigned);
extern s64 __negdi2(s64);
extern int __ucmpdi2(u64, u64);
EXPORT_SYMBOL(__ashrdi3);
EXPORT_SYMBOL(__ashldi3);
EXPORT_SYMBOL(__lshrdi3);
EXPORT_SYMBOL(__negdi2);
EXPORT_SYMBOL(__ucmpdi2);

View File

@@ -0,0 +1,156 @@
/* MN10300 Kernel module helper routines
*
* Copyright (C) 2007, 2008, 2009 Red Hat, Inc. All Rights Reserved.
* Written by Mark Salter (msalter@redhat.com)
* - Derived from arch/i386/kernel/module.c
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public Licence as published by
* the Free Software Foundation; either version 2 of the Licence, or
* (at your option) any later version.
*
* 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 Licence for more details.
*
* You should have received a copy of the GNU General Public Licence
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/moduleloader.h>
#include <linux/elf.h>
#include <linux/vmalloc.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/bug.h>
#if 0
#define DEBUGP printk
#else
#define DEBUGP(fmt, ...)
#endif
static void reloc_put16(uint8_t *p, uint32_t val)
{
p[0] = val & 0xff;
p[1] = (val >> 8) & 0xff;
}
static void reloc_put24(uint8_t *p, uint32_t val)
{
reloc_put16(p, val);
p[2] = (val >> 16) & 0xff;
}
static void reloc_put32(uint8_t *p, uint32_t val)
{
reloc_put16(p, val);
reloc_put16(p+2, val >> 16);
}
/*
* apply a RELA relocation
*/
int apply_relocate_add(Elf32_Shdr *sechdrs,
const char *strtab,
unsigned int symindex,
unsigned int relsec,
struct module *me)
{
unsigned int i, sym_diff_seen = 0;
Elf32_Rela *rel = (void *)sechdrs[relsec].sh_addr;
Elf32_Sym *sym;
Elf32_Addr relocation, sym_diff_val = 0;
uint8_t *location;
uint32_t value;
DEBUGP("Applying relocate section %u to %u\n",
relsec, sechdrs[relsec].sh_info);
for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
/* this is where to make the change */
location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
+ rel[i].r_offset;
/* this is the symbol the relocation is referring to (note that
* all undefined symbols have been resolved by the caller) */
sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
+ ELF32_R_SYM(rel[i].r_info);
/* this is the adjustment to be made */
relocation = sym->st_value + rel[i].r_addend;
if (sym_diff_seen) {
switch (ELF32_R_TYPE(rel[i].r_info)) {
case R_MN10300_32:
case R_MN10300_24:
case R_MN10300_16:
case R_MN10300_8:
relocation -= sym_diff_val;
sym_diff_seen = 0;
break;
default:
printk(KERN_ERR "module %s: Unexpected SYM_DIFF relocation: %u\n",
me->name, ELF32_R_TYPE(rel[i].r_info));
return -ENOEXEC;
}
}
switch (ELF32_R_TYPE(rel[i].r_info)) {
/* for the first four relocation types, we simply
* store the adjustment at the location given */
case R_MN10300_32:
reloc_put32(location, relocation);
break;
case R_MN10300_24:
reloc_put24(location, relocation);
break;
case R_MN10300_16:
reloc_put16(location, relocation);
break;
case R_MN10300_8:
*location = relocation;
break;
/* for the next three relocation types, we write the
* adjustment with the address subtracted over the
* value at the location given */
case R_MN10300_PCREL32:
value = relocation - (uint32_t) location;
reloc_put32(location, value);
break;
case R_MN10300_PCREL16:
value = relocation - (uint32_t) location;
reloc_put16(location, value);
break;
case R_MN10300_PCREL8:
*location = relocation - (uint32_t) location;
break;
case R_MN10300_SYM_DIFF:
/* This is used to adjust the next reloc as required
* by relaxation. */
sym_diff_seen = 1;
sym_diff_val = sym->st_value;
break;
case R_MN10300_ALIGN:
/* Just ignore the ALIGN relocs.
* Only interesting if kernel performed relaxation. */
continue;
default:
printk(KERN_ERR "module %s: Unknown relocation: %u\n",
me->name, ELF32_R_TYPE(rel[i].r_info));
return -ENOEXEC;
}
}
if (sym_diff_seen) {
printk(KERN_ERR "module %s: Nothing follows SYM_DIFF relocation: %u\n",
me->name, ELF32_R_TYPE(rel[i].r_info));
return -ENOEXEC;
}
return 0;
}

View File

@@ -0,0 +1,192 @@
/* MN10300 Process handling code
*
* Copyright (C) 2007 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 Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/ptrace.h>
#include <linux/user.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/reboot.h>
#include <linux/percpu.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/rcupdate.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/mmu_context.h>
#include <asm/fpu.h>
#include <asm/reset-regs.h>
#include <asm/gdb-stub.h>
#include "internal.h"
/*
* return saved PC of a blocked thread.
*/
unsigned long thread_saved_pc(struct task_struct *tsk)
{
return ((unsigned long *) tsk->thread.sp)[3];
}
/*
* power off function, if any
*/
void (*pm_power_off)(void);
EXPORT_SYMBOL(pm_power_off);
/*
* On SMP it's slightly faster (but much more power-consuming!)
* to poll the ->work.need_resched flag instead of waiting for the
* cross-CPU IPI to arrive. Use this option with caution.
*
* tglx: No idea why this depends on HOTPLUG_CPU !?!
*/
#if !defined(CONFIG_SMP) || defined(CONFIG_HOTPLUG_CPU)
void arch_cpu_idle(void)
{
safe_halt();
}
#endif
void release_segments(struct mm_struct *mm)
{
}
void machine_restart(char *cmd)
{
#ifdef CONFIG_KERNEL_DEBUGGER
gdbstub_exit(0);
#endif
#ifdef mn10300_unit_hard_reset
mn10300_unit_hard_reset();
#else
mn10300_proc_hard_reset();
#endif
}
void machine_halt(void)
{
#ifdef CONFIG_KERNEL_DEBUGGER
gdbstub_exit(0);
#endif
}
void machine_power_off(void)
{
#ifdef CONFIG_KERNEL_DEBUGGER
gdbstub_exit(0);
#endif
}
void show_regs(struct pt_regs *regs)
{
show_regs_print_info(KERN_DEFAULT);
}
/*
* free current thread data structures etc..
*/
void exit_thread(void)
{
exit_fpu();
}
void flush_thread(void)
{
flush_fpu();
}
void release_thread(struct task_struct *dead_task)
{
}
/*
* we do not have to muck with descriptors here, that is
* done in switch_mm() as needed.
*/
void copy_segments(struct task_struct *p, struct mm_struct *new_mm)
{
}
/*
* this gets called so that we can store lazy state into memory and copy the
* current task into the new thread.
*/
int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
{
unlazy_fpu(src);
*dst = *src;
return 0;
}
/*
* set up the kernel stack for a new thread and copy arch-specific thread
* control information
*/
int copy_thread(unsigned long clone_flags,
unsigned long c_usp, unsigned long ustk_size,
struct task_struct *p)
{
struct thread_info *ti = task_thread_info(p);
struct pt_regs *c_regs;
unsigned long c_ksp;
c_ksp = (unsigned long) task_stack_page(p) + THREAD_SIZE;
/* allocate the userspace exception frame and set it up */
c_ksp -= sizeof(struct pt_regs);
c_regs = (struct pt_regs *) c_ksp;
c_ksp -= 12; /* allocate function call ABI slack */
/* set up things up so the scheduler can start the new task */
p->thread.uregs = c_regs;
ti->frame = c_regs;
p->thread.a3 = (unsigned long) c_regs;
p->thread.sp = c_ksp;
p->thread.wchan = p->thread.pc;
p->thread.usp = c_usp;
if (unlikely(p->flags & PF_KTHREAD)) {
memset(c_regs, 0, sizeof(struct pt_regs));
c_regs->a0 = c_usp; /* function */
c_regs->d0 = ustk_size; /* argument */
local_save_flags(c_regs->epsw);
c_regs->epsw |= EPSW_IE | EPSW_IM_7;
p->thread.pc = (unsigned long) ret_from_kernel_thread;
return 0;
}
*c_regs = *current_pt_regs();
if (c_usp)
c_regs->sp = c_usp;
c_regs->epsw &= ~EPSW_FE; /* my FPU */
/* the new TLS pointer is passed in as arg #5 to sys_clone() */
if (clone_flags & CLONE_SETTLS)
c_regs->e2 = current_frame()->d3;
p->thread.pc = (unsigned long) ret_from_fork;
return 0;
}
unsigned long get_wchan(struct task_struct *p)
{
return p->thread.wchan;
}

View File

@@ -0,0 +1,72 @@
###############################################################################
#
# Fast profiling interrupt handler
#
# Copyright (C) 2007 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 Licence
# as published by the Free Software Foundation; either version
# 2 of the Licence, or (at your option) any later version.
#
###############################################################################
#include <linux/sys.h>
#include <linux/linkage.h>
#include <asm/segment.h>
#include <asm/smp.h>
#include <asm/intctl-regs.h>
#include <asm/timer-regs.h>
#define pi break
.balign 4
counter:
.long -1
###############################################################################
#
# Profiling interrupt entry point
# - intended to run at interrupt priority 1
#
###############################################################################
ENTRY(profile_handler)
movm [d2,d3,a2],(sp)
# ignore userspace
mov (12,sp),d2
and EPSW_nSL,d2
bne out
# do nothing if there's no buffer
mov (prof_buffer),a2
and a2,a2
beq out
or 0x20000000,a2
# calculate relative position in text segment
mov (16,sp),d2
sub _stext,d2
mov (prof_shift),d3
lsr d3,d2
mov (prof_len),d3
cmp d3,d2
bcc outside_text
# increment the appropriate profile bucket
do_inc:
asl2 d2
mov (a2,d2),d3
inc d3
mov d3,(a2,d2)
out:
mov GxICR_DETECT,d2
movbu d2,(TM11ICR) # ACK the interrupt
movbu (TM11ICR),d2
movm (sp),[d2,d3,a2]
rti
outside_text:
sub 1,d3
mov d3,d2
bra do_inc

View File

@@ -0,0 +1,51 @@
/* MN10300 Profiling setup
*
* Copyright (C) 2007 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 Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
/*
* initialise the profiling if enabled
* - using with gdbstub will give anomalous results
* - can't be used with gdbstub if running at IRQ priority 0
*/
static __init int profile_init(void)
{
u16 tmp;
if (!prof_buffer)
return 0;
/* use timer 11 to drive the profiling interrupts */
set_intr_stub(EXCEP_IRQ_LEVEL0, profile_handler);
/* set IRQ priority at which to run */
set_intr_level(TM11IRQ, GxICR_LEVEL_0);
/* set up timer 11
* - source: (IOCLK 33MHz)*2 = 66MHz
* - frequency: (33330000*2) / 8 / 20625 = 202Hz
*/
TM11BR = 20625 - 1;
TM11MD = TM8MD_SRC_IOCLK_8;
TM11MD |= TM8MD_INIT_COUNTER;
TM11MD &= ~TM8MD_INIT_COUNTER;
TM11MD |= TM8MD_COUNT_ENABLE;
TM11ICR |= GxICR_ENABLE;
tmp = TM11ICR;
printk(KERN_INFO "Profiling initiated on timer 11, priority 0, %uHz\n",
MN10300_IOCLK / 8 / (TM11BR + 1));
printk(KERN_INFO "Profile histogram stored %p-%p\n",
prof_buffer, (u8 *)(prof_buffer + prof_len) - 1);
return 0;
}
__initcall(profile_init);

View File

@@ -0,0 +1,385 @@
/* MN10300 Process tracing
*
* Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd.
* Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
* Modified 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 Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/user.h>
#include <linux/regset.h>
#include <linux/elf.h>
#include <linux/tracehook.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
#include <asm/cacheflush.h>
#include <asm/fpu.h>
#include <asm/asm-offsets.h>
/*
* translate ptrace register IDs into struct pt_regs offsets
*/
static const u8 ptrace_regid_to_frame[] = {
[PT_A3 << 2] = REG_A3,
[PT_A2 << 2] = REG_A2,
[PT_D3 << 2] = REG_D3,
[PT_D2 << 2] = REG_D2,
[PT_MCVF << 2] = REG_MCVF,
[PT_MCRL << 2] = REG_MCRL,
[PT_MCRH << 2] = REG_MCRH,
[PT_MDRQ << 2] = REG_MDRQ,
[PT_E1 << 2] = REG_E1,
[PT_E0 << 2] = REG_E0,
[PT_E7 << 2] = REG_E7,
[PT_E6 << 2] = REG_E6,
[PT_E5 << 2] = REG_E5,
[PT_E4 << 2] = REG_E4,
[PT_E3 << 2] = REG_E3,
[PT_E2 << 2] = REG_E2,
[PT_SP << 2] = REG_SP,
[PT_LAR << 2] = REG_LAR,
[PT_LIR << 2] = REG_LIR,
[PT_MDR << 2] = REG_MDR,
[PT_A1 << 2] = REG_A1,
[PT_A0 << 2] = REG_A0,
[PT_D1 << 2] = REG_D1,
[PT_D0 << 2] = REG_D0,
[PT_ORIG_D0 << 2] = REG_ORIG_D0,
[PT_EPSW << 2] = REG_EPSW,
[PT_PC << 2] = REG_PC,
};
static inline int get_stack_long(struct task_struct *task, int offset)
{
return *(unsigned long *)
((unsigned long) task->thread.uregs + offset);
}
static inline
int put_stack_long(struct task_struct *task, int offset, unsigned long data)
{
unsigned long stack;
stack = (unsigned long) task->thread.uregs + offset;
*(unsigned long *) stack = data;
return 0;
}
/*
* retrieve the contents of MN10300 userspace general registers
*/
static int genregs_get(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
void *kbuf, void __user *ubuf)
{
const struct pt_regs *regs = task_pt_regs(target);
int ret;
/* we need to skip regs->next */
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
regs, 0, PT_ORIG_D0 * sizeof(long));
if (ret < 0)
return ret;
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
&regs->orig_d0, PT_ORIG_D0 * sizeof(long),
NR_PTREGS * sizeof(long));
if (ret < 0)
return ret;
return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
NR_PTREGS * sizeof(long), -1);
}
/*
* update the contents of the MN10300 userspace general registers
*/
static int genregs_set(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
const void *kbuf, const void __user *ubuf)
{
struct pt_regs *regs = task_pt_regs(target);
unsigned long tmp;
int ret;
/* we need to skip regs->next */
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
regs, 0, PT_ORIG_D0 * sizeof(long));
if (ret < 0)
return ret;
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
&regs->orig_d0, PT_ORIG_D0 * sizeof(long),
PT_EPSW * sizeof(long));
if (ret < 0)
return ret;
/* we need to mask off changes to EPSW */
tmp = regs->epsw;
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
&tmp, PT_EPSW * sizeof(long),
PT_PC * sizeof(long));
tmp &= EPSW_FLAG_V | EPSW_FLAG_C | EPSW_FLAG_N | EPSW_FLAG_Z;
tmp |= regs->epsw & ~(EPSW_FLAG_V | EPSW_FLAG_C | EPSW_FLAG_N |
EPSW_FLAG_Z);
regs->epsw = tmp;
if (ret < 0)
return ret;
/* and finally load the PC */
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
&regs->pc, PT_PC * sizeof(long),
NR_PTREGS * sizeof(long));
if (ret < 0)
return ret;
return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
NR_PTREGS * sizeof(long), -1);
}
/*
* retrieve the contents of MN10300 userspace FPU registers
*/
static int fpuregs_get(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
void *kbuf, void __user *ubuf)
{
const struct fpu_state_struct *fpregs = &target->thread.fpu_state;
int ret;
unlazy_fpu(target);
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
fpregs, 0, sizeof(*fpregs));
if (ret < 0)
return ret;
return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
sizeof(*fpregs), -1);
}
/*
* update the contents of the MN10300 userspace FPU registers
*/
static int fpuregs_set(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
const void *kbuf, const void __user *ubuf)
{
struct fpu_state_struct fpu_state = target->thread.fpu_state;
int ret;
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
&fpu_state, 0, sizeof(fpu_state));
if (ret < 0)
return ret;
fpu_kill_state(target);
target->thread.fpu_state = fpu_state;
set_using_fpu(target);
return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
sizeof(fpu_state), -1);
}
/*
* determine if the FPU registers have actually been used
*/
static int fpuregs_active(struct task_struct *target,
const struct user_regset *regset)
{
return is_using_fpu(target) ? regset->n : 0;
}
/*
* Define the register sets available on the MN10300 under Linux
*/
enum mn10300_regset {
REGSET_GENERAL,
REGSET_FPU,
};
static const struct user_regset mn10300_regsets[] = {
/*
* General register format is:
* A3, A2, D3, D2, MCVF, MCRL, MCRH, MDRQ
* E1, E0, E7...E2, SP, LAR, LIR, MDR
* A1, A0, D1, D0, ORIG_D0, EPSW, PC
*/
[REGSET_GENERAL] = {
.core_note_type = NT_PRSTATUS,
.n = ELF_NGREG,
.size = sizeof(long),
.align = sizeof(long),
.get = genregs_get,
.set = genregs_set,
},
/*
* FPU register format is:
* FS0-31, FPCR
*/
[REGSET_FPU] = {
.core_note_type = NT_PRFPREG,
.n = sizeof(struct fpu_state_struct) / sizeof(long),
.size = sizeof(long),
.align = sizeof(long),
.get = fpuregs_get,
.set = fpuregs_set,
.active = fpuregs_active,
},
};
static const struct user_regset_view user_mn10300_native_view = {
.name = "mn10300",
.e_machine = EM_MN10300,
.regsets = mn10300_regsets,
.n = ARRAY_SIZE(mn10300_regsets),
};
const struct user_regset_view *task_user_regset_view(struct task_struct *task)
{
return &user_mn10300_native_view;
}
/*
* set the single-step bit
*/
void user_enable_single_step(struct task_struct *child)
{
#ifndef CONFIG_MN10300_USING_JTAG
struct user *dummy = NULL;
long tmp;
tmp = get_stack_long(child, (unsigned long) &dummy->regs.epsw);
tmp |= EPSW_T;
put_stack_long(child, (unsigned long) &dummy->regs.epsw, tmp);
#endif
}
/*
* make sure the single-step bit is not set
*/
void user_disable_single_step(struct task_struct *child)
{
#ifndef CONFIG_MN10300_USING_JTAG
struct user *dummy = NULL;
long tmp;
tmp = get_stack_long(child, (unsigned long) &dummy->regs.epsw);
tmp &= ~EPSW_T;
put_stack_long(child, (unsigned long) &dummy->regs.epsw, tmp);
#endif
}
void ptrace_disable(struct task_struct *child)
{
user_disable_single_step(child);
}
/*
* handle the arch-specific side of process tracing
*/
long arch_ptrace(struct task_struct *child, long request,
unsigned long addr, unsigned long data)
{
unsigned long tmp;
int ret;
unsigned long __user *datap = (unsigned long __user *) data;
switch (request) {
/* read the word at location addr in the USER area. */
case PTRACE_PEEKUSR:
ret = -EIO;
if ((addr & 3) || addr > sizeof(struct user) - 3)
break;
tmp = 0; /* Default return condition */
if (addr < NR_PTREGS << 2)
tmp = get_stack_long(child,
ptrace_regid_to_frame[addr]);
ret = put_user(tmp, datap);
break;
/* write the word at location addr in the USER area */
case PTRACE_POKEUSR:
ret = -EIO;
if ((addr & 3) || addr > sizeof(struct user) - 3)
break;
ret = 0;
if (addr < NR_PTREGS << 2)
ret = put_stack_long(child, ptrace_regid_to_frame[addr],
data);
break;
case PTRACE_GETREGS: /* Get all integer regs from the child. */
return copy_regset_to_user(child, &user_mn10300_native_view,
REGSET_GENERAL,
0, NR_PTREGS * sizeof(long),
datap);
case PTRACE_SETREGS: /* Set all integer regs in the child. */
return copy_regset_from_user(child, &user_mn10300_native_view,
REGSET_GENERAL,
0, NR_PTREGS * sizeof(long),
datap);
case PTRACE_GETFPREGS: /* Get the child FPU state. */
return copy_regset_to_user(child, &user_mn10300_native_view,
REGSET_FPU,
0, sizeof(struct fpu_state_struct),
datap);
case PTRACE_SETFPREGS: /* Set the child FPU state. */
return copy_regset_from_user(child, &user_mn10300_native_view,
REGSET_FPU,
0, sizeof(struct fpu_state_struct),
datap);
default:
ret = ptrace_request(child, request, addr, data);
break;
}
return ret;
}
/*
* handle tracing of system call entry
* - return the revised system call number or ULONG_MAX to cause ENOSYS
*/
asmlinkage unsigned long syscall_trace_entry(struct pt_regs *regs)
{
if (tracehook_report_syscall_entry(regs))
/* tracing decided this syscall should not happen, so
* We'll return a bogus call number to get an ENOSYS
* error, but leave the original number in
* regs->orig_d0
*/
return ULONG_MAX;
return regs->orig_d0;
}
/*
* handle tracing of system call exit
*/
asmlinkage void syscall_trace_exit(struct pt_regs *regs)
{
tracehook_report_syscall_exit(regs, 0);
}

132
arch/mn10300/kernel/rtc.c Normal file
View File

@@ -0,0 +1,132 @@
/* MN10300 RTC management
*
* Copyright (C) 2007 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 Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mc146818rtc.h>
#include <linux/bcd.h>
#include <linux/timex.h>
#include <asm/rtc-regs.h>
#include <asm/rtc.h>
DEFINE_SPINLOCK(rtc_lock);
EXPORT_SYMBOL(rtc_lock);
/*
* Read the current RTC time
*/
void read_persistent_clock(struct timespec *ts)
{
struct rtc_time tm;
get_rtc_time(&tm);
ts->tv_nsec = 0;
ts->tv_sec = mktime(tm.tm_year, tm.tm_mon, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
/* if rtc is way off in the past, set something reasonable */
if (ts->tv_sec < 0)
ts->tv_sec = mktime(2009, 1, 1, 12, 0, 0);
}
/*
* In order to set the CMOS clock precisely, set_rtc_mmss has to be called 500
* ms after the second nowtime has started, because when nowtime is written
* into the registers of the CMOS clock, it will jump to the next second
* precisely 500 ms later. Check the Motorola MC146818A or Dallas DS12887 data
* sheet for details.
*
* BUG: This routine does not handle hour overflow properly; it just
* sets the minutes. Usually you'll only notice that after reboot!
*/
static int set_rtc_mmss(unsigned long nowtime)
{
unsigned char save_control, save_freq_select;
int retval = 0;
int real_seconds, real_minutes, cmos_minutes;
/* gets recalled with irq locally disabled */
spin_lock(&rtc_lock);
save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being
* set */
CMOS_WRITE(save_control | RTC_SET, RTC_CONTROL);
save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset
* prescaler */
CMOS_WRITE(save_freq_select | RTC_DIV_RESET2, RTC_FREQ_SELECT);
cmos_minutes = CMOS_READ(RTC_MINUTES);
if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
cmos_minutes = bcd2bin(cmos_minutes);
/*
* since we're only adjusting minutes and seconds,
* don't interfere with hour overflow. This avoids
* messing with unknown time zones but requires your
* RTC not to be off by more than 15 minutes
*/
real_seconds = nowtime % 60;
real_minutes = nowtime / 60;
if (((abs(real_minutes - cmos_minutes) + 15) / 30) & 1)
/* correct for half hour time zone */
real_minutes += 30;
real_minutes %= 60;
if (abs(real_minutes - cmos_minutes) < 30) {
if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
real_seconds = bin2bcd(real_seconds);
real_minutes = bin2bcd(real_minutes);
}
CMOS_WRITE(real_seconds, RTC_SECONDS);
CMOS_WRITE(real_minutes, RTC_MINUTES);
} else {
printk_once(KERN_NOTICE
"set_rtc_mmss: can't update from %d to %d\n",
cmos_minutes, real_minutes);
retval = -1;
}
/* The following flags have to be released exactly in this order,
* otherwise the DS12887 (popular MC146818A clone with integrated
* battery and quartz) will not reset the oscillator and will not
* update precisely 500 ms later. You won't find this mentioned in
* the Dallas Semiconductor data sheets, but who believes data
* sheets anyway ... -- Markus Kuhn
*/
CMOS_WRITE(save_control, RTC_CONTROL);
CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
spin_unlock(&rtc_lock);
return retval;
}
int update_persistent_clock(struct timespec now)
{
return set_rtc_mmss(now.tv_sec);
}
/*
* calibrate the TSC clock against the RTC
*/
void __init calibrate_clock(void)
{
unsigned char status;
/* make sure the RTC is running and is set to operate in 24hr mode */
status = RTSRC;
RTCRB |= RTCRB_SET;
RTCRB |= RTCRB_TM_24HR;
RTCRB &= ~RTCRB_DM_BINARY;
RTCRA |= RTCRA_DVR;
RTCRA &= ~RTCRA_DVR;
RTCRB &= ~RTCRB_SET;
}

286
arch/mn10300/kernel/setup.c Normal file
View File

@@ -0,0 +1,286 @@
/* MN10300 Arch-specific initialisation
*
* Copyright (C) 2007 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 Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/ptrace.h>
#include <linux/user.h>
#include <linux/tty.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/bootmem.h>
#include <linux/seq_file.h>
#include <linux/cpu.h>
#include <asm/processor.h>
#include <linux/console.h>
#include <asm/uaccess.h>
#include <asm/setup.h>
#include <asm/io.h>
#include <asm/smp.h>
#include <proc/proc.h>
#include <asm/fpu.h>
#include <asm/sections.h>
struct mn10300_cpuinfo boot_cpu_data;
/* For PCI or other memory-mapped resources */
unsigned long pci_mem_start = 0x18000000;
static char __initdata cmd_line[COMMAND_LINE_SIZE];
char redboot_command_line[COMMAND_LINE_SIZE] =
"console=ttyS0,115200 root=/dev/mtdblock3 rw";
char __initdata redboot_platform_name[COMMAND_LINE_SIZE];
static struct resource code_resource = {
.start = 0x100000,
.end = 0,
.name = "Kernel code",
};
static struct resource data_resource = {
.start = 0,
.end = 0,
.name = "Kernel data",
};
static unsigned long __initdata phys_memory_base;
static unsigned long __initdata phys_memory_end;
static unsigned long __initdata memory_end;
unsigned long memory_size;
struct thread_info *__current_ti = &init_thread_union.thread_info;
struct task_struct *__current = &init_task;
#define mn10300_known_cpus 5
static const char *const mn10300_cputypes[] = {
"am33-1",
"am33-2",
"am34-1",
"am33-3",
"am34-2",
"unknown"
};
/*
* Pick out the memory size. We look for mem=size,
* where size is "size[KkMm]"
*/
static int __init early_mem(char *p)
{
memory_size = memparse(p, &p);
if (memory_size == 0)
panic("Memory size not known\n");
return 0;
}
early_param("mem", early_mem);
/*
* architecture specific setup
*/
void __init setup_arch(char **cmdline_p)
{
unsigned long bootmap_size;
unsigned long kstart_pfn, start_pfn, free_pfn, end_pfn;
cpu_init();
unit_setup();
smp_init_cpus();
/* save unparsed command line copy for /proc/cmdline */
strlcpy(boot_command_line, redboot_command_line, COMMAND_LINE_SIZE);
/* populate cmd_line too for later use, preserving boot_command_line */
strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
*cmdline_p = cmd_line;
parse_early_param();
memory_end = (unsigned long) CONFIG_KERNEL_RAM_BASE_ADDRESS +
memory_size;
if (memory_end > phys_memory_end)
memory_end = phys_memory_end;
init_mm.start_code = (unsigned long)&_text;
init_mm.end_code = (unsigned long) &_etext;
init_mm.end_data = (unsigned long) &_edata;
init_mm.brk = (unsigned long) &_end;
code_resource.start = virt_to_bus(&_text);
code_resource.end = virt_to_bus(&_etext)-1;
data_resource.start = virt_to_bus(&_etext);
data_resource.end = virt_to_bus(&_edata)-1;
start_pfn = (CONFIG_KERNEL_RAM_BASE_ADDRESS >> PAGE_SHIFT);
kstart_pfn = PFN_UP(__pa(&_text));
free_pfn = PFN_UP(__pa(&_end));
end_pfn = PFN_DOWN(__pa(memory_end));
bootmap_size = init_bootmem_node(&contig_page_data,
free_pfn,
start_pfn,
end_pfn);
if (kstart_pfn > start_pfn)
free_bootmem(PFN_PHYS(start_pfn),
PFN_PHYS(kstart_pfn - start_pfn));
free_bootmem(PFN_PHYS(free_pfn),
PFN_PHYS(end_pfn - free_pfn));
/* If interrupt vector table is in main ram, then we need to
reserve the page it is occupying. */
if (CONFIG_INTERRUPT_VECTOR_BASE >= CONFIG_KERNEL_RAM_BASE_ADDRESS &&
CONFIG_INTERRUPT_VECTOR_BASE < memory_end)
reserve_bootmem(CONFIG_INTERRUPT_VECTOR_BASE, PAGE_SIZE,
BOOTMEM_DEFAULT);
reserve_bootmem(PAGE_ALIGN(PFN_PHYS(free_pfn)), bootmap_size,
BOOTMEM_DEFAULT);
#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE)
conswitchp = &dummy_con;
#endif
#endif
paging_init();
}
/*
* perform CPU initialisation
*/
void __init cpu_init(void)
{
unsigned long cpurev = CPUREV, type;
type = (CPUREV & CPUREV_TYPE) >> CPUREV_TYPE_S;
if (type > mn10300_known_cpus)
type = mn10300_known_cpus;
printk(KERN_INFO "Panasonic %s, rev %ld\n",
mn10300_cputypes[type],
(cpurev & CPUREV_REVISION) >> CPUREV_REVISION_S);
get_mem_info(&phys_memory_base, &memory_size);
phys_memory_end = phys_memory_base + memory_size;
fpu_init_state();
}
static struct cpu cpu_devices[NR_CPUS];
static int __init topology_init(void)
{
int i;
for_each_present_cpu(i)
register_cpu(&cpu_devices[i], i);
return 0;
}
subsys_initcall(topology_init);
/*
* Get CPU information for use by the procfs.
*/
static int show_cpuinfo(struct seq_file *m, void *v)
{
#ifdef CONFIG_SMP
struct mn10300_cpuinfo *c = v;
unsigned long cpu_id = c - cpu_data;
unsigned long cpurev = c->type, type, icachesz, dcachesz;
#else /* CONFIG_SMP */
unsigned long cpu_id = 0;
unsigned long cpurev = CPUREV, type, icachesz, dcachesz;
#endif /* CONFIG_SMP */
#ifdef CONFIG_SMP
if (!cpu_online(cpu_id))
return 0;
#endif
type = (cpurev & CPUREV_TYPE) >> CPUREV_TYPE_S;
if (type > mn10300_known_cpus)
type = mn10300_known_cpus;
icachesz =
((cpurev & CPUREV_ICWAY ) >> CPUREV_ICWAY_S) *
((cpurev & CPUREV_ICSIZE) >> CPUREV_ICSIZE_S) *
1024;
dcachesz =
((cpurev & CPUREV_DCWAY ) >> CPUREV_DCWAY_S) *
((cpurev & CPUREV_DCSIZE) >> CPUREV_DCSIZE_S) *
1024;
seq_printf(m,
"processor : %ld\n"
"vendor_id : " PROCESSOR_VENDOR_NAME "\n"
"cpu core : %s\n"
"cpu rev : %lu\n"
"model name : " PROCESSOR_MODEL_NAME "\n"
"icache size: %lu\n"
"dcache size: %lu\n",
cpu_id,
mn10300_cputypes[type],
(cpurev & CPUREV_REVISION) >> CPUREV_REVISION_S,
icachesz,
dcachesz
);
seq_printf(m,
"ioclk speed: %lu.%02luMHz\n"
"bogomips : %lu.%02lu\n\n",
MN10300_IOCLK / 1000000,
(MN10300_IOCLK / 10000) % 100,
#ifdef CONFIG_SMP
c->loops_per_jiffy / (500000 / HZ),
(c->loops_per_jiffy / (5000 / HZ)) % 100
#else /* CONFIG_SMP */
loops_per_jiffy / (500000 / HZ),
(loops_per_jiffy / (5000 / HZ)) % 100
#endif /* CONFIG_SMP */
);
return 0;
}
static void *c_start(struct seq_file *m, loff_t *pos)
{
return *pos < NR_CPUS ? cpu_data + *pos : NULL;
}
static void *c_next(struct seq_file *m, void *v, loff_t *pos)
{
++*pos;
return c_start(m, pos);
}
static void c_stop(struct seq_file *m, void *v)
{
}
const struct seq_operations cpuinfo_op = {
.start = c_start,
.next = c_next,
.stop = c_stop,
.show = show_cpuinfo,
};

View File

@@ -0,0 +1,33 @@
/* MN10300 Signal frame definitions
*
* Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd.
* Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
struct sigframe
{
void (*pretcode)(void);
int sig;
struct sigcontext *psc;
struct sigcontext sc;
struct fpucontext fpuctx;
unsigned long extramask[_NSIG_WORDS-1];
char retcode[8];
};
struct rt_sigframe
{
void (*pretcode)(void);
int sig;
struct siginfo *pinfo;
void *puc;
struct siginfo info;
struct ucontext uc;
struct fpucontext fpuctx;
char retcode[8];
};

View File

@@ -0,0 +1,469 @@
/* MN10300 Signal handling
*
* Copyright (C) 2007 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 Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <linux/wait.h>
#include <linux/ptrace.h>
#include <linux/unistd.h>
#include <linux/stddef.h>
#include <linux/tty.h>
#include <linux/personality.h>
#include <linux/suspend.h>
#include <linux/tracehook.h>
#include <asm/cacheflush.h>
#include <asm/ucontext.h>
#include <asm/uaccess.h>
#include <asm/fpu.h>
#include "sigframe.h"
#define DEBUG_SIG 0
/*
* do a signal return; undo the signal stack.
*/
static int restore_sigcontext(struct pt_regs *regs,
struct sigcontext __user *sc, long *_d0)
{
unsigned int err = 0;
/* Always make any pending restarted system calls return -EINTR */
current_thread_info()->restart_block.fn = do_no_restart_syscall;
if (is_using_fpu(current))
fpu_kill_state(current);
#define COPY(x) err |= __get_user(regs->x, &sc->x)
COPY(d1); COPY(d2); COPY(d3);
COPY(a0); COPY(a1); COPY(a2); COPY(a3);
COPY(e0); COPY(e1); COPY(e2); COPY(e3);
COPY(e4); COPY(e5); COPY(e6); COPY(e7);
COPY(lar); COPY(lir);
COPY(mdr); COPY(mdrq);
COPY(mcvf); COPY(mcrl); COPY(mcrh);
COPY(sp); COPY(pc);
#undef COPY
{
unsigned int tmpflags;
#ifndef CONFIG_MN10300_USING_JTAG
#define USER_EPSW (EPSW_FLAG_Z | EPSW_FLAG_N | EPSW_FLAG_C | EPSW_FLAG_V | \
EPSW_T | EPSW_nAR)
#else
#define USER_EPSW (EPSW_FLAG_Z | EPSW_FLAG_N | EPSW_FLAG_C | EPSW_FLAG_V | \
EPSW_nAR)
#endif
err |= __get_user(tmpflags, &sc->epsw);
regs->epsw = (regs->epsw & ~USER_EPSW) |
(tmpflags & USER_EPSW);
regs->orig_d0 = -1; /* disable syscall checks */
}
{
struct fpucontext *buf;
err |= __get_user(buf, &sc->fpucontext);
if (buf) {
if (verify_area(VERIFY_READ, buf, sizeof(*buf)))
goto badframe;
err |= fpu_restore_sigcontext(buf);
}
}
err |= __get_user(*_d0, &sc->d0);
return err;
badframe:
return 1;
}
/*
* standard signal return syscall
*/
asmlinkage long sys_sigreturn(void)
{
struct sigframe __user *frame;
sigset_t set;
long d0;
frame = (struct sigframe __user *) current_frame()->sp;
if (verify_area(VERIFY_READ, frame, sizeof(*frame)))
goto badframe;
if (__get_user(set.sig[0], &frame->sc.oldmask))
goto badframe;
if (_NSIG_WORDS > 1 &&
__copy_from_user(&set.sig[1], &frame->extramask,
sizeof(frame->extramask)))
goto badframe;
set_current_blocked(&set);
if (restore_sigcontext(current_frame(), &frame->sc, &d0))
goto badframe;
return d0;
badframe:
force_sig(SIGSEGV, current);
return 0;
}
/*
* realtime signal return syscall
*/
asmlinkage long sys_rt_sigreturn(void)
{
struct rt_sigframe __user *frame;
sigset_t set;
long d0;
frame = (struct rt_sigframe __user *) current_frame()->sp;
if (verify_area(VERIFY_READ, frame, sizeof(*frame)))
goto badframe;
if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
goto badframe;
set_current_blocked(&set);
if (restore_sigcontext(current_frame(), &frame->uc.uc_mcontext, &d0))
goto badframe;
if (restore_altstack(&frame->uc.uc_stack))
goto badframe;
return d0;
badframe:
force_sig(SIGSEGV, current);
return 0;
}
/*
* store the userspace context into a signal frame
*/
static int setup_sigcontext(struct sigcontext __user *sc,
struct fpucontext *fpuctx,
struct pt_regs *regs,
unsigned long mask)
{
int tmp, err = 0;
#define COPY(x) err |= __put_user(regs->x, &sc->x)
COPY(d0); COPY(d1); COPY(d2); COPY(d3);
COPY(a0); COPY(a1); COPY(a2); COPY(a3);
COPY(e0); COPY(e1); COPY(e2); COPY(e3);
COPY(e4); COPY(e5); COPY(e6); COPY(e7);
COPY(lar); COPY(lir);
COPY(mdr); COPY(mdrq);
COPY(mcvf); COPY(mcrl); COPY(mcrh);
COPY(sp); COPY(epsw); COPY(pc);
#undef COPY
tmp = fpu_setup_sigcontext(fpuctx);
if (tmp < 0)
err = 1;
else
err |= __put_user(tmp ? fpuctx : NULL, &sc->fpucontext);
/* non-iBCS2 extensions.. */
err |= __put_user(mask, &sc->oldmask);
return err;
}
/*
* determine which stack to use..
*/
static inline void __user *get_sigframe(struct k_sigaction *ka,
struct pt_regs *regs,
size_t frame_size)
{
unsigned long sp;
/* default to using normal stack */
sp = regs->sp;
/* this is the X/Open sanctioned signal stack switching. */
if (ka->sa.sa_flags & SA_ONSTACK) {
if (sas_ss_flags(sp) == 0)
sp = current->sas_ss_sp + current->sas_ss_size;
}
return (void __user *) ((sp - frame_size) & ~7UL);
}
/*
* set up a normal signal frame
*/
static int setup_frame(int sig, struct k_sigaction *ka, sigset_t *set,
struct pt_regs *regs)
{
struct sigframe __user *frame;
int rsig;
frame = get_sigframe(ka, regs, sizeof(*frame));
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
goto give_sigsegv;
rsig = sig;
if (sig < 32 &&
current_thread_info()->exec_domain &&
current_thread_info()->exec_domain->signal_invmap)
rsig = current_thread_info()->exec_domain->signal_invmap[sig];
if (__put_user(rsig, &frame->sig) < 0 ||
__put_user(&frame->sc, &frame->psc) < 0)
goto give_sigsegv;
if (setup_sigcontext(&frame->sc, &frame->fpuctx, regs, set->sig[0]))
goto give_sigsegv;
if (_NSIG_WORDS > 1) {
if (__copy_to_user(frame->extramask, &set->sig[1],
sizeof(frame->extramask)))
goto give_sigsegv;
}
/* set up to return from userspace. If provided, use a stub already in
* userspace */
if (ka->sa.sa_flags & SA_RESTORER) {
if (__put_user(ka->sa.sa_restorer, &frame->pretcode))
goto give_sigsegv;
} else {
if (__put_user((void (*)(void))frame->retcode,
&frame->pretcode))
goto give_sigsegv;
/* this is mov $,d0; syscall 0 */
if (__put_user(0x2c, (char *)(frame->retcode + 0)) ||
__put_user(__NR_sigreturn, (char *)(frame->retcode + 1)) ||
__put_user(0x00, (char *)(frame->retcode + 2)) ||
__put_user(0xf0, (char *)(frame->retcode + 3)) ||
__put_user(0xe0, (char *)(frame->retcode + 4)))
goto give_sigsegv;
flush_icache_range((unsigned long) frame->retcode,
(unsigned long) frame->retcode + 5);
}
/* set up registers for signal handler */
regs->sp = (unsigned long) frame;
regs->pc = (unsigned long) ka->sa.sa_handler;
regs->d0 = sig;
regs->d1 = (unsigned long) &frame->sc;
#if DEBUG_SIG
printk(KERN_DEBUG "SIG deliver %d (%s:%d): sp=%p pc=%lx ra=%p\n",
sig, current->comm, current->pid, frame, regs->pc,
frame->pretcode);
#endif
return 0;
give_sigsegv:
force_sigsegv(sig, current);
return -EFAULT;
}
/*
* set up a realtime signal frame
*/
static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
sigset_t *set, struct pt_regs *regs)
{
struct rt_sigframe __user *frame;
int rsig;
frame = get_sigframe(ka, regs, sizeof(*frame));
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
goto give_sigsegv;
rsig = sig;
if (sig < 32 &&
current_thread_info()->exec_domain &&
current_thread_info()->exec_domain->signal_invmap)
rsig = current_thread_info()->exec_domain->signal_invmap[sig];
if (__put_user(rsig, &frame->sig) ||
__put_user(&frame->info, &frame->pinfo) ||
__put_user(&frame->uc, &frame->puc) ||
copy_siginfo_to_user(&frame->info, info))
goto give_sigsegv;
/* create the ucontext. */
if (__put_user(0, &frame->uc.uc_flags) ||
__put_user(0, &frame->uc.uc_link) ||
__save_altstack(&frame->uc.uc_stack, regs->sp) ||
setup_sigcontext(&frame->uc.uc_mcontext,
&frame->fpuctx, regs, set->sig[0]) ||
__copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)))
goto give_sigsegv;
/* set up to return from userspace. If provided, use a stub already in
* userspace */
if (ka->sa.sa_flags & SA_RESTORER) {
if (__put_user(ka->sa.sa_restorer, &frame->pretcode))
goto give_sigsegv;
} else {
if (__put_user((void(*)(void))frame->retcode,
&frame->pretcode) ||
/* This is mov $,d0; syscall 0 */
__put_user(0x2c, (char *)(frame->retcode + 0)) ||
__put_user(__NR_rt_sigreturn,
(char *)(frame->retcode + 1)) ||
__put_user(0x00, (char *)(frame->retcode + 2)) ||
__put_user(0xf0, (char *)(frame->retcode + 3)) ||
__put_user(0xe0, (char *)(frame->retcode + 4)))
goto give_sigsegv;
flush_icache_range((u_long) frame->retcode,
(u_long) frame->retcode + 5);
}
/* Set up registers for signal handler */
regs->sp = (unsigned long) frame;
regs->pc = (unsigned long) ka->sa.sa_handler;
regs->d0 = sig;
regs->d1 = (long) &frame->info;
#if DEBUG_SIG
printk(KERN_DEBUG "SIG deliver %d (%s:%d): sp=%p pc=%lx ra=%p\n",
sig, current->comm, current->pid, frame, regs->pc,
frame->pretcode);
#endif
return 0;
give_sigsegv:
force_sigsegv(sig, current);
return -EFAULT;
}
static inline void stepback(struct pt_regs *regs)
{
regs->pc -= 2;
regs->orig_d0 = -1;
}
/*
* handle the actual delivery of a signal to userspace
*/
static int handle_signal(int sig,
siginfo_t *info, struct k_sigaction *ka,
struct pt_regs *regs)
{
sigset_t *oldset = sigmask_to_save();
int ret;
/* Are we from a system call? */
if (regs->orig_d0 >= 0) {
/* If so, check system call restarting.. */
switch (regs->d0) {
case -ERESTART_RESTARTBLOCK:
case -ERESTARTNOHAND:
regs->d0 = -EINTR;
break;
case -ERESTARTSYS:
if (!(ka->sa.sa_flags & SA_RESTART)) {
regs->d0 = -EINTR;
break;
}
/* fallthrough */
case -ERESTARTNOINTR:
regs->d0 = regs->orig_d0;
stepback(regs);
}
}
/* Set up the stack frame */
if (ka->sa.sa_flags & SA_SIGINFO)
ret = setup_rt_frame(sig, ka, info, oldset, regs);
else
ret = setup_frame(sig, ka, oldset, regs);
if (ret)
return ret;
signal_delivered(sig, info, ka, regs,
test_thread_flag(TIF_SINGLESTEP));
return 0;
}
/*
* handle a potential signal
*/
static void do_signal(struct pt_regs *regs)
{
struct k_sigaction ka;
siginfo_t info;
int signr;
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
if (signr > 0) {
if (handle_signal(signr, &info, &ka, regs) == 0) {
}
return;
}
/* did we come from a system call? */
if (regs->orig_d0 >= 0) {
/* restart the system call - no handlers present */
switch (regs->d0) {
case -ERESTARTNOHAND:
case -ERESTARTSYS:
case -ERESTARTNOINTR:
regs->d0 = regs->orig_d0;
stepback(regs);
break;
case -ERESTART_RESTARTBLOCK:
regs->d0 = __NR_restart_syscall;
stepback(regs);
break;
}
}
/* if there's no signal to deliver, we just put the saved sigmask
* back */
restore_saved_sigmask();
}
/*
* notification of userspace execution resumption
* - triggered by current->work.notify_resume
*/
asmlinkage void do_notify_resume(struct pt_regs *regs, u32 thread_info_flags)
{
/* Pending single-step? */
if (thread_info_flags & _TIF_SINGLESTEP) {
#ifndef CONFIG_MN10300_USING_JTAG
regs->epsw |= EPSW_T;
clear_thread_flag(TIF_SINGLESTEP);
#else
BUG(); /* no h/w single-step if using JTAG unit */
#endif
}
/* deal with pending signal delivery */
if (thread_info_flags & _TIF_SIGPENDING)
do_signal(regs);
if (thread_info_flags & _TIF_NOTIFY_RESUME) {
clear_thread_flag(TIF_NOTIFY_RESUME);
tracehook_notify_resume(current_frame());
}
}

View File

@@ -0,0 +1,97 @@
/* SMP IPI low-level handler
*
* Copyright (C) 2006-2007 Matsushita Electric Industrial Co., Ltd.
* All Rights Reserved.
*
* 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/sys.h>
#include <linux/linkage.h>
#include <asm/smp.h>
#include <asm/thread_info.h>
#include <asm/cpu-regs.h>
#include <asm/intctl-regs.h>
#include <proc/smp-regs.h>
#include <asm/asm-offsets.h>
#include <asm/frame.inc>
.am33_2
###############################################################################
#
# IPI interrupt handler
#
###############################################################################
.globl mn10300_low_ipi_handler
mn10300_low_ipi_handler:
add -4,sp
mov d0,(sp)
movhu (IAGR),d0
and IAGR_GN,d0
lsr 0x2,d0
#ifdef CONFIG_MN10300_CACHE_ENABLED
cmp FLUSH_CACHE_IPI,d0
beq mn10300_flush_cache_ipi
#endif
cmp SMP_BOOT_IRQ,d0
beq mn10300_smp_boot_ipi
/* OTHERS */
mov (sp),d0
add 4,sp
#ifdef CONFIG_GDBSTUB
jmp gdbstub_io_rx_handler
#else
jmp end
#endif
###############################################################################
#
# Cache flush IPI interrupt handler
#
###############################################################################
#ifdef CONFIG_MN10300_CACHE_ENABLED
mn10300_flush_cache_ipi:
mov (sp),d0
add 4,sp
/* FLUSH_CACHE_IPI */
add -4,sp
SAVE_ALL
mov GxICR_DETECT,d2
movbu d2,(GxICR(FLUSH_CACHE_IPI)) # ACK the interrupt
movhu (GxICR(FLUSH_CACHE_IPI)),d2
call smp_cache_interrupt[],0
RESTORE_ALL
jmp end
#endif
###############################################################################
#
# SMP boot CPU IPI interrupt handler
#
###############################################################################
mn10300_smp_boot_ipi:
/* clear interrupt */
movhu (GxICR(SMP_BOOT_IRQ)),d0
and ~GxICR_REQUEST,d0
movhu d0,(GxICR(SMP_BOOT_IRQ))
mov (sp),d0
add 4,sp
# get stack
mov (CPUID),a0
add -1,a0
add a0,a0
add a0,a0
mov (start_stack,a0),a0
mov a0,sp
jmp initialize_secondary
# Jump here after RTI to suppress the icache lookahead
end:

1185
arch/mn10300/kernel/smp.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,179 @@
###############################################################################
#
# MN10300 Context switch operation
#
# Copyright (C) 2007 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 Licence
# as published by the Free Software Foundation; either version
# 2 of the Licence, or (at your option) any later version.
#
###############################################################################
#include <linux/sys.h>
#include <linux/linkage.h>
#include <asm/thread_info.h>
#include <asm/cpu-regs.h>
#ifdef CONFIG_SMP
#include <proc/smp-regs.h>
#endif /* CONFIG_SMP */
.text
###############################################################################
#
# struct task_struct *__switch_to(struct thread_struct *prev,
# struct thread_struct *next,
# struct task_struct *prev_task)
#
###############################################################################
ENTRY(__switch_to)
movm [d2,d3,a2,a3,exreg1],(sp)
or EPSW_NMID,epsw
mov (44,sp),d2
mov d0,a0
mov d1,a1
# save prev context
mov __switch_back,d0
mov sp,a2
mov a2,(THREAD_SP,a0)
mov a3,(THREAD_A3,a0)
#ifdef CONFIG_KGDB
btst 0xff,(kgdb_single_step)
bne __switch_to__lift_sstep_bp
__switch_to__continue:
#endif
mov d0,(THREAD_PC,a0)
mov (THREAD_A3,a1),a3
mov (THREAD_SP,a1),a2
# switch
mov a2,sp
# load next context
GET_THREAD_INFO a2
mov a2,(__current_ti)
mov (TI_task,a2),a2
mov a2,(__current)
#ifdef CONFIG_MN10300_CURRENT_IN_E2
mov a2,e2
#endif
mov (THREAD_PC,a1),a2
mov d2,d0 # for ret_from_fork
mov d0,a0 # for __switch_to
jmp (a2)
__switch_back:
and ~EPSW_NMID,epsw
ret [d2,d3,a2,a3,exreg1],32
#ifdef CONFIG_KGDB
###############################################################################
#
# Lift the single-step breakpoints when the task being traced is switched out
# A0 = prev
# A1 = next
#
###############################################################################
__switch_to__lift_sstep_bp:
add -12,sp
mov a0,e4
mov a1,e5
# Clear the single-step flag to prevent us coming this way until we get
# switched back in
bclr 0xff,(kgdb_single_step)
# Remove first breakpoint
mov (kgdb_sstep_bp_addr),a2
cmp 0,a2
beq 1f
movbu (kgdb_sstep_bp),d0
movbu d0,(a2)
#if defined(CONFIG_MN10300_CACHE_FLUSH_ICACHE) || defined(CONFIG_MN10300_CACHE_INV_ICACHE)
mov a2,d0
mov a2,d1
add 1,d1
calls flush_icache_range
#endif
1:
# Remove second breakpoint
mov (kgdb_sstep_bp_addr+4),a2
cmp 0,a2
beq 2f
movbu (kgdb_sstep_bp+1),d0
movbu d0,(a2)
#if defined(CONFIG_MN10300_CACHE_FLUSH_ICACHE) || defined(CONFIG_MN10300_CACHE_INV_ICACHE)
mov a2,d0
mov a2,d1
add 1,d1
calls flush_icache_range
#endif
2:
# Change the resumption address and return
mov __switch_back__reinstall_sstep_bp,d0
mov e4,a0
mov e5,a1
add 12,sp
bra __switch_to__continue
###############################################################################
#
# Reinstall the single-step breakpoints when the task being traced is switched
# back in (A1 points to the new thread_struct).
#
###############################################################################
__switch_back__reinstall_sstep_bp:
add -12,sp
mov a0,e4 # save the return value
mov 0xff,d3
# Reinstall first breakpoint
mov (kgdb_sstep_bp_addr),a2
cmp 0,a2
beq 1f
movbu (a2),d0
movbu d0,(kgdb_sstep_bp)
movbu d3,(a2)
#if defined(CONFIG_MN10300_CACHE_FLUSH_ICACHE) || defined(CONFIG_MN10300_CACHE_INV_ICACHE)
mov a2,d0
mov a2,d1
add 1,d1
calls flush_icache_range
#endif
1:
# Reinstall second breakpoint
mov (kgdb_sstep_bp_addr+4),a2
cmp 0,a2
beq 2f
movbu (a2),d0
movbu d0,(kgdb_sstep_bp+1)
movbu d3,(a2)
#if defined(CONFIG_MN10300_CACHE_FLUSH_ICACHE) || defined(CONFIG_MN10300_CACHE_INV_ICACHE)
mov a2,d0
mov a2,d1
add 1,d1
calls flush_icache_range
#endif
2:
mov d3,(kgdb_single_step)
# Restore the return value (the previous thread_struct pointer)
mov e4,a0
mov a0,d0
add 12,sp
bra __switch_back
#endif /* CONFIG_KGDB */

View File

@@ -0,0 +1,33 @@
/* MN10300 Weird system calls
*
* Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd.
* Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/syscalls.h>
#include <linux/mm.h>
#include <linux/smp.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/tty.h>
#include <asm/uaccess.h>
asmlinkage long old_mmap(unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags,
unsigned long fd, unsigned long offset)
{
if (offset & ~PAGE_MASK)
return -EINVAL;
return sys_mmap_pgoff(addr, len, prot, flags, fd, offset >> PAGE_SHIFT);
}

124
arch/mn10300/kernel/time.c Normal file
View File

@@ -0,0 +1,124 @@
/* MN10300 Low level time management
*
* Copyright (C) 2007-2008 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
* - Derived from arch/i386/kernel/time.c
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/time.h>
#include <linux/init.h>
#include <linux/smp.h>
#include <linux/profile.h>
#include <linux/cnt32_to_63.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <asm/irq.h>
#include <asm/div64.h>
#include <asm/processor.h>
#include <asm/intctl-regs.h>
#include <asm/rtc.h>
#include "internal.h"
static unsigned long mn10300_last_tsc; /* time-stamp counter at last time
* interrupt occurred */
static unsigned long sched_clock_multiplier;
/*
* scheduler clock - returns current time in nanosec units.
*/
unsigned long long sched_clock(void)
{
union {
unsigned long long ll;
unsigned l[2];
} tsc64, result;
unsigned long tmp;
unsigned product[3]; /* 96-bit intermediate value */
/* cnt32_to_63() is not safe with preemption */
preempt_disable();
/* expand the tsc to 64-bits.
* - sched_clock() must be called once a minute or better or the
* following will go horribly wrong - see cnt32_to_63()
*/
tsc64.ll = cnt32_to_63(get_cycles()) & 0x7fffffffffffffffULL;
preempt_enable();
/* scale the 64-bit TSC value to a nanosecond value via a 96-bit
* intermediate
*/
asm("mulu %2,%0,%3,%0 \n" /* LSW * mult -> 0:%3:%0 */
"mulu %2,%1,%2,%1 \n" /* MSW * mult -> %2:%1:0 */
"add %3,%1 \n"
"addc 0,%2 \n" /* result in %2:%1:%0 */
: "=r"(product[0]), "=r"(product[1]), "=r"(product[2]), "=r"(tmp)
: "0"(tsc64.l[0]), "1"(tsc64.l[1]), "2"(sched_clock_multiplier)
: "cc");
result.l[0] = product[1] << 16 | product[0] >> 16;
result.l[1] = product[2] << 16 | product[1] >> 16;
return result.ll;
}
/*
* initialise the scheduler clock
*/
static void __init mn10300_sched_clock_init(void)
{
sched_clock_multiplier =
__muldiv64u(NSEC_PER_SEC, 1 << 16, MN10300_TSCCLK);
}
/**
* local_timer_interrupt - Local timer interrupt handler
*
* Handle local timer interrupts for this CPU. They may have been propagated
* to this CPU from the CPU that actually gets them by way of an IPI.
*/
irqreturn_t local_timer_interrupt(void)
{
profile_tick(CPU_PROFILING);
update_process_times(user_mode(get_irq_regs()));
return IRQ_HANDLED;
}
/*
* initialise the various timers used by the main part of the kernel
*/
void __init time_init(void)
{
/* we need the prescalar running to be able to use IOCLK/8
* - IOCLK runs at 1/4 (ST5 open) or 1/8 (ST5 closed) internal CPU clock
* - IOCLK runs at Fosc rate (crystal speed)
*/
TMPSCNT |= TMPSCNT_ENABLE;
init_clocksource();
printk(KERN_INFO
"timestamp counter I/O clock running at %lu.%02lu"
" (calibrated against RTC)\n",
MN10300_TSCCLK / 1000000, (MN10300_TSCCLK / 10000) % 100);
mn10300_last_tsc = read_timestamp_counter();
init_clockevents();
#ifdef CONFIG_MN10300_WD_TIMER
/* start the watchdog timer */
watchdog_go();
#endif
mn10300_sched_clock_init();
}

616
arch/mn10300/kernel/traps.c Normal file
View File

@@ -0,0 +1,616 @@
/* MN10300 Exception handling
*
* Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd.
* Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
* Modified 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 Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/timer.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/kallsyms.h>
#include <linux/pci.h>
#include <linux/kdebug.h>
#include <linux/bug.h>
#include <linux/irq.h>
#include <linux/export.h>
#include <asm/processor.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#include <linux/atomic.h>
#include <asm/smp.h>
#include <asm/pgalloc.h>
#include <asm/cacheflush.h>
#include <asm/cpu-regs.h>
#include <asm/busctl-regs.h>
#include <unit/leds.h>
#include <asm/fpu.h>
#include <asm/sections.h>
#include <asm/debugger.h>
#include "internal.h"
#if (CONFIG_INTERRUPT_VECTOR_BASE & 0xffffff)
#error "INTERRUPT_VECTOR_BASE not aligned to 16MiB boundary!"
#endif
int kstack_depth_to_print = 24;
spinlock_t die_lock = __SPIN_LOCK_UNLOCKED(die_lock);
struct exception_to_signal_map {
u8 signo;
u32 si_code;
};
static const struct exception_to_signal_map exception_to_signal_map[256] = {
/* MMU exceptions */
[EXCEP_ITLBMISS >> 3] = { 0, 0 },
[EXCEP_DTLBMISS >> 3] = { 0, 0 },
[EXCEP_IAERROR >> 3] = { 0, 0 },
[EXCEP_DAERROR >> 3] = { 0, 0 },
/* system exceptions */
[EXCEP_TRAP >> 3] = { SIGTRAP, TRAP_BRKPT },
[EXCEP_ISTEP >> 3] = { SIGTRAP, TRAP_TRACE }, /* Monitor */
[EXCEP_IBREAK >> 3] = { SIGTRAP, TRAP_HWBKPT }, /* Monitor */
[EXCEP_OBREAK >> 3] = { SIGTRAP, TRAP_HWBKPT }, /* Monitor */
[EXCEP_PRIVINS >> 3] = { SIGILL, ILL_PRVOPC },
[EXCEP_UNIMPINS >> 3] = { SIGILL, ILL_ILLOPC },
[EXCEP_UNIMPEXINS >> 3] = { SIGILL, ILL_ILLOPC },
[EXCEP_MEMERR >> 3] = { SIGSEGV, SEGV_ACCERR },
[EXCEP_MISALIGN >> 3] = { SIGBUS, BUS_ADRALN },
[EXCEP_BUSERROR >> 3] = { SIGBUS, BUS_ADRERR },
[EXCEP_ILLINSACC >> 3] = { SIGSEGV, SEGV_ACCERR },
[EXCEP_ILLDATACC >> 3] = { SIGSEGV, SEGV_ACCERR },
[EXCEP_IOINSACC >> 3] = { SIGSEGV, SEGV_ACCERR },
[EXCEP_PRIVINSACC >> 3] = { SIGSEGV, SEGV_ACCERR }, /* userspace */
[EXCEP_PRIVDATACC >> 3] = { SIGSEGV, SEGV_ACCERR }, /* userspace */
[EXCEP_DATINSACC >> 3] = { SIGSEGV, SEGV_ACCERR },
[EXCEP_DOUBLE_FAULT >> 3] = { SIGILL, ILL_BADSTK },
/* FPU exceptions */
[EXCEP_FPU_DISABLED >> 3] = { SIGILL, ILL_COPROC },
[EXCEP_FPU_UNIMPINS >> 3] = { SIGILL, ILL_COPROC },
[EXCEP_FPU_OPERATION >> 3] = { SIGFPE, FPE_INTDIV },
/* interrupts */
[EXCEP_WDT >> 3] = { SIGALRM, 0 },
[EXCEP_NMI >> 3] = { SIGQUIT, 0 },
[EXCEP_IRQ_LEVEL0 >> 3] = { SIGINT, 0 },
[EXCEP_IRQ_LEVEL1 >> 3] = { 0, 0 },
[EXCEP_IRQ_LEVEL2 >> 3] = { 0, 0 },
[EXCEP_IRQ_LEVEL3 >> 3] = { 0, 0 },
[EXCEP_IRQ_LEVEL4 >> 3] = { 0, 0 },
[EXCEP_IRQ_LEVEL5 >> 3] = { 0, 0 },
[EXCEP_IRQ_LEVEL6 >> 3] = { 0, 0 },
/* system calls */
[EXCEP_SYSCALL0 >> 3] = { 0, 0 },
[EXCEP_SYSCALL1 >> 3] = { SIGILL, ILL_ILLTRP },
[EXCEP_SYSCALL2 >> 3] = { SIGILL, ILL_ILLTRP },
[EXCEP_SYSCALL3 >> 3] = { SIGILL, ILL_ILLTRP },
[EXCEP_SYSCALL4 >> 3] = { SIGILL, ILL_ILLTRP },
[EXCEP_SYSCALL5 >> 3] = { SIGILL, ILL_ILLTRP },
[EXCEP_SYSCALL6 >> 3] = { SIGILL, ILL_ILLTRP },
[EXCEP_SYSCALL7 >> 3] = { SIGILL, ILL_ILLTRP },
[EXCEP_SYSCALL8 >> 3] = { SIGILL, ILL_ILLTRP },
[EXCEP_SYSCALL9 >> 3] = { SIGILL, ILL_ILLTRP },
[EXCEP_SYSCALL10 >> 3] = { SIGILL, ILL_ILLTRP },
[EXCEP_SYSCALL11 >> 3] = { SIGILL, ILL_ILLTRP },
[EXCEP_SYSCALL12 >> 3] = { SIGILL, ILL_ILLTRP },
[EXCEP_SYSCALL13 >> 3] = { SIGILL, ILL_ILLTRP },
[EXCEP_SYSCALL14 >> 3] = { SIGILL, ILL_ILLTRP },
[EXCEP_SYSCALL15 >> 3] = { SIGABRT, 0 },
};
/*
* Handle kernel exceptions.
*
* See if there's a fixup handler we can force a jump to when an exception
* happens due to something kernel code did
*/
int die_if_no_fixup(const char *str, struct pt_regs *regs,
enum exception_code code)
{
u8 opcode;
int signo, si_code;
if (user_mode(regs))
return 0;
peripheral_leds_display_exception(code);
signo = exception_to_signal_map[code >> 3].signo;
si_code = exception_to_signal_map[code >> 3].si_code;
switch (code) {
/* see if we can fixup the kernel accessing memory */
case EXCEP_ITLBMISS:
case EXCEP_DTLBMISS:
case EXCEP_IAERROR:
case EXCEP_DAERROR:
case EXCEP_MEMERR:
case EXCEP_MISALIGN:
case EXCEP_BUSERROR:
case EXCEP_ILLDATACC:
case EXCEP_IOINSACC:
case EXCEP_PRIVINSACC:
case EXCEP_PRIVDATACC:
case EXCEP_DATINSACC:
if (fixup_exception(regs))
return 1;
break;
case EXCEP_TRAP:
case EXCEP_UNIMPINS:
if (probe_kernel_read(&opcode, (u8 *)regs->pc, 1) < 0)
break;
if (opcode == 0xff) {
if (notify_die(DIE_BREAKPOINT, str, regs, code, 0, 0))
return 1;
if (at_debugger_breakpoint(regs))
regs->pc++;
signo = SIGTRAP;
si_code = TRAP_BRKPT;
}
break;
case EXCEP_SYSCALL1 ... EXCEP_SYSCALL14:
/* syscall return addr is _after_ the instruction */
regs->pc -= 2;
break;
case EXCEP_SYSCALL15:
if (report_bug(regs->pc, regs) == BUG_TRAP_TYPE_WARN)
return 1;
/* syscall return addr is _after_ the instruction */
regs->pc -= 2;
break;
default:
break;
}
if (debugger_intercept(code, signo, si_code, regs) == 0)
return 1;
if (notify_die(DIE_GPF, str, regs, code, 0, 0))
return 1;
/* make the process die as the last resort */
die(str, regs, code);
}
/*
* General exception handler
*/
asmlinkage void handle_exception(struct pt_regs *regs, u32 intcode)
{
siginfo_t info;
/* deal with kernel exceptions here */
if (die_if_no_fixup(NULL, regs, intcode))
return;
/* otherwise it's a userspace exception */
info.si_signo = exception_to_signal_map[intcode >> 3].signo;
info.si_code = exception_to_signal_map[intcode >> 3].si_code;
info.si_errno = 0;
info.si_addr = (void *) regs->pc;
force_sig_info(info.si_signo, &info, current);
}
/*
* handle NMI
*/
asmlinkage void nmi(struct pt_regs *regs, enum exception_code code)
{
/* see if gdbstub wants to deal with it */
if (debugger_intercept(code, SIGQUIT, 0, regs))
return;
printk(KERN_WARNING "--- Register Dump ---\n");
show_registers(regs);
printk(KERN_WARNING "---------------------\n");
}
/*
* show a stack trace from the specified stack pointer
*/
void show_trace(unsigned long *sp)
{
unsigned long bottom, stack, addr, fp, raslot;
printk(KERN_EMERG "\nCall Trace:\n");
//stack = (unsigned long)sp;
asm("mov sp,%0" : "=a"(stack));
asm("mov a3,%0" : "=r"(fp));
raslot = ULONG_MAX;
bottom = (stack + THREAD_SIZE) & ~(THREAD_SIZE - 1);
for (; stack < bottom; stack += sizeof(addr)) {
addr = *(unsigned long *)stack;
if (stack == fp) {
if (addr > stack && addr < bottom) {
fp = addr;
raslot = stack + sizeof(addr);
continue;
}
fp = 0;
raslot = ULONG_MAX;
}
if (__kernel_text_address(addr)) {
printk(" [<%08lx>]", addr);
if (stack >= raslot)
raslot = ULONG_MAX;
else
printk(" ?");
print_symbol(" %s", addr);
printk("\n");
}
}
printk("\n");
}
/*
* show the raw stack from the specified stack pointer
*/
void show_stack(struct task_struct *task, unsigned long *sp)
{
unsigned long *stack;
int i;
if (!sp)
sp = (unsigned long *) &sp;
stack = sp;
printk(KERN_EMERG "Stack:");
for (i = 0; i < kstack_depth_to_print; i++) {
if (((long) stack & (THREAD_SIZE - 1)) == 0)
break;
if ((i % 8) == 0)
printk(KERN_EMERG " ");
printk("%08lx ", *stack++);
}
show_trace(sp);
}
/*
* dump the register file in the specified exception frame
*/
void show_registers_only(struct pt_regs *regs)
{
unsigned long ssp;
ssp = (unsigned long) regs + sizeof(*regs);
printk(KERN_EMERG "PC: %08lx EPSW: %08lx SSP: %08lx mode: %s\n",
regs->pc, regs->epsw, ssp, user_mode(regs) ? "User" : "Super");
printk(KERN_EMERG "d0: %08lx d1: %08lx d2: %08lx d3: %08lx\n",
regs->d0, regs->d1, regs->d2, regs->d3);
printk(KERN_EMERG "a0: %08lx a1: %08lx a2: %08lx a3: %08lx\n",
regs->a0, regs->a1, regs->a2, regs->a3);
printk(KERN_EMERG "e0: %08lx e1: %08lx e2: %08lx e3: %08lx\n",
regs->e0, regs->e1, regs->e2, regs->e3);
printk(KERN_EMERG "e4: %08lx e5: %08lx e6: %08lx e7: %08lx\n",
regs->e4, regs->e5, regs->e6, regs->e7);
printk(KERN_EMERG "lar: %08lx lir: %08lx mdr: %08lx usp: %08lx\n",
regs->lar, regs->lir, regs->mdr, regs->sp);
printk(KERN_EMERG "cvf: %08lx crl: %08lx crh: %08lx drq: %08lx\n",
regs->mcvf, regs->mcrl, regs->mcrh, regs->mdrq);
printk(KERN_EMERG "threadinfo=%p task=%p)\n",
current_thread_info(), current);
if ((unsigned long) current >= PAGE_OFFSET &&
(unsigned long) current < (unsigned long)high_memory)
printk(KERN_EMERG "Process %s (pid: %d)\n",
current->comm, current->pid);
#ifdef CONFIG_SMP
printk(KERN_EMERG "CPUID: %08x\n", CPUID);
#endif
printk(KERN_EMERG "CPUP: %04hx\n", CPUP);
printk(KERN_EMERG "TBR: %08x\n", TBR);
printk(KERN_EMERG "DEAR: %08x\n", DEAR);
printk(KERN_EMERG "sISR: %08x\n", sISR);
printk(KERN_EMERG "NMICR: %04hx\n", NMICR);
printk(KERN_EMERG "BCBERR: %08x\n", BCBERR);
printk(KERN_EMERG "BCBEAR: %08x\n", BCBEAR);
printk(KERN_EMERG "MMUFCR: %08x\n", MMUFCR);
printk(KERN_EMERG "IPTEU : %08x IPTEL2: %08x\n", IPTEU, IPTEL2);
printk(KERN_EMERG "DPTEU: %08x DPTEL2: %08x\n", DPTEU, DPTEL2);
}
/*
* dump the registers and the stack
*/
void show_registers(struct pt_regs *regs)
{
unsigned long sp;
int i;
show_registers_only(regs);
if (!user_mode(regs))
sp = (unsigned long) regs + sizeof(*regs);
else
sp = regs->sp;
/* when in-kernel, we also print out the stack and code at the
* time of the fault..
*/
if (!user_mode(regs)) {
printk(KERN_EMERG "\n");
show_stack(current, (unsigned long *) sp);
#if 0
printk(KERN_EMERG "\nCode: ");
if (regs->pc < PAGE_OFFSET)
goto bad;
for (i = 0; i < 20; i++) {
unsigned char c;
if (__get_user(c, &((unsigned char *) regs->pc)[i]))
goto bad;
printk("%02x ", c);
}
#else
i = 0;
#endif
}
printk("\n");
return;
#if 0
bad:
printk(KERN_EMERG " Bad PC value.");
break;
#endif
}
/*
*
*/
void show_trace_task(struct task_struct *tsk)
{
unsigned long sp = tsk->thread.sp;
/* User space on another CPU? */
if ((sp ^ (unsigned long) tsk) & (PAGE_MASK << 1))
return;
show_trace((unsigned long *) sp);
}
/*
* note the untimely death of part of the kernel
*/
void die(const char *str, struct pt_regs *regs, enum exception_code code)
{
console_verbose();
spin_lock_irq(&die_lock);
printk(KERN_EMERG "\n%s: %04x\n",
str, code & 0xffff);
show_registers(regs);
if (regs->pc >= 0x02000000 && regs->pc < 0x04000000 &&
(regs->epsw & (EPSW_IM | EPSW_IE)) != (EPSW_IM | EPSW_IE)) {
printk(KERN_EMERG "Exception in usermode interrupt handler\n");
printk(KERN_EMERG "\nPlease connect to kernel debugger !!\n");
asm volatile ("0: bra 0b");
}
spin_unlock_irq(&die_lock);
do_exit(SIGSEGV);
}
/*
* display the register file when the stack pointer gets clobbered
*/
asmlinkage void do_double_fault(struct pt_regs *regs)
{
struct task_struct *tsk = current;
strcpy(tsk->comm, "emergency tsk");
tsk->pid = 0;
console_verbose();
printk(KERN_EMERG "--- double fault ---\n");
show_registers(regs);
}
/*
* asynchronous bus error (external, usually I/O DMA)
*/
asmlinkage void io_bus_error(u32 bcberr, u32 bcbear, struct pt_regs *regs)
{
console_verbose();
printk(KERN_EMERG "Asynchronous I/O Bus Error\n");
printk(KERN_EMERG "==========================\n");
if (bcberr & BCBERR_BEME)
printk(KERN_EMERG "- Multiple recorded errors\n");
printk(KERN_EMERG "- Faulting Buses:%s%s%s\n",
bcberr & BCBERR_BEMR_CI ? " CPU-Ins-Fetch" : "",
bcberr & BCBERR_BEMR_CD ? " CPU-Data" : "",
bcberr & BCBERR_BEMR_DMA ? " DMA" : "");
printk(KERN_EMERG "- %s %s access made to %s at address %08x\n",
bcberr & BCBERR_BEBST ? "Burst" : "Single",
bcberr & BCBERR_BERW ? "Read" : "Write",
bcberr & BCBERR_BESB_MON ? "Monitor Space" :
bcberr & BCBERR_BESB_IO ? "Internal CPU I/O Space" :
bcberr & BCBERR_BESB_EX ? "External I/O Bus" :
bcberr & BCBERR_BESB_OPEX ? "External Memory Bus" :
"On Chip Memory",
bcbear
);
printk(KERN_EMERG "- Detected by the %s\n",
bcberr&BCBERR_BESD ? "Bus Control Unit" : "Slave Bus");
#ifdef CONFIG_PCI
#define BRIDGEREGB(X) (*(volatile __u8 *)(0xBE040000 + (X)))
#define BRIDGEREGW(X) (*(volatile __u16 *)(0xBE040000 + (X)))
#define BRIDGEREGL(X) (*(volatile __u32 *)(0xBE040000 + (X)))
printk(KERN_EMERG "- PCI Memory Paging Reg: %08x\n",
*(volatile __u32 *) (0xBFFFFFF4));
printk(KERN_EMERG "- PCI Bridge Base Address 0: %08x\n",
BRIDGEREGL(PCI_BASE_ADDRESS_0));
printk(KERN_EMERG "- PCI Bridge AMPCI Base Address: %08x\n",
BRIDGEREGL(0x48));
printk(KERN_EMERG "- PCI Bridge Command: %04hx\n",
BRIDGEREGW(PCI_COMMAND));
printk(KERN_EMERG "- PCI Bridge Status: %04hx\n",
BRIDGEREGW(PCI_STATUS));
printk(KERN_EMERG "- PCI Bridge Int Status: %08hx\n",
BRIDGEREGL(0x4c));
#endif
printk(KERN_EMERG "\n");
show_registers(regs);
panic("Halted due to asynchronous I/O Bus Error\n");
}
/*
* handle an exception for which a handler has not yet been installed
*/
asmlinkage void uninitialised_exception(struct pt_regs *regs,
enum exception_code code)
{
/* see if gdbstub wants to deal with it */
if (debugger_intercept(code, SIGSYS, 0, regs) == 0)
return;
peripheral_leds_display_exception(code);
printk(KERN_EMERG "Uninitialised Exception 0x%04x\n", code & 0xFFFF);
show_registers(regs);
for (;;)
continue;
}
/*
* set an interrupt stub to jump to a handler
* ! NOTE: this does *not* flush the caches
*/
void __init __set_intr_stub(enum exception_code code, void *handler)
{
unsigned long addr;
u8 *vector = (u8 *)(CONFIG_INTERRUPT_VECTOR_BASE + code);
addr = (unsigned long) handler - (unsigned long) vector;
vector[0] = 0xdc; /* JMP handler */
vector[1] = addr;
vector[2] = addr >> 8;
vector[3] = addr >> 16;
vector[4] = addr >> 24;
vector[5] = 0xcb;
vector[6] = 0xcb;
vector[7] = 0xcb;
}
/*
* set an interrupt stub to jump to a handler
*/
void __init set_intr_stub(enum exception_code code, void *handler)
{
unsigned long addr;
u8 *vector = (u8 *)(CONFIG_INTERRUPT_VECTOR_BASE + code);
unsigned long flags;
addr = (unsigned long) handler - (unsigned long) vector;
flags = arch_local_cli_save();
vector[0] = 0xdc; /* JMP handler */
vector[1] = addr;
vector[2] = addr >> 8;
vector[3] = addr >> 16;
vector[4] = addr >> 24;
vector[5] = 0xcb;
vector[6] = 0xcb;
vector[7] = 0xcb;
arch_local_irq_restore(flags);
#ifndef CONFIG_MN10300_CACHE_SNOOP
mn10300_dcache_flush_inv();
mn10300_icache_inv();
#endif
}
/*
* initialise the exception table
*/
void __init trap_init(void)
{
set_excp_vector(EXCEP_TRAP, handle_exception);
set_excp_vector(EXCEP_ISTEP, handle_exception);
set_excp_vector(EXCEP_IBREAK, handle_exception);
set_excp_vector(EXCEP_OBREAK, handle_exception);
set_excp_vector(EXCEP_PRIVINS, handle_exception);
set_excp_vector(EXCEP_UNIMPINS, handle_exception);
set_excp_vector(EXCEP_UNIMPEXINS, handle_exception);
set_excp_vector(EXCEP_MEMERR, handle_exception);
set_excp_vector(EXCEP_MISALIGN, misalignment);
set_excp_vector(EXCEP_BUSERROR, handle_exception);
set_excp_vector(EXCEP_ILLINSACC, handle_exception);
set_excp_vector(EXCEP_ILLDATACC, handle_exception);
set_excp_vector(EXCEP_IOINSACC, handle_exception);
set_excp_vector(EXCEP_PRIVINSACC, handle_exception);
set_excp_vector(EXCEP_PRIVDATACC, handle_exception);
set_excp_vector(EXCEP_DATINSACC, handle_exception);
set_excp_vector(EXCEP_FPU_UNIMPINS, handle_exception);
set_excp_vector(EXCEP_FPU_OPERATION, fpu_exception);
set_excp_vector(EXCEP_NMI, nmi);
set_excp_vector(EXCEP_SYSCALL1, handle_exception);
set_excp_vector(EXCEP_SYSCALL2, handle_exception);
set_excp_vector(EXCEP_SYSCALL3, handle_exception);
set_excp_vector(EXCEP_SYSCALL4, handle_exception);
set_excp_vector(EXCEP_SYSCALL5, handle_exception);
set_excp_vector(EXCEP_SYSCALL6, handle_exception);
set_excp_vector(EXCEP_SYSCALL7, handle_exception);
set_excp_vector(EXCEP_SYSCALL8, handle_exception);
set_excp_vector(EXCEP_SYSCALL9, handle_exception);
set_excp_vector(EXCEP_SYSCALL10, handle_exception);
set_excp_vector(EXCEP_SYSCALL11, handle_exception);
set_excp_vector(EXCEP_SYSCALL12, handle_exception);
set_excp_vector(EXCEP_SYSCALL13, handle_exception);
set_excp_vector(EXCEP_SYSCALL14, handle_exception);
set_excp_vector(EXCEP_SYSCALL15, handle_exception);
}
/*
* determine if a program counter value is a valid bug address
*/
int is_valid_bugaddr(unsigned long pc)
{
return pc >= PAGE_OFFSET;
}

View File

@@ -0,0 +1,93 @@
/* MN10300 Main kernel linker script
*
* Copyright (C) 2007 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 Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#define __VMLINUX_LDS__
#include <asm-generic/vmlinux.lds.h>
#include <asm/thread_info.h>
#include <asm/page.h>
OUTPUT_FORMAT("elf32-am33lin", "elf32-am33lin", "elf32-am33lin")
OUTPUT_ARCH(mn10300)
ENTRY(_start)
jiffies = jiffies_64;
#ifndef CONFIG_MN10300_CURRENT_IN_E2
current = __current;
#endif
SECTIONS
{
. = CONFIG_KERNEL_TEXT_ADDRESS;
/* read-only */
_stext = .;
_text = .; /* Text and read-only data */
.text : {
HEAD_TEXT
TEXT_TEXT
SCHED_TEXT
LOCK_TEXT
KPROBES_TEXT
*(.fixup)
*(.gnu.warning)
} = 0xcb
_etext = .; /* End of text section */
EXCEPTION_TABLE(16)
BUG_TABLE
RO_DATA(PAGE_SIZE)
/* writeable */
_sdata = .; /* Start of rw data section */
RW_DATA_SECTION(32, PAGE_SIZE, THREAD_SIZE)
_edata = .;
/* might get freed after init */
. = ALIGN(PAGE_SIZE);
.smp_locks : AT(ADDR(.smp_locks) - LOAD_OFFSET) {
__smp_locks = .;
*(.smp_locks)
__smp_locks_end = .;
}
/* will be freed after init */
. = ALIGN(PAGE_SIZE); /* Init code and data */
__init_begin = .;
INIT_TEXT_SECTION(PAGE_SIZE)
INIT_DATA_SECTION(16)
. = ALIGN(4);
__alt_instructions = .;
.altinstructions : { *(.altinstructions) }
__alt_instructions_end = .;
.altinstr_replacement : { *(.altinstr_replacement) }
/* .exit.text is discard at runtime, not link time, to deal with references
from .altinstructions and .eh_frame */
.exit.text : { EXIT_TEXT; }
.exit.data : { EXIT_DATA; }
PERCPU_SECTION(32)
. = ALIGN(PAGE_SIZE);
__init_end = .;
/* freed after init ends here */
BSS_SECTION(0, PAGE_SIZE, 4)
_end = . ;
/* This is where the kernel creates the early boot page tables */
. = ALIGN(PAGE_SIZE);
pg0 = .;
STABS_DEBUG
DWARF_DEBUG
/* Sections to be discarded */
DISCARDS
}