mn10300: add the MN10300/AM33 architecture to the kernel

Add architecture support for the MN10300/AM33 CPUs produced by MEI to the
kernel.

This patch also adds board support for the ASB2303 with the ASB2308 daughter
board, and the ASB2305.  The only processor supported is the MN103E010, which
is an AM33v2 core plus on-chip devices.

[akpm@linux-foundation.org: nuke cvs control strings]
Signed-off-by: Masakazu Urade <urade.masakazu@jp.panasonic.com>
Signed-off-by: Koichi Yasutake <yasutake.koichi@jp.panasonic.com>
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
David Howells 2008-02-08 04:19:31 -08:00 committed by Linus Torvalds
parent ef3d534754
commit b920de1b77
234 changed files with 27906 additions and 7 deletions

View File

@ -0,0 +1,149 @@
=========================
MN10300 FUNCTION CALL ABI
=========================
=======
GENERAL
=======
The MN10300/AM33 kernel runs in little-endian mode; big-endian mode is not
supported.
The stack grows downwards, and should always be 32-bit aligned. There are
separate stack pointer registers for userspace and the kernel.
================
ARGUMENT PASSING
================
The first two arguments (assuming up to 32-bits per argument) to a function are
passed in the D0 and D1 registers respectively; all other arguments are passed
on the stack.
If 64-bit arguments are being passed, then they are never split between
registers and the stack. If the first argument is a 64-bit value, it will be
passed in D0:D1. If the first argument is not a 64-bit value, but the second
is, the second will be passed entirely on the stack and D1 will be unused.
Arguments smaller than 32-bits are not coelesced within a register or a stack
word. For example, two byte-sized arguments will always be passed in separate
registers or word-sized stack slots.
=================
CALLING FUNCTIONS
=================
The caller must allocate twelve bytes on the stack for the callee's use before
it inserts a CALL instruction. The CALL instruction will write into the TOS
word, but won't actually modify the stack pointer; similarly, the RET
instruction reads from the TOS word of the stack, but doesn't move the stack
pointer beyond it.
Stack:
| |
| |
|---------------| SP+20
| 4th Arg |
|---------------| SP+16
| 3rd Arg |
|---------------| SP+12
| D1 Save Slot |
|---------------| SP+8
| D0 Save Slot |
|---------------| SP+4
| Return Addr |
|---------------| SP
| |
| |
The caller must leave space on the stack (hence an allocation of twelve bytes)
in which the callee may store the first two arguments.
============
RETURN VALUE
============
The return value is passed in D0 for an integer (or D0:D1 for a 64-bit value),
or A0 for a pointer.
If the return value is a value larger than 64-bits, or is a structure or an
array, then a hidden first argument will be passed to the callee by the caller:
this will point to a piece of memory large enough to hold the result of the
function. In this case, the callee will return the value in that piece of
memory, and no value will be returned in D0 or A0.
===================
REGISTER CLOBBERING
===================
The values in certain registers may be clobbered by the callee, and other
values must be saved:
Clobber: D0-D1, A0-A1, E0-E3
Save: D2-D3, A2-A3, E4-E7, SP
All other non-supervisor-only registers are clobberable (such as MDR, MCRL,
MCRH).
=================
SPECIAL REGISTERS
=================
Certain ordinary registers may carry special usage for the compiler:
A3: Frame pointer
E2: TLS pointer
==========
KERNEL ABI
==========
The kernel may use a slightly different ABI internally.
(*) E2
If CONFIG_MN10300_CURRENT_IN_E2 is defined, then the current task pointer
will be kept in the E2 register, and that register will be marked
unavailable for the compiler to use as a scratch register.
Normally the kernel uses something like:
MOV SP,An
AND 0xFFFFE000,An
MOV (An),Rm // Rm holds current
MOV (yyy,Rm) // Access current->yyy
To find the address of current; but since this option permits current to
be carried globally in an register, it can use:
MOV (yyy,E2) // Access current->yyy
instead.
===============
SYSTEM CALL ABI
===============
System calls are called with the following convention:
REGISTER ENTRY EXIT
=============== ======================= =======================
D0 Syscall number Return value
A0 1st syscall argument Saved
D1 2nd syscall argument Saved
A3 3rd syscall argument Saved
A2 4th syscall argument Saved
D3 5th syscall argument Saved
D2 6th syscall argument Saved
All other registers are saved. The layout is a consequence of the way the MOVM
instruction stores registers onto the stack.

View File

@ -0,0 +1,60 @@
=========================================
PART-SPECIFIC SOURCE COMPARTMENTALISATION
=========================================
The sources for various parts are compartmentalised at two different levels:
(1) Processor level
The "processor level" is a CPU core plus the other on-silicon
peripherals.
Processor-specific header files are divided among directories in a similar
way to the CPU level:
(*) include/asm-mn10300/proc-mn103e010/
Support for the AM33v2 CPU core.
The appropriate processor is selected by a CONFIG_MN10300_PROC_YYYY option
from the "Processor support" choice menu in the arch/mn10300/Kconfig file.
(2) Unit level
The "unit level" is a processor plus all the external peripherals
controlled by that processor.
Unit-specific header files are divided among directories in a similar way
to the CPU level; not only that, but specific sources may also be
segregated into separate directories under the arch directory:
(*) include/asm-mn10300/unit-asb2303/
(*) arch/mn10300/unit-asb2303/
Support for the ASB2303 board with an ASB2308 daughter board.
(*) include/asm-mn10300/unit-asb2305/
(*) arch/mn10300/unit-asb2305/
Support for the ASB2305 board.
The appropriate processor is selected by a CONFIG_MN10300_UNIT_ZZZZ option
from the "Unit type" choice menu in the arch/mn10300/Kconfig file.
============
COMPILE TIME
============
When the kernel is compiled, symbolic links will be made in the asm header file
directory for this arch:
include/asm-mn10300/proc => include/asm-mn10300/proc-YYYY/
include/asm-mn10300/unit => include/asm-mn10300/unit-ZZZZ/
So that the header files contained in those directories can be accessed without
lots of #ifdef-age.
The appropriate arch/mn10300/unit-ZZZZ directory will also be entered by the
compilation process; all other unit-specific directories will be ignored.

View File

@ -2614,6 +2614,15 @@ L: linux-kernel@vger.kernel.org
W: http://www.linux-mm.org W: http://www.linux-mm.org
S: Maintained S: Maintained
MEI MN10300/AM33 PORT
P: David Howells
M: dhowells@redhat.com
P: Koichi Yasutake
M: yasutake.koichi@jp.panasonic.com
L: linux-am33-list@redhat.com
W: ftp://ftp.redhat.com/pub/redhat/gnupro/AM33/
S: Maintained
MEMORY TECHNOLOGY DEVICES (MTD) MEMORY TECHNOLOGY DEVICES (MTD)
P: David Woodhouse P: David Woodhouse
M: dwmw2@infradead.org M: dwmw2@infradead.org

381
arch/mn10300/Kconfig Normal file
View File

@ -0,0 +1,381 @@
#
# For a description of the syntax of this configuration file,
# see Documentation/kbuild/kconfig-language.txt.
#
mainmenu "Linux Kernel Configuration"
config MN10300
def_bool y
config AM33
def_bool y
config MMU
def_bool y
config HIGHMEM
def_bool n
config NUMA
def_bool n
config UID16
def_bool y
config RWSEM_GENERIC_SPINLOCK
def_bool y
config RWSEM_XCHGADD_ALGORITHM
bool
config GENERIC_HARDIRQS_NO__DO_IRQ
def_bool y
config GENERIC_CALIBRATE_DELAY
def_bool y
config GENERIC_FIND_NEXT_BIT
def_bool y
config GENERIC_HWEIGHT
def_bool y
config GENERIC_TIME
def_bool y
config GENERIC_BUG
def_bool y
config QUICKLIST
def_bool y
config ARCH_HAS_ILOG2_U32
def_bool y
config ARCH_SUPPORTS_AOUT
def_bool n
# Use the generic interrupt handling code in kernel/irq/
config GENERIC_HARDIRQS
def_bool y
config HOTPLUG_CPU
def_bool n
mainmenu "Matsushita MN10300/AM33 Kernel Configuration"
source "init/Kconfig"
menu "Matsushita MN10300 system setup"
choice
prompt "Unit type"
default MN10300_UNIT_ASB2303
help
This option specifies board for which the kernel will be
compiled. It affects the external peripherals catered for.
config MN10300_UNIT_ASB2303
bool "ASB2303"
config MN10300_UNIT_ASB2305
bool "ASB2305"
endchoice
choice
prompt "Processor support"
default MN10300_PROC_MN103E010
help
This option specifies the processor for which the kernel will be
compiled. It affects the on-chip peripherals catered for.
config MN10300_PROC_MN103E010
bool "MN103E010"
depends on MN10300_UNIT_ASB2303 || MN10300_UNIT_ASB2305
select MN10300_PROC_HAS_TTYSM0
select MN10300_PROC_HAS_TTYSM1
select MN10300_PROC_HAS_TTYSM2
endchoice
choice
prompt "Processor core support"
default MN10300_CPU_AM33V2
help
This option specifies the processor core for which the kernel will be
compiled. It affects the instruction set used.
config MN10300_CPU_AM33V2
bool "AM33v2"
endchoice
config FPU
bool "FPU present"
default y
depends on MN10300_PROC_MN103E010
choice
prompt "CPU Caching mode"
default MN10300_CACHE_WBACK
help
This option determines the caching mode for the kernel.
Write-Back caching mode involves the all reads and writes causing
the affected cacheline to be read into the cache first before being
operated upon. Memory is not then updated by a write until the cache
is filled and a cacheline needs to be displaced from the cache to
make room. Only at that point is it written back.
Write-Through caching only fetches cachelines from memory on a
read. Writes always get written directly to memory. If the affected
cacheline is also in cache, it will be updated too.
The final option is to turn of caching entirely.
config MN10300_CACHE_WBACK
bool "Write-Back"
config MN10300_CACHE_WTHRU
bool "Write-Through"
config MN10300_CACHE_DISABLED
bool "Disabled"
endchoice
menu "Memory layout options"
config KERNEL_RAM_BASE_ADDRESS
hex "Base address of kernel RAM"
default "0x90000000"
config INTERRUPT_VECTOR_BASE
hex "Base address of vector table"
default "0x90000000"
help
The base address of the vector table will be programmed into
the TBR register. It must be on 16MiB address boundary.
config KERNEL_TEXT_ADDRESS
hex "Base address of kernel"
default "0x90001000"
config KERNEL_ZIMAGE_BASE_ADDRESS
hex "Base address of compressed vmlinux image"
default "0x90700000"
endmenu
config PREEMPT
bool "Preemptible Kernel"
help
This option reduces the latency of the kernel when reacting to
real-time or interactive events by allowing a low priority process to
be preempted even if it is in kernel mode executing a system call.
This allows applications to run more reliably even when the system is
under load.
Say Y here if you are building a kernel for a desktop, embedded
or real-time system. Say N if you are unsure.
config PREEMPT_BKL
bool "Preempt The Big Kernel Lock"
depends on PREEMPT
default y
help
This option reduces the latency of the kernel by making the
big kernel lock preemptible.
Say Y here if you are building a kernel for a desktop system.
Say N if you are unsure.
config MN10300_CURRENT_IN_E2
bool "Hold current task address in E2 register"
default y
help
This option removes the E2/R2 register from the set available to gcc
for normal use and instead uses it to store the address of the
current process's task_struct whilst in the kernel.
This means the kernel doesn't need to calculate the address each time
"current" is used (take SP, AND with mask and dereference pointer
just to get the address), and instead can just use E2+offset
addressing each time.
This has no effect on userspace.
config MN10300_USING_JTAG
bool "Using JTAG to debug kernel"
default y
help
This options indicates that JTAG will be used to debug the kernel. It
suppresses the use of certain hardware debugging features, such as
single-stepping, which are taken over completely by the JTAG unit.
config MN10300_RTC
bool "Using MN10300 RTC"
depends on MN10300_PROC_MN103E010
default n
help
This option enables support for the RTC, thus enabling time to be
tracked, even when system is powered down. This is available on-chip
on the MN103E010.
config MN10300_WD_TIMER
bool "Using MN10300 watchdog timer"
default y
help
This options indicates that the watchdog timer will be used.
config PCI
bool "Use PCI"
depends on MN10300_UNIT_ASB2305
default y
help
Some systems (such as the ASB2305) have PCI onboard. If you have one
of these boards and you wish to use the PCI facilities, say Y here.
The PCI-HOWTO, available from
<http://www.tldp.org/docs.html#howto>, contains valuable
information about which PCI hardware does work under Linux and which
doesn't.
source "drivers/pci/Kconfig"
source "drivers/pcmcia/Kconfig"
menu "MN10300 internal serial options"
config MN10300_PROC_HAS_TTYSM0
bool
default n
config MN10300_PROC_HAS_TTYSM1
bool
default n
config MN10300_PROC_HAS_TTYSM2
bool
default n
config MN10300_TTYSM
bool "Support for ttySM serial ports"
depends on MN10300
default y
select SERIAL_CORE
help
This option enables support for the on-chip serial ports that the
MN10300 has available.
config MN10300_TTYSM_CONSOLE
bool "Support for console on ttySM serial ports"
depends on MN10300_TTYSM
select SERIAL_CORE_CONSOLE
help
This option enables support for a console on the on-chip serial ports
that the MN10300 has available.
#
# /dev/ttySM0
#
config MN10300_TTYSM0
bool "Enable SIF0 (/dev/ttySM0)"
depends on MN10300_TTYSM && MN10300_PROC_HAS_TTYSM0
help
Enable access to SIF0 through /dev/ttySM0 or gdb-stub
choice
prompt "Select the timer to supply the clock for SIF0"
default MN10300_TTYSM0_TIMER8
depends on MN10300_TTYSM0
config MN10300_TTYSM0_TIMER8
bool "Use timer 8 (16-bit)"
config MN10300_TTYSM0_TIMER2
bool "Use timer 2 (8-bit)"
endchoice
#
# /dev/ttySM1
#
config MN10300_TTYSM1
bool "Enable SIF1 (/dev/ttySM1)"
depends on MN10300_TTYSM && MN10300_PROC_HAS_TTYSM1
help
Enable access to SIF1 through /dev/ttySM1 or gdb-stub
choice
prompt "Select the timer to supply the clock for SIF1"
default MN10300_TTYSM0_TIMER9
depends on MN10300_TTYSM1
config MN10300_TTYSM1_TIMER9
bool "Use timer 9 (16-bit)"
config MN10300_TTYSM1_TIMER3
bool "Use timer 3 (8-bit)"
endchoice
#
# /dev/ttySM2
#
config MN10300_TTYSM2
bool "Enable SIF2 (/dev/ttySM2)"
depends on MN10300_TTYSM && MN10300_PROC_HAS_TTYSM2
help
Enable access to SIF2 through /dev/ttySM2 or gdb-stub
choice
prompt "Select the timer to supply the clock for SIF2"
default MN10300_TTYSM0_TIMER10
depends on MN10300_TTYSM2
config MN10300_TTYSM2_TIMER10
bool "Use timer 10 (16-bit)"
endchoice
config MN10300_TTYSM2_CTS
bool "Enable the use of the CTS line /dev/ttySM2"
depends on MN10300_TTYSM2
endmenu
source "mm/Kconfig"
menu "Power management options"
source kernel/power/Kconfig
endmenu
endmenu
menu "Executable formats"
source "fs/Kconfig.binfmt"
endmenu
source "net/Kconfig"
source "drivers/Kconfig"
source "fs/Kconfig"
source "arch/mn10300/Kconfig.debug"
source "security/Kconfig"
source "crypto/Kconfig"
source "lib/Kconfig"
source "arch/mn10300/oprofile/Kconfig"

135
arch/mn10300/Kconfig.debug Normal file
View File

@ -0,0 +1,135 @@
menu "Kernel hacking"
source "lib/Kconfig.debug"
config DEBUG_STACKOVERFLOW
bool "Check for stack overflows"
depends on DEBUG_KERNEL
config DEBUG_DECOMPRESS_KERNEL
bool "Using serial port during decompressing kernel"
depends on DEBUG_KERNEL
default n
help
If you say Y here you will confirm the start and the end of
decompressing Linux seeing "Uncompressing Linux... " and
"Ok, booting the kernel.\n" on console.
config KPROBES
bool "Kprobes"
depends on DEBUG_KERNEL
help
Kprobes allows you to trap at almost any kernel address and
execute a callback function. register_kprobe() establishes
a probepoint and specifies the callback. Kprobes is useful
for kernel debugging, non-intrusive instrumentation and testing.
If in doubt, say "N".
config GDBSTUB
bool "Remote GDB kernel debugging"
depends on DEBUG_KERNEL
select DEBUG_INFO
select FRAME_POINTER
help
If you say Y here, it will be possible to remotely debug the kernel
using gdb. This enlarges your kernel ELF image disk size by several
megabytes and requires a machine with more than 16 MB, better 32 MB
RAM to avoid excessive linking time. This is only useful for kernel
hackers. If unsure, say N.
config GDBSTUB_IMMEDIATE
bool "Break into GDB stub immediately"
depends on GDBSTUB
help
If you say Y here, GDB stub will break into the program as soon as
possible, leaving the program counter at the beginning of
start_kernel() in init/main.c.
config GDB_CONSOLE
bool "Console output to GDB"
depends on GDBSTUB
help
If you are using GDB for remote debugging over a serial port and
would like kernel messages to be formatted into GDB $O packets so
that GDB prints them as program output, say 'Y'.
config GDBSTUB_DEBUGGING
bool "Debug GDB stub by messages to serial port"
depends on GDBSTUB
help
This causes debugging messages to be displayed at various points
during execution of the GDB stub routines. Such messages will be
displayed on ttyS0 if that isn't the GDB stub's port, or ttySM0
otherwise.
config GDBSTUB_DEBUG_ENTRY
bool "Debug GDB stub entry"
depends on GDBSTUB_DEBUGGING
help
This option causes information to be displayed about entry to or exit
from the main GDB stub routine.
config GDBSTUB_DEBUG_PROTOCOL
bool "Debug GDB stub protocol"
depends on GDBSTUB_DEBUGGING
help
This option causes information to be displayed about the GDB remote
protocol messages generated exchanged with GDB.
config GDBSTUB_DEBUG_IO
bool "Debug GDB stub I/O"
depends on GDBSTUB_DEBUGGING
help
This option causes information to be displayed about GDB stub's
low-level I/O.
config GDBSTUB_DEBUG_BREAKPOINT
bool "Debug GDB stub breakpoint management"
depends on GDBSTUB_DEBUGGING
help
This option causes information to be displayed about GDB stub's
breakpoint management.
choice
prompt "GDB stub port"
default GDBSTUB_TTYSM0
depends on GDBSTUB
help
Select the serial port used for GDB-stub.
config GDBSTUB_ON_TTYSM0
bool "/dev/ttySM0 [SIF0]"
depends on MN10300_TTYSM0
select GDBSTUB_ON_TTYSMx
config GDBSTUB_ON_TTYSM1
bool "/dev/ttySM1 [SIF1]"
depends on MN10300_TTYSM1
select GDBSTUB_ON_TTYSMx
config GDBSTUB_ON_TTYSM2
bool "/dev/ttySM2 [SIF2]"
depends on MN10300_TTYSM2
select GDBSTUB_ON_TTYSMx
config GDBSTUB_ON_TTYS0
bool "/dev/ttyS0"
select GDBSTUB_ON_TTYSx
config GDBSTUB_ON_TTYS1
bool "/dev/ttyS1"
select GDBSTUB_ON_TTYSx
endchoice
config GDBSTUB_ON_TTYSMx
bool
depends on GDBSTUB_ON_TTYSM0 || GDBSTUB_ON_TTYSM1 || GDBSTUB_ON_TTYSM2
default y
config GDBSTUB_ON_TTYSx
bool
depends on GDBSTUB_ON_TTYS0 || GDBSTUB_ON_TTYS1
default y
endmenu

135
arch/mn10300/Makefile Normal file
View File

@ -0,0 +1,135 @@
###############################################################################
#
# MN10300 Kernel makefile system specifications
#
# 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.
#
###############################################################################
KBUILD_DEFCONFIG := asb2303_defconfig
CCSPECS := $(shell $(CC) -v 2>&1 | grep "^Reading specs from " | head -1 | cut -c20-)
CCDIR := $(strip $(patsubst %/specs,%,$(CCSPECS)))
KBUILD_CPPFLAGS += -nostdinc -I$(CCDIR)/include
LDFLAGS :=
OBJCOPYFLAGS := -O binary -R .note -R .comment -S
#LDFLAGS_vmlinux := -Map linkmap.txt
CHECKFLAGS +=
PROCESSOR := unset
UNIT := unset
KBUILD_CFLAGS += -mam33 -mmem-funcs -DCPU=AM33
KBUILD_AFLAGS += -mam33 -DCPU=AM33
ifeq ($(CONFIG_MN10300_CURRENT_IN_E2),y)
KBUILD_CFLAGS += -ffixed-e2 -fcall-saved-e5
endif
ifeq ($(CONFIG_MN10300_PROC_MN103E010),y)
PROCESSOR := mn103e010
endif
ifeq ($(CONFIG_MN10300_UNIT_ASB2303),y)
UNIT := asb2303
endif
ifeq ($(CONFIG_MN10300_UNIT_ASB2305),y)
UNIT := asb2305
endif
head-y := arch/mn10300/kernel/head.o arch/mn10300/kernel/init_task.o
core-y += arch/mn10300/kernel/ arch/mn10300/mm/
ifneq ($(PROCESSOR),unset)
core-y += arch/mn10300/proc-$(PROCESSOR)/
endif
ifneq ($(UNIT),unset)
core-y += arch/mn10300/unit-$(UNIT)/
endif
libs-y += arch/mn10300/lib/
drivers-$(CONFIG_OPROFILE) += arch/mn10300/oprofile/
boot := arch/mn10300/boot
.PHONY: zImage
KBUILD_IMAGE := $(boot)/zImage
CLEAN_FILES += $(boot)/zImage
CLEAN_FILES += $(boot)/compressed/vmlinux
CLEAN_FILES += $(boot)/compressed/vmlinux.bin
CLEAN_FILES += $(boot)/compressed/vmlinux.bin.gz
zImage: vmlinux
$(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
all: zImage
bootstrap:
$(Q)$(MAKEBOOT) bootstrap
archclean:
$(Q)$(MAKE) $(clean)=arch/mn10300/proc-mn103e010
$(Q)$(MAKE) $(clean)=arch/mn10300/unit-asb2303
$(Q)$(MAKE) $(clean)=arch/mn10300/unit-asb2305
define archhelp
echo '* zImage - Compressed kernel image (arch/$(ARCH)/boot/zImage)'
endef
# If you make sure the .S files get compiled with debug info,
# uncomment the following to disable optimisations
# that are unhelpful whilst debugging.
ifdef CONFIG_DEBUG_INFO
#KBUILD_CFLAGS += -O1
KBUILD_AFLAGS += -Wa,--gdwarf2
endif
###################################################################################################
#
# juggle some symlinks in the MN10300 asm include dir
#
# Update machine proc and unit symlinks if something which affects
# them changed. We use .proc / .unit to indicate when they were
# updated last, otherwise make uses the target directory mtime.
#
###################################################################################################
# processor specific definitions
include/asm-mn10300/.proc: $(wildcard include/config/proc/*.h) include/config/auto.conf
@echo ' SYMLINK include/asm-mn10300/proc -> include/asm-mn10300/proc-$(PROCESSOR)'
ifneq ($(KBUILD_SRC),)
$(Q)mkdir -p include/asm-mn10300
$(Q)ln -fsn $(srctree)/include/asm-mn10300/proc-$(PROCESSOR) include/asm-mn10300/proc
else
$(Q)ln -fsn proc-$(PROCESSOR) include/asm-mn10300/proc
endif
@touch $@
CLEAN_FILES += include/asm-mn10300/proc include/asm-mn10300/.proc
prepare: include/asm-mn10300/.proc
# unit specific definitions
include/asm-mn10300/.unit: $(wildcard include/config/unit/*.h) include/config/auto.conf
@echo ' SYMLINK include/asm-mn10300/unit -> include/asm-mn10300/unit-$(UNIT)'
ifneq ($(KBUILD_SRC),)
$(Q)mkdir -p include/asm-mn10300
$(Q)ln -fsn $(srctree)/include/asm-mn10300/unit-$(UNIT) include/asm-mn10300/unit
else
$(Q)ln -fsn unit-$(UNIT) include/asm-mn10300/unit
endif
@touch $@
CLEAN_FILES += include/asm-mn10300/unit include/asm-mn10300/.unit
prepare: include/asm-mn10300/.unit

1
arch/mn10300/boot/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
zImage

View File

@ -0,0 +1,28 @@
# MN10300 kernel compressor and wrapper
#
# Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd.
# 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.
#
targets := vmlinux.bin zImage
subdir- := compressed
# ---------------------------------------------------------------------------
$(obj)/zImage: $(obj)/compressed/vmlinux FORCE
$(call if_changed,objcopy)
@echo 'Kernel: $@ is ready'
$(obj)/vmlinux.bin: $(obj)/compressed/vmlinux FORCE
$(call if_changed,objcopy)
$(obj)/compressed/vmlinux: FORCE
$(Q)$(MAKE) $(build)=$(obj)/compressed IMAGE_OFFSET=$(IMAGE_OFFSET) $@

View File

@ -0,0 +1,22 @@
#
# Create a compressed vmlinux image from the original vmlinux
#
targets := vmlinux vmlinux.bin vmlinux.bin.gz head.o misc.o piggy.o
LDFLAGS_vmlinux := -Ttext $(CONFIG_KERNEL_ZIMAGE_BASE_ADDRESS) -e startup_32
$(obj)/vmlinux: $(obj)/head.o $(obj)/misc.o $(obj)/piggy.o FORCE
$(call if_changed,ld)
@:
$(obj)/vmlinux.bin: vmlinux FORCE
$(call if_changed,objcopy)
$(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin FORCE
$(call if_changed,gzip)
LDFLAGS_piggy.o := -r --format binary --oformat elf32-am33lin -T
$(obj)/piggy.o: $(obj)/vmlinux.lds $(obj)/vmlinux.bin.gz FORCE
$(call if_changed,ld)

View File

@ -0,0 +1,86 @@
/* Boot entry point for a compressed MN10300 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.
*/
.section .text
#define DEBUG
#include <linux/linkage.h>
#include <asm/cpu-regs.h>
.globl startup_32
startup_32:
# first save off parameters from bootloader
mov param_save_area,a0
mov d0,(a0)
mov d1,(4,a0)
mov d2,(8,a0)
mov sp,a3
mov decomp_stack+0x2000-4,a0
mov a0,sp
# invalidate and enable both of the caches
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
mov CHCTR_ICEN|CHCTR_DCEN|CHCTR_DCWTMD,d0 # writethru dcache
movhu d0,(a0) # enable
# clear the BSS area
mov __bss_start,a0
mov _end,a1
clr d0
bssclear:
cmp a1,a0
bge bssclear_end
movbu d0,(a0)
inc a0
bra bssclear
bssclear_end:
# decompress the kernel
call decompress_kernel[],0
# disable caches again
mov CHCTR,a0
clr d0
movhu d0,(a0)
setlb
mov (a0),d0
btst CHCTR_ICBUSY|CHCTR_DCBUSY,d0 # wait till not busy
lne
mov param_save_area,a0
mov (a0),d0
mov (4,a0),d1
mov (8,a0),d2
mov a3,sp
mov CONFIG_KERNEL_TEXT_ADDRESS,a0
jmp (a0)
.data
.align 4
param_save_area:
.rept 3
.word 0
.endr
.section .bss
.align 4
decomp_stack:
.space 0x2000

View File

@ -0,0 +1,429 @@
/* MN10300 Miscellaneous helper routines for kernel decompressor
*
* Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd.
* Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
* Modified by David Howells (dhowells@redhat.com)
* - Derived from arch/x86/boot/compressed/misc_32.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/compiler.h>
#include <asm/serial-regs.h>
#include "misc.h"
#ifndef CONFIG_GDBSTUB_ON_TTYSx
/* display 'Uncompressing Linux... ' messages on ttyS0 or ttyS1 */
#if 1 /* ttyS0 */
#define CYG_DEV_BASE 0xA6FB0000
#else /* ttyS1 */
#define CYG_DEV_BASE 0xA6FC0000
#endif
#define CYG_DEV_THR (*((volatile __u8*)(CYG_DEV_BASE + 0x00)))
#define CYG_DEV_MCR (*((volatile __u8*)(CYG_DEV_BASE + 0x10)))
#define SIO_MCR_DTR 0x01
#define SIO_MCR_RTS 0x02
#define CYG_DEV_LSR (*((volatile __u8*)(CYG_DEV_BASE + 0x14)))
#define SIO_LSR_THRE 0x20 /* transmitter holding register empty */
#define SIO_LSR_TEMT 0x40 /* transmitter register empty */
#define CYG_DEV_MSR (*((volatile __u8*)(CYG_DEV_BASE + 0x18)))
#define SIO_MSR_CTS 0x10 /* clear to send */
#define SIO_MSR_DSR 0x20 /* data set ready */
#define LSR_WAIT_FOR(STATE) \
do { while (!(CYG_DEV_LSR & SIO_LSR_##STATE)) {} } while (0)
#define FLOWCTL_QUERY(LINE) \
({ CYG_DEV_MSR & SIO_MSR_##LINE; })
#define FLOWCTL_WAIT_FOR(LINE) \
do { while (!(CYG_DEV_MSR & SIO_MSR_##LINE)) {} } while (0)
#define FLOWCTL_CLEAR(LINE) \
do { CYG_DEV_MCR &= ~SIO_MCR_##LINE; } while (0)
#define FLOWCTL_SET(LINE) \
do { CYG_DEV_MCR |= SIO_MCR_##LINE; } while (0)
#endif
/*
* gzip declarations
*/
#define OF(args) args
#define STATIC static
#undef memset
#undef memcpy
static inline void *memset(const void *s, int c, size_t n)
{
int i;
char *ss = (char *) s;
for (i = 0; i < n; i++)
ss[i] = c;
return (void *)s;
}
#define memzero(s, n) memset((s), 0, (n))
static inline void *memcpy(void *__dest, const void *__src, size_t __n)
{
int i;
const char *s = __src;
char *d = __dest;
for (i = 0; i < __n; i++)
d[i] = s[i];
return __dest;
}
typedef unsigned char uch;
typedef unsigned short ush;
typedef unsigned long ulg;
#define WSIZE 0x8000 /* Window size must be at least 32k, and a power of
* two */
static uch *inbuf; /* input buffer */
static uch window[WSIZE]; /* sliding window buffer */
static unsigned insize; /* valid bytes in inbuf */
static unsigned inptr; /* index of next byte to be processed in inbuf */
static unsigned outcnt; /* bytes in output buffer */
/* gzip flag byte */
#define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */
#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
#define COMMENT 0x10 /* bit 4 set: file comment present */
#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */
#define RESERVED 0xC0 /* bit 6,7: reserved */
/* Diagnostic functions */
#ifdef DEBUG
# define Assert(cond, msg) { if (!(cond)) error(msg); }
# define Trace(x) fprintf x
# define Tracev(x) { if (verbose) fprintf x ; }
# define Tracevv(x) { if (verbose > 1) fprintf x ; }
# define Tracec(c, x) { if (verbose && (c)) fprintf x ; }
# define Tracecv(c, x) { if (verbose > 1 && (c)) fprintf x ; }
#else
# define Assert(cond, msg)
# define Trace(x)
# define Tracev(x)
# define Tracevv(x)
# define Tracec(c, x)
# define Tracecv(c, x)
#endif
static int fill_inbuf(void);
static void flush_window(void);
static void error(const char *) __attribute__((noreturn));
static void kputs(const char *);
static inline unsigned char get_byte(void)
{
unsigned char ch = inptr < insize ? inbuf[inptr++] : fill_inbuf();
#if 0
char hex[3];
hex[0] = ((ch & 0x0f) > 9) ?
((ch & 0x0f) + 'A' - 0xa) : ((ch & 0x0f) + '0');
hex[1] = ((ch >> 4) > 9) ?
((ch >> 4) + 'A' - 0xa) : ((ch >> 4) + '0');
hex[2] = 0;
kputs(hex);
#endif
return ch;
}
/*
* This is set up by the setup-routine at boot-time
*/
#define EXT_MEM_K (*(unsigned short *)0x90002)
#ifndef STANDARD_MEMORY_BIOS_CALL
#define ALT_MEM_K (*(unsigned long *) 0x901e0)
#endif
#define SCREEN_INFO (*(struct screen_info *)0x90000)
static long bytes_out;
static uch *output_data;
static unsigned long output_ptr;
static void *malloc(int size);
static inline void free(void *where)
{ /* Don't care */
}
static unsigned long free_mem_ptr = (unsigned long) &end;
static unsigned long free_mem_end_ptr = (unsigned long) &end + 0x90000;
static inline void gzip_mark(void **ptr)
{
kputs(".");
*ptr = (void *) free_mem_ptr;
}
static inline void gzip_release(void **ptr)
{
free_mem_ptr = (unsigned long) *ptr;
}
#define INPLACE_MOVE_ROUTINE 0x1000
#define LOW_BUFFER_START 0x2000
#define LOW_BUFFER_END 0x90000
#define LOW_BUFFER_SIZE (LOW_BUFFER_END - LOW_BUFFER_START)
#define HEAP_SIZE 0x3000
static int high_loaded;
static uch *high_buffer_start /* = (uch *)(((ulg)&end) + HEAP_SIZE)*/;
static char *vidmem = (char *)0xb8000;
static int lines, cols;
#include "../../../../lib/inflate.c"
static void *malloc(int size)
{
void *p;
if (size < 0)
error("Malloc error\n");
if (!free_mem_ptr)
error("Memory error\n");
free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */
p = (void *) free_mem_ptr;
free_mem_ptr += size;
if (free_mem_ptr >= free_mem_end_ptr)
error("\nOut of memory\n");
return p;
}
static inline void scroll(void)
{
int i;
memcpy(vidmem, vidmem + cols * 2, (lines - 1) * cols * 2);
for (i = (lines - 1) * cols * 2; i < lines * cols * 2; i += 2)
vidmem[i] = ' ';
}
static inline void kputchar(unsigned char ch)
{
#ifdef CONFIG_MN10300_UNIT_ASB2305
while (SC0STR & SC01STR_TBF)
continue;
if (ch == 0x0a) {
SC0TXB = 0x0d;
while (SC0STR & SC01STR_TBF)
continue;
}
SC0TXB = ch;
#else
while (SC1STR & SC01STR_TBF)
continue;
if (ch == 0x0a) {
SC1TXB = 0x0d;
while (SC1STR & SC01STR_TBF)
continue;
}
SC1TXB = ch;
#endif
}
static void kputs(const char *s)
{
#ifdef CONFIG_DEBUG_DECOMPRESS_KERNEL
#ifndef CONFIG_GDBSTUB_ON_TTYSx
char ch;
FLOWCTL_SET(DTR);
while (*s) {
LSR_WAIT_FOR(THRE);
ch = *s++;
if (ch == 0x0a) {
CYG_DEV_THR = 0x0d;
LSR_WAIT_FOR(THRE);
}
CYG_DEV_THR = ch;
}
FLOWCTL_CLEAR(DTR);
#else
for (; *s; s++)
kputchar(*s);
#endif
#endif /* CONFIG_DEBUG_DECOMPRESS_KERNEL */
}
/* ===========================================================================
* Fill the input buffer. This is called only when the buffer is empty
* and at least one byte is really needed.
*/
static int fill_inbuf()
{
if (insize != 0)
error("ran out of input data\n");
inbuf = input_data;
insize = input_len;
inptr = 1;
return inbuf[0];
}
/* ===========================================================================
* Write the output window window[0..outcnt-1] and update crc and bytes_out.
* (Used for the decompressed data only.)
*/
static void flush_window_low(void)
{
ulg c = crc; /* temporary variable */
unsigned n;
uch *in, *out, ch;
in = window;
out = &output_data[output_ptr];
for (n = 0; n < outcnt; n++) {
ch = *out++ = *in++;
c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8);
}
crc = c;
bytes_out += (ulg)outcnt;
output_ptr += (ulg)outcnt;
outcnt = 0;
}
static void flush_window_high(void)
{
ulg c = crc; /* temporary variable */
unsigned n;
uch *in, ch;
in = window;
for (n = 0; n < outcnt; n++) {
ch = *output_data++ = *in++;
if ((ulg) output_data == LOW_BUFFER_END)
output_data = high_buffer_start;
c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8);
}
crc = c;
bytes_out += (ulg)outcnt;
outcnt = 0;
}
static void flush_window(void)
{
if (high_loaded)
flush_window_high();
else
flush_window_low();
}
static void error(const char *x)
{
kputs("\n\n");
kputs(x);
kputs("\n\n -- System halted");
while (1)
/* Halt */;
}
#define STACK_SIZE (4096)
long user_stack[STACK_SIZE];
struct {
long *a;
short b;
} stack_start = { &user_stack[STACK_SIZE], 0 };
void setup_normal_output_buffer(void)
{
#ifdef STANDARD_MEMORY_BIOS_CALL
if (EXT_MEM_K < 1024)
error("Less than 2MB of memory.\n");
#else
if ((ALT_MEM_K > EXT_MEM_K ? ALT_MEM_K : EXT_MEM_K) < 1024)
error("Less than 2MB of memory.\n");
#endif
output_data = (char *) 0x100000; /* Points to 1M */
}
struct moveparams {
uch *low_buffer_start;
int lcount;
uch *high_buffer_start;
int hcount;
};
void setup_output_buffer_if_we_run_high(struct moveparams *mv)
{
high_buffer_start = (uch *)(((ulg) &end) + HEAP_SIZE);
#ifdef STANDARD_MEMORY_BIOS_CALL
if (EXT_MEM_K < (3 * 1024))
error("Less than 4MB of memory.\n");
#else
if ((ALT_MEM_K > EXT_MEM_K ? ALT_MEM_K : EXT_MEM_K) < (3 * 1024))
error("Less than 4MB of memory.\n");
#endif
mv->low_buffer_start = output_data = (char *) LOW_BUFFER_START;
high_loaded = 1;
free_mem_end_ptr = (long) high_buffer_start;
if (0x100000 + LOW_BUFFER_SIZE > (ulg) high_buffer_start) {
high_buffer_start = (uch *)(0x100000 + LOW_BUFFER_SIZE);
mv->hcount = 0; /* say: we need not to move high_buffer */
} else {
mv->hcount = -1;
}
mv->high_buffer_start = high_buffer_start;
}
void close_output_buffer_if_we_run_high(struct moveparams *mv)
{
mv->lcount = bytes_out;
if (bytes_out > LOW_BUFFER_SIZE) {
mv->lcount = LOW_BUFFER_SIZE;
if (mv->hcount)
mv->hcount = bytes_out - LOW_BUFFER_SIZE;
} else {
mv->hcount = 0;
}
}
#undef DEBUGFLAG
#ifdef DEBUGFLAG
int debugflag;
#endif
int decompress_kernel(struct moveparams *mv)
{
#ifdef DEBUGFLAG
while (!debugflag)
barrier();
#endif
output_data = (char *) CONFIG_KERNEL_TEXT_ADDRESS;
makecrc();
kputs("Uncompressing Linux... ");
gunzip();
kputs("Ok, booting the kernel.\n");
return 0;
}

View File

@ -0,0 +1,18 @@
/* Internal definitions for the MN10300 kernel decompressor
*
* 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.
*/
extern int end;
/*
* vmlinux.lds
*/
extern char input_data[];
extern int input_len;

View File

@ -0,0 +1,9 @@
SECTIONS
{
.data : {
input_len = .;
LONG(input_data_end - input_data) input_data = .;
*(.data)
input_data_end = .;
}
}

View File

@ -0,0 +1,67 @@
#!/bin/sh
#
# arch/mn10300/boot/install -c.sh
#
# This file is subject to the terms and conditions of the GNU General Public
# Licence. See the file "COPYING" in the main directory of this archive
# for more details.
#
# Copyright (C) 1995 by Linus Torvalds
#
# Adapted from code in arch/i386/boot/Makefile by H. Peter Anvin
#
# "make install -c" script for i386 architecture
#
# Arguments:
# $1 - kernel version
# $2 - kernel image file
# $3 - kernel map file
# $4 - default install -c path (blank if root directory)
# $5 - boot rom file
#
# User may have a custom install -c script
rm -fr $4/../usr/include/linux $4/../usr/include/asm
install -c -m 0755 $2 $4/vmlinuz
install -c -m 0755 $5 $4/boot.rom
install -c -m 0755 -d $4/../usr/include/linux
cd $TOPDIR/include/linux
for i in `find . -maxdepth 1 -name '*.h' -print`; do
install -c -m 0644 $i $4/../usr/include/linux
done
install -c -m 0755 -d $4/../usr/include/linux/byteorder
cd $TOPDIR/include/linux/byteorder
for i in `find . -name '*.h' -print`; do
install -c -m 0644 $i $4/../usr/include/linux/byteorder
done
install -c -m 0755 -d $4/../usr/include/linux/lockd
cd $TOPDIR/include/linux/lockd
for i in `find . -name '*.h' -print`; do
install -c -m 0644 $i $4/../usr/include/linux/lockd
done
install -c -m 0755 -d $4/../usr/include/linux/netfilter_ipv4
cd $TOPDIR/include/linux/netfilter_ipv4
for i in `find . -name '*.h' -print`; do
install -c -m 0644 $i $4/../usr/include/linux/netfilter_ipv4
done
install -c -m 0755 -d $4/../usr/include/linux/nfsd
cd $TOPDIR/include/linux/nfsd
for i in `find . -name '*.h' -print`; do
install -c -m 0644 $i $4/../usr/include/linux/nfsd/$i
done
install -c -m 0755 -d $4/../usr/include/linux/raid
cd $TOPDIR/include/linux/raid
for i in `find . -name '*.h' -print`; do
install -c -m 0644 $i $4/../usr/include/linux/raid
done
install -c -m 0755 -d $4/../usr/include/linux/sunrpc
cd $TOPDIR/include/linux/sunrpc
for i in `find . -name '*.h' -print`; do
install -c -m 0644 $i $4/../usr/include/linux/sunrpc
done
install -c -m 0755 -d $4/../usr/include/asm
cd $TOPDIR/include/asm
for i in `find . -name '*.h' -print`; do
install -c -m 0644 $i $4/../usr/include/asm
done

View File

@ -0,0 +1,190 @@
/*
* Copyright (C) 1991, 1992 Linus Torvalds
* Copyright (C) 1997 Martin Mares
*/
/*
* This file builds a disk-image from three different files:
*
* - bootsect: exactly 512 bytes of 8086 machine code, loads the rest
* - setup: 8086 machine code, sets up system parm
* - system: 80386 code for actual system
*
* It does some checking that all files are of the correct type, and
* just writes the result to stdout, removing headers and padding to
* the right amount. It also writes some system data to stderr.
*/
/*
* Changes by tytso to allow root device specification
* High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996
* Cross compiling fixes by Gertjan van Wingerde, July 1996
* Rewritten by Martin Mares, April 1997
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <unistd.h>
#include <fcntl.h>
#include <asm/boot.h>
#define DEFAULT_MAJOR_ROOT 0
#define DEFAULT_MINOR_ROOT 0
/* Minimal number of setup sectors (see also bootsect.S) */
#define SETUP_SECTS 4
uint8_t buf[1024];
int fd;
int is_big_kernel;
__attribute__((noreturn))
void die(const char *str, ...)
{
va_list args;
va_start(args, str);
vfprintf(stderr, str, args);
fputc('\n', stderr);
exit(1);
}
void file_open(const char *name)
{
fd = open(name, O_RDONLY, 0);
if (fd < 0)
die("Unable to open `%s': %m", name);
}
__attribute__((noreturn))
void usage(void)
{
die("Usage: build [-b] bootsect setup system [rootdev] [> image]");
}
int main(int argc, char **argv)
{
unsigned int i, c, sz, setup_sectors;
uint32_t sys_size;
uint8_t major_root, minor_root;
struct stat sb;
if (argc > 2 && !strcmp(argv[1], "-b")) {
is_big_kernel = 1;
argc--, argv++;
}
if ((argc < 4) || (argc > 5))
usage();
if (argc > 4) {
if (!strcmp(argv[4], "CURRENT")) {
if (stat("/", &sb)) {
perror("/");
die("Couldn't stat /");
}
major_root = major(sb.st_dev);
minor_root = minor(sb.st_dev);
} else if (strcmp(argv[4], "FLOPPY")) {
if (stat(argv[4], &sb)) {
perror(argv[4]);
die("Couldn't stat root device.");
}
major_root = major(sb.st_rdev);
minor_root = minor(sb.st_rdev);
} else {
major_root = 0;
minor_root = 0;
}
} else {
major_root = DEFAULT_MAJOR_ROOT;
minor_root = DEFAULT_MINOR_ROOT;
}
fprintf(stderr, "Root device is (%d, %d)\n", major_root, minor_root);
file_open(argv[1]);
i = read(fd, buf, sizeof(buf));
fprintf(stderr, "Boot sector %d bytes.\n", i);
if (i != 512)
die("Boot block must be exactly 512 bytes");
if (buf[510] != 0x55 || buf[511] != 0xaa)
die("Boot block hasn't got boot flag (0xAA55)");
buf[508] = minor_root;
buf[509] = major_root;
if (write(1, buf, 512) != 512)
die("Write call failed");
close(fd);
/* Copy the setup code */
file_open(argv[2]);
for (i = 0; (c = read(fd, buf, sizeof(buf))) > 0; i += c)
if (write(1, buf, c) != c)
die("Write call failed");
if (c != 0)
die("read-error on `setup'");
close(fd);
/* Pad unused space with zeros */
setup_sectors = (i + 511) / 512;
/* for compatibility with ancient versions of LILO. */
if (setup_sectors < SETUP_SECTS)
setup_sectors = SETUP_SECTS;
fprintf(stderr, "Setup is %d bytes.\n", i);
memset(buf, 0, sizeof(buf));
while (i < setup_sectors * 512) {
c = setup_sectors * 512 - i;
if (c > sizeof(buf))
c = sizeof(buf);
if (write(1, buf, c) != c)
die("Write call failed");
i += c;
}
file_open(argv[3]);
if (fstat(fd, &sb))
die("Unable to stat `%s': %m", argv[3]);
sz = sb.st_size;
fprintf(stderr, "System is %d kB\n", sz / 1024);
sys_size = (sz + 15) / 16;
/* 0x28000*16 = 2.5 MB, conservative estimate for the current maximum */
if (sys_size > (is_big_kernel ? 0x28000 : DEF_SYSSIZE))
die("System is too big. Try using %smodules.",
is_big_kernel ? "" : "bzImage or ");
if (sys_size > 0xffff)
fprintf(stderr,
"warning: kernel is too big for standalone boot "
"from floppy\n");
while (sz > 0) {
int l, n;
l = (sz > sizeof(buf)) ? sizeof(buf) : sz;
n = read(fd, buf, l);
if (n != l) {
if (n < 0)
die("Error reading %s: %m", argv[3]);
else
die("%s: Unexpected EOF", argv[3]);
}
if (write(1, buf, l) != l)
die("Write failed");
sz -= l;
}
close(fd);
/* Write sizes to the bootsector */
if (lseek(1, 497, SEEK_SET) != 497)
die("Output: seek failed");
buf[0] = setup_sectors;
if (write(1, buf, 1) != 1)
die("Write of setup sector count failed");
if (lseek(1, 500, SEEK_SET) != 500)
die("Output: seek failed");
buf[0] = (sys_size & 0xff);
buf[1] = ((sys_size >> 8) & 0xff);
if (write(1, buf, 2) != 2)
die("Write of image length failed");
return 0;
}

View File

@ -0,0 +1,555 @@
#
# Automatically generated make config: don't edit
# Linux kernel version: 2.6.24-rc2
# Fri Nov 16 13:36:38 2007
#
CONFIG_MN10300=y
CONFIG_AM33=y
CONFIG_MMU=y
# CONFIG_HIGHMEM is not set
# CONFIG_NUMA is not set
CONFIG_UID16=y
CONFIG_RWSEM_GENERIC_SPINLOCK=y
CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
CONFIG_GENERIC_CALIBRATE_DELAY=y
CONFIG_GENERIC_FIND_NEXT_BIT=y
CONFIG_GENERIC_HWEIGHT=y
CONFIG_GENERIC_TIME=y
CONFIG_GENERIC_BUG=y
CONFIG_QUICKLIST=y
CONFIG_ARCH_HAS_ILOG2_U32=y
# CONFIG_ARCH_SUPPORTS_AOUT is not set
CONFIG_GENERIC_HARDIRQS=y
# CONFIG_HOTPLUG_CPU is not set
CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
#
# General setup
#
CONFIG_EXPERIMENTAL=y
CONFIG_BROKEN_ON_SMP=y
CONFIG_LOCK_KERNEL=y
CONFIG_INIT_ENV_ARG_LIMIT=32
CONFIG_LOCALVERSION=""
CONFIG_LOCALVERSION_AUTO=y
CONFIG_SYSVIPC=y
CONFIG_SYSVIPC_SYSCTL=y
# CONFIG_POSIX_MQUEUE is not set
CONFIG_BSD_PROCESS_ACCT=y
# CONFIG_BSD_PROCESS_ACCT_V3 is not set
# CONFIG_TASKSTATS is not set
# CONFIG_USER_NS is not set
# CONFIG_PID_NS is not set
# CONFIG_AUDIT is not set
# CONFIG_IKCONFIG is not set
CONFIG_LOG_BUF_SHIFT=14
# CONFIG_CGROUPS is not set
# CONFIG_FAIR_GROUP_SCHED is not set
# CONFIG_SYSFS_DEPRECATED is not set
# CONFIG_RELAY is not set
# CONFIG_BLK_DEV_INITRD is not set
# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
CONFIG_SYSCTL=y
CONFIG_EMBEDDED=y
CONFIG_SYSCTL_SYSCALL=y
# CONFIG_KALLSYMS is not set
# CONFIG_HOTPLUG is not set
CONFIG_PRINTK=y
CONFIG_BUG=y
CONFIG_ELF_CORE=y
CONFIG_BASE_FULL=y
CONFIG_FUTEX=y
CONFIG_ANON_INODES=y
CONFIG_EPOLL=y
CONFIG_SIGNALFD=y
CONFIG_EVENTFD=y
CONFIG_SHMEM=y
# CONFIG_VM_EVENT_COUNTERS is not set
CONFIG_SLAB=y
# CONFIG_SLUB is not set
# CONFIG_SLOB is not set
CONFIG_RT_MUTEXES=y
# CONFIG_TINY_SHMEM is not set
CONFIG_BASE_SMALL=0
# CONFIG_MODULES is not set
# CONFIG_BLOCK is not set
#
# Matsushita MN10300 system setup
#
CONFIG_MN10300_UNIT_ASB2303=y
# CONFIG_MN10300_UNIT_ASB2305 is not set
CONFIG_MN10300_PROC_MN103E010=y
CONFIG_MN10300_CPU_AM33V2=y
CONFIG_FPU=y
CONFIG_MN10300_CACHE_WBACK=y
# CONFIG_MN10300_CACHE_WTHRU is not set
# CONFIG_MN10300_CACHE_DISABLED is not set
#
# Memory layout options
#
CONFIG_KERNEL_RAM_BASE_ADDRESS=0x90000000
CONFIG_INTERRUPT_VECTOR_BASE=0x90000000
CONFIG_KERNEL_TEXT_ADDRESS=0x90001000
CONFIG_KERNEL_ZIMAGE_BASE_ADDRESS=0x90700000
CONFIG_PREEMPT=y
CONFIG_PREEMPT_BKL=y
CONFIG_MN10300_CURRENT_IN_E2=y
CONFIG_MN10300_USING_JTAG=y
CONFIG_MN10300_RTC=y
CONFIG_MN10300_WD_TIMER=y
# CONFIG_ARCH_SUPPORTS_MSI is not set
#
# MN10300 internal serial options
#
CONFIG_MN10300_PROC_HAS_TTYSM0=y
CONFIG_MN10300_PROC_HAS_TTYSM1=y
CONFIG_MN10300_PROC_HAS_TTYSM2=y
CONFIG_MN10300_TTYSM=y
CONFIG_MN10300_TTYSM_CONSOLE=y
CONFIG_MN10300_TTYSM0=y
CONFIG_MN10300_TTYSM0_TIMER8=y
# CONFIG_MN10300_TTYSM0_TIMER2 is not set
CONFIG_MN10300_TTYSM1=y
CONFIG_MN10300_TTYSM1_TIMER9=y
# CONFIG_MN10300_TTYSM1_TIMER3 is not set
# CONFIG_MN10300_TTYSM2 is not set
CONFIG_SELECT_MEMORY_MODEL=y
CONFIG_FLATMEM_MANUAL=y
# CONFIG_DISCONTIGMEM_MANUAL is not set
# CONFIG_SPARSEMEM_MANUAL is not set
CONFIG_FLATMEM=y
CONFIG_FLAT_NODE_MEM_MAP=y
# CONFIG_SPARSEMEM_STATIC is not set
# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
CONFIG_SPLIT_PTLOCK_CPUS=4
# CONFIG_RESOURCES_64BIT is not set
CONFIG_ZONE_DMA_FLAG=0
CONFIG_NR_QUICK=1
CONFIG_VIRT_TO_BUS=y
#
# Power management options
#
# CONFIG_PM is not set
#
# Executable formats
#
CONFIG_BINFMT_ELF=y
# CONFIG_BINFMT_MISC is not set
#
# Networking
#
CONFIG_NET=y
#
# Networking options
#
CONFIG_PACKET=y
CONFIG_PACKET_MMAP=y
CONFIG_UNIX=y
# CONFIG_NET_KEY is not set
CONFIG_INET=y
CONFIG_IP_MULTICAST=y
# CONFIG_IP_ADVANCED_ROUTER is not set
CONFIG_IP_FIB_HASH=y
CONFIG_IP_PNP=y
# CONFIG_IP_PNP_DHCP is not set
CONFIG_IP_PNP_BOOTP=y
# CONFIG_IP_PNP_RARP is not set
# CONFIG_NET_IPIP is not set
# CONFIG_NET_IPGRE is not set
# CONFIG_IP_MROUTE is not set
# CONFIG_ARPD is not set
# CONFIG_SYN_COOKIES is not set
# CONFIG_INET_AH is not set
# CONFIG_INET_ESP is not set
# CONFIG_INET_IPCOMP is not set
# CONFIG_INET_XFRM_TUNNEL is not set
# CONFIG_INET_TUNNEL is not set
# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
# CONFIG_INET_XFRM_MODE_TUNNEL is not set
# CONFIG_INET_XFRM_MODE_BEET is not set
# CONFIG_INET_LRO is not set
# CONFIG_INET_DIAG is not set
# CONFIG_TCP_CONG_ADVANCED is not set
CONFIG_TCP_CONG_CUBIC=y
CONFIG_DEFAULT_TCP_CONG="cubic"
# CONFIG_TCP_MD5SIG is not set
# CONFIG_IPV6 is not set
# CONFIG_INET6_XFRM_TUNNEL is not set
# CONFIG_INET6_TUNNEL is not set
# CONFIG_NETWORK_SECMARK is not set
# CONFIG_NETFILTER is not set
# CONFIG_IP_DCCP is not set
# CONFIG_IP_SCTP is not set
# CONFIG_TIPC is not set
# CONFIG_ATM is not set
# CONFIG_BRIDGE is not set
# CONFIG_VLAN_8021Q is not set
# CONFIG_DECNET is not set
# CONFIG_LLC2 is not set
# CONFIG_IPX is not set
# CONFIG_ATALK is not set
# CONFIG_X25 is not set
# CONFIG_LAPB is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
# CONFIG_NET_SCHED is not set
#
# Network testing
#
# CONFIG_NET_PKTGEN is not set
# CONFIG_HAMRADIO is not set
# CONFIG_IRDA is not set
# CONFIG_BT is not set
# CONFIG_AF_RXRPC is not set
#
# Wireless
#
# CONFIG_CFG80211 is not set
# CONFIG_WIRELESS_EXT is not set
# CONFIG_MAC80211 is not set
# CONFIG_IEEE80211 is not set
# CONFIG_RFKILL is not set
# CONFIG_NET_9P is not set
#
# Device Drivers
#
#
# Generic Driver Options
#
CONFIG_STANDALONE=y
CONFIG_PREVENT_FIRMWARE_BUILD=y
# CONFIG_SYS_HYPERVISOR is not set
# CONFIG_CONNECTOR is not set
CONFIG_MTD=y
CONFIG_MTD_DEBUG=y
CONFIG_MTD_DEBUG_VERBOSE=0
# CONFIG_MTD_CONCAT is not set
CONFIG_MTD_PARTITIONS=y
CONFIG_MTD_REDBOOT_PARTS=y
CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK=-1
CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED=y
# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set
# CONFIG_MTD_CMDLINE_PARTS is not set
#
# User Modules And Translation Layers
#
CONFIG_MTD_CHAR=y
# CONFIG_MTD_OOPS is not set
#
# RAM/ROM/Flash chip drivers
#
CONFIG_MTD_CFI=y
CONFIG_MTD_JEDECPROBE=y
CONFIG_MTD_GEN_PROBE=y
CONFIG_MTD_CFI_ADV_OPTIONS=y
CONFIG_MTD_CFI_NOSWAP=y
# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set
# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set
CONFIG_MTD_CFI_GEOMETRY=y
CONFIG_MTD_MAP_BANK_WIDTH_1=y
CONFIG_MTD_MAP_BANK_WIDTH_2=y
CONFIG_MTD_MAP_BANK_WIDTH_4=y
# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
CONFIG_MTD_CFI_I1=y
CONFIG_MTD_CFI_I2=y
CONFIG_MTD_CFI_I4=y
# CONFIG_MTD_CFI_I8 is not set
# CONFIG_MTD_OTP is not set
# CONFIG_MTD_CFI_INTELEXT is not set
CONFIG_MTD_CFI_AMDSTD=y
# CONFIG_MTD_CFI_STAA is not set
CONFIG_MTD_CFI_UTIL=y
# CONFIG_MTD_RAM is not set
# CONFIG_MTD_ROM is not set
# CONFIG_MTD_ABSENT is not set
#
# Mapping drivers for chip access
#
# CONFIG_MTD_COMPLEX_MAPPINGS is not set
# CONFIG_MTD_PHYSMAP is not set
# CONFIG_MTD_PLATRAM is not set
#
# Self-contained MTD device drivers
#
# CONFIG_MTD_SLRAM is not set
# CONFIG_MTD_PHRAM is not set
# CONFIG_MTD_MTDRAM is not set
#
# Disk-On-Chip Device Drivers
#
# CONFIG_MTD_DOC2000 is not set
# CONFIG_MTD_DOC2001 is not set
# CONFIG_MTD_DOC2001PLUS is not set
# CONFIG_MTD_NAND is not set
# CONFIG_MTD_ONENAND is not set
#
# UBI - Unsorted block images
#
# CONFIG_MTD_UBI is not set
# CONFIG_PARPORT is not set
CONFIG_MISC_DEVICES=y
# CONFIG_EEPROM_93CX6 is not set
#
# SCSI device support
#
# CONFIG_SCSI_DMA is not set
# CONFIG_SCSI_NETLINK is not set
CONFIG_NETDEVICES=y
# CONFIG_NETDEVICES_MULTIQUEUE is not set
# CONFIG_DUMMY is not set
# CONFIG_BONDING is not set
# CONFIG_MACVLAN is not set
# CONFIG_EQUALIZER is not set
# CONFIG_TUN is not set
# CONFIG_VETH is not set
# CONFIG_PHYLIB is not set
CONFIG_NET_ETHERNET=y
CONFIG_MII=y
CONFIG_SMC91X=y
# CONFIG_IBM_NEW_EMAC_ZMII is not set
# CONFIG_IBM_NEW_EMAC_RGMII is not set
# CONFIG_IBM_NEW_EMAC_TAH is not set
# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
# CONFIG_B44 is not set
# CONFIG_NETDEV_1000 is not set
# CONFIG_NETDEV_10000 is not set
#
# Wireless LAN
#
# CONFIG_WLAN_PRE80211 is not set
# CONFIG_WLAN_80211 is not set
# CONFIG_WAN is not set
# CONFIG_PPP is not set
# CONFIG_SLIP is not set
# CONFIG_SHAPER is not set
# CONFIG_NETCONSOLE is not set
# CONFIG_NETPOLL is not set
# CONFIG_NET_POLL_CONTROLLER is not set
# CONFIG_ISDN is not set
# CONFIG_PHONE is not set
#
# Input device support
#
# CONFIG_INPUT is not set
#
# Hardware I/O ports
#
# CONFIG_SERIO is not set
# CONFIG_GAMEPORT is not set
#
# Character devices
#
# CONFIG_VT is not set
# CONFIG_SERIAL_NONSTANDARD is not set
#
# Serial drivers
#
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_NR_UARTS=4
CONFIG_SERIAL_8250_RUNTIME_UARTS=4
CONFIG_SERIAL_8250_EXTENDED=y
# CONFIG_SERIAL_8250_MANY_PORTS is not set
CONFIG_SERIAL_8250_SHARE_IRQ=y
# CONFIG_SERIAL_8250_DETECT_IRQ is not set
# CONFIG_SERIAL_8250_RSA is not set
#
# Non-8250 serial port support
#
CONFIG_SERIAL_CORE=y
CONFIG_SERIAL_CORE_CONSOLE=y
CONFIG_UNIX98_PTYS=y
CONFIG_LEGACY_PTYS=y
CONFIG_LEGACY_PTY_COUNT=256
# CONFIG_IPMI_HANDLER is not set
# CONFIG_HW_RANDOM is not set
CONFIG_RTC=y
# CONFIG_R3964 is not set
# CONFIG_TCG_TPM is not set
# CONFIG_I2C is not set
#
# SPI support
#
# CONFIG_SPI is not set
# CONFIG_SPI_MASTER is not set
# CONFIG_W1 is not set
# CONFIG_POWER_SUPPLY is not set
# CONFIG_HWMON is not set
# CONFIG_WATCHDOG is not set
#
# Sonics Silicon Backplane
#
CONFIG_SSB_POSSIBLE=y
# CONFIG_SSB is not set
#
# Multifunction device drivers
#
# CONFIG_MFD_SM501 is not set
#
# Multimedia devices
#
# CONFIG_VIDEO_DEV is not set
# CONFIG_DVB_CORE is not set
# CONFIG_DAB is not set
#
# Graphics support
#
# CONFIG_VGASTATE is not set
# CONFIG_VIDEO_OUTPUT_CONTROL is not set
# CONFIG_FB is not set
# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
#
# Display device support
#
# CONFIG_DISPLAY_SUPPORT is not set
#
# Sound
#
# CONFIG_SOUND is not set
# CONFIG_USB_SUPPORT is not set
# CONFIG_MMC is not set
# CONFIG_NEW_LEDS is not set
# CONFIG_RTC_CLASS is not set
#
# Userspace I/O
#
# CONFIG_UIO is not set
#
# File systems
#
CONFIG_INOTIFY=y
CONFIG_INOTIFY_USER=y
# CONFIG_QUOTA is not set
CONFIG_DNOTIFY=y
# CONFIG_AUTOFS_FS is not set
# CONFIG_AUTOFS4_FS is not set
# CONFIG_FUSE_FS is not set
#
# Pseudo filesystems
#
CONFIG_PROC_FS=y
CONFIG_PROC_KCORE=y
CONFIG_PROC_SYSCTL=y
CONFIG_SYSFS=y
CONFIG_TMPFS=y
# CONFIG_TMPFS_POSIX_ACL is not set
# CONFIG_HUGETLB_PAGE is not set
# CONFIG_CONFIGFS_FS is not set
#
# Miscellaneous filesystems
#
CONFIG_JFFS2_FS=y
CONFIG_JFFS2_FS_DEBUG=0
CONFIG_JFFS2_FS_WRITEBUFFER=y
# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
# CONFIG_JFFS2_SUMMARY is not set
# CONFIG_JFFS2_FS_XATTR is not set
# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
CONFIG_JFFS2_ZLIB=y
# CONFIG_JFFS2_LZO is not set
CONFIG_JFFS2_RTIME=y
# CONFIG_JFFS2_RUBIN is not set
CONFIG_NETWORK_FILESYSTEMS=y
CONFIG_NFS_FS=y
CONFIG_NFS_V3=y
# CONFIG_NFS_V3_ACL is not set
# CONFIG_NFS_V4 is not set
# CONFIG_NFS_DIRECTIO is not set
# CONFIG_NFSD is not set
CONFIG_ROOT_NFS=y
CONFIG_LOCKD=y
CONFIG_LOCKD_V4=y
CONFIG_NFS_COMMON=y
CONFIG_SUNRPC=y
# CONFIG_SUNRPC_BIND34 is not set
# CONFIG_RPCSEC_GSS_KRB5 is not set
# CONFIG_RPCSEC_GSS_SPKM3 is not set
# CONFIG_SMB_FS is not set
# CONFIG_CIFS is not set
# CONFIG_NCP_FS is not set
# CONFIG_CODA_FS is not set
# CONFIG_AFS_FS is not set
# CONFIG_NLS is not set
# CONFIG_DLM is not set
#
# Kernel hacking
#
# CONFIG_PRINTK_TIME is not set
CONFIG_ENABLE_WARN_DEPRECATED=y
CONFIG_ENABLE_MUST_CHECK=y
CONFIG_MAGIC_SYSRQ=y
# CONFIG_UNUSED_SYMBOLS is not set
# CONFIG_DEBUG_FS is not set
# CONFIG_HEADERS_CHECK is not set
# CONFIG_DEBUG_KERNEL is not set
# CONFIG_DEBUG_BUGVERBOSE is not set
# CONFIG_SAMPLES is not set
#
# Security options
#
# CONFIG_KEYS is not set
# CONFIG_SECURITY is not set
# CONFIG_SECURITY_FILE_CAPABILITIES is not set
# CONFIG_CRYPTO is not set
#
# Library routines
#
CONFIG_BITREVERSE=y
# CONFIG_CRC_CCITT is not set
# CONFIG_CRC16 is not set
# CONFIG_CRC_ITU_T is not set
CONFIG_CRC32=y
# CONFIG_CRC7 is not set
# CONFIG_LIBCRC32C is not set
CONFIG_ZLIB_INFLATE=y
CONFIG_ZLIB_DEFLATE=y
CONFIG_PLIST=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT=y
CONFIG_HAS_DMA=y
#
# Profiling support
#
CONFIG_PROFILING=y
CONFIG_OPROFILE=y

View File

@ -0,0 +1,27 @@
#
# Makefile for the MN10300-specific core kernel code
#
extra-y := head.o init_task.o vmlinux.lds
obj-y := process.o semaphore.o signal.o entry.o fpu.o traps.o irq.o \
ptrace.o setup.o time.o sys_mn10300.o io.o kthread.o \
switch_to.o mn10300_ksyms.o kernel_execve.o
obj-$(CONFIG_MN10300_WD_TIMER) += mn10300-watchdog.o mn10300-watchdog-low.o
obj-$(CONFIG_FPU) += fpu-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
ifneq ($(CONFIG_MN10300_CACHE_DISABLED),y)
obj-$(CONFIG_GDBSTUB) += gdb-cache.o
endif
obj-$(CONFIG_MN10300_RTC) += rtc.o
obj-$(CONFIG_PROFILE) += profile.o profile-low.o
obj-$(CONFIG_MODULES) += module.o
obj-$(CONFIG_KPROBES) += kprobes.o

View File

@ -0,0 +1,108 @@
/*
* 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 <asm/ucontext.h>
#include <asm/processor.h>
#include <asm/thread_info.h>
#include <asm/ptrace.h>
#include "sigframe.h"
#include "mn10300-serial.h"
#define DEFINE(sym, val) \
asm volatile("\n->" #sym " %0 " #val : : "i" (val))
#define BLANK() asm volatile("\n->")
#define OFFSET(sym, str, mem) \
DEFINE(sym, offsetof(struct str, mem));
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_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);
OFFSET(THREAD_FRAME, thread_struct, __frame);
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(__tx_info_buffer, mn10300_serial_port, uart.info);
OFFSET(__tx_xchar, mn10300_serial_port, tx_xchar);
OFFSET(__tx_break, mn10300_serial_port, tx_break);
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_info, xmit.buf);
OFFSET(__xmit_head, uart_info, xmit.head);
OFFSET(__xmit_tail, uart_info, xmit.tail);
}

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

@ -0,0 +1,721 @@
###############################################################################
#
# 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/system.h>
#include <asm/thread_info.h>
#include <asm/intctl-regs.h>
#include <asm/busctl-regs.h>
#include <asm/timer-regs.h>
#include <asm/unit/leds.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/errno.h>
#include <asm/asm-offsets.h>
#include <asm/frame.inc>
#ifdef CONFIG_PREEMPT
#define preempt_stop __cli
#else
#define preempt_stop
#define resume_kernel restore_all
#endif
.macro __cli
and ~EPSW_IM,epsw
or EPSW_IE|MN10300_CLI_LEVEL,epsw
nop
nop
nop
.endm
.macro __sti
or EPSW_IE|EPSW_IM_7,epsw
.endm
.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
###############################################################################
#
# 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_trace_entry
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
__cli
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:
btst _TIF_SYSCALL_TRACE,d2
beq work_pending
__sti # could let do_syscall_trace() call
# schedule() instead
mov fp,d0
mov 1,d1
call do_syscall_trace[],0 # do_syscall_trace(regs,entryexit)
jmp resume_userspace
ALIGN
work_pending:
btst _TIF_NEED_RESCHED,d2
beq work_notifysig
work_resched:
call schedule[],0
# make sure we don't miss an interrupt setting need_resched or
# sigpending between sampling and the rti
__cli
# is there any work to be done other than syscall tracing?
mov (TI_flags,a2),d2
btst _TIF_WORK_MASK,d2
beq restore_all
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_trace_entry:
mov -ENOSYS,d0
mov d0,(REG_D0,fp)
mov fp,d0
clr d1
call do_syscall_trace[],0
mov (REG_ORIG_D0,fp),d0
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
ENTRY(resume_userspace)
# make sure we don't miss an interrupt setting need_resched or
# sigpending between sampling and the rti
__cli
# is there any work to be done on int/exception return?
mov (TI_flags,a2),d2
btst _TIF_WORK_MASK,d2
bne work_pending
jmp restore_all
#ifdef CONFIG_PREEMPT
ENTRY(resume_kernel)
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) ?
beq 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
###############################################################################
#
# Monitor Signal handler entry point
#
###############################################################################
ENTRY(monitor_signal)
movbu (0xae000001),d1
cmp 1,d1
beq monsignal
ret [],0
monsignal:
or EPSW_NMID,epsw
mov d0,a0
mov a0,sp
mov (REG_EPSW,fp),d1
and ~EPSW_nSL,d1
mov d1,(REG_EPSW,fp)
movm (sp),[d2,d3,a2,a3,exreg0,exreg1,exother]
mov (sp),a1
mov a1,usp
movm (sp),[other]
add 4,sp
here: jmp 0x8e000008-here+0x8e000008
###############################################################################
#
# 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)
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
###############################################################################
#
# Miscellaneous exception entry points
#
###############################################################################
ENTRY(nmi_handler)
add -4,sp
mov d0,(sp)
mov (TBR),d0
bra __common_exception_nonmi
ENTRY(__common_exception)
add -4,sp
mov d0,(sp)
__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
btst 0x01,(gdbstub_busy)
beq 2f
and ~EPSW_IE,epsw
mov fp,d0
mov a2,d1
call gdbstub_exception[],0 # gdbstub itself caused an exception
bra restore_all
2:
#endif
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
#if defined(CONFIG_MN10300_CACHE_WBACK)
jmp mn10300_dcache_flush_inv_range2
#else
ret [],0
#endif
###############################################################################
#
# 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 old_select
.long sys_symlink
.long sys_lstat
.long sys_readlink /* 85 */
.long sys_uselib
.long sys_swapon
.long sys_reboot
.long 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_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_mmap2
.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
nr_syscalls=(.-sys_call_table)/4

View File

@ -0,0 +1,197 @@
/* 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 <asm/cpu-regs.h>
###############################################################################
#
# 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
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
#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
fmov fs0,(a0+)
fmov fs1,(a0+)
fmov fs2,(a0+)
fmov fs3,(a0+)
fmov fs4,(a0+)
fmov fs5,(a0+)
fmov fs6,(a0+)
fmov fs7,(a0+)
fmov fs8,(a0+)
fmov fs9,(a0+)
fmov fs10,(a0+)
fmov fs11,(a0+)
fmov fs12,(a0+)
fmov fs13,(a0+)
fmov fs14,(a0+)
fmov fs15,(a0+)
fmov fs16,(a0+)
fmov fs17,(a0+)
fmov fs18,(a0+)
fmov fs19,(a0+)
fmov fs20,(a0+)
fmov fs21,(a0+)
fmov fs22,(a0+)
fmov fs23,(a0+)
fmov fs24,(a0+)
fmov fs25,(a0+)
fmov fs26,(a0+)
fmov fs27,(a0+)
fmov fs28,(a0+)
fmov fs29,(a0+)
fmov fs30,(a0+)
fmov fs31,(a0+)
fmov fpcr,d0
mov d0,(a0)
#ifdef CONFIG_MN10300_PROC_MN103E010
nop
nop
#endif
mov d1,epsw
ret [],0
.size fpu_save,.-fpu_save
###############################################################################
#
# void fpu_restore(struct fpu_state_struct *)
# - restore the fpu state
# - note that an FPU Operational exception might occur during this process
#
###############################################################################
.globl fpu_restore
.type fpu_restore,@function
fpu_restore:
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
fmov (a0+),fs0
fmov (a0+),fs1
fmov (a0+),fs2
fmov (a0+),fs3
fmov (a0+),fs4
fmov (a0+),fs5
fmov (a0+),fs6
fmov (a0+),fs7
fmov (a0+),fs8
fmov (a0+),fs9
fmov (a0+),fs10
fmov (a0+),fs11
fmov (a0+),fs12
fmov (a0+),fs13
fmov (a0+),fs14
fmov (a0+),fs15
fmov (a0+),fs16
fmov (a0+),fs17
fmov (a0+),fs18
fmov (a0+),fs19
fmov (a0+),fs20
fmov (a0+),fs21
fmov (a0+),fs22
fmov (a0+),fs23
fmov (a0+),fs24
fmov (a0+),fs25
fmov (a0+),fs26
fmov (a0+),fs27
fmov (a0+),fs28
fmov (a0+),fs29
fmov (a0+),fs30
fmov (a0+),fs31
mov (a0),d0
fmov d0,fpcr
#ifdef CONFIG_MN10300_PROC_MN103E010
nop
nop
nop
#endif
mov d1,epsw
ret [],0
.size fpu_restore,.-fpu_restore

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

@ -0,0 +1,223 @@
/* 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>
struct task_struct *fpu_state_owner;
/*
* handle an exception due to the FPU being disabled
*/
asmlinkage void fpu_disabled(struct pt_regs *regs, enum exception_code code)
{
struct task_struct *tsk = current;
if (!user_mode(regs))
die_if_no_fixup("An FPU Disabled exception happened in"
" kernel space\n",
regs, code);
#ifdef CONFIG_FPU
preempt_disable();
/* transfer the last process's FPU state to memory */
if (fpu_state_owner) {
fpu_save(&fpu_state_owner->thread.fpu_state);
fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE;
}
/* the current process now owns the FPU state */
fpu_state_owner = tsk;
regs->epsw |= EPSW_FE;
/* load the FPU with the current process's FPU state or invent a new
* clean one if the process doesn't have one */
if (is_using_fpu(tsk)) {
fpu_restore(&tsk->thread.fpu_state);
} else {
fpu_init_state();
set_using_fpu(tsk);
}
preempt_enable();
#else
{
siginfo_t info;
info.si_signo = SIGFPE;
info.si_errno = 0;
info.si_addr = (void *) tsk->thread.uregs->pc;
info.si_code = FPE_FLTINV;
force_sig_info(SIGFPE, &info, tsk);
}
#endif /* CONFIG_FPU */
}
/*
* 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 = fpu_state_owner;
siginfo_t info;
if (!user_mode(regs))
die_if_no_fixup("An FPU Operation exception happened in"
" kernel space\n",
regs, code);
if (!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;
#ifdef CONFIG_FPU
{
u32 fpcr;
/* get FPCR (we need to enable the FPU whilst we do this) */
asm volatile(" or %1,epsw \n"
#ifdef CONFIG_MN10300_PROC_MN103E010
" nop \n"
" nop \n"
" nop \n"
#endif
" fmov fpcr,%0 \n"
#ifdef CONFIG_MN10300_PROC_MN103E010
" nop \n"
" nop \n"
" nop \n"
#endif
" and %2,epsw \n"
: "=&d"(fpcr)
: "i"(EPSW_FE), "i"(~EPSW_FE)
);
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;
}
#endif
force_sig_info(SIGFPE, &info, tsk);
}
/*
* save the FPU state to a signal context
*/
int fpu_setup_sigcontext(struct fpucontext *fpucontext)
{
#ifdef CONFIG_FPU
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();
if (fpu_state_owner == tsk) {
fpu_save(&tsk->thread.fpu_state);
fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE;
fpu_state_owner = NULL;
}
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;
#else
return 0;
#endif
}
/*
* kill a process's FPU state during restoration after signal handling
*/
void fpu_kill_state(struct task_struct *tsk)
{
#ifdef CONFIG_FPU
/* disown anything left in the FPU */
preempt_disable();
if (fpu_state_owner == tsk) {
fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE;
fpu_state_owner = NULL;
}
preempt_enable();
#endif
/* 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,105 @@
###############################################################################
#
# MN10300 Low-level cache purging routines for gdbstub
#
# 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 cache purge
#
###############################################################################
.type gdbstub_purge_cache,@function
ENTRY(gdbstub_purge_cache)
#######################################################################
# read the addresses tagged in the cache's tag RAM and attempt to flush
# those addresses specifically
# - we rely on the hardware to filter out invalid tag entry addresses
mov DCACHE_TAG(0,0),a0 # dcache tag RAM access address
mov DCACHE_PURGE(0,0),a1 # dcache purge request address
mov L1_CACHE_NWAYS*L1_CACHE_NENTRIES,d1 # total number of entries
mn10300_dcache_flush_loop:
mov (a0),d0
and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0
or L1_CACHE_TAG_VALID,d0 # retain valid entries in the
# cache
mov d0,(a1) # conditional purge
mn10300_dcache_flush_skip:
add L1_CACHE_BYTES,a0
add L1_CACHE_BYTES,a1
add -1,d1
bne mn10300_dcache_flush_loop
;; # unconditionally flush and invalidate the dcache
;; mov DCACHE_PURGE(0,0),a1 # dcache purge request address
;; mov L1_CACHE_NWAYS*L1_CACHE_NENTRIES,d1 # total number of
;; # entries
;;
;; gdbstub_purge_cache__dcache_loop:
;; mov (a1),d0 # unconditional purge
;;
;; add L1_CACHE_BYTES,a1
;; add -1,d1
;; bne gdbstub_purge_cache__dcache_loop
#######################################################################
# now invalidate the icache
mov CHCTR,a0
movhu (a0),a1
mov epsw,d1
and ~EPSW_IE,epsw
nop
nop
# disable the icache
and ~CHCTR_ICEN,d0
movhu d0,(a0)
# and wait for it to calm down
setlb
movhu (a0),d0
btst CHCTR_ICBUSY,d0
lne
# invalidate
or CHCTR_ICINV,d0
movhu d0,(a0)
# wait for the cache to finish
mov CHCTR,a0
setlb
movhu (a0),d0
btst CHCTR_ICBUSY,d0
lne
# and reenable it
movhu a1,(a0)
movhu (a0),d0 # read back to flush
# (SIGILLs all over without this)
mov d1,epsw
ret [],0
.size gdbstub_purge_cache,.-gdbstub_purge_cache

View File

@ -0,0 +1,90 @@
###############################################################################
#
# 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/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:
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_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,155 @@
/* 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/system.h>
#include <asm/gdb-stub.h>
#include <asm/exceptions.h>
#include <asm/serial-regs.h>
#include <asm/unit/serial.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);
IVAR0 = EXCEP_IRQ_LEVEL0;
set_intr_stub(EXCEP_IRQ_LEVEL0, gdbstub_io_rx_handler);
XIRQxICR(GDBPORT_SERIAL_IRQ) &= ~GxICR_REQUEST;
XIRQxICR(GDBPORT_SERIAL_IRQ) = GxICR_ENABLE | GxICR_LEVEL_0;
tmp = XIRQxICR(GDBPORT_SERIAL_IRQ);
GDBPORT_SERIAL_IER = UART_IER_RDI | UART_IER_RLSI;
/* permit level 0 IRQs to take place */
asm volatile(
" and %0,epsw \n"
" or %1,epsw \n"
:
: "i"(~EPSW_IM), "i"(EPSW_IE | EPSW_IM_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;
*_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;
if (ix == gdbstub_rx_inp) {
if (nonblock)
return -EAGAIN;
#ifdef CONFIG_MN10300_WD_TIMER
watchdog_alert_counter = 0;
#endif /* CONFIG_MN10300_WD_TIMER */
goto try_again;
}
ch = gdbstub_rx_buffer[ix++];
st = gdbstub_rx_buffer[ix++];
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 <asm/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,299 @@
/* 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/system.h>
#include <asm/gdb-stub.h>
#include <asm/exceptions.h>
#include <asm/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, GxICR_LEVEL_0);
set_intr_level(gdbstub_port->tx_irq, GxICR_LEVEL_0);
set_intr_stub(EXCEP_IRQ_LEVEL0, 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 */
asm volatile(
" and %0,epsw \n"
" or %1,epsw \n"
:
: "i"(~EPSW_IM), "i"(EPSW_IE|EPSW_IM_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;
*_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;
if (ix == gdbstub_rx_inp) {
if (nonblock)
return -EAGAIN;
#ifdef CONFIG_MN10300_WD_TIMER
watchdog_alert_counter = 0;
#endif /* CONFIG_MN10300_WD_TIMER */
goto try_again;
}
ch = gdbstub_rx_buffer[ix++];
st = gdbstub_rx_buffer[ix++];
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

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

@ -0,0 +1,255 @@
/* 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/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 <asm/unit/serial.h>
.section .text.head,"ax"
###############################################################################
#
# bootloader entry point
#
###############################################################################
.globl _start
.type _start,@function
_start:
# save commandline pointer
mov d0,a3
# preload the PGD pointer register
mov swapper_pg_dir,d0
mov d0,(PTBR)
# turn on the TLBs
mov MMUCTR_IIV|MMUCTR_DIV,d0
mov d0,(MMUCTR)
mov MMUCTR_ITE|MMUCTR_DTE|MMUCTR_CE,d0
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
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
#ifndef CONFIG_MN10300_CACHE_DISABLED
#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 /* CACHE_DISABLED */
#else
mov CHCTR_ICEN|CHCTR_DCEN|CHCTR_DCWTMD_WRTHROUGH,d0
#endif /* WBACK */
movhu d0,(a0) # enable
#endif /* NOWRALLOC */
# 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_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-.
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,45 @@
/* MN10300 Initial task 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.
*/
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/init_task.h>
#include <linux/fs.h>
#include <linux/mqueue.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
static struct fs_struct init_fs = INIT_FS;
static struct files_struct init_files = INIT_FILES;
static struct signal_struct init_signals = INIT_SIGNALS(init_signals);
static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand);
struct mm_struct init_mm = INIT_MM(init_mm);
EXPORT_SYMBOL(init_mm);
/*
* Initial thread structure.
*
* We need to make sure that this is THREAD_SIZE aligned due to the
* way process stacks are handled. This is done by having a special
* "init_task" linker map entry..
*/
union thread_union init_thread_union
__attribute__((__section__(".data.init_task"))) =
{ INIT_THREAD_INFO(init_task) };
/*
* Initial task structure.
*
* All other task structs will be allocated on slabs in fork.c
*/
struct task_struct init_task = INIT_TASK(init_task);
EXPORT_SYMBOL(init_task);

View File

@ -0,0 +1,20 @@
/* 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.
*/
/*
* kthread.S
*/
extern int kernel_thread_helper(int);
/*
* entry.S
*/
extern void ret_from_fork(struct task_struct *) __attribute__((noreturn));

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);

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

@ -0,0 +1,235 @@
/* 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 <asm/setup.h>
unsigned long __mn10300_irq_enabled_epsw = EPSW_IE | EPSW_IM_7;
EXPORT_SYMBOL(__mn10300_irq_enabled_epsw);
atomic_t irq_err_count;
/*
* MN10300 INTC controller operations
*/
static void mn10300_cpupic_disable(unsigned int irq)
{
u16 tmp = GxICR(irq);
GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_DETECT;
tmp = GxICR(irq);
}
static void mn10300_cpupic_enable(unsigned int irq)
{
u16 tmp = GxICR(irq);
GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_ENABLE;
tmp = GxICR(irq);
}
static void mn10300_cpupic_ack(unsigned int irq)
{
u16 tmp;
*(volatile u8 *) &GxICR(irq) = GxICR_DETECT;
tmp = GxICR(irq);
}
static void mn10300_cpupic_mask(unsigned int irq)
{
u16 tmp = GxICR(irq);
GxICR(irq) = (tmp & GxICR_LEVEL);
tmp = GxICR(irq);
}
static void mn10300_cpupic_mask_ack(unsigned int irq)
{
u16 tmp = GxICR(irq);
GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_DETECT;
tmp = GxICR(irq);
}
static void mn10300_cpupic_unmask(unsigned int irq)
{
u16 tmp = GxICR(irq);
GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_ENABLE | GxICR_DETECT;
tmp = GxICR(irq);
}
static void mn10300_cpupic_end(unsigned int irq)
{
u16 tmp = GxICR(irq);
GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_ENABLE;
tmp = GxICR(irq);
}
static struct irq_chip mn10300_cpu_pic = {
.name = "cpu",
.disable = mn10300_cpupic_disable,
.enable = mn10300_cpupic_enable,
.ack = mn10300_cpupic_ack,
.mask = mn10300_cpupic_mask,
.mask_ack = mn10300_cpupic_mask_ack,
.unmask = mn10300_cpupic_unmask,
.end = mn10300_cpupic_end,
};
/*
* '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)
{
u16 tmp;
if (in_interrupt())
BUG();
tmp = GxICR(irq);
GxICR(irq) = (tmp & GxICR_ENABLE) | level;
tmp = GxICR(irq);
}
/*
* mark an interrupt to be ACK'd after interrupt handlers have been run rather
* than before
* - see Documentation/mn10300/features.txt
*/
void set_intr_postackable(int irq)
{
set_irq_handler(irq, handle_level_irq);
}
/*
* initialise the interrupt system
*/
void __init init_IRQ(void)
{
int irq;
for (irq = 0; irq < NR_IRQS; irq++)
if (irq_desc[irq].chip == &no_irq_type)
set_irq_chip_and_handler(irq, &mn10300_cpu_pic,
handle_edge_irq);
unit_init_IRQ();
}
/*
* handle normal device IRQs
*/
asmlinkage void do_IRQ(void)
{
unsigned long sp, epsw, irq_disabled_epsw, old_irq_enabled_epsw;
int irq;
sp = current_stack_pointer();
if (sp - (sp & ~(THREAD_SIZE - 1)) < STACK_WARN)
BUG();
/* make sure local_irq_enable() doesn't muck up the interrupt priority
* setting in EPSW */
old_irq_enabled_epsw = __mn10300_irq_enabled_epsw;
local_save_flags(epsw);
__mn10300_irq_enabled_epsw = EPSW_IE | (EPSW_IM & epsw);
irq_disabled_epsw = EPSW_IE | MN10300_CLI_LEVEL;
__IRQ_STAT(smp_processor_id(), __irq_count)++;
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 = old_irq_enabled_epsw;
irq_exit();
}
/*
* Display interrupt management information through /proc/interrupts
*/
int show_interrupts(struct seq_file *p, void *v)
{
int i = *(loff_t *) v, j, cpu;
struct irqaction *action;
unsigned long flags;
switch (i) {
/* display column title bar naming CPUs */
case 0:
seq_printf(p, " ");
for (j = 0; j < NR_CPUS; j++)
if (cpu_online(j))
seq_printf(p, "CPU%d ", j);
seq_putc(p, '\n');
break;
/* display information rows, one per active CPU */
case 1 ... NR_IRQS - 1:
spin_lock_irqsave(&irq_desc[i].lock, flags);
action = irq_desc[i].action;
if (action) {
seq_printf(p, "%3d: ", i);
for_each_present_cpu(cpu)
seq_printf(p, "%10u ", kstat_cpu(cpu).irqs[i]);
seq_printf(p, " %14s.%u", irq_desc[i].chip->name,
(GxICR(i) & GxICR_LEVEL) >>
GxICR_LEVEL_SHIFT);
seq_printf(p, " %s", action->name);
for (action = action->next;
action;
action = action->next)
seq_printf(p, ", %s", action->name);
seq_putc(p, '\n');
}
spin_unlock_irqrestore(&irq_desc[i].lock, flags);
break;
/* polish off with NMI and error counters */
case NR_IRQS:
seq_printf(p, "NMI: ");
for (j = 0; j < NR_CPUS; j++)
if (cpu_online(j))
seq_printf(p, "%10u ", nmi_count(j));
seq_putc(p, '\n');
seq_printf(p, "ERR: %10u\n", atomic_read(&irq_err_count));
break;
}
return 0;
}

View File

@ -0,0 +1,37 @@
/* MN10300 In-kernel program execution
*
* 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/unistd.h>
###############################################################################
#
# Do a system call from kernel instead of calling sys_execve so we end up with
# proper pt_regs.
#
# int kernel_execve(const char *filename, char *const argv[],
# char *const envp[])
#
# On entry: D0/D1/8(SP): arguments to function
# On return: D0: syscall return.
#
###############################################################################
.globl kernel_execve
.type kernel_execve,@function
kernel_execve:
mov a3,a1
mov d0,a0
mov (12,sp),a3
mov +__NR_execve,d0
syscall 0
mov a1,a3
rets
.size kernel_execve,.-kernel_execve

View File

@ -0,0 +1,653 @@
/* 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 *current_kprobe;
static unsigned long current_kprobe_orig_pc;
static unsigned long current_kprobe_next_pc;
static int current_kprobe_ss_flags;
static unsigned long kprobe_status;
static kprobe_opcode_t current_kprobe_ss_buf[MAX_INSN_SIZE + 2];
static unsigned long current_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)
{
mn10300_dcache_flush();
mn10300_icache_inv();
}
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;
mn10300_dcache_flush();
mn10300_icache_inv();
}
static inline
void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
{
unsigned long nextpc;
current_kprobe_orig_pc = regs->pc;
memcpy(current_kprobe_ss_buf, &p->ainsn.insn[0], MAX_INSN_SIZE);
regs->pc = (unsigned long) current_kprobe_ss_buf;
nextpc = find_nextpc(regs, &current_kprobe_ss_flags);
if (current_kprobe_ss_flags & SINGLESTEP_PCREL)
current_kprobe_next_pc =
current_kprobe_orig_pc + (nextpc - regs->pc);
else
current_kprobe_next_pc = nextpc;
/* branching instructions need special handling */
if (current_kprobe_ss_flags & SINGLESTEP_BRANCH)
nextpc = singlestep_branch_setup(regs);
current_kprobe_bp_addr = nextpc;
*(u8 *) nextpc = BREAKPOINT_INSTRUCTION;
mn10300_dcache_flush_range2((unsigned) current_kprobe_ss_buf,
sizeof(current_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 = current_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;
current_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 (current_kprobe_ss_flags & SINGLESTEP_BRANCH) {
regs->pc = current_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 = current_kprobe_next_pc;
current_kprobe_bp_addr = 0;
}
static inline int __kprobes post_kprobe_handler(struct pt_regs *regs)
{
if (!kprobe_running())
return 0;
if (current_kprobe->post_handler)
current_kprobe->post_handler(current_kprobe, regs, 0);
resume_execution(current_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 (current_kprobe->fault_handler &&
current_kprobe->fault_handler(current_kprobe, regs, trapnr))
return 1;
if (kprobe_status & KPROBE_HIT_SS) {
resume_execution(current_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 (current_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,31 @@
/* MN10300 Kernel thread trampoline function
*
* Copyright (C) 2007 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.
*/
.text
###############################################################################
#
# kernel_thread_helper - trampoline for kernel_thread()
#
# On entry:
# A2 = address of function to call
# D2 = function argument
#
###############################################################################
.globl kernel_thread_helper
.type kernel_thread_helper,@function
kernel_thread_helper:
mov do_exit,d1
mov d1,(sp)
mov d1,mdr
mov d2,d0
jmp (a2)
.size kernel_thread_helper,.-kernel_thread_helper

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,191 @@
###############################################################################
#
# 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 <asm/proc/cache.h>
#include <asm/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 0x01,(__tx_break,a3) # handle transmit break request
bne mnsc_vdma_tx_break
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 (__tx_info_buffer,a3),a2 # get the uart_info struct for Tx
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 +(GxICR_LEVEL_1|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_break:
movhu (SCxCTR,e2),d2 # turn on break mode
or SC01CTR_BKE,d2
movhu d2,(SCxCTR,e2)
mov +(GxICR_LEVEL_1|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,126 @@
/* 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
#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_break; /* transmit break 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 void *_rxb; /* receive buffer register pointer */
volatile void *_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,59 @@
###############################################################################
#
# 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>
.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
mov d0,(watchdog_alert_counter)
ret [],0
.size touch_nmi_watchdog,.-touch_nmi_watchdog

View File

@ -0,0 +1,183 @@
/* 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 <asm/system.h>
#include <asm/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 <asm/proc/clock.h>
static DEFINE_SPINLOCK(watchdog_print_lock);
static unsigned int watchdog;
static unsigned int watchdog_hz = 1;
unsigned int watchdog_alert_counter;
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.
*
* as these watchdog NMI IRQs are generated on every CPU, we only
* have to check the current processor.
*
* 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();
}
}
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 = smp_processor_id();
u8 wdt, tmp;
wdt = WDCTR & ~WDCTR_WDCNE;
WDCTR = wdt;
tmp = WDCTR;
NMICR = NMICR_WDIF;
nmi_count(cpu)++;
kstat_this_cpu.irqs[NMIIRQ]++;
sum = irq_stat[cpu].__irq_count;
if (last_irq_sums[cpu] == sum) {
/*
* Ayiee, looks like this CPU is stuck ...
* wait a few IRQs (5 seconds) before doing the oops ...
*/
watchdog_alert_counter++;
if (watchdog_alert_counter == 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);
show_registers(regs);
printk("console shuts up ...\n");
console_silent();
spin_unlock(&watchdog_print_lock);
bust_spinlocks(0);
#ifdef CONFIG_GDBSTUB
if (gdbstub_busy)
gdbstub_exception(regs, excep);
else
gdbstub_intercept(regs, excep);
#endif
do_exit(SIGSEGV);
}
} else {
last_irq_sums[cpu] = sum;
watchdog_alert_counter = 0;
}
WDCTR = wdt | WDCTR_WDRST;
tmp = WDCTR;
WDCTR = wdt | WDCTR_WDCNE;
tmp = WDCTR;
}

View File

@ -0,0 +1,37 @@
/* 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>
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);
EXPORT_SYMBOL(__ashrdi3);
EXPORT_SYMBOL(__ashldi3);
EXPORT_SYMBOL(__lshrdi3);
EXPORT_SYMBOL(__negdi2);

View File

@ -0,0 +1,206 @@
/* MN10300 Kernel module helper routines
*
* Copyright (C) 2007 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>
#if 0
#define DEBUGP printk
#else
#define DEBUGP(fmt, ...)
#endif
/*
* allocate storage for a module
*/
void *module_alloc(unsigned long size)
{
if (size == 0)
return NULL;
return vmalloc_exec(size);
}
/*
* free memory returned from module_alloc()
*/
void module_free(struct module *mod, void *module_region)
{
vfree(module_region);
/* FIXME: If module_region == mod->init_region, trim exception
* table entries. */
}
/*
* allow the arch to fix up the section table
* - we don't need anything special
*/
int module_frob_arch_sections(Elf_Ehdr *hdr,
Elf_Shdr *sechdrs,
char *secstrings,
struct module *mod)
{
return 0;
}
static uint32_t reloc_get16(uint8_t *p)
{
return p[0] | (p[1] << 8);
}
static uint32_t reloc_get24(uint8_t *p)
{
return reloc_get16(p) | (p[2] << 16);
}
static uint32_t reloc_get32(uint8_t *p)
{
return reloc_get16(p) | (reloc_get16(p+2) << 16);
}
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 REL relocation
*/
int apply_relocate(Elf32_Shdr *sechdrs,
const char *strtab,
unsigned int symindex,
unsigned int relsec,
struct module *me)
{
printk(KERN_ERR "module %s: RELOCATION unsupported\n",
me->name);
return -ENOEXEC;
}
/*
* 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;
Elf32_Rela *rel = (void *)sechdrs[relsec].sh_addr;
Elf32_Sym *sym;
Elf32_Addr relocation;
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;
switch (ELF32_R_TYPE(rel[i].r_info)) {
/* for the first four relocation types, we add the
* adjustment into the value at the location given */
case R_MN10300_32:
value = reloc_get32(location);
value += relocation;
reloc_put32(location, value);
break;
case R_MN10300_24:
value = reloc_get24(location);
value += relocation;
reloc_put24(location, value);
break;
case R_MN10300_16:
value = reloc_get16(location);
value += relocation;
reloc_put16(location, value);
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;
default:
printk(KERN_ERR "module %s: Unknown relocation: %u\n",
me->name, ELF32_R_TYPE(rel[i].r_info));
return -ENOEXEC;
}
}
return 0;
}
/*
* finish loading the module
*/
int module_finalize(const Elf_Ehdr *hdr,
const Elf_Shdr *sechdrs,
struct module *me)
{
return 0;
}
/*
* finish clearing the module
*/
void module_arch_cleanup(struct module *mod)
{
}

View File

@ -0,0 +1,297 @@
/* 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/smp_lock.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/user.h>
#include <linux/a.out.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 <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/system.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"
/*
* power management idle function, if any..
*/
void (*pm_idle)(void);
EXPORT_SYMBOL(pm_idle);
/*
* 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);
/*
* we use this if we don't have any better idle routine
*/
static void default_idle(void)
{
local_irq_disable();
if (!need_resched())
safe_halt();
else
local_irq_enable();
}
/*
* the idle thread
* - there's no useful work to be done, so just try to conserve power and have
* a low exit latency (ie sit in a loop waiting for somebody to say that
* they'd like to reschedule)
*/
void cpu_idle(void)
{
int cpu = smp_processor_id();
/* endless idle loop with no priority at all */
for (;;) {
while (!need_resched()) {
void (*idle)(void);
smp_rmb();
idle = pm_idle;
if (!idle)
idle = default_idle;
irq_stat[cpu].idle_timestamp = jiffies;
idle();
}
preempt_enable_no_resched();
schedule();
preempt_disable();
}
}
void release_segments(struct mm_struct *mm)
{
}
void machine_restart(char *cmd)
{
#ifdef CONFIG_GDBSTUB
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_GDBSTUB
gdbstub_exit(0);
#endif
}
void machine_power_off(void)
{
#ifdef CONFIG_GDBSTUB
gdbstub_exit(0);
#endif
}
void show_regs(struct pt_regs *regs)
{
}
/*
* create a kernel thread
*/
int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
struct pt_regs regs;
memset(&regs, 0, sizeof(regs));
regs.a2 = (unsigned long) fn;
regs.d2 = (unsigned long) arg;
regs.pc = (unsigned long) kernel_thread_helper;
local_save_flags(regs.epsw);
regs.epsw |= EPSW_IE | EPSW_IM_7;
/* Ok, create the new process.. */
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0,
NULL, NULL);
}
/*
* 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 before we allocate a new thread and copy the current task
* into it so that we can store lazy state into memory
*/
void prepare_to_copy(struct task_struct *tsk)
{
unlazy_fpu(tsk);
}
/*
* set up the kernel stack for a new thread and copy arch-specific thread
* control information
*/
int copy_thread(int nr, unsigned long clone_flags,
unsigned long c_usp, unsigned long ustk_size,
struct task_struct *p, struct pt_regs *kregs)
{
struct pt_regs *c_uregs, *c_kregs, *uregs;
unsigned long c_ksp;
uregs = current->thread.uregs;
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_uregs = (struct pt_regs *) c_ksp;
p->thread.uregs = c_uregs;
*c_uregs = *uregs;
c_uregs->sp = c_usp;
c_uregs->epsw &= ~EPSW_FE; /* my FPU */
c_ksp -= 12; /* allocate function call ABI slack */
/* the new TLS pointer is passed in as arg #5 to sys_clone() */
if (clone_flags & CLONE_SETTLS)
c_uregs->e2 = __frame->d3;
/* set up the return kernel frame if called from kernel_thread() */
c_kregs = c_uregs;
if (kregs != uregs) {
c_ksp -= sizeof(struct pt_regs);
c_kregs = (struct pt_regs *) c_ksp;
*c_kregs = *kregs;
c_kregs->sp = c_usp;
c_kregs->next = c_uregs;
#ifdef CONFIG_MN10300_CURRENT_IN_E2
c_kregs->e2 = (unsigned long) p; /* current */
#endif
c_ksp -= 12; /* allocate function call ABI slack */
}
/* set up things up so the scheduler can start the new task */
p->thread.__frame = c_kregs;
p->thread.a3 = (unsigned long) c_kregs;
p->thread.sp = c_ksp;
p->thread.pc = (unsigned long) ret_from_fork;
p->thread.wchan = (unsigned long) ret_from_fork;
p->thread.usp = c_usp;
return 0;
}
/*
* clone a process
* - tlsptr is retrieved by copy_thread() from __frame->d3
*/
asmlinkage long sys_clone(unsigned long clone_flags, unsigned long newsp,
int __user *parent_tidptr, int __user *child_tidptr,
int __user *tlsptr)
{
return do_fork(clone_flags, newsp ?: __frame->sp, __frame, 0,
parent_tidptr, child_tidptr);
}
asmlinkage long sys_fork(void)
{
return do_fork(SIGCHLD, __frame->sp, __frame, 0, NULL, NULL);
}
asmlinkage long sys_vfork(void)
{
return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, __frame->sp, __frame,
0, NULL, NULL);
}
asmlinkage long sys_execve(char __user *name,
char __user * __user *argv,
char __user * __user *envp)
{
char *filename;
int error;
lock_kernel();
filename = getname(name);
error = PTR_ERR(filename);
if (!IS_ERR(filename)) {
error = do_execve(filename, argv, envp, __frame);
if (error == 0)
current->ptrace &= ~PT_DTRACE;
putname(filename);
}
unlock_kernel();
return error;
}
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,379 @@
/* 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/smp_lock.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/user.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/system.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);
}
/*
* this routine will put a word on the processes privileged stack.
* the offset is how far from the base addr as stored in the TSS.
* this routine assumes that all the privileged stacks are in our
* data space.
*/
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;
}
static inline unsigned long get_fpregs(struct fpu_state_struct *buf,
struct task_struct *tsk)
{
return __copy_to_user(buf, &tsk->thread.fpu_state,
sizeof(struct fpu_state_struct));
}
static inline unsigned long set_fpregs(struct task_struct *tsk,
struct fpu_state_struct *buf)
{
return __copy_from_user(&tsk->thread.fpu_state, buf,
sizeof(struct fpu_state_struct));
}
static inline void fpsave_init(struct task_struct *task)
{
memset(&task->thread.fpu_state, 0, sizeof(struct fpu_state_struct));
}
/*
* make sure the single step bit is not set
*/
void ptrace_disable(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
}
/*
* set the single step bit
*/
void ptrace_enable(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
}
/*
* handle the arch-specific side of process tracing
*/
long arch_ptrace(struct task_struct *child, long request, long addr, long data)
{
struct fpu_state_struct fpu_state;
int i, ret;
switch (request) {
/* read the word at location addr. */
case PTRACE_PEEKTEXT: {
unsigned long tmp;
int copied;
copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
ret = -EIO;
if (copied != sizeof(tmp))
break;
ret = put_user(tmp, (unsigned long *) data);
break;
}
/* read the word at location addr. */
case PTRACE_PEEKDATA: {
unsigned long tmp;
int copied;
copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
ret = -EIO;
if (copied != sizeof(tmp))
break;
ret = put_user(tmp, (unsigned long *) data);
break;
}
/* read the word at location addr in the USER area. */
case PTRACE_PEEKUSR: {
unsigned long tmp;
ret = -EIO;
if ((addr & 3) || addr < 0 ||
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, (unsigned long *) data);
break;
}
/* write the word at location addr. */
case PTRACE_POKETEXT:
case PTRACE_POKEDATA:
if (access_process_vm(child, addr, &data, sizeof(data), 1) ==
sizeof(data))
ret = 0;
else
ret = -EIO;
break;
/* write the word at location addr in the USER area */
case PTRACE_POKEUSR:
ret = -EIO;
if ((addr & 3) || addr < 0 ||
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;
/* continue and stop at next (return from) syscall */
case PTRACE_SYSCALL:
/* restart after signal. */
case PTRACE_CONT:
ret = -EIO;
if ((unsigned long) data > _NSIG)
break;
if (request == PTRACE_SYSCALL)
set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
else
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
child->exit_code = data;
ptrace_disable(child);
wake_up_process(child);
ret = 0;
break;
/*
* make the child exit
* - the best I can do is send it a sigkill
* - perhaps it should be put in the status that it wants to
* exit
*/
case PTRACE_KILL:
ret = 0;
if (child->exit_state == EXIT_ZOMBIE) /* already dead */
break;
child->exit_code = SIGKILL;
clear_tsk_thread_flag(child, TIF_SINGLESTEP);
ptrace_disable(child);
wake_up_process(child);
break;
case PTRACE_SINGLESTEP: /* set the trap flag. */
#ifndef CONFIG_MN10300_USING_JTAG
ret = -EIO;
if ((unsigned long) data > _NSIG)
break;
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
ptrace_enable(child);
child->exit_code = data;
wake_up_process(child);
ret = 0;
#else
ret = -EINVAL;
#endif
break;
case PTRACE_DETACH: /* detach a process that was attached. */
ret = ptrace_detach(child, data);
break;
/* Get all gp regs from the child. */
case PTRACE_GETREGS: {
unsigned long tmp;
if (!access_ok(VERIFY_WRITE, (unsigned *) data, NR_PTREGS << 2)) {
ret = -EIO;
break;
}
for (i = 0; i < NR_PTREGS << 2; i += 4) {
tmp = get_stack_long(child, ptrace_regid_to_frame[i]);
__put_user(tmp, (unsigned long *) data);
data += sizeof(tmp);
}
ret = 0;
break;
}
case PTRACE_SETREGS: { /* Set all gp regs in the child. */
unsigned long tmp;
if (!access_ok(VERIFY_READ, (unsigned long *)data,
sizeof(struct pt_regs))) {
ret = -EIO;
break;
}
for (i = 0; i < NR_PTREGS << 2; i += 4) {
__get_user(tmp, (unsigned long *) data);
put_stack_long(child, ptrace_regid_to_frame[i], tmp);
data += sizeof(tmp);
}
ret = 0;
break;
}
case PTRACE_GETFPREGS: { /* Get the child FPU state. */
if (is_using_fpu(child)) {
unlazy_fpu(child);
fpu_state = child->thread.fpu_state;
} else {
memset(&fpu_state, 0, sizeof(fpu_state));
}
ret = -EIO;
if (copy_to_user((void *) data, &fpu_state,
sizeof(fpu_state)) == 0)
ret = 0;
break;
}
case PTRACE_SETFPREGS: { /* Set the child FPU state. */
ret = -EFAULT;
if (copy_from_user(&fpu_state, (const void *) data,
sizeof(fpu_state)) == 0) {
fpu_kill_state(child);
child->thread.fpu_state = fpu_state;
set_using_fpu(child);
ret = 0;
}
break;
}
case PTRACE_SETOPTIONS: {
if (data & PTRACE_O_TRACESYSGOOD)
child->ptrace |= PT_TRACESYSGOOD;
else
child->ptrace &= ~PT_TRACESYSGOOD;
ret = 0;
break;
}
default:
ret = -EIO;
break;
}
return ret;
}
/*
* notification of system call entry/exit
* - triggered by current->work.syscall_trace
*/
asmlinkage void do_syscall_trace(struct pt_regs *regs, int entryexit)
{
#if 0
/* just in case... */
printk(KERN_DEBUG "[%d] syscall_%lu(%lx,%lx,%lx,%lx) = %lx\n",
current->pid,
regs->orig_d0,
regs->a0,
regs->d1,
regs->a3,
regs->a2,
regs->d0);
return;
#endif
if (!test_thread_flag(TIF_SYSCALL_TRACE) &&
!test_thread_flag(TIF_SINGLESTEP))
return;
if (!(current->ptrace & PT_PTRACED))
return;
/* the 0x80 provides a way for the tracing parent to distinguish
between a syscall stop and SIGTRAP delivery */
ptrace_notify(SIGTRAP |
((current->ptrace & PT_TRACESYSGOOD) &&
!test_thread_flag(TIF_SINGLESTEP) ? 0x80 : 0));
/*
* this isn't the same as continuing with a signal, but it will do
* for normal use. strace only continues with a signal if the
* stopping signal is not SIGTRAP. -brl
*/
if (current->exit_code) {
send_sig(current->exit_code, current, 1);
current->exit_code = 0;
}
}

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

@ -0,0 +1,173 @@
/* 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);
/* last time the RTC got updated */
static long last_rtc_update;
/* time for RTC to update itself in ioclks */
static unsigned long mn10300_rtc_update_period;
/*
* read the current RTC time
*/
unsigned long __init get_initial_rtc_time(void)
{
struct rtc_time tm;
get_rtc_time(&tm);
return mktime(tm.tm_year, tm.tm_mon, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
}
/*
* 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)
BCD_TO_BIN(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) {
BIN_TO_BCD(real_seconds);
BIN_TO_BCD(real_minutes);
}
CMOS_WRITE(real_seconds, RTC_SECONDS);
CMOS_WRITE(real_minutes, RTC_MINUTES);
} else {
printk(KERN_WARNING
"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;
}
void check_rtc_time(void)
{
/* the RTC clock just finished ticking over again this second
* - if we have an externally synchronized Linux clock, then update
* RTC clock accordingly every ~11 minutes. set_rtc_mmss() has to be
* called as close as possible to 500 ms before the new second starts.
*/
if ((time_status & STA_UNSYNC) == 0 &&
xtime.tv_sec > last_rtc_update + 660 &&
xtime.tv_nsec / 1000 >= 500000 - ((unsigned) TICK_SIZE) / 2 &&
xtime.tv_nsec / 1000 <= 500000 + ((unsigned) TICK_SIZE) / 2
) {
if (set_rtc_mmss(xtime.tv_sec) == 0)
last_rtc_update = xtime.tv_sec;
else
/* do it again in 60s */
last_rtc_update = xtime.tv_sec - 600;
}
}
/*
* calibrate the TSC clock against the RTC
*/
void __init calibrate_clock(void)
{
unsigned long count0, counth, count1;
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;
RTCRA |= RTCRA_DVR;
RTCRA &= ~RTCRA_DVR;
RTCRB &= ~RTCRB_SET;
/* work out the clock speed by counting clock cycles between ends of
* the RTC update cycle - track the RTC through one complete update
* cycle (1 second)
*/
startup_timestamp_counter();
while (!(RTCRA & RTCRA_UIP)) {}
while ((RTCRA & RTCRA_UIP)) {}
count0 = TMTSCBC;
while (!(RTCRA & RTCRA_UIP)) {}
counth = TMTSCBC;
while ((RTCRA & RTCRA_UIP)) {}
count1 = TMTSCBC;
shutdown_timestamp_counter();
MN10300_TSCCLK = count0 - count1; /* the timers count down */
mn10300_rtc_update_period = counth - count1;
MN10300_TSC_PER_HZ = MN10300_TSCCLK / HZ;
}

View File

@ -0,0 +1,149 @@
/* MN10300 Semaphore implementation
*
* 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/module.h>
#include <asm/semaphore.h>
struct sem_waiter {
struct list_head list;
struct task_struct *task;
};
#if SEMAPHORE_DEBUG
void semtrace(struct semaphore *sem, const char *str)
{
if (sem->debug)
printk(KERN_DEBUG "[%d] %s({%d,%d})\n",
current->pid,
str,
atomic_read(&sem->count),
list_empty(&sem->wait_list) ? 0 : 1);
}
#else
#define semtrace(SEM, STR) do { } while (0)
#endif
/*
* wait for a token to be granted from a semaphore
* - entered with lock held and interrupts disabled
*/
void __down(struct semaphore *sem, unsigned long flags)
{
struct task_struct *tsk = current;
struct sem_waiter waiter;
semtrace(sem, "Entering __down");
/* set up my own style of waitqueue */
waiter.task = tsk;
get_task_struct(tsk);
list_add_tail(&waiter.list, &sem->wait_list);
/* we don't need to touch the semaphore struct anymore */
spin_unlock_irqrestore(&sem->wait_lock, flags);
/* wait to be given the semaphore */
set_task_state(tsk, TASK_UNINTERRUPTIBLE);
for (;;) {
if (!waiter.task)
break;
schedule();
set_task_state(tsk, TASK_UNINTERRUPTIBLE);
}
tsk->state = TASK_RUNNING;
semtrace(sem, "Leaving __down");
}
EXPORT_SYMBOL(__down);
/*
* interruptibly wait for a token to be granted from a semaphore
* - entered with lock held and interrupts disabled
*/
int __down_interruptible(struct semaphore *sem, unsigned long flags)
{
struct task_struct *tsk = current;
struct sem_waiter waiter;
int ret;
semtrace(sem, "Entering __down_interruptible");
/* set up my own style of waitqueue */
waiter.task = tsk;
get_task_struct(tsk);
list_add_tail(&waiter.list, &sem->wait_list);
/* we don't need to touch the semaphore struct anymore */
set_task_state(tsk, TASK_INTERRUPTIBLE);
spin_unlock_irqrestore(&sem->wait_lock, flags);
/* wait to be given the semaphore */
ret = 0;
for (;;) {
if (!waiter.task)
break;
if (unlikely(signal_pending(current)))
goto interrupted;
schedule();
set_task_state(tsk, TASK_INTERRUPTIBLE);
}
out:
tsk->state = TASK_RUNNING;
semtrace(sem, "Leaving __down_interruptible");
return ret;
interrupted:
spin_lock_irqsave(&sem->wait_lock, flags);
list_del(&waiter.list);
spin_unlock_irqrestore(&sem->wait_lock, flags);
ret = 0;
if (!waiter.task) {
put_task_struct(current);
ret = -EINTR;
}
goto out;
}
EXPORT_SYMBOL(__down_interruptible);
/*
* release a single token back to a semaphore
* - entered with lock held and interrupts disabled
*/
void __up(struct semaphore *sem)
{
struct task_struct *tsk;
struct sem_waiter *waiter;
semtrace(sem, "Entering __up");
/* grant the token to the process at the front of the queue */
waiter = list_entry(sem->wait_list.next, struct sem_waiter, list);
/* We must be careful not to touch 'waiter' after we set ->task = NULL.
* It is an allocated on the waiter's stack and may become invalid at
* any time after that point (due to a wakeup from another source).
*/
list_del_init(&waiter->list);
tsk = waiter->task;
smp_mb();
waiter->task = NULL;
wake_up_process(tsk);
put_task_struct(tsk);
semtrace(sem, "Leaving __up");
}
EXPORT_SYMBOL(__up);

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

@ -0,0 +1,298 @@
/* 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/slab.h>
#include <linux/user.h>
#include <linux/a.out.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 <asm/processor.h>
#include <linux/console.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/setup.h>
#include <asm/io.h>
#include <asm/smp.h>
#include <asm/proc/proc.h>
#include <asm/busctl-regs.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;
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 3
static const char *const mn10300_cputypes[] = {
"am33v1",
"am33v2",
"am34v1",
"unknown"
};
/*
*
*/
static void __init parse_mem_cmdline(char **cmdline_p)
{
char *from, *to, c;
/* save unparsed command line copy for /proc/cmdline */
strcpy(boot_command_line, redboot_command_line);
/* see if there's an explicit memory size option */
from = redboot_command_line;
to = redboot_command_line;
c = ' ';
for (;;) {
if (c == ' ' && !memcmp(from, "mem=", 4)) {
if (to != redboot_command_line)
to--;
memory_size = memparse(from + 4, &from);
}
c = *(from++);
if (!c)
break;
*(to++) = c;
}
*to = '\0';
*cmdline_p = redboot_command_line;
if (memory_size == 0)
panic("Memory size not known\n");
memory_end = (unsigned long) CONFIG_KERNEL_RAM_BASE_ADDRESS +
memory_size;
if (memory_end > phys_memory_end)
memory_end = phys_memory_end;
}
/*
* 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();
parse_mem_cmdline(cmdline_p);
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;
#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT)
#define PFN_DOWN(x) ((x) >> PAGE_SHIFT)
#define PFN_PHYS(x) ((x) << PAGE_SHIFT)
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, 1,
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;
unsigned long base, size;
type = (CPUREV & CPUREV_TYPE) >> CPUREV_TYPE_S;
if (type > mn10300_known_cpus)
type = mn10300_known_cpus;
printk(KERN_INFO "Matsushita %s, rev %ld\n",
mn10300_cputypes[type],
(cpurev & CPUREV_REVISION) >> CPUREV_REVISION_S);
/* determine the memory size and base from the memory controller regs */
memory_size = 0;
base = SDBASE(0);
if (base & SDBASE_CE) {
size = (base & SDBASE_CBAM) << SDBASE_CBAM_SHIFT;
size = ~size + 1;
base &= SDBASE_CBA;
printk(KERN_INFO "SDRAM[0]: %luMb @%08lx\n", size >> 20, base);
memory_size += size;
phys_memory_base = base;
}
base = SDBASE(1);
if (base & SDBASE_CE) {
size = (base & SDBASE_CBAM) << SDBASE_CBAM_SHIFT;
size = ~size + 1;
base &= SDBASE_CBA;
printk(KERN_INFO "SDRAM[1]: %luMb @%08lx\n", size >> 20, base);
memory_size += size;
if (phys_memory_base == 0)
phys_memory_base = base;
}
phys_memory_end = phys_memory_base + memory_size;
#ifdef CONFIG_FPU
fpu_init_state();
#endif
}
/*
* Get CPU information for use by the procfs.
*/
static int show_cpuinfo(struct seq_file *m, void *v)
{
unsigned long cpurev = CPUREV, type, icachesz, dcachesz;
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 : 0\n"
"vendor_id : Matsushita\n"
"cpu core : %s\n"
"cpu rev : %lu\n"
"model name : " PROCESSOR_MODEL_NAME "\n"
"icache size: %lu\n"
"dcache size: %lu\n",
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,
loops_per_jiffy / (500000 / HZ),
(loops_per_jiffy / (5000 / HZ)) % 100
);
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)
{
}
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,564 @@
/* 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/smp_lock.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 <asm/cacheflush.h>
#include <asm/ucontext.h>
#include <asm/uaccess.h>
#include <asm/fpu.h>
#include "sigframe.h"
#define DEBUG_SIG 0
#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
/*
* atomically swap in the new signal mask, and wait for a signal.
*/
asmlinkage long sys_sigsuspend(int history0, int history1, old_sigset_t mask)
{
mask &= _BLOCKABLE;
spin_lock_irq(&current->sighand->siglock);
current->saved_sigmask = current->blocked;
siginitset(&current->blocked, mask);
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
current->state = TASK_INTERRUPTIBLE;
schedule();
set_thread_flag(TIF_RESTORE_SIGMASK);
return -ERESTARTNOHAND;
}
/*
* set signal action syscall
*/
asmlinkage long sys_sigaction(int sig,
const struct old_sigaction __user *act,
struct old_sigaction __user *oact)
{
struct k_sigaction new_ka, old_ka;
int ret;
if (act) {
old_sigset_t mask;
if (verify_area(VERIFY_READ, act, sizeof(*act)) ||
__get_user(new_ka.sa.sa_handler, &act->sa_handler) ||
__get_user(new_ka.sa.sa_restorer, &act->sa_restorer))
return -EFAULT;
__get_user(new_ka.sa.sa_flags, &act->sa_flags);
__get_user(mask, &act->sa_mask);
siginitset(&new_ka.sa.sa_mask, mask);
}
ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
if (!ret && oact) {
if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) ||
__put_user(old_ka.sa.sa_handler, &oact->sa_handler) ||
__put_user(old_ka.sa.sa_restorer, &oact->sa_restorer))
return -EFAULT;
__put_user(old_ka.sa.sa_flags, &oact->sa_flags);
__put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
}
return ret;
}
/*
* set alternate signal stack syscall
*/
asmlinkage long sys_sigaltstack(const stack_t __user *uss, stack_t *uoss)
{
return do_sigaltstack(uss, uoss, __frame->sp);
}
/*
* 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;
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 = (struct sigframe __user *) __frame->sp;
sigset_t set;
long d0;
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;
sigdelsetmask(&set, ~_BLOCKABLE);
spin_lock_irq(&current->sighand->siglock);
current->blocked = set;
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
if (restore_sigcontext(__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 =
(struct rt_sigframe __user *) __frame->sp;
sigset_t set;
unsigned long d0;
if (verify_area(VERIFY_READ, frame, sizeof(*frame)))
goto badframe;
if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
goto badframe;
sigdelsetmask(&set, ~_BLOCKABLE);
spin_lock_irq(&current->sighand->siglock);
current->blocked = set;
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
if (restore_sigcontext(__frame, &frame->uc.uc_mcontext, &d0))
goto badframe;
if (do_sigaltstack(&frame->uc.uc_stack, NULL, __frame->sp) == -EFAULT)
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 (!on_sig_stack(sp))
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;
set_fs(USER_DS);
/* the tracer may want to single-step inside the handler */
if (test_thread_flag(TIF_SINGLESTEP))
ptrace_notify(SIGTRAP);
#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_sig(SIGSEGV, 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) ||
__put_user((void *)current->sas_ss_sp, &frame->uc.uc_stack.ss_sp) ||
__put_user(sas_ss_flags(regs->sp), &frame->uc.uc_stack.ss_flags) ||
__put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size) ||
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;
set_fs(USER_DS);
/* the tracer may want to single-step inside the handler */
if (test_thread_flag(TIF_SINGLESTEP))
ptrace_notify(SIGTRAP);
#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_sig(SIGSEGV, current);
return -EFAULT;
}
/*
* handle the actual delivery of a signal to userspace
*/
static int handle_signal(int sig,
siginfo_t *info, struct k_sigaction *ka,
sigset_t *oldset, struct pt_regs *regs)
{
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;
regs->pc -= 2;
}
}
/* 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 == 0) {
spin_lock_irq(&current->sighand->siglock);
sigorsets(&current->blocked, &current->blocked,
&ka->sa.sa_mask);
if (!(ka->sa.sa_flags & SA_NODEFER))
sigaddset(&current->blocked, sig);
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
}
return ret;
}
/*
* handle a potential signal
*/
static void do_signal(struct pt_regs *regs)
{
struct k_sigaction ka;
siginfo_t info;
sigset_t *oldset;
int signr;
/* we want the common case to go fast, which is why we may in certain
* cases get here from kernel mode */
if (!user_mode(regs))
return;
if (test_thread_flag(TIF_RESTORE_SIGMASK))
oldset = &current->saved_sigmask;
else
oldset = &current->blocked;
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
if (signr > 0) {
if (handle_signal(signr, &info, &ka, oldset, regs) == 0) {
/* a signal was successfully delivered; the saved
* sigmask will have been stored in the signal frame,
* and will be restored by sigreturn, so we can simply
* clear the TIF_RESTORE_SIGMASK flag */
if (test_thread_flag(TIF_RESTORE_SIGMASK))
clear_thread_flag(TIF_RESTORE_SIGMASK);
}
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;
regs->pc -= 2;
break;
case -ERESTART_RESTARTBLOCK:
regs->d0 = __NR_restart_syscall;
regs->pc -= 2;
break;
}
}
/* if there's no signal to deliver, we just put the saved sigmask
* back */
if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
clear_thread_flag(TIF_RESTORE_SIGMASK);
sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
}
}
/*
* 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 | _TIF_RESTORE_SIGMASK))
do_signal(regs);
}

View File

@ -0,0 +1,71 @@
###############################################################################
#
# 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>
.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 (__frame),d0
mov d0,(THREAD_FRAME,a0)
mov __switch_back,d0
mov d0,(THREAD_PC,a0)
mov sp,a2
mov a2,(THREAD_SP,a0)
mov a3,(THREAD_A3,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_FRAME,a1),a2
mov a2,(__frame)
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

View File

@ -0,0 +1,193 @@
/* 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/smp_lock.h>
#include <linux/sem.h>
#include <linux/msg.h>
#include <linux/shm.h>
#include <linux/stat.h>
#include <linux/mman.h>
#include <linux/file.h>
#include <linux/utsname.h>
#include <linux/syscalls.h>
#include <linux/tty.h>
#include <asm/uaccess.h>
#define MIN_MAP_ADDR PAGE_SIZE /* minimum fixed mmap address */
/*
* sys_pipe() is the normal C calling standard for creating
* a pipe. It's not the way Unix traditionally does this, though.
*/
asmlinkage long sys_pipe(unsigned long __user *fildes)
{
int fd[2];
int error;
error = do_pipe(fd);
if (!error) {
if (copy_to_user(fildes, fd, 2 * sizeof(int)))
error = -EFAULT;
}
return error;
}
/*
* memory mapping syscall
*/
asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags,
unsigned long fd, unsigned long pgoff)
{
struct file *file = NULL;
long error = -EINVAL;
flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
if (flags & MAP_FIXED && addr < MIN_MAP_ADDR)
goto out;
error = -EBADF;
if (!(flags & MAP_ANONYMOUS)) {
file = fget(fd);
if (!file)
goto out;
}
down_write(&current->mm->mmap_sem);
error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
up_write(&current->mm->mmap_sem);
if (file)
fput(file);
out:
return error;
}
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_mmap2(addr, len, prot, flags, fd, offset >> PAGE_SHIFT);
}
struct sel_arg_struct {
unsigned long n;
fd_set *inp;
fd_set *outp;
fd_set *exp;
struct timeval *tvp;
};
asmlinkage int old_select(struct sel_arg_struct __user *arg)
{
struct sel_arg_struct a;
if (copy_from_user(&a, arg, sizeof(a)))
return -EFAULT;
/* sys_select() does the appropriate kernel locking */
return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp);
}
/*
* sys_ipc() is the de-multiplexer for the SysV IPC calls..
*
* This is really horribly ugly.
*/
asmlinkage long sys_ipc(uint call, int first, int second,
int third, void __user *ptr, long fifth)
{
int version, ret;
version = call >> 16; /* hack for backward compatibility */
call &= 0xffff;
switch (call) {
case SEMOP:
return sys_semtimedop(first, (struct sembuf __user *)ptr,
second, NULL);
case SEMTIMEDOP:
return sys_semtimedop(first, (struct sembuf __user *)ptr,
second,
(const struct timespec __user *)fifth);
case SEMGET:
return sys_semget(first, second, third);
case SEMCTL: {
union semun fourth;
if (!ptr)
return -EINVAL;
if (get_user(fourth.__pad, (void __user * __user *) ptr))
return -EFAULT;
return sys_semctl(first, second, third, fourth);
}
case MSGSND:
return sys_msgsnd(first, (struct msgbuf __user *) ptr,
second, third);
case MSGRCV:
switch (version) {
case 0: {
struct ipc_kludge tmp;
if (!ptr)
return -EINVAL;
if (copy_from_user(&tmp,
(struct ipc_kludge __user *) ptr,
sizeof(tmp)))
return -EFAULT;
return sys_msgrcv(first, tmp.msgp, second,
tmp.msgtyp, third);
}
default:
return sys_msgrcv(first,
(struct msgbuf __user *) ptr,
second, fifth, third);
}
case MSGGET:
return sys_msgget((key_t) first, second);
case MSGCTL:
return sys_msgctl(first, second,
(struct msqid_ds __user *) ptr);
case SHMAT:
switch (version) {
default: {
ulong raddr;
ret = do_shmat(first, (char __user *) ptr, second,
&raddr);
if (ret)
return ret;
return put_user(raddr, (ulong *) third);
}
case 1: /* iBCS2 emulator entry point */
if (!segment_eq(get_fs(), get_ds()))
return -EINVAL;
return do_shmat(first, (char __user *) ptr, second,
(ulong *) third);
}
case SHMDT:
return sys_shmdt((char __user *)ptr);
case SHMGET:
return sys_shmget(first, second, third);
case SHMCTL:
return sys_shmctl(first, second,
(struct shmid_ds __user *) ptr);
default:
return -EINVAL;
}
}

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

@ -0,0 +1,129 @@
/* MN10300 Low level time management
*
* Copyright (C) 2007 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 <asm/irq.h>
#include <asm/div64.h>
#include <asm/processor.h>
#include <asm/intctl-regs.h>
#include <asm/rtc.h>
#ifdef CONFIG_MN10300_RTC
unsigned long mn10300_ioclk; /* system I/O clock frequency */
unsigned long mn10300_iobclk; /* system I/O clock frequency */
unsigned long mn10300_tsc_per_HZ; /* number of ioclks per jiffy */
#endif /* CONFIG_MN10300_RTC */
static unsigned long mn10300_last_tsc; /* time-stamp counter at last time
* interrupt occurred */
static irqreturn_t timer_interrupt(int irq, void *dev_id);
static struct irqaction timer_irq = {
.handler = timer_interrupt,
.flags = IRQF_DISABLED | IRQF_SHARED | IRQF_TIMER,
.mask = CPU_MASK_NONE,
.name = "timer",
};
/*
* scheduler clock - returns current time in nanosec units.
*/
unsigned long long sched_clock(void)
{
union {
unsigned long long l;
u32 w[2];
} quot;
quot.w[0] = mn10300_last_tsc - get_cycles();
quot.w[1] = 1000000000;
asm("mulu %2,%3,%0,%1"
: "=r"(quot.w[1]), "=r"(quot.w[0])
: "0"(quot.w[1]), "1"(quot.w[0])
: "cc");
do_div(quot.l, MN10300_TSCCLK);
return quot.l;
}
/*
* advance the kernel's time keeping clocks (xtime and jiffies)
* - we use Timer 0 & 1 cascaded as a clock to nudge us the next time
* there's a need to update
*/
static irqreturn_t timer_interrupt(int irq, void *dev_id)
{
unsigned tsc, elapse;
write_seqlock(&xtime_lock);
while (tsc = get_cycles(),
elapse = mn10300_last_tsc - tsc, /* time elapsed since last
* tick */
elapse > MN10300_TSC_PER_HZ
) {
mn10300_last_tsc -= MN10300_TSC_PER_HZ;
/* advance the kernel's time tracking system */
profile_tick(CPU_PROFILING);
do_timer(1);
update_process_times(user_mode(get_irq_regs()));
check_rtc_time();
}
write_sequnlock(&xtime_lock);
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;
startup_timestamp_counter();
printk(KERN_INFO
"timestamp counter I/O clock running at %lu.%02lu"
" (calibrated against RTC)\n",
MN10300_TSCCLK / 1000000, (MN10300_TSCCLK / 10000) % 100);
xtime.tv_sec = get_initial_rtc_time();
xtime.tv_nsec = 0;
mn10300_last_tsc = TMTSCBC;
/* use timer 0 & 1 cascaded to tick at as close to HZ as possible */
setup_irq(TMJCIRQ, &timer_irq);
set_intr_level(TMJCIRQ, TMJCICR_LEVEL);
startup_jiffies_counter();
#ifdef CONFIG_MN10300_WD_TIMER
/* start the watchdog timer */
watchdog_go();
#endif
}

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

@ -0,0 +1,619 @@
/* 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/smp_lock.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 <asm/processor.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/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 <asm/unit/leds.h>
#include <asm/fpu.h>
#include <asm/gdb-stub.h>
#include <asm/sections.h>
#if (CONFIG_INTERRUPT_VECTOR_BASE & 0xffffff)
#error "INTERRUPT_VECTOR_BASE not aligned to 16MiB boundary!"
#endif
struct pt_regs *__frame; /* current frame pointer */
EXPORT_SYMBOL(__frame);
int kstack_depth_to_print = 24;
spinlock_t die_lock = __SPIN_LOCK_UNLOCKED(die_lock);
ATOMIC_NOTIFIER_HEAD(mn10300_die_chain);
/*
* These constants are for searching for possible module text
* segments. MODULE_RANGE is a guess of how much space is likely
* to be vmalloced.
*/
#define MODULE_RANGE (8 * 1024 * 1024)
#define DO_ERROR(signr, prologue, str, name) \
asmlinkage void name(struct pt_regs *regs, u32 intcode) \
{ \
prologue; \
if (die_if_no_fixup(str, regs, intcode)) \
return; \
force_sig(signr, current); \
}
#define DO_EINFO(signr, prologue, str, name, sicode) \
asmlinkage void name(struct pt_regs *regs, u32 intcode) \
{ \
siginfo_t info; \
prologue; \
if (die_if_no_fixup(str, regs, intcode)) \
return; \
info.si_signo = signr; \
if (signr == SIGILL && sicode == ILL_ILLOPC) { \
uint8_t opcode; \
if (get_user(opcode, (uint8_t __user *)regs->pc) == 0) \
if (opcode == 0xff) \
info.si_signo = SIGTRAP; \
} \
info.si_errno = 0; \
info.si_code = sicode; \
info.si_addr = (void *) regs->pc; \
force_sig_info(info.si_signo, &info, current); \
}
DO_ERROR(SIGTRAP, {}, "trap", trap);
DO_ERROR(SIGSEGV, {}, "ibreak", ibreak);
DO_ERROR(SIGSEGV, {}, "obreak", obreak);
DO_EINFO(SIGSEGV, {}, "access error", access_error, SEGV_ACCERR);
DO_EINFO(SIGSEGV, {}, "insn access error", insn_acc_error, SEGV_ACCERR);
DO_EINFO(SIGSEGV, {}, "data access error", data_acc_error, SEGV_ACCERR);
DO_EINFO(SIGILL, {}, "privileged opcode", priv_op, ILL_PRVOPC);
DO_EINFO(SIGILL, {}, "invalid opcode", invalid_op, ILL_ILLOPC);
DO_EINFO(SIGILL, {}, "invalid ex opcode", invalid_exop, ILL_ILLOPC);
DO_EINFO(SIGBUS, {}, "invalid address", mem_error, BUS_ADRERR);
DO_EINFO(SIGBUS, {}, "bus error", bus_error, BUS_ADRERR);
DO_EINFO(SIGILL, {}, "FPU invalid opcode", fpu_invalid_op, ILL_COPROC);
DO_ERROR(SIGTRAP,
#ifndef CONFIG_MN10300_USING_JTAG
DCR &= ~0x0001,
#else
{},
#endif
"single step", istep);
/*
* handle NMI
*/
asmlinkage void nmi(struct pt_regs *regs, enum exception_code code)
{
/* see if gdbstub wants to deal with it */
#ifdef CONFIG_GDBSTUB
if (gdbstub_intercept(regs, code))
return;
#endif
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 *stack, addr, module_start, module_end;
int i;
printk(KERN_EMERG "\n"
KERN_EMERG "Call Trace:");
stack = sp;
i = 0;
module_start = VMALLOC_START;
module_end = VMALLOC_END;
while (((long) stack & (THREAD_SIZE - 1)) != 0) {
addr = *stack++;
if (__kernel_text_address(addr)) {
#if 1
printk(" [<%08lx>]", addr);
print_symbol(" %s", addr);
printk("\n");
#else
if ((i % 6) == 0)
printk("\n" KERN_EMERG " ");
printk("[<%08lx>] ", addr);
i++;
#endif
}
}
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("\n" KERN_EMERG " ");
printk("%08lx ", *stack++);
}
show_trace(sp);
}
/*
* the architecture-independent dump_stack generator
*/
void dump_stack(void)
{
unsigned long stack;
show_stack(current, &stack);
}
EXPORT_SYMBOL(dump_stack);
/*
* 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 >= 0x90000000UL &&
(unsigned long) current < 0x94000000UL)
printk(KERN_EMERG "Process %s (pid: %d)\n",
current->comm, current->pid);
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 "\n"
KERN_EMERG "Code: ");
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"
KERN_EMERG "%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 "\n"
KERN_EMERG " Please connect to kernel debugger !!\n");
asm volatile ("0: bra 0b");
}
spin_unlock_irq(&die_lock);
do_exit(SIGSEGV);
}
/*
* 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)
{
if (user_mode(regs))
return 0;
peripheral_leds_display_exception(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;
case EXCEP_UNIMPINS:
if (regs->pc && *(uint8_t *)regs->pc == 0xff)
if (notify_die(DIE_BREAKPOINT, str, regs, code, 0, 0))
return 1;
break;
default:
break;
}
/* see if gdbstub wants to deal with it */
#ifdef CONFIG_GDBSTUB
if (gdbstub_intercept(regs, code))
return 1;
#endif
if (notify_die(DIE_GPF, str, regs, code, 0, 0))
return 1;
/* make the process die as the last resort */
die(str, regs, code);
}
/*
* handle unsupported syscall instructions (syscall 1-15)
*/
static asmlinkage void unsupported_syscall(struct pt_regs *regs,
enum exception_code code)
{
struct task_struct *tsk = current;
siginfo_t info;
/* catch a kernel BUG() */
if (code == EXCEP_SYSCALL15 && !user_mode(regs)) {
if (report_bug(regs->pc, regs) == BUG_TRAP_TYPE_BUG) {
#ifdef CONFIG_GDBSTUB
__gdbstub_bug_trap();
#endif
}
}
regs->pc -= 2; /* syscall return addr is _after_ the instruction */
die_if_no_fixup("An unsupported syscall insn was used by the kernel\n",
regs, code);
info.si_signo = SIGILL;
info.si_errno = ENOSYS;
info.si_code = ILL_ILLTRP;
info.si_addr = (void *) regs->pc;
force_sig_info(SIGILL, &info, tsk);
}
/*
* 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 "\n"
KERN_EMERG "Asynchronous I/O Bus Error\n"
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 */
#ifdef CONFIG_GDBSTUB
if (gdbstub_intercept(regs, code))
return;
#endif
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);
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;
mn10300_dcache_flush_inv();
mn10300_icache_inv();
}
/*
* set an interrupt stub to invoke the JTAG unit and then jump to a handler
*/
void __init set_jtag_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 + 1);
vector[0] = 0xff; /* PI to jump into JTAG debugger */
vector[1] = 0xdc; /* jmp handler */
vector[2] = addr;
vector[3] = addr >> 8;
vector[4] = addr >> 16;
vector[5] = addr >> 24;
vector[6] = 0xcb;
vector[7] = 0xcb;
mn10300_dcache_flush_inv();
flush_icache_range((unsigned long) vector, (unsigned long) vector + 8);
}
/*
* initialise the exception table
*/
void __init trap_init(void)
{
set_excp_vector(EXCEP_TRAP, trap);
set_excp_vector(EXCEP_ISTEP, istep);
set_excp_vector(EXCEP_IBREAK, ibreak);
set_excp_vector(EXCEP_OBREAK, obreak);
set_excp_vector(EXCEP_PRIVINS, priv_op);
set_excp_vector(EXCEP_UNIMPINS, invalid_op);
set_excp_vector(EXCEP_UNIMPEXINS, invalid_exop);
set_excp_vector(EXCEP_MEMERR, mem_error);
set_excp_vector(EXCEP_MISALIGN, misalignment);
set_excp_vector(EXCEP_BUSERROR, bus_error);
set_excp_vector(EXCEP_ILLINSACC, insn_acc_error);
set_excp_vector(EXCEP_ILLDATACC, data_acc_error);
set_excp_vector(EXCEP_IOINSACC, insn_acc_error);
set_excp_vector(EXCEP_PRIVINSACC, insn_acc_error);
set_excp_vector(EXCEP_PRIVDATACC, data_acc_error);
set_excp_vector(EXCEP_DATINSACC, insn_acc_error);
set_excp_vector(EXCEP_FPU_DISABLED, fpu_disabled);
set_excp_vector(EXCEP_FPU_UNIMPINS, fpu_invalid_op);
set_excp_vector(EXCEP_FPU_OPERATION, fpu_exception);
set_excp_vector(EXCEP_NMI, nmi);
set_excp_vector(EXCEP_SYSCALL1, unsupported_syscall);
set_excp_vector(EXCEP_SYSCALL2, unsupported_syscall);
set_excp_vector(EXCEP_SYSCALL3, unsupported_syscall);
set_excp_vector(EXCEP_SYSCALL4, unsupported_syscall);
set_excp_vector(EXCEP_SYSCALL5, unsupported_syscall);
set_excp_vector(EXCEP_SYSCALL6, unsupported_syscall);
set_excp_vector(EXCEP_SYSCALL7, unsupported_syscall);
set_excp_vector(EXCEP_SYSCALL8, unsupported_syscall);
set_excp_vector(EXCEP_SYSCALL9, unsupported_syscall);
set_excp_vector(EXCEP_SYSCALL10, unsupported_syscall);
set_excp_vector(EXCEP_SYSCALL11, unsupported_syscall);
set_excp_vector(EXCEP_SYSCALL12, unsupported_syscall);
set_excp_vector(EXCEP_SYSCALL13, unsupported_syscall);
set_excp_vector(EXCEP_SYSCALL14, unsupported_syscall);
set_excp_vector(EXCEP_SYSCALL15, unsupported_syscall);
}
/*
* 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,159 @@
/* 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>
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 : {
*(
.text.head
.text
)
TEXT_TEXT
SCHED_TEXT
LOCK_TEXT
KPROBES_TEXT
*(.fixup)
*(.gnu.warning)
} = 0xcb
_etext = .; /* End of text section */
. = ALIGN(16); /* Exception table */
__start___ex_table = .;
__ex_table : { *(__ex_table) }
__stop___ex_table = .;
BUG_TABLE
RODATA
/* writeable */
.data : { /* Data */
DATA_DATA
CONSTRUCTORS
}
. = ALIGN(4096);
__nosave_begin = .;
.data_nosave : { *(.data.nosave) }
. = ALIGN(4096);
__nosave_end = .;
. = ALIGN(4096);
.data.page_aligned : { *(.data.idt) }
. = ALIGN(32);
.data.cacheline_aligned : { *(.data.cacheline_aligned) }
/* rarely changed data like cpu maps */
. = ALIGN(32);
.data.read_mostly : AT(ADDR(.data.read_mostly)) {
*(.data.read_mostly)
_edata = .; /* End of data section */
}
. = ALIGN(THREAD_SIZE); /* init_task */
.data.init_task : { *(.data.init_task) }
/* might get freed after init */
. = ALIGN(4096);
.smp_locks : AT(ADDR(.smp_locks) - LOAD_OFFSET) {
__smp_locks = .;
*(.smp_locks)
__smp_locks_end = .;
}
/* will be freed after init */
. = ALIGN(4096); /* Init code and data */
__init_begin = .;
.init.text : {
_sinittext = .;
*(.init.text)
_einittext = .;
}
.init.data : { *(.init.data) }
. = ALIGN(16);
__setup_start = .;
.setup.init : { KEEP(*(.init.setup)) }
__setup_end = .;
__initcall_start = .;
.initcall.init : {
INITCALLS
}
__initcall_end = .;
__con_initcall_start = .;
.con_initcall.init : { *(.con_initcall.init) }
__con_initcall_end = .;
SECURITY_INIT
. = 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) }
#ifdef CONFIG_BLK_DEV_INITRD
. = ALIGN(4096);
__initramfs_start = .;
.init.ramfs : { *(.init.ramfs) }
__initramfs_end = .;
#endif
. = ALIGN(32);
__per_cpu_start = .;
.data.percpu : { *(.data.percpu) }
__per_cpu_end = .;
. = ALIGN(4096);
__init_end = .;
/* freed after init ends here */
__bss_start = .; /* BSS */
.bss : {
*(.bss.page_aligned)
*(.bss)
}
. = ALIGN(4);
__bss_stop = .;
_end = . ;
/* This is where the kernel creates the early boot page tables */
. = ALIGN(4096);
pg0 = .;
/* Sections to be discarded */
/DISCARD/ : {
*(.exitcall.exit)
}
STABS_DEBUG
DWARF_DEBUG
}

View File

@ -0,0 +1,7 @@
#
# Makefile for the MN10300-specific library files..
#
lib-y = delay.o usercopy.o checksum.o bitops.o memcpy.o memmove.o memset.o
lib-y += do_csum.o
lib-y += __ashldi3.o __ashrdi3.o __lshrdi3.o negdi2.o

View File

@ -0,0 +1,51 @@
/* MN10300 64-bit arithmetic left shift
*
* 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/cache.h>
.text
.balign L1_CACHE_BYTES
###############################################################################
#
# unsigned long long __ashldi3(unsigned long long value [D1:D0],
# unsigned by [(12,SP)])
#
###############################################################################
.globl __ashldi3
.type __ashldi3,@function
__ashldi3:
mov (12,sp),a0
and +63,a0
beq __ashldi3_zero
cmp +31,a0
bhi __ashldi3_32plus
# the count is in the range 1-31
asl a0,d1
mov +32,a1
sub a0,a1,a1 # a1 = 32 - count
lsr a1,d0,a1 # get overflow from LSW -> MSW
or_asl a1,d1,a0,d0 # insert overflow into MSW and
# shift the LSW
rets
.balign L1_CACHE_BYTES
# the count is in the range 32-63
__ashldi3_32plus:
asl a0,d0,d1
clr d0
__ashldi3_zero:
rets
.size __ashldi3, .-__ashldi3

View File

@ -0,0 +1,52 @@
/* MN10300 64-bit arithmetic right shift
*
* 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/cache.h>
.text
.balign L1_CACHE_BYTES
###############################################################################
#
# unsigned long long __ashrdi3(unsigned long long value [D1:D0],
# unsigned by [(12,SP)])
#
###############################################################################
.globl __ashrdi3
.type __ashrdi3,@function
__ashrdi3:
mov (12,sp),a0
and +63,a0
beq __ashrdi3_zero
cmp +31,a0
bhi __ashrdi3_32plus
# the count is in the range 1-31
lsr a0,d0
mov +32,a1
sub a0,a1,a1 # a1 = 32 - count
asl a1,d1,a1 # get underflow from MSW -> LSW
or_asr a1,d0,a0,d1 # insert underflow into LSW and
# shift the MSW
rets
.balign L1_CACHE_BYTES
# the count is in the range 32-63
__ashrdi3_32plus:
asr a0,d1,d0
ext d0 # sign-extend result through MDR
mov mdr,d1
__ashrdi3_zero:
rets
.size __ashrdi3, .-__ashrdi3

View File

@ -0,0 +1,52 @@
/* MN10300 64-bit logical right shift
*
* 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/cache.h>
.text
.balign L1_CACHE_BYTES
###############################################################################
#
# unsigned long long __lshrdi3(unsigned long long value [D1:D0],
# unsigned by [(12,SP)])
#
###############################################################################
.globl __lshrdi3
.type __lshrdi3,@function
__lshrdi3:
mov (12,sp),a0
and +63,a0
beq __lshrdi3_zero
cmp +31,a0
bhi __lshrdi3_32plus
# the count is in the range 1-31
lsr a0,d0
mov +32,a1
sub a0,a1,a1 # a1 = 32 - count
asl a1,d1,a1 # get underflow from MSW -> LSW
or_lsr a1,d0,a0,d1 # insert underflow into LSW and
# shift the MSW
rets
.balign L1_CACHE_BYTES
# the count is in the range 32-63
__lshrdi3_32plus:
lsr a0,d1,d0
clr d1
__lshrdi3_zero:
rets
.size __lshrdi3, .-__lshrdi3

View File

@ -0,0 +1,61 @@
/* ashrdi3.c extracted from gcc-2.7.2/libgcc2.c which is: */
/* Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
This file is part of GNU CC.
GNU CC 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, or (at your option)
any later version.
GNU CC 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 GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#define BITS_PER_UNIT 8
typedef int SItype __attribute__((mode(SI)));
typedef unsigned int USItype __attribute__((mode(SI)));
typedef int DItype __attribute__((mode(DI)));
typedef int word_type __attribute__((mode(__word__)));
struct DIstruct {
SItype low;
SItype high;
};
union DIunion {
struct DIstruct s;
DItype ll;
};
DItype __ashrdi3(DItype u, word_type b)
{
union DIunion w;
union DIunion uu;
word_type bm;
if (b == 0)
return u;
uu.ll = u;
bm = (sizeof(SItype) * BITS_PER_UNIT) - b;
if (bm <= 0) {
/* w.s.high = 1..1 or 0..0 */
w.s.high = uu.s.high >> (sizeof(SItype) * BITS_PER_UNIT - 1);
w.s.low = uu.s.high >> -bm;
} else {
USItype carries = (USItype)uu.s.high << bm;
w.s.high = uu.s.high >> b;
w.s.low = ((USItype)uu.s.low >> b) | carries;
}
return w.ll;
}

51
arch/mn10300/lib/bitops.c Normal file
View File

@ -0,0 +1,51 @@
/* MN10300 Non-trivial bit 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/module.h>
#include <asm/bitops.h>
#include <asm/system.h>
/*
* try flipping a bit using BSET and BCLR
*/
void change_bit(int nr, volatile void *addr)
{
if (test_bit(nr, addr))
goto try_clear_bit;
try_set_bit:
if (!test_and_set_bit(nr, addr))
return;
try_clear_bit:
if (test_and_clear_bit(nr, addr))
return;
goto try_set_bit;
}
/*
* try flipping a bit using BSET and BCLR and returning the old value
*/
int test_and_change_bit(int nr, volatile void *addr)
{
if (test_bit(nr, addr))
goto try_clear_bit;
try_set_bit:
if (!test_and_set_bit(nr, addr))
return 0;
try_clear_bit:
if (test_and_clear_bit(nr, addr))
return 1;
goto try_set_bit;
}

View File

@ -0,0 +1,99 @@
/* MN10300 Optimised checksumming wrappers
*
* Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd.
* 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 <asm/byteorder.h>
#include <asm/uaccess.h>
#include <asm/checksum.h>
#include "internal.h"
static inline unsigned short from32to16(__wsum sum)
{
asm(" add %1,%0 \n"
" addc 0xffff,%0 \n"
: "=r" (sum)
: "r" (sum << 16), "0" (sum & 0xffff0000)
);
return sum >> 16;
}
__sum16 ip_fast_csum(const void *iph, unsigned int ihl)
{
return ~do_csum(iph, ihl * 4);
}
EXPORT_SYMBOL(ip_fast_csum);
__wsum csum_partial(const void *buff, int len, __wsum sum)
{
__wsum result;
result = do_csum(buff, len);
result += sum;
if (sum > result)
result++;
return result;
}
EXPORT_SYMBOL(csum_partial);
__sum16 ip_compute_csum(const void *buff, int len)
{
return ~from32to16(do_csum(buff, len));
}
EXPORT_SYMBOL(ip_compute_csum);
__wsum csum_partial_copy(const void *src, void *dst, int len, __wsum sum)
{
copy_from_user(dst, src, len);
return csum_partial(dst, len, sum);
}
EXPORT_SYMBOL(csum_partial_copy);
__wsum csum_partial_copy_nocheck(const void *src, void *dst,
int len, __wsum sum)
{
sum = csum_partial(src, len, sum);
memcpy(dst, src, len);
return sum;
}
EXPORT_SYMBOL(csum_partial_copy_nocheck);
__wsum csum_partial_copy_from_user(const void *src, void *dst,
int len, __wsum sum,
int *err_ptr)
{
int missing;
missing = copy_from_user(dst, src, len);
if (missing) {
memset(dst + len - missing, 0, missing);
*err_ptr = -EFAULT;
}
return csum_partial(dst, len, sum);
}
EXPORT_SYMBOL(csum_partial_copy_from_user);
__wsum csum_and_copy_to_user(const void *src, void *dst,
int len, __wsum sum,
int *err_ptr)
{
int missing;
missing = copy_to_user(dst, src, len);
if (missing) {
memset(dst + len - missing, 0, missing);
*err_ptr = -EFAULT;
}
return csum_partial(src, len, sum);
}
EXPORT_SYMBOL(csum_and_copy_to_user);

50
arch/mn10300/lib/delay.c Normal file
View File

@ -0,0 +1,50 @@
/* MN10300 Short delay interpolation 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/module.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <asm/div64.h>
/*
* basic delay loop
*/
void __delay(unsigned long loops)
{
int d0;
asm volatile(
" bra 1f \n"
" .align 4 \n"
"1: bra 2f \n"
" .align 4 \n"
"2: add -1,%0 \n"
" bne 2b \n"
: "=&d" (d0)
: "0" (loops));
}
EXPORT_SYMBOL(__delay);
/*
* handle a delay specified in terms of microseconds
*/
void __udelay(unsigned long usecs)
{
signed long ioclk, stop;
/* usecs * CLK / 1E6 */
stop = __muldiv64u(usecs, MN10300_TSCCLK, 1000000);
stop = TMTSCBC - stop;
do {
ioclk = TMTSCBC;
} while (stop < ioclk);
}
EXPORT_SYMBOL(__udelay);

162
arch/mn10300/lib/do_csum.S Normal file
View File

@ -0,0 +1,162 @@
/* Optimised simple memory checksum
*
* 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/cache.h>
.section .text
.balign L1_CACHE_BYTES
###############################################################################
#
# unsigned int do_csum(const unsigned char *buff, size_t len)
#
###############################################################################
.globl do_csum
.type do_csum,@function
do_csum:
movm [d2,d3],(sp)
mov d0,(12,sp)
mov d1,(16,sp)
mov d1,d2 # count
mov d0,a0 # buff
clr d1 # accumulator
cmp +0,d2
beq do_csum_done # return if zero-length buffer
# 4-byte align the buffer pointer
btst +3,a0
beq do_csum_now_4b_aligned
btst +1,a0
beq do_csum_addr_not_odd
movbu (a0),d0
inc a0
asl +8,d0
add d0,d1
addc +0,d1
add -1,d2
do_csum_addr_not_odd:
cmp +2,d2
bcs do_csum_fewer_than_4
btst +2,a0
beq do_csum_now_4b_aligned
movhu (a0+),d0
add d0,d1
addc +0,d1
add -2,d2
cmp +4,d2
bcs do_csum_fewer_than_4
do_csum_now_4b_aligned:
# we want to checksum as much as we can in chunks of 32 bytes
cmp +31,d2
bls do_csum_remainder # 4-byte aligned remainder
add -32,d2
mov +32,d3
do_csum_loop:
mov (a0+),d0
add d0,d1
mov (a0+),e0
addc e0,d1
mov (a0+),e1
addc e1,d1
mov (a0+),e3
addc e3,d1
mov (a0+),d0
addc d0,d1
mov (a0+),e0
addc e0,d1
mov (a0+),e1
addc e1,d1
mov (a0+),e3
addc e3,d1
addc +0,d1
sub d3,d2
bcc do_csum_loop
add d3,d2
beq do_csum_done
do_csum_remainder:
# cut 16-31 bytes down to 0-15
cmp +16,d2
bcs do_csum_fewer_than_16
mov (a0+),d0
add d0,d1
mov (a0+),e0
addc e0,d1
mov (a0+),e1
addc e1,d1
mov (a0+),e3
addc e3,d1
addc +0,d1
add -16,d2
beq do_csum_done
do_csum_fewer_than_16:
# copy the remaining whole words
cmp +4,d2
bcs do_csum_fewer_than_4
cmp +8,d2
bcs do_csum_one_word
cmp +12,d2
bcs do_csum_two_words
mov (a0+),d0
add d0,d1
addc +0,d1
do_csum_two_words:
mov (a0+),d0
add d0,d1
addc +0,d1
do_csum_one_word:
mov (a0+),d0
add d0,d1
addc +0,d1
do_csum_fewer_than_4:
and +3,d2
beq do_csum_done
xor_cmp d0,d0,+2,d2
bcs do_csum_fewer_than_2
movhu (a0+),d0
do_csum_fewer_than_2:
and +1,d2
beq do_csum_add_last_bit
movbu (a0),d3
add d3,d0
do_csum_add_last_bit:
add d0,d1
addc +0,d1
do_csum_done:
# compress the checksum down to 16 bits
mov +0xffff0000,d2
and d1,d2
asl +16,d1
add d2,d1,d0
addc +0xffff,d0
lsr +16,d0
# flip the halves of the word result if the buffer was oddly aligned
mov (12,sp),d1
and +1,d1
beq do_csum_not_oddly_aligned
swaph d0,d0 # exchange bits 15:8 with 7:0
do_csum_not_oddly_aligned:
ret [d2,d3],8
do_csum_end:
.size do_csum, do_csum_end-do_csum

View File

@ -0,0 +1,15 @@
/* Internal definitions for the arch part of the kernel library
*
* 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.
*/
/*
* do_csum.S
*/
extern unsigned int do_csum(const unsigned char *, size_t);

View File

@ -0,0 +1,60 @@
/* lshrdi3.c extracted from gcc-2.7.2/libgcc2.c which is: */
/* Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
This file is part of GNU CC.
GNU CC 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, or (at your option)
any later version.
GNU CC 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 GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#define BITS_PER_UNIT 8
typedef int SItype __attribute__((mode(SI)));
typedef unsigned int USItype __attribute__((mode(SI)));
typedef int DItype __attribute__((mode(DI)));
typedef int word_type __attribute__((mode(__word__)));
struct DIstruct {
SItype low;
SItype high;
};
union DIunion {
struct DIstruct s;
DItype ll;
};
DItype __lshrdi3(DItype u, word_type b)
{
union DIunion w;
word_type bm;
union DIunion uu;
if (b == 0)
return u;
uu.ll = u;
bm = (sizeof(SItype) * BITS_PER_UNIT) - b;
if (bm <= 0) {
w.s.high = 0;
w.s.low = (USItype) uu.s.high >> -bm;
} else {
USItype carries = (USItype) uu.s.high << bm;
w.s.high = (USItype) uu.s.high >> b;
w.s.low = ((USItype) uu.s.low >> b) | carries;
}
return w.ll;
}

135
arch/mn10300/lib/memcpy.S Normal file
View File

@ -0,0 +1,135 @@
/* MN10300 Optimised simple memory to memory copy
*
* 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/cache.h>
.section .text
.balign L1_CACHE_BYTES
###############################################################################
#
# void *memcpy(void *dst, const void *src, size_t n)
#
###############################################################################
.globl memcpy
.type memcpy,@function
memcpy:
movm [d2,d3],(sp)
mov d0,(12,sp)
mov d1,(16,sp)
mov (20,sp),d2 # count
mov d0,a0 # dst
mov d1,a1 # src
mov d0,e3 # the return value
cmp +0,d2
beq memcpy_done # return if zero-length copy
# see if the three parameters are all four-byte aligned
or d0,d1,d3
or d2,d3
and +3,d3
bne memcpy_1 # jump if not
# we want to transfer as much as we can in chunks of 32 bytes
cmp +31,d2
bls memcpy_4_remainder # 4-byte aligned remainder
movm [exreg1],(sp)
add -32,d2
mov +32,d3
memcpy_4_loop:
mov (a1+),d0
mov (a1+),d1
mov (a1+),e0
mov (a1+),e1
mov (a1+),e4
mov (a1+),e5
mov (a1+),e6
mov (a1+),e7
mov d0,(a0+)
mov d1,(a0+)
mov e0,(a0+)
mov e1,(a0+)
mov e4,(a0+)
mov e5,(a0+)
mov e6,(a0+)
mov e7,(a0+)
sub d3,d2
bcc memcpy_4_loop
movm (sp),[exreg1]
add d3,d2
beq memcpy_4_no_remainder
memcpy_4_remainder:
# cut 4-7 words down to 0-3
cmp +16,d2
bcs memcpy_4_three_or_fewer_words
mov (a1+),d0
mov (a1+),d1
mov (a1+),e0
mov (a1+),e1
mov d0,(a0+)
mov d1,(a0+)
mov e0,(a0+)
mov e1,(a0+)
add -16,d2
beq memcpy_4_no_remainder
# copy the remaining 1, 2 or 3 words
memcpy_4_three_or_fewer_words:
cmp +8,d2
bcs memcpy_4_one_word
beq memcpy_4_two_words
mov (a1+),d0
mov d0,(a0+)
memcpy_4_two_words:
mov (a1+),d0
mov d0,(a0+)
memcpy_4_one_word:
mov (a1+),d0
mov d0,(a0+)
memcpy_4_no_remainder:
# check we copied the correct amount
# TODO: REMOVE CHECK
sub e3,a0,d2
mov (20,sp),d1
cmp d2,d1
beq memcpy_done
break
break
break
memcpy_done:
mov e3,a0
ret [d2,d3],8
# handle misaligned copying
memcpy_1:
add -1,d2
mov +1,d3
setlb # setlb requires the next insns
# to occupy exactly 4 bytes
sub d3,d2
movbu (a1),d0
movbu d0,(a0)
add_add d3,a1,d3,a0
lcc
mov e3,a0
ret [d2,d3],8
memcpy_end:
.size memcpy, memcpy_end-memcpy

160
arch/mn10300/lib/memmove.S Normal file
View File

@ -0,0 +1,160 @@
/* MN10300 Optimised simple memory to memory copy, with support for overlapping
* regions
*
* 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/cache.h>
.section .text
.balign L1_CACHE_BYTES
###############################################################################
#
# void *memmove(void *dst, const void *src, size_t n)
#
###############################################################################
.globl memmove
.type memmove,@function
memmove:
# fall back to memcpy if dst < src to work bottom up
cmp d1,d0
bcs memmove_memcpy
# work top down
movm [d2,d3],(sp)
mov d0,(12,sp)
mov d1,(16,sp)
mov (20,sp),d2 # count
add d0,d2,a0 # dst end
add d1,d2,a1 # src end
mov d0,e3 # the return value
cmp +0,d2
beq memmove_done # return if zero-length copy
# see if the three parameters are all four-byte aligned
or d0,d1,d3
or d2,d3
and +3,d3
bne memmove_1 # jump if not
# we want to transfer as much as we can in chunks of 32 bytes
add -4,a1
cmp +31,d2
bls memmove_4_remainder # 4-byte aligned remainder
add -32,d2
mov +32,d3
memmove_4_loop:
mov (a1),d0
sub_sub +4,a1,+4,a0
mov d0,(a0)
mov (a1),d1
sub_sub +4,a1,+4,a0
mov d1,(a0)
mov (a1),d0
sub_sub +4,a1,+4,a0
mov d0,(a0)
mov (a1),d1
sub_sub +4,a1,+4,a0
mov d1,(a0)
mov (a1),d0
sub_sub +4,a1,+4,a0
mov d0,(a0)
mov (a1),d1
sub_sub +4,a1,+4,a0
mov d1,(a0)
mov (a1),d0
sub_sub +4,a1,+4,a0
mov d0,(a0)
mov (a1),d1
sub_sub +4,a1,+4,a0
mov d1,(a0)
sub d3,d2
bcc memmove_4_loop
add d3,d2
beq memmove_4_no_remainder
memmove_4_remainder:
# cut 4-7 words down to 0-3
cmp +16,d2
bcs memmove_4_three_or_fewer_words
mov (a1),d0
sub_sub +4,a1,+4,a0
mov d0,(a0)
mov (a1),d1
sub_sub +4,a1,+4,a0
mov d1,(a0)
mov (a1),e0
sub_sub +4,a1,+4,a0
mov e0,(a0)
mov (a1),e1
sub_sub +4,a1,+4,a0
mov e1,(a0)
add -16,d2
beq memmove_4_no_remainder
# copy the remaining 1, 2 or 3 words
memmove_4_three_or_fewer_words:
cmp +8,d2
bcs memmove_4_one_word
beq memmove_4_two_words
mov (a1),d0
sub_sub +4,a1,+4,a0
mov d0,(a0)
memmove_4_two_words:
mov (a1),d0
sub_sub +4,a1,+4,a0
mov d0,(a0)
memmove_4_one_word:
mov (a1),d0
sub_sub +4,a1,+4,a0
mov d0,(a0)
memmove_4_no_remainder:
# check we copied the correct amount
# TODO: REMOVE CHECK
sub e3,a0,d2
beq memmove_done
break
break
break
memmove_done:
mov e3,a0
ret [d2,d3],8
# handle misaligned copying
memmove_1:
add -1,a1
add -1,d2
mov +1,d3
setlb # setlb requires the next insns
# to occupy exactly 4 bytes
sub d3,d2
movbu (a1),d0
sub_sub d3,a1,d3,a0
movbu d0,(a0)
lcc
mov e3,a0
ret [d2,d3],8
memmove_memcpy:
jmp memcpy
memmove_end:
.size memmove, memmove_end-memmove

121
arch/mn10300/lib/memset.S Normal file
View File

@ -0,0 +1,121 @@
/* Optimised simple memory fill
*
* 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/cache.h>
.section .text
.balign L1_CACHE_BYTES
###############################################################################
#
# void *memset(void *dst, int c, size_t n)
#
###############################################################################
.globl memset
.type memset,@function
memset:
movm [d2,d3],(sp)
mov d0,(12,sp)
mov d1,(16,sp)
mov (20,sp),d2 # count
mov d0,a0 # dst
mov d0,e3 # the return value
cmp +0,d2
beq memset_done # return if zero-length fill
# see if the region parameters are four-byte aligned
or d0,d2,d3
and +3,d3
bne memset_1 # jump if not
extbu d1
mov_asl d1,d3,8,d1
or_asl d1,d3,8,d1
or_asl d1,d3,8,d1
or d3,d1
# we want to transfer as much as we can in chunks of 32 bytes
cmp +31,d2
bls memset_4_remainder # 4-byte aligned remainder
add -32,d2
mov +32,d3
memset_4_loop:
mov d1,(a0+)
mov d1,(a0+)
mov d1,(a0+)
mov d1,(a0+)
mov d1,(a0+)
mov d1,(a0+)
mov d1,(a0+)
mov d1,(a0+)
sub d3,d2
bcc memset_4_loop
add d3,d2
beq memset_4_no_remainder
memset_4_remainder:
# cut 4-7 words down to 0-3
cmp +16,d2
bcs memset_4_three_or_fewer_words
mov d1,(a0+)
mov d1,(a0+)
mov d1,(a0+)
mov d1,(a0+)
add -16,d2
beq memset_4_no_remainder
# copy the remaining 1, 2 or 3 words
memset_4_three_or_fewer_words:
cmp +8,d2
bcs memset_4_one_word
beq memset_4_two_words
mov d1,(a0+)
memset_4_two_words:
mov d1,(a0+)
memset_4_one_word:
mov d1,(a0+)
memset_4_no_remainder:
# check we set the correct amount
# TODO: REMOVE CHECK
sub e3,a0,d2
mov (20,sp),d1
cmp d2,d1
beq memset_done
break
break
break
memset_done:
mov e3,a0
ret [d2,d3],8
# handle misaligned copying
memset_1:
add -1,d2
mov +1,d3
setlb # setlb requires the next insns
# to occupy exactly 4 bytes
sub d3,d2
movbu d1,(a0)
inc a0
lcc
mov e3,a0
ret [d2,d3],8
memset_end:
.size memset, memset_end-memset

57
arch/mn10300/lib/negdi2.c Normal file
View File

@ -0,0 +1,57 @@
/* More subroutines needed by GCC output code on some machines. */
/* Compile this one with gcc. */
/* Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
2000, 2001 Free Software Foundation, Inc.
This file is part of GNU CC.
GNU CC 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, or (at your option)
any later version.
In addition to the permissions in the GNU General Public Licence, the
Free Software Foundation gives you unlimited permission to link the
compiled version of this file into combinations with other programs,
and to distribute those combinations without any restriction coming
from the use of this file. (The General Public Licence restrictions
do apply in other respects; for example, they cover modification of
the file, and distribution when not linked into a combine
executable.)
GNU CC 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 GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
/* It is incorrect to include config.h here, because this file is being
compiled for the target, and hence definitions concerning only the host
do not apply. */
#include <linux/types.h>
union DWunion {
s64 ll;
struct {
s32 low;
s32 high;
} s;
};
s64 __negdi2(s64 u)
{
union DWunion w;
union DWunion uu;
uu.ll = u;
w.s.low = -uu.s.low;
w.s.high = -uu.s.high - ((u32) w.s.low > 0);
return w.ll;
}

166
arch/mn10300/lib/usercopy.c Normal file
View File

@ -0,0 +1,166 @@
/* MN10300 Userspace accessor functions
*
* Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd.
* 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>
unsigned long
__generic_copy_to_user(void *to, const void *from, unsigned long n)
{
if (access_ok(VERIFY_WRITE, to, n))
__copy_user(to, from, n);
return n;
}
unsigned long
__generic_copy_from_user(void *to, const void *from, unsigned long n)
{
if (access_ok(VERIFY_READ, from, n))
__copy_user_zeroing(to, from, n);
return n;
}
/*
* Copy a null terminated string from userspace.
*/
#define __do_strncpy_from_user(dst, src, count, res) \
do { \
int w; \
asm volatile( \
" mov %1,%0\n" \
" cmp 0,%1\n" \
" beq 2f\n" \
"0:\n" \
" movbu (%5),%2\n" \
"1:\n" \
" movbu %2,(%6)\n" \
" inc %5\n" \
" inc %6\n" \
" cmp 0,%2\n" \
" beq 2f\n" \
" add -1,%1\n" \
" bne 0b\n" \
"2:\n" \
" sub %1,%0\n" \
"3:\n" \
" .section .fixup,\"ax\"\n" \
"4:\n" \
" mov %3,%0\n" \
" jmp 3b\n" \
" .previous\n" \
" .section __ex_table,\"a\"\n" \
" .balign 4\n" \
" .long 0b,4b\n" \
" .long 1b,4b\n" \
" .previous" \
:"=&r"(res), "=r"(count), "=&r"(w) \
:"i"(-EFAULT), "1"(count), "a"(src), "a"(dst) \
:"memory"); \
} while (0)
long
__strncpy_from_user(char *dst, const char *src, long count)
{
long res;
__do_strncpy_from_user(dst, src, count, res);
return res;
}
long
strncpy_from_user(char *dst, const char *src, long count)
{
long res = -EFAULT;
if (access_ok(VERIFY_READ, src, 1))
__do_strncpy_from_user(dst, src, count, res);
return res;
}
/*
* Clear a userspace memory
*/
#define __do_clear_user(addr, size) \
do { \
int w; \
asm volatile( \
" cmp 0,%0\n" \
" beq 1f\n" \
" clr %1\n" \
"0: movbu %1,(%3,%2)\n" \
" inc %3\n" \
" cmp %0,%3\n" \
" bne 0b\n" \
"1:\n" \
" sub %3,%0\n" \
"2:\n" \
".section .fixup,\"ax\"\n" \
"3: jmp 2b\n" \
".previous\n" \
".section __ex_table,\"a\"\n" \
" .balign 4\n" \
" .long 0b,3b\n" \
".previous\n" \
: "+r"(size), "=&r"(w) \
: "a"(addr), "d"(0) \
: "memory"); \
} while (0)
unsigned long
__clear_user(void *to, unsigned long n)
{
__do_clear_user(to, n);
return n;
}
unsigned long
clear_user(void *to, unsigned long n)
{
if (access_ok(VERIFY_WRITE, to, n))
__do_clear_user(to, n);
return n;
}
/*
* Return the size of a string (including the ending 0)
*
* Return 0 on exception, a value greater than N if too long
*/
long strnlen_user(const char *s, long n)
{
unsigned long res, w;
if (!__addr_ok(s))
return 0;
if (n < 0 || n + (u_long) s > current_thread_info()->addr_limit.seg)
n = current_thread_info()->addr_limit.seg - (u_long)s;
asm volatile(
"0: cmp %4,%0\n"
" beq 2f\n"
"1: movbu (%0,%3),%1\n"
" inc %0\n"
" cmp 0,%1\n"
" beq 3f\n"
" bra 0b\n"
"2: clr %0\n"
"3:\n"
".section .fixup,\"ax\"\n"
"4: jmp 2b\n"
".previous\n"
".section __ex_table,\"a\"\n"
" .balign 4\n"
" .long 1b,4b\n"
".previous\n"
:"=d"(res), "=&r"(w)
:"0"(0), "a"(s), "r"(n)
:"memory");
return res;
}

14
arch/mn10300/mm/Makefile Normal file
View File

@ -0,0 +1,14 @@
#
# Makefile for the MN10300-specific memory management code
#
obj-y := \
init.o fault.o pgtable.o extable.o tlb-mn10300.o mmu-context.o \
misalignment.o dma-alloc.o
ifneq ($(CONFIG_MN10300_CACHE_DISABLED),y)
obj-y += cache.o cache-mn10300.o
ifeq ($(CONFIG_MN10300_CACHE_WBACK),y)
obj-y += cache-flush-mn10300.o
endif
endif

View File

@ -0,0 +1,192 @@
/* MN10300 CPU core caching 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/page.h>
#include <asm/cache.h>
.am33_2
.globl mn10300_dcache_flush
.globl mn10300_dcache_flush_page
.globl mn10300_dcache_flush_range
.globl mn10300_dcache_flush_range2
.globl mn10300_dcache_flush_inv
.globl mn10300_dcache_flush_inv_page
.globl mn10300_dcache_flush_inv_range
.globl mn10300_dcache_flush_inv_range2
###############################################################################
#
# void mn10300_dcache_flush(void)
# Flush the entire data cache back to RAM
#
###############################################################################
ALIGN
mn10300_dcache_flush:
movhu (CHCTR),d0
btst CHCTR_DCEN,d0
beq mn10300_dcache_flush_end
# read the addresses tagged in the cache's tag RAM and attempt to flush
# those addresses specifically
# - we rely on the hardware to filter out invalid tag entry addresses
mov DCACHE_TAG(0,0),a0 # dcache tag RAM access address
mov DCACHE_PURGE(0,0),a1 # dcache purge request address
mov L1_CACHE_NWAYS*L1_CACHE_NENTRIES,d1 # total number of entries
mn10300_dcache_flush_loop:
mov (a0),d0
and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0
or L1_CACHE_TAG_VALID,d0 # retain valid entries in the
# cache
mov d0,(a1) # conditional purge
mn10300_dcache_flush_skip:
add L1_CACHE_BYTES,a0
add L1_CACHE_BYTES,a1
add -1,d1
bne mn10300_dcache_flush_loop
mn10300_dcache_flush_end:
ret [],0
###############################################################################
#
# void mn10300_dcache_flush_page(unsigned start)
# void mn10300_dcache_flush_range(unsigned start, unsigned end)
# void mn10300_dcache_flush_range2(unsigned start, unsigned size)
# Flush a range of addresses on a page in the dcache
#
###############################################################################
ALIGN
mn10300_dcache_flush_page:
mov PAGE_SIZE,d1
mn10300_dcache_flush_range2:
add d0,d1
mn10300_dcache_flush_range:
movm [d2,d3],(sp)
movhu (CHCTR),d2
btst CHCTR_DCEN,d2
beq mn10300_dcache_flush_range_end
# round start addr down
and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0
mov d0,a1
add L1_CACHE_BYTES,d1 # round end addr up
and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d1
# write a request to flush all instances of an address from the cache
mov DCACHE_PURGE(0,0),a0
mov a1,d0
and L1_CACHE_TAG_ENTRY,d0
add d0,a0 # starting dcache purge control
# reg address
sub a1,d1
lsr L1_CACHE_SHIFT,d1 # total number of entries to
# examine
or L1_CACHE_TAG_VALID,a1 # retain valid entries in the
# cache
mn10300_dcache_flush_range_loop:
mov a1,(L1_CACHE_WAYDISP*0,a0) # conditionally purge this line
# all ways
add L1_CACHE_BYTES,a0
add L1_CACHE_BYTES,a1
and ~L1_CACHE_WAYDISP,a0 # make sure way stay on way 0
add -1,d1
bne mn10300_dcache_flush_range_loop
mn10300_dcache_flush_range_end:
ret [d2,d3],8
###############################################################################
#
# void mn10300_dcache_flush_inv(void)
# Flush the entire data cache and invalidate all entries
#
###############################################################################
ALIGN
mn10300_dcache_flush_inv:
movhu (CHCTR),d0
btst CHCTR_DCEN,d0
beq mn10300_dcache_flush_inv_end
# hit each line in the dcache with an unconditional purge
mov DCACHE_PURGE(0,0),a1 # dcache purge request address
mov L1_CACHE_NWAYS*L1_CACHE_NENTRIES,d1 # total number of entries
mn10300_dcache_flush_inv_loop:
mov (a1),d0 # unconditional purge
add L1_CACHE_BYTES,a1
add -1,d1
bne mn10300_dcache_flush_inv_loop
mn10300_dcache_flush_inv_end:
ret [],0
###############################################################################
#
# void mn10300_dcache_flush_inv_page(unsigned start)
# void mn10300_dcache_flush_inv_range(unsigned start, unsigned end)
# void mn10300_dcache_flush_inv_range2(unsigned start, unsigned size)
# Flush and invalidate a range of addresses on a page in the dcache
#
###############################################################################
ALIGN
mn10300_dcache_flush_inv_page:
mov PAGE_SIZE,d1
mn10300_dcache_flush_inv_range2:
add d0,d1
mn10300_dcache_flush_inv_range:
movm [d2,d3],(sp)
movhu (CHCTR),d2
btst CHCTR_DCEN,d2
beq mn10300_dcache_flush_inv_range_end
and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0 # round start
# addr down
mov d0,a1
add L1_CACHE_BYTES,d1 # round end addr up
and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d1
# write a request to flush and invalidate all instances of an address
# from the cache
mov DCACHE_PURGE(0,0),a0
mov a1,d0
and L1_CACHE_TAG_ENTRY,d0
add d0,a0 # starting dcache purge control
# reg address
sub a1,d1
lsr L1_CACHE_SHIFT,d1 # total number of entries to
# examine
mn10300_dcache_flush_inv_range_loop:
mov a1,(L1_CACHE_WAYDISP*0,a0) # conditionally purge this line
# in all ways
add L1_CACHE_BYTES,a0
add L1_CACHE_BYTES,a1
and ~L1_CACHE_WAYDISP,a0 # make sure way stay on way 0
add -1,d1
bne mn10300_dcache_flush_inv_range_loop
mn10300_dcache_flush_inv_range_end:
ret [d2,d3],8

View File

@ -0,0 +1,289 @@
/* MN10300 CPU core caching 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/page.h>
#include <asm/cache.h>
#define mn10300_dcache_inv_range_intr_interval \
+((1 << MN10300_DCACHE_INV_RANGE_INTR_LOG2_INTERVAL) - 1)
#if mn10300_dcache_inv_range_intr_interval > 0xff
#error MN10300_DCACHE_INV_RANGE_INTR_LOG2_INTERVAL must be 8 or less
#endif
.am33_2
.globl mn10300_icache_inv
.globl mn10300_dcache_inv
.globl mn10300_dcache_inv_range
.globl mn10300_dcache_inv_range2
.globl mn10300_dcache_inv_page
###############################################################################
#
# void mn10300_icache_inv(void)
# Invalidate the entire icache
#
###############################################################################
ALIGN
mn10300_icache_inv:
mov CHCTR,a0
movhu (a0),d0
btst CHCTR_ICEN,d0
beq mn10300_icache_inv_end
mov epsw,d1
and ~EPSW_IE,epsw
nop
nop
# disable the icache
and ~CHCTR_ICEN,d0
movhu d0,(a0)
# and wait for it to calm down
setlb
movhu (a0),d0
btst CHCTR_ICBUSY,d0
lne
# invalidate
or CHCTR_ICINV,d0
movhu d0,(a0)
# wait for the cache to finish
mov CHCTR,a0
setlb
movhu (a0),d0
btst CHCTR_ICBUSY,d0
lne
# and reenable it
and ~CHCTR_ICINV,d0
or CHCTR_ICEN,d0
movhu d0,(a0)
movhu (a0),d0
mov d1,epsw
mn10300_icache_inv_end:
ret [],0
###############################################################################
#
# void mn10300_dcache_inv(void)
# Invalidate the entire dcache
#
###############################################################################
ALIGN
mn10300_dcache_inv:
mov CHCTR,a0
movhu (a0),d0
btst CHCTR_DCEN,d0
beq mn10300_dcache_inv_end
mov epsw,d1
and ~EPSW_IE,epsw
nop
nop
# disable the dcache
and ~CHCTR_DCEN,d0
movhu d0,(a0)
# and wait for it to calm down
setlb
movhu (a0),d0
btst CHCTR_DCBUSY,d0
lne
# invalidate
or CHCTR_DCINV,d0
movhu d0,(a0)
# wait for the cache to finish
mov CHCTR,a0
setlb
movhu (a0),d0
btst CHCTR_DCBUSY,d0
lne
# and reenable it
and ~CHCTR_DCINV,d0
or CHCTR_DCEN,d0
movhu d0,(a0)
movhu (a0),d0
mov d1,epsw
mn10300_dcache_inv_end:
ret [],0
###############################################################################
#
# void mn10300_dcache_inv_range(unsigned start, unsigned end)
# void mn10300_dcache_inv_range2(unsigned start, unsigned size)
# void mn10300_dcache_inv_page(unsigned start)
# Invalidate a range of addresses on a page in the dcache
#
###############################################################################
ALIGN
mn10300_dcache_inv_page:
mov PAGE_SIZE,d1
mn10300_dcache_inv_range2:
add d0,d1
mn10300_dcache_inv_range:
movm [d2,d3,a2],(sp)
mov CHCTR,a2
movhu (a2),d2
btst CHCTR_DCEN,d2
beq mn10300_dcache_inv_range_end
and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0 # round start
# addr down
mov d0,a1
add L1_CACHE_BYTES,d1 # round end addr up
and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d1
clr d2 # we're going to clear tag ram
# entries
# read the tags from the tag RAM, and if they indicate a valid dirty
# cache line then invalidate that line
mov DCACHE_TAG(0,0),a0
mov a1,d0
and L1_CACHE_TAG_ENTRY,d0
add d0,a0 # starting dcache tag RAM
# access address
sub a1,d1
lsr L1_CACHE_SHIFT,d1 # total number of entries to
# examine
and ~(L1_CACHE_DISPARITY-1),a1 # determine comparator base
mn10300_dcache_inv_range_outer_loop:
# disable interrupts
mov epsw,d3
and ~EPSW_IE,epsw
nop # note that reading CHCTR and
# AND'ing D0 occupy two delay
# slots after disabling
# interrupts
# disable the dcache
movhu (a2),d0
and ~CHCTR_DCEN,d0
movhu d0,(a2)
# and wait for it to calm down
setlb
movhu (a2),d0
btst CHCTR_DCBUSY,d0
lne
mn10300_dcache_inv_range_loop:
# process the way 0 slot
mov (L1_CACHE_WAYDISP*0,a0),d0 # read the tag in the way 0 slot
btst L1_CACHE_TAG_VALID,d0
beq mn10300_dcache_inv_range_skip_0 # jump if this cacheline is not
# valid
xor a1,d0
lsr 12,d0
bne mn10300_dcache_inv_range_skip_0 # jump if not this cacheline
mov d2,(a0) # kill the tag
mn10300_dcache_inv_range_skip_0:
# process the way 1 slot
mov (L1_CACHE_WAYDISP*1,a0),d0 # read the tag in the way 1 slot
btst L1_CACHE_TAG_VALID,d0
beq mn10300_dcache_inv_range_skip_1 # jump if this cacheline is not
# valid
xor a1,d0
lsr 12,d0
bne mn10300_dcache_inv_range_skip_1 # jump if not this cacheline
mov d2,(a0) # kill the tag
mn10300_dcache_inv_range_skip_1:
# process the way 2 slot
mov (L1_CACHE_WAYDISP*2,a0),d0 # read the tag in the way 2 slot
btst L1_CACHE_TAG_VALID,d0
beq mn10300_dcache_inv_range_skip_2 # jump if this cacheline is not
# valid
xor a1,d0
lsr 12,d0
bne mn10300_dcache_inv_range_skip_2 # jump if not this cacheline
mov d2,(a0) # kill the tag
mn10300_dcache_inv_range_skip_2:
# process the way 3 slot
mov (L1_CACHE_WAYDISP*3,a0),d0 # read the tag in the way 3 slot
btst L1_CACHE_TAG_VALID,d0
beq mn10300_dcache_inv_range_skip_3 # jump if this cacheline is not
# valid
xor a1,d0
lsr 12,d0
bne mn10300_dcache_inv_range_skip_3 # jump if not this cacheline
mov d2,(a0) # kill the tag
mn10300_dcache_inv_range_skip_3:
# approx every N steps we re-enable the cache and see if there are any
# interrupts to be processed
# we also break out if we've reached the end of the loop
# (the bottom nibble of the count is zero in both cases)
add L1_CACHE_BYTES,a0
add L1_CACHE_BYTES,a1
add -1,d1
btst mn10300_dcache_inv_range_intr_interval,d1
bne mn10300_dcache_inv_range_loop
# wait for the cache to finish what it's doing
setlb
movhu (a2),d0
btst CHCTR_DCBUSY,d0
lne
# and reenable it
or CHCTR_DCEN,d0
movhu d0,(a2)
movhu (a2),d0
# re-enable interrupts
# - we don't bother with delay NOPs as we'll have enough instructions
# before we disable interrupts again to give the interrupts a chance
# to happen
mov d3,epsw
# go around again if the counter hasn't yet reached zero
add 0,d1
bne mn10300_dcache_inv_range_outer_loop
mn10300_dcache_inv_range_end:
ret [d2,d3,a2],12

121
arch/mn10300/mm/cache.c Normal file
View File

@ -0,0 +1,121 @@
/* MN10300 Cache flushing 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/module.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/threads.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
#include <asm/cacheflush.h>
#include <asm/io.h>
#include <asm/uaccess.h>
EXPORT_SYMBOL(mn10300_icache_inv);
EXPORT_SYMBOL(mn10300_dcache_inv);
EXPORT_SYMBOL(mn10300_dcache_inv_range);
EXPORT_SYMBOL(mn10300_dcache_inv_range2);
EXPORT_SYMBOL(mn10300_dcache_inv_page);
#ifdef CONFIG_MN10300_CACHE_WBACK
EXPORT_SYMBOL(mn10300_dcache_flush);
EXPORT_SYMBOL(mn10300_dcache_flush_inv);
EXPORT_SYMBOL(mn10300_dcache_flush_inv_range);
EXPORT_SYMBOL(mn10300_dcache_flush_inv_range2);
EXPORT_SYMBOL(mn10300_dcache_flush_inv_page);
EXPORT_SYMBOL(mn10300_dcache_flush_range);
EXPORT_SYMBOL(mn10300_dcache_flush_range2);
EXPORT_SYMBOL(mn10300_dcache_flush_page);
#endif
/*
* write a page back from the dcache and invalidate the icache so that we can
* run code from it that we've just written into it
*/
void flush_icache_page(struct vm_area_struct *vma, struct page *page)
{
mn10300_dcache_flush_page(page_to_phys(page));
mn10300_icache_inv();
}
EXPORT_SYMBOL(flush_icache_page);
/*
* write some code we've just written back from the dcache and invalidate the
* icache so that we can run that code
*/
void flush_icache_range(unsigned long start, unsigned long end)
{
#ifdef CONFIG_MN10300_CACHE_WBACK
unsigned long addr, size, off;
struct page *page;
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_t *ppte, pte;
for (; start < end; start += size) {
/* work out how much of the page to flush */
off = start & (PAGE_SIZE - 1);
size = end - start;
if (size > PAGE_SIZE - off)
size = PAGE_SIZE - off;
/* get the physical address the page is mapped to from the page
* tables */
pgd = pgd_offset(current->mm, start);
if (!pgd || !pgd_val(*pgd))
continue;
pud = pud_offset(pgd, start);
if (!pud || !pud_val(*pud))
continue;
pmd = pmd_offset(pud, start);
if (!pmd || !pmd_val(*pmd))
continue;
ppte = pte_offset_map(pmd, start);
if (!ppte)
continue;
pte = *ppte;
pte_unmap(ppte);
if (pte_none(pte))
continue;
page = pte_page(pte);
if (!page)
continue;
addr = page_to_phys(page);
/* flush the dcache and invalidate the icache coverage on that
* region */
mn10300_dcache_flush_range2(addr + off, size);
}
#endif
mn10300_icache_inv();
}
EXPORT_SYMBOL(flush_icache_range);
/*
* allow userspace to flush the instruction cache
*/
asmlinkage long sys_cacheflush(unsigned long start, unsigned long end)
{
if (end < start)
return -EINVAL;
flush_icache_range(start, end);
return 0;
}

View File

@ -0,0 +1,56 @@
/* MN10300 Dynamic DMA mapping support
*
* Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
* Derived from: arch/i386/kernel/pci-dma.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/types.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/pci.h>
#include <asm/io.h>
void *dma_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, int gfp)
{
unsigned long addr;
void *ret;
/* ignore region specifiers */
gfp &= ~(__GFP_DMA | __GFP_HIGHMEM);
if (dev == NULL || dev->coherent_dma_mask < 0xffffffff)
gfp |= GFP_DMA;
addr = __get_free_pages(gfp, get_order(size));
if (!addr)
return NULL;
/* map the coherent memory through the uncached memory window */
ret = (void *) (addr | 0x20000000);
/* fill the memory with obvious rubbish */
memset((void *) addr, 0xfb, size);
/* write back and evict all cache lines covering this region */
mn10300_dcache_flush_inv_range2(virt_to_phys((void *) addr), PAGE_SIZE);
*dma_handle = virt_to_bus((void *) addr);
return ret;
}
EXPORT_SYMBOL(dma_alloc_coherent);
void dma_free_coherent(struct device *dev, size_t size, void *vaddr,
dma_addr_t dma_handle)
{
unsigned long addr = (unsigned long) vaddr & ~0x20000000;
free_pages(addr, get_order(size));
}
EXPORT_SYMBOL(dma_free_coherent);

26
arch/mn10300/mm/extable.c Normal file
View File

@ -0,0 +1,26 @@
/* MN10300 In-kernel exception handling
*
* 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 <linux/spinlock.h>
#include <asm/uaccess.h>
int fixup_exception(struct pt_regs *regs)
{
const struct exception_table_entry *fixup;
fixup = search_exception_tables(regs->pc);
if (fixup) {
regs->pc = fixup->fixup;
return 1;
}
return 0;
}

405
arch/mn10300/mm/fault.c Normal file
View File

@ -0,0 +1,405 @@
/* MN10300 MMU Fault handler
*
* 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/signal.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/ptrace.h>
#include <linux/mman.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/vt_kern.h> /* For unblank_screen() */
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/pgalloc.h>
#include <asm/hardirq.h>
#include <asm/gdb-stub.h>
#include <asm/cpu-regs.h>
/*
* Unlock any spinlocks which will prevent us from getting the
* message out
*/
void bust_spinlocks(int yes)
{
if (yes) {
oops_in_progress = 1;
#ifdef CONFIG_SMP
/* Many serial drivers do __global_cli() */
global_irq_lock = 0;
#endif
} else {
int loglevel_save = console_loglevel;
#ifdef CONFIG_VT
unblank_screen();
#endif
oops_in_progress = 0;
/*
* OK, the message is on the console. Now we call printk()
* without oops_in_progress set so that printk will give klogd
* a poke. Hold onto your hats...
*/
console_loglevel = 15; /* NMI oopser may have shut the console
* up */
printk(" ");
console_loglevel = loglevel_save;
}
}
void do_BUG(const char *file, int line)
{
bust_spinlocks(1);
printk(KERN_EMERG "------------[ cut here ]------------\n");
printk(KERN_EMERG "kernel BUG at %s:%d!\n", file, line);
}
#if 0
static void print_pagetable_entries(pgd_t *pgdir, unsigned long address)
{
pgd_t *pgd;
pmd_t *pmd;
pte_t *pte;
pgd = pgdir + __pgd_offset(address);
printk(KERN_DEBUG "pgd entry %p: %016Lx\n",
pgd, (long long) pgd_val(*pgd));
if (!pgd_present(*pgd)) {
printk(KERN_DEBUG "... pgd not present!\n");
return;
}
pmd = pmd_offset(pgd, address);
printk(KERN_DEBUG "pmd entry %p: %016Lx\n",
pmd, (long long)pmd_val(*pmd));
if (!pmd_present(*pmd)) {
printk(KERN_DEBUG "... pmd not present!\n");
return;
}
pte = pte_offset(pmd, address);
printk(KERN_DEBUG "pte entry %p: %016Lx\n",
pte, (long long) pte_val(*pte));
if (!pte_present(*pte))
printk(KERN_DEBUG "... pte not present!\n");
}
#endif
asmlinkage void monitor_signal(struct pt_regs *);
/*
* This routine handles page faults. It determines the address,
* and the problem, and then passes it off to one of the appropriate
* routines.
*
* fault_code:
* - LSW: either MMUFCR_IFC or MMUFCR_DFC as appropriate
* - MSW: 0 if data access, 1 if instruction access
* - bit 0: TLB miss flag
* - bit 1: initial write
* - bit 2: page invalid
* - bit 3: protection violation
* - bit 4: accessor (0=user 1=kernel)
* - bit 5: 0=read 1=write
* - bit 6-8: page protection spec
* - bit 9: illegal address
* - bit 16: 0=data 1=ins
*
*/
asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long fault_code,
unsigned long address)
{
struct vm_area_struct *vma;
struct task_struct *tsk;
struct mm_struct *mm;
unsigned long page;
siginfo_t info;
int write, fault;
#ifdef CONFIG_GDBSTUB
/* handle GDB stub causing a fault */
if (gdbstub_busy) {
gdbstub_exception(regs, TBR & TBR_INT_CODE);
return;
}
#endif
#if 0
printk(KERN_DEBUG "--- do_page_fault(%p,%s:%04lx,%08lx)\n",
regs,
fault_code & 0x10000 ? "ins" : "data",
fault_code & 0xffff, address);
#endif
tsk = current;
/*
* We fault-in kernel-space virtual memory on-demand. The
* 'reference' page table is init_mm.pgd.
*
* NOTE! We MUST NOT take any locks for this case. We may
* be in an interrupt or a critical region, and should
* only copy the information from the master page table,
* nothing more.
*
* This verifies that the fault happens in kernel space
* and that the fault was a page not present (invalid) error
*/
if (address >= VMALLOC_START && address < VMALLOC_END &&
(fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_SR &&
(fault_code & MMUFCR_xFC_PGINVAL) == MMUFCR_xFC_PGINVAL
)
goto vmalloc_fault;
mm = tsk->mm;
info.si_code = SEGV_MAPERR;
/*
* If we're in an interrupt or have no user
* context, we must not take the fault..
*/
if (in_interrupt() || !mm)
goto no_context;
down_read(&mm->mmap_sem);
vma = find_vma(mm, address);
if (!vma)
goto bad_area;
if (vma->vm_start <= address)
goto good_area;
if (!(vma->vm_flags & VM_GROWSDOWN))
goto bad_area;
if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_USR) {
/* accessing the stack below the stack pointer is always a
* bug */
if ((address & PAGE_MASK) + 2 * PAGE_SIZE < regs->sp) {
#if 0
printk(KERN_WARNING
"[%d] ### Access below stack @%lx (sp=%lx)\n",
current->pid, address, regs->sp);
printk(KERN_WARNING
"vma [%08x - %08x]\n",
vma->vm_start, vma->vm_end);
show_registers(regs);
printk(KERN_WARNING
"[%d] ### Code: [%08lx]"
" %02x %02x %02x %02x %02x %02x %02x %02x\n",
current->pid,
regs->pc,
((u8 *) regs->pc)[0],
((u8 *) regs->pc)[1],
((u8 *) regs->pc)[2],
((u8 *) regs->pc)[3],
((u8 *) regs->pc)[4],
((u8 *) regs->pc)[5],
((u8 *) regs->pc)[6],
((u8 *) regs->pc)[7]
);
#endif
goto bad_area;
}
}
if (expand_stack(vma, address))
goto bad_area;
/*
* Ok, we have a good vm_area for this memory access, so
* we can handle it..
*/
good_area:
info.si_code = SEGV_ACCERR;
write = 0;
switch (fault_code & (MMUFCR_xFC_PGINVAL|MMUFCR_xFC_TYPE)) {
default: /* 3: write, present */
case MMUFCR_xFC_TYPE_WRITE:
#ifdef TEST_VERIFY_AREA
if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_SR)
printk(KERN_DEBUG "WP fault at %08lx\n", regs->pc);
#endif
/* write to absent page */
case MMUFCR_xFC_PGINVAL | MMUFCR_xFC_TYPE_WRITE:
if (!(vma->vm_flags & VM_WRITE))
goto bad_area;
write++;
break;
/* read from protected page */
case MMUFCR_xFC_TYPE_READ:
goto bad_area;
/* read from absent page present */
case MMUFCR_xFC_PGINVAL | MMUFCR_xFC_TYPE_READ:
if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
goto bad_area;
break;
}
/*
* If for any reason at all we couldn't handle the fault,
* make sure we exit gracefully rather than endlessly redo
* the fault.
*/
fault = handle_mm_fault(mm, vma, address, write);
if (unlikely(fault & VM_FAULT_ERROR)) {
if (fault & VM_FAULT_OOM)
goto out_of_memory;
else if (fault & VM_FAULT_SIGBUS)
goto do_sigbus;
BUG();
}
if (fault & VM_FAULT_MAJOR)
current->maj_flt++;
else
current->min_flt++;
up_read(&mm->mmap_sem);
return;
/*
* Something tried to access memory that isn't in our memory map..
* Fix it, but check if it's kernel or user first..
*/
bad_area:
up_read(&mm->mmap_sem);
monitor_signal(regs);
/* User mode accesses just cause a SIGSEGV */
if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_USR) {
info.si_signo = SIGSEGV;
info.si_errno = 0;
/* info.si_code has been set above */
info.si_addr = (void *)address;
force_sig_info(SIGSEGV, &info, tsk);
return;
}
no_context:
monitor_signal(regs);
/* Are we prepared to handle this kernel fault? */
if (fixup_exception(regs))
return;
/*
* Oops. The kernel tried to access some bad page. We'll have to
* terminate things with extreme prejudice.
*/
bust_spinlocks(1);
if (address < PAGE_SIZE)
printk(KERN_ALERT
"Unable to handle kernel NULL pointer dereference");
else
printk(KERN_ALERT
"Unable to handle kernel paging request");
printk(" at virtual address %08lx\n", address);
printk(" printing pc:\n");
printk(KERN_ALERT "%08lx\n", regs->pc);
#ifdef CONFIG_GDBSTUB
gdbstub_intercept(
regs, fault_code & 0x00010000 ? EXCEP_IAERROR : EXCEP_DAERROR);
#endif
page = PTBR;
page = ((unsigned long *) __va(page))[address >> 22];
printk(KERN_ALERT "*pde = %08lx\n", page);
if (page & 1) {
page &= PAGE_MASK;
address &= 0x003ff000;
page = ((unsigned long *) __va(page))[address >> PAGE_SHIFT];
printk(KERN_ALERT "*pte = %08lx\n", page);
}
die("Oops", regs, fault_code);
do_exit(SIGKILL);
/*
* We ran out of memory, or some other thing happened to us that made
* us unable to handle the page fault gracefully.
*/
out_of_memory:
up_read(&mm->mmap_sem);
monitor_signal(regs);
printk(KERN_ALERT "VM: killing process %s\n", tsk->comm);
if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_USR)
do_exit(SIGKILL);
goto no_context;
do_sigbus:
up_read(&mm->mmap_sem);
monitor_signal(regs);
/*
* Send a sigbus, regardless of whether we were in kernel
* or user mode.
*/
info.si_signo = SIGBUS;
info.si_errno = 0;
info.si_code = BUS_ADRERR;
info.si_addr = (void *)address;
force_sig_info(SIGBUS, &info, tsk);
/* Kernel mode? Handle exceptions or die */
if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_SR)
goto no_context;
return;
vmalloc_fault:
{
/*
* Synchronize this task's top level page-table
* with the 'reference' page table.
*
* Do _not_ use "tsk" here. We might be inside
* an interrupt in the middle of a task switch..
*/
int index = pgd_index(address);
pgd_t *pgd, *pgd_k;
pud_t *pud, *pud_k;
pmd_t *pmd, *pmd_k;
pte_t *pte_k;
pgd_k = init_mm.pgd + index;
if (!pgd_present(*pgd_k))
goto no_context;
pud_k = pud_offset(pgd_k, address);
if (!pud_present(*pud_k))
goto no_context;
pmd_k = pmd_offset(pud_k, address);
if (!pmd_present(*pmd_k))
goto no_context;
pgd = (pgd_t *) PTBR + index;
pud = pud_offset(pgd, address);
pmd = pmd_offset(pud, address);
set_pmd(pmd, *pmd_k);
pte_k = pte_offset_kernel(pmd_k, address);
if (!pte_present(*pte_k))
goto no_context;
return;
}
}

160
arch/mn10300/mm/init.c Normal file
View File

@ -0,0 +1,160 @@
/* MN10300 Memory management initialisation
*
* 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/signal.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/ptrace.h>
#include <linux/mman.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/swap.h>
#include <linux/smp.h>
#include <linux/init.h>
#include <linux/initrd.h>
#include <linux/highmem.h>
#include <linux/pagemap.h>
#include <linux/bootmem.h>
#include <asm/processor.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/pgalloc.h>
#include <asm/dma.h>
#include <asm/tlb.h>
#include <asm/sections.h>
DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
unsigned long highstart_pfn, highend_pfn;
/*
* set up paging
*/
void __init paging_init(void)
{
unsigned long zones_size[MAX_NR_ZONES] = {0,};
pte_t *ppte;
int loop;
/* main kernel space -> RAM mapping is handled as 1:1 transparent by
* the MMU */
memset(swapper_pg_dir, 0, sizeof(swapper_pg_dir));
memset(kernel_vmalloc_ptes, 0, sizeof(kernel_vmalloc_ptes));
/* load the VMALLOC area PTE table addresses into the kernel PGD */
ppte = kernel_vmalloc_ptes;
for (loop = VMALLOC_START / (PAGE_SIZE * PTRS_PER_PTE);
loop < VMALLOC_END / (PAGE_SIZE * PTRS_PER_PTE);
loop++
) {
set_pgd(swapper_pg_dir + loop, __pgd(__pa(ppte) | _PAGE_TABLE));
ppte += PAGE_SIZE / sizeof(pte_t);
}
/* declare the sizes of the RAM zones (only use the normal zone) */
zones_size[ZONE_NORMAL] =
(contig_page_data.bdata->node_low_pfn) -
(contig_page_data.bdata->node_boot_start >> PAGE_SHIFT);
/* pass the memory from the bootmem allocator to the main allocator */
free_area_init(zones_size);
__flush_tlb_all();
}
/*
* transfer all the memory from the bootmem allocator to the runtime allocator
*/
void __init mem_init(void)
{
int codesize, reservedpages, datasize, initsize;
int tmp;
if (!mem_map)
BUG();
#define START_PFN (contig_page_data.bdata->node_boot_start >> PAGE_SHIFT)
#define MAX_LOW_PFN (contig_page_data.bdata->node_low_pfn)
max_mapnr = num_physpages = MAX_LOW_PFN - START_PFN;
high_memory = (void *) __va(MAX_LOW_PFN * PAGE_SIZE);
/* clear the zero-page */
memset(empty_zero_page, 0, PAGE_SIZE);
/* this will put all low memory onto the freelists */
totalram_pages += free_all_bootmem();
reservedpages = 0;
for (tmp = 0; tmp < num_physpages; tmp++)
if (PageReserved(&mem_map[tmp]))
reservedpages++;
codesize = (unsigned long) &_etext - (unsigned long) &_stext;
datasize = (unsigned long) &_edata - (unsigned long) &_etext;
initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin;
printk(KERN_INFO
"Memory: %luk/%luk available"
" (%dk kernel code, %dk reserved, %dk data, %dk init,"
" %ldk highmem)\n",
(unsigned long) nr_free_pages() << (PAGE_SHIFT - 10),
max_mapnr << (PAGE_SHIFT - 10),
codesize >> 10,
reservedpages << (PAGE_SHIFT - 10),
datasize >> 10,
initsize >> 10,
(unsigned long) (totalhigh_pages << (PAGE_SHIFT - 10))
);
}
/*
*
*/
void free_init_pages(char *what, unsigned long begin, unsigned long end)
{
unsigned long addr;
for (addr = begin; addr < end; addr += PAGE_SIZE) {
ClearPageReserved(virt_to_page(addr));
init_page_count(virt_to_page(addr));
memset((void *) addr, 0xcc, PAGE_SIZE);
free_page(addr);
totalram_pages++;
}
printk(KERN_INFO "Freeing %s: %ldk freed\n", what, (end - begin) >> 10);
}
/*
* recycle memory containing stuff only required for initialisation
*/
void free_initmem(void)
{
free_init_pages("unused kernel memory",
(unsigned long) &__init_begin,
(unsigned long) &__init_end);
}
/*
* dispose of the memory on which the initial ramdisk resided
*/
#ifdef CONFIG_BLK_DEV_INITRD
void free_initrd_mem(unsigned long start, unsigned long end)
{
free_init_pages("initrd memory", start, end);
}
#endif

View File

@ -0,0 +1,661 @@
/* MN10300 Misalignment fixup 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/module.h>
#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/smp_lock.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <asm/processor.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/atomic.h>
#include <asm/smp.h>
#include <asm/pgalloc.h>
#include <asm/cpu-regs.h>
#include <asm/busctl-regs.h>
#include <asm/fpu.h>
#include <asm/gdb-stub.h>
#include <asm/asm-offsets.h>
#if 0
#define kdebug(FMT, ...) printk(KERN_DEBUG FMT, ##__VA_ARGS__)
#else
#define kdebug(FMT, ...) do {} while (0)
#endif
static int misalignment_addr(unsigned long *registers, unsigned params,
unsigned opcode, unsigned disp,
void **_address, unsigned long **_postinc);
static int misalignment_reg(unsigned long *registers, unsigned params,
unsigned opcode, unsigned disp,
unsigned long **_register);
static inline unsigned int_log2(unsigned x)
{
unsigned y;
asm("bsch %1,%0" : "=r"(y) : "r"(x), "0"(0));
return y;
}
#define log2(x) int_log2(x)
static const unsigned Dreg_index[] = {
REG_D0 >> 2, REG_D1 >> 2, REG_D2 >> 2, REG_D3 >> 2
};
static const unsigned Areg_index[] = {
REG_A0 >> 2, REG_A1 >> 2, REG_A2 >> 2, REG_A3 >> 2
};
static const unsigned Rreg_index[] = {
REG_E0 >> 2, REG_E1 >> 2, REG_E2 >> 2, REG_E3 >> 2,
REG_E4 >> 2, REG_E5 >> 2, REG_E6 >> 2, REG_E7 >> 2,
REG_A0 >> 2, REG_A1 >> 2, REG_A2 >> 2, REG_A3 >> 2,
REG_D0 >> 2, REG_D1 >> 2, REG_D2 >> 2, REG_D3 >> 2
};
enum format_id {
FMT_S0,
FMT_S1,
FMT_S2,
FMT_S4,
FMT_D0,
FMT_D1,
FMT_D2,
FMT_D4,
FMT_D6,
FMT_D7,
FMT_D8,
FMT_D9,
};
struct {
u_int8_t opsz, dispsz;
} format_tbl[16] = {
[FMT_S0] = { 8, 0 },
[FMT_S1] = { 8, 8 },
[FMT_S2] = { 8, 16 },
[FMT_S4] = { 8, 32 },
[FMT_D0] = { 16, 0 },
[FMT_D1] = { 16, 8 },
[FMT_D2] = { 16, 16 },
[FMT_D4] = { 16, 32 },
[FMT_D6] = { 24, 0 },
[FMT_D7] = { 24, 8 },
[FMT_D8] = { 24, 24 },
[FMT_D9] = { 24, 32 },
};
enum value_id {
DM0, /* data reg in opcode in bits 0-1 */
DM1, /* data reg in opcode in bits 2-3 */
DM2, /* data reg in opcode in bits 4-5 */
AM0, /* addr reg in opcode in bits 0-1 */
AM1, /* addr reg in opcode in bits 2-3 */
AM2, /* addr reg in opcode in bits 4-5 */
RM0, /* reg in opcode in bits 0-3 */
RM1, /* reg in opcode in bits 2-5 */
RM2, /* reg in opcode in bits 4-7 */
RM4, /* reg in opcode in bits 8-11 */
RM6, /* reg in opcode in bits 12-15 */
RD0, /* reg in displacement in bits 0-3 */
RD2, /* reg in displacement in bits 4-7 */
SP, /* stack pointer */
SD8, /* 8-bit signed displacement */
SD16, /* 16-bit signed displacement */
SD24, /* 24-bit signed displacement */
SIMM4_2, /* 4-bit signed displacement in opcode bits 4-7 */
SIMM8, /* 8-bit signed immediate */
IMM24, /* 24-bit unsigned immediate */
IMM32, /* 32-bit unsigned immediate */
IMM32_HIGH8, /* 32-bit unsigned immediate, high 8-bits in opcode */
DN0 = DM0,
DN1 = DM1,
DN2 = DM2,
AN0 = AM0,
AN1 = AM1,
AN2 = AM2,
RN0 = RM0,
RN1 = RM1,
RN2 = RM2,
RN4 = RM4,
RN6 = RM6,
DI = DM1,
RI = RM2,
};
struct mn10300_opcode {
const char *name;
u_int32_t opcode;
u_int32_t opmask;
unsigned exclusion;
enum format_id format;
unsigned cpu_mask;
#define AM33 330
unsigned params[2];
#define MEM(ADDR) (0x80000000 | (ADDR))
#define MEM2(ADDR1, ADDR2) (0x80000000 | (ADDR1) << 8 | (ADDR2))
#define MEMINC(ADDR) (0x81000000 | (ADDR))
#define MEMINC2(ADDR, INC) (0x81000000 | (ADDR) << 8 | (INC))
};
/* LIBOPCODES EXCERPT
Assemble Matsushita MN10300 instructions.
Copyright 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
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.
*/
static const struct mn10300_opcode mn10300_opcodes[] = {
{ "mov", 0x60, 0xf0, 0, FMT_S0, 0, {DM1, MEM(AN0)}},
{ "mov", 0x70, 0xf0, 0, FMT_S0, 0, {MEM(AM0), DN1}},
{ "mov", 0xf000, 0xfff0, 0, FMT_D0, 0, {MEM(AM0), AN1}},
{ "mov", 0xf010, 0xfff0, 0, FMT_D0, 0, {AM1, MEM(AN0)}},
{ "mov", 0xf300, 0xffc0, 0, FMT_D0, 0, {MEM2(DI, AM0), DN2}},
{ "mov", 0xf340, 0xffc0, 0, FMT_D0, 0, {DM2, MEM2(DI, AN0)}},
{ "mov", 0xf380, 0xffc0, 0, FMT_D0, 0, {MEM2(DI, AM0), AN2}},
{ "mov", 0xf3c0, 0xffc0, 0, FMT_D0, 0, {AM2, MEM2(DI, AN0)}},
{ "mov", 0xf80000, 0xfff000, 0, FMT_D1, 0, {MEM2(SD8, AM0), DN1}},
{ "mov", 0xf81000, 0xfff000, 0, FMT_D1, 0, {DM1, MEM2(SD8, AN0)}},
{ "mov", 0xf82000, 0xfff000, 0, FMT_D1, 0, {MEM2(SD8,AM0), AN1}},
{ "mov", 0xf83000, 0xfff000, 0, FMT_D1, 0, {AM1, MEM2(SD8, AN0)}},
{ "mov", 0xf8f000, 0xfffc00, 0, FMT_D1, AM33, {MEM2(SD8, AM0), SP}},
{ "mov", 0xf8f400, 0xfffc00, 0, FMT_D1, AM33, {SP, MEM2(SD8, AN0)}},
{ "mov", 0xf90a00, 0xffff00, 0, FMT_D6, AM33, {MEM(RM0), RN2}},
{ "mov", 0xf91a00, 0xffff00, 0, FMT_D6, AM33, {RM2, MEM(RN0)}},
{ "mov", 0xf96a00, 0xffff00, 0x12, FMT_D6, AM33, {MEMINC(RM0), RN2}},
{ "mov", 0xf97a00, 0xffff00, 0, FMT_D6, AM33, {RM2, MEMINC(RN0)}},
{ "mov", 0xfa000000, 0xfff00000, 0, FMT_D2, 0, {MEM2(SD16, AM0), DN1}},
{ "mov", 0xfa100000, 0xfff00000, 0, FMT_D2, 0, {DM1, MEM2(SD16, AN0)}},
{ "mov", 0xfa200000, 0xfff00000, 0, FMT_D2, 0, {MEM2(SD16, AM0), AN1}},
{ "mov", 0xfa300000, 0xfff00000, 0, FMT_D2, 0, {AM1, MEM2(SD16, AN0)}},
{ "mov", 0xfb0a0000, 0xffff0000, 0, FMT_D7, AM33, {MEM2(SD8, RM0), RN2}},
{ "mov", 0xfb1a0000, 0xffff0000, 0, FMT_D7, AM33, {RM2, MEM2(SD8, RN0)}},
{ "mov", 0xfb6a0000, 0xffff0000, 0x22, FMT_D7, AM33, {MEMINC2 (RM0, SIMM8), RN2}},
{ "mov", 0xfb7a0000, 0xffff0000, 0, FMT_D7, AM33, {RM2, MEMINC2 (RN0, SIMM8)}},
{ "mov", 0xfb8e0000, 0xffff000f, 0, FMT_D7, AM33, {MEM2(RI, RM0), RD2}},
{ "mov", 0xfb9e0000, 0xffff000f, 0, FMT_D7, AM33, {RD2, MEM2(RI, RN0)}},
{ "mov", 0xfc000000, 0xfff00000, 0, FMT_D4, 0, {MEM2(IMM32,AM0), DN1}},
{ "mov", 0xfc100000, 0xfff00000, 0, FMT_D4, 0, {DM1, MEM2(IMM32,AN0)}},
{ "mov", 0xfc200000, 0xfff00000, 0, FMT_D4, 0, {MEM2(IMM32,AM0), AN1}},
{ "mov", 0xfc300000, 0xfff00000, 0, FMT_D4, 0, {AM1, MEM2(IMM32,AN0)}},
{ "mov", 0xfd0a0000, 0xffff0000, 0, FMT_D8, AM33, {MEM2(SD24, RM0), RN2}},
{ "mov", 0xfd1a0000, 0xffff0000, 0, FMT_D8, AM33, {RM2, MEM2(SD24, RN0)}},
{ "mov", 0xfd6a0000, 0xffff0000, 0x22, FMT_D8, AM33, {MEMINC2 (RM0, IMM24), RN2}},
{ "mov", 0xfd7a0000, 0xffff0000, 0, FMT_D8, AM33, {RM2, MEMINC2 (RN0, IMM24)}},
{ "mov", 0xfe0a0000, 0xffff0000, 0, FMT_D9, AM33, {MEM2(IMM32_HIGH8,RM0), RN2}},
{ "mov", 0xfe1a0000, 0xffff0000, 0, FMT_D9, AM33, {RM2, MEM2(IMM32_HIGH8, RN0)}},
{ "mov", 0xfe6a0000, 0xffff0000, 0x22, FMT_D9, AM33, {MEMINC2 (RM0, IMM32_HIGH8), RN2}},
{ "mov", 0xfe7a0000, 0xffff0000, 0, FMT_D9, AM33, {RN2, MEMINC2 (RM0, IMM32_HIGH8)}},
{ "movhu", 0xf060, 0xfff0, 0, FMT_D0, 0, {MEM(AM0), DN1}},
{ "movhu", 0xf070, 0xfff0, 0, FMT_D0, 0, {DM1, MEM(AN0)}},
{ "movhu", 0xf480, 0xffc0, 0, FMT_D0, 0, {MEM2(DI, AM0), DN2}},
{ "movhu", 0xf4c0, 0xffc0, 0, FMT_D0, 0, {DM2, MEM2(DI, AN0)}},
{ "movhu", 0xf86000, 0xfff000, 0, FMT_D1, 0, {MEM2(SD8, AM0), DN1}},
{ "movhu", 0xf87000, 0xfff000, 0, FMT_D1, 0, {DM1, MEM2(SD8, AN0)}},
{ "movhu", 0xf94a00, 0xffff00, 0, FMT_D6, AM33, {MEM(RM0), RN2}},
{ "movhu", 0xf95a00, 0xffff00, 0, FMT_D6, AM33, {RM2, MEM(RN0)}},
{ "movhu", 0xf9ea00, 0xffff00, 0x12, FMT_D6, AM33, {MEMINC(RM0), RN2}},
{ "movhu", 0xf9fa00, 0xffff00, 0, FMT_D6, AM33, {RM2, MEMINC(RN0)}},
{ "movhu", 0xfa600000, 0xfff00000, 0, FMT_D2, 0, {MEM2(SD16, AM0), DN1}},
{ "movhu", 0xfa700000, 0xfff00000, 0, FMT_D2, 0, {DM1, MEM2(SD16, AN0)}},
{ "movhu", 0xfb4a0000, 0xffff0000, 0, FMT_D7, AM33, {MEM2(SD8, RM0), RN2}},
{ "movhu", 0xfb5a0000, 0xffff0000, 0, FMT_D7, AM33, {RM2, MEM2(SD8, RN0)}},
{ "movhu", 0xfbce0000, 0xffff000f, 0, FMT_D7, AM33, {MEM2(RI, RM0), RD2}},
{ "movhu", 0xfbde0000, 0xffff000f, 0, FMT_D7, AM33, {RD2, MEM2(RI, RN0)}},
{ "movhu", 0xfbea0000, 0xffff0000, 0x22, FMT_D7, AM33, {MEMINC2 (RM0, SIMM8), RN2}},
{ "movhu", 0xfbfa0000, 0xffff0000, 0, FMT_D7, AM33, {RM2, MEMINC2 (RN0, SIMM8)}},
{ "movhu", 0xfc600000, 0xfff00000, 0, FMT_D4, 0, {MEM2(IMM32,AM0), DN1}},
{ "movhu", 0xfc700000, 0xfff00000, 0, FMT_D4, 0, {DM1, MEM2(IMM32,AN0)}},
{ "movhu", 0xfd4a0000, 0xffff0000, 0, FMT_D8, AM33, {MEM2(SD24, RM0), RN2}},
{ "movhu", 0xfd5a0000, 0xffff0000, 0, FMT_D8, AM33, {RM2, MEM2(SD24, RN0)}},
{ "movhu", 0xfdea0000, 0xffff0000, 0x22, FMT_D8, AM33, {MEMINC2 (RM0, IMM24), RN2}},
{ "movhu", 0xfdfa0000, 0xffff0000, 0, FMT_D8, AM33, {RM2, MEMINC2 (RN0, IMM24)}},
{ "movhu", 0xfe4a0000, 0xffff0000, 0, FMT_D9, AM33, {MEM2(IMM32_HIGH8,RM0), RN2}},
{ "movhu", 0xfe5a0000, 0xffff0000, 0, FMT_D9, AM33, {RM2, MEM2(IMM32_HIGH8, RN0)}},
{ "movhu", 0xfeea0000, 0xffff0000, 0x22, FMT_D9, AM33, {MEMINC2 (RM0, IMM32_HIGH8), RN2}},
{ "movhu", 0xfefa0000, 0xffff0000, 0, FMT_D9, AM33, {RN2, MEMINC2 (RM0, IMM32_HIGH8)}},
{ 0, 0, 0, 0, 0, 0, {0}},
};
/*
* fix up misalignment problems where possible
*/
asmlinkage void misalignment(struct pt_regs *regs, enum exception_code code)
{
const struct exception_table_entry *fixup;
const struct mn10300_opcode *pop;
unsigned long *registers = (unsigned long *) regs;
unsigned long data, *store, *postinc;
mm_segment_t seg;
siginfo_t info;
uint32_t opcode, disp, noc, xo, xm;
uint8_t *pc, byte;
void *address;
unsigned tmp, npop;
kdebug("MISALIGN at %lx\n", regs->pc);
if (in_interrupt())
die("Misalignment trap in interrupt context", regs, code);
if (regs->epsw & EPSW_IE)
asm volatile("or %0,epsw" : : "i"(EPSW_IE));
seg = get_fs();
set_fs(KERNEL_DS);
fixup = search_exception_tables(regs->pc);
/* first thing to do is to match the opcode */
pc = (u_int8_t *) regs->pc;
if (__get_user(byte, pc) != 0)
goto fetch_error;
opcode = byte;
noc = 8;
for (pop = mn10300_opcodes; pop->name; pop++) {
npop = log2(pop->opcode | pop->opmask);
if (npop <= 0 || npop > 31)
continue;
npop = (npop + 8) & ~7;
got_more_bits:
if (npop == noc) {
if ((opcode & pop->opmask) == pop->opcode)
goto found_opcode;
} else if (npop > noc) {
xo = pop->opcode >> (npop - noc);
xm = pop->opmask >> (npop - noc);
if ((opcode & xm) != xo)
continue;
/* we've got a partial match (an exact match on the
* first N bytes), so we need to get some more data */
pc++;
if (__get_user(byte, pc) != 0)
goto fetch_error;
opcode = opcode << 8 | byte;
noc += 8;
goto got_more_bits;
} else {
/* there's already been a partial match as long as the
* complete match we're now considering, so this one
* should't match */
continue;
}
}
/* didn't manage to find a fixup */
if (!user_mode(regs))
printk(KERN_CRIT "MISALIGN: %lx: unsupported instruction %x\n",
regs->pc, opcode);
failed:
set_fs(seg);
if (die_if_no_fixup("misalignment error", regs, code))
return;
info.si_signo = SIGBUS;
info.si_errno = 0;
info.si_code = BUS_ADRALN;
info.si_addr = (void *) regs->pc;
force_sig_info(SIGBUS, &info, current);
return;
/* error reading opcodes */
fetch_error:
if (!user_mode(regs))
printk(KERN_CRIT
"MISALIGN: %p: fault whilst reading instruction data\n",
pc);
goto failed;
bad_addr_mode:
if (!user_mode(regs))
printk(KERN_CRIT
"MISALIGN: %lx: unsupported addressing mode %x\n",
regs->pc, opcode);
goto failed;
bad_reg_mode:
if (!user_mode(regs))
printk(KERN_CRIT
"MISALIGN: %lx: unsupported register mode %x\n",
regs->pc, opcode);
goto failed;
unsupported_instruction:
if (!user_mode(regs))
printk(KERN_CRIT
"MISALIGN: %lx: unsupported instruction %x (%s)\n",
regs->pc, opcode, pop->name);
goto failed;
transfer_failed:
set_fs(seg);
if (fixup) {
regs->pc = fixup->fixup;
return;
}
if (die_if_no_fixup("misalignment fixup", regs, code))
return;
info.si_signo = SIGSEGV;
info.si_errno = 0;
info.si_code = 0;
info.si_addr = (void *) regs->pc;
force_sig_info(SIGSEGV, &info, current);
return;
/* we matched the opcode */
found_opcode:
kdebug("MISALIGN: %lx: %x==%x { %x, %x }\n",
regs->pc, opcode, pop->opcode, pop->params[0], pop->params[1]);
tmp = format_tbl[pop->format].opsz;
if (tmp > noc)
BUG(); /* match was less complete than it ought to have been */
if (tmp < noc) {
tmp = noc - tmp;
opcode >>= tmp;
pc -= tmp >> 3;
}
/* grab the extra displacement (note it's LSB first) */
disp = 0;
tmp = format_tbl[pop->format].dispsz >> 3;
while (tmp > 0) {
tmp--;
disp <<= 8;
pc++;
if (__get_user(byte, pc) != 0)
goto fetch_error;
disp |= byte;
}
set_fs(KERNEL_XDS);
if (fixup || regs->epsw & EPSW_nSL)
set_fs(seg);
tmp = (pop->params[0] ^ pop->params[1]) & 0x80000000;
if (!tmp) {
if (!user_mode(regs))
printk(KERN_CRIT
"MISALIGN: %lx:"
" insn not move to/from memory %x\n",
regs->pc, opcode);
goto failed;
}
if (pop->params[0] & 0x80000000) {
/* move memory to register */
if (!misalignment_addr(registers, pop->params[0], opcode, disp,
&address, &postinc))
goto bad_addr_mode;
if (!misalignment_reg(registers, pop->params[1], opcode, disp,
&store))
goto bad_reg_mode;
if (strcmp(pop->name, "mov") == 0) {
kdebug("FIXUP: mov (%p),DARn\n", address);
if (copy_from_user(&data, (void *) address, 4) != 0)
goto transfer_failed;
if (pop->params[0] & 0x1000000)
*postinc += 4;
} else if (strcmp(pop->name, "movhu") == 0) {
kdebug("FIXUP: movhu (%p),DARn\n", address);
data = 0;
if (copy_from_user(&data, (void *) address, 2) != 0)
goto transfer_failed;
if (pop->params[0] & 0x1000000)
*postinc += 2;
} else {
goto unsupported_instruction;
}
*store = data;
} else {
/* move register to memory */
if (!misalignment_reg(registers, pop->params[0], opcode, disp,
&store))
goto bad_reg_mode;
if (!misalignment_addr(registers, pop->params[1], opcode, disp,
&address, &postinc))
goto bad_addr_mode;
data = *store;
if (strcmp(pop->name, "mov") == 0) {
kdebug("FIXUP: mov %lx,(%p)\n", data, address);
if (copy_to_user((void *) address, &data, 4) != 0)
goto transfer_failed;
if (pop->params[1] & 0x1000000)
*postinc += 4;
} else if (strcmp(pop->name, "movhu") == 0) {
kdebug("FIXUP: movhu %hx,(%p)\n",
(uint16_t) data, address);
if (copy_to_user((void *) address, &data, 2) != 0)
goto transfer_failed;
if (pop->params[1] & 0x1000000)
*postinc += 2;
} else {
goto unsupported_instruction;
}
}
tmp = format_tbl[pop->format].opsz + format_tbl[pop->format].dispsz;
regs->pc += tmp >> 3;
set_fs(seg);
return;
}
/*
* determine the address that was being accessed
*/
static int misalignment_addr(unsigned long *registers, unsigned params,
unsigned opcode, unsigned disp,
void **_address, unsigned long **_postinc)
{
unsigned long *postinc = NULL, address = 0, tmp;
params &= 0x7fffffff;
do {
switch (params & 0xff) {
case DM0:
postinc = &registers[Dreg_index[opcode & 0x03]];
address += *postinc;
break;
case DM1:
postinc = &registers[Dreg_index[opcode >> 2 & 0x0c]];
address += *postinc;
break;
case DM2:
postinc = &registers[Dreg_index[opcode >> 4 & 0x30]];
address += *postinc;
break;
case AM0:
postinc = &registers[Areg_index[opcode & 0x03]];
address += *postinc;
break;
case AM1:
postinc = &registers[Areg_index[opcode >> 2 & 0x0c]];
address += *postinc;
break;
case AM2:
postinc = &registers[Areg_index[opcode >> 4 & 0x30]];
address += *postinc;
break;
case RM0:
postinc = &registers[Rreg_index[opcode & 0x0f]];
address += *postinc;
break;
case RM1:
postinc = &registers[Rreg_index[opcode >> 2 & 0x0f]];
address += *postinc;
break;
case RM2:
postinc = &registers[Rreg_index[opcode >> 4 & 0x0f]];
address += *postinc;
break;
case RM4:
postinc = &registers[Rreg_index[opcode >> 8 & 0x0f]];
address += *postinc;
break;
case RM6:
postinc = &registers[Rreg_index[opcode >> 12 & 0x0f]];
address += *postinc;
break;
case RD0:
postinc = &registers[Rreg_index[disp & 0x0f]];
address += *postinc;
break;
case RD2:
postinc = &registers[Rreg_index[disp >> 4 & 0x0f]];
address += *postinc;
break;
case SD8:
case SIMM8:
address += (int32_t) (int8_t) (disp & 0xff);
break;
case SD16:
address += (int32_t) (int16_t) (disp & 0xffff);
break;
case SD24:
tmp = disp << 8;
asm("asr 8,%0" : "=r"(tmp) : "0"(tmp));
address += tmp;
break;
case SIMM4_2:
tmp = opcode >> 4 & 0x0f;
tmp <<= 28;
asm("asr 28,%0" : "=r"(tmp) : "0"(tmp));
address += tmp;
break;
case IMM24:
address += disp & 0x00ffffff;
break;
case IMM32:
case IMM32_HIGH8:
address += disp;
break;
default:
return 0;
}
} while ((params >>= 8));
*_address = (void *) address;
*_postinc = postinc;
return 1;
}
/*
* determine the register that is acting as source/dest
*/
static int misalignment_reg(unsigned long *registers, unsigned params,
unsigned opcode, unsigned disp,
unsigned long **_register)
{
params &= 0x7fffffff;
if (params & 0xffffff00)
return 0;
switch (params & 0xff) {
case DM0:
*_register = &registers[Dreg_index[opcode & 0x03]];
break;
case DM1:
*_register = &registers[Dreg_index[opcode >> 2 & 0x03]];
break;
case DM2:
*_register = &registers[Dreg_index[opcode >> 4 & 0x03]];
break;
case AM0:
*_register = &registers[Areg_index[opcode & 0x03]];
break;
case AM1:
*_register = &registers[Areg_index[opcode >> 2 & 0x03]];
break;
case AM2:
*_register = &registers[Areg_index[opcode >> 4 & 0x03]];
break;
case RM0:
*_register = &registers[Rreg_index[opcode & 0x0f]];
break;
case RM1:
*_register = &registers[Rreg_index[opcode >> 2 & 0x0f]];
break;
case RM2:
*_register = &registers[Rreg_index[opcode >> 4 & 0x0f]];
break;
case RM4:
*_register = &registers[Rreg_index[opcode >> 8 & 0x0f]];
break;
case RM6:
*_register = &registers[Rreg_index[opcode >> 12 & 0x0f]];
break;
case RD0:
*_register = &registers[Rreg_index[disp & 0x0f]];
break;
case RD2:
*_register = &registers[Rreg_index[disp >> 4 & 0x0f]];
break;
case SP:
*_register = &registers[REG_SP >> 2];
break;
default:
return 0;
}
return 1;
}

View File

@ -0,0 +1,80 @@
/* MN10300 MMU context allocation and 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/sched.h>
#include <linux/mm.h>
#include <asm/mmu_context.h>
#include <asm/tlbflush.h>
/*
* list of the MMU contexts last allocated on each CPU
*/
unsigned long mmu_context_cache[NR_CPUS] = {
[0 ... NR_CPUS - 1] = MMU_CONTEXT_FIRST_VERSION * 2 - 1,
};
/*
* flush the specified TLB entry
*/
void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
{
unsigned long pteu, cnx, flags;
addr &= PAGE_MASK;
/* make sure the context doesn't migrate and defend against
* interference from vmalloc'd regions */
local_irq_save(flags);
cnx = mm_context(vma->vm_mm);
if (cnx != MMU_NO_CONTEXT) {
pteu = addr | (cnx & 0x000000ffUL);
IPTEU = pteu;
DPTEU = pteu;
if (IPTEL & xPTEL_V)
IPTEL = 0;
if (DPTEL & xPTEL_V)
DPTEL = 0;
}
local_irq_restore(flags);
}
/*
* preemptively set a TLB entry
*/
void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, pte_t pte)
{
unsigned long pteu, ptel, cnx, flags;
addr &= PAGE_MASK;
ptel = pte_val(pte) & ~(xPTEL_UNUSED1 | xPTEL_UNUSED2);
/* make sure the context doesn't migrate and defend against
* interference from vmalloc'd regions */
local_irq_save(flags);
cnx = mm_context(vma->vm_mm);
if (cnx != MMU_NO_CONTEXT) {
pteu = addr | (cnx & 0x000000ffUL);
if (!(pte_val(pte) & _PAGE_NX)) {
IPTEU = pteu;
if (IPTEL & xPTEL_V)
IPTEL = ptel;
}
DPTEU = pteu;
if (DPTEL & xPTEL_V)
DPTEL = ptel;
}
local_irq_restore(flags);
}

197
arch/mn10300/mm/pgtable.c Normal file
View File

@ -0,0 +1,197 @@
/* MN10300 Page table management
*
* 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/errno.h>
#include <linux/mm.h>
#include <linux/swap.h>
#include <linux/smp.h>
#include <linux/highmem.h>
#include <linux/slab.h>
#include <linux/pagemap.h>
#include <linux/spinlock.h>
#include <linux/quicklist.h>
#include <asm/system.h>
#include <asm/pgtable.h>
#include <asm/pgalloc.h>
#include <asm/tlb.h>
#include <asm/tlbflush.h>
void show_mem(void)
{
unsigned long i;
int free = 0, total = 0, reserved = 0, shared = 0;
int cached = 0;
printk(KERN_INFO "Mem-info:\n");
show_free_areas();
i = max_mapnr;
while (i-- > 0) {
total++;
if (PageReserved(mem_map + i))
reserved++;
else if (PageSwapCache(mem_map + i))
cached++;
else if (!page_count(mem_map + i))
free++;
else
shared += page_count(mem_map + i) - 1;
}
printk(KERN_INFO "%d pages of RAM\n", total);
printk(KERN_INFO "%d free pages\n", free);
printk(KERN_INFO "%d reserved pages\n", reserved);
printk(KERN_INFO "%d pages shared\n", shared);
printk(KERN_INFO "%d pages swap cached\n", cached);
}
/*
* Associate a large virtual page frame with a given physical page frame
* and protection flags for that frame. pfn is for the base of the page,
* vaddr is what the page gets mapped to - both must be properly aligned.
* The pmd must already be instantiated. Assumes PAE mode.
*/
void set_pmd_pfn(unsigned long vaddr, unsigned long pfn, pgprot_t flags)
{
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
if (vaddr & (PMD_SIZE-1)) { /* vaddr is misaligned */
printk(KERN_ERR "set_pmd_pfn: vaddr misaligned\n");
return; /* BUG(); */
}
if (pfn & (PTRS_PER_PTE-1)) { /* pfn is misaligned */
printk(KERN_ERR "set_pmd_pfn: pfn misaligned\n");
return; /* BUG(); */
}
pgd = swapper_pg_dir + pgd_index(vaddr);
if (pgd_none(*pgd)) {
printk(KERN_ERR "set_pmd_pfn: pgd_none\n");
return; /* BUG(); */
}
pud = pud_offset(pgd, vaddr);
pmd = pmd_offset(pud, vaddr);
set_pmd(pmd, pfn_pmd(pfn, flags));
/*
* It's enough to flush this one mapping.
* (PGE mappings get flushed as well)
*/
__flush_tlb_one(vaddr);
}
pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address)
{
pte_t *pte = (pte_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT);
if (pte)
clear_page(pte);
return pte;
}
struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address)
{
struct page *pte;
#ifdef CONFIG_HIGHPTE
pte = alloc_pages(GFP_KERNEL|__GFP_HIGHMEM|__GFP_REPEAT, 0);
#else
pte = alloc_pages(GFP_KERNEL|__GFP_REPEAT, 0);
#endif
if (pte)
clear_highpage(pte);
return pte;
}
/*
* List of all pgd's needed for non-PAE so it can invalidate entries
* in both cached and uncached pgd's; not needed for PAE since the
* kernel pmd is shared. If PAE were not to share the pmd a similar
* tactic would be needed. This is essentially codepath-based locking
* against pageattr.c; it is the unique case in which a valid change
* of kernel pagetables can't be lazily synchronized by vmalloc faults.
* vmalloc faults work because attached pagetables are never freed.
* If the locking proves to be non-performant, a ticketing scheme with
* checks at dup_mmap(), exec(), and other mmlist addition points
* could be used. The locking scheme was chosen on the basis of
* manfred's recommendations and having no core impact whatsoever.
* -- wli
*/
DEFINE_SPINLOCK(pgd_lock);
struct page *pgd_list;
static inline void pgd_list_add(pgd_t *pgd)
{
struct page *page = virt_to_page(pgd);
page->index = (unsigned long) pgd_list;
if (pgd_list)
set_page_private(pgd_list, (unsigned long) &page->index);
pgd_list = page;
set_page_private(page, (unsigned long) &pgd_list);
}
static inline void pgd_list_del(pgd_t *pgd)
{
struct page *next, **pprev, *page = virt_to_page(pgd);
next = (struct page *) page->index;
pprev = (struct page **) page_private(page);
*pprev = next;
if (next)
set_page_private(next, (unsigned long) pprev);
}
void pgd_ctor(void *pgd)
{
unsigned long flags;
if (PTRS_PER_PMD == 1)
spin_lock_irqsave(&pgd_lock, flags);
memcpy((pgd_t *)pgd + USER_PTRS_PER_PGD,
swapper_pg_dir + USER_PTRS_PER_PGD,
(PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
if (PTRS_PER_PMD > 1)
return;
pgd_list_add(pgd);
spin_unlock_irqrestore(&pgd_lock, flags);
memset(pgd, 0, USER_PTRS_PER_PGD * sizeof(pgd_t));
}
/* never called when PTRS_PER_PMD > 1 */
void pgd_dtor(void *pgd)
{
unsigned long flags; /* can be called from interrupt context */
spin_lock_irqsave(&pgd_lock, flags);
pgd_list_del(pgd);
spin_unlock_irqrestore(&pgd_lock, flags);
}
pgd_t *pgd_alloc(struct mm_struct *mm)
{
return quicklist_alloc(0, GFP_KERNEL, pgd_ctor);
}
void pgd_free(struct mm_struct *mm, pgd_t *pgd)
{
quicklist_free(0, pgd_dtor, pgd);
}
void __init pgtable_cache_init(void)
{
}
void check_pgt_cache(void)
{
quicklist_trim(0, pgd_dtor, 25, 16);
}

View File

@ -0,0 +1,207 @@
###############################################################################
#
# TLB loading functions
#
# 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/intctl-regs.h>
#include <asm/frame.inc>
#include <asm/page.h>
#include <asm/pgtable.h>
###############################################################################
#
# Instruction TLB Miss handler entry point
#
###############################################################################
.type itlb_miss,@function
ENTRY(itlb_miss)
and ~EPSW_NMID,epsw
#ifdef CONFIG_GDBSTUB
movm [d2,d3,a2],(sp)
#else
or EPSW_nAR,epsw # switch D0-D3 & A0-A3 to the alternate
# register bank
nop
nop
nop
#endif
mov (IPTEU),d3
mov (PTBR),a2
mov d3,d2
and 0xffc00000,d2
lsr 20,d2
mov (a2,d2),a2 # PTD *ptd = PGD[addr 31..22]
btst _PAGE_VALID,a2
beq itlb_miss_fault # jump if doesn't point anywhere
and ~(PAGE_SIZE-1),a2
mov d3,d2
and 0x003ff000,d2
lsr 10,d2
add d2,a2
mov (a2),d2 # get pte from PTD[addr 21..12]
btst _PAGE_VALID,d2
beq itlb_miss_fault # jump if doesn't point to a page
# (might be a swap id)
bset _PAGE_ACCESSED,(0,a2)
and ~(xPTEL_UNUSED1|xPTEL_UNUSED2),d2
itlb_miss_set:
mov d2,(IPTEL) # change the TLB
#ifdef CONFIG_GDBSTUB
movm (sp),[d2,d3,a2]
#endif
rti
itlb_miss_fault:
mov _PAGE_VALID,d2 # force address error handler to be
# invoked
bra itlb_miss_set
.size itlb_miss, . - itlb_miss
###############################################################################
#
# Data TLB Miss handler entry point
#
###############################################################################
.type dtlb_miss,@function
ENTRY(dtlb_miss)
and ~EPSW_NMID,epsw
#ifdef CONFIG_GDBSTUB
movm [d2,d3,a2],(sp)
#else
or EPSW_nAR,epsw # switch D0-D3 & A0-A3 to the alternate
# register bank
nop
nop
nop
#endif
mov (DPTEU),d3
mov (PTBR),a2
mov d3,d2
and 0xffc00000,d2
lsr 20,d2
mov (a2,d2),a2 # PTD *ptd = PGD[addr 31..22]
btst _PAGE_VALID,a2
beq dtlb_miss_fault # jump if doesn't point anywhere
and ~(PAGE_SIZE-1),a2
mov d3,d2
and 0x003ff000,d2
lsr 10,d2
add d2,a2
mov (a2),d2 # get pte from PTD[addr 21..12]
btst _PAGE_VALID,d2
beq dtlb_miss_fault # jump if doesn't point to a page
# (might be a swap id)
bset _PAGE_ACCESSED,(0,a2)
and ~(xPTEL_UNUSED1|xPTEL_UNUSED2),d2
dtlb_miss_set:
mov d2,(DPTEL) # change the TLB
#ifdef CONFIG_GDBSTUB
movm (sp),[d2,d3,a2]
#endif
rti
dtlb_miss_fault:
mov _PAGE_VALID,d2 # force address error handler to be
# invoked
bra dtlb_miss_set
.size dtlb_miss, . - dtlb_miss
###############################################################################
#
# Instruction TLB Address Error handler entry point
#
###############################################################################
.type itlb_aerror,@function
ENTRY(itlb_aerror)
and ~EPSW_NMID,epsw
add -4,sp
SAVE_ALL
add -4,sp # need to pass three params
# calculate the fault code
movhu (MMUFCR_IFC),d1
or 0x00010000,d1 # it's an instruction fetch
# determine the page address
mov (IPTEU),a2
mov a2,d0
and PAGE_MASK,d0
mov d0,(12,sp)
clr d0
mov d0,(IPTEL)
and ~EPSW_NMID,epsw
or EPSW_IE,epsw
mov fp,d0
call do_page_fault[],0 # do_page_fault(regs,code,addr
jmp ret_from_exception
.size itlb_aerror, . - itlb_aerror
###############################################################################
#
# Data TLB Address Error handler entry point
#
###############################################################################
.type dtlb_aerror,@function
ENTRY(dtlb_aerror)
and ~EPSW_NMID,epsw
add -4,sp
mov d1,(sp)
movhu (MMUFCR_DFC),d1 # is it the initial valid write
# to this page?
and MMUFCR_xFC_INITWR,d1
beq dtlb_pagefault # jump if not
mov (DPTEL),d1 # set the dirty bit
# (don't replace with BSET!)
or _PAGE_DIRTY,d1
mov d1,(DPTEL)
mov (sp),d1
add 4,sp
rti
ALIGN
dtlb_pagefault:
mov (sp),d1
SAVE_ALL
add -4,sp # need to pass three params
# calculate the fault code
movhu (MMUFCR_DFC),d1
# determine the page address
mov (DPTEU),a2
mov a2,d0
and PAGE_MASK,d0
mov d0,(12,sp)
clr d0
mov d0,(DPTEL)
and ~EPSW_NMID,epsw
or EPSW_IE,epsw
mov fp,d0
call do_page_fault[],0 # do_page_fault(regs,code,addr
jmp ret_from_exception
.size dtlb_aerror, . - dtlb_aerror

View File

@ -0,0 +1,23 @@
menu "Profiling support"
depends on EXPERIMENTAL
config PROFILING
bool "Profiling support (EXPERIMENTAL)"
help
Say Y here to enable the extended profiling support mechanisms used
by profilers such as OProfile.
config OPROFILE
tristate "OProfile system profiling (EXPERIMENTAL)"
depends on PROFILING
help
OProfile is a profiling system capable of profiling the
whole system, include the kernel, kernel modules, libraries,
and applications.
If unsure, say N.
endmenu

View File

@ -0,0 +1,13 @@
#
# Makefile for the MN10300-specific profiling code
#
obj-$(CONFIG_OPROFILE) += oprofile.o
DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \
oprof.o cpu_buffer.o buffer_sync.o \
event_buffer.o oprofile_files.o \
oprofilefs.o oprofile_stats.o \
timer_int.o )
oprofile-y := $(DRIVER_OBJS) op_model_null.o

View File

@ -0,0 +1,22 @@
/* Null profiling driver
*
* Copyright (C) 2003 Paul Mundt
*
* This file is subject to the terms and conditions of the GNU General Public
* Licence. See the file "COPYING" in the main directory of this archive
* for more details.
*/
#include <linux/kernel.h>
#include <linux/oprofile.h>
#include <linux/init.h>
#include <linux/errno.h>
int __init oprofile_arch_init(struct oprofile_operations *ops)
{
return -ENODEV;
}
void oprofile_arch_exit(void)
{
}

View File

@ -0,0 +1,5 @@
#
# Makefile for the MN103E010 processor chip specific code
#
obj-y := proc-init.o

View File

@ -0,0 +1,75 @@
/* MN103E010 Processor 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/kernel.h>
#include <asm/rtc.h>
/*
* initialise the on-silicon processor peripherals
*/
asmlinkage void __init processor_init(void)
{
int loop;
/* set up the exception table first */
for (loop = 0x000; loop < 0x400; loop += 8)
__set_intr_stub(loop, __common_exception);
__set_intr_stub(EXCEP_ITLBMISS, itlb_miss);
__set_intr_stub(EXCEP_DTLBMISS, dtlb_miss);
__set_intr_stub(EXCEP_IAERROR, itlb_aerror);
__set_intr_stub(EXCEP_DAERROR, dtlb_aerror);
__set_intr_stub(EXCEP_BUSERROR, raw_bus_error);
__set_intr_stub(EXCEP_DOUBLE_FAULT, double_fault);
__set_intr_stub(EXCEP_SYSCALL0, system_call);
__set_intr_stub(EXCEP_NMI, nmi_handler);
__set_intr_stub(EXCEP_WDT, nmi_handler);
__set_intr_stub(EXCEP_IRQ_LEVEL0, irq_handler);
__set_intr_stub(EXCEP_IRQ_LEVEL1, irq_handler);
__set_intr_stub(EXCEP_IRQ_LEVEL2, irq_handler);
__set_intr_stub(EXCEP_IRQ_LEVEL3, irq_handler);
__set_intr_stub(EXCEP_IRQ_LEVEL4, irq_handler);
__set_intr_stub(EXCEP_IRQ_LEVEL5, irq_handler);
__set_intr_stub(EXCEP_IRQ_LEVEL6, irq_handler);
IVAR0 = EXCEP_IRQ_LEVEL0;
IVAR1 = EXCEP_IRQ_LEVEL1;
IVAR2 = EXCEP_IRQ_LEVEL2;
IVAR3 = EXCEP_IRQ_LEVEL3;
IVAR4 = EXCEP_IRQ_LEVEL4;
IVAR5 = EXCEP_IRQ_LEVEL5;
IVAR6 = EXCEP_IRQ_LEVEL6;
mn10300_dcache_flush_inv();
mn10300_icache_inv();
/* disable all interrupts and set to priority 6 (lowest) */
for (loop = 0; loop < NR_IRQS; loop++)
GxICR(loop) = GxICR_LEVEL_6 | GxICR_DETECT;
/* clear the timers */
TM0MD = 0;
TM1MD = 0;
TM2MD = 0;
TM3MD = 0;
TM4MD = 0;
TM5MD = 0;
TM6MD = 0;
TM6MDA = 0;
TM6MDB = 0;
TM7MD = 0;
TM8MD = 0;
TM9MD = 0;
TM10MD = 0;
TM11MD = 0;
calibrate_clock();
}

View File

@ -0,0 +1,6 @@
###############################################################################
#
# Makefile for the ASB2303 board
#
###############################################################################
obj-y := unit-init.o smc91111.o leds.o

View File

@ -0,0 +1,52 @@
/* ASB2303 peripheral 7-segment LEDs x1 support
*
* 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/param.h>
#include <linux/init.h>
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/intctl-regs.h>
#include <asm/rtc-regs.h>
#include <asm/unit/leds.h>
#if 0
static const u8 asb2303_led_hex_tbl[16] = {
0x80, 0xf2, 0x48, 0x60, 0x32, 0x24, 0x04, 0xf0,
0x00, 0x20, 0x10, 0x06, 0x8c, 0x42, 0x0c, 0x1c
};
#endif
static const u8 asb2303_led_chase_tbl[6] = {
~0x02, /* top - segA */
~0x04, /* right top - segB */
~0x08, /* right bottom - segC */
~0x10, /* bottom - segD */
~0x20, /* left bottom - segE */
~0x40, /* left top - segF */
};
static unsigned asb2303_led_chase;
void peripheral_leds_display_exception(enum exception_code code)
{
ASB2303_GPIO0DEF = 0x5555; /* configure as an output port */
ASB2303_7SEGLEDS = 0x6d; /* triple horizontal bar */
}
void peripheral_leds_led_chase(void)
{
ASB2303_GPIO0DEF = 0x5555; /* configure as an output port */
ASB2303_7SEGLEDS = asb2303_led_chase_tbl[asb2303_led_chase];
asb2303_led_chase++;
if (asb2303_led_chase >= 6)
asb2303_led_chase = 0;
}

View File

@ -0,0 +1,52 @@
/* ASB2303 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/kernel.h>
#include <linux/param.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <asm/timex.h>
#include <asm/processor.h>
#include <asm/intctl-regs.h>
#include <asm/unit/smc91111.h>
static struct resource smc91c111_resources[] = {
[0] = {
.start = SMC91111_BASE,
.end = SMC91111_BASE_END,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = SMC91111_IRQ,
.end = SMC91111_IRQ,
.flags = IORESOURCE_IRQ,
},
};
static struct platform_device smc91c111_device = {
.name = "smc91x",
.id = 0,
.num_resources = ARRAY_SIZE(smc91c111_resources),
.resource = smc91c111_resources,
};
/*
* add platform devices
*/
static int __init unit_device_init(void)
{
platform_device_register(&smc91c111_device);
return 0;
}
device_initcall(unit_device_init);

View File

@ -0,0 +1,60 @@
/* ASB2303 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/kernel.h>
#include <linux/param.h>
#include <linux/init.h>
#include <linux/device.h>
#include <asm/io.h>
#include <asm/setup.h>
#include <asm/processor.h>
#include <asm/irq.h>
#include <asm/intctl-regs.h>
/*
* initialise some of the unit hardware before gdbstub is set up
*/
asmlinkage void __init unit_init(void)
{
/* set up the external interrupts */
SET_XIRQ_TRIGGER(0, XIRQ_TRIGGER_HILEVEL);
SET_XIRQ_TRIGGER(2, XIRQ_TRIGGER_LOWLEVEL);
SET_XIRQ_TRIGGER(3, XIRQ_TRIGGER_HILEVEL);
SET_XIRQ_TRIGGER(4, XIRQ_TRIGGER_LOWLEVEL);
SET_XIRQ_TRIGGER(5, XIRQ_TRIGGER_LOWLEVEL);
}
/*
* initialise the rest of the unit hardware after gdbstub is ready
*/
void __init unit_setup(void)
{
}
/*
* initialise the external interrupts used by a unit of this type
*/
void __init unit_init_IRQ(void)
{
unsigned int extnum;
for (extnum = 0; extnum < NR_XIRQS; extnum++) {
switch (GET_XIRQ_TRIGGER(extnum)) {
case XIRQ_TRIGGER_HILEVEL:
case XIRQ_TRIGGER_LOWLEVEL:
set_irq_handler(XIRQ2IRQ(extnum), handle_level_irq);
break;
default:
break;
}
}
}

View File

@ -0,0 +1,8 @@
###############################################################################
#
# Makefile for the ASB2305 board
#
###############################################################################
obj-y := unit-init.o leds.o
obj-$(CONFIG_PCI) += pci.o pci-asb2305.o pci-irq.o pci-iomap.o

View File

@ -0,0 +1,124 @@
/* ASB2305 Peripheral 7-segment LEDs x4 support
*
* 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/param.h>
#include <linux/init.h>
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/cpu/intctl-regs.h>
#include <asm/cpu/rtc-regs.h>
#include <asm/unit/leds.h>
static const u8 asb2305_led_hex_tbl[16] = {
0x80, 0xf2, 0x48, 0x60, 0x32, 0x24, 0x04, 0xf0,
0x00, 0x20, 0x10, 0x06, 0x8c, 0x42, 0x0c, 0x1c
};
static const u32 asb2305_led_chase_tbl[6] = {
~0x02020202, /* top - segA */
~0x04040404, /* right top - segB */
~0x08080808, /* right bottom - segC */
~0x10101010, /* bottom - segD */
~0x20202020, /* left bottom - segE */
~0x40404040, /* left top - segF */
};
static unsigned asb2305_led_chase;
void peripheral_leds7x4_display_dec(unsigned int val, unsigned int points)
{
u32 leds;
leds = asb2305_led_hex_tbl[(val/1000) % 10];
leds <<= 8;
leds |= asb2305_led_hex_tbl[(val/100) % 10];
leds <<= 8;
leds |= asb2305_led_hex_tbl[(val/10) % 10];
leds <<= 8;
leds |= asb2305_led_hex_tbl[val % 10];
leds |= points^0x01010101;
ASB2305_7SEGLEDS = leds;
}
void peripheral_leds7x4_display_hex(unsigned int val, unsigned int points)
{
u32 leds;
leds = asb2305_led_hex_tbl[(val/1000) % 10];
leds <<= 8;
leds |= asb2305_led_hex_tbl[(val/100) % 10];
leds <<= 8;
leds |= asb2305_led_hex_tbl[(val/10) % 10];
leds <<= 8;
leds |= asb2305_led_hex_tbl[val % 10];
leds |= points^0x01010101;
ASB2305_7SEGLEDS = leds;
}
void peripheral_leds_display_exception(enum exception_code code)
{
u32 leds;
leds = asb2305_led_hex_tbl[(code/0x100) % 0x10];
leds <<= 8;
leds |= asb2305_led_hex_tbl[(code/0x10) % 0x10];
leds <<= 8;
leds |= asb2305_led_hex_tbl[code % 0x10];
leds |= 0x6d010101;
ASB2305_7SEGLEDS = leds;
}
void peripheral_leds7x4_display_minssecs(unsigned int time, unsigned int points)
{
u32 leds;
leds = asb2305_led_hex_tbl[(time/600) % 6];
leds <<= 8;
leds |= asb2305_led_hex_tbl[(time/60) % 10];
leds <<= 8;
leds |= asb2305_led_hex_tbl[(time/10) % 6];
leds <<= 8;
leds |= asb2305_led_hex_tbl[time % 10];
leds |= points^0x01010101;
ASB2305_7SEGLEDS = leds;
}
void peripheral_leds7x4_display_rtc(void)
{
unsigned int clock;
u8 mins, secs;
mins = RTMCR;
secs = RTSCR;
clock = ((mins & 0xf0) >> 4);
clock *= 10;
clock += (mins & 0x0f);
clock *= 6;
clock += ((secs & 0xf0) >> 4);
clock *= 10;
clock += (secs & 0x0f);
peripheral_leds7x4_display_minssecs(clock, 0);
}
void peripheral_leds_led_chase(void)
{
ASB2305_7SEGLEDS = asb2305_led_chase_tbl[asb2305_led_chase];
asb2305_led_chase++;
if (asb2305_led_chase >= 6)
asb2305_led_chase = 0;
}

View File

@ -0,0 +1,303 @@
/* ASB2305 PCI resource stuff
*
* Copyright (C) 2001 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
* - Derived from arch/i386/pci-i386.c
* - Copyright 1997--2000 Martin Mares <mj@suse.cz>
*
* 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/types.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/errno.h>
#include "pci-asb2305.h"
/*
* We need to avoid collisions with `mirrored' VGA ports
* and other strange ISA hardware, so we always want the
* addresses to be allocated in the 0x000-0x0ff region
* modulo 0x400.
*
* Why? Because some silly external IO cards only decode
* the low 10 bits of the IO address. The 0x00-0xff region
* is reserved for motherboard devices that decode all 16
* bits, so it's ok to allocate at, say, 0x2800-0x28ff,
* but we want to try to avoid allocating at 0x2900-0x2bff
* which might have be mirrored at 0x0100-0x03ff..
*/
void pcibios_align_resource(void *data, struct resource *res,
resource_size_t size, resource_size_t align)
{
#if 0
struct pci_dev *dev = data;
printk(KERN_DEBUG
"### PCIBIOS_ALIGN_RESOURCE(%s,,{%08lx-%08lx,%08lx},%lx)\n",
pci_name(dev),
res->start,
res->end,
res->flags,
size
);
#endif
if (res->flags & IORESOURCE_IO) {
unsigned long start = res->start;
if (start & 0x300) {
start = (start + 0x3ff) & ~0x3ff;
res->start = start;
}
}
}
/*
* Handle resources of PCI devices. If the world were perfect, we could
* just allocate all the resource regions and do nothing more. It isn't.
* On the other hand, we cannot just re-allocate all devices, as it would
* require us to know lots of host bridge internals. So we attempt to
* keep as much of the original configuration as possible, but tweak it
* when it's found to be wrong.
*
* Known BIOS problems we have to work around:
* - I/O or memory regions not configured
* - regions configured, but not enabled in the command register
* - bogus I/O addresses above 64K used
* - expansion ROMs left enabled (this may sound harmless, but given
* the fact the PCI specs explicitly allow address decoders to be
* shared between expansion ROMs and other resource regions, it's
* at least dangerous)
*
* Our solution:
* (1) Allocate resources for all buses behind PCI-to-PCI bridges.
* This gives us fixed barriers on where we can allocate.
* (2) Allocate resources for all enabled devices. If there is
* a collision, just mark the resource as unallocated. Also
* disable expansion ROMs during this step.
* (3) Try to allocate resources for disabled devices. If the
* resources were assigned correctly, everything goes well,
* if they weren't, they won't disturb allocation of other
* resources.
* (4) Assign new addresses to resources which were either
* not configured at all or misconfigured. If explicitly
* requested by the user, configure expansion ROM address
* as well.
*/
static void __init pcibios_allocate_bus_resources(struct list_head *bus_list)
{
struct pci_bus *bus;
struct pci_dev *dev;
int idx;
struct resource *r, *pr;
/* Depth-First Search on bus tree */
list_for_each_entry(bus, bus_list, node) {
dev = bus->self;
if (dev) {
for (idx = PCI_BRIDGE_RESOURCES;
idx < PCI_NUM_RESOURCES;
idx++) {
r = &dev->resource[idx];
if (!r->flags)
continue;
pr = pci_find_parent_resource(dev, r);
if (!r->start ||
!pr ||
request_resource(pr, r) < 0) {
printk(KERN_ERR "PCI:"
" Cannot allocate resource"
" region %d of bridge %s\n",
idx, pci_name(dev));
/* Something is wrong with the region.
* Invalidate the resource to prevent
* child resource allocations in this
* range. */
r->flags = 0;
}
}
}
pcibios_allocate_bus_resources(&bus->children);
}
}
static void __init pcibios_allocate_resources(int pass)
{
struct pci_dev *dev = NULL;
int idx, disabled;
u16 command;
struct resource *r, *pr;
for_each_pci_dev(dev) {
pci_read_config_word(dev, PCI_COMMAND, &command);
for (idx = 0; idx < 6; idx++) {
r = &dev->resource[idx];
if (r->parent) /* Already allocated */
continue;
if (!r->start) /* Address not assigned */
continue;
if (r->flags & IORESOURCE_IO)
disabled = !(command & PCI_COMMAND_IO);
else
disabled = !(command & PCI_COMMAND_MEMORY);
if (pass == disabled) {
DBG("PCI[%s]: Resource %08lx-%08lx"
" (f=%lx, d=%d, p=%d)\n",
pci_name(dev), r->start, r->end, r->flags,
disabled, pass);
pr = pci_find_parent_resource(dev, r);
if (!pr || request_resource(pr, r) < 0) {
printk(KERN_ERR "PCI:"
" Cannot allocate resource"
" region %d of device %s\n",
idx, pci_name(dev));
/* We'll assign a new address later */
r->end -= r->start;
r->start = 0;
}
}
}
if (!pass) {
r = &dev->resource[PCI_ROM_RESOURCE];
if (r->flags & IORESOURCE_ROM_ENABLE) {
/* Turn the ROM off, leave the resource region,
* but keep it unregistered. */
u32 reg;
DBG("PCI: Switching off ROM of %s\n",
pci_name(dev));
r->flags &= ~IORESOURCE_ROM_ENABLE;
pci_read_config_dword(
dev, dev->rom_base_reg, &reg);
pci_write_config_dword(
dev, dev->rom_base_reg,
reg & ~PCI_ROM_ADDRESS_ENABLE);
}
}
}
}
static int __init pcibios_assign_resources(void)
{
struct pci_dev *dev = NULL;
struct resource *r, *pr;
if (!(pci_probe & PCI_ASSIGN_ROMS)) {
/* Try to use BIOS settings for ROMs, otherwise let
pci_assign_unassigned_resources() allocate the new
addresses. */
for_each_pci_dev(dev) {
r = &dev->resource[PCI_ROM_RESOURCE];
if (!r->flags || !r->start)
continue;
pr = pci_find_parent_resource(dev, r);
if (!pr || request_resource(pr, r) < 0) {
r->end -= r->start;
r->start = 0;
}
}
}
pci_assign_unassigned_resources();
return 0;
}
fs_initcall(pcibios_assign_resources);
void __init pcibios_resource_survey(void)
{
DBG("PCI: Allocating resources\n");
pcibios_allocate_bus_resources(&pci_root_buses);
pcibios_allocate_resources(0);
pcibios_allocate_resources(1);
}
int pcibios_enable_resources(struct pci_dev *dev, int mask)
{
u16 cmd, old_cmd;
int idx;
struct resource *r;
pci_read_config_word(dev, PCI_COMMAND, &cmd);
old_cmd = cmd;
for (idx = 0; idx < 6; idx++) {
/* Only set up the requested stuff */
if (!(mask & (1 << idx)))
continue;
r = &dev->resource[idx];
if (!r->start && r->end) {
printk(KERN_ERR
"PCI: Device %s not available because of"
" resource collisions\n",
pci_name(dev));
return -EINVAL;
}
if (r->flags & IORESOURCE_IO)
cmd |= PCI_COMMAND_IO;
if (r->flags & IORESOURCE_MEM)
cmd |= PCI_COMMAND_MEMORY;
}
if (dev->resource[PCI_ROM_RESOURCE].start)
cmd |= PCI_COMMAND_MEMORY;
if (cmd != old_cmd)
pci_write_config_word(dev, PCI_COMMAND, cmd);
return 0;
}
/*
* If we set up a device for bus mastering, we need to check the latency
* timer as certain crappy BIOSes forget to set it properly.
*/
unsigned int pcibios_max_latency = 255;
void pcibios_set_master(struct pci_dev *dev)
{
u8 lat;
pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
if (lat < 16)
lat = (64 <= pcibios_max_latency) ? 64 : pcibios_max_latency;
else if (lat > pcibios_max_latency)
lat = pcibios_max_latency;
else
return;
pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat);
}
int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
enum pci_mmap_state mmap_state, int write_combine)
{
unsigned long prot;
/* Leave vm_pgoff as-is, the PCI space address is the physical
* address on this platform.
*/
vma->vm_flags |= VM_LOCKED | VM_IO;
prot = pgprot_val(vma->vm_page_prot);
prot &= ~_PAGE_CACHE;
vma->vm_page_prot = __pgprot(prot);
/* Write-combine setting is ignored */
if (io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
vma->vm_end - vma->vm_start,
vma->vm_page_prot))
return -EAGAIN;
return 0;
}

View File

@ -0,0 +1,82 @@
/* ASB2305 Arch-specific PCI declarations
*
* Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
* Derived from: arch/i386/kernel/pci-i386.h: (c) 1999 Martin Mares <mj@ucw.cz>
*
* 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 _PCI_ASB2305_H
#define _PCI_ASB2305_H
#undef DEBUG
#ifdef DEBUG
#define DBG(x...) printk(x)
#else
#define DBG(x...)
#endif
#define PCI_PROBE_BIOS 1
#define PCI_PROBE_CONF1 2
#define PCI_PROBE_CONF2 4
#define PCI_NO_SORT 0x100
#define PCI_BIOS_SORT 0x200
#define PCI_NO_CHECKS 0x400
#define PCI_ASSIGN_ROMS 0x1000
#define PCI_BIOS_IRQ_SCAN 0x2000
extern unsigned int pci_probe;
/* pci-asb2305.c */
extern unsigned int pcibios_max_latency;
extern void pcibios_resource_survey(void);
extern int pcibios_enable_resources(struct pci_dev *dev, int mask);
/* pci.c */
extern int pcibios_last_bus;
extern struct pci_bus *pci_root_bus;
extern struct pci_ops *pci_root_ops;
extern struct irq_routing_table *pcibios_get_irq_routing_table(void);
extern int pcibios_set_irq_routing(struct pci_dev *dev, int pin, int irq);
/* pci-irq.c */
struct irq_info {
u8 bus, devfn; /* Bus, device and function */
struct {
u8 link; /* IRQ line ID, chipset dependent,
* 0=not routed */
u16 bitmap; /* Available IRQs */
} __attribute__((packed)) irq[4];
u8 slot; /* Slot number, 0=onboard */
u8 rfu;
} __attribute__((packed));
struct irq_routing_table {
u32 signature; /* PIRQ_SIGNATURE should be here */
u16 version; /* PIRQ_VERSION */
u16 size; /* Table size in bytes */
u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */
u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */
u16 rtr_vendor, rtr_device; /* Vendor and device ID of interrupt router */
u32 miniport_data; /* Crap */
u8 rfu[11];
u8 checksum; /* Modulo 256 checksum must give zero */
struct irq_info slots[0];
} __attribute__((packed));
extern unsigned int pcibios_irq_mask;
extern void pcibios_irq_init(void);
extern void pcibios_fixup_irqs(void);
extern void pcibios_enable_irq(struct pci_dev *dev);
#endif /* PCI_ASB2305_H */

View File

@ -0,0 +1,31 @@
/* ASB2305 PCI I/O mapping 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/pci.h>
#include <linux/module.h>
/*
* Create a virtual mapping cookie for a PCI BAR (memory or IO)
*/
void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen)
{
unsigned long start = pci_resource_start(dev, bar);
unsigned long len = pci_resource_len(dev, bar);
unsigned long flags = pci_resource_flags(dev, bar);
if (!len || !start)
return NULL;
if ((flags & IORESOURCE_IO) || (flags & IORESOURCE_MEM))
return (void __iomem *) start;
return NULL;
}
EXPORT_SYMBOL(pci_iomap);

Some files were not shown because too many files have changed in this diff Show More