Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
This commit is contained in:
17
arch/arm26/kernel/Makefile
Normal file
17
arch/arm26/kernel/Makefile
Normal file
@@ -0,0 +1,17 @@
|
||||
#
|
||||
# Makefile for the linux kernel.
|
||||
#
|
||||
|
||||
# Object file lists.
|
||||
|
||||
AFLAGS_head.o := -DTEXTADDR=$(TEXTADDR)
|
||||
|
||||
obj-y := compat.o dma.o entry.o irq.o process.o ptrace.o \
|
||||
semaphore.o setup.o signal.o sys_arm.o time.o traps.o \
|
||||
ecard.o dma.o ecard.o fiq.o time.o
|
||||
|
||||
extra-y := head.o init_task.o vmlinux.lds
|
||||
|
||||
obj-$(CONFIG_FIQ) += fiq.o
|
||||
obj-$(CONFIG_MODULES) += armksyms.o
|
||||
|
220
arch/arm26/kernel/armksyms.c
Normal file
220
arch/arm26/kernel/armksyms.c
Normal file
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* linux/arch/arm26/kernel/armksyms.c
|
||||
*
|
||||
* Copyright (C) 2003 Ian Molton
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/user.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/mman.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/in6.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/vt_kern.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/syscalls.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/elf.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/semaphore.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/checksum.h>
|
||||
#include <asm/mach-types.h>
|
||||
|
||||
extern void dump_thread(struct pt_regs *, struct user *);
|
||||
extern int dump_fpu(struct pt_regs *, struct user_fp_struct *);
|
||||
extern void inswb(unsigned int port, void *to, int len);
|
||||
extern void outswb(unsigned int port, const void *to, int len);
|
||||
|
||||
extern void __bad_xchg(volatile void *ptr, int size);
|
||||
|
||||
/*
|
||||
* libgcc functions - functions that are used internally by the
|
||||
* compiler... (prototypes are not correct though, but that
|
||||
* doesn't really matter since they're not versioned).
|
||||
*/
|
||||
extern void __ashldi3(void);
|
||||
extern void __ashrdi3(void);
|
||||
extern void __divsi3(void);
|
||||
extern void __lshrdi3(void);
|
||||
extern void __modsi3(void);
|
||||
extern void __muldi3(void);
|
||||
extern void __ucmpdi2(void);
|
||||
extern void __udivdi3(void);
|
||||
extern void __umoddi3(void);
|
||||
extern void __udivmoddi4(void);
|
||||
extern void __udivsi3(void);
|
||||
extern void __umodsi3(void);
|
||||
extern void abort(void);
|
||||
|
||||
extern void ret_from_exception(void);
|
||||
extern void fpundefinstr(void);
|
||||
extern void fp_enter(void);
|
||||
|
||||
/*
|
||||
* This has a special calling convention; it doesn't
|
||||
* modify any of the usual registers, except for LR.
|
||||
* FIXME - we used to use our own local version - looks to be in kernel/softirq now
|
||||
*/
|
||||
//extern void __do_softirq(void);
|
||||
|
||||
#define EXPORT_SYMBOL_ALIAS(sym,orig) \
|
||||
const char __kstrtab_##sym[] \
|
||||
__attribute__((section(".kstrtab"))) = \
|
||||
__MODULE_STRING(sym); \
|
||||
const struct module_symbol __ksymtab_##sym \
|
||||
__attribute__((section("__ksymtab"))) = \
|
||||
{ (unsigned long)&orig, __kstrtab_##sym };
|
||||
|
||||
/*
|
||||
* floating point math emulator support.
|
||||
* These symbols will never change their calling convention...
|
||||
*/
|
||||
EXPORT_SYMBOL_ALIAS(kern_fp_enter,fp_enter);
|
||||
EXPORT_SYMBOL_ALIAS(fp_printk,printk);
|
||||
EXPORT_SYMBOL_ALIAS(fp_send_sig,send_sig);
|
||||
|
||||
EXPORT_SYMBOL(fpundefinstr);
|
||||
EXPORT_SYMBOL(ret_from_exception);
|
||||
|
||||
#ifdef CONFIG_VT
|
||||
EXPORT_SYMBOL(kd_mksound);
|
||||
#endif
|
||||
|
||||
//EXPORT_SYMBOL(__do_softirq);
|
||||
|
||||
/* platform dependent support */
|
||||
EXPORT_SYMBOL(dump_thread);
|
||||
EXPORT_SYMBOL(dump_fpu);
|
||||
EXPORT_SYMBOL(udelay);
|
||||
EXPORT_SYMBOL(kernel_thread);
|
||||
EXPORT_SYMBOL(system_rev);
|
||||
EXPORT_SYMBOL(system_serial_low);
|
||||
EXPORT_SYMBOL(system_serial_high);
|
||||
#ifdef CONFIG_DEBUG_BUGVERBOSE
|
||||
EXPORT_SYMBOL(__bug);
|
||||
#endif
|
||||
EXPORT_SYMBOL(__bad_xchg);
|
||||
EXPORT_SYMBOL(__readwrite_bug);
|
||||
EXPORT_SYMBOL(enable_irq);
|
||||
EXPORT_SYMBOL(disable_irq);
|
||||
EXPORT_SYMBOL(set_irq_type);
|
||||
EXPORT_SYMBOL(pm_idle);
|
||||
EXPORT_SYMBOL(pm_power_off);
|
||||
|
||||
/* processor dependencies */
|
||||
EXPORT_SYMBOL(__machine_arch_type);
|
||||
|
||||
/* networking */
|
||||
EXPORT_SYMBOL(csum_partial_copy_nocheck);
|
||||
EXPORT_SYMBOL(__csum_ipv6_magic);
|
||||
|
||||
/* io */
|
||||
#ifndef __raw_readsb
|
||||
EXPORT_SYMBOL(__raw_readsb);
|
||||
#endif
|
||||
#ifndef __raw_readsw
|
||||
EXPORT_SYMBOL(__raw_readsw);
|
||||
#endif
|
||||
#ifndef __raw_readsl
|
||||
EXPORT_SYMBOL(__raw_readsl);
|
||||
#endif
|
||||
#ifndef __raw_writesb
|
||||
EXPORT_SYMBOL(__raw_writesb);
|
||||
#endif
|
||||
#ifndef __raw_writesw
|
||||
EXPORT_SYMBOL(__raw_writesw);
|
||||
#endif
|
||||
#ifndef __raw_writesl
|
||||
EXPORT_SYMBOL(__raw_writesl);
|
||||
#endif
|
||||
|
||||
/* string / mem functions */
|
||||
EXPORT_SYMBOL(strcpy);
|
||||
EXPORT_SYMBOL(strncpy);
|
||||
EXPORT_SYMBOL(strcat);
|
||||
EXPORT_SYMBOL(strncat);
|
||||
EXPORT_SYMBOL(strcmp);
|
||||
EXPORT_SYMBOL(strncmp);
|
||||
EXPORT_SYMBOL(strchr);
|
||||
EXPORT_SYMBOL(strlen);
|
||||
EXPORT_SYMBOL(strnlen);
|
||||
EXPORT_SYMBOL(strpbrk);
|
||||
EXPORT_SYMBOL(strrchr);
|
||||
EXPORT_SYMBOL(strstr);
|
||||
EXPORT_SYMBOL(memset);
|
||||
EXPORT_SYMBOL(memcpy);
|
||||
EXPORT_SYMBOL(memmove);
|
||||
EXPORT_SYMBOL(memcmp);
|
||||
EXPORT_SYMBOL(memscan);
|
||||
EXPORT_SYMBOL(__memzero);
|
||||
|
||||
/* user mem (segment) */
|
||||
EXPORT_SYMBOL(uaccess_kernel);
|
||||
EXPORT_SYMBOL(uaccess_user);
|
||||
|
||||
EXPORT_SYMBOL(__get_user_1);
|
||||
EXPORT_SYMBOL(__get_user_2);
|
||||
EXPORT_SYMBOL(__get_user_4);
|
||||
EXPORT_SYMBOL(__get_user_8);
|
||||
|
||||
EXPORT_SYMBOL(__put_user_1);
|
||||
EXPORT_SYMBOL(__put_user_2);
|
||||
EXPORT_SYMBOL(__put_user_4);
|
||||
EXPORT_SYMBOL(__put_user_8);
|
||||
|
||||
/* gcc lib functions */
|
||||
EXPORT_SYMBOL(__ashldi3);
|
||||
EXPORT_SYMBOL(__ashrdi3);
|
||||
EXPORT_SYMBOL(__divsi3);
|
||||
EXPORT_SYMBOL(__lshrdi3);
|
||||
EXPORT_SYMBOL(__modsi3);
|
||||
EXPORT_SYMBOL(__muldi3);
|
||||
EXPORT_SYMBOL(__ucmpdi2);
|
||||
EXPORT_SYMBOL(__udivdi3);
|
||||
EXPORT_SYMBOL(__umoddi3);
|
||||
EXPORT_SYMBOL(__udivmoddi4);
|
||||
EXPORT_SYMBOL(__udivsi3);
|
||||
EXPORT_SYMBOL(__umodsi3);
|
||||
|
||||
/* bitops */
|
||||
EXPORT_SYMBOL(_set_bit_le);
|
||||
EXPORT_SYMBOL(_test_and_set_bit_le);
|
||||
EXPORT_SYMBOL(_clear_bit_le);
|
||||
EXPORT_SYMBOL(_test_and_clear_bit_le);
|
||||
EXPORT_SYMBOL(_change_bit_le);
|
||||
EXPORT_SYMBOL(_test_and_change_bit_le);
|
||||
EXPORT_SYMBOL(_find_first_zero_bit_le);
|
||||
EXPORT_SYMBOL(_find_next_zero_bit_le);
|
||||
|
||||
/* elf */
|
||||
EXPORT_SYMBOL(elf_platform);
|
||||
EXPORT_SYMBOL(elf_hwcap);
|
||||
|
||||
/* syscalls */
|
||||
EXPORT_SYMBOL(sys_write);
|
||||
EXPORT_SYMBOL(sys_read);
|
||||
EXPORT_SYMBOL(sys_lseek);
|
||||
EXPORT_SYMBOL(sys_open);
|
||||
EXPORT_SYMBOL(sys_exit);
|
||||
EXPORT_SYMBOL(sys_wait4);
|
||||
|
||||
EXPORT_SYMBOL(get_wchan);
|
||||
|
||||
#ifdef CONFIG_PREEMPT
|
||||
EXPORT_SYMBOL(kernel_flag);
|
||||
#endif
|
63
arch/arm26/kernel/asm-offsets.c
Normal file
63
arch/arm26/kernel/asm-offsets.c
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 1995-2001 Russell King
|
||||
* 2001-2002 Keith Owens
|
||||
* 2003 Ian Molton
|
||||
*
|
||||
* Generate definitions needed by assembly language modules.
|
||||
* This code generates raw asm output which is post-processed to extract
|
||||
* and format the required data.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
/*
|
||||
* Make sure that the compiler and target are compatible.
|
||||
*/
|
||||
#if defined(__APCS_32__) && defined(CONFIG_CPU_26)
|
||||
#error Sorry, your compiler targets APCS-32 but this kernel requires APCS-26
|
||||
#endif
|
||||
#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 95)
|
||||
#error Sorry, your compiler is known to miscompile kernels. Only use gcc 2.95.3 and later.
|
||||
#endif
|
||||
#if __GNUC__ == 2 && __GNUC_MINOR__ == 95
|
||||
/* shame we can't detect the .1 or .2 releases */
|
||||
#warning GCC 2.95.2 and earlier miscompiles kernels.
|
||||
#endif
|
||||
|
||||
/* Use marker if you need to separate the values later */
|
||||
|
||||
#define DEFINE(sym, val) \
|
||||
asm volatile("\n->" #sym " %0 " #val : : "i" (val))
|
||||
|
||||
#define BLANK() asm volatile("\n->" : : )
|
||||
|
||||
int main(void)
|
||||
{
|
||||
DEFINE(TSK_ACTIVE_MM, offsetof(struct task_struct, active_mm));
|
||||
BLANK();
|
||||
DEFINE(VMA_VM_MM, offsetof(struct vm_area_struct, vm_mm));
|
||||
DEFINE(VMA_VM_FLAGS, offsetof(struct vm_area_struct, vm_flags));
|
||||
BLANK();
|
||||
DEFINE(VM_EXEC, VM_EXEC);
|
||||
BLANK();
|
||||
BLANK();
|
||||
DEFINE(PAGE_PRESENT, _PAGE_PRESENT);
|
||||
DEFINE(PAGE_READONLY, _PAGE_READONLY);
|
||||
DEFINE(PAGE_NOT_USER, _PAGE_NOT_USER);
|
||||
DEFINE(PAGE_OLD, _PAGE_OLD);
|
||||
DEFINE(PAGE_CLEAN, _PAGE_CLEAN);
|
||||
BLANK();
|
||||
DEFINE(PAGE_SZ, PAGE_SIZE);
|
||||
BLANK();
|
||||
DEFINE(SYS_ERROR0, 0x9f0000);
|
||||
return 0;
|
||||
}
|
265
arch/arm26/kernel/calls.S
Normal file
265
arch/arm26/kernel/calls.S
Normal file
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* linux/arch/arm26/kernel/calls.S
|
||||
*
|
||||
* Copyright (C) 2003 Ian Molton
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* FIXME
|
||||
* This file is included twice in entry.S which may not be necessary
|
||||
*/
|
||||
|
||||
//FIXME - clearly NR_syscalls is never defined here
|
||||
|
||||
#ifndef NR_syscalls
|
||||
#define NR_syscalls 256
|
||||
#else
|
||||
|
||||
__syscall_start:
|
||||
/* 0 */ .long sys_ni_syscall
|
||||
.long sys_exit
|
||||
.long sys_fork_wrapper
|
||||
.long sys_read
|
||||
.long sys_write
|
||||
/* 5 */ .long sys_open
|
||||
.long sys_close
|
||||
.long sys_ni_syscall /* was sys_waitpid */
|
||||
.long sys_creat
|
||||
.long sys_link
|
||||
/* 10 */ .long sys_unlink
|
||||
.long sys_execve_wrapper
|
||||
.long sys_chdir
|
||||
.long sys_time /* used by libc4 */
|
||||
.long sys_mknod
|
||||
/* 15 */ .long sys_chmod
|
||||
.long sys_lchown16
|
||||
.long sys_ni_syscall /* was sys_break */
|
||||
.long sys_ni_syscall /* was sys_stat */
|
||||
.long sys_lseek
|
||||
/* 20 */ .long sys_getpid
|
||||
.long sys_mount
|
||||
.long sys_oldumount /* used by libc4 */
|
||||
.long sys_setuid16
|
||||
.long sys_getuid16
|
||||
/* 25 */ .long sys_stime
|
||||
.long sys_ptrace
|
||||
.long sys_alarm /* used by libc4 */
|
||||
.long sys_ni_syscall /* was sys_fstat */
|
||||
.long sys_pause
|
||||
/* 30 */ .long sys_utime /* used by libc4 */
|
||||
.long sys_ni_syscall /* was sys_stty */
|
||||
.long sys_ni_syscall /* was sys_getty */
|
||||
.long sys_access
|
||||
.long sys_nice
|
||||
/* 35 */ .long sys_ni_syscall /* was sys_ftime */
|
||||
.long sys_sync
|
||||
.long sys_kill
|
||||
.long sys_rename
|
||||
.long sys_mkdir
|
||||
/* 40 */ .long sys_rmdir
|
||||
.long sys_dup
|
||||
.long sys_pipe
|
||||
.long sys_times
|
||||
.long sys_ni_syscall /* was sys_prof */
|
||||
/* 45 */ .long sys_brk
|
||||
.long sys_setgid16
|
||||
.long sys_getgid16
|
||||
.long sys_ni_syscall /* was sys_signal */
|
||||
.long sys_geteuid16
|
||||
/* 50 */ .long sys_getegid16
|
||||
.long sys_acct
|
||||
.long sys_umount
|
||||
.long sys_ni_syscall /* was sys_lock */
|
||||
.long sys_ioctl
|
||||
/* 55 */ .long sys_fcntl
|
||||
.long sys_ni_syscall /* was sys_mpx */
|
||||
.long sys_setpgid
|
||||
.long sys_ni_syscall /* was sys_ulimit */
|
||||
.long sys_ni_syscall /* was sys_olduname */
|
||||
/* 60 */ .long sys_umask
|
||||
.long sys_chroot
|
||||
.long sys_ustat
|
||||
.long sys_dup2
|
||||
.long sys_getppid
|
||||
/* 65 */ .long sys_getpgrp
|
||||
.long sys_setsid
|
||||
.long sys_sigaction
|
||||
.long sys_ni_syscall /* was sys_sgetmask */
|
||||
.long sys_ni_syscall /* was sys_ssetmask */
|
||||
/* 70 */ .long sys_setreuid16
|
||||
.long sys_setregid16
|
||||
.long sys_sigsuspend_wrapper
|
||||
.long sys_sigpending
|
||||
.long sys_sethostname
|
||||
/* 75 */ .long sys_setrlimit
|
||||
.long sys_old_getrlimit /* used by libc4 */
|
||||
.long sys_getrusage
|
||||
.long sys_gettimeofday
|
||||
.long sys_settimeofday
|
||||
/* 80 */ .long sys_getgroups16
|
||||
.long sys_setgroups16
|
||||
.long old_select /* used by libc4 */
|
||||
.long sys_symlink
|
||||
.long sys_ni_syscall /* was sys_lstat */
|
||||
/* 85 */ .long sys_readlink
|
||||
.long sys_uselib
|
||||
.long sys_swapon
|
||||
.long sys_reboot
|
||||
.long old_readdir /* used by libc4 */
|
||||
/* 90 */ .long old_mmap /* used by libc4 */
|
||||
.long sys_munmap
|
||||
.long sys_truncate
|
||||
.long sys_ftruncate
|
||||
.long sys_fchmod
|
||||
/* 95 */ .long sys_fchown16
|
||||
.long sys_getpriority
|
||||
.long sys_setpriority
|
||||
.long sys_ni_syscall /* was sys_profil */
|
||||
.long sys_statfs
|
||||
/* 100 */ .long sys_fstatfs
|
||||
.long sys_ni_syscall
|
||||
.long sys_socketcall
|
||||
.long sys_syslog
|
||||
.long sys_setitimer
|
||||
/* 105 */ .long sys_getitimer
|
||||
.long sys_newstat
|
||||
.long sys_newlstat
|
||||
.long sys_newfstat
|
||||
.long sys_ni_syscall /* was sys_uname */
|
||||
/* 110 */ .long sys_ni_syscall /* was sys_iopl */
|
||||
.long sys_vhangup
|
||||
.long sys_ni_syscall
|
||||
.long sys_syscall /* call a syscall */
|
||||
.long sys_wait4
|
||||
/* 115 */ .long sys_swapoff
|
||||
.long sys_sysinfo
|
||||
.long sys_ipc
|
||||
.long sys_fsync
|
||||
.long sys_sigreturn_wrapper
|
||||
/* 120 */ .long sys_clone_wapper
|
||||
.long sys_setdomainname
|
||||
.long sys_newuname
|
||||
.long sys_ni_syscall
|
||||
.long sys_adjtimex
|
||||
/* 125 */ .long sys_mprotect
|
||||
.long sys_sigprocmask
|
||||
.long sys_ni_syscall /* WAS: sys_create_module */
|
||||
.long sys_init_module
|
||||
.long sys_delete_module
|
||||
/* 130 */ .long sys_ni_syscall /* WAS: sys_get_kernel_syms */
|
||||
.long sys_quotactl
|
||||
.long sys_getpgid
|
||||
.long sys_fchdir
|
||||
.long sys_bdflush
|
||||
/* 135 */ .long sys_sysfs
|
||||
.long sys_personality
|
||||
.long sys_ni_syscall /* .long _sys_afs_syscall */
|
||||
.long sys_setfsuid16
|
||||
.long sys_setfsgid16
|
||||
/* 140 */ .long sys_llseek
|
||||
.long sys_getdents
|
||||
.long sys_select
|
||||
.long sys_flock
|
||||
.long sys_msync
|
||||
/* 145 */ .long sys_readv
|
||||
.long sys_writev
|
||||
.long sys_getsid
|
||||
.long sys_fdatasync
|
||||
.long sys_sysctl
|
||||
/* 150 */ .long sys_mlock
|
||||
.long sys_munlock
|
||||
.long sys_mlockall
|
||||
.long sys_munlockall
|
||||
.long sys_sched_setparam
|
||||
/* 155 */ .long sys_sched_getparam
|
||||
.long sys_sched_setscheduler
|
||||
.long sys_sched_getscheduler
|
||||
.long sys_sched_yield
|
||||
.long sys_sched_get_priority_max
|
||||
/* 160 */ .long sys_sched_get_priority_min
|
||||
.long sys_sched_rr_get_interval
|
||||
.long sys_nanosleep
|
||||
.long sys_arm_mremap
|
||||
.long sys_setresuid16
|
||||
/* 165 */ .long sys_getresuid16
|
||||
.long sys_ni_syscall
|
||||
.long sys_ni_syscall /* WAS: sys_query_module */
|
||||
.long sys_poll
|
||||
.long sys_nfsservctl
|
||||
/* 170 */ .long sys_setresgid16
|
||||
.long sys_getresgid16
|
||||
.long sys_prctl
|
||||
.long sys_rt_sigreturn_wrapper
|
||||
.long sys_rt_sigaction
|
||||
/* 175 */ .long sys_rt_sigprocmask
|
||||
.long sys_rt_sigpending
|
||||
.long sys_rt_sigtimedwait
|
||||
.long sys_rt_sigqueueinfo
|
||||
.long sys_rt_sigsuspend_wrapper
|
||||
/* 180 */ .long sys_pread64
|
||||
.long sys_pwrite64
|
||||
.long sys_chown16
|
||||
.long sys_getcwd
|
||||
.long sys_capget
|
||||
/* 185 */ .long sys_capset
|
||||
.long sys_sigaltstack_wrapper
|
||||
.long sys_sendfile
|
||||
.long sys_ni_syscall
|
||||
.long sys_ni_syscall
|
||||
/* 190 */ .long sys_vfork_wrapper
|
||||
.long sys_getrlimit
|
||||
.long sys_mmap2
|
||||
.long sys_truncate64
|
||||
.long sys_ftruncate64
|
||||
/* 195 */ .long sys_stat64
|
||||
.long sys_lstat64
|
||||
.long sys_fstat64
|
||||
.long sys_lchown
|
||||
.long sys_getuid
|
||||
/* 200 */ .long sys_getgid
|
||||
.long sys_geteuid
|
||||
.long sys_getegid
|
||||
.long sys_setreuid
|
||||
.long sys_setregid
|
||||
/* 205 */ .long sys_getgroups
|
||||
.long sys_setgroups
|
||||
.long sys_fchown
|
||||
.long sys_setresuid
|
||||
.long sys_getresuid
|
||||
/* 210 */ .long sys_setresgid
|
||||
.long sys_getresgid
|
||||
.long sys_chown
|
||||
.long sys_setuid
|
||||
.long sys_setgid
|
||||
/* 215 */ .long sys_setfsuid
|
||||
.long sys_setfsgid
|
||||
.long sys_getdents64
|
||||
.long sys_pivot_root
|
||||
.long sys_mincore
|
||||
/* 220 */ .long sys_madvise
|
||||
.long sys_fcntl64
|
||||
.long sys_ni_syscall /* TUX */
|
||||
.long sys_ni_syscall /* WAS: sys_security */
|
||||
.long sys_gettid
|
||||
/* 225 */ .long sys_readahead
|
||||
.long sys_setxattr
|
||||
.long sys_lsetxattr
|
||||
.long sys_fsetxattr
|
||||
.long sys_getxattr
|
||||
/* 230 */ .long sys_lgetxattr
|
||||
.long sys_fgetxattr
|
||||
.long sys_listxattr
|
||||
.long sys_llistxattr
|
||||
.long sys_flistxattr
|
||||
/* 235 */ .long sys_removexattr
|
||||
.long sys_lremovexattr
|
||||
.long sys_fremovexattr
|
||||
.long sys_tkill
|
||||
__syscall_end:
|
||||
|
||||
.rept NR_syscalls - (__syscall_end - __syscall_start) / 4
|
||||
.long sys_ni_syscall
|
||||
.endr
|
||||
#endif
|
174
arch/arm26/kernel/compat.c
Normal file
174
arch/arm26/kernel/compat.c
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* linux/arch/arm26/kernel/compat.c
|
||||
*
|
||||
* Copyright (C) 2001 Russell King
|
||||
* 2003 Ian Molton
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* We keep the old params compatibility cruft in one place (here)
|
||||
* so we don't end up with lots of mess around other places.
|
||||
*
|
||||
* NOTE:
|
||||
* The old struct param_struct is deprecated, but it will be kept in
|
||||
* the kernel for 5 years from now (2001). This will allow boot loaders
|
||||
* to convert to the new struct tag way.
|
||||
*/
|
||||
#include <linux/config.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/setup.h>
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/page.h>
|
||||
|
||||
//#include <asm/arch.h>
|
||||
//#include <asm/mach/irq.h>
|
||||
|
||||
/*
|
||||
* Usage:
|
||||
* - do not go blindly adding fields, add them at the end
|
||||
* - when adding fields, don't rely on the address until
|
||||
* a patch from me has been released
|
||||
* - unused fields should be zero (for future expansion)
|
||||
* - this structure is relatively short-lived - only
|
||||
* guaranteed to contain useful data in setup_arch()
|
||||
*
|
||||
* This is the old deprecated way to pass parameters to the kernel
|
||||
*/
|
||||
struct param_struct {
|
||||
union {
|
||||
struct {
|
||||
unsigned long page_size; /* 0 */
|
||||
unsigned long nr_pages; /* 4 */
|
||||
unsigned long ramdisk_size; /* 8 */
|
||||
unsigned long flags; /* 12 */
|
||||
#define FLAG_READONLY 1
|
||||
#define FLAG_RDLOAD 4
|
||||
#define FLAG_RDPROMPT 8
|
||||
unsigned long rootdev; /* 16 */
|
||||
unsigned long video_num_cols; /* 20 */
|
||||
unsigned long video_num_rows; /* 24 */
|
||||
unsigned long video_x; /* 28 */
|
||||
unsigned long video_y; /* 32 */
|
||||
unsigned long memc_control_reg; /* 36 */
|
||||
unsigned char sounddefault; /* 40 */
|
||||
unsigned char adfsdrives; /* 41 */
|
||||
unsigned char bytes_per_char_h; /* 42 */
|
||||
unsigned char bytes_per_char_v; /* 43 */
|
||||
unsigned long pages_in_bank[4]; /* 44 */
|
||||
unsigned long pages_in_vram; /* 60 */
|
||||
unsigned long initrd_start; /* 64 */
|
||||
unsigned long initrd_size; /* 68 */
|
||||
unsigned long rd_start; /* 72 */
|
||||
unsigned long system_rev; /* 76 */
|
||||
unsigned long system_serial_low; /* 80 */
|
||||
unsigned long system_serial_high; /* 84 */
|
||||
unsigned long mem_fclk_21285; /* 88 */
|
||||
} s;
|
||||
char unused[256];
|
||||
} u1;
|
||||
union {
|
||||
char paths[8][128];
|
||||
struct {
|
||||
unsigned long magic;
|
||||
char n[1024 - sizeof(unsigned long)];
|
||||
} s;
|
||||
} u2;
|
||||
char commandline[COMMAND_LINE_SIZE];
|
||||
};
|
||||
|
||||
static struct tag * __init memtag(struct tag *tag, unsigned long start, unsigned long size)
|
||||
{
|
||||
tag = tag_next(tag);
|
||||
tag->hdr.tag = ATAG_MEM;
|
||||
tag->hdr.size = tag_size(tag_mem32);
|
||||
tag->u.mem.size = size;
|
||||
tag->u.mem.start = start;
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
static void __init build_tag_list(struct param_struct *params, void *taglist)
|
||||
{
|
||||
struct tag *tag = taglist;
|
||||
|
||||
if (params->u1.s.page_size != PAGE_SIZE) {
|
||||
printk(KERN_WARNING "Warning: bad configuration page, "
|
||||
"trying to continue\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "Converting old-style param struct to taglist\n");
|
||||
|
||||
tag->hdr.tag = ATAG_CORE;
|
||||
tag->hdr.size = tag_size(tag_core);
|
||||
tag->u.core.flags = params->u1.s.flags & FLAG_READONLY;
|
||||
tag->u.core.pagesize = params->u1.s.page_size;
|
||||
tag->u.core.rootdev = params->u1.s.rootdev;
|
||||
|
||||
tag = tag_next(tag);
|
||||
tag->hdr.tag = ATAG_RAMDISK;
|
||||
tag->hdr.size = tag_size(tag_ramdisk);
|
||||
tag->u.ramdisk.flags = (params->u1.s.flags & FLAG_RDLOAD ? 1 : 0) |
|
||||
(params->u1.s.flags & FLAG_RDPROMPT ? 2 : 0);
|
||||
tag->u.ramdisk.size = params->u1.s.ramdisk_size;
|
||||
tag->u.ramdisk.start = params->u1.s.rd_start;
|
||||
|
||||
tag = tag_next(tag);
|
||||
tag->hdr.tag = ATAG_INITRD;
|
||||
tag->hdr.size = tag_size(tag_initrd);
|
||||
tag->u.initrd.start = params->u1.s.initrd_start;
|
||||
tag->u.initrd.size = params->u1.s.initrd_size;
|
||||
|
||||
tag = tag_next(tag);
|
||||
tag->hdr.tag = ATAG_SERIAL;
|
||||
tag->hdr.size = tag_size(tag_serialnr);
|
||||
tag->u.serialnr.low = params->u1.s.system_serial_low;
|
||||
tag->u.serialnr.high = params->u1.s.system_serial_high;
|
||||
|
||||
tag = tag_next(tag);
|
||||
tag->hdr.tag = ATAG_REVISION;
|
||||
tag->hdr.size = tag_size(tag_revision);
|
||||
tag->u.revision.rev = params->u1.s.system_rev;
|
||||
|
||||
tag = memtag(tag, PHYS_OFFSET, params->u1.s.nr_pages * PAGE_SIZE);
|
||||
|
||||
tag = tag_next(tag);
|
||||
tag->hdr.tag = ATAG_ACORN;
|
||||
tag->hdr.size = tag_size(tag_acorn);
|
||||
tag->u.acorn.memc_control_reg = params->u1.s.memc_control_reg;
|
||||
tag->u.acorn.vram_pages = params->u1.s.pages_in_vram;
|
||||
tag->u.acorn.sounddefault = params->u1.s.sounddefault;
|
||||
tag->u.acorn.adfsdrives = params->u1.s.adfsdrives;
|
||||
|
||||
tag = tag_next(tag);
|
||||
tag->hdr.tag = ATAG_CMDLINE;
|
||||
tag->hdr.size = (strlen(params->commandline) + 3 +
|
||||
sizeof(struct tag_header)) >> 2;
|
||||
strcpy(tag->u.cmdline.cmdline, params->commandline);
|
||||
|
||||
tag = tag_next(tag);
|
||||
tag->hdr.tag = ATAG_NONE;
|
||||
tag->hdr.size = 0;
|
||||
|
||||
memmove(params, taglist, ((int)tag) - ((int)taglist) +
|
||||
sizeof(struct tag_header));
|
||||
}
|
||||
|
||||
void __init convert_to_tag_list(struct tag *tags)
|
||||
{
|
||||
struct param_struct *params = (struct param_struct *)tags;
|
||||
build_tag_list(params, ¶ms->u2);
|
||||
}
|
||||
|
||||
void __init squash_mem_tags(struct tag *tag)
|
||||
{
|
||||
for (; tag->hdr.size; tag = tag_next(tag))
|
||||
if (tag->hdr.tag == ATAG_MEM)
|
||||
tag->hdr.tag = ATAG_NONE;
|
||||
}
|
273
arch/arm26/kernel/dma.c
Normal file
273
arch/arm26/kernel/dma.c
Normal file
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* linux/arch/arm26/kernel/dma.c
|
||||
*
|
||||
* Copyright (C) 1995-2000 Russell King
|
||||
* 2003 Ian Molton
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Front-end to the DMA handling. This handles the allocation/freeing
|
||||
* of DMA channels, and provides a unified interface to the machines
|
||||
* DMA facilities.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/mman.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/errno.h>
|
||||
|
||||
#include <asm/dma.h>
|
||||
|
||||
DEFINE_SPINLOCK(dma_spin_lock);
|
||||
|
||||
static dma_t dma_chan[MAX_DMA_CHANNELS];
|
||||
|
||||
/*
|
||||
* Get dma list for /proc/dma
|
||||
*/
|
||||
int get_dma_list(char *buf)
|
||||
{
|
||||
dma_t *dma;
|
||||
char *p = buf;
|
||||
int i;
|
||||
|
||||
for (i = 0, dma = dma_chan; i < MAX_DMA_CHANNELS; i++, dma++)
|
||||
if (dma->lock)
|
||||
p += sprintf(p, "%2d: %14s %s\n", i,
|
||||
dma->d_ops->type, dma->device_id);
|
||||
|
||||
return p - buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* Request DMA channel
|
||||
*
|
||||
* On certain platforms, we have to allocate an interrupt as well...
|
||||
*/
|
||||
int request_dma(dmach_t channel, const char *device_id)
|
||||
{
|
||||
dma_t *dma = dma_chan + channel;
|
||||
int ret;
|
||||
|
||||
if (channel >= MAX_DMA_CHANNELS || !dma->d_ops)
|
||||
goto bad_dma;
|
||||
|
||||
if (xchg(&dma->lock, 1) != 0)
|
||||
goto busy;
|
||||
|
||||
dma->device_id = device_id;
|
||||
dma->active = 0;
|
||||
dma->invalid = 1;
|
||||
|
||||
ret = 0;
|
||||
if (dma->d_ops->request)
|
||||
ret = dma->d_ops->request(channel, dma);
|
||||
|
||||
if (ret)
|
||||
xchg(&dma->lock, 0);
|
||||
|
||||
return ret;
|
||||
|
||||
bad_dma:
|
||||
printk(KERN_ERR "dma: trying to allocate DMA%d\n", channel);
|
||||
return -EINVAL;
|
||||
|
||||
busy:
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free DMA channel
|
||||
*
|
||||
* On certain platforms, we have to free interrupt as well...
|
||||
*/
|
||||
void free_dma(dmach_t channel)
|
||||
{
|
||||
dma_t *dma = dma_chan + channel;
|
||||
|
||||
if (channel >= MAX_DMA_CHANNELS || !dma->d_ops)
|
||||
goto bad_dma;
|
||||
|
||||
if (dma->active) {
|
||||
printk(KERN_ERR "dma%d: freeing active DMA\n", channel);
|
||||
dma->d_ops->disable(channel, dma);
|
||||
dma->active = 0;
|
||||
}
|
||||
|
||||
if (xchg(&dma->lock, 0) != 0) {
|
||||
if (dma->d_ops->free)
|
||||
dma->d_ops->free(channel, dma);
|
||||
return;
|
||||
}
|
||||
|
||||
printk(KERN_ERR "dma%d: trying to free free DMA\n", channel);
|
||||
return;
|
||||
|
||||
bad_dma:
|
||||
printk(KERN_ERR "dma: trying to free DMA%d\n", channel);
|
||||
}
|
||||
|
||||
/* Set DMA Scatter-Gather list
|
||||
*/
|
||||
void set_dma_sg (dmach_t channel, struct scatterlist *sg, int nr_sg)
|
||||
{
|
||||
dma_t *dma = dma_chan + channel;
|
||||
|
||||
if (dma->active)
|
||||
printk(KERN_ERR "dma%d: altering DMA SG while "
|
||||
"DMA active\n", channel);
|
||||
|
||||
dma->sg = sg;
|
||||
dma->sgcount = nr_sg;
|
||||
dma->using_sg = 1;
|
||||
dma->invalid = 1;
|
||||
}
|
||||
|
||||
/* Set DMA address
|
||||
*
|
||||
* Copy address to the structure, and set the invalid bit
|
||||
*/
|
||||
void set_dma_addr (dmach_t channel, unsigned long physaddr)
|
||||
{
|
||||
dma_t *dma = dma_chan + channel;
|
||||
|
||||
if (dma->active)
|
||||
printk(KERN_ERR "dma%d: altering DMA address while "
|
||||
"DMA active\n", channel);
|
||||
|
||||
dma->sg = &dma->buf;
|
||||
dma->sgcount = 1;
|
||||
dma->buf.__address = (char *)physaddr;//FIXME - not pretty
|
||||
dma->using_sg = 0;
|
||||
dma->invalid = 1;
|
||||
}
|
||||
|
||||
/* Set DMA byte count
|
||||
*
|
||||
* Copy address to the structure, and set the invalid bit
|
||||
*/
|
||||
void set_dma_count (dmach_t channel, unsigned long count)
|
||||
{
|
||||
dma_t *dma = dma_chan + channel;
|
||||
|
||||
if (dma->active)
|
||||
printk(KERN_ERR "dma%d: altering DMA count while "
|
||||
"DMA active\n", channel);
|
||||
|
||||
dma->sg = &dma->buf;
|
||||
dma->sgcount = 1;
|
||||
dma->buf.length = count;
|
||||
dma->using_sg = 0;
|
||||
dma->invalid = 1;
|
||||
}
|
||||
|
||||
/* Set DMA direction mode
|
||||
*/
|
||||
void set_dma_mode (dmach_t channel, dmamode_t mode)
|
||||
{
|
||||
dma_t *dma = dma_chan + channel;
|
||||
|
||||
if (dma->active)
|
||||
printk(KERN_ERR "dma%d: altering DMA mode while "
|
||||
"DMA active\n", channel);
|
||||
|
||||
dma->dma_mode = mode;
|
||||
dma->invalid = 1;
|
||||
}
|
||||
|
||||
/* Enable DMA channel
|
||||
*/
|
||||
void enable_dma (dmach_t channel)
|
||||
{
|
||||
dma_t *dma = dma_chan + channel;
|
||||
|
||||
if (!dma->lock)
|
||||
goto free_dma;
|
||||
|
||||
if (dma->active == 0) {
|
||||
dma->active = 1;
|
||||
dma->d_ops->enable(channel, dma);
|
||||
}
|
||||
return;
|
||||
|
||||
free_dma:
|
||||
printk(KERN_ERR "dma%d: trying to enable free DMA\n", channel);
|
||||
BUG();
|
||||
}
|
||||
|
||||
/* Disable DMA channel
|
||||
*/
|
||||
void disable_dma (dmach_t channel)
|
||||
{
|
||||
dma_t *dma = dma_chan + channel;
|
||||
|
||||
if (!dma->lock)
|
||||
goto free_dma;
|
||||
|
||||
if (dma->active == 1) {
|
||||
dma->active = 0;
|
||||
dma->d_ops->disable(channel, dma);
|
||||
}
|
||||
return;
|
||||
|
||||
free_dma:
|
||||
printk(KERN_ERR "dma%d: trying to disable free DMA\n", channel);
|
||||
BUG();
|
||||
}
|
||||
|
||||
/*
|
||||
* Is the specified DMA channel active?
|
||||
*/
|
||||
int dma_channel_active(dmach_t channel)
|
||||
{
|
||||
return dma_chan[channel].active;
|
||||
}
|
||||
|
||||
void set_dma_page(dmach_t channel, char pagenr)
|
||||
{
|
||||
printk(KERN_ERR "dma%d: trying to set_dma_page\n", channel);
|
||||
}
|
||||
|
||||
void set_dma_speed(dmach_t channel, int cycle_ns)
|
||||
{
|
||||
dma_t *dma = dma_chan + channel;
|
||||
int ret = 0;
|
||||
|
||||
if (dma->d_ops->setspeed)
|
||||
ret = dma->d_ops->setspeed(channel, dma, cycle_ns);
|
||||
dma->speed = ret;
|
||||
}
|
||||
|
||||
int get_dma_residue(dmach_t channel)
|
||||
{
|
||||
dma_t *dma = dma_chan + channel;
|
||||
int ret = 0;
|
||||
|
||||
if (dma->d_ops->residue)
|
||||
ret = dma->d_ops->residue(channel, dma);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void __init init_dma(void)
|
||||
{
|
||||
arch_dma_init(dma_chan);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(request_dma);
|
||||
EXPORT_SYMBOL(free_dma);
|
||||
EXPORT_SYMBOL(enable_dma);
|
||||
EXPORT_SYMBOL(disable_dma);
|
||||
EXPORT_SYMBOL(set_dma_addr);
|
||||
EXPORT_SYMBOL(set_dma_count);
|
||||
EXPORT_SYMBOL(set_dma_mode);
|
||||
EXPORT_SYMBOL(set_dma_page);
|
||||
EXPORT_SYMBOL(get_dma_residue);
|
||||
EXPORT_SYMBOL(set_dma_sg);
|
||||
EXPORT_SYMBOL(set_dma_speed);
|
||||
|
||||
EXPORT_SYMBOL(dma_spin_lock);
|
850
arch/arm26/kernel/ecard.c
Normal file
850
arch/arm26/kernel/ecard.c
Normal file
@@ -0,0 +1,850 @@
|
||||
/*
|
||||
* linux/arch/arm26/kernel/ecard.c
|
||||
*
|
||||
* Copyright 1995-2001 Russell King
|
||||
* Copyright 2003 Ian Molton
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Find all installed expansion cards, and handle interrupts from them.
|
||||
*
|
||||
* Created from information from Acorns RiscOS3 PRMs
|
||||
* 15-Jun-2003 IM Modified from ARM32 (RiscPC capable) version
|
||||
* 10-Jan-1999 RMK Run loaders in a simulated RISC OS environment.
|
||||
* 06-May-1997 RMK Added blacklist for cards whose loader doesn't work.
|
||||
* 12-Sep-1997 RMK Created new handling of interrupt enables/disables
|
||||
* - cards can now register their own routine to control
|
||||
* interrupts (recommended).
|
||||
* 29-Sep-1997 RMK Expansion card interrupt hardware not being re-enabled
|
||||
* on reset from Linux. (Caused cards not to respond
|
||||
* under RiscOS without hard reset).
|
||||
*
|
||||
*/
|
||||
#define ECARD_C
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/dma.h>
|
||||
#include <asm/ecard.h>
|
||||
#include <asm/hardware.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <asm/irqchip.h>
|
||||
#include <asm/tlbflush.h>
|
||||
|
||||
enum req {
|
||||
req_readbytes,
|
||||
req_reset
|
||||
};
|
||||
|
||||
struct ecard_request {
|
||||
enum req req;
|
||||
ecard_t *ec;
|
||||
unsigned int address;
|
||||
unsigned int length;
|
||||
unsigned int use_loader;
|
||||
void *buffer;
|
||||
};
|
||||
|
||||
struct expcard_blacklist {
|
||||
unsigned short manufacturer;
|
||||
unsigned short product;
|
||||
const char *type;
|
||||
};
|
||||
|
||||
static ecard_t *cards;
|
||||
static ecard_t *slot_to_expcard[MAX_ECARDS];
|
||||
static unsigned int ectcr;
|
||||
|
||||
/* List of descriptions of cards which don't have an extended
|
||||
* identification, or chunk directories containing a description.
|
||||
*/
|
||||
static struct expcard_blacklist __initdata blacklist[] = {
|
||||
{ MANU_ACORN, PROD_ACORN_ETHER1, "Acorn Ether1" }
|
||||
};
|
||||
|
||||
asmlinkage extern int
|
||||
ecard_loader_reset(volatile unsigned char *pa, loader_t loader);
|
||||
asmlinkage extern int
|
||||
ecard_loader_read(int off, volatile unsigned char *pa, loader_t loader);
|
||||
|
||||
static const struct ecard_id *
|
||||
ecard_match_device(const struct ecard_id *ids, struct expansion_card *ec);
|
||||
|
||||
static inline unsigned short
|
||||
ecard_getu16(unsigned char *v)
|
||||
{
|
||||
return v[0] | v[1] << 8;
|
||||
}
|
||||
|
||||
static inline signed long
|
||||
ecard_gets24(unsigned char *v)
|
||||
{
|
||||
return v[0] | v[1] << 8 | v[2] << 16 | ((v[2] & 0x80) ? 0xff000000 : 0);
|
||||
}
|
||||
|
||||
static inline ecard_t *
|
||||
slot_to_ecard(unsigned int slot)
|
||||
{
|
||||
return slot < MAX_ECARDS ? slot_to_expcard[slot] : NULL;
|
||||
}
|
||||
|
||||
/* ===================== Expansion card daemon ======================== */
|
||||
/*
|
||||
* Since the loader programs on the expansion cards need to be run
|
||||
* in a specific environment, create a separate task with this
|
||||
* environment up, and pass requests to this task as and when we
|
||||
* need to.
|
||||
*
|
||||
* This should allow 99% of loaders to be called from Linux.
|
||||
*
|
||||
* From a security standpoint, we trust the card vendors. This
|
||||
* may be a misplaced trust.
|
||||
*/
|
||||
#define BUS_ADDR(x) ((((unsigned long)(x)) << 2) + IO_BASE)
|
||||
#define POD_INT_ADDR(x) ((volatile unsigned char *)\
|
||||
((BUS_ADDR((x)) - IO_BASE) + IO_START))
|
||||
|
||||
static inline void ecard_task_reset(struct ecard_request *req)
|
||||
{
|
||||
struct expansion_card *ec = req->ec;
|
||||
if (ec->loader)
|
||||
ecard_loader_reset(POD_INT_ADDR(ec->podaddr), ec->loader);
|
||||
}
|
||||
|
||||
static void
|
||||
ecard_task_readbytes(struct ecard_request *req)
|
||||
{
|
||||
unsigned char *buf = (unsigned char *)req->buffer;
|
||||
volatile unsigned char *base_addr =
|
||||
(volatile unsigned char *)POD_INT_ADDR(req->ec->podaddr);
|
||||
unsigned int len = req->length;
|
||||
unsigned int off = req->address;
|
||||
|
||||
if (!req->use_loader || !req->ec->loader) {
|
||||
off *= 4;
|
||||
while (len--) {
|
||||
*buf++ = base_addr[off];
|
||||
off += 4;
|
||||
}
|
||||
} else {
|
||||
while(len--) {
|
||||
/*
|
||||
* The following is required by some
|
||||
* expansion card loader programs.
|
||||
*/
|
||||
*(unsigned long *)0x108 = 0;
|
||||
*buf++ = ecard_loader_read(off++, base_addr,
|
||||
req->ec->loader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ecard_do_request(struct ecard_request *req)
|
||||
{
|
||||
switch (req->req) {
|
||||
case req_readbytes:
|
||||
ecard_task_readbytes(req);
|
||||
break;
|
||||
|
||||
case req_reset:
|
||||
ecard_task_reset(req);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* On 26-bit processors, we don't need the kcardd thread to access the
|
||||
* expansion card loaders. We do it directly.
|
||||
*/
|
||||
#define ecard_call(req) ecard_do_request(req)
|
||||
|
||||
/* ======================= Mid-level card control ===================== */
|
||||
|
||||
static void
|
||||
ecard_readbytes(void *addr, ecard_t *ec, int off, int len, int useld)
|
||||
{
|
||||
struct ecard_request req;
|
||||
|
||||
req.req = req_readbytes;
|
||||
req.ec = ec;
|
||||
req.address = off;
|
||||
req.length = len;
|
||||
req.use_loader = useld;
|
||||
req.buffer = addr;
|
||||
|
||||
ecard_call(&req);
|
||||
}
|
||||
|
||||
int ecard_readchunk(struct in_chunk_dir *cd, ecard_t *ec, int id, int num)
|
||||
{
|
||||
struct ex_chunk_dir excd;
|
||||
int index = 16;
|
||||
int useld = 0;
|
||||
|
||||
if (!ec->cid.cd)
|
||||
return 0;
|
||||
|
||||
while(1) {
|
||||
ecard_readbytes(&excd, ec, index, 8, useld);
|
||||
index += 8;
|
||||
if (c_id(&excd) == 0) {
|
||||
if (!useld && ec->loader) {
|
||||
useld = 1;
|
||||
index = 0;
|
||||
continue;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (c_id(&excd) == 0xf0) { /* link */
|
||||
index = c_start(&excd);
|
||||
continue;
|
||||
}
|
||||
if (c_id(&excd) == 0x80) { /* loader */
|
||||
if (!ec->loader) {
|
||||
ec->loader = (loader_t)kmalloc(c_len(&excd),
|
||||
GFP_KERNEL);
|
||||
if (ec->loader)
|
||||
ecard_readbytes(ec->loader, ec,
|
||||
(int)c_start(&excd),
|
||||
c_len(&excd), useld);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (c_id(&excd) == id && num-- == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (c_id(&excd) & 0x80) {
|
||||
switch (c_id(&excd) & 0x70) {
|
||||
case 0x70:
|
||||
ecard_readbytes((unsigned char *)excd.d.string, ec,
|
||||
(int)c_start(&excd), c_len(&excd),
|
||||
useld);
|
||||
break;
|
||||
case 0x00:
|
||||
break;
|
||||
}
|
||||
}
|
||||
cd->start_offset = c_start(&excd);
|
||||
memcpy(cd->d.string, excd.d.string, 256);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ======================= Interrupt control ============================ */
|
||||
|
||||
static void ecard_def_irq_enable(ecard_t *ec, int irqnr)
|
||||
{
|
||||
}
|
||||
|
||||
static void ecard_def_irq_disable(ecard_t *ec, int irqnr)
|
||||
{
|
||||
}
|
||||
|
||||
static int ecard_def_irq_pending(ecard_t *ec)
|
||||
{
|
||||
return !ec->irqmask || ec->irqaddr[0] & ec->irqmask;
|
||||
}
|
||||
|
||||
static void ecard_def_fiq_enable(ecard_t *ec, int fiqnr)
|
||||
{
|
||||
panic("ecard_def_fiq_enable called - impossible");
|
||||
}
|
||||
|
||||
static void ecard_def_fiq_disable(ecard_t *ec, int fiqnr)
|
||||
{
|
||||
panic("ecard_def_fiq_disable called - impossible");
|
||||
}
|
||||
|
||||
static int ecard_def_fiq_pending(ecard_t *ec)
|
||||
{
|
||||
return !ec->fiqmask || ec->fiqaddr[0] & ec->fiqmask;
|
||||
}
|
||||
|
||||
static expansioncard_ops_t ecard_default_ops = {
|
||||
ecard_def_irq_enable,
|
||||
ecard_def_irq_disable,
|
||||
ecard_def_irq_pending,
|
||||
ecard_def_fiq_enable,
|
||||
ecard_def_fiq_disable,
|
||||
ecard_def_fiq_pending
|
||||
};
|
||||
|
||||
/*
|
||||
* Enable and disable interrupts from expansion cards.
|
||||
* (interrupts are disabled for these functions).
|
||||
*
|
||||
* They are not meant to be called directly, but via enable/disable_irq.
|
||||
*/
|
||||
static void ecard_irq_unmask(unsigned int irqnr)
|
||||
{
|
||||
ecard_t *ec = slot_to_ecard(irqnr - 32);
|
||||
|
||||
if (ec) {
|
||||
if (!ec->ops)
|
||||
ec->ops = &ecard_default_ops;
|
||||
|
||||
if (ec->claimed && ec->ops->irqenable)
|
||||
ec->ops->irqenable(ec, irqnr);
|
||||
else
|
||||
printk(KERN_ERR "ecard: rejecting request to "
|
||||
"enable IRQs for %d\n", irqnr);
|
||||
}
|
||||
}
|
||||
|
||||
static void ecard_irq_mask(unsigned int irqnr)
|
||||
{
|
||||
ecard_t *ec = slot_to_ecard(irqnr - 32);
|
||||
|
||||
if (ec) {
|
||||
if (!ec->ops)
|
||||
ec->ops = &ecard_default_ops;
|
||||
|
||||
if (ec->ops && ec->ops->irqdisable)
|
||||
ec->ops->irqdisable(ec, irqnr);
|
||||
}
|
||||
}
|
||||
|
||||
static struct irqchip ecard_chip = {
|
||||
.ack = ecard_irq_mask,
|
||||
.mask = ecard_irq_mask,
|
||||
.unmask = ecard_irq_unmask,
|
||||
};
|
||||
|
||||
void ecard_enablefiq(unsigned int fiqnr)
|
||||
{
|
||||
ecard_t *ec = slot_to_ecard(fiqnr);
|
||||
|
||||
if (ec) {
|
||||
if (!ec->ops)
|
||||
ec->ops = &ecard_default_ops;
|
||||
|
||||
if (ec->claimed && ec->ops->fiqenable)
|
||||
ec->ops->fiqenable(ec, fiqnr);
|
||||
else
|
||||
printk(KERN_ERR "ecard: rejecting request to "
|
||||
"enable FIQs for %d\n", fiqnr);
|
||||
}
|
||||
}
|
||||
|
||||
void ecard_disablefiq(unsigned int fiqnr)
|
||||
{
|
||||
ecard_t *ec = slot_to_ecard(fiqnr);
|
||||
|
||||
if (ec) {
|
||||
if (!ec->ops)
|
||||
ec->ops = &ecard_default_ops;
|
||||
|
||||
if (ec->ops->fiqdisable)
|
||||
ec->ops->fiqdisable(ec, fiqnr);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ecard_dump_irq_state(ecard_t *ec)
|
||||
{
|
||||
printk(" %d: %sclaimed, ",
|
||||
ec->slot_no,
|
||||
ec->claimed ? "" : "not ");
|
||||
|
||||
if (ec->ops && ec->ops->irqpending &&
|
||||
ec->ops != &ecard_default_ops)
|
||||
printk("irq %spending\n",
|
||||
ec->ops->irqpending(ec) ? "" : "not ");
|
||||
else
|
||||
printk("irqaddr %p, mask = %02X, status = %02X\n",
|
||||
ec->irqaddr, ec->irqmask, *ec->irqaddr);
|
||||
}
|
||||
|
||||
static void ecard_check_lockup(struct irqdesc *desc)
|
||||
{
|
||||
static int last, lockup;
|
||||
ecard_t *ec;
|
||||
|
||||
/*
|
||||
* If the timer interrupt has not run since the last million
|
||||
* unrecognised expansion card interrupts, then there is
|
||||
* something seriously wrong. Disable the expansion card
|
||||
* interrupts so at least we can continue.
|
||||
*
|
||||
* Maybe we ought to start a timer to re-enable them some time
|
||||
* later?
|
||||
*/
|
||||
if (last == jiffies) {
|
||||
lockup += 1;
|
||||
if (lockup > 1000000) {
|
||||
printk(KERN_ERR "\nInterrupt lockup detected - "
|
||||
"disabling all expansion card interrupts\n");
|
||||
|
||||
desc->chip->mask(IRQ_EXPANSIONCARD);
|
||||
|
||||
printk("Expansion card IRQ state:\n");
|
||||
|
||||
for (ec = cards; ec; ec = ec->next)
|
||||
ecard_dump_irq_state(ec);
|
||||
}
|
||||
} else
|
||||
lockup = 0;
|
||||
|
||||
/*
|
||||
* If we did not recognise the source of this interrupt,
|
||||
* warn the user, but don't flood the user with these messages.
|
||||
*/
|
||||
if (!last || time_after(jiffies, (unsigned long)(last + 5*HZ))) {
|
||||
last = jiffies;
|
||||
printk(KERN_WARNING "Unrecognised interrupt from backplane\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ecard_irq_handler(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs)
|
||||
{
|
||||
ecard_t *ec;
|
||||
int called = 0;
|
||||
|
||||
desc->chip->mask(irq);
|
||||
for (ec = cards; ec; ec = ec->next) {
|
||||
int pending;
|
||||
|
||||
if (!ec->claimed || ec->irq == NO_IRQ)
|
||||
continue;
|
||||
|
||||
if (ec->ops && ec->ops->irqpending)
|
||||
pending = ec->ops->irqpending(ec);
|
||||
else
|
||||
pending = ecard_default_ops.irqpending(ec);
|
||||
|
||||
if (pending) {
|
||||
struct irqdesc *d = irq_desc + ec->irq;
|
||||
d->handle(ec->irq, d, regs);
|
||||
called ++;
|
||||
}
|
||||
}
|
||||
desc->chip->unmask(irq);
|
||||
|
||||
if (called == 0)
|
||||
ecard_check_lockup(desc);
|
||||
}
|
||||
|
||||
#define ecard_irqexp_handler NULL
|
||||
#define ecard_probeirqhw() (0)
|
||||
|
||||
unsigned int ecard_address(ecard_t *ec, card_type_t type, card_speed_t speed)
|
||||
{
|
||||
unsigned long address = 0;
|
||||
int slot = ec->slot_no;
|
||||
|
||||
ectcr &= ~(1 << slot);
|
||||
|
||||
switch (type) {
|
||||
case ECARD_MEMC:
|
||||
address = IO_EC_MEMC_BASE + (slot << 12);
|
||||
break;
|
||||
|
||||
case ECARD_IOC:
|
||||
address = IO_EC_IOC_BASE + (slot << 12) + (speed << 17);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
static int ecard_prints(char *buffer, ecard_t *ec)
|
||||
{
|
||||
char *start = buffer;
|
||||
|
||||
buffer += sprintf(buffer, " %d: ", ec->slot_no);
|
||||
|
||||
if (ec->cid.id == 0) {
|
||||
struct in_chunk_dir incd;
|
||||
|
||||
buffer += sprintf(buffer, "[%04X:%04X] ",
|
||||
ec->cid.manufacturer, ec->cid.product);
|
||||
|
||||
if (!ec->card_desc && ec->cid.cd &&
|
||||
ecard_readchunk(&incd, ec, 0xf5, 0)) {
|
||||
ec->card_desc = kmalloc(strlen(incd.d.string)+1, GFP_KERNEL);
|
||||
|
||||
if (ec->card_desc)
|
||||
strcpy((char *)ec->card_desc, incd.d.string);
|
||||
}
|
||||
|
||||
buffer += sprintf(buffer, "%s\n", ec->card_desc ? ec->card_desc : "*unknown*");
|
||||
} else
|
||||
buffer += sprintf(buffer, "Simple card %d\n", ec->cid.id);
|
||||
|
||||
return buffer - start;
|
||||
}
|
||||
|
||||
static int get_ecard_dev_info(char *buf, char **start, off_t pos, int count)
|
||||
{
|
||||
ecard_t *ec = cards;
|
||||
off_t at = 0;
|
||||
int len, cnt;
|
||||
|
||||
cnt = 0;
|
||||
while (ec && count > cnt) {
|
||||
len = ecard_prints(buf, ec);
|
||||
at += len;
|
||||
if (at >= pos) {
|
||||
if (!*start) {
|
||||
*start = buf + (pos - (at - len));
|
||||
cnt = at - pos;
|
||||
} else
|
||||
cnt += len;
|
||||
buf += len;
|
||||
}
|
||||
ec = ec->next;
|
||||
}
|
||||
return (count > cnt) ? cnt : count;
|
||||
}
|
||||
|
||||
static struct proc_dir_entry *proc_bus_ecard_dir = NULL;
|
||||
|
||||
static void ecard_proc_init(void)
|
||||
{
|
||||
proc_bus_ecard_dir = proc_mkdir("ecard", proc_bus);
|
||||
create_proc_info_entry("devices", 0, proc_bus_ecard_dir,
|
||||
get_ecard_dev_info);
|
||||
}
|
||||
|
||||
#define ec_set_resource(ec,nr,st,sz,flg) \
|
||||
do { \
|
||||
(ec)->resource[nr].name = ec->dev.bus_id; \
|
||||
(ec)->resource[nr].start = st; \
|
||||
(ec)->resource[nr].end = (st) + (sz) - 1; \
|
||||
(ec)->resource[nr].flags = flg; \
|
||||
} while (0)
|
||||
|
||||
static void __init ecard_init_resources(struct expansion_card *ec)
|
||||
{
|
||||
unsigned long base = PODSLOT_IOC0_BASE;
|
||||
unsigned int slot = ec->slot_no;
|
||||
int i;
|
||||
|
||||
ec_set_resource(ec, ECARD_RES_MEMC,
|
||||
PODSLOT_MEMC_BASE + (slot << 14),
|
||||
PODSLOT_MEMC_SIZE, IORESOURCE_MEM);
|
||||
|
||||
for (i = 0; i < ECARD_RES_IOCSYNC - ECARD_RES_IOCSLOW; i++) {
|
||||
ec_set_resource(ec, i + ECARD_RES_IOCSLOW,
|
||||
base + (slot << 14) + (i << 19),
|
||||
PODSLOT_IOC_SIZE, IORESOURCE_MEM);
|
||||
}
|
||||
|
||||
for (i = 0; i < ECARD_NUM_RESOURCES; i++) {
|
||||
if (ec->resource[i].start &&
|
||||
request_resource(&iomem_resource, &ec->resource[i])) {
|
||||
printk(KERN_ERR "%s: resource(s) not available\n",
|
||||
ec->dev.bus_id);
|
||||
ec->resource[i].end -= ec->resource[i].start;
|
||||
ec->resource[i].start = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t ecard_show_irq(struct device *dev, char *buf)
|
||||
{
|
||||
struct expansion_card *ec = ECARD_DEV(dev);
|
||||
return sprintf(buf, "%u\n", ec->irq);
|
||||
}
|
||||
|
||||
static ssize_t ecard_show_vendor(struct device *dev, char *buf)
|
||||
{
|
||||
struct expansion_card *ec = ECARD_DEV(dev);
|
||||
return sprintf(buf, "%u\n", ec->cid.manufacturer);
|
||||
}
|
||||
|
||||
static ssize_t ecard_show_device(struct device *dev, char *buf)
|
||||
{
|
||||
struct expansion_card *ec = ECARD_DEV(dev);
|
||||
return sprintf(buf, "%u\n", ec->cid.product);
|
||||
}
|
||||
|
||||
static ssize_t ecard_show_dma(struct device *dev, char *buf)
|
||||
{
|
||||
struct expansion_card *ec = ECARD_DEV(dev);
|
||||
return sprintf(buf, "%u\n", ec->dma);
|
||||
}
|
||||
|
||||
static ssize_t ecard_show_resources(struct device *dev, char *buf)
|
||||
{
|
||||
struct expansion_card *ec = ECARD_DEV(dev);
|
||||
char *str = buf;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ECARD_NUM_RESOURCES; i++)
|
||||
str += sprintf(str, "%08lx %08lx %08lx\n",
|
||||
ec->resource[i].start,
|
||||
ec->resource[i].end,
|
||||
ec->resource[i].flags);
|
||||
|
||||
return str - buf;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(irq, S_IRUGO, ecard_show_irq, NULL);
|
||||
static DEVICE_ATTR(vendor, S_IRUGO, ecard_show_vendor, NULL);
|
||||
static DEVICE_ATTR(device, S_IRUGO, ecard_show_device, NULL);
|
||||
static DEVICE_ATTR(dma, S_IRUGO, ecard_show_dma, NULL);
|
||||
static DEVICE_ATTR(resource, S_IRUGO, ecard_show_resources, NULL);
|
||||
|
||||
/*
|
||||
* Probe for an expansion card.
|
||||
*
|
||||
* If bit 1 of the first byte of the card is set, then the
|
||||
* card does not exist.
|
||||
*/
|
||||
static int __init
|
||||
ecard_probe(int slot, card_type_t type)
|
||||
{
|
||||
ecard_t **ecp;
|
||||
ecard_t *ec;
|
||||
struct ex_ecid cid;
|
||||
int i, rc = -ENOMEM;
|
||||
|
||||
ec = kmalloc(sizeof(ecard_t), GFP_KERNEL);
|
||||
if (!ec)
|
||||
goto nomem;
|
||||
|
||||
memset(ec, 0, sizeof(ecard_t));
|
||||
|
||||
ec->slot_no = slot;
|
||||
ec->type = type;
|
||||
ec->irq = NO_IRQ;
|
||||
ec->fiq = NO_IRQ;
|
||||
ec->dma = NO_DMA;
|
||||
ec->card_desc = NULL;
|
||||
ec->ops = &ecard_default_ops;
|
||||
|
||||
rc = -ENODEV;
|
||||
if ((ec->podaddr = ecard_address(ec, type, ECARD_SYNC)) == 0)
|
||||
goto nodev;
|
||||
|
||||
cid.r_zero = 1;
|
||||
ecard_readbytes(&cid, ec, 0, 16, 0);
|
||||
if (cid.r_zero)
|
||||
goto nodev;
|
||||
|
||||
ec->cid.id = cid.r_id;
|
||||
ec->cid.cd = cid.r_cd;
|
||||
ec->cid.is = cid.r_is;
|
||||
ec->cid.w = cid.r_w;
|
||||
ec->cid.manufacturer = ecard_getu16(cid.r_manu);
|
||||
ec->cid.product = ecard_getu16(cid.r_prod);
|
||||
ec->cid.country = cid.r_country;
|
||||
ec->cid.irqmask = cid.r_irqmask;
|
||||
ec->cid.irqoff = ecard_gets24(cid.r_irqoff);
|
||||
ec->cid.fiqmask = cid.r_fiqmask;
|
||||
ec->cid.fiqoff = ecard_gets24(cid.r_fiqoff);
|
||||
ec->fiqaddr =
|
||||
ec->irqaddr = (unsigned char *)ioaddr(ec->podaddr);
|
||||
|
||||
if (ec->cid.is) {
|
||||
ec->irqmask = ec->cid.irqmask;
|
||||
ec->irqaddr += ec->cid.irqoff;
|
||||
ec->fiqmask = ec->cid.fiqmask;
|
||||
ec->fiqaddr += ec->cid.fiqoff;
|
||||
} else {
|
||||
ec->irqmask = 1;
|
||||
ec->fiqmask = 4;
|
||||
}
|
||||
|
||||
for (i = 0; i < sizeof(blacklist) / sizeof(*blacklist); i++)
|
||||
if (blacklist[i].manufacturer == ec->cid.manufacturer &&
|
||||
blacklist[i].product == ec->cid.product) {
|
||||
ec->card_desc = blacklist[i].type;
|
||||
break;
|
||||
}
|
||||
|
||||
snprintf(ec->dev.bus_id, sizeof(ec->dev.bus_id), "ecard%d", slot);
|
||||
ec->dev.parent = NULL;
|
||||
ec->dev.bus = &ecard_bus_type;
|
||||
ec->dev.dma_mask = &ec->dma_mask;
|
||||
ec->dma_mask = (u64)0xffffffff;
|
||||
|
||||
ecard_init_resources(ec);
|
||||
|
||||
/*
|
||||
* hook the interrupt handlers
|
||||
*/
|
||||
ec->irq = 32 + slot;
|
||||
set_irq_chip(ec->irq, &ecard_chip);
|
||||
set_irq_handler(ec->irq, do_level_IRQ);
|
||||
set_irq_flags(ec->irq, IRQF_VALID);
|
||||
|
||||
for (ecp = &cards; *ecp; ecp = &(*ecp)->next);
|
||||
|
||||
*ecp = ec;
|
||||
slot_to_expcard[slot] = ec;
|
||||
|
||||
device_register(&ec->dev);
|
||||
device_create_file(&ec->dev, &dev_attr_dma);
|
||||
device_create_file(&ec->dev, &dev_attr_irq);
|
||||
device_create_file(&ec->dev, &dev_attr_resource);
|
||||
device_create_file(&ec->dev, &dev_attr_vendor);
|
||||
device_create_file(&ec->dev, &dev_attr_device);
|
||||
|
||||
return 0;
|
||||
|
||||
nodev:
|
||||
kfree(ec);
|
||||
nomem:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialise the expansion card system.
|
||||
* Locate all hardware - interrupt management and
|
||||
* actual cards.
|
||||
*/
|
||||
static int __init ecard_init(void)
|
||||
{
|
||||
int slot, irqhw;
|
||||
|
||||
printk("Probing expansion cards\n");
|
||||
|
||||
for (slot = 0; slot < MAX_ECARDS; slot ++) {
|
||||
ecard_probe(slot, ECARD_IOC);
|
||||
}
|
||||
|
||||
irqhw = ecard_probeirqhw();
|
||||
|
||||
set_irq_chained_handler(IRQ_EXPANSIONCARD,
|
||||
irqhw ? ecard_irqexp_handler : ecard_irq_handler);
|
||||
|
||||
ecard_proc_init();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
subsys_initcall(ecard_init);
|
||||
|
||||
/*
|
||||
* ECARD "bus"
|
||||
*/
|
||||
static const struct ecard_id *
|
||||
ecard_match_device(const struct ecard_id *ids, struct expansion_card *ec)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; ids[i].manufacturer != 65535; i++)
|
||||
if (ec->cid.manufacturer == ids[i].manufacturer &&
|
||||
ec->cid.product == ids[i].product)
|
||||
return ids + i;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int ecard_drv_probe(struct device *dev)
|
||||
{
|
||||
struct expansion_card *ec = ECARD_DEV(dev);
|
||||
struct ecard_driver *drv = ECARD_DRV(dev->driver);
|
||||
const struct ecard_id *id;
|
||||
int ret;
|
||||
|
||||
id = ecard_match_device(drv->id_table, ec);
|
||||
|
||||
ecard_claim(ec);
|
||||
ret = drv->probe(ec, id);
|
||||
if (ret)
|
||||
ecard_release(ec);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ecard_drv_remove(struct device *dev)
|
||||
{
|
||||
struct expansion_card *ec = ECARD_DEV(dev);
|
||||
struct ecard_driver *drv = ECARD_DRV(dev->driver);
|
||||
|
||||
drv->remove(ec);
|
||||
ecard_release(ec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Before rebooting, we must make sure that the expansion card is in a
|
||||
* sensible state, so it can be re-detected. This means that the first
|
||||
* page of the ROM must be visible. We call the expansion cards reset
|
||||
* handler, if any.
|
||||
*/
|
||||
static void ecard_drv_shutdown(struct device *dev)
|
||||
{
|
||||
struct expansion_card *ec = ECARD_DEV(dev);
|
||||
struct ecard_driver *drv = ECARD_DRV(dev->driver);
|
||||
struct ecard_request req;
|
||||
|
||||
if (drv->shutdown)
|
||||
drv->shutdown(ec);
|
||||
ecard_release(ec);
|
||||
req.req = req_reset;
|
||||
req.ec = ec;
|
||||
ecard_call(&req);
|
||||
}
|
||||
|
||||
int ecard_register_driver(struct ecard_driver *drv)
|
||||
{
|
||||
drv->drv.bus = &ecard_bus_type;
|
||||
drv->drv.probe = ecard_drv_probe;
|
||||
drv->drv.remove = ecard_drv_remove;
|
||||
drv->drv.shutdown = ecard_drv_shutdown;
|
||||
|
||||
return driver_register(&drv->drv);
|
||||
}
|
||||
|
||||
void ecard_remove_driver(struct ecard_driver *drv)
|
||||
{
|
||||
driver_unregister(&drv->drv);
|
||||
}
|
||||
|
||||
static int ecard_match(struct device *_dev, struct device_driver *_drv)
|
||||
{
|
||||
struct expansion_card *ec = ECARD_DEV(_dev);
|
||||
struct ecard_driver *drv = ECARD_DRV(_drv);
|
||||
int ret;
|
||||
|
||||
if (drv->id_table) {
|
||||
ret = ecard_match_device(drv->id_table, ec) != NULL;
|
||||
} else {
|
||||
ret = ec->cid.id == drv->id;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct bus_type ecard_bus_type = {
|
||||
.name = "ecard",
|
||||
.match = ecard_match,
|
||||
};
|
||||
|
||||
static int ecard_bus_init(void)
|
||||
{
|
||||
return bus_register(&ecard_bus_type);
|
||||
}
|
||||
|
||||
postcore_initcall(ecard_bus_init);
|
||||
|
||||
EXPORT_SYMBOL(ecard_readchunk);
|
||||
EXPORT_SYMBOL(ecard_address);
|
||||
EXPORT_SYMBOL(ecard_register_driver);
|
||||
EXPORT_SYMBOL(ecard_remove_driver);
|
||||
EXPORT_SYMBOL(ecard_bus_type);
|
961
arch/arm26/kernel/entry.S
Normal file
961
arch/arm26/kernel/entry.S
Normal file
@@ -0,0 +1,961 @@
|
||||
/* arch/arm26/kernel/entry.S
|
||||
*
|
||||
* Assembled from chunks of code in arch/arm
|
||||
*
|
||||
* Copyright (C) 2003 Ian Molton
|
||||
* Based on the work of RMK.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/linkage.h>
|
||||
|
||||
#include <asm/assembler.h>
|
||||
#include <asm/asm_offsets.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/hardware.h>
|
||||
#include <asm/sysirq.h>
|
||||
#include <asm/thread_info.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/ptrace.h>
|
||||
|
||||
.macro zero_fp
|
||||
#ifndef CONFIG_NO_FRAME_POINTER
|
||||
mov fp, #0
|
||||
#endif
|
||||
.endm
|
||||
|
||||
.text
|
||||
|
||||
@ Bad Abort numbers
|
||||
@ -----------------
|
||||
@
|
||||
#define BAD_PREFETCH 0
|
||||
#define BAD_DATA 1
|
||||
#define BAD_ADDREXCPTN 2
|
||||
#define BAD_IRQ 3
|
||||
#define BAD_UNDEFINSTR 4
|
||||
|
||||
@ OS version number used in SWIs
|
||||
@ RISC OS is 0
|
||||
@ RISC iX is 8
|
||||
@
|
||||
#define OS_NUMBER 9
|
||||
#define ARMSWI_OFFSET 0x000f0000
|
||||
|
||||
@
|
||||
@ Stack format (ensured by USER_* and SVC_*)
|
||||
@ PSR and PC are comined on arm26
|
||||
@
|
||||
|
||||
#define S_OFF 8
|
||||
|
||||
#define S_OLD_R0 64
|
||||
#define S_PC 60
|
||||
#define S_LR 56
|
||||
#define S_SP 52
|
||||
#define S_IP 48
|
||||
#define S_FP 44
|
||||
#define S_R10 40
|
||||
#define S_R9 36
|
||||
#define S_R8 32
|
||||
#define S_R7 28
|
||||
#define S_R6 24
|
||||
#define S_R5 20
|
||||
#define S_R4 16
|
||||
#define S_R3 12
|
||||
#define S_R2 8
|
||||
#define S_R1 4
|
||||
#define S_R0 0
|
||||
|
||||
.macro save_user_regs
|
||||
str r0, [sp, #-4]! @ Store SVC r0
|
||||
str lr, [sp, #-4]! @ Store user mode PC
|
||||
sub sp, sp, #15*4
|
||||
stmia sp, {r0 - lr}^ @ Store the other user-mode regs
|
||||
mov r0, r0
|
||||
.endm
|
||||
|
||||
.macro slow_restore_user_regs
|
||||
ldmia sp, {r0 - lr}^ @ restore the user regs not including PC
|
||||
mov r0, r0
|
||||
ldr lr, [sp, #15*4] @ get user PC
|
||||
add sp, sp, #15*4+8 @ free stack
|
||||
movs pc, lr @ return
|
||||
.endm
|
||||
|
||||
.macro fast_restore_user_regs
|
||||
add sp, sp, #S_OFF
|
||||
ldmib sp, {r1 - lr}^
|
||||
mov r0, r0
|
||||
ldr lr, [sp, #15*4]
|
||||
add sp, sp, #15*4+8
|
||||
movs pc, lr
|
||||
.endm
|
||||
|
||||
.macro save_svc_regs
|
||||
str sp, [sp, #-16]!
|
||||
str lr, [sp, #8]
|
||||
str lr, [sp, #4]
|
||||
stmfd sp!, {r0 - r12}
|
||||
mov r0, #-1
|
||||
str r0, [sp, #S_OLD_R0]
|
||||
zero_fp
|
||||
.endm
|
||||
|
||||
.macro save_svc_regs_irq
|
||||
str sp, [sp, #-16]!
|
||||
str lr, [sp, #4]
|
||||
ldr lr, .LCirq
|
||||
ldr lr, [lr]
|
||||
str lr, [sp, #8]
|
||||
stmfd sp!, {r0 - r12}
|
||||
mov r0, #-1
|
||||
str r0, [sp, #S_OLD_R0]
|
||||
zero_fp
|
||||
.endm
|
||||
|
||||
.macro restore_svc_regs
|
||||
ldmfd sp, {r0 - pc}^
|
||||
.endm
|
||||
|
||||
.macro mask_pc, rd, rm
|
||||
bic \rd, \rm, #PCMASK
|
||||
.endm
|
||||
|
||||
.macro disable_irqs, temp
|
||||
mov \temp, pc
|
||||
orr \temp, \temp, #PSR_I_BIT
|
||||
teqp \temp, #0
|
||||
.endm
|
||||
|
||||
.macro enable_irqs, temp
|
||||
mov \temp, pc
|
||||
and \temp, \temp, #~PSR_I_BIT
|
||||
teqp \temp, #0
|
||||
.endm
|
||||
|
||||
.macro initialise_traps_extra
|
||||
.endm
|
||||
|
||||
.macro get_thread_info, rd
|
||||
mov \rd, sp, lsr #13
|
||||
mov \rd, \rd, lsl #13
|
||||
.endm
|
||||
|
||||
/*
|
||||
* These are the registers used in the syscall handler, and allow us to
|
||||
* have in theory up to 7 arguments to a function - r0 to r6.
|
||||
*
|
||||
* Note that tbl == why is intentional.
|
||||
*
|
||||
* We must set at least "tsk" and "why" when calling ret_with_reschedule.
|
||||
*/
|
||||
scno .req r7 @ syscall number
|
||||
tbl .req r8 @ syscall table pointer
|
||||
why .req r8 @ Linux syscall (!= 0)
|
||||
tsk .req r9 @ current thread_info
|
||||
|
||||
/*
|
||||
* Get the system call number.
|
||||
*/
|
||||
.macro get_scno
|
||||
mask_pc lr, lr
|
||||
ldr scno, [lr, #-4] @ get SWI instruction
|
||||
.endm
|
||||
/*
|
||||
* -----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
* We rely on the fact that R0 is at the bottom of the stack (due to
|
||||
* slow/fast restore user regs).
|
||||
*/
|
||||
#if S_R0 != 0
|
||||
#error "Please fix"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is the fast syscall return path. We do as little as
|
||||
* possible here, and this includes saving r0 back into the SVC
|
||||
* stack.
|
||||
*/
|
||||
ret_fast_syscall:
|
||||
disable_irqs r1 @ disable interrupts
|
||||
ldr r1, [tsk, #TI_FLAGS]
|
||||
tst r1, #_TIF_WORK_MASK
|
||||
bne fast_work_pending
|
||||
fast_restore_user_regs
|
||||
|
||||
/*
|
||||
* Ok, we need to do extra processing, enter the slow path.
|
||||
*/
|
||||
fast_work_pending:
|
||||
str r0, [sp, #S_R0+S_OFF]! @ returned r0
|
||||
work_pending:
|
||||
tst r1, #_TIF_NEED_RESCHED
|
||||
bne work_resched
|
||||
tst r1, #_TIF_NOTIFY_RESUME | _TIF_SIGPENDING
|
||||
beq no_work_pending
|
||||
mov r0, sp @ 'regs'
|
||||
mov r2, why @ 'syscall'
|
||||
bl do_notify_resume
|
||||
disable_irqs r1 @ disable interrupts
|
||||
b no_work_pending
|
||||
|
||||
work_resched:
|
||||
bl schedule
|
||||
/*
|
||||
* "slow" syscall return path. "why" tells us if this was a real syscall.
|
||||
*/
|
||||
ENTRY(ret_to_user)
|
||||
ret_slow_syscall:
|
||||
disable_irqs r1 @ disable interrupts
|
||||
ldr r1, [tsk, #TI_FLAGS]
|
||||
tst r1, #_TIF_WORK_MASK
|
||||
bne work_pending
|
||||
no_work_pending:
|
||||
slow_restore_user_regs
|
||||
|
||||
/*
|
||||
* This is how we return from a fork.
|
||||
*/
|
||||
ENTRY(ret_from_fork)
|
||||
bl schedule_tail
|
||||
get_thread_info tsk
|
||||
ldr r1, [tsk, #TI_FLAGS] @ check for syscall tracing
|
||||
mov why, #1
|
||||
tst r1, #_TIF_SYSCALL_TRACE @ are we tracing syscalls?
|
||||
beq ret_slow_syscall
|
||||
mov r1, sp
|
||||
mov r0, #1 @ trace exit [IP = 1]
|
||||
bl syscall_trace
|
||||
b ret_slow_syscall
|
||||
|
||||
// FIXME - is this strictly necessary?
|
||||
#include "calls.S"
|
||||
|
||||
/*=============================================================================
|
||||
* SWI handler
|
||||
*-----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
.align 5
|
||||
ENTRY(vector_swi)
|
||||
save_user_regs
|
||||
zero_fp
|
||||
get_scno
|
||||
|
||||
#ifdef CONFIG_ALIGNMENT_TRAP
|
||||
ldr ip, __cr_alignment
|
||||
ldr ip, [ip]
|
||||
mcr p15, 0, ip, c1, c0 @ update control register
|
||||
#endif
|
||||
enable_irqs ip
|
||||
|
||||
str r4, [sp, #-S_OFF]! @ push fifth arg
|
||||
|
||||
get_thread_info tsk
|
||||
ldr ip, [tsk, #TI_FLAGS] @ check for syscall tracing
|
||||
bic scno, scno, #0xff000000 @ mask off SWI op-code
|
||||
eor scno, scno, #OS_NUMBER << 20 @ check OS number
|
||||
adr tbl, sys_call_table @ load syscall table pointer
|
||||
tst ip, #_TIF_SYSCALL_TRACE @ are we tracing syscalls?
|
||||
bne __sys_trace
|
||||
|
||||
adral lr, ret_fast_syscall @ set return address
|
||||
orral lr, lr, #PSR_I_BIT | MODE_SVC26 @ Force SVC mode on return
|
||||
cmp scno, #NR_syscalls @ check upper syscall limit
|
||||
ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine
|
||||
|
||||
add r1, sp, #S_OFF
|
||||
2: mov why, #0 @ no longer a real syscall
|
||||
cmp scno, #ARMSWI_OFFSET
|
||||
eor r0, scno, #OS_NUMBER << 20 @ put OS number back
|
||||
bcs arm_syscall
|
||||
b sys_ni_syscall @ not private func
|
||||
|
||||
/*
|
||||
* This is the really slow path. We're going to be doing
|
||||
* context switches, and waiting for our parent to respond.
|
||||
*/
|
||||
__sys_trace:
|
||||
add r1, sp, #S_OFF
|
||||
mov r0, #0 @ trace entry [IP = 0]
|
||||
bl syscall_trace
|
||||
|
||||
adral lr, __sys_trace_return @ set return address
|
||||
orral lr, lr, #PSR_I_BIT | MODE_SVC26 @ Force SVC mode on return
|
||||
add r1, sp, #S_R0 + S_OFF @ pointer to regs
|
||||
cmp scno, #NR_syscalls @ check upper syscall limit
|
||||
ldmccia r1, {r0 - r3} @ have to reload r0 - r3
|
||||
ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine
|
||||
b 2b
|
||||
|
||||
__sys_trace_return:
|
||||
str r0, [sp, #S_R0 + S_OFF]! @ save returned r0
|
||||
mov r1, sp
|
||||
mov r0, #1 @ trace exit [IP = 1]
|
||||
bl syscall_trace
|
||||
b ret_slow_syscall
|
||||
|
||||
.align 5
|
||||
#ifdef CONFIG_ALIGNMENT_TRAP
|
||||
.type __cr_alignment, #object
|
||||
__cr_alignment:
|
||||
.word cr_alignment
|
||||
#endif
|
||||
|
||||
.type sys_call_table, #object
|
||||
ENTRY(sys_call_table)
|
||||
#include "calls.S"
|
||||
|
||||
/*============================================================================
|
||||
* Special system call wrappers
|
||||
*/
|
||||
@ r0 = syscall number
|
||||
@ r5 = syscall table
|
||||
.type sys_syscall, #function
|
||||
sys_syscall:
|
||||
eor scno, r0, #OS_NUMBER << 20
|
||||
cmp scno, #NR_syscalls @ check range
|
||||
stmleia sp, {r5, r6} @ shuffle args
|
||||
movle r0, r1
|
||||
movle r1, r2
|
||||
movle r2, r3
|
||||
movle r3, r4
|
||||
ldrle pc, [tbl, scno, lsl #2]
|
||||
b sys_ni_syscall
|
||||
|
||||
sys_fork_wrapper:
|
||||
add r0, sp, #S_OFF
|
||||
b sys_fork
|
||||
|
||||
sys_vfork_wrapper:
|
||||
add r0, sp, #S_OFF
|
||||
b sys_vfork
|
||||
|
||||
sys_execve_wrapper:
|
||||
add r3, sp, #S_OFF
|
||||
b sys_execve
|
||||
|
||||
sys_clone_wapper:
|
||||
add r2, sp, #S_OFF
|
||||
b sys_clone
|
||||
|
||||
sys_sigsuspend_wrapper:
|
||||
add r3, sp, #S_OFF
|
||||
b sys_sigsuspend
|
||||
|
||||
sys_rt_sigsuspend_wrapper:
|
||||
add r2, sp, #S_OFF
|
||||
b sys_rt_sigsuspend
|
||||
|
||||
sys_sigreturn_wrapper:
|
||||
add r0, sp, #S_OFF
|
||||
b sys_sigreturn
|
||||
|
||||
sys_rt_sigreturn_wrapper:
|
||||
add r0, sp, #S_OFF
|
||||
b sys_rt_sigreturn
|
||||
|
||||
sys_sigaltstack_wrapper:
|
||||
ldr r2, [sp, #S_OFF + S_SP]
|
||||
b do_sigaltstack
|
||||
|
||||
/*
|
||||
* Note: off_4k (r5) is always units of 4K. If we can't do the requested
|
||||
* offset, we return EINVAL. FIXME - this lost some stuff from arm32 to
|
||||
* ifdefs. check it out.
|
||||
*/
|
||||
sys_mmap2:
|
||||
tst r5, #((1 << (PAGE_SHIFT - 12)) - 1)
|
||||
moveq r5, r5, lsr #PAGE_SHIFT - 12
|
||||
streq r5, [sp, #4]
|
||||
beq do_mmap2
|
||||
mov r0, #-EINVAL
|
||||
RETINSTR(mov,pc, lr)
|
||||
|
||||
/*
|
||||
* Design issues:
|
||||
* - We have several modes that each vector can be called from,
|
||||
* each with its own set of registers. On entry to any vector,
|
||||
* we *must* save the registers used in *that* mode.
|
||||
*
|
||||
* - This code must be as fast as possible.
|
||||
*
|
||||
* There are a few restrictions on the vectors:
|
||||
* - the SWI vector cannot be called from *any* non-user mode
|
||||
*
|
||||
* - the FP emulator is *never* called from *any* non-user mode undefined
|
||||
* instruction.
|
||||
*
|
||||
*/
|
||||
|
||||
.text
|
||||
|
||||
.macro handle_irq
|
||||
1: mov r4, #IOC_BASE
|
||||
ldrb r6, [r4, #0x24] @ get high priority first
|
||||
adr r5, irq_prio_h
|
||||
teq r6, #0
|
||||
ldreqb r6, [r4, #0x14] @ get low priority
|
||||
adreq r5, irq_prio_l
|
||||
|
||||
teq r6, #0 @ If an IRQ happened...
|
||||
ldrneb r0, [r5, r6] @ get IRQ number
|
||||
movne r1, sp @ get struct pt_regs
|
||||
adrne lr, 1b @ Set return address to 1b
|
||||
orrne lr, lr, #PSR_I_BIT | MODE_SVC26 @ (and force SVC mode)
|
||||
bne asm_do_IRQ @ process IRQ (if asserted)
|
||||
.endm
|
||||
|
||||
|
||||
/*
|
||||
* Interrupt table (incorporates priority)
|
||||
*/
|
||||
.macro irq_prio_table
|
||||
irq_prio_l: .byte 0, 0, 1, 0, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3
|
||||
.byte 4, 0, 1, 0, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3
|
||||
.byte 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
|
||||
.byte 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
|
||||
.byte 6, 6, 6, 6, 6, 6, 6, 6, 3, 3, 3, 3, 3, 3, 3, 3
|
||||
.byte 6, 6, 6, 6, 6, 6, 6, 6, 3, 3, 3, 3, 3, 3, 3, 3
|
||||
.byte 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
|
||||
.byte 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
|
||||
.byte 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
|
||||
.byte 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
|
||||
.byte 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
|
||||
.byte 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
|
||||
.byte 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
|
||||
.byte 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
|
||||
.byte 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
|
||||
.byte 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
|
||||
irq_prio_h: .byte 0, 8, 9, 8,10,10,10,10,11,11,11,11,10,10,10,10
|
||||
.byte 12, 8, 9, 8,10,10,10,10,11,11,11,11,10,10,10,10
|
||||
.byte 13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
|
||||
.byte 13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
|
||||
.byte 14,14,14,14,10,10,10,10,11,11,11,11,10,10,10,10
|
||||
.byte 14,14,14,14,10,10,10,10,11,11,11,11,10,10,10,10
|
||||
.byte 13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
|
||||
.byte 13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
|
||||
.byte 15,15,15,15,10,10,10,10,11,11,11,11,10,10,10,10
|
||||
.byte 15,15,15,15,10,10,10,10,11,11,11,11,10,10,10,10
|
||||
.byte 13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
|
||||
.byte 13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
|
||||
.byte 15,15,15,15,10,10,10,10,11,11,11,11,10,10,10,10
|
||||
.byte 15,15,15,15,10,10,10,10,11,11,11,11,10,10,10,10
|
||||
.byte 13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
|
||||
.byte 13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
|
||||
.endm
|
||||
|
||||
#if 1
|
||||
/*
|
||||
* Uncomment these if you wish to get more debugging into about data aborts.
|
||||
* FIXME - I bet we can find a way to encode these and keep performance.
|
||||
*/
|
||||
#define FAULT_CODE_LDRSTRPOST 0x80
|
||||
#define FAULT_CODE_LDRSTRPRE 0x40
|
||||
#define FAULT_CODE_LDRSTRREG 0x20
|
||||
#define FAULT_CODE_LDMSTM 0x10
|
||||
#define FAULT_CODE_LDCSTC 0x08
|
||||
#endif
|
||||
#define FAULT_CODE_PREFETCH 0x04
|
||||
#define FAULT_CODE_WRITE 0x02
|
||||
#define FAULT_CODE_FORCECOW 0x01
|
||||
|
||||
/*=============================================================================
|
||||
* Undefined FIQs
|
||||
*-----------------------------------------------------------------------------
|
||||
*/
|
||||
_unexp_fiq: ldr sp, .LCfiq
|
||||
mov r12, #IOC_BASE
|
||||
strb r12, [r12, #0x38] @ Disable FIQ register
|
||||
teqp pc, #PSR_I_BIT | PSR_F_BIT | MODE_SVC26
|
||||
mov r0, r0
|
||||
stmfd sp!, {r0 - r3, ip, lr}
|
||||
adr r0, Lfiqmsg
|
||||
bl printk
|
||||
ldmfd sp!, {r0 - r3, ip, lr}
|
||||
teqp pc, #PSR_I_BIT | PSR_F_BIT | MODE_FIQ26
|
||||
mov r0, r0
|
||||
movs pc, lr
|
||||
|
||||
Lfiqmsg: .ascii "*** Unexpected FIQ\n\0"
|
||||
.align
|
||||
|
||||
.LCfiq: .word __temp_fiq
|
||||
.LCirq: .word __temp_irq
|
||||
|
||||
/*=============================================================================
|
||||
* Undefined instruction handler
|
||||
*-----------------------------------------------------------------------------
|
||||
* Handles floating point instructions
|
||||
*/
|
||||
vector_undefinstr:
|
||||
tst lr, #MODE_SVC26 @ did we come from a non-user mode?
|
||||
bne __und_svc @ yes - deal with it.
|
||||
/* Otherwise, fall through for the user-space (common) case. */
|
||||
save_user_regs
|
||||
zero_fp @ zero frame pointer
|
||||
teqp pc, #PSR_I_BIT | MODE_SVC26 @ disable IRQs
|
||||
.Lbug_undef:
|
||||
ldr r4, .LC2
|
||||
ldr pc, [r4] @ Call FP module entry point
|
||||
/* FIXME - should we trap for a null pointer here? */
|
||||
|
||||
/* The SVC mode case */
|
||||
__und_svc: save_svc_regs @ Non-user mode
|
||||
mask_pc r0, lr
|
||||
and r2, lr, #3
|
||||
sub r0, r0, #4
|
||||
mov r1, sp
|
||||
bl do_undefinstr
|
||||
restore_svc_regs
|
||||
|
||||
/* We get here if the FP emulator doesnt handle the undef instr.
|
||||
* If the insn WAS handled, the emulator jumps to ret_from_exception by itself/
|
||||
*/
|
||||
.globl fpundefinstr
|
||||
fpundefinstr:
|
||||
mov r0, lr
|
||||
mov r1, sp
|
||||
teqp pc, #MODE_SVC26
|
||||
bl do_undefinstr
|
||||
b ret_from_exception @ Normal FP exit
|
||||
|
||||
#if defined CONFIG_FPE_NWFPE || defined CONFIG_FPE_FASTFPE
|
||||
/* The FPE is always present */
|
||||
.equ fpe_not_present, 0
|
||||
#else
|
||||
/* We get here if an undefined instruction happens and the floating
|
||||
* point emulator is not present. If the offending instruction was
|
||||
* a WFS, we just perform a normal return as if we had emulated the
|
||||
* operation. This is a hack to allow some basic userland binaries
|
||||
* to run so that the emulator module proper can be loaded. --philb
|
||||
* FIXME - probably a broken useless hack...
|
||||
*/
|
||||
fpe_not_present:
|
||||
adr r10, wfs_mask_data
|
||||
ldmia r10, {r4, r5, r6, r7, r8}
|
||||
ldr r10, [sp, #S_PC] @ Load PC
|
||||
sub r10, r10, #4
|
||||
mask_pc r10, r10
|
||||
ldrt r10, [r10] @ get instruction
|
||||
and r5, r10, r5
|
||||
teq r5, r4 @ Is it WFS?
|
||||
beq ret_from_exception
|
||||
and r5, r10, r8
|
||||
teq r5, r6 @ Is it LDF/STF on sp or fp?
|
||||
teqne r5, r7
|
||||
bne fpundefinstr
|
||||
tst r10, #0x00200000 @ Does it have WB
|
||||
beq ret_from_exception
|
||||
and r4, r10, #255 @ get offset
|
||||
and r6, r10, #0x000f0000
|
||||
tst r10, #0x00800000 @ +/-
|
||||
ldr r5, [sp, r6, lsr #14] @ Load reg
|
||||
rsbeq r4, r4, #0
|
||||
add r5, r5, r4, lsl #2
|
||||
str r5, [sp, r6, lsr #14] @ Save reg
|
||||
b ret_from_exception
|
||||
|
||||
wfs_mask_data: .word 0x0e200110 @ WFS/RFS
|
||||
.word 0x0fef0fff
|
||||
.word 0x0d0d0100 @ LDF [sp]/STF [sp]
|
||||
.word 0x0d0b0100 @ LDF [fp]/STF [fp]
|
||||
.word 0x0f0f0f00
|
||||
#endif
|
||||
|
||||
.LC2: .word fp_enter
|
||||
|
||||
/*=============================================================================
|
||||
* Prefetch abort handler
|
||||
*-----------------------------------------------------------------------------
|
||||
*/
|
||||
#define DEBUG_UNDEF
|
||||
/* remember: lr = USR pc */
|
||||
vector_prefetch:
|
||||
sub lr, lr, #4
|
||||
tst lr, #MODE_SVC26
|
||||
bne __pabt_invalid
|
||||
save_user_regs
|
||||
teqp pc, #MODE_SVC26 @ Enable IRQs...
|
||||
mask_pc r0, lr @ Address of abort
|
||||
mov r1, sp @ Tasks registers
|
||||
bl do_PrefetchAbort
|
||||
teq r0, #0 @ If non-zero, we believe this abort..
|
||||
bne ret_from_exception
|
||||
#ifdef DEBUG_UNDEF
|
||||
adr r0, t
|
||||
bl printk
|
||||
#endif
|
||||
ldr lr, [sp,#S_PC] @ FIXME program to test this on. I think its
|
||||
b .Lbug_undef @ broken at the moment though!)
|
||||
|
||||
__pabt_invalid: save_svc_regs
|
||||
mov r0, sp @ Prefetch aborts are definitely *not*
|
||||
mov r1, #BAD_PREFETCH @ allowed in non-user modes. We cant
|
||||
and r2, lr, #3 @ recover from this problem.
|
||||
b bad_mode
|
||||
|
||||
#ifdef DEBUG_UNDEF
|
||||
t: .ascii "*** undef ***\r\n\0"
|
||||
.align
|
||||
#endif
|
||||
|
||||
/*=============================================================================
|
||||
* Address exception handler
|
||||
*-----------------------------------------------------------------------------
|
||||
* These aren't too critical.
|
||||
* (they're not supposed to happen).
|
||||
* In order to debug the reason for address exceptions in non-user modes,
|
||||
* we have to obtain all the registers so that we can see what's going on.
|
||||
*/
|
||||
|
||||
vector_addrexcptn:
|
||||
sub lr, lr, #8
|
||||
tst lr, #3
|
||||
bne Laddrexcptn_not_user
|
||||
save_user_regs
|
||||
teq pc, #MODE_SVC26
|
||||
mask_pc r0, lr @ Point to instruction
|
||||
mov r1, sp @ Point to registers
|
||||
mov r2, #0x400
|
||||
mov lr, pc
|
||||
bl do_excpt
|
||||
b ret_from_exception
|
||||
|
||||
Laddrexcptn_not_user:
|
||||
save_svc_regs
|
||||
and r2, lr, #3
|
||||
teq r2, #3
|
||||
bne Laddrexcptn_illegal_mode
|
||||
teqp pc, #MODE_SVC26
|
||||
mask_pc r0, lr
|
||||
mov r1, sp
|
||||
orr r2, r2, #0x400
|
||||
bl do_excpt
|
||||
ldmia sp, {r0 - lr} @ I cant remember the reason I changed this...
|
||||
add sp, sp, #15*4
|
||||
movs pc, lr
|
||||
|
||||
Laddrexcptn_illegal_mode:
|
||||
mov r0, sp
|
||||
str lr, [sp, #-4]!
|
||||
orr r1, r2, #PSR_I_BIT | PSR_F_BIT
|
||||
teqp r1, #0 @ change into mode (wont be user mode)
|
||||
mov r0, r0
|
||||
mov r1, r8 @ Any register from r8 - r14 can be banked
|
||||
mov r2, r9
|
||||
mov r3, r10
|
||||
mov r4, r11
|
||||
mov r5, r12
|
||||
mov r6, r13
|
||||
mov r7, r14
|
||||
teqp pc, #PSR_F_BIT | MODE_SVC26 @ back to svc
|
||||
mov r0, r0
|
||||
stmfd sp!, {r1-r7}
|
||||
ldmia r0, {r0-r7}
|
||||
stmfd sp!, {r0-r7}
|
||||
mov r0, sp
|
||||
mov r1, #BAD_ADDREXCPTN
|
||||
b bad_mode
|
||||
|
||||
/*=============================================================================
|
||||
* Interrupt (IRQ) handler
|
||||
*-----------------------------------------------------------------------------
|
||||
* Note: if the IRQ was taken whilst in user mode, then *no* kernel routine
|
||||
* is running, so do not have to save svc lr.
|
||||
*
|
||||
* Entered in IRQ mode.
|
||||
*/
|
||||
|
||||
vector_IRQ: ldr sp, .LCirq @ Setup some temporary stack
|
||||
sub lr, lr, #4
|
||||
str lr, [sp] @ push return address
|
||||
|
||||
tst lr, #3
|
||||
bne __irq_non_usr
|
||||
|
||||
__irq_usr: teqp pc, #PSR_I_BIT | MODE_SVC26 @ Enter SVC mode
|
||||
mov r0, r0
|
||||
|
||||
ldr lr, .LCirq
|
||||
ldr lr, [lr] @ Restore lr for jump back to USR
|
||||
|
||||
save_user_regs
|
||||
|
||||
handle_irq
|
||||
|
||||
mov why, #0
|
||||
get_thread_info tsk
|
||||
b ret_to_user
|
||||
|
||||
@ Place the IRQ priority table here so that the handle_irq macros above
|
||||
@ and below here can access it.
|
||||
|
||||
irq_prio_table
|
||||
|
||||
__irq_non_usr: teqp pc, #PSR_I_BIT | MODE_SVC26 @ Enter SVC mode
|
||||
mov r0, r0
|
||||
|
||||
save_svc_regs_irq
|
||||
|
||||
and r2, lr, #3
|
||||
teq r2, #3
|
||||
bne __irq_invalid @ IRQ not from SVC mode
|
||||
|
||||
handle_irq
|
||||
|
||||
restore_svc_regs
|
||||
|
||||
__irq_invalid: mov r0, sp
|
||||
mov r1, #BAD_IRQ
|
||||
b bad_mode
|
||||
|
||||
/*=============================================================================
|
||||
* Data abort handler code
|
||||
*-----------------------------------------------------------------------------
|
||||
*
|
||||
* This handles both exceptions from user and SVC modes, computes the address
|
||||
* range of the problem, and does any correction that is required. It then
|
||||
* calls the kernel data abort routine.
|
||||
*
|
||||
* This is where I wish that the ARM would tell you which address aborted.
|
||||
*/
|
||||
|
||||
vector_data: sub lr, lr, #8 @ Correct lr
|
||||
tst lr, #3
|
||||
bne Ldata_not_user
|
||||
save_user_regs
|
||||
teqp pc, #MODE_SVC26
|
||||
mask_pc r0, lr
|
||||
bl Ldata_do
|
||||
b ret_from_exception
|
||||
|
||||
Ldata_not_user:
|
||||
save_svc_regs
|
||||
and r2, lr, #3
|
||||
teq r2, #3
|
||||
bne Ldata_illegal_mode
|
||||
tst lr, #PSR_I_BIT
|
||||
teqeqp pc, #MODE_SVC26
|
||||
mask_pc r0, lr
|
||||
bl Ldata_do
|
||||
restore_svc_regs
|
||||
|
||||
Ldata_illegal_mode:
|
||||
mov r0, sp
|
||||
mov r1, #BAD_DATA
|
||||
b bad_mode
|
||||
|
||||
Ldata_do: mov r3, sp
|
||||
ldr r4, [r0] @ Get instruction
|
||||
mov r2, #0
|
||||
tst r4, #1 << 20 @ Check to see if it is a write instruction
|
||||
orreq r2, r2, #FAULT_CODE_WRITE @ Indicate write instruction
|
||||
mov r1, r4, lsr #22 @ Now branch to the relevent processing routine
|
||||
and r1, r1, #15 << 2
|
||||
add pc, pc, r1
|
||||
movs pc, lr
|
||||
b Ldata_unknown
|
||||
b Ldata_unknown
|
||||
b Ldata_unknown
|
||||
b Ldata_unknown
|
||||
b Ldata_ldrstr_post @ ldr rd, [rn], #m
|
||||
b Ldata_ldrstr_numindex @ ldr rd, [rn, #m] @ RegVal
|
||||
b Ldata_ldrstr_post @ ldr rd, [rn], rm
|
||||
b Ldata_ldrstr_regindex @ ldr rd, [rn, rm]
|
||||
b Ldata_ldmstm @ ldm*a rn, <rlist>
|
||||
b Ldata_ldmstm @ ldm*b rn, <rlist>
|
||||
b Ldata_unknown
|
||||
b Ldata_unknown
|
||||
b Ldata_ldrstr_post @ ldc rd, [rn], #m @ Same as ldr rd, [rn], #m
|
||||
b Ldata_ldcstc_pre @ ldc rd, [rn, #m]
|
||||
b Ldata_unknown
|
||||
Ldata_unknown: @ Part of jumptable
|
||||
mov r0, r1
|
||||
mov r1, r4
|
||||
mov r2, r3
|
||||
b baddataabort
|
||||
|
||||
Ldata_ldrstr_post:
|
||||
mov r0, r4, lsr #14 @ Get Rn
|
||||
and r0, r0, #15 << 2 @ Mask out reg.
|
||||
teq r0, #15 << 2
|
||||
ldr r0, [r3, r0] @ Get register
|
||||
biceq r0, r0, #PCMASK
|
||||
mov r1, r0
|
||||
#ifdef FAULT_CODE_LDRSTRPOST
|
||||
orr r2, r2, #FAULT_CODE_LDRSTRPOST
|
||||
#endif
|
||||
b do_DataAbort
|
||||
|
||||
Ldata_ldrstr_numindex:
|
||||
mov r0, r4, lsr #14 @ Get Rn
|
||||
and r0, r0, #15 << 2 @ Mask out reg.
|
||||
teq r0, #15 << 2
|
||||
ldr r0, [r3, r0] @ Get register
|
||||
mov r1, r4, lsl #20
|
||||
biceq r0, r0, #PCMASK
|
||||
tst r4, #1 << 23
|
||||
addne r0, r0, r1, lsr #20
|
||||
subeq r0, r0, r1, lsr #20
|
||||
mov r1, r0
|
||||
#ifdef FAULT_CODE_LDRSTRPRE
|
||||
orr r2, r2, #FAULT_CODE_LDRSTRPRE
|
||||
#endif
|
||||
b do_DataAbort
|
||||
|
||||
Ldata_ldrstr_regindex:
|
||||
mov r0, r4, lsr #14 @ Get Rn
|
||||
and r0, r0, #15 << 2 @ Mask out reg.
|
||||
teq r0, #15 << 2
|
||||
ldr r0, [r3, r0] @ Get register
|
||||
and r7, r4, #15
|
||||
biceq r0, r0, #PCMASK
|
||||
teq r7, #15 @ Check for PC
|
||||
ldr r7, [r3, r7, lsl #2] @ Get Rm
|
||||
and r8, r4, #0x60 @ Get shift types
|
||||
biceq r7, r7, #PCMASK
|
||||
mov r9, r4, lsr #7 @ Get shift amount
|
||||
and r9, r9, #31
|
||||
teq r8, #0
|
||||
moveq r7, r7, lsl r9
|
||||
teq r8, #0x20 @ LSR shift
|
||||
moveq r7, r7, lsr r9
|
||||
teq r8, #0x40 @ ASR shift
|
||||
moveq r7, r7, asr r9
|
||||
teq r8, #0x60 @ ROR shift
|
||||
moveq r7, r7, ror r9
|
||||
tst r4, #1 << 23
|
||||
addne r0, r0, r7
|
||||
subeq r0, r0, r7 @ Apply correction
|
||||
mov r1, r0
|
||||
#ifdef FAULT_CODE_LDRSTRREG
|
||||
orr r2, r2, #FAULT_CODE_LDRSTRREG
|
||||
#endif
|
||||
b do_DataAbort
|
||||
|
||||
Ldata_ldmstm:
|
||||
mov r7, #0x11
|
||||
orr r7, r7, r7, lsl #8
|
||||
and r0, r4, r7
|
||||
and r1, r4, r7, lsl #1
|
||||
add r0, r0, r1, lsr #1
|
||||
and r1, r4, r7, lsl #2
|
||||
add r0, r0, r1, lsr #2
|
||||
and r1, r4, r7, lsl #3
|
||||
add r0, r0, r1, lsr #3
|
||||
add r0, r0, r0, lsr #8
|
||||
add r0, r0, r0, lsr #4
|
||||
and r7, r0, #15 @ r7 = no. of registers to transfer.
|
||||
mov r5, r4, lsr #14 @ Get Rn
|
||||
and r5, r5, #15 << 2
|
||||
ldr r0, [r3, r5] @ Get reg
|
||||
eor r6, r4, r4, lsl #2
|
||||
tst r6, #1 << 23 @ Check inc/dec ^ writeback
|
||||
rsbeq r7, r7, #0
|
||||
add r7, r0, r7, lsl #2 @ Do correction (signed)
|
||||
subne r1, r7, #1
|
||||
subeq r1, r0, #1
|
||||
moveq r0, r7
|
||||
tst r4, #1 << 21 @ Check writeback
|
||||
strne r7, [r3, r5]
|
||||
eor r6, r4, r4, lsl #1
|
||||
tst r6, #1 << 24 @ Check Pre/Post ^ inc/dec
|
||||
addeq r0, r0, #4
|
||||
addeq r1, r1, #4
|
||||
teq r5, #15*4 @ CHECK FOR PC
|
||||
biceq r1, r1, #PCMASK
|
||||
biceq r0, r0, #PCMASK
|
||||
#ifdef FAULT_CODE_LDMSTM
|
||||
orr r2, r2, #FAULT_CODE_LDMSTM
|
||||
#endif
|
||||
b do_DataAbort
|
||||
|
||||
Ldata_ldcstc_pre:
|
||||
mov r0, r4, lsr #14 @ Get Rn
|
||||
and r0, r0, #15 << 2 @ Mask out reg.
|
||||
teq r0, #15 << 2
|
||||
ldr r0, [r3, r0] @ Get register
|
||||
mov r1, r4, lsl #24 @ Get offset
|
||||
biceq r0, r0, #PCMASK
|
||||
tst r4, #1 << 23
|
||||
addne r0, r0, r1, lsr #24
|
||||
subeq r0, r0, r1, lsr #24
|
||||
mov r1, r0
|
||||
#ifdef FAULT_CODE_LDCSTC
|
||||
orr r2, r2, #FAULT_CODE_LDCSTC
|
||||
#endif
|
||||
b do_DataAbort
|
||||
|
||||
|
||||
/*
|
||||
* This is the return code to user mode for abort handlers
|
||||
*/
|
||||
ENTRY(ret_from_exception)
|
||||
get_thread_info tsk
|
||||
mov why, #0
|
||||
b ret_to_user
|
||||
|
||||
.data
|
||||
ENTRY(fp_enter)
|
||||
.word fpe_not_present
|
||||
.text
|
||||
/*
|
||||
* Register switch for older 26-bit only ARMs
|
||||
*/
|
||||
ENTRY(__switch_to)
|
||||
add r0, r0, #TI_CPU_SAVE
|
||||
stmia r0, {r4 - sl, fp, sp, lr}
|
||||
add r1, r1, #TI_CPU_SAVE
|
||||
ldmia r1, {r4 - sl, fp, sp, pc}^
|
||||
|
||||
/*
|
||||
*=============================================================================
|
||||
* Low-level interface code
|
||||
*-----------------------------------------------------------------------------
|
||||
* Trap initialisation
|
||||
*-----------------------------------------------------------------------------
|
||||
*
|
||||
* Note - FIQ code has changed. The default is a couple of words in 0x1c, 0x20
|
||||
* that call _unexp_fiq. Nowever, we now copy the FIQ routine to 0x1c (removes
|
||||
* some excess cycles).
|
||||
*
|
||||
* What we need to put into 0-0x1c are branches to branch to the kernel.
|
||||
*/
|
||||
|
||||
.section ".init.text",#alloc,#execinstr
|
||||
|
||||
.Ljump_addresses:
|
||||
swi SYS_ERROR0
|
||||
.word vector_undefinstr - 12
|
||||
.word vector_swi - 16
|
||||
.word vector_prefetch - 20
|
||||
.word vector_data - 24
|
||||
.word vector_addrexcptn - 28
|
||||
.word vector_IRQ - 32
|
||||
.word _unexp_fiq - 36
|
||||
b . + 8
|
||||
/*
|
||||
* initialise the trap system
|
||||
*/
|
||||
ENTRY(__trap_init)
|
||||
stmfd sp!, {r4 - r7, lr}
|
||||
adr r1, .Ljump_addresses
|
||||
ldmia r1, {r1 - r7, ip, lr}
|
||||
orr r2, lr, r2, lsr #2
|
||||
orr r3, lr, r3, lsr #2
|
||||
orr r4, lr, r4, lsr #2
|
||||
orr r5, lr, r5, lsr #2
|
||||
orr r6, lr, r6, lsr #2
|
||||
orr r7, lr, r7, lsr #2
|
||||
orr ip, lr, ip, lsr #2
|
||||
mov r0, #0
|
||||
stmia r0, {r1 - r7, ip}
|
||||
ldmfd sp!, {r4 - r7, pc}^
|
||||
|
||||
.bss
|
||||
__temp_irq: .space 4 @ saved lr_irq
|
||||
__temp_fiq: .space 128
|
202
arch/arm26/kernel/fiq.c
Normal file
202
arch/arm26/kernel/fiq.c
Normal file
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* linux/arch/arm26/kernel/fiq.c
|
||||
*
|
||||
* Copyright (C) 1998 Russell King
|
||||
* Copyright (C) 1998, 1999 Phil Blundell
|
||||
* Copyright (C) 2003 Ian Molton
|
||||
*
|
||||
* FIQ support written by Philip Blundell <philb@gnu.org>, 1998.
|
||||
*
|
||||
* FIQ support re-written by Russell King to be more generic
|
||||
*
|
||||
* We now properly support a method by which the FIQ handlers can
|
||||
* be stacked onto the vector. We still do not support sharing
|
||||
* the FIQ vector itself.
|
||||
*
|
||||
* Operation is as follows:
|
||||
* 1. Owner A claims FIQ:
|
||||
* - default_fiq relinquishes control.
|
||||
* 2. Owner A:
|
||||
* - inserts code.
|
||||
* - sets any registers,
|
||||
* - enables FIQ.
|
||||
* 3. Owner B claims FIQ:
|
||||
* - if owner A has a relinquish function.
|
||||
* - disable FIQs.
|
||||
* - saves any registers.
|
||||
* - returns zero.
|
||||
* 4. Owner B:
|
||||
* - inserts code.
|
||||
* - sets any registers,
|
||||
* - enables FIQ.
|
||||
* 5. Owner B releases FIQ:
|
||||
* - Owner A is asked to reacquire FIQ:
|
||||
* - inserts code.
|
||||
* - restores saved registers.
|
||||
* - enables FIQ.
|
||||
* 6. Goto 3
|
||||
*/
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/mman.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
#include <asm/fiq.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/pgalloc.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#define FIQ_VECTOR (vectors_base() + 0x1c)
|
||||
|
||||
static unsigned long no_fiq_insn;
|
||||
|
||||
#define unprotect_page_0()
|
||||
#define protect_page_0()
|
||||
|
||||
/* Default reacquire function
|
||||
* - we always relinquish FIQ control
|
||||
* - we always reacquire FIQ control
|
||||
*/
|
||||
static int fiq_def_op(void *ref, int relinquish)
|
||||
{
|
||||
if (!relinquish) {
|
||||
unprotect_page_0();
|
||||
*(unsigned long *)FIQ_VECTOR = no_fiq_insn;
|
||||
protect_page_0();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct fiq_handler default_owner = {
|
||||
.name = "default",
|
||||
.fiq_op = fiq_def_op,
|
||||
};
|
||||
|
||||
static struct fiq_handler *current_fiq = &default_owner;
|
||||
|
||||
int show_fiq_list(struct seq_file *p, void *v)
|
||||
{
|
||||
if (current_fiq != &default_owner)
|
||||
seq_printf(p, "FIQ: %s\n", current_fiq->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void set_fiq_handler(void *start, unsigned int length)
|
||||
{
|
||||
unprotect_page_0();
|
||||
|
||||
memcpy((void *)FIQ_VECTOR, start, length);
|
||||
|
||||
protect_page_0();
|
||||
}
|
||||
|
||||
/*
|
||||
* Taking an interrupt in FIQ mode is death, so both these functions
|
||||
* disable irqs for the duration.
|
||||
*/
|
||||
void set_fiq_regs(struct pt_regs *regs)
|
||||
{
|
||||
register unsigned long tmp, tmp2;
|
||||
__asm__ volatile (
|
||||
"mov %0, pc
|
||||
bic %1, %0, #0x3
|
||||
orr %1, %1, %3
|
||||
teqp %1, #0 @ select FIQ mode
|
||||
mov r0, r0
|
||||
ldmia %2, {r8 - r14}
|
||||
teqp %0, #0 @ return to SVC mode
|
||||
mov r0, r0"
|
||||
: "=&r" (tmp), "=&r" (tmp2)
|
||||
: "r" (®s->ARM_r8), "I" (PSR_I_BIT | PSR_F_BIT | MODE_FIQ26)
|
||||
/* These registers aren't modified by the above code in a way
|
||||
visible to the compiler, but we mark them as clobbers anyway
|
||||
so that GCC won't put any of the input or output operands in
|
||||
them. */
|
||||
: "r8", "r9", "r10", "r11", "r12", "r13", "r14");
|
||||
}
|
||||
|
||||
void get_fiq_regs(struct pt_regs *regs)
|
||||
{
|
||||
register unsigned long tmp, tmp2;
|
||||
__asm__ volatile (
|
||||
"mov %0, pc
|
||||
bic %1, %0, #0x3
|
||||
orr %1, %1, %3
|
||||
teqp %1, #0 @ select FIQ mode
|
||||
mov r0, r0
|
||||
stmia %2, {r8 - r14}
|
||||
teqp %0, #0 @ return to SVC mode
|
||||
mov r0, r0"
|
||||
: "=&r" (tmp), "=&r" (tmp2)
|
||||
: "r" (®s->ARM_r8), "I" (PSR_I_BIT | PSR_F_BIT | MODE_FIQ26)
|
||||
/* These registers aren't modified by the above code in a way
|
||||
visible to the compiler, but we mark them as clobbers anyway
|
||||
so that GCC won't put any of the input or output operands in
|
||||
them. */
|
||||
: "r8", "r9", "r10", "r11", "r12", "r13", "r14");
|
||||
}
|
||||
|
||||
int claim_fiq(struct fiq_handler *f)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (current_fiq) {
|
||||
ret = -EBUSY;
|
||||
|
||||
if (current_fiq->fiq_op != NULL)
|
||||
ret = current_fiq->fiq_op(current_fiq->dev_id, 1);
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
f->next = current_fiq;
|
||||
current_fiq = f;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void release_fiq(struct fiq_handler *f)
|
||||
{
|
||||
if (current_fiq != f) {
|
||||
printk(KERN_ERR "%s FIQ trying to release %s FIQ\n",
|
||||
f->name, current_fiq->name);
|
||||
#ifdef CONFIG_DEBUG_ERRORS
|
||||
__backtrace();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
do
|
||||
current_fiq = current_fiq->next;
|
||||
while (current_fiq->fiq_op(current_fiq->dev_id, 0));
|
||||
}
|
||||
|
||||
void enable_fiq(int fiq)
|
||||
{
|
||||
enable_irq(fiq + FIQ_START);
|
||||
}
|
||||
|
||||
void disable_fiq(int fiq)
|
||||
{
|
||||
disable_irq(fiq + FIQ_START);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(set_fiq_handler);
|
||||
EXPORT_SYMBOL(set_fiq_regs);
|
||||
EXPORT_SYMBOL(get_fiq_regs);
|
||||
EXPORT_SYMBOL(claim_fiq);
|
||||
EXPORT_SYMBOL(release_fiq);
|
||||
EXPORT_SYMBOL(enable_fiq);
|
||||
EXPORT_SYMBOL(disable_fiq);
|
||||
|
||||
void __init init_FIQ(void)
|
||||
{
|
||||
no_fiq_insn = *(unsigned long *)FIQ_VECTOR;
|
||||
set_fs(get_fs());
|
||||
}
|
113
arch/arm26/kernel/head.S
Normal file
113
arch/arm26/kernel/head.S
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* linux/arch/arm26/kernel/head.S
|
||||
*
|
||||
* Copyright (C) 1994-2000 Russell King
|
||||
* Copyright (C) 2003 Ian Molton
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 26-bit kernel startup code
|
||||
*/
|
||||
#include <linux/config.h>
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/mach-types.h>
|
||||
|
||||
.globl swapper_pg_dir
|
||||
.equ swapper_pg_dir, 0x0207d000
|
||||
|
||||
/*
|
||||
* Entry point.
|
||||
*/
|
||||
.section ".init.text",#alloc,#execinstr
|
||||
ENTRY(stext)
|
||||
|
||||
__entry:
|
||||
cmp pc, #0x02000000
|
||||
ldrlt pc, LC0 @ if 0x01800000, call at 0x02080000
|
||||
teq r0, #0 @ Check for old calling method
|
||||
blne oldparams @ Move page if old
|
||||
|
||||
adr r0, LC0
|
||||
ldmib r0, {r2-r5, sp} @ Setup stack (and fetch other values)
|
||||
|
||||
mov r0, #0 @ Clear BSS
|
||||
1: cmp r2, r3
|
||||
strcc r0, [r2], #4
|
||||
bcc 1b
|
||||
|
||||
bl detect_proc_type
|
||||
str r0, [r4]
|
||||
bl detect_arch_type
|
||||
str r0, [r5]
|
||||
|
||||
#ifdef CONFIG_XIP_KERNEL
|
||||
ldr r3, ETEXT @ data section copy
|
||||
ldr r4, SDATA
|
||||
ldr r5, EDATA
|
||||
1:
|
||||
ldr r6, [r3], #4
|
||||
str r6, [r4], #4
|
||||
cmp r4, r5
|
||||
blt 1b
|
||||
#endif
|
||||
mov fp, #0
|
||||
b start_kernel
|
||||
|
||||
LC0: .word _stext
|
||||
.word __bss_start @ r2
|
||||
.word _end @ r3
|
||||
.word processor_id @ r4
|
||||
.word __machine_arch_type @ r5
|
||||
.word init_thread_union+8192 @ sp
|
||||
#ifdef CONFIG_XIP_KERNEL
|
||||
ETEXT: .word _endtext
|
||||
SDATA: .word _sdata
|
||||
EDATA: .word __bss_start
|
||||
#endif
|
||||
|
||||
arm2_id: .long 0x41560200 @ ARM2 and 250 dont have a CPUID
|
||||
arm250_id: .long 0x41560250 @ So we create some after probing for them
|
||||
.align
|
||||
|
||||
oldparams: mov r4, #0x02000000
|
||||
add r3, r4, #0x00080000
|
||||
add r4, r4, #0x0007c000
|
||||
1: ldmia r0!, {r5 - r12}
|
||||
stmia r4!, {r5 - r12}
|
||||
cmp r4, r3
|
||||
blt 1b
|
||||
mov pc, lr
|
||||
|
||||
/*
|
||||
* We need some way to automatically detect the difference between
|
||||
* these two machines. Unfortunately, it is not possible to detect
|
||||
* the presence of the SuperIO chip, because that will hang the old
|
||||
* Archimedes machines solid.
|
||||
*/
|
||||
/* DAG: Outdated, these have been combined !!!!!!! */
|
||||
detect_arch_type:
|
||||
#if defined(CONFIG_ARCH_ARC)
|
||||
mov r0, #MACH_TYPE_ARCHIMEDES
|
||||
#elif defined(CONFIG_ARCH_A5K)
|
||||
mov r0, #MACH_TYPE_A5K
|
||||
#endif
|
||||
mov pc, lr
|
||||
|
||||
detect_proc_type:
|
||||
mov ip, lr
|
||||
mov r2, #0xea000000 @ Point undef instr to continuation
|
||||
adr r0, continue - 12
|
||||
orr r0, r2, r0, lsr #2
|
||||
mov r1, #0
|
||||
str r0, [r1, #4]
|
||||
ldr r0, arm2_id
|
||||
swp r2, r2, [r1] @ check for swp (ARM2 cant)
|
||||
ldr r0, arm250_id
|
||||
mrc 15, 0, r3, c0, c0 @ check for CP#15 (ARM250 cant)
|
||||
mov r0, r3
|
||||
continue: mov r2, #0xeb000000 @ Make undef vector loop
|
||||
sub r2, r2, #2
|
||||
str r2, [r1, #4]
|
||||
mov pc, ip
|
49
arch/arm26/kernel/init_task.c
Normal file
49
arch/arm26/kernel/init_task.c
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* linux/arch/arm26/kernel/init_task.c
|
||||
*
|
||||
* Copyright (C) 2003 Ian Molton
|
||||
*
|
||||
*/
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/init_task.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 8192-byte aligned due to the
|
||||
* way process stacks are handled. This is done by making sure
|
||||
* the linker maps this in the .text segment right after head.S,
|
||||
* and making the linker scripts ensure the proper alignment.
|
||||
*
|
||||
* FIXME - should this be 32K alignment on arm26?
|
||||
*
|
||||
* The things we do for performance...
|
||||
*/
|
||||
union thread_union init_thread_union
|
||||
__attribute__((__section__(".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);
|
716
arch/arm26/kernel/irq.c
Normal file
716
arch/arm26/kernel/irq.c
Normal file
@@ -0,0 +1,716 @@
|
||||
/*
|
||||
* linux/arch/arm/kernel/irq.c
|
||||
*
|
||||
* Copyright (C) 1992 Linus Torvalds
|
||||
* Modifications for ARM processor Copyright (C) 1995-2000 Russell King.
|
||||
* 'Borrowed' for ARM26 and (C) 2003 Ian Molton.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This file contains the code used by various IRQ handling routines:
|
||||
* asking for different IRQ's should be done through these routines
|
||||
* instead of just grabbing them. Thus setups with different IRQ numbers
|
||||
* shouldn't result in any weird surprises, and installing new handlers
|
||||
* should be easier.
|
||||
*
|
||||
* IRQ's are in fact implemented a bit like signal handlers for the kernel.
|
||||
* Naturally it's not a 1:1 relation, but there are similarities.
|
||||
*/
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/kernel_stat.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/errno.h>
|
||||
|
||||
#include <asm/irq.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/irqchip.h>
|
||||
|
||||
//FIXME - this ought to be in a header IMO
|
||||
void __init arc_init_irq(void);
|
||||
|
||||
/*
|
||||
* Maximum IRQ count. Currently, this is arbitary. However, it should
|
||||
* not be set too low to prevent false triggering. Conversely, if it
|
||||
* is set too high, then you could miss a stuck IRQ.
|
||||
*
|
||||
* FIXME Maybe we ought to set a timer and re-enable the IRQ at a later time?
|
||||
*/
|
||||
#define MAX_IRQ_CNT 100000
|
||||
|
||||
static volatile unsigned long irq_err_count;
|
||||
static DEFINE_SPINLOCK(irq_controller_lock);
|
||||
|
||||
struct irqdesc irq_desc[NR_IRQS];
|
||||
|
||||
/*
|
||||
* Dummy mask/unmask handler
|
||||
*/
|
||||
void dummy_mask_unmask_irq(unsigned int irq)
|
||||
{
|
||||
}
|
||||
|
||||
void do_bad_IRQ(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs)
|
||||
{
|
||||
irq_err_count += 1;
|
||||
printk(KERN_ERR "IRQ: spurious interrupt %d\n", irq);
|
||||
}
|
||||
|
||||
static struct irqchip bad_chip = {
|
||||
.ack = dummy_mask_unmask_irq,
|
||||
.mask = dummy_mask_unmask_irq,
|
||||
.unmask = dummy_mask_unmask_irq,
|
||||
};
|
||||
|
||||
static struct irqdesc bad_irq_desc = {
|
||||
.chip = &bad_chip,
|
||||
.handle = do_bad_IRQ,
|
||||
.depth = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* disable_irq - disable an irq and wait for completion
|
||||
* @irq: Interrupt to disable
|
||||
*
|
||||
* Disable the selected interrupt line. We do this lazily.
|
||||
*
|
||||
* This function may be called from IRQ context.
|
||||
*/
|
||||
void disable_irq(unsigned int irq)
|
||||
{
|
||||
struct irqdesc *desc = irq_desc + irq;
|
||||
unsigned long flags;
|
||||
spin_lock_irqsave(&irq_controller_lock, flags);
|
||||
if (!desc->depth++)
|
||||
desc->enabled = 0;
|
||||
spin_unlock_irqrestore(&irq_controller_lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* enable_irq - enable interrupt handling on an irq
|
||||
* @irq: Interrupt to enable
|
||||
*
|
||||
* Re-enables the processing of interrupts on this IRQ line.
|
||||
* Note that this may call the interrupt handler, so you may
|
||||
* get unexpected results if you hold IRQs disabled.
|
||||
*
|
||||
* This function may be called from IRQ context.
|
||||
*/
|
||||
void enable_irq(unsigned int irq)
|
||||
{
|
||||
struct irqdesc *desc = irq_desc + irq;
|
||||
unsigned long flags;
|
||||
int pending = 0;
|
||||
|
||||
spin_lock_irqsave(&irq_controller_lock, flags);
|
||||
if (unlikely(!desc->depth)) {
|
||||
printk("enable_irq(%u) unbalanced from %p\n", irq,
|
||||
__builtin_return_address(0)); //FIXME bum addresses reported - why?
|
||||
} else if (!--desc->depth) {
|
||||
desc->probing = 0;
|
||||
desc->enabled = 1;
|
||||
desc->chip->unmask(irq);
|
||||
pending = desc->pending;
|
||||
desc->pending = 0;
|
||||
/*
|
||||
* If the interrupt was waiting to be processed,
|
||||
* retrigger it.
|
||||
*/
|
||||
if (pending)
|
||||
desc->chip->rerun(irq);
|
||||
}
|
||||
spin_unlock_irqrestore(&irq_controller_lock, flags);
|
||||
}
|
||||
|
||||
int show_interrupts(struct seq_file *p, void *v)
|
||||
{
|
||||
int i = *(loff_t *) v;
|
||||
struct irqaction * action;
|
||||
|
||||
if (i < NR_IRQS) {
|
||||
action = irq_desc[i].action;
|
||||
if (!action)
|
||||
continue;
|
||||
seq_printf(p, "%3d: %10u ", i, kstat_irqs(i));
|
||||
seq_printf(p, " %s", action->name);
|
||||
for (action = action->next; action; action = action->next) {
|
||||
seq_printf(p, ", %s", action->name);
|
||||
}
|
||||
seq_putc(p, '\n');
|
||||
} else if (i == NR_IRQS) {
|
||||
show_fiq_list(p, v);
|
||||
seq_printf(p, "Err: %10lu\n", irq_err_count);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* IRQ lock detection.
|
||||
*
|
||||
* Hopefully, this should get us out of a few locked situations.
|
||||
* However, it may take a while for this to happen, since we need
|
||||
* a large number if IRQs to appear in the same jiffie with the
|
||||
* same instruction pointer (or within 2 instructions).
|
||||
*/
|
||||
static int check_irq_lock(struct irqdesc *desc, int irq, struct pt_regs *regs)
|
||||
{
|
||||
unsigned long instr_ptr = instruction_pointer(regs);
|
||||
|
||||
if (desc->lck_jif == jiffies &&
|
||||
desc->lck_pc >= instr_ptr && desc->lck_pc < instr_ptr + 8) {
|
||||
desc->lck_cnt += 1;
|
||||
|
||||
if (desc->lck_cnt > MAX_IRQ_CNT) {
|
||||
printk(KERN_ERR "IRQ LOCK: IRQ%d is locking the system, disabled\n", irq);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
desc->lck_cnt = 0;
|
||||
desc->lck_pc = instruction_pointer(regs);
|
||||
desc->lck_jif = jiffies;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
__do_irq(unsigned int irq, struct irqaction *action, struct pt_regs *regs)
|
||||
{
|
||||
unsigned int status;
|
||||
int ret;
|
||||
|
||||
spin_unlock(&irq_controller_lock);
|
||||
if (!(action->flags & SA_INTERRUPT))
|
||||
local_irq_enable();
|
||||
|
||||
status = 0;
|
||||
do {
|
||||
ret = action->handler(irq, action->dev_id, regs);
|
||||
if (ret == IRQ_HANDLED)
|
||||
status |= action->flags;
|
||||
action = action->next;
|
||||
} while (action);
|
||||
|
||||
if (status & SA_SAMPLE_RANDOM)
|
||||
add_interrupt_randomness(irq);
|
||||
|
||||
spin_lock_irq(&irq_controller_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is for software-decoded IRQs. The caller is expected to
|
||||
* handle the ack, clear, mask and unmask issues.
|
||||
*/
|
||||
void
|
||||
do_simple_IRQ(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs)
|
||||
{
|
||||
struct irqaction *action;
|
||||
const int cpu = smp_processor_id();
|
||||
|
||||
desc->triggered = 1;
|
||||
|
||||
kstat_cpu(cpu).irqs[irq]++;
|
||||
|
||||
action = desc->action;
|
||||
if (action)
|
||||
__do_irq(irq, desc->action, regs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Most edge-triggered IRQ implementations seem to take a broken
|
||||
* approach to this. Hence the complexity.
|
||||
*/
|
||||
void
|
||||
do_edge_IRQ(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs)
|
||||
{
|
||||
const int cpu = smp_processor_id();
|
||||
|
||||
desc->triggered = 1;
|
||||
|
||||
/*
|
||||
* If we're currently running this IRQ, or its disabled,
|
||||
* we shouldn't process the IRQ. Instead, turn on the
|
||||
* hardware masks.
|
||||
*/
|
||||
if (unlikely(desc->running || !desc->enabled))
|
||||
goto running;
|
||||
|
||||
/*
|
||||
* Acknowledge and clear the IRQ, but don't mask it.
|
||||
*/
|
||||
desc->chip->ack(irq);
|
||||
|
||||
/*
|
||||
* Mark the IRQ currently in progress.
|
||||
*/
|
||||
desc->running = 1;
|
||||
|
||||
kstat_cpu(cpu).irqs[irq]++;
|
||||
|
||||
do {
|
||||
struct irqaction *action;
|
||||
|
||||
action = desc->action;
|
||||
if (!action)
|
||||
break;
|
||||
|
||||
if (desc->pending && desc->enabled) {
|
||||
desc->pending = 0;
|
||||
desc->chip->unmask(irq);
|
||||
}
|
||||
|
||||
__do_irq(irq, action, regs);
|
||||
} while (desc->pending);
|
||||
|
||||
desc->running = 0;
|
||||
|
||||
/*
|
||||
* If we were disabled or freed, shut down the handler.
|
||||
*/
|
||||
if (likely(desc->action && !check_irq_lock(desc, irq, regs)))
|
||||
return;
|
||||
|
||||
running:
|
||||
/*
|
||||
* We got another IRQ while this one was masked or
|
||||
* currently running. Delay it.
|
||||
*/
|
||||
desc->pending = 1;
|
||||
desc->chip->mask(irq);
|
||||
desc->chip->ack(irq);
|
||||
}
|
||||
|
||||
/*
|
||||
* Level-based IRQ handler. Nice and simple.
|
||||
*/
|
||||
void
|
||||
do_level_IRQ(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs)
|
||||
{
|
||||
struct irqaction *action;
|
||||
const int cpu = smp_processor_id();
|
||||
|
||||
desc->triggered = 1;
|
||||
|
||||
/*
|
||||
* Acknowledge, clear _AND_ disable the interrupt.
|
||||
*/
|
||||
desc->chip->ack(irq);
|
||||
|
||||
if (likely(desc->enabled)) {
|
||||
kstat_cpu(cpu).irqs[irq]++;
|
||||
|
||||
/*
|
||||
* Return with this interrupt masked if no action
|
||||
*/
|
||||
action = desc->action;
|
||||
if (action) {
|
||||
__do_irq(irq, desc->action, regs);
|
||||
|
||||
if (likely(desc->enabled &&
|
||||
!check_irq_lock(desc, irq, regs)))
|
||||
desc->chip->unmask(irq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* do_IRQ handles all hardware IRQ's. Decoded IRQs should not
|
||||
* come via this function. Instead, they should provide their
|
||||
* own 'handler'
|
||||
*/
|
||||
asmlinkage void asm_do_IRQ(int irq, struct pt_regs *regs)
|
||||
{
|
||||
struct irqdesc *desc = irq_desc + irq;
|
||||
|
||||
/*
|
||||
* Some hardware gives randomly wrong interrupts. Rather
|
||||
* than crashing, do something sensible.
|
||||
*/
|
||||
if (irq >= NR_IRQS)
|
||||
desc = &bad_irq_desc;
|
||||
|
||||
irq_enter();
|
||||
spin_lock(&irq_controller_lock);
|
||||
desc->handle(irq, desc, regs);
|
||||
spin_unlock(&irq_controller_lock);
|
||||
irq_exit();
|
||||
}
|
||||
|
||||
void __set_irq_handler(unsigned int irq, irq_handler_t handle, int is_chained)
|
||||
{
|
||||
struct irqdesc *desc;
|
||||
unsigned long flags;
|
||||
|
||||
if (irq >= NR_IRQS) {
|
||||
printk(KERN_ERR "Trying to install handler for IRQ%d\n", irq);
|
||||
return;
|
||||
}
|
||||
|
||||
if (handle == NULL)
|
||||
handle = do_bad_IRQ;
|
||||
|
||||
desc = irq_desc + irq;
|
||||
|
||||
if (is_chained && desc->chip == &bad_chip)
|
||||
printk(KERN_WARNING "Trying to install chained handler for IRQ%d\n", irq);
|
||||
|
||||
spin_lock_irqsave(&irq_controller_lock, flags);
|
||||
if (handle == do_bad_IRQ) {
|
||||
desc->chip->mask(irq);
|
||||
desc->chip->ack(irq);
|
||||
desc->depth = 1;
|
||||
desc->enabled = 0;
|
||||
}
|
||||
desc->handle = handle;
|
||||
if (handle != do_bad_IRQ && is_chained) {
|
||||
desc->valid = 0;
|
||||
desc->probe_ok = 0;
|
||||
desc->depth = 0;
|
||||
desc->chip->unmask(irq);
|
||||
}
|
||||
spin_unlock_irqrestore(&irq_controller_lock, flags);
|
||||
}
|
||||
|
||||
void set_irq_chip(unsigned int irq, struct irqchip *chip)
|
||||
{
|
||||
struct irqdesc *desc;
|
||||
unsigned long flags;
|
||||
|
||||
if (irq >= NR_IRQS) {
|
||||
printk(KERN_ERR "Trying to install chip for IRQ%d\n", irq);
|
||||
return;
|
||||
}
|
||||
|
||||
if (chip == NULL)
|
||||
chip = &bad_chip;
|
||||
|
||||
desc = irq_desc + irq;
|
||||
spin_lock_irqsave(&irq_controller_lock, flags);
|
||||
desc->chip = chip;
|
||||
spin_unlock_irqrestore(&irq_controller_lock, flags);
|
||||
}
|
||||
|
||||
int set_irq_type(unsigned int irq, unsigned int type)
|
||||
{
|
||||
struct irqdesc *desc;
|
||||
unsigned long flags;
|
||||
int ret = -ENXIO;
|
||||
|
||||
if (irq >= NR_IRQS) {
|
||||
printk(KERN_ERR "Trying to set irq type for IRQ%d\n", irq);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
desc = irq_desc + irq;
|
||||
if (desc->chip->type) {
|
||||
spin_lock_irqsave(&irq_controller_lock, flags);
|
||||
ret = desc->chip->type(irq, type);
|
||||
spin_unlock_irqrestore(&irq_controller_lock, flags);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void set_irq_flags(unsigned int irq, unsigned int iflags)
|
||||
{
|
||||
struct irqdesc *desc;
|
||||
unsigned long flags;
|
||||
|
||||
if (irq >= NR_IRQS) {
|
||||
printk(KERN_ERR "Trying to set irq flags for IRQ%d\n", irq);
|
||||
return;
|
||||
}
|
||||
|
||||
desc = irq_desc + irq;
|
||||
spin_lock_irqsave(&irq_controller_lock, flags);
|
||||
desc->valid = (iflags & IRQF_VALID) != 0;
|
||||
desc->probe_ok = (iflags & IRQF_PROBE) != 0;
|
||||
desc->noautoenable = (iflags & IRQF_NOAUTOEN) != 0;
|
||||
spin_unlock_irqrestore(&irq_controller_lock, flags);
|
||||
}
|
||||
|
||||
int setup_irq(unsigned int irq, struct irqaction *new)
|
||||
{
|
||||
int shared = 0;
|
||||
struct irqaction *old, **p;
|
||||
unsigned long flags;
|
||||
struct irqdesc *desc;
|
||||
|
||||
/*
|
||||
* Some drivers like serial.c use request_irq() heavily,
|
||||
* so we have to be careful not to interfere with a
|
||||
* running system.
|
||||
*/
|
||||
if (new->flags & SA_SAMPLE_RANDOM) {
|
||||
/*
|
||||
* This function might sleep, we want to call it first,
|
||||
* outside of the atomic block.
|
||||
* Yes, this might clear the entropy pool if the wrong
|
||||
* driver is attempted to be loaded, without actually
|
||||
* installing a new handler, but is this really a problem,
|
||||
* only the sysadmin is able to do this.
|
||||
*/
|
||||
rand_initialize_irq(irq);
|
||||
}
|
||||
|
||||
/*
|
||||
* The following block of code has to be executed atomically
|
||||
*/
|
||||
desc = irq_desc + irq;
|
||||
spin_lock_irqsave(&irq_controller_lock, flags);
|
||||
p = &desc->action;
|
||||
if ((old = *p) != NULL) {
|
||||
/* Can't share interrupts unless both agree to */
|
||||
if (!(old->flags & new->flags & SA_SHIRQ)) {
|
||||
spin_unlock_irqrestore(&irq_controller_lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* add new interrupt at end of irq queue */
|
||||
do {
|
||||
p = &old->next;
|
||||
old = *p;
|
||||
} while (old);
|
||||
shared = 1;
|
||||
}
|
||||
|
||||
*p = new;
|
||||
|
||||
if (!shared) {
|
||||
desc->probing = 0;
|
||||
desc->running = 0;
|
||||
desc->pending = 0;
|
||||
desc->depth = 1;
|
||||
if (!desc->noautoenable) {
|
||||
desc->depth = 0;
|
||||
desc->enabled = 1;
|
||||
desc->chip->unmask(irq);
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&irq_controller_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* request_irq - allocate an interrupt line
|
||||
* @irq: Interrupt line to allocate
|
||||
* @handler: Function to be called when the IRQ occurs
|
||||
* @irqflags: Interrupt type flags
|
||||
* @devname: An ascii name for the claiming device
|
||||
* @dev_id: A cookie passed back to the handler function
|
||||
*
|
||||
* This call allocates interrupt resources and enables the
|
||||
* interrupt line and IRQ handling. From the point this
|
||||
* call is made your handler function may be invoked. Since
|
||||
* your handler function must clear any interrupt the board
|
||||
* raises, you must take care both to initialise your hardware
|
||||
* and to set up the interrupt handler in the right order.
|
||||
*
|
||||
* Dev_id must be globally unique. Normally the address of the
|
||||
* device data structure is used as the cookie. Since the handler
|
||||
* receives this value it makes sense to use it.
|
||||
*
|
||||
* If your interrupt is shared you must pass a non NULL dev_id
|
||||
* as this is required when freeing the interrupt.
|
||||
*
|
||||
* Flags:
|
||||
*
|
||||
* SA_SHIRQ Interrupt is shared
|
||||
*
|
||||
* SA_INTERRUPT Disable local interrupts while processing
|
||||
*
|
||||
* SA_SAMPLE_RANDOM The interrupt can be used for entropy
|
||||
*
|
||||
*/
|
||||
|
||||
//FIXME - handler used to return void - whats the significance of the change?
|
||||
int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *),
|
||||
unsigned long irq_flags, const char * devname, void *dev_id)
|
||||
{
|
||||
unsigned long retval;
|
||||
struct irqaction *action;
|
||||
|
||||
if (irq >= NR_IRQS || !irq_desc[irq].valid || !handler ||
|
||||
(irq_flags & SA_SHIRQ && !dev_id))
|
||||
return -EINVAL;
|
||||
|
||||
action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL);
|
||||
if (!action)
|
||||
return -ENOMEM;
|
||||
|
||||
action->handler = handler;
|
||||
action->flags = irq_flags;
|
||||
cpus_clear(action->mask);
|
||||
action->name = devname;
|
||||
action->next = NULL;
|
||||
action->dev_id = dev_id;
|
||||
|
||||
retval = setup_irq(irq, action);
|
||||
|
||||
if (retval)
|
||||
kfree(action);
|
||||
return retval;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(request_irq);
|
||||
|
||||
/**
|
||||
* free_irq - free an interrupt
|
||||
* @irq: Interrupt line to free
|
||||
* @dev_id: Device identity to free
|
||||
*
|
||||
* Remove an interrupt handler. The handler is removed and if the
|
||||
* interrupt line is no longer in use by any driver it is disabled.
|
||||
* On a shared IRQ the caller must ensure the interrupt is disabled
|
||||
* on the card it drives before calling this function.
|
||||
*
|
||||
* This function may be called from interrupt context.
|
||||
*/
|
||||
void free_irq(unsigned int irq, void *dev_id)
|
||||
{
|
||||
struct irqaction * action, **p;
|
||||
unsigned long flags;
|
||||
|
||||
if (irq >= NR_IRQS || !irq_desc[irq].valid) {
|
||||
printk(KERN_ERR "Trying to free IRQ%d\n",irq);
|
||||
#ifdef CONFIG_DEBUG_ERRORS
|
||||
__backtrace();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&irq_controller_lock, flags);
|
||||
for (p = &irq_desc[irq].action; (action = *p) != NULL; p = &action->next) {
|
||||
if (action->dev_id != dev_id)
|
||||
continue;
|
||||
|
||||
/* Found it - now free it */
|
||||
*p = action->next;
|
||||
kfree(action);
|
||||
goto out;
|
||||
}
|
||||
printk(KERN_ERR "Trying to free free IRQ%d\n",irq);
|
||||
#ifdef CONFIG_DEBUG_ERRORS
|
||||
__backtrace();
|
||||
#endif
|
||||
out:
|
||||
spin_unlock_irqrestore(&irq_controller_lock, flags);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(free_irq);
|
||||
|
||||
/* Start the interrupt probing. Unlike other architectures,
|
||||
* we don't return a mask of interrupts from probe_irq_on,
|
||||
* but return the number of interrupts enabled for the probe.
|
||||
* The interrupts which have been enabled for probing is
|
||||
* instead recorded in the irq_desc structure.
|
||||
*/
|
||||
unsigned long probe_irq_on(void)
|
||||
{
|
||||
unsigned int i, irqs = 0;
|
||||
unsigned long delay;
|
||||
|
||||
/*
|
||||
* first snaffle up any unassigned but
|
||||
* probe-able interrupts
|
||||
*/
|
||||
spin_lock_irq(&irq_controller_lock);
|
||||
for (i = 0; i < NR_IRQS; i++) {
|
||||
if (!irq_desc[i].probe_ok || irq_desc[i].action)
|
||||
continue;
|
||||
|
||||
irq_desc[i].probing = 1;
|
||||
irq_desc[i].triggered = 0;
|
||||
if (irq_desc[i].chip->type)
|
||||
irq_desc[i].chip->type(i, IRQT_PROBE);
|
||||
irq_desc[i].chip->unmask(i);
|
||||
irqs += 1;
|
||||
}
|
||||
spin_unlock_irq(&irq_controller_lock);
|
||||
|
||||
/*
|
||||
* wait for spurious interrupts to mask themselves out again
|
||||
*/
|
||||
for (delay = jiffies + HZ/10; time_before(jiffies, delay); )
|
||||
/* min 100ms delay */;
|
||||
|
||||
/*
|
||||
* now filter out any obviously spurious interrupts
|
||||
*/
|
||||
spin_lock_irq(&irq_controller_lock);
|
||||
for (i = 0; i < NR_IRQS; i++) {
|
||||
if (irq_desc[i].probing && irq_desc[i].triggered) {
|
||||
irq_desc[i].probing = 0;
|
||||
irqs -= 1;
|
||||
}
|
||||
}
|
||||
spin_unlock_irq(&irq_controller_lock);
|
||||
|
||||
return irqs;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(probe_irq_on);
|
||||
|
||||
/*
|
||||
* Possible return values:
|
||||
* >= 0 - interrupt number
|
||||
* -1 - no interrupt/many interrupts
|
||||
*/
|
||||
int probe_irq_off(unsigned long irqs)
|
||||
{
|
||||
unsigned int i;
|
||||
int irq_found = NO_IRQ;
|
||||
|
||||
/*
|
||||
* look at the interrupts, and find exactly one
|
||||
* that we were probing has been triggered
|
||||
*/
|
||||
spin_lock_irq(&irq_controller_lock);
|
||||
for (i = 0; i < NR_IRQS; i++) {
|
||||
if (irq_desc[i].probing &&
|
||||
irq_desc[i].triggered) {
|
||||
if (irq_found != NO_IRQ) {
|
||||
irq_found = NO_IRQ;
|
||||
goto out;
|
||||
}
|
||||
irq_found = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (irq_found == -1)
|
||||
irq_found = NO_IRQ;
|
||||
out:
|
||||
spin_unlock_irq(&irq_controller_lock);
|
||||
|
||||
return irq_found;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(probe_irq_off);
|
||||
|
||||
void __init init_irq_proc(void)
|
||||
{
|
||||
}
|
||||
|
||||
void __init init_IRQ(void)
|
||||
{
|
||||
struct irqdesc *desc;
|
||||
extern void init_dma(void);
|
||||
int irq;
|
||||
|
||||
for (irq = 0, desc = irq_desc; irq < NR_IRQS; irq++, desc++)
|
||||
*desc = bad_irq_desc;
|
||||
|
||||
arc_init_irq();
|
||||
init_dma();
|
||||
}
|
401
arch/arm26/kernel/process.c
Normal file
401
arch/arm26/kernel/process.c
Normal file
@@ -0,0 +1,401 @@
|
||||
/*
|
||||
* linux/arch/arm26/kernel/process.c
|
||||
*
|
||||
* Copyright (C) 2003 Ian Molton - adapted for ARM26
|
||||
* Copyright (C) 1996-2000 Russell King - Converted to ARM.
|
||||
* Origional Copyright (C) 1995 Linus Torvalds
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.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/delay.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/system.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/leds.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
extern const char *processor_modes[];
|
||||
extern void setup_mm_for_reboot(char mode);
|
||||
|
||||
static volatile int hlt_counter;
|
||||
|
||||
void disable_hlt(void)
|
||||
{
|
||||
hlt_counter++;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(disable_hlt);
|
||||
|
||||
void enable_hlt(void)
|
||||
{
|
||||
hlt_counter--;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(enable_hlt);
|
||||
|
||||
static int __init nohlt_setup(char *__unused)
|
||||
{
|
||||
hlt_counter = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __init hlt_setup(char *__unused)
|
||||
{
|
||||
hlt_counter = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("nohlt", nohlt_setup);
|
||||
__setup("hlt", hlt_setup);
|
||||
|
||||
/*
|
||||
* This is our default idle handler. We need to disable
|
||||
* interrupts here to ensure we don't miss a wakeup call.
|
||||
*/
|
||||
void cpu_idle(void)
|
||||
{
|
||||
/* endless idle loop with no priority at all */
|
||||
preempt_disable();
|
||||
while (1) {
|
||||
while (!need_resched()) {
|
||||
local_irq_disable();
|
||||
if (!need_resched() && !hlt_counter)
|
||||
local_irq_enable();
|
||||
}
|
||||
}
|
||||
schedule();
|
||||
}
|
||||
|
||||
static char reboot_mode = 'h';
|
||||
|
||||
int __init reboot_setup(char *str)
|
||||
{
|
||||
reboot_mode = str[0];
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("reboot=", reboot_setup);
|
||||
|
||||
/* ARM26 cant do these but we still need to define them. */
|
||||
void machine_halt(void)
|
||||
{
|
||||
}
|
||||
void machine_power_off(void)
|
||||
{
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(machine_halt);
|
||||
EXPORT_SYMBOL(machine_power_off);
|
||||
|
||||
void machine_restart(char * __unused)
|
||||
{
|
||||
/*
|
||||
* Clean and disable cache, and turn off interrupts
|
||||
*/
|
||||
cpu_proc_fin();
|
||||
|
||||
/*
|
||||
* Tell the mm system that we are going to reboot -
|
||||
* we may need it to insert some 1:1 mappings so that
|
||||
* soft boot works.
|
||||
*/
|
||||
setup_mm_for_reboot(reboot_mode);
|
||||
|
||||
/*
|
||||
* copy branch instruction to reset location and call it
|
||||
*/
|
||||
|
||||
*(unsigned long *)0 = *(unsigned long *)0x03800000;
|
||||
((void(*)(void))0)();
|
||||
|
||||
/*
|
||||
* Whoops - the architecture was unable to reboot.
|
||||
* Tell the user! Should never happen...
|
||||
*/
|
||||
mdelay(1000);
|
||||
printk("Reboot failed -- System halted\n");
|
||||
while (1);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(machine_restart);
|
||||
|
||||
void show_regs(struct pt_regs * regs)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
flags = condition_codes(regs);
|
||||
|
||||
printk("pc : [<%08lx>] lr : [<%08lx>] %s\n"
|
||||
"sp : %08lx ip : %08lx fp : %08lx\n",
|
||||
instruction_pointer(regs),
|
||||
regs->ARM_lr, print_tainted(), regs->ARM_sp,
|
||||
regs->ARM_ip, regs->ARM_fp);
|
||||
printk("r10: %08lx r9 : %08lx r8 : %08lx\n",
|
||||
regs->ARM_r10, regs->ARM_r9,
|
||||
regs->ARM_r8);
|
||||
printk("r7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx\n",
|
||||
regs->ARM_r7, regs->ARM_r6,
|
||||
regs->ARM_r5, regs->ARM_r4);
|
||||
printk("r3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx\n",
|
||||
regs->ARM_r3, regs->ARM_r2,
|
||||
regs->ARM_r1, regs->ARM_r0);
|
||||
printk("Flags: %c%c%c%c",
|
||||
flags & PSR_N_BIT ? 'N' : 'n',
|
||||
flags & PSR_Z_BIT ? 'Z' : 'z',
|
||||
flags & PSR_C_BIT ? 'C' : 'c',
|
||||
flags & PSR_V_BIT ? 'V' : 'v');
|
||||
printk(" IRQs o%s FIQs o%s Mode %s Segment %s\n",
|
||||
interrupts_enabled(regs) ? "n" : "ff",
|
||||
fast_interrupts_enabled(regs) ? "n" : "ff",
|
||||
processor_modes[processor_mode(regs)],
|
||||
get_fs() == get_ds() ? "kernel" : "user");
|
||||
}
|
||||
|
||||
void show_fpregs(struct user_fp *regs)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
unsigned long *p;
|
||||
char type;
|
||||
|
||||
p = (unsigned long *)(regs->fpregs + i);
|
||||
|
||||
switch (regs->ftype[i]) {
|
||||
case 1: type = 'f'; break;
|
||||
case 2: type = 'd'; break;
|
||||
case 3: type = 'e'; break;
|
||||
default: type = '?'; break;
|
||||
}
|
||||
if (regs->init_flag)
|
||||
type = '?';
|
||||
|
||||
printk(" f%d(%c): %08lx %08lx %08lx%c",
|
||||
i, type, p[0], p[1], p[2], i & 1 ? '\n' : ' ');
|
||||
}
|
||||
|
||||
|
||||
printk("FPSR: %08lx FPCR: %08lx\n",
|
||||
(unsigned long)regs->fpsr,
|
||||
(unsigned long)regs->fpcr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Task structure and kernel stack allocation.
|
||||
*/
|
||||
static unsigned long *thread_info_head;
|
||||
static unsigned int nr_thread_info;
|
||||
|
||||
extern unsigned long get_page_8k(int priority);
|
||||
extern void free_page_8k(unsigned long page);
|
||||
|
||||
// FIXME - is this valid?
|
||||
#define EXTRA_TASK_STRUCT 0
|
||||
#define ll_alloc_task_struct() ((struct thread_info *)get_page_8k(GFP_KERNEL))
|
||||
#define ll_free_task_struct(p) free_page_8k((unsigned long)(p))
|
||||
|
||||
//FIXME - do we use *task param below looks like we dont, which is ok?
|
||||
//FIXME - if EXTRA_TASK_STRUCT is zero we can optimise the below away permanently. *IF* its supposed to be zero.
|
||||
struct thread_info *alloc_thread_info(struct task_struct *task)
|
||||
{
|
||||
struct thread_info *thread = NULL;
|
||||
|
||||
if (EXTRA_TASK_STRUCT) {
|
||||
unsigned long *p = thread_info_head;
|
||||
|
||||
if (p) {
|
||||
thread_info_head = (unsigned long *)p[0];
|
||||
nr_thread_info -= 1;
|
||||
}
|
||||
thread = (struct thread_info *)p;
|
||||
}
|
||||
|
||||
if (!thread)
|
||||
thread = ll_alloc_task_struct();
|
||||
|
||||
#ifdef CONFIG_MAGIC_SYSRQ
|
||||
/*
|
||||
* The stack must be cleared if you want SYSRQ-T to
|
||||
* give sensible stack usage information
|
||||
*/
|
||||
if (thread) {
|
||||
char *p = (char *)thread;
|
||||
memzero(p+KERNEL_STACK_SIZE, KERNEL_STACK_SIZE);
|
||||
}
|
||||
#endif
|
||||
return thread;
|
||||
}
|
||||
|
||||
void free_thread_info(struct thread_info *thread)
|
||||
{
|
||||
if (EXTRA_TASK_STRUCT && nr_thread_info < EXTRA_TASK_STRUCT) {
|
||||
unsigned long *p = (unsigned long *)thread;
|
||||
p[0] = (unsigned long)thread_info_head;
|
||||
thread_info_head = p;
|
||||
nr_thread_info += 1;
|
||||
} else
|
||||
ll_free_task_struct(thread);
|
||||
}
|
||||
|
||||
/*
|
||||
* Free current thread data structures etc..
|
||||
*/
|
||||
void exit_thread(void)
|
||||
{
|
||||
}
|
||||
|
||||
void flush_thread(void)
|
||||
{
|
||||
struct thread_info *thread = current_thread_info();
|
||||
struct task_struct *tsk = current;
|
||||
|
||||
memset(&tsk->thread.debug, 0, sizeof(struct debug_info));
|
||||
memset(&thread->fpstate, 0, sizeof(union fp_state));
|
||||
|
||||
clear_used_math();
|
||||
}
|
||||
|
||||
void release_thread(struct task_struct *dead_task)
|
||||
{
|
||||
}
|
||||
|
||||
asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
|
||||
|
||||
int
|
||||
copy_thread(int nr, unsigned long clone_flags, unsigned long stack_start,
|
||||
unsigned long unused, struct task_struct *p, struct pt_regs *regs)
|
||||
{
|
||||
struct thread_info *thread = p->thread_info;
|
||||
struct pt_regs *childregs;
|
||||
|
||||
childregs = __get_user_regs(thread);
|
||||
*childregs = *regs;
|
||||
childregs->ARM_r0 = 0;
|
||||
childregs->ARM_sp = stack_start;
|
||||
|
||||
memset(&thread->cpu_context, 0, sizeof(struct cpu_context_save));
|
||||
thread->cpu_context.sp = (unsigned long)childregs;
|
||||
thread->cpu_context.pc = (unsigned long)ret_from_fork | MODE_SVC26 | PSR_I_BIT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* fill in the fpe structure for a core dump...
|
||||
*/
|
||||
int dump_fpu (struct pt_regs *regs, struct user_fp *fp)
|
||||
{
|
||||
struct thread_info *thread = current_thread_info();
|
||||
int used_math = !!used_math();
|
||||
|
||||
if (used_math)
|
||||
memcpy(fp, &thread->fpstate.soft, sizeof (*fp));
|
||||
|
||||
return used_math;
|
||||
}
|
||||
|
||||
/*
|
||||
* fill in the user structure for a core dump..
|
||||
*/
|
||||
void dump_thread(struct pt_regs * regs, struct user * dump)
|
||||
{
|
||||
struct task_struct *tsk = current;
|
||||
|
||||
dump->magic = CMAGIC;
|
||||
dump->start_code = tsk->mm->start_code;
|
||||
dump->start_stack = regs->ARM_sp & ~(PAGE_SIZE - 1);
|
||||
|
||||
dump->u_tsize = (tsk->mm->end_code - tsk->mm->start_code) >> PAGE_SHIFT;
|
||||
dump->u_dsize = (tsk->mm->brk - tsk->mm->start_data + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
||||
dump->u_ssize = 0;
|
||||
|
||||
dump->u_debugreg[0] = tsk->thread.debug.bp[0].address;
|
||||
dump->u_debugreg[1] = tsk->thread.debug.bp[1].address;
|
||||
dump->u_debugreg[2] = tsk->thread.debug.bp[0].insn;
|
||||
dump->u_debugreg[3] = tsk->thread.debug.bp[1].insn;
|
||||
dump->u_debugreg[4] = tsk->thread.debug.nsaved;
|
||||
|
||||
if (dump->start_stack < 0x04000000)
|
||||
dump->u_ssize = (0x04000000 - dump->start_stack) >> PAGE_SHIFT;
|
||||
|
||||
dump->regs = *regs;
|
||||
dump->u_fpvalid = dump_fpu (regs, &dump->u_fp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Shuffle the argument into the correct register before calling the
|
||||
* thread function. r1 is the thread argument, r2 is the pointer to
|
||||
* the thread function, and r3 points to the exit function.
|
||||
* FIXME - make sure this is right - the older code used to zero fp
|
||||
* and cause the parent to call sys_exit (do_exit in this version)
|
||||
*/
|
||||
extern void kernel_thread_helper(void);
|
||||
|
||||
asm( ".section .text\n"
|
||||
" .align\n"
|
||||
" .type kernel_thread_helper, #function\n"
|
||||
"kernel_thread_helper:\n"
|
||||
" mov r0, r1\n"
|
||||
" mov lr, r3\n"
|
||||
" mov pc, r2\n"
|
||||
" .size kernel_thread_helper, . - kernel_thread_helper\n"
|
||||
" .previous");
|
||||
|
||||
/*
|
||||
* Create a kernel thread.
|
||||
*/
|
||||
pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
|
||||
{
|
||||
struct pt_regs regs;
|
||||
|
||||
memset(®s, 0, sizeof(regs));
|
||||
|
||||
regs.ARM_r1 = (unsigned long)arg;
|
||||
regs.ARM_r2 = (unsigned long)fn;
|
||||
regs.ARM_r3 = (unsigned long)do_exit;
|
||||
regs.ARM_pc = (unsigned long)kernel_thread_helper | MODE_SVC26;
|
||||
|
||||
return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, ®s, 0, NULL, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(kernel_thread);
|
||||
|
||||
|
||||
unsigned long get_wchan(struct task_struct *p)
|
||||
{
|
||||
unsigned long fp, lr;
|
||||
unsigned long stack_page;
|
||||
int count = 0;
|
||||
if (!p || p == current || p->state == TASK_RUNNING)
|
||||
return 0;
|
||||
|
||||
stack_page = 4096 + (unsigned long)p;
|
||||
fp = thread_saved_fp(p);
|
||||
do {
|
||||
if (fp < stack_page || fp > 4092+stack_page)
|
||||
return 0;
|
||||
lr = pc_pointer (((unsigned long *)fp)[-1]);
|
||||
if (!in_sched_functions(lr))
|
||||
return lr;
|
||||
fp = *(unsigned long *) (fp - 12);
|
||||
} while (count ++ < 16);
|
||||
return 0;
|
||||
}
|
744
arch/arm26/kernel/ptrace.c
Normal file
744
arch/arm26/kernel/ptrace.c
Normal file
@@ -0,0 +1,744 @@
|
||||
/*
|
||||
* linux/arch/arm26/kernel/ptrace.c
|
||||
*
|
||||
* By Ross Biro 1/23/92
|
||||
* edited by Linus Torvalds
|
||||
* ARM modifications Copyright (C) 2000 Russell King
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/config.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/user.h>
|
||||
#include <linux/security.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/system.h>
|
||||
//#include <asm/processor.h>
|
||||
|
||||
#include "ptrace.h"
|
||||
|
||||
#define REG_PC 15
|
||||
#define REG_PSR 15
|
||||
/*
|
||||
* does not yet catch signals sent when the child dies.
|
||||
* in exit.c or in signal.c.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Breakpoint SWI instruction: SWI &9F0001
|
||||
*/
|
||||
#define BREAKINST_ARM 0xef9f0001
|
||||
|
||||
/*
|
||||
* Get the address of the live pt_regs for the specified task.
|
||||
* These are saved onto the top kernel stack when the process
|
||||
* is not running.
|
||||
*
|
||||
* Note: if a user thread is execve'd from kernel space, the
|
||||
* kernel stack will not be empty on entry to the kernel, so
|
||||
* ptracing these tasks will fail.
|
||||
*/
|
||||
static inline struct pt_regs *
|
||||
get_user_regs(struct task_struct *task)
|
||||
{
|
||||
return __get_user_regs(task->thread_info);
|
||||
}
|
||||
|
||||
/*
|
||||
* this routine will get a word off of the processes privileged stack.
|
||||
* the offset is how far from the base addr as stored in the THREAD.
|
||||
* this routine assumes that all the privileged stacks are in our
|
||||
* data space.
|
||||
*/
|
||||
static inline long get_user_reg(struct task_struct *task, int offset)
|
||||
{
|
||||
return get_user_regs(task)->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 THREAD.
|
||||
* this routine assumes that all the privileged stacks are in our
|
||||
* data space.
|
||||
*/
|
||||
static inline int
|
||||
put_user_reg(struct task_struct *task, int offset, long data)
|
||||
{
|
||||
struct pt_regs newregs, *regs = get_user_regs(task);
|
||||
int ret = -EINVAL;
|
||||
|
||||
newregs = *regs;
|
||||
newregs.uregs[offset] = data;
|
||||
|
||||
if (valid_user_regs(&newregs)) {
|
||||
regs->uregs[offset] = data;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
read_u32(struct task_struct *task, unsigned long addr, u32 *res)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = access_process_vm(task, addr, res, sizeof(*res), 0);
|
||||
|
||||
return ret == sizeof(*res) ? 0 : -EIO;
|
||||
}
|
||||
|
||||
static inline int
|
||||
read_instr(struct task_struct *task, unsigned long addr, u32 *res)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
ret = access_process_vm(task, addr & ~3, &val, sizeof(val), 0);
|
||||
ret = ret == sizeof(val) ? 0 : -EIO;
|
||||
*res = val;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get value of register `rn' (in the instruction)
|
||||
*/
|
||||
static unsigned long
|
||||
ptrace_getrn(struct task_struct *child, unsigned long insn)
|
||||
{
|
||||
unsigned int reg = (insn >> 16) & 15;
|
||||
unsigned long val;
|
||||
|
||||
val = get_user_reg(child, reg);
|
||||
if (reg == 15)
|
||||
val = pc_pointer(val + 8); //FIXME - correct for arm26?
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get value of operand 2 (in an ALU instruction)
|
||||
*/
|
||||
static unsigned long
|
||||
ptrace_getaluop2(struct task_struct *child, unsigned long insn)
|
||||
{
|
||||
unsigned long val;
|
||||
int shift;
|
||||
int type;
|
||||
|
||||
if (insn & 1 << 25) {
|
||||
val = insn & 255;
|
||||
shift = (insn >> 8) & 15;
|
||||
type = 3;
|
||||
} else {
|
||||
val = get_user_reg (child, insn & 15);
|
||||
|
||||
if (insn & (1 << 4))
|
||||
shift = (int)get_user_reg (child, (insn >> 8) & 15);
|
||||
else
|
||||
shift = (insn >> 7) & 31;
|
||||
|
||||
type = (insn >> 5) & 3;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 0: val <<= shift; break;
|
||||
case 1: val >>= shift; break;
|
||||
case 2:
|
||||
val = (((signed long)val) >> shift);
|
||||
break;
|
||||
case 3:
|
||||
val = (val >> shift) | (val << (32 - shift));
|
||||
break;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get value of operand 2 (in a LDR instruction)
|
||||
*/
|
||||
static unsigned long
|
||||
ptrace_getldrop2(struct task_struct *child, unsigned long insn)
|
||||
{
|
||||
unsigned long val;
|
||||
int shift;
|
||||
int type;
|
||||
|
||||
val = get_user_reg(child, insn & 15);
|
||||
shift = (insn >> 7) & 31;
|
||||
type = (insn >> 5) & 3;
|
||||
|
||||
switch (type) {
|
||||
case 0: val <<= shift; break;
|
||||
case 1: val >>= shift; break;
|
||||
case 2:
|
||||
val = (((signed long)val) >> shift);
|
||||
break;
|
||||
case 3:
|
||||
val = (val >> shift) | (val << (32 - shift));
|
||||
break;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
#define OP_MASK 0x01e00000
|
||||
#define OP_AND 0x00000000
|
||||
#define OP_EOR 0x00200000
|
||||
#define OP_SUB 0x00400000
|
||||
#define OP_RSB 0x00600000
|
||||
#define OP_ADD 0x00800000
|
||||
#define OP_ADC 0x00a00000
|
||||
#define OP_SBC 0x00c00000
|
||||
#define OP_RSC 0x00e00000
|
||||
#define OP_ORR 0x01800000
|
||||
#define OP_MOV 0x01a00000
|
||||
#define OP_BIC 0x01c00000
|
||||
#define OP_MVN 0x01e00000
|
||||
|
||||
static unsigned long
|
||||
get_branch_address(struct task_struct *child, unsigned long pc, unsigned long insn)
|
||||
{
|
||||
u32 alt = 0;
|
||||
|
||||
switch (insn & 0x0e000000) {
|
||||
case 0x00000000:
|
||||
case 0x02000000: {
|
||||
/*
|
||||
* data processing
|
||||
*/
|
||||
long aluop1, aluop2, ccbit;
|
||||
|
||||
if ((insn & 0xf000) != 0xf000)
|
||||
break;
|
||||
|
||||
aluop1 = ptrace_getrn(child, insn);
|
||||
aluop2 = ptrace_getaluop2(child, insn);
|
||||
ccbit = get_user_reg(child, REG_PSR) & PSR_C_BIT ? 1 : 0;
|
||||
|
||||
switch (insn & OP_MASK) {
|
||||
case OP_AND: alt = aluop1 & aluop2; break;
|
||||
case OP_EOR: alt = aluop1 ^ aluop2; break;
|
||||
case OP_SUB: alt = aluop1 - aluop2; break;
|
||||
case OP_RSB: alt = aluop2 - aluop1; break;
|
||||
case OP_ADD: alt = aluop1 + aluop2; break;
|
||||
case OP_ADC: alt = aluop1 + aluop2 + ccbit; break;
|
||||
case OP_SBC: alt = aluop1 - aluop2 + ccbit; break;
|
||||
case OP_RSC: alt = aluop2 - aluop1 + ccbit; break;
|
||||
case OP_ORR: alt = aluop1 | aluop2; break;
|
||||
case OP_MOV: alt = aluop2; break;
|
||||
case OP_BIC: alt = aluop1 & ~aluop2; break;
|
||||
case OP_MVN: alt = ~aluop2; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x04000000:
|
||||
case 0x06000000:
|
||||
/*
|
||||
* ldr
|
||||
*/
|
||||
if ((insn & 0x0010f000) == 0x0010f000) {
|
||||
unsigned long base;
|
||||
|
||||
base = ptrace_getrn(child, insn);
|
||||
if (insn & 1 << 24) {
|
||||
long aluop2;
|
||||
|
||||
if (insn & 0x02000000)
|
||||
aluop2 = ptrace_getldrop2(child, insn);
|
||||
else
|
||||
aluop2 = insn & 0xfff;
|
||||
|
||||
if (insn & 1 << 23)
|
||||
base += aluop2;
|
||||
else
|
||||
base -= aluop2;
|
||||
}
|
||||
if (read_u32(child, base, &alt) == 0)
|
||||
alt = pc_pointer(alt);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x08000000:
|
||||
/*
|
||||
* ldm
|
||||
*/
|
||||
if ((insn & 0x00108000) == 0x00108000) {
|
||||
unsigned long base;
|
||||
unsigned int nr_regs;
|
||||
|
||||
if (insn & (1 << 23)) {
|
||||
nr_regs = hweight16(insn & 65535) << 2;
|
||||
|
||||
if (!(insn & (1 << 24)))
|
||||
nr_regs -= 4;
|
||||
} else {
|
||||
if (insn & (1 << 24))
|
||||
nr_regs = -4;
|
||||
else
|
||||
nr_regs = 0;
|
||||
}
|
||||
|
||||
base = ptrace_getrn(child, insn);
|
||||
|
||||
if (read_u32(child, base + nr_regs, &alt) == 0)
|
||||
alt = pc_pointer(alt);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x0a000000: {
|
||||
/*
|
||||
* bl or b
|
||||
*/
|
||||
signed long displ;
|
||||
/* It's a branch/branch link: instead of trying to
|
||||
* figure out whether the branch will be taken or not,
|
||||
* we'll put a breakpoint at both locations. This is
|
||||
* simpler, more reliable, and probably not a whole lot
|
||||
* slower than the alternative approach of emulating the
|
||||
* branch.
|
||||
*/
|
||||
displ = (insn & 0x00ffffff) << 8;
|
||||
displ = (displ >> 6) + 8;
|
||||
if (displ != 0 && displ != 4)
|
||||
alt = pc + displ;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return alt;
|
||||
}
|
||||
|
||||
static int
|
||||
swap_insn(struct task_struct *task, unsigned long addr,
|
||||
void *old_insn, void *new_insn, int size)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = access_process_vm(task, addr, old_insn, size, 0);
|
||||
if (ret == size)
|
||||
ret = access_process_vm(task, addr, new_insn, size, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
add_breakpoint(struct task_struct *task, struct debug_info *dbg, unsigned long addr)
|
||||
{
|
||||
int nr = dbg->nsaved;
|
||||
|
||||
if (nr < 2) {
|
||||
u32 new_insn = BREAKINST_ARM;
|
||||
int res;
|
||||
|
||||
res = swap_insn(task, addr, &dbg->bp[nr].insn, &new_insn, 4);
|
||||
|
||||
if (res == 4) {
|
||||
dbg->bp[nr].address = addr;
|
||||
dbg->nsaved += 1;
|
||||
}
|
||||
} else
|
||||
printk(KERN_ERR "ptrace: too many breakpoints\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear one breakpoint in the user program. We copy what the hardware
|
||||
* does and use bit 0 of the address to indicate whether this is a Thumb
|
||||
* breakpoint or an ARM breakpoint.
|
||||
*/
|
||||
static void clear_breakpoint(struct task_struct *task, struct debug_entry *bp)
|
||||
{
|
||||
unsigned long addr = bp->address;
|
||||
u32 old_insn;
|
||||
int ret;
|
||||
|
||||
ret = swap_insn(task, addr & ~3, &old_insn,
|
||||
&bp->insn, 4);
|
||||
|
||||
if (ret != 4 || old_insn != BREAKINST_ARM)
|
||||
printk(KERN_ERR "%s:%d: corrupted ARM breakpoint at "
|
||||
"0x%08lx (0x%08x)\n", task->comm, task->pid,
|
||||
addr, old_insn);
|
||||
}
|
||||
|
||||
void ptrace_set_bpt(struct task_struct *child)
|
||||
{
|
||||
struct pt_regs *regs;
|
||||
unsigned long pc;
|
||||
u32 insn;
|
||||
int res;
|
||||
|
||||
regs = get_user_regs(child);
|
||||
pc = instruction_pointer(regs);
|
||||
|
||||
res = read_instr(child, pc, &insn);
|
||||
if (!res) {
|
||||
struct debug_info *dbg = &child->thread.debug;
|
||||
unsigned long alt;
|
||||
|
||||
dbg->nsaved = 0;
|
||||
|
||||
alt = get_branch_address(child, pc, insn);
|
||||
if (alt)
|
||||
add_breakpoint(child, dbg, alt);
|
||||
|
||||
/*
|
||||
* Note that we ignore the result of setting the above
|
||||
* breakpoint since it may fail. When it does, this is
|
||||
* not so much an error, but a forewarning that we may
|
||||
* be receiving a prefetch abort shortly.
|
||||
*
|
||||
* If we don't set this breakpoint here, then we can
|
||||
* lose control of the thread during single stepping.
|
||||
*/
|
||||
if (!alt || predicate(insn) != PREDICATE_ALWAYS)
|
||||
add_breakpoint(child, dbg, pc + 4);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure no single-step breakpoint is pending. Returns non-zero
|
||||
* value if child was being single-stepped.
|
||||
*/
|
||||
void ptrace_cancel_bpt(struct task_struct *child)
|
||||
{
|
||||
int i, nsaved = child->thread.debug.nsaved;
|
||||
|
||||
child->thread.debug.nsaved = 0;
|
||||
|
||||
if (nsaved > 2) {
|
||||
printk("ptrace_cancel_bpt: bogus nsaved: %d!\n", nsaved);
|
||||
nsaved = 2;
|
||||
}
|
||||
|
||||
for (i = 0; i < nsaved; i++)
|
||||
clear_breakpoint(child, &child->thread.debug.bp[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by kernel/ptrace.c when detaching..
|
||||
*
|
||||
* Make sure the single step bit is not set.
|
||||
*/
|
||||
void ptrace_disable(struct task_struct *child)
|
||||
{
|
||||
child->ptrace &= ~PT_SINGLESTEP;
|
||||
ptrace_cancel_bpt(child);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle hitting a breakpoint.
|
||||
*/
|
||||
void ptrace_break(struct task_struct *tsk, struct pt_regs *regs)
|
||||
{
|
||||
siginfo_t info;
|
||||
|
||||
/*
|
||||
* The PC is always left pointing at the next instruction. Fix this.
|
||||
*/
|
||||
regs->ARM_pc -= 4;
|
||||
|
||||
if (tsk->thread.debug.nsaved == 0)
|
||||
printk(KERN_ERR "ptrace: bogus breakpoint trap\n");
|
||||
|
||||
ptrace_cancel_bpt(tsk);
|
||||
|
||||
info.si_signo = SIGTRAP;
|
||||
info.si_errno = 0;
|
||||
info.si_code = TRAP_BRKPT;
|
||||
info.si_addr = (void *)instruction_pointer(regs) - 4;
|
||||
|
||||
force_sig_info(SIGTRAP, &info, tsk);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the word at offset "off" into the "struct user". We
|
||||
* actually access the pt_regs stored on the kernel stack.
|
||||
*/
|
||||
static int ptrace_read_user(struct task_struct *tsk, unsigned long off,
|
||||
unsigned long *ret)
|
||||
{
|
||||
unsigned long tmp;
|
||||
|
||||
if (off & 3 || off >= sizeof(struct user))
|
||||
return -EIO;
|
||||
|
||||
tmp = 0;
|
||||
if (off < sizeof(struct pt_regs))
|
||||
tmp = get_user_reg(tsk, off >> 2);
|
||||
|
||||
return put_user(tmp, ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write the word at offset "off" into "struct user". We
|
||||
* actually access the pt_regs stored on the kernel stack.
|
||||
*/
|
||||
static int ptrace_write_user(struct task_struct *tsk, unsigned long off,
|
||||
unsigned long val)
|
||||
{
|
||||
if (off & 3 || off >= sizeof(struct user))
|
||||
return -EIO;
|
||||
|
||||
if (off >= sizeof(struct pt_regs))
|
||||
return 0;
|
||||
|
||||
return put_user_reg(tsk, off >> 2, val);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get all user integer registers.
|
||||
*/
|
||||
static int ptrace_getregs(struct task_struct *tsk, void *uregs)
|
||||
{
|
||||
struct pt_regs *regs = get_user_regs(tsk);
|
||||
|
||||
return copy_to_user(uregs, regs, sizeof(struct pt_regs)) ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set all user integer registers.
|
||||
*/
|
||||
static int ptrace_setregs(struct task_struct *tsk, void *uregs)
|
||||
{
|
||||
struct pt_regs newregs;
|
||||
int ret;
|
||||
|
||||
ret = -EFAULT;
|
||||
if (copy_from_user(&newregs, uregs, sizeof(struct pt_regs)) == 0) {
|
||||
struct pt_regs *regs = get_user_regs(tsk);
|
||||
|
||||
ret = -EINVAL;
|
||||
if (valid_user_regs(&newregs)) {
|
||||
*regs = newregs;
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the child FPU state.
|
||||
*/
|
||||
static int ptrace_getfpregs(struct task_struct *tsk, void *ufp)
|
||||
{
|
||||
return copy_to_user(ufp, &tsk->thread_info->fpstate,
|
||||
sizeof(struct user_fp)) ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the child FPU state.
|
||||
*/
|
||||
static int ptrace_setfpregs(struct task_struct *tsk, void *ufp)
|
||||
{
|
||||
set_stopped_child_used_math(tsk);
|
||||
return copy_from_user(&tsk->thread_info->fpstate, ufp,
|
||||
sizeof(struct user_fp)) ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
static int do_ptrace(int request, struct task_struct *child, long addr, long data)
|
||||
{
|
||||
unsigned long tmp;
|
||||
int ret;
|
||||
|
||||
switch (request) {
|
||||
/*
|
||||
* read word at location "addr" in the child process.
|
||||
*/
|
||||
case PTRACE_PEEKTEXT:
|
||||
case PTRACE_PEEKDATA:
|
||||
ret = access_process_vm(child, addr, &tmp,
|
||||
sizeof(unsigned long), 0);
|
||||
if (ret == sizeof(unsigned long))
|
||||
ret = put_user(tmp, (unsigned long *) data);
|
||||
else
|
||||
ret = -EIO;
|
||||
break;
|
||||
|
||||
case PTRACE_PEEKUSR:
|
||||
ret = ptrace_read_user(child, addr, (unsigned long *)data);
|
||||
break;
|
||||
|
||||
/*
|
||||
* write the word at location addr.
|
||||
*/
|
||||
case PTRACE_POKETEXT:
|
||||
case PTRACE_POKEDATA:
|
||||
ret = access_process_vm(child, addr, &data,
|
||||
sizeof(unsigned long), 1);
|
||||
if (ret == sizeof(unsigned long))
|
||||
ret = 0;
|
||||
else
|
||||
ret = -EIO;
|
||||
break;
|
||||
|
||||
case PTRACE_POKEUSR:
|
||||
ret = ptrace_write_user(child, addr, data);
|
||||
break;
|
||||
|
||||
/*
|
||||
* continue/restart and stop at next (return from) syscall
|
||||
*/
|
||||
case PTRACE_SYSCALL:
|
||||
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;
|
||||
/* make sure single-step breakpoint is gone. */
|
||||
child->ptrace &= ~PT_SINGLESTEP;
|
||||
ptrace_cancel_bpt(child);
|
||||
wake_up_process(child);
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
/*
|
||||
* make the child exit. 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:
|
||||
/* make sure single-step breakpoint is gone. */
|
||||
child->ptrace &= ~PT_SINGLESTEP;
|
||||
ptrace_cancel_bpt(child);
|
||||
if (child->exit_state != EXIT_ZOMBIE) {
|
||||
child->exit_code = SIGKILL;
|
||||
wake_up_process(child);
|
||||
}
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
/*
|
||||
* execute single instruction.
|
||||
*/
|
||||
case PTRACE_SINGLESTEP:
|
||||
ret = -EIO;
|
||||
if ((unsigned long) data > _NSIG)
|
||||
break;
|
||||
child->ptrace |= PT_SINGLESTEP;
|
||||
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
|
||||
child->exit_code = data;
|
||||
/* give it a chance to run. */
|
||||
wake_up_process(child);
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
case PTRACE_DETACH:
|
||||
ret = ptrace_detach(child, data);
|
||||
break;
|
||||
|
||||
case PTRACE_GETREGS:
|
||||
ret = ptrace_getregs(child, (void *)data);
|
||||
break;
|
||||
|
||||
case PTRACE_SETREGS:
|
||||
ret = ptrace_setregs(child, (void *)data);
|
||||
break;
|
||||
|
||||
case PTRACE_GETFPREGS:
|
||||
ret = ptrace_getfpregs(child, (void *)data);
|
||||
break;
|
||||
|
||||
case PTRACE_SETFPREGS:
|
||||
ret = ptrace_setfpregs(child, (void *)data);
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = ptrace_request(child, request, addr, data);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
|
||||
{
|
||||
struct task_struct *child;
|
||||
int ret;
|
||||
|
||||
lock_kernel();
|
||||
ret = -EPERM;
|
||||
if (request == PTRACE_TRACEME) {
|
||||
/* are we already being traced? */
|
||||
if (current->ptrace & PT_PTRACED)
|
||||
goto out;
|
||||
ret = security_ptrace(current->parent, current);
|
||||
if (ret)
|
||||
goto out;
|
||||
/* set the ptrace bit in the process flags. */
|
||||
current->ptrace |= PT_PTRACED;
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
ret = -ESRCH;
|
||||
read_lock(&tasklist_lock);
|
||||
child = find_task_by_pid(pid);
|
||||
if (child)
|
||||
get_task_struct(child);
|
||||
read_unlock(&tasklist_lock);
|
||||
if (!child)
|
||||
goto out;
|
||||
|
||||
ret = -EPERM;
|
||||
if (pid == 1) /* you may not mess with init */
|
||||
goto out_tsk;
|
||||
|
||||
if (request == PTRACE_ATTACH) {
|
||||
ret = ptrace_attach(child);
|
||||
goto out_tsk;
|
||||
}
|
||||
ret = ptrace_check_attach(child, request == PTRACE_KILL);
|
||||
if (ret == 0)
|
||||
ret = do_ptrace(request, child, addr, data);
|
||||
|
||||
out_tsk:
|
||||
put_task_struct(child);
|
||||
out:
|
||||
unlock_kernel();
|
||||
return ret;
|
||||
}
|
||||
|
||||
asmlinkage void syscall_trace(int why, struct pt_regs *regs)
|
||||
{
|
||||
unsigned long ip;
|
||||
|
||||
if (!test_thread_flag(TIF_SYSCALL_TRACE))
|
||||
return;
|
||||
if (!(current->ptrace & PT_PTRACED))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Save IP. IP is used to denote syscall entry/exit:
|
||||
* IP = 0 -> entry, = 1 -> exit
|
||||
*/
|
||||
ip = regs->ARM_ip;
|
||||
regs->ARM_ip = why;
|
||||
|
||||
/* 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)
|
||||
? 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;
|
||||
}
|
||||
regs->ARM_ip = ip;
|
||||
}
|
13
arch/arm26/kernel/ptrace.h
Normal file
13
arch/arm26/kernel/ptrace.h
Normal file
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* linux/arch/arm26/kernel/ptrace.h
|
||||
*
|
||||
* Copyright (C) 2000-2003 Russell King
|
||||
* Copyright (C) 2003 Ian Molton
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
extern void ptrace_cancel_bpt(struct task_struct *);
|
||||
extern void ptrace_set_bpt(struct task_struct *);
|
||||
extern void ptrace_break(struct task_struct *, struct pt_regs *);
|
223
arch/arm26/kernel/semaphore.c
Normal file
223
arch/arm26/kernel/semaphore.c
Normal file
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* ARM semaphore implementation, taken from
|
||||
*
|
||||
* i386 semaphore implementation.
|
||||
*
|
||||
* (C) Copyright 1999 Linus Torvalds
|
||||
* (C) Copyright 2003 Ian Molton (ARM26 mods)
|
||||
*
|
||||
* Modified for ARM by Russell King
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/config.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/semaphore.h>
|
||||
|
||||
/*
|
||||
* Semaphores are implemented using a two-way counter:
|
||||
* The "count" variable is decremented for each process
|
||||
* that tries to acquire the semaphore, while the "sleeping"
|
||||
* variable is a count of such acquires.
|
||||
*
|
||||
* Notably, the inline "up()" and "down()" functions can
|
||||
* efficiently test if they need to do any extra work (up
|
||||
* needs to do something only if count was negative before
|
||||
* the increment operation.
|
||||
*
|
||||
* "sleeping" and the contention routine ordering is
|
||||
* protected by the semaphore spinlock.
|
||||
*
|
||||
* Note that these functions are only called when there is
|
||||
* contention on the lock, and as such all this is the
|
||||
* "non-critical" part of the whole semaphore business. The
|
||||
* critical part is the inline stuff in <asm/semaphore.h>
|
||||
* where we want to avoid any extra jumps and calls.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Logic:
|
||||
* - only on a boundary condition do we need to care. When we go
|
||||
* from a negative count to a non-negative, we wake people up.
|
||||
* - when we go from a non-negative count to a negative do we
|
||||
* (a) synchronize with the "sleeper" count and (b) make sure
|
||||
* that we're on the wakeup list before we synchronize so that
|
||||
* we cannot lose wakeup events.
|
||||
*/
|
||||
|
||||
void __up(struct semaphore *sem)
|
||||
{
|
||||
wake_up(&sem->wait);
|
||||
}
|
||||
|
||||
static DEFINE_SPINLOCK(semaphore_lock);
|
||||
|
||||
void __sched __down(struct semaphore * sem)
|
||||
{
|
||||
struct task_struct *tsk = current;
|
||||
DECLARE_WAITQUEUE(wait, tsk);
|
||||
tsk->state = TASK_UNINTERRUPTIBLE;
|
||||
add_wait_queue_exclusive(&sem->wait, &wait);
|
||||
|
||||
spin_lock_irq(&semaphore_lock);
|
||||
sem->sleepers++;
|
||||
for (;;) {
|
||||
int sleepers = sem->sleepers;
|
||||
|
||||
/*
|
||||
* Add "everybody else" into it. They aren't
|
||||
* playing, because we own the spinlock.
|
||||
*/
|
||||
if (!atomic_add_negative(sleepers - 1, &sem->count)) {
|
||||
sem->sleepers = 0;
|
||||
break;
|
||||
}
|
||||
sem->sleepers = 1; /* us - see -1 above */
|
||||
spin_unlock_irq(&semaphore_lock);
|
||||
|
||||
schedule();
|
||||
tsk->state = TASK_UNINTERRUPTIBLE;
|
||||
spin_lock_irq(&semaphore_lock);
|
||||
}
|
||||
spin_unlock_irq(&semaphore_lock);
|
||||
remove_wait_queue(&sem->wait, &wait);
|
||||
tsk->state = TASK_RUNNING;
|
||||
wake_up(&sem->wait);
|
||||
}
|
||||
|
||||
int __sched __down_interruptible(struct semaphore * sem)
|
||||
{
|
||||
int retval = 0;
|
||||
struct task_struct *tsk = current;
|
||||
DECLARE_WAITQUEUE(wait, tsk);
|
||||
tsk->state = TASK_INTERRUPTIBLE;
|
||||
add_wait_queue_exclusive(&sem->wait, &wait);
|
||||
|
||||
spin_lock_irq(&semaphore_lock);
|
||||
sem->sleepers ++;
|
||||
for (;;) {
|
||||
int sleepers = sem->sleepers;
|
||||
|
||||
/*
|
||||
* With signals pending, this turns into
|
||||
* the trylock failure case - we won't be
|
||||
* sleeping, and we* can't get the lock as
|
||||
* it has contention. Just correct the count
|
||||
* and exit.
|
||||
*/
|
||||
if (signal_pending(current)) {
|
||||
retval = -EINTR;
|
||||
sem->sleepers = 0;
|
||||
atomic_add(sleepers, &sem->count);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add "everybody else" into it. They aren't
|
||||
* playing, because we own the spinlock. The
|
||||
* "-1" is because we're still hoping to get
|
||||
* the lock.
|
||||
*/
|
||||
if (!atomic_add_negative(sleepers - 1, &sem->count)) {
|
||||
sem->sleepers = 0;
|
||||
break;
|
||||
}
|
||||
sem->sleepers = 1; /* us - see -1 above */
|
||||
spin_unlock_irq(&semaphore_lock);
|
||||
|
||||
schedule();
|
||||
tsk->state = TASK_INTERRUPTIBLE;
|
||||
spin_lock_irq(&semaphore_lock);
|
||||
}
|
||||
spin_unlock_irq(&semaphore_lock);
|
||||
tsk->state = TASK_RUNNING;
|
||||
remove_wait_queue(&sem->wait, &wait);
|
||||
wake_up(&sem->wait);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Trylock failed - make sure we correct for
|
||||
* having decremented the count.
|
||||
*
|
||||
* We could have done the trylock with a
|
||||
* single "cmpxchg" without failure cases,
|
||||
* but then it wouldn't work on a 386.
|
||||
*/
|
||||
int __down_trylock(struct semaphore * sem)
|
||||
{
|
||||
int sleepers;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&semaphore_lock, flags);
|
||||
sleepers = sem->sleepers + 1;
|
||||
sem->sleepers = 0;
|
||||
|
||||
/*
|
||||
* Add "everybody else" and us into it. They aren't
|
||||
* playing, because we own the spinlock.
|
||||
*/
|
||||
if (!atomic_add_negative(sleepers, &sem->count))
|
||||
wake_up(&sem->wait);
|
||||
|
||||
spin_unlock_irqrestore(&semaphore_lock, flags);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* The semaphore operations have a special calling sequence that
|
||||
* allow us to do a simpler in-line version of them. These routines
|
||||
* need to convert that sequence back into the C sequence when
|
||||
* there is contention on the semaphore.
|
||||
*
|
||||
* ip contains the semaphore pointer on entry. Save the C-clobbered
|
||||
* registers (r0 to r3 and lr), but not ip, as we use it as a return
|
||||
* value in some cases..
|
||||
*/
|
||||
asm(" .section .sched.text , #alloc, #execinstr \n\
|
||||
.align 5 \n\
|
||||
.globl __down_failed \n\
|
||||
__down_failed: \n\
|
||||
stmfd sp!, {r0 - r3, lr} \n\
|
||||
mov r0, ip \n\
|
||||
bl __down \n\
|
||||
ldmfd sp!, {r0 - r3, pc}^ \n\
|
||||
\n\
|
||||
.align 5 \n\
|
||||
.globl __down_interruptible_failed \n\
|
||||
__down_interruptible_failed: \n\
|
||||
stmfd sp!, {r0 - r3, lr} \n\
|
||||
mov r0, ip \n\
|
||||
bl __down_interruptible \n\
|
||||
mov ip, r0 \n\
|
||||
ldmfd sp!, {r0 - r3, pc}^ \n\
|
||||
\n\
|
||||
.align 5 \n\
|
||||
.globl __down_trylock_failed \n\
|
||||
__down_trylock_failed: \n\
|
||||
stmfd sp!, {r0 - r3, lr} \n\
|
||||
mov r0, ip \n\
|
||||
bl __down_trylock \n\
|
||||
mov ip, r0 \n\
|
||||
ldmfd sp!, {r0 - r3, pc}^ \n\
|
||||
\n\
|
||||
.align 5 \n\
|
||||
.globl __up_wakeup \n\
|
||||
__up_wakeup: \n\
|
||||
stmfd sp!, {r0 - r3, lr} \n\
|
||||
mov r0, ip \n\
|
||||
bl __up \n\
|
||||
ldmfd sp!, {r0 - r3, pc}^ \n\
|
||||
");
|
||||
|
||||
EXPORT_SYMBOL(__down_failed);
|
||||
EXPORT_SYMBOL(__down_interruptible_failed);
|
||||
EXPORT_SYMBOL(__down_trylock_failed);
|
||||
EXPORT_SYMBOL(__up_wakeup);
|
||||
|
573
arch/arm26/kernel/setup.c
Normal file
573
arch/arm26/kernel/setup.c
Normal file
@@ -0,0 +1,573 @@
|
||||
/*
|
||||
* linux/arch/arm26/kernel/setup.c
|
||||
*
|
||||
* Copyright (C) 1995-2001 Russell King
|
||||
* Copyright (C) 2003 Ian Molton
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/config.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/utsname.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/bootmem.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/root_dev.h>
|
||||
|
||||
#include <asm/elf.h>
|
||||
#include <asm/hardware.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/procinfo.h>
|
||||
#include <asm/setup.h>
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/tlbflush.h>
|
||||
|
||||
#include <asm/irqchip.h>
|
||||
|
||||
#ifndef MEM_SIZE
|
||||
#define MEM_SIZE (16*1024*1024)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PREEMPT
|
||||
DEFINE_SPINLOCK(kernel_flag);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_FPE_NWFPE)
|
||||
char fpe_type[8];
|
||||
|
||||
static int __init fpe_setup(char *line)
|
||||
{
|
||||
memcpy(fpe_type, line, 8);
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("fpe=", fpe_setup);
|
||||
#endif
|
||||
|
||||
extern void paging_init(struct meminfo *);
|
||||
extern void convert_to_tag_list(struct tag *tags);
|
||||
extern void squash_mem_tags(struct tag *tag);
|
||||
extern void bootmem_init(struct meminfo *);
|
||||
extern int root_mountflags;
|
||||
extern int _stext, _text, _etext, _edata, _end;
|
||||
#ifdef CONFIG_XIP_KERNEL
|
||||
extern int _endtext, _sdata;
|
||||
#endif
|
||||
|
||||
|
||||
unsigned int processor_id;
|
||||
unsigned int __machine_arch_type;
|
||||
unsigned int system_rev;
|
||||
unsigned int system_serial_low;
|
||||
unsigned int system_serial_high;
|
||||
unsigned int elf_hwcap;
|
||||
unsigned int memc_ctrl_reg;
|
||||
unsigned int number_mfm_drives;
|
||||
|
||||
struct processor processor;
|
||||
|
||||
char elf_platform[ELF_PLATFORM_SIZE];
|
||||
|
||||
unsigned long phys_initrd_start __initdata = 0;
|
||||
unsigned long phys_initrd_size __initdata = 0;
|
||||
static struct meminfo meminfo __initdata = { 0, };
|
||||
static struct proc_info_item proc_info;
|
||||
static const char *machine_name;
|
||||
static char command_line[COMMAND_LINE_SIZE];
|
||||
|
||||
static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;
|
||||
|
||||
/*
|
||||
* Standard memory resources
|
||||
*/
|
||||
static struct resource mem_res[] = {
|
||||
{ "Video RAM", 0, 0, IORESOURCE_MEM },
|
||||
{ "Kernel code", 0, 0, IORESOURCE_MEM },
|
||||
{ "Kernel data", 0, 0, IORESOURCE_MEM }
|
||||
};
|
||||
|
||||
#define video_ram mem_res[0]
|
||||
#define kernel_code mem_res[1]
|
||||
#define kernel_data mem_res[2]
|
||||
|
||||
static struct resource io_res[] = {
|
||||
{ "reserved", 0x3bc, 0x3be, IORESOURCE_IO | IORESOURCE_BUSY },
|
||||
{ "reserved", 0x378, 0x37f, IORESOURCE_IO | IORESOURCE_BUSY },
|
||||
{ "reserved", 0x278, 0x27f, IORESOURCE_IO | IORESOURCE_BUSY }
|
||||
};
|
||||
|
||||
#define lp0 io_res[0]
|
||||
#define lp1 io_res[1]
|
||||
#define lp2 io_res[2]
|
||||
|
||||
#define dump_cpu_info() do { } while (0)
|
||||
|
||||
static void __init setup_processor(void)
|
||||
{
|
||||
extern struct proc_info_list __proc_info_begin, __proc_info_end;
|
||||
struct proc_info_list *list;
|
||||
|
||||
/*
|
||||
* locate processor in the list of supported processor
|
||||
* types. The linker builds this table for us from the
|
||||
* entries in arch/arm26/mm/proc-*.S
|
||||
*/
|
||||
for (list = &__proc_info_begin; list < &__proc_info_end ; list++)
|
||||
if ((processor_id & list->cpu_mask) == list->cpu_val)
|
||||
break;
|
||||
|
||||
/*
|
||||
* If processor type is unrecognised, then we
|
||||
* can do nothing...
|
||||
*/
|
||||
if (list >= &__proc_info_end) {
|
||||
printk("CPU configuration botched (ID %08x), unable "
|
||||
"to continue.\n", processor_id);
|
||||
while (1);
|
||||
}
|
||||
|
||||
proc_info = *list->info;
|
||||
processor = *list->proc;
|
||||
|
||||
|
||||
printk("CPU: %s %s revision %d\n",
|
||||
proc_info.manufacturer, proc_info.cpu_name,
|
||||
(int)processor_id & 15);
|
||||
|
||||
dump_cpu_info();
|
||||
|
||||
sprintf(system_utsname.machine, "%s", list->arch_name);
|
||||
sprintf(elf_platform, "%s", list->elf_name);
|
||||
elf_hwcap = list->elf_hwcap;
|
||||
|
||||
cpu_proc_init();
|
||||
}
|
||||
|
||||
/*
|
||||
* Initial parsing of the command line. We need to pick out the
|
||||
* memory size. We look for mem=size@start, where start and size
|
||||
* are "size[KkMm]"
|
||||
*/
|
||||
static void __init
|
||||
parse_cmdline(struct meminfo *mi, char **cmdline_p, char *from)
|
||||
{
|
||||
char c = ' ', *to = command_line;
|
||||
int usermem = 0, len = 0;
|
||||
|
||||
for (;;) {
|
||||
if (c == ' ' && !memcmp(from, "mem=", 4)) {
|
||||
unsigned long size, start;
|
||||
|
||||
if (to != command_line)
|
||||
to -= 1;
|
||||
|
||||
/*
|
||||
* If the user specifies memory size, we
|
||||
* blow away any automatically generated
|
||||
* size.
|
||||
*/
|
||||
if (usermem == 0) {
|
||||
usermem = 1;
|
||||
mi->nr_banks = 0;
|
||||
}
|
||||
|
||||
start = PHYS_OFFSET;
|
||||
size = memparse(from + 4, &from);
|
||||
if (*from == '@')
|
||||
start = memparse(from + 1, &from);
|
||||
|
||||
mi->bank[mi->nr_banks].start = start;
|
||||
mi->bank[mi->nr_banks].size = size;
|
||||
mi->bank[mi->nr_banks].node = PHYS_TO_NID(start);
|
||||
mi->nr_banks += 1;
|
||||
}
|
||||
c = *from++;
|
||||
if (!c)
|
||||
break;
|
||||
if (COMMAND_LINE_SIZE <= ++len)
|
||||
break;
|
||||
*to++ = c;
|
||||
}
|
||||
*to = '\0';
|
||||
*cmdline_p = command_line;
|
||||
}
|
||||
|
||||
static void __init
|
||||
setup_ramdisk(int doload, int prompt, int image_start, unsigned int rd_sz)
|
||||
{
|
||||
#ifdef CONFIG_BLK_DEV_RAM
|
||||
extern int rd_size, rd_image_start, rd_prompt, rd_doload;
|
||||
|
||||
rd_image_start = image_start;
|
||||
rd_prompt = prompt;
|
||||
rd_doload = doload;
|
||||
|
||||
if (rd_sz)
|
||||
rd_size = rd_sz;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void __init
|
||||
request_standard_resources(struct meminfo *mi)
|
||||
{
|
||||
struct resource *res;
|
||||
int i;
|
||||
|
||||
kernel_code.start = init_mm.start_code;
|
||||
kernel_code.end = init_mm.end_code - 1;
|
||||
#ifdef CONFIG_XIP_KERNEL
|
||||
kernel_data.start = init_mm.start_data;
|
||||
#else
|
||||
kernel_data.start = init_mm.end_code;
|
||||
#endif
|
||||
kernel_data.end = init_mm.brk - 1;
|
||||
|
||||
for (i = 0; i < mi->nr_banks; i++) {
|
||||
unsigned long virt_start, virt_end;
|
||||
|
||||
if (mi->bank[i].size == 0)
|
||||
continue;
|
||||
|
||||
virt_start = mi->bank[i].start;
|
||||
virt_end = virt_start + mi->bank[i].size - 1;
|
||||
|
||||
res = alloc_bootmem_low(sizeof(*res));
|
||||
res->name = "System RAM";
|
||||
res->start = virt_start;
|
||||
res->end = virt_end;
|
||||
res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
|
||||
|
||||
request_resource(&iomem_resource, res);
|
||||
|
||||
if (kernel_code.start >= res->start &&
|
||||
kernel_code.end <= res->end)
|
||||
request_resource(res, &kernel_code);
|
||||
if (kernel_data.start >= res->start &&
|
||||
kernel_data.end <= res->end)
|
||||
request_resource(res, &kernel_data);
|
||||
}
|
||||
|
||||
/* FIXME - needed? if (mdesc->video_start) {
|
||||
video_ram.start = mdesc->video_start;
|
||||
video_ram.end = mdesc->video_end;
|
||||
request_resource(&iomem_resource, &video_ram);
|
||||
}*/
|
||||
|
||||
/*
|
||||
* Some machines don't have the possibility of ever
|
||||
* possessing lp1 or lp2
|
||||
*/
|
||||
if (0) /* FIXME - need to do this for A5k at least */
|
||||
request_resource(&ioport_resource, &lp0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Tag parsing.
|
||||
*
|
||||
* This is the new way of passing data to the kernel at boot time. Rather
|
||||
* than passing a fixed inflexible structure to the kernel, we pass a list
|
||||
* of variable-sized tags to the kernel. The first tag must be a ATAG_CORE
|
||||
* tag for the list to be recognised (to distinguish the tagged list from
|
||||
* a param_struct). The list is terminated with a zero-length tag (this tag
|
||||
* is not parsed in any way).
|
||||
*/
|
||||
static int __init parse_tag_core(const struct tag *tag)
|
||||
{
|
||||
if (tag->hdr.size > 2) {
|
||||
if ((tag->u.core.flags & 1) == 0)
|
||||
root_mountflags &= ~MS_RDONLY;
|
||||
ROOT_DEV = old_decode_dev(tag->u.core.rootdev);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
__tagtable(ATAG_CORE, parse_tag_core);
|
||||
|
||||
static int __init parse_tag_mem32(const struct tag *tag)
|
||||
{
|
||||
if (meminfo.nr_banks >= NR_BANKS) {
|
||||
printk(KERN_WARNING
|
||||
"Ignoring memory bank 0x%08x size %dKB\n",
|
||||
tag->u.mem.start, tag->u.mem.size / 1024);
|
||||
return -EINVAL;
|
||||
}
|
||||
meminfo.bank[meminfo.nr_banks].start = tag->u.mem.start;
|
||||
meminfo.bank[meminfo.nr_banks].size = tag->u.mem.size;
|
||||
meminfo.bank[meminfo.nr_banks].node = PHYS_TO_NID(tag->u.mem.start);
|
||||
meminfo.nr_banks += 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
__tagtable(ATAG_MEM, parse_tag_mem32);
|
||||
|
||||
#if defined(CONFIG_DUMMY_CONSOLE)
|
||||
struct screen_info screen_info = {
|
||||
.orig_video_lines = 30,
|
||||
.orig_video_cols = 80,
|
||||
.orig_video_mode = 0,
|
||||
.orig_video_ega_bx = 0,
|
||||
.orig_video_isVGA = 1,
|
||||
.orig_video_points = 8
|
||||
};
|
||||
|
||||
static int __init parse_tag_videotext(const struct tag *tag)
|
||||
{
|
||||
screen_info.orig_x = tag->u.videotext.x;
|
||||
screen_info.orig_y = tag->u.videotext.y;
|
||||
screen_info.orig_video_page = tag->u.videotext.video_page;
|
||||
screen_info.orig_video_mode = tag->u.videotext.video_mode;
|
||||
screen_info.orig_video_cols = tag->u.videotext.video_cols;
|
||||
screen_info.orig_video_ega_bx = tag->u.videotext.video_ega_bx;
|
||||
screen_info.orig_video_lines = tag->u.videotext.video_lines;
|
||||
screen_info.orig_video_isVGA = tag->u.videotext.video_isvga;
|
||||
screen_info.orig_video_points = tag->u.videotext.video_points;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__tagtable(ATAG_VIDEOTEXT, parse_tag_videotext);
|
||||
#endif
|
||||
|
||||
static int __init parse_tag_acorn(const struct tag *tag)
|
||||
{
|
||||
memc_ctrl_reg = tag->u.acorn.memc_control_reg;
|
||||
number_mfm_drives = tag->u.acorn.adfsdrives;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__tagtable(ATAG_ACORN, parse_tag_acorn);
|
||||
|
||||
static int __init parse_tag_ramdisk(const struct tag *tag)
|
||||
{
|
||||
setup_ramdisk((tag->u.ramdisk.flags & 1) == 0,
|
||||
(tag->u.ramdisk.flags & 2) == 0,
|
||||
tag->u.ramdisk.start, tag->u.ramdisk.size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
__tagtable(ATAG_RAMDISK, parse_tag_ramdisk);
|
||||
|
||||
static int __init parse_tag_initrd(const struct tag *tag)
|
||||
{
|
||||
printk(KERN_WARNING "ATAG_INITRD is deprecated; please update your bootloader. \n");
|
||||
phys_initrd_start = (unsigned long)tag->u.initrd.start;
|
||||
phys_initrd_size = (unsigned long)tag->u.initrd.size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__tagtable(ATAG_INITRD, parse_tag_initrd);
|
||||
|
||||
static int __init parse_tag_initrd2(const struct tag *tag)
|
||||
{
|
||||
printk(KERN_WARNING "ATAG_INITRD is deprecated; please update your bootloader. \n");
|
||||
phys_initrd_start = (unsigned long)tag->u.initrd.start;
|
||||
phys_initrd_size = (unsigned long)tag->u.initrd.size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__tagtable(ATAG_INITRD2, parse_tag_initrd2);
|
||||
|
||||
static int __init parse_tag_serialnr(const struct tag *tag)
|
||||
{
|
||||
system_serial_low = tag->u.serialnr.low;
|
||||
system_serial_high = tag->u.serialnr.high;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__tagtable(ATAG_SERIAL, parse_tag_serialnr);
|
||||
|
||||
static int __init parse_tag_revision(const struct tag *tag)
|
||||
{
|
||||
system_rev = tag->u.revision.rev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__tagtable(ATAG_REVISION, parse_tag_revision);
|
||||
|
||||
static int __init parse_tag_cmdline(const struct tag *tag)
|
||||
{
|
||||
strncpy(default_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE);
|
||||
default_command_line[COMMAND_LINE_SIZE - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
__tagtable(ATAG_CMDLINE, parse_tag_cmdline);
|
||||
|
||||
/*
|
||||
* Scan the tag table for this tag, and call its parse function.
|
||||
* The tag table is built by the linker from all the __tagtable
|
||||
* declarations.
|
||||
*/
|
||||
static int __init parse_tag(const struct tag *tag)
|
||||
{
|
||||
extern struct tagtable __tagtable_begin, __tagtable_end;
|
||||
struct tagtable *t;
|
||||
|
||||
for (t = &__tagtable_begin; t < &__tagtable_end; t++)
|
||||
if (tag->hdr.tag == t->tag) {
|
||||
t->parse(tag);
|
||||
break;
|
||||
}
|
||||
|
||||
return t < &__tagtable_end;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse all tags in the list, checking both the global and architecture
|
||||
* specific tag tables.
|
||||
*/
|
||||
static void __init parse_tags(const struct tag *t)
|
||||
{
|
||||
for (; t->hdr.size; t = tag_next(t))
|
||||
if (!parse_tag(t))
|
||||
printk(KERN_WARNING
|
||||
"Ignoring unrecognised tag 0x%08x\n",
|
||||
t->hdr.tag);
|
||||
}
|
||||
|
||||
/*
|
||||
* This holds our defaults.
|
||||
*/
|
||||
static struct init_tags {
|
||||
struct tag_header hdr1;
|
||||
struct tag_core core;
|
||||
struct tag_header hdr2;
|
||||
struct tag_mem32 mem;
|
||||
struct tag_header hdr3;
|
||||
} init_tags __initdata = {
|
||||
{ tag_size(tag_core), ATAG_CORE },
|
||||
{ 1, PAGE_SIZE, 0xff },
|
||||
{ tag_size(tag_mem32), ATAG_MEM },
|
||||
{ MEM_SIZE, PHYS_OFFSET },
|
||||
{ 0, ATAG_NONE }
|
||||
};
|
||||
|
||||
void __init setup_arch(char **cmdline_p)
|
||||
{
|
||||
struct tag *tags = (struct tag *)&init_tags;
|
||||
char *from = default_command_line;
|
||||
|
||||
setup_processor();
|
||||
if(machine_arch_type == MACH_TYPE_A5K)
|
||||
machine_name = "A5000";
|
||||
else if(machine_arch_type == MACH_TYPE_ARCHIMEDES)
|
||||
machine_name = "Archimedes";
|
||||
else
|
||||
machine_name = "UNKNOWN";
|
||||
|
||||
//FIXME - the tag struct is always copied here but this is a block
|
||||
// of RAM that is accidentally reserved along with video RAM. perhaps
|
||||
// it would be a good idea to explicitly reserve this?
|
||||
|
||||
tags = (struct tag *)0x0207c000;
|
||||
|
||||
/*
|
||||
* If we have the old style parameters, convert them to
|
||||
* a tag list.
|
||||
*/
|
||||
if (tags->hdr.tag != ATAG_CORE)
|
||||
convert_to_tag_list(tags);
|
||||
if (tags->hdr.tag != ATAG_CORE)
|
||||
tags = (struct tag *)&init_tags;
|
||||
if (tags->hdr.tag == ATAG_CORE) {
|
||||
if (meminfo.nr_banks != 0)
|
||||
squash_mem_tags(tags);
|
||||
parse_tags(tags);
|
||||
}
|
||||
|
||||
init_mm.start_code = (unsigned long) &_text;
|
||||
#ifndef CONFIG_XIP_KERNEL
|
||||
init_mm.end_code = (unsigned long) &_etext;
|
||||
#else
|
||||
init_mm.end_code = (unsigned long) &_endtext;
|
||||
init_mm.start_data = (unsigned long) &_sdata;
|
||||
#endif
|
||||
init_mm.end_data = (unsigned long) &_edata;
|
||||
init_mm.brk = (unsigned long) &_end;
|
||||
|
||||
memcpy(saved_command_line, from, COMMAND_LINE_SIZE);
|
||||
saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
|
||||
parse_cmdline(&meminfo, cmdline_p, from);
|
||||
bootmem_init(&meminfo);
|
||||
paging_init(&meminfo);
|
||||
request_standard_resources(&meminfo);
|
||||
|
||||
#ifdef CONFIG_VT
|
||||
#if defined(CONFIG_DUMMY_CONSOLE)
|
||||
conswitchp = &dummy_con;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
static const char *hwcap_str[] = {
|
||||
"swp",
|
||||
"half",
|
||||
"thumb",
|
||||
"26bit",
|
||||
"fastmult",
|
||||
"fpa",
|
||||
"vfp",
|
||||
"edsp",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int c_show(struct seq_file *m, void *v)
|
||||
{
|
||||
int i;
|
||||
|
||||
seq_printf(m, "Processor\t: %s %s rev %d (%s)\n",
|
||||
proc_info.manufacturer, proc_info.cpu_name,
|
||||
(int)processor_id & 15, elf_platform);
|
||||
|
||||
seq_printf(m, "BogoMIPS\t: %lu.%02lu\n",
|
||||
loops_per_jiffy / (500000/HZ),
|
||||
(loops_per_jiffy / (5000/HZ)) % 100);
|
||||
|
||||
/* dump out the processor features */
|
||||
seq_puts(m, "Features\t: ");
|
||||
|
||||
for (i = 0; hwcap_str[i]; i++)
|
||||
if (elf_hwcap & (1 << i))
|
||||
seq_printf(m, "%s ", hwcap_str[i]);
|
||||
|
||||
seq_puts(m, "\n");
|
||||
|
||||
seq_printf(m, "CPU part\t\t: %07x\n", processor_id >> 4);
|
||||
seq_printf(m, "CPU revision\t: %d\n\n", processor_id & 15);
|
||||
seq_printf(m, "Hardware\t: %s\n", machine_name);
|
||||
seq_printf(m, "Revision\t: %04x\n", system_rev);
|
||||
seq_printf(m, "Serial\t\t: %08x%08x\n",
|
||||
system_serial_high, system_serial_low);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *c_start(struct seq_file *m, loff_t *pos)
|
||||
{
|
||||
return *pos < 1 ? (void *)1 : NULL;
|
||||
}
|
||||
|
||||
static void *c_next(struct seq_file *m, void *v, loff_t *pos)
|
||||
{
|
||||
++*pos;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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 = c_show
|
||||
};
|
540
arch/arm26/kernel/signal.c
Normal file
540
arch/arm26/kernel/signal.c
Normal file
@@ -0,0 +1,540 @@
|
||||
/*
|
||||
* linux/arch/arm26/kernel/signal.c
|
||||
*
|
||||
* Copyright (C) 1995-2002 Russell King
|
||||
* Copyright (C) 2003 Ian Molton (ARM26)
|
||||
*
|
||||
* FIXME!!! This is probably very broken (13/05/2003)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/config.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/personality.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/binfmts.h>
|
||||
#include <linux/elf.h>
|
||||
|
||||
#include <asm/pgalloc.h>
|
||||
#include <asm/ucontext.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/unistd.h>
|
||||
|
||||
#include "ptrace.h"
|
||||
|
||||
#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
|
||||
|
||||
/*
|
||||
* For ARM syscalls, we encode the syscall number into the instruction.
|
||||
*/
|
||||
#define SWI_SYS_SIGRETURN (0xef000000|(__NR_sigreturn))
|
||||
#define SWI_SYS_RT_SIGRETURN (0xef000000|(__NR_rt_sigreturn))
|
||||
|
||||
static int do_signal(sigset_t *oldset, struct pt_regs * regs, int syscall);
|
||||
|
||||
/*
|
||||
* atomically swap in the new signal mask, and wait for a signal.
|
||||
*/
|
||||
asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, old_sigset_t mask, struct pt_regs *regs)
|
||||
{
|
||||
sigset_t saveset;
|
||||
|
||||
mask &= _BLOCKABLE;
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
saveset = current->blocked;
|
||||
siginitset(¤t->blocked, mask);
|
||||
recalc_sigpending();
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
regs->ARM_r0 = -EINTR;
|
||||
|
||||
while (1) {
|
||||
current->state = TASK_INTERRUPTIBLE;
|
||||
schedule();
|
||||
if (do_signal(&saveset, regs, 0))
|
||||
return regs->ARM_r0;
|
||||
}
|
||||
}
|
||||
|
||||
asmlinkage int
|
||||
sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, struct pt_regs *regs)
|
||||
{
|
||||
sigset_t saveset, newset;
|
||||
|
||||
/* XXX: Don't preclude handling different sized sigset_t's. */
|
||||
if (sigsetsize != sizeof(sigset_t))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(&newset, unewset, sizeof(newset)))
|
||||
return -EFAULT;
|
||||
sigdelsetmask(&newset, ~_BLOCKABLE);
|
||||
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
saveset = current->blocked;
|
||||
current->blocked = newset;
|
||||
recalc_sigpending();
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
regs->ARM_r0 = -EINTR;
|
||||
|
||||
while (1) {
|
||||
current->state = TASK_INTERRUPTIBLE;
|
||||
schedule();
|
||||
if (do_signal(&saveset, regs, 0))
|
||||
return regs->ARM_r0;
|
||||
}
|
||||
}
|
||||
|
||||
asmlinkage int
|
||||
sys_sigaction(int sig, const struct old_sigaction *act,
|
||||
struct old_sigaction *oact)
|
||||
{
|
||||
struct k_sigaction new_ka, old_ka;
|
||||
int ret;
|
||||
|
||||
if (act) {
|
||||
old_sigset_t mask;
|
||||
if (!access_ok(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 (!access_ok(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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do a signal return; undo the signal stack.
|
||||
*/
|
||||
struct sigframe
|
||||
{
|
||||
struct sigcontext sc;
|
||||
unsigned long extramask[_NSIG_WORDS-1];
|
||||
unsigned long retcode;
|
||||
};
|
||||
|
||||
struct rt_sigframe
|
||||
{
|
||||
struct siginfo *pinfo;
|
||||
void *puc;
|
||||
struct siginfo info;
|
||||
struct ucontext uc;
|
||||
unsigned long retcode;
|
||||
};
|
||||
|
||||
static int
|
||||
restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
__get_user_error(regs->ARM_r0, &sc->arm_r0, err);
|
||||
__get_user_error(regs->ARM_r1, &sc->arm_r1, err);
|
||||
__get_user_error(regs->ARM_r2, &sc->arm_r2, err);
|
||||
__get_user_error(regs->ARM_r3, &sc->arm_r3, err);
|
||||
__get_user_error(regs->ARM_r4, &sc->arm_r4, err);
|
||||
__get_user_error(regs->ARM_r5, &sc->arm_r5, err);
|
||||
__get_user_error(regs->ARM_r6, &sc->arm_r6, err);
|
||||
__get_user_error(regs->ARM_r7, &sc->arm_r7, err);
|
||||
__get_user_error(regs->ARM_r8, &sc->arm_r8, err);
|
||||
__get_user_error(regs->ARM_r9, &sc->arm_r9, err);
|
||||
__get_user_error(regs->ARM_r10, &sc->arm_r10, err);
|
||||
__get_user_error(regs->ARM_fp, &sc->arm_fp, err);
|
||||
__get_user_error(regs->ARM_ip, &sc->arm_ip, err);
|
||||
__get_user_error(regs->ARM_sp, &sc->arm_sp, err);
|
||||
__get_user_error(regs->ARM_lr, &sc->arm_lr, err);
|
||||
__get_user_error(regs->ARM_pc, &sc->arm_pc, err);
|
||||
|
||||
err |= !valid_user_regs(regs);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
asmlinkage int sys_sigreturn(struct pt_regs *regs)
|
||||
{
|
||||
struct sigframe *frame;
|
||||
sigset_t set;
|
||||
|
||||
/*
|
||||
* Since we stacked the signal on a 64-bit boundary,
|
||||
* then 'sp' should be word aligned here. If it's
|
||||
* not, then the user is trying to mess with us.
|
||||
*/
|
||||
if (regs->ARM_sp & 7)
|
||||
goto badframe;
|
||||
|
||||
frame = (struct sigframe *)regs->ARM_sp;
|
||||
|
||||
if (!access_ok(VERIFY_READ, frame, sizeof (*frame)))
|
||||
goto badframe;
|
||||
if (__get_user(set.sig[0], &frame->sc.oldmask)
|
||||
|| (_NSIG_WORDS > 1
|
||||
&& __copy_from_user(&set.sig[1], &frame->extramask,
|
||||
sizeof(frame->extramask))))
|
||||
goto badframe;
|
||||
|
||||
sigdelsetmask(&set, ~_BLOCKABLE);
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
current->blocked = set;
|
||||
recalc_sigpending();
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
|
||||
if (restore_sigcontext(regs, &frame->sc))
|
||||
goto badframe;
|
||||
|
||||
/* Send SIGTRAP if we're single-stepping */
|
||||
if (current->ptrace & PT_SINGLESTEP) {
|
||||
ptrace_cancel_bpt(current);
|
||||
send_sig(SIGTRAP, current, 1);
|
||||
}
|
||||
|
||||
return regs->ARM_r0;
|
||||
|
||||
badframe:
|
||||
force_sig(SIGSEGV, current);
|
||||
return 0;
|
||||
}
|
||||
|
||||
asmlinkage int sys_rt_sigreturn(struct pt_regs *regs)
|
||||
{
|
||||
struct rt_sigframe *frame;
|
||||
sigset_t set;
|
||||
|
||||
/*
|
||||
* Since we stacked the signal on a 64-bit boundary,
|
||||
* then 'sp' should be word aligned here. If it's
|
||||
* not, then the user is trying to mess with us.
|
||||
*/
|
||||
if (regs->ARM_sp & 7)
|
||||
goto badframe;
|
||||
|
||||
frame = (struct rt_sigframe *)regs->ARM_sp;
|
||||
|
||||
if (!access_ok(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(¤t->sighand->siglock);
|
||||
current->blocked = set;
|
||||
recalc_sigpending();
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
|
||||
if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
|
||||
goto badframe;
|
||||
|
||||
/* Send SIGTRAP if we're single-stepping */
|
||||
if (current->ptrace & PT_SINGLESTEP) {
|
||||
ptrace_cancel_bpt(current);
|
||||
send_sig(SIGTRAP, current, 1);
|
||||
}
|
||||
|
||||
return regs->ARM_r0;
|
||||
|
||||
badframe:
|
||||
force_sig(SIGSEGV, current);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
setup_sigcontext(struct sigcontext *sc, /*struct _fpstate *fpstate,*/
|
||||
struct pt_regs *regs, unsigned long mask)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
__put_user_error(regs->ARM_r0, &sc->arm_r0, err);
|
||||
__put_user_error(regs->ARM_r1, &sc->arm_r1, err);
|
||||
__put_user_error(regs->ARM_r2, &sc->arm_r2, err);
|
||||
__put_user_error(regs->ARM_r3, &sc->arm_r3, err);
|
||||
__put_user_error(regs->ARM_r4, &sc->arm_r4, err);
|
||||
__put_user_error(regs->ARM_r5, &sc->arm_r5, err);
|
||||
__put_user_error(regs->ARM_r6, &sc->arm_r6, err);
|
||||
__put_user_error(regs->ARM_r7, &sc->arm_r7, err);
|
||||
__put_user_error(regs->ARM_r8, &sc->arm_r8, err);
|
||||
__put_user_error(regs->ARM_r9, &sc->arm_r9, err);
|
||||
__put_user_error(regs->ARM_r10, &sc->arm_r10, err);
|
||||
__put_user_error(regs->ARM_fp, &sc->arm_fp, err);
|
||||
__put_user_error(regs->ARM_ip, &sc->arm_ip, err);
|
||||
__put_user_error(regs->ARM_sp, &sc->arm_sp, err);
|
||||
__put_user_error(regs->ARM_lr, &sc->arm_lr, err);
|
||||
__put_user_error(regs->ARM_pc, &sc->arm_pc, err);
|
||||
|
||||
__put_user_error(current->thread.trap_no, &sc->trap_no, err);
|
||||
__put_user_error(current->thread.error_code, &sc->error_code, err);
|
||||
__put_user_error(current->thread.address, &sc->fault_address, err);
|
||||
__put_user_error(mask, &sc->oldmask, err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline void *
|
||||
get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, int framesize)
|
||||
{
|
||||
unsigned long sp = regs->ARM_sp;
|
||||
|
||||
/*
|
||||
* This is the X/Open sanctioned signal stack switching.
|
||||
*/
|
||||
if ((ka->sa.sa_flags & SA_ONSTACK) && !sas_ss_flags(sp))
|
||||
sp = current->sas_ss_sp + current->sas_ss_size;
|
||||
|
||||
/*
|
||||
* ATPCS B01 mandates 8-byte alignment
|
||||
*/
|
||||
return (void *)((sp - framesize) & ~7);
|
||||
}
|
||||
|
||||
static int
|
||||
setup_return(struct pt_regs *regs, struct k_sigaction *ka,
|
||||
unsigned long *rc, void *frame, int usig)
|
||||
{
|
||||
unsigned long handler = (unsigned long)ka->sa.sa_handler;
|
||||
unsigned long retcode;
|
||||
|
||||
if (ka->sa.sa_flags & SA_RESTORER) {
|
||||
retcode = (unsigned long)ka->sa.sa_restorer;
|
||||
} else {
|
||||
|
||||
if (__put_user((ka->sa.sa_flags & SA_SIGINFO)?SWI_SYS_RT_SIGRETURN:SWI_SYS_SIGRETURN, rc))
|
||||
return 1;
|
||||
|
||||
retcode = ((unsigned long)rc);
|
||||
}
|
||||
|
||||
regs->ARM_r0 = usig;
|
||||
regs->ARM_sp = (unsigned long)frame;
|
||||
regs->ARM_lr = retcode;
|
||||
regs->ARM_pc = handler & ~3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
setup_frame(int usig, struct k_sigaction *ka, sigset_t *set, struct pt_regs *regs)
|
||||
{
|
||||
struct sigframe *frame = get_sigframe(ka, regs, sizeof(*frame));
|
||||
int err = 0;
|
||||
|
||||
if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame)))
|
||||
return 1;
|
||||
|
||||
err |= setup_sigcontext(&frame->sc, /*&frame->fpstate,*/ regs, set->sig[0]);
|
||||
|
||||
if (_NSIG_WORDS > 1) {
|
||||
err |= __copy_to_user(frame->extramask, &set->sig[1],
|
||||
sizeof(frame->extramask));
|
||||
}
|
||||
|
||||
if (err == 0)
|
||||
err = setup_return(regs, ka, &frame->retcode, frame, usig);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info,
|
||||
sigset_t *set, struct pt_regs *regs)
|
||||
{
|
||||
struct rt_sigframe *frame = get_sigframe(ka, regs, sizeof(*frame));
|
||||
int err = 0;
|
||||
|
||||
if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame)))
|
||||
return 1;
|
||||
|
||||
__put_user_error(&frame->info, &frame->pinfo, err);
|
||||
__put_user_error(&frame->uc, &frame->puc, err);
|
||||
err |= copy_siginfo_to_user(&frame->info, info);
|
||||
|
||||
/* Clear all the bits of the ucontext we don't use. */
|
||||
err |= __clear_user(&frame->uc, offsetof(struct ucontext, uc_mcontext));
|
||||
|
||||
err |= setup_sigcontext(&frame->uc.uc_mcontext, /*&frame->fpstate,*/
|
||||
regs, set->sig[0]);
|
||||
err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
|
||||
|
||||
if (err == 0)
|
||||
err = setup_return(regs, ka, &frame->retcode, frame, usig);
|
||||
|
||||
if (err == 0) {
|
||||
/*
|
||||
* For realtime signals we must also set the second and third
|
||||
* arguments for the signal handler.
|
||||
* -- Peter Maydell <pmaydell@chiark.greenend.org.uk> 2000-12-06
|
||||
*/
|
||||
regs->ARM_r1 = (unsigned long)frame->pinfo;
|
||||
regs->ARM_r2 = (unsigned long)frame->puc;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline void restart_syscall(struct pt_regs *regs)
|
||||
{
|
||||
regs->ARM_r0 = regs->ARM_ORIG_r0;
|
||||
regs->ARM_pc -= 4;
|
||||
}
|
||||
|
||||
/*
|
||||
* OK, we're invoking a handler
|
||||
*/
|
||||
static void
|
||||
handle_signal(unsigned long sig, siginfo_t *info, sigset_t *oldset,
|
||||
struct pt_regs * regs, int syscall)
|
||||
{
|
||||
struct thread_info *thread = current_thread_info();
|
||||
struct task_struct *tsk = current;
|
||||
struct k_sigaction *ka = &tsk->sighand->action[sig-1];
|
||||
int usig = sig;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* If we were from a system call, check for system call restarting...
|
||||
*/
|
||||
if (syscall) {
|
||||
switch (regs->ARM_r0) {
|
||||
case -ERESTART_RESTARTBLOCK:
|
||||
current_thread_info()->restart_block.fn =
|
||||
do_no_restart_syscall;
|
||||
case -ERESTARTNOHAND:
|
||||
regs->ARM_r0 = -EINTR;
|
||||
break;
|
||||
case -ERESTARTSYS:
|
||||
if (!(ka->sa.sa_flags & SA_RESTART)) {
|
||||
regs->ARM_r0 = -EINTR;
|
||||
break;
|
||||
}
|
||||
/* fallthrough */
|
||||
case -ERESTARTNOINTR:
|
||||
restart_syscall(regs);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* translate the signal
|
||||
*/
|
||||
if (usig < 32 && thread->exec_domain && thread->exec_domain->signal_invmap)
|
||||
usig = thread->exec_domain->signal_invmap[usig];
|
||||
|
||||
/*
|
||||
* Set up the stack frame
|
||||
*/
|
||||
if (ka->sa.sa_flags & SA_SIGINFO)
|
||||
ret = setup_rt_frame(usig, ka, info, oldset, regs);
|
||||
else
|
||||
ret = setup_frame(usig, ka, oldset, regs);
|
||||
|
||||
/*
|
||||
* Check that the resulting registers are actually sane.
|
||||
*/
|
||||
ret |= !valid_user_regs(regs);
|
||||
|
||||
if (ret == 0) {
|
||||
if (ka->sa.sa_flags & SA_ONESHOT)
|
||||
ka->sa.sa_handler = SIG_DFL;
|
||||
|
||||
if (!(ka->sa.sa_flags & SA_NODEFER)) {
|
||||
spin_lock_irq(&tsk->sighand->siglock);
|
||||
sigorsets(&tsk->blocked, &tsk->blocked,
|
||||
&ka->sa.sa_mask);
|
||||
sigaddset(&tsk->blocked, sig);
|
||||
recalc_sigpending();
|
||||
spin_unlock_irq(&tsk->sighand->siglock);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
force_sigsegv(sig, tsk);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that 'init' is a special process: it doesn't get signals it doesn't
|
||||
* want to handle. Thus you cannot kill init even with a SIGKILL even by
|
||||
* mistake.
|
||||
*
|
||||
* Note that we go through the signals twice: once to check the signals that
|
||||
* the kernel can handle, and then we build all the user-level signal handling
|
||||
* stack-frames in one go after that.
|
||||
*/
|
||||
static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall)
|
||||
{
|
||||
siginfo_t info;
|
||||
int signr;
|
||||
|
||||
/*
|
||||
* We want the common case to go fast, which
|
||||
* is why we may in certain cases get here from
|
||||
* kernel mode. Just return without doing anything
|
||||
* if so.
|
||||
*/
|
||||
if (!user_mode(regs))
|
||||
return 0;
|
||||
|
||||
if (current->ptrace & PT_SINGLESTEP)
|
||||
ptrace_cancel_bpt(current);
|
||||
|
||||
signr = get_signal_to_deliver(&info, regs, NULL);
|
||||
if (signr > 0) {
|
||||
handle_signal(signr, &info, oldset, regs, syscall);
|
||||
if (current->ptrace & PT_SINGLESTEP)
|
||||
ptrace_set_bpt(current);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* No signal to deliver to the process - restart the syscall.
|
||||
*/
|
||||
if (syscall) {
|
||||
if (regs->ARM_r0 == -ERESTART_RESTARTBLOCK) {
|
||||
u32 *usp;
|
||||
|
||||
regs->ARM_sp -= 12;
|
||||
usp = (u32 *)regs->ARM_sp;
|
||||
|
||||
put_user(regs->ARM_pc, &usp[0]);
|
||||
/* swi __NR_restart_syscall */
|
||||
put_user(0xef000000 | __NR_restart_syscall, &usp[1]);
|
||||
/* ldr pc, [sp], #12 */
|
||||
// FIXME!!! is #12 correct there?
|
||||
put_user(0xe49df00c, &usp[2]);
|
||||
|
||||
regs->ARM_pc = regs->ARM_sp + 4;
|
||||
}
|
||||
if (regs->ARM_r0 == -ERESTARTNOHAND ||
|
||||
regs->ARM_r0 == -ERESTARTSYS ||
|
||||
regs->ARM_r0 == -ERESTARTNOINTR) {
|
||||
restart_syscall(regs);
|
||||
}
|
||||
}
|
||||
if (current->ptrace & PT_SINGLESTEP)
|
||||
ptrace_set_bpt(current);
|
||||
return 0;
|
||||
}
|
||||
|
||||
asmlinkage void
|
||||
do_notify_resume(struct pt_regs *regs, unsigned int thread_flags, int syscall)
|
||||
{
|
||||
if (thread_flags & _TIF_SIGPENDING)
|
||||
do_signal(¤t->blocked, regs, syscall);
|
||||
}
|
324
arch/arm26/kernel/sys_arm.c
Normal file
324
arch/arm26/kernel/sys_arm.c
Normal file
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
* linux/arch/arm26/kernel/sys_arm.c
|
||||
*
|
||||
* Copyright (C) People who wrote linux/arch/i386/kernel/sys_i386.c
|
||||
* Copyright (C) 1995, 1996 Russell King.
|
||||
* Copyright (C) 2003 Ian Molton.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This file contains various random system calls that
|
||||
* have a non-standard calling sequence on the Linux/arm
|
||||
* platform.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/sem.h>
|
||||
#include <linux/msg.h>
|
||||
#include <linux/shm.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/mman.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/utsname.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/ipc.h>
|
||||
|
||||
extern unsigned long do_mremap(unsigned long addr, unsigned long old_len,
|
||||
unsigned long new_len, unsigned long flags,
|
||||
unsigned long new_addr);
|
||||
|
||||
/*
|
||||
* sys_pipe() is the normal C calling standard for creating
|
||||
* a pipe. It's not the way unix traditionally does this, though.
|
||||
*/
|
||||
asmlinkage int sys_pipe(unsigned long * fildes)
|
||||
{
|
||||
int fd[2];
|
||||
int error;
|
||||
|
||||
error = do_pipe(fd);
|
||||
if (!error) {
|
||||
if (copy_to_user(fildes, fd, 2*sizeof(int)))
|
||||
error = -EFAULT;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
/* common code for old and new mmaps */
|
||||
inline long do_mmap2(
|
||||
unsigned long addr, unsigned long len,
|
||||
unsigned long prot, unsigned long flags,
|
||||
unsigned long fd, unsigned long pgoff)
|
||||
{
|
||||
int error = -EINVAL;
|
||||
struct file * file = NULL;
|
||||
|
||||
flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
|
||||
|
||||
/*
|
||||
* If we are doing a fixed mapping, and address < PAGE_SIZE,
|
||||
* then deny it.
|
||||
*/
|
||||
if (flags & MAP_FIXED && addr < PAGE_SIZE && vectors_base() == 0)
|
||||
goto out;
|
||||
|
||||
error = -EBADF;
|
||||
if (!(flags & MAP_ANONYMOUS)) {
|
||||
file = fget(fd);
|
||||
if (!file)
|
||||
goto out;
|
||||
}
|
||||
|
||||
down_write(¤t->mm->mmap_sem);
|
||||
error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
|
||||
up_write(¤t->mm->mmap_sem);
|
||||
|
||||
if (file)
|
||||
fput(file);
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
struct mmap_arg_struct {
|
||||
unsigned long addr;
|
||||
unsigned long len;
|
||||
unsigned long prot;
|
||||
unsigned long flags;
|
||||
unsigned long fd;
|
||||
unsigned long offset;
|
||||
};
|
||||
|
||||
asmlinkage int old_mmap(struct mmap_arg_struct *arg)
|
||||
{
|
||||
int error = -EFAULT;
|
||||
struct mmap_arg_struct a;
|
||||
|
||||
if (copy_from_user(&a, arg, sizeof(a)))
|
||||
goto out;
|
||||
|
||||
error = -EINVAL;
|
||||
if (a.offset & ~PAGE_MASK)
|
||||
goto out;
|
||||
|
||||
error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT);
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
asmlinkage unsigned long
|
||||
sys_arm_mremap(unsigned long addr, unsigned long old_len,
|
||||
unsigned long new_len, unsigned long flags,
|
||||
unsigned long new_addr)
|
||||
{
|
||||
unsigned long ret = -EINVAL;
|
||||
|
||||
/*
|
||||
* If we are doing a fixed mapping, and address < PAGE_SIZE,
|
||||
* then deny it.
|
||||
*/
|
||||
if (flags & MREMAP_FIXED && new_addr < PAGE_SIZE &&
|
||||
vectors_base() == 0)
|
||||
goto out;
|
||||
|
||||
down_write(¤t->mm->mmap_sem);
|
||||
ret = do_mremap(addr, old_len, new_len, flags, new_addr);
|
||||
up_write(¤t->mm->mmap_sem);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform the select(nd, in, out, ex, tv) and mmap() system
|
||||
* calls.
|
||||
*/
|
||||
|
||||
struct sel_arg_struct {
|
||||
unsigned long n;
|
||||
fd_set *inp, *outp, *exp;
|
||||
struct timeval *tvp;
|
||||
};
|
||||
|
||||
asmlinkage int old_select(struct sel_arg_struct *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 int sys_ipc (uint call, int first, int second, int third, void *ptr, long fifth)
|
||||
{
|
||||
int version, ret;
|
||||
|
||||
version = call >> 16; /* hack for backward compatibility */
|
||||
call &= 0xffff;
|
||||
|
||||
switch (call) {
|
||||
case SEMOP:
|
||||
return sys_semop (first, (struct sembuf *)ptr, second);
|
||||
case SEMGET:
|
||||
return sys_semget (first, second, third);
|
||||
case SEMCTL: {
|
||||
union semun fourth;
|
||||
if (!ptr)
|
||||
return -EINVAL;
|
||||
if (get_user(fourth.__pad, (void **) ptr))
|
||||
return -EFAULT;
|
||||
return sys_semctl (first, second, third, fourth);
|
||||
}
|
||||
|
||||
case MSGSND:
|
||||
return sys_msgsnd (first, (struct msgbuf *) 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 *) ptr,
|
||||
sizeof (tmp)))
|
||||
return -EFAULT;
|
||||
return sys_msgrcv (first, tmp.msgp, second,
|
||||
tmp.msgtyp, third);
|
||||
}
|
||||
default:
|
||||
return sys_msgrcv (first,
|
||||
(struct msgbuf *) ptr,
|
||||
second, fifth, third);
|
||||
}
|
||||
case MSGGET:
|
||||
return sys_msgget ((key_t) first, second);
|
||||
case MSGCTL:
|
||||
return sys_msgctl (first, second, (struct msqid_ds *) ptr);
|
||||
|
||||
case SHMAT:
|
||||
switch (version) {
|
||||
default: {
|
||||
ulong raddr;
|
||||
ret = do_shmat (first, (char *) 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 *) ptr,
|
||||
second, (ulong *) third);
|
||||
}
|
||||
case SHMDT:
|
||||
return sys_shmdt ((char *)ptr);
|
||||
case SHMGET:
|
||||
return sys_shmget (first, second, third);
|
||||
case SHMCTL:
|
||||
return sys_shmctl (first, second,
|
||||
(struct shmid_ds *) ptr);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fork a new task - this creates a new program thread.
|
||||
* This is called indirectly via a small wrapper
|
||||
*/
|
||||
asmlinkage int sys_fork(struct pt_regs *regs)
|
||||
{
|
||||
return do_fork(SIGCHLD, regs->ARM_sp, regs, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
/* Clone a task - this clones the calling program thread.
|
||||
* This is called indirectly via a small wrapper
|
||||
*/
|
||||
asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp, struct pt_regs *regs)
|
||||
{
|
||||
/*
|
||||
* We don't support SETTID / CLEARTID (FIXME!!! (nicked from arm32))
|
||||
*/
|
||||
if (clone_flags & (CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID))
|
||||
return -EINVAL;
|
||||
|
||||
if (!newsp)
|
||||
newsp = regs->ARM_sp;
|
||||
|
||||
return do_fork(clone_flags, newsp, regs, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
asmlinkage int sys_vfork(struct pt_regs *regs)
|
||||
{
|
||||
return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->ARM_sp, regs, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
/* sys_execve() executes a new program.
|
||||
* This is called indirectly via a small wrapper
|
||||
*/
|
||||
asmlinkage int sys_execve(char *filenamei, char **argv, char **envp, struct pt_regs *regs)
|
||||
{
|
||||
int error;
|
||||
char * filename;
|
||||
|
||||
filename = getname(filenamei);
|
||||
error = PTR_ERR(filename);
|
||||
if (IS_ERR(filename))
|
||||
goto out;
|
||||
error = do_execve(filename, argv, envp, regs);
|
||||
putname(filename);
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
/* FIXME - see if this is correct for arm26 */
|
||||
long execve(const char *filename, char **argv, char **envp)
|
||||
{
|
||||
struct pt_regs regs;
|
||||
int ret;
|
||||
memset(®s, 0, sizeof(struct pt_regs));
|
||||
ret = do_execve((char *)filename, (char __user * __user *)argv, (char __user * __user *)envp, ®s);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Save argc to the register structure for userspace.
|
||||
*/
|
||||
regs.ARM_r0 = ret;
|
||||
|
||||
/*
|
||||
* We were successful. We won't be returning to our caller, but
|
||||
* instead to user space by manipulating the kernel stack.
|
||||
*/
|
||||
asm( "add r0, %0, %1\n\t"
|
||||
"mov r1, %2\n\t"
|
||||
"mov r2, %3\n\t"
|
||||
"bl memmove\n\t" /* copy regs to top of stack */
|
||||
"mov r8, #0\n\t" /* not a syscall */
|
||||
"mov r9, %0\n\t" /* thread structure */
|
||||
"mov sp, r0\n\t" /* reposition stack pointer */
|
||||
"b ret_to_user"
|
||||
:
|
||||
: "r" (current_thread_info()),
|
||||
"Ir" (THREAD_SIZE - 8 - sizeof(regs)),
|
||||
"r" (®s),
|
||||
"Ir" (sizeof(regs))
|
||||
: "r0", "r1", "r2", "r3", "ip", "memory");
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(execve);
|
234
arch/arm26/kernel/time.c
Normal file
234
arch/arm26/kernel/time.c
Normal file
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* linux/arch/arm26/kernel/time.c
|
||||
*
|
||||
* Copyright (C) 1991, 1992, 1995 Linus Torvalds
|
||||
* Modifications for ARM (C) 1994-2001 Russell King
|
||||
* Mods for ARM26 (C) 2003 Ian Molton
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This file contains the ARM-specific time handling details:
|
||||
* reading the RTC at bootup, etc...
|
||||
*
|
||||
* 1994-07-02 Alan Modra
|
||||
* fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime
|
||||
* 1998-12-20 Updated NTP code according to technical memorandum Jan '96
|
||||
* "A Kernel Model for Precision Timekeeping" by Dave Mills
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/timex.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/profile.h>
|
||||
|
||||
#include <asm/hardware.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/ioc.h>
|
||||
|
||||
u64 jiffies_64 = INITIAL_JIFFIES;
|
||||
|
||||
EXPORT_SYMBOL(jiffies_64);
|
||||
|
||||
extern unsigned long wall_jiffies;
|
||||
|
||||
/* this needs a better home */
|
||||
DEFINE_SPINLOCK(rtc_lock);
|
||||
|
||||
/* change this if you have some constant time drift */
|
||||
#define USECS_PER_JIFFY (1000000/HZ)
|
||||
|
||||
static int dummy_set_rtc(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* hook for setting the RTC's idea of the current time.
|
||||
*/
|
||||
int (*set_rtc)(void) = dummy_set_rtc;
|
||||
|
||||
/*
|
||||
* Get time offset based on IOCs timer.
|
||||
* FIXME - if this is called with interrutps off, why the shennanigans
|
||||
* below ?
|
||||
*/
|
||||
static unsigned long gettimeoffset(void)
|
||||
{
|
||||
unsigned int count1, count2, status;
|
||||
long offset;
|
||||
|
||||
ioc_writeb (0, IOC_T0LATCH);
|
||||
barrier ();
|
||||
count1 = ioc_readb(IOC_T0CNTL) | (ioc_readb(IOC_T0CNTH) << 8);
|
||||
barrier ();
|
||||
status = ioc_readb(IOC_IRQREQA);
|
||||
barrier ();
|
||||
ioc_writeb (0, IOC_T0LATCH);
|
||||
barrier ();
|
||||
count2 = ioc_readb(IOC_T0CNTL) | (ioc_readb(IOC_T0CNTH) << 8);
|
||||
|
||||
offset = count2;
|
||||
if (count2 < count1) {
|
||||
/*
|
||||
* We have not had an interrupt between reading count1
|
||||
* and count2.
|
||||
*/
|
||||
if (status & (1 << 5))
|
||||
offset -= LATCH;
|
||||
} else if (count2 > count1) {
|
||||
/*
|
||||
* We have just had another interrupt between reading
|
||||
* count1 and count2.
|
||||
*/
|
||||
offset -= LATCH;
|
||||
}
|
||||
|
||||
offset = (LATCH - offset) * (tick_nsec / 1000);
|
||||
return (offset + LATCH/2) / LATCH;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scheduler clock - returns current time in nanosec units.
|
||||
*/
|
||||
unsigned long long sched_clock(void)
|
||||
{
|
||||
return (unsigned long long)jiffies * (1000000000 / HZ);
|
||||
}
|
||||
|
||||
static unsigned long next_rtc_update;
|
||||
|
||||
/*
|
||||
* If we have an externally synchronized linux clock, then update
|
||||
* CMOS clock accordingly every ~11 minutes. set_rtc() has to be
|
||||
* called as close as possible to 500 ms before the new second
|
||||
* starts.
|
||||
*/
|
||||
static inline void do_set_rtc(void)
|
||||
{
|
||||
if (time_status & STA_UNSYNC || set_rtc == NULL)
|
||||
return;
|
||||
|
||||
//FIXME - timespec.tv_sec is a time_t not unsigned long
|
||||
if (next_rtc_update &&
|
||||
time_before((unsigned long)xtime.tv_sec, next_rtc_update))
|
||||
return;
|
||||
|
||||
if (xtime.tv_nsec < 500000000 - ((unsigned) tick_nsec >> 1) &&
|
||||
xtime.tv_nsec >= 500000000 + ((unsigned) tick_nsec >> 1))
|
||||
return;
|
||||
|
||||
if (set_rtc())
|
||||
/*
|
||||
* rtc update failed. Try again in 60s
|
||||
*/
|
||||
next_rtc_update = xtime.tv_sec + 60;
|
||||
else
|
||||
next_rtc_update = xtime.tv_sec + 660;
|
||||
}
|
||||
|
||||
#define do_leds()
|
||||
|
||||
void do_gettimeofday(struct timeval *tv)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long seq;
|
||||
unsigned long usec, sec, lost;
|
||||
|
||||
do {
|
||||
seq = read_seqbegin_irqsave(&xtime_lock, flags);
|
||||
usec = gettimeoffset();
|
||||
|
||||
lost = jiffies - wall_jiffies;
|
||||
if (lost)
|
||||
usec += lost * USECS_PER_JIFFY;
|
||||
|
||||
sec = xtime.tv_sec;
|
||||
usec += xtime.tv_nsec / 1000;
|
||||
} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
|
||||
|
||||
/* usec may have gone up a lot: be safe */
|
||||
while (usec >= 1000000) {
|
||||
usec -= 1000000;
|
||||
sec++;
|
||||
}
|
||||
|
||||
tv->tv_sec = sec;
|
||||
tv->tv_usec = usec;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(do_gettimeofday);
|
||||
|
||||
int do_settimeofday(struct timespec *tv)
|
||||
{
|
||||
if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
|
||||
return -EINVAL;
|
||||
|
||||
write_seqlock_irq(&xtime_lock);
|
||||
/*
|
||||
* This is revolting. We need to set "xtime" correctly. However, the
|
||||
* value in this location is the value at the most recent update of
|
||||
* wall time. Discover what correction gettimeofday() would have
|
||||
* done, and then undo it!
|
||||
*/
|
||||
tv->tv_nsec -= 1000 * (gettimeoffset() +
|
||||
(jiffies - wall_jiffies) * USECS_PER_JIFFY);
|
||||
|
||||
while (tv->tv_nsec < 0) {
|
||||
tv->tv_nsec += NSEC_PER_SEC;
|
||||
tv->tv_sec--;
|
||||
}
|
||||
|
||||
xtime.tv_sec = tv->tv_sec;
|
||||
xtime.tv_nsec = tv->tv_nsec;
|
||||
time_adjust = 0; /* stop active adjtime() */
|
||||
time_status |= STA_UNSYNC;
|
||||
time_maxerror = NTP_PHASE_LIMIT;
|
||||
time_esterror = NTP_PHASE_LIMIT;
|
||||
write_sequnlock_irq(&xtime_lock);
|
||||
clock_was_set();
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(do_settimeofday);
|
||||
|
||||
static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
|
||||
{
|
||||
do_timer(regs);
|
||||
#ifndef CONFIG_SMP
|
||||
update_process_times(user_mode(regs));
|
||||
#endif
|
||||
do_set_rtc(); //FIME - EVERY timer IRQ?
|
||||
profile_tick(CPU_PROFILING, regs);
|
||||
return IRQ_HANDLED; //FIXME - is this right?
|
||||
}
|
||||
|
||||
static struct irqaction timer_irq = {
|
||||
.name = "timer",
|
||||
.flags = SA_INTERRUPT,
|
||||
.handler = timer_interrupt,
|
||||
};
|
||||
|
||||
extern void ioctime_init(void);
|
||||
|
||||
/*
|
||||
* Set up timer interrupt.
|
||||
*/
|
||||
void __init time_init(void)
|
||||
{
|
||||
ioc_writeb(LATCH & 255, IOC_T0LTCHL);
|
||||
ioc_writeb(LATCH >> 8, IOC_T0LTCHH);
|
||||
ioc_writeb(0, IOC_T0GO);
|
||||
|
||||
|
||||
setup_irq(IRQ_TIMER, &timer_irq);
|
||||
}
|
||||
|
548
arch/arm26/kernel/traps.c
Normal file
548
arch/arm26/kernel/traps.c
Normal file
@@ -0,0 +1,548 @@
|
||||
/*
|
||||
* linux/arch/arm26/kernel/traps.c
|
||||
*
|
||||
* Copyright (C) 1995-2002 Russell King
|
||||
* Fragments that appear the same as linux/arch/i386/kernel/traps.c (C) Linus Torvalds
|
||||
* Copyright (C) 2003 Ian Molton (ARM26)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 'traps.c' handles hardware exceptions after we have saved some state in
|
||||
* 'linux/arch/arm26/lib/traps.S'. Mostly a debugging aid, but will probably
|
||||
* kill the offending process.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/config.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/personality.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/elf.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/atomic.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <asm/semaphore.h>
|
||||
|
||||
#include "ptrace.h"
|
||||
|
||||
extern void c_backtrace (unsigned long fp, int pmode);
|
||||
extern void show_pte(struct mm_struct *mm, unsigned long addr);
|
||||
|
||||
const char *processor_modes[] = { "USER_26", "FIQ_26" , "IRQ_26" , "SVC_26" };
|
||||
|
||||
static const char *handler[]= { "prefetch abort", "data abort", "address exception", "interrupt" "*bad reason*"};
|
||||
|
||||
/*
|
||||
* Stack pointers should always be within the kernels view of
|
||||
* physical memory. If it is not there, then we can't dump
|
||||
* out any information relating to the stack.
|
||||
*/
|
||||
static int verify_stack(unsigned long sp)
|
||||
{
|
||||
if (sp < PAGE_OFFSET || (sp > (unsigned long)high_memory && high_memory != 0))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dump out the contents of some memory nicely...
|
||||
*/
|
||||
static void dump_mem(const char *str, unsigned long bottom, unsigned long top)
|
||||
{
|
||||
unsigned long p = bottom & ~31;
|
||||
mm_segment_t fs;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* We need to switch to kernel mode so that we can use __get_user
|
||||
* to safely read from kernel space. Note that we now dump the
|
||||
* code first, just in case the backtrace kills us.
|
||||
*/
|
||||
fs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
|
||||
printk("%s", str);
|
||||
printk("(0x%08lx to 0x%08lx)\n", bottom, top);
|
||||
|
||||
for (p = bottom & ~31; p < top;) {
|
||||
printk("%04lx: ", p & 0xffff);
|
||||
|
||||
for (i = 0; i < 8; i++, p += 4) {
|
||||
unsigned int val;
|
||||
|
||||
if (p < bottom || p >= top)
|
||||
printk(" ");
|
||||
else {
|
||||
__get_user(val, (unsigned long *)p);
|
||||
printk("%08x ", val);
|
||||
}
|
||||
}
|
||||
printk ("\n");
|
||||
}
|
||||
|
||||
set_fs(fs);
|
||||
}
|
||||
|
||||
static void dump_instr(struct pt_regs *regs)
|
||||
{
|
||||
unsigned long addr = instruction_pointer(regs);
|
||||
const int width = 8;
|
||||
mm_segment_t fs;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* We need to switch to kernel mode so that we can use __get_user
|
||||
* to safely read from kernel space. Note that we now dump the
|
||||
* code first, just in case the backtrace kills us.
|
||||
*/
|
||||
fs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
|
||||
printk("Code: ");
|
||||
for (i = -4; i < 1; i++) {
|
||||
unsigned int val, bad;
|
||||
|
||||
bad = __get_user(val, &((u32 *)addr)[i]);
|
||||
|
||||
if (!bad)
|
||||
printk(i == 0 ? "(%0*x) " : "%0*x ", width, val);
|
||||
else {
|
||||
printk("bad PC value.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
printk("\n");
|
||||
|
||||
set_fs(fs);
|
||||
}
|
||||
|
||||
/*static*/ void __dump_stack(struct task_struct *tsk, unsigned long sp)
|
||||
{
|
||||
dump_mem("Stack: ", sp, 8192+(unsigned long)tsk->thread_info);
|
||||
}
|
||||
|
||||
void dump_stack(void)
|
||||
{
|
||||
#ifdef CONFIG_DEBUG_ERRORS
|
||||
__backtrace();
|
||||
#endif
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(dump_stack);
|
||||
|
||||
//FIXME - was a static fn
|
||||
void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
|
||||
{
|
||||
unsigned int fp;
|
||||
int ok = 1;
|
||||
|
||||
printk("Backtrace: ");
|
||||
fp = regs->ARM_fp;
|
||||
if (!fp) {
|
||||
printk("no frame pointer");
|
||||
ok = 0;
|
||||
} else if (verify_stack(fp)) {
|
||||
printk("invalid frame pointer 0x%08x", fp);
|
||||
ok = 0;
|
||||
} else if (fp < (unsigned long)(tsk->thread_info + 1))
|
||||
printk("frame pointer underflow");
|
||||
printk("\n");
|
||||
|
||||
if (ok)
|
||||
c_backtrace(fp, processor_mode(regs));
|
||||
}
|
||||
|
||||
/* FIXME - this is probably wrong.. */
|
||||
void show_stack(struct task_struct *task, unsigned long *sp) {
|
||||
dump_mem("Stack: ", (unsigned long)sp, 8192+(unsigned long)task->thread_info);
|
||||
}
|
||||
|
||||
DEFINE_SPINLOCK(die_lock);
|
||||
|
||||
/*
|
||||
* This function is protected against re-entrancy.
|
||||
*/
|
||||
NORET_TYPE void die(const char *str, struct pt_regs *regs, int err)
|
||||
{
|
||||
struct task_struct *tsk = current;
|
||||
|
||||
console_verbose();
|
||||
spin_lock_irq(&die_lock);
|
||||
|
||||
printk("Internal error: %s: %x\n", str, err);
|
||||
printk("CPU: %d\n", smp_processor_id());
|
||||
show_regs(regs);
|
||||
printk("Process %s (pid: %d, stack limit = 0x%p)\n",
|
||||
current->comm, current->pid, tsk->thread_info + 1);
|
||||
|
||||
if (!user_mode(regs) || in_interrupt()) {
|
||||
__dump_stack(tsk, (unsigned long)(regs + 1));
|
||||
dump_backtrace(regs, tsk);
|
||||
dump_instr(regs);
|
||||
}
|
||||
while(1);
|
||||
spin_unlock_irq(&die_lock);
|
||||
do_exit(SIGSEGV);
|
||||
}
|
||||
|
||||
void die_if_kernel(const char *str, struct pt_regs *regs, int err)
|
||||
{
|
||||
if (user_mode(regs))
|
||||
return;
|
||||
|
||||
die(str, regs, err);
|
||||
}
|
||||
|
||||
static DECLARE_MUTEX(undef_sem);
|
||||
static int (*undef_hook)(struct pt_regs *);
|
||||
|
||||
int request_undef_hook(int (*fn)(struct pt_regs *))
|
||||
{
|
||||
int ret = -EBUSY;
|
||||
|
||||
down(&undef_sem);
|
||||
if (undef_hook == NULL) {
|
||||
undef_hook = fn;
|
||||
ret = 0;
|
||||
}
|
||||
up(&undef_sem);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int release_undef_hook(int (*fn)(struct pt_regs *))
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
|
||||
down(&undef_sem);
|
||||
if (undef_hook == fn) {
|
||||
undef_hook = NULL;
|
||||
ret = 0;
|
||||
}
|
||||
up(&undef_sem);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int undefined_extension(struct pt_regs *regs, unsigned int op)
|
||||
{
|
||||
switch (op) {
|
||||
case 1: /* 0xde01 / 0x?7f001f0 */
|
||||
ptrace_break(current, regs);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
asmlinkage void do_undefinstr(struct pt_regs *regs)
|
||||
{
|
||||
siginfo_t info;
|
||||
void *pc;
|
||||
|
||||
regs->ARM_pc -= 4;
|
||||
|
||||
pc = (unsigned long *)instruction_pointer(regs); /* strip PSR */
|
||||
|
||||
if (user_mode(regs)) {
|
||||
u32 instr;
|
||||
|
||||
get_user(instr, (u32 *)pc);
|
||||
|
||||
if ((instr & 0x0fff00ff) == 0x07f000f0 &&
|
||||
undefined_extension(regs, (instr >> 8) & 255) == 0) {
|
||||
regs->ARM_pc += 4;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (undef_hook && undef_hook(regs) == 0) {
|
||||
regs->ARM_pc += 4;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_USER
|
||||
printk(KERN_INFO "%s (%d): undefined instruction: pc=%p\n",
|
||||
current->comm, current->pid, pc);
|
||||
dump_instr(regs);
|
||||
#endif
|
||||
|
||||
current->thread.error_code = 0;
|
||||
current->thread.trap_no = 6;
|
||||
|
||||
info.si_signo = SIGILL;
|
||||
info.si_errno = 0;
|
||||
info.si_code = ILL_ILLOPC;
|
||||
info.si_addr = pc;
|
||||
|
||||
force_sig_info(SIGILL, &info, current);
|
||||
|
||||
die_if_kernel("Oops - undefined instruction", regs, 0);
|
||||
}
|
||||
|
||||
asmlinkage void do_excpt(unsigned long address, struct pt_regs *regs, int mode)
|
||||
{
|
||||
siginfo_t info;
|
||||
|
||||
#ifdef CONFIG_DEBUG_USER
|
||||
printk(KERN_INFO "%s (%d): address exception: pc=%08lx\n",
|
||||
current->comm, current->pid, instruction_pointer(regs));
|
||||
dump_instr(regs);
|
||||
#endif
|
||||
|
||||
current->thread.error_code = 0;
|
||||
current->thread.trap_no = 11;
|
||||
|
||||
info.si_signo = SIGBUS;
|
||||
info.si_errno = 0;
|
||||
info.si_code = BUS_ADRERR;
|
||||
info.si_addr = (void *)address;
|
||||
|
||||
force_sig_info(SIGBUS, &info, current);
|
||||
|
||||
die_if_kernel("Oops - address exception", regs, mode);
|
||||
}
|
||||
|
||||
asmlinkage void do_unexp_fiq (struct pt_regs *regs)
|
||||
{
|
||||
#ifndef CONFIG_IGNORE_FIQ
|
||||
printk("Hmm. Unexpected FIQ received, but trying to continue\n");
|
||||
printk("You may have a hardware problem...\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* bad_mode handles the impossible case in the vectors. If you see one of
|
||||
* these, then it's extremely serious, and could mean you have buggy hardware.
|
||||
* It never returns, and never tries to sync. We hope that we can at least
|
||||
* dump out some state information...
|
||||
*/
|
||||
asmlinkage void bad_mode(struct pt_regs *regs, int reason, int proc_mode)
|
||||
{
|
||||
unsigned int vectors = vectors_base();
|
||||
|
||||
console_verbose();
|
||||
|
||||
printk(KERN_CRIT "Bad mode in %s handler detected: mode %s\n",
|
||||
handler[reason<5?reason:4], processor_modes[proc_mode]);
|
||||
|
||||
/*
|
||||
* Dump out the vectors and stub routines. Maybe a better solution
|
||||
* would be to dump them out only if we detect that they are corrupted.
|
||||
*/
|
||||
dump_mem(KERN_CRIT "Vectors: ", vectors, vectors + 0x40);
|
||||
dump_mem(KERN_CRIT "Stubs: ", vectors + 0x200, vectors + 0x4b8);
|
||||
|
||||
die("Oops", regs, 0);
|
||||
local_irq_disable();
|
||||
panic("bad mode");
|
||||
}
|
||||
|
||||
static int bad_syscall(int n, struct pt_regs *regs)
|
||||
{
|
||||
struct thread_info *thread = current_thread_info();
|
||||
siginfo_t info;
|
||||
|
||||
if (current->personality != PER_LINUX && thread->exec_domain->handler) {
|
||||
thread->exec_domain->handler(n, regs);
|
||||
return regs->ARM_r0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_USER
|
||||
printk(KERN_ERR "[%d] %s: obsolete system call %08x.\n",
|
||||
current->pid, current->comm, n);
|
||||
dump_instr(regs);
|
||||
#endif
|
||||
|
||||
info.si_signo = SIGILL;
|
||||
info.si_errno = 0;
|
||||
info.si_code = ILL_ILLTRP;
|
||||
info.si_addr = (void *)instruction_pointer(regs) - 4;
|
||||
|
||||
force_sig_info(SIGILL, &info, current);
|
||||
die_if_kernel("Oops", regs, n);
|
||||
return regs->ARM_r0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
do_cache_op(unsigned long start, unsigned long end, int flags)
|
||||
{
|
||||
struct vm_area_struct *vma;
|
||||
|
||||
if (end < start)
|
||||
return;
|
||||
|
||||
vma = find_vma(current->active_mm, start);
|
||||
if (vma && vma->vm_start < end) {
|
||||
if (start < vma->vm_start)
|
||||
start = vma->vm_start;
|
||||
if (end > vma->vm_end)
|
||||
end = vma->vm_end;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle all unrecognised system calls.
|
||||
* 0x9f0000 - 0x9fffff are some more esoteric system calls
|
||||
*/
|
||||
#define NR(x) ((__ARM_NR_##x) - __ARM_NR_BASE)
|
||||
asmlinkage int arm_syscall(int no, struct pt_regs *regs)
|
||||
{
|
||||
siginfo_t info;
|
||||
|
||||
if ((no >> 16) != 0x9f)
|
||||
return bad_syscall(no, regs);
|
||||
|
||||
switch (no & 0xffff) {
|
||||
case 0: /* branch through 0 */
|
||||
info.si_signo = SIGSEGV;
|
||||
info.si_errno = 0;
|
||||
info.si_code = SEGV_MAPERR;
|
||||
info.si_addr = NULL;
|
||||
|
||||
force_sig_info(SIGSEGV, &info, current);
|
||||
|
||||
die_if_kernel("branch through zero", regs, 0);
|
||||
return 0;
|
||||
|
||||
case NR(breakpoint): /* SWI BREAK_POINT */
|
||||
ptrace_break(current, regs);
|
||||
return regs->ARM_r0;
|
||||
|
||||
case NR(cacheflush):
|
||||
return 0;
|
||||
|
||||
case NR(usr26):
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Calls 9f00xx..9f07ff are defined to return -ENOSYS
|
||||
if not implemented, rather than raising SIGILL. This
|
||||
way the calling program can gracefully determine whether
|
||||
a feature is supported. */
|
||||
if (no <= 0x7ff)
|
||||
return -ENOSYS;
|
||||
break;
|
||||
}
|
||||
#ifdef CONFIG_DEBUG_USER
|
||||
/*
|
||||
* experience shows that these seem to indicate that
|
||||
* something catastrophic has happened
|
||||
*/
|
||||
printk("[%d] %s: arm syscall %d\n", current->pid, current->comm, no);
|
||||
dump_instr(regs);
|
||||
if (user_mode(regs)) {
|
||||
show_regs(regs);
|
||||
c_backtrace(regs->ARM_fp, processor_mode(regs));
|
||||
}
|
||||
#endif
|
||||
info.si_signo = SIGILL;
|
||||
info.si_errno = 0;
|
||||
info.si_code = ILL_ILLTRP;
|
||||
info.si_addr = (void *)instruction_pointer(regs) - 4;
|
||||
|
||||
force_sig_info(SIGILL, &info, current);
|
||||
die_if_kernel("Oops", regs, no);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __bad_xchg(volatile void *ptr, int size)
|
||||
{
|
||||
printk("xchg: bad data size: pc 0x%p, ptr 0x%p, size %d\n",
|
||||
__builtin_return_address(0), ptr, size);
|
||||
BUG();
|
||||
}
|
||||
|
||||
/*
|
||||
* A data abort trap was taken, but we did not handle the instruction.
|
||||
* Try to abort the user program, or panic if it was the kernel.
|
||||
*/
|
||||
asmlinkage void
|
||||
baddataabort(int code, unsigned long instr, struct pt_regs *regs)
|
||||
{
|
||||
unsigned long addr = instruction_pointer(regs);
|
||||
siginfo_t info;
|
||||
|
||||
#ifdef CONFIG_DEBUG_USER
|
||||
printk(KERN_ERR "[%d] %s: bad data abort: code %d instr 0x%08lx\n",
|
||||
current->pid, current->comm, code, instr);
|
||||
dump_instr(regs);
|
||||
show_pte(current->mm, addr);
|
||||
#endif
|
||||
|
||||
info.si_signo = SIGILL;
|
||||
info.si_errno = 0;
|
||||
info.si_code = ILL_ILLOPC;
|
||||
info.si_addr = (void *)addr;
|
||||
|
||||
force_sig_info(SIGILL, &info, current);
|
||||
die_if_kernel("unknown data abort code", regs, instr);
|
||||
}
|
||||
|
||||
volatile void __bug(const char *file, int line, void *data)
|
||||
{
|
||||
printk(KERN_CRIT"kernel BUG at %s:%d!", file, line);
|
||||
if (data)
|
||||
printk(KERN_CRIT" - extra data = %p", data);
|
||||
printk("\n");
|
||||
*(int *)0 = 0;
|
||||
}
|
||||
|
||||
void __readwrite_bug(const char *fn)
|
||||
{
|
||||
printk("%s called, but not implemented", fn);
|
||||
BUG();
|
||||
}
|
||||
|
||||
void __pte_error(const char *file, int line, unsigned long val)
|
||||
{
|
||||
printk("%s:%d: bad pte %08lx.\n", file, line, val);
|
||||
}
|
||||
|
||||
void __pmd_error(const char *file, int line, unsigned long val)
|
||||
{
|
||||
printk("%s:%d: bad pmd %08lx.\n", file, line, val);
|
||||
}
|
||||
|
||||
void __pgd_error(const char *file, int line, unsigned long val)
|
||||
{
|
||||
printk("%s:%d: bad pgd %08lx.\n", file, line, val);
|
||||
}
|
||||
|
||||
asmlinkage void __div0(void)
|
||||
{
|
||||
printk("Division by zero in kernel.\n");
|
||||
dump_stack();
|
||||
}
|
||||
|
||||
void abort(void)
|
||||
{
|
||||
BUG();
|
||||
|
||||
/* if that doesn't kill us, halt */
|
||||
panic("Oops failed to kill thread");
|
||||
}
|
||||
|
||||
void __init trap_init(void)
|
||||
{
|
||||
extern void __trap_init(unsigned long);
|
||||
unsigned long base = vectors_base();
|
||||
|
||||
__trap_init(base);
|
||||
if (base != 0)
|
||||
printk(KERN_DEBUG "Relocating machine vectors to 0x%08lx\n",
|
||||
base);
|
||||
}
|
134
arch/arm26/kernel/vmlinux-arm26-xip.lds.in
Normal file
134
arch/arm26/kernel/vmlinux-arm26-xip.lds.in
Normal file
@@ -0,0 +1,134 @@
|
||||
/* ld script to make ARM Linux kernel
|
||||
* taken from the i386 version by Russell King
|
||||
* Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>
|
||||
* borrowed from Russels ARM port by Ian Molton
|
||||
*/
|
||||
|
||||
#include <asm-generic/vmlinux.lds.h>
|
||||
|
||||
OUTPUT_ARCH(arm)
|
||||
ENTRY(stext)
|
||||
jiffies = jiffies_64;
|
||||
SECTIONS
|
||||
{
|
||||
. = TEXTADDR;
|
||||
.init : { /* Init code and data */
|
||||
_stext = .;
|
||||
__init_begin = .;
|
||||
_sinittext = .;
|
||||
*(.init.text)
|
||||
_einittext = .;
|
||||
__proc_info_begin = .;
|
||||
*(.proc.info)
|
||||
__proc_info_end = .;
|
||||
__arch_info_begin = .;
|
||||
*(.arch.info)
|
||||
__arch_info_end = .;
|
||||
__tagtable_begin = .;
|
||||
*(.taglist)
|
||||
__tagtable_end = .;
|
||||
. = ALIGN(16);
|
||||
__setup_start = .;
|
||||
*(.init.setup)
|
||||
__setup_end = .;
|
||||
__early_begin = .;
|
||||
*(__early_param)
|
||||
__early_end = .;
|
||||
__initcall_start = .;
|
||||
*(.initcall1.init)
|
||||
*(.initcall2.init)
|
||||
*(.initcall3.init)
|
||||
*(.initcall4.init)
|
||||
*(.initcall5.init)
|
||||
*(.initcall6.init)
|
||||
*(.initcall7.init)
|
||||
__initcall_end = .;
|
||||
__con_initcall_start = .;
|
||||
*(.con_initcall.init)
|
||||
__con_initcall_end = .;
|
||||
. = ALIGN(32);
|
||||
__initramfs_start = .;
|
||||
usr/built-in.o(.init.ramfs)
|
||||
__initramfs_end = .;
|
||||
. = ALIGN(32768);
|
||||
__init_end = .;
|
||||
}
|
||||
|
||||
/DISCARD/ : { /* Exit code and data */
|
||||
*(.exit.text)
|
||||
*(.exit.data)
|
||||
*(.exitcall.exit)
|
||||
}
|
||||
|
||||
.text : { /* Real text segment */
|
||||
_text = .; /* Text and read-only data */
|
||||
*(.text)
|
||||
SCHED_TEXT
|
||||
LOCK_TEXT /* FIXME - borrowed from arm32 - check*/
|
||||
*(.fixup)
|
||||
*(.gnu.warning)
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
*(.glue_7)
|
||||
*(.glue_7t)
|
||||
*(.got) /* Global offset table */
|
||||
|
||||
_etext = .; /* End of text section */
|
||||
}
|
||||
|
||||
. = ALIGN(16);
|
||||
__ex_table : { /* Exception table */
|
||||
__start___ex_table = .;
|
||||
*(__ex_table)
|
||||
__stop___ex_table = .;
|
||||
}
|
||||
|
||||
RODATA
|
||||
|
||||
_endtext = .;
|
||||
|
||||
. = DATAADDR;
|
||||
|
||||
_sdata = .;
|
||||
|
||||
.data : {
|
||||
. = ALIGN(8192);
|
||||
/*
|
||||
* first, the init thread union, aligned
|
||||
* to an 8192 byte boundary. (see arm26/kernel/init_task.c)
|
||||
* FIXME - sould this be 32K aligned on arm26?
|
||||
*/
|
||||
*(.init.task)
|
||||
|
||||
/*
|
||||
* The cacheline aligned data
|
||||
*/
|
||||
. = ALIGN(32);
|
||||
*(.data.cacheline_aligned)
|
||||
|
||||
/*
|
||||
* and the usual data section
|
||||
*/
|
||||
*(.data)
|
||||
CONSTRUCTORS
|
||||
|
||||
*(.init.data)
|
||||
|
||||
_edata = .;
|
||||
}
|
||||
|
||||
.bss : {
|
||||
__bss_start = .; /* BSS */
|
||||
*(.bss)
|
||||
*(COMMON)
|
||||
_end = . ;
|
||||
}
|
||||
/* Stabs debugging sections. */
|
||||
.stab 0 : { *(.stab) }
|
||||
.stabstr 0 : { *(.stabstr) }
|
||||
.stab.excl 0 : { *(.stab.excl) }
|
||||
.stab.exclstr 0 : { *(.stab.exclstr) }
|
||||
.stab.index 0 : { *(.stab.index) }
|
||||
.stab.indexstr 0 : { *(.stab.indexstr) }
|
||||
.comment 0 : { *(.comment) }
|
||||
}
|
127
arch/arm26/kernel/vmlinux-arm26.lds.in
Normal file
127
arch/arm26/kernel/vmlinux-arm26.lds.in
Normal file
@@ -0,0 +1,127 @@
|
||||
/* ld script to make ARM Linux kernel
|
||||
* taken from the i386 version by Russell King
|
||||
* Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>
|
||||
* borrowed from Russels ARM port by Ian Molton and subsequently modified.
|
||||
*/
|
||||
|
||||
#include <asm-generic/vmlinux.lds.h>
|
||||
|
||||
OUTPUT_ARCH(arm)
|
||||
ENTRY(stext)
|
||||
jiffies = jiffies_64;
|
||||
SECTIONS
|
||||
{
|
||||
. = TEXTADDR;
|
||||
.init : { /* Init code and data */
|
||||
_stext = .;
|
||||
__init_begin = .;
|
||||
_sinittext = .;
|
||||
*(.init.text)
|
||||
_einittext = .;
|
||||
__proc_info_begin = .;
|
||||
*(.proc.info)
|
||||
__proc_info_end = .;
|
||||
__arch_info_begin = .;
|
||||
*(.arch.info)
|
||||
__arch_info_end = .;
|
||||
__tagtable_begin = .;
|
||||
*(.taglist)
|
||||
__tagtable_end = .;
|
||||
*(.init.data)
|
||||
. = ALIGN(16);
|
||||
__setup_start = .;
|
||||
*(.init.setup)
|
||||
__setup_end = .;
|
||||
__early_begin = .;
|
||||
*(__early_param)
|
||||
__early_end = .;
|
||||
__initcall_start = .;
|
||||
*(.initcall1.init)
|
||||
*(.initcall2.init)
|
||||
*(.initcall3.init)
|
||||
*(.initcall4.init)
|
||||
*(.initcall5.init)
|
||||
*(.initcall6.init)
|
||||
*(.initcall7.init)
|
||||
__initcall_end = .;
|
||||
__con_initcall_start = .;
|
||||
*(.con_initcall.init)
|
||||
__con_initcall_end = .;
|
||||
. = ALIGN(32);
|
||||
__initramfs_start = .;
|
||||
usr/built-in.o(.init.ramfs)
|
||||
__initramfs_end = .;
|
||||
. = ALIGN(32768);
|
||||
__init_end = .;
|
||||
}
|
||||
|
||||
/DISCARD/ : { /* Exit code and data */
|
||||
*(.exit.text)
|
||||
*(.exit.data)
|
||||
*(.exitcall.exit)
|
||||
}
|
||||
|
||||
.text : { /* Real text segment */
|
||||
_text = .; /* Text and read-only data */
|
||||
*(.text)
|
||||
SCHED_TEXT
|
||||
LOCK_TEXT
|
||||
*(.fixup)
|
||||
*(.gnu.warning)
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
*(.glue_7)
|
||||
*(.glue_7t)
|
||||
*(.got) /* Global offset table */
|
||||
|
||||
_etext = .; /* End of text section */
|
||||
}
|
||||
|
||||
. = ALIGN(16);
|
||||
__ex_table : { /* Exception table */
|
||||
__start___ex_table = .;
|
||||
*(__ex_table)
|
||||
__stop___ex_table = .;
|
||||
}
|
||||
|
||||
RODATA
|
||||
|
||||
. = ALIGN(8192);
|
||||
|
||||
.data : {
|
||||
/*
|
||||
* first, the init task union, aligned
|
||||
* to an 8192 byte boundary. (see arm26/kernel/init_task.c)
|
||||
*/
|
||||
*(.init.task)
|
||||
|
||||
/*
|
||||
* The cacheline aligned data
|
||||
*/
|
||||
. = ALIGN(32);
|
||||
*(.data.cacheline_aligned)
|
||||
|
||||
/*
|
||||
* and the usual data section
|
||||
*/
|
||||
*(.data)
|
||||
CONSTRUCTORS
|
||||
|
||||
_edata = .;
|
||||
}
|
||||
|
||||
.bss : {
|
||||
__bss_start = .; /* BSS */
|
||||
*(.bss)
|
||||
*(COMMON)
|
||||
_end = . ;
|
||||
}
|
||||
/* Stabs debugging sections. */
|
||||
.stab 0 : { *(.stab) }
|
||||
.stabstr 0 : { *(.stabstr) }
|
||||
.stab.excl 0 : { *(.stab.excl) }
|
||||
.stab.exclstr 0 : { *(.stab.exclstr) }
|
||||
.stab.index 0 : { *(.stab.index) }
|
||||
.stab.indexstr 0 : { *(.stab.indexstr) }
|
||||
.comment 0 : { *(.comment) }
|
||||
}
|
12
arch/arm26/kernel/vmlinux.lds.S
Normal file
12
arch/arm26/kernel/vmlinux.lds.S
Normal file
@@ -0,0 +1,12 @@
|
||||
#include <linux/config.h>
|
||||
|
||||
#ifdef CONFIG_XIP_KERNEL
|
||||
|
||||
#include "vmlinux-arm26-xip.lds.in"
|
||||
|
||||
#else
|
||||
|
||||
#include "vmlinux-arm26.lds.in"
|
||||
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user